smooth_operator 1.3.0 → 1.8.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 (60) hide show
  1. checksums.yaml +8 -8
  2. data/Gemfile +0 -5
  3. data/README.md +11 -308
  4. data/console.rb +3 -28
  5. data/lib/smooth_operator.rb +7 -75
  6. data/lib/smooth_operator/array_with_meta_data.rb +21 -20
  7. data/lib/smooth_operator/attribute_assignment.rb +53 -57
  8. data/lib/smooth_operator/delegation.rb +34 -15
  9. data/lib/smooth_operator/finder_methods.rb +17 -33
  10. data/lib/smooth_operator/helpers.rb +7 -37
  11. data/lib/smooth_operator/internal_attribute.rb +49 -0
  12. data/lib/smooth_operator/model_schema.rb +72 -0
  13. data/lib/smooth_operator/open_struct.rb +4 -7
  14. data/lib/smooth_operator/operator.rb +64 -102
  15. data/lib/smooth_operator/persistence.rb +64 -94
  16. data/lib/smooth_operator/remote_call.rb +70 -0
  17. data/lib/smooth_operator/serialization.rb +33 -89
  18. data/lib/smooth_operator/translation.rb +13 -26
  19. data/lib/smooth_operator/type_converter.rb +69 -0
  20. data/lib/smooth_operator/validations.rb +3 -25
  21. data/lib/smooth_operator/version.rb +1 -1
  22. data/smooth_operator.gemspec +5 -9
  23. data/spec/factories/user_factory.rb +4 -5
  24. data/spec/smooth_operator/attribute_assignment_spec.rb +11 -145
  25. data/spec/smooth_operator/delegation_spec.rb +54 -57
  26. data/spec/smooth_operator/finder_methods_spec.rb +2 -91
  27. data/spec/smooth_operator/{resource_name_spec.rb → model_schema_spec.rb} +2 -2
  28. data/spec/smooth_operator/operator_spec.rb +1 -1
  29. data/spec/smooth_operator/persistence_spec.rb +20 -140
  30. data/spec/smooth_operator/serialization_spec.rb +4 -28
  31. data/spec/spec_helper.rb +9 -7
  32. data/spec/support/models/address.rb +0 -9
  33. data/spec/support/models/post.rb +3 -9
  34. data/spec/support/models/user.rb +7 -30
  35. data/spec/support/models/user_with_address_and_posts.rb +12 -20
  36. data/spec/support/test_server.rb +7 -63
  37. metadata +18 -55
  38. data/lib/smooth_operator/associations.rb +0 -110
  39. data/lib/smooth_operator/associations/association_reflection.rb +0 -79
  40. data/lib/smooth_operator/associations/has_many_relation.rb +0 -45
  41. data/lib/smooth_operator/associations/reflection.rb +0 -41
  42. data/lib/smooth_operator/cookie_jar.rb +0 -21
  43. data/lib/smooth_operator/http_methods.rb +0 -17
  44. data/lib/smooth_operator/internal_data.rb +0 -45
  45. data/lib/smooth_operator/operators/connection_wrapper.rb +0 -15
  46. data/lib/smooth_operator/operators/faraday.rb +0 -75
  47. data/lib/smooth_operator/operators/typhoeus.rb +0 -87
  48. data/lib/smooth_operator/options.rb +0 -30
  49. data/lib/smooth_operator/remote_call/base.rb +0 -76
  50. data/lib/smooth_operator/remote_call/errors/connection_failed.rb +0 -20
  51. data/lib/smooth_operator/remote_call/errors/timeout.rb +0 -20
  52. data/lib/smooth_operator/remote_call/faraday.rb +0 -19
  53. data/lib/smooth_operator/remote_call/typhoeus.rb +0 -19
  54. data/lib/smooth_operator/resource_name.rb +0 -46
  55. data/lib/smooth_operator/schema.rb +0 -21
  56. data/lib/smooth_operator/type_casting.rb +0 -127
  57. data/spec/require_helper.rb +0 -11
  58. data/spec/smooth_operator/remote_call_spec.rb +0 -340
  59. data/spec/smooth_operator/validations_spec.rb +0 -42
  60. data/spec/support/models/comment.rb +0 -5
@@ -1,35 +1,36 @@
1
1
  module SmoothOperator
2
- class ArrayWithMetaData < SimpleDelegator
3
2
 
4
- attr_reader :meta_data, :internal_array
3
+ class ArrayWithMetaData < OpenStruct
4
+
5
+ extend Forwardable
5
6
 
