ardm 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +5 -13
  2. data/Gemfile +1 -2
  3. data/LICENSE +2 -2
  4. data/README.md +72 -16
  5. data/ardm.gemspec +1 -0
  6. data/lib/ardm.rb +2 -1
  7. data/lib/ardm/active_record.rb +8 -1
  8. data/lib/ardm/active_record/associations.rb +33 -4
  9. data/lib/ardm/active_record/base.rb +2 -0
  10. data/lib/ardm/active_record/collection.rb +5 -0
  11. data/lib/ardm/active_record/data_mapper_constant.rb +1 -0
  12. data/lib/ardm/active_record/data_mapper_constant_proxy.rb +24 -0
  13. data/lib/ardm/active_record/finalize.rb +18 -0
  14. data/lib/ardm/active_record/predicate_builder/array_handler.rb +10 -16
  15. data/lib/ardm/active_record/predicate_builder/rails3.rb +42 -15
  16. data/lib/ardm/active_record/predicate_builder/rails4.rb +39 -13
  17. data/lib/ardm/active_record/property.rb +24 -12
  18. data/lib/ardm/active_record/query.rb +9 -18
  19. data/lib/ardm/active_record/record.rb +54 -11
  20. data/lib/ardm/active_record/relation.rb +36 -6
  21. data/lib/ardm/active_record/repository.rb +6 -2
  22. data/lib/ardm/data_mapper.rb +2 -0
  23. data/lib/ardm/data_mapper/record.rb +3 -9
  24. data/lib/ardm/version.rb +1 -1
  25. data/spec/ardm/datamapper_constants_spec.rb +31 -0
  26. data/spec/fixtures/article.rb +2 -0
  27. data/spec/integration/api_key_spec.rb +3 -3
  28. data/spec/integration/bcrypt_hash_spec.rb +7 -7
  29. data/spec/integration/comma_separated_list_spec.rb +11 -11
  30. data/spec/integration/dirty_minder_spec.rb +23 -39
  31. data/spec/integration/enum_spec.rb +11 -11
  32. data/spec/integration/epoch_time_spec.rb +6 -6
  33. data/spec/integration/file_path_spec.rb +23 -23
  34. data/spec/integration/flag_spec.rb +11 -13
  35. data/spec/integration/ip_address_spec.rb +15 -15
  36. data/spec/integration/json_spec.rb +7 -7
  37. data/spec/integration/slug_spec.rb +6 -6
  38. data/spec/integration/uri_spec.rb +11 -11
  39. data/spec/integration/uuid_spec.rb +16 -16
  40. data/spec/integration/yaml_spec.rb +8 -8
  41. data/spec/public/model_spec.rb +193 -0
  42. data/spec/public/property/binary_spec.rb +4 -4
  43. data/spec/public/property/boolean_spec.rb +3 -3
  44. data/spec/public/property/class_spec.rb +2 -2
  45. data/spec/public/property/date_spec.rb +2 -2
  46. data/spec/public/property/date_time_spec.rb +2 -2
  47. data/spec/public/property/decimal_spec.rb +2 -2
  48. data/spec/public/property/discriminator_spec.rb +21 -20
  49. data/spec/public/property/float_spec.rb +2 -2
  50. data/spec/public/property/integer_spec.rb +2 -2
  51. data/spec/public/property/object_spec.rb +14 -13
  52. data/spec/public/property/serial_spec.rb +2 -2
  53. data/spec/public/property/string_spec.rb +2 -2
  54. data/spec/public/property/text_spec.rb +2 -2
  55. data/spec/public/property/time_spec.rb +2 -2
  56. data/spec/public/property_spec.rb +44 -48
  57. data/spec/public/resource_spec.rb +278 -0
  58. data/spec/schema.rb +33 -4
  59. data/spec/semipublic/property/boolean_spec.rb +5 -5
  60. data/spec/semipublic/property/class_spec.rb +3 -3
  61. data/spec/semipublic/property/date_spec.rb +8 -8
  62. data/spec/semipublic/property/date_time_spec.rb +9 -9
  63. data/spec/semipublic/property/decimal_spec.rb +16 -16
  64. data/spec/semipublic/property/float_spec.rb +16 -16
  65. data/spec/semipublic/property/integer_spec.rb +16 -16
  66. data/spec/semipublic/property/lookup_spec.rb +4 -4
  67. data/spec/semipublic/property/text_spec.rb +2 -2
  68. data/spec/semipublic/property/time_spec.rb +10 -10
  69. data/spec/semipublic/property_spec.rb +4 -4
  70. data/spec/shared/finder_shared_spec.rb +1151 -0
  71. data/spec/shared/flags_shared_spec.rb +6 -6
  72. data/spec/shared/identity_function_group.rb +1 -1
  73. data/spec/shared/public_property_spec.rb +26 -25
  74. data/spec/shared/resource_spec.rb +1218 -0
  75. data/spec/shared/semipublic_property_spec.rb +23 -22
  76. data/spec/spec_helper.rb +17 -0
  77. data/spec/unit/bcrypt_hash_spec.rb +15 -15
  78. data/spec/unit/csv_spec.rb +11 -11
  79. data/spec/unit/dirty_minder_spec.rb +3 -5
  80. data/spec/unit/enum_spec.rb +17 -17
  81. data/spec/unit/epoch_time_spec.rb +8 -8
  82. data/spec/unit/file_path_spec.rb +9 -9
  83. data/spec/unit/flag_spec.rb +9 -9
  84. data/spec/unit/ip_address_spec.rb +9 -9
  85. data/spec/unit/json_spec.rb +11 -11
  86. data/spec/unit/paranoid_boolean_spec.rb +19 -17
  87. data/spec/unit/paranoid_datetime_spec.rb +21 -19
  88. data/spec/unit/regexp_spec.rb +4 -4
  89. data/spec/unit/uri_spec.rb +8 -8
  90. data/spec/unit/yaml_spec.rb +9 -9
  91. metadata +35 -27
  92. data/lib/ardm/active_record/not_found.rb +0 -7
  93. data/lib/ardm/data_mapper/not_found.rb +0 -5
@@ -1,4 +1,4 @@
1
- share_examples_for "A property with flags" do
1
+ shared_examples "A property with flags" do
2
2
  before do
3
3
  %w[ @property_klass ].each do |ivar|
4
4
  raise "+#{ivar}+ should be defined in before block" unless instance_variable_defined?(ivar)
@@ -14,23 +14,23 @@ share_examples_for "A property with flags" do
14
14
 
15
15
  describe ".generated_classes" do
16
16
  it "should cache the generated class" do
17
- @property_klass.generated_classes[@flags].should_not be_nil
17
+ expect(@property_klass.generated_classes[@flags]).not_to be_nil
18
18
  end
19
19
  end
20
20
 
