smooth_operator 1.10.6 → 1.10.9
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.
- checksums.yaml +8 -8
- data/lib/smooth_operator/array_with_meta_data.rb +3 -2
- data/lib/smooth_operator/attribute_assignment.rb +36 -11
- data/lib/smooth_operator/attributes/base.rb +91 -5
- data/lib/smooth_operator/attributes/dirty.rb +7 -9
- data/lib/smooth_operator/attributes/normal.rb +15 -0
- data/lib/smooth_operator/finder_methods.rb +5 -3
- data/lib/smooth_operator/operators/faraday.rb +11 -8
- data/lib/smooth_operator/persistence.rb +26 -20
- data/lib/smooth_operator/version.rb +1 -1
- data/spec/smooth_operator/attribute_assignment_spec.rb +30 -0
- data/spec/smooth_operator/finder_methods_spec.rb +30 -5
- data/spec/smooth_operator/persistence_spec.rb +46 -0
- data/spec/support/test_server.rb +9 -3
- metadata +3 -3
- data/lib/smooth_operator/type_converter.rb +0 -104
checksums.yaml
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
!binary "U0hBMQ==":
|
|
3
3
|
metadata.gz: !binary |-
|
|
4
|
-
|
|
4
|
+
NTYxNDdmNGJiNTIzZWU5YTc2MzlmYTkzYTJjOTZjOTcyZWU5YjMwMg==
|
|
5
5
|
data.tar.gz: !binary |-
|
|
6
|
-
|
|
6
|
+
YjlhYzNiNWUwNDdiYjUyOTMyNDY2YjhmYmM3YmE0YzNlM2I2MTBjZQ==
|
|
7
7
|
SHA512:
|
|
8
8
|
metadata.gz: !binary |-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
NTQwNzAxYTExZDA0ZjE3YWMwNDJhMDljZjRkMWU3OTFlNzgzNWU4ODliN2Ez
|
|
10
|
+
MzI2NjUyZDMyYjdlYjJmYThkOGUzN2NlYjkxMTc1ODNiMGUyNmJiNzdkMjdl
|
|
11
|
+
OTVhYjAwNDlhZTliNDA4ODE1ODM1NWY5NzIwYTExN2MyOWZjNTQ=
|
|
12
12
|
data.tar.gz: !binary |-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
YTc1ODM3MmZlNmFjOGM0ZjYzNjc3ZTA0MmE1NGRlNzkyOWQzM2NiNmM5MTc2
|
|
14
|
+
YjE0ZDg5YjcxNWY1ODk4ODE5ZjNkZWMyOWVmNWZlNzE5MTRiMTdmOTY1MmYz
|
|
15
|
+
MTVlMDBhZGQyNjg1ZDNjMGJjM2I1YmE4MmY5OWFkZGNiNDg2Zjk=
|
|
@@ -10,10 +10,11 @@ module SmoothOperator
|
|
|
10
10
|
|
|
11
11
|
def_delegators :internal_array, :length, :<<, :[]
|
|
12
12
|
|
|
13
|
-
def initialize(attributes,
|
|
13
|
+
def initialize(attributes, object_class)
|
|
14
14
|
_attributes = attributes.dup
|
|
15
15
|
|
|
16
|
-
@
|
|
16
|
+
@object_class = object_class
|
|
17
|
+
@table_name = object_class.table_name
|
|
17
18
|
|
|
18
19
|
@internal_array = [*_attributes[table_name]].map { |array_entry| object_class.new(array_entry).tap { |object| object.reloaded = true } }
|
|
19
20
|
_attributes.delete(table_name)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'smooth_operator/attributes/base'
|
|
2
2
|
require 'smooth_operator/attributes/dirty'
|
|
3
|
+
require 'smooth_operator/attributes/normal'
|
|
3
4
|
|
|
4
5
|
module SmoothOperator
|
|
5
6
|
|
|
@@ -44,20 +45,44 @@ module SmoothOperator
|
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
|
|
47
|
-
def initialize(attributes = {})
|
|
48
|
-
|
|
48
|
+
def initialize(attributes = {}, options = {})
|
|
49
|
+
@_options = {}
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
before_initialize(attributes, options)
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
assign_attributes attributes, options
|
|
54
|
+
|
|
55
|
+
after_initialize(attributes, options)
|
|
53
56
|
end
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
attr_reader :_options, :_meta_data
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def assign_attributes(_attributes = {}, options = {})
|
|
62
|
+
return nil unless _attributes.is_a?(Hash)
|
|
63
|
+
|
|
64
|
+
attributes = _attributes = Helpers.stringify_keys(_attributes)
|
|
65
|
+
|
|
66
|
+
if _attributes.include?(model_name)
|
|
67
|
+
attributes = _attributes.delete(model_name)
|
|
68
|
+
@_meta_data = _attributes
|
|
69
|
+
end
|
|
57
70
|
|
|
71
|
+
options.each { |key, value| @_options[key] = value } if options.is_a?(Hash)
|
|
72
|
+
|
|
58
73
|
attributes.each { |name, value| push_to_internal_data(name, value) }
|
|
59
74
|
end
|
|
60
75
|
|
|
76
|
+
def parent_object
|
|
77
|
+
_options[:parent_object]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def has_data_from_server
|
|
81
|
+
_options[:from_server] == true
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
alias :from_server :has_data_from_server
|
|
85
|
+
|
|
61
86
|
def internal_data
|
|
62
87
|
@internal_data ||= {}
|
|
63
88
|
end
|
|
@@ -91,9 +116,9 @@ module SmoothOperator
|
|
|
91
116
|
|
|
92
117
|
protected #################### PROTECTED METHODS DOWN BELOW ######################
|
|
93
118
|
|
|
94
|
-
def before_initialize(attributes); end
|
|
119
|
+
def before_initialize(attributes, options); end
|
|
95
120
|
|
|
96
|
-
def after_initialize(attributes); end
|
|
121
|
+
def after_initialize(attributes, options); end
|
|
97
122
|
|
|
98
123
|
def allowed_attribute(attribute)
|
|
99
124
|
if !self.class.attributes_white_list.empty?
|
|
@@ -116,16 +141,16 @@ module SmoothOperator
|
|
|
116
141
|
|
|
117
142
|
def update_internal_data(attribute_name, attribute_value)
|
|
118
143
|
if self.class.dirty_attributes?
|
|
119
|
-
internal_data[attribute_name].set_value(attribute_value)
|
|
144
|
+
internal_data[attribute_name].set_value(attribute_value, self.class.unknown_hash_class, self)
|
|
120
145
|
else
|
|
121
146
|
internal_data[attribute_name] = new_attribute_object(attribute_name, attribute_value).value
|
|
122
147
|
end
|
|
123
148
|
end
|
|
124
149
|
|
|
125
150
|
def new_attribute_object(attribute_name, attribute_value)
|
|
126
|
-
attribute_class = self.class.dirty_attributes? ? Attributes::Dirty : Attributes::
|
|
151
|
+
attribute_class = self.class.dirty_attributes? ? Attributes::Dirty : Attributes::Normal
|
|
127
152
|
|
|
128
|
-
attribute_class.new(attribute_name, attribute_value, internal_structure[attribute_name], self.class.unknown_hash_class)
|
|
153
|
+
attribute_class.new(attribute_name, attribute_value, internal_structure[attribute_name], self.class.unknown_hash_class, self)
|
|
129
154
|
end
|
|
130
155
|
|
|
131
156
|
end
|
|
@@ -1,14 +1,100 @@
|
|
|
1
|
-
require 'smooth_operator/type_converter'
|
|
2
|
-
|
|
3
1
|
module SmoothOperator
|
|
4
2
|
module Attributes
|
|
5
3
|
|
|
6
4
|
class Base
|
|
7
5
|
|
|
8
|
-
|
|
6
|
+
protected ##################### PROTECTED ########################
|
|
7
|
+
|
|
8
|
+
def cast_to_type(name, value, type, unknown_hash_class, parent_object)
|
|
9
|
+
case value
|
|
10
|
+
when Array
|
|
11
|
+
value.map { |array_entry| self.class.new(name, array_entry, type, unknown_hash_class, parent_object).value }
|
|
12
|
+
when Hash
|
|
13
|
+
type.nil? ? new_unknown_hash(value, unknown_hash_class, parent_object) : type.new(value, parent_object: parent_object)
|
|
14
|
+
else
|
|
15
|
+
convert(value, type)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def convert(value, type)
|
|
20
|
+
case type
|
|
21
|
+
|
|
22
|
+
when :string, :text, String
|
|
23
|
+
value.to_s
|
|
24
|
+
|
|
25
|
+
when :int, :integer, Integer, Fixnum
|
|
26
|
+
to_int(value)
|
|
27
|
+
|
|
28
|
+
when :date, Date
|
|
29
|
+
to_date(value)
|
|
30
|
+
|
|
31
|
+
when :float, Float
|
|
32
|
+
to_float(value)
|
|
33
|
+
|
|
34
|
+
when :bool, :boolean
|
|
35
|
+
to_boolean(value)
|
|
36
|
+
|
|
37
|
+
when :datetime, :date_time, DateTime
|
|
38
|
+
to_datetime(value)
|
|
39
|
+
|
|
40
|
+
else
|
|
41
|
+
Helpers.duplicate(value)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def to_int(string)
|
|
46
|
+
return string if string.is_a?(Fixnum)
|
|
47
|
+
|
|
48
|
+
to_float(string).to_i
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_date(string)
|
|
52
|
+
return string if string.is_a?(Date)
|
|
53
|
+
|
|
54
|
+
Date.parse(string) rescue nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def to_datetime(string)
|
|
58
|
+
return string if string.is_a?(DateTime)
|
|
59
|
+
|
|
60
|
+
DateTime.parse(string) rescue nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def to_boolean(string)
|
|
64
|
+
value = string.to_s.downcase
|
|
65
|
+
|
|
66
|
+
['1', 'true'].include?(value) ? true : ['0', 'false'].include?(value) ? false : nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def to_float(string)
|
|
70
|
+
return string if string.is_a?(Float)
|
|
71
|
+
|
|
72
|
+
return 0 if string.nil? || !string.is_a?(String)
|
|
73
|
+
|
|
74
|
+
value = string.scan(/-*\d+[,.]*\d*/).flatten.map(&:to_f).first
|
|
75
|
+
|
|
76
|
+
value.nil? ? 0 : value
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def new_unknown_hash(hash, unknown_hash_class, parent_object)
|
|
80
|
+
if unknown_hash_class == :none
|
|
81
|
+
hash
|
|
82
|
+
else
|
|
83
|
+
unknown_hash_class.new(cast_params(hash, unknown_hash_class, parent_object))
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
private ################### PRIVATE #####################
|
|
89
|
+
|
|
90
|
+
def cast_params(attributes, unknown_hash_class, parent_object)
|
|
91
|
+
hash = {}
|
|
92
|
+
|
|
93
|
+
attributes.each do |key, value|
|
|
94
|
+
hash[key] = cast_to_type(key, value, nil, unknown_hash_class, parent_object)
|
|
95
|
+
end
|
|
9
96
|
|
|
10
|
-
|
|
11
|
-
@value = TypeConverter.cast_to_type(name, value, type, self.class, unknown_hash_class)
|
|
97
|
+
hash
|
|
12
98
|
end
|
|
13
99
|
|
|
14
100
|
end
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
require 'smooth_operator/type_converter'
|
|
2
|
-
|
|
3
1
|
module SmoothOperator
|
|
4
2
|
module Attributes
|
|
5
3
|
|
|
6
|
-
class Dirty
|
|
4
|
+
class Dirty < Base
|
|
7
5
|
|
|
8
|
-
attr_reader :original_name, :original_value, :first_value, :value, :type
|
|
6
|
+
attr_reader :original_name, :original_value, :first_value, :value, :type
|
|
9
7
|
|
|
10
|
-
def initialize(name, value, type, unknown_hash_class)
|
|
11
|
-
@original_name, @original_value, @type
|
|
8
|
+
def initialize(name, value, type, unknown_hash_class, parent_object)
|
|
9
|
+
@original_name, @original_value, @type = name, value, type
|
|
12
10
|
|
|
13
|
-
@first_value = set_value(value)
|
|
11
|
+
@first_value = set_value(value, unknown_hash_class, parent_object)
|
|
14
12
|
end
|
|
15
13
|
|
|
16
|
-
def set_value(new_value)
|
|
17
|
-
@value =
|
|
14
|
+
def set_value(new_value, unknown_hash_class, parent_object)
|
|
15
|
+
@value = cast_to_type(original_name, new_value, type, unknown_hash_class, parent_object)
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
def changed?
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module SmoothOperator
|
|
2
|
+
module Attributes
|
|
3
|
+
|
|
4
|
+
class Normal < Base
|
|
5
|
+
|
|
6
|
+
attr_reader :value
|
|
7
|
+
|
|
8
|
+
def initialize(name, value, type, unknown_hash_class, parent_object)
|
|
9
|
+
@value = cast_to_type(name, value, type, unknown_hash_class, parent_object)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -27,13 +27,15 @@ module SmoothOperator
|
|
|
27
27
|
|
|
28
28
|
def build_object(parsed_response, options)
|
|
29
29
|
options ||={}
|
|
30
|
-
|
|
31
|
-
table_name = (options[:table_name] || self.table_name).to_s
|
|
32
30
|
|
|
33
31
|
if parsed_response.is_a?(Array)
|
|
34
32
|
parsed_response.map { |array_entry| build_object(array_entry, options) }
|
|
35
33
|
elsif parsed_response.is_a?(Hash)
|
|
36
|
-
parsed_response.include?(table_name)
|
|
34
|
+
if parsed_response.include?(table_name)
|
|
35
|
+
ArrayWithMetaData.new(parsed_response, self)
|
|
36
|
+
else
|
|
37
|
+
new(parsed_response, from_server: true)
|
|
38
|
+
end
|
|
37
39
|
else
|
|
38
40
|
parsed_response
|
|
39
41
|
end
|
|
@@ -12,14 +12,7 @@ module SmoothOperator
|
|
|
12
12
|
remote_call = begin
|
|
13
13
|
set_basic_authentication
|
|
14
14
|
|
|
15
|
-
response = connection.send(http_verb)
|
|
16
|
-
operator_options.each { |key, value| request.options.send("#{key}=", value) }
|
|
17
|
-
options[:headers].each { |key, value| request.headers[key] = value }
|
|
18
|
-
params.each { |key, value| request.params[key] = value }
|
|
19
|
-
|
|
20
|
-
request.url relative_path
|
|
21
|
-
request.body = body
|
|
22
|
-
end
|
|
15
|
+
response = connection.send(http_verb, relative_path) { |request| request_configuration(request) }
|
|
23
16
|
|
|
24
17
|
RemoteCall::Faraday.new(response)
|
|
25
18
|
rescue ::Faraday::Error::ConnectionFailed
|
|
@@ -40,6 +33,16 @@ module SmoothOperator
|
|
|
40
33
|
connection.basic_auth(endpoint_user, endpoint_pass) if Helpers.present?(endpoint_user)
|
|
41
34
|
end
|
|
42
35
|
|
|
36
|
+
def request_configuration(request)
|
|
37
|
+
operator_options.each { |key, value| request.options.send("#{key}=", value) }
|
|
38
|
+
|
|
39
|
+
options[:headers].each { |key, value| request.headers[key] = value }
|
|
40
|
+
|
|
41
|
+
params.each { |key, value| request.params[key] = value }
|
|
42
|
+
|
|
43
|
+
request.body = body
|
|
44
|
+
end
|
|
45
|
+
|
|
43
46
|
end
|
|
44
47
|
|
|
45
48
|
end
|
|
@@ -27,10 +27,13 @@ module SmoothOperator
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
attr_accessor :extra_params
|
|
30
|
+
attr_accessor :extra_params
|
|
31
31
|
|
|
32
32
|
def reload(relative_path = nil, data = {}, options = {})
|
|
33
|
-
|
|
33
|
+
raise 'UnknownPath' if !respond_to?(:id) || Helpers.blank?(id)
|
|
34
|
+
|
|
35
|
+
relative_path, options = build_relative_path(relative_path, options)
|
|
36
|
+
|
|
34
37
|
success = {}
|
|
35
38
|
|
|
36
39
|
make_remote_call(:get, relative_path, data, options) do |remote_call|
|
|
@@ -52,12 +55,6 @@ module SmoothOperator
|
|
|
52
55
|
@destroyed = false
|
|
53
56
|
end
|
|
54
57
|
|
|
55
|
-
def reloaded?
|
|
56
|
-
return @reloaded if defined?(@reloaded)
|
|
57
|
-
|
|
58
|
-
@reloaded = false
|
|
59
|
-
end
|
|
60
|
-
|
|
61
58
|
def persisted?
|
|
62
59
|
!(new_record? || destroyed?)
|
|
63
60
|
end
|
|
@@ -73,7 +70,7 @@ module SmoothOperator
|
|
|
73
70
|
def destroy(relative_path = nil, data = {}, options = {})
|
|
74
71
|
return false unless persisted?
|
|
75
72
|
|
|
76
|
-
relative_path =
|
|
73
|
+
relative_path, options = build_relative_path(relative_path, options)
|
|
77
74
|
|
|
78
75
|
success = {}
|
|
79
76
|
|
|
@@ -106,7 +103,7 @@ module SmoothOperator
|
|
|
106
103
|
end
|
|
107
104
|
|
|
108
105
|
def update(relative_path, data, options)
|
|
109
|
-
relative_path =
|
|
106
|
+
relative_path, options = build_relative_path(relative_path, options)
|
|
110
107
|
|
|
111
108
|
success = {}
|
|
112
109
|
|
|
@@ -120,6 +117,24 @@ module SmoothOperator
|
|
|
120
117
|
|
|
121
118
|
private ##################### PRIVATE ####################
|
|
122
119
|
|
|
120
|
+
def build_relative_path(relative_path, options)
|
|
121
|
+
if Helpers.blank?(relative_path)
|
|
122
|
+
if parent_object.nil?
|
|
123
|
+
relative_path = id.to_s
|
|
124
|
+
else
|
|
125
|
+
options ||= {}
|
|
126
|
+
options[:table_name] = ''
|
|
127
|
+
relative_path = "#{parent_object.send(:rest_relative_path)}/#{table_name}/#{id}"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
[relative_path, options]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def rest_relative_path
|
|
135
|
+
"#{table_name}/#{id}"
|
|
136
|
+
end
|
|
137
|
+
|
|
123
138
|
def make_remote_call(http_verb, relative_path, data, options)
|
|
124
139
|
data ||= {}
|
|
125
140
|
data.merge!(extra_params || {})
|
|
@@ -132,16 +147,7 @@ module SmoothOperator
|
|
|
132
147
|
returning_data = @last_remote_call.parsed_response
|
|
133
148
|
|
|
134
149
|
if !@last_remote_call.error? && returning_data.is_a?(Hash)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
attributes = returning_data
|
|
138
|
-
|
|
139
|
-
if returning_data.include?(model_name)
|
|
140
|
-
attributes = returning_data.delete(model_name)
|
|
141
|
-
@meta_data = returning_data
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
assign_attributes attributes
|
|
150
|
+
assign_attributes returning_data, from_server: true
|
|
145
151
|
end
|
|
146
152
|
|
|
147
153
|
yield(remote_call)
|
|
@@ -4,6 +4,36 @@ require "spec_helper"
|
|
|
4
4
|
describe SmoothOperator::AttributeAssignment do
|
|
5
5
|
|
|
6
6
|
describe "#assign_attributes" do
|
|
7
|
+
|
|
8
|
+
describe "receiving data from server" do
|
|
9
|
+
subject { User::Base.new }
|
|
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
|
+
context "when receiving a Hash with meta_data on it" do
|
|
21
|
+
before { subject.assign_attributes({ user: attributes_for(:user), status: 1 }) }
|
|
22
|
+
|
|
23
|
+
it "#meta_data should reflect the receiving meta_data" do
|
|
24
|
+
expect(subject._meta_data).to eq({ "status" => 1 })
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "subject should NOT contain meta_data" do
|
|
28
|
+
expect{ subject.status }.to raise_error NoMethodError
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "subject should contain all other data" do
|
|
32
|
+
expect(subject.attributes).to eq(attributes_for(:user))
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
7
37
|
|
|
8
38
|
describe "white and black list" do
|
|
9
39
|
subject { UserWithAddressAndPosts::Son.new(attributes_for(:user_with_address_and_posts)) }
|
|
@@ -1,5 +1,20 @@
|
|
|
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
|
+
|
|
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
|
+
end
|
|
17
|
+
|
|
3
18
|
describe SmoothOperator::FinderMethods do
|
|
4
19
|
subject { UserWithAddressAndPosts::Son }
|
|
5
20
|
|
|
@@ -11,12 +26,22 @@ describe SmoothOperator::FinderMethods do
|
|
|
11
26
|
|
|
12
27
|
describe ".find" do
|
|
13
28
|
context "when the server returns a single hash" do
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
29
|
+
let(:user) { subject.find(5).object }
|
|
30
|
+
|
|
31
|
+
it_behaves_like "finder method"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context "when the server returns a hash with meta_data" do
|
|
35
|
+
let(:user) { subject.find("5/with_metadata").object }
|
|
36
|
+
|
|
37
|
+
it_behaves_like "finder method"
|
|
38
|
+
|
|
39
|
+
it "#meta_data should reflect the receiving meta_data" do
|
|
40
|
+
expect(user._meta_data).to eq({ "status" => 1 })
|
|
41
|
+
end
|
|
17
42
|
|
|
18
|
-
|
|
19
|
-
expect
|
|
43
|
+
it "user should NOT contain meta_data" do
|
|
44
|
+
expect{ user.status }.to raise_error NoMethodError
|
|
20
45
|
end
|
|
21
46
|
end
|
|
22
47
|
|
|
@@ -90,6 +90,52 @@ end
|
|
|
90
90
|
|
|
91
91
|
describe SmoothOperator::Persistence, helpers: :persistence do
|
|
92
92
|
|
|
93
|
+
describe "#reload", current: true do
|
|
94
|
+
subject { new_user }
|
|
95
|
+
|
|
96
|
+
context "before calling #reload" do
|
|
97
|
+
it "#has_data_from_server and #from_server should return false" do
|
|
98
|
+
expect(subject.from_server).to be_falsey
|
|
99
|
+
expect(subject.has_data_from_server).to be_falsey
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
context "when subject doesn't has an id" do
|
|
104
|
+
it "it should raise 'UnknownPath'" do
|
|
105
|
+
expect{ subject.reload }.to raise_error 'UnknownPath'
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context "when subject has an id" do
|
|
110
|
+
before do
|
|
111
|
+
subject.id = 1
|
|
112
|
+
subject.reload
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "it should fetch server data" do
|
|
116
|
+
expect(subject.attributes).to eq(attributes_for(:user_with_address_and_posts))
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "#has_data_from_server and #from_server should return true" do
|
|
120
|
+
expect(subject.from_server).to be true
|
|
121
|
+
expect(subject.has_data_from_server).to be true
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
context "when calling #reload on a nested object" do
|
|
126
|
+
before do
|
|
127
|
+
subject.id = 1
|
|
128
|
+
subject.reload
|
|
129
|
+
subject.posts.first.reload
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it "it should fetch server data, with the correct nested REST url" do
|
|
133
|
+
# binding.pry
|
|
134
|
+
expect(subject.posts.first.attributes).to eq({ id: 1, body: 'from_server' })
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
93
139
|
describe ".create" do
|
|
94
140
|
|
|
95
141
|
subject { created_subject }
|
data/spec/support/test_server.rb
CHANGED
|
@@ -9,8 +9,8 @@ class TestServer < Sinatra::Base
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
get '/posts/:id' do
|
|
13
|
-
post_data = { id: 1, body: '
|
|
12
|
+
get '/users/:id/posts/:id' do
|
|
13
|
+
post_data = { id: 1, body: 'from_server' }
|
|
14
14
|
json post_data
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -54,8 +54,14 @@ class TestServer < Sinatra::Base
|
|
|
54
54
|
json FactoryGirl.attributes_for(:user_with_address_and_posts)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
+
get '/users/:id/with_metadata' do
|
|
58
|
+
user_data = { user: FactoryGirl.attributes_for(:user_with_address_and_posts), status: 1 }
|
|
59
|
+
json user_data
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
57
63
|
put '/users/send_error' do
|
|
58
|
-
data_with_error = { id: 1, errors: { first_name: ["can't be blank"] } }
|
|
64
|
+
data_with_error = { id: 1, errors: [{ first_name: ["can't be blank"] }] }
|
|
59
65
|
json data_with_error
|
|
60
66
|
end
|
|
61
67
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: smooth_operator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.10.
|
|
4
|
+
version: 1.10.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- João Gonçalves
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-05-
|
|
11
|
+
date: 2014-05-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -85,6 +85,7 @@ files:
|
|
|
85
85
|
- lib/smooth_operator/attribute_assignment.rb
|
|
86
86
|
- lib/smooth_operator/attributes/base.rb
|
|
87
87
|
- lib/smooth_operator/attributes/dirty.rb
|
|
88
|
+
- lib/smooth_operator/attributes/normal.rb
|
|
88
89
|
- lib/smooth_operator/delegation.rb
|
|
89
90
|
- lib/smooth_operator/finder_methods.rb
|
|
90
91
|
- lib/smooth_operator/helpers.rb
|
|
@@ -102,7 +103,6 @@ files:
|
|
|
102
103
|
- lib/smooth_operator/remote_call/typhoeus.rb
|
|
103
104
|
- lib/smooth_operator/serialization.rb
|
|
104
105
|
- lib/smooth_operator/translation.rb
|
|
105
|
-
- lib/smooth_operator/type_converter.rb
|
|
106
106
|
- lib/smooth_operator/validations.rb
|
|
107
107
|
- lib/smooth_operator/version.rb
|
|
108
108
|
- smooth_operator.gemspec
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
module SmoothOperator
|
|
2
|
-
|
|
3
|
-
module TypeConverter
|
|
4
|
-
|
|
5
|
-
extend self
|
|
6
|
-
|
|
7
|
-
def cast_to_type(name, value, type, array_class, unknown_hash_class)
|
|
8
|
-
case value
|
|
9
|
-
when Array
|
|
10
|
-
value.map { |array_entry| array_class.new(name, array_entry, type, unknown_hash_class).value }
|
|
11
|
-
when Hash
|
|
12
|
-
type.nil? ? new_unknown_hash(value, array_class, unknown_hash_class) : type.new(value)
|
|
13
|
-
else
|
|
14
|
-
TypeConverter.convert(value, type)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def convert(value, type)
|
|
19
|
-
case type
|
|
20
|
-
|
|
21
|
-
when :string, :text, String
|
|
22
|
-
value.to_s
|
|
23
|
-
|
|
24
|
-
when :int, :integer, Integer, Fixnum
|
|
25
|
-
to_int(value)
|
|
26
|
-
|
|
27
|
-
when :date, Date
|
|
28
|
-
to_date(value)
|
|
29
|
-
|
|
30
|
-
when :float, Float
|
|
31
|
-
to_float(value)
|
|
32
|
-
|
|
33
|
-
when :bool, :boolean
|
|
34
|
-
to_boolean(value)
|
|
35
|
-
|
|
36
|
-
when :datetime, :date_time, DateTime
|
|
37
|
-
to_datetime(value)
|
|
38
|
-
|
|
39
|
-
else
|
|
40
|
-
Helpers.duplicate(value)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def to_int(string)
|
|
45
|
-
return string if string.is_a?(Fixnum)
|
|
46
|
-
|
|
47
|
-
to_float(string).to_i
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def to_date(string)
|
|
51
|
-
return string if string.is_a?(Date)
|
|
52
|
-
|
|
53
|
-
Date.parse(string) rescue nil
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def to_datetime(string)
|
|
57
|
-
return string if string.is_a?(DateTime)
|
|
58
|
-
|
|
59
|
-
DateTime.parse(string) rescue nil
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def to_boolean(string)
|
|
63
|
-
value = string.to_s.downcase
|
|
64
|
-
|
|
65
|
-
['1', 'true'].include?(value) ? true : ['0', 'false'].include?(value) ? false : nil
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def to_float(string)
|
|
69
|
-
return string if string.is_a?(Float)
|
|
70
|
-
|
|
71
|
-
return 0 if string.nil? || !string.is_a?(String)
|
|
72
|
-
|
|
73
|
-
value = string.scan(/-*\d+[,.]*\d*/).flatten.map(&:to_f).first
|
|
74
|
-
|
|
75
|
-
value.nil? ? 0 : value
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
protected ################### PROTECTED #####################
|
|
80
|
-
|
|
81
|
-
def new_unknown_hash(hash, array_class, unknown_hash_class)
|
|
82
|
-
if unknown_hash_class == :none
|
|
83
|
-
hash
|
|
84
|
-
else
|
|
85
|
-
unknown_hash_class.new(cast_params(hash, array_class, unknown_hash_class))
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
private ################### PRIVATE #####################
|
|
91
|
-
|
|
92
|
-
def cast_params(attributes, array_class, unknown_hash_class)
|
|
93
|
-
hash = {}
|
|
94
|
-
|
|
95
|
-
attributes.each do |key, value|
|
|
96
|
-
hash[key] = cast_to_type(key, value, nil, array_class, unknown_hash_class)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
hash
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
end
|