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.
- checksums.yaml +8 -8
- data/README.md +2 -2
- data/lib/smooth_operator/array_with_meta_data.rb +20 -8
- data/lib/smooth_operator/attribute_assignment.rb +41 -54
- data/lib/smooth_operator/delegation.rb +14 -33
- data/lib/smooth_operator/finder_methods.rb +26 -17
- data/lib/smooth_operator/helpers.rb +10 -8
- data/lib/smooth_operator/internal_data.rb +45 -0
- data/lib/smooth_operator/open_struct.rb +9 -27
- data/lib/smooth_operator/operator.rb +11 -12
- data/lib/smooth_operator/persistence.rb +52 -41
- data/lib/smooth_operator/relation/array_relation.rb +2 -12
- data/lib/smooth_operator/relation/association_reflection.rb +2 -6
- data/lib/smooth_operator/relation/associations.rb +3 -3
- data/lib/smooth_operator/remote_call/base.rb +0 -4
- data/lib/smooth_operator/{model_name.rb → resource_name.rb} +2 -2
- data/lib/smooth_operator/schema.rb +9 -20
- data/lib/smooth_operator/serialization.rb +11 -11
- data/lib/smooth_operator/translation.rb +21 -12
- data/lib/smooth_operator/type_casting.rb +127 -0
- data/lib/smooth_operator/validations.rb +19 -3
- data/lib/smooth_operator/version.rb +1 -1
- data/lib/smooth_operator.rb +24 -3
- data/spec/smooth_operator/attribute_assignment_spec.rb +4 -13
- data/spec/smooth_operator/finder_methods_spec.rb +4 -9
- data/spec/smooth_operator/persistence_spec.rb +3 -15
- data/spec/smooth_operator/{model_name_spec.rb → resource_name_spec.rb} +1 -1
- data/spec/support/models/address.rb +0 -2
- data/spec/support/models/user.rb +3 -3
- data/spec/support/models/user_with_address_and_posts.rb +6 -14
- metadata +7 -12
- data/lib/smooth_operator/attribute_methods.rb +0 -92
- data/lib/smooth_operator/attributes/base.rb +0 -107
- data/lib/smooth_operator/attributes/dirty.rb +0 -29
- data/lib/smooth_operator/attributes/normal.rb +0 -15
- data/lib/smooth_operator/blank_slate.rb +0 -7
- data/spec/smooth_operator/attributes_dirty_spec.rb +0 -53
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
M2EyMTBiMWExYzQ4Mjc0ZDM3M2IyMGRkZmFjMTEzYmVjYzg5NDJkMw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YmZjYzA1YWM0ZjJmYzMwOWZjN2ZmMzVkMjRiNDFlYzc5MjVmYzNlNQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
Mzg4ZGRhNDA4NWJjNmEzNDkzMWNjYjZhZTM5YTFiMGRiMmUzZWNmOWFmMzg4
|
10
|
+
NjFiMGVjMDJhZGRkNTExNzdhNTAzOTU4MjQxNmVmZTBiMzIxM2Q5Y2I5MzZh
|
11
|
+
MTlhYTZiYWYxNWU1OTZjZmM0YjQ0ZjFlMTYxMjIxZjE5NWMzMmE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ODljN2NlMTBlZDM1NWYwMzhlZmJjMDBkNDM3ZGEwOTYzZmNmZGFmNDQwMjUy
|
14
|
+
OWFhNjNmODRlNmUxYmI3MzdlZWQzZGEzZjg1MzE3ZmZkZDY2NmI5YjZiMDNj
|
15
|
+
OTdhNTA0OWNlMTk5NmM2OTNkZTlkZTlkODZhNDZiODhhNmVhZjc=
|
data/README.md
CHANGED
@@ -139,7 +139,7 @@ page.save # will make a http PATCH call to 'http://myblog.com/api/v0/pages/2'
|
|
139
139
|
remote_call = Page.find(:all) # Will make a GET call to 'http://myblog.com/api/v0/pages'
|
140
140
|
# and will return a SmoothOperator::RemoteCall instance
|
141
141
|
|
142
|
-
pages = remote_call.
|
142
|
+
pages = remote_call.data
|
143
143
|
|
144
144
|
# If the server response is positive (http code between 200 and 299):
|
145
145
|
remote_call.ok? # true
|
@@ -179,7 +179,7 @@ pages = remote_call.objects # 'pages = remote_call.data' also works
|
|
179
179
|
remote_call = Page.find(2) # Will make a GET call to 'http://myblog.com/api/v0/pages/2'
|
180
180
|
# and will return a SmoothOperator::RemoteCall instance
|
181
181
|
|
182
|
-
page = remote_call.
|
182
|
+
page = remote_call.data
|
183
183
|
```
|
184
184
|
|
185
185
|
---
|
@@ -1,22 +1,34 @@
|
|
1
1
|
module SmoothOperator
|
2
|
-
class ArrayWithMetaData <
|
2
|
+
class ArrayWithMetaData < SimpleDelegator
|
3
3
|
|
4
4
|
attr_reader :meta_data, :internal_array
|
5
5
|
|
6
6
|
def initialize(attributes, object_class)
|
7
|
-
|
7
|
+
resources_name = object_class.resources_name
|
8
8
|
|
9
|
-
@internal_array = [*
|
10
|
-
|
9
|
+
@internal_array = [*attributes[resources_name]].map do |array_entry|
|
10
|
+
object_class.new(array_entry)
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
+
attributes.delete(resources_name)
|
14
|
+
|
15
|
+
@meta_data = attributes
|
16
|
+
|
17
|
+
define_metada_methods
|
13
18
|
|
14
19
|
super(@internal_array)
|
15
20
|
end
|
16
21
|
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
protected ############# PROTECTED ###################
|
23
|
+
|
24
|
+
def define_metada_methods
|
25
|
+
@meta_data.keys.each do |method|
|
26
|
+
instance_eval <<-RUBY, __FILE__, __LINE__ + 1
|
27
|
+
def #{method}
|
28
|
+
@meta_data['#{method}']
|
29
|
+
end
|
30
|
+
RUBY
|
31
|
+
end
|
20
32
|
end
|
21
33
|
|
22
34
|
end
|
@@ -1,61 +1,18 @@
|
|
1
|
-
require 'smooth_operator/attributes/base'
|
2
|
-
require 'smooth_operator/attributes/dirty'
|
3
|
-
require 'smooth_operator/attributes/normal'
|
4
|
-
|
5
1
|
module SmoothOperator
|
6
2
|
module AttributeAssignment
|
7
3
|
|
8
|
-
def self.included(base)
|
9
|
-
base.extend(ClassMethods)
|
10
|
-
end
|
11
|
-
|
12
|
-
module ClassMethods
|
13
|
-
|
14
|
-
attr_writer :unknown_hash_class
|
15
|
-
|
16
|
-
def unknown_hash_class
|
17
|
-
Helpers.get_instance_variable(self, :unknown_hash_class, ::OpenStruct)
|
18
|
-
end
|
19
|
-
|
20
|
-
def attributes_white_list
|
21
|
-
Helpers.get_instance_variable(self, :attributes_white_list, Set.new)
|
22
|
-
end
|
23
|
-
|
24
|
-
def attributes_black_list
|
25
|
-
Helpers.get_instance_variable(self, :attributes_black_list, Set.new)
|
26
|
-
end
|
27
|
-
|
28
|
-
def attributes_white_list_add(*getters)
|
29
|
-
attributes_white_list.merge getters.map(&:to_s)
|
30
|
-
end
|
31
|
-
|
32
|
-
def attributes_black_list_add(*getters)
|
33
|
-
attributes_black_list.merge getters.map(&:to_s)
|
34
|
-
end
|
35
|
-
|
36
|
-
def dirty_attributes
|
37
|
-
@dirty_attributes = true
|
38
|
-
end
|
39
|
-
|
40
|
-
def dirty_attributes?
|
41
|
-
@dirty_attributes
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
4
|
def initialize(attributes = {}, options = {})
|
47
5
|
@_options = {}
|
48
6
|
|
49
7
|
before_initialize(attributes, options)
|
50
8
|
|
51
|
-
assign_attributes
|
9
|
+
assign_attributes(attributes, options)
|
52
10
|
|
53
11
|
after_initialize(attributes, options)
|
54
12
|
end
|
55
13
|
|
56
14
|
attr_reader :_options, :_meta_data
|
57
15
|
|
58
|
-
|
59
16
|
def assign_attributes(_attributes = {}, options = {})
|
60
17
|
return nil unless _attributes.is_a?(Hash)
|
61
18
|
|
@@ -66,22 +23,22 @@ module SmoothOperator
|
|
66
23
|
@_meta_data = _attributes
|
67
24
|
end
|
68
25
|
|
69
|
-
|
26
|
+
@_internal_errors = attributes.delete(self.class.errors_key)
|
70
27
|
|
71
|
-
|
72
|
-
end
|
28
|
+
options.each { |key, value| @_options[key] = value } if options.is_a? Hash
|
73
29
|
|
74
|
-
|
75
|
-
|
76
|
-
end
|
30
|
+
attributes.each do |name, value|
|
31
|
+
next unless allowed_attribute(name)
|
77
32
|
|
78
|
-
|
79
|
-
|
33
|
+
internal_data_push(name, value)
|
34
|
+
end
|
80
35
|
end
|
81
36
|
|
82
|
-
|
37
|
+
def _parent_object
|
38
|
+
_options[:parent_object]
|
39
|
+
end
|
83
40
|
|
84
|
-
protected
|
41
|
+
protected ################# PROTECTED METHODS DOWN BELOW ###################
|
85
42
|
|
86
43
|
def before_initialize(attributes, options); end
|
87
44
|
|
@@ -97,5 +54,35 @@ module SmoothOperator
|
|
97
54
|
end
|
98
55
|
end
|
99
56
|
|
57
|
+
def self.included(base)
|
58
|
+
base.extend(ClassMethods)
|
59
|
+
end
|
60
|
+
|
61
|
+
module ClassMethods
|
62
|
+
|
63
|
+
attr_writer :unknown_hash_class
|
64
|
+
|
65
|
+
def unknown_hash_class
|
66
|
+
Helpers.get_instance_variable(self, :unknown_hash_class, ::OpenStruct)
|
67
|
+
end
|
68
|
+
|
69
|
+
def attributes_white_list
|
70
|
+
Helpers.get_instance_variable(self, :attributes_white_list, Set.new)
|
71
|
+
end
|
72
|
+
|
73
|
+
def attributes_black_list
|
74
|
+
Helpers.get_instance_variable(self, :attributes_black_list, Set.new)
|
75
|
+
end
|
76
|
+
|
77
|
+
def attributes_white_list_add(*getters)
|
78
|
+
attributes_white_list.merge getters.map(&:to_s)
|
79
|
+
end
|
80
|
+
|
81
|
+
def attributes_black_list_add(*getters)
|
82
|
+
attributes_black_list.merge getters.map(&:to_s)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
100
87
|
end
|
101
88
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
module SmoothOperator
|
2
|
-
|
3
2
|
module Delegation
|
4
3
|
|
5
4
|
def respond_to?(method, include_private = false)
|
@@ -7,48 +6,30 @@ module SmoothOperator
|
|
7
6
|
end
|
8
7
|
|
9
8
|
def method_missing(method, *args, &block)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
get_internal_data(method_name, :changed?)
|
17
|
-
when :setter
|
18
|
-
return push_to_internal_data(method_name, args.first)
|
9
|
+
method_name = method.to_s
|
10
|
+
|
11
|
+
if !! ((method.to_s) =~ /=$/) #setter method
|
12
|
+
internal_data_push(method_name[0..-2], args.first)
|
13
|
+
elsif !self.class.strict_behaviour || known_attribute?(method_name)
|
14
|
+
internal_data_get(method_name)
|
19
15
|
else
|
20
|
-
|
21
|
-
return get_internal_data(method_name)
|
22
|
-
end
|
16
|
+
super
|
23
17
|
end
|
24
|
-
|
25
|
-
result.nil? ? super : result
|
26
18
|
end
|
27
19
|
|
20
|
+
def self.included(base)
|
21
|
+
base.extend(ClassMethods)
|
22
|
+
end
|
28
23
|
|
29
|
-
|
30
|
-
|
31
|
-
def parse_method(method)
|
32
|
-
method = method.to_s
|
24
|
+
module ClassMethods
|
33
25
|
|
34
|
-
|
35
|
-
|
36
|
-
elsif method?(method, /_was$/)
|
37
|
-
[:was, method[0..-5]]
|
38
|
-
elsif method?(method, /_changed\?$/)
|
39
|
-
[:changed, method[0..-10]]
|
40
|
-
else
|
41
|
-
[nil, method]
|
26
|
+
def strict_behaviour
|
27
|
+
Helpers.get_instance_variable(self, :strict_behaviour, false)
|
42
28
|
end
|
43
|
-
end
|
44
29
|
|
30
|
+
attr_writer :strict_behaviour
|
45
31
|
|
46
|
-
private #################### PRIVATE ################
|
47
|
-
|
48
|
-
def method?(method, regex)
|
49
|
-
!! ((method.to_s) =~ regex)
|
50
32
|
end
|
51
33
|
|
52
34
|
end
|
53
|
-
|
54
35
|
end
|
@@ -1,43 +1,52 @@
|
|
1
1
|
require 'smooth_operator/array_with_meta_data'
|
2
2
|
|
3
3
|
module SmoothOperator
|
4
|
-
|
5
4
|
module FinderMethods
|
6
5
|
|
7
6
|
def find(relative_path, data = {}, options = {})
|
8
7
|
relative_path = '' if relative_path == :all
|
9
8
|
|
10
9
|
get(relative_path, data, options) do |remote_call|
|
11
|
-
|
10
|
+
if remote_call.ok?
|
11
|
+
remote_call.object = HelperMethods
|
12
|
+
.build_object(self, remote_call.parsed_response, options)
|
13
|
+
end
|
12
14
|
|
13
15
|
block_given? ? yield(remote_call) : remote_call
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
19
|
+
module HelperMethods
|
17
20
|
|
18
|
-
|
21
|
+
extend self
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
ArrayWithMetaData.new(parsed_response, object_class)
|
23
|
+
def build_object(object, parsed_response, options, from_array = false)
|
24
|
+
if parsed_response.is_a?(Array)
|
25
|
+
parse_array(parsed_response, object, options)
|
26
|
+
elsif parsed_response.is_a?(Hash)
|
27
|
+
parse_hash(object, parsed_response, options, from_array)
|
26
28
|
else
|
27
|
-
|
29
|
+
parsed_response
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_array(parsed_response, object, options)
|
34
|
+
parsed_response.map do |array_entry|
|
35
|
+
build_object(object, array_entry, options, true)
|
28
36
|
end
|
29
|
-
else
|
30
|
-
parsed_response
|
31
37
|
end
|
32
|
-
end
|
33
38
|
|
39
|
+
def parse_hash(object, parsed_response, options, from_array)
|
40
|
+
object_class ||= object.class == Class ? object : object.class
|
34
41
|
|
35
|
-
|
42
|
+
if parsed_response.include?(object_class.resources_name) && !from_array
|
43
|
+
ArrayWithMetaData.new(parsed_response.dup, object_class)
|
44
|
+
else
|
45
|
+
object_class.new(parsed_response, data_from_server: true)
|
46
|
+
end
|
47
|
+
end
|
36
48
|
|
37
|
-
def object_class
|
38
|
-
@object_class ||= self.class == Class ? self : self.class
|
39
49
|
end
|
40
50
|
|
41
51
|
end
|
42
|
-
|
43
52
|
end
|
@@ -4,16 +4,18 @@ module SmoothOperator
|
|
4
4
|
|
5
5
|
extend self
|
6
6
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
def primary_key(object)
|
8
|
+
object.internal_data_get(object.class.primary_key)
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_primary_key?(object)
|
12
|
+
blank? primary_key(object)
|
13
13
|
end
|
14
14
|
|
15
15
|
def super_method(object, method_name, *args)
|
16
|
-
|
16
|
+
if object.superclass.respond_to?(method_name)
|
17
|
+
object.superclass.send(method_name, *args)
|
18
|
+
end
|
17
19
|
end
|
18
20
|
|
19
21
|
def get_instance_variable(object, variable, default_value)
|
@@ -56,7 +58,7 @@ module SmoothOperator
|
|
56
58
|
case object
|
57
59
|
when String
|
58
60
|
object.to_s == ''
|
59
|
-
when Array
|
61
|
+
when Array, Hash
|
60
62
|
object.empty?
|
61
63
|
else
|
62
64
|
object.nil?
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'smooth_operator/type_casting'
|
2
|
+
|
3
|
+
module SmoothOperator
|
4
|
+
module InternalData
|
5
|
+
|
6
|
+
def internal_data
|
7
|
+
@internal_data ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def known_attributes
|
11
|
+
return @known_attributes if defined?(@known_attributes)
|
12
|
+
|
13
|
+
schema_attributes = if self.class.respond_to?(:internal_structure)
|
14
|
+
self.class.internal_structure.keys
|
15
|
+
else
|
16
|
+
[]
|
17
|
+
end
|
18
|
+
|
19
|
+
@known_attributes = Set.new(schema_attributes)
|
20
|
+
end
|
21
|
+
|
22
|
+
def known_attribute?(attribute)
|
23
|
+
known_attributes.include?(attribute.to_s)
|
24
|
+
end
|
25
|
+
|
26
|
+
def internal_data_get(attribute_name)
|
27
|
+
internal_data[attribute_name]
|
28
|
+
end
|
29
|
+
|
30
|
+
def internal_data_push(attribute_name, attribute_value)
|
31
|
+
attribute_name = attribute_name.to_s
|
32
|
+
|
33
|
+
known_attributes.add attribute_name
|
34
|
+
|
35
|
+
internal_data[attribute_name] = TypeCasting.cast_to_type(attribute_name, attribute_value, self)
|
36
|
+
|
37
|
+
if self.class.respond_to?(:smooth_operator?)
|
38
|
+
marked_for_destruction?(attribute_value) if attribute_name == self.class.destroy_key
|
39
|
+
|
40
|
+
new_record?(true) if attribute_name == self.class.primary_key
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -1,38 +1,20 @@
|
|
1
|
-
require "smooth_operator/model_name"
|
2
1
|
require "smooth_operator/delegation"
|
3
2
|
require "smooth_operator/validations"
|
3
|
+
require "smooth_operator/resource_name"
|
4
4
|
require "smooth_operator/serialization"
|
5
|
-
require "smooth_operator/
|
5
|
+
require "smooth_operator/internal_data"
|
6
6
|
require "smooth_operator/attribute_assignment"
|
7
7
|
|
8
8
|
module SmoothOperator
|
9
|
-
|
9
|
+
class OpenStruct
|
10
10
|
|
11
|
-
|
11
|
+
extend ResourceName
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
include AttributeMethods
|
19
|
-
include AttributeAssignment
|
20
|
-
|
21
|
-
def self.strict_behaviour=(value)
|
22
|
-
@strict_behaviour = value
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.strict_behaviour
|
26
|
-
Helpers.get_instance_variable(self, :strict_behaviour, false)
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
class Dirty < Base
|
32
|
-
|
33
|
-
dirty_attributes
|
34
|
-
|
35
|
-
end
|
13
|
+
include Delegation
|
14
|
+
include Validations
|
15
|
+
include InternalData
|
16
|
+
include Serialization
|
17
|
+
include AttributeAssignment
|
36
18
|
|
37
19
|
end
|
38
20
|
end
|