rails icon indicating copy to clipboard operation
rails copied to clipboard

Introduce `alias_association` feature

Open nvasilevski opened this issue 7 months ago • 2 comments

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.

nvasilevski avatar Oct 26 '23 21:10 nvasilevski