6
- def initialize(attributes, object_class)
7
- resources_name = object_class.resources_name
7
+ include Enumerable
8
8
 
9
- @internal_array = [*attributes[resources_name]].map do |array_entry|
10
- object_class.new(array_entry)
11
- end
9
+ attr_reader :meta_data, :internal_array, :table_name, :object_class
12
10
 
13
- attributes.delete(resources_name)
11
+ def_delegators :internal_array, :length, :<<
14
12
 
15
- @meta_data = attributes
13
+ def initialize(attributes, table_name, object_class)
14
+ _attributes = attributes.dup
16
15
 
17
- define_metada_methods
16
+ @table_name, @object_class = table_name, object_class
18
17
 
19
- super(@internal_array)
18
+ @internal_array = [*_attributes[table_name]].map { |array_entry| object_class.new(array_entry) }
19
+ _attributes.delete(table_name)
20
+
21
+ @meta_data = _attributes
20
22
  end
21
23
 
22
- protected ############# PROTECTED ###################
23
24
 
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
25
+ def each
26
+ internal_array.each { |array_entry| yield array_entry }
27
+ end
28
+
29
+ def method_missing(method, *args, &block)
30
+ _method = method.to_s
31
+ meta_data.include?(_method) ? meta_data[_method] : super
32
32
  end
33
33
 
34
34
  end
35
+
35
36
  end
@@ -1,55 +1,78 @@
1
+ require 'smooth_operator/internal_attribute'
2
+
1
3
  module SmoothOperator
4
+
2
5
  module AttributeAssignment
3
6
 
4
- def initialize(attributes = {}, options = {})
5
- @_options = {}
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
6
10
 
7
- before_initialize(attributes, options)
11
+ module ClassMethods
8
12
 
9
- assign_attributes(attributes, options)
13
+ attr_accessor :turn_unknown_hash_to_open_struct
10
14
 
11
- after_initialize(attributes, options)
12
- end
15
+ def attributes_white_list
16
+ Helpers.get_instance_variable(self, :attributes_white_list, Set.new)
17
+ end
13
18
 
14
- attr_reader :_options, :_meta_data
19
+ def attributes_black_list
20
+ Helpers.get_instance_variable(self, :attributes_black_list, Set.new)
21
+ end
15
22
 
16
- def assign_attributes(attributes = {}, options = {})
17
- attributes = _extract_attributes(attributes)
23
+ def attributes_white_list_add(*getters)
24
+ attributes_white_list.merge getters.map(&:to_s)
25
+ end
18
26
 
19
- return nil unless attributes.is_a?(Hash)
27
+ def attributes_black_list_add(*getters)
28
+ attributes_black_list.merge getters.map(&:to_s)
29
+ end
20
30
 
21
- induce_errors(attributes.delete(self.class.errors_key))
31
+ end
22
32
 
23
- @_options.merge!(options) if options.is_a? Hash
24
33
 
25
- attributes.each do |name, value|
26
- next unless allowed_attribute(name)
34
+ def initialize(attributes = {})
35
+ before_initialize(attributes)
27
36
 
28
- send("#{name}=", value)
29
- end
30
- end
37
+ assign_attributes attributes
31
38
 
32
- protected ################# PROTECTED METHODS DOWN BELOW ###################
39
+ after_initialize(attributes)
40
+ end
33
41
 
34
- def _extract_attributes(attributes)
42
+ def assign_attributes(attributes = {})
35
43
  return nil unless attributes.is_a?(Hash)
44
+
45
+ attributes.each { |name, value| push_to_internal_data(name, value) }
46
+ end
47
+
48
+ def internal_data
49
+ @internal_data ||= {}
50
+ end
51
+
52
+ def get_internal_data(field, method = :value)
53
+ internal_data[field].nil? ? nil : internal_data[field].send(method)
54
+ end
36
55
 
37
- _attributes = Helpers.stringify_keys(attributes)
56
+ def push_to_internal_data(attribute_name, attribute_value)
57
+ attribute_name = attribute_name.to_s
38
58
 
39
- if _attributes.include?(self.class.resource_name)
40
- attributes = _attributes.delete(self.class.resource_name)
41
- @_meta_data = _attributes
59
+ return nil unless allowed_attribute(attribute_name)
60
+
61
+ known_attributes.add attribute_name
62
+
63
+ if internal_data[attribute_name].nil?
64
+ internal_data[attribute_name] = InternalAttribute.new(attribute_name, attribute_value, internal_structure[attribute_name], self.class.turn_unknown_hash_to_open_struct)
42
65
  else
