smooth_operator 1.3.0 → 1.8.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 (60) hide show
  1. checksums.yaml +8 -8
  2. data/Gemfile +0 -5
  3. data/README.md +11 -308
  4. data/console.rb +3 -28
  5. data/lib/smooth_operator.rb +7 -75
  6. data/lib/smooth_operator/array_with_meta_data.rb +21 -20
  7. data/lib/smooth_operator/attribute_assignment.rb +53 -57
  8. data/lib/smooth_operator/delegation.rb +34 -15
  9. data/lib/smooth_operator/finder_methods.rb +17 -33
  10. data/lib/smooth_operator/helpers.rb +7 -37
  11. data/lib/smooth_operator/internal_attribute.rb +49 -0
  12. data/lib/smooth_operator/model_schema.rb +72 -0
  13. data/lib/smooth_operator/open_struct.rb +4 -7
  14. data/lib/smooth_operator/operator.rb +64 -102
  15. data/lib/smooth_operator/persistence.rb +64 -94
  16. data/lib/smooth_operator/remote_call.rb +70 -0
  17. data/lib/smooth_operator/serialization.rb +33 -89
  18. data/lib/smooth_operator/translation.rb +13 -26
  19. data/lib/smooth_operator/type_converter.rb +69 -0
  20. data/lib/smooth_operator/validations.rb +3 -25
  21. data/lib/smooth_operator/version.rb +1 -1
  22. data/smooth_operator.gemspec +5 -9
  23. data/spec/factories/user_factory.rb +4 -5
  24. data/spec/smooth_operator/attribute_assignment_spec.rb +11 -145
  25. data/spec/smooth_operator/delegation_spec.rb +54 -57
  26. data/spec/smooth_operator/finder_methods_spec.rb +2 -91
  27. data/spec/smooth_operator/{resource_name_spec.rb → model_schema_spec.rb} +2 -2
  28. data/spec/smooth_operator/operator_spec.rb +1 -1
  29. data/spec/smooth_operator/persistence_spec.rb +20 -140
  30. data/spec/smooth_operator/serialization_spec.rb +4 -28
  31. data/spec/spec_helper.rb +9 -7
  32. data/spec/support/models/address.rb +0 -9
  33. data/spec/support/models/post.rb +3 -9
  34. data/spec/support/models/user.rb +7 -30
  35. data/spec/support/models/user_with_address_and_posts.rb +12 -20
  36. data/spec/support/test_server.rb +7 -63
  37. metadata +18 -55
  38. data/lib/smooth_operator/associations.rb +0 -110
  39. data/lib/smooth_operator/associations/association_reflection.rb +0 -79
  40. data/lib/smooth_operator/associations/has_many_relation.rb +0 -45
  41. data/lib/smooth_operator/associations/reflection.rb +0 -41
  42. data/lib/smooth_operator/cookie_jar.rb +0 -21
  43. data/lib/smooth_operator/http_methods.rb +0 -17
  44. data/lib/smooth_operator/internal_data.rb +0 -45
  45. data/lib/smooth_operator/operators/connection_wrapper.rb +0 -15
  46. data/lib/smooth_operator/operators/faraday.rb +0 -75
  47. data/lib/smooth_operator/operators/typhoeus.rb +0 -87
  48. data/lib/smooth_operator/options.rb +0 -30
  49. data/lib/smooth_operator/remote_call/base.rb +0 -76
  50. data/lib/smooth_operator/remote_call/errors/connection_failed.rb +0 -20
  51. data/lib/smooth_operator/remote_call/errors/timeout.rb +0 -20
  52. data/lib/smooth_operator/remote_call/faraday.rb +0 -19
  53. data/lib/smooth_operator/remote_call/typhoeus.rb +0 -19
  54. data/lib/smooth_operator/resource_name.rb +0 -46
  55. data/lib/smooth_operator/schema.rb +0 -21
  56. data/lib/smooth_operator/type_casting.rb +0 -127
  57. data/spec/require_helper.rb +0 -11
  58. data/spec/smooth_operator/remote_call_spec.rb +0 -340
  59. data/spec/smooth_operator/validations_spec.rb +0 -42
  60. data/spec/support/models/comment.rb +0 -5
