gom-couchdb-adapter 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -83,6 +83,10 @@ Since lazy loading is supported, the author will just be fetched on the first ac
83
83
 
84
84
  ... will invoke another fetch of the author's object.
85
85
 
86
+ == Views
87
+
88
+ This adapter currently supports class and map/reduce views. For documentation see http://github.com/phifty/gom.
89
+
86
90
  == Development
87
91
 
88
92
  Development has been done test-driven and the code follows at most the Clean Code paradigms. Code smells has been
@@ -5,23 +5,22 @@ module GOM
5
5
 
6
6
  module CouchDB
7
7
 
8
- # The couchdb storage adapter
8
+ # The couchdb storage adapter.
9
9
  class Adapter < GOM::Storage::Adapter
10
10
 
11
11
  def setup
12
12
  initialize_server
13
13
  initialize_database
14
14
  setup_database
15
+ push_design
15
16
  end
16
17
 
17
18
  def fetch(id)
18
- fetcher = Fetcher.new @database, id, revisions
19
- fetcher.perform
20
- fetcher.object_hash
19
+ Fetcher.new(@database, id, revisions).draft
21
20
  end
22
21
 
23
- def store(object_hash)
24
- saver = Saver.new @database, object_hash, revisions, configuration.name
22
+ def store(draft)
23
+ saver = Saver.new @database, draft, revisions, configuration.name
25
24
  saver.perform
26
25
  saver.id
27
26
  end
@@ -31,6 +30,13 @@ module GOM
31
30
  remover.perform
32
31
  end
33
32
 
33
+ def collection(name, options = { })
34
+ view = @design.views[name.to_s]
35
+ raise ViewNotFoundError, "there are no view with the name #{name}" unless view
36
+ fetcher = Collection::Fetcher.new view, options
37
+ GOM::Object::Collection.new fetcher
38
+ end
39
+
34
40
  def revisions
35
41
  @revisions ||= { }
36
42
  end
@@ -51,6 +57,12 @@ module GOM
51
57
  @database.create_if_missing! if create_database_if_missing
52
58
  end
53
59
 
60
+ def push_design
61
+ pusher = View::Pusher.new @database, configuration.views
62
+ pusher.perform
63
+ @design = pusher.design
64
+ end
65
+
54
66
  end
55
67
 
56
68
  end
@@ -0,0 +1,49 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ module CouchDB
7
+
8
+ module Collection
9
+
10
+ # Fetches a result-set of a CouchDB view and provides it to a GOM collection.
11
+ class Fetcher
12
+
13
+ def initialize(view, options)
14
+ @view, @options = view, options
15
+ end
16
+
17
+ def drafts
18
+ return nil if @view.reduce
19
+ fetch_collection
20
+ fetch_drafts
21
+ @drafts
22
+ end
23
+
24
+ def rows
25
+ fetch_collection
26
+ @collection
27
+ end
28
+
29
+ private
30
+
31
+ def fetch_collection
32
+ @collection = @view.collection @options
33
+ end
34
+
35
+ def fetch_drafts
36
+ @drafts = @collection.documents.map do |document|
37
+ GOM::Storage::CouchDB::Draft::Builder.new(document).draft
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,18 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ module CouchDB
7
+
8
+ module Collection
9
+
10
+ autoload :Fetcher, File.join(File.dirname(__FILE__), "collection", "fetcher")
11
+
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,72 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ module CouchDB
7
+
8
+ module Draft
9
+
10
+ # Builds a draft out of a CouchDB document.
11
+ class Builder
12
+
13
+ def initialize(document)
14
+ @document = document
15
+ end
16
+
17
+ def draft
18
+ initialize_draft
19
+ set_id
20
+ set_class
21
+ set_properties_and_relations
22
+ @draft
23
+ end
24
+
25
+ private
26
+
27
+ def initialize_draft
28
+ @draft = GOM::Object::Draft.new
29
+ end
30
+
31
+ def set_id
32
+ @draft.id = @document.id
33
+ end
34
+
35
+ def set_class
36
+ @draft.class_name = @document["model_class"]
37
+ end
38
+
39
+ def set_properties_and_relations
40
+ @document.each do |key, value|
41
+ set_property key, value if property_key?(key)
42
+ set_relation key, value if relation_key?(key)
43
+ end
44
+ end
45
+
46
+ def set_property(key, value)
47
+ @draft.properties[key.to_sym] = value
48
+ end
49
+
50
+ def set_relation(key, value)
51
+ name = key.sub /_id$/, ""
52
+ id = GOM::Object::Id.new value
53
+ @draft.relations[name.to_sym] = GOM::Object::Proxy.new id
54
+ end
55
+
56
+ def property_key?(key)
57
+ !relation_key?(key) && ![ "_id", "_rev", "model_class" ].include?(key)
58
+ end
59
+
60
+ def relation_key?(key)
61
+ key =~ /.+_id$/
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,18 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ module CouchDB
7
+
8
+ module Draft
9
+
10
+ autoload :Builder, File.join(File.dirname(__FILE__), "draft", "builder")
11
+
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -5,21 +5,24 @@ module GOM
5
5
 
