smooth_operator 1.11.2 → 1.20.9

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.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OGZkNjkyYmRiYThmY2U0MmIzMWRlN2FkYjMzYzBlMmQ0MTdkMWRjMA==
4
+ ZDBhM2U5NjlkNjVmMzk4MGQxNTVkNTBiZDBhODI5NDlkMjRkZGQ2OQ==
5
5
  data.tar.gz: !binary |-
6
- MzI4M2U5YmI5NzhlYjVkMjIwMGRiYzM2Y2IxNmNiYjI0NTIzYjQ5Yw==
6
+ ZTg1N2IwOTY0ZDlmMmVjMTVjNGFmMzNjM2YxZjE5MGFlMTNiMDk3Mg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZGU4MDc5MjcxZmUyMTg2N2RiNWVlMjBjMTgwNzcwODMxZWJiZDM3YjQ3OThl
10
- ODMyZDcyNDNhMzk0YjVkMmZlODZiZjg5NDZlMzY2MzA4Yzk5ZWI0YmViODMx
11
- Y2IwMGI0ZTQyZWFhMWU3MmJhNjg1Y2M3YzYxMjZkZDgyYWYwYWU=
9
+ MWQzYjFkMGNmNDY2NmU2MzFjOThjMWQzYWZjMjNiNjNjYTQwZDZkNGVlYzdm
10
+ YzIxODAyNWI4YTZiNjVhYjk2NzgzMGNjYjc1MGVhNWY0ZGRiNWNiZDQ5MGFl
11
+ Y2E3NGM2ZDNiNWFiYTg2YjMzNTVhMmNkODM0MTYxZTg2YzBmMTU=
12
12
  data.tar.gz: !binary |-
13
- YTIwYmQ1YTNjYzliZjJmOTg3YWEyNjczZWZhMWU1YzkwODM2NDJjZGM2YzU4
14
- MmNmOWQ2ZjZkNWRiMDQ0MDgxMTY1N2UxYzgxN2ZiNTVmNGFmNzQxOWM0N2M3
15
- MWFiY2I1ZThiOTM2MjI1MGI5NmIyZGY4YTU3YTk5YTEyMGJhN2U=
13
+ OGYwYjBiY2U2NWUzMThhZGIwOTZiOWMzOWZkYzM2NDZiODE4NmFkYWJmNzhj
14
+ NTI3MWU4NDVjOTU0ZTdkNTU1MWRiOTk3NzRmYmFjOGJkNjgyNzhlOTFjMzMx
15
+ NTgxNzUzZjQ1MDQ3MzNkNTJiYzNlM2E2NjUyN2ZiZWI3YjRmMmQ=
data/console.rb CHANGED
@@ -27,5 +27,8 @@ LocalhostServer.new(TestServer.new, 4567)
27
27
 
28
28
  # "[{\"patient_id\"=>33, \"messages\"=>[{\"id\"=>\"53722c20cb38247c36000003\", \"title\"=>\"Joao Goncalves\", \"created_at\"=>\"2014-05-13T14:28:48Z\"}, {\"id\"=>\"53722bfccb382485d5000002\", \"title\"=>\"Joao Goncalves\", \"created_at\"=>\"2014-05-13T14:28:12Z\"}, {\"id\"=>\"53722b91cb3824e913000001\", \"title\"=>\"Joao Goncalves\", \"created_at\"=>\"2014-05-13T14:26:25Z\"}]}]"
29
29
 
30
- binding.pry
30
+ post = Post.new(comments: [{ id: 1, name: '1' }, { id: 2, name: '2' }], address: { id: 1, name: 'address' })
31
+
32
+ comments_attributes = { "0" => { id: 1, name: '3' }, "1" => { name: '4' } }
31
33
 
34
+ binding.pry
@@ -1,15 +1,8 @@
1
1
  module SmoothOperator
2
-
3
- class ArrayWithMetaData < OpenStruct::Base
4
-
5
- extend Forwardable
6
-
7
- include Enumerable
2
+ class ArrayWithMetaData < ::SimpleDelegator
8
3
 
9
4
  attr_reader :meta_data, :internal_array
10
5
 
11
- def_delegators :internal_array, :length, :<<, :[]
12
-
13
6
  def initialize(attributes, object_class)
14
7
  _attributes, _resources_name = attributes.dup, object_class.resources_name
15
8
 
@@ -17,18 +10,14 @@ module SmoothOperator
17
10
  _attributes.delete(_resources_name)
18
11
 
19
12
  @meta_data = _attributes
20
- end
21
13
 
22
-
23
- def each
24
- internal_array.each { |array_entry| yield array_entry }
14
+ super(@internal_array)
25
15
  end
26
-
16
+
27
17
  def method_missing(method, *args, &block)
28
18
  _method = method.to_s
29
19
  meta_data.include?(_method) ? meta_data[_method] : super
30
20
  end
31
21
 
32
22
  end
33
-
34
23
  end
@@ -3,7 +3,6 @@ require 'smooth_operator/attributes/dirty'
3
3
  require 'smooth_operator/attributes/normal'
4
4
 
5
5
  module SmoothOperator
6
-
7
6
  module AttributeAssignment
8
7
 
9
8
  def self.included(base)
