her 0.9.0 → 0.10.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 +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
|