6
6
  module CouchDB
7
7
 
8
- # Fetches a CouchDB document with the given id and returns an object hash.
8
+ # Fetches a CouchDB document with the given id and returns a draft.
9
9
  class Fetcher
10
10
 
11
- attr_reader :object_hash
11
+ attr_accessor :database
12
+ attr_accessor :id
13
+ attr_accessor :revisions
12
14
 
13
15
  def initialize(database, id, revisions)
14
16
  @database, @id, @revisions = database, id, revisions
15
17
  end
16
18
 
17
- def perform
19
+ def draft
18
20
  initialize_document
19
21
  load_document
20
- return unless has_object_hash?
21
- transfer_properties
22
- transfer_class
22
+ build_draft
23
+ @draft
24
+ rescue ::CouchDB::Document::NotFoundError
25
+ nil
23
26
  end
24
27
 
25
28
  private
@@ -31,49 +34,15 @@ module GOM
31
34
 
32
35
  def load_document
33
36
  @document.load
34
- @object_hash = { :id => @document.id }
35
37
  set_revision
36
- rescue ::CouchDB::Document::NotFoundError
37
- @object_hash = nil
38
38
  end
39
39
 
40
40
  def set_revision
41
41
  @revisions[@document.id] = @document.rev
42
42
  end
43
43
 
44
- def has_object_hash?
45
- !!@object_hash
46
- end
47
-
48
- def transfer_properties
49
- @document.each do |key, value|
50
- set_property key, value if property_key?(key)
51
- set_relation key, value if relation_key?(key)
52
- end
53
- end
54
-
55
- def transfer_class
56
- @object_hash[:class] = @document["model_class"]
57
- end
58
-
59
- def property_key?(key)
60
- !relation_key?(key) && ![ "_id", "_rev", "model_class" ].include?(key)
61
- end
62
-
63
- def relation_key?(key)
64
- key =~ /.+_id$/
65
- end
66
-
67
- def set_property(key, value)
68
- @object_hash[:properties] ||= { }
69
- @object_hash[:properties][key.to_sym] = value
70
- end
71
-
72
- def set_relation(key, value)
73
- name = key.sub /_id$/, ""
74
- id = GOM::Object::Id.new value
75
- @object_hash[:relations] ||= { }
76
- @object_hash[:relations][name.to_sym] = GOM::Object::Proxy.new id
44
+ def build_draft
45
+ @draft = GOM::Storage::CouchDB::Draft::Builder.new(@document).draft
77
46
  end
78
47
 
79
48
  end
@@ -5,13 +5,13 @@ module GOM
5
5
 
6
6
  module CouchDB
7
7
 
8
- # Saves the given object hash to a CouchDB document.
8
+ # Saves the given draft to a CouchDB document.
9
9
  class Saver
10
10
 
11
11
  attr_reader :id
12
12
 
13
- def initialize(database, object_hash, revisions, relation_storage_name)
14
- @database, @object_hash, @revisions, @relation_storage_name = database, object_hash, revisions, relation_storage_name
13
+ def initialize(database, draft, revisions, relation_storage_name)
14
+ @database, @draft, @revisions, @relation_storage_name = database, draft, revisions, relation_storage_name
15
15
  end
16
16
 
17
17
  def perform
@@ -26,18 +26,18 @@ module GOM
26
26
 
27
27
  def initialize_document
28
28
  @document = ::CouchDB::Document.new @database
29
- @document.id = @object_hash[:id] if @object_hash.has_key?(:id)
30
- @document["model_class"] = @object_hash[:class]
29
+ @document.id = @draft.id if @draft.id
30
+ @document["model_class"] = @draft.class_name
31
31
  end
32
32
 
33
33
  def set_properties
34
- (@object_hash[:properties] || { }).each do |key, value|
34
+ @draft.properties.each do |key, value|
35
35
  @document[key.to_s] = value
36
36
  end
37
37
  end
38
38
 
39
39
  def set_relations
40
- (@object_hash[:relations] || { }).each do |key, object_proxy|
40
+ @draft.relations.each do |key, object_proxy|
41
41
  id, object = object_proxy.id, object_proxy.object
42
42
  @document["#{key}_id"] = if id
43
43
  id.to_s
@@ -0,0 +1,32 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ module CouchDB
7
+
8
+ module View
9
+
10
+ # Builds a javascript map-reduce-view out of a class view.
11
+ class Builder
12
+
13
+ def initialize(class_view)
14
+ @class_view = class_view
15
+ end
16
+
17
+ def map_reduce_view
18
+ GOM::Storage::Configuration::View::MapReduce.new(
19
+ "function(document) {\n if (document['model_class'] == '#{@class_view.class_name}') {\n emit(document['_id'], null);\n }\n}",
20
+ nil
21
+ )
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,67 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ module CouchDB
7
+
8
+ module View
9
+
10
+ # A helper to push design documents to the CouchDB server.
11
+ class Pusher
12
+
13
+ attr_reader :design
14
+
15
+ def initialize(database, view_hash)
16
+ @database, @view_hash = database, view_hash
17
+ end
18
+
19
+ def perform
20
+ initialize_design
21
+ add_views
22
+ push_design
23
+ end
24
+
25
+ private
26
+
27
+ def initialize_design
28
+ @design = ::CouchDB::Design.new @database, "gom"
29
+ end
30
+
31
+ def add_views
32
+ @view_hash.each do |name, view|
33
+ add_view name.to_s, view
34
+ end
35
+ end
36
+
37
+ def push_design
38
+ @design.save
39
+ end
40
+
41
+ def add_view(name, view)
42
+ case view.class.to_s
43
+ when "GOM::Storage::Configuration::View::Class"
44
+ add_view_by_class_view name, view
45
+ when "GOM::Storage::Configuration::View::MapReduce"
46
+ add_view_by_map_reduce_view name, view
47
+ end
48
+ end
49
+
50
+ def add_view_by_class_view(name, class_view)
51
+ map_reduce_view = Builder.new(class_view).map_reduce_view
52
+ @design.views << ::CouchDB::Design::View.new(@design, name, map_reduce_view.map, map_reduce_view.reduce)
53
+ end
54
+
55
+ def add_view_by_map_reduce_view(name, map_reduce_view)
56
+ @design.views << ::CouchDB::Design::View.new(@design, name, map_reduce_view.map, map_reduce_view.reduce)
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,19 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ module CouchDB
7
+
8
+ module View
9
+
10
+ autoload :Builder, File.join(File.dirname(__FILE__), "view", "builder")
11
+ autoload :Pusher, File.join(File.dirname(__FILE__), "view", "pusher")
12
+
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -6,9 +6,12 @@ module GOM
6
6
  module CouchDB
7
7
 
8
8
  autoload :Adapter, File.join(File.dirname(__FILE__), "couchdb", "adapter")
9
+ autoload :Collection, File.join(File.dirname(__FILE__), "couchdb", "collection")
9
10
  autoload :Fetcher, File.join(File.dirname(__FILE__), "couchdb", "fetcher")
11
+ autoload :Draft, File.join(File.dirname(__FILE__), "couchdb", "draft")
10
12
  autoload :Saver, File.join(File.dirname(__FILE__), "couchdb", "saver")
11
13
  autoload :Remover, File.join(File.dirname(__FILE__), "couchdb", "remover")
14
+ autoload :View, File.join(File.dirname(__FILE__), "couchdb", "view")
12
15
 
13
16
  end
14
17
 
@@ -0,0 +1,43 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib", "couchdb-adapter"))
3
+
4
+ GOM::Storage::Configuration.read File.join(File.dirname(__FILE__), "..", "storage.configuration")
5
+
6
+ describe "couchdb adapter map reduce collection" do
7
+
8
+ before :all do
9
+ GOM::Storage.setup
10
+ end
11
+
12
+ before :each do
13
+ @object_one = Object.new
14
+ @object_one.instance_variable_set :@number, 11
15
+ GOM::Storage.store @object_one, :test_storage
16
+
17
+ @object_two = Object.new
18
+ @object_two.instance_variable_set :@number, 18
19
+ GOM::Storage.store @object_two, :test_storage
20
+
21
+ @collection = GOM::Storage.collection :test_storage, :test_map_reduce_view
22
+ end
23
+
24
+ after :each do
25
+ GOM::Storage.remove @object_one
26
+ GOM::Storage.remove @object_two
27
+ end
28
+
29
+ describe "first" do
30
+
31
+ it "should return a row" do
32
+ row = @collection.first
33
+ row.should be_instance_of(CouchDB::Row)
34
+ end
35
+
36
+ it "should return a row with the correct result" do
37
+ row = @collection.first
38
+ row.value.should == 29
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -9,7 +9,7 @@ describe GOM::Storage::CouchDB::Adapter do
9
9
  @database = mock CouchDB::Database, :delete_if_exists! => nil, :create_if_missing! => nil
