gom-couchdb-adapter 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +4 -0
- data/lib/gom/storage/couchdb/adapter.rb +18 -6
- data/lib/gom/storage/couchdb/collection/fetcher.rb +49 -0
- data/lib/gom/storage/couchdb/collection.rb +18 -0
- data/lib/gom/storage/couchdb/draft/builder.rb +72 -0
- data/lib/gom/storage/couchdb/draft.rb +18 -0
- data/lib/gom/storage/couchdb/fetcher.rb +11 -42
- data/lib/gom/storage/couchdb/saver.rb +7 -7
- data/lib/gom/storage/couchdb/view/builder.rb +32 -0
- data/lib/gom/storage/couchdb/view/pusher.rb +67 -0
- data/lib/gom/storage/couchdb/view.rb +19 -0
- data/lib/gom/storage/couchdb.rb +3 -0
- data/spec/acceptance/map_reduce_spec.rb +43 -0
- data/spec/lib/gom/storage/couchdb/adapter_spec.rb +72 -16
- data/spec/lib/gom/storage/couchdb/collection/fetcher_spec.rb +65 -0
- data/spec/lib/gom/storage/couchdb/draft/builder_spec.rb +37 -0
- data/spec/lib/gom/storage/couchdb/fetcher_spec.rb +20 -41
- data/spec/lib/gom/storage/couchdb/saver_spec.rb +8 -8
- data/spec/lib/gom/storage/couchdb/view/builder_spec.rb +22 -0
- data/spec/lib/gom/storage/couchdb/view/pusher_spec.rb +64 -0
- data/spec/storage.configuration +24 -0
- metadata +24 -7
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
|
-
|
19
|
-
fetcher.perform
|
20
|
-
fetcher.object_hash
|
19
|
+
Fetcher.new(@database, id, revisions).draft
|
21
20
|
end
|
22
21
|
|
23
|
-
def store(
|
24
|
-
saver = Saver.new @database,
|
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,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
|
@@ -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
|
8
|
+
# Fetches a CouchDB document with the given id and returns a draft.
|
9
9
|
class Fetcher
|
10
10
|
|
11
|
-
|
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
|
19
|
+
def draft
|
18
20
|
initialize_document
|
19
21
|
load_document
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
45
|
-
|
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
|
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,
|
14
|
-
@database, @
|
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 = @
|
30
|
-
@document["model_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
|
-
|
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
|
-
|
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
|
data/lib/gom/storage/couchdb.rb
CHANGED
@@ -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
|
-
@
|
73
|
+
@draft = mock GOM::Object::Draft
|
60
74
|
|
61
|
-
@fetcher = mock GOM::Storage::CouchDB::Fetcher, :
|
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
|
71
|
-
@
|
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
|
-
@
|
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, @
|
96
|
-
@adapter.store @
|
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 @
|
110
|
+
@adapter.store @draft
|
102
111
|
end
|
103
112
|
|
104
|
-
it "should return the
|
105
|
-
@adapter.store(@
|
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 "
|
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.
|
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.
|
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.
|
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
|
53
|
-
|
54
|
-
@fetcher.
|
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
|
59
|
-
@fetcher.
|
60
|
-
|
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
|
47
|
+
it "should return nil if document couldn't be loaded" do
|
69
48
|
@document.stub(:load).and_raise(CouchDB::Document::NotFoundError)
|
70
|
-
@fetcher.
|
71
|
-
|
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
|
-
@
|
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, @
|
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 "
|
26
|
+
context "draft with properties" do
|
27
27
|
|
28
28
|
before :each do
|
29
|
-
@
|
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 "
|
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
|
-
@
|
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
|
74
|
+
it "should not set the id if not included in the draft" do
|
75
75
|
@document.should_not_receive(:id=)
|
76
|
-
@
|
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
|
data/spec/storage.configuration
CHANGED
@@ -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
|
-
-
|
7
|
+
- 2
|
8
8
|
- 0
|
9
|
-
version: 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-
|
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
|
-
-
|
30
|
+
- 2
|
31
31
|
- 0
|
32
|
-
version: 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
|
-
-
|
47
|
-
version: 0.1.
|
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
|