@@ -5,27 +5,6 @@ describe SmoothOperator::AttributeAssignment do
5
5
 
6
6
  describe "#assign_attributes" do
7
7
 
8
- describe "receiving data from server" do
9
- subject { User::Base.new }
10
-
11
- context "when receiving a Hash with meta_data on it" do
12
- before { subject.assign_attributes({ user: attributes_for(:user), status: 1 }) }
13
-
14
- it "#meta_data should reflect the receiving meta_data" do
15
- expect(subject._meta_data).to eq({ "status" => 1 })
16
- end
17
-
18
- it "subject should NOT contain meta_data" do
19
- expect{ subject.status }.to raise_error NoMethodError
20
- end
21
-
22
- it "subject should contain all other data" do
23
- expect(subject.attributes).to eq(attributes_for(:user))
24
- end
25
- end
26
-
27
- end
28
-
29
8
  describe "white and black list" do
30
9
  subject { UserWithAddressAndPosts::Son.new(attributes_for(:user_with_address_and_posts)) }
31
10
 
@@ -55,28 +34,14 @@ describe SmoothOperator::AttributeAssignment do
55
34
  context "when something other than a hash is introduced" do
56
35
  it "should do nothing" do
57
36
  [nil, '', [1, 2], 'test', 1, 2].each do |something_other_than_a_hash|
58
- expect(User::Base.new(something_other_than_a_hash).internal_data).to eq({})
37
+ expect(User.new(something_other_than_a_hash).internal_data).to eq({})
59
38
  end
60
39
  end
61
40
  end
62
41
 
63
42
  context "when one of the attribute's value, is an hash and is unknown to the schema" do
64
- context "when the .unknown_hash_class is unused", current: true do
65
- subject { User::Base.new(address: { street: 'something', postal_code: { code: '123' } }) }
66
-
67
- it "a new instance of OpenStruct will be initialized with that hash" do
68
- address = subject.address
69
-
70
- expect(address).to be_instance_of(OpenStruct)
71
- expect(address.street).to eq('something')
72
-
73
- expect(address.postal_code).to be_instance_of(OpenStruct)
74
- expect(address.postal_code.code).to eq('123')
75
- end
76
- end
77
-
78
- context "when the .unknown_hash_class is set to SmoothOperator::OpenStruct" do
79
- subject { User::UnknownHashClass::OpenStructBase.new(address: { street: 'something', postal_code: { code: '123' } }) }
43
+ context "when the .turn_unknown_hash_to_open_struct is set to true or unused" do
44
+ subject { User.new(address: { street: 'something', postal_code: { code: '123' } }) }
80
45
 
81
46
  it "a new instance of SmoothOperator::OpenStruct will be initialized with that hash" do
82
47
  address = subject.address
@@ -88,9 +53,9 @@ describe SmoothOperator::AttributeAssignment do
88
53
  expect(address.postal_code.code).to eq('123')
89
54
  end
90
55
  end
91
-
92
- context "when the .unknown_hash_class is set to nil" do
93
- subject { User::UnknownHashClass::None.new(creator: { first_name: 'admin', address: { street: 'something' } }) }
56
+
57
+ context "when the .turn_unknown_hash_to_open_struct is set to false", current: true do
58
+ subject { Post.new(creator: { first_name: 'admin', address: { street: 'something' } }) }
94
59
 
95
60
  it "the hash will be copied as it is" do
96
61
  creator = subject.creator
@@ -105,7 +70,7 @@ describe SmoothOperator::AttributeAssignment do
105
70
  end
106
71
 
107
72
  context "when there is no declared schema" do
108
- subject { User::Base.new(attributes_for(:user)) }
73
+ subject { User.new(attributes_for(:user)) }
109
74
  let(:expected_internal_data) { SmoothOperator::Helpers.stringify_keys(attributes_for(:user)) }
110
75
 
111
76
  it "it should populate 'internal_data' with unaltered duplicate data from the received hash" do