@@ -44,7 +43,6 @@ module SmoothOperator
44
43
 
45
44
  end
46
45
 
47
-
48
46
  def initialize(attributes = {}, options = {})
49
47
  @_options = {}
50
48
 
@@ -70,7 +68,7 @@ module SmoothOperator
70
68
 
71
69
  options.each { |key, value| @_options[key] = value } if options.is_a?(Hash)
72
70
 
73
- attributes.each { |name, value| push_to_internal_data(name, value, true) }
71
+ attributes.each { |name, value| push_to_internal_data(name, value) }
74
72
  end
75
73
 
76
74
  def parent_object
@@ -83,40 +81,6 @@ module SmoothOperator
83
81
 
84
82
  alias :from_server :has_data_from_server
85
83
 
86
- def internal_data
87
- @internal_data ||= {}
88
- end
89
-
90
- def get_internal_data(field, method = :value)
91
- result = internal_data[field]
92
-
93
- if result.nil?
94
- nil
95
- elsif method == :value
96
- result.is_a?(Attributes::Dirty) ? internal_data[field].send(method) : internal_data[field]
97
- else
98
- internal_data[field].send(method)
99
- end
100
- end
101
-
102
- def push_to_internal_data(attribute_name, attribute_value, cast = false)
103
- attribute_name = attribute_name.to_s
104
-
105
- return nil unless allowed_attribute(attribute_name)
106
-
107
- known_attributes.add attribute_name
108
-
109
- if internal_data[attribute_name].nil?
110
- initiate_internal_data(attribute_name, attribute_value, cast)
111
- else
112
- update_internal_data(attribute_name, attribute_value, cast)
113
- end
114
-
115
- if self.class.respond_to?(:smooth_operator?) && attribute_name == self.class.primary_key
116
- new_record?(true)
117
- end
118
- end
119
-
120
84
  protected #################### PROTECTED METHODS DOWN BELOW ######################
121
85
 
122
86
  def before_initialize(attributes, options); end
@@ -133,33 +97,5 @@ module SmoothOperator
133
97
  end
134
98
  end
135
99
 
136
-
137
- private ######################## PRIVATE #############################
138
-
139
- def initiate_internal_data(attribute_name, attribute_value, cast)
140
- if cast
141
- internal_data[attribute_name] = new_attribute_object(attribute_name, attribute_value)
142
-
143
- internal_data[attribute_name] = internal_data[attribute_name].value unless self.class.dirty_attributes?
144
- else
145
- internal_data[attribute_name] = attribute_value
146
- end
147
- end
148
-
149
- def update_internal_data(attribute_name, attribute_value, cast)
150
- if self.class.dirty_attributes?
151
- internal_data[attribute_name].set_value(attribute_value, self)
152
- else
153
- internal_data[attribute_name] = cast ? new_attribute_object(attribute_name, attribute_value).value : attribute_value
154
- end
155
- end
156
-
157
- def new_attribute_object(attribute_name, attribute_value)
158
- attribute_class = self.class.dirty_attributes? ? Attributes::Dirty : Attributes::Normal
159
-
160
- attribute_class.new(attribute_name, attribute_value, self)
161
- end
162
-
163
100
  end
164
-
165
101
  end
@@ -0,0 +1,78 @@
1
+ module SmoothOperator
2
+ module AttributeMethods
3
+
4
+ def internal_data
5
+ @internal_data ||= {}
6
+ end
7
+
8
+ def get_internal_data(field, method = :value)
9
+ result = internal_data[field]
10
+
11
+ if result.nil?
12
+ nil
13
+ elsif method == :value
14
+ result.is_a?(Attributes::Dirty) ? internal_data[field].send(method) : internal_data[field]
15
+ else
16
+ internal_data[field].send(method)
17
+ end
18
+ end
19
+
20
+ def get_attribute_type(attribute)
21
+ self.class.internal_structure[attribute.to_s]
22
+ end
23
+
24
+ def push_to_internal_data(attribute_name, attribute_value)
25
+ attribute_name = attribute_name.to_s
26
+
27
+ return nil unless allowed_attribute(attribute_name)
28
+
29
+ known_attributes.add attribute_name
30
+
31
+ initiate_or_update_internal_data(attribute_name, attribute_value)
32
+
33
+ new_record_or_mark_for_destruction?(attribute_name, attribute_value)
34
+ end
35
+
36
+ protected #################### PROTECTED METHODS DOWN BELOW ######################
37
+
38
+ def initiate_or_update_internal_data(attribute_name, attribute_value)
39
+ if internal_data[attribute_name].nil?
40
+ initiate_internal_data(attribute_name, attribute_value)
41
+ else
42
+ update_internal_data(attribute_name, attribute_value)
43
+ end
44
+ end
45
+
46
+ def new_record_or_mark_for_destruction?(attribute_name, attribute_value)
47
+ return nil unless self.class.respond_to?(:smooth_operator?)
48
+
49
+ marked_for_destruction?(attribute_value) if attribute_name == self.class.destroy_key
50
+
51
+ new_record?(true) if attribute_name == self.class.primary_key
52
+ end
53
+
54
+ private ######################## PRIVATE #############################
55
+
56
+ def initiate_internal_data(attribute_name, attribute_value)
57
+ internal_data[attribute_name] = new_attribute_object(attribute_name, attribute_value)
58
+
59
+ internal_data[attribute_name] = internal_data[attribute_name].value unless self.class.dirty_attributes?
60
+ end
61
+
62
+ def update_internal_data(attribute_name, attribute_value)
63
+ if self.class.dirty_attributes?
64
+ internal_data[attribute_name].set_value(attribute_value, self)
65
+ else
66
+ internal_data[attribute_name] = new_attribute_object(attribute_name, attribute_value).value
67
+ end
68
+ end
69
+
70
+ def new_attribute_object(attribute_name, attribute_value)
71
+ attribute_class = self.class.dirty_attributes? ? Attributes::Dirty : Attributes::Normal
72
+
73
+ attribute_class.new(attribute_name, attribute_value, self)
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -6,7 +6,7 @@ module SmoothOperator
6
6
  protected ##################### PROTECTED ########################
