Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 273 lines (232 sloc) 9.792 kb
e8550ee @jeremy Cherry-pick core extensions
jeremy authored
1 require 'active_support/core_ext/enumerable'
1a421ce @jonleighton Deprecate using method_missing for attributes that are columns.
jonleighton authored
2 require 'active_support/deprecation'
e8550ee @jeremy Cherry-pick core extensions
jeremy authored
3
2b3cc24 @jeremy r4854@ks: jeremy | 2006-07-30 00:59:18 -0700
jeremy authored
4 module ActiveRecord
4ad6103 @rizwanreza Adds title and basic description where needed.
rizwanreza authored
5 # = Active Record Attribute Methods
2b3cc24 @jeremy r4854@ks: jeremy | 2006-07-30 00:59:18 -0700
jeremy authored
6 module AttributeMethods #:nodoc:
4e50a35 @josh Break up DependencyModule's dual function of providing a "depend_on" DSL...
josh authored
7 extend ActiveSupport::Concern
f8d3c72 @josh Extract generic attribute method generation to AMo
josh authored
8 include ActiveModel::AttributeMethods
a2875be @brynary Use DependencyModule for included hooks in ActiveRecord
brynary authored
9
17ad71e @jonleighton Let AttributeMethods do its own including etc
jonleighton authored
10 included do
11 include Read
12 include Write
13 include BeforeTypeCast
14 include Query
15 include PrimaryKey
16 include TimeZoneConversion
17 include Dirty
18 include Serialization
19 include DeprecatedUnderscoreRead
20
21 # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
22 # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
23 # (Alias for the protected read_attribute method).
bb78804 @spastorino Merge pull request #4408 from tomstuart/read-and-write-attribute-aliases
spastorino authored
24 def [](attr_name)
25 read_attribute(attr_name)
26 end
17ad71e @jonleighton Let AttributeMethods do its own including etc
jonleighton authored
27
28 # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
29 # (Alias for the protected write_attribute method).
bb78804 @spastorino Merge pull request #4408 from tomstuart/read-and-write-attribute-aliases
spastorino authored
30 def []=(attr_name, value)
31 write_attribute(attr_name, value)
32 end
17ad71e @jonleighton Let AttributeMethods do its own including etc
jonleighton authored
33 end
34
2b3cc24 @jeremy r4854@ks: jeremy | 2006-07-30 00:59:18 -0700
jeremy authored
35 module ClassMethods
46f30f9 @lifo Merge documentation changes from docrails.
lifo authored
36 # Generates all the attribute related methods for columns in the database
37 # accessors, mutators and query methods.
5b801b5 @NZKoz Change the implementation of ActiveRecord's attribute reader and writer ...
NZKoz authored
38 def define_attribute_methods
0bfc504 @jonleighton Add workaround and deprecation if the inherited hook is not executed. Cl...
jonleighton authored
39 unless defined?(@attribute_methods_mutex)
e1de540 @jonleighton Improve deprecation message
jonleighton authored
40 msg = "It looks like something (probably a gem/plugin) is overriding the " \
41 "ActiveRecord::Base.inherited method. It is important that this hook executes so " \
42 "that your models are set up correctly. A workaround has been added to stop this " \
43 "causing an error in 3.2, but future versions will simply not work if the hook is " \
44 "overridden. If you are using Kaminari, please upgrade as it is known to have had " \
45 "this problem.\n\n"
46 msg << "The following may help track down the problem:"
47
48 meth = method(:inherited)
49 if meth.respond_to?(:source_location)
50 msg << " #{meth.source_location.inspect}"
51 else
52 msg << " #{meth.inspect}"
53 end
54 msg << "\n\n"
55
56 ActiveSupport::Deprecation.warn(msg)
0bfc504 @jonleighton Add workaround and deprecation if the inherited hook is not executed. Cl...
jonleighton authored
57
58 @attribute_methods_mutex = Mutex.new
59 end
60
70b762d @jonleighton Fix race condition :bomb:
jonleighton authored
61 # Use a mutex; we don't want two thread simaltaneously trying to define
62 # attribute methods.
63 @attribute_methods_mutex.synchronize do
64 return if attribute_methods_generated?
65 superclass.define_attribute_methods unless self == base_class
66 super(column_names)
67 @attribute_methods_generated = true
68 end
24faddd @wildchild Move ActiveModel::AttributeMethods#attribute_methods_generated? to Activ...
wildchild authored
69 end
70
71 def attribute_methods_generated?
7bb754e @jonleighton Fix #4046.
jonleighton authored
72 @attribute_methods_generated ||= false
f1a534a @jonleighton Remove the need for type_cast_attribute.
jonleighton authored
73 end
74
7bb754e @jonleighton Fix #4046.
jonleighton authored
75 # We will define the methods as instance methods, but will call them as singleton
76 # methods. This allows us to use method_defined? to check if the method exists,
77 # which is fast and won't give any false positives from the ancestors (because
78 # there are no ancestors).
bb44e5a @jonleighton Use a separate module for 'external' attribute methods.
jonleighton authored
79 def generated_external_attribute_methods
7bb754e @jonleighton Fix #4046.
jonleighton authored
80 @generated_external_attribute_methods ||= Module.new { extend self }
bb44e5a @jonleighton Use a separate module for 'external' attribute methods.
jonleighton authored
81 end
82
365e10b @jonleighton Remove unnecessary *args
jonleighton authored
83 def undefine_attribute_methods
7bb754e @jonleighton Fix #4046.
jonleighton authored
84 super
85 @attribute_methods_generated = false
e129c56 @josh Wrap up attribute method reset concerns in 'undefine_attribute_methods'
josh authored
86 end
87
b31aa63 Allow column accessors to be created even if Kernel. or Object# methods ...
Tobias Lütke authored
88 def instance_method_already_implemented?(method_name)
55da28d @jonleighton We don't need to build a set for DangerousAttributeError.
jonleighton authored
89 if dangerous_attribute_method?(method_name)
90 raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord"
91 end
92
7bb754e @jonleighton Fix #4046.
jonleighton authored
93 if superclass == Base
94 super
95 else
0a2e379 @jonleighton Fix situation where id method didn't get defined causing postgres to fai...
jonleighton authored
96 # If B < A and A defines its own attribute method, then we don't want to overwrite that.
97 defined = method_defined_within?(method_name, superclass, superclass.generated_attribute_methods)
98 defined && !ActiveRecord::Base.method_defined?(method_name) || super
7bb754e @jonleighton Fix #4046.
jonleighton authored
99 end
b31aa63 Allow column accessors to be created even if Kernel. or Object# methods ...
Tobias Lütke authored
100 end
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
101
55da28d @jonleighton We don't need to build a set for DangerousAttributeError.
jonleighton authored
102 # A method name is 'dangerous' if it is already defined by Active Record, but
103 # not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
7bb754e @jonleighton Fix #4046.
jonleighton authored
104 def dangerous_attribute_method?(name)
105 method_defined_within?(name, Base)
106 end
55da28d @jonleighton We don't need to build a set for DangerousAttributeError.
jonleighton authored
107
7bb754e @jonleighton Fix #4046.
jonleighton authored
108 def method_defined_within?(name, klass, sup = klass.superclass)
109 if klass.method_defined?(name) || klass.private_method_defined?(name)
110 if sup.method_defined?(name) || sup.private_method_defined?(name)
111 klass.instance_method(name).owner != sup.instance_method(name).owner
112 else
113 true
114 end
115 else
116 false
117 end
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
118 end
ceb33f8 @jonleighton Split out most of the AR::Base code into separate modules :cake:
jonleighton authored
119
120 def attribute_method?(attribute)
121 super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
122 end
123
124 # Returns an array of column names as strings if it's not
125 # an abstract class and table exists.
126 # Otherwise it returns an empty array.
127 def attribute_names
128 @attribute_names ||= if !abstract_class? && table_exists?
129 column_names
130 else
131 []
132 end
133 end
c30a0ce @paulgillard Modified ActiveRecord::AttributeMethods to allow classes to specify attr...
paulgillard authored
134 end
64eecdd @josh whitespace
josh authored
135
ac687ed @jonleighton Let Ruby deal with method visibility.
jonleighton authored
136 # If we haven't generated any methods yet, generate them, then
137 # see if we've created the method we're looking for.
138 def method_missing(method, *args, &block)
139 unless self.class.attribute_methods_generated?
5b801b5 @NZKoz Change the implementation of ActiveRecord's attribute reader and writer ...
NZKoz authored
140 self.class.define_attribute_methods
ac687ed @jonleighton Let Ruby deal with method visibility.
jonleighton authored
141
142 if respond_to_without_attributes?(method)
143 send(method, *args, &block)
144 else
145 super
146 end
364a8f3 @jeremy No need to check for generated method, just redispatch
jeremy authored
147 else
148 super
5b801b5 @NZKoz Change the implementation of ActiveRecord's attribute reader and writer ...
NZKoz authored
149 end
150 end
151
1a421ce @jonleighton Deprecate using method_missing for attributes that are columns.
jonleighton authored
152 def attribute_missing(match, *args, &block)
153 if self.class.columns_hash[match.attr_name]
154 ActiveSupport::Deprecation.warn(
155 "The method `#{match.method_name}', matching the attribute `#{match.attr_name}' has " \
156 "dispatched through method_missing. This shouldn't happen, because `#{match.attr_name}' " \
157 "is a column of the table. If this error has happened through normal usage of Active " \
158 "Record (rather than through your own code or external libraries), please report it as " \
159 "a bug."
160 )
161 end
162
163 super
164 end
165
6a283d5 @tenderlove match method signature of the superclass
tenderlove authored
166 def respond_to?(name, include_private = false)
e0e3adf @jeremy Cheaper attribute reads and respond_to?. Add underscore-prefixed method ...
jeremy authored
167 self.class.define_attribute_methods unless self.class.attribute_methods_generated?
5b801b5 @NZKoz Change the implementation of ActiveRecord's attribute reader and writer ...
NZKoz authored
168 super
169 end
2b3cc24 @jeremy r4854@ks: jeremy | 2006-07-30 00:59:18 -0700
jeremy authored
170
ceb33f8 @jonleighton Split out most of the AR::Base code into separate modules :cake:
jonleighton authored
171 # Returns true if the given attribute is in the attributes hash
172 def has_attribute?(attr_name)
173 @attributes.has_key?(attr_name.to_s)
174 end
175
176 # Returns an array of names for the attributes available on this object.
177 def attribute_names
178 @attributes.keys
179 end
180
181 # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
182 def attributes
183 Hash[@attributes.map { |name, _| [name, read_attribute(name)] }]
184 end
185
186 # Returns an <tt>#inspect</tt>-like string for the value of the
187 # attribute +attr_name+. String attributes are truncated upto 50
188 # characters, and Date and Time attributes are returned in the
189 # <tt>:db</tt> format. Other attributes return the value of
190 # <tt>#inspect</tt> without modification.
191 #
192 # person = Person.create!(:name => "David Heinemeier Hansson " * 3)
193 #
194 # person.attribute_for_inspect(:name)
195 # # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
196 #
197 # person.attribute_for_inspect(:created_at)
198 # # => '"2009-01-12 04:48:57"'
199 def attribute_for_inspect(attr_name)
200 value = read_attribute(attr_name)
201
202 if value.is_a?(String) && value.length > 50
203 "#{value[0..50]}...".inspect
204 elsif value.is_a?(Date) || value.is_a?(Time)
205 %("#{value.to_s(:db)}")
206 else
207 value.inspect
208 end
209 end
210
211 # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
212 # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
213 def attribute_present?(attribute)
214 value = read_attribute(attribute)
524e8a1 @josevalim Merge pull request #5316 from Jacobkg/master
josevalim authored
215 !value.nil? && !(value.respond_to?(:empty?) && value.empty?)
ceb33f8 @jonleighton Split out most of the AR::Base code into separate modules :cake:
jonleighton authored
216 end
217
218 # Returns the column object for the named attribute.
219 def column_for_attribute(name)
220 self.class.columns_hash[name.to_s]
221 end
222
f8d3c72 @josh Extract generic attribute method generation to AMo
josh authored
223 protected
ceb33f8 @jonleighton Split out most of the AR::Base code into separate modules :cake:
jonleighton authored
224
225 def clone_attributes(reader_method = :read_attribute, attributes = {})
226 attribute_names.each do |name|
227 attributes[name] = clone_attribute_value(reader_method, name)
228 end
229 attributes
230 end
231
232 def clone_attribute_value(reader_method, attribute_name)
233 value = send(reader_method, attribute_name)
234 value.duplicable? ? value.clone : value
235 rescue TypeError, NoMethodError
236 value
237 end
238
239 # Returns a copy of the attributes hash where all the values have been safely quoted for use in
240 # an Arel insert/update method.
241 def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
242 attrs = {}
243 klass = self.class
244 arel_table = klass.arel_table
245
246 attribute_names.each do |name|
247 if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
248
249 if include_readonly_attributes || !self.class.readonly_attributes.include?(name)
250
251 value = if klass.serialized_attributes.include?(name)
252 @attributes[name].serialized_value
253 else
254 # FIXME: we need @attributes to be used consistently.
255 # If the values stored in @attributes were already type
256 # casted, this code could be simplified
257 read_attribute(name)
258 end
259
260 attrs[arel_table[name]] = value
261 end
262 end
5b801b5 @NZKoz Change the implementation of ActiveRecord's attribute reader and writer ...
NZKoz authored
263 end
ceb33f8 @jonleighton Split out most of the AR::Base code into separate modules :cake:
jonleighton authored
264
265 attrs
266 end
267
268 def attribute_method?(attr_name)
269 attr_name == 'id' || (defined?(@attributes) && @attributes.include?(attr_name))
270 end
2b3cc24 @jeremy r4854@ks: jeremy | 2006-07-30 00:59:18 -0700
jeremy authored
271 end
272 end
Something went wrong with that request. Please try again.