her 1.0.1 → 1.0.2

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +19 -1279
  3. data/.rubocop_todo.yml +232 -0
  4. data/README.md +16 -0
  5. data/her.gemspec +3 -2
  6. data/lib/her/api.rb +8 -7
  7. data/lib/her/collection.rb +2 -1
  8. data/lib/her/errors.rb +3 -1
  9. data/lib/her/json_api/model.rb +8 -12
  10. data/lib/her/middleware.rb +1 -1
  11. data/lib/her/middleware/accept_json.rb +1 -0
  12. data/lib/her/middleware/first_level_parse_json.rb +6 -5
  13. data/lib/her/middleware/json_api_parser.rb +6 -5
  14. data/lib/her/middleware/parse_json.rb +2 -1
  15. data/lib/her/middleware/second_level_parse_json.rb +6 -5
  16. data/lib/her/model/associations.rb +6 -6
  17. data/lib/her/model/associations/association.rb +7 -9
  18. data/lib/her/model/associations/association_proxy.rb +2 -3
  19. data/lib/her/model/associations/belongs_to_association.rb +1 -1
  20. data/lib/her/model/attributes.rb +6 -6
  21. data/lib/her/model/base.rb +2 -2
  22. data/lib/her/model/http.rb +1 -1
  23. data/lib/her/model/introspection.rb +5 -3
  24. data/lib/her/model/nested_attributes.rb +1 -1
  25. data/lib/her/model/orm.rb +9 -8
  26. data/lib/her/model/parse.rb +9 -12
  27. data/lib/her/model/paths.rb +3 -4
  28. data/lib/her/model/relation.rb +5 -4
  29. data/lib/her/version.rb +1 -1
  30. data/spec/api_spec.rb +3 -0
  31. data/spec/middleware/accept_json_spec.rb +1 -0
  32. data/spec/middleware/first_level_parse_json_spec.rb +2 -1
  33. data/spec/middleware/json_api_parser_spec.rb +1 -0
  34. data/spec/middleware/second_level_parse_json_spec.rb +1 -0
  35. data/spec/model/associations/association_proxy_spec.rb +1 -0
  36. data/spec/model/associations_spec.rb +4 -3
  37. data/spec/model/attributes_spec.rb +5 -3
  38. data/spec/model/callbacks_spec.rb +14 -15
  39. data/spec/model/dirty_spec.rb +1 -0
  40. data/spec/model/http_spec.rb +1 -0
  41. data/spec/model/introspection_spec.rb +3 -2
  42. data/spec/model/nested_attributes_spec.rb +1 -0
  43. data/spec/model/orm_spec.rb +22 -16
  44. data/spec/model/parse_spec.rb +3 -0
  45. data/spec/model/paths_spec.rb +1 -0
  46. data/spec/model/relation_spec.rb +3 -2
  47. data/spec/model/validations_spec.rb +1 -0
  48. data/spec/model_spec.rb +1 -0
  49. data/spec/support/extensions/array.rb +1 -0
  50. data/spec/support/extensions/hash.rb +1 -0
  51. metadata +12 -11
@@ -3,6 +3,7 @@ module Her
3
3
  # This middleware expects the resource/collection data to be contained in the `data`
4
4
  # key of the JSON object
5
5
  class JsonApiParser < ParseJSON
6
+
6
7
  # Parse the response body
7
8
  #
8
9
  # @param [String] body The response body
@@ -25,11 +26,11 @@ module Her
25
26
  # @private
26
27
  def on_complete(env)
27
28
  env[:body] = case env[:status]
28
- when 204, 304
29
- parse('{}')
30
- else
31
- parse(env[:body])
32
- end
29
+ when 204, 304
30
+ parse('{}')
31
+ else
32
+ parse(env[:body])
33
+ end
33
34
  end
34
35
  end
35
36
  end
@@ -1,6 +1,7 @@
1
1
  module Her
2
2
  module Middleware
3
3
  class ParseJSON < Faraday::Response::Middleware
4
+
4
5
  # @private
5
6
  def parse_json(body = nil)
6
7
  body = '{}' if body.blank?
@@ -12,7 +13,7 @@ module Her
12
13
  raise Her::Errors::ParseError, message
13
14
  end
14
15
 
15
- raise Her::Errors::ParseError, message unless json.is_a?(Hash) or json.is_a?(Array)
16
+ raise Her::Errors::ParseError, message unless json.is_a?(Hash) || json.is_a?(Array)
16
17
 
17
18
  json
18
19
  end
@@ -3,6 +3,7 @@ module Her
3
3
  # This middleware expects the resource/collection data to be contained in the `data`
4
4
  # key of the JSON object
5
5
  class SecondLevelParseJSON < ParseJSON
6
+
6
7
  # Parse the response body
7
8
  #
8
9
  # @param [String] body The response body
@@ -25,11 +26,11 @@ module Her
25
26
  # @private
26
27
  def on_complete(env)
27
28
  env[:body] = case env[:status]
28
- when 204
29
- parse('{}')
30
- else
31
- parse(env[:body])
32
- end
29
+ when 204
30
+ parse('{}')
31
+ else
32
+ parse(env[:body])
33
+ end
33
34
  end
34
35
  end
35
36
  end
@@ -32,18 +32,18 @@ module Her
32
32
  # @private
33
33
  def associations
34
34
  @_her_associations ||= begin
35
- superclass.respond_to?(:associations) ? superclass.associations.dup : Hash.new { |h,k| h[k] = [] }
35
+ superclass.respond_to?(:associations) ? superclass.associations.dup : Hash.new { |h, k| h[k] = [] }
36
36
  end
37
37
  end
38
38
 
39
39
  # @private
40
40
  def association_names
41
- associations.inject([]) { |memo, (name, details)| memo << details }.flatten.map { |a| a[:name] }
41
+ associations.inject([]) { |memo, (_, details)| memo << details }.flatten.map { |a| a[:name] }
42
42
  end
43
43
 
44
44
  # @private
45
45
  def association_keys
46
- associations.inject([]) { |memo, (name, details)| memo << details }.flatten.map { |a| a[:data_key] }
46
+ associations.inject([]) { |memo, (_, details)| memo << details }.flatten.map { |a| a[:data_key] }
47
47
  end
48
48
 
49
49
  # Parse associations data after initializing a new object
@@ -81,7 +81,7 @@ module Her
81
81
  # @user = User.find(1)
82
82
  # @user.articles # => [#<Article(articles/2) id=2 title="Hello world.">]
83
83
  # # Fetched via GET "/users/1/articles"
84
- def has_many(name, opts={})
84
+ def has_many(name, opts = {})
85
85
  Her::Model::Associations::HasManyAssociation.attach(self, name, opts)
86
86
  end
87
87
 
@@ -106,7 +106,7 @@ module Her
106
106
  # @user = User.find(1)
107
107
  # @user.organization # => #<Organization(organizations/2) id=2 name="Foobar Inc.">
108
108
  # # Fetched via GET "/users/1/organization"
109
- def has_one(name, opts={})
109
+ def has_one(name, opts = {})
110
110
  Her::Model::Associations::HasOneAssociation.attach(self, name, opts)
111
111
  end
112
112
 
@@ -132,7 +132,7 @@ module Her
132
132
  # @user = User.find(1) # => #<User(users/1) id=1 team_id=2 name="Tobias">
133
133
  # @user.team # => #<Team(teams/2) id=2 name="Developers">
134
134
  # # Fetched via GET "/teams/2"
135
- def belongs_to(name, opts={})
135
+ def belongs_to(name, opts = {})
136
136
  Her::Model::Associations::BelongsToAssociation.attach(self, name, opts)
137
137
  end
138
138
  end
@@ -2,6 +2,7 @@ module Her
2
2
  module Model
3
3
  module Associations
4
4
  class Association
5
+
5
6
  # @private
6
7
  attr_accessor :params
7
8
 
@@ -47,7 +48,7 @@ module Her
47
48
  return @parent.attributes[@name] unless @params.any? || @parent.attributes[@name].blank?
48
49
  return @opts[:default].try(:dup) if @parent.new?
49
50
 
50
- path = build_association_path lambda { "#{@parent.request_path(@params)}#{@opts[:path]}" }
51
+ path = build_association_path -> { "#{@parent.request_path(@params)}#{@opts[:path]}" }
51
52
  @klass.get(path, @params).tap do |result|
52
53
  @cached_result = result unless @params.any?
53
54
  end
@@ -55,11 +56,9 @@ module Her
55
56
 
56
57
  # @private
57
58
  def build_association_path(code)