43
- attributes = _attributes
44
- @_meta_data = {}
66
+ internal_data[attribute_name].set_value(attribute_value)
45
67
  end
46
-
47
- attributes
48
68
  end
49
69
 
50
- def before_initialize(attributes, options); end
70
+
71
+ protected #################### PROTECTED METHODS DOWN BELOW ######################
72
+
73
+ def before_initialize(attributes); end
51
74
 
52
- def after_initialize(attributes, options); end
75
+ def after_initialize(attributes); end
53
76
 
54
77
  def allowed_attribute(attribute)
55
78
  if !self.class.attributes_white_list.empty?
@@ -61,33 +84,6 @@ module SmoothOperator
61
84
  end
62
85
  end
63
86
 
64
- def self.included(base)
65
- base.extend(ClassMethods)
66
- end
67
-
68
- module ClassMethods
69
-
70
- def unknown_hash_class
71
- get_option :unknown_hash_class, ::OpenStruct
72
- end
73
-
74
- def attributes_white_list
75
- Helpers.get_instance_variable(self, :attributes_white_list, Set.new)
76
- end
77
-
78
- def attributes_black_list
79
- Helpers.get_instance_variable(self, :attributes_black_list, Set.new)
80
- end
81
-
82
- def attributes_white_list_add(*getters)
83
- attributes_white_list.merge getters.map(&:to_s)
84
- end
85
-
86
- def attributes_black_list_add(*getters)
87
- attributes_black_list.merge getters.map(&:to_s)
88
- end
89
-
90
- end
91
-
92
87
  end
88
+
93
89
  end
@@ -1,33 +1,52 @@
1
1
  module SmoothOperator
2
+
2
3
  module Delegation
3
4
 
4
- def respond_to?(method, include_private = false)
5
- known_attribute?(method) ? true : super
5
+ def respond_to?(method)
6
+ known_attributes.include?(method.to_s) ? true : super
6
7
  end
7
8
 
8
9
  def method_missing(method, *args, &block)
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)
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)
15
19
  else
16
- super
20
+ return get_internal_data(method_name) if respond_to?(method_name)
17
21
  end
18
- end
19
22
 
20
- def self.included(base)
21
- base.extend(ClassMethods)
23
+ result.nil? ? super : result
22
24
  end
23
25
 
24
- module ClassMethods
25
26
 
26
- def strict_behaviour
27
- get_option :strict_behaviour, false
27
+ protected #################### PROTECTED ################
28
+
29
+ def parse_method(method)
30
+ method = method.to_s
31
+
32
+ if method?(method, /=$/)
33
+ [:setter, method[0..-2]]
34
+ elsif method?(method, /_was$/)
35
+ [:was, method[0..-5]]
36
+ elsif method?(method, /_changed\?$/)
37
+ [:changed, method[0..-10]]
38
+ else
39
+ [nil, method]
28
40
  end
41
+ end
42
+
29
43
 
44
+ private #################### PRIVATE ################
45
+
46
+ def method?(method, regex)
47
+ !! ((method.to_s) =~ regex)
30
48
  end
31
49
 
32
50
  end
51
+
33
52
  end
@@ -1,52 +1,36 @@
1
1
  require 'smooth_operator/array_with_meta_data'
2
2
 
3
3
  module SmoothOperator
4
+
4
5
  module FinderMethods
5
6
 
7
+ def all(data = {}, options = {})
8
+ find(:all, data, options)
9
+ end
10
+
6
11
  def find(relative_path, data = {}, options = {})
7
12
  relative_path = '' if relative_path == :all
8
13
 
9
- get(relative_path, data, options) do |remote_call|
10
- if remote_call.ok?
11
- remote_call.object = HelperMethods
12
- .build_object(self, remote_call.parsed_response, options)
13
- end
14
-
15
- block_given? ? yield(remote_call) : remote_call
14
+ get(relative_path, data, options).tap do |remote_call|
15
+ remote_call.object = build_object(remote_call.parsed_response, options)
16
16
  end
17
17
  end
18
18
 
19
- module HelperMethods
20
-
21
- extend self
22
19
 
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)
28
- else
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)
36
- end
37
- end
20
+ protected #################### PROTECTED ##################
38
21
 
