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
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NWVmZWM5OGI3M2I3NmM1MDA1NmQ4ODIzYTRlYzU1NjIzZDBiZDI1ZQ==
4
+ M2EyMTBiMWExYzQ4Mjc0ZDM3M2IyMGRkZmFjMTEzYmVjYzg5NDJkMw==
5
5
  data.tar.gz: !binary |-
6
- ZjUxMTRlMDk0NDE1ZWI1ZWUwYTZhZWJhYWE3NzFmMmY0ZWEzYmQ2ZA==
6
+ YmZjYzA1YWM0ZjJmYzMwOWZjN2ZmMzVkMjRiNDFlYzc5MjVmYzNlNQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YTFlYWU5MzJhMzZjZjk1MDdiOTlmNGYxNTkxMTJiOWJlYjg4MmZlMjVlYjU5
10
- MDdhMGVjYjU2NDVjNWNkOTgzMjIwYzZhYzQ0ZGM2ZTQyYTY4ZDhmNmJmMTNm
11
- MmFkNTBlMWM5MTBmZDU2Mjc2M2U1MWI0OTBhYmZmOTY2MWFkM2Q=
9
+ Mzg4ZGRhNDA4NWJjNmEzNDkzMWNjYjZhZTM5YTFiMGRiMmUzZWNmOWFmMzg4
10
+ NjFiMGVjMDJhZGRkNTExNzdhNTAzOTU4MjQxNmVmZTBiMzIxM2Q5Y2I5MzZh
11
+ MTlhYTZiYWYxNWU1OTZjZmM0YjQ0ZjFlMTYxMjIxZjE5NWMzMmE=
12
12
  data.tar.gz: !binary |-
13
- YWI0ZTIxYjY5NjJhYThhYWEyZmJmZTQ1ZmQyMDJhMDY0NTUxNDc3ODkyMGI3
14
- MzZiY2I2YjMzNGFjMzZmMDdkYmM2YTY0NWE1Mjg2ODYyY2RiMDc4YjY0ODM3
15
- YjY1MTM1OGQ1MWE0OTA5NzBjMjA3OWRjMmQ1YmM5OTQyOTkwOWY=
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.objects # 'pages = remote_call.data' also works
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.object # 'page = remote_call.data' also works
182
+ page = remote_call.data
183
183
  ```
184
184
 
185
185
  ---
@@ -1,22 +1,34 @@
1
1
  module SmoothOperator
2
- class ArrayWithMetaData < ::SimpleDelegator
2
+ class ArrayWithMetaData < SimpleDelegator
3
3
 
4
4
  attr_reader :meta_data, :internal_array
5
5
 
6
6
  def initialize(attributes, object_class)
7
- _attributes, _resources_name = attributes.dup, object_class.resources_name
7
+ resources_name = object_class.resources_name
8
8
 
9
- @internal_array = [*_attributes[_resources_name]].map { |array_entry| object_class.new(array_entry).tap { |object| object.reloaded = true } }
10
- _attributes.delete(_resources_name)
9
+ @internal_array = [*attributes[resources_name]].map do |array_entry|
10
+ object_class.new(array_entry)
11
+ end
11
12
 
12
- @meta_data = _attributes
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
- def method_missing(method, *args, &block)
18
- _method = method.to_s
19
- meta_data.include?(_method) ? meta_data[_method] : super
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 attributes, options
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
- options.each { |key, value| @_options[key] = value } if options.is_a?(Hash)
26
+ @_internal_errors = attributes.delete(self.class.errors_key)
70
27
 
71
- attributes.each { |name, value| push_to_internal_data(name, value) }
72
- end
28
+ options.each { |key, value| @_options[key] = value } if options.is_a? Hash
73
29
 
74
- def parent_object
75
- _options[:parent_object]
76
- end
30
+ attributes.each do |name, value|
31
+ next unless allowed_attribute(name)
77
32
 
78
- def has_data_from_server
79
- _options[:from_server] == true
33
+ internal_data_push(name, value)
34
+ end
80
35
  end
81
36
 
82
- alias :from_server :has_data_from_server
37
+ def _parent_object
38
+ _options[:parent_object]
39
+ end
83
40
 
84
- protected #################### PROTECTED METHODS DOWN BELOW ######################
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
- method_type, method_name = *parse_method(method)
11
-
12
- result = case method_type
13
- when :was
14
- get_internal_data(method_name, :was)
15
- when :changed
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
- if !self.class.strict_behaviour || known_attribute?(method_name)
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
- protected #################### PROTECTED ################
30
-
31
- def parse_method(method)
32
- method = method.to_s
24
+ module ClassMethods
33
25
 
34
- if method?(method, /=$/)
35
- [:setter, method[0..-2]]
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
- remote_call.object = build_object(remote_call.parsed_response, options) if remote_call.ok?
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
- protected #################### PROTECTED ##################
21
+ extend self
19
22
 
20
- def build_object(parsed_response, options, from_array = false)
21
- if parsed_response.is_a?(Array)
22
- parsed_response.map { |array_entry| build_object(array_entry, options, true) }
23
- elsif parsed_response.is_a?(Hash)
24
- if parsed_response.include?(object_class.resources_name) && !from_array
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
- object_class.new(parsed_response, from_server: true)
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
- private #################### PRIVATE ##################
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 safe_call(object, method, *args)
8
- if object.respond_to?(method)
9
- object.send(method, *args)
10
- else
11
- false
12
- end
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
- object.superclass.send(method_name, *args) if object.superclass.respond_to?(method_name)
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/attribute_methods"
5
+ require "smooth_operator/internal_data"
6
6
  require "smooth_operator/attribute_assignment"
7
7
 
8
8
  module SmoothOperator
9
- module OpenStruct
9
+ class OpenStruct
10
10
 
11
- class Base
11
+ extend ResourceName
12
12
 
13
- extend ModelName
14
-
15
- include Delegation
16
- include Validations
17
- include Serialization
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