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

Scoped attribute keys with a generated prefix #125

Closed
wants to merge 4 commits into from

Conversation

lewispb
Copy link
Contributor

@lewispb lewispb commented Aug 13, 2023

Closes #124

It's common for models to "belong" to a parent model, such as a user or identity. Previously scoping Redis keys to a parent would require custom key generation. With this change it's possible to scope the attribute keys to the parent model, so that the keys can be easily identified and deleted in case of a user deletion. This scoping takes the form of a prefix, e.g.: model_name:1

Here's some real-ish examples from HEY, where it's common for classes to relate to a parent class.

Before

class Identity::Training
  kredis_enum :status, values: %w[ waiting confirmed ], default: nil, key: ->(t) { "identity:#{t.identity.id}:training:status" }
  kredis_set :trained_with_creator_ids, typed: :integer, key: ->(t) { "identity:#{t.identity.id}:training:trained_with_creator_ids" }

class Redacted::Requirement
  kredis_flag :bypassed, key: ->(r) { "identities/#{r.identity.id}/redacted/bypass" }

class Widget::Selection
  kredis_set :selected_widgets, typed: :integer, key: ->(selection) { "identity:#{selection.identity.id}:selected_widgets" }

Notice the inconsistent key construction, : vs /, identities vs identity namespace, and varying namespacing for the class - training but no widget.

Time for some conventions:

After

class Identity::Training
  kredis_enum :status, values: %w[ waiting confirmed ], default: nil, scope: :identity
  kredis_set :trained_with_creator_ids, typed: :integer, scope: :identity

class Redacted::Requirement
  kredis_flag :bypassed, scope: :identity, key: "redacted/bypass"

class Widget::Selection
  kredis_set :selected_widgets, typed: :integer, scope: :identity, key: "selected_widgets"

scope is a model attribute-only option, and it takes a string, proc, or symbol. A string or a proc simply return a prefix, but the symbol is sent and the return value assumed to be a model with an appropriate #id.

cc @jeremy @jorgemanrubia

It's common for models to belong a parent model, such as a user. We can
now scope the attribute keys to the parent model, so that the keys can
be easily identified and deleted in case of a user deletion.
@lewispb lewispb changed the title Support scoping attribute keys with a generated prefix Scoped attribute keys with a generated prefix Aug 13, 2023
@dhh dhh requested a review from jeremy October 19, 2023 17:40
Copy link
Member

@jeremy jeremy left a comment

Choose a reason for hiding this comment

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

When we walked through usage in HEY, we noted that each was a helper class that wrapped another entity yet declared Kredis attributes on itself, e.g. Identity::Training.new(identity), so what we really want to say is "Kredis attributes are part of this other model" with, perhaps, an added intermediate namespace to indicate that this wrapper class declared the attributes.

For example:

class Identity::Training
  include Kredis::Attributes

  attr_reader :identity

  kredis_enum :status, values: %w[ waiting confirmed ], default: nil, key: ->(t) { "identity:#{t.identity.id}:training:status" }
  kredis_set :trained_with_creator_ids, typed: :integer, key: ->(t) { "identity:#{t.identity.id}:training:trained_with_creator_ids" }

becomes

class Identity::Training
  include Kredis::Attributes

  attr_reader :identity

  kredis_enum :status, values: %w[ waiting confirmed ], default: nil, belongs_to: :identity
  kredis_set :trained_with_creator_ids, typed: :integer, belongs_to: :identity

or even something like a general declaration:

class Identity::Training
  include Kredis::Attributes

  attr_reader :identity

  kredis_belongs_to :identity
  kredis_enum :status, values: %w[ waiting confirmed ], default: nil
  kredis_set :trained_with_creator_ids, typed: :integer

However… this also indicates that perhaps these attributes should live within that model in the first place, obviating these declaration-at-a-distance effects. Then the Identity::Training class would simply use identity.training_status and identity.trained_with_creator_ids.

All told, I think we should pursue a different approach here, but only after refactoring HEY to move Kredis attributes to cozier homes.

@jeremy jeremy closed this Feb 28, 2024
@jeremy jeremy deleted the scoped-keys branch July 10, 2024 20:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Feature: delegate ID
2 participants