her 0.6.2 → 0.6.3

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