58
- begin
59
- instance_exec(&code)
60
- rescue Her::Errors::PathError
61
- return nil
62
- end
59
+ instance_exec(&code)
60
+ rescue Her::Errors::PathError
61
+ nil
63
62
  end
64
63
 
65
64
  # @private
@@ -81,7 +80,7 @@ module Her
81
80
  # user.comments.where(:approved => 1) # Fetched via GET "/users/1/comments?approved=1
82
81
  def where(params = {})
83
82
  return self if params.blank? && @parent.attributes[@name].blank?
84
- AssociationProxy.new self.clone.tap { |a| a.params = a.params.merge(params) }
83
+ AssociationProxy.new clone.tap { |a| a.params = a.params.merge(params) }
85
84
  end
86
85
  alias all where
87
86
 
@@ -97,7 +96,7 @@ module Her
97
96
  # user.comments.find(3) # Fetched via GET "/users/1/comments/3
98
97
  def find(id)
99
98
  return nil if id.blank?
100
- path = build_association_path lambda { "#{@parent.request_path(@params)}#{@opts[:path]}/#{id}" }
99
+ path = build_association_path -> { "#{@parent.request_path(@params)}#{@opts[:path]}/#{id}" }
101
100
  @klass.get_resource(path, @params)
102
101
  end
103
102
 
@@ -123,7 +122,6 @@ module Her
123
122
  reset
124
123
  fetch
125
124
  end
126
-
127
125
  end
128
126
  end
129
127
  end
@@ -15,7 +15,7 @@ module Her
15
15
  end
16
16
 
17
17
  install_proxy_methods :association,
18
- :build, :create, :where, :find, :all, :assign_nested_attributes, :reload
18
+ :build, :create, :where, :find, :all, :assign_nested_attributes, :reload
19
19
 
20
20
  # @private
21
21
  def initialize(association)
@@ -28,7 +28,7 @@ module Her
28
28
 
29
29
  # @private
30
30
  def method_missing(name, *args, &block)
31
- if :object_id == name # avoid redefining object_id
31
+ if name == :object_id # avoid redefining object_id
32
32
  return association.fetch.object_id
33
33
  end
34
34
 
@@ -38,7 +38,6 @@ module Her
38
38
  # resend message to fetched object
39
39
  __send__(name, *args, &block)
40
40
  end
41
-
42
41
  end
43
42
  end
44
43
  end
@@ -80,7 +80,7 @@ module Her
80
80
  return @parent.attributes[@name] unless @params.any? || @parent.attributes[@name].blank?
81
81
 
82
82
  path_params = @parent.attributes.merge(@params.merge(@klass.primary_key => foreign_key_value))
83
- path = build_association_path lambda { @klass.build_request_path(path_params) }
83
+ path = build_association_path -> { @klass.build_request_path(path_params) }
84
84
  @klass.get_resource(path, @params).tap do |result|
85
85
  @cached_result = result if @params.blank?
86
86
  end
@@ -23,7 +23,7 @@ module Her
23
23
  # u.name = "Tobias"
24
24
  # end
25
25
  # # => #<User name="Tobias">
26
- def initialize(attributes={})
26
+ def initialize(attributes = {})
27
27
  attributes ||= {}
28
28
  @metadata = attributes.delete(:_metadata) || {}
29
29
  @response_errors = attributes.delete(:_errors) || {}
@@ -151,19 +151,18 @@ module Her
151
151
  end
152
152
 
153
153
  module ClassMethods
154
-
155
154
  # Initialize a single resource
156
155
  #
157
156
  # @private
158
157
  def instantiate_record(klass, parsed_data)
159
- if record = parsed_data[:data] and record.kind_of?(klass)
158
+ if (record = parsed_data[:data]) && record.is_a?(klass)
160
159
  record
161
160
  else
162
161
  attributes = klass.parse(record).merge(_metadata: parsed_data[:metadata],
163
162
  _errors: parsed_data[:errors])
164
- klass.new(attributes).tap do |record|
165
- record.send :clear_changes_information
166
- record.run_callbacks :find
163
+ klass.new(attributes).tap do |record_instance|
164
+ record_instance.send :clear_changes_information
165
+ record_instance.run_callbacks :find
167
166
  end
168
167
  end
169
168
  end
@@ -289,6 +288,7 @@ module Her
289
288
  end
290
289
 
291
290
  private
291
+
292
292
  # @private
293
293
  def store_her_data(name, value)
294
294
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
@@ -11,7 +11,7 @@ module Her
11
11
  # @private
12
12
  def has_key?(attribute_name)
13
13
  has_attribute?(attribute_name) ||
14
- has_association?(attribute_name)
14
+ has_association?(attribute_name)
15
15
  end
16
16
 
17
17
  # Returns
@@ -21,7 +21,7 @@ module Her
21
21
  # @private
22
22
  def [](attribute_name)
23
23
  get_attribute(attribute_name) ||
24
- get_association(attribute_name)
24
+ get_association(attribute_name)
25
25
  end
26
26
 
27
27
  # @private
@@ -52,7 +52,7 @@ module Her
52
52
  # Main request wrapper around Her::API. Used to make custom request to the API.
53
53
  #
54
54
  # @private
55
- def request(params={})
55
+ def request(params = {})
56
56
  request = her_api.request(params)
57
57
 
58
58
  if block_given?
@@ -22,6 +22,7 @@ module Her
22
22
  end
23
23
 
24
24
  private
25
+
25
26
  def attribute_for_inspect(value)
26
27
  if value.is_a?(String) && value.length > 50
27
28
  "#{value[0..50]}...".inspect
@@ -42,11 +43,12 @@ module Her
42
43
  end
43
44
 
44
45
  protected
46
+
45
47
  # Looks for a class at the same level as this one with the given name.
46
48
  #
47
49
  # @private
48
50
  def her_sibling_class(name)
49
- if mod = self.her_containing_module
51
+ if mod = her_containing_module
50
52
  @_her_sibling_class ||= Hash.new { Hash.new }
51
53
  @_her_sibling_class[mod][name] ||= "#{mod.name}::#{name}".constantize rescue nil
52
54
  end
@@ -56,8 +58,8 @@ module Her
56
58
  #
57
59
  # @private
58
60
  def her_containing_module
59
- return unless self.name =~ /::/
60
- self.name.split("::")[0..-2].join("::").constantize
61
+ return unless name =~ /::/
62
+ name.split("::")[0..-2].join("::").constantize
61
63
  end
62
64
  end
63
65
  end
@@ -25,7 +25,7 @@ module Her
25
25
 
26
26
  associations.each do |association_name|
27
27
  unless allowed_association_names.include?(association_name)
28
- raise Her::Errors::AssociationUnknownError.new("Unknown association name :#{association_name}")
28
+ raise Her::Errors::AssociationUnknownError, "Unknown association name :#{association_name}"
29
29
  end
30
30
 
31
31
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
@@ -1,4 +1,5 @@
1
1
  # coding: utf-8
2
+
2
3
  module Her
3
4
  module Model
4
5
  # This module adds ORM-like capabilities to the model
@@ -54,7 +55,7 @@ module Her
54
55
 
55
56
  # Similar to save(), except that ResourceInvalid is raised if the save fails
56
57
  def save!
57
- if !self.save
58
+ unless save
58
59
  raise Her::Errors::ResourceInvalid, self
59
60
  end
60
61
  self
@@ -195,7 +196,7 @@ module Her
195
196
  #
196
197
  # User.all # Called via GET "/users?admin=1"
197
198
  # User.new.admin # => 1
198
- def default_scope(block=nil)
199
+ def default_scope(block = nil)
199
200
  @_her_default_scope ||= (!respond_to?(:default_scope) && superclass.respond_to?(:default_scope)) ? superclass.default_scope : scoped
200
201
  @_her_default_scope = @_her_default_scope.instance_exec(&block) unless block.nil?
201
202
  @_her_default_scope
@@ -227,7 +228,7 @@ module Her
227
228
  # @example
228
229
  # User.destroy_existing(1)
229
230
  # # Called via DELETE "/users/1"
230
- def destroy_existing(id, params={})
231
+ def destroy_existing(id, params = {})
231
232
  request(params.merge(:_method => method_for(:destroy), :_path => build_request_path(params.merge(primary_key => id)))) do |parsed_data, response|
232
233
  data = parse(parsed_data[:data])
233
234
  metadata = parsed_data[:metadata]
@@ -256,15 +257,15 @@ module Her
256
257
  # If the request_new_object_on_build flag is set, the new object is requested via API.
257
258
  def build(attributes = {})
258
259
  params = attributes
