her 0.5.3 → 0.5.4

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 (72) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +2 -2
  3. data/.rspec +1 -2
  4. data/.travis.yml +2 -2
  5. data/README.md +10 -16
  6. data/UPGRADE.md +4 -0
  7. data/examples/grape-and-her/.env.default +3 -0
  8. data/examples/grape-and-her/Procfile +2 -0
  9. data/examples/grape-and-her/README.md +27 -0
  10. data/examples/grape-and-her/api/Gemfile +11 -0
  11. data/examples/grape-and-her/api/Rakefile +14 -0
  12. data/examples/grape-and-her/api/app/api.rb +49 -0
  13. data/examples/grape-and-her/api/app/models/organization.rb +7 -0
  14. data/examples/grape-and-her/api/app/models/user.rb +9 -0
  15. data/examples/grape-and-her/api/app/views/organizations/_base.rabl +2 -0
  16. data/examples/grape-and-her/api/app/views/organizations/index.rabl +3 -0
  17. data/examples/grape-and-her/api/app/views/organizations/show.rabl +3 -0
  18. data/examples/grape-and-her/api/app/views/users/_base.rabl +8 -0
  19. data/examples/grape-and-her/api/app/views/users/index.rabl +3 -0
  20. data/examples/grape-and-her/api/app/views/users/show.rabl +3 -0
  21. data/examples/grape-and-her/api/config.ru +5 -0
  22. data/examples/grape-and-her/api/config/boot.rb +17 -0
  23. data/examples/grape-and-her/api/config/unicorn.rb +7 -0
  24. data/examples/grape-and-her/api/db/migrations/001_create_users.rb +11 -0
  25. data/examples/grape-and-her/api/db/migrations/002_create_organizations.rb +8 -0
  26. data/examples/grape-and-her/consumer/Gemfile +23 -0
  27. data/examples/grape-and-her/consumer/app/assets/stylesheets/application.scss +190 -0
  28. data/examples/grape-and-her/consumer/app/assets/stylesheets/reset.scss +53 -0
  29. data/examples/grape-and-her/consumer/app/consumer.rb +74 -0
  30. data/examples/grape-and-her/consumer/app/models/organization.rb +13 -0
  31. data/examples/grape-and-her/consumer/app/models/user.rb +13 -0
  32. data/examples/grape-and-her/consumer/app/views/index.haml +9 -0
  33. data/examples/grape-and-her/consumer/app/views/layout.haml +20 -0
  34. data/examples/grape-and-her/consumer/app/views/organizations/index.haml +25 -0
  35. data/examples/grape-and-her/consumer/app/views/organizations/show.haml +11 -0
  36. data/examples/grape-and-her/consumer/app/views/users/index.haml +33 -0
  37. data/examples/grape-and-her/consumer/app/views/users/show.haml +9 -0
  38. data/examples/grape-and-her/consumer/config.ru +20 -0
  39. data/examples/grape-and-her/consumer/config/boot.rb +30 -0
  40. data/examples/grape-and-her/consumer/config/unicorn.rb +7 -0
  41. data/examples/grape-and-her/consumer/lib/response_logger.rb +18 -0
  42. data/her.gemspec +2 -2
  43. data/lib/her/model.rb +22 -26
  44. data/lib/her/model/associations.rb +19 -19
  45. data/lib/her/model/attributes.rb +173 -0
  46. data/lib/her/model/base.rb +17 -0
  47. data/lib/her/model/http.rb +58 -242
  48. data/lib/her/model/introspection.rb +7 -8
  49. data/lib/her/model/nested_attributes.rb +3 -3
  50. data/lib/her/model/orm.rb +15 -205
  51. data/lib/her/model/parse.rb +86 -0
  52. data/lib/her/model/paths.rb +54 -14
  53. data/lib/her/version.rb +1 -1
  54. data/spec/model/attributes_spec.rb +139 -0
  55. data/spec/model/dirty_spec.rb +40 -0
  56. data/spec/model/introspection_spec.rb +5 -5
  57. data/spec/model/orm_spec.rb +14 -128
  58. data/spec/model/paths_spec.rb +26 -0
  59. data/spec/model/validations_spec.rb +17 -0
  60. data/spec/spec_helper.rb +7 -32
  61. data/spec/support/extensions/array.rb +5 -0
  62. data/spec/support/extensions/hash.rb +5 -0
  63. data/spec/support/macros/model_macros.rb +29 -0
  64. metadata +52 -15
  65. data/examples/twitter-oauth/Gemfile +0 -13
  66. data/examples/twitter-oauth/app.rb +0 -50
  67. data/examples/twitter-oauth/config.ru +0 -5
  68. data/examples/twitter-oauth/views/index.haml +0 -9
  69. data/examples/twitter-search/Gemfile +0 -12
  70. data/examples/twitter-search/app.rb +0 -55
  71. data/examples/twitter-search/config.ru +0 -5
  72. data/examples/twitter-search/views/index.haml +0 -9
@@ -18,12 +18,12 @@ module Her
18
18
  alias :get_relationship :get_association
19
19
 
20
20
  module ClassMethods
21
- # Return @her_associations, lazily initialized with copy of the
21
+ # Return @_her_associations, lazily initialized with copy of the
22
22
  # superclass' her_associations, or an empty hash.
23
23
  #
24
24
  # @private
25
25
  def associations
26
- @her_associations ||= begin
26
+ @_her_associations ||= begin
27
27
  if superclass.respond_to?(:associations)
28
28
  superclass.associations.dup
29
29
  else
@@ -42,12 +42,12 @@ module Her
42
42
  data_key = association[:data_key]
43
43
  next unless data[data_key]
44
44
 
45
- klass = self.nearby_class(association[:class_name])
45
+ klass = self.her_nearby_class(association[:class_name])
46
46
  name = association[:name]
47
47
 
48
48
  data[name] = case type
49
49
  when :has_many
50
- Her::Model::ORM.initialize_collection(klass, :data => data[data_key])
50
+ Her::Model::Attributes.initialize_collection(klass, :data => data[data_key])
51
51
  when :has_one, :belongs_to
52
52
  klass.new(data[data_key])
53
53
  else
@@ -65,12 +65,12 @@ module Her
65
65
  #
66
66
  # @example
67
67
  # class User
68
- # include Her::API
68
+ # include Her::Model
69
69
  # has_many :articles
70
70
  # end
71
71
  #
72
72
  # class Article
73
- # include Her::API
73
+ # include Her::Model
74
74
  # end
75
75
  #
76
76
  # @user = User.find(1)
@@ -88,13 +88,13 @@ module Her
88
88
 
89
89
  define_method(name) do |*method_attrs|
90
90
  method_attrs = method_attrs[0] || {}
91
- klass = self.class.nearby_class(attrs[:class_name])
91
+ klass = self.class.her_nearby_class(attrs[:class_name])
92
92
 
93
93
  return Her::Collection.new if @attributes.include?(name) && @attributes[name].empty? && method_attrs.empty?
94
94
 
95
95
  if @attributes[name].blank? || method_attrs.any?
96
96
  path = begin
97
- self.class.build_request_path(@attributes.merge(method_attrs))
97
+ request_path(method_attrs)
98
98
  rescue Her::Errors::PathError
99
99
  return nil
100
100
  end
@@ -115,16 +115,16 @@ module Her
115
115
  # Define an *has_one* association.
116
116
  #
117
117
  # @param [Symbol] name The name of the model
118
- # @param [Hash] attrs Options (currently not used)
118
+ # @param [Hash] attrs Options
119
119
  #
120
120
  # @example
121
121
  # class User
122
- # include Her::API
122
+ # include Her::Model
123
123
  # has_one :organization
124
124
  # end
125
125
  #
126
126
  # class Organization
127
- # include Her::API
127
+ # include Her::Model
128
128
  # end
129
129
  #
130
130
  # @user = User.find(1)
@@ -141,13 +141,13 @@ module Her
141
141
 
142
142
  define_method(name) do |*method_attrs|
143
143
  method_attrs = method_attrs[0] || {}
144
- klass = self.class.nearby_class(attrs[:class_name])
144
+ klass = self.class.her_nearby_class(attrs[:class_name])
145
145
 
146
146
  return nil if @attributes.include?(name) && @attributes[name].nil? && method_attrs.empty?
147
147
 
148
148
  if @attributes[name].blank? || method_attrs.any?
149
149
  path = begin
150
- self.class.build_request_path(@attributes.merge(method_attrs))
150
+ request_path(method_attrs)
151
151
  rescue Her::Errors::PathError
152
152
  return nil
153
153
  end
@@ -162,19 +162,19 @@ module Her
162
162
  # Define a *belongs_to* association.
163
163
  #
164
164
  # @param [Symbol] name The name of the model
165
- # @param [Hash] attrs Options (currently not used)
165
+ # @param [Hash] attrs Options
166
166
  #
167
167
  # @example
168
168
  # class User
169
- # include Her::API
169
+ # include Her::Model
170
170
  # belongs_to :team, :class_name => "Group"
171
171
  # end
172
172
  #
173
173
  # class Group
174
- # include Her::API
174
+ # include Her::Model
175
175
  # end
176
176
  #
177
- # @user = User.find(1)
177
+ # @user = User.find(1) # => #<User(users/1) id=1 team_id=2 name="Tobias">
178
178
  # @user.team # => #<Team(teams/2) id=2 name="Developers">
179
179
  # # Fetched via GET "/teams/2"
180
180
  def belongs_to(name, attrs={})
@@ -189,13 +189,13 @@ module Her
189
189
 
190
190
  define_method(name) do |*method_attrs|
191
191
  method_attrs = method_attrs[0] || {}
192
- klass = self.class.nearby_class(attrs[:class_name])
192
+ klass = self.class.her_nearby_class(attrs[:class_name])
193
193
 
194
194
  return nil if @attributes.include?(name) && @attributes[name].nil? && method_attrs.empty?
195
195
 
196
196
  if @attributes[name].blank? || method_attrs.any?
197
197
  path = begin
198
- klass.build_request_path(@attributes.merge(method_attrs.merge(:id => @attributes[attrs[:foreign_key].to_sym])))
198
+ klass.build_request_path(@attributes.merge(method_attrs.merge(klass.primary_key => @attributes[attrs[:foreign_key].to_sym])))
199
199
  rescue Her::Errors::PathError
200
200
  return nil
201
201
  end
