active_data 1.0.0 → 1.1.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.
Files changed (115) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +13 -0
  3. data/.rubocop.yml +56 -0
  4. data/.rubocop_todo.yml +53 -0
  5. data/.rvmrc +1 -1
  6. data/.travis.yml +15 -2
  7. data/Appraisals +1 -1
  8. data/CHANGELOG.md +31 -0
  9. data/Guardfile +8 -8
  10. data/README.md +256 -0
  11. data/Rakefile +2 -4
  12. data/active_data.gemspec +8 -7
  13. data/gemfiles/rails.4.0.gemfile +1 -1
  14. data/gemfiles/rails.4.1.gemfile +1 -1
  15. data/gemfiles/rails.4.2.gemfile +1 -1
  16. data/gemfiles/rails.5.0.gemfile +1 -1
  17. data/gemfiles/rails.5.1.gemfile +14 -0
  18. data/lib/active_data/active_record/associations.rb +18 -13
  19. data/lib/active_data/active_record/nested_attributes.rb +8 -14
  20. data/lib/active_data/base.rb +13 -0
  21. data/lib/active_data/config.rb +4 -4
  22. data/lib/active_data/errors.rb +29 -13
  23. data/lib/active_data/extensions.rb +22 -21
  24. data/lib/active_data/model/associations/base.rb +22 -6
  25. data/lib/active_data/model/associations/embeds_any.rb +17 -0
  26. data/lib/active_data/model/associations/embeds_many.rb +29 -19
  27. data/lib/active_data/model/associations/embeds_one.rb +30 -26
  28. data/lib/active_data/model/associations/nested_attributes.rb +82 -50
  29. data/lib/active_data/model/associations/persistence_adapters/active_record/referenced_proxy.rb +31 -0
  30. data/lib/active_data/model/associations/persistence_adapters/active_record.rb +66 -0
  31. data/lib/active_data/model/associations/persistence_adapters/base.rb +53 -0
  32. data/lib/active_data/model/associations/references_any.rb +41 -0
  33. data/lib/active_data/model/associations/references_many.rb +51 -37
  34. data/lib/active_data/model/associations/references_one.rb +43 -41
  35. data/lib/active_data/model/associations/reflections/base.rb +19 -29
  36. data/lib/active_data/model/associations/reflections/embeds_any.rb +43 -0
  37. data/lib/active_data/model/associations/reflections/embeds_many.rb +3 -13
  38. data/lib/active_data/model/associations/reflections/embeds_one.rb +5 -37
  39. data/lib/active_data/model/associations/reflections/references_any.rb +62 -0
  40. data/lib/active_data/model/associations/reflections/references_many.rb +7 -7
  41. data/lib/active_data/model/associations/reflections/references_one.rb +9 -7
  42. data/lib/active_data/model/associations/reflections/singular.rb +35 -0
  43. data/lib/active_data/model/associations/validations.rb +2 -27
  44. data/lib/active_data/model/associations.rb +12 -10
  45. data/lib/active_data/model/attributes/attribute.rb +10 -10
  46. data/lib/active_data/model/attributes/base.rb +8 -7
  47. data/lib/active_data/model/attributes/localized.rb +4 -4
  48. data/lib/active_data/model/attributes/reference_many.rb +6 -8
  49. data/lib/active_data/model/attributes/reference_one.rb +17 -9
  50. data/lib/active_data/model/attributes/reflections/attribute.rb +2 -2
  51. data/lib/active_data/model/attributes/reflections/base.rb +8 -11
  52. data/lib/active_data/model/attributes/reflections/localized.rb +2 -2
  53. data/lib/active_data/model/attributes/reflections/reference_one.rb +11 -22
  54. data/lib/active_data/model/attributes/reflections/represents.rb +5 -6
  55. data/lib/active_data/model/attributes/represents.rb +6 -5
  56. data/lib/active_data/model/attributes.rb +33 -87
  57. data/lib/active_data/model/callbacks.rb +6 -7
  58. data/lib/active_data/model/conventions.rb +2 -0
  59. data/lib/active_data/model/dirty.rb +4 -4
  60. data/lib/active_data/model/lifecycle.rb +18 -20
  61. data/lib/active_data/model/localization.rb +5 -2
  62. data/lib/active_data/model/persistence.rb +2 -2
  63. data/lib/active_data/model/primary.rb +19 -14
  64. data/lib/active_data/model/representation.rb +81 -0
  65. data/lib/active_data/model/scopes.rb +22 -12
  66. data/lib/active_data/model/validations/associated.rb +3 -2
  67. data/lib/active_data/model/validations/nested.rb +6 -1
  68. data/lib/active_data/model/validations.rb +3 -3
  69. data/lib/active_data/model.rb +2 -1
  70. data/lib/active_data/undefined_class.rb +9 -0
  71. data/lib/active_data/version.rb +1 -1
  72. data/lib/active_data.rb +40 -17
  73. data/spec/lib/active_data/active_record/associations_spec.rb +107 -45
  74. data/spec/lib/active_data/active_record/nested_attributes_spec.rb +1 -2
  75. data/spec/lib/active_data/config_spec.rb +37 -15
  76. data/spec/lib/active_data/model/associations/embeds_many_spec.rb +475 -172
  77. data/spec/lib/active_data/model/associations/embeds_one_spec.rb +353 -96
  78. data/spec/lib/active_data/model/associations/nested_attributes_spec.rb +108 -12
  79. data/spec/lib/active_data/model/associations/persistence_adapters/active_record_spec.rb +58 -0
  80. data/spec/lib/active_data/model/associations/references_many_spec.rb +440 -64
  81. data/spec/lib/active_data/model/associations/references_one_spec.rb +347 -36
  82. data/spec/lib/active_data/model/associations/reflections/embeds_many_spec.rb +8 -7
  83. data/spec/lib/active_data/model/associations/reflections/embeds_one_spec.rb +7 -6
  84. data/spec/lib/active_data/model/associations/reflections/references_many_spec.rb +81 -33
  85. data/spec/lib/active_data/model/associations/reflections/references_one_spec.rb +116 -37
  86. data/spec/lib/active_data/model/associations/validations_spec.rb +27 -43
  87. data/spec/lib/active_data/model/associations_spec.rb +34 -25
  88. data/spec/lib/active_data/model/attributes/attribute_spec.rb +26 -23
  89. data/spec/lib/active_data/model/attributes/base_spec.rb +5 -6
  90. data/spec/lib/active_data/model/attributes/collection_spec.rb +7 -8
  91. data/spec/lib/active_data/model/attributes/dictionary_spec.rb +40 -33
  92. data/spec/lib/active_data/model/attributes/localized_spec.rb +27 -28
  93. data/spec/lib/active_data/model/attributes/reflections/attribute_spec.rb +6 -6
  94. data/spec/lib/active_data/model/attributes/represents_spec.rb +10 -78
  95. data/spec/lib/active_data/model/attributes_spec.rb +150 -45
  96. data/spec/lib/active_data/model/callbacks_spec.rb +69 -70
  97. data/spec/lib/active_data/model/conventions_spec.rb +0 -1
  98. data/spec/lib/active_data/model/dirty_spec.rb +22 -13
  99. data/spec/lib/active_data/model/lifecycle_spec.rb +49 -23
  100. data/spec/lib/active_data/model/persistence_spec.rb +5 -6
  101. data/spec/lib/active_data/model/representation_spec.rb +126 -0
  102. data/spec/lib/active_data/model/scopes_spec.rb +1 -3
  103. data/spec/lib/active_data/model/typecasting_spec.rb +6 -5
  104. data/spec/lib/active_data/model/validations/associated_spec.rb +26 -18
  105. data/spec/lib/active_data/model/validations/nested_spec.rb +89 -18
  106. data/spec/lib/active_data/model_spec.rb +1 -2
  107. data/spec/lib/active_data_spec.rb +0 -1
  108. data/spec/shared/nested_attribute_examples.rb +332 -0
  109. data/spec/spec_helper.rb +3 -0
  110. data/spec/support/model_helpers.rb +2 -2
  111. data/spec/support/muffle_helper.rb +7 -0
  112. metadata +52 -18
  113. data/lib/active_data/model/associations/collection/referenced.rb +0 -26
  114. data/lib/active_data/model/associations/reflections/reference_reflection.rb +0 -45
  115. data/spec/lib/active_data/model/nested_attributes.rb +0 -202
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
3
  describe ActiveData::Model::Lifecycle do
