activerecord 3.1.12 → 3.2.22.1

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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +804 -338
  3. data/README.rdoc +3 -3
  4. data/examples/performance.rb +20 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +3 -6
  7. data/lib/active_record/associations/association.rb +13 -45
  8. data/lib/active_record/associations/association_scope.rb +3 -15
  9. data/lib/active_record/associations/belongs_to_association.rb +1 -1
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +2 -1
  11. data/lib/active_record/associations/builder/association.rb +6 -4
  12. data/lib/active_record/associations/builder/belongs_to.rb +7 -4
  13. data/lib/active_record/associations/builder/collection_association.rb +2 -2
  14. data/lib/active_record/associations/builder/has_many.rb +4 -4
  15. data/lib/active_record/associations/builder/has_one.rb +5 -6
  16. data/lib/active_record/associations/builder/singular_association.rb +3 -16
  17. data/lib/active_record/associations/collection_association.rb +65 -32
  18. data/lib/active_record/associations/collection_proxy.rb +8 -41
  19. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +1 -0
  20. data/lib/active_record/associations/has_many_association.rb +11 -7
  21. data/lib/active_record/associations/has_many_through_association.rb +19 -9
  22. data/lib/active_record/associations/has_one_association.rb +23 -13
  23. data/lib/active_record/associations/join_dependency/join_association.rb +6 -1
  24. data/lib/active_record/associations/join_dependency.rb +3 -3
  25. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  26. data/lib/active_record/associations/preloader.rb +14 -10
  27. data/lib/active_record/associations/through_association.rb +8 -4
  28. data/lib/active_record/associations.rb +92 -76
  29. data/lib/active_record/attribute_assignment.rb +221 -0
  30. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  31. data/lib/active_record/attribute_methods/dirty.rb +21 -11
  32. data/lib/active_record/attribute_methods/primary_key.rb +62 -25
  33. data/lib/active_record/attribute_methods/read.rb +73 -83
  34. data/lib/active_record/attribute_methods/serialization.rb +120 -0
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
  36. data/lib/active_record/attribute_methods/write.rb +32 -6
  37. data/lib/active_record/attribute_methods.rb +231 -30
  38. data/lib/active_record/autosave_association.rb +44 -26
  39. data/lib/active_record/base.rb +227 -1708
  40. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +150 -148
  41. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +85 -29
  42. data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -34
  43. data/lib/active_record/connection_adapters/abstract/query_cache.rb +10 -2
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -4
  45. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +39 -28
  46. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -19
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +77 -42
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +676 -0
  49. data/lib/active_record/connection_adapters/column.rb +37 -11
  50. data/lib/active_record/connection_adapters/mysql2_adapter.rb +133 -581
  51. data/lib/active_record/connection_adapters/mysql_adapter.rb +136 -693
  52. data/lib/active_record/connection_adapters/postgresql_adapter.rb +209 -97
  53. data/lib/active_record/connection_adapters/schema_cache.rb +69 -0
  54. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
  55. data/lib/active_record/connection_adapters/sqlite_adapter.rb +62 -35
  56. data/lib/active_record/counter_cache.rb +9 -4
  57. data/lib/active_record/dynamic_finder_match.rb +12 -0
  58. data/lib/active_record/dynamic_matchers.rb +84 -0
  59. data/lib/active_record/errors.rb +11 -1
  60. data/lib/active_record/explain.rb +86 -0
  61. data/lib/active_record/explain_subscriber.rb +25 -0
  62. data/lib/active_record/fixtures/file.rb +65 -0
  63. data/lib/active_record/fixtures.rb +57 -86
  64. data/lib/active_record/identity_map.rb +3 -4
  65. data/lib/active_record/inheritance.rb +174 -0
  66. data/lib/active_record/integration.rb +60 -0
  67. data/lib/active_record/locking/optimistic.rb +33 -26
  68. data/lib/active_record/locking/pessimistic.rb +23 -1
  69. data/lib/active_record/log_subscriber.rb +8 -4
  70. data/lib/active_record/migration/command_recorder.rb +8 -8
  71. data/lib/active_record/migration.rb +68 -35
  72. data/lib/active_record/model_schema.rb +368 -0
  73. data/lib/active_record/nested_attributes.rb +60 -24
  74. data/lib/active_record/persistence.rb +57 -11
  75. data/lib/active_record/query_cache.rb +6 -6
  76. data/lib/active_record/querying.rb +58 -0
  77. data/lib/active_record/railtie.rb +37 -29
  78. data/lib/active_record/railties/controller_runtime.rb +3 -1
  79. data/lib/active_record/railties/databases.rake +213 -117
  80. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  81. data/lib/active_record/readonly_attributes.rb +26 -0
  82. data/lib/active_record/reflection.rb +7 -15
  83. data/lib/active_record/relation/batches.rb +7 -4
  84. data/lib/active_record/relation/calculations.rb +55 -16
  85. data/lib/active_record/relation/delegation.rb +49 -0
  86. data/lib/active_record/relation/finder_methods.rb +16 -11
  87. data/lib/active_record/relation/predicate_builder.rb +8 -6
  88. data/lib/active_record/relation/query_methods.rb +75 -9
  89. data/lib/active_record/relation/spawn_methods.rb +48 -7
  90. data/lib/active_record/relation.rb +78 -32
  91. data/lib/active_record/result.rb +10 -4
  92. data/lib/active_record/sanitization.rb +194 -0
  93. data/lib/active_record/schema_dumper.rb +12 -5
  94. data/lib/active_record/scoping/default.rb +142 -0
  95. data/lib/active_record/scoping/named.rb +200 -0
  96. data/lib/active_record/scoping.rb +152 -0
  97. data/lib/active_record/serialization.rb +1 -43
  98. data/lib/active_record/serializers/xml_serializer.rb +4 -45
  99. data/lib/active_record/session_store.rb +18 -16
  100. data/lib/active_record/store.rb +52 -0
  101. data/lib/active_record/test_case.rb +11 -7
  102. data/lib/active_record/timestamp.rb +17 -3
  103. data/lib/active_record/transactions.rb +27 -6
  104. data/lib/active_record/translation.rb +22 -0
  105. data/lib/active_record/validations/associated.rb +5 -4
  106. data/lib/active_record/validations/uniqueness.rb +8 -8
  107. data/lib/active_record/validations.rb +1 -1
  108. data/lib/active_record/version.rb +3 -3
  109. data/lib/active_record.rb +38 -3
  110. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  111. data/lib/rails/generators/active_record/migration/templates/migration.rb +12 -3
  112. data/lib/rails/generators/active_record/model/model_generator.rb +9 -1
  113. data/lib/rails/generators/active_record/model/templates/migration.rb +3 -5
  114. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  115. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
  116. metadata +49 -28
  117. data/lib/active_record/named_scope.rb +0 -200
