her 0.6.2 → 0.6.3

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.
@@ -12,20 +12,59 @@ module Her
12
12
  }.merge(attrs)
13
13
  klass.associations[:has_one] << attrs
14
14
 
15
- klass.instance_eval do
16
- define_method(name) do
15
+ klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
16
+ def #{name}
17
17
  cached_name = :"@_her_association_#{name}"
18
18
 
19
19
  cached_data = (instance_variable_defined?(cached_name) && instance_variable_get(cached_name))
20
- cached_data || instance_variable_set(cached_name, Her::Model::Associations::HasOneAssociation.new(self, attrs))
20
+ cached_data || instance_variable_set(cached_name, Her::Model::Associations::HasOneAssociation.new(self, #{attrs.inspect}))
21
21
  end
22
- end
22
+ RUBY
23
+ end
24
+
25
+ # @private
26
+ def self.parse(association, klass, data)
27
+ data_key = association[:data_key]
28
+ return {} unless data[data_key]
29
+
30
+ klass = klass.her_nearby_class(association[:class_name])
31
+ { association[:name] => klass.new(data[data_key]) }
23
32
  end
24
33
 
34
+ # Initialize a new object with a foreign key to the parent
35
+ #
36
+ # @example
37
+ # class User
38
+ # include Her::Model
39
+ # has_one :role
40
+ # end
41
+ #
42
+ # class Role
43
+ # include Her::Model
44
+ # end
45
+ #
46
+ # user = User.find(1)
47
+ # new_role = user.role.build(:title => "moderator")
48
+ # new_role # => #<Role user_id=1 title="moderator">
25
49
  def build(attributes = {})
26
50
  @klass.new(attributes.merge(:"#{@parent.singularized_resource_name}_id" => @parent.id))
27
51
  end
28
52
 
53
+ # Create a new object, save it and associate it to the parent
54
+ #
55
+ # @example
56
+ # class User
57
+ # include Her::Model
58
+ # has_one :role
59
+ # end
60
+ #
61
+ # class Role
62
+ # include Her::Model
63
+ # end
64
+ #
65
+ # user = User.find(1)
66
+ # user.role.create(:title => "moderator")
67
+ # user.role # => #<Role id=2 user_id=1 title="moderator">
29
68
  def create(attributes = {})
30
69
  resource = build(attributes)
31
70
  @parent.attributes[@name] = resource if resource.save
@@ -48,6 +87,15 @@ module Her
48
87
  @parent.attributes[@name]
49
88
  end
50
89
  end
90
+
91
+ # @private
92
+ def assign_nested_attributes(attributes)
93
+ if @parent.attributes[@name].blank?
94
+ @parent.attributes[@name] = @klass.new(@klass.parse(attributes))
95
+ else
96
+ @parent.attributes[@name].assign_attributes(attributes)
97
+ end
98
+ end
51
99
  end
52
100
  end
53
101
  end
@@ -3,22 +3,33 @@ module Her
3
3
  # This module handles all methods related to model attributes
4
4
  module Attributes
5
5
  extend ActiveSupport::Concern
6
-
7
6
  attr_accessor :attributes
8
- alias :data :attributes
9
- alias :data= :attributes=
10
7
 
11
8
  # Initialize a new object with data
9
+ #
10
+ # @param [Hash] attributes The attributes to initialize the object with
11
+ # @option attributes [Hash,Array] :_metadata
12
+ # @option attributes [Hash,Array] :_errors
13
+ # @option attributes [Boolean] :_destroyed
14
+ #
15
+ # @example
16
+ # class User
17
+ # include Her::Model
18
+ # end
19
+ #
20
+ # User.new(name: "Tobias") # => #<User name="Tobias">
12
21
  def initialize(attributes={})
13
22
  attributes ||= {}
14
23
  @metadata = attributes.delete(:_metadata) || {}
15
24
  @response_errors = attributes.delete(:_errors) || {}
16
25
  @destroyed = attributes.delete(:_destroyed) || false
17
26
 
27
+ attributes = self.class.default_scope.apply_to(attributes)
18
28
  assign_attributes(attributes)
19
29
  end
20
30
 
21
31
  # Initialize a collection of resources
32
+ #
22
33
  # @private
23
34
  def self.initialize_collection(klass, parsed_data={})
24
35
  collection_data = parsed_data[:data].map do |item_data|
@@ -31,6 +42,7 @@ module Her
31
42
 
32
43
  # Use setter methods of model for each key / value pair in params
33
44
  # Return key / value pairs for which no setter method was defined on the model
45
+ #
34
46
  # @private
35
47
  def self.use_setter_methods(model, params)
36
48
  setter_method_names = model.class.setter_method_names
@@ -48,6 +60,7 @@ module Her
48
60
  end
49
61
 
50
62
  # Handles missing methods
63
+ #
51
64
  # @private
52
65
  def method_missing(method, *args, &blk)
53
66
  if method.to_s =~ /[?=]$/ || attributes.include?(method)
@@ -74,8 +87,16 @@ module Her
74
87
  method.to_s.end_with?('=') || method.to_s.end_with?('?') || @attributes.include?(method) || @attributes.include?(method) || super
75
88
  end
76
89
 
77
- # Assign new attributes
78
- # @private
90
+ # Assign new attributes to a resource
91
+ #
92
+ # @example
93
+ # class User
94
+ # include Her::Model
95
+ # end
96
+ #
97
+ # user = User.find(1) # => #<User id=1 name="Tobias">
98
+ # user.assign_attributes(name: "Lindsay")
99
+ # user.changes # => { :name => ["Tobias", "Lindsay"] }
79
100
  def assign_attributes(raw_data)
80
101
  @attributes ||= {}
81
102
  # Use setter methods first, then translate attributes of associations
@@ -84,36 +105,37 @@ module Her
84
105
  parsed_attributes = self.class.parse_associations(unset_attributes)
85
106
  attributes.update(parsed_attributes)
86
107
  end
87
- alias :update_attributes :assign_attributes
88
- alias :assign_data :assign_attributes
89
108
 
90
109
  # Handles returning true for the accessible attributes
110
+ #
91
111
  # @private
92
112
  def has_attribute?(attribute_name)
93
113
  attributes.include?(attribute_name)
94
114
  end
95
- alias :has_data? :has_attribute?
96
115
 
97
116
  # Handles returning data for a specific attribute
117
+ #
98
118
  # @private
99
119
  def get_attribute(attribute_name)
100
120
  attributes[attribute_name]
101
121
  end
102
- alias :get_data :get_attribute
103
122
 
104
123
  # Override the method to prevent from returning the object ID
124
+ #
105
125
  # @private
106
126
  def id
107
127
  attributes[self.class.primary_key]
108
128
  end
109
129
 
110
130
  # Return `true` if the other object is also a Her::Model and has matching data
131
+ #
111
132
  # @private
112
133
  def ==(other)
113
134
  other.is_a?(Her::Model) && attributes == other.attributes
114
135
  end
115
136
 
116
137
  # Delegate to the == method
138
+ #
117
139
  # @private
118
140
  def eql?(other)
119
141
  self == other
@@ -137,28 +159,44 @@ module Her
137
159
  # Define the attributes that will be used to track dirty attributes and validations
138
160
  #
139
161
  # @param [Array] attributes
162
+ #
163
+ # @example
164
+ # class User
165
+ # include Her::Model
166
+ # attributes :name, :email
167
+ # end
140
168
  def attributes(*attributes)
141
169
  define_attribute_methods attributes
142
170
 
143
171
  attributes.each do |attribute|
144
172
  attribute = attribute.to_sym
145
173
 
146
- define_method attribute do
147
- @attributes.include?(attribute) ? @attributes[attribute] : nil
148
- end
174
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
175
+ def #{attribute}
176
+ @attributes.include?(:'#{attribute}') ? @attributes[:'#{attribute}'] : nil
177
+ end
149
178
 
150
- define_method :"#{attribute}=" do |value|
151
- self.send(:"#{attribute}_will_change!") if @attributes[attribute] != value
152
- @attributes[attribute] = value
153
- end
179
+ def #{attribute}=(value)
180
+ self.send(:"#{attribute}_will_change!") if @attributes[:'#{attribute}'] != value
181
+ @attributes[:'#{attribute}'] = value
182
+ end
154
183
 
155
- define_method :"#{attribute}?" do
156
- @attributes.include?(attribute) && @attributes[attribute].present?
157
- end
184
+ def #{attribute}?
185
+ @attributes.include?(:'#{attribute}') && @attributes[:'#{attribute}'].present?
186
+ end
187
+ RUBY
158
188
  end
159
189
  end
160
190
 
161
191
  # Define the accessor in which the API response errors (obtained from the parsing middleware) will be stored
192
+ #
193
+ # @param [Symbol] store_response_errors
194
+ #
195
+ # @example
196
+ # class User
197
+ # include Her::Model
198
+ # store_response_errors :server_errors
199
+ # end
162
200
  def store_response_errors(value = nil)
163
201
  if @_her_store_response_errors
164
202
  remove_method @_her_store_response_errors
@@ -172,11 +210,21 @@ module Her
172
210
  return @_her_store_response_errors unless value
173
211
  @_her_store_response_errors = value
174
212
 
175
- define_method(@_her_store_response_errors) { @response_errors }
176
- define_method(:"#{@_her_store_response_errors}=") { |value| @response_errors = value }
213
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
214
+ def #{@_her_store_response_errors}; @response_errors; end
215
+ def #{@_her_store_response_errors}=(value); @response_errors = value; end
216
+ RUBY
177
217
  end
178
218
 
179
219
  # Define the accessor in which the API response metadata (obtained from the parsing middleware) will be stored
220
+ #
221
+ # @param [Symbol] store_metadata
222
+ #
223
+ # @example
224
+ # class User
225
+ # include Her::Model
226
+ # store_metadata :server_data
227
+ # end
180
228
  def store_metadata(value = nil)
181
229
  if @_her_store_metadata
182
230
  remove_method @_her_store_metadata
@@ -190,8 +238,10 @@ module Her
190
238
  return @_her_store_metadata unless value
191
239
  @_her_store_metadata = value
192
240
 
193
- define_method(@_her_store_metadata) { @metadata }
194
- define_method(:"#{@_her_store_metadata}=") { |value| @metadata = value }
241
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
242
+ def #{@_her_store_metadata}; @metadata; end
243
+ def #{@_her_store_metadata}=(value); @metadata = value; end
244
+ RUBY
195
245
  end
196
246
 
197
247
  # @private
@@ -7,6 +7,8 @@ module Her
7
7
  # Returns true if attribute_name is
8
8
  # * in resource attributes
9
9
  # * an association
10
+ #
11
+ # @private
10
12
  def has_key?(attribute_name)
11
13
  has_attribute?(attribute_name) ||
12
14
  has_association?(attribute_name)
@@ -15,6 +17,8 @@ module Her
15
17
  # Returns
16
18
  # * the value of the attribute_name attribute if it's in orm data
17
19
  # * the resource/collection corrsponding to attribute_name if it's an association
20
+ #
21
+ # @private
18
22
  def [](attribute_name)
19
23
  get_attribute(attribute_name) ||
20
24
  get_association(attribute_name)
@@ -0,0 +1,61 @@
1
+ module Her
2
+ module Model
3
+ # @private
4
+ module DeprecatedMethods
5
+ extend ActiveSupport::Concern
6
+
7
+ def self.deprecate!(old, new, object, *args)
8
+ line = begin
9
+ raise StandardError
10
+ rescue StandardError => e
11
+ e.backtrace[2]
12
+ end
13
+
14
+ warn "#{line} - The `#{old}` method is deprecated and may be removed soon. Please update your code with `#{new}` instead."
15
+ object.send(new, *args)
16
+ end
17
+
18
+ def data(*args)
19
+ Her::Model::DeprecatedMethods.deprecate! :data, :attributes, self, *args
20
+ end
21
+
22
+ def data=(*args)
23
+ Her::Model::DeprecatedMethods.deprecate! :data=, :attributes=, self, *args
24
+ end
25
+
26
+ def update_attributes(*args)
27
+ Her::Model::DeprecatedMethods.deprecate! :update_attributes, :assign_attributes, self, *args
28
+ end
29
+
30
+ def assign_data(*args)
31
+ Her::Model::DeprecatedMethods.deprecate! :assign_data, :assign_attributes, self, *args
32
+ end
33
+
34
+ def has_data?(*args)
35
+ Her::Model::DeprecatedMethods.deprecate! :has_data?, :has_attribute?, self, *args
36
+ end
37
+
38
+ def get_data(*args)
39
+ Her::Model::DeprecatedMethods.deprecate! :get_data, :get_attribute, self, *args
40
+ end
41
+
42
+ module ClassMethods
43
+ def has_relationship?(*args)
44
+ Her::Model::DeprecatedMethods.deprecate! :has_relationship?, :has_association?, self, *args
45
+ end
46
+
47
+ def get_relationship(*args)
48
+ Her::Model::DeprecatedMethods.deprecate! :get_relationship, :get_association, self, *args
49
+ end
50
+
51
+ def relationships(*args)
52
+ Her::Model::DeprecatedMethods.deprecate! :relationships, :associations, self, *args
53
+ end
54
+
55
+ def her_api(*args)
56
+ Her::Model::DeprecatedMethods.deprecate! :her_api, :use_api, self, *args
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -7,6 +7,17 @@ module Her
7
7
 
8
8
  module ClassMethods
9
9
  # Change which API the model will use to make its HTTP requests
10
+ #
11
+ # @example
12
+ # secondary_api = Her::API.new :url => "https://api.example" do |connection|
13
+ # connection.use Faraday::Request::UrlEncoded
14
+ # connection.use Her::Middleware::DefaultParseJSON
15
+ # end
16
+ #
17
+ # class User
18
+ # include Her::Model
19
+ # use_api secondary_api
20
+ # end
10
21
  def use_api(value = nil)
11
22
  @_her_use_api ||= begin
12
23
  superclass.use_api if superclass.respond_to?(:use_api)
@@ -15,10 +26,11 @@ module Her
15
26
  return @_her_use_api unless value
16
27
  @_her_use_api = value
17
28
  end
18
- alias :her_api :use_api
19
- alias :uses_api :use_api
29
+ alias her_api use_api
30
+ alias uses_api use_api
20
31
 
21
32
  # Main request wrapper around Her::API. Used to make custom request to the API.
33
+ #
22
34
  # @private
23
35
  def request(attrs={})
24
36
  request = her_api.request(attrs)
@@ -38,52 +50,49 @@ module Her
38
50
  # - <method>_resource(path, attrs, &block)
39
51
  # - custom_<method>(path, attrs)
40
52
  METHODS.each do |method|
41
- define_method method do |path, *attrs|
42
- attrs = attrs.first || {}
43
- path = build_request_path_from_string_or_symbol(path, attrs)
44
- send(:"#{method}_raw", path, attrs) do |parsed_data, response|
45
- if parsed_data[:data].is_a?(Array)
46
- new_collection(parsed_data)
47
- else
48
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
53
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
54
+ def #{method}(path, attrs={})
55
+ path = build_request_path_from_string_or_symbol(path, attrs)
56
+ send(:'#{method}_raw', path, attrs) do |parsed_data, response|
57
+ if parsed_data[:data].is_a?(Array)
58
+ new_collection(parsed_data)
59
+ else
60
+ new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
61
+ end
49
62
  end
50
63
  end
51
- end
52
64
 
53
- define_method :"#{method}_raw" do |path, *attrs, &block|
54
- attrs = attrs.first || {}
55
- path = build_request_path_from_string_or_symbol(path, attrs)
56
- request(attrs.merge(:_method => method, :_path => path), &block)
57
- end
65
+ def #{method}_raw(path, attrs={}, &block)
66
+ path = build_request_path_from_string_or_symbol(path, attrs)
67
+ request(attrs.merge(:_method => #{method.to_sym.inspect}, :_path => path), &block)
68
+ end
58
69
 
59
- define_method :"#{method}_collection" do |*args|
60
- path = args.first
61
- attrs = args[1] || {}
62
- path = build_request_path_from_string_or_symbol(path, attrs)
63
- send(:"#{method}_raw", build_request_path_from_string_or_symbol(path, attrs), attrs) do |parsed_data, response|
64
- new_collection(parsed_data)
70
+ def #{method}_collection(path, attrs={})
71
+ path = build_request_path_from_string_or_symbol(path, attrs)
72
+ send(:'#{method}_raw', build_request_path_from_string_or_symbol(path, attrs), attrs) do |parsed_data, response|
73
+ new_collection(parsed_data)
74
+ end
65
75
  end
66
- end
67
76
 
68
- define_method :"#{method}_resource" do |path, *attrs|
69
- attrs = attrs.first || {}
70
- path = build_request_path_from_string_or_symbol(path, attrs)
71
- send(:"#{method}_raw", path, attrs) do |parsed_data, response|
72
- new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
77
+ def #{method}_resource(path, attrs={})
78
+ path = build_request_path_from_string_or_symbol(path, attrs)
79
+ send(:"#{method}_raw", path, attrs) do |parsed_data, response|
80
+ new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
81
+ end
73
82
  end
74
- end
75
83
 
76
- define_method :"custom_#{method}" do |*paths|
77
- metaclass = (class << self; self; end)
78
- opts = paths.last.is_a?(Hash) ? paths.pop : Hash.new
84
+ def custom_#{method}(*paths)
85
+ metaclass = (class << self; self; end)
86
+ opts = paths.last.is_a?(Hash) ? paths.pop : Hash.new
79
87
 
80
- paths.each do |path|
81
- metaclass.send(:define_method, path) do |*attrs|
82
- attrs = attrs.first || Hash.new
83
- send(method, path, attrs)
88
+ paths.each do |path|
89
+ metaclass.send(:define_method, path) do |*attrs|
90
+ attrs = attrs.first || Hash.new
91
+ send(#{method.to_sym.inspect}, path, attrs)
92
+ end
84
93
  end
85
94
  end
86
- end
95
+ RUBY
87
96
  end
88
97
  end
89
98
  end