smooth_operator 1.2.9 → 1.3.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.
- checksums.yaml +8 -8
- data/README.md +78 -22
- data/console.rb +2 -0
- data/lib/smooth_operator/array_with_meta_data.rb +20 -8
- data/lib/smooth_operator/{relation → associations}/association_reflection.rb +8 -8
- data/lib/smooth_operator/{relation/array_relation.rb → associations/has_many_relation.rb} +3 -13
- data/lib/smooth_operator/{relation → associations}/reflection.rb +2 -2
- data/lib/smooth_operator/associations.rb +110 -0
- data/lib/smooth_operator/attribute_assignment.rb +52 -60
- data/lib/smooth_operator/cookie_jar.rb +21 -0
- data/lib/smooth_operator/delegation.rb +13 -34
- data/lib/smooth_operator/finder_methods.rb +26 -17
- data/lib/smooth_operator/helpers.rb +14 -8
- data/lib/smooth_operator/http_methods.rb +17 -0
- data/lib/smooth_operator/internal_data.rb +45 -0
- data/lib/smooth_operator/open_struct.rb +11 -26
- data/lib/smooth_operator/operator.rb +65 -59
- data/lib/smooth_operator/operators/connection_wrapper.rb +15 -0
- data/lib/smooth_operator/operators/faraday.rb +6 -6
- data/lib/smooth_operator/operators/typhoeus.rb +22 -12
- data/lib/smooth_operator/options.rb +30 -0
- data/lib/smooth_operator/persistence.rb +64 -61
- data/lib/smooth_operator/remote_call/base.rb +7 -6
- data/lib/smooth_operator/resource_name.rb +46 -0
- data/lib/smooth_operator/schema.rb +21 -0
- data/lib/smooth_operator/serialization.rb +80 -36
- data/lib/smooth_operator/translation.rb +21 -12
- data/lib/smooth_operator/type_casting.rb +127 -0
- data/lib/smooth_operator/validations.rb +25 -3
- data/lib/smooth_operator/version.rb +1 -1
- data/lib/smooth_operator.rb +55 -5
- data/smooth_operator.gemspec +5 -5
- data/spec/smooth_operator/attribute_assignment_spec.rb +5 -14
- data/spec/smooth_operator/finder_methods_spec.rb +4 -9
- data/spec/smooth_operator/persistence_spec.rb +27 -19
- data/spec/smooth_operator/remote_call_spec.rb +104 -84
- data/spec/smooth_operator/{model_schema_spec.rb → resource_name_spec.rb} +1 -1
- data/spec/support/models/address.rb +8 -10
- data/spec/support/models/comment.rb +2 -0
- data/spec/support/models/post.rb +7 -7
- data/spec/support/models/user.rb +10 -13
- data/spec/support/models/user_with_address_and_posts.rb +9 -17
- data/spec/support/test_server.rb +7 -7
- metadata +25 -25
- data/lib/smooth_operator/attribute_methods.rb +0 -78
- data/lib/smooth_operator/attributes/base.rb +0 -107
- data/lib/smooth_operator/attributes/dirty.rb +0 -29
- data/lib/smooth_operator/attributes/normal.rb +0 -15
- data/lib/smooth_operator/blank_slate.rb +0 -7
- data/lib/smooth_operator/model_schema.rb +0 -81
- data/lib/smooth_operator/relation/associations.rb +0 -102
- 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
|
-
|
4
|
+
MmY1MDZiMTA3ZGQ4OThhNWZlNDA1YzgzNmRhZTg3YzE2OWQ0ZjM1ZQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
M2VlYzc3M2ZlZmZiM2I4ZTI3ZmE4NTUyYzIzMzEwNTMwNTAwZTY5OQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZmQ5NDFiMDAxYzhhNjQ0NjQzMjdlMTQ4MTM0OTg5MTRmOGZmOTlkM2YwZWJh
|
10
|
+
YzJhZDBmYjRlMzdjOGJiYmFmNTBkZjBmODk0Mzc4MmRmNTcxZDdhZTUwMTE5
|
11
|
+
MDQ4ZjU3YTM5YTYwMDI0Yjc4ODI2MjZmYTIwMGU4OGUwMGE5NGY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NTEzZjYwMzQyN2IxMWRmMDc4ZjFlZGZmY2M4ZjY1Zjk4OTM5NTY3OGRkZTEw
|
14
|
+
OGJhMjNiYjVjOWRmMzQwZDJiZjM4ZGUwMjQ1YTVkZjZiNDhmMTkwMjEyZjhm
|
15
|
+
YmIxMmRmMzExMjQ2ZmFjNTNhMThjZjBiZmIwMmRjOGJkNWI0NGM=
|
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
|
|
@@ -29,11 +37,14 @@ Or install it yourself as:
|
|
29
37
|
|
30
38
|
```ruby
|
31
39
|
class MyBlogResource < SmoothOperator::Base
|
32
|
-
self.endpoint = 'http://myblog.com/api/v0'
|
33
40
|
|
34
41
|
# HTTP BASIC AUTH
|
35
|
-
|
36
|
-
|
42
|
+
options endpoint_user: 'admin',
|
43
|
+
endpoint_pass: 'admin',
|
44
|
+
endpoint: 'http://myblog.com/api/v0'
|
45
|
+
|
46
|
+
# OR
|
47
|
+
# smooth_operator_options
|
37
48
|
end
|
38
49
|
|
39
50
|
class Post < MyBlogResource
|
@@ -109,10 +120,16 @@ post = Post.new(id: 2, body: 'editing my second page')
|
|
109
120
|
post.new_record? # false
|
110
121
|
post.persisted? # true
|
111
122
|
|
112
|
-
post.save("
|
123
|
+
post.save("save_and_add_to_list", { admin: true, post: { author: 'Agent Smith', list_id: 1 } }, { timeout: 1 })
|
113
124
|
# Will make a PUT to 'http://myblog.com/api/v0/posts/2/save_and_add_to_list'
|
114
125
|
# with { admin: true, post: { body: 'editing my second page', list_id: 1 } }
|
115
126
|
# and will only wait 1sec for the server to respond.
|
127
|
+
|
128
|
+
post.save('/#{post.id}/save_and_add_to_list')
|
129
|
+
# Will make a PUT to 'http://myblog.com/api/v0/posts/2/save_and_add_to_list'
|
130
|
+
|
131
|
+
post.save('/save_and_add_to_list')
|
132
|
+
# Will make a PUT to 'http://myblog.com/api/v0/posts/save_and_add_to_list'
|
116
133
|
```
|
117
134
|
|
118
135
|
---
|
@@ -120,7 +137,9 @@ post.save("#{post.id}/save_and_add_to_list", { admin: true, post: { author: 'Age
|
|
120
137
|
### 2.4) Saving using HTTP Patch verb
|
121
138
|
```ruby
|
122
139
|
class Page < MyBlogResource
|
123
|
-
|
140
|
+
options update_http_verb: 'patch'
|
141
|
+
# OR
|
142
|
+
#smooth_operator_options update_http_verb: 'patch'
|
124
143
|
end
|
125
144
|
|
126
145
|
page = Page.find(2)
|
@@ -139,9 +158,9 @@ page.save # will make a http PATCH call to 'http://myblog.com/api/v0/pages/2'
|
|
139
158
|
remote_call = Page.find(:all) # Will make a GET call to 'http://myblog.com/api/v0/pages'
|
140
159
|
# and will return a SmoothOperator::RemoteCall instance
|
141
160
|
|
142
|
-
pages = remote_call.
|
161
|
+
pages = remote_call.data
|
143
162
|
|
144
|
-
# If the server response is positive (http code between 200 and 299):
|
163
|
+
# If the server response is positive (http code between 200 and 299, or 304):
|
145
164
|
remote_call.ok? # true
|
146
165
|
remote_call.not_processed? # false
|
147
166
|
remote_call.error? # false
|
@@ -179,7 +198,9 @@ pages = remote_call.objects # 'pages = remote_call.data' also works
|
|
179
198
|
remote_call = Page.find(2) # Will make a GET call to 'http://myblog.com/api/v0/pages/2'
|
180
199
|
# and will return a SmoothOperator::RemoteCall instance
|
181
200
|
|
182
|
-
|
201
|
+
service_down = remote_call.error?
|
202
|
+
|
203
|
+
page = remote_call.data
|
183
204
|
```
|
184
205
|
|
185
206
|
---
|
@@ -190,24 +211,59 @@ remote_call = Page.find('my_pages', { q: body_contains: 'link' }, { endpoint_use
|
|
190
211
|
# will make a GET call to 'http://myblog.com/api/v0/pages/my_pages?q={body_contains="link"}'
|
191
212
|
# and will change the HTTP BASIC AUTH credentials to user: 'admin' and pass: 'new_password' for this connection only.
|
192
213
|
|
214
|
+
@service_down = remote_call.error?
|
215
|
+
|
193
216
|
# If the server json response is an Array [{ id: 1 }, { id: 2 }]
|
194
|
-
pages = remote.data # will return an array with 2 Page's instances
|
195
|
-
pages[0].id # 1
|
196
|
-
pages[1].id # 2
|
217
|
+
@pages = remote.data # will return an array with 2 Page's instances
|
218
|
+
@pages[0].id # 1
|
219
|
+
@pages[1].id # 2
|
197
220
|
|
198
221
|
# If the server json response is a Hash { id: 3 }
|
199
|
-
page = remote.data # will return a single Page instance
|
200
|
-
page.id # 3
|
222
|
+
@page = remote.data # will return a single Page instance
|
223
|
+
@page.id # 3
|
224
|
+
|
225
|
+
# If the server json response is Hash with a key called 'pages' { current_page: 1, total_pages: 3, limit_value: 10, pages: [{ id: 4 }, { id: 5 }] }
|
226
|
+
@pages = remote.data # will return a single ArrayWithMetaData instance, that will allow you to access to both the Page's instances array and the metadata.
|
201
227
|
|
202
|
-
#
|
203
|
-
pages
|
204
|
-
pages.
|
205
|
-
pages.
|
228
|
+
# @pages is now a valid object to work with kaminari
|
229
|
+
@pages.total_pages # 3
|
230
|
+
@pages.current_page # 1
|
231
|
+
@pages.limit_value # 10
|
206
232
|
|
207
|
-
pages[0].id # 4
|
208
|
-
pages[1].id # 5
|
233
|
+
@pages[0].id # 4
|
234
|
+
@pages[1].id # 5
|
209
235
|
```
|
210
236
|
|
237
|
+
### 2.8) Keeping your session alive - custom HTTP Headers
|
238
|
+
|
239
|
+
Controllers
|
240
|
+
ApplicationController
|
241
|
+
```ruby
|
242
|
+
```
|
243
|
+
|
244
|
+
Models
|
245
|
+
SmoothResource
|
246
|
+
```ruby
|
247
|
+
class SmoothResource < SmoothOperator::Rails
|
248
|
+
|
249
|
+
options headers: :custom_headers
|
250
|
+
|
251
|
+
def self.custom_headers
|
252
|
+
{
|
253
|
+
cookie: current_user.blog_cookie,
|
254
|
+
"X_CSRF_TOKEN" => current_user.blog_auth_token
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
protected ############## PROTECTED #################
|
259
|
+
|
260
|
+
def self.current_user
|
261
|
+
User.current_user
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
```
|
266
|
+
|
211
267
|
---
|
212
268
|
|
213
269
|
## 3) Methods
|
data/console.rb
CHANGED
@@ -31,4 +31,6 @@ post = Post.new(comments: [{ id: 1, name: '1' }, { id: 2, name: '2' }], address:
|
|
31
31
|
|
32
32
|
comments_attributes = { "0" => { id: 1, name: '3' }, "1" => { name: '4' } }
|
33
33
|
|
34
|
+
comments_with_errors = { "0" => { id: 1, name: '3', errors: { body: ["can't be blank"] } }, "1" => { name: '4', errors: { body: ["can't be blank"] } } }
|
35
|
+
|
34
36
|
binding.pry
|
@@ -1,22 +1,34 @@
|
|
1
1
|
module SmoothOperator
|
2
|
-
class ArrayWithMetaData <
|
2
|
+
class ArrayWithMetaData < SimpleDelegator
|
3
3
|
|
4
4
|
attr_reader :meta_data, :internal_array
|
5
5
|
|
6
6
|
def initialize(attributes, object_class)
|
7
|
-
|
7
|
+
resources_name = object_class.resources_name
|
8
8
|
|
9
|
-
@internal_array = [*
|
10
|
-
|
9
|
+
@internal_array = [*attributes[resources_name]].map do |array_entry|
|
10
|
+
object_class.new(array_entry)
|
11
|
+
end
|
11
12
|
|
12
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
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,15 +1,15 @@
|
|
1
|
-
require "smooth_operator/
|
1
|
+
require "smooth_operator/associations/reflection"
|
2
2
|
|
3
3
|
module SmoothOperator
|
4
|
-
module
|
4
|
+
module Associations
|
5
5
|
class AssociationReflection < Reflection
|
6
6
|
|
7
7
|
attr_reader :related_reflection, :macro
|
8
8
|
|
9
9
|
def initialize(association, related_reflection, options)
|
10
10
|
super(association, options)
|
11
|
-
|
12
|
-
@related_reflection = related_reflection
|
11
|
+
|
12
|
+
@related_reflection, @macro = related_reflection, options[:macro]
|
13
13
|
end
|
14
14
|
|
15
15
|
def primary_key
|
@@ -60,12 +60,12 @@ module SmoothOperator
|
|
60
60
|
has_many?
|
61
61
|
end
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
def macro_default(association)
|
66
|
-
Helpers.plural?(association) ? :has_many : :belongs_to
|
63
|
+
def rails_serialization?
|
64
|
+
options[:rails_serialization] == true
|
67
65
|
end
|
68
66
|
|
67
|
+
private ################################# private
|
68
|
+
|
69
69
|
def foreign_key_default
|
70
70
|
if has_many? || has_one?
|
71
71
|
"#{related_reflection.single_name}_id"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module SmoothOperator
|
2
|
-
module
|
3
|
-
class
|
2
|
+
module Associations
|
3
|
+
class HasManyRelation < SimpleDelegator
|
4
4
|
|
5
5
|
attr_reader :object, :association
|
6
6
|
|
@@ -30,18 +30,8 @@ module SmoothOperator
|
|
30
30
|
|
31
31
|
protected ############### PROTECTED ###############
|
32
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
33
|
def get_array
|
44
|
-
data = object.
|
34
|
+
data = object.internal_data_get(association.to_s)
|
45
35
|
|
46
36
|
data.nil? ? [] : [*data]
|
47
37
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module SmoothOperator
|
2
|
-
module
|
2
|
+
module Associations
|
3
3
|
class Reflection
|
4
4
|
|
5
5
|
attr_reader :name, :klass, :options
|
@@ -14,7 +14,7 @@ module SmoothOperator
|
|
14
14
|
if options.include?(:class_name) && options[:class_name].nil?
|
15
15
|
@klass = nil
|
16
16
|
elsif @klass.is_a?(String)
|
17
|
-
@klass = @klass.constantize
|
17
|
+
@klass = @klass.constantize rescue OpenStruct
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -0,0 +1,110 @@
|
|
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(association, options = {})
|
8
|
+
accepts_nested_objects(association, :has_many, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_one(association, options = {})
|
12
|
+
accepts_nested_objects(association, :has_one, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def belongs_to(association, options = {})
|
16
|
+
accepts_nested_objects(association, :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
|
+
def rails_serialization
|
32
|
+
get_option :rails_serialization, false
|
33
|
+
end
|
34
|
+
|
35
|
+
protected ###################### PROTECTED ###################
|
36
|
+
|
37
|
+
# TODO: THIS MUST GO TO A HELPER_METHODS MODULE
|
38
|
+
|
39
|
+
def accepts_nested_objects(association, macro, options = {})
|
40
|
+
options = parse_options(options, { macro: macro })
|
41
|
+
|
42
|
+
reflection = AssociationReflection.new(association, Reflection.new(name, {}), options)
|
43
|
+
|
44
|
+
schema(association => reflection.klass)
|
45
|
+
|
46
|
+
reflections.merge!(association => reflection)
|
47
|
+
|
48
|
+
if reflection.has_many?
|
49
|
+
define_has_many_association_method(reflection, association)
|
50
|
+
else
|
51
|
+
define_single_association_method(reflection, association)
|
52
|
+
end
|
53
|
+
|
54
|
+
self.send(:attr_reader, "#{association}_attributes".to_sym)
|
55
|
+
|
56
|
+
define_attributes_setter_methods(reflection, association)
|
57
|
+
end
|
58
|
+
|
59
|
+
private ####################### PRIVATE ######################
|
60
|
+
|
61
|
+
def define_has_many_association_method(reflection, association)
|
62
|
+
define_method(association) do
|
63
|
+
has_many_relation = instance_variable_get("@#{association}")
|
64
|
+
|
65
|
+
if has_many_relation.nil?
|
66
|
+
has_many_relation = HasManyRelation.new(self, association)
|
67
|
+
|
68
|
+
instance_variable_set("@#{association}", has_many_relation)
|
69
|
+
end
|
70
|
+
|
71
|
+
has_many_relation.send(:refresh)
|
72
|
+
|
73
|
+
has_many_relation
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def define_single_association_method(reflection, association)
|
78
|
+
define_method(association) { internal_data_get(association.to_s) }
|
79
|
+
|
80
|
+
define_method("build_#{association}") do |attributes = {}|
|
81
|
+
new_instance = reflection.klass.new(attributes)
|
82
|
+
|
83
|
+
internal_data_push(association, new_instance)
|
84
|
+
|
85
|
+
new_instance
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def define_attributes_setter_methods(reflection, association)
|
90
|
+
define_method("#{association}_attributes=") do |attributes|
|
91
|
+
instance_variable_set("@#{association}_attributes", attributes)
|
92
|
+
|
93
|
+
attributes = attributes.values if reflection.has_many?
|
94
|
+
|
95
|
+
internal_data_push(association.to_s, attributes)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def parse_options(options, default_options)
|
100
|
+
options = options.is_a?(Hash) ? options.merge(default_options) : default_options
|
101
|
+
|
102
|
+
if options[:rails_serialization].nil?
|
103
|
+
options[:rails_serialization] = rails_serialization
|
104
|
+
end
|
105
|
+
|
106
|
+
Helpers.symbolyze_keys(options)
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -1,87 +1,51 @@
|
|
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
|
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
|
|
16
|
+
def assign_attributes(attributes = {}, options = {})
|
17
|
+
attributes = _extract_attributes(attributes)
|
58
18
|
|
59
|
-
|
60
|
-
return nil unless _attributes.is_a?(Hash)
|
19
|
+
return nil unless attributes.is_a?(Hash)
|
61
20
|
|
62
|
-
attributes
|
21
|
+
induce_errors(attributes.delete(self.class.errors_key))
|
63
22
|
|
64
|
-
if
|
65
|
-
attributes = _attributes.delete(self.class.resource_name)
|
66
|
-
@_meta_data = _attributes
|
67
|
-
end
|
23
|
+
@_options.merge!(options) if options.is_a? Hash
|
68
24
|
|
69
|
-
|
25
|
+
attributes.each do |name, value|
|
26
|
+
next unless allowed_attribute(name)
|
70
27
|
|
71
|
-
|
28
|
+
send("#{name}=", value)
|
29
|
+
end
|
72
30
|
end
|
73
31
|
|
74
|
-
|
75
|
-
_options[:parent_object]
|
76
|
-
end
|
32
|
+
protected ################# PROTECTED METHODS DOWN BELOW ###################
|
77
33
|
|
78
|
-
def
|
79
|
-
|
80
|
-
end
|
34
|
+
def _extract_attributes(attributes)
|
35
|
+
return nil unless attributes.is_a?(Hash)
|
81
36
|
|
82
|
-
|
37
|
+
_attributes = Helpers.stringify_keys(attributes)
|
38
|
+
|
39
|
+
if _attributes.include?(self.class.resource_name)
|
40
|
+
attributes = _attributes.delete(self.class.resource_name)
|
41
|
+
@_meta_data = _attributes
|
42
|
+
else
|
43
|
+
attributes = _attributes
|
44
|
+
@_meta_data = {}
|
45
|
+
end
|
83
46
|
|
84
|
-
|
47
|
+
attributes
|
48
|
+
end
|
85
49
|
|
86
50
|
def before_initialize(attributes, options); end
|
87
51
|
|
@@ -97,5 +61,33 @@ module SmoothOperator
|
|
97
61
|
end
|
98
62
|
end
|
99
63
|
|
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
|
+
|
100
92
|
end
|
101
93
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SmoothOperator
|
2
|
+
class CookieJar < Hash
|
3
|
+
|
4
|
+
def to_s
|
5
|
+
self.map { |key, value| "#{key}=#{value}"}.join("; ")
|
6
|
+
end
|
7
|
+
|
8
|
+
def parse(*cookie_strings)
|
9
|
+
cookie_strings.each do |cookie_string|
|
10
|
+
next unless cookie_string.is_a?(String)
|
11
|
+
|
12
|
+
key, value = cookie_string.split('; ').first.split('=', 2)
|
13
|
+
|
14
|
+
self[key] = value
|
15
|
+
end
|
16
|
+
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
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,28 @@ module SmoothOperator
|
|
7
6
|
end
|
8
7
|
|
9
8
|
def method_missing(method, *args, &block)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
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
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
def parse_method(method)
|
32
|
-
method = method.to_s
|
33
|
-
|
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]
|
42
|
-
end
|
20
|
+
def self.included(base)
|
21
|
+
base.extend(ClassMethods)
|
43
22
|
end
|
44
23
|
|
24
|
+
module ClassMethods
|
45
25
|
|
46
|
-
|
26
|
+
def strict_behaviour
|
27
|
+
get_option :strict_behaviour, false
|
28
|
+
end
|
47
29
|
|
48
|
-
def method?(method, regex)
|
49
|
-
!! ((method.to_s) =~ regex)
|
50
30
|
end
|
51
31
|
|
52
32
|
end
|
53
|
-
|
54
33
|
end
|