activerecord 2.3.5 → 2.3.6
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.
- data/CHANGELOG +33 -0
- data/Rakefile +1 -1
- data/examples/performance.sql +85 -0
- data/lib/active_record.rb +1 -2
- data/lib/active_record/association_preload.rb +9 -2
- data/lib/active_record/associations.rb +48 -38
- data/lib/active_record/associations/association_collection.rb +15 -11
- data/lib/active_record/associations/association_proxy.rb +16 -6
- data/lib/active_record/associations/belongs_to_association.rb +11 -1
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +34 -10
- data/lib/active_record/associations/has_many_association.rb +5 -0
- data/lib/active_record/associations/has_many_through_association.rb +5 -5
- data/lib/active_record/associations/has_one_association.rb +10 -1
- data/lib/active_record/attribute_methods.rb +5 -1
- data/lib/active_record/autosave_association.rb +66 -35
- data/lib/active_record/base.rb +77 -36
- data/lib/active_record/batches.rb +13 -9
- data/lib/active_record/calculations.rb +6 -3
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +64 -10
- data/lib/active_record/connection_adapters/abstract_adapter.rb +2 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +31 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +31 -66
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +2 -2
- data/lib/active_record/dirty.rb +2 -2
- data/lib/active_record/fixtures.rb +1 -0
- data/lib/active_record/locking/optimistic.rb +34 -1
- data/lib/active_record/migration.rb +5 -0
- data/lib/active_record/nested_attributes.rb +64 -52
- data/lib/active_record/reflection.rb +66 -1
- data/lib/active_record/schema.rb +5 -1
- data/lib/active_record/schema_dumper.rb +3 -0
- data/lib/active_record/serializers/json_serializer.rb +1 -1
- data/lib/active_record/validations.rb +13 -1
- data/lib/active_record/version.rb +1 -1
- data/test/cases/active_schema_test_mysql.rb +22 -0
- data/test/cases/associations/belongs_to_associations_test.rb +13 -0
- data/test/cases/associations/eager_load_nested_include_test.rb +8 -7
- data/test/cases/associations/eager_test.rb +7 -1
- data/test/cases/associations/has_many_associations_test.rb +26 -0
- data/test/cases/associations/inverse_associations_test.rb +566 -0
- data/test/cases/associations_test.rb +10 -0
- data/test/cases/autosave_association_test.rb +86 -10
- data/test/cases/base_test.rb +29 -0
- data/test/cases/batches_test.rb +20 -0
- data/test/cases/calculations_test.rb +2 -3
- data/test/cases/encoding_test.rb +6 -0
- data/test/cases/finder_test.rb +19 -3
- data/test/cases/fixtures_test.rb +5 -0
- data/test/cases/json_serialization_test.rb +14 -0
- data/test/cases/locking_test.rb +48 -3
- data/test/cases/migration_test.rb +115 -0
- data/test/cases/modules_test.rb +28 -0
- data/test/cases/named_scope_test.rb +1 -1
- data/test/cases/nested_attributes_test.rb +239 -7
- data/test/cases/query_cache_test.rb +7 -1
- data/test/cases/reflection_test.rb +47 -7
- data/test/cases/schema_test_postgresql.rb +2 -2
- data/test/cases/validations_i18n_test.rb +6 -36
- data/test/cases/validations_test.rb +33 -1
- data/test/cases/yaml_serialization_test.rb +11 -0
- data/test/fixtures/faces.yml +11 -0
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/fixtures/interests.yml +33 -0
- data/test/fixtures/men.yml +5 -0
- data/test/fixtures/zines.yml +5 -0
- data/test/models/author.rb +3 -0
- data/test/models/bird.rb +6 -0
- data/test/models/company_in_module.rb +17 -0
- data/test/models/event_author.rb +5 -0
- data/test/models/face.rb +7 -0
- data/test/models/interest.rb +5 -0
- data/test/models/invoice.rb +4 -0
- data/test/models/line_item.rb +3 -0
- data/test/models/man.rb +9 -0
- data/test/models/parrot.rb +6 -0
- data/test/models/pirate.rb +10 -0
- data/test/models/ship.rb +10 -1
- data/test/models/ship_part.rb +3 -1
- data/test/models/zine.rb +3 -0
- data/test/schema/schema.rb +41 -0
- metadata +37 -11
- data/lib/active_record/i18n_interpolation_deprecation.rb +0 -26
@@ -31,6 +31,8 @@ module ActiveRecord
|
|
31
31
|
@updated = true
|
32
32
|
end
|
33
33
|
|
34
|
+
set_inverse_instance(record, @owner)
|
35
|
+
|
34
36
|
loaded
|
35
37
|
record
|
36
38
|
end
|
@@ -46,13 +48,15 @@ module ActiveRecord
|
|
46
48
|
else
|
47
49
|
"find"
|
48
50
|
end
|
49
|
-
@reflection.klass.send(find_method,
|
51
|
+
the_target = @reflection.klass.send(find_method,
|
50
52
|
@owner[@reflection.primary_key_name],
|
51
53
|
:select => @reflection.options[:select],
|
52
54
|
:conditions => conditions,
|
53
55
|
:include => @reflection.options[:include],
|
54
56
|
:readonly => @reflection.options[:readonly]
|
55
57
|
) if @owner[@reflection.primary_key_name]
|
58
|
+
set_inverse_instance(the_target, @owner)
|
59
|
+
the_target
|
56
60
|
end
|
57
61
|
|
58
62
|
def foreign_key_present
|
@@ -71,6 +75,12 @@ module ActiveRecord
|
|
71
75
|
@owner[@reflection.primary_key_name]
|
72
76
|
end
|
73
77
|
end
|
78
|
+
|
79
|
+
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
80
|
+
# has_one associations.
|
81
|
+
def we_can_set_the_inverse_on_this?(record)
|
82
|
+
@reflection.has_inverse? && @reflection.inverse_of.macro == :has_one
|
83
|
+
end
|
74
84
|
end
|
75
85
|
end
|
76
86
|
end
|
@@ -13,6 +13,7 @@ module ActiveRecord
|
|
13
13
|
@updated = true
|
14
14
|
end
|
15
15
|
|
16
|
+
set_inverse_instance(record, @owner)
|
16
17
|
loaded
|
17
18
|
record
|
18
19
|
end
|
@@ -22,21 +23,44 @@ module ActiveRecord
|
|
22
23
|
end
|
23
24
|
|
24
25
|
private
|
25
|
-
def find_target
|
26
|
-
return nil if association_class.nil?
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
)
|
27
|
+
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
28
|
+
# has_one associations.
|
29
|
+
def we_can_set_the_inverse_on_this?(record)
|
30
|
+
if @reflection.has_inverse?
|
31
|
+
inverse_association = @reflection.polymorphic_inverse_of(record.class)
|
32
|
+
inverse_association && inverse_association.macro == :has_one
|
35
33
|
else
|
36
|
-
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_inverse_instance(record, instance)
|
39
|
+
return if record.nil? || !we_can_set_the_inverse_on_this?(record)
|
40
|
+
inverse_relationship = @reflection.polymorphic_inverse_of(record.class)
|
41
|
+
unless inverse_relationship.nil?
|
42
|
+
record.send(:"set_#{inverse_relationship.name}_target", instance)
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
46
|
+
def find_target
|
47
|
+
return nil if association_class.nil?
|
48
|
+
|
49
|
+
target =
|
50
|
+
if @reflection.options[:conditions]
|
51
|
+
association_class.find(
|
52
|
+
@owner[@reflection.primary_key_name],
|
53
|
+
:select => @reflection.options[:select],
|
54
|
+
:conditions => conditions,
|
55
|
+
:include => @reflection.options[:include]
|
56
|
+
)
|
57
|
+
else
|
58
|
+
association_class.find(@owner[@reflection.primary_key_name], :select => @reflection.options[:select], :include => @reflection.options[:include])
|
59
|
+
end
|
60
|
+
set_inverse_instance(target, @owner)
|
61
|
+
target
|
62
|
+
end
|
63
|
+
|
40
64
|
def foreign_key_present
|
41
65
|
!@owner[@reflection.primary_key_name].nil?
|
42
66
|
end
|
@@ -1,11 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Associations
|
3
3
|
class HasManyThroughAssociation < HasManyAssociation #:nodoc:
|
4
|
-
def initialize(owner, reflection)
|
5
|
-
reflection.check_validity!
|
6
|
-
super
|
7
|
-
end
|
8
|
-
|
9
4
|
alias_method :new, :build
|
10
5
|
|
11
6
|
def create!(attrs = nil)
|
@@ -261,6 +256,11 @@ module ActiveRecord
|
|
261
256
|
def cached_counter_attribute_name
|
262
257
|
"#{@reflection.name}_count"
|
263
258
|
end
|
259
|
+
|
260
|
+
# NOTE - not sure that we can actually cope with inverses here
|
261
|
+
def we_can_set_the_inverse_on_this?(record)
|
262
|
+
false
|
263
|
+
end
|
264
264
|
end
|
265
265
|
end
|
266
266
|
end
|
@@ -57,6 +57,7 @@ module ActiveRecord
|
|
57
57
|
@target = (AssociationProxy === obj ? obj.target : obj)
|
58
58
|
end
|
59
59
|
|
60
|
+
set_inverse_instance(obj, @owner)
|
60
61
|
@loaded = true
|
61
62
|
|
62
63
|
unless @owner.new_record? or obj.nil? or dont_save
|
@@ -77,13 +78,15 @@ module ActiveRecord
|
|
77
78
|
|
78
79
|
private
|
79
80
|
def find_target
|
80
|
-
@reflection.klass.find(:first,
|
81
|
+
the_target = @reflection.klass.find(:first,
|
81
82
|
:conditions => @finder_sql,
|
82
83
|
:select => @reflection.options[:select],
|
83
84
|
:order => @reflection.options[:order],
|
84
85
|
:include => @reflection.options[:include],
|
85
86
|
:readonly => @reflection.options[:readonly]
|
86
87
|
)
|
88
|
+
set_inverse_instance(the_target, @owner)
|
89
|
+
the_target
|
87
90
|
end
|
88
91
|
|
89
92
|
def construct_sql
|
@@ -118,6 +121,7 @@ module ActiveRecord
|
|
118
121
|
else
|
119
122
|
record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
|
120
123
|
self.target = record
|
124
|
+
set_inverse_instance(record, @owner)
|
121
125
|
end
|
122
126
|
|
123
127
|
record
|
@@ -128,6 +132,11 @@ module ActiveRecord
|
|
128
132
|
attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
|
129
133
|
attrs
|
130
134
|
end
|
135
|
+
|
136
|
+
def we_can_set_the_inverse_on_this?(record)
|
137
|
+
inverse = @reflection.inverse_of
|
138
|
+
return !inverse.nil?
|
139
|
+
end
|
131
140
|
end
|
132
141
|
end
|
133
142
|
end
|
@@ -208,7 +208,7 @@ module ActiveRecord
|
|
208
208
|
end
|
209
209
|
|
210
210
|
begin
|
211
|
-
class_eval(method_definition, __FILE__
|
211
|
+
class_eval(method_definition, __FILE__)
|
212
212
|
rescue SyntaxError => err
|
213
213
|
generated_methods.delete(attr_name)
|
214
214
|
if logger
|
@@ -230,6 +230,10 @@ module ActiveRecord
|
|
230
230
|
# It's also possible to instantiate related objects, so a Client class belonging to the clients
|
231
231
|
# table with a +master_id+ foreign key can instantiate master through Client#master.
|
232
232
|
def method_missing(method_id, *args, &block)
|
233
|
+
if method_id == :to_ary || method_id == :to_str
|
234
|
+
raise NoMethodError, "undefined method `#{method_id}' for #{inspect}:#{self.class}"
|
235
|
+
end
|
236
|
+
|
233
237
|
method_name = method_id.to_s
|
234
238
|
|
235
239
|
if self.class.private_method_defined?(method_name)
|
@@ -156,38 +156,42 @@ module ActiveRecord
|
|
156
156
|
|
157
157
|
# Adds a validate and save callback for the association as specified by
|
158
158
|
# the +reflection+.
|
159
|
+
#
|
160
|
+
# For performance reasons, we don't check whether to validate at runtime,
|
161
|
+
# but instead only define the method and callback when needed. However,
|
162
|
+
# this can change, for instance, when using nested attributes, which is
|
163
|
+
# called _after_ the association has been defined. Since we don't want
|
164
|
+
# the callbacks to get defined multiple times, there are guards that
|
165
|
+
# check if the save or validation methods have already been defined
|
166
|
+
# before actually defining them.
|
159
167
|
def add_autosave_association_callbacks(reflection)
|
160
|
-
save_method = "autosave_associated_records_for_#{reflection.name}"
|
161
|
-
validation_method = "validate_associated_records_for_#{reflection.name}"
|
162
|
-
|
168
|
+
save_method = :"autosave_associated_records_for_#{reflection.name}"
|
169
|
+
validation_method = :"validate_associated_records_for_#{reflection.name}"
|
170
|
+
collection = reflection.collection?
|
163
171
|
|
164
|
-
|
165
|
-
|
166
|
-
|
172
|
+
unless method_defined?(save_method)
|
173
|
+
if collection
|
174
|
+
before_save :before_save_collection_association
|
167
175
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
define_method(save_method) { save_has_one_association(reflection) }
|
181
|
-
after_save save_method
|
182
|
-
when :belongs_to
|
183
|
-
define_method(save_method) { save_belongs_to_association(reflection) }
|
184
|
-
before_save save_method
|
176
|
+
define_method(save_method) { save_collection_association(reflection) }
|
177
|
+
# Doesn't use after_save as that would save associations added in after_create/after_update twice
|
178
|
+
after_create save_method
|
179
|
+
after_update save_method
|
180
|
+
else
|
181
|
+
if reflection.macro == :has_one
|
182
|
+
define_method(save_method) { save_has_one_association(reflection) }
|
183
|
+
after_save save_method
|
184
|
+
else
|
185
|
+
define_method(save_method) { save_belongs_to_association(reflection) }
|
186
|
+
before_save save_method
|
187
|
+
end
|
185
188
|
end
|
189
|
+
end
|
186
190
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
+
if reflection.validate? && !method_defined?(validation_method)
|
192
|
+
method = (collection ? :validate_collection_association : :validate_single_association)
|
193
|
+
define_method(validation_method) { send(method, reflection) }
|
194
|
+
validate validation_method
|
191
195
|
end
|
192
196
|
end
|
193
197
|
end
|
@@ -213,6 +217,12 @@ module ActiveRecord
|
|
213
217
|
@marked_for_destruction
|
214
218
|
end
|
215
219
|
|
220
|
+
# Returns whether or not this record has been changed in any way (including whether
|
221
|
+
# any of its nested autosave associations are likewise changed)
|
222
|
+
def changed_for_autosave?
|
223
|
+
new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
|
224
|
+
end
|
225
|
+
|
216
226
|
private
|
217
227
|
|
218
228
|
# Returns the record for an association collection that should be validated
|
@@ -221,13 +231,28 @@ module ActiveRecord
|
|
221
231
|
def associated_records_to_validate_or_save(association, new_record, autosave)
|
222
232
|
if new_record
|
223
233
|
association
|
224
|
-
elsif
|
225
|
-
|
234
|
+
elsif autosave
|
235
|
+
association.target.select { |record| record.changed_for_autosave? }
|
226
236
|
else
|
227
|
-
|
237
|
+
association.target.select { |record| record.new_record? }
|
228
238
|
end
|
229
239
|
end
|
230
|
-
|
240
|
+
|
241
|
+
# go through nested autosave associations that are loaded in memory (without loading
|
242
|
+
# any new ones), and return true if is changed for autosave
|
243
|
+
def nested_records_changed_for_autosave?
|
244
|
+
self.class.reflect_on_all_autosave_associations.each do |reflection|
|
245
|
+
if association = association_instance_get(reflection.name)
|
246
|
+
if [:belongs_to, :has_one].include?(reflection.macro)
|
247
|
+
return true if association.target && association.target.changed_for_autosave?
|
248
|
+
else
|
249
|
+
association.target.each {|record| return true if record.changed_for_autosave? }
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
false
|
254
|
+
end
|
255
|
+
|
231
256
|
# Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
|
232
257
|
# turned on for the association specified by +reflection+.
|
233
258
|
def validate_single_association(reflection)
|
@@ -293,13 +318,15 @@ module ActiveRecord
|
|
293
318
|
association.destroy(record)
|
294
319
|
elsif autosave != false && (@new_record_before_save || record.new_record?)
|
295
320
|
if autosave
|
296
|
-
association.send(:insert_record, record, false, false)
|
321
|
+
saved = association.send(:insert_record, record, false, false)
|
297
322
|
else
|
298
323
|
association.send(:insert_record, record)
|
299
324
|
end
|
300
325
|
elsif autosave
|
301
|
-
record.save(false)
|
326
|
+
saved = record.save(false)
|
302
327
|
end
|
328
|
+
|
329
|
+
raise ActiveRecord::Rollback if saved == false
|
303
330
|
end
|
304
331
|
end
|
305
332
|
|
@@ -326,7 +353,9 @@ module ActiveRecord
|
|
326
353
|
key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
|
327
354
|
if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave)
|
328
355
|
association[reflection.primary_key_name] = key
|
329
|
-
association.save(!autosave)
|
356
|
+
saved = association.save(!autosave)
|
357
|
+
raise ActiveRecord::Rollback if !saved && autosave
|
358
|
+
saved
|
330
359
|
end
|
331
360
|
end
|
332
361
|
end
|
@@ -347,7 +376,7 @@ module ActiveRecord
|
|
347
376
|
if autosave && association.marked_for_destruction?
|
348
377
|
association.destroy
|
349
378
|
elsif autosave != false
|
350
|
-
association.save(!autosave) if association.new_record? || autosave
|
379
|
+
saved = association.save(!autosave) if association.new_record? || autosave
|
351
380
|
|
352
381
|
if association.updated?
|
353
382
|
association_id = association.send(reflection.options[:primary_key] || :id)
|
@@ -357,6 +386,8 @@ module ActiveRecord
|
|
357
386
|
self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s
|
358
387
|
end
|
359
388
|
end
|
389
|
+
|
390
|
+
saved if autosave
|
360
391
|
end
|
361
392
|
end
|
362
393
|
end
|
data/lib/active_record/base.rb
CHANGED
@@ -461,6 +461,9 @@ module ActiveRecord #:nodoc:
|
|
461
461
|
# Accessor for the name of the prefix string to prepend to every table name. So if set to "basecamp_", all
|
462
462
|
# table names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient way of creating a namespace
|
463
463
|
# for tables in a shared database. By default, the prefix is the empty string.
|
464
|
+
#
|
465
|
+
# If you are organising your models within modules you can add a prefix to the models within a namespace by defining
|
466
|
+
# a singleton method in the parent module called table_name_prefix which returns your chosen prefix.
|
464
467
|
cattr_accessor :table_name_prefix, :instance_writer => false
|
465
468
|
@@table_name_prefix = ""
|
466
469
|
|
@@ -916,6 +919,29 @@ module ActiveRecord #:nodoc:
|
|
916
919
|
connection.select_value(sql, "#{name} Count").to_i
|
917
920
|
end
|
918
921
|
|
922
|
+
# Resets one or more counter caches to their correct value using an SQL
|
923
|
+
# count query. This is useful when adding new counter caches, or if the
|
924
|
+
# counter has been corrupted or modified directly by SQL.
|
925
|
+
#
|
926
|
+
# ==== Parameters
|
927
|
+
#
|
928
|
+
# * +id+ - The id of the object you wish to reset a counter on.
|
929
|
+
# * +counters+ - One or more counter names to reset
|
930
|
+
#
|
931
|
+
# ==== Examples
|
932
|
+
#
|
933
|
+
# # For Post with id #1 records reset the comments_count
|
934
|
+
# Post.reset_counters(1, :comments)
|
935
|
+
def reset_counters(id, *counters)
|
936
|
+
object = find(id)
|
937
|
+
counters.each do |association|
|
938
|
+
child_class = reflect_on_association(association).klass
|
939
|
+
counter_name = child_class.reflect_on_association(self.name.downcase.to_sym).counter_cache_column
|
940
|
+
|
941
|
+
connection.update("UPDATE #{quoted_table_name} SET #{connection.quote_column_name(counter_name)} = #{object.send(association).count} WHERE #{connection.quote_column_name(primary_key)} = #{quote_value(object.id)}", "#{name} UPDATE")
|
942
|
+
end
|
943
|
+
end
|
944
|
+
|
919
945
|
# A generic "counter updater" implementation, intended primarily to be
|
920
946
|
# used by increment_counter and decrement_counter, but which may also
|
921
947
|
# be useful on its own. It simply does a direct SQL update for the record
|
@@ -1148,7 +1174,7 @@ module ActiveRecord #:nodoc:
|
|
1148
1174
|
contained = contained.singularize if parent.pluralize_table_names
|
1149
1175
|
contained << '_'
|
1150
1176
|
end
|
1151
|
-
name = "#{
|
1177
|
+
name = "#{full_table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
|
1152
1178
|
end
|
1153
1179
|
|
1154
1180
|
set_table_name(name)
|
@@ -1178,6 +1204,10 @@ module ActiveRecord #:nodoc:
|
|
1178
1204
|
key
|
1179
1205
|
end
|
1180
1206
|
|
1207
|
+
def full_table_name_prefix #:nodoc:
|
1208
|
+
(parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
|
1209
|
+
end
|
1210
|
+
|
1181
1211
|
# Defines the column name for use with single table inheritance
|
1182
1212
|
# -- can be set in subclasses like so: self.inheritance_column = "type_id"
|
1183
1213
|
def inheritance_column
|
@@ -1861,7 +1891,7 @@ module ActiveRecord #:nodoc:
|
|
1861
1891
|
# find(:first, options.merge(finder_options))
|
1862
1892
|
# end
|
1863
1893
|
# end
|
1864
|
-
self.class_eval
|
1894
|
+
self.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
1865
1895
|
def self.#{method_id}(*args)
|
1866
1896
|
options = args.extract_options!
|
1867
1897
|
attributes = construct_attributes_from_arguments(
|
@@ -1881,7 +1911,7 @@ module ActiveRecord #:nodoc:
|
|
1881
1911
|
end
|
1882
1912
|
#{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(\', \')}")' if bang}
|
1883
1913
|
end
|
1884
|
-
|
1914
|
+
EOS
|
1885
1915
|
send(method_id, *arguments)
|
1886
1916
|
elsif match.instantiator?
|
1887
1917
|
instantiator = match.instantiator
|
@@ -1910,25 +1940,30 @@ module ActiveRecord #:nodoc:
|
|
1910
1940
|
# record
|
1911
1941
|
# end
|
1912
1942
|
# end
|
1913
|
-
self.class_eval
|
1943
|
+
self.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
1914
1944
|
def self.#{method_id}(*args)
|
1915
|
-
|
1916
|
-
|
1917
|
-
|
1918
|
-
|
1919
|
-
|
1920
|
-
|
1921
|
-
|
1922
|
-
|
1945
|
+
attributes = [:#{attribute_names.join(',:')}]
|
1946
|
+
protected_attributes_for_create, unprotected_attributes_for_create = {}, {}
|
1947
|
+
args.each_with_index do |arg, i|
|
1948
|
+
if arg.is_a?(Hash)
|
1949
|
+
protected_attributes_for_create = args[i].with_indifferent_access
|
1950
|
+
else
|
1951
|
+
unprotected_attributes_for_create[attributes[i]] = args[i]
|
1952
|
+
end
|
1923
1953
|
end
|
1924
1954
|
|
1955
|
+
find_attributes = (protected_attributes_for_create.merge(unprotected_attributes_for_create)).slice(*attributes)
|
1956
|
+
|
1925
1957
|
options = { :conditions => find_attributes }
|
1926
1958
|
set_readonly_option!(options)
|
1927
1959
|
|
1928
1960
|
record = find(:first, options)
|
1929
1961
|
|
1930
1962
|
if record.nil?
|
1931
|
-
record = self.new
|
1963
|
+
record = self.new do |r|
|
1964
|
+
r.send(:attributes=, protected_attributes_for_create, true) unless protected_attributes_for_create.empty?
|
1965
|
+
r.send(:attributes=, unprotected_attributes_for_create, false) unless unprotected_attributes_for_create.empty?
|
1966
|
+
end
|
1932
1967
|
#{'yield(record) if block_given?'}
|
1933
1968
|
#{'record.save' if instantiator == :create}
|
1934
1969
|
record
|
@@ -1936,14 +1971,14 @@ module ActiveRecord #:nodoc:
|
|
1936
1971
|
record
|
1937
1972
|
end
|
1938
1973
|
end
|
1939
|
-
|
1974
|
+
EOS
|
1940
1975
|
send(method_id, *arguments, &block)
|
1941
1976
|
end
|
1942
1977
|
elsif match = DynamicScopeMatch.match(method_id)
|
1943
1978
|
attribute_names = match.attribute_names
|
1944
1979
|
super unless all_attributes_exists?(attribute_names)
|
1945
1980
|
if match.scope?
|
1946
|
-
self.class_eval
|
1981
|
+
self.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
1947
1982
|
def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
|
1948
1983
|
options = args.extract_options! # options = args.extract_options!
|
1949
1984
|
attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments(
|
@@ -1952,7 +1987,7 @@ module ActiveRecord #:nodoc:
|
|
1952
1987
|
#
|
1953
1988
|
scoped(:conditions => attributes) # scoped(:conditions => attributes)
|
1954
1989
|
end # end
|
1955
|
-
|
1990
|
+
EOS
|
1956
1991
|
send(method_id, *arguments)
|
1957
1992
|
end
|
1958
1993
|
else
|
@@ -2194,9 +2229,9 @@ module ActiveRecord #:nodoc:
|
|
2194
2229
|
modularized_name = type_name_with_module(type_name)
|
2195
2230
|
silence_warnings do
|
2196
2231
|
begin
|
2197
|
-
class_eval(modularized_name, __FILE__
|
2232
|
+
class_eval(modularized_name, __FILE__)
|
2198
2233
|
rescue NameError
|
2199
|
-
class_eval(type_name, __FILE__
|
2234
|
+
class_eval(type_name, __FILE__)
|
2200
2235
|
end
|
2201
2236
|
end
|
2202
2237
|
end
|
@@ -2436,7 +2471,7 @@ module ActiveRecord #:nodoc:
|
|
2436
2471
|
@new_record = true
|
2437
2472
|
ensure_proper_type
|
2438
2473
|
self.attributes = attributes unless attributes.nil?
|
2439
|
-
self.class.send(:scope, :create)
|
2474
|
+
assign_attributes(self.class.send(:scope, :create)) if self.class.send(:scoped?, :create)
|
2440
2475
|
result = yield self if block_given?
|
2441
2476
|
callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
|
2442
2477
|
result
|
@@ -2693,7 +2728,7 @@ module ActiveRecord #:nodoc:
|
|
2693
2728
|
def reload(options = nil)
|
2694
2729
|
clear_aggregation_cache
|
2695
2730
|
clear_association_cache
|
2696
|
-
@attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
|
2731
|
+
@attributes.update(self.class.send(:with_exclusive_scope) { self.class.find(self.id, options) }.instance_variable_get('@attributes'))
|
2697
2732
|
@attributes_cache = {}
|
2698
2733
|
self
|
2699
2734
|
end
|
@@ -2736,26 +2771,15 @@ module ActiveRecord #:nodoc:
|
|
2736
2771
|
attributes = new_attributes.dup
|
2737
2772
|
attributes.stringify_keys!
|
2738
2773
|
|
2739
|
-
multi_parameter_attributes = []
|
2740
2774
|
attributes = remove_attributes_protected_from_mass_assignment(attributes) if guard_protected_attributes
|
2741
|
-
|
2742
|
-
attributes.each do |k, v|
|
2743
|
-
if k.include?("(")
|
2744
|
-
multi_parameter_attributes << [ k, v ]
|
2745
|
-
else
|
2746
|
-
respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
|
2747
|
-
end
|
2748
|
-
end
|
2749
|
-
|
2750
|
-
assign_multiparameter_attributes(multi_parameter_attributes)
|
2775
|
+
assign_attributes(attributes) if attributes and attributes.any?
|
2751
2776
|
end
|
2752
2777
|
|
2753
2778
|
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
|
2754
2779
|
def attributes
|
2755
|
-
|
2756
|
-
|
2757
|
-
|
2758
|
-
end
|
2780
|
+
attrs = {}
|
2781
|
+
attribute_names.each { |name| attrs[name] = read_attribute(name) }
|
2782
|
+
attrs
|
2759
2783
|
end
|
2760
2784
|
|
2761
2785
|
# Returns a hash of attributes before typecasting and deserialization.
|
@@ -2869,6 +2893,23 @@ module ActiveRecord #:nodoc:
|
|
2869
2893
|
end
|
2870
2894
|
|
2871
2895
|
private
|
2896
|
+
# Assigns attributes, dealing nicely with both multi and single paramater attributes
|
2897
|
+
# Assumes attributes is a hash
|
2898
|
+
|
2899
|
+
def assign_attributes(attributes={})
|
2900
|
+
multiparameter_attributes = []
|
2901
|
+
|
2902
|
+
attributes.each do |k, v|
|
2903
|
+
if k.to_s.include?("(")
|
2904
|
+
multiparameter_attributes << [ k, v ]
|
2905
|
+
else
|
2906
|
+
respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
|
2907
|
+
end
|
2908
|
+
end
|
2909
|
+
|
2910
|
+
assign_multiparameter_attributes(multiparameter_attributes) unless multiparameter_attributes.empty?
|
2911
|
+
end
|
2912
|
+
|
2872
2913
|
def create_or_update
|
2873
2914
|
raise ReadOnlyRecord if readonly?
|
2874
2915
|
result = new_record? ? create : update
|
@@ -3099,7 +3140,7 @@ module ActiveRecord #:nodoc:
|
|
3099
3140
|
|
3100
3141
|
# Returns a comma-separated pair list, like "key1 = val1, key2 = val2".
|
3101
3142
|
def comma_pair_list(hash)
|
3102
|
-
hash.
|
3143
|
+
hash.map { |k,v| "#{k} = #{v}" }.join(", ")
|
3103
3144
|
end
|
3104
3145
|
|
3105
3146
|
def quoted_column_names(attributes = attributes_with_quotes)
|