couchrest_model 2.0.0.beta2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.travis.yml +8 -0
  2. data/Gemfile +1 -1
  3. data/README.md +1 -1
  4. data/Rakefile +9 -24
  5. data/VERSION +1 -1
  6. data/couchrest_model.gemspec +7 -5
  7. data/history.md +17 -1
  8. data/lib/couchrest/model/associations.rb +16 -11
  9. data/lib/couchrest/model/base.rb +17 -15
  10. data/lib/couchrest/model/casted_array.rb +7 -1
  11. data/lib/couchrest/model/core_extensions/time_parsing.rb +0 -23
  12. data/lib/couchrest/model/design.rb +282 -0
  13. data/lib/couchrest/model/designs/design_mapper.rb +79 -0
  14. data/lib/couchrest/model/designs/view.rb +9 -6
  15. data/lib/couchrest/model/designs.rb +37 -70
  16. data/lib/couchrest/model/persistence.rb +5 -5
  17. data/lib/couchrest/model/properties.rb +5 -16
  18. data/lib/couchrest/model/property.rb +34 -16
  19. data/lib/couchrest/model/translation.rb +22 -0
  20. data/lib/couchrest/model/typecast.rb +54 -43
  21. data/lib/couchrest/model/utils/migrate.rb +106 -0
  22. data/lib/couchrest_model.rb +4 -2
  23. data/lib/tasks/migrations.rake +5 -5
  24. data/spec/fixtures/models/course.rb +1 -0
  25. data/spec/fixtures/models/designs.rb +22 -0
  26. data/spec/spec_helper.rb +1 -0
  27. data/spec/unit/assocations_spec.rb +7 -0
  28. data/spec/unit/base_spec.rb +3 -1
  29. data/spec/unit/{designs/design_spec.rb → design_spec.rb} +6 -6
  30. data/spec/unit/designs/design_mapper_spec.rb +124 -0
  31. data/spec/unit/designs/view_spec.rb +30 -4
  32. data/spec/unit/designs_spec.rb +5 -140
  33. data/spec/unit/dirty_spec.rb +15 -1
  34. data/spec/unit/embeddable_spec.rb +2 -2
  35. data/spec/unit/property_spec.rb +70 -28
  36. data/spec/unit/translations_spec.rb +31 -0
  37. data/spec/unit/typecast_spec.rb +99 -19
  38. data/spec/unit/utils/migrate_spec.rb +25 -0
  39. metadata +43 -19
  40. data/lib/couchrest/model/designs/design.rb +0 -284
  41. data/lib/couchrest/model/migrate.rb +0 -92
@@ -75,6 +75,13 @@ describe "Assocations" do
75
75
  @invoice.client_id.should be_nil
76
76
  end
77
77
 
78
+ it "should allow replacement of object after updating key" do
79
+ @invoice.client = @client
80
+ @invoice.client.should eql(@client)
81
+ @invoice.client_id = nil
82
+ @invoice.client.should be_nil
83
+ end
84
+
78
85
  it "should allow override of foreign key" do
79
86
  @invoice.respond_to?(:alternate_client).should be_true
80
87
  @invoice.respond_to?("alternate_client=").should be_true
@@ -469,6 +469,7 @@ describe "Model Base" do
469
469
 
470
470
  it "should define the updated_at and created_at getters and set the values" do
471
471
  @obj.save
472
+ json = @obj.to_json
472
473
  obj = WithDefaultValues.get(@obj.id)
473
474
  obj.should be_an_instance_of(WithDefaultValues)
474
475
  obj.created_at.should be_an_instance_of(Time)
@@ -487,7 +488,8 @@ describe "Model Base" do
487
488
  it "should set the time on create" do
488
489
  (Time.now - @art.created_at).should < 2
489
490
  foundart = Article.get @art.id
490
- foundart.created_at.should == foundart.updated_at
491
+ # Use string for comparison to cope with microsecond differences
492
+ foundart.created_at.to_s.should == foundart.updated_at.to_s
491
493
  end
492
494
  it "should set the time on update" do
493
495
  @art.title = "new title" # only saved if @art.changed? == true
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe CouchRest::Model::Designs::Design do
5
+ describe CouchRest::Model::Design do
6
6
 
7
7
  before :all do
8
8
  reset_test_db!
@@ -26,7 +26,7 @@ describe CouchRest::Model::Designs::Design do
26
26
  describe "class methods" do
27
27
 
28
28
  before :all do