@@ -120,34 +85,6 @@ describe SmoothOperator::AttributeAssignment do
120
85
  context "when there is a known schema and the received hash has an attribute" do
121
86
  subject { UserWithAddressAndPosts::Son }
122
87
 
123
- context "that is declared (in schema) as an nil" do
124
-
125
- it "when the attributes's value is '1', should return '1'" do
126
- expect(subject.new(complex_field: '1').complex_field).to eq('1')
127
- end
128
-
129
- it "when the attributes's value is ['1', '2'], should return ['1', '2']" do
130
- expect(subject.new(complex_field: ['1', '2']).complex_field).to eq(['1', '2'])
131
- end
132
-
133
- it "when the attributes's value is 1, should be converted to 1" do
134
- expect(subject.new(complex_field: 1).complex_field).to eq(1)
135
- end
136
-
137
- it "when the attributes's value is { first_name: ['1', '2'] }, should be converted to { first_name: ['1', '2'] }" do
138
- expect(subject.new(complex_field: { first_name: ['1', '2'] }).complex_field).to eq({ first_name: ['1', '2'] })
139
- end
140
-
141
- it "when the attributes's value is -1, should be converted to -1" do
142
- expect(subject.new(complex_field: -1).complex_field).to eq(-1)
143
- end
144
-
145
- it "when the attributes's value is 0.35, should be converted to 0.35" do
146
- expect(subject.new(complex_field: 0.35).complex_field).to eq(0.35)
147
- end
148
-
149
- end
150
-
151
88
  context "that is declared (in schema) as an integer" do
152
89
 
153
90
  it "when the attributes's value is '1', should be converted to 1" do
@@ -176,50 +113,6 @@ describe SmoothOperator::AttributeAssignment do
176
113
 
177
114
  end
178
115
 
179
- context "that is declared (in schema) as an float" do
180
-
181
- it "when the attributes's value is '1', should be converted to 1" do
182
- expect(subject.new(price: '1').price).to eq(1.0)
183
- end
184
-
185
- it "when the attributes's value is '-1', should be converted to -1" do
186
- expect(subject.new(price: '-1').price).to eq(-1.0)
187
- end
188
-
189
- it "when the attributes's value is 's-10s', should be converted to -10" do
190
- expect(subject.new(price: 's-10s').price).to eq(-10.0)
191
- end
192
-
193
- it "when the attributes's value is ' 10s', should be converted to 10" do
194
- expect(subject.new(price: ' 10s').price).to eq(10.0)
195
- end
196
-
197
- it "when the attributes's value is 123, should be converted to 123" do
198
- expect(subject.new(price: 123).price).to eq(123.0)
199
- end
200
-
201
- it "when the attributes's value is -5, should be converted to -5" do
202
- expect(subject.new(price: -5).price).to eq(-5.0)
203
- end
204
-
205
- it "when the attributes's value is '12.3', should be converted to 12.3" do
206
- expect(subject.new(price: '12.3').price).to eq(12.3)
207
- end
208
-
209
- it "when the attributes's value is 's12.3s', should be converted to 12.3" do
210
- expect(subject.new(price: 's12.3s').price).to eq(12.3)
211
- end
212
-
213
- it "when the attributes's value is 's12,3s', should be converted to 12.3" do
214
- expect(subject.new(price: 's12,3s').price).to eq(12.3)
215
- end
216
-
217
- it "when the attributes's value is 1.2, should be converted to 1.2" do
218
- expect(subject.new(price: 1.2).price).to eq(1.2)
219
- end
220
-
221
- end
222
-
223
116
  context "that is declared (in schema) as an boolean" do
224
117
 
225
118
  it "when the attributes's value is true, should be converted to true" do
@@ -237,7 +130,7 @@ describe SmoothOperator::AttributeAssignment do
237
130
  it "when the attributes's value is 'false', should be converted to false" do
238
131
  expect(subject.new(manager: 'false').manager).to be(false)
239
132
  end
240
-
133
+
241
134
  it "when the attributes's value is '1', should be converted to true" do
