smooth_operator 1.2.9 → 1.3.0

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