openlogic-couchrest_model 1.0.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/.gitignore +11 -0
- data/.rspec +4 -0
- data/Gemfile +4 -0
- data/LICENSE +176 -0
- data/README.md +137 -0
- data/Rakefile +38 -0
- data/THANKS.md +21 -0
- data/VERSION +1 -0
- data/benchmarks/dirty.rb +118 -0
- data/couchrest_model.gemspec +36 -0
- data/history.md +309 -0
- data/init.rb +1 -0
- data/lib/couchrest/model.rb +10 -0
- data/lib/couchrest/model/associations.rb +231 -0
- data/lib/couchrest/model/base.rb +129 -0
- data/lib/couchrest/model/callbacks.rb +28 -0
- data/lib/couchrest/model/casted_array.rb +83 -0
- data/lib/couchrest/model/casted_by.rb +33 -0
- data/lib/couchrest/model/casted_hash.rb +84 -0
- data/lib/couchrest/model/class_proxy.rb +135 -0
- data/lib/couchrest/model/collection.rb +273 -0
- data/lib/couchrest/model/configuration.rb +67 -0
- data/lib/couchrest/model/connection.rb +70 -0
- data/lib/couchrest/model/core_extensions/hash.rb +9 -0
- data/lib/couchrest/model/core_extensions/time_parsing.rb +66 -0
- data/lib/couchrest/model/design_doc.rb +128 -0
- data/lib/couchrest/model/designs.rb +91 -0
- data/lib/couchrest/model/designs/view.rb +513 -0
- data/lib/couchrest/model/dirty.rb +39 -0
- data/lib/couchrest/model/document_queries.rb +99 -0
- data/lib/couchrest/model/embeddable.rb +78 -0
- data/lib/couchrest/model/errors.rb +25 -0
- data/lib/couchrest/model/extended_attachments.rb +83 -0
- data/lib/couchrest/model/persistence.rb +178 -0
- data/lib/couchrest/model/properties.rb +228 -0
- data/lib/couchrest/model/property.rb +114 -0
- data/lib/couchrest/model/property_protection.rb +71 -0
- data/lib/couchrest/model/proxyable.rb +183 -0
- data/lib/couchrest/model/support/couchrest_database.rb +13 -0
- data/lib/couchrest/model/support/couchrest_design.rb +33 -0
- data/lib/couchrest/model/typecast.rb +154 -0
- data/lib/couchrest/model/validations.rb +80 -0
- data/lib/couchrest/model/validations/casted_model.rb +16 -0
- data/lib/couchrest/model/validations/locale/en.yml +5 -0
- data/lib/couchrest/model/validations/uniqueness.rb +69 -0
- data/lib/couchrest/model/views.rb +151 -0
- data/lib/couchrest/railtie.rb +24 -0
- data/lib/couchrest_model.rb +66 -0
- data/lib/rails/generators/couchrest_model.rb +16 -0
- data/lib/rails/generators/couchrest_model/config/config_generator.rb +18 -0
- data/lib/rails/generators/couchrest_model/config/templates/couchdb.yml +21 -0
- data/lib/rails/generators/couchrest_model/model/model_generator.rb +27 -0
- data/lib/rails/generators/couchrest_model/model/templates/model.rb +2 -0
- data/spec/.gitignore +1 -0
- data/spec/fixtures/attachments/README +3 -0
- data/spec/fixtures/attachments/couchdb.png +0 -0
- data/spec/fixtures/attachments/test.html +11 -0
- data/spec/fixtures/config/couchdb.yml +10 -0
- data/spec/fixtures/models/article.rb +36 -0
- data/spec/fixtures/models/base.rb +164 -0
- data/spec/fixtures/models/card.rb +19 -0
- data/spec/fixtures/models/cat.rb +23 -0
- data/spec/fixtures/models/client.rb +6 -0
- data/spec/fixtures/models/course.rb +27 -0
- data/spec/fixtures/models/event.rb +8 -0
- data/spec/fixtures/models/invoice.rb +14 -0
- data/spec/fixtures/models/key_chain.rb +5 -0
- data/spec/fixtures/models/membership.rb +4 -0
- data/spec/fixtures/models/person.rb +11 -0
- data/spec/fixtures/models/project.rb +6 -0
- data/spec/fixtures/models/question.rb +7 -0
- data/spec/fixtures/models/sale_entry.rb +9 -0
- data/spec/fixtures/models/sale_invoice.rb +14 -0
- data/spec/fixtures/models/service.rb +10 -0
- data/spec/fixtures/models/user.rb +22 -0
- data/spec/fixtures/views/lib.js +3 -0
- data/spec/fixtures/views/test_view/lib.js +3 -0
- data/spec/fixtures/views/test_view/only-map.js +4 -0
- data/spec/fixtures/views/test_view/test-map.js +3 -0
- data/spec/fixtures/views/test_view/test-reduce.js +3 -0
- data/spec/functional/validations_spec.rb +8 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/unit/active_model_lint_spec.rb +30 -0
- data/spec/unit/assocations_spec.rb +242 -0
- data/spec/unit/attachment_spec.rb +176 -0
- data/spec/unit/base_spec.rb +537 -0
- data/spec/unit/casted_spec.rb +72 -0
- data/spec/unit/class_proxy_spec.rb +167 -0
- data/spec/unit/collection_spec.rb +86 -0
- data/spec/unit/configuration_spec.rb +77 -0
- data/spec/unit/connection_spec.rb +148 -0
- data/spec/unit/core_extensions/time_parsing.rb +77 -0
- data/spec/unit/design_doc_spec.rb +241 -0
- data/spec/unit/designs/view_spec.rb +831 -0
- data/spec/unit/designs_spec.rb +134 -0
- data/spec/unit/dirty_spec.rb +436 -0
- data/spec/unit/embeddable_spec.rb +498 -0
- data/spec/unit/inherited_spec.rb +33 -0
- data/spec/unit/persistence_spec.rb +481 -0
- data/spec/unit/property_protection_spec.rb +192 -0
- data/spec/unit/property_spec.rb +481 -0
- data/spec/unit/proxyable_spec.rb +376 -0
- data/spec/unit/subclass_spec.rb +85 -0
- data/spec/unit/typecast_spec.rb +521 -0
- data/spec/unit/validations_spec.rb +140 -0
- data/spec/unit/view_spec.rb +367 -0
- metadata +301 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
class DesignModel < CouchRest::Model::Base
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
describe CouchRest::Model::Designs do
|
|
7
|
+
|
|
8
|
+
it "should accessable from model" do
|
|
9
|
+
DesignModel.respond_to?(:design).should be_true
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe "class methods" do
|
|
13
|
+
|
|
14
|
+
describe ".design" do
|
|
15
|
+
before :each do
|
|
16
|
+
@mapper = mock('DesignMapper')
|
|
17
|
+
@mapper.stub!(:create_view_method)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should instantiate a new DesignMapper" do
|
|
21
|
+
CouchRest::Model::Designs::DesignMapper.should_receive(:new).with(DesignModel).and_return(@mapper)
|
|
22
|
+
@mapper.should_receive(:create_view_method).with(:all)
|
|
23
|
+
@mapper.should_receive(:instance_eval)
|
|
24
|
+
DesignModel.design() { }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should allow methods to be called in mapper" do
|
|
28
|
+
@mapper.should_receive(:foo)
|
|
29
|
+
CouchRest::Model::Designs::DesignMapper.stub!(:new).and_return(@mapper)
|
|
30
|
+
DesignModel.design { foo }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should work even if a block is not provided" do
|
|
34
|
+
lambda { DesignModel.design }.should_not raise_error
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "default_per_page" do
|
|
40
|
+
it "should return 25 default" do
|
|
41
|
+
DesignModel.default_per_page.should eql(25)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe ".paginates_per" do
|
|
46
|
+
it "should set the default per page value" do
|
|
47
|
+
DesignModel.paginates_per(21)
|
|
48
|
+
DesignModel.default_per_page.should eql(21)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe "DesignMapper" do
|
|
54
|
+
|
|
55
|
+
before :all do
|
|
56
|
+
@klass = CouchRest::Model::Designs::DesignMapper
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "should initialize and set model" do
|
|
60
|
+
object = @klass.new(DesignModel)
|
|
61
|
+
object.send(:model).should eql(DesignModel)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe "#view" do
|
|
65
|
+
|
|
66
|
+
before :each do
|
|
67
|
+
@object = @klass.new(DesignModel)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "should call create method on view" do
|
|
71
|
+
CouchRest::Model::Designs::View.should_receive(:create).with(DesignModel, 'test', {})
|
|
72
|
+
@object.view('test')
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "should create a method on parent model" do
|
|
76
|
+
CouchRest::Model::Designs::View.stub!(:create)
|
|
77
|
+
@object.view('test_view')
|
|
78
|
+
DesignModel.should respond_to(:test_view)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "should create a method for view instance" do
|
|
82
|
+
CouchRest::Model::Designs::View.stub!(:create)
|
|
83
|
+
@object.should_receive(:create_view_method).with('test')
|
|
84
|
+
@object.view('test')
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
context "for model with auto_update_design_doc disabled " do
|
|
89
|
+
class ::DesignModelAutoUpdateDesignDocDisabled < ::CouchRest::Model::Base
|
|
90
|
+
self.auto_update_design_doc = false
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe "#view" do
|
|
94
|
+
before :each do
|
|
95
|
+
@object = @klass.new(DesignModelAutoUpdateDesignDocDisabled)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "does not attempt to create view" do
|
|
99
|
+
CouchRest::Model::Designs::View.should_not_receive(:create)
|
|
100
|
+
@object.view('test')
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe "#filter" do
|
|
106
|
+
|
|
107
|
+
before :each do
|
|
108
|
+
@object = @klass.new(DesignModel)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "should add the provided function to the design doc" do
|
|
112
|
+
@object.filter(:important, "function(doc, req) { return doc.priority == 'high'; }")
|
|
113
|
+
DesignModel.design_doc['filters'].should_not be_empty
|
|
114
|
+
DesignModel.design_doc['filters']['important'].should_not be_blank
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe "#create_view_method" do
|
|
120
|
+
before :each do
|
|
121
|
+
@object = @klass.new(DesignModel)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it "should create a method that returns view instance" do
|
|
125
|
+
CouchRest::Model::Designs::View.should_receive(:new).with(DesignModel, {}, 'test_view').and_return(nil)
|
|
126
|
+
@object.create_view_method('test_view')
|
|
127
|
+
DesignModel.test_view
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
end
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
class WithCastedModelMixin
|
|
4
|
+
include CouchRest::Model::CastedModel
|
|
5
|
+
property :name
|
|
6
|
+
property :details, Object, :default => {}
|
|
7
|
+
property :casted_attribute, WithCastedModelMixin
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class DirtyModel < CouchRest::Model::Base
|
|
11
|
+
use_database DB
|
|
12
|
+
|
|
13
|
+
property :casted_attribute, WithCastedModelMixin
|
|
14
|
+
property :title, :default => 'Sample Title'
|
|
15
|
+
property :details, Object, :default => { 'color' => 'blue' }
|
|
16
|
+
property :keywords, [String], :default => ['default-keyword']
|
|
17
|
+
property :sub_models do
|
|
18
|
+
property :title
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class DirtyUniqueIdModel < CouchRest::Model::Base
|
|
23
|
+
use_database DB
|
|
24
|
+
attr_accessor :code
|
|
25
|
+
unique_id :code
|
|
26
|
+
property :title, String, :default => "Sample Title"
|
|
27
|
+
timestamps!
|
|
28
|
+
|
|
29
|
+
def code; self['_id'] || @code; end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "Dirty" do
|
|
33
|
+
|
|
34
|
+
describe "changes" do
|
|
35
|
+
|
|
36
|
+
it "should return changes on an attribute" do
|
|
37
|
+
@card = Card.new(:first_name => "matt")
|
|
38
|
+
@card.first_name = "andrew"
|
|
39
|
+
@card.first_name_changed?.should be_true
|
|
40
|
+
@card.changes.should == { "first_name" => ["matt", "andrew"] }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe "save" do
|
|
46
|
+
|
|
47
|
+
it "should not save unchanged records" do
|
|
48
|
+
card_id = Card.create!(:first_name => "matt").id
|
|
49
|
+
@card = Card.find(card_id)
|
|
50
|
+
@card.database.should_not_receive(:save_doc)
|
|
51
|
+
@card.save
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "should save changed records" do
|
|
55
|
+
card_id = Card.create!(:first_name => "matt").id
|
|
56
|
+
@card = Card.find(card_id)
|
|
57
|
+
@card.first_name = "andrew"
|
|
58
|
+
@card.database.should_receive(:save_doc).and_return({"ok" => true})
|
|
59
|
+
@card.save
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe "changed?" do
|
|
65
|
+
|
|
66
|
+
# match activerecord behaviour
|
|
67
|
+
it "should report no changes on a new object with no attributes set" do
|
|
68
|
+
@card = Card.new
|
|
69
|
+
@card.changed?.should be_false
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should report no changes on a hash property with a default value" do
|
|
73
|
+
@obj = DirtyModel.new
|
|
74
|
+
@obj.details.changed?.should be_false
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# match activerecord behaviour
|
|
78
|
+
it "should report changes on a new object with attributes set" do
|
|
79
|
+
@card = Card.new(:first_name => "matt")
|
|
80
|
+
@card.changed?.should be_true
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "should report no changes on new object with 'unique_id' set" do
|
|
84
|
+
@obj = DirtyUniqueIdModel.new
|
|
85
|
+
@obj.changed?.should be_false
|
|
86
|
+
@obj.changes.should be_empty
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "should report no changes on objects fetched from the database" do
|
|
90
|
+
card_id = Card.create!(:first_name => "matt").id
|
|
91
|
+
@card = Card.find(card_id)
|
|
92
|
+
@card.changed?.should be_false
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should report changes if the record is modified" do
|
|
96
|
+
@card = Card.new
|
|
97
|
+
@card.first_name = "andrew"
|
|
98
|
+
@card.changed?.should be_true
|
|
99
|
+
@card.first_name_changed?.should be_true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "should report no changes for unmodified records" do
|
|
103
|
+
card_id = Card.create!(:first_name => "matt").id
|
|
104
|
+
@card = Card.find(card_id)
|
|
105
|
+
@card.first_name = "matt"
|
|
106
|
+
@card.changed?.should be_false
|
|
107
|
+
@card.first_name_changed?.should be_false
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "should report no changes after a new record has been saved" do
|
|
111
|
+
@card = Card.new(:first_name => "matt")
|
|
112
|
+
@card.save!
|
|
113
|
+
@card.changed?.should be_false
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "should report no changes after a record has been saved" do
|
|
117
|
+
card_id = Card.create!(:first_name => "matt").id
|
|
118
|
+
@card = Card.find(card_id)
|
|
119
|
+
@card.first_name = "andrew"
|
|
120
|
+
@card.save!
|
|
121
|
+
@card.changed?.should be_false
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# test changing list properties
|
|
125
|
+
|
|
126
|
+
it "should report changes if a list property is modified" do
|
|
127
|
+
cat_id = Cat.create!(:name => "Felix", :toys => [{:name => "Mouse"}]).id
|
|
128
|
+
@cat = Cat.find(cat_id)
|
|
129
|
+
@cat.toys = [{:name => "Feather"}]
|
|
130
|
+
@cat.changed?.should be_true
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "should report no changes if a list property is unmodified" do
|
|
134
|
+
cat_id = Cat.create!(:name => "Felix", :toys => [{:name => "Mouse"}]).id
|
|
135
|
+
@cat = Cat.find(cat_id)
|
|
136
|
+
@cat.toys = [{:name => "Mouse"}] # same as original list
|
|
137
|
+
@cat.changed?.should be_false
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# attachments
|
|
141
|
+
|
|
142
|
+
it "should report changes if an attachment is added" do
|
|
143
|
+
cat_id = Cat.create!(:name => "Felix", :toys => [{:name => "Mouse"}]).id
|
|
144
|
+
@file = File.open(FIXTURE_PATH + '/attachments/test.html')
|
|
145
|
+
@cat = Cat.find(cat_id)
|
|
146
|
+
@cat.create_attachment(:file => @file, :name => "my_attachment")
|
|
147
|
+
@cat.changed?.should be_true
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "should report changes if an attachment is deleted" do
|
|
151
|
+
@cat = Cat.create!(:name => "Felix", :toys => [{:name => "Mouse"}])
|
|
152
|
+
@file = File.open(FIXTURE_PATH + '/attachments/test.html')
|
|
153
|
+
@attachment_name = "my_attachment"
|
|
154
|
+
@cat.create_attachment(:file => @file, :name => @attachment_name)
|
|
155
|
+
@cat.save
|
|
156
|
+
@cat = Cat.find(@cat.id)
|
|
157
|
+
@cat.delete_attachment(@attachment_name)
|
|
158
|
+
@cat.changed?.should be_true
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# casted models
|
|
162
|
+
|
|
163
|
+
it "should report changes to casted models" do
|
|
164
|
+
@cat = Cat.create!(:name => "Felix", :favorite_toy => { :name => "Mouse" })
|
|
165
|
+
@cat = Cat.find(@cat.id)
|
|
166
|
+
@cat.favorite_toy.name = 'Feather'
|
|
167
|
+
@cat.changed?.should be_true
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "should report changes to casted model in array" do
|
|
171
|
+
@obj = Cat.create!(:name => 'felix', :toys => [{:name => "Catnip"}])
|
|
172
|
+
@obj = Cat.get(@obj.id)
|
|
173
|
+
@obj.toys.first.name.should eql('Catnip')
|
|
174
|
+
@obj.toys.first.changed?.should be_false
|
|
175
|
+
@obj.changed?.should be_false
|
|
176
|
+
@obj.toys.first.name = "Super Catnip"
|
|
177
|
+
@obj.toys.first.changed?.should be_true
|
|
178
|
+
@obj.changed?.should be_true
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "should report changes to anonymous casted models in array" do
|
|
182
|
+
@obj = DirtyModel.create!(:sub_models => [{:title => "Sample"}])
|
|
183
|
+
@obj = DirtyModel.get(@obj.id)
|
|
184
|
+
@obj.sub_models.first.title.should eql("Sample")
|
|
185
|
+
@obj.sub_models.first.changed?.should be_false
|
|
186
|
+
@obj.changed?.should be_false
|
|
187
|
+
@obj.sub_models.first.title = "Another Sample"
|
|
188
|
+
@obj.sub_models.first.changed?.should be_true
|
|
189
|
+
@obj.changed?.should be_true
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# casted arrays
|
|
193
|
+
|
|
194
|
+
def test_casted_array(change_expected)
|
|
195
|
+
obj = DirtyModel.create!
|
|
196
|
+
obj = DirtyModel.get(obj.id)
|
|
197
|
+
array = obj.keywords
|
|
198
|
+
yield array, obj
|
|
199
|
+
if change_expected
|
|
200
|
+
obj.changed?.should be_true
|
|
201
|
+
else
|
|
202
|
+
obj.changed?.should be_false
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def should_change_array
|
|
207
|
+
test_casted_array(true) { |a,b| yield a,b }
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def should_not_change_array
|
|
211
|
+
test_casted_array(false) { |a,b| yield a,b }
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it "should report changes if an array index is modified" do
|
|
215
|
+
should_change_array do |array, obj|
|
|
216
|
+
array[0] = "keyword"
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it "should report no changes if an array index is unmodified" do
|
|
221
|
+
should_not_change_array do |array, obj|
|
|
222
|
+
array[0] = array[0]
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
it "should report changes if an array is appended with <<" do
|
|
227
|
+
should_change_array do |array, obj|
|
|
228
|
+
array << 'keyword'
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
it "should report changes if an array is popped" do
|
|
233
|
+
should_change_array do |array, obj|
|
|
234
|
+
array.pop
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
it "should report changes if an array is popped after reload" do
|
|
239
|
+
should_change_array do |array, obj|
|
|
240
|
+
obj.reload
|
|
241
|
+
obj.keywords.pop
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
it "should report no changes if an empty array is popped" do
|
|
247
|
+
should_not_change_array do |array, obj|
|
|
248
|
+
array.clear
|
|
249
|
+
obj.save! # clears changes
|
|
250
|
+
array.pop
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
it "should report changes on deletion from an array" do
|
|
255
|
+
should_change_array do |array, obj|
|
|
256
|
+
array << "keyword"
|
|
257
|
+
obj.save!
|
|
258
|
+
array.delete_at(0)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
should_change_array do |array, obj|
|
|
262
|
+
array << "keyword"
|
|
263
|
+
obj.save!
|
|
264
|
+
array.delete("keyword")
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
it "should report changes on deletion from an array after reload" do
|
|
269
|
+
should_change_array do |array, obj|
|
|
270
|
+
array << "keyword"
|
|
271
|
+
obj.save!
|
|
272
|
+
obj.reload
|
|
273
|
+
array.delete_at(0)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
should_change_array do |array, obj|
|
|
277
|
+
array << "keyword"
|
|
278
|
+
obj.save!
|
|
279
|
+
obj.reload
|
|
280
|
+
array.delete("keyword")
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
it "should report no changes on deletion from an empty array" do
|
|
285
|
+
should_not_change_array do |array, obj|
|
|
286
|
+
array.clear
|
|
287
|
+
obj.save!
|
|
288
|
+
array.delete_at(0)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
should_not_change_array do |array, obj|
|
|
292
|
+
array.clear
|
|
293
|
+
obj.save!
|
|
294
|
+
array.delete("keyword")
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
it "should report changes if an array is pushed" do
|
|
299
|
+
should_change_array do |array, obj|
|
|
300
|
+
array.push("keyword")
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
it "should report changes if an array is shifted" do
|
|
305
|
+
should_change_array do |array, obj|
|
|
306
|
+
array.shift
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
it "should report no changes if an empty array is shifted" do
|
|
311
|
+
should_not_change_array do |array, obj|
|
|
312
|
+
array.clear
|
|
313
|
+
obj.save! # clears changes
|
|
314
|
+
array.shift
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
it "should report changes if an array is unshifted" do
|
|
319
|
+
should_change_array do |array, obj|
|
|
320
|
+
array.unshift("keyword")
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
it "should report changes if an array is cleared" do
|
|
325
|
+
should_change_array do |array, obj|
|
|
326
|
+
array.clear
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Object, {} (casted hash)
|
|
331
|
+
|
|
332
|
+
def test_casted_hash(change_expected)
|
|
333
|
+
obj = DirtyModel.create!
|
|
334
|
+
obj = DirtyModel.get(obj.id)
|
|
335
|
+
hash = obj.details
|
|
336
|
+
yield hash, obj
|
|
337
|
+
if change_expected
|
|
338
|
+
obj.changed?.should be_true
|
|
339
|
+
else
|
|
340
|
+
obj.changed?.should be_false
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def should_change_hash
|
|
345
|
+
test_casted_hash(true) { |a,b| yield a,b }
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def should_not_change_hash
|
|
349
|
+
test_casted_hash(false) { |a,b| yield a,b }
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
it "should report changes if a hash is modified" do
|
|
353
|
+
should_change_hash do |hash, obj|
|
|
354
|
+
hash['color'] = 'orange'
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
it "should report no changes if a hash is unmodified" do
|
|
359
|
+
should_not_change_hash do |hash, obj|
|
|
360
|
+
hash['color'] = hash['color']
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
it "should report changes when deleting from a hash" do
|
|
365
|
+
should_change_hash do |hash, obj|
|
|
366
|
+
hash.delete('color')
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
it "should report no changes when deleting a non existent key from a hash" do
|
|
371
|
+
should_not_change_hash do |hash, obj|
|
|
372
|
+
hash.delete('non-existent-key')
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
it "should report changes when clearing a hash" do
|
|
377
|
+
should_change_hash do |hash, obj|
|
|
378
|
+
hash.clear
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
it "should report changes when merging changes to a hash" do
|
|
383
|
+
should_change_hash do |hash, obj|
|
|
384
|
+
hash.merge!('foo' => 'bar')
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
it "should report no changes when merging no changes to a hash" do
|
|
389
|
+
should_not_change_hash do |hash, obj|
|
|
390
|
+
hash.merge!('color' => hash['color'])
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
it "should report changes when replacing hash content" do
|
|
395
|
+
should_change_hash do |hash, obj|
|
|
396
|
+
hash.replace('foo' => 'bar')
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
it "should report no changes when replacing hash content with same content" do
|
|
401
|
+
should_not_change_hash do |hash, obj|
|
|
402
|
+
hash.replace(hash)
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
it "should report changes when removing records with delete_if" do
|
|
407
|
+
should_change_hash do |hash, obj|
|
|
408
|
+
hash.delete_if { true }
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
it "should report no changes when removing no records with delete_if" do
|
|
413
|
+
should_not_change_hash do |hash, obj|
|
|
414
|
+
hash.delete_if { false }
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
if {}.respond_to?(:keep_if)
|
|
419
|
+
|
|
420
|
+
it "should report changes when removing records with keep_if" do
|
|
421
|
+
should_change_hash do |hash, obj|
|
|
422
|
+
hash.keep_if { false }
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
it "should report no changes when removing no records with keep_if" do
|
|
427
|
+
should_not_change_hash do |hash, obj|
|
|
428
|
+
hash.keep_if { true }
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
end
|