7
7
 
8
8
  def cast_to_type(name, value, parent_object)
9
- known_by_schema, type, unknown_hash_class = parent_object.internal_structure.include?(name), parent_object.internal_structure[name], parent_object.class.unknown_hash_class
9
+ known_by_schema, type, unknown_hash_class = parent_object.known_by_schema?(name), parent_object.get_attribute_type(name), parent_object.class.unknown_hash_class
10
10
 
11
11
  return Helpers.duplicate(value) if known_by_schema && type.nil?
12
12
 
@@ -25,7 +25,7 @@ module SmoothOperator
25
25
 
26
26
  when :string, :text, String
27
27
  value.to_s
28
-
28
+
29
29
  when :int, :integer, Integer, Fixnum
30
30
  to_int(value)
31
31
 
@@ -74,7 +74,7 @@ module SmoothOperator
74
74
  return string if string.is_a?(Float)
75
75
 
76
76
  return 0 if string.nil? || !(string.is_a?(String) || string.is_a?(Fixnum))
77
-
77
+
78
78
  value = string.to_s.gsub(',', '.').scan(/-*\d+[.]*\d*/).flatten.map(&:to_f).first
79
79
 
80
80
  value.nil? ? 0 : value
@@ -0,0 +1,7 @@
1
+ # THANKS tdantas! https://github.com/tdantas
2
+
3
+ module SmoothOperator
4
+ class BlankSlate
5
+ instance_methods.each { |m| undef_method m unless m =~ /^__|object_id/ }
6
+ end
7
+ end
@@ -2,8 +2,8 @@ module SmoothOperator
2
2
 
3
3
  module Delegation
4
4
 
5
- def respond_to?(method)
6
- known_attributes.include?(method.to_s) ? true : super
5
+ def respond_to?(method, include_private = false)
6
+ known_attribute?(method) ? true : super
7
7
  end
8
8
 
9
9
  def method_missing(method, *args, &block)
@@ -17,7 +17,9 @@ module SmoothOperator
17
17
  when :setter
18
18
  return push_to_internal_data(method_name, args.first)
19
19
  else
20
- return get_internal_data(method_name) if !self.class.strict_behaviour || respond_to?(method_name)
20
+ if !self.class.strict_behaviour || known_attribute?(method_name)
21
+ return get_internal_data(method_name)
22
+ end
21
23
  end
22
24
 
23
25
  result.nil? ? super : result
@@ -28,7 +30,7 @@ module SmoothOperator
28
30
 
29
31
  def parse_method(method)
30
32
  method = method.to_s
31
-
33
+
32
34
  if method?(method, /=$/)
33
35
  [:setter, method[0..-2]]
34
36
  elsif method?(method, /_was$/)
@@ -3,7 +3,15 @@ module SmoothOperator
3
3
  module Helpers
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
13
+ end
14
+
7
15
  def super_method(object, method_name, *args)
8
16
  object.superclass.send(method_name, *args) if object.superclass.respond_to?(method_name)
9
17
  end
@@ -35,6 +43,11 @@ module SmoothOperator
35
43
  end
36
44
  end
37
45
 
46
+ def plural?(string)
47
+ string = string.to_s
48
+ string == string.pluralize
49
+ end
50
+
38
51
  def duplicate(object)
39
52
  object.dup rescue object
40
53
  end
@@ -61,7 +74,6 @@ module SmoothOperator
61
74
  def remove_initial_slash(string)
62
75
  string[1..-1]
63
76
  end
64
-
77
+
65
78
  end
66
-
67
79
  end
@@ -1,22 +1,24 @@
1
1
  module SmoothOperator
2
-
3
2
  module ModelSchema
4
3
 
5
4
  def self.included(base)
6
5
  base.extend(ClassMethods)
7
6
  end
8
7
 
8
+ def known_attribute?(attribute)
9
+ known_attributes.include?(attribute.to_s)
10
+ end
11
+
12
+ def known_by_schema?(attribute)
13
+ self.class.internal_structure.include?(attribute.to_s)
14
+ end
15
+
9
16
  def known_attributes
10
17
  @known_attributes ||= self.class.known_attributes.dup
11
18
  end
12
19
 
13
- def internal_structure
14
- @internal_structure ||= self.class.internal_structure.dup
15
- end
16
-
17
-
18
20
  module ClassMethods
19
-
21
+
20
22
  def resources_name(default_bypass = nil)
21
23
  return @resources_name if defined?(@resources_name)
22
24
 
@@ -58,7 +60,6 @@ module SmoothOperator
58
60
  def model_name=(name)
59
61
  @_model_name = name
60
62
  end
61
-
62
63
 
63
64
  protected ############## PROTECTED #############
64
65
 
@@ -77,5 +78,4 @@ module SmoothOperator
77
78
  end
78
79
 
79
80
  end
80
-
81
81
  end
@@ -2,6 +2,7 @@ require "smooth_operator/delegation"
2
2
  require "smooth_operator/validations"
3
3
  require "smooth_operator/model_schema"
4
4
  require "smooth_operator/serialization"
5
+ require "smooth_operator/attribute_methods"
5
6
  require "smooth_operator/attribute_assignment"
6
7
 
7
8
  module SmoothOperator
@@ -13,6 +14,7 @@ module SmoothOperator
13
14
  include Validations
14
15
  include ModelSchema
15
16
  include Serialization
17
+ include AttributeMethods
16
18
  include AttributeAssignment
17
19
 
18
20
  def self.strict_behaviour=(value)
@@ -28,7 +30,7 @@ module SmoothOperator
28
30
  class Dirty < Base
29
31
 
30
32
  dirty_attributes
31
-
33
+
32
34
  end
33
35
 
34
36
  end
@@ -5,18 +5,17 @@ require "smooth_operator/remote_call/errors/timeout"
5
5
  require "smooth_operator/remote_call/errors/connection_failed"
6
6
 
7
7
  module SmoothOperator
8
-
9
8
  module Operator
10
9
 
11
10
  def make_the_call(http_verb, relative_path = '', data = {}, options = {})
12
11
  options ||= {}
13
-
12
+
14
13
  relative_path = resource_path(relative_path)
15
-
14
+
16
15
  if !parent_object.nil? && options[:ignore_parent] != true
17
16
  options[:resources_name] ||= "#{parent_object.class.resources_name}/#{parent_object.get_primary_key}/#{self.class.resources_name}"
18
17
  end
19
-
18
+
20
19
  self.class.make_the_call(http_verb, relative_path, data, options) do |remote_call|
21
20
  yield(remote_call)
22
21
  end
@@ -34,13 +33,12 @@ module SmoothOperator
34
33
  end
35
34
  end
36
35
 
37
-
38
36
  ########################### MODULES BELLOW ###############################
39
37
 
40
38
  module HttpMethods
41
39
 
42
40
  HTTP_VERBS = %w[get post put patch delete]
43
-
41
+
44
42
  HTTP_VERBS.each do |method|
45
43
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
46
44
  def #{method}(relative_path = '', params = {}, options = {})
@@ -78,7 +76,7 @@ module SmoothOperator
78
76
  else
79
77
  operator_call = Operators::Faraday
80
78
  end
81
-
79
+
82
80
  operator_call.make_the_call(*operator_args) do |remote_call|
83
81
  block_given? ? yield(remote_call) : remote_call
84
82
  end
@@ -88,7 +86,6 @@ module SmoothOperator
88
86
  params
89
87
  end
90
88
 
91
-
92
89
  protected #################### PROTECTED ##################
93
90
 
94
91
  def operator_method_args(http_verb, relative_path, data, options)
@@ -97,7 +94,6 @@ module SmoothOperator
97
94
  [http_verb, resource_path(relative_path, options), *strip_params(http_verb, data), options]
98
95
  end
99
96
 
100
-
101
97
  private #################### PRIVATE ##################
102
98
 
103
99
  def populate_options(options)
@@ -106,7 +102,7 @@ module SmoothOperator
106
102
  OPTIONS.each { |option| options[option] ||= send(option) }
107
103
 
108
104
  options[:headers] = headers.merge(options[:headers] || {})
109
-
105
+
110
106
  options
111
107
  end
112
108
 
@@ -122,17 +118,15 @@ module SmoothOperator
122
118
 
123
119
  def strip_params(http_verb, data)
124
120
  data ||= {}
125
-
121
+
126
122
  if [:get, :head, :delete].include?(http_verb)
127
123
  [query_string(data), nil]
128
124
  else
129
125
  [query_string({}), data]
130
126
  end
131
127
  end
132
-
133
128
  end
134
129
 
135
-
136
130
  include HttpMethods
137
131
 
138
132
  def self.included(base)
@@ -1,5 +1,4 @@
1
1
  module SmoothOperator
2
-
3
2
  module Persistence
4
3
 
5
4
  def self.included(base)
@@ -8,7 +7,6 @@ module SmoothOperator
8
7
 
9
8
  attr_reader :last_remote_call
10
9
 
11
-
12
10
  def get_primary_key
13
11
  get_internal_data(self.class.primary_key)
14
12
  end
@@ -27,6 +25,12 @@ module SmoothOperator
27
25
  @new_record = Helpers.blank?(get_primary_key)
28
26
  end
29
27
 
28
+ def marked_for_destruction?(bypass_cache = false)
29
+ return @marked_for_destruction if !bypass_cache && defined?(@marked_for_destruction)
30
+
31
+ @marked_for_destruction = ["true", "1", true].include?(get_internal_data(self.class.destroy_key))
32
+ end
33
+
30
34
  def destroyed?