29
- @klass = CouchRest::Model::Designs::Design
29
+ @klass = CouchRest::Model::Design
30
30
  end
31
31
 
32
32
  describe ".method_name" do
@@ -46,7 +46,7 @@ describe CouchRest::Model::Designs::Design do
46
46
  before :each do
47
47
  @model = mock("ModelExample")
48
48
  @model.stub(:to_s).and_return("ModelExample")
49
- @obj = CouchRest::Model::Designs::Design.new(@model)
49
+ @obj = CouchRest::Model::Design.new(@model)
50
50
  end
51
51
 
52
52
 
@@ -68,13 +68,13 @@ describe CouchRest::Model::Designs::Design do
68
68
  describe "initialisation with prefix" do
69
69
 
70
70
  it "should associate model and set method name" do
71
- @obj = CouchRest::Model::Designs::Design.new(@model, 'stats')
71
+ @obj = CouchRest::Model::Design.new(@model, 'stats')
72
72
  @obj.model.should eql(@model)
73
73
  @obj.method_name.should eql("stats_design_doc")
74
74
  end
75
75
 
76
76
  it "should generate correct id with prefix" do
77
- @obj = CouchRest::Model::Designs::Design.new(@model, 'stats')
77
+ @obj = CouchRest::Model::Design.new(@model, 'stats')
78
78
  @obj['_id'].should eql("_design/ModelExample_stats")
79
79
  end
80
80
 
@@ -358,7 +358,7 @@ describe CouchRest::Model::Designs::Design do
358
358
 
359
359
  it "should calculate checksum for complex model" do
360
360
  #Article.design_doc.checksum.should eql('70dff8caea143bf40fad09adf0701104')
361
- Article.design_doc.checksum.should eql('0f25a2d9f86e31ebd61b29863a41d5ed')
361
+ Article.design_doc.checksum.should eql('81f6553c44ecc3fe12a39331b0cdee46')
362
362
  end
363
363
 
364
364
  it "should cache the generated checksum value" do
@@ -0,0 +1,124 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe CouchRest::Model::Designs::DesignMapper do
6
+
7
+ before :all do
8
+ @klass = CouchRest::Model::Designs::DesignMapper
9
+ end
10
+
11
+ describe 'initialize without prefix' do
12
+
13
+ before :all do
14
+ @object = @klass.new(DesignModel)
15
+ end
16
+
17
+ it "should set basic variables" do
18
+ @object.send(:model).should eql(DesignModel)
19
+ @object.send(:prefix).should be_nil
20
+ @object.send(:method).should eql('design_doc')
21
+ end
22
+
23
+ it "should add design doc to list" do
24
+ @object.model.design_docs.should include(@object.model.design_doc)
25
+ end
26
+
27
+ it "should create a design doc method" do
28
+ @object.model.should respond_to('design_doc')
29
+ @object.design_doc.should eql(@object.model.design_doc)
30
+ end
31
+
32
+ it "should use default for autoupdate" do
33
+ @object.design_doc.auto_update.should be_true
34
+ end
35
+
36
+ end
37
+
38
+ describe 'initialize with prefix' do
39
+ before :all do
40
+ @object = @klass.new(DesignModel, 'stats')
41
+ end
42
+
43
+ it "should set basics" do
44
+ @object.send(:model).should eql(DesignModel)
45
+ @object.send(:prefix).should eql('stats')
46
+ @object.send(:method).should eql('stats_design_doc')
47
+ end
48
+
49
+ it "should add design doc to list" do
50
+ @object.model.design_docs.should include(@object.model.stats_design_doc)
51
+ end
52
+
53
+ it "should not create an all method" do
54
+ @object.model.should_not respond_to('all')
55
+ end
56
+
57
+ it "should create a design doc method" do
58
+ @object.model.should respond_to('stats_design_doc')
59
+ @object.design_doc.should eql(@object.model.stats_design_doc)
60
+ end
61
+
62
+ end
63
+
64
+ describe "#disable_auto_update" do
65
+ it "should disable auto updates" do
66
+ @object = @klass.new(DesignModel)
67
+ @object.disable_auto_update
68
+ @object.design_doc.auto_update.should be_false
69
+ end
70
+ end
71
+
72
+ describe "#enable_auto_update" do
73
+ it "should enable auto updates" do
74
+ @object = @klass.new(DesignModel)
75
+ @object.enable_auto_update
76
+ @object.design_doc.auto_update.should be_true
77
+ end
78
+ end
79
+
80
+ describe "#model_type_key" do
81
+ it "should return models type key" do
82
+ @object = @klass.new(DesignModel)
83
+ @object.model_type_key.should eql(@object.model.model_type_key)
84
+ end
85
+ end
86
+
87
+ describe "#view" do
88
+
89
+ before :each do
90
+ @object = @klass.new(DesignModel)
91
+ end
92
+
93
+ it "should call create method on view" do
94
+ CouchRest::Model::Designs::View.should_receive(:define).with(@object.design_doc, 'test', {})
95
+ @object.view('test')
96
+ end
97
+
98
+ it "should create a method on parent model" do
99
+ CouchRest::Model::Designs::View.stub!(:define)
100
+ @object.view('test_view')
101
+ DesignModel.should respond_to(:test_view)
102
+ end
103
+
104
+ it "should create a method for view instance" do
105
+ @object.design_doc.should_receive(:create_view).with('test', {})
106
+ @object.view('test')
107
+ end
108
+ end
109
+
110
+ describe "#filter" do
111
+
112
+ before :each do
113
+ @object = @klass.new(DesignModel)
114
+ end
115
+
116
+ it "should add the provided function to the design doc" do
117
+ @object.filter(:important, "function(doc, req) { return doc.priority == 'high'; }")
118
+ DesignModel.design_doc['filters'].should_not be_empty
119
+ DesignModel.design_doc['filters']['important'].should_not be_blank
120
+ end
121
+ end
122
+
123
+ end
124
+
@@ -118,13 +118,25 @@ describe "Design View" do
118
118
  @design_doc['views']['by_title']['reduce'].should eql(true)
119
119
  end
120
120
 
121
+ it "should replace reduce symbol with string name" do
122
+ @klass.define(@design_doc, 'by_title', :reduce => :sum)
123
+ @design_doc['views']['by_title']['map'].should_not be_blank
124
+ @design_doc['views']['by_title']['reduce'].should eql('_sum')
125
+ end
126
+
127
+ it "should replace reduce symbol with string if map function present" do
128
+ @klass.define(@design_doc, 'by_title', :map => "function(d) { }", :reduce => :sum)
129
+ @design_doc['views']['by_title']['map'].should_not be_blank
130
+ @design_doc['views']['by_title']['reduce'].should eql('_sum')
131
+ end
132
+
121
133
  it "should auto generate mapping from name" do
122
134
  lambda { @klass.define(@design_doc, 'by_title') }.should_not raise_error
123
135
  str = @design_doc['views']['by_title']['map']
124
136
  str.should include("((doc['#{DesignViewModel.model_type_key}'] == 'DesignViewModel') && (doc['title'] != null))")
125
137
  str.should include("emit(doc['title'], 1);")
126
138
  str = @design_doc['views']['by_title']['reduce']
127
- str.should include("return sum(values);")
139
+ str.should include("_sum")
128
140
  end
129
141
 
130
142
  it "should auto generate mapping from name with and" do
@@ -133,7 +145,12 @@ describe "Design View" do
133
145
  str.should include("(doc['title'] != null) && (doc['name'] != null)")
134
146
  str.should include("emit([doc['title'], doc['name']], 1);")
135
147
  str = @design_doc['views']['by_title_and_name']['reduce']
136
- str.should include("return sum(values);")
148
+ str.should include("_sum")
149
+ end
150
+
151
+ it "should allow reduce methods as symbols" do
152
+ @klass.define(@design_doc, 'by_title', :reduce => :stats)
153
+ @design_doc['views']['by_title']['reduce'].should eql('_stats')
137
154
  end
138
155
  end
139
156
 
@@ -719,6 +736,15 @@ describe "Design View" do
719
736
  end
720
737
  end
721
738
 
739
+ describe "#total_pages" do
740
+ it "should use total_count and limit_value" do
741
+ @obj.should_receive(:total_count).and_return(200)
742
+ @obj.should_receive(:limit_value).and_return(25)
743
+ @obj.total_pages.should eql(8)
744
+ end
745
+ end
746
+
747
+ # `num_pages` aliases to `total_pages` for compatibility for Kaminari '< 0.14'
722
748
  describe "#num_pages" do
723
749
  it "should use total_count and limit_value" do
724
750
  @obj.should_receive(:total_count).and_return(200)
@@ -887,7 +913,7 @@ describe "Design View" do
887
913
  end
888
914
 
