smooth_operator 1.2.9 → 1.3.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 (52) hide show
  1. checksums.yaml +8 -8
  2. data/README.md +78 -22
  3. data/console.rb +2 -0
  4. data/lib/smooth_operator/array_with_meta_data.rb +20 -8
  5. data/lib/smooth_operator/{relation → associations}/association_reflection.rb +8 -8
  6. data/lib/smooth_operator/{relation/array_relation.rb → associations/has_many_relation.rb} +3 -13
  7. data/lib/smooth_operator/{relation → associations}/reflection.rb +2 -2
  8. data/lib/smooth_operator/associations.rb +110 -0
  9. data/lib/smooth_operator/attribute_assignment.rb +52 -60
  10. data/lib/smooth_operator/cookie_jar.rb +21 -0
  11. data/lib/smooth_operator/delegation.rb +13 -34
  12. data/lib/smooth_operator/finder_methods.rb +26 -17
  13. data/lib/smooth_operator/helpers.rb +14 -8
  14. data/lib/smooth_operator/http_methods.rb +17 -0
  15. data/lib/smooth_operator/internal_data.rb +45 -0
  16. data/lib/smooth_operator/open_struct.rb +11 -26
  17. data/lib/smooth_operator/operator.rb +65 -59
  18. data/lib/smooth_operator/operators/connection_wrapper.rb +15 -0
  19. data/lib/smooth_operator/operators/faraday.rb +6 -6
  20. data/lib/smooth_operator/operators/typhoeus.rb +22 -12
  21. data/lib/smooth_operator/options.rb +30 -0
  22. data/lib/smooth_operator/persistence.rb +64 -61
  23. data/lib/smooth_operator/remote_call/base.rb +7 -6
  24. data/lib/smooth_operator/resource_name.rb +46 -0
  25. data/lib/smooth_operator/schema.rb +21 -0
  26. data/lib/smooth_operator/serialization.rb +80 -36
  27. data/lib/smooth_operator/translation.rb +21 -12
  28. data/lib/smooth_operator/type_casting.rb +127 -0
  29. data/lib/smooth_operator/validations.rb +25 -3
  30. data/lib/smooth_operator/version.rb +1 -1
  31. data/lib/smooth_operator.rb +55 -5
  32. data/smooth_operator.gemspec +5 -5
  33. data/spec/smooth_operator/attribute_assignment_spec.rb +5 -14
  34. data/spec/smooth_operator/finder_methods_spec.rb +4 -9
  35. data/spec/smooth_operator/persistence_spec.rb +27 -19
  36. data/spec/smooth_operator/remote_call_spec.rb +104 -84
  37. data/spec/smooth_operator/{model_schema_spec.rb → resource_name_spec.rb} +1 -1
  38. data/spec/support/models/address.rb +8 -10
  39. data/spec/support/models/comment.rb +2 -0
  40. data/spec/support/models/post.rb +7 -7
  41. data/spec/support/models/user.rb +10 -13
  42. data/spec/support/models/user_with_address_and_posts.rb +9 -17
  43. data/spec/support/test_server.rb +7 -7
  44. metadata +25 -25
  45. data/lib/smooth_operator/attribute_methods.rb +0 -78
  46. data/lib/smooth_operator/attributes/base.rb +0 -107
  47. data/lib/smooth_operator/attributes/dirty.rb +0 -29
  48. data/lib/smooth_operator/attributes/normal.rb +0 -15
  49. data/lib/smooth_operator/blank_slate.rb +0 -7
  50. data/lib/smooth_operator/model_schema.rb +0 -81
  51. data/lib/smooth_operator/relation/associations.rb +0 -102
  52. data/spec/smooth_operator/attributes_dirty_spec.rb +0 -53
@@ -1,24 +1,29 @@
1
+ require "smooth_operator/schema"
1
2
  require "smooth_operator/version"
2
3
  require "smooth_operator/helpers"
3
4
  require "smooth_operator/operator"
4
5
  require "smooth_operator/persistence"
5
6
  require "smooth_operator/translation"
