gom-couchdb-adapter 0.1.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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Philipp Brüll
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,91 @@
1
+
2
+ = Generic Object Mapper - CouchDB adapter
3
+
4
+ The CouchDB adapter for the Generic Object Mapper (http://github.com/phifty/gom) provides an easy way store and fetch
5
+ object data to/from a CouchDB server. Object properties are mapped as well as object relations.
6
+
7
+ == Configuration
8
+
9
+ If the couchdb adapter is chosen in the storage configuration, the following configuration values should be specified.
10
+
11
+ * <tt>host</tt> - Hostname where the CouchDB server is running on. Default is localhost.
12
+ * <tt>port</tt> - Port where the CouchDB server is listening to. Default is 5984.
13
+ * <tt>database</tt> - Name of the database that should be used.
14
+ * <tt>delete_database_if_exists</tt> - Can be true or false and specifies if an existing database should be delete or
15
+ not. This can be useful in your test setup. Default is false.
16
+ * <tt>create_database_if_missing</tt> - Can be true or false and specifies if a missing database should be created or
17
+ not. Default is false.
18
+
19
+ === Example
20
+
21
+ storage_name:
22
+ adapter: couchdb
23
+ host: another_host
24
+ database: production_db
25
+ delete_database_if_exists: false
26
+ create_database_if_missing: true
27
+
28
+ == Documents
29
+
30
+ Basically one document is created in the CouchDB database for each stored object. The instance variables from the
31
+ object are stored as the document's keys and values. The objects relations to other objects are stored as references
32
+ to the other object's document id. So if the following object is saved ...
33
+
34
+ class Book
35
+
36
+ attr_accessor :pages
37
+ attr_accessor :author
38
+
39
+ end
40
+
41
+ class Author
42
+
43
+ attr_accessor :name
44
+
45
+ end
46
+
47
+ author = Author.new
48
+ author.name = "Mr. Storyteller"
49
+
50
+ book = Book.new
51
+ book.pages = 1253
52
+ book.author = GOM::Object.reference author
53
+
54
+ GOM::Storage.store book, :storage_name
55
+
56
+ ... the following documents will occur in the database.
57
+
58
+ {
59
+ "_id": "book_1",
60
+ "_rev": "...",
61
+ "model_class": "Book",
62
+ "pages": 1253,
63
+ "author_id": "author_1"
64
+ }
65
+
66
+ {
67
+ "_id": "author_1",
68
+ "_rev": "...",
69
+ "model_class": "Author",
70
+ "name": "Mr. Storyteller"
71
+ }
72
+
73
+ If the author would have been stored before and assigned to an id, it wouldn't be stored again.
74
+
75
+ To get back the book from the storage, simply call ...
76
+
77
+ book = GOM::Storage.fetch "storage_name:book_1"
78
+
79
+ ... and an object of the class <tt>Book</tt> with same instance variable values as it had before, will be returned.
80
+ Since lazy loading is supported, the author will just be fetched on the first access. So ...
81
+
82
+ author_name = book.author.name
83
+
84
+ ... will invoke another fetch of the author's object.
85
+
86
+ == Development
87
+
88
+ Development has been done test-driven and the code follows at most the Clean Code paradigms. Code smells has been
89
+ removed by using the reek[http://github.com/kevinrutherford/reek] code smell detector.
90
+
91
+ The project is still experimental and under development. Any bug report and contribution is welcome!
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ gem 'rspec'
3
+ gem 'reek'
4
+ require 'rspec'
5
+ require 'rake/rdoctask'
6
+ require 'rspec/core/rake_task'
7
+ require 'reek/rake/task'
8
+
9
+ task :default => :spec
10
+
11
+ namespace :gem do
12
+
13
+ desc "Builds the gem"
14
+ task :build do
15
+ system "gem build *.gemspec && mkdir -p pkg/ && mv *.gem pkg/"
16
+ end
17
+
18
+ desc "Builds and installs the gem"
19
+ task :install => :build do
20
+ system "gem install pkg/"
21
+ end
22
+
23
+ end
24
+
25
+ Reek::Rake::Task.new do |task|
26
+ task.fail_on_error = true
27
+ end
28
+
29
+ desc "Generate the rdoc"
30
+ Rake::RDocTask.new do |rdoc|
31
+ rdoc.rdoc_files.add [ "README.rdoc", "lib/**/*.rb" ]
32
+ rdoc.main = "README.rdoc"
33
+ rdoc.title = ""
34
+ end
35
+
36
+ desc "Run all specs in spec directory"
37
+ RSpec::Core::RakeTask.new do |task|
38
+ task.pattern = "spec/gom/**/*_spec.rb"
39
+ end
40
+
41
+ namespace :spec do
42
+
43
+ desc "Run all integration specs in spec/acceptance directory"
44
+ RSpec::Core::RakeTask.new(:acceptance) do |task|
45
+ task.pattern = "spec/acceptance/**/*_spec.rb"
46
+ end
47
+
48
+ end
@@ -0,0 +1,5 @@
1
+ require 'gom'
2
+ require 'couchdb'
3
+ require File.join(File.dirname(__FILE__), "gom", "storage")
4
+
5
+ GOM::Storage::Adapter.register :couchdb, GOM::Storage::CouchDB::Adapter
@@ -0,0 +1,10 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ autoload :CouchDB, File.join(File.dirname(__FILE__), "storage", "couchdb")
7
+
8
+ end
9
+
10
+ end
@@ -0,0 +1,17 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ module CouchDB
7
+
8
+ autoload :Adapter, File.join(File.dirname(__FILE__), "couchdb", "adapter")
9
+ autoload :Fetcher, File.join(File.dirname(__FILE__), "couchdb", "fetcher")
10
+ autoload :Saver, File.join(File.dirname(__FILE__), "couchdb", "saver")
11
+ autoload :Remover, File.join(File.dirname(__FILE__), "couchdb", "remover")
12
+
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,60 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ module CouchDB
7
+
8
+ # The couchdb storage adapter
9
+ class Adapter < GOM::Storage::Adapter
10
+
11
+ def setup
12
+ initialize_server
13
+ initialize_database
14
+ setup_database
15
+ end
16
+
17
+ def fetch(id)
18
+ fetcher = Fetcher.new @database, id, revisions
19
+ fetcher.perform
20
+ fetcher.object_hash
21
+ end
22
+
23
+ def store(object_hash)
24
+ saver = Saver.new @database, object_hash, revisions, configuration.name
25
+ saver.perform
26
+ saver.id
27
+ end
28
+
29
+ def remove(id)
30
+ remover = Remover.new @database, id, revisions
31
+ remover.perform
32
+ end
33
+
34
+ def revisions
35
+ @revisions ||= { }
36
+ end
37
+
38
+ private
39
+
40
+ def initialize_server
41
+ @server = ::CouchDB::Server.new *configuration.values_at(:host, :port).compact
42
+ end
43
+
44
+ def initialize_database
45
+ @database = ::CouchDB::Database.new *[ @server, configuration[:database] ].compact
46
+ end
47
+
48
+ def setup_database
49
+ delete_database_if_exists, create_database_if_missing = configuration.values_at :delete_database_if_exists, :create_database_if_missing
50
+ @database.delete_if_exists! if delete_database_if_exists
51
+ @database.create_if_missing! if create_database_if_missing
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,85 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ module CouchDB
7
+
8
+ # Fetches a CouchDB document with the given id and returns an object hash.
9
+ class Fetcher
10
+
11
+ attr_reader :object_hash
12
+
13
+ def initialize(database, id, revisions)
14
+ @database, @id, @revisions = database, id, revisions
15
+ end
16
+
17
+ def perform
18
+ initialize_document
19
+ load_document
20
+ return unless has_object_hash?
21
+ transfer_properties
22
+ transfer_class
23
+ end
24
+
25
+ private
26
+
27
+ def initialize_document
28
+ @document = ::CouchDB::Document.new @database
29
+ @document.id = @id
30
+ end
31
+
32
+ def load_document
33
+ @document.load
34
+ @object_hash = { :id => @document.id }
35
+ set_revision
36
+ rescue ::CouchDB::Document::NotFoundError
37
+ @object_hash = nil
38
+ end
39
+
40
+ def set_revision
41
+ @revisions[@document.id] = @document.rev
42
+ end
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
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -0,0 +1,38 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ module CouchDB
7
+
8
+ # Removes the CouchDB document with the given id.
9
+ class Remover
10
+
11
+ def initialize(database, id, revisions)
12
+ @database, @id, @revisions = database, id, revisions
13
+ end
14
+
15
+ def perform
16
+ initialize_document
17
+ destroy_document
18
+ end
19
+
20
+ private
21
+
22
+ def initialize_document
23
+ @document = ::CouchDB::Document.new @database
24
+ @document.id = @id
25
+ @document.rev = @revisions[@id]
26
+ end
27
+
28
+ def destroy_document
29
+ @document.destroy
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,66 @@
1
+
2
+ module GOM
3
+
4
+ module Storage
5
+
6
+ module CouchDB
7
+
8
+ # Saves the given object hash to a CouchDB document.
9
+ class Saver
10
+
11
+ attr_reader :id
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
15
+ end
16
+
17
+ def perform
18
+ initialize_document
19
+ set_properties
20
+ set_relations
21
+ save_document
22
+ store_revision
23
+ end
24
+
25
+ private
26
+
27
+ def initialize_document
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]
31
+ end
32
+
33
+ def set_properties
34
+ (@object_hash[:properties] || { }).each do |key, value|
35
+ @document[key.to_s] = value
36
+ end
37
+ end
38
+
39
+ def set_relations
40
+ (@object_hash[:relations] || { }).each do |key, object_proxy|
41
+ id, object = object_proxy.id, object_proxy.object
42
+ @document["#{key}_id"] = if id
43
+ id.to_s
44
+ else
45
+ GOM::Storage.store object, @relation_storage_name
46
+ GOM::Object.id object
47
+ end
48
+ end
49
+ end
50
+
51
+ def save_document
52
+ @document.save
53
+ @id = @document.id
54
+ end
55
+
56
+ def store_revision
57
+ @revisions[@document.id] = @document.rev
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,10 @@
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" do
7
+
8
+ it_should_behave_like "an adapter connected to a stateful storage"
9
+
10
+ end
@@ -0,0 +1,134 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "spec_helper"))
2
+
3
+ describe GOM::Storage::CouchDB::Adapter do
4
+
5
+ before :each do
6
+ @server = mock CouchDB::Server
7
+ CouchDB::Server.stub(:new).and_return(@server)
8
+
9
+ @database = mock CouchDB::Database, :delete_if_exists! => nil, :create_if_missing! => nil
10
+ CouchDB::Database.stub(:new).and_return(@database)
11
+
12
+ @configuration = mock GOM::Storage::Configuration, :name => "test_storage"
13
+ @configuration.stub(:[]).with(:database).and_return("test")
14
+ @configuration.stub(:values_at) do |*arguments|
15
+ result = nil
16
+ result = [ "host", 1234 ] if arguments == [ :host, :port ]
17
+ result = [ true, true ] if arguments == [ :delete_database_if_exists, :create_database_if_missing ]
18
+ result
19
+ end
20
+
21
+ @adapter = described_class.new @configuration
22
+ end
23
+
24
+ it "should register the adapter" do
25
+ GOM::Storage::Adapter[:couchdb].should == GOM::Storage::CouchDB::Adapter
26
+ end
27
+
28
+ describe "setup" do
29
+
30
+ it "should initialize a server" do
31
+ CouchDB::Server.should_receive(:new).with("host", 1234).and_return(@server)
32
+ @adapter.setup
33
+ end
34
+
35
+ it "should initialize a database" do
36
+ CouchDB::Database.should_receive(:new).with(@server, "test").and_return(@database)
37
+ @adapter.setup
38
+ end
39
+
40
+ it "should delete the database if requested and existing" do
41
+ @database.should_receive(:delete_if_exists!)
42
+ @adapter.setup
43
+ end
44
+
45
+ it "should create the database if requested and missing" do
46
+ @database.should_receive(:create_if_missing!)
47
+ @adapter.setup
48
+ end
49
+
50
+ end
51
+
52
+ describe "fetch" do
53
+
54
+ before :each do
55
+ @adapter.setup
56
+
57
+ @id = "test_object_1"
58
+ @revisions = @adapter.send :revisions
59
+ @object_hash = mock Hash
60
+
61
+ @fetcher = mock GOM::Storage::CouchDB::Fetcher, :perform => nil, :object_hash => @object_hash
62
+ GOM::Storage::CouchDB::Fetcher.stub(:new).and_return(@fetcher)
63
+ end
64
+
65
+ it "should initialize the fetcher" do
66
+ GOM::Storage::CouchDB::Fetcher.should_receive(:new).with(@database, @id, @revisions).and_return(@fetcher)
67
+ @adapter.fetch @id
68
+ end
69
+
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
77
+ end
78
+
79
+ end
80
+
81
+ describe "store" do
82
+
83
+ before :each do
84
+ @adapter.setup
85
+
86
+ @object_hash = mock Hash
87
+ @revisions = @adapter.send :revisions
88
+ @id = "test_object_1"
89
+
90
+ @saver = mock GOM::Storage::CouchDB::Saver, :perform => nil, :id => @id
91
+ GOM::Storage::CouchDB::Saver.stub(:new).and_return(@saver)
92
+ end
93
+
94
+ 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
97
+ end
98
+
99
+ it "should perform a fetch" do
100
+ @saver.should_receive(:perform)
101
+ @adapter.store @object_hash
102
+ end
103
+
104
+ it "should return the object_hash" do
105
+ @adapter.store(@object_hash).should == @id
106
+ end
107
+
108
+ end
109
+
110
+ describe "remove" do
111
+
112
+ before :each do
113
+ @adapter.setup
114
+
115
+ @id = "test_object_1"
116
+ @revisions = @adapter.send :revisions
117
+
118
+ @remover = mock GOM::Storage::CouchDB::Remover, :perform => nil
119
+ GOM::Storage::CouchDB::Remover.stub(:new).and_return(@remover)
120
+ end
121
+
122
+ it "should initialize the remover" do
123
+ GOM::Storage::CouchDB::Remover.should_receive(:new).with(@database, @id, @revisions).and_return(@remover)
124
+ @adapter.remove @id
125
+ end
126
+
127
+ it "should perform a fetch" do
128
+ @remover.should_receive(:perform)
129
+ @adapter.remove @id
130
+ end
131
+
132
+ end
133
+
134
+ end
@@ -0,0 +1,76 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "spec_helper"))
2
+
3
+ describe GOM::Storage::CouchDB::Fetcher do
4
+
5
+ before :each do
6
+ @database = mock CouchDB::Database
7
+ @id = "test_object_1"
8
+ @revisions = mock Hash, :[]= => nil
9
+
10
+ @fetcher = described_class.new @database, @id, @revisions
11
+ end
12
+
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
+
21
+ it "should initialize a document" do
22
+ 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
29
+ end
30
+
31
+ it "should load the document" do
32
+ @document.should_receive(:load).and_return(true)
33
+ @fetcher.perform
34
+ end
35
+
36
+ it "should store the fetched revision" do
37
+ @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" })
50
+ end
51
+
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)
56
+ end
57
+
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
+ }
66
+ end
67
+
68
+ it "should set object hash to nil if document couldn't be loaded" do
69
+ @document.stub(:load).and_raise(CouchDB::Document::NotFoundError)
70
+ @fetcher.perform
71
+ @fetcher.object_hash.should be_nil
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "spec_helper"))
2
+
3
+ describe GOM::Storage::CouchDB::Remover do
4
+
5
+ before :each do
6
+ @database = mock CouchDB::Database
7
+ @id = "test_object_1"
8
+ @revisions = mock Hash, :[] => 1
9
+
10
+ @remover = described_class.new @database, @id, @revisions
11
+ end
12
+
13
+ describe "perform" do
14
+
15
+ before :each do
16
+ @document = mock CouchDB::Document, :id= => nil, :rev= => nil, :destroy => true
17
+ CouchDB::Document.stub(:new).and_return(@document)
18
+ end
19
+
20
+ it "should initialize a document" do
21
+ CouchDB::Document.should_receive(:new).with(@database).and_return(@document)
22
+ @remover.perform
23
+ end
24
+
25
+ it "should set the id" do
26
+ @document.should_receive(:id=).with("test_object_1")
27
+ @remover.perform
28
+ end
29
+
30
+ it "should set the revision" do
31
+ @revisions.should_receive(:[]).with("test_object_1").and_return(1)
32
+ @document.should_receive(:rev=).with(1)
33
+ @remover.perform
34
+ end
35
+
36
+ it "should destroy the document" do
37
+ @document.should_receive(:destroy).and_return(true)
38
+ @remover.perform
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,97 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "spec_helper"))
2
+
3
+ describe GOM::Storage::CouchDB::Saver do
4
+
5
+ before :each do
6
+ @database = mock CouchDB::Database
7
+ @object_hash = { :id => "test_object_1" }
8
+ @revisions = mock Hash, :[]= => nil
9
+ @relation_storage_name = "test_storage"
10
+
11
+ @saver = described_class.new @database, @object_hash, @revisions, @relation_storage_name
12
+ end
13
+
14
+ describe "perform" do
15
+
16
+ before :each do
17
+ @document = mock CouchDB::Document, :[]= => nil, :id= => nil, :save => true, :id => "test_object_1", :rev => 1
18
+ CouchDB::Document.stub(:new).and_return(@document)
19
+ end
20
+
21
+ it "should initialize a document" do
22
+ CouchDB::Document.should_receive(:new).with(@database).and_return(@document)
23
+ @saver.perform
24
+ end
25
+
26
+ context "object hash with properties" do
27
+
28
+ before :each do
29
+ @object_hash.merge! :properties => { :test => "test value" }
30
+ end
31
+
32
+ it "should set the properties" do
33
+ @document.should_receive(:[]=).with("test", "test value")
34
+ @saver.perform
35
+ end
36
+
37
+ end
38
+
39
+ context "object hash with relations" do
40
+
41
+ before :each do
42
+ @related_object = Object.new
43
+ @related_object_id = mock GOM::Object::Id, :to_s => "test_storage:test_object_2"
44
+ @related_object_proxy = mock GOM::Object::Proxy, :id => @related_object_id, :object => @related_object
45
+
46
+ GOM::Storage.stub(:store)
47
+ GOM::Object.stub(:id).and_return(@related_object_id)
48
+
49
+ @object_hash.merge! :relations => { :related_object => @related_object_proxy }
50
+ end
51
+
52
+ it "should set the relations" do
53
+ @document.should_receive(:[]=).with("related_object_id", "test_storage:test_object_2")
54
+ @saver.perform
55
+ end
56
+
57
+ it "should store the related object if it hasn't an id" do
58
+ GOM::Storage.should_receive(:store).with(@related_object, "test_storage")
59
+ GOM::Object.should_receive(:id).with(@related_object).and_return(@related_object_id)
60
+
61
+ @related_object_proxy.stub(:id).and_return(nil)
62
+ @related_object_proxy.stub(:object).and_return(@related_object)
63
+
64
+ @saver.perform
65
+ end
66
+
67
+ end
68
+
69
+ it "should set the id" do
70
+ @document.should_receive(:id=).with("test_object_1")
71
+ @saver.perform
72
+ end
73
+
74
+ it "should not set the id if not included in the object hash" do
75
+ @document.should_not_receive(:id=)
76
+ @object_hash.delete :id
77
+ @saver.perform
78
+ end
79
+
80
+ it "should save the document" do
81
+ @document.should_receive(:save).and_return(true)
82
+ @saver.perform
83
+ end
84
+
85
+ it "should store the revision" do
86
+ @revisions.should_receive(:[]=).with("test_object_1", 1)
87
+ @saver.perform
88
+ end
89
+
90
+ it "should return the (new) object id" do
91
+ @saver.perform
92
+ @saver.id.should == "test_object_1"
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ gem 'rspec', '>= 2'
3
+ require 'rspec'
4
+
5
+ require 'gom/spec'
6
+
7
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "couchdb-adapter"))
@@ -0,0 +1,6 @@
1
+
2
+ test_storage:
3
+ adapter: couchdb
4
+ database: test
5
+ delete_database_if_exists: true
6
+ create_database_if_missing: true
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gom-couchdb-adapter
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - "Philipp Br\xC3\xBCll"
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-12-08 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: gom
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ - 1
31
+ - 0
32
+ version: 0.1.0
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: couchdb
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ - 1
46
+ - 0
47
+ version: 0.1.0
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: rspec
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 2
60
+ version: "2"
61
+ type: :development
62
+ version_requirements: *id003
63
+ - !ruby/object:Gem::Dependency
64
+ name: reek
65
+ prerelease: false
66
+ requirement: &id004 !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ segments:
72
+ - 1
73
+ - 2
74
+ version: "1.2"
75
+ type: :development
76
+ version_requirements: *id004
77
+ description: CouchDB storage adapter for the General Object Mapper. Currently, version 1.0 of CouchDB is supported.
78
+ email: b.phifty@gmail.com
79
+ executables: []
80
+
81
+ extensions: []
82
+
83
+ extra_rdoc_files:
84
+ - README.rdoc
85
+ files:
86
+ - README.rdoc
87
+ - LICENSE
88
+ - Rakefile
89
+ - lib/gom/storage/couchdb/adapter.rb
90
+ - lib/gom/storage/couchdb/saver.rb
91
+ - lib/gom/storage/couchdb/fetcher.rb
92
+ - lib/gom/storage/couchdb/remover.rb
93
+ - lib/gom/storage/couchdb.rb
94
+ - lib/gom/storage.rb
95
+ - lib/couchdb-adapter.rb
96
+ - spec/lib/gom/storage/couchdb/fetcher_spec.rb
97
+ - spec/lib/gom/storage/couchdb/remover_spec.rb
98
+ - spec/lib/gom/storage/couchdb/saver_spec.rb
99
+ - spec/lib/gom/storage/couchdb/adapter_spec.rb
100
+ - spec/storage.configuration
101
+ - spec/spec_helper.rb
102
+ - spec/acceptance/adapter_spec.rb
103
+ has_rdoc: true
104
+ homepage: http://github.com/phifty/gom-couchdb-adapter
105
+ licenses: []
106
+
107
+ post_install_message:
108
+ rdoc_options: []
109
+
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ segments:
118
+ - 0
119
+ version: "0"
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ segments:
126
+ - 0
127
+ version: "0"
128
+ requirements: []
129
+
130
+ rubyforge_project: gom-couchdb-adapter
131
+ rubygems_version: 1.3.7
132
+ signing_key:
133
+ specification_version: 3
134
+ summary: CouchDB storage adapter for the General Object Mapper.
135
+ test_files:
136
+ - spec/lib/gom/storage/couchdb/fetcher_spec.rb
137
+ - spec/lib/gom/storage/couchdb/remover_spec.rb
138
+ - spec/lib/gom/storage/couchdb/saver_spec.rb
139
+ - spec/lib/gom/storage/couchdb/adapter_spec.rb
140
+ - spec/acceptance/adapter_spec.rb