Last active
February 3, 2025 21:08
-
-
Save woahdae/44ffff96096c71c780aecffdb4c4c53f to your computer and use it in GitHub Desktop.
Module to use the Gerund pattern on ActiveRecord models
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
module Gerund | |
def self.included(base) | |
base.extend ClassMethods | |
end | |
module ClassMethods | |
delegate :model_name, | |
:sti_name, | |
:finder_needs_type_condition?, | |
:_to_partial_path, | |
to: :superclass | |
end | |
# Not great to monkeypatch this, but it's the least amount of code | |
# that does what we want. The issue here is #becomes is super handy | |
# with gerunds, but the instance returned from #becomes has @attributes | |
# literally as a pointer to the original attributes hash. This is a | |
# problem if our subclass defines more attributes with the ::attribute method | |
# - assignment won't work because the parent instance attributes won't have | |
# the subclass' attributes defined. This monkeypatch fixes that, just in time | |
# for anything that depends on these attributes, including `after_initialize` | |
# callbacks. | |
def _run_initialize_callbacks | |
extra_attrs = self.class._default_attributes.keys - @attributes.keys | |
# Copy over any extra attributes from the gerund to the original attribute | |
# set. Personally I'd like it to be a new set of attributes, but our code | |
# accidentally depends on this in a couple places, and I guess I'd rather not | |
# have it two ways so let's do it the way Rails ships with. | |
extra_attrs.each do |attr| | |
@attributes.instance_variable_get(:@attributes)[attr] = | |
self.class._default_attributes.instance_variable_get(:@attributes)[attr] | |
end | |
super | |
end | |
# GlobalID depends on the model to get class_name, rather than passing | |
# it in directly. Maybe someday we can try to get GlobalID to be easier | |
# to work with in this way; until then, this works. | |
def to_global_id(*args) | |
becomes(self.class.superclass).to_global_id(*args) | |
end | |
alias :to_gid :to_global_id | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment