her 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/README.md +3 -3
- data/lib/her/api.rb +16 -9
- data/lib/her/model/associations/association.rb +1 -5
- data/lib/her/model/associations/has_many_association.rb +2 -2
- data/lib/her/model/attributes.rb +80 -60
- data/lib/her/model/http.rb +3 -3
- data/lib/her/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b11481769c945a97654d3d95432044346945d41
|
4
|
+
data.tar.gz: 5d9d9d9f02a10ccd3424314ed13b870651995637
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ba766a53928bf5a48027e5caaee70081768a8c924c1a306afca64dd6e72ded1dc5999da454f3cdd66ca47c96ad41e639ae4bd8bed8df8bd33a269d7d28584cc
|
7
|
+
data.tar.gz: cf562af326ca2bc7d77be3f27de84cfd8d17620775833947815ff985751c11fa89153c4bb4de78027f7bbd36d55209b4cd2f36095e003c250b0ccd9ae5b14d1b
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -234,7 +234,7 @@ end
|
|
234
234
|
@tweets = Tweet.get("/statuses/home_timeline.json")
|
235
235
|
```
|
236
236
|
|
237
|
-
See the *Authentication
|
237
|
+
See the [*Authentication middleware section*](#authentication) for an example of how to pass different credentials based on the current user.
|
238
238
|
|
239
239
|
### Parsing JSON data
|
240
240
|
|
@@ -248,7 +248,7 @@ By default, Her handles JSON data. It expects the resource/collection data to be
|
|
248
248
|
[{ "id" : 1, "name" : "Tobias Fünke" }]
|
249
249
|
```
|
250
250
|
|
251
|
-
However, if you want Her to be able to parse the data from a single root element (usually based on the model name), you’ll have to use the `parse_root_in_json` method (See the **JSON attributes-wrapping** section).
|
251
|
+
However, if you want Her to be able to parse the data from a single root element (usually based on the model name), you’ll have to use the `parse_root_in_json` method (See the [**JSON attributes-wrapping**](#json-attributes-wrapping) section).
|
252
252
|
|
253
253
|
Also, you can define your own parsing method using a response middleware. The middleware should set `env[:body]` to a hash with three symbol keys: `:data`, `:errors` and `:metadata`. The following code uses a custom middleware to parse the JSON data:
|
254
254
|
|
@@ -452,7 +452,7 @@ class Organization
|
|
452
452
|
end
|
453
453
|
```
|
454
454
|
|
455
|
-
Her expects all `User` resources to have an `:organization_id` (or `:_organization_id`) attribute. Otherwise, calling mostly all methods, like `User.all`, will
|
455
|
+
Her expects all `User` resources to have an `:organization_id` (or `:_organization_id`) attribute. Otherwise, calling mostly all methods, like `User.all`, will throw an exception like this one:
|
456
456
|
|
457
457
|
```ruby
|
458
458
|
Her::Errors::PathError: Missing :_organization_id parameter to build the request path. Path is `organizations/:organization_id/users`. Parameters are `{ … }`.
|
data/lib/her/api.rb
CHANGED
@@ -89,19 +89,26 @@ module Her
|
|
89
89
|
path = opts.delete(:_path)
|
90
90
|
headers = opts.delete(:_headers)
|
91
91
|
opts.delete_if { |key, value| key.to_s =~ /^_/ } # Remove all internal parameters
|
92
|
-
|
92
|
+
if method == :options
|
93
|
+
# Faraday doesn't support the OPTIONS verb because of a name collision with an internal options method
|
94
|
+
# so we need to call run_request directly.
|
93
95
|
request.headers.merge!(headers) if headers
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
96
|
+
response = @connection.run_request method, path, opts, headers
|
97
|
+
else
|
98
|
+
response = @connection.send method do |request|
|
99
|
+
request.headers.merge!(headers) if headers
|
100
|
+
if method == :get
|
101
|
+
# For GET requests, treat additional parameters as querystring data
|
102
|
+
request.url path, opts
|
103
|
+
else
|
104
|
+
# For POST, PUT and DELETE requests, treat additional parameters as request body
|
105
|
+
request.url path
|
106
|
+
request.body = opts
|
107
|
+
end
|
101
108
|
end
|
102
109
|
end
|
103
|
-
|
104
110
|
{ :parsed_data => response.env[:body], :response => response }
|
111
|
+
|
105
112
|
end
|
106
113
|
|
107
114
|
private
|
@@ -26,11 +26,7 @@ module Her
|
|
26
26
|
return {} unless data[data_key]
|
27
27
|
|
28
28
|
klass = klass.her_nearby_class(association[:class_name])
|
29
|
-
|
30
|
-
{ association[:name] => data[data_key] }
|
31
|
-
else
|
32
|
-
{ association[:name] => klass.new(klass.parse(data[data_key])) }
|
33
|
-
end
|
29
|
+
{ association[:name] => klass.instantiate_record(klass, data: data[data_key]) }
|
34
30
|
end
|
35
31
|
|
36
32
|
# @private
|
@@ -31,7 +31,7 @@ module Her
|
|
31
31
|
return {} unless data[data_key]
|
32
32
|
|
33
33
|
klass = klass.her_nearby_class(association[:class_name])
|
34
|
-
{ association[:name] =>
|
34
|
+
{ association[:name] => klass.instantiate_collection(klass, :data => data[data_key]) }
|
35
35
|
end
|
36
36
|
|
37
37
|
# Initialize a new object with a foreign key to the parent
|
@@ -92,7 +92,7 @@ module Her
|
|
92
92
|
# @private
|
93
93
|
def assign_nested_attributes(attributes)
|
94
94
|
data = attributes.is_a?(Hash) ? attributes.values : attributes
|
95
|
-
@parent.attributes[@name] =
|
95
|
+
@parent.attributes[@name] = @klass.instantiate_collection(@klass, :data => data)
|
96
96
|
end
|
97
97
|
end
|
98
98
|
end
|
data/lib/her/model/attributes.rb
CHANGED
@@ -16,7 +16,13 @@ module Her
|
|
16
16
|
# include Her::Model
|
17
17
|
# end
|
18
18
|
#
|
19
|
-
#
|
19
|
+
# User.new(name: "Tobias")
|
20
|
+
# # => #<User name="Tobias">
|
21
|
+
#
|
22
|
+
# User.new do |u|
|
23
|
+
# u.name = "Tobias"
|
24
|
+
# end
|
25
|
+
# # => #<User name="Tobias">
|
20
26
|
def initialize(attributes={})
|
21
27
|
attributes ||= {}
|
22
28
|
@metadata = attributes.delete(:_metadata) || {}
|
@@ -25,50 +31,10 @@ module Her
|
|
25
31
|
|
26
32
|
attributes = self.class.default_scope.apply_to(attributes)
|
27
33
|
assign_attributes(attributes)
|
28
|
-
|
29
34
|
yield self if block_given?
|
30
35
|
run_callbacks :initialize
|
31
36
|
end
|
32
37
|
|
33
|
-
# Initialize a collection of resources
|
34
|
-
#
|
35
|
-
# @private
|
36
|
-
def self.initialize_collection(klass, parsed_data={})
|
37
|
-
collection_data = klass.extract_array(parsed_data).map do |item_data|
|
38
|
-
if item_data.kind_of?(klass)
|
39
|
-
resource = item_data
|
40
|
-
else
|
41
|
-
resource = klass.new(klass.parse(item_data))
|
42
|
-
resource.run_callbacks :find
|
43
|
-
end
|
44
|
-
resource
|
45
|
-
end
|
46
|
-
Her::Collection.new(collection_data, parsed_data[:metadata], parsed_data[:errors])
|
47
|
-
end
|
48
|
-
|
49
|
-
# Use setter methods of model for each key / value pair in params
|
50
|
-
# Return key / value pairs for which no setter method was defined on the model
|
51
|
-
#
|
52
|
-
# @private
|
53
|
-
def self.use_setter_methods(model, params)
|
54
|
-
params ||= {}
|
55
|
-
|
56
|
-
reserved_keys = [:id, model.class.primary_key] + model.class.association_keys
|
57
|
-
model.class.attributes *params.keys.reject { |k| reserved_keys.include?(k) || reserved_keys.map(&:to_s).include?(k) }
|
58
|
-
|
59
|
-
setter_method_names = model.class.setter_method_names
|
60
|
-
params.inject({}) do |memo, (key, value)|
|
61
|
-
setter_method = key.to_s + '='
|
62
|
-
if setter_method_names.include?(setter_method)
|
63
|
-
model.send(setter_method, value)
|
64
|
-
else
|
65
|
-
key = key.to_sym if key.is_a?(String)
|
66
|
-
memo[key] = value
|
67
|
-
end
|
68
|
-
memo
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
38
|
# Handles missing methods
|
73
39
|
#
|
74
40
|
# @private
|
@@ -89,7 +55,7 @@ module Her
|
|
89
55
|
|
90
56
|
# @private
|
91
57
|
def respond_to_missing?(method, include_private = false)
|
92
|
-
method.to_s
|
58
|
+
method.to_s =~ /[?=]$/ || @attributes.include?(method) || super
|
93
59
|
end
|
94
60
|
|
95
61
|
# Assign new attributes to a resource
|
@@ -105,7 +71,7 @@ module Her
|
|
105
71
|
def assign_attributes(new_attributes)
|
106
72
|
@attributes ||= attributes
|
107
73
|
# Use setter methods first
|
108
|
-
unset_attributes =
|
74
|
+
unset_attributes = self.class.use_setter_methods(self, new_attributes)
|
109
75
|
|
110
76
|
# Then translate attributes of associations into association instances
|
111
77
|
parsed_attributes = self.class.parse_associations(unset_attributes)
|
@@ -139,7 +105,8 @@ module Her
|
|
139
105
|
@attributes[self.class.primary_key]
|
140
106
|
end
|
141
107
|
|
142
|
-
# Return `true` if the other object is also a Her::Model and has matching
|
108
|
+
# Return `true` if the other object is also a Her::Model and has matching
|
109
|
+
# data
|
143
110
|
#
|
144
111
|
# @private
|
145
112
|
def ==(other)
|
@@ -177,23 +144,69 @@ module Her
|
|
177
144
|
end
|
178
145
|
|
179
146
|
module ClassMethods
|
147
|
+
|
148
|
+
# Initialize a single resource
|
149
|
+
#
|
150
|
+
# @private
|
151
|
+
def instantiate_record(klass, parsed_data)
|
152
|
+
if record = parsed_data[:data] and record.kind_of?(klass)
|
153
|
+
record
|
154
|
+
else
|
155
|
+
attributes = klass.parse(record).merge(_metadata: parsed_data[:metadata],
|
156
|
+
_errors: parsed_data[:errors])
|
157
|
+
klass.new(attributes).tap do |record|
|
158
|
+
record.run_callbacks :find
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Initialize a collection of resources
|
164
|
+
#
|
165
|
+
# @private
|
166
|
+
def instantiate_collection(klass, parsed_data = {})
|
167
|
+
items = klass.extract_array(parsed_data).map do |item|
|
168
|
+
instantiate_record(klass, data: item)
|
169
|
+
end
|
170
|
+
Her::Collection.new(items, parsed_data[:metadata], parsed_data[:errors])
|
171
|
+
end
|
172
|
+
|
180
173
|
# Initialize a collection of resources with raw data from an HTTP request
|
181
174
|
#
|
182
175
|
# @param [Array] parsed_data
|
183
176
|
# @private
|
184
177
|
def new_collection(parsed_data)
|
185
|
-
|
178
|
+
instantiate_collection(self, parsed_data)
|
186
179
|
end
|
187
180
|
|
188
181
|
# Initialize a new object with the "raw" parsed_data from the parsing middleware
|
189
182
|
#
|
190
183
|
# @private
|
191
184
|
def new_from_parsed_data(parsed_data)
|
192
|
-
|
193
|
-
|
185
|
+
instantiate_record(self, parsed_data)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Use setter methods of model for each key / value pair in params
|
189
|
+
# Return key / value pairs for which no setter method was defined on the
|
190
|
+
# model
|
191
|
+
#
|
192
|
+
# @private
|
193
|
+
def use_setter_methods(model, params = {})
|
194
|
+
reserved = [:id, model.class.primary_key, *model.class.association_keys]
|
195
|
+
model.class.attributes *params.keys.reject { |k| reserved.include?(k) }
|
196
|
+
|
197
|
+
setter_method_names = model.class.setter_method_names
|
198
|
+
params.each_with_object({}) do |(key, value), memo|
|
199
|
+
setter_method = key.to_s + '='
|
200
|
+
if setter_method_names.include?(setter_method)
|
201
|
+
model.send(setter_method, value)
|
202
|
+
else
|
203
|
+
memo[key.to_sym] = value
|
204
|
+
end
|
205
|
+
end
|
194
206
|
end
|
195
207
|
|
196
|
-
# Define attribute method matchers to automatically define them using
|
208
|
+
# Define attribute method matchers to automatically define them using
|
209
|
+
# ActiveModel's define_attribute_methods.
|
197
210
|
#
|
198
211
|
# @private
|
199
212
|
def define_attribute_method_matchers
|
@@ -201,18 +214,22 @@ module Her
|
|
201
214
|
attribute_method_suffix '?'
|
202
215
|
end
|
203
216
|
|
204
|
-
# Create a mutex for dynamically generated attribute methods or use one
|
217
|
+
# Create a mutex for dynamically generated attribute methods or use one
|
218
|
+
# defined by ActiveModel.
|
205
219
|
#
|
206
220
|
# @private
|
207
221
|
def attribute_methods_mutex
|
208
|
-
@attribute_methods_mutex ||=
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
222
|
+
@attribute_methods_mutex ||= begin
|
223
|
+
if generated_attribute_methods.respond_to? :mu_synchronize
|
224
|
+
generated_attribute_methods
|
225
|
+
else
|
226
|
+
Mutex.new
|
227
|
+
end
|
228
|
+
end
|
213
229
|
end
|
214
230
|
|
215
|
-
# Define the attributes that will be used to track dirty attributes and
|
231
|
+
# Define the attributes that will be used to track dirty attributes and
|
232
|
+
# validations
|
216
233
|
#
|
217
234
|
# @param [Array] attributes
|
218
235
|
# @example
|
@@ -226,7 +243,8 @@ module Her
|
|
226
243
|
end
|
227
244
|
end
|
228
245
|
|
229
|
-
# Define the accessor in which the API response errors (obtained from
|
246
|
+
# Define the accessor in which the API response errors (obtained from
|
247
|
+
# the parsing middleware) will be stored
|
230
248
|
#
|
231
249
|
# @param [Symbol] store_response_errors
|
232
250
|
#
|
@@ -239,7 +257,8 @@ module Her
|
|
239
257
|
store_her_data(:response_errors, value)
|
240
258
|
end
|
241
259
|
|
242
|
-
# Define the accessor in which the API response metadata (obtained from
|
260
|
+
# Define the accessor in which the API response metadata (obtained from
|
261
|
+
# the parsing middleware) will be stored
|
243
262
|
#
|
244
263
|
# @param [Symbol] store_metadata
|
245
264
|
#
|
@@ -254,9 +273,10 @@ module Her
|
|
254
273
|
|
255
274
|
# @private
|
256
275
|
def setter_method_names
|
257
|
-
@_her_setter_method_names ||=
|
258
|
-
|
259
|
-
|
276
|
+
@_her_setter_method_names ||= begin
|
277
|
+
instance_methods.each_with_object(Set.new) do |method, memo|
|
278
|
+
memo << method.to_s if method.to_s.end_with?('=')
|
279
|
+
end
|
260
280
|
end
|
261
281
|
end
|
262
282
|
|
data/lib/her/model/http.rb
CHANGED
@@ -3,7 +3,7 @@ module Her
|
|
3
3
|
# This module interacts with Her::API to fetch HTTP data
|
4
4
|
module HTTP
|
5
5
|
extend ActiveSupport::Concern
|
6
|
-
METHODS = [:get, :post, :put, :patch, :delete]
|
6
|
+
METHODS = [:get, :post, :put, :patch, :delete, :options]
|
7
7
|
|
8
8
|
# For each HTTP method, define these class methods:
|
9
9
|
#
|
@@ -71,7 +71,7 @@ module Her
|
|
71
71
|
if parsed_data[:data].is_a?(Array) || active_model_serializers_format? || json_api_format?
|
72
72
|
new_collection(parsed_data)
|
73
73
|
else
|
74
|
-
|
74
|
+
new_from_parsed_data(parsed_data)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
@@ -91,7 +91,7 @@ module Her
|
|
91
91
|
def #{method}_resource(path, params={})
|
92
92
|
path = build_request_path_from_string_or_symbol(path, params)
|
93
93
|
send(:"#{method}_raw", path, params) do |parsed_data, response|
|
94
|
-
|
94
|
+
new_from_parsed_data(parsed_data)
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
data/lib/her/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: her
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rémi Prévost
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|