smooth_operator 1.3.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
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