@@ -11,7 +10,7 @@ describe ActiveData::Model::Lifecycle do
11
10
 
12
11
  subject { User.new }
13
12
 
14
- [:save, :create, :update, :destroy].each do |action|
13
+ %i[save create update destroy].each do |action|
15
14
  specify { expect { subject.public_send "_#{action}_performer=", '' }.to raise_error NoMethodError }
16
15
  end
17
16
 
@@ -19,9 +18,9 @@ describe ActiveData::Model::Lifecycle do
19
18
  let(:foo) { true }
20
19
 
21
20
  specify { expect { subject.save { foo } }.to raise_error NameError }
22
- specify { expect { subject.save { |instance| attributes } }.to raise_error NameError }
21
+ specify { expect { subject.save { |_| attributes } }.to raise_error NameError }
23
22
  specify { expect(subject.save { attributes }).to eq(true) }
24
- specify { expect(subject.save { |instance| foo }).to eq(true) }
23
+ specify { expect(subject.save { |_| foo }).to eq(true) }
25
24
  end
26
25
 
27
26
  context 'save' do
@@ -87,7 +86,7 @@ describe ActiveData::Model::Lifecycle do
87
86
 
88
87
  attribute :actions, Array, default: []
89
88
 
90
- def append action
89
+ def append(action)
91
90
  self.actions = actions + [action]
