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
@@ -22,29 +22,50 @@ describe ActiveData::ActiveRecord::Associations do
22
22
 
23
23
  attribute :first_name, String
24
24
  attribute :last_name, String
25
+ attribute :admin, Boolean
25
26
  end
26
27
 
27
28
  stub_class(:user, ActiveRecord::Base) do
28
29
  embeds_many :projects
29
30
  embeds_one :profile
30
31
 
32
+ # Simulating JSON attribute
33
+ serialize :projects, JSON
34
+
35
+ def read_attribute(name)
36
+ if name.to_s == 'projects'
37
+ value = super
38
+ JSON.parse(value) if value
39
+ else
40
+ super
41
+ end
42
+ end
43
+
31
44
  validates :projects, associated: true
32
45
  end
33
46
  end
34
47
 
35
- its(:projects) { should = [] }
36
- its(:profile) { should = nil }
48
+ let(:user) { User.new }
49
+
50
+ specify { expect(user.projects).to eq([]) }
51
+ specify { expect(user.profile).to be_nil }
37
52
 
38
53
  context 'new owner' do
39
- subject(:user) { User.new }
54
+ let(:user) { User.new }
40
55
 
41
56
  describe '#projects' do
42
- specify { expect { user.projects << Project.new }
43
- .not_to change { user.read_attribute(:projects) } }
44
- specify { expect { user.projects << Project.new(title: 'First') }
45
- .not_to change { user.read_attribute(:projects) } }
46
- specify { expect { user.projects << Project.new(title: 'First') }
47
- .not_to change { user.projects.reload.count } }
57
+ specify do
58
+ expect { user.projects << Project.new }
59
+ .not_to change { user.read_attribute(:projects) }
60
+ end
61
+ specify do
62
+ expect { user.projects << Project.new(title: 'First') }
63
+ .not_to change { user.read_attribute(:projects) }
64
+ end
65
+ specify do
66
+ expect { user.projects << Project.new(title: 'First') }
67
+ .not_to change { user.projects.reload.count }
68
+ end
48
69
  specify do
49
70
  user.projects << Project.new(title: 'First')
50
71
  user.save
@@ -53,73 +74,111 @@ describe ActiveData::ActiveRecord::Associations do
53
74
  end
54
75
 
55
76
  describe '#profile' do
56
- specify { expect { user.profile = Profile.new(first_name: 'google.com') }
57
- .not_to change { user.read_attribute(:profile) } }
58
- specify { expect { user.profile = Profile.new(first_name: 'google.com') }
59
- .to change { user.profile }.from(nil).to(an_instance_of(Profile)) }
77
+ specify do
78
+ expect { user.profile = Profile.new(first_name: 'google.com') }
79
+ .not_to change { user.read_attribute(:profile) }
80
+ end
81
+ specify do
82
+ expect { user.profile = Profile.new(first_name: 'google.com') }
83
+ .to change { user.profile }.from(nil).to(an_instance_of(Profile))
84
+ end
60
85
  specify do
61
86
  user.profile = Profile.new(first_name: 'google.com')
62
87
  user.save
63
88
  expect(user.reload.profile.first_name).to eq('google.com')
64
89
  end
90
+
91
+ context 'with profile already set' do
92
+ before { user.create_profile(admin: true) }
93
+
94
+ specify do
95
+ user.profile.admin = false
96
+ user.save
97
+ expect(user.reload.profile.admin).to eq(false)
98
+ end
99
+ end
65
100
  end
66
101
  end
67
102
 
68
103
  context 'persisted owner' do
69
- subject(:user) { User.create }
104
+ let(:user) { User.create }
70
105
 
71
106
  describe '#projects' do
72
- specify { expect { user.projects << Project.new }
73
- .not_to change { user.read_attribute(:projects) } }
74
- specify { expect { user.projects << Project.new(title: 'First') }
75
- .to change { user.projects.reload.count }.from(0).to(1) }
76
- specify {
107
+ specify do
108
+ expect { user.projects << Project.new }
109
+ .not_to change { user.read_attribute(:projects) }
110
+ end
111
+ specify do
112
+ expect { user.projects << Project.new(title: 'First') }
113
+ .to change { user.projects.reload.count }.from(0).to(1)
114
+ end
115
+ specify do
77
116
  user.projects << Project.new(title: 'First')
78
117
  user.save
79
- expect(user.reload.projects.first.title).to eq('First') }
118
+ expect(user.reload.projects.first.title).to eq('First')
119
+ end
80
120
 