21
21
  it "should include :flags in accepted_options" do
22
- @property_klass.accepted_options.should include(:flags)
22
+ expect(@property_klass.accepted_options).to include(:flags)
23
23
  end
24
24
 
25
25
  it "should respond to :generated_classes" do
26
- @property_klass.should respond_to(:generated_classes)
26
+ expect(@property_klass).to respond_to(:generated_classes)
27
27
  end
28
28
 
29
29
  it "should respond to :flag_map" do
30
- @property.should respond_to(:flag_map)
30
+ expect(@property).to respond_to(:flag_map)
31
31
  end
32
32
 
33
33
  it "should be custom" do
34
- @property.custom?.should be(true)
34
+ expect(@property.custom?).to be(true)
35
35
  end
36
36
  end
@@ -1,5 +1,5 @@
1
1
  shared_examples_for "identity function" do
2
2
  it "returns value unchanged" do
3
- @result.should == @input
3
+ expect(@result).to eq(@input)
4
4
  end
5
5
  end
@@ -1,4 +1,4 @@
1
- share_examples_for 'A public Property' do
1
+ shared_examples 'A public Property' do
2
2
  before do
3
3
  %w[ @type @load_as @name @value @other_value ].each do |ivar|
4
4
  raise "+#{ivar}+ should be defined in before block" unless instance_variable_defined?(ivar)
@@ -8,6 +8,7 @@ share_examples_for 'A public Property' do
8
8
  class Article < Ardm::Record
9
9
  self.table_name = "articles"
10
10
  property :id, Serial
11
+ timestamps :at
11
12
  end
12
13
  end
13
14
 
@@ -49,7 +50,7 @@ share_examples_for 'A public Property' do
49
50
  end
50
51
 
51
52
  it "should not override the child's type setting" do
52
- @child_subtype.default.should eql(nil)
53
+ expect(@child_subtype.default).to eql(nil)
53
54
  end
54
55
  end
55
56
  end
@@ -63,15 +64,15 @@ share_examples_for 'A public Property' do
63
64
  describe "when provided :foo, :bar" do
64
65
  it "should add new options" do
65
66
  [@type, @subtype].each do |type|
66
- type.accepted_options.include?(:foo).should be(true)
67
- type.accepted_options.include?(:bar).should be(true)
67
+ expect(type.accepted_options.include?(:foo)).to be(true)
68
+ expect(type.accepted_options.include?(:bar)).to be(true)
68
69
  end
69
70
  end
70
71
 
71
72
  it "should create predefined option setters" do
72
73
  [@type, @subtype].each do |type|
73
- type.should respond_to(:foo)
74
- type.should respond_to(:bar)
74
+ expect(type).to respond_to(:foo)
75
+ expect(type).to respond_to(:bar)
75
76
  end
76
77
  end
77
78
 
@@ -83,13 +84,13 @@ share_examples_for 'A public Property' do
83
84
  end
84
85
 
85
86
  it "should set the pre-defined option values" do
86
- @property.options[:foo].should == true
87
- @property.options[:bar].should == 1
87
+ expect(@property.options[:foo]).to eq(true)
88
+ expect(@property.options[:bar]).to eq(1)
88
89
  end
89
90
 
90
91
  it "should ask the superclass for the value if unknown" do
91
- @subtype.foo.should == true
92
- @subtype.bar.should == 1
92
+ expect(@subtype.foo).to eq(true)
93
+ expect(@subtype.bar).to eq(1)
93
94
  end
94
95
  end
95
96
  end
@@ -97,20 +98,20 @@ share_examples_for 'A public Property' do
97
98
 
98
99
  describe ".descendants" do
99
100
  it "should include the sub-type" do
100
- @type.descendants.include?(SubType).should be(true)
101
+ expect(@type.descendants.include?(SubType)).to be(true)
101
102
  end
102
103
  end
103
104
 
104
105
  describe ".load_as" do
105
106
  it "should return the load_as" do
106
107
  [@type, @subtype].each do |type|
107
- type.load_as.should be(@load_as)
108
+ expect(type.load_as).to be(@load_as)
108
109
  end
109
110
  end
110
111
 
111
112
  it "should change the load_as class" do
112
113
  @subtype.load_as Object
113
- @subtype.load_as.should be(Object)
114
+ expect(@subtype.load_as).to be(Object)
114
115
  end
115
116
  end
116
117
  end
@@ -124,16 +125,16 @@ share_examples_for 'A public Property' do
124
125
  end
125
126
 
126
127
  it "should return #{value}" do
127
- @property.send(method).should be(value)
128
+ expect(@property.send(method)).to be(value)
128
129
  end
129
130
  end
130
131
  end
131
132
 
132
133
  describe "when created with :#{opt} => true and :required => true" do
133
134
  it "should fail with ArgumentError" do
134
- lambda {
135
+ expect {
135
136
  @property = @type.new(@model, @name, @options.merge(opt => true, :required => true))
136
- }.should raise_error(ArgumentError,
137
+ }.to raise_error(ArgumentError,
137
138
  "options[:required] cannot be mixed with :allow_nil or :allow_blank")
138
139
  end
139
140
  end
@@ -150,7 +151,7 @@ share_examples_for 'A public Property' do
150
151
  end
151
152
 
152
153
  it "should return #{value}" do
153
- @property.send(method).should be(value)
154
+ expect(@property.send(method)).to be(value)
154
155
  end
155
156
  end
156
157
  end
@@ -164,7 +165,7 @@ share_examples_for 'A public Property' do
164
165
  end
165
166
 
166
167
  it "should return true" do
167
- @property.lazy?.should be(true)
168
+ expect(@property.lazy?).to be(true)
168
169
  end
169
170
  end
170
171
 
@@ -174,7 +175,7 @@ share_examples_for 'A public Property' do
174
175
  end
175
176
 
176
177
  it "should return false" do
177
- @property.lazy?.should be(false)
178
+ expect(@property.lazy?).to be(false)
178
179
  end
179
180
  end
180
181
  end
@@ -187,19 +188,19 @@ share_examples_for 'A public Property' do
187
188
  context 'when provided the property class' do
188
189
  let(:klass) { @type }
189
190
 
190
- it { should be(true) }
191
+ it { is_expected.to be(true) }
191
192
  end
192
193
 
193
194
  context 'when provided the property superclass' do
194
195
  let(:klass) { @type.superclass }
195
196
 
196
- it { should be(false) }
197
+ it { is_expected.to be(false) }
197
198
  end
198
199
 
199
200
  context 'when provided the Ardm::Property class' do
200
201
  let(:klass) { Ardm::Property }
201
202
 
202
- it { should be(false) }
203
+ it { is_expected.to be(false) }
203
204
  end
204
205
  end
205
206
 
@@ -211,19 +212,19 @@ share_examples_for 'A public Property' do
211
212
  context 'when provided the property class' do
212
213
  let(:klass) { @type }
213
214
 
214
- it { should be(true) }
215
+ it { is_expected.to be(true) }
215
216
  end
216
217
 
217
218
  context 'when provided the property superclass' do
218
219
  let(:klass) { @type.superclass }
219
220
 
220
- it { should be(true) }
221
+ it { is_expected.to be(true) }
221
222
  end
222
223
 
223
224
  context 'when provided the Ardm::Property class' do
224
225
  let(:klass) { Ardm::Property }
225
226
 
226
- it { should be(true) }
227
+ it { is_expected.to be(true) }
227
228
  end
228
229
  end
229
230
  end
@@ -0,0 +1,1218 @@
1
+ shared_examples 'A public Resource' do
2
+ before :all do
3
+ @no_join = defined?(Ardm::Adapters::InMemoryAdapter) && @adapter.kind_of?(Ardm::Adapters::InMemoryAdapter) ||
4
+ defined?(Ardm::Adapters::YamlAdapter) && @adapter.kind_of?(Ardm::Adapters::YamlAdapter)
5
+
6
+ relationship = @user_model.relationships[:referrer]
7
+ @one_to_one_through = relationship.kind_of?(Ardm::Associations::OneToOne::Relationship) && relationship.respond_to?(:through)
8
+
9
+ @skip = @no_join && @one_to_one_through
10
+ end
11
+
12
+ before :all do
13
+ unless @skip
14
+ %w[ @user_model @user @comment_model ].each do |ivar|
15
+ raise "+#{ivar}+ should be defined in before block" unless instance_variable_get(ivar)
16
+ end
17
+ end
18
+ end
19
+
20
+ before do
21
+ skip if @skip
22
+ end
23
+
24
+ [ :==, :=== ].each do |method|
25
+ it { expect(@user).to respond_to(method) }
26
+
27
+ describe "##{method}" do
28
+ describe 'when comparing to the same resource' do
29
+ before :all do
30
+ @other = @user
31
+ @return = @user.__send__(method, @other)
32
+ end
33
+
34
+ it 'should return true' do
35
+ expect(@return).to be(true)
36
+ end
37
+ end
38
+
39
+ describe 'when comparing to an resource that does not respond to resource methods' do
40
+ before :all do
41
+ @other = Object.new
42
+ @return = @user.__send__(method, @other)
43
+ end
44
+
45
+ it 'should return false' do
46
+ expect(@return).to be(false)
47
+ end
48
+ end
49
+
50
+ describe 'when comparing to a resource with the same properties, but the model is a subclass' do
51
+ before :all do
52
+ rescue_if @skip do
53
+ @other = @author_model.new(@user.attributes)
54
+ @return = @user.__send__(method, @other)
55
+ end
56
+ end
57
+
58
+ it 'should return true' do
59
+ expect(@return).to be(true)
60
+ end
61
+ end
62
+
63
+ describe 'when comparing to a resource with the same repository, key and neither self or the other resource is dirty' do
64
+ before :all do
65
+ rescue_if @skip do
66
+ @other = @user_model.get(*@user.key)
67
+ @return = @user.__send__(method, @other)
68
+ end
69
+ end
70
+
71
+ it 'should return true' do
72
+ expect(@return).to be(true)
73
+ end
74
+ end
75
+
76
+ describe 'when comparing to a resource with the same repository, key but either self or the other resource is dirty' do
77
+ before :all do
78
+ rescue_if @skip do
79
+ @user.age = 20
80
+ @other = @user_model.get(*@user.key)
81
+ @return = @user.__send__(method, @other)
82
+ end
83
+ end
84
+
85
+ it 'should return false' do
86
+ expect(@return).to be(false)
87
+ end
88
+ end
89
+
90
+ describe 'when comparing to a resource with the same properties' do
91
+ before :all do
92
+ rescue_if @skip do
93
+ @other = @user_model.new(@user.attributes)
94
+ @return = @user.__send__(method, @other)
95
+ end
96
+ end
97
+
98
+ it 'should return true' do
99
+ expect(@return).to be(true)
100
+ end
101
+ end
102
+
103
+ with_alternate_adapter do
104
+ before :all do
105
+ if @user_model.respond_to?(:auto_migrate!)
106
+ # force the user model to be available in the alternate repository
107
+ @user_model.auto_migrate!(@adapter.name)
108
+ end
109
+ end
110
+
111
+ describe 'when comparing to a resource with a different repository, but the same properties' do
112
+ before :all do
113
+ rescue_if @skip do
114
+ @other = @repository.scope { @user_model.create(@user.attributes) }
115
+ @return = @user.__send__(method, @other)
116
+ end
117
+ end
118
+
119
+ it 'should return false' do
120
+ expect(@return).to be(false)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ it { expect(@user).to respond_to(:<=>) }
128
+
129
+ describe '#<=>' do
130
+ describe 'when the default order properties are equal with another resource' do
131
+ before :all do
132
+ rescue_if @skip && RUBY_VERSION < '1.9.2' do
133
+ @other = @user_model.new(:name => 'dbussink')
134
+ @return = @user <=> @other
135
+ end
136
+ end
137
+
138
+ it 'should return 0' do
139
+ expect(@return).to eq(0)
140
+ end
141
+ end
142
+
143
+ describe 'when the default order property values are sorted before another resource' do
144
+ before :all do
145
+ rescue_if @skip && RUBY_VERSION < '1.9.2' do
146
+ @other = @user_model.new(:name => 'c')
147
+ @return = @user <=> @other
148
+ end
149
+ end
150
+
151
+ it 'should return 1' do
152
+ expect(@return).to eq(1)
153
+ end
154
+ end
155
+
156
+ describe 'when the default order property values are sorted after another resource' do
157
+ before :all do
158
+ rescue_if @skip && RUBY_VERSION < '1.9.2' do
159
+ @other = @user_model.new(:name => 'e')
160
+ @return = @user <=> @other
161
+ end
162
+ end
163
+
164
+ it 'should return -1' do
165
+ expect(@return).to eq(-1)
166
+ end
167
+ end
168
+
169
+ describe 'when comparing an unrelated type of Object' do
170
+ it 'should raise an exception' do
171
+ expect { @user <=> @comment_model.new }.to raise_error(ArgumentError, "Cannot compare a #{@comment_model} instance with a #{@user_model} instance")
172
+ end
173
+ end
174
+ end
175
+
176
+ it { expect(@user).to respond_to(:attribute_get) }
177
+
178
+ describe '#attribute_get' do
179
+
180
+ it { expect(@user.attribute_get(:name)).to eq('dbussink') }
181
+
182
+ end
183
+
184
+ it { expect(@user).to respond_to(:attribute_set) }
185
+
186
+ describe '#attribute_set' do
187
+
188
+ before { @user.attribute_set(:name, 'dkubb') }
189
+
190
+ it { expect(@user.name).to eq('dkubb') }
191
+
192
+ end
193
+
194
+ it { expect(@user).to respond_to(:attributes) }
195
+
196
+ describe '#attributes' do
197
+ describe 'with a new resource' do
198
+ before :all do
199
+ rescue_if @skip do
200
+ @user = @user.model.new
201
+ end
202
+ end
203
+
204
+ it 'should return the expected values' do
205
+ expect(@user.attributes).to eq({})
206
+ end
207
+ end
208
+
209
+ describe 'with a new resource with a property set' do
210
+ before :all do
211
+ rescue_if @skip do
212
+ @user = @user.model.new
213
+ @user.name = 'dbussink'
214
+ end
215
+ end
216
+
217
+ it 'should return the expected values' do
218
+ expect(@user.attributes).to eq({:name => 'dbussink'})
219
+ end
220
+ end
221
+
222
+ describe 'with a saved resource' do
223
+ it 'should return the expected values' do
224
+ expect(Ardm::Ext::Hash.only(@user.attributes, :name, :description, :age)).to eq(
225
+ { :name => 'dbussink', :description => 'Test', :age => 25 }
226
+ )
227
+ end
228
+ end
229
+ end
230
+
231
+ it { expect(@user).to respond_to(:attributes=) }
232
+
233
+ describe '#attributes=' do
234
+ describe 'when a public mutator is specified' do
235
+ before :all do
236
+ rescue_if @skip do
237
+ @user.attributes = { :name => 'dkubb' }
238
+ end
239
+ end
240
+
241
+ it 'should set the value' do
242
+ expect(@user.name).to eql('dkubb')
243
+ end
244
+ end
245
+
246
+ describe 'when a non-public mutator is specified' do
247
+ it 'should raise an exception' do
248
+ expect {
249
+ @user.attributes = { :admin => true }
250
+ }.to raise_error(ArgumentError, "The attribute \'admin\' is not accessible in #{@user_model}")
251
+ end
252
+ end
253
+ end
254
+
255
+ [ :destroy, :destroy! ].each do |method|
256
+ it { expect(@user).to respond_to(:destroy) }
257
+
258
+ describe "##{method}" do
259
+ describe 'on a single resource' do
260
+ before :all do
261
+ @resource = @user_model.create(:name => 'hacker', :age => 20, :comment => @comment)
262
+
263
+ @return = @resource.__send__(method)
264
+ end
265
+
266
+ it 'should successfully remove a resource' do
267
+ expect(@return).to be(true)
268
+ end
269
+
270
+ it 'should mark the destroyed resource as readonly' do
271
+ expect(@resource).to be_readonly
272
+ end
273
+
274
+ it "should return true when calling #{method} on a destroyed resource" do
275
+ expect(@resource.__send__(method)).to be(true)
276
+ end
277
+
278
+ it 'should remove resource from persistent storage' do
279
+ expect(@user_model.get(*@resource.key)).to be_nil
280
+ end
281
+ end
282
+
283
+ describe 'with has relationship resources' do
284
+ it 'should raise an exception'
285
+ end
286
+ end
287
+ end
288
+
289
+ it { expect(@user).to respond_to(:dirty?) }
290
+
291
+ describe '#dirty?' do
292
+ describe 'on a record, with dirty attributes' do
293
+ before { @user.age = 100 }
294
+
295
+ it { expect(@user).to be_dirty }
296
+ end
297
+
298
+ describe 'on a record, with no dirty attributes, and dirty parents' do
299
+ before :all do
300
+ rescue_if @skip do
301
+ expect(@user).not_to be_dirty
302
+
303
+ parent = @user.parent = @user_model.new(:name => 'Parent')
304
+ expect(parent).to be_dirty
305
+ end
306
+ end
307
+
308
+ it { expect(@user).to be_dirty }
309
+ end
310
+
311
+ describe 'on a record, with no dirty attributes, and dirty children' do
312
+ before :all do
313
+ rescue_if @skip do
314
+ expect(@user).not_to be_dirty
315
+
316
+ child = @user.children.new(:name => 'Child')
317
+ expect(child).to be_dirty
318
+ end
319
+ end
320
+
321
+ it { expect(@user).to be_dirty }
322
+ end
323
+
324
+ describe 'on a record, with no dirty attributes, and dirty siblings' do
325
+ before :all do
326
+ rescue_if @skip do
327
+ expect(@user).not_to be_dirty
328
+
329
+ parent = @user_model.create(:name => 'Parent', :comment => @comment)
330
+ expect(parent).not_to be_dirty
331
+
332
+ @user.update(:parent => parent)
333
+ expect(@user).not_to be_dirty
334
+
335
+ sibling = parent.children.new(:name => 'Sibling')
336
+ expect(sibling).to be_dirty
337
+ expect(parent).to be_dirty
338
+ end
339
+ end
340
+
341
+ it { expect(@user).not_to be_dirty }
342
+ end
343
+
344
+ describe 'on a saved record, with no dirty attributes' do
345
+ it { expect(@user).not_to be_dirty }
346
+ end
347
+
348
+ describe 'on a new record, with no dirty attributes, no default attributes, and no identity field' do
349
+ before { @user = @user_model.new }
350
+
351
+ it { expect(@user).not_to be_dirty }
352
+ end
353
+
354
+ describe 'on a new record, with no dirty attributes, no default attributes, and an identity field' do
355
+ before { @comment = @comment_model.new }
356
+
357
+ it { expect(@comment).to be_dirty }
358
+ end
359
+
360
+ describe 'on a new record, with no dirty attributes, default attributes, and no identity field' do
361
+ before { @default = Default.new }
362
+
363
+ it { expect(@default).to be_dirty }
364
+ end
365
+
366
+ describe 'on a record with itself as a parent (circular dependency)' do
367
+ before :all do
368
+ rescue_if @skip do
369
+ @user.parent = @user
370
+ end
371
+ end
372
+
373
+ it 'should not raise an exception' do
374
+ expect {
375
+ expect(@user.dirty?).to be(true)
376
+ }.not_to raise_error
377
+ end
378
+ end
379
+
380
+ describe 'on a record with itself as a child (circular dependency)' do
381
+ before :all do
382
+ rescue_if @skip do
383
+ @user.children = [ @user ]
384
+ end
385
+ end
386
+
387
+ it 'should not raise an exception' do
388
+ expect {
389
+ expect(@user.dirty?).to be(true)
390
+ }.not_to raise_error
391
+ end
392
+ end
393
+
394
+ describe 'on a record with a parent as a child (circular dependency)' do
395
+ before :all do
396
+ rescue_if @skip do
397
+ @user.children = [ @user.parent = @user_model.new(:name => 'Parent', :comment => @comment) ]
398
+ expect(@user.save).to be(true)
399
+ end
400
+ end
401
+
402
+ it 'should not raise an exception' do
403
+ expect {
404
+ expect(@user.dirty?).to be(true)
405
+ }.not_to raise_error
406
+ end
407
+ end
408
+ end
409
+
410
+ it { expect(@user).to respond_to(:eql?) }
411
+
412
+ describe '#eql?' do
413
+ describe 'when comparing to the same resource' do
414
+ before :all do
415
+ @other = @user
416
+ @return = @user.eql?(@other)
417
+ end
418
+
419
+ it 'should return true' do
420
+ expect(@return).to be(true)
421
+ end
422
+ end
423
+
424
+ describe 'when comparing to an resource that does not respond to model' do
425
+ before :all do
426
+ @other = Object.new
427
+ @return = @user.eql?(@other)
428
+ end
429
+
430
+ it 'should return false' do
431
+ expect(@return).to be(false)
432
+ end
433
+ end
434
+
435
+ describe 'when comparing to a resource with the same properties, but the model is a subclass' do
436
+ before :all do
437
+ rescue_if @skip do
438
+ @other = @author_model.new(@user.attributes)
439
+ @return = @user.eql?(@other)
440
+ end
441
+ end
442
+
443
+ it 'should return false' do
444
+ expect(@return).to be(false)
445
+ end
446
+ end
447
+
448
+ describe 'when comparing to a resource with a different key' do
449
+ before :all do
450
+ @other = @user_model.create(:name => 'dkubb', :age => 33, :comment => @comment)
451
+ @return = @user.eql?(@other)
452
+ end
453
+
454
+ it 'should return false' do
455
+ expect(@return).to be(false)
456
+ end
457
+ end
458
+
459
+ describe 'when comparing to a resource with the same repository, key and neither self or the other resource is dirty' do
460
+ before :all do
461
+ rescue_if @skip do
462
+ @other = @user_model.get(*@user.key)
463
+ @return = @user.eql?(@other)
464
+ end
465
+ end
466
+
467
+ it 'should return true' do
468
+ expect(@return).to be(true)
469
+ end
470
+ end
471
+
472
+ describe 'when comparing to a resource with the same repository, key but either self or the other resource is dirty' do
473
+ before :all do
474
+ rescue_if @skip do
475
+ @user.age = 20
476
+ @other = @user_model.get(*@user.key)
477
+ @return = @user.eql?(@other)
478
+ end
479
+ end
480
+
481
+ it 'should return false' do
482
+ expect(@return).to be(false)
483
+ end
484
+ end
485
+
486
+ describe 'when comparing to a resource with the same properties' do
487
+ before :all do
488
+ rescue_if @skip do
489
+ @other = @user_model.new(@user.attributes)
490
+ @return = @user.eql?(@other)
491
+ end
492
+ end
493
+
494
+ it 'should return true' do
495
+ expect(@return).to be(true)
496
+ end
497
+ end
498
+
499
+ with_alternate_adapter do
500
+ before :all do
501
+ if @user_model.respond_to?(:auto_migrate!)
502
+ # force the user model to be available in the alternate repository
503
+ @user_model.auto_migrate!(@adapter.name)
504
+ end
505
+ end
506
+
507
+ describe 'when comparing to a resource with a different repository, but the same properties' do
508
+ before :all do
509
+ rescue_if @skip do
510
+ @other = @repository.scope { @user_model.create(@user.attributes) }
511
+ @return = @user.eql?(@other)
512
+ end
513
+ end
514
+
515
+ it 'should return false' do
516
+ expect(@return).to be(false)
517
+ end
518
+ end
519
+ end
520
+ end
521
+
522
+ it { expect(@user).to respond_to(:inspect) }
523
+
524
+ describe '#inspect' do
525
+
526
+ before :all do
527
+ rescue_if @skip do
528
+ @user = @user_model.get(*@user.key)
529
+ @inspected = @user.inspect
530
+ end
531
+ end
532
+
533
+ it { expect(@inspected).to match(/^#<#{@user_model}/) }
534
+
535
+ it { expect(@inspected).to match(/name="dbussink"/) }
536
+
537
+ it { expect(@inspected).to match(/age=25/) }
538
+
539
+ it { expect(@inspected).to match(/description=<not loaded>/) }
540
+
541
+ end
542
+
543
+ it { expect(@user).to respond_to(:key) }
544
+
545
+ describe '#key' do
546
+
547
+ before :all do
548
+ rescue_if @skip do
549
+ @key = @user.key
550
+ @user.name = 'dkubb'
551
+ end
552
+ end
553
+
554
+ it { expect(@key).to be_kind_of(Array) }
555
+
556
+ it 'should always return the key value persisted in the back end' do
557
+ expect(@key.first).to eql("dbussink")
558
+ end
559
+
560
+ it { expect(@user.key).to eql(@key) }
561
+
562
+ end
563
+
564
+ it { expect(@user).to respond_to(:new?) }
565
+
566
+ describe '#new?' do
567
+
568
+ describe 'on an existing record' do
569
+
570
+ it { expect(@user).not_to be_new }
571
+
572
+ end
573
+
574
+ describe 'on a new record' do
575
+
576
+ before { @user = @user_model.new }
577
+
578
+ it { expect(@user).to be_new }
579
+
580
+ end
581
+
582
+ end
583
+
584
+ it { expect(@user).to respond_to(:reload) }
585
+
586
+ describe '#reload' do
587
+ before do
588
+ # reset the user for each spec
589
+ rescue_if(@skip) do
590
+ @user.update(:name => 'dbussink', :age => 25, :description => 'Test')
591
+ end
592
+ end
593
+
594
+ subject { rescue_if(@skip) { @user.reload } }
595
+
596
+ describe 'on a resource not persisted' do
597
+ before do
598
+ @user.attributes = { :description => 'Changed' }
599
+ end
600
+
601
+ it { is_expected.to be_kind_of(Ardm::Resource) }
602
+
603
+ it { is_expected.to equal(@user) }
604
+
605
+ it { is_expected.to be_clean }
606
+
607
+ it 'reset the changed attributes' do
608
+ expect(method(:subject)).to change(@user, :description).from('Changed').to('Test')
609
+ end
610
+ end
611
+
612
+ describe 'on a resource where the key is changed, but not persisted' do
613
+ before do
614
+ @user.attributes = { :name => 'dkubb' }
615
+ end
616
+
617
+ it { is_expected.to be_kind_of(Ardm::Resource) }
618
+
619
+ it { is_expected.to equal(@user) }
620
+
621
+ it { is_expected.to be_clean }
622
+
623
+ it 'reset the changed attributes' do
624
+ expect(method(:subject)).to change(@user, :name).from('dkubb').to('dbussink')
625
+ end
626
+ end
627
+
628
+ describe 'on a resource that is changed outside another resource' do
629
+ before do
630
+ rescue_if @skip do
631
+ @user.dup.update(:description => 'Changed')
632
+ end
633
+ end
634
+
635
+ it { is_expected.to be_kind_of(Ardm::Resource) }
636
+
637
+ it { is_expected.to equal(@user) }
638
+
639
+ it { is_expected.to be_clean }
640
+
641
+ it 'should reload the resource from the data store' do
642
+ expect(method(:subject)).to change(@user, :description).from('Test').to('Changed')
643
+ end
644
+ end
645
+
646
+ describe 'on an anonymous resource' do
647
+ before do
648
+ rescue_if @skip do
649
+ @user = @user.model.first(:fields => [ :description ])
650
+ expect(@user.description).to eq('Test')
651
+ end
652
+ end
653
+
654
+ it { is_expected.to be_kind_of(Ardm::Resource) }
655
+
656
+ it { is_expected.to equal(@user) }
657
+
658
+ it { is_expected.to be_clean }
659
+
660
+ it 'should not reload any attributes' do
661
+ expect(method(:subject)).not_to change(@user, :attributes)
662
+ end
663
+ end
664
+ end
665
+
666
+ it { expect(@user).to respond_to(:readonly?) }
667
+
668
+ describe '#readonly?' do
669
+ describe 'on a new resource' do
670
+ before :all do
671
+ rescue_if @skip do
672
+ @user = @user.model.new
673
+ end
674
+ end
675
+
676
+ it 'should return false' do
677
+ expect(@user.readonly?).to be(false)
678
+ end
679
+ end
680
+
681
+ describe 'on a saved resource' do
682
+ before :all do
683
+ rescue_if @skip do
684
+ expect(@user).to be_saved
685
+ end
686
+ end
687
+
688
+ it 'should return false' do
689
+ expect(@user.readonly?).to be(false)
690
+ end
691
+ end
692
+
693
+ describe 'on a destroyed resource' do
694
+ before :all do
695
+ rescue_if @skip do
696
+ expect(@user.destroy).to be(true)
697
+ end
698
+ end
699
+
700
+ it 'should return true' do
701
+ expect(@user.readonly?).to be(true)
702
+ end
703
+ end
704
+
705
+ describe 'on an anonymous resource' do
706
+ before :all do
707
+ rescue_if @skip do
708
+ # load the user without a key
709
+ @user = @user.model.first(:fields => @user_model.properties - @user_model.key)
710
+ end
711
+ end
712
+
713
+ it 'should return true' do
714
+ expect(@user.readonly?).to be(true)
715
+ end
716
+ end
717
+ end
718
+
719
+ [ :save, :save! ].each do |method|
720
+ it { expect(@user).to respond_to(method) }
721
+
722
+ describe "##{method}" do
723
+ before :all do
724
+ @user_model.class_eval do
725
+ attr_accessor :save_hook_call_count
726
+
727
+ before :save do
728
+ @save_hook_call_count ||= 0
729
+ @save_hook_call_count += 1
730
+ end
731
+ end
732
+ end
733
+
734
+ describe 'on a new, not dirty resource' do
735
+ before :all do
736
+ @user = @user_model.new
737
+ @return = @user.__send__(method)
738
+ end
739
+
740
+ it 'should return false' do
741
+ expect(@return).to be(false)
742
+ end
743
+
744
+ it 'should call save hook expected number of times' do
745
+ expect(@user.save_hook_call_count).to be_nil
746
+ end
747
+ end
748
+
749
+ describe 'on a not new, not dirty resource' do
750
+ before :all do
751
+ rescue_if @skip do
752
+ @return = @user.__send__(method)
753
+ end
754
+ end
755
+
756
+ it 'should return true even when resource is not dirty' do
757
+ expect(@return).to be(true)
758
+ end
759
+
760
+ it 'should call save hook expected number of times' do
761
+ expect(@user.save_hook_call_count).to be_nil
762
+ end
763
+ end
764
+
765
+ describe 'on a not new, dirty resource' do
766
+ before :all do
767
+ rescue_if @skip do
768
+ @user.age = 26
769
+ @return = @user.__send__(method)
770
+ end
771
+ end
772
+
773
+ it 'should save a resource succesfully when dirty' do
774
+ expect(@return).to be(true)
775
+ end
776
+
777
+ it 'should actually store the changes to persistent storage' do
778
+ expect(@user.attributes).to eq(@user.reload.attributes)
779
+ end
780
+
781
+ it 'should call save hook expected number of times' do
782
+ expect(@user.save_hook_call_count).to eq(method == :save ? 1 : nil)
783
+ end
784
+ end
785
+
786
+ describe 'on a new, invalid resource' do
787
+ before :all do
788
+ @user = @user_model.new(:name => nil)
789
+ expect { @user.__send__(method) }.to(raise_error(Ardm::Property::InvalidValueError) do |error|
790
+ expect(error.property).to eq(@user_model.properties[:name])
791
+ end)
792
+ end
793
+
794
+ it 'should call save hook expected number of times' do
795
+ expect(@user.save_hook_call_count).to eq(method == :save ? 1 : nil)
796
+ end
797
+ end
798
+
799
+ describe 'on a dirty invalid resource' do
800
+ before :all do
801
+ rescue_if @skip do
802
+ @user.name = nil
803
+ end
804
+ end
805
+
806
+ it 'should not save an invalid resource' do
807
+ expect { @user.__send__(method) }.to(raise_error(Ardm::Property::InvalidValueError) do |error|
808
+ expect(error.property).to eq(@user_model.properties[:name])
809
+ end)
810
+ end
811
+
812
+ it 'should call save hook expected number of times' do
813
+ expect(@user.save_hook_call_count).to eq(method == :save ? 1 : nil)
814
+ end
815
+ end
816
+
817
+ describe 'with new resources in a has relationship' do
818
+ before do
819
+ rescue_if 'TODO: fix for one to one association', !@user.respond_to?(:comments) do
820
+ @initial_comments = @user.comments.size
821
+ @first_comment = @user.comments.new(:body => "DM is great!")
822
+ @second_comment = @comment_model.new(:user => @user, :body => "is it really?")
823
+ @return = @user.__send__(method)
824
+ end
825
+ end
826
+
827
+ it 'should save resource' do
828
+ pending_if !@user.respond_to?(:comments)
829
+ expect(@return).to be(true)
830
+ end
831
+
832
+ it 'should save the first resource created through new' do
833
+ pending_if !@user.respond_to?(:comments)
834
+ expect(@first_comment.new?).to be(false)
835
+ end
836
+
837
+ it 'should save the correct foreign key for the first resource' do
838
+ pending_if !@user.respond_to?(:comments)
839
+ expect(@first_comment.user).to eql(@user)
840
+ end
841
+
842
+ it 'should save the second resource created through the constructor' do
843
+ skip "Changing a belongs_to parent should add the resource to the correct association"
844
+ expect(@second_comment.new?).to be(false)
845
+ end
846
+
847
+ it 'should save the correct foreign key for the second resource' do
848
+ pending_if !@user.respond_to?(:comments)
849
+ expect(@second_comment.user).to eql(@user)
850
+ end
851
+
852
+ it 'should create 2 extra resources in persistent storage' do
853
+ skip "Changing a belongs_to parent should add the resource to the correct association"
854
+ expect(@user.comments.size).to eq(@initial_comments + 2)
855
+ end
856
+ end
857
+
858
+ describe 'with dirty resources in a has relationship' do
859
+ before :all do
860
+ rescue_if 'TODO: fix for one to one association', !@user.respond_to?(:comments) do
861
+ @first_comment = @user.comments.create(:body => 'DM is great!')
862
+ @second_comment = @comment_model.create(:user => @user, :body => 'is it really?')
863
+
864
+ @first_comment.body = 'It still has rough edges'
865
+ @second_comment.body = 'But these cool specs help fixing that'
866
+ @second_comment.user = @user_model.create(:name => 'dkubb')
867
+
868
+ @return = @user.__send__(method)
869
+ end
870
+ end
871
+
872
+ it 'should return true' do
873
+ pending_if !@user.respond_to?(:comments)
874
+ expect(@return).to be(true)
875
+ end
876
+
877
+ it 'should not be dirty' do
878
+ expect(@user).not_to be_dirty
879
+ end
880
+
881
+ it 'should have saved the first child resource' do
882
+ pending_if !@user.respond_to?(:comments)
883
+ expect(@first_comment.model.get(*@first_comment.key).body).to eq('It still has rough edges')
884
+ end
885
+
886
+ it 'should not have saved the second child resource' do
887
+ pending_if !@user.respond_to?(:comments)
888
+ expect(@second_comment.model.get(*@second_comment.key).body).to eq('is it really?')
889
+ end
890
+ end
891
+
892
+ describe 'with a new dependency' do
893
+ before :all do
894
+ @first_comment = @comment_model.new(:body => "DM is great!")
895
+ @first_comment.user = @user_model.new(:name => 'dkubb')
896
+ end
897
+
898
+ it 'should not raise an exception when saving the resource' do
899
+ skip
900
+ expect { expect(@first_comment.send(method)).to be(false) }.not_to raise_error
901
+ end
902
+ end
903
+
904
+ describe 'with a dirty dependency' do
905
+ before :all do
906
+ rescue_if @skip do
907
+ @user.name = 'dbussink-the-second'
908
+
909
+ @first_comment = @comment_model.new(:body => 'DM is great!')
910
+ @first_comment.user = @user
911
+
912
+ @return = @first_comment.__send__(method)
913
+ end
914
+ end
915
+
916
+ it 'should succesfully save the resource' do
917
+ expect(@return).to be(true)
918
+ end
919
+
920
+ it 'should not have a dirty dependency' do
921
+ expect(@user).not_to be_dirty
922
+ end
923
+
924
+ it 'should succesfully save the dependency' do
925
+ expect(@user.name).to eq(@user_model.get(*@user.key).name)
926
+ end
927
+ end
928
+
929
+ describe 'with a new resource and new relations' do
930
+ before :all do
931
+ @article = @article_model.new(:body => "Main")
932
+ rescue_if 'TODO: fix for one to one association', (!@article.respond_to?(:paragraphs)) do
933
+ @paragraph = @article.paragraphs.new(:text => 'Content')
934
+
935
+ @article.__send__(method)
936
+ end
937
+ end
938
+
939
+ it 'should not be dirty' do
940
+ pending_if !@article.respond_to?(:paragraphs)
941
+ expect(@article).not_to be_dirty
942
+ end
943
+
944
+ it 'should not be dirty' do
945
+ pending_if !@article.respond_to?(:paragraphs)
946
+ expect(@paragraph).not_to be_dirty
947
+ end
948
+
949
+ it 'should set the related resource' do
950
+ pending_if !@article.respond_to?(:paragraphs)
951
+ expect(@paragraph.article).to eq(@article)
952
+ end
953
+
954
+ it 'should set the foreign key properly' do
955
+ pending_if !@article.respond_to?(:paragraphs)
956
+ expect(@paragraph.article_id).to eq(@article.id)
957
+ end
958
+ end
959
+
960
+ describe 'with a dirty resource with a changed key' do
961
+ before :all do
962
+ rescue_if @skip do
963
+ @original_key = @user.key
964
+ @user.name = 'dkubb'
965
+ @return = @user.__send__(method)
966
+ end
967
+ end
968
+
969
+ it 'should save a resource succesfully when dirty' do
970
+ expect(@return).to be(true)
971
+ end
972
+
973
+ it 'should actually store the changes to persistent storage' do
974
+ expect(@user.name).to eq(@user.reload.name)
975
+ end
976
+
977
+ it 'should update the identity map' do
978
+ expect(@user.repository.identity_map(@user_model)).to have_key(%w[ dkubb ])
979
+ end
980
+
981
+ it 'should remove the old entry from the identity map' do
982
+ expect(@user.repository.identity_map(@user_model)).not_to have_key(@original_key)
983
+ end
984
+ end
985
+
986
+ describe 'on a new resource with unsaved parent and grandparent' do
987
+ before :all do
988
+ @grandparent = @user_model.new(:name => 'dkubb', :comment => @comment)
989
+ @parent = @user_model.new(:name => 'ashleymoran', :comment => @comment, :referrer => @grandparent)
990
+ @child = @user_model.new(:name => 'mrship', :comment => @comment, :referrer => @parent)
991
+
992
+ @response = @child.__send__(method)
993
+ end
994
+
995
+ it 'should return true' do
996
+ expect(@response).to be(true)
997
+ end
998
+
999
+ it 'should save the child' do
1000
+ expect(@child).to be_saved
1001
+ end
1002
+
1003
+ it 'should save the parent' do
1004
+ expect(@parent).to be_saved
1005
+ end
1006
+
1007
+ it 'should save the grandparent' do
1008
+ expect(@grandparent).to be_saved
1009
+ end
1010
+
1011
+ it 'should relate the child to the parent' do
1012
+ expect(@child.model.get(*@child.key).referrer).to eq(@parent)
1013
+ end
1014
+
1015
+ it 'should relate the parent to the grandparent' do
1016
+ expect(@parent.model.get(*@parent.key).referrer).to eq(@grandparent)
1017
+ end
1018
+
1019
+ it 'should relate the grandparent to nothing' do
1020
+ expect(@grandparent.model.get(*@grandparent.key).referrer).to be_nil
1021
+ end
1022
+ end
1023
+
1024
+ describe 'on a destroyed resource' do
1025
+ before :all do
1026
+ rescue_if @skip do
1027
+ @user.destroy
1028
+ end
1029
+ end
1030
+
1031
+ it 'should raise an exception' do
1032
+ expect {
1033
+ @user.__send__(method)
1034
+ }.to raise_error(Ardm::PersistenceError, "#{@user.model}##{method} cannot be called on a destroyed resource")
1035
+ end
1036
+ end
1037
+
1038
+ describe 'on a record with itself as a parent (circular dependency)' do
1039
+ before :all do
1040
+ rescue_if @skip do
1041
+ @user.parent = @user
1042
+ end
1043
+ end
1044
+
1045
+ it 'should not raise an exception' do
1046
+ expect {
1047
+ expect(@user.__send__(method)).to be(true)
1048
+ }.not_to raise_error
1049
+ end
1050
+ end
1051
+
1052
+ describe 'on a record with itself as a child (circular dependency)' do
1053
+ before :all do
1054
+ rescue_if @skip do
1055
+ @user.children = [ @user ]
1056
+ end
1057
+ end
1058
+
1059
+ it 'should not raise an exception' do
1060
+ expect {
1061
+ expect(@user.__send__(method)).to be(true)
1062
+ }.not_to raise_error
1063
+ end
1064
+ end
1065
+
1066
+ describe 'on a record with a parent as a child (circular dependency)' do
1067
+ before :all do
1068
+ rescue_if @skip do
1069
+ @user.children = [ @user.parent = @user_model.new(:name => 'Parent', :comment => @comment) ]
1070
+ end
1071
+ end
1072
+
1073
+ it 'should not raise an exception' do
1074
+ expect {
1075
+ expect(@user.__send__(method)).to be(true)
1076
+ }.not_to raise_error
1077
+ end
1078
+ end
1079
+ end
1080
+ end
1081
+
1082
+ it { expect(@user).to respond_to(:saved?) }
1083
+
1084
+ describe '#saved?' do
1085
+
1086
+ describe 'on an existing record' do
1087
+
1088
+ it { expect(@user).to be_saved }
1089
+
1090
+ end
1091
+
1092
+ describe 'on a new record' do
1093
+
1094
+ before { @user = @user_model.new }
1095
+
1096
+ it { expect(@user).not_to be_saved }
1097
+
1098
+ end
1099
+
1100
+ end
1101
+
1102
+ [ :update, :update! ].each do |method|
1103
+ it { expect(@user).to respond_to(method) }
1104
+
1105
+ describe "##{method}" do
1106
+ describe 'with attributes' do
1107
+ before :all do
1108
+ rescue_if @skip do
1109
+ @attributes = { :description => 'Changed' }
1110
+ @return = @user.__send__(method, @attributes)
1111
+ end
1112
+ end
1113
+
1114
+ it 'should return true' do
1115
+ expect(@return).to be(true)
1116
+ end
1117
+
1118
+ it 'should update attributes of Resource' do
1119
+ @attributes.each { |key, value| expect(@user.__send__(key)).to eq(value) }
1120
+ end
1121
+
1122
+ it 'should persist the changes' do
1123
+ resource = @user_model.get(*@user.key)
1124
+ @attributes.each { |key, value| expect(resource.__send__(key)).to eq(value) }
1125
+ end
1126
+ end
1127
+
1128
+ describe 'with attributes where one is a parent association' do
1129
+ before :all do
1130
+ rescue_if @skip do
1131
+ @attributes = { :referrer => @user_model.create(:name => 'dkubb', :age => 33, :comment => @comment) }
1132
+ @return = @user.__send__(method, @attributes)
1133
+ end
1134
+ end
1135
+
1136
+ it 'should return true' do
1137
+ expect(@return).to be(true)
1138
+ end
1139
+
1140
+ it 'should update attributes of Resource' do
1141
+ @attributes.each { |key, value| expect(@user.__send__(key)).to eq(value) }
1142
+ end
1143
+
1144
+ it 'should persist the changes' do
1145
+ resource = @user_model.get(*@user.key)
1146
+ @attributes.each { |key, value| expect(resource.__send__(key)).to eq(value) }
1147
+ end
1148
+ end
1149
+
1150
+ describe 'with attributes where a value is nil for a property that does not allow nil' do
1151
+ before do
1152
+ expect { @user.__send__(method, :name => nil) }.to(raise_error(Ardm::Property::InvalidValueError) do |error|
1153
+ expect(error.property).to eq(@user_model.properties[:name])
1154
+ end)
1155
+ end
1156
+
1157
+ it 'should not persist the changes' do
1158
+ expect(@user.reload.name).not_to be_nil
1159
+ end
1160
+ end
1161
+
1162
+ describe 'on a new resource' do
1163
+ before :all do
1164
+ rescue_if @skip do
1165
+ @user = @user.model.new(@user.attributes)
1166
+ @user.age = 99
1167
+ end
1168
+ end
1169
+
1170
+ it 'should raise an exception' do
1171
+ expect {
1172
+ @user.__send__(method, :admin => true)
1173
+ }.to raise_error(Ardm::UpdateConflictError, "#{@user.model}##{method} cannot be called on a new resource")
1174
+ end
1175
+ end
1176
+
1177
+ describe 'on a dirty resource' do
1178
+ before :all do
1179
+ rescue_if @skip do
1180
+ @user.age = 99
1181
+ end
1182
+ end
1183
+
1184
+ it 'should raise an exception' do
1185
+ expect {
1186
+ @user.__send__(method, :admin => true)
1187
+ }.to raise_error(Ardm::UpdateConflictError, "#{@user.model}##{method} cannot be called on a dirty resource")
1188
+ end
1189
+ end
1190
+ end
1191
+ end
1192
+
1193
+ describe 'lazy loading' do
1194
+ before :all do
1195
+ rescue_if @skip do
1196
+ @user.name = 'dkubb'
1197
+ @user.age = 33
1198
+ @user.summary = 'Programmer'
1199
+
1200
+ # lazy load the description
1201
+ @user.description
1202
+ end
1203
+ end
1204
+
1205
+ it 'should not overwrite dirty attribute' do
1206
+ expect(@user.age).to eq(33)
1207
+ end
1208
+
1209
+ it 'should not overwrite dirty lazy attribute' do
1210
+ expect(@user.summary).to eq('Programmer')
1211
+ end
1212
+
1213
+ it 'should not overwrite dirty key' do
1214
+ skip
1215
+ expect(@user.name).to eq('dkubb')
1216
+ end
1217
+ end
1218
+ end