10
10
  CouchDB::Database.stub(:new).and_return(@database)
11
11
 
12
- @configuration = mock GOM::Storage::Configuration, :name => "test_storage"
12
+ @configuration = mock GOM::Storage::Configuration, :name => "test_storage", :views => :test_views
13
13
  @configuration.stub(:[]).with(:database).and_return("test")
14
14
  @configuration.stub(:values_at) do |*arguments|
15
15
  result = nil
@@ -18,6 +18,10 @@ describe GOM::Storage::CouchDB::Adapter do
18
18
  result
19
19
  end
20
20
 
21
+ @design = mock CouchDB::Design
22
+ @pusher = mock GOM::Storage::CouchDB::View::Pusher, :perform => nil, :design => @design
23
+ GOM::Storage::CouchDB::View::Pusher.stub(:new).and_return(@pusher)
24
+
21
25
  @adapter = described_class.new @configuration
22
26
  end
23
27
 
@@ -47,6 +51,16 @@ describe GOM::Storage::CouchDB::Adapter do
47
51
  @adapter.setup
48
52
  end
49
53
 
54
+ it "should initialize the view pusher" do
55
+ GOM::Storage::CouchDB::View::Pusher.should_receive(:new).with(@database, :test_views).and_return(@pusher)
56
+ @adapter.setup
57
+ end
58
+
59
+ it "should push the views" do
60
+ @pusher.should_receive(:perform)
61
+ @adapter.setup
62
+ end
63
+
50
64
  end
51
65
 
52
66
  describe "fetch" do
@@ -56,9 +70,9 @@ describe GOM::Storage::CouchDB::Adapter do
56
70
 
57
71
  @id = "test_object_1"
58
72
  @revisions = @adapter.send :revisions
59
- @object_hash = mock Hash
73
+ @draft = mock GOM::Object::Draft
60
74
 
61
- @fetcher = mock GOM::Storage::CouchDB::Fetcher, :perform => nil, :object_hash => @object_hash
75
+ @fetcher = mock GOM::Storage::CouchDB::Fetcher, :draft => @draft
62
76
  GOM::Storage::CouchDB::Fetcher.stub(:new).and_return(@fetcher)
63
77
  end
64
78
 
@@ -67,13 +81,8 @@ describe GOM::Storage::CouchDB::Adapter do
67
81
  @adapter.fetch @id
68
82
  end
69
83
 
70
- it "should perform a fetch" do
71
- @fetcher.should_receive(:perform)
72
- @adapter.fetch @id
73
- end
74
-
75
- it "should return the object_hash" do
76
- @adapter.fetch(@id).should == @object_hash
84
+ it "should return the draft" do
85
+ @adapter.fetch(@id).should == @draft
77
86
  end
78
87
 
79
88
  end
@@ -83,7 +92,7 @@ describe GOM::Storage::CouchDB::Adapter do
83
92
  before :each do
84
93
  @adapter.setup
85
94
 
86
- @object_hash = mock Hash
95
+ @draft = mock GOM::Object::Draft
87
96
  @revisions = @adapter.send :revisions
88
97
  @id = "test_object_1"
89
98
 
@@ -92,17 +101,17 @@ describe GOM::Storage::CouchDB::Adapter do
92
101
  end
93
102
 
94
103
  it "should initialize the saver" do
95
- GOM::Storage::CouchDB::Saver.should_receive(:new).with(@database, @object_hash, @revisions, "test_storage").and_return(@saver)
96
- @adapter.store @object_hash
104
+ GOM::Storage::CouchDB::Saver.should_receive(:new).with(@database, @draft, @revisions, "test_storage").and_return(@saver)
105
+ @adapter.store @draft
97
106
  end
98
107
 
99
108
  it "should perform a fetch" do
100
109
  @saver.should_receive(:perform)
101
- @adapter.store @object_hash
110
+ @adapter.store @draft
102
111
  end
103
112
 
104
- it "should return the object_hash" do
105
- @adapter.store(@object_hash).should == @id
113
+ it "should return the draft" do
114
+ @adapter.store(@draft).should == @id
106
115
  end
107
116
 
108
117
  end
@@ -131,4 +140,51 @@ describe GOM::Storage::CouchDB::Adapter do
131
140
 
132
141
  end
133
142
 
