Class: EcsRails::Component
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- EcsRails::Component
- Includes:
- Lazy::Component
- Defined in:
- lib/ecs_rails/component.rb
Overview
An ordinary ActiveRecord model that belongs to exactly one entity.
Implements RFC-0003. See docs/architecture.md §1 for the invariants.
Host apps subclass this once, as ApplicationComponent, then subclass that per component type:
class ApplicationComponent < EcsRails::Component self.abstract_class = true end
class Email < ApplicationComponent validates :address, presence: true end
Email.where(verified: false) # queried directly; no join to entities
Email.first.entity # => #
A component owns its own table and knows nothing about entity subclasses. Scopes, validations, callbacks and associations all work as normal: this is a plain AR model that happens to hang off an entity.
The one sanctioned exception to “knows nothing about entity subclasses” is a
relationship component’s class_name: — see ADR-0006.
Instance Method Summary collapse
-
#ecs_dirty? ⇒ Boolean
included
from Lazy::Component
Does this component deserve a row?.
Instance Method Details
#ecs_dirty? ⇒ Boolean Originally defined in module Lazy::Component
Does this component deserve a row?
RFC-0006 defines dirty as “at least one attribute differs from its default”, explicitly not ActiveModel’s “differs from the last saved value”. Both halves of that matter, and neither is quite what it looks like:
-
ActiveModel’s dirty cannot be used for a component with no row. Building a virtual component sets entity_id, and ActiveModel counts that as a change — so
user.email.changed?is true for a component nobody has touched, and a cascade built on it would insert a row for every component ever read. That is the whole feature, inverted. -
RFC-0006’s own wording fails for the same reason, taken literally: entity_id differs from its column default (nil) on every virtual component too. The foreign key is identity, not state — it says which entity this is, never anything about it — so the comparison has to skip it, and the primary key with it. See #state_attribute?.
-
“Differs from default” is only the right question while there is no row. Once there is one, the question is ActiveModel’s, because the row now has a value to differ from: clearing an attribute back to its default is an UPDATE, and answering “not dirty” would silently discard it. This is where the two definitions genuinely part company, and the RFC’s destroy-then-reset case is the same split seen from the other side.