242
135
  expect(subject.new(manager: '1').manager).to be(true)
243
136
  end
@@ -255,7 +148,7 @@ describe SmoothOperator::AttributeAssignment do
255
148
  end
256
149
 
257
150
  end
258
-
151
+
259
152
  context "that is declared (in schema) as an existing class" do
260
153
 
261
154
  it "if the attribute's value is an hash a new instance of that class will be initialized with that hash" do
@@ -271,12 +164,12 @@ describe SmoothOperator::AttributeAssignment do
271
164
 
272
165
  it "if the attribute's value is an array, a new instance of that class will be initialized for each array entry" do
273
166
  posts = subject.new(posts: [{ body: 'post1' }, { body: 'post2' }]).posts
274
-
167
+
275
168
  expect(posts.length).to be(2)
276
169
 
277
170
  expect(posts[0]).to be_instance_of(Post)
278
171
  expect(posts[0].body).to eq('post1')
279
-
172
+
280
173
  expect(posts[1]).to be_instance_of(Post)
281
174
  expect(posts[1].body).to eq('post2')
282
175
  end
@@ -308,33 +201,6 @@ describe SmoothOperator::AttributeAssignment do
308
201
 
309
202
  end
310
203
 
311
- context "that is declared (in schema) as a datetime" do
312
-
313
- it "if the attribute's value is a valid datetime string" do
314
- date = subject.new(date: '2-2-2222 12:30').date
315
-
316
- expect(date).to be_instance_of(DateTime)
317
- expect(date.day).to be(2)
318
- expect(date.month).to be(2)
319
- expect(date.year).to be(2222)
320
- expect(date.hour).to be(12)
321
- expect(date.min).to be(30)
322
- end
323
-
324
- it "if the attribute's value is a valid datetime" do
325
- date_now = DateTime.now
326
- date = subject.new(date: date_now).date
327
-
328
- expect(date).to be_instance_of(DateTime)
329
- expect(date).to eq(date_now)
330
- end
331
-
332
- it "if the attribute's value is an invalid datetime string, the returning value should be nil" do
333
- expect(subject.new(date: '2s-2-2222').date).to be_nil
334
- end
335
-
336
- end
337
-
338
204
  end
339
205
 
340
206
  end
@@ -45,7 +45,7 @@ describe SmoothOperator::Delegation do
45
45
  describe "#method_missing" do
46
46
 
47
47
  context "when calling a method that matches the initialized attributes" do
48
- subject { User::Base.new(attributes_for(:user)) }
48
+ subject { User.new(attributes_for(:user)) }
49
49
 
50
50
  it 'it should return the value of that same attribute' do
51
51
  attributes_for(:user).each do |key, value|
@@ -54,83 +54,80 @@ describe SmoothOperator::Delegation do
54
54
  end
55
55
  end
56
56
 
57
- context "when .strict_behaviour is true" do
58
- subject { UserWithAddressAndPosts::Son.new(attributes_for(:user_with_address_and_posts)) }
59
57
 
60
- context "when calling a method that doesn't match the initialized attributes but matches the schema" do
61
- it 'it should return nil' do
62
- expect(subject.manager).to eq(nil)
63
- end
58
+ subject { UserWithAddressAndPosts::Son.new(attributes_for(:user_with_address_and_posts)) }
64
59
 
65
- it "#respond_to? must return true" do
66
- expect(subject.respond_to?(:manager)).to eq(true)
67
- end
60
+ context "when calling a method that doesn't match the initialized attributes but matches the schema" do
61
+ it 'it should return nil' do
62
+ expect(subject.manager).to eq(nil)
68
63
  end
64
+ end
69
65
 
70
- context "when calling a method that doesn't match either the schema nor the initialized attributes" do
71
- it 'it should raise NoMethodError' do
72
- expect { subject.unknown_method }.to raise_error NoMethodError
73
- end
66
+ context "when calling a method that doesn't match either the schema nor the initialized attributes" do
67
+ it 'it should raise NoMethodError' do
68
+ expect { subject.unknown_method }.to raise_error NoMethodError
69
+ end
70
+ end
74
71
 
75
- it "#respond_to? must return false" do
76
- expect(subject.respond_to?(:unknown_method)).to eq(false)
77
- end
72
+ context "when setting a new attribute not declared on schema" do
73
+ before { subject.unknown_attribute = 'unknown_value' }
74
+
75
+ it "#known_attributes must reflect that new attribute" do
76
+ expect(subject.known_attributes.to_a).to include('unknown_attribute')
77
+ end
78
+
79
+ it "#respond_to? must return true" do
80
+ expect(subject.respond_to?(:unknown_attribute)).to eq(true)
78
81
  end
82
+
83
+ it "calling a method with the same name must return that attribute's value" do
84
+ expect(subject.unknown_attribute).to eq('unknown_value')
85
+ end
86
+ end
79
87
 
80
- context "when setting a new attribute not declared on schema" do
81
- before { subject.unknown_attribute = 'unknown_value' }
88
+ context "when no changes are made to an attribute" do
89
+ it "checking it that attribute is changed, should return false" do
90
+ expect(subject.first_name_changed?).to be false
91
+ end
82
92
 
83
- it "#known_attributes must reflect that new attribute" do
84
- expect(subject.known_attributes.to_a).to include('unknown_attribute')
85
- end
86
-
87
- it "#respond_to? must return true" do
88
- expect(subject.respond_to?(:unknown_attribute)).to eq(true)
89
- end
90
-
91
- it "calling a method with the same name must return that attribute's value" do
92
- expect(subject.unknown_attribute).to eq('unknown_value')
93
- end
93
+ it "checking that attribute past value, should its original value" do
94
+ expect(subject.first_name_was).to eq('John')
94
95
  end
95
96
  end
96
97
 
97
- context "when .strict_behaviour is false" do
98
- subject { UserWithAddressAndPosts::SoftBehaviour.new(attributes_for(:user_with_address_and_posts)) }
98
+ context "when there are changes made to an attribute" do
99
+ before { subject.first_name = 'nhoJ' }
99
100
 
100
- context "when calling a method that doesn't match the initialized attributes but matches the schema" do
101
- it 'it should return nil' do
102
- expect(subject.manager).to eq(nil)
103
- end
101
+ it "checking if that attribute is changed, should return true" do
102
+ expect(subject.first_name_changed?).to be true
103
+ end
104
104
 
105
- it "#respond_to? must return true" do
106
- expect(subject.respond_to?(:manager)).to eq(true)
107
- end
105
+ it "checking that attribute past value, should its original value" do
106
+ expect(subject.first_name_was).to eq('John')
108
107
  end
109
108
 
110
- context "when calling a method that doesn't match either the schema nor the initialized attributes" do
111
- it 'it should return nil' do
112
- expect(subject.unknown_method).to eq(nil)
109
+ context "when there are changes to the changes made to an attribute" do
110
+ before { subject.first_name = 'no_name' }
111
+
112
+ it "checking it that attribute is changed, should return true" do
113
+ expect(subject.first_name_changed?).to be true
113
114
  end
114
115
 
115
- it "#respond_to? must return false" do
116
- expect(subject.respond_to?(:unknown_method)).to eq(false)
116
+ it "checking that attribute past value, should its first original value" do
117
+ expect(subject.first_name_was).to eq('John')
117
118
  end
118
119
  end
120
+ end
119
121
 
120
- context "when setting a new attribute not declared on schema" do
121
- before { subject.unknown_attribute = 'unknown_value' }
122
+ context "when there are changes made to a nested object" do
123
+ before { subject.address.street = 'my street' }
122
124
 
123
- it "#known_attributes must reflect that new attribute" do
124
- expect(subject.known_attributes.to_a).to include('unknown_attribute')
125
- end
126
-
127
- it "#respond_to? must return true" do
128
- expect(subject.respond_to?(:unknown_attribute)).to eq(true)
129
- end
130
-
131
- it "calling a method with the same name must return that attribute's value" do
132
- expect(subject.unknown_attribute).to eq('unknown_value')
133
- end
125
+ it "checking if the nested object as changed, should return false" do
126
+ expect(subject.address_changed?).to be false
127
+ end
128
+
129
+ it "checking if the nested object's attribute as changed, should return true" do
130
+ expect(subject.address.street_changed?).to be true
134
131
  end