@@ -0,0 +1,173 @@
1
+ module Her
2
+ module Model
3
+ # This module handles all methods related to model attributes
4
+ module Attributes
5
+ extend ActiveSupport::Concern
6
+
7
+ attr_accessor :attributes, :metadata, :response_errors
8
+ alias :data :attributes
9
+ alias :data= :attributes=
10
+
11
+ # Initialize a new object with data
12
+ def initialize(attributes={})
13
+ attributes ||= {}
14
+ @metadata = attributes.delete(:_metadata) || {}
15
+ @response_errors = attributes.delete(:_errors) || {}
16
+ @destroyed = attributes.delete(:_destroyed) || false
17
+
18
+ assign_attributes(attributes)
19
+ end
20
+
21
+ # Initialize a collection of resources
22
+ # @private
23
+ def self.initialize_collection(klass, parsed_data={})
24
+ collection_data = parsed_data[:data].map do |item_data|
25
+ resource = klass.new(klass.parse(item_data))
26
+ resource.run_callbacks :find
27
+ resource
28
+ end
29
+ Her::Collection.new(collection_data, parsed_data[:metadata], parsed_data[:errors])
30
+ end
31
+
32
+ # Use setter methods of model for each key / value pair in params
33
+ # Return key / value pairs for which no setter method was defined on the model
34
+ # @private
35
+ def self.use_setter_methods(model, params)
36
+ setter_method_names = model.class.setter_method_names
37
+ params ||= {}
38
+ params.inject({}) do |memo, (key, value)|
39
+ setter_method = key.to_s + '='
40
+ if setter_method_names.include?(setter_method)
41
+ model.send(setter_method, value)
42
+ else
43
+ if key.is_a?(String)
44
+ key = key.to_sym
45
+ end
46
+ memo[key] = value
47
+ end
48
+ memo
49
+ end
50
+ end
51
+
52
+ # Handles missing methods
53
+ # @private
54
+ def method_missing(method, *args, &blk)
55
+ if method.to_s =~ /[?=]$/ || attributes.include?(method)
56
+ # Extract the attribute
57
+ attribute = method.to_s.sub(/[?=]$/, '')
58
+
59
+ # Create a new `attribute` methods set
60
+ self.class.attributes(*attribute)
61
+
62
+ # Resend the method!
63
+ send(method, *args, &blk)
64
+ else
65
+ super
66
+ end
67
+ end
68
+
69
+ # @private
70
+ def respond_to?(method, include_private = false)
71
+ method.to_s.end_with?('=') || method.to_s.end_with?('?') || @attributes.include?(method) || super
72
+ end
73
+
74
+ # @private
75
+ def respond_to_missing?(method, include_private = false)
76
+ method.to_s.end_with?('=') || method.to_s.end_with?('?') || @attributes.include?(method) || @attributes.include?(method) || super
77
+ end
78
+
79
+ # Assign new attributes
80
+ # @private
81
+ def assign_attributes(raw_data)
82
+ @attributes ||= {}
83
+ # Use setter methods first, then translate attributes of associations
84
+ # into association instances, then merge the parsed_data into @attributes.
85
+ unset_attributes = Her::Model::Attributes.use_setter_methods(self, raw_data)
86
+ parsed_attributes = self.class.parse_associations(unset_attributes)
87
+ attributes.update(parsed_attributes)
88
+ end
89
+ alias :update_attributes :assign_attributes
90
+ alias :assign_data :assign_attributes
91
+
92
+ # Handles returning true for the accessible attributes
93
+ # @private
94
+ def has_attribute?(attribute_name)
95
+ attributes.include?(attribute_name)
96
+ end
97
+ alias :has_data? :has_attribute?
98
+
99
+ # Handles returning data for a specific attribute
100
+ # @private
101
+ def get_attribute(attribute_name)
102
+ attributes[attribute_name]
103
+ end
104
+ alias :get_data :get_attribute
105
+
106
+ # Override the method to prevent from returning the object ID
107
+ # @private
108
+ def id
109
+ attributes[self.class.primary_key] || super
110
+ end
111
+
112
+ # Return `true` if the other object is also a Her::Model and has matching data
113
+ # @private
114
+ def ==(other)
115
+ other.is_a?(Her::Model) && attributes == other.attributes
116
+ end
117
+
118
+ # Delegate to the == method
119
+ # @private
120
+ def eql?(other)
121
+ self == other
122
+ end
123
+
124
+ # Delegate to @attributes, allowing models to act correctly in code like:
125
+ # [ Model.find(1), Model.find(1) ].uniq # => [ Model.find(1) ]
126
+ # @private
127
+ def hash
128
+ attributes.hash
129
+ end
130
+
131
+ module ClassMethods
132
+ # Initialize a collection of resources with raw data from an HTTP request
133
+ #
134
+ # @param [Array] parsed_data
135
+ def new_collection(parsed_data)
136
+ Her::Model::Attributes.initialize_collection(self, parsed_data)
137
+ end
138
+
139
+ # Define the attributes that will be used to track dirty attributes and validations
140
+ #
141
+ # @param [Array] attributes
142
+ def attributes(*attributes)
143
+ define_attribute_methods attributes
144
+
145
+ attributes.each do |attribute|
146
+ attribute = attribute.to_sym
147
+
148
+ define_method "#{attribute}".to_sym do
149
+ @attributes.include?(attribute) ? @attributes[attribute] : nil
150
+ end
151
+
152
+ define_method "#{attribute}=".to_sym do |value|
153
+ self.send("#{attribute}_will_change!".to_sym) if @attributes[attribute] != value
154
+ @attributes[attribute] = value
155
+ end
156
+
157
+ define_method "#{attribute}?".to_sym do
158
+ @attributes.include?(attribute) && @attributes[attribute].present?
159
+ end
160
+ end
161
+ end
162
+
163
+ # @private
164
+ def setter_method_names
165
+ @_her_setter_method_names ||= instance_methods.inject(Set.new) do |memo, method_name|
166
+ memo << method_name.to_s if method_name.to_s.end_with?('=')
167
+ memo
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
@@ -2,6 +2,23 @@ module Her
2
2
  module Model
3
3
  # This module includes basic functionnality to Her::Model
4
4
  module Base
5
+ extend ActiveSupport::Concern
6
+
7
+ # Returns true if attribute_name is
8
+ # * in resource attributes
9
+ # * an association
10
+ def has_key?(attribute_name)
11
+ has_attribute?(attribute_name) ||
12
+ has_association?(attribute_name)
13
+ end
14
+
15
+ # Returns
16
+ # * the value of the attribute_name attribute if it's in orm data
17
+ # * the resource/collection corrsponding to attribute_name if it's an association
18
+ def [](attribute_name)
19
+ get_attribute(attribute_name) ||
20
+ get_association(attribute_name)
21
+ end
5
22
  end
6
23
  end
7
24
  end
@@ -2,266 +2,82 @@ module Her
2
2
  module Model
3
3
  # This module interacts with Her::API to fetch HTTP data
4
4
  module HTTP
5
- # Automatically inherit a superclass' api
6
- def her_api
7
- @her_api ||= begin
8
- if superclass.respond_to?(:her_api)
9
- superclass.her_api
10
- else
11
- Her::API.default_api
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ # Automatically inherit a superclass' api
9
+ def her_api
10
+ @_her_api ||= begin
11
+ if superclass.respond_to?(:her_api)
12
+ superclass.her_api
13
+ else
14
+ Her::API.default_api
15
+ end
12
16
  end
13
17
  end
14
- end
15
18
 
16
- # Link a model with a Her::API object
17
- def uses_api(api)
18
- @her_api = api
19
- end
20
-
21
- # Main request wrapper around Her::API. Used to make custom request to the API.
22
- # @private
23
- def request(attrs={})
24
- request = her_api.request(attrs)
25
- if block_given?
26
- yield request[:parsed_data], request[:response]
27
- else
28
- { :parsed_data => request[:parsed_data], :response => request[:response] }
19
+ # Link a model with a Her::API object
20
+ def uses_api(api)
21
+ @_her_api = api
29
22
  end
30
- end
31
23
 
32
- # Make a GET request and return either a collection or a resource
33
- #
34
- # @example
35
- # class User
36
- # include Her::Model
37
- # end
38
- #
39
- # @popular_users = User.get(:popular)
40
- # # Fetched via GET "/users/popular"
41
- def get(path, attrs={})
42
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
43
- get_raw(path, attrs) do |parsed_data, response|
44
- if parsed_data[:data].is_a?(Array)
45
- new_collection(parsed_data)
46
- else
47
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
48
- end
49
- end
50
- end
24
+ # Main request wrapper around Her::API. Used to make custom request to the API.
25
+ # @private
26
+ def request(attrs={})
27
+ request = her_api.request(attrs)
51
28
 
52
- # Make a GET request and return the parsed JSON response (not mapped to objects)
53
- def get_raw(path, attrs={}, &block)
54
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
55
- request(attrs.merge(:_method => :get, :_path => path), &block)
56
- end
57
-
58
- # Make a GET request and return a collection of resources
59
- def get_collection(path=nil, attrs={})
60
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
61
- get_raw(path, attrs) do |parsed_data, response|
62
- new_collection(parsed_data)
63
- end
64
- end
65
-
66
- # Make a GET request and return a collection of resources
67
- def get_resource(path, attrs={})
68
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
69
- get_raw(path, attrs) do |parsed_data, response|
70
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
71
- end
72
- end
73
-
74
- # Make a POST request and return either a collection or a resource
75
- def post(path, attrs={})
76
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
77
- post_raw(path, attrs) do |parsed_data, response|
78
- if parsed_data[:data].is_a?(Array)
79
- new_collection(parsed_data)
29
+ if block_given?
30
+ yield request[:parsed_data], request[:response]
80
31
  else
81
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
32
+ { :parsed_data => request[:parsed_data], :response => request[:response] }
82
33
  end
83
34
  end
84
- end
85
35
 
86
- # Make a POST request and return the parsed JSON response (not mapped to objects)
87
- def post_raw(path, attrs={}, &block)
88
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
89
- request(attrs.merge(:_method => :post, :_path => path), &block)
90
- end
91
-
92
- # Make a POST request and return a collection of resources
93
- def post_collection(path, attrs={})
94
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
95
- post_raw(path, attrs) do |parsed_data, response|
96
- new_collection(parsed_data)
97
- end
98
- end
99
-
100
- # Make a POST request and return a collection of resources
101
- def post_resource(path, attrs={})
102
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
103
- post_raw(path, attrs) do |parsed_data, response|
104
- new(parse(parsed_data[:data]))
105
- end
106
- end
107
-
108
- # Make a PUT request and return either a collection or a resource
109
- def put(path, attrs={})
110
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
111
- put_raw(path, attrs) do |parsed_data, response|
112
- if parsed_data[:data].is_a?(Array)
113
- new_collection(parsed_data)
114
- else
115
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
36
+ # For each HTTP method, define these methods:
37
+ #
38
+ # - <method>(path, attrs)
39
+ # - <method>_raw(path, attrs, &block)
40
+ # - <method>_collection(path, attrs, &block)
41
+ # - <method>_resource(path, attrs, &block)
42
+ # - custom_<method>(path, attrs)
43
+ %w{GET POST PUT PATCH DELETE}.map(&:downcase).map(&:to_sym).each do |method|
44
+ define_method method do |path, attrs={}|
45
+ path = build_request_path_from_string_or_symbol(path, attrs)
46
+ send("#{method}_raw".to_sym, path, attrs) do |parsed_data, response|
47
+ if parsed_data[:data].is_a?(Array)
48
+ new_collection(parsed_data)
49
+ else
50
+ new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
51
+ end
52
+ end
116
53
  end
117
- end
118
- end
119
-
120
- # Make a PUT request and return the parsed JSON response (not mapped to objects)
121
- def put_raw(path, attrs={}, &block)
122
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
123
- request(attrs.merge(:_method => :put, :_path => path), &block)
124
- end
125
-
126
- # Make a PUT request and return a collection of resources
127
- def put_collection(path, attrs={})
128
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
129
- put_raw(path, attrs) do |parsed_data, response|
130
- new_collection(parsed_data)
131
- end
132
- end
133
-
134
- # Make a PUT request and return a collection of resources
135
- def put_resource(path, attrs={})
136
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
137
- put_raw(path, attrs) do |parsed_data, response|
138
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
139
- end
140
- end
141
54
 
