smooth_operator 1.3.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
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