dm-couchdb-adapter 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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