6
7
  require "smooth_operator/open_struct"
8
+ require "smooth_operator/http_methods"
9
+ require "smooth_operator/associations"
7
10
  require "smooth_operator/finder_methods"
8
- require "smooth_operator/relation/associations"
9
11
 
10
12
  module SmoothOperator
11
- class Base < OpenStruct::Base
13
+ class Base < OpenStruct
12
14
 
15
+ extend Schema
16
+ extend HttpMethods
17
+ extend Associations
13
18
  extend FinderMethods
14
- extend Relation::Associations
15
19
  extend Translation if defined? I18n
16
20
 
17
21
  include Operator
22
+ include HttpMethods
18
23
  include Persistence
19
24
  include FinderMethods
20
25
 
21
- self.strict_behaviour = true
26
+ options strict_behaviour: true
22
27
 
23
28
  def self.smooth_operator?
24
29
  true
@@ -33,12 +38,57 @@ module SmoothOperator
33
38
  include ActiveModel::Validations::Callbacks
34
39
  include ActiveModel::Conversion
35
40
 
41
+ options unknown_hash_class: SmoothOperator::OpenStruct
42
+
43
+ validate :validate_induced_errors, :validate_nested_objects
44
+
36
45
  def column_for_attribute(attribute_name)
37
- type = get_attribute_type(attribute_name)
46
+ type = self.class.attribute_type(attribute_name)
38
47
 
39
48
  ActiveRecord::ConnectionAdapters::Column.new(attribute_name.to_sym, type, type)
40
49
  end
41
50
 
51
+ def save(relative_path = nil, data = {}, options = {})
52
+ return false unless before_save
53
+
54
+ clear_induced_errors
55
+
56
+ save_result = valid? ? super : false
57
+
58
+ after_save if valid? && save_result
59
+
60
+ save_result
61
+ end
62
+
63
+ def before_save
64
+ true
65
+ end
66
+
67
+ def after_save; end
68
+
69
+ def self.model_name
70
+ smooth_model_name
71
+ end
72
+
73
+ protected ################# PROTECTED ###################
74
+
75
+ def validate_induced_errors
76
+ induced_errors.each do |key, value|
77
+ [*value].each do |_value|
78
+ self.errors.add(key, _value) unless self.errors.added?(key, _value)
79
+ end
80
+ end
81
+
82
+ Helpers.blank?(induced_errors)
83
+ end
84
+
85
+ def validate_nested_objects
86
+ all_nested_objects = self.class.reflections.keys
87
+ .map { |association| send(association) }.flatten.compact
88
+
89
+ all_nested_objects.map { |nested_object| nested_object.valid? }.all?
90
+ end
91
+
42
92
  end
43
93
  end
44
94
  end
@@ -20,12 +20,12 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
21
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
22
  spec.require_paths = ["lib"]
23
-
23
+
24
24
  spec.add_development_dependency "bundler", "~> 1.3"
25
-
26
- spec.add_dependency "json"
27
- spec.add_dependency "faraday", "~> 0.8.9"
28
- spec.add_dependency "typhoeus", "~> 0.6.8"
25
+
26
+ spec.add_dependency "json", '~> 1.5'
27
+ spec.add_dependency "faraday", "~> 0.8"
28
+ spec.add_dependency "typhoeus", "~> 0.6"
29
29
 
30
30
  # this is necessary if you want to typhoeus to correctly encode arrays
31
31
  # spec.add_dependency "ethon", :git => 'https://github.com/goncalvesjoao/ethon'
@@ -8,15 +8,6 @@ describe SmoothOperator::AttributeAssignment do
8
8
  describe "receiving data from server" do
9
9
  subject { User::Base.new }
10
10
 
11
- context "when receiving the option 'from_server = true'" do
12
- before { subject.assign_attributes({}, from_server: true) }
13
-
14
- it "#has_data_from_server and #from_server should return true" do
15
- expect(subject.has_data_from_server).to be true
16
- expect(subject.from_server).to be true
17
- end
18
- end
19
-
20
11
  context "when receiving a Hash with meta_data on it" do
21
12
  before { subject.assign_attributes({ user: attributes_for(:user), status: 1 }) }
22
13
 
@@ -84,21 +75,21 @@ describe SmoothOperator::AttributeAssignment do
84
75
  end
85
76
  end
86
77
 
87
- context "when the .unknown_hash_class is set to SmoothOperator::OpenStruct::Base" do
78
+ context "when the .unknown_hash_class is set to SmoothOperator::OpenStruct" do
88
79
  subject { User::UnknownHashClass::OpenStructBase.new(address: { street: 'something', postal_code: { code: '123' } }) }
89
80
 
90
- it "a new instance of SmoothOperator::OpenStruct::Base will be initialized with that hash" do
81
+ it "a new instance of SmoothOperator::OpenStruct will be initialized with that hash" do
91
82
  address = subject.address
92
83
 
93
- expect(address).to be_instance_of(SmoothOperator::OpenStruct::Base)
84
+ expect(address).to be_instance_of(SmoothOperator::OpenStruct)
94
85
  expect(address.street).to eq('something')
95
86
 
96
- expect(address.postal_code).to be_instance_of(SmoothOperator::OpenStruct::Base)
87
+ expect(address.postal_code).to be_instance_of(SmoothOperator::OpenStruct)
97
88
  expect(address.postal_code.code).to eq('123')
98
89
  end
99
90
  end
100
91
 
101
- context "when the .unknown_hash_class is set to :none" do
92
+ context "when the .unknown_hash_class is set to nil" do
102
93
  subject { User::UnknownHashClass::None.new(creator: { first_name: 'admin', address: { street: 'something' } }) }
103
94
 
104
95
  it "the hash will be copied as it is" do
@@ -8,11 +8,6 @@ shared_examples_for "finder method" do
8
8
  it "the instance class should be populated with the returned hash" do
9
9
  expect(user.attributes).to eq(attributes_for(:user_with_address_and_posts))
10
10
  end
11
-
12
- it "#has_data_from_server and #from_server should return true" do
13
- expect(user.has_data_from_server).to be true
14
- expect(user.from_server).to be true
15
- end
16
11
  end
17
12
 
18
13
  describe SmoothOperator::FinderMethods do
@@ -39,13 +34,13 @@ describe SmoothOperator::FinderMethods do
39
34
 
40
35
  describe ".find" do
41
36
  context "when the server returns a single hash" do
42
- let(:user) { subject.find(5).object }
37
+ let(:user) { subject.find(5).data }
43
38
 
44
39
  it_behaves_like "finder method"
45
40
  end
46
41
 
47
42
  context "when the server returns a hash with meta_data" do
48
- let(:user) { subject.find("5/with_metadata").object }
43
+ let(:user) { subject.find("5/with_metadata").data }
49
44
 
50
45
  it_behaves_like "finder method"
51
46
 
@@ -61,7 +56,7 @@ describe SmoothOperator::FinderMethods do
61
56
  context "when the server returns an array" do
62
57
  it "it should return a RemoteCall instance an array that contains a subject's class instance, one for every array's entry" do
63
58
  remote_call = subject.find(:all)
64
- users = remote_call.objects
59
+ users = remote_call.data
65
60
 
66
61
  expect(users).to be_instance_of(Array)
67
62
  expect(users[0]).to be_instance_of(subject)
@@ -70,7 +65,7 @@ describe SmoothOperator::FinderMethods do
70
65
 
71
66
  it "if any of the array entries is not a hash, it shall not be converted or alteread" do
72
67
  remote_call = subject.find('misc_array')
73
- users = remote_call.objects
68
+ users = remote_call.data
74
69
 
75
70
  expect(users).to be_instance_of(Array)
76
71
  expect(users[0]).to be_instance_of(subject)
@@ -23,7 +23,12 @@ shared_examples_for "persistent remote call" do
23
23
  let(:method_arguments) { ['', { status: 200 }] }
24
24
 
25
25
  it "it should return true" do
