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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 458601a1de64f520013856ef805b17e8293bfb9d
4
- data.tar.gz: 1e965c550664039d6b6f649a9b10a7821d90f717
3
+ metadata.gz: 9b11481769c945a97654d3d95432044346945d41
4
+ data.tar.gz: 5d9d9d9f02a10ccd3424314ed13b870651995637
5
5
  SHA512:
6
- metadata.gz: e7d147d58050f847942df92440c9e71013c1fc54fbb56d0a6e5dd9d6167a1ffc4e30c329524f1bac07a378d06991198cb1bf44215067ab911f2d0ff1ee5b95f7
7
- data.tar.gz: 1c3ab969c7fbe8f90f730945cd9392f9e2d3df6b3df577291f86f97aa7405758f1086a7708e2586760d674a362703c465bd66516f828d1c1a389fb7c304f4a16
6
+ metadata.gz: 0ba766a53928bf5a48027e5caaee70081768a8c924c1a306afca64dd6e72ded1dc5999da454f3cdd66ca47c96ad41e639ae4bd8bed8df8bd33a269d7d28584cc
7
+ data.tar.gz: cf562af326ca2bc7d77be3f27de84cfd8d17620775833947815ff985751c11fa89153c4bb4de78027f7bbd36d55209b4cd2f36095e003c250b0ccd9ae5b14d1b
data/.travis.yml CHANGED
@@ -16,4 +16,7 @@ gemfile:
16
16
  - gemfiles/Gemfile.activemodel-4.0
17
17
  - gemfiles/Gemfile.activemodel-3.2.x
18
18
 
19
+ before_install:
20
+ - gem install bundler
21
+
19
22
  script: "echo 'COME ON!' && bundle exec rake spec"
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* middleware section for an example of how to pass different credentials based on the current user.
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 thrown an exception like this one:
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
- response = @connection.send method do |request|
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
- if method == :get
95
- # For GET requests, treat additional parameters as querystring data
96
- request.url path, opts
97
- else
98
- # For POST, PUT and DELETE requests, treat additional parameters as request body
99
- request.url path
100
- request.body = opts
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
- if data[data_key].kind_of?(klass)
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] => Her::Model::Attributes.initialize_collection(klass, :data => data[data_key]) }
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] = Her::Model::Attributes.initialize_collection(@klass, :data => data)
95
+ @parent.attributes[@name] = @klass.instantiate_collection(@klass, :data => data)
96
96
  end
97
97
  end
98
98
  end
@@ -16,7 +16,13 @@ module Her
16
16
  # include Her::Model
17
17
  # end
18
18
  #
19
- # User.new(name: "Tobias") # => #<User name="Tobias">
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.end_with?('=') || method.to_s.end_with?('?') || @attributes.include?(method) || super
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 = Her::Model::Attributes.use_setter_methods(self, new_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 data
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
- Her::Model::Attributes.initialize_collection(self, parsed_data)
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
- parsed_data = parsed_data.with_indifferent_access
193
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
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 ActiveModel's define_attribute_methods.
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 defined by ActiveModel.
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 ||= if generated_attribute_methods.respond_to? :mu_synchronize
209
- generated_attribute_methods
210
- else
211
- Mutex.new
212
- end
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 validations
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 the parsing middleware) will be stored
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 the parsing middleware) will be stored
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 ||= instance_methods.inject(Set.new) do |memo, method_name|
258
- memo << method_name.to_s if method_name.to_s.end_with?('=')
259
- memo
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
 
@@ -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
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
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
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
94
+ new_from_parsed_data(parsed_data)
95
95
  end
96
96
  end
97
97
 
data/lib/her/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Her
2
- VERSION = "0.9.0"
2
+ VERSION = "0.10.0"
3
3
  end
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.9.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-06-25 00:00:00.000000000 Z
11
+ date: 2017-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake