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 +20 -0
- data/README.rdoc +91 -0
- data/Rakefile +48 -0
- data/lib/couchdb-adapter.rb +5 -0
- data/lib/gom/storage.rb +10 -0
- data/lib/gom/storage/couchdb.rb +17 -0
- data/lib/gom/storage/couchdb/adapter.rb +60 -0
- data/lib/gom/storage/couchdb/fetcher.rb +85 -0
- data/lib/gom/storage/couchdb/remover.rb +38 -0
- data/lib/gom/storage/couchdb/saver.rb +66 -0
- data/spec/acceptance/adapter_spec.rb +10 -0
- data/spec/lib/gom/storage/couchdb/adapter_spec.rb +134 -0
- data/spec/lib/gom/storage/couchdb/fetcher_spec.rb +76 -0
- data/spec/lib/gom/storage/couchdb/remover_spec.rb +43 -0
- data/spec/lib/gom/storage/couchdb/saver_spec.rb +97 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/storage.configuration +6 -0
- metadata +140 -0
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
|
data/lib/gom/storage.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
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
|