39
- def parse_hash(object, parsed_response, options, from_array)
40
- object_class ||= object.class == Class ? object : object.class
22
+ def build_object(parsed_response, options)
23
+ table_name = (options[:table_name] || self.table_name).to_s
41
24
 
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
25
+ if parsed_response.is_a?(Array)
26
+ parsed_response.map { |array_entry| build_object(array_entry, options) }
27
+ elsif parsed_response.is_a?(Hash)
28
+ parsed_response.include?(table_name) ? ArrayWithMetaData.new(parsed_response, table_name, self) : new(parsed_response)
29
+ else
30
+ parsed_response
47
31
  end
48
-
49
32
  end
50
33
 
51
34
  end
35
+
52
36
  end
@@ -3,23 +3,9 @@ module SmoothOperator
3
3
  module Helpers
4
4
 
5
5
  extend self
6
-
7
- def generated_id
8
- Time.now.to_f.to_s.split('.')[1]
9
- end
10
-
11
- def primary_key(object)
12
- object.internal_data_get(object.class.primary_key)
13
- end
14
-
15
- def has_primary_key?(object)
16
- blank? primary_key(object)
17
- end
18
-
6
+
19
7
  def super_method(object, method_name, *args)
20
- if object.superclass.respond_to?(method_name)
21
- object.superclass.send(method_name, *args)
22
- end
8
+ object.superclass.send(method_name, *args) if object.superclass.respond_to?(method_name)
23
9
  end
24
10
 
25
11
  def get_instance_variable(object, variable, default_value)
@@ -27,12 +13,8 @@ module SmoothOperator
27
13
 
28
14
  return instance_var unless instance_var.nil?
29
15
 
30
- instance_var = (super_method(object, variable) || default_value)
31
-
32
- if instance_var.class == Class
16
+ (super_method(object, variable) || default_value).dup.tap do |instance_var|
33
17
  object.instance_variable_set("@#{variable}", instance_var)
34
- else
35
- object.instance_variable_set("@#{variable}", duplicate(instance_var))
36
18
  end
37
19
  end
38
20
 
@@ -49,11 +31,6 @@ module SmoothOperator
49
31
  end
50
32
  end
51
33
 
52
- def plural?(string)
53
- string = string.to_s
54
- string == string.pluralize
55
- end
56
-
57
34
  def duplicate(object)
58
35
  object.dup rescue object
59
36
  end
@@ -62,7 +39,7 @@ module SmoothOperator
62
39
  case object
63
40
  when String
64
41
  object.to_s == ''
65
- when Array, Hash
42
+ when Array
66
43
  object.empty?
67
44
  else
68
45
  object.nil?
@@ -72,14 +49,7 @@ module SmoothOperator
72
49
  def present?(object)
73
50
  !blank?(object)
74
51
  end
75
-
76
- def absolute_path?(string)
77
- present?(string) && string[0] == '/'
78
- end
79
-
80
- def remove_initial_slash(string)
81
- string[1..-1]
82
- end
83
-
52
+
84
53
  end
85
- end
54
+
55
+ end
@@ -0,0 +1,49 @@
1
+ require 'smooth_operator/type_converter'
2
+
3
+ module SmoothOperator
4
+
5
+ class InternalAttribute
6
+
7
+ attr_reader :original_name, :original_value, :first_value, :value, :type, :turn_unknown_hash_to_open_struct
8
+
9
+ def initialize(name, value, type, turn_to_open_struct = nil)
10
+ @original_name, @original_value, @type = name, value, type
11
+
12
+ @turn_unknown_hash_to_open_struct = turn_to_open_struct.nil? ? true : turn_to_open_struct
13
+
14
+ @first_value = set_value(value)
15
+ end
16
+
17
+ def set_value(new_value)
18
+ @value = cast_to_type(new_value)
19
+ end
20
+
21
+ def changed?
22
+ @first_value != @value
23
+ end
24
+
25
+ def was
26
+ @first_value
27
+ end
28
+
29
+
30
+ protected ######################## PROTECTED #####################
31
+
32
+ def cast_to_type(_value)
33
+ case _value
34
+ when Array
35
+ _value.map { |array_entry| InternalAttribute.new(original_name, array_entry, type, turn_unknown_hash_to_open_struct).value }
36
+ when Hash
37
+ if turn_unknown_hash_to_open_struct
38
+ (type || OpenStruct).new(_value)
39
+ else
40
+ type.nil? ? _value : type.new(_value)
41
+ end
42
+ else
43
+ TypeConverter.convert(_value, type)
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end