Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/trunk/activerecord/lib/active_record/base.rb

Revision 5593, 92.4 kB (checked in by bitsweat, 4 years ago)

Simplify query_attribute by typecasting the attribute value and checking whether it's nil, false, zero or blank. Closes #6659.

  • Property svn:executable set to *
Line 
1 require 'base64'
2 require 'yaml'
3 require 'set'
4 require 'active_record/deprecated_finders'
5
6 module ActiveRecord #:nodoc:
7   class ActiveRecordError < StandardError #:nodoc:
8   end
9   class SubclassNotFound < ActiveRecordError #:nodoc:
10   end
11   class AssociationTypeMismatch < ActiveRecordError #:nodoc:
12   end
13   class SerializationTypeMismatch < ActiveRecordError #:nodoc:
14   end
15   class AdapterNotSpecified < ActiveRecordError # :nodoc:
16   end
17   class AdapterNotFound < ActiveRecordError # :nodoc:
18   end
19   class ConnectionNotEstablished < ActiveRecordError #:nodoc:
20   end
21   class ConnectionFailed < ActiveRecordError #:nodoc:
22   end
23   class RecordNotFound < ActiveRecordError #:nodoc:
24   end
25   class RecordNotSaved < ActiveRecordError #:nodoc:
26   end
27   class StatementInvalid < ActiveRecordError #:nodoc:
28   end
29   class PreparedStatementInvalid < ActiveRecordError #:nodoc:
30   end
31   class StaleObjectError < ActiveRecordError #:nodoc:
32   end
33   class ConfigurationError < StandardError #:nodoc:
34   end
35   class ReadOnlyRecord < StandardError #:nodoc:
36   end
37  
38   class AttributeAssignmentError < ActiveRecordError #:nodoc:
39     attr_reader :exception, :attribute
40     def initialize(message, exception, attribute)
41       @exception = exception
42       @attribute = attribute
43       @message = message
44     end
45   end
46
47   class MultiparameterAssignmentErrors < ActiveRecordError #:nodoc:
48     attr_reader :errors
49     def initialize(errors)
50       @errors = errors
51     end
52   end
53
54   # Active Record objects don't specify their attributes directly, but rather infer them from the table definition with
55   # which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change
56   # is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain
57   # database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.
58   #
59   # See the mapping rules in table_name and the full example in link:files/README.html for more insight.
60   #
61   # == Creation
62   #
63   # Active Records accept constructor parameters either in a hash or as a block. The hash method is especially useful when
64   # you're receiving the data from somewhere else, like a HTTP request. It works like this:
65   #
66   #   user = User.new(:name => "David", :occupation => "Code Artist")
67   #   user.name # => "David"
68   #
69   # You can also use block initialization:
70   #
71   #   user = User.new do |u|
72   #     u.name = "David"
73   #     u.occupation = "Code Artist"
74   #   end
75   #
76   # And of course you can just create a bare object and specify the attributes after the fact:
77   #
78   #   user = User.new
79   #   user.name = "David"
80   #   user.occupation = "Code Artist"
81   #
82   # == Conditions
83   #
84   # Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement.
85   # The array form is to be used when the condition input is tainted and requires sanitization. The string form can
86   # be used for statements that don't involve tainted data. The hash form works much like the array form, except
87   # only equality is possible. Examples:
88   #
89   #   class User < ActiveRecord::Base
90   #     def self.authenticate_unsafely(user_name, password)
91   #       find(:first, :conditions => "user_name = '#{user_name}' AND password = '#{password}'")
92   #     end
93   #
94   #     def self.authenticate_safely(user_name, password)
95   #       find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ])
96   #     end
97   #
98   #     def self.authenticate_safely_simply(user_name, password)
99   #       find(:first, :conditions => { :user_name => user_name, :password => password })
100   #     end
101   #   end
102   #
103   # The <tt>authenticate_unsafely</tt> method inserts the parameters directly into the query and is thus susceptible to SQL-injection
104   # attacks if the <tt>user_name</tt> and +password+ parameters come directly from a HTTP request. The <tt>authenticate_safely</tt>  and
105   # <tt>authenticate_safely_simply</tt> both will sanitize the <tt>user_name</tt> and +password+ before inserting them in the query,
106   # which will ensure that an attacker can't escape the query and fake the login (or worse).
107   #
108   # When using multiple parameters in the conditions, it can easily become hard to read exactly what the fourth or fifth
109   # question mark is supposed to represent. In those cases, you can resort to named bind variables instead. That's done by replacing
110   # the question marks with symbols and supplying a hash with values for the matching symbol keys:
111   #
112   #   Company.find(:first, [
113   #     "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
114   #     { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
115   #   ])
116   #
117   # Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
118   # operator. For instance:
119   #
120   #   Student.find(:all, :conditions => { :first_name => "Harvey", :status => 1 })
121   #   Student.find(:all, :conditions => params[:student])
122   #
123   #
124   # == Overwriting default accessors
125   #
126   # All column values are automatically available through basic accessors on the Active Record object, but some times you
127   # want to specialize this behavior. This can be done by either by overwriting the default accessors (using the same
128   # name as the attribute) calling read_attribute(attr_name) and write_attribute(attr_name, value) to actually change things.
129   # Example:
130   #
131   #   class Song < ActiveRecord::Base
132   #     # Uses an integer of seconds to hold the length of the song
133   #
134   #     def length=(minutes)
135   #       write_attribute(:length, minutes * 60)
136   #     end
137   #
138   #     def length
139   #       read_attribute(:length) / 60
140   #     end
141   #   end
142   #
143   # You can alternatively use self[:attribute]=(value) and self[:attribute] instead of write_attribute(:attribute, vaule) and
144   # read_attribute(:attribute) as a shorter form.
145   #
146   # == Accessing attributes before they have been typecasted
147   #
148   # Sometimes you want to be able to read the raw attribute data without having the column-determined typecast run its course first.
149   # That can be done by using the <attribute>_before_type_cast accessors that all attributes have. For example, if your Account model
150   # has a balance attribute, you can call account.balance_before_type_cast or account.id_before_type_cast.
151   #
152   # This is especially useful in validation situations where the user might supply a string for an integer field and you want to display
153   # the original string back in an error message. Accessing the attribute normally would typecast the string to 0, which isn't what you
154   # want.
155   #
156   # == Dynamic attribute-based finders
157   #
158   # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by
159   # appending the name of an attribute to <tt>find_by_</tt> or <tt>find_all_by_</tt>, so you get finders like Person.find_by_user_name,
160   # Person.find_all_by_last_name, Payment.find_by_transaction_id. So instead of writing
161   # <tt>Person.find(:first, ["user_name = ?", user_name])</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
162   # And instead of writing <tt>Person.find(:all, ["last_name = ?", last_name])</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>.
163   #
164   # It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
165   # <tt>Person.find_by_user_name_and_password</tt> or even <tt>Payment.find_by_purchaser_and_state_and_country</tt>. So instead of writing
166   # <tt>Person.find(:first, ["user_name = ? AND password = ?", user_name, password])</tt>, you just do
167   # <tt>Person.find_by_user_name_and_password(user_name, password)</tt>.
168   #
169   # It's even possible to use all the additional parameters to find. For example, the full interface for Payment.find_all_by_amount
170   # is actually Payment.find_all_by_amount(amount, options). And the full interface to Person.find_by_user_name is
171   # actually Person.find_by_user_name(user_name, options). So you could call <tt>Payment.find_all_by_amount(50, :order => "created_on")</tt>.
172   #
173   # The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with
174   # <tt>find_or_create_by_</tt> and will return the object if it already exists and otherwise creates it, then returns it. Example:
175   #
176   #   # No 'Summer' tag exists
177   #   Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
178   #   
179   #   # Now the 'Summer' tag does exist
180   #   Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
181   #
182   # Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without saving it first. Example:
183   #
184   #   # No 'Winter' tag exists
185   #   winter = Tag.find_or_initialize_by_name("Winter")
186   #   winter.new_record? # true
187   #
188   # == Saving arrays, hashes, and other non-mappable objects in text columns
189   #
190   # Active Record can serialize any object in text columns using YAML. To do so, you must specify this with a call to the class method +serialize+.
191   # This makes it possible to store arrays, hashes, and other non-mappable objects without doing any additional work. Example:
192   #
193   #   class User < ActiveRecord::Base
194   #     serialize :preferences
195   #   end
196   #
197   #   user = User.create(:preferences => { "background" => "black", "display" => large })
198   #   User.find(user.id).preferences # => { "background" => "black", "display" => large }
199   #
200   # You can also specify a class option as the second parameter that'll raise an exception if a serialized object is retrieved as a
201   # descendent of a class not in the hierarchy. Example:
202   #
203   #   class User < ActiveRecord::Base
204   #     serialize :preferences, Hash
205   #   end
206   #
207   #   user = User.create(:preferences => %w( one two three ))
208   #   User.find(user.id).preferences    # raises SerializationTypeMismatch
209   #
210   # == Single table inheritance
211   #
212   # Active Record allows inheritance by storing the name of the class in a column that by default is called "type" (can be changed
213   # by overwriting <tt>Base.inheritance_column</tt>). This means that an inheritance looking like this:
214   #
215   #   class Company < ActiveRecord::Base; end
216   #   class Firm < Company; end
217   #   class Client < Company; end
218   #   class PriorityClient < Client; end
219   #
220   # When you do Firm.create(:name => "37signals"), this record will be saved in the companies table with type = "Firm". You can then
221   # fetch this row again using Company.find(:first, "name = '37signals'") and it will return a Firm object.
222   #
223   # If you don't have a type column defined in your table, single-table inheritance won't be triggered. In that case, it'll work just
224   # like normal subclasses with no special magic for differentiating between them or reloading the right type with find.
225   #
226   # Note, all the attributes for all the cases are kept in the same table. Read more:
227   # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
228   #
229   # == Connection to multiple databases in different models
230   #
231   # Connections are usually created through ActiveRecord::Base.establish_connection and retrieved by ActiveRecord::Base.connection.
232   # All classes inheriting from ActiveRecord::Base will use this connection. But you can also set a class-specific connection.
233   # For example, if Course is a ActiveRecord::Base, but resides in a different database you can just say Course.establish_connection
234   # and Course *and all its subclasses* will use this connection instead.
235   #
236   # This feature is implemented by keeping a connection pool in ActiveRecord::Base that is a Hash indexed by the class. If a connection is
237   # requested, the retrieve_connection method will go up the class-hierarchy until a connection is found in the connection pool.
238   #
239   # == Exceptions
240   #
241   # * +ActiveRecordError+ -- generic error class and superclass of all other errors raised by Active Record
242   # * +AdapterNotSpecified+ -- the configuration hash used in <tt>establish_connection</tt> didn't include a
243   #   <tt>:adapter</tt> key.
244   # * +AdapterNotFound+ -- the <tt>:adapter</tt> key used in <tt>establish_connection</tt> specified an non-existent adapter
245   #   (or a bad spelling of an existing one).
246   # * +AssociationTypeMismatch+ -- the object assigned to the association wasn't of the type specified in the association definition.
247   # * +SerializationTypeMismatch+ -- the object serialized wasn't of the class specified as the second parameter.
248   # * +ConnectionNotEstablished+ -- no connection has been established. Use <tt>establish_connection</tt> before querying.
249   # * +RecordNotFound+ -- no record responded to the find* method.
250   #   Either the row with the given ID doesn't exist or the row didn't meet the additional restrictions.
251   # * +StatementInvalid+ -- the database server rejected the SQL statement. The precise error is added in the  message.
252   #   Either the record with the given ID doesn't exist or the record didn't meet the additional restrictions.
253   # * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
254   #   +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
255   #   objects that should be inspected to determine which attributes triggered the errors.
256   # * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
257   #   You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
258   #
259   # *Note*: The attributes listed are class-level attributes (accessible from both the class and instance level).
260   # So it's possible to assign a logger to the class through Base.logger= which will then be used by all
261   # instances in the current object space.
262   class Base
263     # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then passed
264     # on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
265     cattr_accessor :logger
266    
267     include Reloadable::Deprecated
268    
269     def self.inherited(child) #:nodoc:
270       @@subclasses[self] ||= []
271       @@subclasses[self] << child
272       super
273     end
274    
275     def self.reset_subclasses #:nodoc:
276       nonreloadables = []
277       subclasses.each do |klass|
278         unless Dependencies.autoloaded? klass
279           nonreloadables << klass
280           next
281         end
282         klass.instance_variables.each { |var| klass.send(:remove_instance_variable, var) }
283         klass.instance_methods(false).each { |m| klass.send :undef_method, m }
284       end
285       @@subclasses = {}
286       nonreloadables.each { |klass| (@@subclasses[klass.superclass] ||= []) << klass }
287     end
288
289     @@subclasses = {}
290
291     cattr_accessor :configurations
292     @@configurations = {}
293
294     # Accessor for the prefix type that will be prepended to every primary key column name. The options are :table_name and
295     # :table_name_with_underscore. If the first is specified, the Product class will look for "productid" instead of "id" as
296     # the primary column. If the latter is specified, the Product class will look for "product_id" instead of "id". Remember
297     # that this is a global setting for all Active Records.
298     cattr_accessor :primary_key_prefix_type
299     @@primary_key_prefix_type = nil
300
301     # Accessor for the name of the prefix string to prepend to every table name. So if set to "basecamp_", all
302     # table names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient way of creating a namespace
303     # for tables in a shared database. By default, the prefix is the empty string.
304     cattr_accessor :table_name_prefix
305     @@table_name_prefix = ""
306
307     # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
308     # "people_basecamp"). By default, the suffix is the empty string.
309     cattr_accessor :table_name_suffix
310     @@table_name_suffix = ""
311
312     # Indicates whether or not table names should be the pluralized versions of the corresponding class names.
313     # If true, the default table name for a +Product+ class will be +products+. If false, it would just be +product+.
314     # See table_name for the full rules on table/class naming. This is true, by default.
315     cattr_accessor :pluralize_table_names
316     @@pluralize_table_names = true
317
318     # Determines whether or not to use ANSI codes to colorize the logging statements committed by the connection adapter. These colors
319     # make it much easier to overview things during debugging (when used through a reader like +tail+ and on a black background), but
320     # may complicate matters if you use software like syslog. This is true, by default.
321     cattr_accessor :colorize_logging
322     @@colorize_logging = true
323
324     # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates and times from the database.
325     # This is set to :local by default.
326     cattr_accessor :default_timezone
327     @@default_timezone = :local
328
329     # Determines whether or not to use a connection for each thread, or a single shared connection for all threads.
330     # Defaults to false. Set to true if you're writing a threaded application.
331     cattr_accessor :allow_concurrency
332     @@allow_concurrency = false
333
334     # Determines whether to speed up access by generating optimized reader
335     # methods to avoid expensive calls to method_missing when accessing
336     # attributes by name. You might want to set this to false in development
337     # mode, because the methods would be regenerated on each request.
338     cattr_accessor :generate_read_methods
339     @@generate_read_methods = true
340    
341     # Specifies the format to use when dumping the database schema with Rails'
342     # Rakefile.  If :sql, the schema is dumped as (potentially database-
343     # specific) SQL statements.  If :ruby, the schema is dumped as an
344     # ActiveRecord::Schema file which can be loaded into any database that
345     # supports migrations.  Use :ruby if you want to have different database
346     # adapters for, e.g., your development and test environments.
347     cattr_accessor :schema_format
348     @@schema_format = :ruby
349
350     class << self # Class methods
351       # Find operates with three different retrieval approaches:
352       #
353       # * Find by id: This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
354       #   If no record can be found for all of the listed ids, then RecordNotFound will be raised.
355       # * Find first: This will return the first record matched by the options used. These options can either be specific
356       #   conditions or merely an order. If no record can matched, nil is returned.
357       # * Find all: This will return all the records matched by the options used. If no records are found, an empty array is returned.
358       #
359       # All approaches accept an option hash as their last parameter. The options are:
360       #
361       # * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
362       # * <tt>:order</tt>: An SQL fragment like "created_at DESC, name".
363       # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
364       # * <tt>:limit</tt>: An integer determining the limit on the number of rows that should be returned.
365       # * <tt>:offset</tt>: An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
366       # * <tt>:joins</tt>: An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
367       #   The records will be returned read-only since they will have attributes that do not correspond to the table's columns.
368       #   Pass :readonly => false to override.
369       # * <tt>:include</tt>: Names associations that should be loaded alongside using LEFT OUTER JOINs. The symbols named refer
370       #   to already defined associations. See eager loading under Associations.
371       # * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not
372       #   include the joined columns.
373       # * <tt>:from</tt>: By default, this is the table name of the class, but can be changed to an alternate table name (or even the name
374       #   of a database view).
375       # * <tt>:readonly</tt>: Mark the returned records read-only so they cannot be saved or updated.
376       # * <tt>:lock</tt>: An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE".
377       #   :lock => true gives connection's default exclusive lock, usually "FOR UPDATE".
378       #
379       # Examples for find by id:
380       #   Person.find(1)       # returns the object for ID = 1
381       #   Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
382       #   Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
383       #   Person.find([1])     # returns an array for objects the object with ID = 1
384       #   Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
385       #
386       # Examples for find first:
387       #   Person.find(:first) # returns the first object fetched by SELECT * FROM people
388       #   Person.find(:first, :conditions => [ "user_name = ?", user_name])
389       #   Person.find(:first, :order => "created_on DESC", :offset => 5)
390       #
391       # Examples for find all:
392       #   Person.find(:all) # returns an array of objects for all the rows fetched by SELECT * FROM people
393       #   Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
394       #   Person.find(:all, :offset => 10, :limit => 10)
395       #   Person.find(:all, :include => [ :account, :friends ])
396       #   Person.find(:all, :group => "category")
397       #
398       # Example for find with a lock. Imagine two concurrent transactions:
399       # each will read person.visits == 2, add 1 to it, and save, resulting
400       # in two saves of person.visits = 3.  By locking the row, the second
401       # transaction has to wait until the first is finished; we get the
402       # expected person.visits == 4.
403       #   Person.transaction do
404       #     person = Person.find(1, :lock => true)
405       #     person.visits += 1
406       #     person.save!
407       #   end
408       def find(*args)
409         options = extract_options_from_args!(args)
410         validate_find_options(options)
411         set_readonly_option!(options)
412
413         case args.first
414           when :first then find_initial(options)
415           when :all   then find_every(options)
416           else             find_from_ids(args, options)
417         end
418       end
419      
420       # Works like find(:all), but requires a complete SQL string. Examples:
421       #   Post.find_by_sql "SELECT p.*, c.author FROM posts p, comments c WHERE p.id = c.post_id"
422       #   Post.find_by_sql ["SELECT * FROM posts WHERE author = ? AND created > ?", author_id, start_date]
423       def find_by_sql(sql)
424         connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
425       end
426
427       # Returns true if the given +id+ represents the primary key of a record in the database, false otherwise.
428       # You can also pass a set of SQL conditions.
429       # Example:
430       #   Person.exists?(5)
431       #   Person.exists?('5')
432       #   Person.exists?(:name => "David")
433       #   Person.exists?(['name LIKE ?', "%#{query}%"])
434       def exists?(id_or_conditions)
435         !find(:first, :conditions => expand_id_conditions(id_or_conditions)).nil?
436       rescue ActiveRecord::ActiveRecordError
437         false
438       end
439
440       # Creates an object, instantly saves it as a record (if the validation permits it), and returns it. If the save
441       # fails under validations, the unsaved object is still returned.
442       def create(attributes = nil)
443         if attributes.is_a?(Array)
444           attributes.collect { |attr| create(attr) }
445         else
446           object = new(attributes)
447           scope(:create).each { |att,value| object.send("#{att}=", value) } if scoped?(:create)
448           object.save
449           object
450         end
451       end
452
453       # Finds the record from the passed +id+, instantly saves it with the passed +attributes+ (if the validation permits it),
454       # and returns it. If the save fails under validations, the unsaved object is still returned.
455       #
456       # The arguments may also be given as arrays in which case the update method is called for each pair of +id+ and
457       # +attributes+ and an array of objects is returned.
458       #
459       # Example of updating one record:
460       #   Person.update(15, {:user_name => 'Samuel', :group => 'expert'})
461       #
462       # Example of updating multiple records:
463       #   people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }       
464       #   Person.update(people.keys, people.values)
465       def update(id, attributes)
466         if id.is_a?(Array)
467           idx = -1
468           id.collect { |id| idx += 1; update(id, attributes[idx]) }
469         else
470           object = find(id)
471           object.update_attributes(attributes)
472           object
473         end
474       end
475
476       # Deletes the record with the given +id+ without instantiating an object first. If an array of ids is provided, all of them
477       # are deleted.
478       def delete(id)
479         delete_all([ "#{primary_key} IN (?)", id ])
480       end
481
482       # Destroys the record with the given +id+ by instantiating the object and calling #destroy (all the callbacks are the triggered).
483       # If an array of ids is provided, all of them are destroyed.
484       def destroy(id)
485         id.is_a?(Array) ? id.each { |id| destroy(id) } : find(id).destroy
486       end
487
488       # Updates all records with the SET-part of an SQL update statement in +updates+ and returns an integer with the number of rows updated.
489       # A subset of the records can be selected by specifying +conditions+. Example:
490       #   Billing.update_all "category = 'authorized', approved = 1", "author = 'David'"
491       def update_all(updates, conditions = nil)
492         sql  = "UPDATE #{table_name} SET #{sanitize_sql(updates)} "
493         add_conditions!(sql, conditions, scope(:find))
494         connection.update(sql, "#{name} Update")
495       end
496
497       # Destroys the objects for all the records that match the +condition+ by instantiating each object and calling
498       # the destroy method. Example:
499       #   Person.destroy_all "last_login < '2004-04-04'"
500       def destroy_all(conditions = nil)
501         find(:all, :conditions => conditions).each { |object| object.destroy }
502       end
503
504       # Deletes all the records that match the +condition+ without instantiating the objects first (and hence not
505       # calling the destroy method). Example:
506       #   Post.delete_all "person_id = 5 AND (category = 'Something' OR category = 'Else')"
507       def delete_all(conditions = nil)
508         sql = "DELETE FROM #{table_name} "
509         add_conditions!(sql, conditions, scope(:find))
510         connection.delete(sql, "#{name} Delete all")
511       end
512
513       # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
514       #   Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
515       def count_by_sql(sql)
516         sql = sanitize_conditions(sql)
517         connection.select_value(sql, "#{name} Count").to_i
518       end
519
520       # Increments the specified counter by one. So <tt>DiscussionBoard.increment_counter("post_count",
521       # discussion_board_id)</tt> would increment the "post_count" counter on the board responding to discussion_board_id.
522       # This is used for caching aggregate values, so that they don't need to be computed every time. Especially important
523       # for looping over a collection where each element require a number of aggregate values. Like the DiscussionBoard
524       # that needs to list both the number of posts and comments.
525       def increment_counter(counter_name, id)
526         update_all "#{counter_name} = #{counter_name} + 1", "#{primary_key} = #{quote_value(id)}"
527       end
528
529       # Works like increment_counter, but decrements instead.
530       def decrement_counter(counter_name, id)
531         update_all "#{counter_name} = #{counter_name} - 1", "#{primary_key} = #{quote_value(id)}"
532       end
533
534
535       # Attributes named in this macro are protected from mass-assignment, such as <tt>new(attributes)</tt> and
536       # <tt>attributes=(attributes)</tt>. Their assignment will simply be ignored. Instead, you can use the direct writer
537       # methods to do assignment. This is meant to protect sensitive attributes from being overwritten by URL/form hackers. Example:
538       #
539       #   class Customer < ActiveRecord::Base
540       #     attr_protected :credit_rating
541       #   end
542       #
543       #   customer = Customer.new("name" => David, "credit_rating" => "Excellent")
544       #   customer.credit_rating # => nil
545       #   customer.attributes = { "description" => "Jolly fellow", "credit_rating" => "Superb" }
546       #   customer.credit_rating # => nil
547       #
548       #   customer.credit_rating = "Average"
549       #   customer.credit_rating # => "Average"
550       def attr_protected(*attributes)
551         write_inheritable_array("attr_protected", attributes - (protected_attributes || []))
552       end
553
554       # Returns an array of all the attributes that have been protected from mass-assignment.
555       def protected_attributes # :nodoc:
556         read_inheritable_attribute("attr_protected")
557       end
558
559       # If this macro is used, only those attributes named in it will be accessible for mass-assignment, such as
560       # <tt>new(attributes)</tt> and <tt>attributes=(attributes)</tt>. This is the more conservative choice for mass-assignment
561       # protection. If you'd rather start from an all-open default and restrict attributes as needed, have a look at
562       # attr_protected.
563       def attr_accessible(*attributes)
564         write_inheritable_array("attr_accessible", attributes - (accessible_attributes || []))
565       end
566
567       # Returns an array of all the attributes that have been made accessible to mass-assignment.
568       def accessible_attributes # :nodoc:
569         read_inheritable_attribute("attr_accessible")
570       end
571
572
573       # Specifies that the attribute by the name of +attr_name+ should be serialized before saving to the database and unserialized
574       # after loading from the database. The serialization is done through YAML. If +class_name+ is specified, the serialized
575       # object must be of that class on retrieval or +SerializationTypeMismatch+ will be raised.
576       def serialize(attr_name, class_name = Object)
577         serialized_attributes[attr_name.to_s] = class_name
578       end
579
580       # Returns a hash of all the attributes that have been specified for serialization as keys and their class restriction as values.
581       def serialized_attributes
582         read_inheritable_attribute("attr_serialized") or write_inheritable_attribute("attr_serialized", {})
583       end
584
585
586       # Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending
587       # directly from ActiveRecord. So if the hierarchy looks like: Reply < Message < ActiveRecord, then Message is used
588       # to guess the table name from even when called on Reply. The rules used to do the guess are handled by the Inflector class
589       # in Active Support, which knows almost all common English inflections (report a bug if your inflection isn't covered).
590       #
591       # Nested classes are given table names prefixed by the singular form of
592       # the parent's table name. Example:
593       #   file                  class               table_name
594       #   invoice.rb            Invoice             invoices
595       #   invoice/lineitem.rb   Invoice::Lineitem   invoice_lineitems
596       #
597       # Additionally, the class-level table_name_prefix is prepended and the
598       # table_name_suffix is appended.  So if you have "myapp_" as a prefix,
599       # the table name guess for an Invoice class becomes "myapp_invoices".
600       # Invoice::Lineitem becomes "myapp_invoice_lineitems".
601       #
602       # You can also overwrite this class method to allow for unguessable
603       # links, such as a Mouse class with a link to a "mice" table. Example:
604       #
605       #   class Mouse < ActiveRecord::Base
606       #     set_table_name "mice"
607       #   end
608       def table_name
609         reset_table_name
610       end
611
612       def reset_table_name #:nodoc:
613         base = base_class
614
615         name =
616           # STI subclasses always use their superclass' table.
617           unless self == base
618             base.table_name
619           else
620             # Nested classes are prefixed with singular parent table name.
621             if parent < ActiveRecord::Base && !parent.abstract_class?
622               contained = parent.table_name
623               contained = contained.singularize if parent.pluralize_table_names
624               contained << '_'
625             end
626             name = "#{table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
627           end
628
629         set_table_name(name)
630         name
631       end
632
633       # Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
634       # primary_key_prefix_type setting, though.
635       def primary_key
636         reset_primary_key
637       end
638
639       def reset_primary_key #:nodoc:
640         key = 'id'
641         case primary_key_prefix_type
642           when :table_name
643             key = Inflector.foreign_key(base_class.name, false)
644           when :table_name_with_underscore
645             key = Inflector.foreign_key(base_class.name)
646         end
647         set_primary_key(key)
648         key
649       end
650
651       # Defines the column name for use with single table inheritance
652       # -- can be set in subclasses like so: self.inheritance_column = "type_id"
653       def inheritance_column
654         @inheritance_column ||= "type".freeze
655       end
656
657       # Lazy-set the sequence name to the connection's default.  This method
658       # is only ever called once since set_sequence_name overrides it.
659       def sequence_name #:nodoc:
660         reset_sequence_name
661       end
662
663       def reset_sequence_name #:nodoc:
664         default = connection.default_sequence_name(table_name, primary_key)
665         set_sequence_name(default)
666         default
667       end
668
669       # Sets the table name to use to the given value, or (if the value
670       # is nil or false) to the value returned by the given block.
671       #
672       # Example:
673       #
674       #   class Project < ActiveRecord::Base
675       #     set_table_name "project"
676       #   end
677       def set_table_name(value = nil, &block)
678         define_attr_method :table_name, value, &block
679       end
680       alias :table_name= :set_table_name
681
682       # Sets the name of the primary key column to use to the given value,
683       # or (if the value is nil or false) to the value returned by the given
684       # block.
685       #
686       # Example:
687       #
688       #   class Project < ActiveRecord::Base
689       #     set_primary_key "sysid"
690       #   end
691       def set_primary_key(value = nil, &block)
692         define_attr_method :primary_key, value, &block
693       end
694       alias :primary_key= :set_primary_key
695
696       # Sets the name of the inheritance column to use to the given value,
697       # or (if the value # is nil or false) to the value returned by the
698       # given block.
699       #
700       # Example:
701       #
702       #   class Project < ActiveRecord::Base
703       #     set_inheritance_column do
704       #       original_inheritance_column + "_id"
705       #     end
706       #   end
707       def set_inheritance_column(value = nil, &block)
708         define_attr_method :inheritance_column, value, &block
709       end
710       alias :inheritance_column= :set_inheritance_column
711
712       # Sets the name of the sequence to use when generating ids to the given
713       # value, or (if the value is nil or false) to the value returned by the
714       # given block. This is required for Oracle and is useful for any
715       # database which relies on sequences for primary key generation.
716       #
717       # If a sequence name is not explicitly set when using Oracle or Firebird,
718       # it will default to the commonly used pattern of: #{table_name}_seq
719       #
720       # If a sequence name is not explicitly set when using PostgreSQL, it
721       # will discover the sequence corresponding to your primary key for you.
722       #
723       # Example:
724       #
725       #   class Project < ActiveRecord::Base
726       #     set_sequence_name "projectseq"   # default would have been "project_seq"
727       #   end
728       def set_sequence_name(value = nil, &block)
729         define_attr_method :sequence_name, value, &block
730       end
731       alias :sequence_name= :set_sequence_name
732
733       # Turns the +table_name+ back into a class name following the reverse rules of +table_name+.
734       def class_name(table_name = table_name) # :nodoc:
735         # remove any prefix and/or suffix from the table name
736         class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].camelize
737         class_name = class_name.singularize if pluralize_table_names
738         class_name
739       end
740
741       # Indicates whether the table associated with this class exists
742       def table_exists?
743         if connection.respond_to?(:tables)
744           connection.tables.include? table_name
745         else
746           # if the connection adapter hasn't implemented tables, there are two crude tests that can be
747           # used - see if getting column info raises an error, or if the number of columns returned is zero
748           begin
749             reset_column_information
750             columns.size > 0
751           rescue ActiveRecord::StatementInvalid
752             false
753           end         
754         end
755       end
756
757       # Returns an array of column objects for the table associated with this class.
758       def columns
759         unless @columns
760           @columns = connection.columns(table_name, "#{name} Columns")
761           @columns.each {|column| column.primary = column.name == primary_key}
762         end
763         @columns
764       end
765
766       # Returns an array of column objects for the table associated with this class.
767       def columns_hash
768         @columns_hash ||= columns.inject({}) { |hash, column| hash[column.name] = column; hash }
769       end
770
771       # Returns an array of column names as strings.
772       def column_names
773         @column_names ||= columns.map { |column| column.name }
774       end
775
776       # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
777       # and columns used for single table inheritance have been removed.
778       def content_columns
779         @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
780       end
781
782       # Returns a hash of all the methods added to query each of the columns in the table with the name of the method as the key
783       # and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute
784       # is available.
785       def column_methods_hash #:nodoc:
786         @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
787           attr_name = attr.to_s
788           methods[attr.to_sym]       = attr_name
789           methods["#{attr}=".to_sym] = attr_name
790           methods["#{attr}?".to_sym] = attr_name
791           methods["#{attr}_before_type_cast".to_sym] = attr_name
792           methods
793         end
794       end
795
796       # Contains the names of the generated reader methods.
797       def read_methods #:nodoc:
798         @read_methods ||= Set.new
799       end
800
801       # Resets all the cached information about columns, which will cause them to be reloaded on the next request.
802       def reset_column_information
803         read_methods.each { |name| undef_method(name) }
804         @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @read_methods = @inheritance_column = nil
805       end
806
807       def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
808         subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
809       end
810
811       # Transforms attribute key names into a more humane format, such as "First name" instead of "first_name". Example:
812       #   Person.human_attribute_name("first_name") # => "First name"
813       # Deprecated in favor of just calling "first_name".humanize
814       def human_attribute_name(attribute_key_name) #:nodoc:
815         attribute_key_name.humanize
816       end
817
818       def descends_from_active_record? # :nodoc:
819         superclass == Base || !columns_hash.include?(inheritance_column)
820       end
821
822
823       def quote_value(value, column = nil) #:nodoc:
824         connection.quote(value,column)
825       end
826
827       def quote(value, column = nil) #:nodoc:
828         connection.quote(value, column)
829       end
830       deprecate :quote => :quote_value
831
832       # Used to sanitize objects before they're used in an SELECT SQL-statement. Delegates to <tt>connection.quote</tt>.
833       def sanitize(object) #:nodoc:
834         connection.quote(object)
835       end
836
837       # Log and benchmark multiple statements in a single block. Example:
838       #
839       #   Project.benchmark("Creating project") do
840       #     project = Project.create("name" => "stuff")
841       #     project.create_manager("name" => "David")
842       #     project.milestones << Milestone.find(:all)
843       #   end
844       #
845       # The benchmark is only recorded if the current level of the logger matches the <tt>log_level</tt>, which makes it
846       # easy to include benchmarking statements in production software that will remain inexpensive because the benchmark
847       # will only be conducted if the log level is low enough.
848       #
849       # The logging of the multiple statements is turned off unless <tt>use_silence</tt> is set to false.
850       def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
851         if logger && logger.level == log_level
852           result = nil
853           seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield }
854           logger.add(log_level, "#{title} (#{'%.5f' % seconds})")
855           result
856         else
857           yield
858         end
859       end
860
861       # Silences the logger for the duration of the block.
862       def silence
863         old_logger_level, logger.level = logger.level, Logger::ERROR if logger
864         yield
865       ensure
866         logger.level = old_logger_level if logger
867       end
868
869       # Scope parameters to method calls within the block.  Takes a hash of method_name => parameters hash.
870       # method_name may be :find or :create. :find parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>,
871       # <tt>:include</tt>, <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options. :create parameters are an attributes hash.
872       #
873       #   Article.with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do
874       #     Article.find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
875       #     a = Article.create(1)
876       #     a.blog_id # => 1
877       #   end
878       #
879       # In nested scopings, all previous parameters are overwritten by inner rule
880       # except :conditions in :find, that are merged as hash.
881       #
882       #   Article.with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
883       #     Article.with_scope(:find => { :limit => 10})
884       #       Article.find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
885       #     end
886       #     Article.with_scope(:find => { :conditions => "author_id = 3" })
887       #       Article.find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
888       #     end
889       #   end
890       #
891       # You can ignore any previous scopings by using <tt>with_exclusive_scope</tt> method.
892       #
893       #   Article.with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }) do
894       #     Article.with_exclusive_scope(:find => { :limit => 10 })
895       #       Article.find(:all) # => SELECT * from articles LIMIT 10
896       #     end
897       #   end
898       def with_scope(method_scoping = {}, action = :merge, &block)
899         method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
900
901         # Dup first and second level of hash (method and params).
902         method_scoping = method_scoping.inject({}) do |hash, (method, params)|
903           hash[method] = (params == true) ? params : params.dup
904           hash
905         end
906
907         method_scoping.assert_valid_keys([ :find, :create ])
908
909         if f = method_scoping[:find]
910           f.assert_valid_keys([ :conditions, :joins, :select, :include, :from, :offset, :limit, :order, :readonly, :lock ])
911           f[:readonly] = true if !f[:joins].blank? && !f.has_key?(:readonly)
912         end
913
914         # Merge scopings
915         if action == :merge && current_scoped_methods
916           method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
917             case hash[method]
918               when Hash
919                 if method == :find
920                   (hash[method].keys + params.keys).uniq.each do |key|
921                     merge = hash[method][key] && params[key] # merge if both scopes have the same key
922                     if key == :conditions && merge
923                       hash[method][key] = [params[key], hash[method][key]].collect{ |sql| "( %s )" % sanitize_sql(sql) }.join(" AND ")
924                     elsif key == :include && merge
925                       hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
926                     else
927                       hash[method][key] = hash[method][key] || params[key]
928                     end
929                   end
930                 else
931                   hash[method] = params.merge(hash[method])
932                 end
933               else
934                 hash[method] = params
935             end
936             hash
937           end
938         end
939
940         self.scoped_methods << method_scoping
941
942         begin
943           yield
944         ensure
945           self.scoped_methods.pop
946         end
947       end
948
949       # Works like with_scope, but discards any nested properties.
950       def with_exclusive_scope(method_scoping = {}, &block)
951         with_scope(method_scoping, :overwrite, &block)
952       end
953
954       # Overwrite the default class equality method to provide support for association proxies.
955       def ===(object)
956         object.is_a?(self)
957       end     
958
959       # Deprecated
960       def threaded_connections #:nodoc:
961         allow_concurrency
962       end
963
964       # Deprecated
965       def threaded_connections=(value) #:nodoc:
966         self.allow_concurrency = value
967       end
968
969       # Returns the base AR subclass that this class descends from. If A
970       # extends AR::Base, A.base_class will return A. If B descends from A
971       # through some arbitrarily deep hierarchy, B.base_class will return A.
972       def base_class
973         class_of_active_record_descendant(self)
974       end
975
976       # Set this to true if this is an abstract class (see #abstract_class?).
977       attr_accessor :abstract_class
978
979       # Returns whether this class is a base AR class.  If A is a base class and
980       # B descends from A, then B.base_class will return B.
981       def abstract_class?
982         abstract_class == true
983       end
984
985       private
986         def find_initial(options)
987           options.update(:limit => 1) unless options[:include]
988           find_every(options).first
989         end
990
991         def find_every(options)
992           records = scoped?(:find, :include) || options[:include] ?
993             find_with_associations(options) :
994             find_by_sql(construct_finder_sql(options))
995
996           records.each { |record| record.readonly! } if options[:readonly]
997
998           records
999         end
1000
1001         def find_from_ids(ids, options)
1002           expects_array = ids.first.kind_of?(Array)
1003           return ids.first if expects_array && ids.first.empty?
1004
1005           ids = ids.flatten.compact.uniq
1006
1007           case ids.size
1008             when 0
1009               raise RecordNotFound, "Couldn't find #{name} without an ID"
1010             when 1
1011               result = find_one(ids.first, options)
1012               expects_array ? [ result ] : result
1013             else
1014               find_some(ids, options)
1015           end
1016         end
1017      
1018         def find_one(id, options)
1019           conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
1020           options.update :conditions => "#{table_name}.#{primary_key} = #{quote_value(id,columns_hash[primary_key])}#{conditions}"
1021
1022           # Use find_every(options).first since the primary key condition
1023           # already ensures we have a single record. Using find_initial adds
1024           # a superfluous :limit => 1.
1025           if result = find_every(options).first
1026             result
1027           else
1028             raise RecordNotFound, "Couldn't find #{name} with ID=#{id}#{conditions}"
1029           end
1030         end
1031      
1032         def find_some(ids, options)
1033           conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
1034           ids_list   = ids.map { |id| quote_value(id,columns_hash[primary_key]) }.join(',')
1035           options.update :conditions => "#{table_name}.#{primary_key} IN (#{ids_list})#{conditions}"
1036
1037           result = find_every(options)
1038
1039           if result.size == ids.size
1040             result
1041           else
1042             raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions}"
1043           end
1044         end
1045
1046         # Finder methods must instantiate through this method to work with the
1047         # single-table inheritance model that makes it possible to create
1048         # objects of different types from the same table.
1049         def instantiate(record)
1050           object =
1051             if subclass_name = record[inheritance_column]
1052               # No type given.
1053               if subclass_name.empty?
1054                 allocate
1055
1056               else
1057                 require_association_class(subclass_name)
1058
1059                 # Ignore type if no column is present since it was probably
1060                 # pulled in from a sloppy join.
1061                 unless columns_hash.include?(inheritance_column)
1062                   allocate
1063
1064                 else
1065                   begin
1066                     compute_type(subclass_name).allocate
1067                   rescue NameError
1068                     raise SubclassNotFound,
1069                       "The single-table inheritance mechanism failed to locate the subclass: '#{record[inheritance_column]}'. " +
1070                       "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
1071                       "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
1072                       "or overwrite #{self.to_s}.inheritance_column to use another column for that information."
1073                   end
1074                 end
1075               end
1076             else
1077               allocate
1078             end
1079
1080           object.instance_variable_set("@attributes", record)
1081           object
1082         end
1083
1084         # Nest the type name in the same module as this class.
1085         # Bar is "MyApp::Business::Bar" relative to MyApp::Business::Foo
1086         def type_name_with_module(type_name)
1087           (/^::/ =~ type_name) ? type_name : "#{parent.name}::#{type_name}"
1088         end
1089
1090         def construct_finder_sql(options)
1091           scope = scope(:find)
1092           sql  = "SELECT #{(scope && scope[:select]) || options[:select] || '*'} "
1093           sql << "FROM #{(scope && scope[:from]) || options[:from] || table_name} "
1094
1095           add_joins!(sql, options, scope)
1096           add_conditions!(sql, options[:conditions], scope)
1097
1098           sql << " GROUP BY #{options[:group]} " if options[:group]
1099
1100           add_order!(sql, options[:order], scope)
1101           add_limit!(sql, options, scope)
1102           add_lock!(sql, options, scope)
1103
1104           sql
1105         end
1106
1107         # Merges includes so that the result is a valid +include+
1108         def merge_includes(first, second)
1109          (safe_to_array(first) + safe_to_array(second)).uniq
1110         end
1111
1112         # Object#to_a is deprecated, though it does have the desired behavior
1113         def safe_to_array(o)
1114           case o
1115           when NilClass
1116             []
1117           when Array
1118             o
1119           else
1120             [o]
1121           end
1122         end
1123
1124         def add_order!(sql, order, scope = :auto)
1125           scope = scope(:find) if :auto == scope
1126           scoped_order = scope[:order] if scope
1127           if order
1128             sql << " ORDER BY #{order}"
1129             sql << ", #{scoped_order}" if scoped_order
1130           else
1131             sql << " ORDER BY #{scoped_order}" if scoped_order
1132           end
1133         end
1134
1135         # The optional scope argument is for the current :find scope.
1136         def add_limit!(sql, options, scope = :auto)
1137           scope = scope(:find) if :auto == scope
1138           options = options.reverse_merge(:limit => scope[:limit], :offset => scope[:offset]) if scope
1139           connection.add_limit_offset!(sql, options)
1140         end
1141
1142         # The optional scope argument is for the current :find scope.
1143         # The :lock option has precedence over a scoped :lock.
1144         def add_lock!(sql, options, scope = :auto)
1145           scope = scope(:find) if :auto == :scope
1146           options = options.reverse_merge(:lock => scope[:lock]) if scope
1147           connection.add_lock!(sql, options)
1148         end
1149
1150         # The optional scope argument is for the current :find scope.
1151         def add_joins!(sql, options, scope = :auto)
1152           scope = scope(:find) if :auto == scope
1153           join = (scope && scope[:joins]) || options[:joins]
1154           sql << " #{join} " if join
1155         end
1156
1157         # Adds a sanitized version of +conditions+ to the +sql+ string. Note that the passed-in +sql+ string is changed.
1158         # The optional scope argument is for the current :find scope.
1159         def add_conditions!(sql, conditions, scope = :auto)
1160           scope = scope(:find) if :auto == scope
1161           segments = []
1162           segments << sanitize_sql(scope[:conditions]) if scope && scope[:conditions]
1163           segments << sanitize_sql(conditions) unless conditions.nil?
1164           segments << type_condition unless descends_from_active_record?       
1165           segments.compact!
1166           sql << "WHERE (#{segments.join(") AND (")}) " unless segments.empty?
1167         end
1168
1169         def type_condition
1170           quoted_inheritance_column = connection.quote_column_name(inheritance_column)
1171           type_condition = subclasses.inject("#{table_name}.#{quoted_inheritance_column} = '#{name.demodulize}' ") do |condition, subclass|
1172             condition << "OR #{table_name}.#{quoted_inheritance_column} = '#{subclass.name.demodulize}' "
1173           end
1174
1175           " (#{type_condition}) "
1176         end
1177
1178         # Guesses the table name, but does not decorate it with prefix and suffix information.
1179         def undecorated_table_name(class_name = base_class.name)
1180           table_name = Inflector.underscore(Inflector.demodulize(class_name))
1181           table_name = Inflector.pluralize(table_name) if pluralize_table_names
1182           table_name
1183         end
1184
1185         # Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) that are turned into
1186         # find(:first, :conditions => ["user_name = ?", user_name]) and  find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])
1187         # respectively. Also works for find(:all), but using find_all_by_amount(50) that are turned into find(:all, :conditions => ["amount = ?", 50]).
1188         #
1189         # It's even possible to use all the additional parameters to find. For example, the full interface for find_all_by_amount
1190         # is actually find_all_by_amount(amount, options).
1191         def method_missing(method_id, *arguments)
1192           if match = /find_(all_by|by)_([_a-zA-Z]\w*)/.match(method_id.to_s)
1193             finder, deprecated_finder = determine_finder(match), determine_deprecated_finder(match)
1194
1195             attribute_names = extract_attribute_names_from_match(match)
1196             super unless all_attributes_exists?(attribute_names)
1197
1198             attributes = construct_attributes_from_arguments(attribute_names, arguments)
1199
1200             case extra_options = arguments[attribute_names.size]
1201               when nil
1202                 options = { :conditions => attributes }
1203                 set_readonly_option!(options)
1204                 ActiveSupport::Deprecation.silence { send(finder, options) }
1205
1206               when Hash
1207                 finder_options = extra_options.merge(:conditions => attributes)
1208                 validate_find_options(finder_options)
1209                 set_readonly_option!(finder_options)
1210
1211                 if extra_options[:conditions]
1212                   with_scope(:find => { :conditions => extra_options[:conditions] }) do
1213                     ActiveSupport::Deprecation.silence { send(finder, finder_options) }
1214                   end
1215                 else
1216                   ActiveSupport::Deprecation.silence { send(finder, finder_options) }
1217                 end
1218
1219               else
1220                 ActiveSupport::Deprecation.silence do
1221                   send(deprecated_finder, sanitize_sql(attributes), *arguments[attribute_names.length..-1])
1222                 end
1223             end
1224           elsif match = /find_or_(initialize|create)_by_([_a-zA-Z]\w*)/.match(method_id.to_s)
1225             instantiator = determine_instantiator(match)
1226             attribute_names = extract_attribute_names_from_match(match)
1227             super unless all_attributes_exists?(attribute_names)
1228
1229             attributes = construct_attributes_from_arguments(attribute_names, arguments)
1230             options = { :conditions => attributes }
1231             set_readonly_option!(options)
1232
1233             find_initial(options) || send(instantiator, attributes)
1234           else
1235             super
1236           end
1237         end
1238
1239         def determine_finder(match)
1240           match.captures.first == 'all_by' ? :find_every : :find_initial
1241         end
1242
1243         def determine_deprecated_finder(match)
1244           match.captures.first == 'all_by' ? :find_all : :find_first
1245         end
1246
1247         def determine_instantiator(match)
1248           match.captures.first == 'initialize' ? :new : :create
1249         end
1250
1251         def extract_attribute_names_from_match(match)
1252           match.captures.last.split('_and_')
1253         end
1254
1255         def construct_attributes_from_arguments(attribute_names, arguments)
1256           attributes = {}
1257           attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }
1258           attributes
1259         end
1260
1261         def all_attributes_exists?(attribute_names)
1262           attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) }
1263         end
1264
1265         def attribute_condition(argument)
1266           case argument
1267             when nil   then "IS ?"
1268             when Array then "IN (?)"
1269             else            "= ?"
1270           end
1271         end
1272
1273         # Interpret Array and Hash as conditions and anything else as an id.
1274         def expand_id_conditions(id_or_conditions)
1275           case id_or_conditions
1276             when Array, Hash then id_or_conditions
1277             else sanitize_sql(primary_key => id_or_conditions)
1278           end
1279         end
1280
1281
1282         # Defines an "attribute" method (like #inheritance_column or
1283         # #table_name). A new (class) method will be created with the
1284         # given name. If a value is specified, the new method will
1285         # return that value (as a string). Otherwise, the given block
1286         # will be used to compute the value of the method.
1287         #
1288         # The original method will be aliased, with the new name being
1289         # prefixed with "original_". This allows the new method to
1290         # access the original value.
1291         #
1292         # Example:
1293         #
1294         #   class A < ActiveRecord::Base
1295         #     define_attr_method :primary_key, "sysid"
1296         #     define_attr_method( :inheritance_column ) do
1297         #       original_inheritance_column + "_id"
1298         #     end
1299         #   end
1300         def define_attr_method(name, value=nil, &block)
1301           sing = class << self; self; end
1302           sing.send :alias_method, "original_#{name}", name
1303           if block_given?
1304             sing.send :define_method, name, &block
1305           else
1306             # use eval instead of a block to work around a memory leak in dev
1307             # mode in fcgi
1308             sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
1309           end
1310         end
1311
1312       protected
1313         def subclasses #:nodoc:
1314           @@subclasses[self] ||= []
1315           @@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
1316         end
1317
1318         # Test whether the given method and optional key are scoped.
1319         def scoped?(method, key = nil) #:nodoc:
1320           if current_scoped_methods && (scope = current_scoped_methods[method])
1321             !key || scope.has_key?(key)
1322           end
1323         end
1324
1325         # Retrieve the scope for the given method and optional key.
1326         def scope(method, key = nil) #:nodoc:
1327           if current_scoped_methods && (scope = current_scoped_methods[method])
1328             key ? scope[key] : scope
1329           end
1330         end
1331
1332         def thread_safe_scoped_methods #:nodoc:
1333           scoped_methods = (Thread.current[:scoped_methods] ||= {})
1334           scoped_methods[self] ||= []
1335         end
1336        
1337         def single_threaded_scoped_methods #:nodoc:
1338           @scoped_methods ||= []
1339         end
1340        
1341         # pick up the correct scoped_methods version from @@allow_concurrency
1342         if @@allow_concurrency
1343           alias_method :scoped_methods, :thread_safe_scoped_methods
1344         else
1345           alias_method :scoped_methods, :single_threaded_scoped_methods
1346         end
1347        
1348         def current_scoped_methods #:nodoc:
1349           scoped_methods.last
1350         end
1351
1352         # Returns the class type of the record using the current module as a prefix. So descendents of
1353         # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
1354         def compute_type(type_name)
1355           modularized_name = type_name_with_module(type_name)
1356           begin
1357             instance_eval(modularized_name)
1358           rescue NameError => e
1359             instance_eval(type_name)
1360           end
1361         end
1362
1363         # Returns the class descending directly from ActiveRecord in the inheritance hierarchy.
1364         def class_of_active_record_descendant(klass)
1365           if klass.superclass == Base || klass.superclass.abstract_class?
1366             klass
1367           elsif klass.superclass.nil?
1368             raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
1369           else
1370             class_of_active_record_descendant(klass.superclass)
1371           end
1372         end
1373
1374         # Returns the name of the class descending directly from ActiveRecord in the inheritance hierarchy.
1375         def class_name_of_active_record_descendant(klass) #:nodoc:
1376           klass.base_class.name
1377         end
1378
1379         # Accepts an array, hash, or string of sql conditions and sanitizes
1380         # them into a valid SQL fragment.
1381         #   ["name='%s' and group_id='%s'", "foo'bar", 4]  returns  "name='foo''bar' and group_id='4'"
1382         #   { :name => "foo'bar", :group_id => 4 }  returns "name='foo''bar' and group_id='4'"
1383         #   "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
1384         def sanitize_sql(condition)
1385           case condition
1386             when Array; sanitize_sql_array(condition)
1387             when Hash;  sanitize_sql_hash(condition)
1388             else        condition
1389           end
1390         end
1391
1392         # Sanitizes a hash of attribute/value pairs into SQL conditions.
1393         #   { :name => "foo'bar", :group_id => 4 }
1394         #     # => "name='foo''bar' and group_id= 4"
1395         #   { :status => nil, :group_id => [1,2,3] }
1396         #     # => "status IS NULL and group_id IN (1,2,3)"
1397         def sanitize_sql_hash(attrs)
1398           conditions = attrs.map do |attr, value|
1399             "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}"
1400           end.join(' AND ')
1401
1402           replace_bind_variables(conditions, attrs.values)
1403         end
1404
1405         # Accepts an array of conditions.  The array has each value
1406         # sanitized and interpolated into the sql statement.
1407         #   ["name='%s' and group_id='%s'", "foo'bar", 4]  returns  "name='foo''bar' and group_id='4'"
1408         def sanitize_sql_array(ary)
1409           statement, *values = ary
1410           if values.first.is_a?(Hash) and statement =~ /:\w+/
1411             replace_named_bind_variables(statement, values.first)
1412           elsif statement.include?('?')
1413             replace_bind_variables(statement, values)
1414           else
1415             statement % values.collect { |value| connection.quote_string(value.to_s) }
1416           end
1417         end
1418
1419         alias_method :sanitize_conditions, :sanitize_sql
1420
1421         def replace_bind_variables(statement, values) #:nodoc:
1422           raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
1423           bound = values.dup
1424           statement.gsub('?') { quote_bound_value(bound.shift) }
1425         end
1426
1427         def replace_named_bind_variables(statement, bind_vars) #:nodoc:
1428           statement.gsub(/:(\w+)/) do
1429             match = $1.to_sym
1430             if bind_vars.include?(match)
1431               quote_bound_value(bind_vars[match])
1432             else
1433               raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
1434             end
1435           end
1436         end
1437
1438         def quote_bound_value(value) #:nodoc:
1439           if value.respond_to?(:map) && !value.is_a?(String)
1440             if value.respond_to?(:empty?) && value.empty?
1441               connection.quote(nil)
1442             else
1443               value.map { |v| connection.quote(v) }.join(',')
1444             end
1445           else
1446             connection.quote(value)
1447           end
1448         end
1449
1450         def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
1451           unless expected == provided
1452             raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
1453           end
1454         end
1455
1456         def extract_options_from_args!(args) #:nodoc:
1457           args.last.is_a?(Hash) ? args.pop : {}
1458         end
1459
1460         VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset,
1461                                :order, :select, :readonly, :group, :from, :lock ]
1462
1463         def validate_find_options(options) #:nodoc:
1464           options.assert_valid_keys(VALID_FIND_OPTIONS)
1465         end
1466
1467         def set_readonly_option!(options) #:nodoc:
1468           # Inherit :readonly from finder scope if set.  Otherwise,
1469           # if :joins is not blank then :readonly defaults to true.
1470           unless options.has_key?(:readonly)
1471             if scoped?(:find, :readonly)
1472               options[:readonly] = scope(:find, :readonly)
1473             elsif !options[:joins].blank? && !options[:select]
1474               options[:readonly] = true
1475             end
1476           end
1477         end
1478
1479         def encode_quoted_value(value) #:nodoc:
1480           quoted_value = connection.quote(value)
1481           quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "
1482           quoted_value
1483         end
1484     end
1485
1486     public
1487       # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
1488       # attributes but not yet saved (pass a hash with key names matching the associated table column names).
1489       # In both instances, valid attribute keys are determined by the column names of the associated table --
1490       # hence you can't have attributes that aren't part of the table columns.
1491       def initialize(attributes = nil)
1492         @attributes = attributes_from_column_definition
1493         @new_record = true
1494         ensure_proper_type
1495         self.attributes = attributes unless attributes.nil?
1496         yield self if block_given?
1497       end
1498
1499       # A model instance's primary key is always available as model.id
1500       # whether you name it the default 'id' or set it to something else.
1501       def id
1502         attr_name = self.class.primary_key
1503         column = column_for_attribute(attr_name)
1504         define_read_method(:id, attr_name, column) if self.class.generate_read_methods
1505         read_attribute(attr_name)
1506       end
1507
1508       # Enables Active Record objects to be used as URL parameters in Action Pack automatically.
1509       def to_param
1510         # We can't use alias_method here, because method 'id' optimizes itself on the fly.
1511         (id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes
1512       end
1513
1514       def id_before_type_cast #:nodoc:
1515         read_attribute_before_type_cast(self.class.primary_key)
1516       end
1517
1518       def quoted_id #:nodoc:
1519         quote_value(id, column_for_attribute(self.class.primary_key))
1520       end
1521
1522       # Sets the primary ID.
1523       def id=(value)
1524         write_attribute(self.class.primary_key, value)
1525       end
1526
1527       # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet.
1528       def new_record?
1529         @new_record
1530       end
1531
1532       # * No record exists: Creates a new record with values matching those of the object attributes.
1533       # * A record does exist: Updates the record with values matching those of the object attributes.
1534       def save
1535         create_or_update
1536       end
1537      
1538       # Attempts to save the record, but instead of just returning false if it couldn't happen, it raises a
1539       # RecordNotSaved exception
1540       def save!
1541         create_or_update || raise(RecordNotSaved)
1542       end
1543
1544       # Deletes the record in the database and freezes this instance to reflect that no changes should
1545       # be made (since they can't be persisted).
1546       def destroy
1547         unless new_record?
1548           connection.delete <<-end_sql, "#{self.class.name} Destroy"
1549             DELETE FROM #{self.class.table_name}
1550             WHERE #{self.class.primary_key} = #{quoted_id}
1551           end_sql
1552         end
1553
1554         freeze
1555       end
1556
1557       # Returns a clone of the record that hasn't been assigned an id yet and
1558       # is treated as a new record.  Note that this is a "shallow" clone:
1559       # it copies the object's attributes only, not its associations.
1560       # The extent of a "deep" clone is application-specific and is therefore
1561       # left to the application to implement according to its need.
1562       def clone
1563         attrs = self.attributes_before_type_cast
1564         attrs.delete(self.class.primary_key)
1565         self.class.new do |record|
1566           record.send :instance_variable_set, '@attributes', attrs
1567         end
1568       end
1569
1570       # Updates a single attribute and saves the record. This is especially useful for boolean flags on existing records.
1571       # Note: This method is overwritten by the Validation module that'll make sure that updates made with this method
1572       # doesn't get subjected to validation checks. Hence, attributes can be updated even if the full object isn't valid.
1573       def update_attribute(name, value)
1574         send(name.to_s + '=', value)
1575         save
1576       end
1577
1578       # Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will
1579       # fail and false will be returned.
1580       def update_attributes(attributes)
1581         self.attributes = attributes
1582         save
1583       end
1584      
1585       # Updates an object just like Base.update_attributes but calls save! instead of save so an exception is raised if the record is invalid.
1586       def update_attributes!(attributes)
1587         self.attributes = attributes
1588         save!
1589       end
1590
1591       # Initializes the +attribute+ to zero if nil and adds one. Only makes sense for number-based attributes. Returns self.
1592       def increment(attribute)
1593         self[attribute] ||= 0
1594         self[attribute] += 1
1595         self
1596       end
1597
1598       # Increments the +attribute+ and saves the record.
1599       def increment!(attribute)
1600         increment(attribute).update_attribute(attribute, self[attribute])
1601       end
1602
1603       # Initializes the +attribute+ to zero if nil and subtracts one. Only makes sense for number-based attributes. Returns self.
1604       def decrement(attribute)
1605         self[attribute] ||= 0
1606         self[attribute] -= 1
1607         self
1608       end
1609
1610       # Decrements the +attribute+ and saves the record.
1611       def decrement!(attribute)
1612         decrement(attribute).update_attribute(attribute, self[attribute])
1613       end
1614
1615       # Turns an +attribute+ that's currently true into false and vice versa. Returns self.
1616       def toggle(attribute)
1617         self[attribute] = !send("#{attribute}?")
1618         self
1619       end
1620
1621       # Toggles the +attribute+ and saves the record.
1622       def toggle!(attribute)
1623         toggle(attribute).update_attribute(attribute, self[attribute])
1624       end
1625
1626       # Reloads the attributes of this object from the database.
1627       # The optional options argument is passed to find when reloading so you
1628       # may do e.g. record.reload(:lock => true) to reload the same record with
1629       # an exclusive row lock.
1630       def reload(options = nil)
1631         clear_aggregation_cache
1632         clear_association_cache
1633         @attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
1634         self
1635       end
1636
1637       # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
1638       # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
1639       # (Alias for the protected read_attribute method).
1640       def [](attr_name)
1641         read_attribute(attr_name)
1642       end
1643
1644       # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
1645       # (Alias for the protected write_attribute method).
1646       def []=(attr_name, value)
1647         write_attribute(attr_name, value)
1648       end
1649
1650       # Allows you to set all the attributes at once by passing in a hash with keys
1651       # matching the attribute names (which again matches the column names). Sensitive attributes can be protected
1652       # from this form of mass-assignment by using the +attr_protected+ macro. Or you can alternatively
1653       # specify which attributes *can* be accessed in with the +attr_accessible+ macro. Then all the
1654       # attributes not included in that won't be allowed to be mass-assigned.
1655       def attributes=(new_attributes)
1656         return if new_attributes.nil?
1657         attributes = new_attributes.dup
1658         attributes.stringify_keys!
1659
1660         multi_parameter_attributes = []
1661         remove_attributes_protected_from_mass_assignment(attributes).each do |k, v|
1662           k.include?("(") ? multi_parameter_attributes << [ k, v ] : send(k + "=", v)
1663         end
1664
1665         assign_multiparameter_attributes(multi_parameter_attributes)
1666       end
1667
1668
1669       # Returns a hash of all the attributes with their names as keys and clones of their objects as values.
1670       def attributes(options = nil)
1671         attributes = clone_attributes :read_attribute
1672        
1673         if options.nil?
1674           attributes
1675         else
1676           if except = options[:except]
1677             except = Array(except).collect { |attribute| attribute.to_s }
1678             except.each { |attribute_name| attributes.delete(attribute_name) }
1679             attributes
1680           elsif only = options[:only]
1681             only = Array(only).collect { |attribute| attribute.to_s }
1682             attributes.delete_if { |key, value| !only.include?(key) }
1683             attributes
1684           else
1685             raise ArgumentError, "Options does not specify :except or :only (#{options.keys.inspect})"
1686           end
1687         end
1688       end
1689
1690       # Returns a hash of cloned attributes before typecasting and deserialization.
1691       def attributes_before_type_cast
1692         clone_attributes :read_attribute_before_type_cast
1693       end
1694
1695       # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
1696       # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
1697       def attribute_present?(attribute)
1698         value = read_attribute(attribute)
1699         !value.blank? or value == 0
1700       end
1701
1702       # Returns true if the given attribute is in the attributes hash
1703       def has_attribute?(attr_name)
1704         @attributes.has_key?(attr_name.to_s)
1705       end
1706
1707       # Returns an array of names for the attributes available on this object sorted alphabetically.
1708       def attribute_names
1709         @attributes.keys.sort
1710       end
1711
1712       # Returns the column object for the named attribute.
1713       def column_for_attribute(name)
1714         self.class.columns_hash[name.to_s]
1715       end
1716
1717       # Returns true if the +comparison_object+ is the same object, or is of the same type and has the same id.
1718       def ==(comparison_object)
1719         comparison_object.equal?(self) ||
1720           (comparison_object.instance_of?(self.class) &&
1721             comparison_object.id == id &&
1722             !comparison_object.new_record?)
1723       end
1724
1725       # Delegates to ==
1726       def eql?(comparison_object)
1727         self == (comparison_object)
1728       end
1729
1730       # Delegates to id in order to allow two records of the same type and id to work with something like:
1731       #   [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
1732       def hash
1733         id.hash
1734       end
1735
1736       # For checking respond_to? without searching the attributes (which is faster).
1737       alias_method :respond_to_without_attributes?, :respond_to?
1738
1739       # A Person object with a name attribute can ask person.respond_to?("name"), person.respond_to?("name="), and
1740       # person.respond_to?("name?") which will all return true.
1741       def respond_to?(method, include_priv = false)
1742         if @attributes.nil?
1743           return super
1744         elsif attr_name = self.class.column_methods_hash[method.to_sym]
1745           return true if @attributes.include?(attr_name) || attr_name == self.class.primary_key
1746           return false if self.class.read_methods.include?(attr_name)
1747         elsif @attributes.include?(method_name = method.to_s)
1748           return true
1749         elsif md = self.class.match_attribute_method?(method.to_s)
1750           return true if @attributes.include?(md.pre_match)
1751         end
1752         # super must be called at the end of the method, because the inherited respond_to?
1753         # would return true for generated readers, even if the attribute wasn't present
1754         super
1755       end
1756
1757       # Just freeze the attributes hash, such that associations are still accessible even on destroyed records.
1758       def freeze
1759         @attributes.freeze; self
1760       end
1761
1762       def frozen?
1763         @attributes.frozen?
1764       end
1765
1766       # Records loaded through joins with piggy-back attributes will be marked as read only as they cannot be saved and return true to this query.
1767       def readonly?
1768         @readonly == true
1769       end
1770
1771       def readonly! #:nodoc:
1772         @readonly = true
1773       end
1774
1775
1776     private
1777       def create_or_update
1778         raise ReadOnlyRecord if readonly?
1779         result = new_record? ? create : update
1780         result != false
1781       end
1782
1783       # Updates the associated record with values matching those of the instance attributes.
1784       # Returns the number of affected rows.
1785       def update
1786         connection.update(
1787           "UPDATE #{self.class.table_name} " +
1788           "SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))} " +
1789           "WHERE #{self.class.primary_key} = #{quote_value(id)}",
1790           "#{self.class.name} Update"
1791         )
1792       end
1793
1794       # Creates a record with values matching those of the instance attributes
1795       # and returns its id.
1796       def create
1797         if self.id.nil? && connection.prefetch_primary_key?(self.class.table_name)
1798           self.id = connection.next_sequence_value(self.class.sequence_name)
1799         end
1800
1801         self.id = connection.insert(
1802           "INSERT INTO #{self.class.table_name} " +
1803           "(#{quoted_column_names.join(', ')}) " +
1804           "VALUES(#{attributes_with_quotes.values.join(', ')})",
1805           "#{self.class.name} Create",
1806           self.class.primary_key, self.id, self.class.sequence_name
1807         )
1808
1809         @new_record = false
1810         id
1811       end
1812
1813       # Sets the attribute used for single table inheritance to this class name if this is not the ActiveRecord descendent.
1814       # Considering the hierarchy Reply < Message < ActiveRecord, this makes it possible to do Reply.new without having to
1815       # set Reply[Reply.inheritance_column] = "Reply" yourself. No such attribute would be set for objects of the
1816       # Message class in that example.
1817       def ensure_proper_type
1818         unless self.class.descends_from_active_record?
1819           write_attribute(self.class.inheritance_column, Inflector.demodulize(self.class.name))
1820         end
1821       end
1822
1823
1824       # Allows access to the object attributes, which are held in the @attributes hash, as were
1825       # they first-class methods. So a Person class with a name attribute can use Person#name and
1826       # Person#name= and never directly use the attributes hash -- except for multiple assigns with
1827       # ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that
1828       # the completed attribute is not nil or 0.
1829       #
1830       # It's also possible to instantiate related objects, so a Client class belonging to the clients
1831       # table with a master_id foreign key can instantiate master through Client#master.
1832       def method_missing(method_id, *args, &block)
1833         method_name = method_id.to_s
1834         if @attributes.include?(method_name) or
1835             (md = /\?$/.match(method_name) and
1836             @attributes.include?(query_method_name = md.pre_match) and
1837             method_name = query_method_name)
1838           define_read_methods if self.class.read_methods.empty? && self.class.generate_read_methods
1839           md ? query_attribute(method_name) : read_attribute(method_name)
1840         elsif self.class.primary_key.to_s == method_name
1841           id
1842         elsif md = self.class.match_attribute_method?(method_name)
1843           attribute_name, method_type = md.pre_match, md.to_s
1844           if @attributes.include?(attribute_name)
1845             __send__("attribute#{method_type}", attribute_name, *args, &block)
1846           else
1847             super
1848           end
1849         else
1850           super
1851         end
1852       end
1853
1854       # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
1855       # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
1856       def read_attribute(attr_name)
1857         attr_name = attr_name.to_s
1858         if !(value = @attributes[attr_name]).nil?
1859           if column = column_for_attribute(attr_name)
1860             if unserializable_attribute?(attr_name, column)
1861               unserialize_attribute(attr_name)
1862             else
1863               column.type_cast(value)
1864             end
1865           else
1866             value
1867           end
1868         else
1869           nil
1870         end
1871       end
1872
1873       def read_attribute_before_type_cast(attr_name)
1874         @attributes[attr_name]
1875       end
1876
1877       # Called on first read access to any given column and generates reader
1878       # methods for all columns in the columns_hash if
1879       # ActiveRecord::Base.generate_read_methods is set to true.
1880       def define_read_methods
1881         self.class.columns_hash.each do |name, column|
1882           unless respond_to_without_attributes?(name)
1883             if self.class.serialized_attributes[name]
1884               define_read_method_for_serialized_attribute(name)
1885             else
1886               define_read_method(name.to_sym, name, column)
1887             end
1888           end
1889
1890           unless respond_to_without_attributes?("#{name}?")
1891             define_question_method(name)
1892           end
1893         end
1894       end
1895
1896       # Define an attribute reader method.  Cope with nil column.
1897       def define_read_method(symbol, attr_name, column)
1898         cast_code = column.type_cast_code('v') if column
1899         access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
1900        
1901         unless attr_name.to_s == self.class.primary_key.to_s
1902           access_code = access_code.insert(0, "raise NoMethodError, 'missing attribute: #{attr_name}', caller unless @attributes.has_key?('#{attr_name}'); ")
1903           self.class.read_methods << attr_name
1904         end
1905        
1906         evaluate_read_method attr_name, "def #{symbol}; #{access_code}; end"
1907       end
1908      
1909       # Define read method for serialized attribute.
1910       def define_read_method_for_serialized_attribute(attr_name)
1911         unless attr_name.to_s == self.class.primary_key.to_s
1912           self.class.read_methods << attr_name
1913         end
1914        
1915         evaluate_read_method attr_name, "def #{attr_name}; unserialize_attribute('#{attr_name}'); end"
1916       end
1917            
1918       # Define an attribute ? method.
1919       def define_question_method(attr_name)
1920         unless attr_name.to_s == self.class.primary_key.to_s
1921           self.class.read_methods << "#{attr_name}?"
1922         end
1923        
1924         evaluate_read_method attr_name, "def #{attr_name}?; query_attribute('#{attr_name}'); end"
1925       end
1926      
1927       # Evaluate the definition for an attribute reader or ? method
1928       def evaluate_read_method(attr_name, method_definition)
1929         begin
1930           self.class.class_eval(method_definition)
1931         rescue SyntaxError => err
1932           self.class.read_methods.delete(attr_name)
1933           if logger
1934             logger.warn "Exception occurred during reader method compilation."
1935             logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
1936             logger.warn "#{err.message}"
1937           end
1938         end
1939       end
1940
1941       # Returns true if the attribute is of a text column and marked for serialization.
1942       def unserializable_attribute?(attr_name, column)
1943         column.text? && self.class.serialized_attributes[attr_name]
1944       end
1945
1946       # Returns the unserialized object of the attribute.
1947       def unserialize_attribute(attr_name)
1948         unserialized_object = object_from_yaml(@attributes[attr_name])
1949
1950         if unserialized_object.is_a?(self.class.serialized_attributes[attr_name])
1951           @attributes[attr_name] = unserialized_object
1952         else
1953           raise SerializationTypeMismatch,
1954             "#{attr_name} was supposed to be a #{self.class.serialized_attributes[attr_name]}, but was a #{unserialized_object.class.to_s}"
1955         end
1956       end
1957
1958       # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings for fixnum and float
1959       # columns are turned into nil.
1960       def write_attribute(attr_name, value)
1961         attr_name = attr_name.to_s
1962         if (column = column_for_attribute(attr_name)) && column.number?
1963           @attributes[attr_name] = convert_number_column_value(value)
1964         else
1965           @attributes[attr_name] = value
1966         end
1967       end
1968
1969       def convert_number_column_value(value)
1970         case value
1971           when FalseClass: 0
1972           when TrueClass:  1
1973           when '':         nil
1974           else value
1975         end
1976       end
1977
1978       def query_attribute(attr_name)
1979         unless value = read_attribute(attr_name)
1980           false
1981         else
1982           column = self.class.columns_hash[attr_name]
1983           if column.number?
1984             !value.zero?
1985           else
1986             !value.blank?
1987           end
1988         end
1989       end
1990
1991       def remove_attributes_protected_from_mass_assignment(attributes)
1992         if self.class.accessible_attributes.nil? && self.class.protected_attributes.nil?
1993           attributes.reject { |key, value| attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
1994         elsif self.class.protected_attributes.nil?
1995           attributes.reject { |key, value| !self.class.accessible_attributes.include?(key.gsub(/\(.+/, "").intern) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
1996         elsif self.class.accessible_attributes.nil?
1997           attributes.reject { |key, value| self.class.protected_attributes.include?(key.gsub(/\(.+/,"").intern) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
1998         end
1999       end
2000
2001       # The primary key and inheritance column can never be set by mass-assignment for security reasons.
2002       def attributes_protected_by_default
2003         default = [ self.class.primary_key, self.class.inheritance_column ]
2004         default << 'id' unless self.class.primary_key.eql? 'id'
2005         default
2006       end
2007
2008       # Returns copy of the attributes hash where all the values have been safely quoted for use in
2009       # an SQL statement.
2010       def attributes_with_quotes(include_primary_key = true)
2011         attributes.inject({}) do |quoted, (name, value)|
2012           if column = column_for_attribute(name)
2013             quoted[name] = quote_value(value, column) unless !include_primary_key && column.primary
2014           end
2015           quoted
2016         end
2017       end
2018
2019       # Quote strings appropriately for SQL statements.
2020       def quote_value(value, column = nil)
2021         self.class.connection.quote(value, column)
2022       end
2023
2024       # Deprecated, use quote_value
2025       def quote(value, column = nil)
2026         self.class.connection.quote(value, column)
2027       end
2028       deprecate :quote => :quote_value
2029      
2030      
2031       # Interpolate custom sql string in instance context.
2032       # Optional record argument is meant for custom insert_sql.
2033       def interpolate_sql(sql, record = nil)
2034         instance_eval("%@#{sql.gsub('@', '\@')}@")
2035       end
2036
2037       # Initializes the attributes array with keys matching the columns from the linked table and
2038       # the values matching the corresponding default value of that column, so
2039       # that a new instance, or one populated from a passed-in Hash, still has all the attributes
2040       # that instances loaded from the database would.
2041       def attributes_from_column_definition
2042         self.class.columns.inject({}) do |attributes, column|
2043           attributes[column.name] = column.default unless column.name == self.class.primary_key
2044           attributes
2045         end
2046       end
2047
2048       # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
2049       # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
2050       # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
2051       # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
2052       # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
2053       # s for String, and a for Array. If all the values for a given attribute is empty, the attribute will be set to nil.
2054       def assign_multiparameter_attributes(pairs)
2055         execute_callstack_for_multiparameter_attributes(
2056           extract_callstack_for_multiparameter_attributes(pairs)
2057         )
2058       end
2059
2060       # Includes an ugly hack for Time.local instead of Time.new because the latter is reserved by Time itself.
2061       def execute_callstack_for_multiparameter_attributes(callstack)
2062         errors = []
2063         callstack.each do |name, values|
2064           klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
2065           if values.empty?
2066             send(name + "=", nil)
2067           else
2068             begin
2069               send(name + "=", Time == klass ? (@@default_timezone == :utc ? klass.utc(*values) : klass.local(*values)) : klass.new(*values))
2070             rescue => ex
2071               errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
2072             end
2073           end
2074         end
2075         unless errors.empty?
2076           raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
2077         end
2078       end
2079
2080       def extract_callstack_for_multiparameter_attributes(pairs)
2081         attributes = { }
2082
2083         for pair in pairs
2084           multiparameter_name, value = pair
2085           attribute_name = multiparameter_name.split("(").first
2086           attributes[attribute_name] = [] unless attributes.include?(attribute_name)
2087
2088           unless value.empty?
2089             attributes[attribute_name] <<
2090               [ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ]
2091           end
2092         end
2093
2094         attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
2095       end
2096
2097       def type_cast_attribute_value(multiparameter_name, value)
2098         multiparameter_name =~ /\([0-9]*([a-z])\)/ ? value.send("to_" + $1) : value
2099       end
2100
2101       def find_parameter_position(multiparameter_name)
2102         multiparameter_name.scan(/\(([0-9]*).*\)/).first.first
2103       end
2104
2105       # Returns a comma-separated pair list, like "key1 = val1, key2 = val2".
2106       def comma_pair_list(hash)
2107         hash.inject([]) { |list, pair| list << "#{pair.first} = #{pair.last}" }.join(", ")
2108       end
2109
2110       def quoted_column_names(attributes = attributes_with_quotes)
2111         attributes.keys.collect do |column_name|
2112           self.class.connection.quote_column_name(column_name)
2113         end
2114       end
2115
2116       def quote_columns(quoter, hash)
2117         hash.inject({}) do |quoted, (name, value)|
2118           quoted[quoter.quote_column_name(name)] = value
2119           quoted
2120         end
2121       end
2122
2123       def quoted_comma_pair_list(quoter, hash)
2124         comma_pair_list(quote_columns(quoter, hash))
2125       end
2126
2127       def object_from_yaml(string)
2128         return string unless string.is_a?(String)
2129         YAML::load(string) rescue string
2130       end
2131
2132       def clone_attributes(reader_method = :read_attribute, attributes = {})
2133         self.attribute_names.inject(attributes) do |attributes, name|
2134           attributes[name] = clone_attribute_value(reader_method, name)
2135           attributes
2136         end
2137       end
2138
2139       def clone_attribute_value(reader_method, attribute_name)
2140         value = send(reader_method, attribute_name)
2141         value.clone
2142       rescue TypeError, NoMethodError
2143         value
2144       end
2145   end
2146 end
Note: See TracBrowser for help on using the browser.