31
35
  return @destroyed if defined?(@destroyed)
32
36
 
@@ -105,32 +109,36 @@ module SmoothOperator
105
109
 
106
110
  { self.class.resource_name => hash }.merge(data)
107
111
  end
108
-
109
-
112
+
113
+
110
114
  module ClassMethods
111
-
115
+
112
116
  METHODS_VS_HTTP_VERBS = { reload: :get, create: :post, update: :put, destroy: :delete }
113
-
117
+
114
118
  def methods_vs_http_verbs
115
119
  Helpers.get_instance_variable(self, :methods_vs_http_verbs, METHODS_VS_HTTP_VERBS.dup)
116
120
  end
117
-
121
+
118
122
  def primary_key
119
123
  Helpers.get_instance_variable(self, :primary_key, 'id')
120
124
  end
121
125
 
122
126
  attr_writer :primary_key
123
127
 
128
+ def destroy_key
129
+ Helpers.get_instance_variable(self, :destroy_key, '_destroy')
130
+ end
131
+
132
+ attr_writer :destroy_key
133
+
124
134
  METHODS_VS_HTTP_VERBS.keys.each do |method|
125
135
  define_method("#{method}_http_verb=") { |http_verb| methods_vs_http_verbs[method] = http_verb }
126
136
  end
127
-
137
+
128
138
  def create(attributes = nil, relative_path = nil, data = {}, options = {})
129
139
  new(attributes).tap { |object| object.save(relative_path, data, options) }
130
140
  end
131
-
132
- end
133
141
 
142
+ end
134
143
  end
135
-
136
144
  end
@@ -0,0 +1,55 @@
1
+ module SmoothOperator
2
+ module Relation
3
+ class ArrayRelation < ::SimpleDelegator
4
+
5
+ attr_reader :object, :association
6
+
7
+ def initialize(object, association)
8
+ @object, @association = object, association
9
+ end
10
+
11
+ def reload
12
+ "TODO"
13
+ end
14
+
15
+ def new(attributes = {})
16
+ object.class.reflect_on_association(association).klass.new(attributes)
17
+ end
18
+
19
+ def build(attributes = {})
20
+ new_array, new_array_entry = get_array, new(attributes)
21
+
22
+ new_array.push new_array_entry
23
+
24
+ object.send("#{association}=", new_array)
25
+
26
+ new_array_entry
27
+ end
28
+
29
+ undef :is_a?
30
+
31
+ protected ############### PROTECTED ###############
32
+
33
+ # def method_missing(method, *args, &block)
34
+ # if get_array.respond_to?(method)
35
+ # puts "if true #{method} - #{args}"
36
+ # get_array.send(method, *args)
37
+ # else
38
+ # puts "if else #{method}"
39
+ # super
40
+ # end
41
+ # end
42
+
43
+ def get_array
44
+ data = object.get_internal_data(association.to_s)
45
+
46
+ data.nil? ? [] : [*data]
47
+ end
48
+
49
+ def refresh
50
+ __setobj__ get_array
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,79 @@
1
+ require "smooth_operator/relation/reflection"
2
+
3
+ module SmoothOperator
4
+ module Relation
5
+ class AssociationReflection < Reflection
6
+
7
+ attr_reader :related_reflection, :macro
8
+
9
+ def initialize(association, related_reflection, options)
10
+ super(association, options)
11
+ @macro = options[:macro] || macro_default(association)
12
+ @related_reflection = related_reflection
13
+ end
14
+
15
+ def primary_key
16
+ @primary_key ||= options[:primary_key] || :id
17
+ end
18
+
19
+ def foreign_key
20
+ @foreign_key ||= options[:foreign_key] || foreign_key_default
21
+ end
22
+
23
+ def set_relational_keys(origin, destination)
24
+ return nil if options[:standalone] == true
25
+
26
+ if has_many? || has_one?
27
+ set_foreign_key(destination, primary_key_of(origin))
28
+ elsif belongs_to?
29
+ set_foreign_key(origin, primary_key_of(destination))
30
+ end
31
+ end
32
+
33
+ def set_foreign_key(object, id)
34
+ setter = "#{foreign_key}="
35
+
36
+ if object.respond_to?(setter)
37
+ object.send(setter, id)
38
+ elsif object.respond_to?("send_to_representative")
39
+ object.send_to_representative(setter, id)
40
+ end
41
+ end
42
+
43
+ def primary_key_of(object)
44
+ object.send(primary_key)
45
+ end
46
+
47
+ def has_many?
48
+ macro == :has_many
49
+ end
50
+
51
+ def has_one?
52
+ macro == :has_one
53
+ end
54
+
55
+ def belongs_to?
56
+ macro == :belongs_to
57
+ end
58
+
59
+ def collection?
60
+ has_many?
61
+ end
62
+
63
+ private ################################# private
64
+
65
+ def macro_default(association)
66
+ Helpers.plural?(association) ? :has_many : :belongs_to
67
+ end
68
+
69
+ def foreign_key_default
70
+ if has_many? || has_one?
71
+ "#{related_reflection.single_name}_id"
72
+ elsif belongs_to?
73
+ "#{single_name}_id"
74
+ end.to_sym
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,102 @@
1
+ require "smooth_operator/relation/array_relation"
2
+ require "smooth_operator/relation/association_reflection"
3
+
4
+ module SmoothOperator
5
+ module Relation
6
+ module Associations
7
+
8
+ def has_many(nested_object_name, options = {})
9
+ accepts_nested_objects(nested_object_name, :has_many, options)
10
+ end
11
+
12
+ def has_one(nested_object_name, options = {})
13
+ accepts_nested_objects(nested_object_name, :has_one, options)
14
+ end
15
+
16
+ def belongs_to(nested_object_name, options = {})
17
+ accepts_nested_objects(nested_object_name, :belongs_to, options)
18
+ end
19
+
20
+ def reflections
21
+ Helpers.get_instance_variable(self, :reflections, {})
22
+ end
23
+
24
+ def reflect_on_association(association)
25
+ reflections[association]
26
+ end
27
+
28
+ def reflect_on_all_associations(macro = nil)
29
+ macro ? reflections.values.select { |reflection| reflection.macro == macro } : reflections.values
30
+ end
31
+
32
+ protected ###################### PROTECTED ###################
33
+
34
+ def accepts_nested_objects(association, macro, options = {})
35
+ options = parse_options(options, { macro: macro })
36
+
37
+ reflection = AssociationReflection.new(association, Reflection.new(name, {}), options)
38
+
39
+ schema(association => reflection.klass)
40
+
41
+ reflections.merge!(association => reflection)
42
+
43
+ if reflection.has_many?
44
+ define_has_many_association_method(reflection, association)
45
+ else
46
+ define_single_association_method(reflection, association)
47
+ end
48
+
49
+ self.send(:attr_reader, "#{association}_attributes".to_sym)
50
+
51
+ define_attributes_setter_methods(reflection, association)
52
+ end
53
+
54
+ private ####################### PRIVATE ######################
55
+
56
+ def define_has_many_association_method(reflection, association)
57
+ define_method(association) do
58
+ array_relation = instance_variable_get("@#{association}")
59
+
60
+ if array_relation.nil?
61
+ array_relation = ArrayRelation.new(self, association)
62
+
63
+ instance_variable_set("@#{association}", array_relation)
64
+ end
65
+
66
+ array_relation.send(:refresh)
67
+
68
+ array_relation
69
+ end
70
+ end
71
+
72
+ def define_single_association_method(reflection, association)
73
+ define_method(association) { get_internal_data(association.to_s) }
74
+
75
+ define_method("build_#{association}") do |attributes = {}|
76
+ new_instance = reflection.klass.new(attributes)
77
+
78
+ push_to_internal_data(association, new_instance)
79
+
80
+ new_instance
81
+ end
82
+ end
83
+
84
+ def define_attributes_setter_methods(reflection, association)
85
+ define_method("#{association}_attributes=") do |attributes|
86
+ instance_variable_set("@#{association}_attributes", attributes)
87
+
88
+ attributes = attributes.values if reflection.has_many?
89
+
90
+ push_to_internal_data(association.to_s, attributes)
91
+ end
92
+ end
93
+
94
+ def parse_options(options, default_options)
95
+ options = options.is_a?(Hash) ? options.merge(default_options) : default_options
96
+
97
+ Helpers.symbolyze_keys(options)
98
+ end
99
+
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,41 @@
1
+ module SmoothOperator
2
+ module Relation
3
+ class Reflection
4
+
5
+ attr_reader :name, :klass, :options
6
+
7
+ def initialize(class_name, options)
8
+ options = options.is_a?(Hash) ? options : {}
9
+
10
+ @name, @options = class_name, options
11
+
12
+ @klass = options[:class_name] || klass_default(@name)
13
+
14
+ if options.include?(:class_name) && options[:class_name].nil?
15
+ @klass = nil
16
+ elsif @klass.is_a?(String)
17
+ @klass = @klass.constantize
18
+ end
19
+ end
20
+
21
+ def single_name
22
+ @single_name ||= options[:single_name] || name.to_s.singularize
23
+ end
24
+
25
+ def plural_name
26
+ @plural_name ||= options[:plural_name] || name.to_s.pluralize
27
+ end
28
+
29
+ private ################################# private
30
+
31
+ def klass_default(class_name)
32
+ if Helpers.plural?(class_name)
33
+ class_name.to_s.singularize.camelize
34
+ else
35
+ class_name.to_s.camelize
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -14,7 +14,6 @@ module SmoothOperator
14
14
  @response = response
15
15
  end
16
16
 
17
-
18
17
  def ok?
19
18
  http_status.between?(200, 299) || http_status == 304
20
19
  end
@@ -35,7 +34,6 @@ module SmoothOperator
35
34
  http_status.between?(500, 599) || http_status == 0
36
35
  end
37
36
 
38
-
39
37
  def not_found?
40
38
  http_status == 404
41
39
  end
@@ -48,12 +46,11 @@ module SmoothOperator
48
46
  false
49
47
  end
50
48
 
51
-
52
49
  def parsed_response
53
50
  return nil if body.nil?