92
91
  end
93
92
 
@@ -106,7 +105,7 @@ describe ActiveData::Model::Lifecycle do
106
105
  subject.destroy
107
106
  subject.destroy
108
107
  subject.save
109
- expect(subject.actions).to eq([:destroy, :create, :update, :destroy, :destroy, :create])
108
+ expect(subject.actions).to eq(%i[destroy create update destroy destroy create])
110
109
  end
111
110
  end
112
111
  end
@@ -198,10 +197,14 @@ describe ActiveData::Model::Lifecycle do
198
197
  specify { expect { subject.update({}) }.not_to change { subject.persisted? } }
199
198
  specify { expect { subject.update!({}) }.to raise_error ActiveData::ValidationError }
200
199
 
201
- specify { expect { subject.update(name: 'Jonny') }
202
- .to change { Storage.storage.keys }.from([]).to([subject.id]) }
203
- specify { expect { subject.update!(name: 'Jonny') }
204
- .to change { Storage.storage.keys }.from([]).to([subject.id]) }
200
+ specify do
201
+ expect { subject.update(name: 'Jonny') }
202
+ .to change { Storage.storage.keys }.from([]).to([subject.id])
203
+ end
204
+ specify do
205
+ expect { subject.update!(name: 'Jonny') }
206
+ .to change { Storage.storage.keys }.from([]).to([subject.id])
207
+ end
205
208
  end
206
209
 
207
210
  describe '#update_attributes, #update_attributes!' do
@@ -213,10 +216,14 @@ describe ActiveData::Model::Lifecycle do
213
216
  specify { expect { subject.update_attributes({}) }.not_to change { subject.persisted? } }
214
217
  specify { expect { subject.update_attributes!({}) }.to raise_error ActiveData::ValidationError }
215
218
 
216
- specify { expect { subject.update_attributes(name: 'Jonny') }
217
- .to change { Storage.storage.keys }.from([]).to([subject.id]) }
218
- specify { expect { subject.update_attributes!(name: 'Jonny') }
219
- .to change { Storage.storage.keys }.from([]).to([subject.id]) }
219
+ specify do
220
+ expect { subject.update_attributes(name: 'Jonny') }
221
+ .to change { Storage.storage.keys }.from([]).to([subject.id])
222
+ end
223
+ specify do
224
+ expect { subject.update_attributes!(name: 'Jonny') }
225
+ .to change { Storage.storage.keys }.from([]).to([subject.id])
226
+ end
220
227
  end
221
228
 
222
229
  describe '#save, #save!' do
@@ -230,7 +237,10 @@ describe ActiveData::Model::Lifecycle do
230
237
  specify { expect { subject.save! }.to raise_error ActiveData::ValidationError }
231
238
 
232
239
  specify { expect { subject.save }.not_to change { subject.persisted? } }
233
- specify { expect { subject.save! rescue nil }.not_to change { subject.persisted? } }
240
+ specify do
241
+ expect { muffle(ActiveData::ValidationError) { subject.save! } }
242
+ .not_to change { subject.persisted? }
243
+ end
234
244
  end
235
245
 
236
246
  context 'create' do
@@ -257,7 +267,10 @@ describe ActiveData::Model::Lifecycle do
257
267
  specify { expect { subject.save! }.to raise_error ActiveData::ObjectNotSaved }
258
268
 
259
269
  specify { expect { subject.save }.not_to change { subject.persisted? } }
260
- specify { expect { subject.save! rescue nil }.not_to change { subject.persisted? } }
270
+ specify do
271
+ expect { muffle(ActiveData::ObjectNotSaved) { subject.save! } }
272
+ .not_to change { subject.persisted? }
273
+ end
261
274
  end
262
275
  end
263
276
 
@@ -273,10 +286,14 @@ describe ActiveData::Model::Lifecycle do
273
286
  specify { expect { subject.save }.not_to change { subject.persisted? } }
274
287
  specify { expect { subject.save! }.not_to change { subject.persisted? } }
275
288
 
