smooth_operator 1.2.9 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Code Climate](https://codeclimate.com/repos/536a7b9f6956801228014b02/badges/13f79897976274a9de33/gpa.png)](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
|