Skip to content

HTTPS clone URL

Subversion checkout URL

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