143
+ describe "collection" do
144
+
145
+ before :each do
146
+ @adapter.setup
147
+
148
+ @view = mock CouchDB::Design::View
149
+ @views = mock Hash, :[] => @view
150
+ @design.stub(:views).and_return(@views)
151
+
152
+ @options = mock Hash
153
+
154
+ @fetcher = mock GOM::Storage::CouchDB::Collection::Fetcher
155
+ GOM::Storage::CouchDB::Collection::Fetcher.stub(:new).and_return(@fetcher)
156
+
157
+ @collection = mock GOM::Object::Collection
158
+ GOM::Object::Collection.stub(:new).and_return(@collection)
159
+ end
160
+
161
+ it "should select the right view" do
162
+ @views.should_receive(:[]).with("test_view").and_return(@view)
163
+ @adapter.collection :test_view, @options
164
+ end
165
+
166
+ it "should raise #{described_class::ViewNotFoundError} if the view name is invalid" do
167
+ @views.stub(:[]).and_return(nil)
168
+ lambda do
169
+ @adapter.collection :test_view, @options
170
+ end.should raise_error(described_class::ViewNotFoundError)
171
+ end
172
+
173
+ it "should initialize a collection fetcher" do
174
+ GOM::Storage::CouchDB::Collection::Fetcher.should_receive(:new).with(@view, @options).and_return(@fetcher)
175
+ @adapter.collection :test_view, @options
176
+ end
177
+
178
+ it "should initialize a collection with the fetcher" do
179
+ GOM::Object::Collection.should_receive(:new).with(@fetcher).and_return(@collection)
180
+ @adapter.collection :test_view, @options
181
+ end
182
+
183
+ it "should return the collection" do
184
+ collection = @adapter.collection :test_view, @options
185
+ collection.should == @collection
186
+ end
187
+
188
+ end
189
+
134
190
  end
@@ -0,0 +1,65 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "..", "spec_helper"))
2
+
3
+ describe GOM::Storage::CouchDB::Collection::Fetcher do
4
+
5
+ before :each do
6
+ @document = mock CouchDB::Document
7
+ @documents = [ @document ]
8
+ @collection = mock CouchDB::Collection, :documents => @documents
9
+ @view = mock CouchDB::Design::View, :reduce => nil, :collection => @collection
10
+ @options = mock Hash
11
+
12
+ @draft = mock GOM::Object::Draft
13
+
14
+ @builder = mock GOM::Storage::CouchDB::Draft::Builder, :draft => @draft
15
+ GOM::Storage::CouchDB::Draft::Builder.stub(:new).and_return(@builder)
16
+
17
+ @fetcher = described_class.new @view, @options
18
+ end
19
+
20
+ describe "drafts" do
21
+
22
+ context "with a view that don't has a reduce function" do
23
+
24
+ it "should pass the options to the collection of the view" do
25
+ @view.should_receive(:collection).with(@options).and_return(@collection)
26
+ @fetcher.drafts
27
+ end
28
+
29
+ it "should initialize the draft builder of each document" do
30
+ GOM::Storage::CouchDB::Draft::Builder.should_receive(:new).with(@document).and_return(@builder)
31
+ @fetcher.drafts
32
+ end
33
+
34
+ it "should return an array of drafts" do
35
+ drafts = @fetcher.drafts
36
+ drafts.should == [ @draft ]
37
+ end
38
+
39
+ end
40
+
41
+ context "with a view that has a reduce function" do
42
+
43
+ before :each do
44
+ @view.stub(:reduce).and_return(:test_reduce_function)
45
+ end
46
+
47
+ it "should return nil" do
48
+ drafts = @fetcher.drafts
49
+ drafts.should be_nil
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ describe "rows" do
57
+
58
+ it "should return the original couchdb collection" do
59
+ rows = @fetcher.rows
60
+ rows.should == @collection
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,37 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "..", "spec_helper"))
2
+
3
+ describe GOM::Storage::CouchDB::Draft::Builder do
4
+
5
+ before :each do
6
+ @document = mock CouchDB::Document, :id => "test_document_1", :[] => "Object", :each => nil
7
+
8
+ @builder = described_class.new @document
9
+ end
10
+
11
+ describe "draft" do
12
+
13
+ it "should set the id" do
14
+ draft = @builder.draft
15
+ draft.id.should == "test_document_1"
16
+ end
17
+
18
+ it "should set the class" do
19
+ draft = @builder.draft
20
+ draft.class_name.should == "Object"
21
+ end
22
+
23
+ it "should transfer each property" do
24
+ @document.stub(:each).and_yield("test", "test value")
25
+ draft = @builder.draft
26
+ draft.properties.should == { :test => "test value" }
27
+ end
28
+
29
+ it "should transfer each relation" do
30
+ @document.stub(:each).and_yield("test_id", "test_storage:test_object_2")
31
+ draft = @builder.draft
32
+ draft.relations[:test].should be_instance_of(GOM::Object::Proxy)
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -7,68 +7,47 @@ describe GOM::Storage::CouchDB::Fetcher do
7
7
  @id = "test_object_1"
8
8
  @revisions = mock Hash, :[]= => nil
9
9
 