@@ -0,0 +1,368 @@
1
+ require 'active_support/concern'
2
+
3
+ module ActiveRecord
4
+ module ModelSchema
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ ##
9
+ # :singleton-method:
10
+ # Accessor for the prefix type that will be prepended to every primary key column name.
11
+ # The options are :table_name and :table_name_with_underscore. If the first is specified,
12
+ # the Product class will look for "productid" instead of "id" as the primary column. If the
13
+ # latter is specified, the Product class will look for "product_id" instead of "id". Remember
14
+ # that this is a global setting for all Active Records.
15
+ cattr_accessor :primary_key_prefix_type, :instance_writer => false
16
+ self.primary_key_prefix_type = nil
17
+
18
+ ##
19
+ # :singleton-method:
20
+ # Accessor for the name of the prefix string to prepend to every table name. So if set
21
+ # to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
22
+ # etc. This is a convenient way of creating a namespace for tables in a shared database.
23
+ # By default, the prefix is the empty string.
24
+ #
25
+ # If you are organising your models within modules you can add a prefix to the models within
26
+ # a namespace by defining a singleton method in the parent module called table_name_prefix which
27
+ # returns your chosen prefix.
28
+ class_attribute :table_name_prefix, :instance_writer => false
29
+ self.table_name_prefix = ""
30
+
31
+ ##
32
+ # :singleton-method:
33
+ # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
34
+ # "people_basecamp"). By default, the suffix is the empty string.
35
+ class_attribute :table_name_suffix, :instance_writer => false
36
+ self.table_name_suffix = ""
37
+
38
+ ##
39
+ # :singleton-method:
40
+ # Indicates whether table names should be the pluralized versions of the corresponding class names.
41
+ # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
42
+ # See table_name for the full rules on table/class naming. This is true, by default.
43
+ class_attribute :pluralize_table_names, :instance_writer => false
44
+ self.pluralize_table_names = true
45
+ end
46
+
47
+ module ClassMethods
48
+ # Guesses the table name (in forced lower-case) based on the name of the class in the
49
+ # inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy
50
+ # looks like: Reply < Message < ActiveRecord::Base, then Message is used
51
+ # to guess the table name even when called on Reply. The rules used to do the guess
52
+ # are handled by the Inflector class in Active Support, which knows almost all common
53
+ # English inflections. You can add new inflections in config/initializers/inflections.rb.
54
+ #
55
+ # Nested classes are given table names prefixed by the singular form of
56
+ # the parent's table name. Enclosing modules are not considered.
57
+ #
58
+ # ==== Examples
59
+ #
60
+ # class Invoice < ActiveRecord::Base
61
+ # end
62
+ #
63
+ # file class table_name
64
+ # invoice.rb Invoice invoices
65
+ #
66
+ # class Invoice < ActiveRecord::Base
67
+ # class Lineitem < ActiveRecord::Base
68
+ # end
69
+ # end
70
+ #
71
+ # file class table_name
72
+ # invoice.rb Invoice::Lineitem invoice_lineitems
73
+ #
74
+ # module Invoice
75
+ # class Lineitem < ActiveRecord::Base
76
+ # end
77
+ # end
78
+ #
79
+ # file class table_name
80
+ # invoice/lineitem.rb Invoice::Lineitem lineitems
81
+ #
82
+ # Additionally, the class-level +table_name_prefix+ is prepended and the
83
+ # +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
84
+ # the table name guess for an Invoice class becomes "myapp_invoices".
85
+ # Invoice::Lineitem becomes "myapp_invoice_lineitems".
86
+ #
87
+ # You can also set your own table name explicitly:
88
+ #
89
+ # class Mouse < ActiveRecord::Base
90
+ # self.table_name = "mice"
91
+ # end
92
+ #
93
+ # Alternatively, you can override the table_name method to define your
94
+ # own computation. (Possibly using <tt>super</tt> to manipulate the default
95
+ # table name.) Example:
96
+ #
97
+ # class Post < ActiveRecord::Base
98
+ # def self.table_name
99
+ # "special_" + super
100
+ # end
101
+ # end
102
+ # Post.table_name # => "special_posts"
103
+ def table_name
104
+ reset_table_name unless defined?(@table_name)
105
+ @table_name
106
+ end
107
+
108
+ def original_table_name #:nodoc:
109
+ deprecated_original_property_getter :table_name
110
+ end
111
+
112
+ # Sets the table name explicitly. Example:
113
+ #
114
+ # class Project < ActiveRecord::Base
115
+ # self.table_name = "project"
116
+ # end
117
+ #
118
+ # You can also just define your own <tt>self.table_name</tt> method; see
119
+ # the documentation for ActiveRecord::Base#table_name.
120
+ def table_name=(value)
121
+ @original_table_name = @table_name if defined?(@table_name)
122
+ @table_name = value && value.to_s
123
+ @quoted_table_name = nil
124
+ @arel_table = nil
125
+ @relation = Relation.new(self, arel_table)
126
+ end
127
+
128
+ def set_table_name(value = nil, &block) #:nodoc:
129
+ deprecated_property_setter :table_name, value, block
130
+ @quoted_table_name = nil
131
+ @arel_table = nil
132
+ @relation = Relation.new(self, arel_table)
133
+ end
134
+
135
+ # Returns a quoted version of the table name, used to construct SQL statements.
136
+ def quoted_table_name
137
+ @quoted_table_name ||= connection.quote_table_name(table_name)
138
+ end
139
+
140
+ # Computes the table name, (re)sets it internally, and returns it.
141
+ def reset_table_name #:nodoc:
142
+ if abstract_class?
143
+ self.table_name = if superclass == Base || superclass.abstract_class?
144
+ nil
145
+ else
146
+ superclass.table_name
147
+ end
148
+ elsif superclass.abstract_class?
149
+ self.table_name = superclass.table_name || compute_table_name
150
+ else
151
+ self.table_name = compute_table_name
152
+ end
153
+ end
154
+
155
+ def full_table_name_prefix #:nodoc:
156
+ (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
157
+ end
158
+
159
+ # The name of the column containing the object's class when Single Table Inheritance is used
160
+ def inheritance_column
161
+ if self == Base
162
+ (@inheritance_column ||= nil) || 'type'
163
+ else
164
+ (@inheritance_column ||= nil) || superclass.inheritance_column
165
+ end
166
+ end
167
+
168
+ def original_inheritance_column #:nodoc:
169
+ deprecated_original_property_getter :inheritance_column
170
+ end
171
+
172
+ # Sets the value of inheritance_column
173
+ def inheritance_column=(value)
174
+ @original_inheritance_column = inheritance_column
175
+ @inheritance_column = value.to_s
176
+ @explicit_inheritance_column = true
177
+ end
178
+
179
+ def set_inheritance_column(value = nil, &block) #:nodoc:
180
+ deprecated_property_setter :inheritance_column, value, block
181
+ end
182
+
183
+ def sequence_name
184
+ if base_class == self
185
+ @sequence_name ||= reset_sequence_name
186
+ else
187
+ (@sequence_name ||= nil) || base_class.sequence_name
188
+ end
189
+ end
190
+
191
+ def original_sequence_name #:nodoc:
192
+ deprecated_original_property_getter :sequence_name
193
+ end
194
+
195
+ def reset_sequence_name #:nodoc:
196
+ self.sequence_name = connection.default_sequence_name(table_name, primary_key)
197
+ end
198
+
199
+ # Sets the name of the sequence to use when generating ids to the given
200
+ # value, or (if the value is nil or false) to the value returned by the
201
+ # given block. This is required for Oracle and is useful for any
202
+ # database which relies on sequences for primary key generation.
203
+ #
204
+ # If a sequence name is not explicitly set when using Oracle or Firebird,
205
+ # it will default to the commonly used pattern of: #{table_name}_seq
206
+ #
207
+ # If a sequence name is not explicitly set when using PostgreSQL, it
208
+ # will discover the sequence corresponding to your primary key for you.
209
+ #
210
+ # class Project < ActiveRecord::Base
211
+ # self.sequence_name = "projectseq" # default would have been "project_seq"
212
+ # end
213
+ def sequence_name=(value)
214
+ @original_sequence_name = @sequence_name if defined?(@sequence_name)
215
+ @sequence_name = value.to_s
216
+ end
217
+
218
+ def set_sequence_name(value = nil, &block) #:nodoc:
219
+ deprecated_property_setter :sequence_name, value, block
220
+ end
221
+
222
+ # Indicates whether the table associated with this class exists
223
+ def table_exists?
224
+ connection.schema_cache.table_exists?(table_name)
225
+ end
226
+
227
+ # Returns an array of column objects for the table associated with this class.
228
+ def columns
229
+ @columns ||= connection.schema_cache.columns[table_name].map do |col|
230
+ col = col.dup
231
+ col.primary = (col.name == primary_key)
232
+ col
233
+ end
234
+ end
235
+
236
+ # Returns a hash of column objects for the table associated with this class.
237
+ def columns_hash
238
+ @columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
239
+ end
240
+
241
+ # Returns a hash where the keys are column names and the values are
242
+ # default values when instantiating the AR object for this table.
243
+ def column_defaults
244
+ @column_defaults ||= Hash[columns.map { |c| [c.name, c.default] }]
245
+ end
246
+
247
+ # Returns an array of column names as strings.
248
+ def column_names
249
+ @column_names ||= columns.map { |column| column.name }
250
+ end
251
+
252
+ # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
253
+ # and columns used for single table inheritance have been removed.
254
+ def content_columns
255
+ @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
256
+ end
257
+
258
+ # 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
259
+ # 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
260
+ # is available.
261
+ def column_methods_hash #:nodoc:
262
+ @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
263
+ attr_name = attr.to_s
264
+ methods[attr.to_sym] = attr_name
265
+ methods["#{attr}=".to_sym] = attr_name
266
+ methods["#{attr}?".to_sym] = attr_name
267
+ methods["#{attr}_before_type_cast".to_sym] = attr_name
268
+ methods
269
+ end
270
+ end
271
+
272
+ # Resets all the cached information about columns, which will cause them
273
+ # to be reloaded on the next request.
274
+ #
275
+ # The most common usage pattern for this method is probably in a migration,
276
+ # when just after creating a table you want to populate it with some default
277
+ # values, eg:
278
+ #
279
+ # class CreateJobLevels < ActiveRecord::Migration
280
+ # def up
281
+ # create_table :job_levels do |t|
282
+ # t.integer :id
283
+ # t.string :name
284
+ #
285
+ # t.timestamps
286
+ # end
287
+ #
288
+ # JobLevel.reset_column_information
289
+ # %w{assistant executive manager director}.each do |type|
290
+ # JobLevel.create(:name => type)
291
+ # end
292
+ # end
293
+ #
294
+ # def down
295
+ # drop_table :job_levels
296
+ # end
297
+ # end
298
+ def reset_column_information
299
+ connection.clear_cache!
300
+ undefine_attribute_methods
301
+ connection.schema_cache.clear_table_cache!(table_name) if table_exists?
302
+
303
+ @column_names = @content_columns = @column_defaults = @columns = @columns_hash = nil
304
+ @dynamic_methods_hash = nil
305
+ @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
306
+ @arel_engine = @relation = nil
307
+ end
308
+
309
+ def clear_cache! # :nodoc:
310
+ connection.schema_cache.clear!
311
+ end
312
+
313
+ private
314
+
315
+ # Guesses the table name, but does not decorate it with prefix and suffix information.
316
+ def undecorated_table_name(class_name = base_class.name)
317
+ table_name = class_name.to_s.demodulize.underscore
318
+ table_name = table_name.pluralize if pluralize_table_names
319
+ table_name
320
+ end
321
+
322
+ # Computes and returns a table name according to default conventions.
323
+ def compute_table_name
324
+ base = base_class
325
+ if self == base
326
+ # Nested classes are prefixed with singular parent table name.
327
+ if parent < ActiveRecord::Base && !parent.abstract_class?
328
+ contained = parent.table_name
329
+ contained = contained.singularize if parent.pluralize_table_names
330
+ contained += '_'
331
+ end
332
+ "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
333
+ else
334
+ # STI subclasses always use their superclass' table.
335
+ base.table_name
336
+ end
337
+ end
338
+
339
+ def deprecated_property_setter(property, value, block)
340
+ if block
341
+ ActiveSupport::Deprecation.warn(
342
+ "Calling set_#{property} is deprecated. If you need to lazily evaluate " \
343
+ "the #{property}, define your own `self.#{property}` class method. You can use `super` " \
344
+ "to get the default #{property} where you would have called `original_#{property}`."
345
+ )
346
+
347
+ define_attr_method property, value, false, &block
348
+ else
349
+ ActiveSupport::Deprecation.warn(
350
+ "Calling set_#{property} is deprecated. Please use `self.#{property} = 'the_name'` instead."
351
+ )
352
+
353
+ define_attr_method property, value, false
354
+ end
355
+ end
356
+
357
+ def deprecated_original_property_getter(property)
358
+ ActiveSupport::Deprecation.warn("original_#{property} is deprecated. Define self.#{property} and call super instead.")
359
+
360
+ if !instance_variable_defined?("@original_#{property}") && respond_to?("reset_#{property}")
361
+ send("reset_#{property}")
362
+ else
363
+ instance_variable_get("@original_#{property}")
364
+ end
365
+ end
366
+ end
367
+ end
368
+ end
@@ -92,8 +92,9 @@ module ActiveRecord
92
92
  # accepts_nested_attributes_for :posts
93
93
  # end
94
94
  #
95
- # You can now set or update attributes on an associated post model through
96
- # the attribute hash.
95
+ # You can now set or update attributes on the associated posts through
96
+ # an attribute hash for a member: include the key +:posts_attributes+
97
+ # with an array of hashes of post attributes as a value.
97
98
  #
98
99
  # For each hash that does _not_ have an <tt>id</tt> key a new record will
99
100
  # be instantiated, unless the hash also contains a <tt>_destroy</tt> key
@@ -116,10 +117,10 @@ module ActiveRecord
116
117
  # hashes if they fail to pass your criteria. For example, the previous
117
118
  # example could be rewritten as:
118
119
  #
119
- # class Member < ActiveRecord::Base
120
- # has_many :posts
121
- # accepts_nested_attributes_for :posts, :reject_if => proc { |attributes| attributes['title'].blank? }
122
- # end
120
+ # class Member < ActiveRecord::Base
121
+ # has_many :posts
122
+ # accepts_nested_attributes_for :posts, :reject_if => proc { |attributes| attributes['title'].blank? }
123
+ # end
123
124
  #
124
125
  # params = { :member => {
125
126
  # :name => 'joe', :posts_attributes => [
@@ -136,19 +137,19 @@ module ActiveRecord
136
137
  #
137
138
  # Alternatively, :reject_if also accepts a symbol for using methods:
138
139
  #
139
- # class Member < ActiveRecord::Base
140
- # has_many :posts
141
- # accepts_nested_attributes_for :posts, :reject_if => :new_record?
142
- # end
140
+ # class Member < ActiveRecord::Base
141
+ # has_many :posts
142
+ # accepts_nested_attributes_for :posts, :reject_if => :new_record?
143
+ # end
143
144
  #
144
- # class Member < ActiveRecord::Base
145
- # has_many :posts
146
- # accepts_nested_attributes_for :posts, :reject_if => :reject_posts
145
+ # class Member < ActiveRecord::Base
146
+ # has_many :posts
147
+ # accepts_nested_attributes_for :posts, :reject_if => :reject_posts
147
148
  #
148
- # def reject_posts(attributed)
149
- # attributed['title'].blank?
150
- # end
151
- # end
149
+ # def reject_posts(attributed)
150
+ # attributed['title'].blank?
151
+ # end
152
+ # end
152
153
  #
153
154
  # If the hash contains an <tt>id</tt> key that matches an already
154
155
  # associated record, the matching record will be modified:
@@ -185,6 +186,29 @@ module ActiveRecord
185
186
  # member.save
186
187
  # member.reload.posts.length # => 1
187
188
  #
189
+ # Nested attributes for an associated collection can also be passed in
190
+ # the form of a hash of hashes instead of an array of hashes:
191
+ #
192
+ # Member.create(:name => 'joe',
193
+ # :posts_attributes => { :first => { :title => 'Foo' },
194
+ # :second => { :title => 'Bar' } })
195
+ #
196
+ # has the same effect as
197
+ #
198
+ # Member.create(:name => 'joe',
199
+ # :posts_attributes => [ { :title => 'Foo' },
200
+ # { :title => 'Bar' } ])
201
+ #
202
+ # The keys of the hash which is the value for +:posts_attributes+ are
203
+ # ignored in this case.
204
+ # However, it is not allowed to use +'id'+ or +:id+ for one of
205
+ # such keys, otherwise the hash will be wrapped in an array and
206
+ # interpreted as an attribute hash for a single post.
207
+ #
208
+ # Passing attributes for an associated collection in the form of a hash
209
+ # of hashes can be used with hashes generated from HTTP/HTML parameters,
210
+ # where there maybe no natural way to submit an array of hashes.
211
+ #
188
212
  # === Saving
189
213
  #
190
214
  # All changes to models, including the destruction of those marked for
@@ -220,7 +244,7 @@ module ActiveRecord
220
244
  # validates_presence_of :member
221
245
  # end
222
246
  module ClassMethods
223
- REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |_, value| value.blank? } }
247
+ REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } }
224
248
 
