Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MONGOID-5734 Custom polymorphic types #5845

Merged
merged 8 commits into from
Jul 31, 2024

Conversation

jamis
Copy link
Contributor

@jamis jamis commented Jul 29, 2024

This PR adds support for custom polymorphic types via a global registry. While polymorphic type keys still default to the fully qualified name of the class, it is now possible to specify different aliases by which a polymorphic type might be identified in the database.

For example, consider a simple data model involving a manager, a department, and a team:

class Department
  include Mongoid::Document
  has_many :managers, as: :unit
end

class Team
  include Mongoid::Document
  has_one :manager, as: :unit
end

class Manager
  include Mongoid::Document
  belongs_to :unit, polymorphic: true
end

Each document in the managers collection will include two fields, unit_id and unit_type, where the unit_type field names the class (and thus, indirectly, the collection) of the unit that the manager is in charge of. For example:

team = Team.create
tina = Manager.new(unit: team)

Inspecting tina.unit_type would return the string "Team".

With this PR, programmers may now specify alternative keys to represent different classes, thus decoupling the code from the data. For example:

class Department
  include Mongoid::Document
  identify_as 'dept'
  has_many :managers, as: :unit
end

The identify_as 'dept' directive says that this class should be identified by the string "dept" in the database. You can specify multiple aliases, too (e.g. identify_as 'dept', 'div', 'agency'), in which case the first key is the "default", and the others are only used for looking up records. This lets you refactor your code without breaking the associations in your data.

It is important to note that these aliases are global. The keys you specify must be unique across your entire code base. However, it is possible to register alternate resolvers, which may be used for different subsets of your models. In this case, the keys must only be unique for each resolver. For example:

Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :eng
Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :purch

module Engineering
  class Department
    include Mongoid::Document
    identify_as 'dept', resolver: :eng
  end

  class Manager
    include Mongoid::Document
    belongs_to :unit, polymorphic: :eng
  end
end

module Purchasing
  class Department
    include Mongoid::Document
    identify_as 'dept', resolver: :purch
  end

  class Manager
    include Mongoid::Document
    belongs_to :unit, polymorphic: :purch
  end
end

Both Engineering::Department and Purchasing::Department are aliased as "dept", but use their own resolver, so there is no conflict.

Copy link
Contributor

@alexbevi alexbevi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API and developer experience LGTM

@jamis jamis merged commit 607a199 into mongodb:master Jul 31, 2024
58 checks passed
@jamis jamis deleted the 5734-custom-polymorphic-types branch July 31, 2024 20:58
jamis added a commit to jamis/mongoid that referenced this pull request Jul 31, 2024
* first pass at a global resolver registry

* tests

* fix problem with interpreting nested attribute data

* need to register subclasses, too

* raise custom exceptions when failing to resolve models

* fix specs to implement functional around(:context)

* trailing white space
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants