smooth_operator 1.20.10 → 1.21.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 (37) hide show
  1. checksums.yaml +8 -8
  2. data/README.md +2 -2
  3. data/lib/smooth_operator/array_with_meta_data.rb +20 -8
  4. data/lib/smooth_operator/attribute_assignment.rb +41 -54
  5. data/lib/smooth_operator/delegation.rb +14 -33
  6. data/lib/smooth_operator/finder_methods.rb +26 -17
  7. data/lib/smooth_operator/helpers.rb +10 -8
  8. data/lib/smooth_operator/internal_data.rb +45 -0
  9. data/lib/smooth_operator/open_struct.rb +9 -27
  10. data/lib/smooth_operator/operator.rb +11 -12
  11. data/lib/smooth_operator/persistence.rb +52 -41
  12. data/lib/smooth_operator/relation/array_relation.rb +2 -12
  13. data/lib/smooth_operator/relation/association_reflection.rb +2 -6
  14. data/lib/smooth_operator/relation/associations.rb +3 -3
  15. data/lib/smooth_operator/remote_call/base.rb +0 -4
  16. data/lib/smooth_operator/{model_name.rb → resource_name.rb} +2 -2
  17. data/lib/smooth_operator/schema.rb +9 -20
  18. data/lib/smooth_operator/serialization.rb +11 -11
  19. data/lib/smooth_operator/translation.rb +21 -12
  20. data/lib/smooth_operator/type_casting.rb +127 -0
  21. data/lib/smooth_operator/validations.rb +19 -3
  22. data/lib/smooth_operator/version.rb +1 -1
  23. data/lib/smooth_operator.rb +24 -3
  24. data/spec/smooth_operator/attribute_assignment_spec.rb +4 -13
  25. data/spec/smooth_operator/finder_methods_spec.rb +4 -9
  26. data/spec/smooth_operator/persistence_spec.rb +3 -15
  27. data/spec/smooth_operator/{model_name_spec.rb → resource_name_spec.rb} +1 -1
  28. data/spec/support/models/address.rb +0 -2
  29. data/spec/support/models/user.rb +3 -3
  30. data/spec/support/models/user_with_address_and_posts.rb +6 -14
  31. metadata +7 -12
  32. data/lib/smooth_operator/attribute_methods.rb +0 -92
  33. data/lib/smooth_operator/attributes/base.rb +0 -107
  34. data/lib/smooth_operator/attributes/dirty.rb +0 -29
  35. data/lib/smooth_operator/attributes/normal.rb +0 -15
  36. data/lib/smooth_operator/blank_slate.rb +0 -7
  37. data/spec/smooth_operator/attributes_dirty_spec.rb +0 -53
@@ -0,0 +1,127 @@
1
+ module SmoothOperator
2
+ module TypeCasting
3
+
4
+ # RIPPED FROM RAILS
5
+ TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set
6
+ FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set
7
+
8
+ extend self
9
+
10
+ def cast_to_type(name, value, parent_object)
11
+ type, known_attribute, unknown_hash_class = extract_args(parent_object.class, name)
12
+
13
+ return Helpers.duplicate(value) if known_attribute && type.nil?
14
+
15
+ case value
16
+ when Array
17
+ value.map { |array_entry| cast_to_type(name, array_entry, parent_object) }
18
+ when Hash
19
+ type.nil? ? new_unknown_hash(value, unknown_hash_class, parent_object) : type.new(value, parent_object: parent_object)
20
+ else
21
+ convert(value, type)
22
+ end
23
+ end
24
+
25
+ protected ##################### PROTECTED ########################
26
+
27
+ def extract_args(parent_object_class, name)
28
+ known_attribute, attribute_type = false, false
29
+
30
+ if parent_object_class.respond_to?(:known_attribute?)
31
+ known_attribute = parent_object_class.known_attribute?(name)
32
+ end
33
+
34
+ if parent_object_class.respond_to?(:attribute_type)
35
+ attribute_type = parent_object_class.attribute_type(name)
36
+ end
37
+
38
+ [
39
+ attribute_type,
40
+ known_attribute,
41
+ parent_object_class.unknown_hash_class
42
+ ]
43
+ end
44
+
45
+ def convert(value, type)
46
+ case type
47
+
48
+ when :string, :text, String
49
+ value.to_s
50
+
51
+ when :int, :integer, Integer, Fixnum
52
+ to_int(value)
53
+
54
+ when :date, Date
55
+ to_date(value)
56
+
57
+ when :float, Float
58
+ to_float(value)
59
+
60
+ when :bool, :boolean
61
+ to_boolean(value)
62
+
63
+ when :datetime, :date_time, DateTime
64
+ to_datetime(value)
65
+
66
+ else
67
+ Helpers.duplicate(value)
68
+ end
69
+ end
70
+
71
+ def to_date(string)
72
+ return string if string.is_a?(Date)
73
+
74
+ Date.parse(string) rescue nil
75
+ end
76
+
77
+ def to_datetime(string)
78
+ return string if string.is_a?(DateTime)
79
+
80
+ DateTime.parse(string) rescue nil
81
+ end
82
+
83
+ def to_boolean(string)
84
+ value = string.to_s.downcase
85
+
86
+ TRUE_VALUES.include?(value) ? true : FALSE_VALUES.include?(value) ? false : nil
87
+ end
88
+
89
+ def to_int(string)
90
+ return string if string.is_a?(Fixnum)
91
+
92
+ to_float(string).to_i
93
+ end
94
+
95
+ def to_float(string)
96
+ return string if string.is_a?(Float)
97
+
98
+ return 0 if string.nil? || !(string.is_a?(String) || string.is_a?(Fixnum))
99
+
100
+ value = string.to_s.gsub(',', '.').scan(/-*\d+[.]*\d*/).flatten.map(&:to_f).first
101
+
102
+ value.nil? ? 0 : value
103
+ end
104
+
105
+ def new_unknown_hash(hash, unknown_hash_class, parent_object)
106
+ if unknown_hash_class == :none
107
+ hash
108
+ else
109
+ unknown_hash_class.new(cast_params(hash, unknown_hash_class, parent_object))
110
+ end
111
+ end
112
+
113
+ private ################### PRIVATE #####################
114
+
115
+ def cast_params(attributes, unknown_hash_class, parent_object)
116
+ hash = {}
117
+
118
+ attributes.each do |key, value|
119
+ hash[key] = cast_to_type(key, value, parent_object)
120
+ end
121
+
122
+ hash
123
+ end
124
+
125
+ end
126
+
127
+ end
@@ -1,15 +1,31 @@
1
1
  module SmoothOperator
2
-
3
2
  module Validations
4
3
 
5
4
  def valid?(context = nil)
6
- Helpers.blank?(get_internal_data("errors"))
5
+ Helpers.blank?(_internal_errors)
7
6
  end
8
7
 
9
8
  def invalid?
10
9
  !valid?
11
10
  end
12
11
 
13
- end
12
+ def _internal_errors
13
+ @_internal_errors ||= {}
14
+ end
15
+
16
+ def self.included(base)
17
+ base.extend(ClassMethods)
18
+ end
19
+
20
+ module ClassMethods
14
21
 
22
+ def errors_key
23
+ Helpers.get_instance_variable(self, :errors_key, 'errors')
24
+ end
25
+
26
+ attr_writer :errors_key
27
+
28
+ end
29
+
30
+ end
15
31
  end
@@ -1,3 +1,3 @@
1
1
  module SmoothOperator
2
- VERSION = "1.20.10"
2
+ VERSION = "1.21.0"
3
3
  end
@@ -9,16 +9,18 @@ require "smooth_operator/finder_methods"
9
9
  require "smooth_operator/relation/associations"
10
10
 
11
11
  module SmoothOperator
12
- class Base < OpenStruct::Base
12
+ class Base < OpenStruct
13
13
 
14
+ extend Schema
14
15
  extend FinderMethods
16
+ extend Operator::HttpMethods
15
17
  extend Relation::Associations
16
18
  extend Translation if defined? I18n
17
19
 
18
- include Schema
19
20
  include Operator
20
21
  include Persistence
21
22
  include FinderMethods
23
+ include Operator::HttpMethods
22
24
 
23
25
  self.strict_behaviour = true
24
26
 
@@ -36,11 +38,30 @@ module SmoothOperator
36
38
  include ActiveModel::Conversion
37
39
 
38
40
  def column_for_attribute(attribute_name)
39
- type = get_attribute_type(attribute_name)
41
+ type = self.class.attribute_type(attribute_name)
40
42
 
41
43
  ActiveRecord::ConnectionAdapters::Column.new(attribute_name.to_sym, type, type)
42
44
  end
43
45
 
46
+ def save(relative_path = nil, data = {}, options = {})
47
+ # clear_server_errors
48
+ return false unless before_save
49
+
50
+ save_result = valid? ? super : false
51
+
52
+ # import_server_errors
53
+
54
+ after_save if valid? && save_result
55
+
56
+ save_result
57
+ end
58
+
59
+ def before_save
60
+ true
61
+ end
62
+
63
+ def after_save; end
64
+
44
65
  end
45
66
  end
46
67
  end
@@ -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,16 +75,16 @@ 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
@@ -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)
@@ -133,13 +133,6 @@ describe SmoothOperator::Persistence, helpers: :persistence do
133
133
  subject { new_user }
134
134
  let(:method_to_execute) { :reload }
135
135
 
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
136
  context "when subject doesn't has an id" do
144
137
  it "it should raise 'UnknownPath'" do
145
138
  expect{ subject.reload }.to raise_error 'UnknownPath'
@@ -155,11 +148,6 @@ describe SmoothOperator::Persistence, helpers: :persistence do
155
148
  it "it should fetch server data" do
156
149
  expect(subject.attributes).to eq(attributes_for(:user_with_address_and_posts))
157
150
  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
151
  end
164
152
 
165
153
  context "when calling #reload on a nested object" do
@@ -194,7 +182,7 @@ describe SmoothOperator::Persistence, helpers: :persistence do
194
182
  end
195
183
 
196
184
  describe ".create" do
197
-
185
+
198
186
  subject { created_subject }
199
187
  let(:method_arguments) { [] }
200
188
 
@@ -263,7 +251,7 @@ describe SmoothOperator::Persistence, helpers: :persistence do
263
251
  expect(subject.destroyed?).to be(true)
264
252
  end
265
253
  end
266
-
254
+
267
255
  context "after a failed execution of #destroy" do
268
256
  subject { existing_user }
269
257
  before { subject.destroy('', { status: 422 }) }
@@ -387,7 +375,7 @@ describe SmoothOperator::Persistence, helpers: :persistence do
387
375
  end
388
376
 
389
377
  describe "#destroy" do
390
-
378
+
391
379
  let(:method_to_execute) { :destroy }
392
380
  let(:method_arguments) { [] }
393
381
 
@@ -1,6 +1,6 @@
1
1
  require "spec_helper"
2
2
 
3
- describe SmoothOperator::ModelName do
3
+ describe SmoothOperator::ResourceName do
4
4
 
5
5
  describe "#known_attributes" do
6
6
  let(:initial_attributes_keys) { attributes_for(:user).keys.map(&:to_s) }
@@ -1,7 +1,5 @@
1
1
  class Address < SmoothOperator::Base
2
2
 
3
- self.dirty_attributes
4
-
5
3
  self.resource_name = ''
6
4
 
7
5
  self.endpoint_user = 'admin'
@@ -1,5 +1,5 @@
1
1
  module User
2
-
2
+
3
3
  class Base < SmoothOperator::Base
4
4
 
5
5
  self.resource_name = 'user'
@@ -19,9 +19,9 @@ module User
19
19
  end
20
20
 
21
21
  module UnknownHashClass
22
-
22
+
23
23
  class OpenStructBase < User::Base
24
- self.unknown_hash_class = SmoothOperator::OpenStruct::Base
24
+ self.unknown_hash_class = SmoothOperator::OpenStruct
25
25
  end
26
26
 
27
27
  class None < User::Base
@@ -1,7 +1,7 @@
1
1
  module UserWithAddressAndPosts
2
-
2
+
3
3
  class Father < User::Base
4
-
4
+
5
5
  self.resource_name = 'user'
6
6
 
7
7
  schema(
@@ -36,10 +36,10 @@ module UserWithAddressAndPosts
36
36
  self.update_http_verb = :patch
37
37
 
38
38
  end
39
-
39
+
40
40
 
41
41
  module UserBlackListed
42
-
42
+
43
43
  class Father < ::UserWithAddressAndPosts::Son
44
44
 
45
45
  attributes_black_list_add "last_name"
@@ -55,7 +55,7 @@ module UserWithAddressAndPosts
55
55
  end
56
56
 
57
57
  module UserWhiteListed
58
-
58
+
59
59
  class Father < ::UserWithAddressAndPosts::Son
60
60
 
61
61
  attributes_white_list_add "id"
@@ -69,7 +69,7 @@ module UserWithAddressAndPosts
69
69
  end
70
70
 
71
71
  end
72
-
72
+
73
73
  class UserWithMyMethod < UserWithAddressAndPosts::Son
74
74
 
75
75
  def my_method
@@ -78,12 +78,4 @@ module UserWithAddressAndPosts
78
78
 
79
79
  end
80
80
 
81
- class DirtyAttributes < UserWithAddressAndPosts::Son
82
-
83
- self.dirty_attributes
84
-
85
- self.unknown_hash_class = SmoothOperator::OpenStruct::Dirty
86
-
87
- end
88
-
89
81
  end