54
-
51
+
55
52
  require 'json' unless defined? JSON
56
-
53
+
57
54
  begin
58
55
  JSON.parse(body)
59
56
  rescue JSON::ParserError
@@ -74,7 +71,5 @@ module SmoothOperator
74
71
  end
75
72
 
76
73
  end
77
-
78
74
  end
79
-
80
75
  end
@@ -1,27 +1,24 @@
1
1
  module SmoothOperator
2
-
3
2
  module Translation
4
3
 
5
4
  def human_attribute_name(attribute_key_name, options = {})
6
5
  _translate("attributes.#{model_name.i18n_key}.#{attribute_key_name}", options = {})
7
6
  end
8
7
 
9
-
10
8
  private ###################### PRIVATE #########################
11
9
 
12
10
  def _translate(namespace = '', options = {})
13
11
  no_translation = "-- no translation --"
14
-
12
+
15
13
  defaults = ["smooth_operator.#{namespace}".to_sym]
16
14
  defaults << "activerecord.#{namespace}".to_sym
17
15
  defaults << options[:default] if options[:default]
18
16
  defaults.flatten!
19
17
  defaults << no_translation
20
-
18
+
21
19
  options = { count: 1, default: defaults }.merge!(options.except(:default))
22
20
  I18n.translate(defaults.shift, options)
23
21
  end
24
22
 
25
23
  end
26
-
27
24
  end
@@ -1,3 +1,3 @@
1
1
  module SmoothOperator
2
- VERSION = "1.11.2"
2
+ VERSION = "1.20.9"
3
3
  end
@@ -5,11 +5,13 @@ require "smooth_operator/persistence"
5
5
  require "smooth_operator/translation"
6
6
  require "smooth_operator/open_struct"
7
7
  require "smooth_operator/finder_methods"
8
+ require "smooth_operator/relation/associations"
8
9
 
9
10
  module SmoothOperator
10
11
  class Base < OpenStruct::Base
11
12
 
12
13
  extend FinderMethods
14
+ extend Relation::Associations
13
15
  extend Translation if defined? I18n
14
16
 
15
17
  include Operator
@@ -23,4 +25,20 @@ module SmoothOperator
23
25
  end
24
26
 
25
27
  end
28
+
29
+ if defined?(ActiveModel)
30
+ class Rails < Base
31
+
32
+ include ActiveModel::Validations
33
+ include ActiveModel::Validations::Callbacks
34
+ include ActiveModel::Conversion
35
+
36
+ def column_for_attribute(attribute_name)
37
+ type = get_attribute_type(attribute_name)
38
+
39
+ ActiveRecord::ConnectionAdapters::Column.new(attribute_name.to_sym, type, type)
40
+ end
41
+
42
+ end
43
+ end
26
44
  end
@@ -20,12 +20,12 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
21
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
22
  spec.require_paths = ["lib"]
23
-
23
+
24
24
  spec.add_development_dependency "bundler", "~> 1.3"
25
-
26
- spec.add_dependency "json"
27
- spec.add_dependency "faraday", "~> 0.8.9"
28
- spec.add_dependency "typhoeus", "~> 0.6.8"
25
+
26
+ spec.add_dependency "json", '~> 1.8'
27
+ spec.add_dependency "faraday", "~> 0.8"
28
+ spec.add_dependency "typhoeus", "~> 0.6"
29
29
 
30
30
  # this is necessary if you want to typhoeus to correctly encode arrays
31
31
  # spec.add_dependency "ethon", :git => 'https://github.com/goncalvesjoao/ethon'
@@ -4,7 +4,7 @@ require "spec_helper"
4
4
  describe SmoothOperator::AttributeAssignment do
5
5
 
6
6
  describe "#assign_attributes" do
7
-
7
+
8
8
  describe "receiving data from server" do
9
9
  subject { User::Base.new }
10
10
 
@@ -70,7 +70,7 @@ describe SmoothOperator::AttributeAssignment do
70
70
  end
71
71
 
72
72
  context "when one of the attribute's value, is an hash and is unknown to the schema" do
73
- context "when the .unknown_hash_class is unused" do
73
+ context "when the .unknown_hash_class is unused", current: true do
74
74
  subject { User::Base.new(address: { street: 'something', postal_code: { code: '123' } }) }
75
75
 
76
76
  it "a new instance of OpenStruct will be initialized with that hash" do
@@ -83,7 +83,7 @@ describe SmoothOperator::AttributeAssignment do
83
83
  expect(address.postal_code.code).to eq('123')
84
84
  end
85
85
  end
86
-
86
+
87
87
  context "when the .unknown_hash_class is set to SmoothOperator::OpenStruct::Base" do
88
88
  subject { User::UnknownHashClass::OpenStructBase.new(address: { street: 'something', postal_code: { code: '123' } }) }
89
89
 
@@ -246,7 +246,7 @@ describe SmoothOperator::AttributeAssignment do
246
246
  it "when the attributes's value is 'false', should be converted to false" do
247
247
  expect(subject.new(manager: 'false').manager).to be(false)
248
248
  end
249
-
249
+
250
250
  it "when the attributes's value is '1', should be converted to true" do
251
251
  expect(subject.new(manager: '1').manager).to be(true)
252
252
  end
