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