889
915
  it "should calculate number of pages" do
890
- @view.num_pages.should eql(2)
916
+ @view.total_pages.should eql(2)
891
917
  end
892
918
  it "should return results from first page" do
893
919
  @view.all.first.name.should eql('Judith')
@@ -900,7 +926,7 @@ describe "Design View" do
900
926
 
901
927
  it "should allow overriding per page count" do
902
928
  @view = @view.per(10)
903
- @view.num_pages.should eql(1)
929
+ @view.total_pages.should eql(1)
904
930
  @view.all.last.name.should eql('Vilma')
905
931
  end
906
932
  end
@@ -1,9 +1,5 @@
1
1
  require "spec_helper"
2
2
 
3
- class DesignModel < CouchRest::Model::Base
4
- use_database DB
5
- property :name
6
- end
7
3
 
8
4
  describe CouchRest::Model::Designs do
9
5
 
@@ -16,7 +12,7 @@ describe CouchRest::Model::Designs do
16
12
  describe ".design" do
17
13
 
18
14
  before :each do
19
- @klass = DesignModel.dup
15
+ @klass = DesignsModel.dup
20
16
  end
21
17
 
22
18
  describe "without block" do
@@ -36,14 +32,14 @@ describe CouchRest::Model::Designs do
36
32
  @klass.design
37
33
  blocks = @klass.instance_variable_get(:@_design_blocks)
38
34
  blocks.length.should eql(1)
39
- blocks.first.should eql({:args => [nil], :block => nil})
35
+ blocks.first.should eql({:args => [], :block => nil})
40
36
  end
41
37
 
42
38
  it "should have added itself to a design_blocks array" do
43
39
  @klass.design
44
40
  blocks = @klass.instance_variable_get(:@_design_blocks)
45
41
  blocks.length.should eql(1)
46
- blocks.first.should eql({:args => [nil], :block => nil})
42
+ blocks.first.should eql({:args => [], :block => nil})
47
43
  end
48
44
 
49
45
  it "should have added itself to a design_blocks array with prefix" do
@@ -69,7 +65,7 @@ describe CouchRest::Model::Designs do
69
65
  it "should have added itself to a design_blocks array" do
70
66
  blocks = @klass.instance_variable_get(:@_design_blocks)
71
67
  blocks.length.should eql(1)
72
- blocks.first.should eql({:args => [nil], :block => @block})
68
+ blocks.first.should eql({:args => [], :block => @block})
73
69
  end
74
70
 
75
71
  it "should handle multiple designs" do
@@ -79,7 +75,7 @@ describe CouchRest::Model::Designs do
79
75
  @klass.design :stats, &@block2
80
76
  blocks = @klass.instance_variable_get(:@_design_blocks)
81
77
  blocks.length.should eql(2)
82
- blocks.first.should eql({:args => [nil], :block => @block})
78
+ blocks.first.should eql({:args => [], :block => @block})
83
79
  blocks.last.should eql({:args => [:stats], :block => @block2})
84
80
  end
85
81
  end
@@ -115,137 +111,6 @@ describe CouchRest::Model::Designs do
115
111
  end
116
112
  end
117
113
 