135
132
  end
136
133
 
@@ -1,100 +1,11 @@
1
1
  require "spec_helper"
2
2
 
3
- shared_examples_for "finder method" do
4
- it "it should return a RemoteCall instance with a subject's class instance" do
5
- expect(user).to be_instance_of(subject)
6
- end
7
-
8
- it "the instance class should be populated with the returned hash" do
9
- expect(user.attributes).to eq(attributes_for(:user_with_address_and_posts))
10
- end
11
- end
12
-
13
3
  describe SmoothOperator::FinderMethods do
14
- subject { UserWithAddressAndPosts::Son }
15
-
16
- # describe ".all" do
17
- # context "when NO arguments are passed" do
18
- # it "it should can .find(:all, {}, {})" do
19
- # expect(subject).to receive(:find).with(:all, {}, {})
20
- # subject.all
21
- # end
22
- # end
4
+ subject { User }
23
5
 
24
- # context "when arguments are passed" do
25
- # it "it should can .find(:all) with the same arguments that .alll has received" do
26
- # arguments = [{ id: 2 }, { http_verb: 'head' }]
27
-
28
- # expect(subject).to receive(:find).with(:all, *arguments)
29
-
30
- # subject.all(*arguments)
31
- # end
32
- # end
33
- # end
34
6
 
35
7
  describe ".find" do
36
- context "when the server returns a single hash" do
37
- let(:user) { subject.find(5).data }
38
-
39
- it_behaves_like "finder method"
40
- end
41
-
42
- context "when the server returns a hash with meta_data" do
43
- let(:user) { subject.find("5/with_metadata").data }
44
-
45
- it_behaves_like "finder method"
46
-
47
- it "#meta_data should reflect the receiving meta_data" do
48
- expect(user._meta_data).to eq({ "status" => 1 })
49
- end
50
-
51
- it "user should NOT contain meta_data" do
52
- expect { user.status }.to raise_error NoMethodError
53
- end
54
- end
55
-
56
- context "when the server returns an array" do
57
- it "it should return a RemoteCall instance an array that contains a subject's class instance, one for every array's entry" do
58
- remote_call = subject.find(:all)
59
- users = remote_call.data
60
-
61
- expect(users).to be_instance_of(Array)
62
- expect(users[0]).to be_instance_of(subject)
63
- expect(users[1]).to be_instance_of(subject)
64
- end
65
-
66
- it "if any of the array entries is not a hash, it shall not be converted or alteread" do
67
- remote_call = subject.find('misc_array')
68
- users = remote_call.data
69
-
70
- expect(users).to be_instance_of(Array)
71
- expect(users[0]).to be_instance_of(subject)
72
- expect(users[1]).to be(2)
73
- end
74
- end
75
-
76
- context "when the server returns a hash with a key (equal to subject's call.resources_name) containing an array" do
77
- it "it should return a RemoteCall instance an instance of ArrayWithMetaData" do
78
- remote_call = subject.find('with_metadata')
79
- users = remote_call.data
80
-
81
- expect(users).to be_instance_of(SmoothOperator::ArrayWithMetaData)
82
- expect(users.page).to be(1)
83
- expect(users.total).to be(6)
84
- users.each { |user| expect(user).to be_instance_of(subject) }
85
- end
86
- end
87
-
88
- context "when the server returns an array with nested object on each entry, with the same name has the resource" do
89
- it "it should return an Array with Class instances" do
90
- remote_call = subject.find('array_with_nested_users')
91
- users = remote_call.data
92
-
93
- expect(users).to be_instance_of(Array)
94
- users.each { |user| expect(user).to be_instance_of(subject) }
95
- end
96
- end
97
-
8
+
98
9
  end
99
10
 
100
11
  end