Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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