Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100755 1732 lines (1539 sloc) 77.957 kB
db045db @dhh Initial
dhh authored
1 require 'yaml'
aabf909 @jeremy Correct reader method generation for primary key attribute: handle ca…
jeremy authored
2 require 'set'
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
3 require 'active_record/deprecated_finders'
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
24 class StatementInvalid < ActiveRecordError #:nodoc:
25 end
554597d @dhh Added named bind-style variable interpolation #281 [Michael Koziarski]
dhh authored
26 class PreparedStatementInvalid < ActiveRecordError #:nodoc:
27 end
fbf9281 @dhh Added automated optimistic locking if the field lock_version is prese…
dhh authored
28 class StaleObjectError < ActiveRecordError #:nodoc:
29 end
5b9b904 @dhh Added support for limit and offset with eager loading of has_one and …
dhh authored
30 class ConfigurationError < StandardError #:nodoc:
31 end
64fcb75 @jeremy r3618@sedna: jeremy | 2005-10-14 12:06:03 -0700
jeremy authored
32 class ReadOnlyRecord < StandardError #:nodoc:
33 end
5707027 @dhh Added better exception error when unknown column types are used with …
dhh authored
34
d2fefbe @dhh Added MultiparameterAssignmentErrors and AttributeAssignmentError exc…
dhh authored
35 class AttributeAssignmentError < ActiveRecordError #:nodoc:
36 attr_reader :exception, :attribute
37 def initialize(message, exception, attribute)
38 @exception = exception
39 @attribute = attribute
40 @message = message
41 end
42 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
43
d2fefbe @dhh Added MultiparameterAssignmentErrors and AttributeAssignmentError exc…
dhh authored
44 class MultiparameterAssignmentErrors < ActiveRecordError #:nodoc:
45 attr_reader :errors
46 def initialize(errors)
47 @errors = errors
48 end
49 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
50
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
51 # Active Record objects don't specify their attributes directly, but rather infer them from the table definition with
db045db @dhh Initial
dhh authored
52 # which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change
53 # 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
54 # database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.
55 #
db045db @dhh Initial
dhh authored
56 # 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
57 #
db045db @dhh Initial
dhh authored
58 # == Creation
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
59 #
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
60 # 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
61 # 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
62 #
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
63 # user = User.new(:name => "David", :occupation => "Code Artist")
db045db @dhh Initial
dhh authored
64 # user.name # => "David"
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
65 #
db045db @dhh Initial
dhh authored
66 # You can also use block initialization:
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
67 #
db045db @dhh Initial
dhh authored
68 # user = User.new do |u|
69 # u.name = "David"
70 # u.occupation = "Code Artist"
71 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
72 #
db045db @dhh Initial
dhh authored
73 # 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
74 #
db045db @dhh Initial
dhh authored
75 # user = User.new
76 # user.name = "David"
77 # user.occupation = "Code Artist"
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
78 #
db045db @dhh Initial
dhh authored
79 # == Conditions
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
80 #
db045db @dhh Initial
dhh authored
81 # Conditions can either be specified as a string or an array representing the WHERE-part of an SQL statement.
82 # The array form is to be used when the condition input is tainted and requires sanitization. The string form can
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
83 # be used for statements that don't involve tainted data. Examples:
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
84 #
db045db @dhh Initial
dhh authored
85 # User < ActiveRecord::Base
86 # 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
87 # find(:first, :conditions => "user_name = '#{user_name}' AND password = '#{password}'")
db045db @dhh Initial
dhh authored
88 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
89 #
db045db @dhh Initial
dhh authored
90 # 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
91 # find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ])
db045db @dhh Initial
dhh authored
92 # end
93 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
94 #
2575b3b @dhh Added extra words of caution for guarding against SQL-injection attacks
dhh authored
95 # The <tt>authenticate_unsafely</tt> method inserts the parameters directly into the query and is thus susceptible to SQL-injection
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
96 # attacks if the <tt>user_name</tt> and +password+ parameters come directly from a HTTP request. The <tt>authenticate_safely</tt> method,
2575b3b @dhh Added extra words of caution for guarding against SQL-injection attacks
dhh authored
97 # on the other hand, will sanitize the <tt>user_name</tt> and +password+ before inserting them in the query, which will ensure that
db045db @dhh Initial
dhh authored
98 # 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
99 #
5cd38ca @dhh Added documentation about named bind variables
dhh authored
100 # 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
101 # 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
102 # the question marks with symbols and supplying a hash with values for the matching symbol keys:
103 #
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
104 # Company.find(:first, [
105 # "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
5cd38ca @dhh Added documentation about named bind variables
dhh authored
106 # { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
107 # ])
108 #
db045db @dhh Initial
dhh authored
109 # == Overwriting default accessors
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
110 #
db045db @dhh Initial
dhh authored
111 # All column values are automatically available through basic accessors on the Active Record object, but some times you
112 # want to specialize this behavior. This can be done by either by overwriting the default accessors (using the same
113 # name as the attribute) calling read_attribute(attr_name) and write_attribute(attr_name, value) to actually change things.
114 # Example:
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
115 #
db045db @dhh Initial
dhh authored
116 # class Song < ActiveRecord::Base
117 # # 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
118 #
db045db @dhh Initial
dhh authored
119 # def length=(minutes)
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
120 # write_attribute(:length, minutes * 60)
db045db @dhh Initial
dhh authored
121 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
122 #
db045db @dhh Initial
dhh authored
123 # def length
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
124 # read_attribute(:length) / 60
db045db @dhh Initial
dhh authored
125 # end
126 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
127 #
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
128 # You can alternatively use self[:attribute]=(value) and self[:attribute] instead of write_attribute(:attribute, vaule) and
129 # read_attribute(:attribute) as a shorter form.
130 #
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
131 # == Accessing attributes before they have been typecasted
4eab375 @dhh Finished polishing API docs
dhh authored
132 #
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
133 # 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
134 # 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
135 # 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
136 #
137 # 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
138 # 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
139 # want.
140 #
ac8fd7d @dhh Added dynamic attribute-based finders as a cleaner way of getting obj…
dhh authored
141 # == Dynamic attribute-based finders
142 #
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
143 # 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
144 # 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
145 # Person.find_all_by_last_name, Payment.find_by_transaction_id. So instead of writing
146 # <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
147 # 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
148 #
ac8fd7d @dhh Added dynamic attribute-based finders as a cleaner way of getting obj…
dhh authored
149 # It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
150 # <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
151 # <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
152 # <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
153 #
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
154 # It's even possible to use all the additional parameters to find. For example, the full interface for Payment.find_all_by_amount
155 # is actually Payment.find_all_by_amount(amount, options). And the full interface to Person.find_by_user_name is
156 # 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
157 #
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
158 # The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with
159 # <tt>find_or_create_by_</tt> and will return the object if it already exists and otherwise creates it, then returns it. Example:
160 #
161 # # No 'Summer' tag exists
162 # Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
163 #
164 # # Now the 'Summer' tag does exist
165 # Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
166 #
098fa94 @dhh Fixed documentation snafus #575, #576, #577, #585
dhh authored
167 # == 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
168 #
169 # 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
170 # 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
171 #
db045db @dhh Initial
dhh authored
172 # class User < ActiveRecord::Base
173 # serialize :preferences
174 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
175 #
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
176 # user = User.create(:preferences) => { "background" => "black", "display" => large })
db045db @dhh Initial
dhh authored
177 # User.find(user.id).preferences # => { "background" => "black", "display" => large }
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
178 #
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
179 # 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
180 # descendent of a class not in the hierarchy. Example:
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
181 #
db045db @dhh Initial
dhh authored
182 # class User < ActiveRecord::Base
66f44e6 @dhh Updated documentation for serialize
dhh authored
183 # serialize :preferences, Hash
db045db @dhh Initial
dhh authored
184 # end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
185 #
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
186 # user = User.create(:preferences => %w( one two three ))
db045db @dhh Initial
dhh authored
187 # User.find(user.id).preferences # raises SerializationTypeMismatch
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
188 #
db045db @dhh Initial
dhh authored
189 # == Single table inheritance
190 #
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
191 # 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
192 # by overwriting <tt>Base.inheritance_column</tt>). This means that an inheritance looking like this:
193 #
194 # class Company < ActiveRecord::Base; end
195 # class Firm < Company; end
196 # class Client < Company; end
197 # class PriorityClient < Client; end
198 #
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
199 # When you do Firm.create(:name => "37signals"), this record will be saved in the companies table with type = "Firm". You can then
200 # fetch this row again using Company.find(:first, "name = '37signals'") and it will return a Firm object.
db045db @dhh Initial
dhh authored
201 #
f033833 @dhh Improving documentation...
dhh authored
202 # 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
203 # like normal subclasses with no special magic for differentiating between them or reloading the right type with find.
204 #
db045db @dhh Initial
dhh authored
205 # Note, all the attributes for all the cases are kept in the same table. Read more:
206 # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
207 #
db045db @dhh Initial
dhh authored
208 # == Connection to multiple databases in different models
209 #
210 # 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
211 # All classes inheriting from ActiveRecord::Base will use this connection. But you can also set a class-specific connection.
db045db @dhh Initial
dhh authored
212 # For example, if Course is a ActiveRecord::Base, but resides in a different database you can just say Course.establish_connection
213 # and Course *and all its subclasses* will use this connection instead.
214 #
215 # This feature is implemented by keeping a connection pool in ActiveRecord::Base that is a Hash indexed by the class. If a connection is
216 # requested, the retrieve_connection method will go up the class-hierarchy until a connection is found in the connection pool.
217 #
218 # == Exceptions
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
219 #
db045db @dhh Initial
dhh authored
220 # * +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
221 # * +AdapterNotSpecified+ -- the configuration hash used in <tt>establish_connection</tt> didn't include a
db045db @dhh Initial
dhh authored
222 # <tt>:adapter</tt> key.
1aab0e2 @dhh Doc fixes #1775, #1776 [jon@instance-design.co.uk]
dhh authored
223 # * +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
224 # (or a bad spelling of an existing one).
225 # * +AssociationTypeMismatch+ -- the object assigned to the association wasn't of the type specified in the association definition.
226 # * +SerializationTypeMismatch+ -- the object serialized wasn't of the class specified as the second parameter.
db045db @dhh Initial
dhh authored
227 # * +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
228 # * +RecordNotFound+ -- no record responded to the find* method.
db045db @dhh Initial
dhh authored
229 # Either the row with the given ID doesn't exist or the row didn't meet the additional restrictions.
230 # * +StatementInvalid+ -- the database server rejected the SQL statement. The precise error is added in the message.
231 # 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
232 # * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
233 # +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
d2fefbe @dhh Added MultiparameterAssignmentErrors and AttributeAssignmentError exc…
dhh authored
234 # objects that should be inspected to determine which attributes triggered the errors.
235 # * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
236 # 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
237 #
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
238 # *Note*: The attributes listed are class-level attributes (accessible from both the class and instance level).
db045db @dhh Initial
dhh authored
239 # So it's possible to assign a logger to the class through Base.logger= which will then be used by all
240 # instances in the current object space.
241 class Base
242 # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then passed
243 # on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
244 cattr_accessor :logger
245
246 # Returns the connection currently associated with the class. This can
247 # also be used to "borrow" the connection to do database work unrelated
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
248 # to any of the specific Active Records.
db045db @dhh Initial
dhh authored
249 def self.connection
b840e4e Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveR…
Marcel Molina authored
250 if allow_concurrency
dcc2263 Speed up for unthreaded environments. Closes #2431.
Marcel Molina authored
251 retrieve_connection
252 else
253 @connection ||= retrieve_connection
254 end
db045db @dhh Initial
dhh authored
255 end
256
257 # Returns the connection currently associated with the class. This can
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
258 # also be used to "borrow" the connection to do database work that isn't
259 # easily done without going straight to SQL.
db045db @dhh Initial
dhh authored
260 def connection
261 self.class.connection
262 end
263
264 def self.inherited(child) #:nodoc:
265 @@subclasses[self] ||= []
266 @@subclasses[self] << child
267 super
268 end
bfbf6bb @jamis Allow ARStore::Session to indicate that it should not be reloaded in …
jamis authored
269
270 # Allow all subclasses of AR::Base to be reloaded in dev mode, unless they
271 # explicitly decline the honor. USE WITH CAUTION. Only AR subclasses kept
272 # in the framework should use the flag, so #reset_subclasses and so forth
273 # leave it alone.
274 def self.reloadable? #:nodoc:
275 true
276 end
db045db @dhh Initial
dhh authored
277
3c0129a @dhh Fixed memory leak with Active Record classes when Dependencies.mechan…
dhh authored
278 def self.reset_subclasses
bfbf6bb @jamis Allow ARStore::Session to indicate that it should not be reloaded in …
jamis authored
279 nonreloadables = []
e7f61ea @jamis squash the memleak in dev mode finally (fingers crossed, here)
jamis authored
280 subclasses.each do |klass|
bfbf6bb @jamis Allow ARStore::Session to indicate that it should not be reloaded in …
jamis authored
281 unless klass.reloadable?
282 nonreloadables << klass
283 next
284 end
e7f61ea @jamis squash the memleak in dev mode finally (fingers crossed, here)
jamis authored
285 klass.instance_variables.each { |var| klass.send(:remove_instance_variable, var) }
286 klass.instance_methods(false).each { |m| klass.send :undef_method, m }
287 end
bfbf6bb @jamis Allow ARStore::Session to indicate that it should not be reloaded in …
jamis authored
288 @@subclasses = {}
289 nonreloadables.each { |klass| (@@subclasses[klass.superclass] ||= []) << klass }
3c0129a @dhh Fixed memory leak with Active Record classes when Dependencies.mechan…
dhh authored
290 end
291
db045db @dhh Initial
dhh authored
292 @@subclasses = {}
c4a3634 @jeremy Corrected @@configurations typo. #1410 [david@ruppconsulting.com]
jeremy authored
293
db045db @dhh Initial
dhh authored
294 cattr_accessor :configurations
c4a3634 @jeremy Corrected @@configurations typo. #1410 [david@ruppconsulting.com]
jeremy authored
295 @@configurations = {}
296
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
297 # 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
298 # :table_name_with_underscore. If the first is specified, the Product class will look for "productid" instead of "id" as
299 # 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
300 # that this is a global setting for all Active Records.
db045db @dhh Initial
dhh authored
301 cattr_accessor :primary_key_prefix_type
302 @@primary_key_prefix_type = nil
303
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
304 # 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
305 # 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
306 # for tables in a shared database. By default, the prefix is the empty string.
307 cattr_accessor :table_name_prefix
308 @@table_name_prefix = ""
309
310 # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
311 # "people_basecamp"). By default, the suffix is the empty string.
312 cattr_accessor :table_name_suffix
313 @@table_name_suffix = ""
314
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
315 # Indicates whether or not table names should be the pluralized versions of the corresponding class names.
316 # 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
317 # See table_name for the full rules on table/class naming. This is true, by default.
318 cattr_accessor :pluralize_table_names
319 @@pluralize_table_names = true
320
911614d @dhh Added ActiveRecord::Base.colorize_logging to control whether to use c…
dhh authored
321 # 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
322 # 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
323 # may complicate matters if you use software like syslog. This is true, by default.
324 cattr_accessor :colorize_logging
325 @@colorize_logging = true
326
60de8c1 @dhh Added Base.default_timezone accessor that determines whether to use T…
dhh authored
327 # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates and times from the database.
328 # This is set to :local by default.
329 cattr_accessor :default_timezone
330 @@default_timezone = :local
6049977 @dhh Fixed that each request with the WEBrick adapter would open a new dat…
dhh authored
331
332 # Determines whether or not to use a connection for each thread, or a single shared connection for all threads.
333 # Defaults to true; Railties' WEBrick server sets this to false.
b840e4e Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveR…
Marcel Molina authored
334 cattr_accessor :allow_concurrency
335 @@allow_concurrency = true
336
f218771 Add option (true by default) to generate reader methods for each attr…
Marcel Molina authored
337 # Determines whether to speed up access by generating optimized reader
338 # methods to avoid expensive calls to method_missing when accessing
339 # attributes by name. You might want to set this to false in development
340 # mode, because the methods would be regenerated on each request.
341 cattr_accessor :generate_read_methods
342 @@generate_read_methods = true
24c3599 @sstephenson Support using different database adapters for development and test wi…
sstephenson authored
343
344 # Specifies the format to use when dumping the database schema with Rails'
345 # Rakefile. If :sql, the schema is dumped as (potentially database-
346 # specific) SQL statements. If :ruby, the schema is dumped as an
347 # ActiveRecord::Schema file which can be loaded into any database that
348 # supports migrations. Use :ruby if you want to have different database
349 # adapters for, e.g., your development and test environments.
350 cattr_accessor :schema_format
351 @@schema_format = :sql
f218771 Add option (true by default) to generate reader methods for each attr…
Marcel Molina authored
352
db045db @dhh Initial
dhh authored
353 class << self # Class methods
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
354 # Find operates with three different retrieval approaches:
7669011 @dhh Fixes for postgresql testing #1129, #1130, #1131
dhh authored
355 #
356 # * 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]).
357 # If no record can be found for all of the listed ids, then RecordNotFound will be raised.
358 # * Find first: This will return the first record matched by the options used. These options can either be specific
359 # 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
360 # * 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
361 #
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
362 # All approaches accept an option hash as their last parameter. The options are:
7669011 @dhh Fixes for postgresql testing #1129, #1130, #1131
dhh authored
363 #
515886a @dhh Added documentation for new Base.find API and eager association loading
dhh authored
364 # * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
365 # * <tt>:order</tt>: An SQL fragment like "created_at DESC, name".
366 # * <tt>:limit</tt>: An integer determining the limit on the number of rows that should be returned.
367 # * <tt>:offset</tt>: An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
368 # * <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
369 # 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
370 # Pass :readonly => false to override.
515886a @dhh Added documentation for new Base.find API and eager association loading
dhh authored
371 # * <tt>:include</tt>: Names associations that should be loaded alongside using LEFT OUTER JOINs. The symbols named refer
372 # 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
373 # * <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
374 # include the joined columns.
64fcb75 @jeremy r3618@sedna: jeremy | 2005-10-14 12:06:03 -0700
jeremy authored
375 # * <tt>:readonly</tt>: Mark the returned records read-only so they cannot be saved or updated.
7669011 @dhh Fixes for postgresql testing #1129, #1130, #1131
dhh authored
376 #
515886a @dhh Added documentation for new Base.find API and eager association loading
dhh authored
377 # Examples for find by id:
db045db @dhh Initial
dhh authored
378 # Person.find(1) # returns the object for ID = 1
379 # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
380 # 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
381 # 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
382 # Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
383 #
384 # Examples for find first:
7d01005 @dhh Fixed documentation and prepared for release of 0.12
dhh authored
385 # 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
386 # Person.find(:first, :conditions => [ "user_name = ?", user_name])
387 # Person.find(:first, :order => "created_on DESC", :offset => 5)
388 #
389 # Examples for find all:
7d01005 @dhh Fixed documentation and prepared for release of 0.12
dhh authored
390 # 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
391 # Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
392 # Person.find(:all, :offset => 10, :limit => 10)
393 # Person.find(:all, :include => [ :account, :friends ])
6bd672e @dhh Added that Base#find takes an optional options hash, including :condi…
dhh authored
394 def find(*args)
395 options = extract_options_from_args!(args)
db045db @dhh Initial
dhh authored
396
e7cd7e9 @jeremy r3800@sedna: jeremy | 2005-10-28 00:39:05 -0700
jeremy authored
397 # Inherit :readonly from scope_constraints if set. Otherwise,
398 # if :joins is not blank then :readonly defaults to true.
399 unless options.has_key?(:readonly)
400 if scope_constraints.has_key?(:readonly)
401 options[:readonly] = scope_constraints[:readonly]
402 elsif !options[:joins].blank?
403 options[:readonly] = true
404 end
7219e82 @jeremy HABTM finder sets :readonly => false. Closes #2525.
jeremy authored
405 end
64fcb75 @jeremy r3618@sedna: jeremy | 2005-10-14 12:06:03 -0700
jeremy authored
406
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
407 case args.first
408 when :first
5b9b904 @dhh Added support for limit and offset with eager loading of has_one and …
dhh authored
409 find(:all, options.merge(options[:include] ? { } : { :limit => 1 })).first
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
410 when :all
64fcb75 @jeremy r3618@sedna: jeremy | 2005-10-14 12:06:03 -0700
jeremy authored
411 records = options[:include] ? find_with_associations(options) : find_by_sql(construct_finder_sql(options))
412 records.each { |record| record.readonly! } if options[:readonly]
413 records
db045db @dhh Initial
dhh authored
414 else
a9fd639 @dhh Fixed that calling Model.find([]) returns [] and doesn't throw an exc…
dhh authored
415 return args.first if args.first.kind_of?(Array) && args.first.empty?
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
416 expects_array = args.first.kind_of?(Array)
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
417
2a35baa @jamis Wrap :conditions in parentheses to prevent problems with OR's #1871
jamis authored
418 conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
419
420 ids = args.flatten.compact.uniq
421 case ids.size
422 when 0
423 raise RecordNotFound, "Couldn't find #{name} without an ID#{conditions}"
424 when 1
9e799e7 @dhh Prefix primary key with table name so it works as part of a joined fetch
dhh authored
425 if result = find(:first, options.merge({ :conditions => "#{table_name}.#{primary_key} = #{sanitize(ids.first)}#{conditions}" }))
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
426 return expects_array ? [ result ] : result
427 else
428 raise RecordNotFound, "Couldn't find #{name} with ID=#{ids.first}#{conditions}"
429 end
430 else
431 # Find multiple ids
432 ids_list = ids.map { |id| sanitize(id) }.join(',')
7879513 @dhh Fixed incompatibility with Base#find with an array of ids that would …
dhh authored
433 result = find(:all, options.merge({ :conditions => "#{table_name}.#{primary_key} IN (#{ids_list})#{conditions}"}))
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
434 if result.size == ids.size
435 return result
436 else
437 raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions}"
438 end
6bd672e @dhh Added that Base#find takes an optional options hash, including :condi…
dhh authored
439 end
db045db @dhh Initial
dhh authored
440 end
441 end
442
3dfa56c @dhh Updated all references to the old find_first and find_all to use the …
dhh authored
443 # Works like find(:all), but requires a complete SQL string. Examples:
db045db @dhh Initial
dhh authored
444 # 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
445 # Post.find_by_sql ["SELECT * FROM posts WHERE author = ? AND created > ?", author_id, start_date]
db045db @dhh Initial
dhh authored
446 def find_by_sql(sql)
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
447 connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
db045db @dhh Initial
dhh authored
448 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
449
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
450 # Returns true if the given +id+ represents the primary key of a record in the database, false otherwise.
451 # Example:
452 # Person.exists?(5)
453 def exists?(id)
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
454 !find(:first, :conditions => ["#{primary_key} = ?", id]).nil? rescue false
db045db @dhh Initial
dhh authored
455 end
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
456
db045db @dhh Initial
dhh authored
457 # 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
458 # fails under validations, the unsaved object is still returned.
db045db @dhh Initial
dhh authored
459 def create(attributes = nil)
efa81da @dhh Added the option of supplying an array of ids and attributes to Base#…
dhh authored
460 if attributes.is_a?(Array)
461 attributes.collect { |attr| create(attr) }
462 else
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
463 attributes.reverse_merge!(scope_constraints[:creation]) if scope_constraints[:creation]
464
efa81da @dhh Added the option of supplying an array of ids and attributes to Base#…
dhh authored
465 object = new(attributes)
466 object.save
467 object
468 end
db045db @dhh Initial
dhh authored
469 end
470
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
471 # 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
472 # and returns it. If the save fails under validations, the unsaved object is still returned.
db045db @dhh Initial
dhh authored
473 def update(id, attributes)
efa81da @dhh Added the option of supplying an array of ids and attributes to Base#…
dhh authored
474 if id.is_a?(Array)
475 idx = -1
476 id.collect { |id| idx += 1; update(id, attributes[idx]) }
477 else
478 object = find(id)
479 object.update_attributes(attributes)
480 object
481 end
db045db @dhh Initial
dhh authored
482 end
483
efa81da @dhh Added the option of supplying an array of ids and attributes to Base#…
dhh authored
484 # Deletes the record with the given +id+ without instantiating an object first. If an array of ids is provided, all of them
485 # are deleted.
648b8fd @dhh Added Base.destroy and Base.delete to remove records without holding …
dhh authored
486 def delete(id)
efa81da @dhh Added the option of supplying an array of ids and attributes to Base#…
dhh authored
487 delete_all([ "#{primary_key} IN (?)", id ])
648b8fd @dhh Added Base.destroy and Base.delete to remove records without holding …
dhh authored
488 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
489
648b8fd @dhh Added Base.destroy and Base.delete to remove records without holding …
dhh authored
490 # 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
491 # 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
492 def destroy(id)
efa81da @dhh Added the option of supplying an array of ids and attributes to Base#…
dhh authored
493 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
494 end
495
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
496 # 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
497 # A subset of the records can be selected by specifying +conditions+. Example:
db045db @dhh Initial
dhh authored
498 # Billing.update_all "category = 'authorized', approved = 1", "author = 'David'"
499 def update_all(updates, conditions = nil)
566a369 @dhh Added that update_all calls sanitize_sql on its updates argument, so …
dhh authored
500 sql = "UPDATE #{table_name} SET #{sanitize_sql(updates)} "
db045db @dhh Initial
dhh authored
501 add_conditions!(sql, conditions)
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
502 connection.update(sql, "#{name} Update")
db045db @dhh Initial
dhh authored
503 end
0d2db8a @dhh Added Base.update_collection that can update an array of id/attribute…
dhh authored
504
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
505 # Destroys the objects for all the records that match the +condition+ by instantiating each object and calling
db045db @dhh Initial
dhh authored
506 # the destroy method. Example:
507 # Person.destroy_all "last_login < '2004-04-04'"
508 def destroy_all(conditions = nil)
3dfa56c @dhh Updated all references to the old find_first and find_all to use the …
dhh authored
509 find(:all, :conditions => conditions).each { |object| object.destroy }
db045db @dhh Initial
dhh authored
510 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
511
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
512 # Deletes all the records that match the +condition+ without instantiating the objects first (and hence not
db045db @dhh Initial
dhh authored
513 # calling the destroy method). Example:
514 # Post.destroy_all "person_id = 5 AND (category = 'Something' OR category = 'Else')"
515 def delete_all(conditions = nil)
516 sql = "DELETE FROM #{table_name} "
517 add_conditions!(sql, conditions)
518 connection.delete(sql, "#{name} Delete all")
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 # Returns the number of records that meet the +conditions+. Zero is returned if no records match. Example:
db045db @dhh Initial
dhh authored
522 # Product.count "sales > 1"
06a6133 @dhh Added a join parameter as the third argument to Base.find_first and a…
dhh authored
523 def count(conditions = nil, joins = nil)
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
524 sql = "SELECT COUNT(*) FROM #{table_name} "
eefe4d0 @dhh Fixed extraneous comma in count() function that made it not work with…
dhh authored
525 sql << " #{joins} " if joins
db045db @dhh Initial
dhh authored
526 add_conditions!(sql, conditions)
527 count_by_sql(sql)
528 end
529
530 # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
e17bf81 @jamis Fix typo in count_by_sql documentation #1969 [Alexey Verkhovsky]
jamis authored
531 # Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
db045db @dhh Initial
dhh authored
532 def count_by_sql(sql)
a775cb1 @dhh Added the option for sanitizing find_by_sql and the offset parts in r…
dhh authored
533 sql = sanitize_conditions(sql)
caaf40d @dhh Added AbstractAdapter#select_value and AbstractAdapter#select_values …
dhh authored
534 connection.select_value(sql, "#{name} Count").to_i
db045db @dhh Initial
dhh authored
535 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
536
537 # Increments the specified counter by one. So <tt>DiscussionBoard.increment_counter("post_count",
db045db @dhh Initial
dhh authored
538 # discussion_board_id)</tt> would increment the "post_count" counter on the board responding to discussion_board_id.
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
539 # This is used for caching aggregate values, so that they don't need to be computed every time. Especially important
db045db @dhh Initial
dhh authored
540 # for looping over a collection where each element require a number of aggregate values. Like the DiscussionBoard
541 # that needs to list both the number of posts and comments.
542 def increment_counter(counter_name, id)
4940383 @dhh Fixed value quoting in all generated SQL statements, so that integers…
dhh authored
543 update_all "#{counter_name} = #{counter_name} + 1", "#{primary_key} = #{quote(id)}"
db045db @dhh Initial
dhh authored
544 end
545
546 # Works like increment_counter, but decrements instead.
547 def decrement_counter(counter_name, id)
4940383 @dhh Fixed value quoting in all generated SQL statements, so that integers…
dhh authored
548 update_all "#{counter_name} = #{counter_name} - 1", "#{primary_key} = #{quote(id)}"
db045db @dhh Initial
dhh authored
549 end
550
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
551 # Attributes named in this macro are protected from mass-assignment, such as <tt>new(attributes)</tt> and
db045db @dhh Initial
dhh authored
552 # <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
553 # methods to do assignment. This is meant to protect sensitive attributes from being overwritten by URL/form hackers. Example:
db045db @dhh Initial
dhh authored
554 #
555 # class Customer < ActiveRecord::Base
556 # attr_protected :credit_rating
557 # end
558 #
559 # customer = Customer.new("name" => David, "credit_rating" => "Excellent")
560 # customer.credit_rating # => nil
561 # customer.attributes = { "description" => "Jolly fellow", "credit_rating" => "Superb" }
562 # customer.credit_rating # => nil
563 #
564 # customer.credit_rating = "Average"
565 # customer.credit_rating # => "Average"
566 def attr_protected(*attributes)
0e0e774 Protect id attribute from mass assigment even when the primary key is…
Marcel Molina authored
567 write_inheritable_array("attr_protected", attributes - (protected_attributes || []))
db045db @dhh Initial
dhh authored
568 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
569
098fa94 @dhh Fixed documentation snafus #575, #576, #577, #585
dhh authored
570 # Returns an array of all the attributes that have been protected from mass-assignment.
db045db @dhh Initial
dhh authored
571 def protected_attributes # :nodoc:
572 read_inheritable_attribute("attr_protected")
573 end
574
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
575 # If this macro is used, only those attributes named in it will be accessible for mass-assignment, such as
db045db @dhh Initial
dhh authored
576 # <tt>new(attributes)</tt> and <tt>attributes=(attributes)</tt>. This is the more conservative choice for mass-assignment
577 # protection. If you'd rather start from an all-open default and restrict attributes as needed, have a look at
578 # attr_protected.
579 def attr_accessible(*attributes)
0e0e774 Protect id attribute from mass assigment even when the primary key is…
Marcel Molina authored
580 write_inheritable_array("attr_accessible", attributes - (accessible_attributes || []))
db045db @dhh Initial
dhh authored
581 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
582
098fa94 @dhh Fixed documentation snafus #575, #576, #577, #585
dhh authored
583 # Returns an array of all the attributes that have been made accessible to mass-assignment.
db045db @dhh Initial
dhh authored
584 def accessible_attributes # :nodoc:
585 read_inheritable_attribute("attr_accessible")
586 end
587
588 # Specifies that the attribute by the name of +attr_name+ should be serialized before saving to the database and unserialized
589 # after loading from the database. The serialization is done through YAML. If +class_name+ is specified, the serialized
098fa94 @dhh Fixed documentation snafus #575, #576, #577, #585
dhh authored
590 # object must be of that class on retrieval or +SerializationTypeMismatch+ will be raised.
db045db @dhh Initial
dhh authored
591 def serialize(attr_name, class_name = Object)
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
592 serialized_attributes[attr_name.to_s] = class_name
db045db @dhh Initial
dhh authored
593 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
594
db045db @dhh Initial
dhh authored
595 # Returns a hash of all the attributes that have been specified for serialization as keys and their class restriction as values.
596 def serialized_attributes
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
597 read_inheritable_attribute("attr_serialized") or write_inheritable_attribute("attr_serialized", {})
db045db @dhh Initial
dhh authored
598 end
599
600 # Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending
601 # directly from ActiveRecord. So if the hierarchy looks like: Reply < Message < ActiveRecord, then Message is used
4eab375 @dhh Finished polishing API docs
dhh authored
602 # to guess the table name from even when called on Reply. The rules used to do the guess are handled by the Inflector class
603 # in Active Support, which knows almost all common English inflections (report a bug if your inflection isn't covered).
db045db @dhh Initial
dhh authored
604 #
605 # Additionally, the class-level table_name_prefix is prepended to the table_name and the table_name_suffix is appended.
606 # So if you have "myapp_" as a prefix, the table name guess for an Account class becomes "myapp_accounts".
607 #
608 # You can also overwrite this class method to allow for unguessable links, such as a Mouse class with a link to a
609 # "mice" table. Example:
610 #
611 # class Mouse < ActiveRecord::Base
9982578 @dhh Doc fix #805
dhh authored
612 # set_table_name "mice"
db045db @dhh Initial
dhh authored
613 # end
dcc4868 @dhh Fixed that Base.table_name would expect a parameter when used in has_…
dhh authored
614 def table_name
d736568 Speed up the setting of table_name. Closes #2428.
Marcel Molina authored
615 reset_table_name
616 end
617
618 def reset_table_name
619 name = "#{table_name_prefix}#{undecorated_table_name(class_name_of_active_record_descendant(self))}#{table_name_suffix}"
620 set_table_name name
621 name
db045db @dhh Initial
dhh authored
622 end
623
098fa94 @dhh Fixed documentation snafus #575, #576, #577, #585
dhh authored
624 # Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
db045db @dhh Initial
dhh authored
625 # primary_key_prefix_type setting, though.
626 def primary_key
c0899bc Add convenience predicate methods on Column class. In partial fullfil…
Marcel Molina authored
627 reset_primary_key
628 end
629
630 def reset_primary_key
631 key = 'id'
db045db @dhh Initial
dhh authored
632 case primary_key_prefix_type
633 when :table_name
c0899bc Add convenience predicate methods on Column class. In partial fullfil…
Marcel Molina authored
634 key = Inflector.foreign_key(class_name_of_active_record_descendant(self), false)
db045db @dhh Initial
dhh authored
635 when :table_name_with_underscore
c0899bc Add convenience predicate methods on Column class. In partial fullfil…
Marcel Molina authored
636 key = Inflector.foreign_key(class_name_of_active_record_descendant(self))
db045db @dhh Initial
dhh authored
637 end
c0899bc Add convenience predicate methods on Column class. In partial fullfil…
Marcel Molina authored
638 set_primary_key(key)
639 key
db045db @dhh Initial
dhh authored
640 end
641
642 # Defines the column name for use with single table inheritance -- can be overridden in subclasses.
643 def inheritance_column
644 "type"
645 end
646
7117fdb @jeremy r3616@asus: jeremy | 2005-09-26 23:09:28 -0700
jeremy authored
647 # Default sequence_name. Use set_sequence_name to override.
14ea312 @dhh Made Oracle a first-class connection adapter by adhering closer to id…
dhh authored
648 def sequence_name
7117fdb @jeremy r3616@asus: jeremy | 2005-09-26 23:09:28 -0700
jeremy authored
649 connection.default_sequence_name(table_name, primary_key)
14ea312 @dhh Made Oracle a first-class connection adapter by adhering closer to id…
dhh authored
650 end
651
1aa82b3 @dhh Added keyword-style approach to defining the custom relational bindin…
dhh authored
652 # Sets the table name to use to the given value, or (if the value
4eab375 @dhh Finished polishing API docs
dhh authored
653 # 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
654 #
655 # Example:
656 #
657 # class Project < ActiveRecord::Base
658 # set_table_name "project"
659 # end
660 def set_table_name( value=nil, &block )
661 define_attr_method :table_name, value, &block
662 end
663 alias :table_name= :set_table_name
664
665 # Sets the name of the primary key column to use to the given value,
666 # or (if the value is nil or false) to the value returned by the given
4eab375 @dhh Finished polishing API docs
dhh authored
667 # block.
1aa82b3 @dhh Added keyword-style approach to defining the custom relational bindin…
dhh authored
668 #
669 # Example:
670 #
671 # class Project < ActiveRecord::Base
672 # set_primary_key "sysid"
673 # end
674 def set_primary_key( value=nil, &block )
675 define_attr_method :primary_key, value, &block
676 end
677 alias :primary_key= :set_primary_key
678
679 # Sets the name of the inheritance column to use to the given value,
680 # or (if the value # is nil or false) to the value returned by the
4eab375 @dhh Finished polishing API docs
dhh authored
681 # given block.
1aa82b3 @dhh Added keyword-style approach to defining the custom relational bindin…
dhh authored
682 #
683 # Example:
684 #
685 # class Project < ActiveRecord::Base
686 # set_inheritance_column do
687 # original_inheritance_column + "_id"
688 # end
689 # end
690 def set_inheritance_column( value=nil, &block )
691 define_attr_method :inheritance_column, value, &block
692 end
693 alias :inheritance_column= :set_inheritance_column
694
14ea312 @dhh Made Oracle a first-class connection adapter by adhering closer to id…
dhh authored
695 # Sets the name of the sequence to use when generating ids to the given
696 # 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
697 # given block. This is required for Oracle and is useful for any
698 # 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
699 #
700 # Setting the sequence name when using other dbs will have no effect.
701 # If a sequence name is not explicitly set when using Oracle, it will
702 # default to the commonly used pattern of: #{table_name}_seq
703 #
704 # Example:
705 #
706 # class Project < ActiveRecord::Base
707 # set_sequence_name "projectseq" # default would have been "project_seq"
708 # end
709 def set_sequence_name( value=nil, &block )
710 define_attr_method :sequence_name, value, &block
711 end
712 alias :sequence_name= :set_sequence_name
713
db045db @dhh Initial
dhh authored
714 # Turns the +table_name+ back into a class name following the reverse rules of +table_name+.
715 def class_name(table_name = table_name) # :nodoc:
716 # remove any prefix and/or suffix from the table name
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
717 class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].camelize
718 class_name = class_name.singularize if pluralize_table_names
719 class_name
db045db @dhh Initial
dhh authored
720 end
721
816f37a @dhh Added migration support to SQL Server adapter (please someone do the …
dhh authored
722 # Indicates whether the table associated with this class exists
723 def table_exists?
724 if connection.respond_to?(:tables)
725 connection.tables.include? table_name
726 else
727 # if the connection adapter hasn't implemented tables, there are two crude tests that can be
728 # used - see if getting column info raises an error, or if the number of columns returned is zero
729 begin
730 reset_column_information
731 columns.size > 0
732 rescue ActiveRecord::StatementInvalid
733 false
734 end
735 end
736 end
737
db045db @dhh Initial
dhh authored
738 # Returns an array of column objects for the table associated with this class.
739 def columns
c0899bc Add convenience predicate methods on Column class. In partial fullfil…
Marcel Molina authored
740 unless @columns
741 @columns = connection.columns(table_name, "#{name} Columns")
742 @columns.each {|column| column.primary = column.name == primary_key}
743 end
744 @columns
db045db @dhh Initial
dhh authored
745 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
746
db045db @dhh Initial
dhh authored
747 # Returns an array of column objects for the table associated with this class.
748 def columns_hash
749 @columns_hash ||= columns.inject({}) { |hash, column| hash[column.name] = column; hash }
750 end
d0bd3b5 @jeremy Return PostgreSQL columns in the order they are declared #1374 (perlg…
jeremy authored
751
49d0f0c @dhh Speeded up eager loading a whole bunch
dhh authored
752 def column_names
d0bd3b5 @jeremy Return PostgreSQL columns in the order they are declared #1374 (perlg…
jeremy authored
753 @column_names ||= columns.map { |column| column.name }
49d0f0c @dhh Speeded up eager loading a whole bunch
dhh authored
754 end
db045db @dhh Initial
dhh authored
755
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
756 # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
757 # and columns used for single table inheritance have been removed.
db045db @dhh Initial
dhh authored
758 def content_columns
c0899bc Add convenience predicate methods on Column class. In partial fullfil…
Marcel Molina authored
759 @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
db045db @dhh Initial
dhh authored
760 end
761
762 # 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
763 # 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
764 # is available.
db045db @dhh Initial
dhh authored
765 def column_methods_hash
d0bd3b5 @jeremy Return PostgreSQL columns in the order they are declared #1374 (perlg…
jeremy authored
766 @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
767 attr_name = attr.to_s
768 methods[attr.to_sym] = attr_name
769 methods["#{attr}=".to_sym] = attr_name
770 methods["#{attr}?".to_sym] = attr_name
771 methods["#{attr}_before_type_cast".to_sym] = attr_name
db045db @dhh Initial
dhh authored
772 methods
773 end
774 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
775
f218771 Add option (true by default) to generate reader methods for each attr…
Marcel Molina authored
776 # Contains the names of the generated reader methods.
777 def read_methods
aabf909 @jeremy Correct reader method generation for primary key attribute: handle ca…
jeremy authored
778 @read_methods ||= Set.new
f218771 Add option (true by default) to generate reader methods for each attr…
Marcel Molina authored
779 end
aabf909 @jeremy Correct reader method generation for primary key attribute: handle ca…
jeremy authored
780
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
781 # 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
782 def reset_column_information
aabf909 @jeremy Correct reader method generation for primary key attribute: handle ca…
jeremy authored
783 read_methods.each { |name| undef_method(name) }
f218771 Add option (true by default) to generate reader methods for each attr…
Marcel Molina authored
784 @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @read_methods = nil
1314f48 @dhh Added methods for resetting the cached information on classes that yo…
dhh authored
785 end
786
4eab375 @dhh Finished polishing API docs
dhh authored
787 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
788 subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
789 end
db045db @dhh Initial
dhh authored
790
791 # Transforms attribute key names into a more humane format, such as "First name" instead of "first_name". Example:
792 # Person.human_attribute_name("first_name") # => "First name"
4eab375 @dhh Finished polishing API docs
dhh authored
793 # Deprecated in favor of just calling "first_name".humanize
794 def human_attribute_name(attribute_key_name) #:nodoc:
1f7e72f @dhh Made human_attribute_name(attribute_key_name) use Inflector.humanize
dhh authored
795 attribute_key_name.humanize
db045db @dhh Initial
dhh authored
796 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
797
db045db @dhh Initial
dhh authored
798 def descends_from_active_record? # :nodoc:
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
799 superclass == Base || !columns_hash.include?(inheritance_column)
db045db @dhh Initial
dhh authored
800 end
801
4eab375 @dhh Finished polishing API docs
dhh authored
802 def quote(object) #:nodoc:
4940383 @dhh Fixed value quoting in all generated SQL statements, so that integers…
dhh authored
803 connection.quote(object)
804 end
805
806 # 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
807 def sanitize(object) #:nodoc:
4940383 @dhh Fixed value quoting in all generated SQL statements, so that integers…
dhh authored
808 connection.quote(object)
db045db @dhh Initial
dhh authored
809 end
810
f2a89d7 @dhh Give AR the new benchmark method too
dhh authored
811 # Log and benchmark multiple statements in a single block. Example:
db045db @dhh Initial
dhh authored
812 #
813 # Project.benchmark("Creating project") do
814 # project = Project.create("name" => "stuff")
815 # project.create_manager("name" => "David")
3dfa56c @dhh Updated all references to the old find_first and find_all to use the …
dhh authored
816 # project.milestones << Milestone.find(:all)
db045db @dhh Initial
dhh authored
817 # end
5e8e8d6 @dhh Added use_silence parameter to ActiveRecord::Base.benchmark that can …
dhh authored
818 #
f2a89d7 @dhh Give AR the new benchmark method too
dhh authored
819 # The benchmark is only recorded if the current level of the logger matches the <tt>log_level</tt>, which makes it
820 # easy to include benchmarking statements in production software that will remain inexpensive because the benchmark
821 # will only be conducted if the log level is low enough.
822 #
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
823 # 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
824 def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
825 if logger && logger.level == log_level
5e8e8d6 @dhh Added use_silence parameter to ActiveRecord::Base.benchmark that can …
dhh authored
826 result = nil
827 seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield }
f2a89d7 @dhh Give AR the new benchmark method too
dhh authored
828 logger.add(log_level, "#{title} (#{'%.5f' % seconds})")
5e8e8d6 @dhh Added use_silence parameter to ActiveRecord::Base.benchmark that can …
dhh authored
829 result
830 else
831 yield
832 end
c00bf5f @dhh Fixed the verbosity of using the AR store
dhh authored
833 end
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
834
c00bf5f @dhh Fixed the verbosity of using the AR store
dhh authored
835 # Silences the logger for the duration of the block.
836 def silence
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
837 old_logger_level, logger.level = logger.level, Logger::ERROR if logger
838 yield
839 ensure
908e9a1 @dhh Fixed that Base.silence should restore the old logger level when done…
dhh authored
840 logger.level = old_logger_level if logger
db045db @dhh Initial
dhh authored
841 end
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
842
f4d1af3 Fix typo of 'constrains' to 'contraints'. Closes #2069.
Marcel Molina authored
843 # Add constraints to all queries to the same model in the given block.
0b92d38 @dhh Added :offset and :limit to the kinds of options that Base.constrain …
dhh authored
844 # Currently supported constraints are <tt>:conditions</tt>, <tt>:joins</tt>,
845 # <tt>:offset</tt>, and <tt>:limit</tt>
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
846 #
847 # Article.constrain(:conditions => "blog_id = 1") do
848 # Article.find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
849 # end
e7cd7e9 @jeremy r3800@sedna: jeremy | 2005-10-28 00:39:05 -0700
jeremy authored
850 def constrain(options = {})
851 options = options.dup
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
852 options[:readonly] = true if !options[:joins].blank? && !options.has_key?(:readonly)
853
e7cd7e9 @jeremy r3800@sedna: jeremy | 2005-10-28 00:39:05 -0700
jeremy authored
854 self.scope_constraints = options
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
855
e7cd7e9 @jeremy r3800@sedna: jeremy | 2005-10-28 00:39:05 -0700
jeremy authored
856 yield if block_given?
857 ensure
858 self.scope_constraints = nil
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
859 end
db045db @dhh Initial
dhh authored
860
97849de @dhh Fixed that association proxies would fail === tests like PremiumSubsc…
dhh authored
861 # Overwrite the default class equality method to provide support for association proxies.
862 def ===(object)
863 object.is_a?(self)
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
864 end
b840e4e Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveR…
Marcel Molina authored
865
866 # Deprecated
867 def threaded_connections
868 allow_concurrency
869 end
870
871 # Deprecated
872 def threaded_connections=(value)
873 self.allow_concurrency = value
874 end
875
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
876
db045db @dhh Initial
dhh authored
877 private
878 # Finder methods must instantiate through this method to work with the single-table inheritance model
879 # that makes it possible to create objects of different types from the same table.
880 def instantiate(record)
eb2fbf0 Optimize instantiation of STI subclass records. In partial fullfilmen…
Marcel Molina authored
881 object =
882 if subclass_name = record[inheritance_column]
883 if subclass_name.empty?
884 allocate
885 else
886 require_association_class(subclass_name)
887 begin
888 compute_type(subclass_name).allocate
889 rescue NameError
890 raise SubclassNotFound,
891 "The single-table inheritance mechanism failed to locate the subclass: '#{record[inheritance_column]}'. " +
892 "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
893 "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
894 "or overwrite #{self.to_s}.inheritance_column to use another column for that information."
895 end
896 end
897 else
898 allocate
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
899 end
605bc77 @dhh Added a better exception for when a type column is used in a table wi…
dhh authored
900
db045db @dhh Initial
dhh authored
901 object.instance_variable_set("@attributes", record)
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
902 object
db045db @dhh Initial
dhh authored
903 end
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
904
db045db @dhh Initial
dhh authored
905 # Returns the name of the type of the record using the current module as a prefix. So descendents of
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
906 # MyApp::Business::Account would appear as "MyApp::Business::AccountSubclass".
db045db @dhh Initial
dhh authored
907 def type_name_with_module(type_name)
908 self.name =~ /::/ ? self.name.scan(/(.*)::/).first.first + "::" + type_name : type_name
909 end
910
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
911 def construct_finder_sql(options)
6f05696 @dhh Added :select option to find which can specify a different value than…
dhh authored
912 sql = "SELECT #{options[:select] || '*'} FROM #{table_name} "
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
913 add_joins!(sql, options)
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
914 add_conditions!(sql, options[:conditions])
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
915 sql << " ORDER BY #{options[:order]} " if options[:order]
efb55d1 @dhh Allow order, conditions, and joins in finds that include associations
dhh authored
916 add_limit!(sql, options)
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
917 sql
efb55d1 @dhh Allow order, conditions, and joins in finds that include associations
dhh authored
918 end
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
919
efb55d1 @dhh Allow order, conditions, and joins in finds that include associations
dhh authored
920 def add_limit!(sql, options)
0b92d38 @dhh Added :offset and :limit to the kinds of options that Base.constrain …
dhh authored
921 options[:limit] ||= scope_constraints[:limit] if scope_constraints[:limit]
922 options[:offset] ||= scope_constraints[:offset] if scope_constraints[:offset]
f2a29ca @dhh Added support for ODBC connections to MS SQL Server so you can connec…
dhh authored
923 connection.add_limit_offset!(sql, options)
abc895b @dhh Added new Base.find API and deprecated find_all, find_first. Added pr…
dhh authored
924 end
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
925
926 def add_joins!(sql, options)
f4d1af3 Fix typo of 'constrains' to 'contraints'. Closes #2069.
Marcel Molina authored
927 join = scope_constraints[:joins] || options[:joins]
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
928 sql << " #{join} " if join
929 end
930
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
931 # Adds a sanitized version of +conditions+ to the +sql+ string. Note that the passed-in +sql+ string is changed.
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
932 def add_conditions!(sql, conditions)
f4d1af3 Fix typo of 'constrains' to 'contraints'. Closes #2069.
Marcel Molina authored
933 condition_segments = [scope_constraints[:conditions]]
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
934 condition_segments << sanitize_sql(conditions) unless conditions.nil?
935 condition_segments << type_condition unless descends_from_active_record?
328ef3f @jeremy Parenthesize :conditions
jeremy authored
936 condition_segments.compact!
937 sql << "WHERE (#{condition_segments.join(") AND (")}) " unless condition_segments.empty?
db045db @dhh Initial
dhh authored
938 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
939
db045db @dhh Initial
dhh authored
940 def type_condition
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
941 type_condition = subclasses.inject("#{table_name}.#{inheritance_column} = '#{name.demodulize}' ") do |condition, subclass|
fdd2681 @dhh Made eager loading work with inheritance hierarchies #1065 [Ryan Carver]
dhh authored
942 condition << "OR #{table_name}.#{inheritance_column} = '#{subclass.name.demodulize}' "
943 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
944
945 " (#{type_condition}) "
db045db @dhh Initial
dhh authored
946 end
947
948 # Guesses the table name, but does not decorate it with prefix and suffix information.
949 def undecorated_table_name(class_name = class_name_of_active_record_descendant(self))
950 table_name = Inflector.underscore(Inflector.demodulize(class_name))
951 table_name = Inflector.pluralize(table_name) if pluralize_table_names
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
952 table_name
db045db @dhh Initial
dhh authored
953 end
954
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
955 # Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) that are turned into
956 # find(:first, :conditions => ["user_name = ?", user_name]) and find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])
3dfa56c @dhh Updated all references to the old find_first and find_all to use the …
dhh authored
957 # respectively. Also works for find(:all), but using find_all_by_amount(50) that are turned into find(:all, :conditions => ["amount = ?", 50]).
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
958 #
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
959 # It's even possible to use all the additional parameters to find. For example, the full interface for find_all_by_amount
960 # is actually find_all_by_amount(amount, options).
ac8fd7d @dhh Added dynamic attribute-based finders as a cleaner way of getting obj…
dhh authored
961 def method_missing(method_id, *arguments)
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
962 if match = /find_(all_by|by)_([_a-zA-Z]\w*)/.match(method_id.to_s)
963 finder = determine_finder(match)
ac8fd7d @dhh Added dynamic attribute-based finders as a cleaner way of getting obj…
dhh authored
964
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
965 attribute_names = extract_attribute_names_from_match(match)
966 super unless all_attributes_exists?(attribute_names)
93ec130 @dhh Fixed that the dynamic finders didnt treat nil as a "IS NULL" but rat…
dhh authored
967
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
968 conditions = construct_conditions_from_arguments(attribute_names, arguments)
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
969
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
970 if arguments[attribute_names.length].is_a?(Hash)
971 find(finder, { :conditions => conditions }.update(arguments[attribute_names.length]))
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
972 else
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
973 send("find_#{finder}", conditions, *arguments[attribute_names.length..-1]) # deprecated API
0591c53 @dhh Made the dynamic finders use the new find API and updated the example…
dhh authored
974 end
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
975 elsif match = /find_or_create_by_([_a-zA-Z]\w*)/.match(method_id.to_s)
976 attribute_names = extract_attribute_names_from_match(match)
977 super unless all_attributes_exists?(attribute_names)
978
979 find(:first, :conditions => construct_conditions_from_arguments(attribute_names, arguments)) ||
980 create(construct_attributes_from_arguments(attribute_names, arguments))
ac8fd7d @dhh Added dynamic attribute-based finders as a cleaner way of getting obj…
dhh authored
981 else
982 super
983 end
984 end
db045db @dhh Initial
dhh authored
985
a5a82d9 @dhh Added extension capabilities to has_many and has_and_belongs_to_many …
dhh authored
986 def determine_finder(match)
987 match.captures.first == 'all_by' ? :all : :first
988 end
989
990 def extract_attribute_names_from_match(match)
991 match.captures.last.split('_and_')
992 end
993
994 def construct_conditions_from_arguments(attribute_names, arguments)
995 conditions = []
996 attribute_names.each_with_index { |name, idx| conditions << "#{table_name}.#{name} #{attribute_condition(arguments[idx])} " }
997 [ conditions.join(" AND "), *arguments[0...attribute_names.length] ]
998 end
999
1000 def construct_attributes_from_arguments(attribute_names, arguments)
1001 attributes = {}
1002 attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }
1003 attributes
1004 end
1005
1006 def all_attributes_exists?(attribute_names)
1007 attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) }
1008 end
1009
5f77f64 @dhh Added option for passing an array to the find_all version of the dyna…
dhh authored
1010 def attribute_condition(argument)
1011 case argument
1012 when nil then "IS ?"
1013 when Array then "IN (?)"
1014 else "= ?"
1015 end
1016 end
1017
4eab375 @dhh Finished polishing API docs
dhh authored
1018 # Defines an "attribute" method (like #inheritance_column or
1019 # #table_name). A new (class) method will be created with the
1020 # given name. If a value is specified, the new method will
1021 # return that value (as a string). Otherwise, the given block
1022 # will be used to compute the value of the method.
1023 #
1024 # The original method will be aliased, with the new name being
1025 # prefixed with "original_". This allows the new method to
1026 # access the original value.
1027 #
1028 # Example:
1029 #
1030 # class A < ActiveRecord::Base
1031 # define_attr_method :primary_key, "sysid"
1032 # define_attr_method( :inheritance_column ) do
1033 # original_inheritance_column + "_id"
1034 # end
1035 # end
1036 def define_attr_method(name, value=nil, &block)
1037 sing = class << self; self; end
e2fc88e @jamis Avoid memleak in dev mode with fastcgi
jamis authored
1038 sing.send :alias_method, "original_#{name}", name
1039 if value
1040 # use eval instead of a block to work around a memory leak in dev
1041 # mode in fcgi
1042 sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
1043 else
1044 sing.send :define_method, name, &block
1045 end
4eab375 @dhh Finished polishing API docs
dhh authored
1046 end
1047
db045db @dhh Initial
dhh authored
1048 protected
1049 def subclasses
1050 @@subclasses[self] ||= []
1051 @@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
1052 end
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
1053
f4d1af3 Fix typo of 'constrains' to 'contraints'. Closes #2069.
Marcel Molina authored
1054 def scope_constraints
b840e4e Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveR…
Marcel Molina authored
1055 if allow_concurrency
dcc2263 Speed up for unthreaded environments. Closes #2431.
Marcel Molina authored
1056 Thread.current[:constraints] ||= {}
1057 Thread.current[:constraints][self] ||= {}
1058 else
1059 @scope_constraints ||= {}
1060 end
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
1061 end
f4d1af3 Fix typo of 'constrains' to 'contraints'. Closes #2069.
Marcel Molina authored
1062 # backwards compatibility
1063 alias_method :scope_constrains, :scope_constraints
1064
1065 def scope_constraints=(value)
b840e4e Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveR…
Marcel Molina authored
1066 if allow_concurrency
dcc2263 Speed up for unthreaded environments. Closes #2431.
Marcel Molina authored
1067 Thread.current[:constraints] ||= {}
1068 Thread.current[:constraints][self] = value
1069 else
1070 @scope_constraints = value
1071 end
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
1072 end
f4d1af3 Fix typo of 'constrains' to 'contraints'. Closes #2069.
Marcel Molina authored
1073 # backwards compatibility
1074 alias_method :scope_constrains=, :scope_constraints=
34f9d30 @dhh Added support for calling constrained class methods on has_many and h…
dhh authored
1075
2948910 Misc doc fixes (typos/grammar/etc.). Closes #2430.
Marcel Molina authored
1076 # Returns the class type of the record using the current module as a prefix. So descendents of
1077 # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
db045db @dhh Initial
dhh authored
1078 def compute_type(type_name)
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
1079 type_name_with_module(type_name).split("::").inject(Object) do |final_type, part|
5495b14 @jeremy r2979@asus: jeremy | 2005-07-10 01:51:00 -0700
jeremy authored
1080 final_type.const_get(part)
db045db @dhh Initial
dhh authored
1081 end
1082 end
1083
1084 # Returns the name of the class descending directly from ActiveRecord in the inheritance hierarchy.
1085 def class_name_of_active_record_descendant(klass)
1086 if klass.superclass == Base
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
1087 klass.name
db045db @dhh Initial
dhh authored
1088 elsif klass.superclass.nil?
1089 raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
1090 else
1091 class_name_of_active_record_descendant(klass.superclass)
1092 end
1093 end
1094
6bd672e @dhh Added that Base#find takes an optional options hash, including :condi…
dhh authored
1095 # Accepts an array or string. The string is returned untouched, but the array has each value
1096 # sanitized and interpolated into the sql statement.
1097 # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
1098 def sanitize_sql(ary)
1099 return ary unless ary.is_a?(Array)
3e7d191 @dhh Added bind-style variable interpolation for the condition arrays that…
dhh authored
1100
6bd672e @dhh Added that Base#find takes an optional options hash, including :condi…
dhh authored
1101 statement, *values = ary
1102 if values.first.is_a?(Hash) and statement =~ /:\w+/
1103 replace_named_bind_variables(statement, values.first)
1104 elsif statement.include?('?')
554597d @dhh Added named bind-style variable interpolation #281 [Michael Koziarski]
dhh authored
1105 replace_bind_variables(statement, values)
1106 else
4940383 @dhh Fixed value quoting in all generated SQL statements, so that integers…
dhh authored
1107 statement % values.collect { |value| connection.quote_string(value.to_s) }
554597d @dhh Added named bind-style variable interpolation #281 [Michael Koziarski]
dhh authored
1108 end
3e7d191 @dhh Added bind-style variable interpolation for the condition arrays that…
dhh authored
1109 end
1110
6bd672e @dhh Added that Base#find takes an optional options hash, including :condi…
dhh authored
1111 alias_method :sanitize_conditions, :sanitize_sql
1112
3e7d191 @dhh Added bind-style variable interpolation for the condition arrays that…
dhh authored
1113 def replace_bind_variables(statement, values)
9322168 @dhh Restored bind arity checking #412 [bitsweat]
dhh authored
1114 raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
6bd672e @dhh Added that Base#find takes an optional options hash, including :condi…
dhh authored
1115 bound = values.dup
872ddaf @dhh Added bind-named arrays for interpolating a group of ids or strings i…
dhh authored
1116 statement.gsub('?') { quote_bound_value(bound.shift) }
554597d @dhh Added named bind-style variable interpolation #281 [Michael Koziarski]
dhh authored
1117 end
1118
6bd672e @dhh Added that Base#find takes an optional options hash, including :condi…
dhh authored
1119 def replace_named_bind_variables(statement, bind_vars)
9322168 @dhh Restored bind arity checking #412 [bitsweat]
dhh authored
1120 raise_if_bind_arity_mismatch(statement, statement.scan(/:(\w+)/).uniq.size, bind_vars.size)
6bd672e @dhh Added that Base#find takes an optional options hash, including :condi…
dhh authored
1121 statement.gsub(/:(\w+)/) do
1122 match = $1.to_sym
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
1123 if bind_vars.include?(match)
872ddaf @dhh Added bind-named arrays for interpolating a group of ids or strings i…
dhh authored
1124 quote_bound_value(bind_vars[match])
6bd672e @dhh Added that Base#find takes an optional options hash, including :condi…
dhh authored
1125 else
1126 raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
554597d @dhh Added named bind-style variable interpolation #281 [Michael Koziarski]
dhh authored
1127 end
1128 end
9322168 @dhh Restored bind arity checking #412 [bitsweat]
dhh authored
1129 end
1130
872ddaf @dhh Added bind-named arrays for interpolating a group of ids or strings i…
dhh authored
1131 def quote_bound_value(value)
c2ed453 @dhh Fix quote_bound_value to not map Strings #1416 [htonl]
dhh authored
1132 if (value.respond_to?(:map) && !value.is_a?(String))
1133 value.map { |v| connection.quote(v) }.join(',')
1134 else
1135 connection.quote(value)
872ddaf @dhh Added bind-named arrays for interpolating a group of ids or strings i…
dhh authored
1136 end
1137 end
1138
9322168 @dhh Restored bind arity checking #412 [bitsweat]
dhh authored
1139 def raise_if_bind_arity_mismatch(statement, expected, provided)
1140 unless expected == provided
1141 raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
1142 end
6bd672e @dhh Added that Base#find takes an optional options hash, including :condi…
dhh authored
1143 end
554597d @dhh Added named bind-style variable interpolation #281 [Michael Koziarski]
dhh authored
1144
6bd672e @dhh Added that Base#find takes an optional options hash, including :condi…
dhh authored
1145 def extract_options_from_args!(args)
e8b427c Raise an exception when invalid options are passed to ActiveRecord::B…
Marcel Molina authored
1146 options = args.last.is_a?(Hash) ? args.pop : {}
1147 validate_find_options(options)
1148 options
1149 end
64fcb75 @jeremy r3618@sedna: jeremy | 2005-10-14 12:06:03 -0700
jeremy authored
1150
e8b427c Raise an exception when invalid options are passed to ActiveRecord::B…
Marcel Molina authored
1151 def validate_find_options(options)
64fcb75 @jeremy r3618@sedna: jeremy | 2005-10-14 12:06:03 -0700
jeremy authored
1152 options.assert_valid_keys [:conditions, :include, :joins, :limit, :offset, :order, :select, :readonly]
db045db @dhh Initial
dhh authored
1153 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
1154
aaf9a45 @dhh Added Base.validate_uniqueness thatv alidates whether the value of th…
dhh authored
1155 def encode_quoted_value(value)
1156 quoted_value = connection.quote(value)
3ab3a70 @jeremy Clarify semantics of ActiveRecord::Base#respond_to? Closes #2560.
jeremy authored
1157 quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "
1158 quoted_value
aaf9a45 @dhh Added Base.validate_uniqueness thatv alidates whether the value of th…
dhh authored
1159 end
db045db @dhh Initial
dhh authored
1160 end
1161
1162 public
1163 # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
1164 # attributes but not yet saved (pass a hash with key names matching the associated table column names).
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
1165 # In both instances, valid attribute keys are determined by the column names of the associated table --
db045db @dhh Initial
dhh authored
1166 # hence you can't have attributes that aren't part of the table columns.
1167 def initialize(attributes = nil)
1168 @attributes = attributes_from_column_definition
1169 @new_record = true
1170 ensure_proper_type
1171 self.attributes = attributes unless attributes.nil?
1172 yield self if block_given?
1173 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
1174
aabf909 @jeremy Correct reader method generation for primary key attribute: handle ca…
jeremy authored
1175 # A model instance's primary key is always available as model.id
1176 # whether you name it the default 'id' or set it to something else.
db045db @dhh Initial
dhh authored
1177 def id
f218771 Add option (true by default) to generate reader methods for each attr…
Marcel Molina authored
1178 attr_name = self.class.primary_key
aabf909 @jeremy Correct reader method generation for primary key attribute: handle ca…
jeremy authored
1179 column = column_for_attribute(attr_name)
f218771 Add option (true by default) to generate reader methods for each attr…
Marcel Molina authored
1180 define_read_method(:id, attr_name, column) if self.class.generate_read_methods
aabf909 @jeremy Correct reader method generation for primary key attribute: handle ca…
jeremy authored
1181 read_attribute(attr_name)
db045db @dhh Initial
dhh authored
1182 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
1183
ce23862 @dhh Added alias_method :to_param, :id to Base, such that Active Record ob…
dhh authored
1184 # Enables Active Record objects to be used as URL parameters in Action Pack automatically.
1185 alias_method :to_param, :id
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
1186
4eab375 @dhh Finished polishing API docs
dhh authored
1187 def id_before_type_cast #:nodoc:
3e5a880 @dhh Switched strategy on the id_before_type_cast problem and just did an …
dhh authored
1188 read_attribute_before_type_cast(self.class.primary_key)
1189 end
1190
4eab375 @dhh Finished polishing API docs
dhh authored
1191 def quoted_id #:nodoc:
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
1192 quote(id, column_for_attribute(self.class.primary_key))
4940383 @dhh Fixed value quoting in all generated SQL statements, so that integers…
dhh authored
1193 end
81737fc @jeremy r1613@asus: jeremy | 2005-07-03 07:04:53 -0700
jeremy authored
1194
db045db @dhh Initial
dhh authored
1195 # Sets the primary ID.
1196 def id=(value)
1197 write_attribute(self.class.primary_key, value)
1198 end
6e39c9e @jeremy r1614@asus: jeremy | 2005-07-03 08:01:08 -0700
jeremy authored
1199
db045db @dhh