81
121
  context do
82
122
  let(:project) { Project.new(title: 'First') }
83
123
  before { project.build_author(name: 'Author') }
84
124
 
85
- specify { expect { user.projects << project }
86
- .to change { user.attributes['projects'] }.from(nil)
87
- .to([{title: 'First', author: {name: 'Author'}}].to_json) }
88
- specify { expect { user.projects << project; user.save }
89
- .to change { user.reload.attributes['projects'] }.from(nil)
90
- .to([{title: 'First', author: {name: 'Author'}}].to_json) }
125
+ specify do
126
+ expect { user.projects << project }
127
+ .to change { user.read_attribute(:projects) }.from(nil)
128
+ .to([{'title' => 'First', 'author' => {'name' => 'Author'}}])
129
+ end
130
+ specify do
131
+ expect do
132
+ user.projects << project
133
+ user.save
134
+ end
135
+ .to change { user.reload.read_attribute(:projects) }.from(nil)
136
+ .to([{'title' => 'First', 'author' => {'name' => 'Author'}}])
137
+ end
91
138
  end
92
139
  end
93
140
 
94
141
  describe '#profile' do
95
- specify { expect { user.profile = Profile.new(first_name: 'google.com') }
96
- .to change { user.profile }.from(nil).to(an_instance_of(Profile)) }
97
- specify {
142
+ specify do
143
+ expect { user.profile = Profile.new(first_name: 'google.com') }
144
+ .to change { user.profile }.from(nil).to(an_instance_of(Profile))
145
+ end
146
+ specify do
98
147
  user.profile = Profile.new(first_name: 'google.com')
99
148
  user.save
100
- expect(user.reload.profile.first_name).to eq('google.com') }
101
- specify { expect { user.profile = Profile.new(first_name: 'google.com') }
102
- .to change { user.attributes['profile'] }.from(nil)
103
- .to({first_name: 'google.com', last_name: nil}.to_json) }
104
- specify { expect { user.profile = Profile.new(first_name: 'google.com'); user.save }
105
- .to change { user.reload.attributes['profile'] }.from(nil)
106
- .to({first_name: 'google.com', last_name: nil}.to_json) }
149
+ expect(user.reload.profile.first_name).to eq('google.com')
150
+ end
151
+ specify do
152
+ expect { user.profile = Profile.new(first_name: 'google.com') }
153
+ .to change { user.read_attribute(:profile) }.from(nil)
154
+ .to({first_name: 'google.com', last_name: nil, admin: nil}.to_json)
155
+ end
156
+ specify do
157
+ expect do
158
+ user.profile = Profile.new(first_name: 'google.com')
159
+ user.save
160
+ end
161
+ .to change { user.reload.read_attribute(:profile) }.from(nil)
162
+ .to({first_name: 'google.com', last_name: nil, admin: nil}.to_json)
163
+ end
107
164
  end
108
165
  end
109
166
 
110
167
  context 'class determine errors' do
111
168
  specify do
112
- expect { stub_class(:book, ActiveRecord::Base) do
113
- embeds_one :author, class_name: 'Borogoves'
114
- end.reflect_on_association(:author).klass }.to raise_error NameError
169
+ expect do
170
+ stub_class(:book, ActiveRecord::Base) do
171
+ embeds_one :author, class_name: 'Borogoves'
172
+ end.reflect_on_association(:author).klass end.to raise_error NameError
115
173
  end
116
174
 
117
175
  specify do
118
- expect { stub_class(:user, ActiveRecord::Base) do
119
- embeds_many :projects, class_name: 'Borogoves' do
120
- attribute :title
121
- end
122
- end.reflect_on_association(:projects).klass }.to raise_error NameError
176
+ expect do
177
+ stub_class(:user, ActiveRecord::Base) do
178
+ embeds_many :projects, class_name: 'Borogoves' do
179
+ attribute :title
180
+ end
181
+ end.reflect_on_association(:projects).klass end.to raise_error NameError
123
182
  end
124
183
  end
125
184
 
@@ -144,6 +203,9 @@ describe ActiveData::ActiveRecord::Associations do
144
203
  specify { expect(User.reflect_on_association(:profile).klass).to be < Profile }
145
204
  specify { expect(User.new.profile).to be_nil }
146
205
  specify { expect(User.new.tap { |u| u.create_profile(first_name: 'Profile') }.profile).to be_a(User::Profile) }