259
- return self.new(params) unless self.request_new_object_on_build?
260
+ return new(params) unless request_new_object_on_build?
260
261
 
261
- path = self.build_request_path(params.merge(self.primary_key => 'new'))
262
- method = self.method_for(:new)
262
+ path = build_request_path(params.merge(primary_key => 'new'))
263
+ method = method_for(:new)
263
264
 
264
265
  resource = nil
265
- self.request(params.merge(:_method => method, :_path => path)) do |parsed_data, response|
266
+ request(params.merge(:_method => method, :_path => path)) do |parsed_data, response|
266
267
  if response.success?
267
- resource = self.new_from_parsed_data(parsed_data)
268
+ resource = new_from_parsed_data(parsed_data)
268
269
  end
269
270
  end
270
271
  resource
@@ -10,7 +10,7 @@ module Her
10
10
  # @user.to_params
11
11
  # # => { :id => 1, :name => 'John Smith' }
12
12
  def to_params
13
- self.class.to_params(self.attributes, self.changes)
13
+ self.class.to_params(attributes, changes)
14
14
  end
15
15
 
16
16
  module ClassMethods
@@ -31,7 +31,7 @@ module Her
31
31
  end
32
32
 
33
33
  # @private
34
- def to_params(attributes, changes={})
34
+ def to_params(attributes, changes = {})
35
35
  filtered_attributes = attributes.each_with_object({}) do |(key, value), memo|
36
36
  case value
37
37
  when Her::Model
@@ -45,10 +45,7 @@ module Her
45
45
  filtered_attributes.merge!(embeded_params(attributes))
46
46
 
47
47
  if her_api.options[:send_only_modified_attributes]
48
- filtered_attributes = changes.symbolize_keys.keys.inject({}) do |hash, attribute|
49
- hash[attribute] = filtered_attributes[attribute]
50
- hash
51
- end
48
+ filtered_attributes.slice! *changes.keys.map(&:to_sym)
52
49
  end
53
50
 
54
51
  if include_root_in_json?
@@ -133,11 +130,11 @@ module Her
133
130
  # user.name # => "Tobias"
134
131
  def root_element(value = nil)
135
132
  if value.nil?
136
- if json_api_format?
137
- @_her_root_element ||= self.name.split("::").last.pluralize.underscore.to_sym
138
- else
139
- @_her_root_element ||= self.name.split("::").last.underscore.to_sym
140
- end
133
+ @_her_root_element ||= if json_api_format?
134
+ name.split("::").last.pluralize.underscore.to_sym
135
+ else
136
+ name.split("::").last.underscore.to_sym
137
+ end
141
138
  else
142
139
  @_her_root_element = value.to_sym
143
140
  end
@@ -145,7 +142,7 @@ module Her
145
142
 
146
143
  # @private
147
144
  def root_element_included?(data)
148
- element = data[root_element]
145
+ element = data[parsed_root_element]
149
146
  element.is_a?(Hash) || element.is_a?(Array)
150
147
  end
151
148
 
@@ -19,7 +19,6 @@ module Her
19
19
  end
20
20
 
21
21
  module ClassMethods
22
-
23
22
  # Define the primary key field that will be used to find and save records
24
23
  #
25
24
  # @example
@@ -88,13 +87,13 @@ module Her
88
87
  # Return a custom path based on the collection path and variable parameters
89
88
  #
90
89
  # @private
91
- def build_request_path(path=nil, parameters={})
90
+ def build_request_path(path = nil, parameters = {})
92
91
  parameters = parameters.try(:with_indifferent_access)
93
92
 
94
93
  unless path.is_a?(String)
95
94
  parameters = path.try(:with_indifferent_access) || parameters
96
95
  path =
97
- if parameters.include?(primary_key) && parameters[primary_key] && !parameters[primary_key].kind_of?(Array)
96
+ if parameters.include?(primary_key) && parameters[primary_key] && !parameters[primary_key].is_a?(Array)
98
97
  resource_path.dup
99
98
  else
100
99
  collection_path.dup
@@ -117,7 +116,7 @@ module Her
117
116
  end
118
117
 
119
118
  # @private
120
- def build_request_path_from_string_or_symbol(path, params={})
119
+ def build_request_path_from_string_or_symbol(path, params = {})
121
120
  path.is_a?(Symbol) ? "#{build_request_path(params)}/#{path}" : path
122
121
  end
123
122
  end