Skip to content

Instantly share code, notes, and snippets.

@chriscz
Created February 16, 2024 10:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chriscz/335c6d6bbafebbd1a3c874c3b4aa3ffa to your computer and use it in GitHub Desktop.
Save chriscz/335c6d6bbafebbd1a3c874c3b4aa3ffa to your computer and use it in GitHub Desktop.
Fix for unpersisted has_one's through being inaccessible in Rails.
# FIXME Rails has-one through is nil on unpersisted objects (bug)
# https://github.com/rails/rails/issues/33155
module ActiveRecordUnpersistedHasOneFix
extend ActiveSupport::Concern
# Define utility methods
class << self
# @return [Array<String>, nil]
def association_path(model_class, association)
path = []
reflection = model_class.reflections.fetch(association.to_s)
while reflection.through_reflection? && reflection != reflection.source_reflection
path << reflection.through_reflection.name
reflection = reflection.source_reflection
end
if path.length > 0
path << reflection.name
path
else
nil
end
end
def read_association(model_instance, association)
path = association_path(model_instance.class, association)
path && path.reduce(model_instance) { |result, path_segment| result.try(path_segment) }
end
end
class_methods do
def has_one(*args, **kwargs, &block)
super(*args, **kwargs, &block).tap do
association_name = args.first
# This anonymous module is used to define all the has_one's per class
@unpersisted_has_one_fix_module ||= Module.new do
extend ActiveSupport::Concern
end
include @unpersisted_has_one_fix_module
@unpersisted_has_one_fix_module.class_eval <<-METHODS, __FILE__, __LINE__ + 1
def #{association_name}
if persisted?
super
else
super || ::ActiveRecordUnpersistedHasOneFix.read_association(self, "#{association_name}")
end
end
METHODS
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment