rails
rails copied to clipboard
Introduce `alias_association` feature
Motivation / Background
Implements - https://discuss.rubyonrails.org/t/feature-proposal-alias-association/83439/4
Rails 7.1 deprecated using alias_attribute
for anything other than an attribute. Many applications end up using alias_attribute
to alias associations and removing such usages would be challenging in many cases. Recognizing this, Rails introduces a new feature to alias associations called alias_association
.
Detail
Given an alias like BlogPost.alias_association :author, :user
where author
is an alias and user
is the original association name of the belong_to :user
association the feature provides the following capabilities:
Aliased association methods
Defines association reader and writer: BlogPost#author
and BlogPost#author=
(belongs_to
only) Defines build_#{assoc_name}
and create_#{assoc_name}
constructors: BlogPost#build_author
, BlogPost#create_author
and BlogPost#create_author!
(belongs_to
only) Defines change tracking methods: BlogPost#author_changed?
and BlogPost#author_previously_changed?
Aliases support in where()
clauses
BlogPost.where(author: User.find_by(name: "Nikita")).to_a
#=> searches by user_id
Aliases support in includes()
clauses
BlogPost.all.includes(:author).to_a
# => preloads user
association
Implementation details
Aliased association methods
A new define_association_methods
method was defined on ActiveRecord::Associations::Builder::Association
builder. Its job to define association-related instance methods. This method is being reused in the original build
method.
define_accessors
, define_change_tracking_methods
and all downstream methods now accept as:
to serve as a publicly facing name of the new methods while the method body itself will point to the original association.
Since alias_association
methods needs to know which builder to use for method definition builders now self-register themselves into a registry using register_builder_for
method by providing a macro
which later can be used to fetch the builder for a given reflection.
Aliases support in where()
clauses
To support aliased association names in where()
clauses we introduced querying_aliases
storage which will serve as a registry of aliases for both associations and attributes. This way we can avoid checking two places (attribute aliases & association aliases) when processing where()
arguments.
Aliases support in includes()
clauses
To support aliases in includes()
(and most likely more capabilities that look into the _reflections
hash) we register the same reflection under the new name
https://github.com/rails/rails/blob/8917147631838b5c5b85240a61027b39f3adbde2/activerecord/lib/active_record/associations.rb#L1900
Additional information
The capabilities this feature provides are pretty standalone and could be extracted or completely descoped from this PR if needed.
Also while the utility of this feature might be concerning the feature comes at no cost for applications that are not interested in utilizing it.
Checklist
Before submitting the PR make sure the following are checked:
- [x] This Pull Request is related to one change. Changes that are unrelated should be opened in separate PRs.
- [x] Commit message has a detailed description of what changed and why. If this PR fixes a related issue include it in the commit message. Ex:
[Fix #issue-number]
- [x] Tests are added or updated if you fix a bug or add a feature.
- [x] CHANGELOG files are updated for the changed libraries if there is a behavior change or additional feature. Minor bug fixes and documentation changes should not be included.