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
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