her 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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