Skip to content

Commit 9ce56b6

Browse files
committed
A few more fixes, for some reason inverse of needs to be set for building <-> sales
1 parent 475f58f commit 9ce56b6

File tree

3 files changed

+37
-46
lines changed

3 files changed

+37
-46
lines changed

activerecord/lib/active_record/autosave_association.rb

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,25 @@ def self.valid_options
156156

157157
module ClassMethods # :nodoc:
158158
private
159+
def define_non_cyclic_method(name, &block)
160+
return if method_defined?(name, false)
161+
162+
define_method(name) do |*args|
163+
result = true; @_already_called ||= {}
164+
# Loop prevention for validation of associations
165+
unless @_already_called[name]
166+
begin
167+
@_already_called[name] = true
168+
result = instance_eval(&block)
169+
ensure
170+
@_already_called[name] = false
171+
end
172+
end
173+
174+
result
175+
end
176+
end
177+
159178
# Adds validation and save callbacks for the association as specified by
160179
# the +reflection+.
161180
#
@@ -173,12 +192,12 @@ def add_autosave_association_callbacks(reflection)
173192
if reflection.collection?
174193
around_save :around_save_collection_association
175194

176-
define_method(save_method) { save_collection_association(reflection) }
195+
define_non_cyclic_method(save_method) { save_collection_association(reflection) }
177196
# Doesn't use after_save as that would save associations added in after_create/after_update twice
178197
after_create save_method
179198
after_update save_method
180199
elsif reflection.has_one?
181-
define_method(save_method) { save_has_one_association(reflection) }
200+
define_non_cyclic_method(save_method) { save_has_one_association(reflection) }
182201
# Configures two callbacks instead of a single after_save so that
183202
# the model may rely on their execution order relative to its
184203
# own callbacks.
@@ -190,7 +209,7 @@ def add_autosave_association_callbacks(reflection)
190209
after_create save_method
191210
after_update save_method
192211
else
193-
define_method(save_method) { throw(:abort) if save_belongs_to_association(reflection) == false }
212+
define_non_cyclic_method(save_method) { throw(:abort) if save_belongs_to_association(reflection) == false }
194213
before_save save_method
195214
end
196215

@@ -208,7 +227,7 @@ def define_autosave_validation_callbacks(reflection)
208227
method = :validate_belongs_to_association
209228
end
210229

211-
define_method(validation_method) { send(method, reflection) }
230+
define_non_cyclic_method(validation_method) { send(method, reflection) }
212231
validate validation_method
213232
after_validation :_ensure_no_duplicate_errors
214233
end
@@ -264,6 +283,7 @@ def changed_for_autosave?(memory)
264283
end
265284
end
266285

286+
#TODO: can go I think
267287
def validating_belongs_to_for?(association)
268288
@validating_belongs_to_for ||= {}
269289
@validating_belongs_to_for[association]
@@ -323,7 +343,7 @@ def validate_has_one_association(reflection)
323343
return if inverse_association && (record.validating_belongs_to_for?(inverse_association) ||
324344
record.autosaving_belongs_to_for?(inverse_association))
325345

326-
association_valid?(reflection, record, @memory)
346+
association_valid?(association, record, @memory)
327347
end
328348

329349
# Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
@@ -356,48 +376,19 @@ def validate_collection_association(reflection)
356376
# Returns whether or not the association is valid and applies any errors to
357377
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
358378
# enabled records if they're marked_for_destruction? or destroyed.
359-
def association_valid?(reflection, record, memory)
379+
def association_valid?(association, record, memory)
360380
return true if record.destroyed? || (association.options[:autosave] && record.marked_for_destruction?)
361381

362382
context = validation_context if custom_validation_context?
363383

364-
if memory.has_key?("valid#{record.object_id}")
365-
return memory["valid#{record.object_id}"]
366-
else
367-
memory["valid#{record.object_id}"] = true
368-
369-
valid = record.valid?(context, memory)
370-
if !valid
371-
if record.changed? || record.new_record? || context
372-
associated_errors = record.errors.objects
373-
else
374-
# If there are existing invalid records in the DB, we should still be able to reference them.
375-
# Unless a record (no matter where in the association chain) is invalid and is being changed.
376-
associated_errors = record.errors.objects.select { |error| error.is_a?(Associations::NestedError) }
377-
end
384+
return memory["valid#{record.object_id}"] if memory.has_key?("valid#{record.object_id}")
378385

379-
if association.options[:autosave]
380-
return if equal?(record)
381-
382-
associated_errors.each { |error|
383-
errors.objects.append(
384-
Associations::NestedError.new(association, error)
385-
)
386-
}
387-
elsif associated_errors.any?
388-
errors.add(association.reflection.name)
389-
end
390-
391-
valid = errors.any?
392-
end
393-
memory["valid#{record.object_id}"] = valid
394-
valid
395-
end
396-
end
386+
memory["valid#{record.object_id}"] = true
387+
388+
return true if record.valid?(context, memory)
397389

398-
def normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
399-
if indexed_attribute
400-
"#{reflection.name}[#{index}].#{attribute}"
390+
if record.changed? || record.new_record? || context
391+
associated_errors = record.errors.objects
401392
else
402393
# If there are existing invalid records in the DB, we should still be able to reference them.
403394
# Unless a record (no matter where in the association chain) is invalid and is being changed.
@@ -416,7 +407,7 @@ def normalize_reflection_attribute(indexed_attribute, reflection, index, attribu
416407
errors.add(association.reflection.name)
417408
end
418409

419-
errors.any?
410+
memory["valid#{record.object_id}"] = errors.any?
420411
end
421412

422413
# Is used as an around_save callback to check while saving a collection
@@ -531,7 +522,7 @@ def save_has_one_association(reflection)
531522
saved = if @memory.has_key?("saved#{record.object_id}")
532523
@memory["saved#{record.object_id}"]
533524
else
534-
record.save(validate: !autosave, memory: @memory)
525+
@memory["saved#{record.object_id}"] = record.save(validate: !autosave, memory: @memory)
535526
end
536527
raise ActiveRecord::Rollback if !saved && autosave
537528
saved
@@ -584,7 +575,7 @@ def save_belongs_to_association(reflection)
584575
begin
585576
@autosaving_belongs_to_for ||= {}
586577
@autosaving_belongs_to_for[association] = true
587-
record.save(validate: !autosave, memory: @memory)
578+
@memory["saved#{record.object_id}"] = record.save(validate: !autosave, memory: @memory)
588579
ensure
589580
@autosaving_belongs_to_for[association] = false
590581
end

activerecord/lib/active_record/validations.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def raise_validation_error
119119
end
120120

121121
def perform_validations(options = {})
122-
options[:validate] == false || valid?(options[:context])
122+
options[:validate] == false || valid?(options[:context], options[:memory])
123123
end
124124
end
125125
end
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
class Building < ActiveRecord::Base
2-
has_many :sales, autosave: true
2+
has_many :sales, autosave: true, inverse_of: :building
33
has_many :expenses, autosave: true
44
end

0 commit comments

Comments
 (0)