couchrest_model 1.0.0.beta7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/LICENSE +176 -0
  2. data/README.md +320 -0
  3. data/Rakefile +71 -0
  4. data/THANKS.md +19 -0
  5. data/examples/model/example.rb +144 -0
  6. data/history.txt +180 -0
  7. data/lib/couchrest/model.rb +10 -0
  8. data/lib/couchrest/model/associations.rb +207 -0
  9. data/lib/couchrest/model/attribute_protection.rb +74 -0
  10. data/lib/couchrest/model/attributes.rb +75 -0
  11. data/lib/couchrest/model/base.rb +111 -0
  12. data/lib/couchrest/model/callbacks.rb +27 -0
  13. data/lib/couchrest/model/casted_array.rb +39 -0
  14. data/lib/couchrest/model/casted_model.rb +68 -0
  15. data/lib/couchrest/model/class_proxy.rb +122 -0
  16. data/lib/couchrest/model/collection.rb +260 -0
  17. data/lib/couchrest/model/design_doc.rb +126 -0
  18. data/lib/couchrest/model/document_queries.rb +82 -0
  19. data/lib/couchrest/model/errors.rb +23 -0
  20. data/lib/couchrest/model/extended_attachments.rb +73 -0
  21. data/lib/couchrest/model/persistence.rb +141 -0
  22. data/lib/couchrest/model/properties.rb +144 -0
  23. data/lib/couchrest/model/property.rb +96 -0
  24. data/lib/couchrest/model/support/couchrest.rb +19 -0
  25. data/lib/couchrest/model/support/hash.rb +9 -0
  26. data/lib/couchrest/model/typecast.rb +170 -0
  27. data/lib/couchrest/model/validations.rb +68 -0
  28. data/lib/couchrest/model/validations/casted_model.rb +14 -0
  29. data/lib/couchrest/model/validations/locale/en.yml +5 -0
  30. data/lib/couchrest/model/validations/uniqueness.rb +45 -0
  31. data/lib/couchrest/model/views.rb +167 -0
  32. data/lib/couchrest_model.rb +56 -0
  33. data/spec/couchrest/assocations_spec.rb +213 -0
  34. data/spec/couchrest/attachment_spec.rb +148 -0
  35. data/spec/couchrest/attribute_protection_spec.rb +153 -0
  36. data/spec/couchrest/base_spec.rb +463 -0
  37. data/spec/couchrest/casted_model_spec.rb +424 -0
  38. data/spec/couchrest/casted_spec.rb +75 -0
  39. data/spec/couchrest/class_proxy_spec.rb +132 -0
  40. data/spec/couchrest/inherited_spec.rb +40 -0
  41. data/spec/couchrest/persistence_spec.rb +409 -0
  42. data/spec/couchrest/property_spec.rb +804 -0
  43. data/spec/couchrest/subclass_spec.rb +99 -0
  44. data/spec/couchrest/validations.rb +73 -0
  45. data/spec/couchrest/view_spec.rb +463 -0
  46. data/spec/fixtures/attachments/README +3 -0
  47. data/spec/fixtures/attachments/couchdb.png +0 -0
  48. data/spec/fixtures/attachments/test.html +11 -0
  49. data/spec/fixtures/base.rb +139 -0
  50. data/spec/fixtures/more/article.rb +35 -0
  51. data/spec/fixtures/more/card.rb +17 -0
  52. data/spec/fixtures/more/cat.rb +19 -0
  53. data/spec/fixtures/more/course.rb +25 -0
  54. data/spec/fixtures/more/event.rb +8 -0
  55. data/spec/fixtures/more/invoice.rb +14 -0
  56. data/spec/fixtures/more/person.rb +9 -0
  57. data/spec/fixtures/more/question.rb +7 -0
  58. data/spec/fixtures/more/service.rb +10 -0
  59. data/spec/fixtures/more/user.rb +22 -0
  60. data/spec/fixtures/views/lib.js +3 -0
  61. data/spec/fixtures/views/test_view/lib.js +3 -0
  62. data/spec/fixtures/views/test_view/only-map.js +4 -0
  63. data/spec/fixtures/views/test_view/test-map.js +3 -0
  64. data/spec/fixtures/views/test_view/test-reduce.js +3 -0
  65. data/spec/spec.opts +5 -0
  66. data/spec/spec_helper.rb +48 -0
  67. data/utils/remap.rb +27 -0
  68. data/utils/subset.rb +30 -0
  69. metadata +232 -0
