square-activerecord 3.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. data/CHANGELOG +6140 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +179 -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 +430 -0
  9. data/lib/active_record/associations.rb +2307 -0
  10. data/lib/active_record/associations/association_collection.rb +572 -0
  11. data/lib/active_record/associations/association_proxy.rb +299 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +82 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +115 -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 +30 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +56 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +145 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +64 -0
  27. data/lib/active_record/attribute_methods/write.rb +43 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1904 -0
  30. data/lib/active_record/callbacks.rb +284 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +364 -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 +333 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +73 -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 +539 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +217 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +657 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1031 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -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 +56 -0
  46. data/lib/active_record/dynamic_scope_match.rb +23 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1006 -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 +419 -0
  56. data/lib/active_record/observer.rb +125 -0
  57. data/lib/active_record/persistence.rb +290 -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 +411 -0
  63. data/lib/active_record/relation.rb +394 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +295 -0
  66. data/lib/active_record/relation/finder_methods.rb +363 -0
  67. data/lib/active_record/relation/predicate_builder.rb +48 -0
  68. data/lib/active_record/relation/query_methods.rb +303 -0
  69. data/lib/active_record/relation/spawn_methods.rb +132 -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 +359 -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 +190 -0
  81. data/lib/active_record/version.rb +10 -0
  82. data/lib/rails/generators/active_record.rb +19 -0
  83. data/lib/rails/generators/active_record/migration.rb +15 -0
  84. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  85. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  86. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  87. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  88. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  89. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  90. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  91. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  92. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  93. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  94. metadata +223 -0
