Skip to content

Instantly share code, notes, and snippets.

@seanlinsley
Last active August 29, 2015 14:28
Show Gist options
  • Select an option

  • Save seanlinsley/c606069560120ec8e4d2 to your computer and use it in GitHub Desktop.

Select an option

Save seanlinsley/c606069560120ec8e4d2 to your computer and use it in GitHub Desktop.
Inheritable settings between two models, in Rails
class Child < ActiveRecord::Base
belongs_to :parent
INHERITABLE_SETTINGS = %i[foo bar baz]
INHERITABLE_SETTINGS.each do |attr|
define_method attr do
value = read_attribute(attr)
default = default_setting(attr)
inherited = inherited_setting(attr)
inherited && value == default ? inherited : value
end
# This is a total hack. It won't respond to `includes` or `joins` correctly.
# If we want better support, we'll have to monkeypatch `add_constraints` in Rails 4.1
# or `last_chain_scope` in >= 4.2 on ActiveRecord::Associations::AssociationScope.
if /(?<association>.+)_id/ =~ attr
define_method association do
assoc = association(association)
assoc.association_scope.bind_values = [[
assoc.klass.columns_hash['id'], public_send(attr)
]]
assoc.load_target
end
end
end
def inherited_setting(attr)
return unless parent
parent.settings[attr.to_s]
end
def default_setting(attr)
self.class.column_defaults[attr.to_s]
end
end
# Allows the child's attributes to be read and written by a regular form.
class Parent < ActiveRecord::Base
Child::INHERITABLE_SETTINGS.each do |attr|
define_method attr do
settings[attr.to_s]
end
define_method "#{attr}=" do |value|
settings_will_change!
if [nil, ''].include? value
settings.delete attr.to_s
else
settings[attr.to_s] = cast_property_value(attr, value)
end
end
end
def cast_child_value(attr, value)
case Child.columns_hash[attr.to_s].type
when :boolean
ActiveRecord::ConnectionAdapters::Column.value_to_boolean value
when :integer
value.to_i
when :decimal
value.to_f
else
value
end
end
end
# The orignal version would prevent inheritable settings from working with associations.
# Specifically, it's used in `find_target?`, which decides whether to do a database request.
module SupportInhertiableAssociations
def foreign_key_present?
owner.public_send reflection.foreign_key
end
end
ActiveRecord::Associations::BelongsToAssociation.prepend SupportInhertiableAssociations
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment