activerecord 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (93) hide show
  1. data/CHANGELOG +6023 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +162 -0
  5. data/examples/simple.rb +14 -0
  6. data/lib/active_record.rb +124 -0
  7. data/lib/active_record/aggregations.rb +277 -0
  8. data/lib/active_record/association_preload.rb +403 -0
  9. data/lib/active_record/associations.rb +2254 -0
  10. data/lib/active_record/associations/association_collection.rb +562 -0
  11. data/lib/active_record/associations/association_proxy.rb +295 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +137 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +116 -0
  17. data/lib/active_record/associations/has_one_association.rb +143 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  19. data/lib/active_record/associations/through_association_scope.rb +154 -0
  20. data/lib/active_record/attribute_methods.rb +60 -0
  21. data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +50 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +116 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
  27. data/lib/active_record/attribute_methods/write.rb +37 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1867 -0
  30. data/lib/active_record/callbacks.rb +288 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -0
  32. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  33. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +329 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -0
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +543 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +212 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +643 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1030 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -0
  43. data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
  44. data/lib/active_record/counter_cache.rb +115 -0
  45. data/lib/active_record/dynamic_finder_match.rb +53 -0
  46. data/lib/active_record/dynamic_scope_match.rb +32 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1008 -0
  49. data/lib/active_record/locale/en.yml +40 -0
  50. data/lib/active_record/locking/optimistic.rb +172 -0
  51. data/lib/active_record/locking/pessimistic.rb +55 -0
  52. data/lib/active_record/log_subscriber.rb +48 -0
  53. data/lib/active_record/migration.rb +617 -0
  54. data/lib/active_record/named_scope.rb +138 -0
  55. data/lib/active_record/nested_attributes.rb +417 -0
  56. data/lib/active_record/observer.rb +140 -0
  57. data/lib/active_record/persistence.rb +291 -0
  58. data/lib/active_record/query_cache.rb +36 -0
  59. data/lib/active_record/railtie.rb +91 -0
  60. data/lib/active_record/railties/controller_runtime.rb +38 -0
  61. data/lib/active_record/railties/databases.rake +512 -0
  62. data/lib/active_record/reflection.rb +403 -0
  63. data/lib/active_record/relation.rb +393 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +286 -0
  66. data/lib/active_record/relation/finder_methods.rb +355 -0
  67. data/lib/active_record/relation/predicate_builder.rb +41 -0
  68. data/lib/active_record/relation/query_methods.rb +261 -0
  69. data/lib/active_record/relation/spawn_methods.rb +112 -0
  70. data/lib/active_record/schema.rb +59 -0
  71. data/lib/active_record/schema_dumper.rb +195 -0
  72. data/lib/active_record/serialization.rb +60 -0
  73. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  74. data/lib/active_record/session_store.rb +340 -0
  75. data/lib/active_record/test_case.rb +67 -0
  76. data/lib/active_record/timestamp.rb +88 -0
  77. data/lib/active_record/transactions.rb +356 -0
  78. data/lib/active_record/validations.rb +84 -0
  79. data/lib/active_record/validations/associated.rb +48 -0
  80. data/lib/active_record/validations/uniqueness.rb +185 -0
  81. data/lib/active_record/version.rb +9 -0
  82. data/lib/rails/generators/active_record.rb +27 -0
  83. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  84. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  85. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  86. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  87. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  88. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  89. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  90. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  91. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  92. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  93. metadata +224 -0
