dm-couchdb-adapter 0.10.2

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.
@@ -0,0 +1,291 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+
3
+ if COUCHDB_AVAILABLE
4
+ class ::User
5
+ include DataMapper::Resource
6
+
7
+ # regular properties
8
+ property :name, String
9
+ property :age, Integer
10
+ property :wealth, Float
11
+ property :created_at, DateTime
12
+ property :created_on, Date
13
+ property :location, JsonObject
14
+
15
+ # creates methods for accessing stored/indexed views in the CouchDB database
16
+ view(:by_name) {{ "map" => "function(doc) { if (#{couchdb_types_condition}) { emit(doc.name, doc); } }" }}
17
+ view(:by_age) {{ "map" => "function(doc) { if (#{couchdb_types_condition}) { emit(doc.age, doc); } }" }}
18
+ view(:count) {{ "map" => "function(doc) { if (#{couchdb_types_condition}) { emit(null, 1); } }",
19
+ "reduce" => "function(keys, values) { return sum(values); }" }}
20
+
21
+ belongs_to :company
22
+
23
+ before :create do
24
+ self.created_at = DateTime.now
25
+ self.created_on = Date.today
26
+ end
27
+ end
28
+
29
+ class ::Company
30
+ include DataMapper::Resource
31
+
32
+ # This class happens to have similar properties
33
+ property :name, String
34
+ property :age, Integer
35
+
36
+ has n, :users
37
+ end
38
+
39
+ class ::Person
40
+ include DataMapper::Resource
41
+
42
+ property :name, String
43
+ end
44
+
45
+ class ::Employee < Person
46
+ property :rank, String
47
+ end
48
+
49
+ class ::Broken
50
+ include DataMapper::Resource
51
+
52
+ property :couchdb_type, Discriminator
53
+ property :name, String
54
+ end
55
+
56
+ describe DataMapper::Adapters::CouchdbAdapter do
57
+
58
+ describe "resource functions" do
59
+
60
+ before(:each) do
61
+ @user = User.new(:name => "Jamie", :age => 67, :wealth => 11.5)
62
+ @user.save.should be_true
63
+ end
64
+
65
+ after(:each) do
66
+ @user.destroy.should be_true
67
+ end
68
+
69
+ it "should create a record with a specified id" do
70
+ user_with_id = User.new(:name => 'user with id')
71
+ user_with_id.id = 'user_id'
72
+ user_with_id.save.should == true
73
+ User.get!('user_id', :repository => :couch).should == user_with_id
74
+ user_with_id.destroy.should be_true
75
+ end
76
+
77
+ it "should get a record" do
78
+ user = User.get!(@user.id)
79
+ user.id.should_not be_nil
80
+ user.name.should == "Jamie"
81
+ user.age.should == 67
82
+ end
83
+
84
+ it "should not get records of the wrong type by id" do
85
+ Company.get(@user.id).should == nil
86
+ lambda { Company.get!(@user.id) }.should raise_error(DataMapper::ObjectNotFoundError)
87
+ end
88
+
89
+ it "should update a record" do
90
+ user = User.get!(@user.id)
91
+ user.name = "Janet"
92
+ user.save
93
+ user.name.should_not == @user.name
94
+ user.rev.should_not == @user.rev
95
+ user.age.should == @user.age
96
+ user.id.should == @user.id
97
+ user.destroy.should be_true
98
+ end
99
+
100
+ it "should get all records" do
101
+ User.all.length.should == 1
102
+ end
103
+
104
+ it "should set total_rows on collection" do
105
+ User.all.total_rows.should == 1
106
+ end
107
+ end
108
+
109
+ describe "ad_hoc queries" do
110
+
111
+ before(:each) do
112
+ @user = User.new({ :name => "Jamie", :age => 67, :wealth => 11.5 })
113
+ @user.save.should be_true
114
+ end
115
+
116
+ after(:each) do
117
+ @user.destroy.should be_true
118
+ end
119
+
120
+ it "should get records by eql matcher" do
121
+ User.all(:name => "Jamie").size.should == 1
122
+ User.all(:age => 50).size.should == 0
123
+ end
124
+
125
+ it "should get records by not matcher" do
126
+ User.all(:age.not => 50).size.should == 1
127
+ end
128
+
129
+ it "should get records by gt matcher" do
130
+ User.all(:age.gt => 67).size.should == 0
131
+ end
132
+
133
+ it "should get records by gte matcher" do
134
+ User.all(:age.gte => 67).size.should == 1
135
+ end
136
+
137
+ it "should get records by lt matcher" do
138
+ User.all(:age.lt => 67).size.should == 0
139
+ end
140
+
141
+ it "should get records by lte matcher" do
142
+ User.all(:age.lte => 67).size.should == 1
143
+ end
144
+
145
+ it "should get records by the like matcher" do
146
+ User.all(:name.like => "Jo").size.should == 0
147
+ User.all(:name.like => "Ja%").size.should == 1
148
+ User.all(:name.like => "%J%m%").size.should == 1
149
+ User.all(:name.like => /^Jam/).size.should == 1
150
+ end
151
+
152
+ it "should get records with multiple matchers" do
153
+ User.all(:name => "Jamie", :age.lt => 80).size.should == 1
154
+ end
155
+
156
+ it "should order records" do
157
+ user = User.new(:name => "Aaron", :age => 30)
158
+ user.save
159
+ users = User.all(:order => [:age])
160
+ users[0].age.should == 30
161
+ users = User.all(:order => [:name, :age])
162
+ users[0].age.should == 30
163
+ users[1].age.should == 67
164
+ user.destroy
165
+ end
166
+
167
+ end
168
+
169
+ describe "view queries" do
170
+
171
+ before(:all) do
172
+ User.auto_migrate!
173
+ end
174
+
175
+ before(:each) do
176
+ @jamie = User.create(:name => "Jamie", :age => 67, :wealth => 11.5)
177
+ @aaron = User.create(:name => "Aaron", :age => 30, :wealth => 20)
178
+ end
179
+
180
+ after(:each) do
181
+ [@jamie, @aaron].each { |user| user.destroy }
182
+ end
183
+
184
+ it "should be able to call stored views" do
185
+ User.by_name.first.should == User.all(:order => [:name]).first
186
+ User.by_age.first.should == User.all(:order => [:age]).first
187
+ end
188
+
189
+ it "should be able to call stored views with keys" do
190
+ User.by_name("Aaron").first == User.all(:name => "Aaron").first
191
+ User.by_age(30).first == User.all(:age => 30).first
192
+ User.by_name("Aaron").first == User.by_name(:key => "Aaron").first
193
+ User.by_age(30).first == User.by_age(:key => 30).first
194
+ end
195
+
196
+ it "should return a value from a view with reduce defined" do
197
+ User.count.should == [ { "value" => User.all.length, "key" => nil } ]
198
+ end
199
+
200
+ it "should be able to perform ordered multi-key fetch on a view" do
201
+ User.by_name(:keys => ["Aaron", "Jamie"]).should == [@aaron, @jamie]
202
+ User.by_name(:keys => ["Jamie", "Aaron"]).should == [@jamie, @aaron]
203
+ end
204
+
205
+ end
206
+
207
+ describe "associations" do
208
+ before(:all) do
209
+ @company = Company.create(:name => "ExCorp")
210
+ @user = User.create(:name => 'John', :company => @company)
211
+ end
212
+ after(:all) do
213
+ @company.destroy
214
+ @user.destroy
215
+ end
216
+
217
+ it "should work with belongs_to associations" do
218
+ User.get(@user.id).company.should == @company
219
+ end
220
+
221
+ it "should work with has n associations" do
222
+ @company.users.should include(@user)
223
+ end
224
+ end
225
+
226
+ describe 'STI' do
227
+
228
+ before(:all) do
229
+ Person.auto_migrate!
230
+ end
231
+
232
+ it "should override default type" do
233
+ person = Person.new(:name => 'Bob')
234
+ person.save.should be_true
235
+ Person.first.couchdb_type.should == Person
236
+ person.destroy.should be_true
237
+ end
238
+
239
+ it "should load descendents on parent.all" do
240
+ employee = Employee.new(:name => 'Bob', :rank => 'Peon')
241
+ employee.save.should be_true
242
+ Person.all.include?(employee).should be_true
243
+ employee.destroy.should be_true
244
+ end
245
+
246
+ it "should be able to get children from parent.get" do
247
+ employee = Employee.new(:name => 'Bob', :rank => 'Peon')
248
+ employee.save.should be_true
249
+ Person.get(employee.id).should_not be_nil
250
+ employee.destroy.should be_true
251
+ end
252
+
253
+ it "should load descendents on parent.by_name" do
254
+ employee = Employee.new(:name => 'Bob', :rank => 'Peon')
255
+ employee.save.should be_true
256
+ Person.by_name(:key => 'Bob').include?(employee).should be_true
257
+ employee.destroy.should be_true
258
+ end
259
+ end
260
+
261
+ describe 'JSON serialization' do
262
+ if DMSERIAL_AVAILABLE
263
+ before(:all) do
264
+ @moe = User.create(:name => "Moe", :age => 46, :wealth => 20)
265
+ @larry = User.create(:name => "Larry", :age => 42, :wealth => 10)
266
+ @curly = User.create(:name => "Curly", :age => 44, :wealth => 1)
267
+ end
268
+
269
+ after(:all) do
270
+ [@moe, @larry, @curly].each { |stooge| stooge.destroy }
271
+ end
272
+
273
+ it "should properly serialize a single resource" do
274
+ moe_serial = JSON.parse(@moe.to_json)
275
+ moe_serial['name'].should == "Moe"
276
+ moe_serial['age'].should == 46
277
+ moe_serial['wealth'].should == 20
278
+ end
279
+
280
+ it "should properly serialize a resource collection" do
281
+ stooges = JSON.parse(User.all.to_json)
282
+ stooges.length.should == 3
283
+ stooges.each { |stooge| stooge['name'].should_not be_blank }
284
+ end
285
+ else
286
+ it "requires dm-serializer to run serialization tests"
287
+ end
288
+ end
289
+
290
+ end
291
+ end
@@ -0,0 +1,116 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+
3
+ if COUCHDB_AVAILABLE
4
+ require 'base64'
5
+ require 'pathname'
6
+
7
+ describe DataMapper::Model do
8
+
9
+ before do
10
+ Object.send(:remove_const, :NonCouch) if defined?(NonCouch)
11
+ class ::NonCouch
12
+ include DataMapper::Resource
13
+
14
+ property :id, Serial
15
+ end
16
+
17
+ Object.send(:remove_const, :Message) if defined?(Message)
18
+ class ::Message
19
+ include DataMapper::Resource
20
+ def self.default_repository_name
21
+ :couch
22
+ end
23
+
24
+ property :content, String
25
+ end
26
+
27
+ @file = File.open(Pathname(__FILE__).dirname.expand_path + "testfile.txt", "r")
28
+ end
29
+
30
+ after do
31
+ @file.close
32
+ end
33
+
34
+ describe "#add_attachment" do
35
+
36
+ it "should add inline attributes to new records" do
37
+ @message = Message.new
38
+ @message.add_attachment(@file, :name => 'test.txt')
39
+ @message.attachments.should == {
40
+ 'test.txt' => {
41
+ 'content_type' => 'text/plain',
42
+ 'data' => Base64.encode64("test string\n").chomp
43
+ }
44
+ }
45
+ end
46
+
47
+ it "should upload standalone attachment for existing record" do
48
+ @message = Message.new(:content => 'test message')
49
+ @message.save.should be_true
50
+ @message.add_attachment(@file, :name => 'test.txt')
51
+ @message.attachments['test.txt']['stub'].should be_true
52
+ @message.attachments['test.txt']['content_type'].should == 'text/plain'
53
+ @message.attachments['test.txt']['data'].should be_nil
54
+ @message.destroy.should be_true
55
+ end
56
+
57
+ it "should have meta data on load" do
58
+ pending("No CouchDB connection.") if @no_connection
59
+ @message = Message.new
60
+ @message.add_attachment(@file, :name => 'test.txt')
61
+ @message.save.should be_true
62
+ @message.reload
63
+ @message.attachments['test.txt']['stub'].should be_true
64
+ @message.attachments['test.txt']['content_type'].should == 'text/plain'
65
+ @message.attachments['test.txt']['data'].should be_nil
66
+ @message.destroy.should be_true
67
+ end
68
+
69
+ end
70
+
71
+
72
+ describe "#delete_attachment" do
73
+
74
+ it "should remove unsaved attachments" do
75
+ @message = Message.new
76
+ @message.add_attachment(@file, :name => 'test.txt')
77
+ @message.delete_attachment('test.txt').should be_true
78
+ @message.attachments.should be_nil
79
+ end
80
+
81
+ it "should remove saved attachments" do
82
+ @message = Message.new
83
+ @message.add_attachment(@file, :name => 'test.txt')
84
+ @message.save.should be_true
85
+ @message.reload
86
+ @message.attachments.should_not be_nil
87
+ @message.delete_attachment('test.txt').should be_true
88
+ @message.attachments.should be_nil
89
+ @message = Message.get(@message.id)
90
+ @message.attachments.should be_nil
91
+ @message.destroy.should be_true
92
+ end
93
+
94
+ end
95
+
96
+
97
+ describe "#get_attachment" do
98
+
99
+ it "should return nil when there is not attachment" do
100
+ @message = Message.new
101
+ @message.get_attachment('test.txt').should be_nil
102
+ end
103
+
104
+ it "should return attachment data when it exists" do
105
+ @message = Message.new
106
+ @message.add_attachment(@file, :name => 'test.txt')
107
+ @message.save.should be_true
108
+ @message.reload
109
+ @message.get_attachment('test.txt').should == "test string\n"
110
+ @message.destroy.should be_true
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,47 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+
3
+ class Viewable
4
+ include DataMapper::Resource
5
+ def self.default_repository_name
6
+ :couch
7
+ end
8
+
9
+ property :name, String
10
+ property :open, Boolean
11
+ end
12
+
13
+ describe DataMapper::Resource::View do
14
+ it "should have a view method" do
15
+ Viewable.should respond_to(:view)
16
+ end
17
+
18
+ it "should store a view when called" do
19
+ Viewable.view :by_name
20
+ Viewable.views.keys.should include(:by_name)
21
+ end
22
+
23
+ it "should initialize a new Procedure instance" do
24
+ proc = Viewable.view :by_name_desc
25
+ proc.should be_an_instance_of(DataMapper::Resource::View)
26
+ end
27
+
28
+ it "should create a getter method" do
29
+ Viewable.view :open
30
+ Viewable.should respond_to(:open)
31
+ end
32
+
33
+ describe "for inherited resources" do
34
+ before(:all) do
35
+ Person.auto_migrate!
36
+ end
37
+
38
+ it "should set the correct couchdb types" do
39
+ Person.couchdb_types.include?(Person).should be_true
40
+ Person.couchdb_types.include?(Employee).should be_true
41
+ end
42
+
43
+ it "should create views with the correct couchdb type conditions" do
44
+ Person.views[:by_name].should == {"map"=>"function(doc) { if (doc.couchdb_type == 'Person' || doc.couchdb_type == 'Employee') { emit(doc.name, doc); } }"}
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,76 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ def load_driver(name, default_uri)
4
+ return false if ENV['ADAPTER'] != name.to_s
5
+
6
+ begin
7
+ DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
8
+ DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
9
+ true
10
+ rescue LoadError => e
11
+ warn "Could not load do_#{name}: #{e}"
12
+ false
13
+ end
14
+ end
15
+
16
+ ENV['ADAPTER'] ||= 'sqlite3'
17
+
18
+ HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
19
+ HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_core_test')
20
+ HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_core_test')
21
+
22
+ if COUCHDB_AVAILABLE && (HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES)
23
+ class ::User
24
+ include DataMapper::Resource
25
+
26
+ property :id, Serial
27
+ property :name, String
28
+
29
+ repository(:couch) do
30
+ has n, :posts
31
+ end
32
+ end
33
+
34
+
35
+ class ::Post
36
+ include DataMapper::CouchResource
37
+
38
+ property :title, String
39
+ property :body, Text
40
+
41
+ def self.default_repository_name
42
+ :couch
43
+ end
44
+
45
+ repository(:default) do
46
+ belongs_to :user
47
+ end
48
+ end
49
+
50
+ User.auto_migrate!
51
+
52
+ describe DataMapper::Model, "working with couch resources" do
53
+ before(:all) do
54
+ @user = User.new(:name => "Jamie")
55
+ @user.save.should be_true
56
+ end
57
+
58
+ after(:all) do
59
+ @user.destroy.should be_true
60
+ Post.all.destroy!.should be_true
61
+ end
62
+
63
+ it "should create resources in couch" do
64
+ @user.posts.create(:title => "I'm a little teapot", :body => "this is my handle, this is my spout").should be_true
65
+ Post.first.title.should == "I'm a little teapot"
66
+ end
67
+
68
+ it "should find child elements" do
69
+ @post = Post.first
70
+ @user.posts.should include(@post)
71
+ @user.posts.length.should == 1
72
+ end
73
+
74
+
75
+ end
76
+ end