147
- specify { expect(User.new.tap { |u| u.create_profile(first_name: 'Profile') }.read_attribute(:profile)).to eq({first_name: 'Profile', last_name: nil, age: nil}.to_json) }
206
+ specify do
207
+ expect(User.new.tap { |u| u.create_profile(first_name: 'Profile') }.read_attribute(:profile))
208
+ .to eq({first_name: 'Profile', last_name: nil, admin: nil, age: nil}.to_json)
209
+ end
148
210
  end
149
211
  end
@@ -1,6 +1,5 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
- require 'lib/active_data/model/nested_attributes'
2
+ require 'shared/nested_attribute_examples'
4
3
 
5
4
  describe ActiveData::ActiveRecord::NestedAttributes do
6
5
  before do
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
3
  describe ActiveData::Config do
@@ -6,14 +5,18 @@ describe ActiveData::Config do
6
5
 
7
6
  describe '#include_root_in_json' do
8
7
  its(:include_root_in_json) { should == false }
9
- specify { expect { subject.include_root_in_json = true }
10
- .to change { subject.include_root_in_json }.from(false).to(true) }
8
+ specify do
9
+ expect { subject.include_root_in_json = true }
10
+ .to change { subject.include_root_in_json }.from(false).to(true)
11
+ end
11
12
  end
12
13
 
13
14
  describe '#i18n_scope' do
14
15
  its(:i18n_scope) { should == :active_data }
15
- specify { expect { subject.i18n_scope = :data_model }
16
- .to change { subject.i18n_scope }.from(:active_data).to(:data_model) }
16
+ specify do
17
+ expect { subject.i18n_scope = :data_model }
18
+ .to change { subject.i18n_scope }.from(:active_data).to(:data_model)
19
+ end
17
20
  end
18
21
 
19
22
  describe '#logger' do
@@ -22,23 +25,42 @@ describe ActiveData::Config do
22
25
 
23
26
  describe '#primary_attribute' do
24
27
  its(:primary_attribute) { should == :id }
25
- specify { expect { subject.primary_attribute = :identified }
26
- .to change { subject.primary_attribute }.from(:id).to(:identified) }
28
+ specify do
29
+ expect { subject.primary_attribute = :identified }
30
+ .to change { subject.primary_attribute }.from(:id).to(:identified)
31
+ end
27
32
  end
28
33
 
29
34
  describe '#normalizer' do
30
- specify { expect { subject.normalizer(:name) { } }
31
- .to change { subject.normalizer(:name) rescue nil }.from(nil).to(an_instance_of(Proc)) }
35
+ specify do
36
+ expect { subject.normalizer(:name) {} }
37
+ .to change {
38
+ begin
39
+ subject.normalizer(:name)
40
+ rescue ActiveData::NormalizerMissing
41
+ nil
42
+ end
43
+ }.from(nil).to(an_instance_of(Proc))
44
+ end
32
45
  specify { expect { subject.normalizer(:wrong) }.to raise_error ActiveData::NormalizerMissing }
33
46
  end
34
47
 
35
48
  describe '#typecaster' do
36
- specify { expect { subject.typecaster('Object') { } }
37
- .to change { subject.typecaster(Time, Object) rescue nil }.from(nil).to(an_instance_of(Proc)) }
38
- specify { expect { subject.typecaster('Object') { } }
39
- .to change { subject.typecaster('time', 'object') rescue nil }.from(nil).to(an_instance_of(Proc)) }
40
- specify { expect { subject.typecaster('Object') { } }
41
- .to change { subject.typecaster(Object) rescue nil }.from(nil).to(an_instance_of(Proc)) }
49
+ specify do
50
+ expect { subject.typecaster('Object') {} }
51
+ .to change { muffle(ActiveData::TypecasterMissing) { subject.typecaster(Time, Object) } }
52
+ .from(nil).to(an_instance_of(Proc))
53
+ end
54
+ specify do
55
+ expect { subject.typecaster('Object') {} }
56
+ .to change { muffle(ActiveData::TypecasterMissing) { subject.typecaster('time', 'object') } }
57
+ .from(nil).to(an_instance_of(Proc))
58
+ end
59
+ specify do
60
+ expect { subject.typecaster('Object') {} }
61
+ .to change { muffle(ActiveData::TypecasterMissing) { subject.typecaster(Object) } }
62
+ .from(nil).to(an_instance_of(Proc))
63
+ end
42
64
  specify { expect { subject.typecaster(Object) }.to raise_error ActiveData::TypecasterMissing }
43
65
  end
44
66
  end