26
- execute_method
26
+ result = execute_method
27
+
28
+ if !(method_to_execute.to_s =~ /create/)
29
+ expect(result).to be true
30
+ end
31
+
27
32
  expect(subject.last_remote_call.ok?).to be true
28
33
  expect(subject.last_remote_call.status).to be true
29
34
  end
@@ -40,7 +45,12 @@ shared_examples_for "persistent remote call" do
40
45
  let(:method_arguments) { ['', { status: 422 }] }
41
46
 
42
47
  it "it should return false" do
43
- execute_method
48
+ result = execute_method
49
+
50
+ if !(method_to_execute.to_s =~ /create/)
51
+ expect(result).to be false
52
+ end
53
+
44
54
  expect(subject.last_remote_call.not_processed?).to be true
45
55
  expect(subject.last_remote_call.status).to be false
46
56
  end
@@ -57,7 +67,12 @@ shared_examples_for "persistent remote call" do
57
67
  let(:method_arguments) { ['', { status: 404 }] }
58
68
 
59
69
  it "it should return nil" do
60
- execute_method
70
+ result = execute_method
71
+
72
+ if !(method_to_execute.to_s =~ /create/)
73
+ expect(result).to be nil
74
+ end
75
+
61
76
  expect(subject.last_remote_call.client_error?).to be true
62
77
  expect(subject.last_remote_call.error?).to be true
63
78
  expect(subject.last_remote_call.status).to be nil
@@ -73,7 +88,12 @@ shared_examples_for "persistent remote call" do
73
88
  let(:method_arguments) { ['', { status: 500 }] }
74
89
 
75
90
  it "it should return nil" do
76
- execute_method
91
+ result = execute_method
92
+
93
+ if !(method_to_execute.to_s =~ /create/)
94
+ expect(result).to be nil
95
+ end
96
+
77
97
  expect(subject.last_remote_call.server_error?).to be true
78
98
  expect(subject.last_remote_call.error?).to be true
79
99
  expect(subject.last_remote_call.status).to be_nil
@@ -133,13 +153,6 @@ describe SmoothOperator::Persistence, helpers: :persistence do
133
153
  subject { new_user }
134
154
  let(:method_to_execute) { :reload }
135
155
 
136
- context "before calling #reload" do
137
- it "#has_data_from_server and #from_server should return false" do
138
- expect(subject.from_server).to be_falsey
139
- expect(subject.has_data_from_server).to be_falsey
140
- end
141
- end
142
-
143
156
  context "when subject doesn't has an id" do
144
157
  it "it should raise 'UnknownPath'" do
145
158
  expect{ subject.reload }.to raise_error 'UnknownPath'
@@ -155,11 +168,6 @@ describe SmoothOperator::Persistence, helpers: :persistence do
155
168
  it "it should fetch server data" do
156
169
  expect(subject.attributes).to eq(attributes_for(:user_with_address_and_posts))
157
170
  end
158
-
159
- it "#has_data_from_server and #from_server should return true" do
160
- expect(subject.from_server).to be true
161
- expect(subject.has_data_from_server).to be true
162
- end
163
171
  end
164
172
 
165
173
  context "when calling #reload on a nested object" do
@@ -194,7 +202,7 @@ describe SmoothOperator::Persistence, helpers: :persistence do
194
202
  end
195
203
 
196
204
  describe ".create" do
197
-
205
+
198
206
  subject { created_subject }
199
207
  let(:method_arguments) { [] }
200
208
 
@@ -263,7 +271,7 @@ describe SmoothOperator::Persistence, helpers: :persistence do
263
271
  expect(subject.destroyed?).to be(true)
264
272
  end
265
273
  end
266
-
274
+
267
275
  context "after a failed execution of #destroy" do
268
276
  subject { existing_user }
269
277
  before { subject.destroy('', { status: 422 }) }
@@ -387,7 +395,7 @@ describe SmoothOperator::Persistence, helpers: :persistence do
387
395
  end
388
396
 
389
397
  describe "#destroy" do
390
-
398
+
391
399
  let(:method_to_execute) { :destroy }
392
400
  let(:method_arguments) { [] }
393
401