Active Record objects doesn’t specify their attributes directly, but rather infer them from the table definition with which they’re linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.
See the mapping rules in table_name and the full example in ../../files/README.html for more insight.
Active Records accepts constructor parameters either in a hash or as a block. The hash method is especially useful when you’re receiving the data from somewhere else, like a HTTP request. It works like this:
user = User.new("name" => "David", "occupation" => "Code Artist")
user.name # => "David"
You can also use block initialization:
user = User.new do |u|
u.name = "David"
u.occupation = "Code Artist"
end
And of course you can just create a bare object and specify the attributes after the fact:
user = User.new user.name = "David" user.occupation = "Code Artist"
Conditions can either be specified as a string or an array representing the WHERE-part of an SQL statement. The array form is to be used when the condition input is tainted and requires sanitization. The string form can be used for statements that doesn’t involve tainted data. Examples:
User < ActiveRecord::Base
def authenticate_unsafely(user_name, password)
find_first("user_name = '#{user_name}' AND password = '#{password}'")
end
def authenticate_safely(user_name, password)
find_first([ "user_name = '%s' AND password = '%s'", user_name, password ])
end
end
The authenticate_unsafely method inserts the parameters directly into the query and is thus susceptible to SQL-injection attacks if the user_name and password parameters come directly from a HTTP request. The authenticate_safely method, on the other hand, will sanitize the user_name and password before inserting them in the query, which will ensure that an attacker can’t escape the query and fake the login (or worse).
All column values are automatically available through basic accessors on the Active Record object, but some times you want to specialize this behavior. This can be done by either by overwriting the default accessors (using the same name as the attribute) calling read_attribute(attr_name) and write_attribute(attr_name, value) to actually change things. Example:
class Song < ActiveRecord::Base
# Uses an integer of seconds to hold the length of the song
def length=(minutes)
write_attribute("length", minutes * 60)
end
def length
read_attribute("length") / 60
end
end
Active Record will automatically attempt to serialize (using YAML) any object in text columns that isn’t either a String, NilClass, TrueClass, FalseClass, Fixnum, Date, or Time. This makes it possible to store arrays, hashes, and other non-mappeable objects without doing any additional work. Example:
user = User.find(1)
user.preferences = { "background" => "black", "display" => large }
user.save
User.find(1).preferences # => { "background" => "black", "display" => large }
Note: The attributes listed are class-level attributes (accessible from both the class and instance level). So it’s possible to assign a logger to the class through Base.logger= which will then be used by all instances in the current object space.
# File vendor/activerecord/lib/active_record/base.rb, line 134
134: def self.connected?
135: !Thread.current['connection'].nil?
136: end
Returns objects for the records responding to either a specific id (1), a list of ids (1, 5, 6) or an array of ids. If only one ID is specified, that object is returned directly. If more than one ID is specified, an array is returned. Examples:
Person.find(1) # returns the object for ID = 1 Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6) Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
RecordNotFound is raised if no record can be found.
# File vendor/activerecord/lib/active_record/base.rb, line 170
170: def find(*ids)
171: ids = [ ids ].flatten.compact
172:
173: if ids.length > 1
174: ids_list = ids.map{ |id| "'#{id}'" }.join(", ")
175: objects = find_all("#{primary_key} IN (#{ids_list})", primary_key)
176:
177: if objects.length == ids.length
178: return objects
179: else
180: raise RecordNotFound, "Couldn't find #{name} with ID in (#{ids_list})"
181: end
182: elsif ids.length == 1
183: id = ids.first
184: sql = "SELECT * FROM #{table_name} WHERE #{primary_key} = '#{id}'"
185: sql << "AND type = '#{name.gsub(/.*::/, '')}'" unless descents_from_active_record?
186:
187: if record = connection.select_one(sql, "#{name} Find")
188: instantiate(record)
189: else
190: raise RecordNotFound, "Couldn't find #{name} with ID = #{id}"
191: end
192: else
193: raise RecordNotFound, "Couldn't find #{name} without an ID"
194: end
195: end
Works like find, but the record matching id must also meet the conditions. RecordNotFound is raised if no record can be found matching the id or meeting the condition. Example:
Person.find_on_conditions 5, "first_name LIKE '%dav%' AND last_name = 'heinemeier'"
# File vendor/activerecord/lib/active_record/base.rb, line 201
201: def find_on_conditions(id, conditions)
202: find_first("#{primary_key} = '#{id}' AND #{sanitize_conditions(conditions)}") ||
203: raise(RecordNotFound, "Couldn't find #{name} with #{primary_key} = #{id} on the condition of #{conditions}")
204: end
Returns an array of all the objects that could be instantiated from the associated table in the database. The conditions can be used to narrow the selection of objects (WHERE-part), such as by "color = ‘red’", and arrangement of the selection can be done through orderings (ORDER BY-part), such as by "last_name, first_name DESC". A maximum of returned objects can be specified in limit. Example:
Project.find_all "category = 'accounts'", "last_accessed DESC", 15
# File vendor/activerecord/lib/active_record/base.rb, line 211
211: def find_all(conditions = nil, orderings = nil, limit = nil, joins = nil)
212: sql = "SELECT * FROM #{table_name} "
213: sql << "#{joins} " if joins
214: add_conditions!(sql, conditions)
215: sql << "ORDER BY #{orderings} " unless orderings.nil?
216: sql << "LIMIT #{limit} " unless limit.nil?
217:
218: find_by_sql(sql)
219: end
Works like find_all, but requires a complete SQL string. Example:
Post.find_by_sql "SELECT p.*, c.author FROM posts p, comments c WHERE p.id = c.post_id"
# File vendor/activerecord/lib/active_record/base.rb, line 223
223: def find_by_sql(sql)
224: connection.select_all(sql, "#{name} Load").inject([]) { |objects, record| objects << instantiate(record) }
225: end
Returns the object for the first record responding to the conditions in conditions, such as "group = ‘master’". If more than one record is returned from the query, it’s the first that’ll be used to create the object. In such cases, it might be beneficial to also specify orderings, like "income DESC, name", to control exactly which record is to be used. Example:
Employee.find_first "income > 50000", "income DESC, name"
# File vendor/activerecord/lib/active_record/base.rb, line 232
232: def find_first(conditions = nil, orderings = nil)
233: sql = "SELECT * FROM #{table_name} "
234: add_conditions!(sql, conditions)
235: sql << "ORDER BY #{orderings} " unless orderings.nil?
236: sql << "LIMIT 1"
237:
238: record = connection.select_one(sql, "#{name} Load First")
239: instantiate(record) unless record.nil?
240: end
Creates an object, instantly saves it as a record (if the validation permits it), and returns it. If the save fail under validations, the unsaved object is still returned.
# File vendor/activerecord/lib/active_record/base.rb, line 244
244: def create(attributes = nil)
245: object = new(attributes)
246: object.save
247: object
248: end
Finds the record from the passed id, instantly saves it with the passed attributes (if the validation permits it), and returns it. If the save fail under validations, the unsaved object is still returned.
# File vendor/activerecord/lib/active_record/base.rb, line 252
252: def update(id, attributes)
253: object = find(id)
254: object.attributes = attributes
255: object.save
256: object
257: end
Updates all records with the SET-part of an SQL update statement in updates. A subset of the records can be selected by specifying conditions. Example:
Billing.update_all "category = 'authorized', approved = 1", "author = 'David'"
# File vendor/activerecord/lib/active_record/base.rb, line 262
262: def update_all(updates, conditions = nil)
263: sql = "UPDATE #{table_name} SET #{updates} "
264: add_conditions!(sql, conditions)
265: connection.update(sql, "#{name} Update")
266: end
Destroys the objects for all the records that matches the condition by instantiating each object and calling the destroy method. Example:
Person.destroy_all "last_login < '2004-04-04'"
# File vendor/activerecord/lib/active_record/base.rb, line 271
271: def destroy_all(conditions = nil)
272: find_all(conditions).each { |object| object.destroy }
273: end
Deletes all the records that matches the condition without instantiating the objects first (and hence not calling the destroy method). Example:
Post.destroy_all "person_id = 5 AND (category = 'Something' OR category = 'Else')"
# File vendor/activerecord/lib/active_record/base.rb, line 278
278: def delete_all(conditions = nil)
279: sql = "DELETE FROM #{table_name} "
280: add_conditions!(sql, conditions)
281: connection.delete(sql, "#{name} Delete all")
282: end
Returns the number of records that meets the conditions. Zero is returned if no records match. Example:
Product.count "sales > 1"
# File vendor/activerecord/lib/active_record/base.rb, line 286
286: def count(conditions = nil)
287: sql = "SELECT COUNT(*) FROM #{table_name} "
288: add_conditions!(sql, conditions)
289: count_by_sql(sql)
290: end
Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
Product.count "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
# File vendor/activerecord/lib/active_record/base.rb, line 294
294: def count_by_sql(sql)
295: count = connection.select_one(sql, "#{name} Count").values.first
296: return count ? count.to_i : 0
297: end
Increments the specified counter by one. So DiscussionBoard.increment_counter("post_count", discussion_board_id) would increment the "post_count" counter on the board responding to discussion_board_id. This is used for caching aggregate values, so that they doesn’t need to be computed every time. Especially important for looping over a collection where each element require a number of aggregate values. Like the DiscussionBoard that needs to list both the number of posts and comments.
# File vendor/activerecord/lib/active_record/base.rb, line 304
304: def increment_counter(counter_name, id)
305: object = find(id)
306: object.update_attribute(counter_name, object.send(counter_name) + 1)
307: end
Works like increment_counter, but decrements instead.
# File vendor/activerecord/lib/active_record/base.rb, line 310
310: def decrement_counter(counter_name, id)
311: object = find(id)
312: object.update_attribute(counter_name, object.send(counter_name) - 1)
313: end
Attributes named in this macro are protected from mass-assignment, such as new(attributes) and attributes=(attributes). Their assignment will simply be ignored. Instead, you can use the direct writer methods to do assignment. This is meant to protect sensitive attributes to be overwritten by URL/form hackers. Example:
class Customer < ActiveRecord::Base
attr_protected :credit_rating
end
customer = Customer.new("name" => David, "credit_rating" => "Excellent")
customer.credit_rating # => nil
customer.attributes = { "description" => "Jolly fellow", "credit_rating" => "Superb" }
customer.credit_rating # => nil
customer.credit_rating = "Average"
customer.credit_rating # => "Average"
# File vendor/activerecord/lib/active_record/base.rb, line 330
330: def attr_protected(*attributes)
331: write_inheritable_array("attr_protected", attributes)
332: end
If this macro is used, only those attributed named in it will be accessible for mass-assignment, such as new(attributes) and attributes=(attributes). This is the more conservative choice for mass-assignment protection. If you’d rather start from an all-open default and restrict attributes as needed, have a look at attr_protected.
# File vendor/activerecord/lib/active_record/base.rb, line 343
343: def attr_accessible(*attributes)
344: write_inheritable_array("attr_accessible", attributes)
345: end
Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending directly from ActiveRecord. So if the hierarchy looks like: Reply < Message < ActiveRecord, then Message is used to guess the table name from even when called on Reply. The guessing rules are as follows:
Additionally, the class-level table_name_prefix is prepended to the table_name and the table_name_suffix is appended. So if you have "myapp_" as a prefix, the table name guess for an Account class becomes "myapp_accounts".
You can also overwrite this class method to allow for unguessable links, such as a Person class with a link to a People table. Example:
class Person < ActiveRecord::Base
def self.table_name() "people" end
end
# File vendor/activerecord/lib/active_record/base.rb, line 368
368: def table_name(class_name = class_name_of_active_record_descendant(self))
369: table_name_prefix + undecorated_table_name(class_name) + table_name_suffix
370: end
Defines the primary key field — can be overridden in subclasses. Overwritting will negate any effect of the primary_key_prefix_type setting, though.
# File vendor/activerecord/lib/active_record/base.rb, line 374
374: def primary_key
375: case primary_key_prefix_type
376: when :table_name
377: "#{class_name_of_active_record_descendant(self).downcase}id"
378: when :table_name_with_underscore
379: "#{class_name_of_active_record_descendant(self).downcase}_id"
380: else
381: "id"
382: end
383: end
Returns an array of column objects for the table associated with this class.
# File vendor/activerecord/lib/active_record/base.rb, line 404
404: def columns
405: @columns ||= connection.columns(table_name, "#{name} Columns")
406: end
Returns an array of column objects for the table associated with this class.
# File vendor/activerecord/lib/active_record/base.rb, line 409
409: def columns_hash
410: @columns_hash ||= columns.inject({}) { |hash, column| hash[column.name] = column; hash }
411: end
Returns an array of columns objects where the primary id, all columns ending in "_id" or "_count", and columns named "type" has been removed.
# File vendor/activerecord/lib/active_record/base.rb, line 415
415: def content_columns
416: columns.reject { |c| c.name == primary_key || c.name =~ /(_id|_count)$/ || c.name == "type" }
417: end
Transforms attribute key names into a more humane format, such as "First name" instead of "first_name". Example:
Person.human_attribute_name("first_name") # => "First name"
# File vendor/activerecord/lib/active_record/base.rb, line 421
421: def human_attribute_name(attribute_key_name)
422: attribute_key_name.gsub(/_/, " ").capitalize unless attribute_key_name.nil?
423: end
New objects can be instantiated as either empty (pass no construction parameter) or pre-set with attributes but not yet saved (pass a hash with key names matching the associated table column names). In both instances, valid attribute keys are determined by the column names of the associated table — hence you can’t have attributes that aren’t part of the table columns.
# File vendor/activerecord/lib/active_record/base.rb, line 514
514: def initialize(attributes = nil)
515: @attributes = attributes_from_column_definition
516: @new_record = true
517: self.attributes = attributes unless attributes.nil?
518: yield self if block_given?
519: end
Establishes the connection to the database. Accepts a hash as input where the :adapter key must be specified with the name of a database adapter (in lower-case) example for regular databases (MySQL, Postgresql, etc):
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:host => "localhost",
:username => "myuser",
:password => "mypass",
:database => "somedatabase"
)
Example for SQLite database:
ActiveRecord::Base.establish_connection(
:adapter => "sqlite",
:dbfile => "path/to/dbfile"
)
Also accepts keys as strings (for parsing from yaml for example):
ActiveRecord::Base.establish_connection(
"adapter" => "sqlite",
"dbfile" => "path/to/dbfile"
)
The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError may be returned on an error.
# File vendor/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 33
33: def self.establish_connection(config)
34: if config.nil? then raise AdapterNotSpecified end
35: symbolize_strings_in_hash(config)
36: unless config.key?(:adapter) then raise AdapterNotSpecified end
37:
38: adapter_method = "#{config[:adapter]}_connection"
39: unless methods.include?(adapter_method) then raise AdapterNotFound end
40:
41: @@config, @@adapter_method = config, adapter_method
42: Thread.current['connection'] = nil
43: end
Converts all strings in a hash to symbols.
# File vendor/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 56
56: def self.symbolize_strings_in_hash(hash)
57: hash.each do |key, value|
58: if key.class == String
59: hash.delete key
60: hash[key.intern] = value
61: end
62: end
63: end
Every Active Record class must use "id" as their primary ID. This getter overwrites the native id method, which isn’t being used in this context.
# File vendor/activerecord/lib/active_record/base.rb, line 523
523: def id
524: read_attribute(self.class.primary_key)
525: end
Sets the primary ID.
# File vendor/activerecord/lib/active_record/base.rb, line 528
528: def id=(value)
529: write_attribute(self.class.primary_key, value)
530: end
Returns true if this object hasn’t been saved yet — that is, a record for the object doesn’t exist yet.
# File vendor/activerecord/lib/active_record/base.rb, line 533
533: def new_record?
534: @new_record
535: end
# File vendor/activerecord/lib/active_record/base.rb, line 539
539: def save
540: create_or_update
541: return true
542: end
Deletes the record in the database and freezes this instance to reflect that no changes should be made (since they can’t be persisted).
# File vendor/activerecord/lib/active_record/base.rb, line 546
546: def destroy
547: unless new_record?
548: connection.delete(
549: "DELETE FROM #{self.class.table_name} " +
550: "WHERE #{self.class.primary_key} = '#{id}'",
551: "#{self.class.name} Destroy"
552: )
553: end
554:
555: freeze
556: end
Updates a single attribute and saves the record. This is especially useful for boolean flags on existing records.
# File vendor/activerecord/lib/active_record/base.rb, line 559
559: def update_attribute(name, value)
560: self.name = value
561: save
562: end
Allows you to set all the attributes at once by passing in a hash with keys matching the attribute names (which again matches the column names). Sensitive attributes can be protected from this form of mass-assignment by using the attr_protected macro. Or you can alternatively specify which attributes can be accessed in with the attr_accessible macro. Then all the attributes not included in that won’t be allowed to be mass-assigned.
# File vendor/activerecord/lib/active_record/base.rb, line 569
569: def attributes=(attributes)
570: return if attributes.nil?
571: remove_attributes_protected_from_mass_assignment(attributes)
572:
573: multi_parameter_attributes = []
574: attributes.each do |k, v|
575: k.include?("(") ? multi_parameter_attributes << [ k, v ] : send(k + "=", v)
576: end
577: assign_multiparameter_attributes(multi_parameter_attributes)
578: end
Returns true if the specified attribute has been set by the user or by a database load and is neither nil nor empty? (the latter only applies to objects that responds to empty?, most notably Strings).
# File vendor/activerecord/lib/active_record/base.rb, line 582
582: def attribute_present?(attribute)
583: is_empty = read_attribute(attribute).respond_to?("empty?") ? read_attribute(attribute).empty? : false
584: @attributes.include?(attribute) && !@attributes[attribute].nil? && !is_empty
585: end
Returns an array of names for the attributes available on this object sorted alphabetically.
# File vendor/activerecord/lib/active_record/base.rb, line 588
588: def attribute_names
589: @attributes.keys.sort
590: end
Returns the column object for the named attribute.
# File vendor/activerecord/lib/active_record/base.rb, line 593
593: def column_for_attribute(name)
594: self.class.columns_hash[name]
595: end
Returns true if the comparison_object is of the same type and has the same id.
# File vendor/activerecord/lib/active_record/base.rb, line 598
598: def ==(comparison_object)
599: comparison_object.instance_of?(self.class) && comparison_object.id == id
600: end
Returns the value of attribute identified by attr_name after it has been type cast (for example, "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). Can be used when
# File vendor/activerecord/lib/active_record/base.rb, line 667
667: def read_attribute(attr_name) #:doc:
668: if column = column_for_attribute(attr_name)
669: @attributes[attr_name] = column.type_cast(@attributes[attr_name])
670: end
671:
672: @attributes[attr_name]
673: end