@@ -0,0 +1,1867 @@
1
+ require 'yaml'
2
+ require 'set'
3
+ require 'active_support/benchmarkable'
4
+ require 'active_support/dependencies'
5
+ require 'active_support/descendants_tracker'
6
+ require 'active_support/time'
7
+ require 'active_support/core_ext/class/attribute'
8
+ require 'active_support/core_ext/class/attribute_accessors'
9
+ require 'active_support/core_ext/class/delegating_attributes'
10
+ require 'active_support/core_ext/class/inheritable_attributes'
11
+ require 'active_support/core_ext/array/extract_options'
12
+ require 'active_support/core_ext/hash/deep_merge'
13
+ require 'active_support/core_ext/hash/indifferent_access'
14
+ require 'active_support/core_ext/hash/slice'
15
+ require 'active_support/core_ext/string/behavior'
16
+ require 'active_support/core_ext/kernel/singleton_class'
17
+ require 'active_support/core_ext/module/delegation'
18
+ require 'active_support/core_ext/module/deprecation'
19
+ require 'active_support/core_ext/module/introspection'
20
+ require 'active_support/core_ext/object/duplicable'
21
+ require 'active_support/core_ext/object/blank'
22
+ require 'arel'
23
+ require 'active_record/errors'
24
+ require 'active_record/log_subscriber'
25
+
26
+ module ActiveRecord #:nodoc:
27
+ # = Active Record
28
+ #
29
+ # Active Record objects don't specify their attributes directly, but rather infer them from
30
+ # the table definition with which they're linked. Adding, removing, and changing attributes
31
+ # and their type is done directly in the database. Any change is instantly reflected in the
32
+ # Active Record objects. The mapping that binds a given Active Record class to a certain
33
+ # database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.
34
+ #
35
+ # See the mapping rules in table_name and the full example in link:files/activerecord/README_rdoc.html for more insight.
36
+ #
37
+ # == Creation
38
+ #
39
+ # Active Records accept constructor parameters either in a hash or as a block. The hash
40
+ # method is especially useful when you're receiving the data from somewhere else, like an
41
+ # HTTP request. It works like this:
42
+ #
43
+ # user = User.new(:name => "David", :occupation => "Code Artist")
44
+ # user.name # => "David"
45
+ #
46
+ # You can also use block initialization:
47
+ #
48
+ # user = User.new do |u|
49
+ # u.name = "David"
50
+ # u.occupation = "Code Artist"
51
+ # end
52
+ #
53
+ # And of course you can just create a bare object and specify the attributes after the fact:
54
+ #
55
+ # user = User.new
56
+ # user.name = "David"
57
+ # user.occupation = "Code Artist"
58
+ #
59
+ # == Conditions
60
+ #
61
+ # Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement.
62
+ # The array form is to be used when the condition input is tainted and requires sanitization. The string form can
63
+ # be used for statements that don't involve tainted data. The hash form works much like the array form, except
64
+ # only equality and range is possible. Examples:
65
+ #
66
+ # class User < ActiveRecord::Base
67
+ # def self.authenticate_unsafely(user_name, password)
68
+ # where("user_name = '#{user_name}' AND password = '#{password}'").first
69
+ # end
70
+ #
71
+ # def self.authenticate_safely(user_name, password)
72
+ # where("user_name = ? AND password = ?", user_name, password).first
73
+ # end
74
+ #
75
+ # def self.authenticate_safely_simply(user_name, password)
76
+ # where(:user_name => user_name, :password => password).first
77
+ # end
78
+ # end
79
+ #
80
+ # The <tt>authenticate_unsafely</tt> method inserts the parameters directly into the query
81
+ # and is thus susceptible to SQL-injection attacks if the <tt>user_name</tt> and +password+
82
+ # parameters come directly from an HTTP request. The <tt>authenticate_safely</tt> and
83
+ # <tt>authenticate_safely_simply</tt> both will sanitize the <tt>user_name</tt> and +password+
84
+ # before inserting them in the query, which will ensure that an attacker can't escape the
85
+ # query and fake the login (or worse).
86
+ #
87
+ # When using multiple parameters in the conditions, it can easily become hard to read exactly
88
+ # what the fourth or fifth question mark is supposed to represent. In those cases, you can
89
+ # resort to named bind variables instead. That's done by replacing the question marks with
90
+ # symbols and supplying a hash with values for the matching symbol keys:
91
+ #
92
+ # Company.where(
93
+ # "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
94
+ # { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
95
+ # ).first
96
+ #
97
+ # Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
98
+ # operator. For instance:
99
+ #
100
+ # Student.where(:first_name => "Harvey", :status => 1)
101
+ # Student.where(params[:student])
102
+ #
103
+ # A range may be used in the hash to use the SQL BETWEEN operator:
104
+ #
105
+ # Student.where(:grade => 9..12)
106
+ #
107
+ # An array may be used in the hash to use the SQL IN operator:
108
+ #
109
+ # Student.where(:grade => [9,11,12])
110
+ #
111
+ # When joining tables, nested hashes or keys written in the form 'table_name.column_name'
112
+ # can be used to qualify the table name of a particular condition. For instance:
113
+ #
114
+ # Student.joins(:schools).where(:schools => { :type => 'public' })
115
+ # Student.joins(:schools).where('schools.type' => 'public' )
116
+ #
117
+ # == Overwriting default accessors
118
+ #
119
+ # All column values are automatically available through basic accessors on the Active Record
120
+ # object, but sometimes you want to specialize this behavior. This can be done by overwriting
121
+ # the default accessors (using the same name as the attribute) and calling
122
+ # <tt>read_attribute(attr_name)</tt> and <tt>write_attribute(attr_name, value)</tt> to actually
123
+ # change things.
124
+ #
125
+ # class Song < ActiveRecord::Base
126
+ # # Uses an integer of seconds to hold the length of the song
127
+ #
128
+ # def length=(minutes)
129
+ # write_attribute(:length, minutes.to_i * 60)
130
+ # end
131
+ #
132
+ # def length
133
+ # read_attribute(:length) / 60
134
+ # end
135
+ # end
136
+ #
137
+ # You can alternatively use <tt>self[:attribute]=(value)</tt> and <tt>self[:attribute]</tt>
138
+ # instead of <tt>write_attribute(:attribute, value)</tt> and <tt>read_attribute(:attribute)</tt>.
139
+ #
140
+ # == Attribute query methods
141
+ #
142
+ # In addition to the basic accessors, query methods are also automatically available on the Active Record object.
143
+ # Query methods allow you to test whether an attribute value is present.
144
+ #
145
+ # For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
146
+ # to determine whether the user has a name:
147
+ #
148
+ # user = User.new(:name => "David")
149
+ # user.name? # => true
150
+ #
151
+ # anonymous = User.new(:name => "")
152
+ # anonymous.name? # => false
153
+ #
154
+ # == Accessing attributes before they have been typecasted
155
+ #
156
+ # Sometimes you want to be able to read the raw attribute data without having the column-determined
157
+ # typecast run its course first. That can be done by using the <tt><attribute>_before_type_cast</tt>
158
+ # accessors that all attributes have. For example, if your Account model has a <tt>balance</tt> attribute,
159
+ # you can call <tt>account.balance_before_type_cast</tt> or <tt>account.id_before_type_cast</tt>.
160
+ #
161
+ # This is especially useful in validation situations where the user might supply a string for an
162
+ # integer field and you want to display the original string back in an error message. Accessing the
163
+ # attribute normally would typecast the string to 0, which isn't what you want.
164
+ #
165
+ # == Dynamic attribute-based finders
166
+ #
167
+ # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects
168
+ # by simple queries without turning to SQL. They work by appending the name of an attribute
169
+ # to <tt>find_by_</tt>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt> and thus produces finders
170
+ # like <tt>Person.find_by_user_name</tt>, <tt>Person.find_all_by_last_name</tt>, and
171
+ # <tt>Payment.find_by_transaction_id</tt>. Instead of writing
172
+ # <tt>Person.where(:user_name => user_name).first</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
173
+ # And instead of writing <tt>Person.where(:last_name => last_name).all</tt>, you just do
174
+ # <tt>Person.find_all_by_last_name(last_name)</tt>.
175
+ #
176
+ # It's also possible to use multiple attributes in the same find by separating them with "_and_".
177
+ #
178
+ # Person.where(:user_name => user_name, :password => password).first
179
+ # Person.find_by_user_name_and_password #with dynamic finder
180
+ #
181
+ # Person.where(:user_name => user_name, :password => password, :gender => 'male').first
182
+ # Payment.find_by_user_name_and_password_and_gender
183
+ #
184
+ # It's even possible to call these dynamic finder methods on relations and named scopes.
185
+ #
186
+ # Payment.order("created_on").find_all_by_amount(50)
187
+ # Payment.pending.find_last_by_amount(100)
188
+ #
189
+ # The same dynamic finder style can be used to create the object if it doesn't already exist.
190
+ # This dynamic finder is called with <tt>find_or_create_by_</tt> and will return the object if
191
+ # it already exists and otherwise creates it, then returns it. Protected attributes won't be set
192
+ # unless they are given in a block.
193
+ #
194
+ # # No 'Summer' tag exists
195
+ # Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
196
+ #
197
+ # # Now the 'Summer' tag does exist
198
+ # Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
199
+ #
200
+ # # Now 'Bob' exist and is an 'admin'
201
+ # User.find_or_create_by_name('Bob', :age => 40) { |u| u.admin = true }
202
+ #
203
+ # Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without
204
+ # saving it first. Protected attributes won't be set unless they are given in a block.
205
+ #
206
+ # # No 'Winter' tag exists
207
+ # winter = Tag.find_or_initialize_by_name("Winter")
208
+ # winter.new_record? # true
209
+ #
210
+ # To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of
211
+ # a list of parameters.
212
+ #
213
+ # Tag.find_or_create_by_name(:name => "rails", :creator => current_user)
214
+ #
215
+ # That will either find an existing tag named "rails", or create a new one while setting the
216
+ # user that created it.
217
+ #
218
+ # Just like <tt>find_by_*</tt>, you can also use <tt>scoped_by_*</tt> to retrieve data. The good thing about
219
+ # using this feature is that the very first time result is returned using <tt>method_missing</tt> technique
220
+ # but after that the method is declared on the class. Henceforth <tt>method_missing</tt> will not be hit.
221
+ #
222
+ # User.scoped_by_user_name('David')
223
+ #
224
+ # == Saving arrays, hashes, and other non-mappable objects in text columns
225
+ #
226
+ # Active Record can serialize any object in text columns using YAML. To do so, you must
227
+ # specify this with a call to the class method +serialize+.
228
+ # This makes it possible to store arrays, hashes, and other non-mappable objects without doing
229
+ # any additional work.
230
+ #
231
+ # class User < ActiveRecord::Base
232
+ # serialize :preferences
233
+ # end
234
+ #
235
+ # user = User.create(:preferences => { "background" => "black", "display" => large })
236
+ # User.find(user.id).preferences # => { "background" => "black", "display" => large }
237
+ #
238
+ # You can also specify a class option as the second parameter that'll raise an exception
239
+ # if a serialized object is retrieved as a descendant of a class not in the hierarchy.
240
+ #
241
+ # class User < ActiveRecord::Base
242
+ # serialize :preferences, Hash
243
+ # end
244
+ #
245
+ # user = User.create(:preferences => %w( one two three ))
246
+ # User.find(user.id).preferences # raises SerializationTypeMismatch
247
+ #
248
+ # == Single table inheritance
249
+ #
250
+ # Active Record allows inheritance by storing the name of the class in a column that by
251
+ # default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
252
+ # This means that an inheritance looking like this:
253
+ #
254
+ # class Company < ActiveRecord::Base; end
255
+ # class Firm < Company; end
256
+ # class Client < Company; end
257
+ # class PriorityClient < Client; end
258
+ #
259
+ # When you do <tt>Firm.create(:name => "37signals")</tt>, this record will be saved in
260
+ # the companies table with type = "Firm". You can then fetch this row again using
261
+ # <tt>Company.where(:name => '37signals').first</tt> and it will return a Firm object.
262
+ #
263
+ # If you don't have a type column defined in your table, single-table inheritance won't
264
+ # be triggered. In that case, it'll work just like normal subclasses with no special magic
265
+ # for differentiating between them or reloading the right type with find.
266
+ #
267
+ # Note, all the attributes for all the cases are kept in the same table. Read more:
268
+ # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
269
+ #
270
+ # == Connection to multiple databases in different models
271
+ #
272
+ # Connections are usually created through ActiveRecord::Base.establish_connection and retrieved
273
+ # by ActiveRecord::Base.connection. All classes inheriting from ActiveRecord::Base will use this
274
+ # connection. But you can also set a class-specific connection. For example, if Course is an
275
+ # ActiveRecord::Base, but resides in a different database, you can just say <tt>Course.establish_connection</tt>
276
+ # and Course and all of its subclasses will use this connection instead.
277
+ #
278
+ # This feature is implemented by keeping a connection pool in ActiveRecord::Base that is
279
+ # a Hash indexed by the class. If a connection is requested, the retrieve_connection method
280
+ # will go up the class-hierarchy until a connection is found in the connection pool.
281
+ #
282
+ # == Exceptions
283
+ #
284
+ # * ActiveRecordError - Generic error class and superclass of all other errors raised by Active Record.
285
+ # * AdapterNotSpecified - The configuration hash used in <tt>establish_connection</tt> didn't include an
286
+ # <tt>:adapter</tt> key.
287
+ # * AdapterNotFound - The <tt>:adapter</tt> key used in <tt>establish_connection</tt> specified a
288
+ # non-existent adapter
289
+ # (or a bad spelling of an existing one).
290
+ # * AssociationTypeMismatch - The object assigned to the association wasn't of the type
291
+ # specified in the association definition.
292
+ # * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
293
+ # * ConnectionNotEstablished+ - No connection has been established. Use <tt>establish_connection</tt>
294
+ # before querying.
295
+ # * RecordNotFound - No record responded to the +find+ method. Either the row with the given ID doesn't exist
296
+ # or the row didn't meet the additional restrictions. Some +find+ calls do not raise this exception to signal
297
+ # nothing was found, please check its documentation for further details.
298
+ # * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message.
299
+ # * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
300
+ # <tt>attributes=</tt> method. The +errors+ property of this exception contains an array of
301
+ # AttributeAssignmentError
302
+ # objects that should be inspected to determine which attributes triggered the errors.
303
+ # * AttributeAssignmentError - An error occurred while doing a mass assignment through the
304
+ # <tt>attributes=</tt> method.
305
+ # You can inspect the +attribute+ property of the exception object to determine which attribute
306
+ # triggered the error.
307
+ #
308
+ # *Note*: The attributes listed are class-level attributes (accessible from both the class and instance level).
309
+ # So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
310
+ # instances in the current object space.
311
+ class Base
312
+ ##
313
+ # :singleton-method:
314
+ # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class,
315
+ # which is then passed on to any new database connections made and which can be retrieved on both
316
+ # a class and instance level by calling +logger+.
317
+ cattr_accessor :logger, :instance_writer => false
318
+
319
+ class << self
320
+ def reset_subclasses #:nodoc:
321
+ ActiveSupport::Deprecation.warn 'ActiveRecord::Base.reset_subclasses no longer does anything in Rails 3. It will be removed in the final release; please update your apps and plugins.', caller
322
+ end
323
+
324
+ def subclasses
325
+ descendants
326
+ end
327
+
328
+ deprecate :subclasses => :descendants
329
+ end
330
+
331
+ ##
332
+ # :singleton-method:
333
+ # Contains the database configuration - as is typically stored in config/database.yml -
334
+ # as a Hash.
335
+ #
336
+ # For example, the following database.yml...
337
+ #
338
+ # development:
339
+ # adapter: sqlite3
340
+ # database: db/development.sqlite3
341
+ #
342
+ # production:
343
+ # adapter: sqlite3
344
+ # database: db/production.sqlite3
345
+ #
346
+ # ...would result in ActiveRecord::Base.configurations to look like this:
347
+ #
348
+ # {
349
+ # 'development' => {
350
+ # 'adapter' => 'sqlite3',
351
+ # 'database' => 'db/development.sqlite3'
352
+ # },
353
+ # 'production' => {
354
+ # 'adapter' => 'sqlite3',
355
+ # 'database' => 'db/production.sqlite3'
356
+ # }
357
+ # }
358
+ cattr_accessor :configurations, :instance_writer => false
359
+ @@configurations = {}
360
+
361
+ ##
362
+ # :singleton-method:
363
+ # Accessor for the prefix type that will be prepended to every primary key column name.
364
+ # The options are :table_name and :table_name_with_underscore. If the first is specified,
365
+ # the Product class will look for "productid" instead of "id" as the primary column. If the
366
+ # latter is specified, the Product class will look for "product_id" instead of "id". Remember
367
+ # that this is a global setting for all Active Records.
368
+ cattr_accessor :primary_key_prefix_type, :instance_writer => false
369
+ @@primary_key_prefix_type = nil
370
+
371
+ ##
372
+ # :singleton-method:
373
+ # Accessor for the name of the prefix string to prepend to every table name. So if set
374
+ # to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
375
+ # etc. This is a convenient way of creating a namespace for tables in a shared database.
376
+ # By default, the prefix is the empty string.
377
+ #
378
+ # If you are organising your models within modules you can add a prefix to the models within
379
+ # a namespace by defining a singleton method in the parent module called table_name_prefix which
380
+ # returns your chosen prefix.
381
+ class_attribute :table_name_prefix, :instance_writer => false
382
+ self.table_name_prefix = ""
383
+
384
+ ##
385
+ # :singleton-method:
386
+ # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
387
+ # "people_basecamp"). By default, the suffix is the empty string.
388
+ class_attribute :table_name_suffix, :instance_writer => false
389
+ self.table_name_suffix = ""
390
+
391
+ ##
392
+ # :singleton-method:
393
+ # Indicates whether table names should be the pluralized versions of the corresponding class names.
394
+ # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
395
+ # See table_name for the full rules on table/class naming. This is true, by default.
396
+ cattr_accessor :pluralize_table_names, :instance_writer => false
397
+ @@pluralize_table_names = true
398
+
399
+ ##
400
+ # :singleton-method:
401
+ # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling
402
+ # dates and times from the database. This is set to :local by default.
403
+ cattr_accessor :default_timezone, :instance_writer => false
404
+ @@default_timezone = :local
405
+
406
+ ##
407
+ # :singleton-method:
408
+ # Specifies the format to use when dumping the database schema with Rails'
409
+ # Rakefile. If :sql, the schema is dumped as (potentially database-
410
+ # specific) SQL statements. If :ruby, the schema is dumped as an
411
+ # ActiveRecord::Schema file which can be loaded into any database that
412
+ # supports migrations. Use :ruby if you want to have different database
413
+ # adapters for, e.g., your development and test environments.
414
+ cattr_accessor :schema_format , :instance_writer => false
415
+ @@schema_format = :ruby
416
+
417
+ ##
418
+ # :singleton-method:
419
+ # Specify whether or not to use timestamps for migration versions
420
+ cattr_accessor :timestamped_migrations , :instance_writer => false
421
+ @@timestamped_migrations = true
422
+
423
+ # Determine whether to store the full constant name including namespace when using STI
424
+ superclass_delegating_accessor :store_full_sti_class
425
+ self.store_full_sti_class = true
426
+
427
+ # Stores the default scope for the class
428
+ class_inheritable_accessor :default_scoping, :instance_writer => false
429
+ self.default_scoping = []
430
+
431
+ class << self # Class methods
432
+ def colorize_logging(*args)
433
+ ActiveSupport::Deprecation.warn "ActiveRecord::Base.colorize_logging and " <<
434
+ "config.active_record.colorize_logging are deprecated. Please use " <<
435
+ "Rails::LogSubscriber.colorize_logging or config.colorize_logging instead", caller
436
+ end
437
+ alias :colorize_logging= :colorize_logging
438
+
439
+ delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
440
+ delegate :find_each, :find_in_batches, :to => :scoped
441
+ delegate :select, :group, :order, :reorder, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
442
+ delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
443
+
444
+ # Executes a custom SQL query against your database and returns all the results. The results will
445
+ # be returned as an array with columns requested encapsulated as attributes of the model you call
446
+ # this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
447
+ # a Product object with the attributes you specified in the SQL query.
448
+ #
449
+ # If you call a complicated SQL query which spans multiple tables the columns specified by the
450
+ # SELECT will be attributes of the model, whether or not they are columns of the corresponding
451
+ # table.
452
+ #
453
+ # The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be
454
+ # no database agnostic conversions performed. This should be a last resort because using, for example,
455
+ # MySQL specific terms will lock you to using that particular database engine or require you to
456
+ # change your call if you switch engines.
457
+ #
458
+ # ==== Examples
459
+ # # A simple SQL query spanning multiple tables
460
+ # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
461
+ # > [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
462
+ #
463
+ # # You can use the same string replacement techniques as you can with ActiveRecord#find
464
+ # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
465
+ # > [#<Post:0x36bff9c @attributes={"first_name"=>"The Cheap Man Buys Twice"}>, ...]
466
+ def find_by_sql(sql)
467
+ connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
468
+ end
469
+
470
+ # Creates an object (or multiple objects) and saves it to the database, if validations pass.
471
+ # The resulting object is returned whether the object was saved successfully to the database or not.
472
+ #
473
+ # The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the
474
+ # attributes on the objects that are to be created.
475
+ #
476
+ # ==== Examples
477
+ # # Create a single new object
478
+ # User.create(:first_name => 'Jamie')
479
+ #
480
+ # # Create an Array of new objects
481
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
482
+ #
483
+ # # Create a single object and pass it into a block to set other attributes.
484
+ # User.create(:first_name => 'Jamie') do |u|
485
+ # u.is_admin = false
486
+ # end
487
+ #
488
+ # # Creating an Array of new objects using a block, where the block is executed for each object:
489
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
490
+ # u.is_admin = false
491
+ # end
492
+ def create(attributes = nil, &block)
493
+ if attributes.is_a?(Array)
494
+ attributes.collect { |attr| create(attr, &block) }
495
+ else
496
+ object = new(attributes)
497
+ yield(object) if block_given?
498
+ object.save
499
+ object
500
+ end
501
+ end
502
+
503
+ # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
504
+ # The use of this method should be restricted to complicated SQL queries that can't be executed
505
+ # using the ActiveRecord::Calculations class methods. Look into those before using this.
506
+ #
507
+ # ==== Parameters
508
+ #
509
+ # * +sql+ - An SQL statement which should return a count query from the database, see the example below.
510
+ #
511
+ # ==== Examples
512
+ #
513
+ # Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
514
+ def count_by_sql(sql)
515
+ sql = sanitize_conditions(sql)
516
+ connection.select_value(sql, "#{name} Count").to_i
517
+ end
518
+
519
+ # Attributes listed as readonly will be used to create a new record but update operations will
520
+ # ignore these fields.
521
+ def attr_readonly(*attributes)
522
+ write_inheritable_attribute(:attr_readonly, Set.new(attributes.map { |a| a.to_s }) + (readonly_attributes || []))
523
+ end
524
+
525
+ # Returns an array of all the attributes that have been specified as readonly.
526
+ def readonly_attributes
527
+ read_inheritable_attribute(:attr_readonly) || []
528
+ end
529
+
530
+ # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
531
+ # then specify the name of that attribute using this method and it will be handled automatically.
532
+ # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
533
+ # class on retrieval or SerializationTypeMismatch will be raised.
534
+ #
535
+ # ==== Parameters
536
+ #
537
+ # * +attr_name+ - The field name that should be serialized.
538
+ # * +class_name+ - Optional, class name that the object type should be equal to.
539
+ #
540
+ # ==== Example
541
+ # # Serialize a preferences attribute
542
+ # class User
543
+ # serialize :preferences
544
+ # end
545
+ def serialize(attr_name, class_name = Object)
546
+ serialized_attributes[attr_name.to_s] = class_name
547
+ end
548
+
549
+ # Returns a hash of all the attributes that have been specified for serialization as
550
+ # keys and their class restriction as values.
551
+ def serialized_attributes
552
+ read_inheritable_attribute(:attr_serialized) or write_inheritable_attribute(:attr_serialized, {})
553
+ end
554
+
555
+ # Guesses the table name (in forced lower-case) based on the name of the class in the
556
+ # inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy
557
+ # looks like: Reply < Message < ActiveRecord::Base, then Message is used
558
+ # to guess the table name even when called on Reply. The rules used to do the guess
559
+ # are handled by the Inflector class in Active Support, which knows almost all common
560
+ # English inflections. You can add new inflections in config/initializers/inflections.rb.
561
+ #
562
+ # Nested classes are given table names prefixed by the singular form of
563
+ # the parent's table name. Enclosing modules are not considered.
564
+ #
565
+ # ==== Examples
566
+ #
567
+ # class Invoice < ActiveRecord::Base; end;
568
+ # file class table_name
569
+ # invoice.rb Invoice invoices
570
+ #
571
+ # class Invoice < ActiveRecord::Base; class Lineitem < ActiveRecord::Base; end; end;
572
+ # file class table_name
573
+ # invoice.rb Invoice::Lineitem invoice_lineitems
574
+ #
575
+ # module Invoice; class Lineitem < ActiveRecord::Base; end; end;
576
+ # file class table_name
577
+ # invoice/lineitem.rb Invoice::Lineitem lineitems
578
+ #
579
+ # Additionally, the class-level +table_name_prefix+ is prepended and the
580
+ # +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
581
+ # the table name guess for an Invoice class becomes "myapp_invoices".
582
+ # Invoice::Lineitem becomes "myapp_invoice_lineitems".
583
+ #
584
+ # You can also overwrite this class method to allow for unguessable
585
+ # links, such as a Mouse class with a link to a "mice" table. Example:
586
+ #
587
+ # class Mouse < ActiveRecord::Base
588
+ # set_table_name "mice"
589
+ # end
590
+ def table_name
591
+ reset_table_name
592
+ end
593
+
594
+ # Returns a quoted version of the table name, used to construct SQL statements.
595
+ def quoted_table_name
596
+ @quoted_table_name ||= connection.quote_table_name(table_name)
597
+ end
598
+
599
+ # Computes the table name, (re)sets it internally, and returns it.
600
+ def reset_table_name #:nodoc:
601
+ self.table_name = compute_table_name
602
+ end
603
+
604
+ def full_table_name_prefix #:nodoc:
605
+ (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
606
+ end
607
+
608
+ # Defines the column name for use with single table inheritance. Use
609
+ # <tt>set_inheritance_column</tt> to set a different value.
610
+ def inheritance_column
611
+ @inheritance_column ||= "type".freeze
612
+ end
613
+
614
+ # Lazy-set the sequence name to the connection's default. This method
615
+ # is only ever called once since set_sequence_name overrides it.
616
+ def sequence_name #:nodoc:
617
+ reset_sequence_name
618
+ end
619
+
620
+ def reset_sequence_name #:nodoc:
621
+ default = connection.default_sequence_name(table_name, primary_key)
622
+ set_sequence_name(default)
623
+ default
624
+ end
625
+
626
+ # Sets the table name. If the value is nil or false then the value returned by the given
627
+ # block is used.
628
+ #
629
+ # class Project < ActiveRecord::Base
630
+ # set_table_name "project"
631
+ # end
632
+ def set_table_name(value = nil, &block)
633
+ @quoted_table_name = nil
634
+ define_attr_method :table_name, value, &block
635
+ end
636
+ alias :table_name= :set_table_name
637
+
638
+ # Sets the name of the inheritance column to use to the given value,
639
+ # or (if the value # is nil or false) to the value returned by the
640
+ # given block.
641
+ #
642
+ # class Project < ActiveRecord::Base
643
+ # set_inheritance_column do
644
+ # original_inheritance_column + "_id"
645
+ # end
646
+ # end
647
+ def set_inheritance_column(value = nil, &block)
648
+ define_attr_method :inheritance_column, value, &block
649
+ end
650
+ alias :inheritance_column= :set_inheritance_column
651
+
652
+ # Sets the name of the sequence to use when generating ids to the given
653
+ # value, or (if the value is nil or false) to the value returned by the
654
+ # given block. This is required for Oracle and is useful for any
655
+ # database which relies on sequences for primary key generation.
656
+ #
657
+ # If a sequence name is not explicitly set when using Oracle or Firebird,
658
+ # it will default to the commonly used pattern of: #{table_name}_seq
659
+ #
660
+ # If a sequence name is not explicitly set when using PostgreSQL, it
661
+ # will discover the sequence corresponding to your primary key for you.
662
+ #
663
+ # class Project < ActiveRecord::Base
664
+ # set_sequence_name "projectseq" # default would have been "project_seq"
665
+ # end
666
+ def set_sequence_name(value = nil, &block)
667
+ define_attr_method :sequence_name, value, &block
668
+ end
669
+ alias :sequence_name= :set_sequence_name
670
+
671
+ # Indicates whether the table associated with this class exists
672
+ def table_exists?
673
+ connection.table_exists?(table_name)
674
+ end
675
+
676
+ # Returns an array of column objects for the table associated with this class.
677
+ def columns
678
+ unless defined?(@columns) && @columns
679
+ @columns = connection.columns(table_name, "#{name} Columns")
680
+ @columns.each { |column| column.primary = column.name == primary_key }
681
+ end
682
+ @columns
683
+ end
684
+
685
+ # Returns a hash of column objects for the table associated with this class.
686
+ def columns_hash
687
+ @columns_hash ||= columns.inject({}) { |hash, column| hash[column.name] = column; hash }
688
+ end
689
+
690
+ # Returns an array of column names as strings.
691
+ def column_names
692
+ @column_names ||= columns.map { |column| column.name }
693
+ end
694
+
695
+ # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
696
+ # and columns used for single table inheritance have been removed.
697
+ def content_columns
698
+ @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
699
+ end
700
+
701
+ # 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
702
+ # 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
703
+ # is available.
704
+ def column_methods_hash #:nodoc:
705
+ @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
706
+ attr_name = attr.to_s
707
+ methods[attr.to_sym] = attr_name
708
+ methods["#{attr}=".to_sym] = attr_name
709
+ methods["#{attr}?".to_sym] = attr_name
710
+ methods["#{attr}_before_type_cast".to_sym] = attr_name
711
+ methods
712
+ end
713
+ end
714
+
715
+ # Resets all the cached information about columns, which will cause them
716
+ # to be reloaded on the next request.
717
+ #
718
+ # The most common usage pattern for this method is probably in a migration,
719
+ # when just after creating a table you want to populate it with some default
720
+ # values, eg:
721
+ #
722
+ # class CreateJobLevels < ActiveRecord::Migration
723
+ # def self.up
724
+ # create_table :job_levels do |t|
725
+ # t.integer :id
726
+ # t.string :name
727
+ #
728
+ # t.timestamps
729
+ # end
730
+ #
731
+ # JobLevel.reset_column_information
732
+ # %w{assistant executive manager director}.each do |type|
733
+ # JobLevel.create(:name => type)
734
+ # end
735
+ # end
736
+ #
737
+ # def self.down
738
+ # drop_table :job_levels
739
+ # end
740
+ # end
741
+ def reset_column_information
742
+ undefine_attribute_methods
743
+ @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil
744
+ @arel_engine = @relation = @arel_table = nil
745
+ end
746
+
747
+ def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
748
+ descendants.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
749
+ end
750
+
751
+ def attribute_method?(attribute)
752
+ super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
753
+ end
754
+
755
+ # Set the lookup ancestors for ActiveModel.
756
+ def lookup_ancestors #:nodoc:
757
+ klass = self
758
+ classes = [klass]
759
+ while klass != klass.base_class
760
+ classes << klass = klass.superclass
761
+ end
762
+ classes
763
+ rescue
764
+ # OPTIMIZE this rescue is to fix this test: ./test/cases/reflection_test.rb:56:in `test_human_name_for_column'
765
+ # Apparently the method base_class causes some trouble.
766
+ # It now works for sure.
767
+ [self]
768
+ end
769
+
770
+ # Set the i18n scope to overwrite ActiveModel.
771
+ def i18n_scope #:nodoc:
772
+ :activerecord
773
+ end
774
+
775
+ # True if this isn't a concrete subclass needing a STI type condition.
776
+ def descends_from_active_record?
777
+ if superclass.abstract_class?
778
+ superclass.descends_from_active_record?
779
+ else
780
+ superclass == Base || !columns_hash.include?(inheritance_column)
781
+ end
782
+ end
783
+
784
+ def finder_needs_type_condition? #:nodoc:
785
+ # This is like this because benchmarking justifies the strange :false stuff
786
+ :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
787
+ end
788
+
789
+ # Returns a string like 'Post id:integer, title:string, body:text'
790
+ def inspect
791
+ if self == Base
792
+ super
793
+ elsif abstract_class?
794
+ "#{super}(abstract)"
795
+ elsif table_exists?
796
+ attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
797
+ "#{super}(#{attr_list})"
798
+ else
799
+ "#{super}(Table doesn't exist)"
800
+ end
801
+ end
802
+
803
+ def quote_value(value, column = nil) #:nodoc:
804
+ connection.quote(value,column)
805
+ end
806
+
807
+ # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
808
+ def sanitize(object) #:nodoc:
809
+ connection.quote(object)
810
+ end
811
+
812
+ # Overwrite the default class equality method to provide support for association proxies.
813
+ def ===(object)
814
+ object.is_a?(self)
815
+ end
816
+
817
+ # Returns the base AR subclass that this class descends from. If A
818
+ # extends AR::Base, A.base_class will return A. If B descends from A
819
+ # through some arbitrarily deep hierarchy, B.base_class will return A.
820
+ #
821
+ # If B < A and C < B and if A is an abstract_class then both B.base_class
822
+ # and C.base_class would return B as the answer since A is an abstract_class.
823
+ def base_class
824
+ class_of_active_record_descendant(self)
825
+ end
826
+
827
+ # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
828
+ attr_accessor :abstract_class
829
+
830
+ # Returns whether this class is an abstract class or not.
831
+ def abstract_class?
832
+ defined?(@abstract_class) && @abstract_class == true
833
+ end
834
+
835
+ def respond_to?(method_id, include_private = false)
836
+ if match = DynamicFinderMatch.match(method_id)
837
+ return true if all_attributes_exists?(match.attribute_names)
838
+ elsif match = DynamicScopeMatch.match(method_id)
839
+ return true if all_attributes_exists?(match.attribute_names)
840
+ end
841
+
842
+ super
843
+ end
844
+
845
+ def sti_name
846
+ store_full_sti_class ? name : name.demodulize
847
+ end
848
+
849
+ def arel_table
850
+ @arel_table ||= Arel::Table.new(table_name, arel_engine)
851
+ end
852
+
853
+ def arel_engine
854
+ @arel_engine ||= begin
855
+ if self == ActiveRecord::Base
856
+ Arel::Table.engine
857
+ else
858
+ connection_handler.connection_pools[name] ? Arel::Sql::Engine.new(self) : superclass.arel_engine
859
+ end
860
+ end
861
+ end
862
+
863
+ # Returns a scope for this class without taking into account the default_scope.
864
+ #
865
+ # class Post < ActiveRecord::Base
866
+ # default_scope :published => true
867
+ # end
868
+ #
869
+ # Post.all # Fires "SELECT * FROM posts WHERE published = true"
870
+ # Post.unscoped.all # Fires "SELECT * FROM posts"
871
+ #
872
+ # This method also accepts a block meaning that all queries inside the block will
873
+ # not use the default_scope:
874
+ #
875
+ # Post.unscoped {
876
+ # limit(10) # Fires "SELECT * FROM posts LIMIT 10"
877
+ # }
878
+ #
879
+ # It is recommended to use block form of unscoped because chaining unscoped with <tt>named_scope</tt>
880
+ # does not work. Assuming that <tt>published</tt> is a <tt>named_scope</tt> following two statements are same.
881
+ #
882
+ # Post.unscoped.published
883
+ # Post.published
884
+ def unscoped #:nodoc:
885
+ block_given? ? relation.scoping { yield } : relation
886
+ end
887
+
888
+ def scoped_methods #:nodoc:
889
+ key = :"#{self}_scoped_methods"
890
+ Thread.current[key] = Thread.current[key].presence || self.default_scoping.dup
891
+ end
892
+
893
+ private
894
+
895
+ def relation #:nodoc:
896
+ @relation ||= Relation.new(self, arel_table)
897
+ finder_needs_type_condition? ? @relation.where(type_condition) : @relation
898
+ end
899
+
900
+ # Finder methods must instantiate through this method to work with the
901
+ # single-table inheritance model that makes it possible to create
902
+ # objects of different types from the same table.
903
+ def instantiate(record)
904
+ object = find_sti_class(record[inheritance_column]).allocate
905
+
906
+ object.instance_variable_set(:@attributes, record)
907
+ object.instance_variable_set(:@attributes_cache, {})
908
+ object.instance_variable_set(:@new_record, false)
909
+ object.instance_variable_set(:@readonly, false)
910
+ object.instance_variable_set(:@destroyed, false)
911
+ object.instance_variable_set(:@marked_for_destruction, false)
912
+ object.instance_variable_set(:@previously_changed, {})
913
+ object.instance_variable_set(:@changed_attributes, {})
914
+
915
+ object.send(:_run_find_callbacks)
916
+ object.send(:_run_initialize_callbacks)
917
+
918
+ object
919
+ end
920
+
921
+ def find_sti_class(type_name)
922
+ if type_name.blank? || !columns_hash.include?(inheritance_column)
923
+ self
924
+ else
925
+ begin
926
+ if store_full_sti_class
927
+ ActiveSupport::Dependencies.constantize(type_name)
928
+ else
929
+ compute_type(type_name)
930
+ end
931
+ rescue NameError
932
+ raise SubclassNotFound,
933
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
934
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
935
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
936
+ "or overwrite #{name}.inheritance_column to use another column for that information."
937
+ end
938
+ end
939
+ end
940
+
941
+ def construct_finder_arel(options = {}, scope = nil)
942
+ relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : unscoped.merge(options)
943
+ relation = scope.merge(relation) if scope
944
+ relation
945
+ end
946
+
947
+ def type_condition
948
+ sti_column = arel_table[inheritance_column]
949
+ condition = sti_column.eq(sti_name)
950
+ descendants.each { |subclass| condition = condition.or(sti_column.eq(subclass.sti_name)) }
951
+
952
+ condition
953
+ end
954
+
955
+ # Guesses the table name, but does not decorate it with prefix and suffix information.
956
+ def undecorated_table_name(class_name = base_class.name)
957
+ table_name = class_name.to_s.demodulize.underscore
958
+ table_name = table_name.pluralize if pluralize_table_names
959
+ table_name
960
+ end
961
+
962
+ # Computes and returns a table name according to default conventions.
963
+ def compute_table_name
964
+ base = base_class
965
+ if self == base
966
+ # Nested classes are prefixed with singular parent table name.
967
+ if parent < ActiveRecord::Base && !parent.abstract_class?
968
+ contained = parent.table_name
969
+ contained = contained.singularize if parent.pluralize_table_names
970
+ contained << '_'
971
+ end
972
+ "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
973
+ else
974
+ # STI subclasses always use their superclass' table.
975
+ base.table_name
976
+ end
977
+ end
978
+
979
+ # Enables dynamic finders like <tt>User.find_by_user_name(user_name)</tt> and
980
+ # <tt>User.scoped_by_user_name(user_name). Refer to Dynamic attribute-based finders
981
+ # section at the top of this file for more detailed information.
982
+ #
983
+ # It's even possible to use all the additional parameters to +find+. For example, the
984
+ # full interface for +find_all_by_amount+ is actually <tt>find_all_by_amount(amount, options)</tt>.
985
+ #
986
+ # Each dynamic finder using <tt>scoped_by_*</tt> is also defined in the class after it
987
+ # is first invoked, so that future attempts to use it do not run through method_missing.
988
+ def method_missing(method_id, *arguments, &block)
989
+ if match = DynamicFinderMatch.match(method_id)
990
+ attribute_names = match.attribute_names
991
+ super unless all_attributes_exists?(attribute_names)
992
+ if match.finder?
993
+ options = arguments.extract_options!
994
+ relation = options.any? ? construct_finder_arel(options, current_scoped_methods) : scoped
995
+ relation.send :find_by_attributes, match, attribute_names, *arguments
996
+ elsif match.instantiator?
997
+ scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
998
+ end
999
+ elsif match = DynamicScopeMatch.match(method_id)
1000
+ attribute_names = match.attribute_names
1001
+ super unless all_attributes_exists?(attribute_names)
1002
+ if match.scope?
1003
+ self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
1004
+ def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
1005
+ options = args.extract_options! # options = args.extract_options!
1006
+ attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments(
1007
+ [:#{attribute_names.join(',:')}], args # [:user_name, :password], args
1008
+ ) # )
1009
+ #
1010
+ scoped(:conditions => attributes) # scoped(:conditions => attributes)
1011
+ end # end
1012
+ METHOD
1013
+ send(method_id, *arguments)
1014
+ end
1015
+ else
1016
+ super
1017
+ end
1018
+ end
1019
+
1020
+ def construct_attributes_from_arguments(attribute_names, arguments)
1021
+ attributes = {}
1022
+ attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }
1023
+ attributes
1024
+ end
1025
+
1026
+ # Similar in purpose to +expand_hash_conditions_for_aggregates+.
1027
+ def expand_attribute_names_for_aggregates(attribute_names)
1028
+ expanded_attribute_names = []
1029
+ attribute_names.each do |attribute_name|
1030
+ unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
1031
+ aggregate_mapping(aggregation).each do |field_attr, aggregate_attr|
1032
+ expanded_attribute_names << field_attr
1033
+ end
1034
+ else
1035
+ expanded_attribute_names << attribute_name
1036
+ end
1037
+ end
1038
+ expanded_attribute_names
1039
+ end
1040
+
1041
+ def all_attributes_exists?(attribute_names)
1042
+ attribute_names = expand_attribute_names_for_aggregates(attribute_names)
1043
+ attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) }
1044
+ end
1045
+
1046
+ protected
1047
+ # with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be
1048
+ # <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while
1049
+ # <tt>:create</tt> parameters are an attributes hash.
1050
+ #
1051
+ # class Article < ActiveRecord::Base
1052
+ # def self.create_with_scope
1053
+ # with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
1054
+ # find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
1055
+ # a = create(1)
1056
+ # a.blog_id # => 1
1057
+ # end
1058
+ # end
1059
+ # end
1060
+ #
1061
+ # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
1062
+ # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
1063
+ #
1064
+ # <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
1065
+ # problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
1066
+ # array of strings format for your joins.
1067
+ #
1068
+ # class Article < ActiveRecord::Base
1069
+ # def self.find_with_scope
1070
+ # with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
1071
+ # with_scope(:find => limit(10)) do
1072
+ # all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
1073
+ # end
1074
+ # with_scope(:find => where(:author_id => 3)) do
1075
+ # all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
1076
+ # end
1077
+ # end
1078
+ # end
1079
+ # end
1080
+ #
1081
+ # You can ignore any previous scopings by using the <tt>with_exclusive_scope</tt> method.
1082
+ #
1083
+ # class Article < ActiveRecord::Base
1084
+ # def self.find_with_exclusive_scope
1085
+ # with_scope(:find => where(:blog_id => 1).limit(1)) do
1086
+ # with_exclusive_scope(:find => limit(10)) do
1087
+ # all # => SELECT * from articles LIMIT 10
1088
+ # end
1089
+ # end
1090
+ # end
1091
+ # end
1092
+ #
1093
+ # *Note*: the +:find+ scope also has effect on update and deletion methods, like +update_all+ and +delete_all+.
1094
+ def with_scope(method_scoping = {}, action = :merge, &block)
1095
+ method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
1096
+
1097
+ if method_scoping.is_a?(Hash)
1098
+ # Dup first and second level of hash (method and params).
1099
+ method_scoping = method_scoping.inject({}) do |hash, (method, params)|
1100
+ hash[method] = (params == true) ? params : params.dup
1101
+ hash
1102
+ end
1103
+
1104
+ method_scoping.assert_valid_keys([ :find, :create ])
1105
+ relation = construct_finder_arel(method_scoping[:find] || {})
1106
+
1107
+ if current_scoped_methods && current_scoped_methods.create_with_value && method_scoping[:create]
1108
+ scope_for_create = if action == :merge
1109
+ current_scoped_methods.create_with_value.merge(method_scoping[:create])
1110
+ else
1111
+ method_scoping[:create]
1112
+ end
1113
+
1114
+ relation = relation.create_with(scope_for_create)
1115
+ else
1116
+ scope_for_create = method_scoping[:create]
1117
+ scope_for_create ||= current_scoped_methods.create_with_value if current_scoped_methods
1118
+ relation = relation.create_with(scope_for_create) if scope_for_create
1119
+ end
1120
+
1121
+ method_scoping = relation
1122
+ end
1123
+
1124
+ method_scoping = current_scoped_methods.merge(method_scoping) if current_scoped_methods && action == :merge
1125
+
1126
+ self.scoped_methods << method_scoping
1127
+ begin
1128
+ yield
1129
+ ensure
1130
+ self.scoped_methods.pop
1131
+ end
1132
+ end
1133
+
1134
+ # Works like with_scope, but discards any nested properties.
1135
+ def with_exclusive_scope(method_scoping = {}, &block)
1136
+ if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) }
1137
+ raise ArgumentError, <<-MSG
1138
+ New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope:
1139
+
1140
+ User.unscoped.where(:active => true)
1141
+
1142
+ Or call unscoped with a block:
1143
+
1144
+ User.unscoped do
1145
+ User.where(:active => true).all
1146
+ end
1147
+
1148
+ MSG
1149
+ end
1150
+ with_scope(method_scoping, :overwrite, &block)
1151
+ end
1152
+
1153
+ # Sets the default options for the model. The format of the
1154
+ # <tt>options</tt> argument is the same as in find.
1155
+ #
1156
+ # class Person < ActiveRecord::Base
1157
+ # default_scope order('last_name, first_name')
1158
+ # end
1159
+ #
1160
+ # <tt>default_scope</tt> is also applied while creating/building a record. It is not
1161
+ # applied while updating a record.
1162
+ #
1163
+ # class Article < ActiveRecord::Base
1164
+ # default_scope where(:published => true)
1165
+ # end
1166
+ #
1167
+ # Article.new.published # => true
1168
+ # Article.create.published # => true
1169
+ def default_scope(options = {})
1170
+ self.default_scoping << construct_finder_arel(options, default_scoping.pop)
1171
+ end
1172
+
1173
+ def current_scoped_methods #:nodoc:
1174
+ scoped_methods.last
1175
+ end
1176
+
1177
+ # Returns the class type of the record using the current module as a prefix. So descendants of
1178
+ # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
1179
+ def compute_type(type_name)
1180
+ if type_name.match(/^::/)
1181
+ # If the type is prefixed with a scope operator then we assume that
1182
+ # the type_name is an absolute reference.
1183
+ ActiveSupport::Dependencies.constantize(type_name)
1184
+ else
1185
+ # Build a list of candidates to search for
1186
+ candidates = []
1187
+ name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
1188
+ candidates << type_name
1189
+
1190
+ candidates.each do |candidate|
1191
+ begin
1192
+ constant = ActiveSupport::Dependencies.constantize(candidate)
1193
+ return constant if candidate == constant.to_s
1194
+ rescue NameError => e
1195
+ # We don't want to swallow NoMethodError < NameError errors
1196
+ raise e unless e.instance_of?(NameError)
1197
+ rescue ArgumentError
1198
+ end
1199
+ end
1200
+
1201
+ raise NameError, "uninitialized constant #{candidates.first}"
1202
+ end
1203
+ end
1204
+
1205
+ # Returns the class descending directly from ActiveRecord::Base or an
1206
+ # abstract class, if any, in the inheritance hierarchy.
1207
+ def class_of_active_record_descendant(klass)
1208
+ if klass.superclass == Base || klass.superclass.abstract_class?
1209
+ klass
1210
+ elsif klass.superclass.nil?
1211
+ raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
1212
+ else
1213
+ class_of_active_record_descendant(klass.superclass)
1214
+ end
1215
+ end
1216
+
1217
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
1218
+ # them into a valid SQL fragment for a WHERE clause.
1219
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
1220
+ # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
1221
+ # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
1222
+ def sanitize_sql_for_conditions(condition, table_name = self.table_name)
1223
+ return nil if condition.blank?
1224
+
1225
+ case condition
1226
+ when Array; sanitize_sql_array(condition)
1227
+ when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
1228
+ else condition
1229
+ end
1230
+ end
1231
+ alias_method :sanitize_sql, :sanitize_sql_for_conditions
1232
+
1233
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
1234
+ # them into a valid SQL fragment for a SET clause.
1235
+ # { :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
1236
+ def sanitize_sql_for_assignment(assignments)
1237
+ case assignments
1238
+ when Array; sanitize_sql_array(assignments)
1239
+ when Hash; sanitize_sql_hash_for_assignment(assignments)
1240
+ else assignments
1241
+ end
1242
+ end
1243
+
1244
+ def aggregate_mapping(reflection)
1245
+ mapping = reflection.options[:mapping] || [reflection.name, reflection.name]
1246
+ mapping.first.is_a?(Array) ? mapping : [mapping]
1247
+ end
1248
+
1249
+ # Accepts a hash of SQL conditions and replaces those attributes
1250
+ # that correspond to a +composed_of+ relationship with their expanded
1251
+ # aggregate attribute values.
1252
+ # Given:
1253
+ # class Person < ActiveRecord::Base
1254
+ # composed_of :address, :class_name => "Address",
1255
+ # :mapping => [%w(address_street street), %w(address_city city)]
1256
+ # end
1257
+ # Then:
1258
+ # { :address => Address.new("813 abc st.", "chicago") }
1259
+ # # => { :address_street => "813 abc st.", :address_city => "chicago" }
1260
+ def expand_hash_conditions_for_aggregates(attrs)
1261
+ expanded_attrs = {}
1262
+ attrs.each do |attr, value|
1263
+ unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
1264
+ mapping = aggregate_mapping(aggregation)
1265
+ mapping.each do |field_attr, aggregate_attr|
1266
+ if mapping.size == 1 && !value.respond_to?(aggregate_attr)
1267
+ expanded_attrs[field_attr] = value
1268
+ else
1269
+ expanded_attrs[field_attr] = value.send(aggregate_attr)
1270
+ end
1271
+ end
1272
+ else
1273
+ expanded_attrs[attr] = value
1274
+ end
1275
+ end
1276
+ expanded_attrs
1277
+ end
1278
+
1279
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
1280
+ # { :name => "foo'bar", :group_id => 4 }
1281
+ # # => "name='foo''bar' and group_id= 4"
1282
+ # { :status => nil, :group_id => [1,2,3] }
1283
+ # # => "status IS NULL and group_id IN (1,2,3)"
1284
+ # { :age => 13..18 }
1285
+ # # => "age BETWEEN 13 AND 18"
1286
+ # { 'other_records.id' => 7 }
1287
+ # # => "`other_records`.`id` = 7"
1288
+ # { :other_records => { :id => 7 } }
1289
+ # # => "`other_records`.`id` = 7"
1290
+ # And for value objects on a composed_of relationship:
1291
+ # { :address => Address.new("123 abc st.", "chicago") }
1292
+ # # => "address_street='123 abc st.' and address_city='chicago'"
1293
+ def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
1294
+ attrs = expand_hash_conditions_for_aggregates(attrs)
1295
+
1296
+ table = Arel::Table.new(self.table_name, :engine => arel_engine, :as => default_table_name)
1297
+ builder = PredicateBuilder.new(arel_engine)
1298
+ builder.build_from_hash(attrs, table).map{ |b| b.to_sql }.join(' AND ')
1299
+ end
1300
+ alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
1301
+
1302
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
1303
+ # { :status => nil, :group_id => 1 }
1304
+ # # => "status = NULL , group_id = 1"
1305
+ def sanitize_sql_hash_for_assignment(attrs)
1306
+ attrs.map do |attr, value|
1307
+ "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
1308
+ end.join(', ')
1309
+ end
1310
+
1311
+ # Accepts an array of conditions. The array has each value
1312
+ # sanitized and interpolated into the SQL statement.
1313
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
1314
+ def sanitize_sql_array(ary)
1315
+ statement, *values = ary
1316
+ if values.first.is_a?(Hash) and statement =~ /:\w+/
1317
+ replace_named_bind_variables(statement, values.first)
1318
+ elsif statement.include?('?')
1319
+ replace_bind_variables(statement, values)
1320
+ elsif statement.blank?
1321
+ statement
1322
+ else
1323
+ statement % values.collect { |value| connection.quote_string(value.to_s) }
1324
+ end
1325
+ end
1326
+
1327
+ alias_method :sanitize_conditions, :sanitize_sql
1328
+
1329
+ def replace_bind_variables(statement, values) #:nodoc:
1330
+ raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
1331
+ bound = values.dup
1332
+ c = connection
1333
+ statement.gsub('?') { quote_bound_value(bound.shift, c) }
1334
+ end
1335
+
1336
+ def replace_named_bind_variables(statement, bind_vars) #:nodoc:
1337
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do
1338
+ if $1 == ':' # skip postgresql casts
1339
+ $& # return the whole match
1340
+ elsif bind_vars.include?(match = $2.to_sym)
1341
+ quote_bound_value(bind_vars[match])
1342
+ else
1343
+ raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
1344
+ end
1345
+ end
1346
+ end
1347
+
1348
+ def expand_range_bind_variables(bind_vars) #:nodoc:
1349
+ expanded = []
1350
+
1351
+ bind_vars.each do |var|
1352
+ next if var.is_a?(Hash)
1353
+
1354
+ if var.is_a?(Range)
1355
+ expanded << var.first
1356
+ expanded << var.last
1357
+ else
1358
+ expanded << var
1359
+ end
1360
+ end
1361
+
1362
+ expanded
1363
+ end
1364
+
1365
+ def quote_bound_value(value, c = connection) #:nodoc:
1366
+ if value.respond_to?(:map) && !value.acts_like?(:string)
1367
+ if value.respond_to?(:empty?) && value.empty?
1368
+ c.quote(nil)
1369
+ else
1370
+ value.map { |v| c.quote(v) }.join(',')
1371
+ end
1372
+ else
1373
+ c.quote(value)
1374
+ end
1375
+ end
1376
+
1377
+ def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
1378
+ unless expected == provided
1379
+ raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
1380
+ end
1381
+ end
1382
+
1383
+ def encode_quoted_value(value) #:nodoc:
1384
+ quoted_value = connection.quote(value)
1385
+ quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "
1386
+ quoted_value
1387
+ end
1388
+ end
1389
+
1390
+ public
1391
+ # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
1392
+ # attributes but not yet saved (pass a hash with key names matching the associated table column names).
1393
+ # In both instances, valid attribute keys are determined by the column names of the associated table --
1394
+ # hence you can't have attributes that aren't part of the table columns.
1395
+ def initialize(attributes = nil)
1396
+ @attributes = attributes_from_column_definition
1397
+ @attributes_cache = {}
1398
+ @new_record = true
1399
+ @readonly = false
1400
+ @destroyed = false
1401
+ @marked_for_destruction = false
1402
+ @previously_changed = {}
1403
+ @changed_attributes = {}
1404
+
1405
+ ensure_proper_type
1406
+
1407
+ if scope = self.class.send(:current_scoped_methods)
1408
+ create_with = scope.scope_for_create
1409
+ create_with.each { |att,value| self.send("#{att}=", value) } if create_with
1410
+ end
1411
+ self.attributes = attributes unless attributes.nil?
1412
+
1413
+ result = yield self if block_given?
1414
+ _run_initialize_callbacks
1415
+ result
1416
+ end
1417
+
1418
+ # Cloned objects have no id assigned and are treated as new records. Note that this is a "shallow" clone
1419
+ # as it copies the object's attributes only, not its associations. The extent of a "deep" clone is
1420
+ # application specific and is therefore left to the application to implement according to its need.
1421
+ def initialize_copy(other)
1422
+ _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks)
1423
+ cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
1424
+ cloned_attributes.delete(self.class.primary_key)
1425
+
1426
+ @attributes = cloned_attributes
1427
+
1428
+ @changed_attributes = {}
1429
+ attributes_from_column_definition.each do |attr, orig_value|
1430
+ @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr])
1431
+ end
1432
+
1433
+ clear_aggregation_cache
1434
+ clear_association_cache
1435
+ @attributes_cache = {}
1436
+ @new_record = true
1437
+ ensure_proper_type
1438
+
1439
+ if scope = self.class.send(:current_scoped_methods)
1440
+ create_with = scope.scope_for_create
1441
+ create_with.each { |att,value| self.send("#{att}=", value) } if create_with
1442
+ end
1443
+ end
1444
+
1445
+ # Returns a String, which Action Pack uses for constructing an URL to this
1446
+ # object. The default implementation returns this record's id as a String,
1447
+ # or nil if this record's unsaved.
1448
+ #
1449
+ # For example, suppose that you have a User model, and that you have a
1450
+ # <tt>resources :users</tt> route. Normally, +user_path+ will
1451
+ # construct a path with the user object's 'id' in it:
1452
+ #
1453
+ # user = User.find_by_name('Phusion')
1454
+ # user_path(user) # => "/users/1"
1455
+ #
1456
+ # You can override +to_param+ in your model to make +user_path+ construct
1457
+ # a path using the user's name instead of the user's id:
1458
+ #
1459
+ # class User < ActiveRecord::Base
1460
+ # def to_param # overridden
1461
+ # name
1462
+ # end
1463
+ # end
1464
+ #
1465
+ # user = User.find_by_name('Phusion')
1466
+ # user_path(user) # => "/users/Phusion"
1467
+ def to_param
1468
+ # We can't use alias_method here, because method 'id' optimizes itself on the fly.
1469
+ id && id.to_s # Be sure to stringify the id for routes
1470
+ end
1471
+
1472
+ # Returns a cache key that can be used to identify this record.
1473
+ #
1474
+ # ==== Examples
1475
+ #
1476
+ # Product.new.cache_key # => "products/new"
1477
+ # Product.find(5).cache_key # => "products/5" (updated_at not available)
1478
+ # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
1479
+ def cache_key
1480
+ case
1481
+ when new_record?
1482
+ "#{self.class.model_name.cache_key}/new"
1483
+ when timestamp = self[:updated_at]
1484
+ "#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}"
1485
+ else
1486
+ "#{self.class.model_name.cache_key}/#{id}"
1487
+ end
1488
+ end
1489
+
1490
+ def quoted_id #:nodoc:
1491
+ quote_value(id, column_for_attribute(self.class.primary_key))
1492
+ end
1493
+
1494
+ # Returns true if the given attribute is in the attributes hash
1495
+ def has_attribute?(attr_name)
1496
+ @attributes.has_key?(attr_name.to_s)
1497
+ end
1498
+
1499
+ # Returns an array of names for the attributes available on this object sorted alphabetically.
1500
+ def attribute_names
1501
+ @attributes.keys.sort
1502
+ end
1503
+
1504
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
1505
+ # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
1506
+ # (Alias for the protected read_attribute method).
1507
+ def [](attr_name)
1508
+ read_attribute(attr_name)
1509
+ end
1510
+
1511
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
1512
+ # (Alias for the protected write_attribute method).
1513
+ def []=(attr_name, value)
1514
+ write_attribute(attr_name, value)
1515
+ end
1516
+
1517
+ # Allows you to set all the attributes at once by passing in a hash with keys
1518
+ # matching the attribute names (which again matches the column names).
1519
+ #
1520
+ # If +guard_protected_attributes+ is true (the default), then sensitive
1521
+ # attributes can be protected from this form of mass-assignment by using
1522
+ # the +attr_protected+ macro. Or you can alternatively specify which
1523
+ # attributes *can* be accessed with the +attr_accessible+ macro. Then all the
1524
+ # attributes not included in that won't be allowed to be mass-assigned.
1525
+ #
1526
+ # class User < ActiveRecord::Base
1527
+ # attr_protected :is_admin
1528
+ # end
1529
+ #
1530
+ # user = User.new
1531
+ # user.attributes = { :username => 'Phusion', :is_admin => true }
1532
+ # user.username # => "Phusion"
1533
+ # user.is_admin? # => false
1534
+ #
1535
+ # user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false)
1536
+ # user.is_admin? # => true
1537
+ def attributes=(new_attributes, guard_protected_attributes = true)
1538
+ return unless new_attributes.is_a?(Hash)
1539
+ attributes = new_attributes.stringify_keys
1540
+
1541
+ multi_parameter_attributes = []
1542
+ attributes = sanitize_for_mass_assignment(attributes) if guard_protected_attributes
1543
+
1544
+ attributes.each do |k, v|
1545
+ if k.include?("(")
1546
+ multi_parameter_attributes << [ k, v ]
1547
+ else
1548
+ respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
1549
+ end
1550
+ end
1551
+
1552
+ assign_multiparameter_attributes(multi_parameter_attributes)
1553
+ end
1554
+
1555
+ # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
1556
+ def attributes
1557
+ attrs = {}
1558
+ attribute_names.each { |name| attrs[name] = read_attribute(name) }
1559
+ attrs
1560
+ end
1561
+
1562
+ # Returns an <tt>#inspect</tt>-like string for the value of the
1563
+ # attribute +attr_name+. String attributes are elided after 50
1564
+ # characters, and Date and Time attributes are returned in the
1565
+ # <tt>:db</tt> format. Other attributes return the value of
1566
+ # <tt>#inspect</tt> without modification.
1567
+ #
1568
+ # person = Person.create!(:name => "David Heinemeier Hansson " * 3)
1569
+ #
1570
+ # person.attribute_for_inspect(:name)
1571
+ # # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
1572
+ #
1573
+ # person.attribute_for_inspect(:created_at)
1574
+ # # => '"2009-01-12 04:48:57"'
1575
+ def attribute_for_inspect(attr_name)
1576
+ value = read_attribute(attr_name)
1577
+
1578
+ if value.is_a?(String) && value.length > 50
1579
+ "#{value[0..50]}...".inspect
1580
+ elsif value.is_a?(Date) || value.is_a?(Time)
1581
+ %("#{value.to_s(:db)}")
1582
+ else
1583
+ value.inspect
1584
+ end
1585
+ end
1586
+
1587
+ # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
1588
+ # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
1589
+ def attribute_present?(attribute)
1590
+ value = read_attribute(attribute)
1591
+ !value.blank?
1592
+ end
1593
+
1594
+ # Returns the column object for the named attribute.
1595
+ def column_for_attribute(name)
1596
+ self.class.columns_hash[name.to_s]
1597
+ end
1598
+
1599
+ # Returns true if the +comparison_object+ is the same object, or is of the same type and has the same id.
1600
+ def ==(comparison_object)
1601
+ comparison_object.equal?(self) ||
1602
+ (comparison_object.instance_of?(self.class) &&
1603
+ comparison_object.id == id && !comparison_object.new_record?)
1604
+ end
1605
+
1606
+ # Delegates to ==
1607
+ def eql?(comparison_object)
1608
+ self == (comparison_object)
1609
+ end
1610
+
1611
+ # Delegates to id in order to allow two records of the same type and id to work with something like:
1612
+ # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
1613
+ def hash
1614
+ id.hash
1615
+ end
1616
+
1617
+ # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
1618
+ def freeze
1619
+ @attributes.freeze; self
1620
+ end
1621
+
1622
+ # Returns +true+ if the attributes hash has been frozen.
1623
+ def frozen?
1624
+ @attributes.frozen?
1625
+ end
1626
+
1627
+ # Returns duplicated record with unfreezed attributes.
1628
+ def dup
1629
+ obj = super
1630
+ obj.instance_variable_set('@attributes', @attributes.dup)
1631
+ obj
1632
+ end
1633
+
1634
+ # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
1635
+ # attributes will be marked as read only since they cannot be saved.
1636
+ def readonly?
1637
+ @readonly
1638
+ end
1639
+
1640
+ # Marks this record as read only.
1641
+ def readonly!
1642
+ @readonly = true
1643
+ end
1644
+
1645
+ # Returns the contents of the record as a nicely formatted string.
1646
+ def inspect
1647
+ attributes_as_nice_string = self.class.column_names.collect { |name|
1648
+ if has_attribute?(name) || new_record?
1649
+ "#{name}: #{attribute_for_inspect(name)}"
1650
+ end
1651
+ }.compact.join(", ")
1652
+ "#<#{self.class} #{attributes_as_nice_string}>"
1653
+ end
1654
+
1655
+ protected
1656
+ def clone_attributes(reader_method = :read_attribute, attributes = {})
1657
+ attribute_names.each do |name|
1658
+ attributes[name] = clone_attribute_value(reader_method, name)
1659
+ end
1660
+ attributes
1661
+ end
1662
+
1663
+ def clone_attribute_value(reader_method, attribute_name)
1664
+ value = send(reader_method, attribute_name)
1665
+ value.duplicable? ? value.clone : value
1666
+ rescue TypeError, NoMethodError
1667
+ value
1668
+ end
1669
+
1670
+ private
1671
+
1672
+ # Sets the attribute used for single table inheritance to this class name if this is not the
1673
+ # ActiveRecord::Base descendant.
1674
+ # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
1675
+ # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
1676
+ # No such attribute would be set for objects of the Message class in that example.
1677
+ def ensure_proper_type
1678
+ unless self.class.descends_from_active_record?
1679
+ write_attribute(self.class.inheritance_column, self.class.sti_name)
1680
+ end
1681
+ end
1682
+
1683
+ # The primary key and inheritance column can never be set by mass-assignment for security reasons.
1684
+ def self.attributes_protected_by_default
1685
+ default = [ primary_key, inheritance_column ]
1686
+ default << 'id' unless primary_key.eql? 'id'
1687
+ default
1688
+ end
1689
+
1690
+ # Returns a copy of the attributes hash where all the values have been safely quoted for use in
1691
+ # an Arel insert/update method.
1692
+ def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
1693
+ attrs = {}
1694
+ attribute_names.each do |name|
1695
+ if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
1696
+
1697
+ if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name))
1698
+ value = read_attribute(name)
1699
+
1700
+ if value && ((self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))) || value.is_a?(Hash) || value.is_a?(Array))
1701
+ value = value.to_yaml
1702
+ end
1703
+ attrs[self.class.arel_table[name]] = value
1704
+ end
1705
+ end
1706
+ end
1707
+ attrs
1708
+ end
1709
+
1710
+ # Quote strings appropriately for SQL statements.
1711
+ def quote_value(value, column = nil)
1712
+ self.class.connection.quote(value, column)
1713
+ end
1714
+
1715
+ # Interpolate custom SQL string in instance context.
1716
+ # Optional record argument is meant for custom insert_sql.
1717
+ def interpolate_sql(sql, record = nil)
1718
+ instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__)
1719
+ end
1720
+
1721
+ # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
1722
+ # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
1723
+ # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
1724
+ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
1725
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum,
1726
+ # f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the
1727
+ # attribute will be set to nil.
1728
+ def assign_multiparameter_attributes(pairs)
1729
+ execute_callstack_for_multiparameter_attributes(
1730
+ extract_callstack_for_multiparameter_attributes(pairs)
1731
+ )
1732
+ end
1733
+
1734
+ def instantiate_time_object(name, values)
1735
+ if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
1736
+ Time.zone.local(*values)
1737
+ else
1738
+ Time.time_with_datetime_fallback(@@default_timezone, *values)
1739
+ end
1740
+ end
1741
+
1742
+ def execute_callstack_for_multiparameter_attributes(callstack)
1743
+ errors = []
1744
+ callstack.each do |name, values_with_empty_parameters|
1745
+ begin
1746
+ klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
1747
+ # in order to allow a date to be set without a year, we must keep the empty values.
1748
+ # Otherwise, we wouldn't be able to distinguish it from a date with an empty day.
1749
+ values = values_with_empty_parameters.reject { |v| v.nil? }
1750
+
1751
+ if values.empty?
1752
+ send(name + "=", nil)
1753
+ else
1754
+
1755
+ value = if Time == klass
1756
+ instantiate_time_object(name, values)
1757
+ elsif Date == klass
1758
+ begin
1759
+ values = values_with_empty_parameters.collect do |v| v.nil? ? 1 : v end
1760
+ Date.new(*values)
1761
+ rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
1762
+ instantiate_time_object(name, values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
1763
+ end
1764
+ else
1765
+ klass.new(*values)
1766
+ end
1767
+
1768
+ send(name + "=", value)
1769
+ end
1770
+ rescue => ex
1771
+ errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
1772
+ end
1773
+ end
1774
+ unless errors.empty?
1775
+ raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
1776
+ end
1777
+ end
1778
+
1779
+ def extract_callstack_for_multiparameter_attributes(pairs)
1780
+ attributes = { }
1781
+
1782
+ for pair in pairs
1783
+ multiparameter_name, value = pair
1784
+ attribute_name = multiparameter_name.split("(").first
1785
+ attributes[attribute_name] = [] unless attributes.include?(attribute_name)
1786
+
1787
+ parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
1788
+ attributes[attribute_name] << [ find_parameter_position(multiparameter_name), parameter_value ]
1789
+ end
1790
+
1791
+ attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
1792
+ end
1793
+
1794
+ def type_cast_attribute_value(multiparameter_name, value)
1795
+ multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
1796
+ end
1797
+
1798
+ def find_parameter_position(multiparameter_name)
1799
+ multiparameter_name.scan(/\(([0-9]*).*\)/).first.first
1800
+ end
1801
+
1802
+ # Returns a comma-separated pair list, like "key1 = val1, key2 = val2".
1803
+ def comma_pair_list(hash)
1804
+ hash.map { |k,v| "#{k} = #{v}" }.join(", ")
1805
+ end
1806
+
1807
+ def quote_columns(quoter, hash)
1808
+ hash.inject({}) do |quoted, (name, value)|
1809
+ quoted[quoter.quote_column_name(name)] = value
1810
+ quoted
1811
+ end
1812
+ end
1813
+
1814
+ def quoted_comma_pair_list(quoter, hash)
1815
+ comma_pair_list(quote_columns(quoter, hash))
1816
+ end
1817
+
1818
+ def convert_number_column_value(value)
1819
+ if value == false
1820
+ 0
1821
+ elsif value == true
1822
+ 1
1823
+ elsif value.is_a?(String) && value.blank?
1824
+ nil
1825
+ else
1826
+ value
1827
+ end
1828
+ end
1829
+
1830
+ def object_from_yaml(string)
1831
+ return string unless string.is_a?(String) && string =~ /^---/
1832
+ YAML::load(string) rescue string
1833
+ end
1834
+ end
1835
+
1836
+ Base.class_eval do
1837
+ include ActiveRecord::Persistence
1838
+ extend ActiveModel::Naming
1839
+ extend QueryCache::ClassMethods
1840
+ extend ActiveSupport::Benchmarkable
1841
+ extend ActiveSupport::DescendantsTracker
1842
+
1843
+ include ActiveModel::Conversion
1844
+ include Validations
1845
+ extend CounterCache
1846
+ include Locking::Optimistic, Locking::Pessimistic
1847
+ include AttributeMethods
1848
+ include AttributeMethods::Read, AttributeMethods::Write, AttributeMethods::BeforeTypeCast, AttributeMethods::Query
1849
+ include AttributeMethods::PrimaryKey
1850
+ include AttributeMethods::TimeZoneConversion
1851
+ include AttributeMethods::Dirty
1852
+ include ActiveModel::MassAssignmentSecurity
1853
+ include Callbacks, ActiveModel::Observing, Timestamp
1854
+ include Associations, AssociationPreload, NamedScope
1855
+
1856
+ # AutosaveAssociation needs to be included before Transactions, because we want
1857
+ # #save_with_autosave_associations to be wrapped inside a transaction.
1858
+ include AutosaveAssociation, NestedAttributes
1859
+ include Aggregations, Transactions, Reflection, Serialization
1860
+
1861
+ NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner)
1862
+ end
1863
+ end
1864
+
1865
+ # TODO: Remove this and make it work with LAZY flag
1866
+ require 'active_record/connection_adapters/abstract_adapter'
1867
+ ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)