276
- specify { expect { subject.save }.to change { Storage.storage[subject.id] }
277
- .from(hash_including(name: 'Jonny')).to(hash_including(name: 'Jimmy')) }
278
- specify { expect { subject.save! }.to change { Storage.storage[subject.id] }
279
- .from(hash_including(name: 'Jonny')).to(hash_including(name: 'Jimmy')) }
289
+ specify do
290
+ expect { subject.save }.to change { Storage.storage[subject.id] }
291
+ .from(hash_including(name: 'Jonny')).to(hash_including(name: 'Jimmy'))
292
+ end
293
+ specify do
294
+ expect { subject.save! }.to change { Storage.storage[subject.id] }
295
+ .from(hash_including(name: 'Jonny')).to(hash_including(name: 'Jimmy'))
296
+ end
280
297
 
281
298
  context 'save failed' do
282
299
  before { User.define_save { false } }
@@ -287,7 +304,10 @@ describe ActiveData::Model::Lifecycle do
287
304
  specify { expect { subject.save! }.to raise_error ActiveData::ObjectNotSaved }
288
305
 
289
306
  specify { expect { subject.save }.not_to change { subject.persisted? } }
290
- specify { expect { subject.save! rescue nil }.not_to change { subject.persisted? } }
307
+ specify do
308
+ expect { muffle(ActiveData::ObjectNotSaved) { subject.save! } }
309
+ .not_to change { subject.persisted? }
310
+ end
291
311
  end
292
312
  end
293
313
  end
@@ -320,10 +340,16 @@ describe ActiveData::Model::Lifecycle do
320
340
  specify { expect { subject.destroy! }.to raise_error ActiveData::ObjectNotDestroyed }
321
341
 
322
342
  specify { expect { subject.destroy }.not_to change { subject.persisted? } }
323
- specify { expect { subject.destroy! rescue nil }.not_to change { subject.persisted? } }
343
+ specify do
344
+ expect { muffle(ActiveData::ObjectNotDestroyed) { subject.destroy! } }
345
+ .not_to change { subject.persisted? }
346
+ end
324
347
 
325
348
  specify { expect { subject.destroy }.not_to change { subject.destroyed? } }
326
- specify { expect { subject.destroy! rescue nil }.not_to change { subject.destroyed? } }
349
+ specify do
350
+ expect { muffle(ActiveData::ObjectNotDestroyed) { subject.destroy! } }
351
+ .not_to change { subject.destroyed? }
352
+ end
327
353
  end
328
354
  end
329
355
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
3
  describe ActiveData::Model::Persistence do
@@ -22,7 +21,7 @@ describe ActiveData::Model::Persistence do
22
21
  context do
23
22
  subject(:instance) { model.instantiate(name: 'Hello', foo: 'Bar') }
24
23
 
25
- specify { expect(subject.instance_variable_get(:@initial_attributes)).to eq({ name: 'Hello' }.stringify_keys) }
24
+ specify { expect(subject.instance_variable_get(:@initial_attributes)).to eq({name: 'Hello'}.stringify_keys) }
26
25
  end
27
26
  end
28
27
 
@@ -31,17 +30,17 @@ describe ActiveData::Model::Persistence do
31
30
  subject(:instances) { model.instantiate_collection(name: 'Hello', foo: 'Bar') }
32
31
 
33
32
  specify { expect(subject).to be_a Array }
34
- specify { expect(subject.first.instance_variable_get(:@initial_attributes)).to eq({ name: 'Hello' }.stringify_keys) }
33
+ specify { expect(subject.first.instance_variable_get(:@initial_attributes)).to eq({name: 'Hello'}.stringify_keys) }
35
34
  end
36
35
 
37
36
  context do
38
37
  before { model.send(:include, ActiveData::Model::Scopes) }
39
- subject(:instances) { model.instantiate_collection([{ name: 'Hello', foo: 'Bar' }, {name: 'World'}]) }
38
+ subject(:instances) { model.instantiate_collection([{name: 'Hello', foo: 'Bar'}, {name: 'World'}]) }
40
39
 
41
40
  specify { expect(subject).to be_a ActiveData::Model::Scopes::ScopeProxy }
42
41
  specify { expect(subject.count).to eq(2) }
43
- specify { expect(subject.first.instance_variable_get(:@initial_attributes)).to eq({ name: 'Hello' }.stringify_keys) }
44
- specify { expect(subject.second.instance_variable_get(:@initial_attributes)).to eq({ name: 'World' }.stringify_keys) }
42
+ specify { expect(subject.first.instance_variable_get(:@initial_attributes)).to eq({name: 'Hello'}.stringify_keys) }
43
+ specify { expect(subject.second.instance_variable_get(:@initial_attributes)).to eq({name: 'World'}.stringify_keys) }
45
44
  end
46
45
  end
47
46
  end
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveData::Model::Representation do
4
+ context 'integration' do
5
+ before do
6
+ stub_model(:author) do
7
+ attribute :rate, Integer
8
+ end
9
+
10
+ stub_model(:post) do
11
+ include ActiveData::Model::Representation
12
+
13
+ attribute :author, Object
14
+ alias_attribute :a, :author
15
+ represents :rate, of: :a
16
+ alias_attribute :r, :rate
17
+ end
18
+ end
19
+ let(:author) { Author.new(rate: '42') }
20
+
21
+ specify { expect(Post.reflect_on_attribute(:rate).reference).to eq('author') }
22
+
23
+ specify { expect(Post.new(author: author).rate).to eq(42) }
24
+ specify { expect(Post.new(author: author).rate_before_type_cast).to eq('42') }
25
+ specify { expect(Post.new(rate: '33', author: author).rate).to eq(33) }
26
+ specify { expect(Post.new(rate: '33', author: author).author.rate).to eq(33) }
27
+ specify { expect(Post.new(r: '33', author: author).rate).to eq(33) }
28
+ specify { expect(Post.new(r: '33', author: author).author.rate).to eq(33) }
29
+ specify { expect(Post.new(author: author).rate?).to eq(true) }
30
+ specify { expect(Post.new(rate: nil, author: author).rate?).to eq(false) }
31
+
32
+ specify { expect(Post.new.rate).to be_nil }
33
+ specify { expect(Post.new.rate_before_type_cast).to be_nil }
34
+ specify { expect { Post.new(rate: '33') }.to raise_error(NoMethodError) }
35
+
36
+ context 'ActionController::Parameters' do
37
+ let(:params) { instance_double('ActionController::Parameters', to_unsafe_hash: {rate: '33', author: author}) }
38
+ specify { expect { Post.new(params) }.not_to raise_error }
39
+ end
40
+
41
+ context 'dirty' do
42
+ before { Post.include ActiveData::Model::Dirty }
43
+
44
+ specify do
45
+ expect(Post.new(author: author, rate: '33').changes)
46
+ .to eq('author' => [nil, author], 'rate' => [42, 33])
47
+ end
48
+ end
49
+
50
+ context do
51
+ before do
52
+ stub_class(:author, ActiveRecord::Base)
53
+
54
+ stub_model(:post) do
55
+ include ActiveData::Model::Associations
56
+ include ActiveData::Model::Representation
57
+
58
+ references_one :author
59
+ alias_association :a, :author
60
+ represents :name, of: :a
61
+ end
62
+ end
63
+ let!(:author) { Author.create!(name: 42) }
64
+
65
+ specify { expect(Post.reflect_on_attribute(:name).reference).to eq('author') }
66
+
67
+ specify { expect(Post.new(name: '33', author: author).name).to eq('33') }
68
+ specify { expect(Post.new(name: '33', author: author).author.name).to eq('33') }
69
+ end
70
+
71
+ context 'multiple attributes in a single represents definition' do
72
+ before do
73
+ stub_model(:author) do
74
+ attribute :first_name, String
75
+ attribute :last_name, String
76
+ end
77
+
78
+ stub_model(:post) do
79
+ include ActiveData::Model::Representation
80
+
81
+ attribute :author, Object
82
+ represents :first_name, :last_name, of: :author
83
+ end
84
+ end
85
+
86
+ let(:author) { Author.new(first_name: 'John', last_name: 'Doe') }
87
+ let(:post) { Post.new }
88
+
89
+ specify do
90
+ expect { post.update(author: author) }
91
+ .to change { post.first_name }.to('John')
92
+ .and change { post.last_name }.to('Doe')
93
+ end
94
+ end
95
+ end
96
+
97
+ describe '#validate' do
98
+ before do
99
+ stub_class(:author, ActiveRecord::Base) do
100
+ validates :name, presence: true
101
+
102
+ # Emulate Active Record association auto save error.
103
+ def errors
104
+ super.tap do |errors|
105
+ errors.add(:'user.email', 'is invalid') if errors[:'user.email'].empty?
106
+ end
107
+ end
108
+ end
109
+
110
+ stub_model(:post) do
111
+ include ActiveData::Model::Associations
112
+ include ActiveData::Model::Representation
113
+
114
+ references_one :author
115
+ represents :name, :email, of: :author
116
+ end
117
+ end
118
+
119
+ let(:post) { Post.new(author: Author.new) }
120
+
121
+ specify do
122
+ expect { post.validate }.to change { post.errors.messages }
123
+ .to(hash_including(:'author.user.email' => ['is invalid'], name: ["can't be blank"]))
124
+ end
125
+ end
126
+ end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
3
  describe ActiveData::Model::Scopes do
@@ -19,8 +18,7 @@ describe ActiveData::Model::Scopes do
19
18
 
20
19
  private
21
20
 
22
- def hidden_method
23
- end
21
+ def hidden_method() end
24
22
  end
25
23
  end
26
24
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
3
  describe ActiveData::Model::Attributes do
@@ -96,10 +95,11 @@ describe ActiveData::Model::Attributes do
96
95
  let(:model) { stub_model { attribute :column, Array } }
97
96
 
98
97
  specify { expect(model.new(column: [1, 2, 3]).column).to eq([1, 2, 3]) }
99
- specify { expect(model.new(column: 'hello, world').column).to eq(['hello', 'world']) }
98
+ specify { expect(model.new(column: 'hello, world').column).to eq(%w[hello world]) }
100
99
  specify { expect(model.new(column: 10).column).to be_nil }
101
100
  end
102
101
 
102
+ # rubocop:disable Style/DateTime
103
103
  context 'date' do
104
104
  let(:model) { stub_model { attribute :column, Date } }
105
105
  let(:date) { Date.new(2013, 6, 13) }
@@ -149,6 +149,7 @@ describe ActiveData::Model::Attributes do
149
149
  specify { expect(model.new(column: Time.new(2013, 6, 13, 23, 13)).column).to eq(Time.new(2013, 6, 13, 23, 13)) }
150
150
  end
151
151
  end
152
+ # rubocop:enable Style/DateTime
152
153
 
153
154
  context 'time_zone' do
154
155
  let(:model) { stub_model { attribute :column, ActiveSupport::TimeZone } }
@@ -175,13 +176,13 @@ describe ActiveData::Model::Attributes do
175
176
  let(:uuid_tools) { UUIDTools::UUID.random_create }
176
177
 
177
178
  specify { expect(uuid.as_json).to eq(uuid.to_s) }
178
- specify { expect(uuid.to_json).to eq("\"#{uuid.to_s}\"") }
179
+ specify { expect(uuid.to_json).to eq("\"#{uuid}\"") }
179
180
  specify { expect(uuid.to_param).to eq(uuid.to_s) }
180
- specify { expect(uuid.to_query(:key)).to eq("key=#{uuid.to_s}") }
181
+ specify { expect(uuid.to_query(:key)).to eq("key=#{uuid}") }
181
182
 
182
183
  specify { expect(model.new(column: nil).column).to be_nil }
183
184
  specify { expect(model.new(column: Object.new).column).to be_nil }
184
- specify { expect(model.new(column: uuid_tools).column).to be_a ActiveData::UUID }
185
+ specify { expect(model.new(column: uuid_tools).column).to be_a ActiveData::UUID }
185
186
  specify { expect(model.new(column: uuid_tools).column).to eq(uuid_tools) }
186
187
  specify { expect(model.new(column: uuid).column).to eq(uuid) }
187
188
  specify { expect(model.new(column: uuid.to_s).column).to eq(uuid) }
@@ -35,60 +35,68 @@ describe ActiveData::Model::Validations::AssociatedValidator do
35
35
  end
36
36
 
37
37
  context do
38
- subject(:instance) { Main.instantiate name: 'hello', validated_one: { name: 'name' } }
38
+ subject(:instance) { Main.instantiate name: 'hello', validated_one: {name: 'name'} }
39
39
  it { is_expected.to be_valid }
40
40
  end
41
41
 
42
42
  context do
43
- subject(:instance) { Main.instantiate name: 'hello', validated_one: { } }
43
+ subject(:instance) { Main.instantiate name: 'hello', validated_one: {} }
44
44
  it { is_expected.not_to be_valid }
45
- specify { expect { instance.validate }.to change { instance.errors.messages }
46
- .to(validated_one: ['is invalid']) }
45
+ specify do
46
+ expect { instance.validate }.to change { instance.errors.messages }
47
+ .to(validated_one: ['is invalid'])
48
+ end
47
49
  end
48
50
 
49
51
  context do
50
- subject(:instance) { Main.instantiate name: 'hello', unvalidated_one: { name: 'name' } }
52
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_one: {name: 'name'} }
51
53
  it { is_expected.to be_valid }
52
54
  end
53
55
 
54
56
  context do
55
- subject(:instance) { Main.instantiate name: 'hello', unvalidated_one: { } }
57
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_one: {} }
56
58
  it { is_expected.to be_valid }
57
59
  end
58
60
 
59
61
  context do
60
- subject(:instance) { Main.instantiate name: 'hello', validated_many: [{ name: 'name' }] }
62
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{name: 'name'}] }
61
63
  it { is_expected.to be_valid }
62
64
  end
63
65
 
64
66
  context do
65
- subject(:instance) { Main.instantiate name: 'hello', validated_many: [{ }] }
67
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{}] }
66
68
  it { is_expected.not_to be_valid }
