Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100755 2164 lines (1919 sloc) 95.359 kB
48052d7 @jeremy to_xml fixes, features, and speedup. Closes #4989.
jeremy authored
1 require 'base64'
db045db @dhh Initial
dhh authored
2 require 'yaml'
aabf909 @jeremy Correct reader method generation for primary key attribute: handle ca…
jeremy authored
3 require 'set'
db045db @dhh Initial
dhh authored
4
5 module ActiveRecord #:nodoc:
6 class ActiveRecordError < StandardError #:nodoc:
7 end
605bc77 @dhh Added a better exception for when a type column is used in a table wi…
dhh authored
8 class SubclassNotFound < ActiveRecordError #:nodoc:
9 end
db045db @dhh Initial
dhh authored
10 class AssociationTypeMismatch < ActiveRecordError #:nodoc:
11 end
12 class SerializationTypeMismatch < ActiveRecordError #:nodoc:
13 end
14 class AdapterNotSpecified < ActiveRecordError # :nodoc:
15 end
16 class AdapterNotFound < ActiveRecordError # :nodoc:
17 end
18 class ConnectionNotEstablished < ActiveRecordError #:nodoc:
19 end
20 class ConnectionFailed < ActiveRecordError #:nodoc:
21 end
22 class RecordNotFound < ActiveRecordError #:nodoc:
23 end
4c7555a @dhh Fixed that Base.save should always return false if the save didn't su…
dhh authored
24 class RecordNotSaved < ActiveRecordError #:nodoc:
25 end
db045db @dhh Initial
dhh authored
26 class StatementInvalid < ActiveRecordError #:nodoc:
27 end
554597d @dhh Added named bind-style variable interpolation #281 [Michael Koziarski]
dhh authored
28 class PreparedStatementInvalid < ActiveRecordError #:nodoc:
29 end
fbf9281 @dhh Added automated optimistic locking if the field lock_version is prese…
dhh authored
30 class StaleObjectError < ActiveRecordError #:nodoc:
31 end
5b9b904 @dhh Added support for limit and offset with eager loading of has_one and …
dhh authored
32 class ConfigurationError < StandardError #:nodoc:
33 end
64fcb75 @jeremy r3618@sedna: jeremy | 2005-10-14 12:06:03 -0700
jeremy authored
34 class ReadOnlyRecord < StandardError #:nodoc:
35 end
ebbe4fb @NZKoz Replace the transaction {|transaction|..} semantics with a new Except…
NZKoz authored
36 class Rollback < StandardError #:nodoc:
37 end
5b801b5 @NZKoz Change the implementation of ActiveRecord's attribute reader and writ…
NZKoz authored
38
39 # Raised when you've tried to access a column, which wasn't
40 # loaded by your finder. Typically this is because :select
41 # has been specified
42 class MissingAttributeError < NoMethodError
43 end
e48b062 @jeremy Sanitize Base#inspect. Closes #8392.
jeremy authored
44
d2fefbe @dhh Added MultiparameterAssignmentErrors and AttributeAssignmentError exc…
dhh authored
45 class AttributeAssignmentError < ActiveRecordError #:nodoc:
46 attr_reader :exception, :attribute
47 def initialize(message, exception, attribute)
48 @exception = exception
49 @attribute = attribute
50 @message = message
51 end
52 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
53
d2fefbe @dhh Added MultiparameterAssignmentErrors and AttributeAssignmentError exc…
dhh authored
54 class MultiparameterAssignmentErrors < ActiveRecordError #:nodoc:
55 attr_reader :errors
56 def initialize(errors)
57 @errors = errors
58 end
59 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
60
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
61 # Active Record objects don't specify their attributes directly, but rather infer them from the table definition with
db045db @dhh Initial
dhh authored
62 # which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change
63 # is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
64 # database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.
65 #
db045db @dhh Initial
dhh authored
66 # See the mapping rules in table_name and the full example in link:files/README.html for more insight.
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
67 #
db045db @dhh Initial
dhh authored
68 # == Creation
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
69 #
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
70 # Active Records accept constructor parameters either in a hash or as a block. The hash method is especially useful when
db045db @dhh Initial
dhh authored
71 # you're receiving the data from somewhere else, like a HTTP request. It works like this:
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
72 #
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
73 # user = User.new(:name => "David", :occupation => "Code Artist")
db045db @dhh Initial
dhh authored
74 # user.name # => "David"
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
75 #
db045db @dhh Initial
dhh authored
76 # You can also use block initialization:
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
77 #
db045db @dhh Initial
dhh authored
78 # user = User.new do |u|
79 # u.name = "David"
80 # u.occupation = "Code Artist"
81 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
82 #
db045db @dhh Initial
dhh authored
83 # And of course you can just create a bare object and specify the attributes after the fact:
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
84 #
db045db @dhh Initial
dhh authored
85 # user = User.new
86 # user.name = "David"
87 # user.occupation = "Code Artist"
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
88 #
db045db @dhh Initial
dhh authored
89 # == Conditions
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
90 #
c5ec16e @dhh Added simple hash conditions to find that'll just convert hash to an …
dhh authored
91 # Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement.
db045db @dhh Initial
dhh authored
92 # The array form is to be used when the condition input is tainted and requires sanitization. The string form can
c5ec16e @dhh Added simple hash conditions to find that'll just convert hash to an …
dhh authored
93 # be used for statements that don't involve tainted data. The hash form works much like the array form, except
2876707 @jeremy Pass a range in :conditions to use the SQL BETWEEN operator. Closes #…
jeremy authored
94 # only equality and range is possible. Examples:
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
95 #
48052d7 @jeremy to_xml fixes, features, and speedup. Closes #4989.
jeremy authored
96 # class User < ActiveRecord::Base
db045db @dhh Initial
dhh authored
97 # def self.authenticate_unsafely(user_name, password)
3dfa56c @dhh Updated all references to the old find_first and find_all to use the …
dhh authored
98 # find(:first, :conditions => "user_name = '#{user_name}' AND password = '#{password}'")
db045db @dhh Initial
dhh authored
99 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
100 #
db045db @dhh Initial
dhh authored
101 # def self.authenticate_safely(user_name, password)
3dfa56c @dhh Updated all references to the old find_first and find_all to use the …
dhh authored
102 # find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ])
db045db @dhh Initial
dhh authored
103 # end
c5ec16e @dhh Added simple hash conditions to find that'll just convert hash to an …
dhh authored
104 #
105 # def self.authenticate_safely_simply(user_name, password)
106 # find(:first, :conditions => { :user_name => user_name, :password => password })
107 # end
db045db @dhh Initial
dhh authored
108 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
109 #
2575b3b @dhh Added extra words of caution for guarding against SQL-injection attacks
dhh authored
110 # The <tt>authenticate_unsafely</tt> method inserts the parameters directly into the query and is thus susceptible to SQL-injection
c5ec16e @dhh Added simple hash conditions to find that'll just convert hash to an …
dhh authored
111 # attacks if the <tt>user_name</tt> and +password+ parameters come directly from a HTTP request. The <tt>authenticate_safely</tt> and
112 # <tt>authenticate_safely_simply</tt> both will sanitize the <tt>user_name</tt> and +password+ before inserting them in the query,
113 # which will ensure that an attacker can't escape the query and fake the login (or worse).
2575b3b @dhh Added extra words of caution for guarding against SQL-injection attacks
dhh authored
114 #
5cd38ca @dhh Added documentation about named bind variables
dhh authored
115 # When using multiple parameters in the conditions, it can easily become hard to read exactly what the fourth or fifth
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
116 # question mark is supposed to represent. In those cases, you can resort to named bind variables instead. That's done by replacing
5cd38ca @dhh Added documentation about named bind variables
dhh authored
117 # the question marks with symbols and supplying a hash with values for the matching symbol keys:
118 #
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
119 # Company.find(:first, [
120 # "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
5cd38ca @dhh Added documentation about named bind variables
dhh authored
121 # { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
122 # ])
123 #
c5ec16e @dhh Added simple hash conditions to find that'll just convert hash to an …
dhh authored
124 # Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
125 # operator. For instance:
126 #
127 # Student.find(:all, :conditions => { :first_name => "Harvey", :status => 1 })
128 # Student.find(:all, :conditions => params[:student])
129 #
2876707 @jeremy Pass a range in :conditions to use the SQL BETWEEN operator. Closes #…
jeremy authored
130 # A range may be used in the hash to use the SQL BETWEEN operator:
131 #
132 # Student.find(:all, :conditions => { :grade => 9..12 })
c5ec16e @dhh Added simple hash conditions to find that'll just convert hash to an …
dhh authored
133 #
db045db @dhh Initial
dhh authored
134 # == Overwriting default accessors
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
135 #
db045db @dhh Initial
dhh authored
136 # All column values are automatically available through basic accessors on the Active Record object, but some times you
137 # want to specialize this behavior. This can be done by either by overwriting the default accessors (using the same
138 # name as the attribute) calling read_attribute(attr_name) and write_attribute(attr_name, value) to actually change things.
139 # Example:
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
140 #
db045db @dhh Initial
dhh authored
141 # class Song < ActiveRecord::Base
142 # # Uses an integer of seconds to hold the length of the song
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
143 #
db045db @dhh Initial
dhh authored
144 # def length=(minutes)
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
145 # write_attribute(:length, minutes * 60)
db045db @dhh Initial
dhh authored
146 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
147 #
db045db @dhh Initial
dhh authored
148 # def length
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
149 # read_attribute(:length) / 60
db045db @dhh Initial
dhh authored
150 # end
151 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
152 #
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
153 # You can alternatively use self[:attribute]=(value) and self[:attribute] instead of write_attribute(:attribute, vaule) and
154 # read_attribute(:attribute) as a shorter form.
155 #
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
156 # == Accessing attributes before they have been typecasted
4eab375 @dhh Finished polishing API docs
dhh authored
157 #
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
158 # Sometimes you want to be able to read the raw attribute data without having the column-determined typecast run its course first.
4eab375 @dhh Finished polishing API docs
dhh authored
159 # That can be done by using the <attribute>_before_type_cast accessors that all attributes have. For example, if your Account model
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
160 # has a balance attribute, you can call account.balance_before_type_cast or account.id_before_type_cast.
4eab375 @dhh Finished polishing API docs
dhh authored
161 #
162 # This is especially useful in validation situations where the user might supply a string for an integer field and you want to display
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
163 # the original string back in an error message. Accessing the attribute normally would typecast the string to 0, which isn't what you
4eab375 @dhh Finished polishing API docs
dhh authored
164 # want.
165 #
ac8fd7d @dhh Added dynamic attribute-based finders as a cleaner way of getting obj…
dhh authored
166 # == Dynamic attribute-based finders
167 #
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
168 # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by
302c23d @dhh Fixed Base#find to honor the documentation on how :joins work and mak…
dhh authored
169 # 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,
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
170 # Person.find_all_by_last_name, Payment.find_by_transaction_id. So instead of writing
171 # <tt>Person.find(:first, ["user_name = ?", user_name])</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
302c23d @dhh Fixed Base#find to honor the documentation on how :joins work and mak…
dhh authored
172 # 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>.
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
173 #
ac8fd7d @dhh Added dynamic attribute-based finders as a cleaner way of getting obj…
dhh authored
174 # It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
175 # <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
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
176 # <tt>Person.find(:first, ["user_name = ? AND password = ?", user_name, password])</tt>, you just do
ac8fd7d @dhh Added dynamic attribute-based finders as a cleaner way of getting obj…
dhh authored
177 # <tt>Person.find_by_user_name_and_password(user_name, password)</tt>.
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
178 #
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
179 # It's even possible to use all the additional parameters to find. For example, the full interface for Payment.find_all_by_amount
180 # is actually Payment.find_all_by_amount(amount, options). And the full interface to Person.find_by_user_name is
181 # actually Person.find_by_user_name(user_name, options). So you could call <tt>Payment.find_all_by_amount(50, :order => "created_on")</tt>.
959f362 @dhh Added find_all style to the new dynamic finders
dhh authored
182 #
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
183 # The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with
184 # <tt>find_or_create_by_</tt> and will return the object if it already exists and otherwise creates it, then returns it. Example:
185 #
186 # # No 'Summer' tag exists
187 # Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
188 #
189 # # Now the 'Summer' tag does exist
190 # Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
191 #
d19e464 @sstephenson Added find_or_initialize_by_X which works like find_or_create_by_X bu…
sstephenson authored
192 # Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without saving it first. Example:
193 #
194 # # No 'Winter' tag exists
195 # winter = Tag.find_or_initialize_by_name("Winter")
85fbb22 @dhh Backed out of new_record? to new? transformation as it would screw up…
dhh authored
196 # winter.new_record? # true
d19e464 @sstephenson Added find_or_initialize_by_X which works like find_or_create_by_X bu…
sstephenson authored
197 #
14cc8d2 @jeremy find_or_create_by_* takes a hash so you can create with more attribut…
jeremy authored
198 # To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of
199 # a list of parameters. For example:
200 #
201 # Tag.find_or_create_by_name(:name => "rails", :creator => current_user)
202 #
203 # That will either find an existing tag named "rails", or create a new one while setting the user that created it.
204 #
098fa94 @dhh Fixed documentation snafus #575, #576, #577, #585
dhh authored
205 # == Saving arrays, hashes, and other non-mappable objects in text columns
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
206 #
207 # 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+.
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
208 # This makes it possible to store arrays, hashes, and other non-mappable objects without doing any additional work. Example:
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
209 #
db045db @dhh Initial
dhh authored
210 # class User < ActiveRecord::Base
211 # serialize :preferences
212 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
213 #
ca2eb16 Fix syntax error in documentation. Closes #4679. [mislav@nippur.irb.hr]
Marcel Molina authored
214 # user = User.create(:preferences => { "background" => "black", "display" => large })
db045db @dhh Initial
dhh authored
215 # User.find(user.id).preferences # => { "background" => "black", "display" => large }
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
216 #
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
217 # You can also specify a class option as the second parameter that'll raise an exception if a serialized object is retrieved as a
db045db @dhh Initial
dhh authored
218 # descendent of a class not in the hierarchy. Example:
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
219 #
db045db @dhh Initial
dhh authored
220 # class User < ActiveRecord::Base
66f44e6 @dhh Updated documentation for serialize
dhh authored
221 # serialize :preferences, Hash
db045db @dhh Initial
dhh authored
222 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
223 #
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
224 # user = User.create(:preferences => %w( one two three ))
db045db @dhh Initial
dhh authored
225 # User.find(user.id).preferences # raises SerializationTypeMismatch
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
226 #
db045db @dhh Initial
dhh authored
227 # == Single table inheritance
228 #
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
229 # Active Record allows inheritance by storing the name of the class in a column that by default is called "type" (can be changed
db045db @dhh Initial
dhh authored
230 # by overwriting <tt>Base.inheritance_column</tt>). This means that an inheritance looking like this:
231 #
232 # class Company < ActiveRecord::Base; end
233 # class Firm < Company; end
234 # class Client < Company; end
235 # class PriorityClient < Client; end
236 #
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
237 # When you do Firm.create(:name => "37signals"), this record will be saved in the companies table with type = "Firm". You can then
238 # fetch this row again using Company.find(:first, "name = '37signals'") and it will return a Firm object.
db045db @dhh Initial
dhh authored
239 #
f033833 @dhh Improving documentation...
dhh authored
240 # 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
241 # like normal subclasses with no special magic for differentiating between them or reloading the right type with find.
242 #
db045db @dhh Initial
dhh authored
243 # Note, all the attributes for all the cases are kept in the same table. Read more:
244 # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
245 #
db045db @dhh Initial
dhh authored
246 # == Connection to multiple databases in different models
247 #
248 # Connections are usually created through ActiveRecord::Base.establish_connection and retrieved by ActiveRecord::Base.connection.
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
249 # All classes inheriting from ActiveRecord::Base will use this connection. But you can also set a class-specific connection.
db045db @dhh Initial
dhh authored
250 # For example, if Course is a ActiveRecord::Base, but resides in a different database you can just say Course.establish_connection
251 # and Course *and all its subclasses* will use this connection instead.
252 #
253 # This feature is implemented by keeping a connection pool in ActiveRecord::Base that is a Hash indexed by the class. If a connection is
254 # requested, the retrieve_connection method will go up the class-hierarchy until a connection is found in the connection pool.
255 #
256 # == Exceptions
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
257 #
db045db @dhh Initial
dhh authored
258 # * +ActiveRecordError+ -- generic error class and superclass of all other errors raised by Active Record
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
259 # * +AdapterNotSpecified+ -- the configuration hash used in <tt>establish_connection</tt> didn't include a
db045db @dhh Initial
dhh authored
260 # <tt>:adapter</tt> key.
1aab0e2 @dhh Doc fixes #1775, #1776 [jon@instance-design.co.uk]
dhh authored
261 # * +AdapterNotFound+ -- the <tt>:adapter</tt> key used in <tt>establish_connection</tt> specified an non-existent adapter
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
262 # (or a bad spelling of an existing one).
263 # * +AssociationTypeMismatch+ -- the object assigned to the association wasn't of the type specified in the association definition.
264 # * +SerializationTypeMismatch+ -- the object serialized wasn't of the class specified as the second parameter.
db045db @dhh Initial
dhh authored
265 # * +ConnectionNotEstablished+ -- no connection has been established. Use <tt>establish_connection</tt> before querying.
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
266 # * +RecordNotFound+ -- no record responded to the find* method.
db045db @dhh Initial
dhh authored
267 # Either the row with the given ID doesn't exist or the row didn't meet the additional restrictions.
268 # * +StatementInvalid+ -- the database server rejected the SQL statement. The precise error is added in the message.
269 # Either the record with the given ID doesn't exist or the record didn't meet the additional restrictions.
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
270 # * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
271 # +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
d2fefbe @dhh Added MultiparameterAssignmentErrors and AttributeAssignmentError exc…
dhh authored
272 # objects that should be inspected to determine which attributes triggered the errors.
273 # * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
274 # You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
5707027 @dhh Added better exception error when unknown column types are used with …
dhh authored
275 #
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
276 # *Note*: The attributes listed are class-level attributes (accessible from both the class and instance level).
db045db @dhh Initial
dhh authored
277 # So it's possible to assign a logger to the class through Base.logger= which will then be used by all
278 # instances in the current object space.
279 class Base
280 # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then passed
281 # on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
1a11bff @technoweenie Don't create instance writer methods for class attributes. Closes #7…
technoweenie authored
282 cattr_accessor :logger, :instance_writer => false
e694114 @jeremy Deprecation: removed Reloadable.
jeremy authored
283
db045db @dhh Initial
dhh authored
284 def self.inherited(child) #:nodoc:
285 @@subclasses[self] ||= []
286 @@subclasses[self] << child
287 super
288 end
e694114 @jeremy Deprecation: removed Reloadable.
jeremy authored
289
fed7d33 @dhh Fixed documentation
dhh authored
290 def self.reset_subclasses #:nodoc:
bfbf6bb @jamis Allow ARStore::Session to indicate that it should not be reloaded in …
jamis authored
291 nonreloadables = []
e7f61ea @jamis squash the memleak in dev mode finally (fingers crossed, here)
jamis authored
292 subclasses.each do |klass|
74165eb @seckar New dependencies implementation
seckar authored
293 unless Dependencies.autoloaded? klass
bfbf6bb @jamis Allow ARStore::Session to indicate that it should not be reloaded in …
jamis authored
294 nonreloadables << klass
295 next
296 end
e7f61ea @jamis squash the memleak in dev mode finally (fingers crossed, here)
jamis authored
297 klass.instance_variables.each { |var| klass.send(:remove_instance_variable, var) }
298 klass.instance_methods(false).each { |m| klass.send :undef_method, m }
299 end
bfbf6bb @jamis Allow ARStore::Session to indicate that it should not be reloaded in …
jamis authored
300 @@subclasses = {}
301 nonreloadables.each { |klass| (@@subclasses[klass.superclass] ||= []) << klass }
3c0129a @dhh Fixed memory leak with Active Record classes when Dependencies.mechan…
dhh authored
302 end
303
db045db @dhh Initial
dhh authored
304 @@subclasses = {}
c4a3634 @jeremy Corrected @@configurations typo. #1410 [david@ruppconsulting.com]
jeremy authored
305
1a11bff @technoweenie Don't create instance writer methods for class attributes. Closes #7…
technoweenie authored
306 cattr_accessor :configurations, :instance_writer => false
c4a3634 @jeremy Corrected @@configurations typo. #1410 [david@ruppconsulting.com]
jeremy authored
307 @@configurations = {}
308
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
309 # Accessor for the prefix type that will be prepended to every primary key column name. The options are :table_name and
db045db @dhh Initial
dhh authored
310 # :table_name_with_underscore. If the first is specified, the Product class will look for "productid" instead of "id" as
311 # the primary column. If the latter is specified, the Product class will look for "product_id" instead of "id". Remember
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
312 # that this is a global setting for all Active Records.
1a11bff @technoweenie Don't create instance writer methods for class attributes. Closes #7…
technoweenie authored
313 cattr_accessor :primary_key_prefix_type, :instance_writer => false
db045db @dhh Initial
dhh authored
314 @@primary_key_prefix_type = nil
315
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
316 # Accessor for the name of the prefix string to prepend to every table name. So if set to "basecamp_", all
098fa94 @dhh Fixed documentation snafus #575, #576, #577, #585
dhh authored
317 # table names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient way of creating a namespace
db045db @dhh Initial
dhh authored
318 # for tables in a shared database. By default, the prefix is the empty string.
1a11bff @technoweenie Don't create instance writer methods for class attributes. Closes #7…
technoweenie authored
319 cattr_accessor :table_name_prefix, :instance_writer => false
db045db @dhh Initial
dhh authored
320 @@table_name_prefix = ""
321
322 # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
323 # "people_basecamp"). By default, the suffix is the empty string.
1a11bff @technoweenie Don't create instance writer methods for class attributes. Closes #7…
technoweenie authored
324 cattr_accessor :table_name_suffix, :instance_writer => false
db045db @dhh Initial
dhh authored
325 @@table_name_suffix = ""
326
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
327 # Indicates whether or not table names should be the pluralized versions of the corresponding class names.
328 # If true, the default table name for a +Product+ class will be +products+. If false, it would just be +product+.
db045db @dhh Initial
dhh authored
329 # See table_name for the full rules on table/class naming. This is true, by default.
1a11bff @technoweenie Don't create instance writer methods for class attributes. Closes #7…
technoweenie authored
330 cattr_accessor :pluralize_table_names, :instance_writer => false
db045db @dhh Initial
dhh authored
331 @@pluralize_table_names = true
332
911614d @dhh Added ActiveRecord::Base.colorize_logging to control whether to use c…
dhh authored
333 # Determines whether or not to use ANSI codes to colorize the logging statements committed by the connection adapter. These colors
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
334 # make it much easier to overview things during debugging (when used through a reader like +tail+ and on a black background), but
911614d @dhh Added ActiveRecord::Base.colorize_logging to control whether to use c…
dhh authored
335 # may complicate matters if you use software like syslog. This is true, by default.
1a11bff @technoweenie Don't create instance writer methods for class attributes. Closes #7…
technoweenie authored
336 cattr_accessor :colorize_logging, :instance_writer => false
911614d @dhh Added ActiveRecord::Base.colorize_logging to control whether to use c…
dhh authored
337 @@colorize_logging = true
338
60de8c1 @dhh Added Base.default_timezone accessor that determines whether to use T…
dhh authored
339 # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates and times from the database.
340 # This is set to :local by default.
1a11bff @technoweenie Don't create instance writer methods for class attributes. Closes #7…
technoweenie authored
341 cattr_accessor :default_timezone, :instance_writer => false
60de8c1 @dhh Added Base.default_timezone accessor that determines whether to use T…
dhh authored
342 @@default_timezone = :local
d8641ca @jeremy CHANGED DEFAULT: set ActiveRecord::Base.allow_concurrency to false. …
jeremy authored
343
6049977 @dhh Fixed that each request with the WEBrick adapter would open a new dat…
dhh authored
344 # Determines whether or not to use a connection for each thread, or a single shared connection for all threads.
d8641ca @jeremy CHANGED DEFAULT: set ActiveRecord::Base.allow_concurrency to false. …
jeremy authored
345 # Defaults to false. Set to true if you're writing a threaded application.
1a11bff @technoweenie Don't create instance writer methods for class attributes. Closes #7…
technoweenie authored
346 cattr_accessor :allow_concurrency, :instance_writer => false
25fb2db @jeremy Dynamically set allow_concurrency. Closes #4044.
jeremy authored
347 @@allow_concurrency = false
d8641ca @jeremy CHANGED DEFAULT: set ActiveRecord::Base.allow_concurrency to false. …
jeremy authored
348
24c3599 @sstephenson Support using different database adapters for development and test wi…
sstephenson authored
349 # Specifies the format to use when dumping the database schema with Rails'
350 # Rakefile. If :sql, the schema is dumped as (potentially database-
351 # specific) SQL statements. If :ruby, the schema is dumped as an
352 # ActiveRecord::Schema file which can be loaded into any database that
353 # supports migrations. Use :ruby if you want to have different database
354 # adapters for, e.g., your development and test environments.
1a11bff @technoweenie Don't create instance writer methods for class attributes. Closes #7…
technoweenie authored
355 cattr_accessor :schema_format , :instance_writer => false
660952e @dhh CHANGED DEFAULT: ActiveRecord::Base.schema_format is now :ruby by def…
dhh authored
356 @@schema_format = :ruby
f218771 Add option (true by default) to generate reader methods for each attr…
Marcel Molina authored
357
db045db @dhh Initial
dhh authored
358 class << self # Class methods
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
359 # Find operates with three different retrieval approaches:
7669011 @dhh Fixes for postgresql testing #1129, #1130, #1131
dhh authored
360 #
361 # * 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]).
362 # If no record can be found for all of the listed ids, then RecordNotFound will be raised.
363 # * Find first: This will return the first record matched by the options used. These options can either be specific
364 # conditions or merely an order. If no record can matched, nil is returned.
7d01005 @dhh Fixed documentation and prepared for release of 0.12
dhh authored
365 # * Find all: This will return all the records matched by the options used. If no records are found, an empty array is returned.
7669011 @dhh Fixes for postgresql testing #1129, #1130, #1131
dhh authored
366 #
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
367 # All approaches accept an option hash as their last parameter. The options are:
7669011 @dhh Fixes for postgresql testing #1129, #1130, #1131
dhh authored
368 #
515886a @dhh Added documentation for new Base.find API and eager association loading
dhh authored
369 # * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
370 # * <tt>:order</tt>: An SQL fragment like "created_at DESC, name".
3309268 @jeremy Add :group option, correspond to GROUP BY, to the find method and to …
jeremy authored
371 # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
515886a @dhh Added documentation for new Base.find API and eager association loading
dhh authored
372 # * <tt>:limit</tt>: An integer determining the limit on the number of rows that should be returned.
39c64ff @jeremy Clarify :offset docs. Closes #3733.
jeremy authored
373 # * <tt>:offset</tt>: An integer determining the offset from where the rows should be fetched. So at 5, it would skip rows 0 through 4.
515886a @dhh Added documentation for new Base.find API and eager association loading
dhh authored
374 # * <tt>:joins</tt>: An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
64fcb75 @jeremy r3618@sedna: jeremy | 2005-10-14 12:06:03 -0700
jeremy authored
375 # The records will be returned read-only since they will have attributes that do not correspond to the table's columns.
7219e82 @jeremy HABTM finder sets :readonly => false. Closes #2525.
jeremy authored
376 # Pass :readonly => false to override.
515886a @dhh Added documentation for new Base.find API and eager association loading
dhh authored
377 # * <tt>:include</tt>: Names associations that should be loaded alongside using LEFT OUTER JOINs. The symbols named refer
378 # to already defined associations. See eager loading under Associations.
6f05696 @dhh Added :select option to find which can specify a different value than…
dhh authored
379 # * <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
380 # include the joined columns.
d3cf2a6 Document find's :from option. Closes #5762. [andrew@redlinesoftware.com]
Marcel Molina authored
381 # * <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
382 # of a database view).
64fcb75 @jeremy r3618@sedna: jeremy | 2005-10-14 12:06:03 -0700
jeremy authored
383 # * <tt>:readonly</tt>: Mark the returned records read-only so they cannot be saved or updated.
15aa6e0 @jeremy r4644@asus: jeremy | 2006-06-16 14:57:03 -0700
jeremy authored
384 # * <tt>:lock</tt>: An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE".
385 # :lock => true gives connection's default exclusive lock, usually "FOR UPDATE".
7669011 @dhh Fixes for postgresql testing #1129, #1130, #1131
dhh authored
386 #
515886a @dhh Added documentation for new Base.find API and eager association loading
dhh authored
387 # Examples for find by id:
db045db @dhh Initial
dhh authored
388 # Person.find(1) # returns the object for ID = 1
389 # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
390 # Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
57ed93e @dhh Fixed that Base#find will return an array if given an array -- regard…
dhh authored
391 # Person.find([1]) # returns an array for objects the object with ID = 1
515886a @dhh Added documentation for new Base.find API and eager association loading
dhh authored
392 # Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
393 #
1e9e198 @jeremy Note that find results may not be in the same order as the id argumen…
jeremy authored
394 # Note that returned records may not be in the same order as the ids you
395 # provide since database rows are unordered. Give an explicit :order
396 # to ensure the results are sorted.
397 #
515886a @dhh Added documentation for new Base.find API and eager association loading
dhh authored
398 # Examples for find first:
7d01005 @dhh Fixed documentation and prepared for release of 0.12
dhh authored
399 # Person.find(:first) # returns the first object fetched by SELECT * FROM people
515886a @dhh Added documentation for new Base.find API and eager association loading
dhh authored
400 # Person.find(:first, :conditions => [ "user_name = ?", user_name])
401 # Person.find(:first, :order => "created_on DESC", :offset => 5)
402 #
403 # Examples for find all:
7d01005 @dhh Fixed documentation and prepared for release of 0.12
dhh authored
404 # Person.find(:all) # returns an array of objects for all the rows fetched by SELECT * FROM people
515886a @dhh Added documentation for new Base.find API and eager association loading
dhh authored
405 # Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
406 # Person.find(:all, :offset => 10, :limit => 10)
407 # Person.find(:all, :include => [ :account, :friends ])
3309268 @jeremy Add :group option, correspond to GROUP BY, to the find method and to …
jeremy authored
408 # Person.find(:all, :group => "category")
15aa6e0 @jeremy r4644@asus: jeremy | 2006-06-16 14:57:03 -0700
jeremy authored
409 #
410 # Example for find with a lock. Imagine two concurrent transactions:
411 # each will read person.visits == 2, add 1 to it, and save, resulting
412 # in two saves of person.visits = 3. By locking the row, the second
413 # transaction has to wait until the first is finished; we get the
414 # expected person.visits == 4.
415 # Person.transaction do
416 # person = Person.find(1, :lock => true)
417 # person.visits += 1
418 # person.save!
419 # end
6bd672e @dhh Added that Base#find takes an optional options hash, including :condi…
dhh authored
420 def find(*args)
edd68a5 @dhh Refactored in use of extract_options! (closes #9079) [josh]
dhh authored
421 options = args.extract_options!
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
422 validate_find_options(options)
423 set_readonly_option!(options)
64fcb75 @jeremy r3618@sedna: jeremy | 2005-10-14 12:06:03 -0700
jeremy authored
424
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
425 case args.first
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
426 when :first then find_initial(options)
427 when :all then find_every(options)
428 else find_from_ids(args, options)
db045db @dhh Initial
dhh authored
429 end
430 end
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
431
3dfa56c @dhh Updated all references to the old find_first and find_all to use the …
dhh authored
432 # Works like find(:all), but requires a complete SQL string. Examples:
db045db @dhh Initial
dhh authored
433 # Post.find_by_sql "SELECT p.*, c.author FROM posts p, comments c WHERE p.id = c.post_id"
a775cb1 @dhh Added the option for sanitizing find_by_sql and the offset parts in r…
dhh authored
434 # Post.find_by_sql ["SELECT * FROM posts WHERE author = ? AND created > ?", author_id, start_date]
db045db @dhh Initial
dhh authored
435 def find_by_sql(sql)
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
436 connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
db045db @dhh Initial
dhh authored
437 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
438
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
439 # Returns true if the given +id+ represents the primary key of a record in the database, false otherwise.
8085cbf @dhh Added support for conditions on Base.exists? (closes #5689) [josh@jos…
dhh authored
440 # You can also pass a set of SQL conditions.
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
441 # Example:
442 # Person.exists?(5)
58ebf30 @jeremy The exists? class method should treat a string argument as an id rath…
jeremy authored
443 # Person.exists?('5')
8085cbf @dhh Added support for conditions on Base.exists? (closes #5689) [josh@jos…
dhh authored
444 # Person.exists?(:name => "David")
58ebf30 @jeremy The exists? class method should treat a string argument as an id rath…
jeremy authored
445 # Person.exists?(['name LIKE ?', "%#{query}%"])
446 def exists?(id_or_conditions)
c4c6662 @NZKoz Performance enhancement for Base.exists?. CLoses #8769. [hasmanyjosh]
NZKoz authored
447 !find(:first, :select => "#{table_name}.#{primary_key}", :conditions => expand_id_conditions(id_or_conditions)).nil?
58ebf30 @jeremy The exists? class method should treat a string argument as an id rath…
jeremy authored
448 rescue ActiveRecord::ActiveRecordError
449 false
db045db @dhh Initial
dhh authored
450 end
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
451
db045db @dhh Initial
dhh authored
452 # Creates an object, instantly saves it as a record (if the validation permits it), and returns it. If the save
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
453 # fails under validations, the unsaved object is still returned.
db045db @dhh Initial
dhh authored
454 def create(attributes = nil)
efa81da @dhh Added the option of supplying an array of ids and attributes to Base#…
dhh authored
455 if attributes.is_a?(Array)
456 attributes.collect { |attr| create(attr) }
457 else
458 object = new(attributes)
459 object.save
460 object
461 end
db045db @dhh Initial
dhh authored
462 end
463
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
464 # Finds the record from the passed +id+, instantly saves it with the passed +attributes+ (if the validation permits it),
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
465 # and returns it. If the save fails under validations, the unsaved object is still returned.
5e99422 @dhh Updated docs (closes #3799) [ruby@bobsilva.com]
dhh authored
466 #
467 # The arguments may also be given as arrays in which case the update method is called for each pair of +id+ and
468 # +attributes+ and an array of objects is returned.
469 #
470 # Example of updating one record:
471 # Person.update(15, {:user_name => 'Samuel', :group => 'expert'})
472 #
473 # Example of updating multiple records:
474 # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
475 # Person.update(people.keys, people.values)
db045db @dhh Initial
dhh authored
476 def update(id, attributes)
efa81da @dhh Added the option of supplying an array of ids and attributes to Base#…
dhh authored
477 if id.is_a?(Array)
478 idx = -1
479 id.collect { |id| idx += 1; update(id, attributes[idx]) }
480 else
481 object = find(id)
482 object.update_attributes(attributes)
483 object
484 end
db045db @dhh Initial
dhh authored
485 end
486
efa81da @dhh Added the option of supplying an array of ids and attributes to Base#…
dhh authored
487 # Deletes the record with the given +id+ without instantiating an object first. If an array of ids is provided, all of them
488 # are deleted.
648b8fd @dhh Added Base.destroy and Base.delete to remove records without holding …
dhh authored
489 def delete(id)
98165fd @jeremy Consistently quote primary key column names. Closes #7763.
jeremy authored
490 delete_all([ "#{connection.quote_column_name(primary_key)} IN (?)", id ])
648b8fd @dhh Added Base.destroy and Base.delete to remove records without holding …
dhh authored
491 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
492
648b8fd @dhh Added Base.destroy and Base.delete to remove records without holding …
dhh authored
493 # Destroys the record with the given +id+ by instantiating the object and calling #destroy (all the callbacks are the triggered).
efa81da @dhh Added the option of supplying an array of ids and attributes to Base#…
dhh authored
494 # If an array of ids is provided, all of them are destroyed.
648b8fd @dhh Added Base.destroy and Base.delete to remove records without holding …
dhh authored
495 def destroy(id)
efa81da @dhh Added the option of supplying an array of ids and attributes to Base#…
dhh authored
496 id.is_a?(Array) ? id.each { |id| destroy(id) } : find(id).destroy
648b8fd @dhh Added Base.destroy and Base.delete to remove records without holding …
dhh authored
497 end
498
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
499 # Updates all records with the SET-part of an SQL update statement in +updates+ and returns an integer with the number of rows updated.
69cb942 @dhh Changed the interface on AbstractAdapter to require that adapters ret…
dhh authored
500 # A subset of the records can be selected by specifying +conditions+. Example:
db045db @dhh Initial
dhh authored
501 # Billing.update_all "category = 'authorized', approved = 1", "author = 'David'"
a38f28f @jeremy Base.update_all :order and :limit options. Useful for MySQL updates t…
jeremy authored
502 #
503 # Optional :order and :limit options may be given as the third parameter,
504 # but their behavior is database-specific.
505 def update_all(updates, conditions = nil, options = {})
5acea7f @jeremy update_all can take a Hash argument. sanitize_sql splits into two met…
jeremy authored
506 sql = "UPDATE #{table_name} SET #{sanitize_sql_for_assignment(updates)} "
a38f28f @jeremy Base.update_all :order and :limit options. Useful for MySQL updates t…
jeremy authored
507 scope = scope(:find)
508 add_conditions!(sql, conditions, scope)
509 add_order!(sql, options[:order], scope)
510 add_limit!(sql, options, scope)
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
511 connection.update(sql, "#{name} Update")
db045db @dhh Initial
dhh authored
512 end
0d2db8a @dhh Added Base.update_collection that can update an array of id/attribute…
dhh authored
513
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
514 # Destroys the objects for all the records that match the +condition+ by instantiating each object and calling
db045db @dhh Initial
dhh authored
515 # the destroy method. Example:
516 # Person.destroy_all "last_login < '2004-04-04'"
517 def destroy_all(conditions = nil)
3dfa56c @dhh Updated all references to the old find_first and find_all to use the …
dhh authored
518 find(:all, :conditions => conditions).each { |object| object.destroy }
db045db @dhh Initial
dhh authored
519 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
520
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
521 # Deletes all the records that match the +condition+ without instantiating the objects first (and hence not
db045db @dhh Initial
dhh authored
522 # calling the destroy method). Example:
090bfce @jeremy Correct documentation for Base.delete_all. References #1568.
jeremy authored
523 # Post.delete_all "person_id = 5 AND (category = 'Something' OR category = 'Else')"
db045db @dhh Initial
dhh authored
524 def delete_all(conditions = nil)
525 sql = "DELETE FROM #{table_name} "
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
526 add_conditions!(sql, conditions, scope(:find))
db045db @dhh Initial
dhh authored
527 connection.delete(sql, "#{name} Delete all")
528 end
529
530 # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
ee614d6 Add documentation caveat about when to use count_by_sql. Closes #8090…
Marcel Molina authored
531 # The use of this method should be restricted to complicated SQL queries that can't be executed
532 # using the ActiveRecord::Calculations class methods. Look into those before using this.
533 #
534 # ==== Options
535 #
536 # +sql+: An SQL statement which should return a count query from the database, see the example below
537 #
538 # ==== Examples
539 #
e17bf81 @jamis Fix typo in count_by_sql documentation #1969 [Alexey Verkhovsky]
jamis authored
540 # Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
db045db @dhh Initial
dhh authored
541 def count_by_sql(sql)
a775cb1 @dhh Added the option for sanitizing find_by_sql and the offset parts in r…
dhh authored
542 sql = sanitize_conditions(sql)
caaf40d @dhh Added AbstractAdapter#select_value and AbstractAdapter#select_values …
dhh authored
543 connection.select_value(sql, "#{name} Count").to_i
db045db @dhh Initial
dhh authored
544 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
545
8375237 @jamis Made increment_counter/decrement_counter play nicely with optimistic …
jamis authored
546 # A generic "counter updater" implementation, intended primarily to be
547 # used by increment_counter and decrement_counter, but which may also
548 # be useful on its own. It simply does a direct SQL update for the record
549 # with the given ID, altering the given hash of counters by the amount
550 # given by the corresponding value:
551 #
552 # Post.update_counters 5, :comment_count => -1, :action_count => 1
553 # # UPDATE posts
554 # # SET comment_count = comment_count - 1,
555 # # action_count = action_count + 1
556 # # WHERE id = 5
557 def update_counters(id, counters)
558 updates = counters.inject([]) { |list, (counter_name, increment)|
559 sign = increment < 0 ? "-" : "+"
98165fd @jeremy Consistently quote primary key column names. Closes #7763.
jeremy authored
560 list << "#{connection.quote_column_name(counter_name)} = #{connection.quote_column_name(counter_name)} #{sign} #{increment.abs}"
8375237 @jamis Made increment_counter/decrement_counter play nicely with optimistic …
jamis authored
561 }.join(", ")
98165fd @jeremy Consistently quote primary key column names. Closes #7763.
jeremy authored
562 update_all(updates, "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}")
8375237 @jamis Made increment_counter/decrement_counter play nicely with optimistic …
jamis authored
563 end
564
15dc567 Also add documentation enhancements for increment_counter. Closes #80…
Marcel Molina authored
565 # Increment a number field by one, usually representing a count.
566 #
567 # This is used for caching aggregate values, so that they don't need to be computed every time.
568 # For example, a DiscussionBoard may cache post_count and comment_count otherwise every time the board is
569 # shown it would have to run a SQL query to find how many posts and comments there are.
570 #
571 # ==== Options
572 #
573 # +counter_name+ The name of the field that should be incremented
574 # +id+ The id of the object that should be incremented
575 #
576 # ==== Examples
577 #
578 # # Increment the post_count column for the record with an id of 5
579 # DiscussionBoard.increment_counter(:post_count, 5)
db045db @dhh Initial
dhh authored
580 def increment_counter(counter_name, id)
8375237 @jamis Made increment_counter/decrement_counter play nicely with optimistic …
jamis authored
581 update_counters(id, counter_name => 1)
db045db @dhh Initial
dhh authored
582 end
583
5bd3570 Enhance documentation for decrement_counter. Closes #8093. [fearoffish]
Marcel Molina authored
584 # Decrement a number field by one, usually representing a count.
585 #
586 # This works the same as increment_counter but reduces the column value by 1 instead of increasing it.
587 #
588 # ==== Options
589 #
590 # +counter_name+ The name of the field that should be decremented
591 # +id+ The id of the object that should be decremented
592 #
593 # ==== Examples
594 #
595 # # Decrement the post_count column for the record with an id of 5
596 # DiscussionBoard.decrement_counter(:post_count, 5)
db045db @dhh Initial
dhh authored
597 def decrement_counter(counter_name, id)
8375237 @jamis Made increment_counter/decrement_counter play nicely with optimistic …
jamis authored
598 update_counters(id, counter_name => -1)
db045db @dhh Initial
dhh authored
599 end
600
c450a36 @dhh Doc fixes
dhh authored
601
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
602 # Attributes named in this macro are protected from mass-assignment, such as <tt>new(attributes)</tt> and
db045db @dhh Initial
dhh authored
603 # <tt>attributes=(attributes)</tt>. Their assignment will simply be ignored. Instead, you can use the direct writer
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
604 # methods to do assignment. This is meant to protect sensitive attributes from being overwritten by URL/form hackers. Example:
db045db @dhh Initial
dhh authored
605 #
606 # class Customer < ActiveRecord::Base
607 # attr_protected :credit_rating
608 # end
609 #
610 # customer = Customer.new("name" => David, "credit_rating" => "Excellent")
611 # customer.credit_rating # => nil
612 # customer.attributes = { "description" => "Jolly fellow", "credit_rating" => "Superb" }
613 # customer.credit_rating # => nil
614 #
615 # customer.credit_rating = "Average"
616 # customer.credit_rating # => "Average"
617 def attr_protected(*attributes)
0e0e774 Protect id attribute from mass assigment even when the primary key is…
Marcel Molina authored
618 write_inheritable_array("attr_protected", attributes - (protected_attributes || []))
db045db @dhh Initial
dhh authored
619 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
620
098fa94 @dhh Fixed documentation snafus #575, #576, #577, #585
dhh authored
621 # Returns an array of all the attributes that have been protected from mass-assignment.
db045db @dhh Initial
dhh authored
622 def protected_attributes # :nodoc:
623 read_inheritable_attribute("attr_protected")
624 end
625
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
626 # If this macro is used, only those attributes named in it will be accessible for mass-assignment, such as
db045db @dhh Initial
dhh authored
627 # <tt>new(attributes)</tt> and <tt>attributes=(attributes)</tt>. This is the more conservative choice for mass-assignment
628 # protection. If you'd rather start from an all-open default and restrict attributes as needed, have a look at
629 # attr_protected.
630 def attr_accessible(*attributes)
0e0e774 Protect id attribute from mass assigment even when the primary key is…
Marcel Molina authored
631 write_inheritable_array("attr_accessible", attributes - (accessible_attributes || []))
db045db @dhh Initial
dhh authored
632 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
633
098fa94 @dhh Fixed documentation snafus #575, #576, #577, #585
dhh authored
634 # Returns an array of all the attributes that have been made accessible to mass-assignment.
db045db @dhh Initial
dhh authored
635 def accessible_attributes # :nodoc:
636 read_inheritable_attribute("attr_accessible")
637 end
638
c450a36 @dhh Doc fixes
dhh authored
639
1a0cdf7 @dhh Docfix (closes #8096)
dhh authored
640 # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
641 # then specify the name of that attribute using this method and it will be handled automatically.
642 # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
643 # class on retrieval or +SerializationTypeMismatch+ will be raised.
644 #
645 # ==== Options
646 #
647 # +attr_name+ The field name that should be serialized
648 # +class_name+ Optional, class name that the object should be equal to
649 #
650 # ==== Example
651 # # Serialize a preferences attribute
652 # class User
653 # serialize :preferences
654 # end
db045db @dhh Initial
dhh authored
655 def serialize(attr_name, class_name = Object)
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
656 serialized_attributes[attr_name.to_s] = class_name
db045db @dhh Initial
dhh authored
657 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
658
db045db @dhh Initial
dhh authored
659 # Returns a hash of all the attributes that have been specified for serialization as keys and their class restriction as values.
660 def serialized_attributes
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
661 read_inheritable_attribute("attr_serialized") or write_inheritable_attribute("attr_serialized", {})
db045db @dhh Initial
dhh authored
662 end
663
c450a36 @dhh Doc fixes
dhh authored
664
db045db @dhh Initial
dhh authored
665 # Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending
666 # directly from ActiveRecord. So if the hierarchy looks like: Reply < Message < ActiveRecord, then Message is used
4eab375 @dhh Finished polishing API docs
dhh authored
667 # to guess the table name from even when called on Reply. The rules used to do the guess are handled by the Inflector class
668 # in Active Support, which knows almost all common English inflections (report a bug if your inflection isn't covered).
db045db @dhh Initial
dhh authored
669 #
14101c7 @jeremy Nested classes are given table names prefixed by the singular form of…
jeremy authored
670 # Nested classes are given table names prefixed by the singular form of
671 # the parent's table name. Example:
672 # file class table_name
673 # invoice.rb Invoice invoices
674 # invoice/lineitem.rb Invoice::Lineitem invoice_lineitems
db045db @dhh Initial
dhh authored
675 #
14101c7 @jeremy Nested classes are given table names prefixed by the singular form of…
jeremy authored
676 # Additionally, the class-level table_name_prefix is prepended and the
677 # table_name_suffix is appended. So if you have "myapp_" as a prefix,
678 # the table name guess for an Invoice class becomes "myapp_invoices".
679 # Invoice::Lineitem becomes "myapp_invoice_lineitems".
680 #
681 # You can also overwrite this class method to allow for unguessable
682 # links, such as a Mouse class with a link to a "mice" table. Example:
db045db @dhh Initial
dhh authored
683 #
684 # class Mouse < ActiveRecord::Base
14101c7 @jeremy Nested classes are given table names prefixed by the singular form of…
jeremy authored
685 # set_table_name "mice"
db045db @dhh Initial
dhh authored
686 # end
dcc4868 @dhh Fixed that Base.table_name would expect a parameter when used in has_…
dhh authored
687 def table_name
d736568 Speed up the setting of table_name. Closes #2428.
Marcel Molina authored
688 reset_table_name
689 end
690
fed7d33 @dhh Fixed documentation
dhh authored
691 def reset_table_name #:nodoc:
bcbce90 @jeremy Nested subclasses are not prefixed with the parent class' table_name …
jeremy authored
692 base = base_class
693
694 name =
695 # STI subclasses always use their superclass' table.
696 unless self == base
697 base.table_name
698 else
699 # Nested classes are prefixed with singular parent table name.
700 if parent < ActiveRecord::Base && !parent.abstract_class?
701 contained = parent.table_name
702 contained = contained.singularize if parent.pluralize_table_names
703 contained << '_'
704 end
705 name = "#{table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
706 end
707
c450a36 @dhh Doc fixes
dhh authored
708 set_table_name(name)
d736568 Speed up the setting of table_name. Closes #2428.
Marcel Molina authored
709 name
db045db @dhh Initial
dhh authored
710 end
711
098fa94 @dhh Fixed documentation snafus #575, #576, #577, #585
dhh authored
712 # Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
db045db @dhh Initial
dhh authored
713 # primary_key_prefix_type setting, though.
714 def primary_key
c0899bc Add convenience predicate methods on Column class. In partial fullfil…
Marcel Molina authored
715 reset_primary_key
716 end
717
fed7d33 @dhh Fixed documentation
dhh authored
718 def reset_primary_key #:nodoc:
c0899bc Add convenience predicate methods on Column class. In partial fullfil…
Marcel Molina authored
719 key = 'id'
db045db @dhh Initial
dhh authored
720 case primary_key_prefix_type
721 when :table_name
def7460 @technoweenie Added Base.abstract_class? that marks which classes are not part of t…
technoweenie authored
722 key = Inflector.foreign_key(base_class.name, false)
db045db @dhh Initial
dhh authored
723 when :table_name_with_underscore
def7460 @technoweenie Added Base.abstract_class? that marks which classes are not part of t…
technoweenie authored
724 key = Inflector.foreign_key(base_class.name)
db045db @dhh Initial
dhh authored
725 end
c0899bc Add convenience predicate methods on Column class. In partial fullfil…
Marcel Molina authored
726 set_primary_key(key)
727 key
db045db @dhh Initial
dhh authored
728 end
729
9d2da04 @jeremy Cache inheritance_column. Closes #6592.
jeremy authored
730 # Defines the column name for use with single table inheritance
731 # -- can be set in subclasses like so: self.inheritance_column = "type_id"
db045db @dhh Initial
dhh authored
732 def inheritance_column
9d2da04 @jeremy Cache inheritance_column. Closes #6592.
jeremy authored
733 @inheritance_column ||= "type".freeze
db045db @dhh Initial
dhh authored
734 end
735
7c8f3ed @jeremy r4325@asus: jeremy | 2005-11-12 03:57:46 -0800
jeremy authored
736 # Lazy-set the sequence name to the connection's default. This method
737 # is only ever called once since set_sequence_name overrides it.
fed7d33 @dhh Fixed documentation
dhh authored
738 def sequence_name #:nodoc:
7c8f3ed @jeremy r4325@asus: jeremy | 2005-11-12 03:57:46 -0800
jeremy authored
739 reset_sequence_name
740 end
741
fed7d33 @dhh Fixed documentation
dhh authored
742 def reset_sequence_name #:nodoc:
7c8f3ed @jeremy r4325@asus: jeremy | 2005-11-12 03:57:46 -0800
jeremy authored
743 default = connection.default_sequence_name(table_name, primary_key)
744 set_sequence_name(default)
745 default
14ea312 @dhh Made Oracle a first-class connection adapter by adhering closer to id…
dhh authored
746 end
747
1aa82b3 @dhh Added keyword-style approach to defining the custom relational bindin…
dhh authored
748 # Sets the table name to use to the given value, or (if the value
4eab375 @dhh Finished polishing API docs
dhh authored
749 # is nil or false) to the value returned by the given block.
1aa82b3 @dhh Added keyword-style approach to defining the custom relational bindin…
dhh authored
750 #
751 # Example:
752 #
753 # class Project < ActiveRecord::Base
754 # set_table_name "project"
755 # end
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
756 def set_table_name(value = nil, &block)
1aa82b3 @dhh Added keyword-style approach to defining the custom relational bindin…
dhh authored
757 define_attr_method :table_name, value, &block
758 end
759 alias :table_name= :set_table_name
760
761 # Sets the name of the primary key column to use to the given value,
762 # or (if the value is nil or false) to the value returned by the given
4eab375 @dhh Finished polishing API docs
dhh authored
763 # block.
1aa82b3 @dhh Added keyword-style approach to defining the custom relational bindin…
dhh authored
764 #
765 # Example:
766 #
767 # class Project < ActiveRecord::Base
768 # set_primary_key "sysid"
769 # end
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
770 def set_primary_key(value = nil, &block)
1aa82b3 @dhh Added keyword-style approach to defining the custom relational bindin…
dhh authored
771 define_attr_method :primary_key, value, &block
772 end
773 alias :primary_key= :set_primary_key
774
775 # Sets the name of the inheritance column to use to the given value,
776 # or (if the value # is nil or false) to the value returned by the
4eab375 @dhh Finished polishing API docs
dhh authored
777 # given block.
1aa82b3 @dhh Added keyword-style approach to defining the custom relational bindin…
dhh authored
778 #
779 # Example:
780 #
781 # class Project < ActiveRecord::Base
782 # set_inheritance_column do
783 # original_inheritance_column + "_id"
784 # end
785 # end
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
786 def set_inheritance_column(value = nil, &block)
1aa82b3 @dhh Added keyword-style approach to defining the custom relational bindin…
dhh authored
787 define_attr_method :inheritance_column, value, &block
788 end
789 alias :inheritance_column= :set_inheritance_column
790
14ea312 @dhh Made Oracle a first-class connection adapter by adhering closer to id…
dhh authored
791 # Sets the name of the sequence to use when generating ids to the given
792 # value, or (if the value is nil or false) to the value returned by the
7117fdb @jeremy r3616@asus: jeremy | 2005-09-26 23:09:28 -0700
jeremy authored
793 # given block. This is required for Oracle and is useful for any
794 # database which relies on sequences for primary key generation.
14ea312 @dhh Made Oracle a first-class connection adapter by adhering closer to id…
dhh authored
795 #
2076dca @jeremy r3095@asus: jeremy | 2005-11-15 22:40:51 -0800
jeremy authored
796 # If a sequence name is not explicitly set when using Oracle or Firebird,
797 # it will default to the commonly used pattern of: #{table_name}_seq
798 #
799 # If a sequence name is not explicitly set when using PostgreSQL, it
800 # will discover the sequence corresponding to your primary key for you.
14ea312 @dhh Made Oracle a first-class connection adapter by adhering closer to id…
dhh authored
801 #
802 # Example:
803 #
804 # class Project < ActiveRecord::Base
805 # set_sequence_name "projectseq" # default would have been "project_seq"
806 # end
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
807 def set_sequence_name(value = nil, &block)
14ea312 @dhh Made Oracle a first-class connection adapter by adhering closer to id…
dhh authored
808 define_attr_method :sequence_name, value, &block
809 end
810 alias :sequence_name= :set_sequence_name
811
db045db @dhh Initial
dhh authored
812 # Turns the +table_name+ back into a class name following the reverse rules of +table_name+.
813 def class_name(table_name = table_name) # :nodoc:
814 # remove any prefix and/or suffix from the table name
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
815 class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].camelize
816 class_name = class_name.singularize if pluralize_table_names
817 class_name
db045db @dhh Initial
dhh authored
818 end
819
816f37a @dhh Added migration support to SQL Server adapter (please someone do the …
dhh authored
820 # Indicates whether the table associated with this class exists
821 def table_exists?
822 if connection.respond_to?(:tables)
823 connection.tables.include? table_name
824 else
825 # if the connection adapter hasn't implemented tables, there are two crude tests that can be
826 # used - see if getting column info raises an error, or if the number of columns returned is zero
827 begin
828 reset_column_information
829 columns.size > 0
830 rescue ActiveRecord::StatementInvalid
831 false
832 end
833 end
834 end
835
db045db @dhh Initial
dhh authored
836 # Returns an array of column objects for the table associated with this class.
837 def columns
c0899bc Add convenience predicate methods on Column class. In partial fullfil…
Marcel Molina authored
838 unless @columns
839 @columns = connection.columns(table_name, "#{name} Columns")
840 @columns.each {|column| column.primary = column.name == primary_key}
841 end
842 @columns
db045db @dhh Initial
dhh authored
843 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
844
3b4450b @NZKoz Docs fix for columns_hash [bradediger]
NZKoz authored
845 # Returns a hash of column objects for the table associated with this class.
db045db @dhh Initial
dhh authored
846 def columns_hash
847 @columns_hash ||= columns.inject({}) { |hash, column| hash[column.name] = column; hash }
848 end
d0bd3b5 @jeremy Return PostgreSQL columns in the order they are declared #1374 (perlg…
jeremy authored
849
fed7d33 @dhh Fixed documentation
dhh authored
850 # Returns an array of column names as strings.
49d0f0c @dhh Speeded up eager loading a whole bunch
dhh authored
851 def column_names
d0bd3b5 @jeremy Return PostgreSQL columns in the order they are declared #1374 (perlg…
jeremy authored
852 @column_names ||= columns.map { |column| column.name }
49d0f0c @dhh Speeded up eager loading a whole bunch
dhh authored
853 end
db045db @dhh Initial
dhh authored
854
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
855 # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
856 # and columns used for single table inheritance have been removed.
db045db @dhh Initial
dhh authored
857 def content_columns
c0899bc Add convenience predicate methods on Column class. In partial fullfil…
Marcel Molina authored
858 @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
db045db @dhh Initial
dhh authored
859 end
860
861 # 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
862 # 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
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
863 # is available.
fed7d33 @dhh Fixed documentation
dhh authored
864 def column_methods_hash #:nodoc:
d0bd3b5 @jeremy Return PostgreSQL columns in the order they are declared #1374 (perlg…
jeremy authored
865 @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
3ab3a70 @jeremy Clarify semantics of ActiveRecord::Base#respond_to? Closes #2560.
jeremy authored
866 attr_name = attr.to_s
867 methods[attr.to_sym] = attr_name
868 methods["#{attr}=".to_sym] = attr_name
869 methods["#{attr}?".to_sym] = attr_name
870 methods["#{attr}_before_type_cast".to_sym] = attr_name
db045db @dhh Initial
dhh authored
871 methods
872 end
873 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
874
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
875 # Resets all the cached information about columns, which will cause them to be reloaded on the next request.
1314f48 @dhh Added methods for resetting the cached information on classes that yo…
dhh authored
876 def reset_column_information
5b801b5 @NZKoz Change the implementation of ActiveRecord's attribute reader and writ…
NZKoz authored
877 generated_methods.each { |name| undef_method(name) }
878 @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @generated_methods = @inheritance_column = nil
1314f48 @dhh Added methods for resetting the cached information on classes that yo…
dhh authored
879 end
880
4eab375 @dhh Finished polishing API docs
dhh authored
881 def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
1314f48 @dhh Added methods for resetting the cached information on classes that yo…
dhh authored
882 subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
883 end
db045db @dhh Initial
dhh authored
884
885 # Transforms attribute key names into a more humane format, such as "First name" instead of "first_name". Example:
886 # Person.human_attribute_name("first_name") # => "First name"
4eab375 @dhh Finished polishing API docs
dhh authored
887 # Deprecated in favor of just calling "first_name".humanize
888 def human_attribute_name(attribute_key_name) #:nodoc:
1f7e72f @dhh Made human_attribute_name(attribute_key_name) use Inflector.humanize
dhh authored
889 attribute_key_name.humanize
db045db @dhh Initial
dhh authored
890 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
891
06afb8c @jeremy Subclasses of an abstract class work with single-table inheritance. R…
jeremy authored
892 # True if this isn't a concrete subclass needing a STI type condition.
893 def descends_from_active_record?
894 if superclass.abstract_class?
895 superclass.descends_from_active_record?
896 else
897 superclass == Base || !columns_hash.include?(inheritance_column)
898 end
db045db @dhh Initial
dhh authored
899 end
900
d0360a4 @jeremy Base.inspect handles Base itself and abstract_class? Don't use #<Foo…
jeremy authored
901 # Returns a string like 'Post id:integer, title:string, body:text'
52a9e50 @dhh Added ActiveRecord::Base.inspect to return a column-view like #<Post …
dhh authored
902 def inspect
d0360a4 @jeremy Base.inspect handles Base itself and abstract_class? Don't use #<Foo…
jeremy authored
903 if self == Base
904 super
905 elsif abstract_class?
906 "#{super}(abstract)"
bb94ce9 @NZKoz Let inspect on AR classes work when the table doesn't exist. Closes #…
NZKoz authored
907 elsif table_exists?
d0360a4 @jeremy Base.inspect handles Base itself and abstract_class? Don't use #<Foo…
jeremy authored
908 attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
909 "#{super}(#{attr_list})"
bb94ce9 @NZKoz Let inspect on AR classes work when the table doesn't exist. Closes #…
NZKoz authored
910 else
911 "#{super}(Table doesn't exist)"
d0360a4 @jeremy Base.inspect handles Base itself and abstract_class? Don't use #<Foo…
jeremy authored
912 end
52a9e50 @dhh Added ActiveRecord::Base.inspect to return a column-view like #<Post …
dhh authored
913 end
914
b445ab9 @NZKoz Rename quote to quote_value so the name can be used in AR models. #36…
NZKoz authored
915
916 def quote_value(value, column = nil) #:nodoc:
b2c0ddf Add support for FrontBase (http://www.frontbase.com/) with a new adap…
Marcel Molina authored
917 connection.quote(value,column)
4940383 @dhh Fixed value quoting in all generated SQL statements, so that integers…
dhh authored
918 end
919
920 # Used to sanitize objects before they're used in an SELECT SQL-statement. Delegates to <tt>connection.quote</tt>.
4eab375 @dhh Finished polishing API docs
dhh authored
921 def sanitize(object) #:nodoc:
4940383 @dhh Fixed value quoting in all generated SQL statements, so that integers…
dhh authored
922 connection.quote(object)
db045db @dhh Initial
dhh authored
923 end
924
f2a89d7 @dhh Give AR the new benchmark method too
dhh authored
925 # Log and benchmark multiple statements in a single block. Example:
db045db @dhh Initial
dhh authored
926 #
927 # Project.benchmark("Creating project") do
928 # project = Project.create("name" => "stuff")
929 # project.create_manager("name" => "David")
3dfa56c @dhh Updated all references to the old find_first and find_all to use the …
dhh authored
930 # project.milestones << Milestone.find(:all)
db045db @dhh Initial
dhh authored
931 # end
5e8e8d6 @dhh Added use_silence parameter to ActiveRecord::Base.benchmark that can …
dhh authored
932 #
f2a89d7 @dhh Give AR the new benchmark method too
dhh authored
933 # The benchmark is only recorded if the current level of the logger matches the <tt>log_level</tt>, which makes it
934 # easy to include benchmarking statements in production software that will remain inexpensive because the benchmark
935 # will only be conducted if the log level is low enough.
936 #
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
937 # The logging of the multiple statements is turned off unless <tt>use_silence</tt> is set to false.
f2a89d7 @dhh Give AR the new benchmark method too
dhh authored
938 def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
939 if logger && logger.level == log_level
5e8e8d6 @dhh Added use_silence parameter to ActiveRecord::Base.benchmark that can …
dhh authored
940 result = nil
941 seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield }
f2a89d7 @dhh Give AR the new benchmark method too
dhh authored
942 logger.add(log_level, "#{title} (#{'%.5f' % seconds})")
5e8e8d6 @dhh Added use_silence parameter to ActiveRecord::Base.benchmark that can …
dhh authored
943 result
944 else
945 yield
946 end
c00bf5f @dhh Fixed the verbosity of using the AR store
dhh authored
947 end
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
948
c00bf5f @dhh Fixed the verbosity of using the AR store
dhh authored
949 # Silences the logger for the duration of the block.
950 def silence
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
951 old_logger_level, logger.level = logger.level, Logger::ERROR if logger
952 yield
953 ensure
908e9a1 @dhh Fixed that Base.silence should restore the old logger level when done…
dhh authored
954 logger.level = old_logger_level if logger
db045db @dhh Initial
dhh authored
955 end
390e6d2 @jeremy r2915@asus: jeremy | 2005-11-06 05:02:53 -0800
jeremy authored
956
97849de @dhh Fixed that association proxies would fail === tests like PremiumSubsc…
dhh authored
957 # Overwrite the default class equality method to provide support for association proxies.
958 def ===(object)
959 object.is_a?(self)
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
960 end
b840e4e Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveR…
Marcel Molina authored
961
962 # Deprecated
ccfcd9f @dhh Doc fixes
dhh authored
963 def threaded_connections #:nodoc:
b840e4e Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveR…
Marcel Molina authored
964 allow_concurrency
965 end
966
967 # Deprecated
ccfcd9f @dhh Doc fixes
dhh authored
968 def threaded_connections=(value) #:nodoc:
b840e4e Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveR…
Marcel Molina authored
969 self.allow_concurrency = value
970 end
971
d2f4750 @jamis Add AR::Base.base_class for querying the ancestor AR::Base subclass […
jamis authored
972 # Returns the base AR subclass that this class descends from. If A
973 # extends AR::Base, A.base_class will return A. If B descends from A
974 # through some arbitrarily deep hierarchy, B.base_class will return A.
975 def base_class
976 class_of_active_record_descendant(self)
977 end
978
def7460 @technoweenie Added Base.abstract_class? that marks which classes are not part of t…
technoweenie authored
979 # Set this to true if this is an abstract class (see #abstract_class?).
980 attr_accessor :abstract_class
981
982 # Returns whether this class is a base AR class. If A is a base class and
983 # B descends from A, then B.base_class will return B.
984 def abstract_class?
985 abstract_class == true
986 end
987
db045db @dhh Initial
dhh authored
988 private
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
989 def find_initial(options)
990 options.update(:limit => 1) unless options[:include]
991 find_every(options).first
992 end
2fbe0ae @jeremy Support nil and Array in :conditions => { attr => value } hashes. Clo…
jeremy authored
993
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
994 def find_every(options)
995 records = scoped?(:find, :include) || options[:include] ?
996 find_with_associations(options) :
997 find_by_sql(construct_finder_sql(options))
998
999 records.each { |record| record.readonly! } if options[:readonly]
1000
1001 records
1002 end
2fbe0ae @jeremy Support nil and Array in :conditions => { attr => value } hashes. Clo…
jeremy authored
1003
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
1004 def find_from_ids(ids, options)
2fbe0ae @jeremy Support nil and Array in :conditions => { attr => value } hashes. Clo…
jeremy authored
1005 expects_array = ids.first.kind_of?(Array)
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
1006 return ids.first if expects_array && ids.first.empty?
2fbe0ae @jeremy Support nil and Array in :conditions => { attr => value } hashes. Clo…
jeremy authored
1007
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
1008 ids = ids.flatten.compact.uniq
1009
1010 case ids.size
1011 when 0
1012 raise RecordNotFound, "Couldn't find #{name} without an ID"
1013 when 1
1014 result = find_one(ids.first, options)
1015 expects_array ? [ result ] : result
1016 else
1017 find_some(ids, options)
1018 end
1019 end
1020
1021 def find_one(id, options)
1022 conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
98165fd @jeremy Consistently quote primary key column names. Closes #7763.
jeremy authored
1023 options.update :conditions => "#{table_name}.#{connection.quote_column_name(primary_key)} = #{quote_value(id,columns_hash[primary_key])}#{conditions}"
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
1024
230c5a0 @jeremy find_one uses find_every.first instead of find_initial since its prim…
jeremy authored
1025 # Use find_every(options).first since the primary key condition
1026 # already ensures we have a single record. Using find_initial adds
1027 # a superfluous :limit => 1.
1028 if result = find_every(options).first
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
1029 result
1030 else
1031 raise RecordNotFound, "Couldn't find #{name} with ID=#{id}#{conditions}"
1032 end
1033 end
1034
1035 def find_some(ids, options)
1036 conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
b445ab9 @NZKoz Rename quote to quote_value so the name can be used in AR models. #36…
NZKoz authored
1037 ids_list = ids.map { |id| quote_value(id,columns_hash[primary_key]) }.join(',')
98165fd @jeremy Consistently quote primary key column names. Closes #7763.
jeremy authored
1038 options.update :conditions => "#{table_name}.#{connection.quote_column_name(primary_key)} IN (#{ids_list})#{conditions}"
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
1039
1040 result = find_every(options)
1041
8158455 @jeremy Fix an edge case with find with a list of ids, limit, and offset. Clo…
jeremy authored
1042 # Determine expected size from limit and offset, not just ids.size.
109926c @jeremy Find with a list of ids supports limit/offset. Closes #8437.
jeremy authored
1043 expected_size =
1044 if options[:limit] && ids.size > options[:limit]
1045 options[:limit]
1046 else
1047 ids.size
1048 end
8158455 @jeremy Fix an edge case with find with a list of ids, limit, and offset. Clo…
jeremy authored
1049
1050 # 11 ids with limit 3, offset 9 should give 2 results.
1051 if options[:offset] && (ids.size - options[:offset] < expected_size)
1052 expected_size = ids.size - options[:offset]
1053 end
109926c @jeremy Find with a list of ids supports limit/offset. Closes #8437.
jeremy authored
1054
1055 if result.size == expected_size
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
1056 result
1057 else
109926c @jeremy Find with a list of ids supports limit/offset. Closes #8437.
jeremy authored
1058 raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions} (found #{result.size} results, but was looking for #{expected_size})"
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
1059 end
1060 end
1061
4b4dd54 @jeremy Clashing type columns due to a sloppy join shouldn't wreck single-tab…
jeremy authored
1062 # Finder methods must instantiate through this method to work with the
1063 # single-table inheritance model that makes it possible to create
1064 # objects of different types from the same table.
db045db @dhh Initial
dhh authored
1065 def instantiate(record)
4b4dd54 @jeremy Clashing type columns due to a sloppy join shouldn't wreck single-tab…
jeremy authored
1066 object =
eb2fbf0 Optimize instantiation of STI subclass records. In partial fullfilmen…
Marcel Molina authored
1067 if subclass_name = record[inheritance_column]
4b4dd54 @jeremy Clashing type columns due to a sloppy join shouldn't wreck single-tab…
jeremy authored
1068 # No type given.
eb2fbf0 Optimize instantiation of STI subclass records. In partial fullfilmen…
Marcel Molina authored
1069 if subclass_name.empty?
1070 allocate
4b4dd54 @jeremy Clashing type columns due to a sloppy join shouldn't wreck single-tab…
jeremy authored
1071
eb2fbf0 Optimize instantiation of STI subclass records. In partial fullfilmen…
Marcel Molina authored
1072 else
4b4dd54 @jeremy Clashing type columns due to a sloppy join shouldn't wreck single-tab…
jeremy authored
1073 # Ignore type if no column is present since it was probably
1074 # pulled in from a sloppy join.
9d2da04 @jeremy Cache inheritance_column. Closes #6592.
jeremy authored
1075 unless columns_hash.include?(inheritance_column)
4b4dd54 @jeremy Clashing type columns due to a sloppy join shouldn't wreck single-tab…
jeremy authored
1076 allocate
1077
1078 else
1079 begin
1080 compute_type(subclass_name).allocate
1081 rescue NameError
1082 raise SubclassNotFound,
1083 "The single-table inheritance mechanism failed to locate the subclass: '#{record[inheritance_column]}'. " +
1084 "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
1085 "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
1086 "or overwrite #{self.to_s}.inheritance_column to use another column for that information."
1087 end
eb2fbf0 Optimize instantiation of STI subclass records. In partial fullfilmen…
Marcel Molina authored
1088 end
1089 end
1090 else
1091 allocate
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
1092 end
605bc77 @dhh Added a better exception for when a type column is used in a table wi…
dhh authored
1093
db045db @dhh Initial
dhh authored
1094 object.instance_variable_set("@attributes", record)
5b801b5 @NZKoz Change the implementation of ActiveRecord's attribute reader and writ…
NZKoz authored
1095 object.instance_variable_set("@attributes_cache", Hash.new)
55efae2 @jeremy Performance: absorb instantiate and initialize_with_callbacks into th…
jeremy authored
1096
1097 if object.respond_to_without_attributes?(:after_find)
1098 object.send(:callback, :after_find)
1099 end
1100
1101 if object.respond_to_without_attributes?(:after_initialize)
1102 object.send(:callback, :after_initialize)
1103 end
1104
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
1105 object
db045db @dhh Initial
dhh authored
1106 end
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
1107
c7d6d68 @jeremy Reflections don't attempt to resolve module nesting of association cl…
jeremy authored
1108 # Nest the type name in the same module as this class.
1109 # Bar is "MyApp::Business::Bar" relative to MyApp::Business::Foo
db045db @dhh Initial
dhh authored
1110 def type_name_with_module(type_name)
6400367 @seckar Fix type_name_with_module to handle type names that begin with '::'. …
seckar authored
1111 (/^::/ =~ type_name) ? type_name : "#{parent.name}::#{type_name}"
db045db @dhh Initial
dhh authored
1112 end
1113
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
1114 def construct_finder_sql(options)
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
1115 scope = scope(:find)
3fbfc13 @NZKoz Move from select * to select tablename.* to avoid clobbering IDs. Clo…
NZKoz authored
1116 sql = "SELECT #{(scope && scope[:select]) || options[:select] || (options[:joins] && table_name + '.*') || '*'} "
e2d8451 @dhh We cant make MySQL 5 happy if it means making SQLite and PostgreSQL c…
dhh authored
1117 sql << "FROM #{(scope && scope[:from]) || options[:from] || table_name} "
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary sup…
dhh authored
1118
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
1119 add_joins!(sql, options, scope)
1120 add_conditions!(sql, options[:conditions], scope)
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary sup…
dhh authored
1121
3566be4 @dhh Fixed that eager loading queries and with_scope should respect the :g…
dhh authored
1122 add_group!(sql, options[:group], scope)
57a3e44 @jamis make add_order a tad faster (Closes #6567)
jamis authored
1123 add_order!(sql, options[:order], scope)
c9c1852 @dhh Making ActiveRecord faster [skaes]
dhh authored
1124 add_limit!(sql, options, scope)
15aa6e0 @jeremy r4644@asus: jeremy | 2006-06-16 14:57:03 -0700
jeremy authored
1125 add_lock!(sql, options, scope)
6abda69 @dhh Added preliminary support for join models [DHH] Added preliminary sup…
dhh authored
1126
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
1127 sql
efb55d1 @dhh Allow order, conditions, and joins in finds that include associations
dhh authored
1128 end
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
1129
445cb5c @NZKoz Add support for :include to with_scope [andrew@redlinesoftware.com]
NZKoz authored
1130 # Merges includes so that the result is a valid +include+
1131 def merge_includes(first, second)
01a52dd @dhh Fixed that :includes merged from with_scope won't cause the same asso…
dhh authored
1132 (safe_to_array(first) + safe_to_array(second)).uniq
445cb5c @NZKoz Add support for :include to with_scope [andrew@redlinesoftware.com]
NZKoz authored
1133 end
1134
48052d7 @jeremy to_xml fixes, features, and speedup. Closes #4989.
jeremy authored
1135 # Object#to_a is deprecated, though it does have the desired behavior
445cb5c @NZKoz Add support for :include to with_scope [andrew@redlinesoftware.com]
NZKoz authored
1136 def safe_to_array(o)
1137 case o
1138 when NilClass
1139 []
1140 when Array
1141 o
1142 else
1143 [o]
1144 end
1145 end
1146
57a3e44 @jamis make add_order a tad faster (Closes #6567)
jamis authored
1147 def add_order!(sql, order, scope = :auto)
1148 scope = scope(:find) if :auto == scope
1149 scoped_order = scope[:order] if scope
9153137 Add support for :order option to with_scope. Closes #3887. [eric.dasp…
Marcel Molina authored
1150 if order
1151 sql << " ORDER BY #{order}"
57a3e44 @jamis make add_order a tad faster (Closes #6567)
jamis authored
1152 sql << ", #{scoped_order}" if scoped_order
9153137 Add support for :order option to with_scope. Closes #3887. [eric.dasp…
Marcel Molina authored
1153 else
57a3e44 @jamis make add_order a tad faster (Closes #6567)
jamis authored
1154 sql << " ORDER BY #{scoped_order}" if scoped_order
9153137 Add support for :order option to with_scope. Closes #3887. [eric.dasp…
Marcel Molina authored
1155 end
1156 end
3566be4 @dhh Fixed that eager loading queries and with_scope should respect the :g…
dhh authored
1157
54a6ed1 @jeremy minor speedups + forward-compat syntax
jeremy authored
1158 def add_group!(sql, group, scope = :auto)
3566be4 @dhh Fixed that eager loading queries and with_scope should respect the :g…
dhh authored
1159 if group
1160 sql << " GROUP BY #{group}"
54a6ed1 @jeremy minor speedups + forward-compat syntax
jeremy authored
1161 else
1162 scope = scope(:find) if :auto == scope
1163 if scope && (scoped_group = scope[:group])
1164 sql << " GROUP BY #{scoped_group}"
1165 end
1166 end
3566be4 @dhh Fixed that eager loading queries and with_scope should respect the :g…
dhh authored
1167 end
9153137 Add support for :order option to with_scope. Closes #3887. [eric.dasp…
Marcel Molina authored
1168
a3f2013 @technoweenie Private ActiveRecord methods add_limit, add_joins, and add_conditions…
technoweenie authored
1169 # The optional scope argument is for the current :find scope.
db7fadd @technoweenie Changed those private ActiveRecord methods to take optional third arg…
technoweenie authored
1170 def add_limit!(sql, options, scope = :auto)
1171 scope = scope(:find) if :auto == scope
54a6ed1 @jeremy minor speedups + forward-compat syntax
jeremy authored
1172
1173 if scope
1174 options[:limit] ||= scope[:limit]
1175 options[:offset] ||= scope[:offset]
1176 end
1177
f2a29ca @dhh Added support for ODBC connections to MS SQL Server so you can connec…
dhh authored
1178 connection.add_limit_offset!(sql, options)
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
1179 end
390e6d2 @jeremy r2915@asus: jeremy | 2005-11-06 05:02:53 -0800
jeremy authored
1180
a3f2013 @technoweenie Private ActiveRecord methods add_limit, add_joins, and add_conditions…
technoweenie authored
1181 # The optional scope argument is for the current :find scope.
15aa6e0 @jeremy r4644@asus: jeremy | 2006-06-16 14:57:03 -0700
jeremy authored
1182 # The :lock option has precedence over a scoped :lock.
1183 def add_lock!(sql, options, scope = :auto)
c302cdf @jeremy Fix scope typo in add_lock! Closes #6482. [zubek]
jeremy authored
1184 scope = scope(:find) if :auto == scope
15aa6e0 @jeremy r4644@asus: jeremy | 2006-06-16 14:57:03 -0700
jeremy authored
1185 options = options.reverse_merge(:lock => scope[:lock]) if scope
1186 connection.add_lock!(sql, options)
1187 end
1188
1189 # The optional scope argument is for the current :find scope.
db7fadd @technoweenie Changed those private ActiveRecord methods to take optional third arg…