10
+ @document = mock CouchDB::Document, :id= => nil, :id => @id, :rev => 1, :load => true
11
+ CouchDB::Document.stub(:new).and_return(@document)
12
+
13
+ @draft = mock GOM::Object::Draft
14
+ @builder = mock GOM::Storage::CouchDB::Draft::Builder, :draft => @draft
15
+ GOM::Storage::CouchDB::Draft::Builder.stub(:new).and_return(@builder)
16
+
10
17
  @fetcher = described_class.new @database, @id, @revisions
11
18
  end
12
19
 
13
- describe "perform" do
14
-
15
- before :each do
16
- @document = mock CouchDB::Document, :id => "test_object_1", :id= => nil, :rev => 1, :[] => "Object", :load => true
17
- @document.stub(:each).and_yield("test", "test value")
18
- CouchDB::Document.stub(:new).and_return(@document)
19
- end
20
+ describe "draft" do
20
21
 
21
22
  it "should initialize a document" do
22
23
  CouchDB::Document.should_receive(:new).with(@database).and_return(@document)
23
- @fetcher.perform
24
- end
25
-
26
- it "should set the id" do
27
- @document.should_receive(:id=).with("test_object_1")
28
- @fetcher.perform
24
+ @fetcher.draft
29
25
  end
30
26
 
31
27
  it "should load the document" do
32
28
  @document.should_receive(:load).and_return(true)
33
- @fetcher.perform
29
+ @fetcher.draft
34
30
  end
35
31
 
36
32
  it "should store the fetched revision" do
37
33
  @revisions.should_receive(:[]=).with(@id, 1)
38
- @fetcher.perform
39
- end
40
-
41
- it "should transfer the class" do
42
- @document.stub(:[]).with("model_class").and_return("Object")
43
- @fetcher.perform
44
- @fetcher.object_hash.should include(:class => "Object")
45
- end
46
-
47
- it "should transfer each property" do
48
- @fetcher.perform
49
- @fetcher.object_hash.should include(:properties => { :test => "test value" })
34
+ @fetcher.draft
50
35
  end
51
36
 
52
- it "should transfer each relation" do
53
- @document.stub(:each).and_yield("test_id", "test_storage:test_object_2")
54
- @fetcher.perform
55
- @fetcher.object_hash[:relations][:test].should be_instance_of(GOM::Object::Proxy)
37
+ it "should initialize the draft builder" do
38
+ GOM::Storage::CouchDB::Draft::Builder.should_receive(:new).with(@document).and_return(@builder)
39
+ @fetcher.draft
56
40
  end
57
41
 
58
- it "should return the correct object hash" do
59
- @fetcher.perform
60
- object_hash = @fetcher.object_hash
61
- object_hash.should == {
62
- :id => "test_object_1",
63
- :class => "Object",
64
- :properties => { :test => "test value" }
65
- }
42
+ it "should return the correct draft" do
43
+ draft = @fetcher.draft
44
+ draft.should == @draft
66
45
  end
67
46
 
68
- it "should set object hash to nil if document couldn't be loaded" do
47
+ it "should return nil if document couldn't be loaded" do
69
48
  @document.stub(:load).and_raise(CouchDB::Document::NotFoundError)
70
- @fetcher.perform
71
- @fetcher.object_hash.should be_nil
49
+ draft = @fetcher.draft
50
+ draft.should be_nil
72
51
  end
73
52
 
74
53
  end
@@ -4,11 +4,11 @@ describe GOM::Storage::CouchDB::Saver do
4
4
 
5
5
  before :each do
6
6
  @database = mock CouchDB::Database
7
- @object_hash = { :id => "test_object_1" }
7
+ @draft = GOM::Object::Draft.new "test_object_1"
8
8
  @revisions = mock Hash, :[]= => nil
9
9
  @relation_storage_name = "test_storage"
10
10
 
11
- @saver = described_class.new @database, @object_hash, @revisions, @relation_storage_name
11
+ @saver = described_class.new @database, @draft, @revisions, @relation_storage_name
12
12
  end
13
13
 
14
14
  describe "perform" do
@@ -23,10 +23,10 @@ describe GOM::Storage::CouchDB::Saver do
23
23
  @saver.perform
24
24
  end
25
25
 
26
- context "object hash with properties" do
26
+ context "draft with properties" do
27
27
 
28
28
  before :each do
29
- @object_hash.merge! :properties => { :test => "test value" }
29
+ @draft.properties = { :test => "test value" }
30
30
  end
31
31
 
32
32
  it "should set the properties" do
@@ -36,7 +36,7 @@ describe GOM::Storage::CouchDB::Saver do
36
36
 
37
37
  end
38
38
 