142
- # Make a PATCH request and return either a collection or a resource
143
- def patch(path, attrs={})
144
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
145
- patch_raw(path, attrs) do |parsed_data, response|
146
- if parsed_data[:data].is_a?(Array)
147
- new_collection(parsed_data)
148
- else
149
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
150
- end
151
- end
152
- end
153
-
154
- # Make a PATCH request and return the parsed JSON response (not mapped to objects)
155
- def patch_raw(path, attrs={}, &block)
156
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
157
- request(attrs.merge(:_method => :patch, :_path => path), &block)
158
- end
159
-
160
- # Make a PATCH request and return a collection of resources
161
- def patch_collection(path, attrs={})
162
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
163
- patch_raw(path, attrs) do |parsed_data, response|
164
- new_collection(parsed_data)
165
- end
166
- end
167
-
168
- # Make a PATCH request and return a collection of resources
169
- def patch_resource(path, attrs={})
170
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
171
- patch_raw(path, attrs) do |parsed_data, response|
172
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
173
- end
174
- end
175
-
176
- # Make a DELETE request and return either a collection or a resource
177
- def delete(path, attrs={})
178
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
179
- delete_raw(path, attrs) do |parsed_data, response|
180
- if parsed_data[:data].is_a?(Array)
181
- new_collection(parsed_data)
182
- else
183
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
184
- end
185
- end
186
- end
187
-
188
- # Make a DELETE request and return the parsed JSON response (not mapped to objects)
189
- def delete_raw(path, attrs={}, &block)
190
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
191
- request(attrs.merge(:_method => :delete, :_path => path), &block)
192
- end
193
-
194
- # Make a DELETE request and return a collection of resources
195
- def delete_collection(path, attrs={})
196
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
197
- delete_raw(path, attrs) do |parsed_data, response|
198
- new_collection(parsed_data)
199
- end
200
- end
201
-
202
- # Make a DELETE request and return a collection of resources
203
- def delete_resource(path, attrs={})
204
- path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
205
- delete_raw(path, attrs) do |parsed_data, response|
206
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
207
- end
208
- end
209
-
210
- # Define custom GET requests
211
- #
212
- # @example
213
- # class User
214
- # include Her::Model
215
- # custom_get :popular
216
- # end
217
- #
218
- # User.popular
219
- # # Fetched from GET "/users/popular"
220
- def custom_get(*paths)
221
- metaclass = (class << self; self; end)
222
- paths.each do |path|
223
- metaclass.send(:define_method, path.to_sym) do |*attrs|
224
- get(path, attrs.first || Hash.new)
225
- end
226
- end
227
- end
228
-
229
- # Define custom POST requests
230
- def custom_post(*paths)
231
- metaclass = (class << self; self; end)
232
- paths.each do |path|
233
- metaclass.send(:define_method, path.to_sym) do |*attrs|
234
- post(path, attrs.first || Hash.new)
55
+ define_method "#{method}_raw".to_sym do |path, attrs={}, &block|
56
+ path = build_request_path_from_string_or_symbol(path, attrs)
57
+ request(attrs.merge(:_method => method, :_path => path), &block)
235
58
  end
236
- end
237
- end
238
59
 
239
- # Define custom PUT requests
240
- def custom_put(*paths)
241
- metaclass = (class << self; self; end)
242
- paths.each do |path|
243
- metaclass.send(:define_method, path.to_sym) do |*attrs|
244
- put(path, attrs.first || Hash.new)
60
+ define_method "#{method}_collection".to_sym do |path=nil, attrs={}|
61
+ path = build_request_path_from_string_or_symbol(path, attrs)
62
+ send("#{method}_raw".to_sym, build_request_path_from_string_or_symbol(path, attrs), attrs) do |parsed_data, response|
63
+ new_collection(parsed_data)
64
+ end
245
65
  end
246
- end
247
- end
248
66
 
249
- # Define custom PATCH requests
250
- def custom_patch(*paths)
251
- metaclass = (class << self; self; end)
252
- paths.each do |path|
253
- metaclass.send(:define_method, path.to_sym) do |*attrs|
254
- patch(path, attrs.first || Hash.new)
67
+ define_method "#{method}_resource".to_sym do |path, attrs={}|
68
+ path = build_request_path_from_string_or_symbol(path, attrs)
69
+ send("#{method}_raw".to_sym, path, attrs) do |parsed_data, response|
70
+ new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
71
+ end
255
72
  end
256
- end
257
- end
258
73
 
259
- # Define custom DELETE requests
260
- def custom_delete(*paths)
261
- metaclass = (class << self; self; end)
262
- paths.each do |path|
263
- metaclass.send(:define_method, path.to_sym) do |*attrs|
264
- delete(path, attrs.first || Hash.new)
74
+ define_method "custom_#{method}".to_sym do |*paths|
75
+ metaclass = (class << self; self; end)
76
+ paths.each do |path|
77
+ metaclass.send(:define_method, path.to_sym) do |*attrs|
78
+ send(method, path, attrs.first || Hash.new)
79
+ end
80
+ end
265
81
  end
266
82
  end
267
83
  end