@@ -0,0 +1,1904 @@
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, :exists?, :any?, :many?, :to => :scoped
440
+ delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped
441
+ delegate :find_each, :find_in_batches, :to => :scoped
442
+ delegate :select, :group, :order, :reorder, :except, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
443
+ delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
444
+
445
+ # Executes a custom SQL query against your database and returns all the results. The results will
446
+ # be returned as an array with columns requested encapsulated as attributes of the model you call
447
+ # this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
448
+ # a Product object with the attributes you specified in the SQL query.
449
+ #
450
+ # If you call a complicated SQL query which spans multiple tables the columns specified by the
451
+ # SELECT will be attributes of the model, whether or not they are columns of the corresponding
452
+ # table.
453
+ #
454
+ # The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be
455
+ # no database agnostic conversions performed. This should be a last resort because using, for example,
456
+ # MySQL specific terms will lock you to using that particular database engine or require you to
457
+ # change your call if you switch engines.
458
+ #
459
+ # ==== Examples
460
+ # # A simple SQL query spanning multiple tables
461
+ # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
462
+ # > [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
463
+ #
464
+ # # You can use the same string replacement techniques as you can with ActiveRecord#find
465
+ # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
466
+ # > [#<Post:0x36bff9c @attributes={"first_name"=>"The Cheap Man Buys Twice"}>, ...]
467
+ def find_by_sql(sql)
468
+ connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
469
+ end
470
+
471
+ # Creates an object (or multiple objects) and saves it to the database, if validations pass.
472
+ # The resulting object is returned whether the object was saved successfully to the database or not.
473
+ #
474
+ # The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the
475
+ # attributes on the objects that are to be created.
476
+ #
477
+ # ==== Examples
478
+ # # Create a single new object
479
+ # User.create(:first_name => 'Jamie')
480
+ #
481
+ # # Create an Array of new objects
482
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
483
+ #
484
+ # # Create a single object and pass it into a block to set other attributes.
485
+ # User.create(:first_name => 'Jamie') do |u|
486
+ # u.is_admin = false
487
+ # end
488
+ #
489
+ # # Creating an Array of new objects using a block, where the block is executed for each object:
490
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
491
+ # u.is_admin = false
492
+ # end
493
+ def create(attributes = nil, &block)
494
+ if attributes.is_a?(Array)
495
+ attributes.collect { |attr| create(attr, &block) }
496
+ else
497
+ object = new(attributes)
498
+ yield(object) if block_given?
499
+ object.save
500
+ object
501
+ end
502
+ end
503
+
504
+ # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
505
+ # The use of this method should be restricted to complicated SQL queries that can't be executed
506
+ # using the ActiveRecord::Calculations class methods. Look into those before using this.
507
+ #
508
+ # ==== Parameters
509
+ #
510
+ # * +sql+ - An SQL statement which should return a count query from the database, see the example below.
511
+ #
512
+ # ==== Examples
513
+ #
514
+ # Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
515
+ def count_by_sql(sql)
516
+ sql = sanitize_conditions(sql)
517
+ connection.select_value(sql, "#{name} Count").to_i
518
+ end
519
+
520
+ # Attributes listed as readonly will be used to create a new record but update operations will
521
+ # ignore these fields.
522
+ def attr_readonly(*attributes)
523
+ write_inheritable_attribute(:attr_readonly, Set.new(attributes.map { |a| a.to_s }) + (readonly_attributes || []))
524
+ end
525
+
526
+ # Returns an array of all the attributes that have been specified as readonly.
527
+ def readonly_attributes
528
+ read_inheritable_attribute(:attr_readonly) || []
529
+ end
530
+
531
+ # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
532
+ # then specify the name of that attribute using this method and it will be handled automatically.
533
+ # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
534
+ # class on retrieval or SerializationTypeMismatch will be raised.
535
+ #
536
+ # ==== Parameters
537
+ #
538
+ # * +attr_name+ - The field name that should be serialized.
539
+ # * +class_name+ - Optional, class name that the object type should be equal to.
540
+ #
541
+ # ==== Example
542
+ # # Serialize a preferences attribute
543
+ # class User
544
+ # serialize :preferences
545
+ # end
546
+ def serialize(attr_name, class_name = Object)
547
+ serialized_attributes[attr_name.to_s] = class_name
548
+ end
549
+
550
+ # Returns a hash of all the attributes that have been specified for serialization as
551
+ # keys and their class restriction as values.
552
+ def serialized_attributes
553
+ read_inheritable_attribute(:attr_serialized) or write_inheritable_attribute(:attr_serialized, {})
554
+ end
555
+
556
+ # Guesses the table name (in forced lower-case) based on the name of the class in the
557
+ # inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy
558
+ # looks like: Reply < Message < ActiveRecord::Base, then Message is used
559
+ # to guess the table name even when called on Reply. The rules used to do the guess
560
+ # are handled by the Inflector class in Active Support, which knows almost all common
561
+ # English inflections. You can add new inflections in config/initializers/inflections.rb.
562
+ #
563
+ # Nested classes are given table names prefixed by the singular form of
564
+ # the parent's table name. Enclosing modules are not considered.
565
+ #
566
+ # ==== Examples
567
+ #
568
+ # class Invoice < ActiveRecord::Base; end;
569
+ # file class table_name
570
+ # invoice.rb Invoice invoices
571
+ #
572
+ # class Invoice < ActiveRecord::Base; class Lineitem < ActiveRecord::Base; end; end;
573
+ # file class table_name
574
+ # invoice.rb Invoice::Lineitem invoice_lineitems
575
+ #
576
+ # module Invoice; class Lineitem < ActiveRecord::Base; end; end;
577
+ # file class table_name
578
+ # invoice/lineitem.rb Invoice::Lineitem lineitems
579
+ #
580
+ # Additionally, the class-level +table_name_prefix+ is prepended and the
581
+ # +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
582
+ # the table name guess for an Invoice class becomes "myapp_invoices".
583
+ # Invoice::Lineitem becomes "myapp_invoice_lineitems".
584
+ #
585
+ # You can also overwrite this class method to allow for unguessable
586
+ # links, such as a Mouse class with a link to a "mice" table. Example:
587
+ #
588
+ # class Mouse < ActiveRecord::Base
589
+ # set_table_name "mice"
590
+ # end
591
+ def table_name
592
+ reset_table_name
593
+ end
594
+
595
+ # Returns a quoted version of the table name, used to construct SQL statements.
596
+ def quoted_table_name
597
+ @quoted_table_name ||= connection.quote_table_name(table_name)
598
+ end
599
+
600
+ # Computes the table name, (re)sets it internally, and returns it.
601
+ def reset_table_name #:nodoc:
602
+ self.table_name = compute_table_name
603
+ end
604
+
605
+ def full_table_name_prefix #:nodoc:
606
+ (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
607
+ end
608
+
609
+ # Defines the column name for use with single table inheritance. Use
610
+ # <tt>set_inheritance_column</tt> to set a different value.
611
+ def inheritance_column
612
+ @inheritance_column ||= "type"
613
+ end
614
+
615
+ # Lazy-set the sequence name to the connection's default. This method
616
+ # is only ever called once since set_sequence_name overrides it.
617
+ def sequence_name #:nodoc:
618
+ reset_sequence_name
619
+ end
620
+
621
+ def reset_sequence_name #:nodoc:
622
+ default = connection.default_sequence_name(table_name, primary_key)
623
+ set_sequence_name(default)
624
+ default
625
+ end
626
+
627
+ # Sets the table name. If the value is nil or false then the value returned by the given
628
+ # block is used.
629
+ #
630
+ # class Project < ActiveRecord::Base
631
+ # set_table_name "project"
632
+ # end
633
+ def set_table_name(value = nil, &block)
634
+ @quoted_table_name = nil
635
+ define_attr_method :table_name, value, &block
636
+ end
637
+ alias :table_name= :set_table_name
638
+
639
+ # Sets the name of the inheritance column to use to the given value,
640
+ # or (if the value # is nil or false) to the value returned by the
641
+ # given block.
642
+ #
643
+ # class Project < ActiveRecord::Base
644
+ # set_inheritance_column do
645
+ # original_inheritance_column + "_id"
646
+ # end
647
+ # end
648
+ def set_inheritance_column(value = nil, &block)
649
+ define_attr_method :inheritance_column, value, &block
650
+ end
651
+ alias :inheritance_column= :set_inheritance_column
652
+
653
+ # Sets the name of the sequence to use when generating ids to the given
654
+ # value, or (if the value is nil or false) to the value returned by the
655
+ # given block. This is required for Oracle and is useful for any
656
+ # database which relies on sequences for primary key generation.
657
+ #
658
+ # If a sequence name is not explicitly set when using Oracle or Firebird,
659
+ # it will default to the commonly used pattern of: #{table_name}_seq
660
+ #
661
+ # If a sequence name is not explicitly set when using PostgreSQL, it
662
+ # will discover the sequence corresponding to your primary key for you.
663
+ #
664
+ # class Project < ActiveRecord::Base
665
+ # set_sequence_name "projectseq" # default would have been "project_seq"
666
+ # end
667
+ def set_sequence_name(value = nil, &block)
668
+ define_attr_method :sequence_name, value, &block
669
+ end
670
+ alias :sequence_name= :set_sequence_name
671
+
672
+ # Indicates whether the table associated with this class exists
673
+ def table_exists?
674
+ connection.table_exists?(table_name)
675
+ end
676
+
677
+ # Returns an array of column objects for the table associated with this class.
678
+ def columns
679
+ unless defined?(@columns) && @columns
680
+ @columns = connection.columns(table_name, "#{name} Columns")
681
+ @columns.each { |column| column.primary = column.name == primary_key }
682
+ end
683
+ @columns
684
+ end
685
+
686
+ # Returns a hash of column objects for the table associated with this class.
687
+ def columns_hash
688
+ @columns_hash ||= Hash[columns.map { |column| [column.name, column] }]
689
+ end
690
+
691
+ # Returns an array of column names as strings.
692
+ def column_names
693
+ @column_names ||= columns.map { |column| column.name }
694
+ end
695
+
696
+ # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
697
+ # and columns used for single table inheritance have been removed.
698
+ def content_columns
699
+ @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
700
+ end
701
+
702
+ # 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
703
+ # 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
704
+ # is available.
705
+ def column_methods_hash #:nodoc:
706
+ @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
707
+ attr_name = attr.to_s
708
+ methods[attr.to_sym] = attr_name
709
+ methods["#{attr}=".to_sym] = attr_name
710
+ methods["#{attr}?".to_sym] = attr_name
711
+ methods["#{attr}_before_type_cast".to_sym] = attr_name
712
+ methods
713
+ end
714
+ end
715
+
716
+ # Resets all the cached information about columns, which will cause them
717
+ # to be reloaded on the next request.
718
+ #
719
+ # The most common usage pattern for this method is probably in a migration,
720
+ # when just after creating a table you want to populate it with some default
721
+ # values, eg:
722
+ #
723
+ # class CreateJobLevels < ActiveRecord::Migration
724
+ # def self.up
725
+ # create_table :job_levels do |t|
726
+ # t.integer :id
727
+ # t.string :name
728
+ #
729
+ # t.timestamps
730
+ # end
731
+ #
732
+ # JobLevel.reset_column_information
733
+ # %w{assistant executive manager director}.each do |type|
734
+ # JobLevel.create(:name => type)
735
+ # end
736
+ # end
737
+ #
738
+ # def self.down
739
+ # drop_table :job_levels
740
+ # end
741
+ # end
742
+ def reset_column_information
743
+ undefine_attribute_methods
744
+ @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil
745
+ @arel_engine = @relation = @arel_table = nil
746
+ end
747
+
748
+ def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
749
+ descendants.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
750
+ end
751
+
752
+ def attribute_method?(attribute)
753
+ super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
754
+ end
755
+
756
+ # Set the lookup ancestors for ActiveModel.
757
+ def lookup_ancestors #:nodoc:
758
+ klass = self
759
+ classes = [klass]
760
+ while klass != klass.base_class
761
+ classes << klass = klass.superclass
762
+ end
763
+ classes
764
+ rescue
765
+ # OPTIMIZE this rescue is to fix this test: ./test/cases/reflection_test.rb:56:in `test_human_name_for_column'
766
+ # Apparently the method base_class causes some trouble.
767
+ # It now works for sure.
768
+ [self]
769
+ end
770
+
771
+ # Set the i18n scope to overwrite ActiveModel.
772
+ def i18n_scope #:nodoc:
773
+ :activerecord
774
+ end
775
+
776
+ # True if this isn't a concrete subclass needing a STI type condition.
777
+ def descends_from_active_record?
778
+ if superclass.abstract_class?
779
+ superclass.descends_from_active_record?
780
+ else
781
+ superclass == Base || !columns_hash.include?(inheritance_column)
782
+ end
783
+ end
784
+
785
+ def finder_needs_type_condition? #:nodoc:
786
+ # This is like this because benchmarking justifies the strange :false stuff
787
+ :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
788
+ end
789
+
790
+ # Returns a string like 'Post id:integer, title:string, body:text'
791
+ def inspect
792
+ if self == Base
793
+ super
794
+ elsif abstract_class?
795
+ "#{super}(abstract)"
796
+ elsif table_exists?
797
+ attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
798
+ "#{super}(#{attr_list})"
799
+ else
800
+ "#{super}(Table doesn't exist)"
801
+ end
802
+ end
803
+
804
+ def quote_value(value, column = nil) #:nodoc:
805
+ connection.quote(value,column)
806
+ end
807
+
808
+ # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
809
+ def sanitize(object) #:nodoc:
810
+ connection.quote(object)
811
+ end
812
+
813
+ # Overwrite the default class equality method to provide support for association proxies.
814
+ def ===(object)
815
+ object.is_a?(self)
816
+ end
817
+
818
+ # Returns the base AR subclass that this class descends from. If A
819
+ # extends AR::Base, A.base_class will return A. If B descends from A
820
+ # through some arbitrarily deep hierarchy, B.base_class will return A.
821
+ #
822
+ # If B < A and C < B and if A is an abstract_class then both B.base_class
823
+ # and C.base_class would return B as the answer since A is an abstract_class.
824
+ def base_class
825
+ class_of_active_record_descendant(self)
826
+ end
827
+
828
+ # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
829
+ attr_accessor :abstract_class
830
+
831
+ # Returns whether this class is an abstract class or not.
832
+ def abstract_class?
833
+ defined?(@abstract_class) && @abstract_class == true
834
+ end
835
+
836
+ def respond_to?(method_id, include_private = false)
837
+ if match = DynamicFinderMatch.match(method_id)
838
+ return true if all_attributes_exists?(match.attribute_names)
839
+ elsif match = DynamicScopeMatch.match(method_id)
840
+ return true if all_attributes_exists?(match.attribute_names)
841
+ end
842
+
843
+ super
844
+ end
845
+
846
+ def sti_name
847
+ store_full_sti_class ? name : name.demodulize
848
+ end
849
+
850
+ def arel_table
851
+ @arel_table ||= Arel::Table.new(table_name, arel_engine)
852
+ end
853
+
854
+ def arel_engine
855
+ @arel_engine ||= begin
856
+ if self == ActiveRecord::Base
857
+ Arel::Table.engine
858
+ else
859
+ connection_handler.connection_pools[name] ? self : superclass.arel_engine
860
+ end
861
+ end
862
+ end
863
+
864
+ # Returns a scope for this class without taking into account the default_scope.
865
+ #
866
+ # class Post < ActiveRecord::Base
867
+ # default_scope :published => true
868
+ # end
869
+ #
870
+ # Post.all # Fires "SELECT * FROM posts WHERE published = true"
871
+ # Post.unscoped.all # Fires "SELECT * FROM posts"
872
+ #
873
+ # This method also accepts a block meaning that all queries inside the block will
874
+ # not use the default_scope:
875
+ #
876
+ # Post.unscoped {
877
+ # limit(10) # Fires "SELECT * FROM posts LIMIT 10"
878
+ # }
879
+ #
880
+ # It is recommended to use block form of unscoped because chaining unscoped with <tt>named_scope</tt>
881
+ # does not work. Assuming that <tt>published</tt> is a <tt>named_scope</tt> following two statements are same.
882
+ #
883
+ # Post.unscoped.published
884
+ # Post.published
885
+ def unscoped #:nodoc:
886
+ block_given? ? relation.scoping { yield } : relation
887
+ end
888
+
889
+ def scoped_methods #:nodoc:
890
+ key = :"#{self}_scoped_methods"
891
+ Thread.current[key] = Thread.current[key].presence || self.default_scoping.dup
892
+ end
893
+
894
+ def before_remove_const #:nodoc:
895
+ reset_scoped_methods
896
+ end
897
+
898
+ private
899
+
900
+ def relation #:nodoc:
901
+ @relation ||= Relation.new(self, arel_table)
902
+ finder_needs_type_condition? ? @relation.where(type_condition) : @relation
903
+ end
904
+
905
+ # Finder methods must instantiate through this method to work with the
906
+ # single-table inheritance model that makes it possible to create
907
+ # objects of different types from the same table.
908
+ def instantiate(record)
909
+ model = find_sti_class(record[inheritance_column]).allocate
910
+ model.init_with('attributes' => record)
911
+ model
912
+ end
913
+
914
+ def find_sti_class(type_name)
915
+ if type_name.blank? || !columns_hash.include?(inheritance_column)
916
+ self
917
+ else
918
+ begin
919
+ if store_full_sti_class
920
+ ActiveSupport::Dependencies.constantize(type_name)
921
+ else
922
+ compute_type(type_name)
923
+ end
924
+ rescue NameError
925
+ raise SubclassNotFound,
926
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
927
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
928
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
929
+ "or overwrite #{name}.inheritance_column to use another column for that information."
930
+ end
931
+ end
932
+ end
933
+
934
+ def construct_finder_arel(options = {}, scope = nil)
935
+ relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : options
936
+ relation = scope.merge(relation) if scope
937
+ relation
938
+ end
939
+
940
+ def type_condition
941
+ sti_column = arel_table[inheritance_column]
942
+ condition = sti_column.eq(sti_name)
943
+ descendants.each { |subclass| condition = condition.or(sti_column.eq(subclass.sti_name)) }
944
+
945
+ condition
946
+ end
947
+
948
+ # Guesses the table name, but does not decorate it with prefix and suffix information.
949
+ def undecorated_table_name(class_name = base_class.name)
950
+ table_name = class_name.to_s.demodulize.underscore
951
+ table_name = table_name.pluralize if pluralize_table_names
952
+ table_name
953
+ end
954
+
955
+ # Computes and returns a table name according to default conventions.
956
+ def compute_table_name
957
+ base = base_class
958
+ if self == base
959
+ # Nested classes are prefixed with singular parent table name.
960
+ if parent < ActiveRecord::Base && !parent.abstract_class?
961
+ contained = parent.table_name
962
+ contained = contained.singularize if parent.pluralize_table_names
963
+ contained += '_'
964
+ end
965
+ "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
966
+ else
967
+ # STI subclasses always use their superclass' table.
968
+ base.table_name
969
+ end
970
+ end
971
+
972
+ # Enables dynamic finders like <tt>User.find_by_user_name(user_name)</tt> and
973
+ # <tt>User.scoped_by_user_name(user_name). Refer to Dynamic attribute-based finders
974
+ # section at the top of this file for more detailed information.
975
+ #
976
+ # It's even possible to use all the additional parameters to +find+. For example, the
977
+ # full interface for +find_all_by_amount+ is actually <tt>find_all_by_amount(amount, options)</tt>.
978
+ #
979
+ # Each dynamic finder using <tt>scoped_by_*</tt> is also defined in the class after it
980
+ # is first invoked, so that future attempts to use it do not run through method_missing.
981
+ def method_missing(method_id, *arguments, &block)
982
+ if match = DynamicFinderMatch.match(method_id)
983
+ attribute_names = match.attribute_names
984
+ super unless all_attributes_exists?(attribute_names)
985
+ if match.finder?
986
+ options = arguments.extract_options!
987
+ relation = options.any? ? construct_finder_arel(options, current_scoped_methods) : scoped
988
+ relation.send :find_by_attributes, match, attribute_names, *arguments
989
+ elsif match.instantiator?
990
+ scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
991
+ end
992
+ elsif match = DynamicScopeMatch.match(method_id)
993
+ attribute_names = match.attribute_names
994
+ super unless all_attributes_exists?(attribute_names)
995
+ if match.scope?
996
+ self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
997
+ def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
998
+ options = args.extract_options! # options = args.extract_options!
999
+ attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments(
1000
+ [:#{attribute_names.join(',:')}], args # [:user_name, :password], args
1001
+ ) # )
1002
+ #
1003
+ scoped(:conditions => attributes) # scoped(:conditions => attributes)
1004
+ end # end
1005
+ METHOD
1006
+ send(method_id, *arguments)
1007
+ end
1008
+ else
1009
+ super
1010
+ end
1011
+ end
1012
+
1013
+ def construct_attributes_from_arguments(attribute_names, arguments)
1014
+ attributes = {}
1015
+ attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }
1016
+ attributes
1017
+ end
1018
+
1019
+ # Similar in purpose to +expand_hash_conditions_for_aggregates+.
1020
+ def expand_attribute_names_for_aggregates(attribute_names)
1021
+ expanded_attribute_names = []
1022
+ attribute_names.each do |attribute_name|
1023
+ unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
1024
+ aggregate_mapping(aggregation).each do |field_attr, aggregate_attr|
1025
+ expanded_attribute_names << field_attr
1026
+ end
1027
+ else
1028
+ expanded_attribute_names << attribute_name
1029
+ end
1030
+ end
1031
+ expanded_attribute_names
1032
+ end
1033
+
1034
+ def all_attributes_exists?(attribute_names)
1035
+ expand_attribute_names_for_aggregates(attribute_names).all? { |name|
1036
+ column_methods_hash.include?(name.to_sym)
1037
+ }
1038
+ end
1039
+
1040
+ protected
1041
+ # with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be
1042
+ # <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while
1043
+ # <tt>:create</tt> parameters are an attributes hash.
1044
+ #
1045
+ # class Article < ActiveRecord::Base
1046
+ # def self.create_with_scope
1047
+ # with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
1048
+ # find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
1049
+ # a = create(1)
1050
+ # a.blog_id # => 1
1051
+ # end
1052
+ # end
1053
+ # end
1054
+ #
1055
+ # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
1056
+ # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
1057
+ #
1058
+ # <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
1059
+ # problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
1060
+ # array of strings format for your joins.
1061
+ #
1062
+ # class Article < ActiveRecord::Base
1063
+ # def self.find_with_scope
1064
+ # with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
1065
+ # with_scope(:find => limit(10)) do
1066
+ # all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
1067
+ # end
1068
+ # with_scope(:find => where(:author_id => 3)) do
1069
+ # all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
1070
+ # end
1071
+ # end
1072
+ # end
1073
+ # end
1074
+ #
1075
+ # You can ignore any previous scopings by using the <tt>with_exclusive_scope</tt> method.
1076
+ #
1077
+ # class Article < ActiveRecord::Base
1078
+ # def self.find_with_exclusive_scope
1079
+ # with_scope(:find => where(:blog_id => 1).limit(1)) do
1080
+ # with_exclusive_scope(:find => limit(10)) do
1081
+ # all # => SELECT * from articles LIMIT 10
1082
+ # end
1083
+ # end
1084
+ # end
1085
+ # end
1086
+ #
1087
+ # *Note*: the +:find+ scope also has effect on update and deletion methods, like +update_all+ and +delete_all+.
1088
+ def with_scope(method_scoping = {}, action = :merge, &block)
1089
+ method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
1090
+
1091
+ if method_scoping.is_a?(Hash)
1092
+ # Dup first and second level of hash (method and params).
1093
+ method_scoping = method_scoping.dup
1094
+ method_scoping.each do |method, params|
1095
+ method_scoping[method] = params.dup unless params == true
1096
+ end
1097
+
1098
+ method_scoping.assert_valid_keys([ :find, :create ])
1099
+ relation = construct_finder_arel(method_scoping[:find] || {})
1100
+
1101
+ if current_scoped_methods && current_scoped_methods.create_with_value && method_scoping[:create]
1102
+ scope_for_create = if action == :merge
1103
+ current_scoped_methods.create_with_value.merge(method_scoping[:create])
1104
+ else
1105
+ method_scoping[:create]
1106
+ end
1107
+
1108
+ relation = relation.create_with(scope_for_create)
1109
+ else
1110
+ scope_for_create = method_scoping[:create]
1111
+ scope_for_create ||= current_scoped_methods.create_with_value if current_scoped_methods
1112
+ relation = relation.create_with(scope_for_create) if scope_for_create
1113
+ end
1114
+
1115
+ method_scoping = relation
1116
+ end
1117
+
1118
+ method_scoping = current_scoped_methods.merge(method_scoping) if current_scoped_methods && action == :merge
1119
+
1120
+ self.scoped_methods << method_scoping
1121
+ begin
1122
+ yield
1123
+ ensure
1124
+ self.scoped_methods.pop
1125
+ end
1126
+ end
1127
+
1128
+ # Works like with_scope, but discards any nested properties.
1129
+ def with_exclusive_scope(method_scoping = {}, &block)
1130
+ if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) }
1131
+ raise ArgumentError, <<-MSG
1132
+ 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:
1133
+
1134
+ User.unscoped.where(:active => true)
1135
+
1136
+ Or call unscoped with a block:
1137
+
1138
+ User.unscoped do
1139
+ User.where(:active => true).all
1140
+ end
1141
+
1142
+ MSG
1143
+ end
1144
+ with_scope(method_scoping, :overwrite, &block)
1145
+ end
1146
+
1147
+ # Sets the default options for the model. The format of the
1148
+ # <tt>options</tt> argument is the same as in find.
1149
+ #
1150
+ # class Person < ActiveRecord::Base
1151
+ # default_scope order('last_name, first_name')
1152
+ # end
1153
+ #
1154
+ # <tt>default_scope</tt> is also applied while creating/building a record. It is not
1155
+ # applied while updating a record.
1156
+ #
1157
+ # class Article < ActiveRecord::Base
1158
+ # default_scope where(:published => true)
1159
+ # end
1160
+ #
1161
+ # Article.new.published # => true
1162
+ # Article.create.published # => true
1163
+ def default_scope(options = {})
1164
+ reset_scoped_methods
1165
+ self.default_scoping << construct_finder_arel(options, default_scoping.pop)
1166
+ end
1167
+
1168
+ def current_scoped_methods #:nodoc:
1169
+ scoped_methods.last
1170
+ end
1171
+
1172
+ def reset_scoped_methods #:nodoc:
1173
+ Thread.current[:"#{self}_scoped_methods"] = nil
1174
+ end
1175
+
1176
+ # Returns the class type of the record using the current module as a prefix. So descendants of
1177
+ # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
1178
+ def compute_type(type_name)
1179
+ if type_name.match(/^::/)
1180
+ # If the type is prefixed with a scope operator then we assume that
1181
+ # the type_name is an absolute reference.
1182
+ ActiveSupport::Dependencies.constantize(type_name)
1183
+ else
1184
+ # Build a list of candidates to search for
1185
+ candidates = []
1186
+ name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
1187
+ candidates << type_name
1188
+
1189
+ candidates.each do |candidate|
1190
+ begin
1191
+ constant = ActiveSupport::Dependencies.constantize(candidate)
1192
+ return constant if candidate == constant.to_s
1193
+ rescue NameError => e
1194
+ # We don't want to swallow NoMethodError < NameError errors
1195
+ raise e unless e.instance_of?(NameError)
1196
+ rescue ArgumentError
1197
+ end
1198
+ end
1199
+
1200
+ raise NameError, "uninitialized constant #{candidates.first}"
1201
+ end
1202
+ end
1203
+
1204
+ # Returns the class descending directly from ActiveRecord::Base or an
1205
+ # abstract class, if any, in the inheritance hierarchy.
1206
+ def class_of_active_record_descendant(klass)
1207
+ if klass.superclass == Base || klass.superclass.abstract_class?
1208
+ klass
1209
+ elsif klass.superclass.nil?
1210
+ raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
1211
+ else
1212
+ class_of_active_record_descendant(klass.superclass)
1213
+ end
1214
+ end
1215
+
1216
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
1217
+ # them into a valid SQL fragment for a WHERE clause.
1218
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
1219
+ # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
1220
+ # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
1221
+ def sanitize_sql_for_conditions(condition, table_name = self.table_name)
1222
+ return nil if condition.blank?
1223
+
1224
+ case condition
1225
+ when Array; sanitize_sql_array(condition)
1226
+ when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
1227
+ else condition
1228
+ end
1229
+ end
1230
+ alias_method :sanitize_sql, :sanitize_sql_for_conditions
1231
+
1232
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
1233
+ # them into a valid SQL fragment for a SET clause.
1234
+ # { :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
1235
+ def sanitize_sql_for_assignment(assignments)
1236
+ case assignments
1237
+ when Array; sanitize_sql_array(assignments)
1238
+ when Hash; sanitize_sql_hash_for_assignment(assignments)
1239
+ else assignments
1240
+ end
1241
+ end
1242
+
1243
+ def aggregate_mapping(reflection)
1244
+ mapping = reflection.options[:mapping] || [reflection.name, reflection.name]
1245
+ mapping.first.is_a?(Array) ? mapping : [mapping]
1246
+ end
1247
+
1248
+ # Accepts a hash of SQL conditions and replaces those attributes
1249
+ # that correspond to a +composed_of+ relationship with their expanded
1250
+ # aggregate attribute values.
1251
+ # Given:
1252
+ # class Person < ActiveRecord::Base
1253
+ # composed_of :address, :class_name => "Address",
1254
+ # :mapping => [%w(address_street street), %w(address_city city)]
1255
+ # end
1256
+ # Then:
1257
+ # { :address => Address.new("813 abc st.", "chicago") }
1258
+ # # => { :address_street => "813 abc st.", :address_city => "chicago" }
1259
+ def expand_hash_conditions_for_aggregates(attrs)
1260
+ expanded_attrs = {}
1261
+ attrs.each do |attr, value|
1262
+ unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
1263
+ mapping = aggregate_mapping(aggregation)
1264
+ mapping.each do |field_attr, aggregate_attr|
1265
+ if mapping.size == 1 && !value.respond_to?(aggregate_attr)
1266
+ expanded_attrs[field_attr] = value
1267
+ else
1268
+ expanded_attrs[field_attr] = value.send(aggregate_attr)
1269
+ end
1270
+ end
1271
+ else
1272
+ expanded_attrs[attr] = value
1273
+ end
1274
+ end
1275
+ expanded_attrs
1276
+ end
1277
+
1278
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
1279
+ # { :name => "foo'bar", :group_id => 4 }
1280
+ # # => "name='foo''bar' and group_id= 4"
1281
+ # { :status => nil, :group_id => [1,2,3] }
1282
+ # # => "status IS NULL and group_id IN (1,2,3)"
1283
+ # { :age => 13..18 }
1284
+ # # => "age BETWEEN 13 AND 18"
1285
+ # { 'other_records.id' => 7 }
1286
+ # # => "`other_records`.`id` = 7"
1287
+ # { :other_records => { :id => 7 } }
1288
+ # # => "`other_records`.`id` = 7"
1289
+ # And for value objects on a composed_of relationship:
1290
+ # { :address => Address.new("123 abc st.", "chicago") }
1291
+ # # => "address_street='123 abc st.' and address_city='chicago'"
1292
+ def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
1293
+ attrs = expand_hash_conditions_for_aggregates(attrs)
1294
+
1295
+ table = Arel::Table.new(self.table_name, :engine => arel_engine, :as => default_table_name)
1296
+ builder = PredicateBuilder.new(arel_engine)
1297
+ builder.build_from_hash(attrs, table).map{ |b| b.to_sql }.join(' AND ')
1298
+ end
1299
+ alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
1300
+
1301
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
1302
+ # { :status => nil, :group_id => 1 }
1303
+ # # => "status = NULL , group_id = 1"
1304
+ def sanitize_sql_hash_for_assignment(attrs)
1305
+ attrs.map do |attr, value|
1306
+ "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
1307
+ end.join(', ')
1308
+ end
1309
+
1310
+ # Accepts an array of conditions. The array has each value
1311
+ # sanitized and interpolated into the SQL statement.
1312
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
1313
+ def sanitize_sql_array(ary)
1314
+ statement, *values = ary
1315
+ if values.first.is_a?(Hash) and statement =~ /:\w+/
1316
+ replace_named_bind_variables(statement, values.first)
1317
+ elsif statement.include?('?')
1318
+ replace_bind_variables(statement, values)
1319
+ elsif statement.blank?
1320
+ statement
1321
+ else
1322
+ statement % values.collect { |value| connection.quote_string(value.to_s) }
1323
+ end
1324
+ end
1325
+
1326
+ alias_method :sanitize_conditions, :sanitize_sql
1327
+
1328
+ def replace_bind_variables(statement, values) #:nodoc:
1329
+ raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
1330
+ bound = values.dup
1331
+ c = connection
1332
+ statement.gsub('?') { quote_bound_value(bound.shift, c) }
1333
+ end
1334
+
1335
+ def replace_named_bind_variables(statement, bind_vars) #:nodoc:
1336
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do
1337
+ if $1 == ':' # skip postgresql casts
1338
+ $& # return the whole match
1339
+ elsif bind_vars.include?(match = $2.to_sym)
1340
+ quote_bound_value(bind_vars[match])
1341
+ else
1342
+ raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
1343
+ end
1344
+ end
1345
+ end
1346
+
1347
+ def expand_range_bind_variables(bind_vars) #:nodoc:
1348
+ expanded = []
1349
+
1350
+ bind_vars.each do |var|
1351
+ next if var.is_a?(Hash)
1352
+
1353
+ if var.is_a?(Range)
1354
+ expanded << var.first
1355
+ expanded << var.last
1356
+ else
1357
+ expanded << var
1358
+ end
1359
+ end
1360
+
1361
+ expanded
1362
+ end
1363
+
1364
+ def quote_bound_value(value, c = connection) #:nodoc:
1365
+ if value.respond_to?(:map) && !value.acts_like?(:string)
1366
+ if value.respond_to?(:empty?) && value.empty?
1367
+ c.quote(nil)
1368
+ else
1369
+ value.map { |v| c.quote(v) }.join(',')
1370
+ end
1371
+ else
1372
+ c.quote(value)
1373
+ end
1374
+ end
1375
+
1376
+ def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
1377
+ unless expected == provided
1378
+ raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
1379
+ end
1380
+ end
1381
+
1382
+ def encode_quoted_value(value) #:nodoc:
1383
+ quoted_value = connection.quote(value)
1384
+ quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "
1385
+ quoted_value
1386
+ end
1387
+ end
1388
+
1389
+ public
1390
+ # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
1391
+ # attributes but not yet saved (pass a hash with key names matching the associated table column names).
1392
+ # In both instances, valid attribute keys are determined by the column names of the associated table --
1393
+ # hence you can't have attributes that aren't part of the table columns.
1394
+ def initialize(attributes = nil)
1395
+ @attributes = attributes_from_column_definition
1396
+ @attributes_cache = {}
1397
+ @new_record = true
1398
+ @readonly = false
1399
+ @destroyed = false
1400
+ @marked_for_destruction = false
1401
+ @previously_changed = {}
1402
+ @changed_attributes = {}
1403
+
1404
+ ensure_proper_type
1405
+
1406
+ populate_with_current_scope_attributes
1407
+ self.attributes = attributes unless attributes.nil?
1408
+
1409
+ result = yield self if block_given?
1410
+ _run_initialize_callbacks
1411
+ result
1412
+ end
1413
+
1414
+ # Cloned objects have no id assigned and are treated as new records. Note that this is a "shallow" clone
1415
+ # as it copies the object's attributes only, not its associations. The extent of a "deep" clone is
1416
+ # application specific and is therefore left to the application to implement according to its need.
1417
+ def initialize_copy(other)
1418
+ _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks)
1419
+ cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
1420
+ cloned_attributes.delete(self.class.primary_key)
1421
+
1422
+ @attributes = cloned_attributes
1423
+
1424
+ @changed_attributes = {}
1425
+ attributes_from_column_definition.each do |attr, orig_value|
1426
+ @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr])
1427
+ end
1428
+
1429
+ clear_aggregation_cache
1430
+ clear_association_cache
1431
+ @attributes_cache = {}
1432
+ @new_record = true
1433
+ ensure_proper_type
1434
+
1435
+ populate_with_current_scope_attributes
1436
+ end
1437
+
1438
+ # Initialize an empty model object from +coder+. +coder+ must contain
1439
+ # the attributes necessary for initializing an empty model object. For
1440
+ # example:
1441
+ #
1442
+ # class Post < ActiveRecord::Base
1443
+ # end
1444
+ #
1445
+ # post = Post.allocate
1446
+ # post.init_with('attributes' => { 'title' => 'hello world' })
1447
+ # post.title # => 'hello world'
1448
+ def init_with(coder)
1449
+ @attributes = coder['attributes']
1450
+ @attributes_cache, @previously_changed, @changed_attributes = {}, {}, {}
1451
+ @new_record = @readonly = @destroyed = @marked_for_destruction = false
1452
+ _run_find_callbacks
1453
+ _run_initialize_callbacks
1454
+ end
1455
+
1456
+ # Returns a String, which Action Pack uses for constructing an URL to this
1457
+ # object. The default implementation returns this record's id as a String,
1458
+ # or nil if this record's unsaved.
1459
+ #
1460
+ # For example, suppose that you have a User model, and that you have a
1461
+ # <tt>resources :users</tt> route. Normally, +user_path+ will
1462
+ # construct a path with the user object's 'id' in it:
1463
+ #
1464
+ # user = User.find_by_name('Phusion')
1465
+ # user_path(user) # => "/users/1"
1466
+ #
1467
+ # You can override +to_param+ in your model to make +user_path+ construct
1468
+ # a path using the user's name instead of the user's id:
1469
+ #
1470
+ # class User < ActiveRecord::Base
1471
+ # def to_param # overridden
1472
+ # name
1473
+ # end
1474
+ # end
1475
+ #
1476
+ # user = User.find_by_name('Phusion')
1477
+ # user_path(user) # => "/users/Phusion"
1478
+ def to_param
1479
+ # We can't use alias_method here, because method 'id' optimizes itself on the fly.
1480
+ id && id.to_s # Be sure to stringify the id for routes
1481
+ end
1482
+
1483
+ # Returns a cache key that can be used to identify this record.
1484
+ #
1485
+ # ==== Examples
1486
+ #
1487
+ # Product.new.cache_key # => "products/new"
1488
+ # Product.find(5).cache_key # => "products/5" (updated_at not available)
1489
+ # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
1490
+ def cache_key
1491
+ case
1492
+ when new_record?
1493
+ "#{self.class.model_name.cache_key}/new"
1494
+ when timestamp = self[:updated_at]
1495
+ "#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}"
1496
+ else
1497
+ "#{self.class.model_name.cache_key}/#{id}"
1498
+ end
1499
+ end
1500
+
1501
+ def quoted_id #:nodoc:
1502
+ quote_value(id, column_for_attribute(self.class.primary_key))
1503
+ end
1504
+
1505
+ # Returns true if the given attribute is in the attributes hash
1506
+ def has_attribute?(attr_name)
1507
+ @attributes.has_key?(attr_name.to_s)
1508
+ end
1509
+
1510
+ # Returns an array of names for the attributes available on this object sorted alphabetically.
1511
+ def attribute_names
1512
+ @attributes.keys.sort
1513
+ end
1514
+
1515
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
1516
+ # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
1517
+ # (Alias for the protected read_attribute method).
1518
+ def [](attr_name)
1519
+ read_attribute(attr_name)
1520
+ end
1521
+
1522
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
1523
+ # (Alias for the protected write_attribute method).
1524
+ def []=(attr_name, value)
1525
+ write_attribute(attr_name, value)
1526
+ end
1527
+
1528
+ # Allows you to set all the attributes at once by passing in a hash with keys
1529
+ # matching the attribute names (which again matches the column names).
1530
+ #
1531
+ # If +guard_protected_attributes+ is true (the default), then sensitive
1532
+ # attributes can be protected from this form of mass-assignment by using
1533
+ # the +attr_protected+ macro. Or you can alternatively specify which
1534
+ # attributes *can* be accessed with the +attr_accessible+ macro. Then all the
1535
+ # attributes not included in that won't be allowed to be mass-assigned.
1536
+ #
1537
+ # class User < ActiveRecord::Base
1538
+ # attr_protected :is_admin
1539
+ # end
1540
+ #
1541
+ # user = User.new
1542
+ # user.attributes = { :username => 'Phusion', :is_admin => true }
1543
+ # user.username # => "Phusion"
1544
+ # user.is_admin? # => false
1545
+ #
1546
+ # user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false)
1547
+ # user.is_admin? # => true
1548
+ def attributes=(new_attributes, guard_protected_attributes = true)
1549
+ return unless new_attributes.is_a?(Hash)
1550
+ attributes = new_attributes.stringify_keys
1551
+
1552
+ multi_parameter_attributes = []
1553
+ attributes = sanitize_for_mass_assignment(attributes) if guard_protected_attributes
1554
+
1555
+ attributes.each do |k, v|
1556
+ if k.include?("(")
1557
+ multi_parameter_attributes << [ k, v ]
1558
+ else
1559
+ respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
1560
+ end
1561
+ end
1562
+
1563
+ assign_multiparameter_attributes(multi_parameter_attributes)
1564
+ end
1565
+
1566
+ # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
1567
+ def attributes
1568
+ attrs = {}
1569
+ attribute_names.each { |name| attrs[name] = read_attribute(name) }
1570
+ attrs
1571
+ end
1572
+
1573
+ # Returns an <tt>#inspect</tt>-like string for the value of the
1574
+ # attribute +attr_name+. String attributes are elided after 50
1575
+ # characters, and Date and Time attributes are returned in the
1576
+ # <tt>:db</tt> format. Other attributes return the value of
1577
+ # <tt>#inspect</tt> without modification.
1578
+ #
1579
+ # person = Person.create!(:name => "David Heinemeier Hansson " * 3)
1580
+ #
1581
+ # person.attribute_for_inspect(:name)
1582
+ # # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
1583
+ #
1584
+ # person.attribute_for_inspect(:created_at)
1585
+ # # => '"2009-01-12 04:48:57"'
1586
+ def attribute_for_inspect(attr_name)
1587
+ value = read_attribute(attr_name)
1588
+
1589
+ if value.is_a?(String) && value.length > 50
1590
+ "#{value[0..50]}...".inspect
1591
+ elsif value.is_a?(Date) || value.is_a?(Time)
1592
+ %("#{value.to_s(:db)}")
1593
+ else
1594
+ value.inspect
1595
+ end
1596
+ end
1597
+
1598
+ # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
1599
+ # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
1600
+ def attribute_present?(attribute)
1601
+ value = read_attribute(attribute)
1602
+ !value.blank?
1603
+ end
1604
+
1605
+ # Returns the column object for the named attribute.
1606
+ def column_for_attribute(name)
1607
+ self.class.columns_hash[name.to_s]
1608
+ end
1609
+
1610
+ # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
1611
+ # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
1612
+ #
1613
+ # Note that new records are different from any other record by definition, unless the
1614
+ # other record is the receiver itself. Besides, if you fetch existing records with
1615
+ # +select+ and leave the ID out, you're on your own, this predicate will return false.
1616
+ #
1617
+ # Note also that destroying a record preserves its ID in the model instance, so deleted
1618
+ # models are still comparable.
1619
+ def ==(comparison_object)
1620
+ comparison_object.equal?(self) ||
1621
+ (comparison_object.instance_of?(self.class) &&
1622
+ comparison_object.id == id && !comparison_object.new_record?)
1623
+ end
1624
+
1625
+ # Delegates to ==
1626
+ def eql?(comparison_object)
1627
+ self == (comparison_object)
1628
+ end
1629
+
1630
+ # Delegates to id in order to allow two records of the same type and id to work with something like:
1631
+ # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
1632
+ def hash
1633
+ id.hash
1634
+ end
1635
+
1636
+ # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
1637
+ def freeze
1638
+ @attributes.freeze; self
1639
+ end
1640
+
1641
+ # Returns +true+ if the attributes hash has been frozen.
1642
+ def frozen?
1643
+ @attributes.frozen?
1644
+ end
1645
+
1646
+ # Returns duplicated record with unfreezed attributes.
1647
+ def dup
1648
+ obj = super
1649
+ obj.instance_variable_set('@attributes', @attributes.dup)
1650
+ obj
1651
+ end
1652
+
1653
+ # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
1654
+ # attributes will be marked as read only since they cannot be saved.
1655
+ def readonly?
1656
+ @readonly
1657
+ end
1658
+
1659
+ # Marks this record as read only.
1660
+ def readonly!
1661
+ @readonly = true
1662
+ end
1663
+
1664
+ # Returns the contents of the record as a nicely formatted string.
1665
+ def inspect
1666
+ attributes_as_nice_string = self.class.column_names.collect { |name|
1667
+ if has_attribute?(name) || new_record?
1668
+ "#{name}: #{attribute_for_inspect(name)}"
1669
+ end
1670
+ }.compact.join(", ")
1671
+ "#<#{self.class} #{attributes_as_nice_string}>"
1672
+ end
1673
+
1674
+ protected
1675
+ def clone_attributes(reader_method = :read_attribute, attributes = {})
1676
+ attribute_names.each do |name|
1677
+ attributes[name] = clone_attribute_value(reader_method, name)
1678
+ end
1679
+ attributes
1680
+ end
1681
+
1682
+ def clone_attribute_value(reader_method, attribute_name)
1683
+ value = send(reader_method, attribute_name)
1684
+ value.duplicable? ? value.clone : value
1685
+ rescue TypeError, NoMethodError
1686
+ value
1687
+ end
1688
+
1689
+ private
1690
+
1691
+ # Sets the attribute used for single table inheritance to this class name if this is not the
1692
+ # ActiveRecord::Base descendant.
1693
+ # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
1694
+ # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
1695
+ # No such attribute would be set for objects of the Message class in that example.
1696
+ def ensure_proper_type
1697
+ unless self.class.descends_from_active_record?
1698
+ write_attribute(self.class.inheritance_column, self.class.sti_name)
1699
+ end
1700
+ end
1701
+
1702
+ # The primary key and inheritance column can never be set by mass-assignment for security reasons.
1703
+ def self.attributes_protected_by_default
1704
+ default = [ primary_key, inheritance_column ]
1705
+ default << 'id' unless primary_key.eql? 'id'
1706
+ default
1707
+ end
1708
+
1709
+ # Returns a copy of the attributes hash where all the values have been safely quoted for use in
1710
+ # an Arel insert/update method.
1711
+ def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
1712
+ attrs = {}
1713
+ attribute_names.each do |name|
1714
+ if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
1715
+
1716
+ if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name))
1717
+ value = read_attribute(name)
1718
+
1719
+ if !value.nil? && self.class.serialized_attributes.key?(name)
1720
+ value = YAML.dump value
1721
+ end
1722
+ attrs[self.class.arel_table[name]] = value
1723
+ end
1724
+ end
1725
+ end
1726
+ attrs
1727
+ end
1728
+
1729
+ # Quote strings appropriately for SQL statements.
1730
+ def quote_value(value, column = nil)
1731
+ self.class.connection.quote(value, column)
1732
+ end
1733
+
1734
+ def interpolate_and_sanitize_sql(sql, record = nil, sanitize_klass = self.class)
1735
+ sanitized = sanitize_klass.send(:sanitize_sql, sql)
1736
+ interpolate_sanitized_sql(sanitized, record, sanitize_klass)
1737
+ end
1738
+
1739
+ def interpolate_sanitized_sql(sanitized, record = nil, sanitize_klass = self.class)
1740
+ if sanitized =~ /\#\{.*\}/
1741
+ ActiveSupport::Deprecation.warn(
1742
+ 'String-based interpolation of association conditions is deprecated. Please use a ' \
1743
+ 'proc instead. So, for example, has_many :older_friends, :conditions => \'age > #{age}\' ' \
1744
+ 'should be changed to has_many :older_friends, :conditions => proc { "age > #{age}" }.'
1745
+ )
1746
+ instance_eval("%@#{sanitized.gsub('@', '\@')}@", __FILE__, __LINE__)
1747
+ elsif sanitized.respond_to?(:to_proc)
1748
+ sanitize_klass.send(:sanitize_sql, instance_exec(record, &sanitized))
1749
+ else
1750
+ sanitized
1751
+ end
1752
+ end
1753
+
1754
+ # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
1755
+ # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
1756
+ # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
1757
+ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
1758
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum,
1759
+ # f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the
1760
+ # attribute will be set to nil.
1761
+ def assign_multiparameter_attributes(pairs)
1762
+ execute_callstack_for_multiparameter_attributes(
1763
+ extract_callstack_for_multiparameter_attributes(pairs)
1764
+ )
1765
+ end
1766
+
1767
+ def instantiate_time_object(name, values)
1768
+ if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
1769
+ Time.zone.local(*values)
1770
+ else
1771
+ Time.time_with_datetime_fallback(@@default_timezone, *values)
1772
+ end
1773
+ end
1774
+
1775
+ def execute_callstack_for_multiparameter_attributes(callstack)
1776
+ errors = []
1777
+ callstack.each do |name, values_with_empty_parameters|
1778
+ begin
1779
+ klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
1780
+ # in order to allow a date to be set without a year, we must keep the empty values.
1781
+ # Otherwise, we wouldn't be able to distinguish it from a date with an empty day.
1782
+ values = values_with_empty_parameters.reject { |v| v.nil? }
1783
+
1784
+ if values.empty?
1785
+ send(name + "=", nil)
1786
+ else
1787
+
1788
+ value = if Time == klass
1789
+ instantiate_time_object(name, values)
1790
+ elsif Date == klass
1791
+ begin
1792
+ values = values_with_empty_parameters.collect do |v| v.nil? ? 1 : v end
1793
+ Date.new(*values)
1794
+ rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
1795
+ 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
1796
+ end
1797
+ else
1798
+ klass.new(*values)
1799
+ end
1800
+
1801
+ send(name + "=", value)
1802
+ end
1803
+ rescue => ex
1804
+ errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
1805
+ end
1806
+ end
1807
+ unless errors.empty?
1808
+ raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
1809
+ end
1810
+ end
1811
+
1812
+ def extract_callstack_for_multiparameter_attributes(pairs)
1813
+ attributes = { }
1814
+
1815
+ for pair in pairs
1816
+ multiparameter_name, value = pair
1817
+ attribute_name = multiparameter_name.split("(").first
1818
+ attributes[attribute_name] = [] unless attributes.include?(attribute_name)
1819
+
1820
+ parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
1821
+ attributes[attribute_name] << [ find_parameter_position(multiparameter_name), parameter_value ]
1822
+ end
1823
+
1824
+ attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
1825
+ end
1826
+
1827
+ def type_cast_attribute_value(multiparameter_name, value)
1828
+ multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
1829
+ end
1830
+
1831
+ def find_parameter_position(multiparameter_name)
1832
+ multiparameter_name.scan(/\(([0-9]*).*\)/).first.first
1833
+ end
1834
+
1835
+ # Returns a comma-separated pair list, like "key1 = val1, key2 = val2".
1836
+ def comma_pair_list(hash)
1837
+ hash.map { |k,v| "#{k} = #{v}" }.join(", ")
1838
+ end
1839
+
1840
+ def quote_columns(quoter, hash)
1841
+ Hash[hash.map { |name, value| [quoter.quote_column_name(name), value] }]
1842
+ end
1843
+
1844
+ def quoted_comma_pair_list(quoter, hash)
1845
+ comma_pair_list(quote_columns(quoter, hash))
1846
+ end
1847
+
1848
+ def convert_number_column_value(value)
1849
+ if value == false
1850
+ 0
1851
+ elsif value == true
1852
+ 1
1853
+ elsif value.is_a?(String) && value.blank?
1854
+ nil
1855
+ else
1856
+ value
1857
+ end
1858
+ end
1859
+
1860
+ def object_from_yaml(string)
1861
+ return string unless string.is_a?(String) && string =~ /^---/
1862
+ YAML::load(string) rescue string
1863
+ end
1864
+
1865
+ def populate_with_current_scope_attributes
1866
+ if scope = self.class.send(:current_scoped_methods)
1867
+ create_with = scope.scope_for_create
1868
+ create_with.each { |att,value| self.respond_to?(:"#{att}=") && self.send("#{att}=", value) } if create_with
1869
+ end
1870
+ end
1871
+ end
1872
+
1873
+ Base.class_eval do
1874
+ include ActiveRecord::Persistence
1875
+ extend ActiveModel::Naming
1876
+ extend QueryCache::ClassMethods
1877
+ extend ActiveSupport::Benchmarkable
1878
+ extend ActiveSupport::DescendantsTracker
1879
+
1880
+ include ActiveModel::Conversion
1881
+ include Validations
1882
+ extend CounterCache
1883
+ include Locking::Optimistic, Locking::Pessimistic
1884
+ include AttributeMethods
1885
+ include AttributeMethods::Read, AttributeMethods::Write, AttributeMethods::BeforeTypeCast, AttributeMethods::Query
1886
+ include AttributeMethods::PrimaryKey
1887
+ include AttributeMethods::TimeZoneConversion
1888
+ include AttributeMethods::Dirty
1889
+ include ActiveModel::MassAssignmentSecurity
1890
+ include Callbacks, ActiveModel::Observing, Timestamp
1891
+ include Associations, AssociationPreload, NamedScope
1892
+
1893
+ # AutosaveAssociation needs to be included before Transactions, because we want
1894
+ # #save_with_autosave_associations to be wrapped inside a transaction.
1895
+ include AutosaveAssociation, NestedAttributes
1896
+ include Aggregations, Transactions, Reflection, Serialization
1897
+
1898
+ NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner)
1899
+ end
1900
+ end
1901
+
1902
+ # TODO: Remove this and make it work with LAZY flag
1903
+ require 'active_record/connection_adapters/abstract_adapter'
1904
+ ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)