39
- context "object hash with relations" do
39
+ context "draft with relations" do
40
40
 
41
41
  before :each do
42
42
  @related_object = Object.new
@@ -46,7 +46,7 @@ describe GOM::Storage::CouchDB::Saver do
46
46
  GOM::Storage.stub(:store)
47
47
  GOM::Object.stub(:id).and_return(@related_object_id)
48
48
 
49
- @object_hash.merge! :relations => { :related_object => @related_object_proxy }
49
+ @draft.relations = { :related_object => @related_object_proxy }
50
50
  end
51
51
 
52
52
  it "should set the relations" do
@@ -71,9 +71,9 @@ describe GOM::Storage::CouchDB::Saver do
71
71
  @saver.perform
72
72
  end
73
73
 
74
- it "should not set the id if not included in the object hash" do
74
+ it "should not set the id if not included in the draft" do
75
75
  @document.should_not_receive(:id=)
76
- @object_hash.delete :id
76
+ @draft.id = nil
77
77
  @saver.perform
78
78
  end
79
79
 
@@ -0,0 +1,22 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "..", "spec_helper"))
2
+
3
+ describe GOM::Storage::CouchDB::View::Builder do
4
+
5
+ before :each do
6
+ @class_view = mock GOM::Storage::Configuration::View::Class, :class_name => "Object"
7
+
8
+ @builder = described_class.new @class_view
9
+ end
10
+
11
+ describe "map_reduce_view" do
12
+
13
+ it "should return a map reduce view that emits all the documents of the given model class" do
14
+ view = @builder.map_reduce_view
15
+ view.should be_instance_of(GOM::Storage::Configuration::View::MapReduce)
16
+ view.map.should == "function(document) {\n if (document['model_class'] == 'Object') {\n emit(document['_id'], null);\n }\n}"
17
+ view.reduce.should be_nil
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,64 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "..", "spec_helper"))
2
+
3
+ describe GOM::Storage::CouchDB::View::Pusher do
4
+
5
+ before :each do
6
+ @database = mock CouchDB::Database
7
+
8
+ @class_view = GOM::Storage::Configuration::View::Class.new "Object"
9
+ @map_reduce_view = GOM::Storage::Configuration::View::MapReduce.new "function(document) { }", "function(key, values) { }"
10
+ @view_hash = { "test_class_view" => @class_view, "test_map_reduce_view" => @map_reduce_view }
11
+
12
+ @pusher = described_class.new @database, @view_hash
13
+ end
14
+
15
+ describe "perform" do
16
+
17
+ before :each do
18
+ @design = mock CouchDB::Design, :views => mock(CouchDB::Design::ViewsProxy, :<< => nil), :save => true
19
+ CouchDB::Design.stub(:new).and_return(@design)
20
+
21
+ @view = mock CouchDB::Design::View
22
+ CouchDB::Design::View.stub(:new).and_return(@view)
23
+
24
+ @class_map_reduce_view = mock GOM::Storage::Configuration::View::MapReduce,
25
+ :map => "function(document) { }", :reduce => nil
26
+ @builder = mock GOM::Storage::CouchDB::View::Builder, :map_reduce_view => @class_map_reduce_view
27
+ GOM::Storage::CouchDB::View::Builder.stub(:new).and_return(@builder)
28
+ end
29
+
30
+ it "should initialize the design document" do
31
+ CouchDB::Design.should_receive(:new).with(@database, "gom").and_return(@design)
32
+ @pusher.perform
33
+ end
34
+
35
+ it "should initialize the builder with the class view" do
36
+ GOM::Storage::CouchDB::View::Builder.should_receive(:new).with(@class_view).and_return(@builder)
37
+ @pusher.perform
38
+ end
39
+
40
+ it "should use the builder to create a map reduce view out of the class view" do
41
+ @builder.should_receive(:map_reduce_view).and_return(@class_map_reduce_view)
42
+ @pusher.perform
43
+ end
44
+
45
+ it "should initialize the created map reduce view" do
46
+ CouchDB::Design::View.should_receive(:new).with(@design, "test_class_view", "function(document) { }", nil).once.and_return(@view)
47
+ CouchDB::Design::View.should_receive(:new).once.and_return(@view)
48
+ @pusher.perform
49
+ end
50
+
51
+ it "should initialize the map reduce view" do
52
+ CouchDB::Design::View.should_receive(:new).once.and_return(@view)
53
+ CouchDB::Design::View.should_receive(:new).with(@design, "test_map_reduce_view", "function(document) { }", "function(key, values) { }").and_return(@view)
54
+ @pusher.perform
55
+ end
56
+
57
+ it "should push the design document" do
58
+ @design.should_receive(:save).and_return(true)
59
+ @pusher.perform
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -4,3 +4,27 @@ test_storage:
4
4
  database: test
