smooth_operator 1.21.0 → 1.21.1
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 +8 -8
- data/README.md +11 -3
- data/lib/smooth_operator/array_with_meta_data.rb +1 -1
- data/lib/smooth_operator/{relation → associations}/association_reflection.rb +2 -2
- data/lib/smooth_operator/{relation/array_relation.rb → associations/has_many_relation.rb} +2 -2
- data/lib/smooth_operator/{relation → associations}/reflection.rb +1 -1
- data/lib/smooth_operator/associations.rb +100 -0
- data/lib/smooth_operator/attribute_assignment.rb +1 -5
- data/lib/smooth_operator/http_methods.rb +17 -0
- data/lib/smooth_operator/operator.rb +40 -51
- data/lib/smooth_operator/persistence.rb +5 -5
- data/lib/smooth_operator/schema.rb +1 -2
- data/lib/smooth_operator/validations.rb +11 -3
- data/lib/smooth_operator/version.rb +1 -1
- data/lib/smooth_operator.rb +25 -7
- metadata +7 -6
- data/lib/smooth_operator/relation/associations.rb +0 -102
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
N2I1ZjBjNDAwNjFjNzE5ODJkNmE4ZTA0MGViZjYxMjgzOGNiNzM2ZQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YzBmMWExNmJiMzJjNWMxMTFhNDFlYWQ4YjBmMGI4ZmNlNTUyZjJlOQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YTYxZmRjNmVlNGViM2MyZjU0NjJiNDhkNWMyN2QyNmU0MmU1YmEzOWY4ZWRh
|
10
|
+
NDE2MDliOTAwZjNjMzRkZDliYmQ3MDc4NjAyMDZjMGRjMGFhZjQ4OWU2ZGYw
|
11
|
+
YjVlYzkwNjYxZjJiNTgzNDM5M2E4MzFkNzQ0Y2U3MGM2YTRhYTA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
N2I4MjM1YjI1MzE5YjhjOGJjNDZkMzU0MTBhMzE5OGFhMTA3MDIwOWNlYzE2
|
14
|
+
OWI2N2I0MjdiYTg1NDhlZDNkMmRiNDI5YmEyOWNjNzY2YWVhZGZlMzcyMDUw
|
15
|
+
ZWMzZmNjYjZkNDAzOTdmOWJjNTAxMTU3MjA2MzQ2M2VkOGRkNWE=
|
data/README.md
CHANGED
@@ -1,11 +1,19 @@
|
|
1
|
-
# SmoothOperator
|
1
|
+
# SmoothOperator [](https://codeclimate.com/repos/536a7b9f6956801228014b02/feed)
|
2
2
|
|
3
3
|
Ruby gem, that mimics the ActiveRecord behaviour but through external API's.
|
4
4
|
It's a lightweight and flexible alternative to ActiveResource, that responds to a REST API like you expect it too.
|
5
5
|
|
6
|
-
|
6
|
+
Be sure to check out this micro-services example: https://github.com/goncalvesjoao/micro-services-example
|
7
7
|
|
8
|
-
|
8
|
+
Where a Rails4 app lists/creates/edits and destroys blog posts from a Padrino (aka Sinatra) app, using SmoothOperator::Rails instead of ActiveRecord::Base classes.
|
9
|
+
|
10
|
+
This micro-services example will also feature other cool stuff like:
|
11
|
+
- parallel requests;
|
12
|
+
- using HTTP PATCH verb for saving instead of PUT;
|
13
|
+
- form errors with simple_form gem;
|
14
|
+
- nested objects using cocoon gem;
|
15
|
+
- endless-pagination with kaminari gem
|
16
|
+
- and others...
|
9
17
|
|
10
18
|
---
|
11
19
|
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require "smooth_operator/associations/has_many_relation"
|
2
|
+
require "smooth_operator/associations/association_reflection"
|
3
|
+
|
4
|
+
module SmoothOperator
|
5
|
+
module Associations
|
6
|
+
|
7
|
+
def has_many(nested_object_name, options = {})
|
8
|
+
accepts_nested_objects(nested_object_name, :has_many, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_one(nested_object_name, options = {})
|
12
|
+
accepts_nested_objects(nested_object_name, :has_one, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def belongs_to(nested_object_name, options = {})
|
16
|
+
accepts_nested_objects(nested_object_name, :belongs_to, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def reflections
|
20
|
+
Helpers.get_instance_variable(self, :reflections, {})
|
21
|
+
end
|
22
|
+
|
23
|
+
def reflect_on_association(association)
|
24
|
+
reflections[association]
|
25
|
+
end
|
26
|
+
|
27
|
+
def reflect_on_all_associations(macro = nil)
|
28
|
+
macro ? reflections.values.select { |reflection| reflection.macro == macro } : reflections.values
|
29
|
+
end
|
30
|
+
|
31
|
+
protected ###################### PROTECTED ###################
|
32
|
+
|
33
|
+
def accepts_nested_objects(association, macro, options = {})
|
34
|
+
options = parse_options(options, { macro: macro })
|
35
|
+
|
36
|
+
reflection = AssociationReflection.new(association, Reflection.new(name, {}), options)
|
37
|
+
|
38
|
+
schema(association => reflection.klass)
|
39
|
+
|
40
|
+
reflections.merge!(association => reflection)
|
41
|
+
|
42
|
+
if reflection.has_many?
|
43
|
+
define_has_many_association_method(reflection, association)
|
44
|
+
else
|
45
|
+
define_single_association_method(reflection, association)
|
46
|
+
end
|
47
|
+
|
48
|
+
self.send(:attr_reader, "#{association}_attributes".to_sym)
|
49
|
+
|
50
|
+
define_attributes_setter_methods(reflection, association)
|
51
|
+
end
|
52
|
+
|
53
|
+
private ####################### PRIVATE ######################
|
54
|
+
|
55
|
+
def define_has_many_association_method(reflection, association)
|
56
|
+
define_method(association) do
|
57
|
+
has_many_relation = instance_variable_get("@#{association}")
|
58
|
+
|
59
|
+
if has_many_relation.nil?
|
60
|
+
has_many_relation = HasManyRelation.new(self, association)
|
61
|
+
|
62
|
+
instance_variable_set("@#{association}", has_many_relation)
|
63
|
+
end
|
64
|
+
|
65
|
+
has_many_relation.send(:refresh)
|
66
|
+
|
67
|
+
has_many_relation
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def define_single_association_method(reflection, association)
|
72
|
+
define_method(association) { internal_data_get(association.to_s) }
|
73
|
+
|
74
|
+
define_method("build_#{association}") do |attributes = {}|
|
75
|
+
new_instance = reflection.klass.new(attributes)
|
76
|
+
|
77
|
+
internal_data_push(association, new_instance)
|
78
|
+
|
79
|
+
new_instance
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def define_attributes_setter_methods(reflection, association)
|
84
|
+
define_method("#{association}_attributes=") do |attributes|
|
85
|
+
instance_variable_set("@#{association}_attributes", attributes)
|
86
|
+
|
87
|
+
attributes = attributes.values if reflection.has_many?
|
88
|
+
|
89
|
+
internal_data_push(association.to_s, attributes)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_options(options, default_options)
|
94
|
+
options = options.is_a?(Hash) ? options.merge(default_options) : default_options
|
95
|
+
|
96
|
+
Helpers.symbolyze_keys(options)
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -23,7 +23,7 @@ module SmoothOperator
|
|
23
23
|
@_meta_data = _attributes
|
24
24
|
end
|
25
25
|
|
26
|
-
|
26
|
+
induce_errors(attributes.delete(self.class.errors_key))
|
27
27
|
|
28
28
|
options.each { |key, value| @_options[key] = value } if options.is_a? Hash
|
29
29
|
|
@@ -34,10 +34,6 @@ module SmoothOperator
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
def _parent_object
|
38
|
-
_options[:parent_object]
|
39
|
-
end
|
40
|
-
|
41
37
|
protected ################# PROTECTED METHODS DOWN BELOW ###################
|
42
38
|
|
43
39
|
def before_initialize(attributes, options); end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module SmoothOperator
|
2
|
+
module HttpMethods
|
3
|
+
|
4
|
+
HTTP_VERBS = %w[get post put patch delete]
|
5
|
+
|
6
|
+
HTTP_VERBS.each do |method|
|
7
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
8
|
+
def #{method}(relative_path = '', params = {}, options = {})
|
9
|
+
make_the_call(:#{method}, relative_path, params, options) do |remote_call|
|
10
|
+
block_given? ? yield(remote_call) : remote_call
|
11
|
+
end
|
12
|
+
end
|
13
|
+
RUBY
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -12,10 +12,12 @@ module SmoothOperator
|
|
12
12
|
|
13
13
|
relative_path = resource_path(relative_path)
|
14
14
|
|
15
|
-
|
16
|
-
id = Helpers.primary_key(_parent_object)
|
15
|
+
parent_object = _options[:parent_object]
|
17
16
|
|
18
|
-
|
17
|
+
if !parent_object.nil? && options[:ignore_parent] != true
|
18
|
+
id = Helpers.primary_key(parent_object)
|
19
|
+
|
20
|
+
options[:resources_name] ||= "#{parent_object.class.resources_name}/#{id}/#{self.class.resources_name}"
|
19
21
|
end
|
20
22
|
|
21
23
|
self.class.make_the_call(http_verb, relative_path, data, options) do |remote_call|
|
@@ -23,8 +25,6 @@ module SmoothOperator
|
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
|
-
protected ######################## PROTECTED ###################
|
27
|
-
|
28
28
|
def resource_path(relative_path)
|
29
29
|
if Helpers.absolute_path?(relative_path)
|
30
30
|
Helpers.remove_initial_slash(relative_path)
|
@@ -41,24 +41,6 @@ module SmoothOperator
|
|
41
41
|
base.extend(ClassMethods)
|
42
42
|
end
|
43
43
|
|
44
|
-
########################### MODULES BELLOW ###############################
|
45
|
-
|
46
|
-
module HttpMethods
|
47
|
-
|
48
|
-
HTTP_VERBS = %w[get post put patch delete]
|
49
|
-
|
50
|
-
HTTP_VERBS.each do |method|
|
51
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
52
|
-
def #{method}(relative_path = '', params = {}, options = {})
|
53
|
-
make_the_call(:#{method}, relative_path, params, options) do |remote_call|
|
54
|
-
block_given? ? yield(remote_call) : remote_call
|
55
|
-
end
|
56
|
-
end
|
57
|
-
RUBY
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
44
|
module ClassMethods
|
63
45
|
|
64
46
|
OPTIONS = [:endpoint, :endpoint_user, :endpoint_pass, :timeout]
|
@@ -76,15 +58,15 @@ module SmoothOperator
|
|
76
58
|
attr_writer :headers
|
77
59
|
|
78
60
|
def make_the_call(http_verb, relative_path = '', data = {}, options = {})
|
79
|
-
|
61
|
+
options = HelperMethods.populate_options(self, options)
|
80
62
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
operator_call = Operators::Faraday
|
85
|
-
end
|
63
|
+
resource_path = resource_path(relative_path, options)
|
64
|
+
|
65
|
+
params, data = *HelperMethods.strip_params(self, http_verb, data)
|
86
66
|
|
87
|
-
|
67
|
+
operator = HelperMethods.get_me_an_operator(options)
|
68
|
+
|
69
|
+
operator.make_the_call(http_verb, resource_path, params, data, options) do |remote_call|
|
88
70
|
block_given? ? yield(remote_call) : remote_call
|
89
71
|
end
|
90
72
|
end
|
@@ -93,45 +75,52 @@ module SmoothOperator
|
|
93
75
|
params
|
94
76
|
end
|
95
77
|
|
96
|
-
|
97
|
-
|
98
|
-
def operator_method_args(http_verb, relative_path, data, options)
|
99
|
-
options = populate_options(options)
|
78
|
+
def resource_path(relative_path, options)
|
79
|
+
resources_name = options[:resources_name] || self.resources_name
|
100
80
|
|
101
|
-
|
81
|
+
if Helpers.present?(resources_name)
|
82
|
+
Helpers.present?(relative_path) ? "#{resources_name}/#{relative_path}" : resources_name
|
83
|
+
else
|
84
|
+
relative_path.to_s
|
85
|
+
end
|
102
86
|
end
|
103
87
|
|
104
|
-
|
105
|
-
|
106
|
-
def populate_options(options)
|
107
|
-
options ||= {}
|
88
|
+
end
|
108
89
|
|
109
|
-
|
90
|
+
module HelperMethods
|
110
91
|
|
111
|
-
|
92
|
+
extend self
|
112
93
|
|
113
|
-
|
94
|
+
def get_me_an_operator(options)
|
95
|
+
if options[:parallel_connection].nil?
|
96
|
+
Operators::Faraday
|
97
|
+
else
|
98
|
+
Operators::Typhoeus
|
99
|
+
end
|
114
100
|
end
|
115
101
|
|
116
|
-
def
|
117
|
-
|
102
|
+
def populate_options(object, options)
|
103
|
+
options ||= {}
|
118
104
|
|
119
|
-
|
120
|
-
|
121
|
-
else
|
122
|
-
relative_path.to_s
|
105
|
+
ClassMethods::OPTIONS.each do |option|
|
106
|
+
options[option] ||= object.send(option)
|
123
107
|
end
|
108
|
+
|
109
|
+
options[:headers] = object.headers.merge(options[:headers] || {})
|
110
|
+
|
111
|
+
options
|
124
112
|
end
|
125
113
|
|
126
|
-
def strip_params(http_verb, data)
|
114
|
+
def strip_params(object, http_verb, data)
|
127
115
|
data ||= {}
|
128
116
|
|
129
117
|
if [:get, :head, :delete].include?(http_verb)
|
130
|
-
[query_string(data), nil]
|
118
|
+
[object.query_string(data), nil]
|
131
119
|
else
|
132
|
-
[query_string({}), data]
|
120
|
+
[object.query_string({}), data]
|
133
121
|
end
|
134
122
|
end
|
123
|
+
|
135
124
|
end
|
136
125
|
|
137
126
|
end
|
@@ -43,7 +43,7 @@ module SmoothOperator
|
|
43
43
|
raise 'UnknownPath'
|
44
44
|
end
|
45
45
|
|
46
|
-
|
46
|
+
make_a_persistence_call(:reload, relative_path, data, options) do |remote_call|
|
47
47
|
block_given? ? yield(remote_call) : remote_call.status
|
48
48
|
end
|
49
49
|
end
|
@@ -67,7 +67,7 @@ module SmoothOperator
|
|
67
67
|
def destroy(relative_path = nil, data = {}, options = {})
|
68
68
|
return false unless persisted?
|
69
69
|
|
70
|
-
|
70
|
+
make_a_persistence_call(:destroy, relative_path, data, options) do |remote_call|
|
71
71
|
@destroyed = true if remote_call.status
|
72
72
|
|
73
73
|
block_given? ? yield(remote_call) : remote_call.status
|
@@ -77,7 +77,7 @@ module SmoothOperator
|
|
77
77
|
protected ######################### PROTECTED ##################
|
78
78
|
|
79
79
|
def create(relative_path, data, options)
|
80
|
-
|
80
|
+
make_a_persistence_call(:create, relative_path, data, options) do |remote_call|
|
81
81
|
@new_record = false if remote_call.status
|
82
82
|
|
83
83
|
block_given? ? yield(remote_call) : remote_call
|
@@ -85,7 +85,7 @@ module SmoothOperator
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def update(relative_path, data, options)
|
88
|
-
|
88
|
+
make_a_persistence_call(:update, relative_path, data, options) do |remote_call|
|
89
89
|
block_given? ? yield(remote_call) : remote_call
|
90
90
|
end
|
91
91
|
end
|
@@ -102,7 +102,7 @@ module SmoothOperator
|
|
102
102
|
|
103
103
|
private ##################### PRIVATE ##################
|
104
104
|
|
105
|
-
def
|
105
|
+
def make_a_persistence_call(method, relative_path, data, options)
|
106
106
|
options ||= {}
|
107
107
|
|
108
108
|
http_verb = options[:http_verb] || self.class.methods_vs_http_verbs[method]
|
@@ -2,15 +2,23 @@ module SmoothOperator
|
|
2
2
|
module Validations
|
3
3
|
|
4
4
|
def valid?(context = nil)
|
5
|
-
Helpers.blank?(
|
5
|
+
Helpers.blank?(induced_errors)
|
6
6
|
end
|
7
7
|
|
8
8
|
def invalid?
|
9
9
|
!valid?
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
@
|
12
|
+
def induced_errors
|
13
|
+
@induced_errors ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def clear_induced_errors
|
17
|
+
@induced_errors = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def induce_errors(value)
|
21
|
+
@induced_errors = value
|
14
22
|
end
|
15
23
|
|
16
24
|
def self.included(base)
|
data/lib/smooth_operator.rb
CHANGED
@@ -5,22 +5,23 @@ require "smooth_operator/operator"
|
|
5
5
|
require "smooth_operator/persistence"
|
6
6
|
require "smooth_operator/translation"
|
7
7
|
require "smooth_operator/open_struct"
|
8
|
+
require "smooth_operator/http_methods"
|
9
|
+
require "smooth_operator/associations"
|
8
10
|
require "smooth_operator/finder_methods"
|
9
|
-
require "smooth_operator/relation/associations"
|
10
11
|
|
11
12
|
module SmoothOperator
|
12
13
|
class Base < OpenStruct
|
13
14
|
|
14
15
|
extend Schema
|
16
|
+
extend HttpMethods
|
17
|
+
extend Associations
|
15
18
|
extend FinderMethods
|
16
|
-
extend Operator::HttpMethods
|
17
|
-
extend Relation::Associations
|
18
19
|
extend Translation if defined? I18n
|
19
20
|
|
20
21
|
include Operator
|
22
|
+
include HttpMethods
|
21
23
|
include Persistence
|
22
24
|
include FinderMethods
|
23
|
-
include Operator::HttpMethods
|
24
25
|
|
25
26
|
self.strict_behaviour = true
|
26
27
|
|
@@ -37,6 +38,8 @@ module SmoothOperator
|
|
37
38
|
include ActiveModel::Validations::Callbacks
|
38
39
|
include ActiveModel::Conversion
|
39
40
|
|
41
|
+
validate :validate_induced_errors, :validate_nested_objects
|
42
|
+
|
40
43
|
def column_for_attribute(attribute_name)
|
41
44
|
type = self.class.attribute_type(attribute_name)
|
42
45
|
|
@@ -44,12 +47,11 @@ module SmoothOperator
|
|
44
47
|
end
|
45
48
|
|
46
49
|
def save(relative_path = nil, data = {}, options = {})
|
47
|
-
# clear_server_errors
|
48
50
|
return false unless before_save
|
49
51
|
|
50
|
-
|
52
|
+
clear_induced_errors
|
51
53
|
|
52
|
-
|
54
|
+
save_result = valid? ? super : false
|
53
55
|
|
54
56
|
after_save if valid? && save_result
|
55
57
|
|
@@ -62,6 +64,22 @@ module SmoothOperator
|
|
62
64
|
|
63
65
|
def after_save; end
|
64
66
|
|
67
|
+
protected ################# PROTECTED ###################
|
68
|
+
|
69
|
+
def validate_induced_errors
|
70
|
+
induced_errors.each do |key, value|
|
71
|
+
[*value].each do |_value|
|
72
|
+
self.errors.add(key, _value) unless self.errors.added?(key, _value)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
Helpers.blank?(induced_errors)
|
77
|
+
end
|
78
|
+
|
79
|
+
def validate_nested_objects
|
80
|
+
# nested_objects.map { |reflection, nested_object| nested_object.valid? }.all?
|
81
|
+
end
|
82
|
+
|
65
83
|
end
|
66
84
|
end
|
67
85
|
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.21.
|
4
|
+
version: 1.21.1
|
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-
|
11
|
+
date: 2014-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -82,20 +82,21 @@ files:
|
|
82
82
|
- console.rb
|
83
83
|
- lib/smooth_operator.rb
|
84
84
|
- lib/smooth_operator/array_with_meta_data.rb
|
85
|
+
- lib/smooth_operator/associations.rb
|
86
|
+
- lib/smooth_operator/associations/association_reflection.rb
|
87
|
+
- lib/smooth_operator/associations/has_many_relation.rb
|
88
|
+
- lib/smooth_operator/associations/reflection.rb
|
85
89
|
- lib/smooth_operator/attribute_assignment.rb
|
86
90
|
- lib/smooth_operator/delegation.rb
|
87
91
|
- lib/smooth_operator/finder_methods.rb
|
88
92
|
- lib/smooth_operator/helpers.rb
|
93
|
+
- lib/smooth_operator/http_methods.rb
|
89
94
|
- lib/smooth_operator/internal_data.rb
|
90
95
|
- lib/smooth_operator/open_struct.rb
|
91
96
|
- lib/smooth_operator/operator.rb
|
92
97
|
- lib/smooth_operator/operators/faraday.rb
|
93
98
|
- lib/smooth_operator/operators/typhoeus.rb
|
94
99
|
- lib/smooth_operator/persistence.rb
|
95
|
-
- lib/smooth_operator/relation/array_relation.rb
|
96
|
-
- lib/smooth_operator/relation/association_reflection.rb
|
97
|
-
- lib/smooth_operator/relation/associations.rb
|
98
|
-
- lib/smooth_operator/relation/reflection.rb
|
99
100
|
- lib/smooth_operator/remote_call/base.rb
|
100
101
|
- lib/smooth_operator/remote_call/errors/connection_failed.rb
|
101
102
|
- lib/smooth_operator/remote_call/errors/timeout.rb
|
@@ -1,102 +0,0 @@
|
|
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) { internal_data_get(association.to_s) }
|
74
|
-
|
75
|
-
define_method("build_#{association}") do |attributes = {}|
|
76
|
-
new_instance = reflection.klass.new(attributes)
|
77
|
-
|
78
|
-
internal_data_push(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
|
-
internal_data_push(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
|