225
249
  # Defines an attributes writer for the specified association(s). If you
226
250
  # are using <tt>attr_protected</tt> or <tt>attr_accessible</tt>, then you
@@ -239,7 +263,8 @@ module ActiveRecord
239
263
  # is specified, a record will be built for all attribute hashes that
240
264
  # do not have a <tt>_destroy</tt> value that evaluates to true.
241
265
  # Passing <tt>:all_blank</tt> instead of a Proc will create a proc
242
- # that will reject a record where all the attributes are blank.
266
+ # that will reject a record where all the attributes are blank excluding
267
+ # any value for _destroy.
243
268
  # [:limit]
244
269
  # Allows you to specify the maximum number of the associated records that
245
270
  # can be processed with the nested attributes. If the size of the
@@ -276,13 +301,14 @@ module ActiveRecord
276
301
 
277
302
  type = (reflection.collection? ? :collection : :one_to_one)
278
303
 
304
+ # remove_possible_method :pirate_attributes=
305
+ #
279
306
  # def pirate_attributes=(attributes)
280
307
  # assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options)
281
308
  # end
282
309
  class_eval <<-eoruby, __FILE__, __LINE__ + 1
283
- if method_defined?(:#{association_name}_attributes=)
284
- remove_method(:#{association_name}_attributes=)
285
- end
310
+ remove_possible_method(:#{association_name}_attributes=)
311
+
286
312
  def #{association_name}_attributes=(attributes)
287
313
  assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, mass_assignment_options)
288
314
  end
@@ -444,11 +470,12 @@ module ActiveRecord
444
470
  # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
445
471
  # association and evaluates to +true+.
446
472
  def reject_new_record?(association_name, attributes)
447
- has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
473
+ will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
448
474
  end
449
475
 
450
476
  def call_reject_if(association_name, attributes)
451
- return false if has_destroy_flag?(attributes)
477
+ return false if will_be_destroyed?(association_name, attributes)
478
+
452
479
  case callback = self.nested_attributes_options[association_name][:reject_if]
453
480
  when Symbol
454
481
  method(callback).arity == 0 ? send(callback) : send(callback, attributes)
@@ -457,6 +484,15 @@ module ActiveRecord
457
484
  end
458
485
  end
459
486
 
487
+ # Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
488
+ def will_be_destroyed?(association_name, attributes)
489
+ allow_destroy?(association_name) && has_destroy_flag?(attributes)
490
+ end
491
+
492
+ def allow_destroy?(association_name)
493
+ self.nested_attributes_options[association_name][:allow_destroy]
494
+ end
495
+
460
496
  def raise_nested_attributes_record_not_found(association_name, record_id)
461
497
  raise RecordNotFound, "Couldn't find #{self.class.reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
462
498
  end
@@ -1,6 +1,53 @@
1
+ require 'active_support/concern'
2
+
1
3
  module ActiveRecord
2
4
  # = Active Record Persistence
3
5
  module Persistence
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ # Creates an object (or multiple objects) and saves it to the database, if validations pass.
10
+ # The resulting object is returned whether the object was saved successfully to the database or not.
11
+ #
12
+ # The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the
13
+ # attributes on the objects that are to be created.
14
+ #
15
+ # +create+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
16
+ # in the +options+ parameter.
17
+ #
18
+ # ==== Examples
19
+ # # Create a single new object
20
+ # User.create(:first_name => 'Jamie')
21
+ #
22
+ # # Create a single new object using the :admin mass-assignment security role
23
+ # User.create({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
24
+ #
25
+ # # Create a single new object bypassing mass-assignment security
26
+ # User.create({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
27
+ #
28
+ # # Create an Array of new objects
29
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
30
+ #
31
+ # # Create a single object and pass it into a block to set other attributes.
32
+ # User.create(:first_name => 'Jamie') do |u|
33
+ # u.is_admin = false
34
+ # end
35
+ #
36
+ # # Creating an Array of new objects using a block, where the block is executed for each object:
37
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
38
+ # u.is_admin = false
39
+ # end
40
+ def create(attributes = nil, options = {}, &block)
41
+ if attributes.is_a?(Array)
42
+ attributes.collect { |attr| create(attr, options, &block) }
43
+ else
44
+ object = new(attributes, options, &block)
45
+ object.save
46
+ object
47
+ end
48
+ end
49
+ end
50
+
4
51
  # Returns true if this object hasn't been saved yet -- that is, a record
5
52
  # for the object doesn't exist in the data store yet; otherwise, returns false.
6
53
  def new_record?
@@ -114,7 +161,8 @@ module ActiveRecord
114
161
  became.instance_variable_set("@attributes_cache", @attributes_cache)
115
162
  became.instance_variable_set("@new_record", new_record?)
116
163
  became.instance_variable_set("@destroyed", destroyed?)
117
- became.type = klass.name unless self.class.descends_from_active_record?
164
+ became.instance_variable_set("@errors", errors)
165
+ became.send("#{klass.inheritance_column}=", klass.name) unless self.class.descends_from_active_record?
118
166
  became
119
167
  end
120
168
 
@@ -139,12 +187,18 @@ module ActiveRecord
139
187
  # * Callbacks are skipped.
140
188
  # * updated_at/updated_on column is not updated if that column is available.
141
189
  #
190
+ # Raises an +ActiveRecordError+ when called on new objects, or when the +name+
191
+ # attribute is marked as readonly.
142
192
  def update_column(name, value)
143
193
  name = name.to_s
144
194
  raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
145
195
  raise ActiveRecordError, "can not update on a new record object" unless persisted?
196
+
197
+ updated_count = self.class.unscoped.update_all({ name => value }, self.class.primary_key => id)
198
+
146
199
  raw_write_attribute(name, value)
147
- self.class.update_all({ name => value }, self.class.primary_key => id) == 1
200
+
201
+ updated_count == 1
148
202
  end
149
203
 
150
204
  # Updates the attributes of the model from the passed-in hash and saves the
@@ -312,19 +366,11 @@ module ActiveRecord
312
366
 
313
367
  new_id = self.class.unscoped.insert attributes_values
314
368
 
315
- self.id ||= new_id
369
+ self.id ||= new_id if self.class.primary_key
316
370
 
317
371
  IdentityMap.add(self) if IdentityMap.enabled?
318
372
  @new_record = false
319
373
  id
320
374
  end
321
-
322
- # Initializes the attributes array with keys matching the columns from the linked table and
323
- # the values matching the corresponding default value of that column, so
324
- # that a new instance, or one populated from a passed-in Hash, still has all the attributes
325
- # that instances loaded from the database would.
326
- def attributes_from_column_definition
327
- self.class.column_defaults.dup
328
- end
329
375
  end
330
376
  end
@@ -6,19 +6,19 @@ module ActiveRecord
6
6
  module ClassMethods
7
7
  # Enable the query cache within the block if Active Record is configured.
8
8
  def cache(&block)
9
- if ActiveRecord::Base.configurations.blank?
10
- yield
11
- else
9
+ if ActiveRecord::Base.connected?
12
10
  connection.cache(&block)
11
+ else
12
+ yield
13
13
  end
14
14
  end
15
15
 
16
16
  # Disable the query cache within the block if Active Record is configured.
17
17
  def uncached(&block)
18
- if ActiveRecord::Base.configurations.blank?
19
- yield
20
- else
18
+ if ActiveRecord::Base.connected?
21
19
  connection.uncached(&block)
20
+ else
21
+ yield
22
22
  end
23
23
  end
24
24
  end