118
- describe "DesignMapper" do
119
-
120
- before :all do
121
- @klass = CouchRest::Model::Designs::DesignMapper
122
- end
123
-
124
- describe 'initialize without prefix' do
125
-
126
- before :all do
127
- @object = @klass.new(DesignModel)
128
- end
129
-
130
- it "should set basic variables" do
131
- @object.send(:model).should eql(DesignModel)
132
- @object.send(:prefix).should be_nil
133
- @object.send(:method).should eql('design_doc')
134
- end
135
-
136
- it "should add design doc to list" do
137
- @object.model.design_docs.should include(@object.model.design_doc)
138
- end
139
-
140
- it "should create a design doc method" do
141
- @object.model.should respond_to('design_doc')
142
- @object.design_doc.should eql(@object.model.design_doc)
143
- end
144
-
145
- it "should use default for autoupdate" do
146
- @object.design_doc.auto_update.should be_true
147
- end
148
-
149
- end
150
-
151
- describe 'initialize with prefix' do
152
- before :all do
153
- @object = @klass.new(DesignModel, 'stats')
154
- end
155
-
156
- it "should set basics" do
157
- @object.send(:model).should eql(DesignModel)
158
- @object.send(:prefix).should eql('stats')
159
- @object.send(:method).should eql('stats_design_doc')
160
- end
161
-
162
- it "should add design doc to list" do
163
- @object.model.design_docs.should include(@object.model.stats_design_doc)
164
- end
165
-
166
- it "should not create an all method" do
167
- @object.model.should_not respond_to('all')
168
- end
169
-
170
- it "should create a design doc method" do
171
- @object.model.should respond_to('stats_design_doc')
172
- @object.design_doc.should eql(@object.model.stats_design_doc)
173
- end
174
-
175
- end
176
-
177
- describe "#disable_auto_update" do
178
- it "should disable auto updates" do
179
- @object = @klass.new(DesignModel)
180
- @object.disable_auto_update
181
- @object.design_doc.auto_update.should be_false
182
- end
183
- end
184
-
185
- describe "#enable_auto_update" do
186
- it "should enable auto updates" do
187
- @object = @klass.new(DesignModel)
188
- @object.enable_auto_update
189
- @object.design_doc.auto_update.should be_true
190
- end
191
- end
192
-
193
- describe "#model_type_key" do
194
- it "should return models type key" do
195
- @object = @klass.new(DesignModel)
196
- @object.model_type_key.should eql(@object.model.model_type_key)
197
- end
198
- end
199
-
200
- describe "#view" do
201
-
202
- before :each do
203
- @object = @klass.new(DesignModel)
204
- end
205
-
206
- it "should call create method on view" do
207
- CouchRest::Model::Designs::View.should_receive(:define).with(@object.design_doc, 'test', {})
208
- @object.view('test')
209
- end
210
-
211
- it "should create a method on parent model" do
212
- CouchRest::Model::Designs::View.stub!(:define)
213
- @object.view('test_view')
214
- DesignModel.should respond_to(:test_view)
215
- end
216
-
217
- it "should create a method for view instance" do
218
- @object.design_doc.should_receive(:create_view).with('test', {})
219
- @object.view('test')
220
- end
221
- end
222
-
223
- describe "#filter" do
224
-
225
- before :each do
226
- @object = @klass.new(DesignModel)
227
- end
228
-
229
- it "should add the provided function to the design doc" do
230
- @object.filter(:important, "function(doc, req) { return doc.priority == 'high'; }")
231
- DesignModel.design_doc['filters'].should_not be_empty
232
- DesignModel.design_doc['filters']['important'].should_not be_blank
233
- end
234
- end
235
-
236
- end
237
-
238
-
239
- class DesignsNoAutoUpdate < CouchRest::Model::Base
240
- use_database DB
241
- property :title, String
242
- design do
243
- disable_auto_update
244
- view :by_title_fail, :by => ['title']
245
- view :by_title, :reduce => true
246
- end
247
- end
248
-
249
114
  describe "Scenario testing" do
250
115
 
251
116
  describe "with auto update disabled" do
@@ -14,7 +14,7 @@ class DirtyModel < CouchRest::Model::Base
14
14
  property :title, :default => 'Sample Title'
15
15
  property :details, Object, :default => { 'color' => 'blue' }
16
16
  property :keywords, [String], :default => ['default-keyword']
17
- property :sub_models do
17
+ property :sub_models, :array => true do
18
18
  property :title
19
19
  end
20
20
  end
@@ -270,6 +270,20 @@ describe "Dirty" do
270
270
  end
271
271
  end
272
272
 
273
+ it "should report changes if item is inserted into array" do
274
+ should_change_array do |array, obj|
275
+ array.insert(0, 'keyword')
276
+ obj.keywords[0].should eql('keyword')
277
+ end
278
+ end
279
+
280
+ it "should report changes if items are inserted into array" do
281
+ should_change_array do |array, obj|
282
+ array.insert(1, 'keyword', 'keyword2')
283
+ obj.keywords[2].should eql('keyword2')
284
+ end
285
+ end
286
+
273
287
  it "should report changes if an array is popped" do
274
288
  should_change_array do |array, obj|
275
289
  array.pop
@@ -20,10 +20,10 @@ class DummyModel < CouchRest::Model::Base
20
20
  property :casted_attribute, WithCastedModelMixin
21
21
  property :keywords, [String]
22
22
  property :old_casted_attribute, OldFashionedMixin
23
- property :sub_models do |child|
23
+ property :sub_models, :array => true do |child|
24
24
  child.property :title
25
25
  end
26
- property :param_free_sub_models do
26
+ property :param_free_sub_models, :array => true do
27
27
  property :title
28
28
  end
29
29
  end