67
- specify { expect { instance.validate }.to change { instance.errors.messages }
68
- .to(:'validated_many.0.name' => ["can't be blank"], validated_many: ['is invalid']) }
69
+ specify do
70
+ expect { instance.validate }.to change { instance.errors.messages }
71
+ .to(:'validated_many.0.name' => ["can't be blank"], validated_many: ['is invalid'])
72
+ end
69
73
  end
70
74
 
71
75
  context do
72
- subject(:instance) { Main.instantiate name: 'hello', unvalidated_many: [{ name: 'name' }] }
76
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_many: [{name: 'name'}] }
73
77
  it { is_expected.to be_valid }
74
78
  end
75
79
 
76
80
  context do
77
- subject(:instance) { Main.instantiate name: 'hello', unvalidated_many: [{ }] }
81
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_many: [{}] }
78
82
  it { is_expected.to be_valid }
79
83
  end
80
84
 
81
85
  context do
82
- subject(:instance) { Main.instantiate name: 'hello', validated_many: [{ name: 'name' }], validated_one: { } }
86
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{name: 'name'}], validated_one: {} }
83
87
  it { is_expected.not_to be_valid }
84
- specify { expect { instance.validate }.to change { instance.errors.messages }
85
- .to(validated_one: ['is invalid']) }
88
+ specify do
89
+ expect { instance.validate }.to change { instance.errors.messages }
90
+ .to(validated_one: ['is invalid'])
91
+ end
86
92
  end
87
93
 
88
94
  context do
89
- subject(:instance) { Main.instantiate name: 'hello', validated_many: [{ }], validated_one: { name: 'name' } }
95
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{}], validated_one: {name: 'name'} }
90
96
  it { is_expected.not_to be_valid }
91
- specify { expect { instance.validate }.to change { instance.errors.messages }
92
- .to(:'validated_many.0.name' => ["can't be blank"], validated_many: ['is invalid']) }
97
+ specify do
98
+ expect { instance.validate }.to change { instance.errors.messages }
99
+ .to(:'validated_many.0.name' => ["can't be blank"], validated_many: ['is invalid'])
100
+ end
93
101
  end
94
102
  end
@@ -5,7 +5,9 @@ describe ActiveData::Model::Validations::NestedValidator do
5
5
  stub_model(:validated_assoc) do
6
6
  include ActiveData::Model
7
7
  include ActiveData::Model::Lifecycle
8
+ include ActiveData::Model::Primary
8
9
 
10
+ primary :id, Integer
9
11
  attribute :name, String
10
12
 
11
13
  validates_presence_of :name
@@ -34,60 +36,129 @@ describe ActiveData::Model::Validations::NestedValidator do
34
36
  end
35
37
 
36
38
  context do
37
- subject(:instance) { Main.instantiate name: 'hello', validated_one: { name: 'name' } }
39
+ subject(:instance) { Main.instantiate name: 'hello', validated_one: {name: 'name'} }
38
40
  it { is_expected.to be_valid }
39
41
  end
40
42
 
41
43
  context do
42
- subject(:instance) { Main.instantiate name: 'hello', validated_one: { } }
44
+ subject(:instance) { Main.instantiate name: 'hello', validated_one: {} }
43
45
  it { is_expected.not_to be_valid }
44
- specify { expect { instance.validate }.to change { instance.errors.messages }
45
- .to(:'validated_one.name' => ["can't be blank"]) }
46
+ specify do
47
+ expect { instance.validate }.to change { instance.errors.messages }
48
+ .to(:'validated_one.name' => ["can't be blank"])
49
+ end
46
50
  end
47
51
 
48
52
  context do
49
- subject(:instance) { Main.instantiate name: 'hello', unvalidated_one: { name: 'name' } }
53
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_one: {name: 'name'} }
50
54
  it { is_expected.to be_valid }
51
55
  end
52
56
 
53
57
  context do
54
- subject(:instance) { Main.instantiate name: 'hello', unvalidated_one: { } }
58
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_one: {} }
55
59
  it { is_expected.to be_valid }
56
60
  end
57
61
 
58
62
  context do
59
- subject(:instance) { Main.instantiate name: 'hello', validated_many: [{ name: 'name' }] }
63
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{name: 'name'}] }
60
64
  it { is_expected.to be_valid }
61
65
  end
62
66
 
63
67
  context do
64
- subject(:instance) { Main.instantiate name: 'hello', validated_many: [{ }] }
68
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{}] }
65
69
  it { is_expected.not_to be_valid }
66
- specify { expect { instance.validate }.to change { instance.errors.messages }
67
- .to(:'validated_many.0.name' => ["can't be blank"]) }
70
+ specify do
71
+ expect { instance.validate }.to change { instance.errors.messages }
72
+ .to(:'validated_many.0.name' => ["can't be blank"])
73
+ end
68
74
  end
69
75
 
70
76
  context do
71
- subject(:instance) { Main.instantiate name: 'hello', unvalidated_many: [{ name: 'name' }] }
77
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_many: [{name: 'name'}] }
72
78
  it { is_expected.to be_valid }
73
79
  end
74
80
 
75
81
  context do
76
- subject(:instance) { Main.instantiate name: 'hello', unvalidated_many: [{ }] }
82
+ subject(:instance) { Main.instantiate name: 'hello', unvalidated_many: [{}] }
77
83
  it { is_expected.to be_valid }
78
84
  end
79
85
 
80
86
  context do
81
- subject(:instance) { Main.instantiate name: 'hello', validated_many: [{ name: 'name' }], validated_one: { } }
87
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{name: 'name'}], validated_one: {} }
82
88
  it { is_expected.not_to be_valid }
83
- specify { expect { instance.validate }.to change { instance.errors.messages }
84
- .to(:'validated_one.name' => ["can't be blank"]) }
89
+ specify do
90
+ expect { instance.validate }.to change { instance.errors.messages }
91
+ .to(:'validated_one.name' => ["can't be blank"])
92
+ end
93
+ end
94
+
95
+ context 'accepts nested attributes for one' do
96
+ before { Main.accepts_nested_attributes_for :validated_one, allow_destroy: true }
97
+ subject(:instance) { Main.instantiate name: 'hello', validated_one: {id: 1, name: 'name'} }
98
+
99
+ specify do
100
+ instance.validated_one_attributes = {id: 1, name: '', _destroy: true}
101
+ is_expected.to be_valid
102
+ end
103
+ end
104
+
105
+ context 'accepts nested attributes for many' do
106
+ before { Main.accepts_nested_attributes_for :validated_many, allow_destroy: true }
107
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{id: 1, name: 'name'}] }
108
+
109
+ specify do
110
+ instance.validated_many_attributes = [{id: 1, name: '', _destroy: true}]
111
+ is_expected.to be_valid
112
+ end
113
+ end
114
+
115
+ context 'object field is invalid and referenced object does not include AutosaveAssociation' do
116
+ before do
117
+ stub_class(:validated_object) do
118
+ include ActiveData::Model::Validations
119
+ include ActiveData::Model::Attributes
120
+ include ActiveData::Model::Primary
121
+
122
+ primary :id
123
+ attribute :title, String
124
+ validates_presence_of :title
125
+ end
126
+
127
+ Main.class_eval do
128
+ include ActiveData::Model::Associations
129
+ attribute :object, Object
130
+ validates :object, nested: true
131
+ end
132
+ end
133
+
134
+ subject(:instance) { Main.instantiate name: 'hello', object: object, validated_one: {name: 'name'} }
135
+
136
+ context do
137
+ let(:object) { ValidatedObject.new(title: 'Mr.') }
138
+ it { is_expected.to be_valid }
139
+ end
140
+
141
+ context do
142
+ let(:object) { ValidatedObject.new }
143
+ it do
144
+ expect { subject.valid? }.not_to raise_error
145
+ expect(subject).not_to be_valid
146
+ end
147
+ end
85
148
  end
86
149
 
87
150
  context do
88
- subject(:instance) { Main.instantiate name: 'hello', validated_many: [{ }], validated_one: { name: 'name' } }
151
+ subject(:instance) { Main.instantiate name: 'hello', validated_many: [{}], validated_one: {name: 'name'} }
89
152
  it { is_expected.not_to be_valid }
90
- specify { expect { instance.validate }.to change { instance.errors.messages }
91
- .to(:'validated_many.0.name' => ["can't be blank"]) }
153
+ specify do
154
+ expect { instance.validate }.to change { instance.errors.messages }
155
+ .to(:'validated_many.0.name' => ["can't be blank"])
156
+ end
157
+ end
158
+
159
+ describe '.validates_nested?' do
160
+ specify { expect(Main).to be_validates_nested(:validated_one) }
161
+ specify { expect(Main).to be_validates_nested(:unvalidated_one) }
162
+ specify { expect(Main).not_to be_validates_nested(:something_else) }
92
163
  end
93
164
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
3
  describe ActiveData::Model do
@@ -6,6 +5,6 @@ describe ActiveData::Model do
6
5
  specify { expect { model.blablabla }.to raise_error NoMethodError }
7
6
 
8
7
  context 'Fault tolerance' do
9
- specify{ expect { model.new(foo: 'bar') }.not_to raise_error }
8
+ specify { expect { model.new(foo: 'bar') }.not_to raise_error }
10
9
  end
11
10
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
3
  describe ActiveData do