@@ -0,0 +1,132 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ class UnattachedDoc < CouchRest::Model::Base
4
+ # Note: no use_database here
5
+ property :title
6
+ property :questions
7
+ property :professor
8
+ view_by :title
9
+ end
10
+
11
+
12
+ describe "Proxy Class" do
13
+
14
+ before(:all) do
15
+ reset_test_db!
16
+ # setup the class default doc to save the design doc
17
+ UnattachedDoc.use_database nil # just to be sure it is really unattached
18
+ @us = UnattachedDoc.on(DB)
19
+ %w{aaa bbb ddd eee}.each do |title|
20
+ u = @us.new(:title => title)
21
+ u.save
22
+ @first_id ||= u.id
23
+ end
24
+ end
25
+
26
+ it "should query all" do
27
+ rs = @us.all
28
+ rs.length.should == 4
29
+ end
30
+ it "should count" do
31
+ @us.count.should == 4
32
+ end
33
+ it "should make the design doc upon first query" do
34
+ @us.by_title
35
+ doc = @us.design_doc
36
+ doc['views']['all']['map'].should include('UnattachedDoc')
37
+ end
38
+ it "should merge query params" do
39
+ rs = @us.by_title :startkey=>"bbb", :endkey=>"eee"
40
+ rs.length.should == 3
41
+ end
42
+ it "should query via view" do
43
+ view = @us.view :by_title
44
+ designed = @us.by_title
45
+ view.should == designed
46
+ end
47
+
48
+ it "should query via first_from_view" do
49
+ UnattachedDoc.should_receive(:first_from_view).with('by_title', 'bbb', {:database => DB})
50
+ @us.first_from_view('by_title', 'bbb')
51
+ end
52
+
53
+ it "should query via first_from_view with complex options" do
54
+ UnattachedDoc.should_receive(:first_from_view).with('by_title', {:key => 'bbb', :database => DB})
55
+ @us.first_from_view('by_title', :key => 'bbb')
56
+ end
57
+
58
+ it "should query via first_from_view with complex extra options" do
59
+ UnattachedDoc.should_receive(:first_from_view).with('by_title', 'bbb', {:limit => 1, :database => DB})
60
+ @us.first_from_view('by_title', 'bbb', :limit => 1)
61
+ end
62
+
63
+ it "should allow dynamic view matching for single elements" do
64
+ @us.should_receive(:first_from_view).with('by_title', 'bbb')
65
+ @us.find_by_title('bbb')
66
+ end
67
+
68
+ it "should yield" do
69
+ things = []
70
+ @us.view(:by_title) do |thing|
71
+ things << thing
72
+ end
73
+ things[0]["doc"]["title"].should =='aaa'
74
+ end
75
+ it "should yield with by_key method" do
76
+ things = []
77
+ @us.by_title do |thing|
78
+ things << thing
79
+ end
80
+ things[0]["doc"]["title"].should =='aaa'
81
+ end
82
+ it "should get from specific database" do
83
+ u = @us.get(@first_id)
84
+ u.title.should == "aaa"
85
+ end
86
+ it "should get first" do
87
+ u = @us.first
88
+ u.title.should =~ /\A...\z/
89
+ end
90
+ it "should set database on first retreived document" do
91
+ u = @us.first
92
+ u.database.should === DB
93
+ end
94
+ it "should set database on all retreived documents" do
95
+ @us.all.each do |u|
96
+ u.database.should === DB
97
+ end
98
+ end
99
+ it "should set database on each retreived document" do
100
+ rs = @us.by_title :startkey=>"bbb", :endkey=>"eee"
101
+ rs.length.should == 3
102
+ rs.each do |u|
103
+ u.database.should === DB
104
+ end
105
+ end
106
+ it "should set database on document retreived by id" do
107
+ u = @us.get(@first_id)
108
+ u.database.should === DB
109
+ end
110
+ it "should not attempt to set database on raw results using :all" do
111
+ @us.all(:raw => true).each do |u|
112
+ u.respond_to?(:database).should be_false
113
+ end
114
+ end
115
+ it "should not attempt to set database on raw results using view" do
116
+ @us.by_title(:raw => true).each do |u|
117
+ u.respond_to?(:database).should be_false
118
+ end
119
+ end
120
+ # Sam Lown 2010-04-07
121
+ # Removed as unclear why this should happen as before my changes
122
+ # this happend by accident, not explicitly.
123
+ # If requested, this feature should be added as a specific method.
124
+ #
125
+ #it "should clean up design docs left around on specific database" do
126
+ # @us.by_title
127
+ # original_id = @us.model_design_doc['_rev']
128
+ # Unattached.view_by :professor
129
+ # @us.by_professor
130
+ # @us.model_design_doc['_rev'].should_not == original_id
131
+ #end
132
+ end
@@ -0,0 +1,40 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ begin
4
+ require 'rubygems' unless ENV['SKIP_RUBYGEMS']
5
+ require 'active_support/json'
6
+ ActiveSupport::JSON.backend = :JSONGem
7
+
8
+ class PlainParent
9
+ class_inheritable_accessor :foo
10
+ self.foo = :bar
11
+ end
12
+
13
+ class PlainChild < PlainParent
14
+ end
15
+
16
+ class ExtendedParent < CouchRest::Model::Base
17
+ class_inheritable_accessor :foo
18
+ self.foo = :bar
19
+ end
20
+
21
+ class ExtendedChild < ExtendedParent
22
+ end
23
+
24
+ describe "Using chained inheritance without CouchRest::ExtendedDocument" do
25
+ it "should preserve inheritable attributes" do
26
+ PlainParent.foo.should == :bar
27
+ PlainChild.foo.should == :bar
28
+ end
29
+ end
30
+
31
+ describe "Using chained inheritance with CouchRest::ExtendedDocument" do
32
+ it "should preserve inheritable attributes" do
33
+ ExtendedParent.foo.should == :bar
34
+ ExtendedChild.foo.should == :bar
35
+ end
36
+ end
37
+
38
+ rescue LoadError
39
+ puts "This spec requires 'active_support/json' to be loaded"
40
+ end
@@ -0,0 +1,409 @@
1
+ # encoding: utf-8
2
+ require File.expand_path('../../spec_helper', __FILE__)
3
+ require File.join(FIXTURE_PATH, 'base')
4
+ require File.join(FIXTURE_PATH, 'more', 'cat')
5
+ require File.join(FIXTURE_PATH, 'more', 'article')
6
+ require File.join(FIXTURE_PATH, 'more', 'course')
7
+ require File.join(FIXTURE_PATH, 'more', 'card')
8
+
9
+ describe "Model Persistence" do
10
+
11
+ before(:each) do
12
+ @obj = WithDefaultValues.new
13
+ end
14
+
15
+ describe "creating a new document from database" do
16
+
17
+ it "should instantialize" do
18
+ doc = Article.create_from_database({'_id' => 'testitem1', '_rev' => 123, 'couchrest-type' => 'Article', 'name' => 'my test'})
19
+ doc.class.should eql(Article)
20
+ end
21
+
22
+ it "should instantialize of same class if no couchrest-type included from DB" do
23
+ doc = Article.create_from_database({'_id' => 'testitem1', '_rev' => 123, 'name' => 'my test'})
24
+ doc.class.should eql(Article)
25
+ end
26
+
27
+ it "should instantialize document of different type" do
28
+ doc = Article.create_from_database({'_id' => 'testitem2', '_rev' => 123, 'couchrest-type' => 'WithTemplateAndUniqueID', 'name' => 'my test'})
29
+ doc.class.should eql(WithTemplateAndUniqueID)
30
+ end
31
+
32
+ end
33
+
34
+ describe "basic saving and retrieving" do
35
+ it "should work fine" do
36
+ @obj.name = "should be easily saved and retrieved"
37
+ @obj.save
38
+ saved_obj = WithDefaultValues.get(@obj.id)
39
+ saved_obj.should_not be_nil
40
+ end
41
+
42
+ it "should parse the Time attributes automatically" do
43
+ @obj.name = "should parse the Time attributes automatically"
44
+ @obj.set_by_proc.should be_an_instance_of(Time)
45
+ @obj.save
46
+ @obj.set_by_proc.should be_an_instance_of(Time)
47
+ saved_obj = WithDefaultValues.get(@obj.id)
48
+ saved_obj.set_by_proc.should be_an_instance_of(Time)
49
+ end
50
+ end
51
+
52
+ describe "creating a model" do
53
+
54
+ before(:each) do
55
+ @sobj = Basic.new
56
+ end
57
+
58
+ it "should accept true or false on save for validation" do
59
+ @sobj.should_receive(:valid?)
60
+ @sobj.save(true)
61
+ end
62
+
63
+ it "should accept hash with validation option" do
64
+ @sobj.should_receive(:valid?)
65
+ @sobj.save(:validate => true)
66
+ end
67
+
68
+ it "should not call validation when option is false" do
69
+ @sobj.should_not_receive(:valid?)
70
+ @sobj.save(false)
71
+ end
72
+
73
+ it "should not call validation when option :validate is false" do
74
+ @sobj.should_not_receive(:valid?)
75
+ @sobj.save(:validate => false)
76
+ end
77
+
78
+ it "should instantialize and save a document" do
79
+ article = Article.create(:title => 'my test')
80
+ article.title.should == 'my test'
81
+ article.should_not be_new
82
+ end
83
+
84
+ it "should trigger the create callbacks" do
85
+ doc = WithCallBacks.create(:name => 'my other test')
86
+ doc.run_before_create.should be_true
87
+ doc.run_after_create.should be_true
88
+ doc.run_before_save.should be_true
89
+ doc.run_after_save.should be_true
90
+ end
91
+
92
+ end
93
+
94
+ describe "saving a model" do
95
+ before(:all) do
96
+ @sobj = Basic.new
97
+ @sobj.save.should be_true
98
+ end
99
+
100
+ it "should save the doc" do
101
+ doc = Basic.get(@sobj.id)
102
+ doc['_id'].should == @sobj.id
103
+ end
104
+
105
+ it "should be set for resaving" do
106
+ rev = @obj.rev
107
+ @sobj['another-key'] = "some value"
108
+ @sobj.save
109
+ @sobj.rev.should_not == rev
110
+ end
111
+
112
+ it "should set the id" do
113
+ @sobj.id.should be_an_instance_of(String)
114
+ end
115
+
116
+ it "should set the type" do
117
+ @sobj['couchrest-type'].should == 'Basic'
118
+ end
119
+
120
+ it "should accept true or false on save for validation" do
121
+ @sobj.should_receive(:valid?)
122
+ @sobj.save(true)
123
+ end
124
+
125
+ it "should accept hash with validation option" do
126
+ @sobj.should_receive(:valid?)
127
+ @sobj.save(:validate => true)
128
+ end
129
+
130
+ it "should not call validation when option is false" do
131
+ @sobj.should_not_receive(:valid?)
132
+ @sobj.save(false)
133
+ end
134
+
135
+ it "should not call validation when option :validate is false" do
136
+ @sobj.should_not_receive(:valid?)
137
+ @sobj.save(:validate => false)
138
+ end
139
+
140
+ describe "save!" do
141
+
142
+ before(:each) do
143
+ @sobj = Card.new(:first_name => "Marcos", :last_name => "Tapajós")
144
+ end
145
+
146
+ it "should return true if save the document" do
147
+ @sobj.save!.should be_true
148
+ end
149
+
150
+ it "should raise error if don't save the document" do
151
+ @sobj.first_name = nil
152
+ lambda { @sobj.save! }.should raise_error(CouchRest::Model::Errors::Validations)
153
+ end
154
+
155
+ end
156
+ end
157
+
158
+ describe "saving a model with a unique_id configured" do
159
+ before(:each) do
160
+ @art = Article.new
161
+ @old = Article.database.get('this-is-the-title') rescue nil
162
+ Article.database.delete_doc(@old) if @old
163
+ end
164
+
165
+ it "should be a new document" do
166
+ @art.should be_new
167
+ @art.title.should be_nil
168
+ end
169
+
170
+ it "should require the title" do
171
+ lambda{@art.save}.should raise_error
172
+ @art.title = 'This is the title'
173
+ @art.save.should be_true
174
+ end
175
+
176
+ it "should not change the slug on update" do
177
+ @art.title = 'This is the title'
178
+ @art.save.should be_true
179
+ @art.title = 'new title'
180
+ @art.save.should be_true
181
+ @art.slug.should == 'this-is-the-title'
182
+ end
183
+
184
+ it "should raise an error when the slug is taken" do
185
+ @art.title = 'This is the title'
186
+ @art.save.should be_true
187
+ @art2 = Article.new(:title => 'This is the title!')
188
+ lambda{@art2.save}.should raise_error
189
+ end
190
+
191
+ it "should set the slug" do
192
+ @art.title = 'This is the title'
193
+ @art.save.should be_true
194
+ @art.slug.should == 'this-is-the-title'
195
+ end
196
+
197
+ it "should set the id" do
198
+ @art.title = 'This is the title'
199
+ @art.save.should be_true
200
+ @art.id.should == 'this-is-the-title'
201
+ end
202
+ end
203
+
204
+ describe "saving a model with a unique_id lambda" do
205
+ before(:each) do
206
+ @templated = WithTemplateAndUniqueID.new
207
+ @old = WithTemplateAndUniqueID.get('very-important') rescue nil
208
+ @old.destroy if @old
209
+ end
210
+
211
+ it "should require the field" do
212
+ lambda{@templated.save}.should raise_error
213
+ @templated['important-field'] = 'very-important'
214
+ @templated.save.should be_true
215
+ end
216
+
217
+ it "should save with the id" do
218
+ @templated['important-field'] = 'very-important'
219
+ @templated.save.should be_true
220
+ t = WithTemplateAndUniqueID.get('very-important')
221
+ t.should == @templated
222
+ end
223
+
224
+ it "should not change the id on update" do
225
+ @templated['important-field'] = 'very-important'
226
+ @templated.save.should be_true
227
+ @templated['important-field'] = 'not-important'
228
+ @templated.save.should be_true
229
+ t = WithTemplateAndUniqueID.get('very-important')
230
+ t.should == @templated
231
+ end
232
+
233
+ it "should raise an error when the id is taken" do
234
+ @templated['important-field'] = 'very-important'
235
+ @templated.save.should be_true
236
+ lambda{WithTemplateAndUniqueID.new('important-field' => 'very-important').save}.should raise_error
237
+ end
238
+
239
+ it "should set the id" do
240
+ @templated['important-field'] = 'very-important'
241
+ @templated.save.should be_true
242
+ @templated.id.should == 'very-important'
243
+ end
244
+ end
245
+
246
+ describe "destroying an instance" do
247
+ before(:each) do
248
+ @dobj = Basic.new
249
+ @dobj.save.should be_true
250
+ end
251
+ it "should return true" do
252
+ result = @dobj.destroy
253
+ result.should be_true
254
+ end
255
+ it "should be resavable" do
256
+ @dobj.destroy
257
+ @dobj.rev.should be_nil
258
+ @dobj.id.should be_nil
259
+ @dobj.save.should be_true
260
+ end
261
+ it "should make it go away" do
262
+ @dobj.destroy
263
+ lambda{Basic.get!(@dobj.id)}.should raise_error
264
+ end
265
+ end
266
+
267
+
268
+ describe "getting a model" do
269
+ before(:all) do
270
+ @art = Article.new(:title => 'All About Getting')
271
+ @art.save
272
+ end
273
+ it "should load and instantiate it" do
274
+ foundart = Article.get @art.id
275
+ foundart.title.should == "All About Getting"
276
+ end
277
+ it "should load and instantiate with find" do
278
+ foundart = Article.find @art.id
279
+ foundart.title.should == "All About Getting"
280
+ end
281
+ it "should return nil if `get` is used and the document doesn't exist" do
282
+ foundart = Article.get 'matt aimonetti'
283
+ foundart.should be_nil
284
+ end
285
+ it "should raise an error if `get!` is used and the document doesn't exist" do
286
+ lambda{foundart = Article.get!('matt aimonetti')}.should raise_error
287
+ end
288
+ it "should raise an error if `find!` is used and the document doesn't exist" do
289
+ lambda{foundart = Article.find!('matt aimonetti')}.should raise_error
290
+ end
291
+ end
292
+
293
+ describe "getting a model with a subobjects array" do
294
+ before(:all) do
295
+ course_doc = {
296
+ "title" => "Metaphysics 200",
297
+ "questions" => [
298
+ {
299
+ "q" => "Carve the ___ of reality at the ___.",
300
+ "a" => ["beast","joints"]
301
+ },{
302
+ "q" => "Who layed the smack down on Leibniz's Law?",
303
+ "a" => "Willard Van Orman Quine"
304
+ }
305
+ ]
306
+ }
307
+ r = Course.database.save_doc course_doc
308
+ @course = Course.get r['id']
309
+ end
310
+ it "should load the course" do
311
+ @course.title.should == "Metaphysics 200"
312
+ end
313
+ it "should instantiate them as such" do
314
+ @course["questions"][0].a[0].should == "beast"
315
+ end
316
+ end
317
+
318
+ describe "callbacks" do
319
+
320
+ before(:each) do
321
+ @doc = WithCallBacks.new
322
+ end
323
+
324
+ describe "validation" do
325
+ it "should run before_validation before validating" do
326
+ @doc.run_before_validate.should be_nil
327
+ @doc.should be_valid
328
+ @doc.run_before_validate.should be_true
329
+ end
330
+ it "should run after_validation after validating" do
331
+ @doc.run_after_validate.should be_nil
332
+ @doc.should be_valid
333
+ @doc.run_after_validate.should be_true
334
+ end
335
+ end
336
+
337
+ describe "save" do
338
+ it "should run the after filter after saving" do
339
+ @doc.run_after_save.should be_nil
340
+ @doc.save.should be_true
341
+ @doc.run_after_save.should be_true
342
+ end
343
+ it "should run the grouped callbacks before saving" do
344
+ @doc.run_one.should be_nil
345
+ @doc.run_two.should be_nil
346
+ @doc.run_three.should be_nil
347
+ @doc.save.should be_true
348
+ @doc.run_one.should be_true
349
+ @doc.run_two.should be_true
350
+ @doc.run_three.should be_true
351
+ end
352
+ it "should not run conditional callbacks" do
353
+ @doc.run_it = false
354
+ @doc.save.should be_true
355
+ @doc.conditional_one.should be_nil
356
+ @doc.conditional_two.should be_nil
357
+ end
358
+ it "should run conditional callbacks" do
359
+ @doc.run_it = true
360
+ @doc.save.should be_true
361
+ @doc.conditional_one.should be_true
362
+ @doc.conditional_two.should be_true
363
+ end
364
+ end
365
+ describe "create" do
366
+ it "should run the before save filter when creating" do
367
+ @doc.run_before_save.should be_nil
368
+ @doc.create.should_not be_nil
369
+ @doc.run_before_save.should be_true
370
+ end
371
+ it "should run the before create filter" do
372
+ @doc.run_before_create.should be_nil
373
+ @doc.create.should_not be_nil
374
+ @doc.create
375
+ @doc.run_before_create.should be_true
376
+ end
377
+ it "should run the after create filter" do
378
+ @doc.run_after_create.should be_nil
379
+ @doc.create.should_not be_nil
380
+ @doc.create
381
+ @doc.run_after_create.should be_true
382
+ end
383
+ end
384
+ describe "update" do
385
+
386
+ before(:each) do
387
+ @doc.save
388
+ end
389
+ it "should run the before update filter when updating an existing document" do
390
+ @doc.run_before_update.should be_nil
391
+ @doc.update
392
+ @doc.run_before_update.should be_true
393
+ end
394
+ it "should run the after update filter when updating an existing document" do
395
+ @doc.run_after_update.should be_nil
396
+ @doc.update
397
+ @doc.run_after_update.should be_true
398
+ end
399
+ it "should run the before update filter when saving an existing document" do
400
+ @doc.run_before_update.should be_nil
401
+ @doc.save
402
+ @doc.run_before_update.should be_true
403
+ end
404
+
405
+ end
406
+ end
407
+
408
+
409
+ end