@@ -264,7 +264,7 @@ describe SmoothOperator::AttributeAssignment do
264
264
  end
265
265
 
266
266
  end
267
-
267
+
268
268
  context "that is declared (in schema) as an existing class" do
269
269
 
270
270
  it "if the attribute's value is an hash a new instance of that class will be initialized with that hash" do
@@ -280,12 +280,12 @@ describe SmoothOperator::AttributeAssignment do
280
280
 
281
281
  it "if the attribute's value is an array, a new instance of that class will be initialized for each array entry" do
282
282
  posts = subject.new(posts: [{ body: 'post1' }, { body: 'post2' }]).posts
283
-
283
+
284
284
  expect(posts.length).to be(2)
285
285
 
286
286
  expect(posts[0]).to be_instance_of(Post)
287
287
  expect(posts[0].body).to eq('post1')
288
-
288
+
289
289
  expect(posts[1]).to be_instance_of(Post)
290
290
  expect(posts[1].body).to eq('post2')
291
291
  end
@@ -10,5 +10,5 @@ class Address < SmoothOperator::Base
10
10
  self.endpoint = 'http://localhost:4567/'
11
11
 
12
12
  self.headers = { "X-APPTOKEN" => "joaquim_app_token", "X-LAYERTOKEN" => "joaquim_layer_token" }
13
-
13
+
14
14
  end
@@ -0,0 +1,3 @@
1
+ class Comment < SmoothOperator::Base
2
+
3
+ end
@@ -3,7 +3,11 @@ class Post < SmoothOperator::Base
3
3
  self.endpoint_user = 'admin'
4
4
 
5
5
  self.endpoint_pass = 'admin'
6
-
6
+
7
7
  self.endpoint = 'http://localhost:4567/'
8
8
 
9
+ has_many :comments
10
+
11
+ belongs_to :address
12
+
9
13
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smooth_operator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.2
4
+ version: 1.20.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - João Gonçalves
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-28 00:00:00.000000000 Z
11
+ date: 2014-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -28,44 +28,44 @@ dependencies:
28
28
  name: json
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ! '>='
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '1.8'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ! '>='
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '1.8'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: faraday
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ~>
46
46
  - !ruby/object:Gem::Version
47
- version: 0.8.9
47
+ version: '0.8'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
- version: 0.8.9
54
+ version: '0.8'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: typhoeus
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: 0.6.8
61
+ version: '0.6'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ~>
67
67
  - !ruby/object:Gem::Version
68
- version: 0.6.8
68
+ version: '0.6'
69
69
  description: ActiveResource alternative
70
70
  email:
71
71
  - goncalves.joao@gmail.com
@@ -83,9 +83,11 @@ files:
83
83
  - lib/smooth_operator.rb
84
84
  - lib/smooth_operator/array_with_meta_data.rb
85
85
  - lib/smooth_operator/attribute_assignment.rb
86
+ - lib/smooth_operator/attribute_methods.rb
86
87
  - lib/smooth_operator/attributes/base.rb
87
88
  - lib/smooth_operator/attributes/dirty.rb
88
89
  - lib/smooth_operator/attributes/normal.rb
90
+ - lib/smooth_operator/blank_slate.rb
89
91
  - lib/smooth_operator/delegation.rb
90
92
  - lib/smooth_operator/finder_methods.rb
91
93
  - lib/smooth_operator/helpers.rb
@@ -95,6 +97,10 @@ files:
95
97
  - lib/smooth_operator/operators/faraday.rb
96
98
  - lib/smooth_operator/operators/typhoeus.rb
97
99
  - lib/smooth_operator/persistence.rb
100
+ - lib/smooth_operator/relation/array_relation.rb
101
+ - lib/smooth_operator/relation/association_reflection.rb
102
+ - lib/smooth_operator/relation/associations.rb
103
+ - lib/smooth_operator/relation/reflection.rb
98
104
  - lib/smooth_operator/remote_call/base.rb
99
105
  - lib/smooth_operator/remote_call/errors/connection_failed.rb
100
106
  - lib/smooth_operator/remote_call/errors/timeout.rb
@@ -121,6 +127,7 @@ files:
121
127
  - spec/support/helpers/persistence_helper.rb
122
128
  - spec/support/localhost_server.rb
123
129
  - spec/support/models/address.rb
130
+ - spec/support/models/comment.rb
124
131
  - spec/support/models/post.rb
125
132
  - spec/support/models/user.rb
126
133
  - spec/support/models/user_with_address_and_posts.rb
@@ -145,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
152
  version: '0'
146
153
  requirements: []
147
154
  rubyforge_project:
148
- rubygems_version: 2.1.10
155
+ rubygems_version: 2.2.2
149
156
  signing_key:
150
157
  specification_version: 4
151
158
  summary: Simple and fully customizable alternative to ActiveResource, using faraday
@@ -167,6 +174,7 @@ test_files:
167
174
  - spec/support/helpers/persistence_helper.rb
168
175
  - spec/support/localhost_server.rb
169
176
  - spec/support/models/address.rb
177
+ - spec/support/models/comment.rb
170
178
  - spec/support/models/post.rb
171
179
  - spec/support/models/user.rb
172
180
  - spec/support/models/user_with_address_and_posts.rb