smooth_operator 1.20.10 → 1.21.0

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