5
5
  delete_database_if_exists: true
6
6
  create_database_if_missing: true
7
+ views:
8
+ test_object_class_view:
9
+ type: class
10
+ class: Object
11
+ test_map_view:
12
+ type: map_reduce
13
+ map:
14
+ function(document) {
15
+ if (document['number'] == 11) {
16
+ emit(document['_id'], null);
17
+ }
18
+ }
19
+ test_map_reduce_view:
20
+ type: map_reduce
21
+ map:
22
+ function(document) {
23
+ if (document['number']) {
24
+ emit(document['_id'], document['number']);
25
+ }
26
+ }
27
+ reduce:
28
+ function(keys, values, rereduce) {
29
+ return sum(values);
30
+ }
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
7
+ - 2
8
8
  - 0
9
- version: 0.1.0
9
+ version: 0.2.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - "Philipp Br\xC3\xBCll"
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-12-08 00:00:00 +01:00
17
+ date: 2010-12-20 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -27,9 +27,9 @@ dependencies:
27
27
  - !ruby/object:Gem::Version
28
28
  segments:
29
29
  - 0
30
- - 1
30
+ - 2
31
31
  - 0
32
- version: 0.1.0
32
+ version: 0.2.0
33
33
  type: :runtime
34
34
  version_requirements: *id001
35
35
  - !ruby/object:Gem::Dependency
@@ -43,8 +43,8 @@ dependencies:
43
43
  segments:
44
44
  - 0
45
45
  - 1
46
- - 0
47
- version: 0.1.0
46
+ - 2
47
+ version: 0.1.2
48
48
  type: :runtime
49
49
  version_requirements: *id002
50
50
  - !ruby/object:Gem::Dependency
@@ -87,18 +87,30 @@ files:
87
87
  - LICENSE
88
88
  - Rakefile
89
89
  - lib/gom/storage/couchdb/adapter.rb
90
+ - lib/gom/storage/couchdb/view/builder.rb
91
+ - lib/gom/storage/couchdb/view/pusher.rb
92
+ - lib/gom/storage/couchdb/view.rb
90
93
  - lib/gom/storage/couchdb/saver.rb
91
94
  - lib/gom/storage/couchdb/fetcher.rb
95
+ - lib/gom/storage/couchdb/draft/builder.rb
96
+ - lib/gom/storage/couchdb/draft.rb
92
97
  - lib/gom/storage/couchdb/remover.rb
98
+ - lib/gom/storage/couchdb/collection/fetcher.rb
99
+ - lib/gom/storage/couchdb/collection.rb
93
100
  - lib/gom/storage/couchdb.rb
94
101
  - lib/gom/storage.rb
95
102
  - lib/couchdb-adapter.rb
96
103
  - spec/lib/gom/storage/couchdb/fetcher_spec.rb
104
+ - spec/lib/gom/storage/couchdb/view/pusher_spec.rb
105
+ - spec/lib/gom/storage/couchdb/view/builder_spec.rb
106
+ - spec/lib/gom/storage/couchdb/draft/builder_spec.rb
107
+ - spec/lib/gom/storage/couchdb/collection/fetcher_spec.rb
97
108
  - spec/lib/gom/storage/couchdb/remover_spec.rb
98
109
  - spec/lib/gom/storage/couchdb/saver_spec.rb
99
110
  - spec/lib/gom/storage/couchdb/adapter_spec.rb
100
111
  - spec/storage.configuration
101
112
  - spec/spec_helper.rb
113
+ - spec/acceptance/map_reduce_spec.rb
102
114
  - spec/acceptance/adapter_spec.rb
103
115
  has_rdoc: true
104
116
  homepage: http://github.com/phifty/gom-couchdb-adapter
@@ -134,7 +146,12 @@ specification_version: 3
134
146
  summary: CouchDB storage adapter for the General Object Mapper.
135
147
  test_files:
136
148
  - spec/lib/gom/storage/couchdb/fetcher_spec.rb
149
+ - spec/lib/gom/storage/couchdb/view/pusher_spec.rb
150
+ - spec/lib/gom/storage/couchdb/view/builder_spec.rb
151
+ - spec/lib/gom/storage/couchdb/draft/builder_spec.rb
152
+ - spec/lib/gom/storage/couchdb/collection/fetcher_spec.rb
137
153
  - spec/lib/gom/storage/couchdb/remover_spec.rb
138
154
  - spec/lib/gom/storage/couchdb/saver_spec.rb
139
155
  - spec/lib/gom/storage/couchdb/adapter_spec.rb
156
+ - spec/acceptance/map_reduce_spec.rb
140
157
  - spec/acceptance/adapter_spec.rb