Changelog
All notable changes to this project are documented here. The format follows Keep a Changelog and this project adheres to Semantic Versioning.
[Unreleased]
[0.2.2] — 2026-07-23
Added
- YARD API documentation across the whole public API —
@param,@return,@raise,@exampleand@seetags on every public module, class, attribute and method (100% documented, verified byyard stats). The existing prose rationale is kept; the tags are additive, so rubydoc.info/gems/ecs_on_rails now renders a real API reference rather than bare comments. - A
.yardopts, shipped in the gem so rubydoc.info honours it. Notably--embed-mixins, without which the DSL thatEcsRails::Entitygains byextending EcsRails::DSL,Querying,PreloadingandRelationshipswould not appear onEntityat all — which is where users look for it. documentation_uriin the gemspec, pointing at the rubydoc.info API reference. The narrative documentation (architecture, ADRs, RFCs) is reached via the homepage andsource_code_uri.
Fixed
changelog_uriin the gemspec pointed at<repo>/blob/main/CHANGELOG.md, which 404s: the gem lives ingem/of a monorepo and there is no changelog at the repo root, so the Changelog link on the 0.2.1 RubyGems page was broken. Gem metadata is immutable once published, so this needed a release to correct.source_code_urinow points at thegem/subtree rather than the repo root.
No code change — 0.2.2 is identical to 0.2.1 apart from gemspec metadata.
[0.2.1] — 2026-07-23
First RubyGems release.
Changed
- The gem is published as
ecs_on_rails. Bothecs-railsandecs_railsare unavailable: an unrelatedecs-railsgem already exists, and RubyGems treats-,_and case as equivalent when comparing names, so every spelling of “ecs rails” collides with it. -
No API change. The require path is still
ecs_rails, the module is stillEcsRails, and the generators are stillecs_rails:install,ecs_rails:componentandecs_rails:relationship. Only the name you put in your Gemfile differs:ruby gem "ecs_on_rails" # Gemfile require "ecs_rails" # everywhere elseA
lib/ecs_on_rails.rbshim requiresecs_rails, so a baregem "ecs_on_rails"works underBundler.require.
[0.2.0] — 2026-07-22
Adds cross-entity relationships. Demo-validated by the bulletin-board app.
Added
- Relationship query & preload sugar (RFC-0013).
Entity.with_related(:author, user)/without_related(:author)/includes_related(:author)query and preload a relationship by its declared name, so the backing component class never appears in application code. Thin sugar over the component verbs. - Relationship DSL (RFC-0012).
relates_to :author, Useron an entity declares a cross-entity link with no relationship component file — the DSL defines the backing component dynamically.post.author/post.author =reach the target;rails g ecs_rails:relationship Post author:Useremits the migration. Deleting the target nullifies the link; deleting the owner cascades.
[0.1.0] — 2026-07-20
Feature-complete, demo-validated. Not yet published to RubyGems. See the v0.1 retrospective.
Added
- Component preloading (RFC-0011).
Entity.includes_components(*Components)batches component loads (all declared, or a named subset) so a list view issues a bounded number of queries instead of one per component per row. A thin wrapper over ActiveRecord’s nativepreload, which already works with lazy components. - Component query DSL (RFC-0010).
Entity.with_component(Component, **conditions)/Entity.without_component(Component)query entities by which components they have, compiling to correlatedEXISTS/NOT EXISTSsubqueries that apply the entity-model scope automatically (a shared component can’t leak across entity types). Chainable with ordinary ActiveRecord. Avoids.with(AR’s CTEs). - Component presence (RFC-0009).
entity.add(Component)/entity.has?(Component)/entity.remove(Component)and a generatedentity.<component>?predicate, so marker components (Moderator, Administrator) — which carry no state and so are never persisted by the lazy save cascade — work. - Validation error merging (RFC-0007).
entity.valid?reflects its touched components’ validity; component errors merge underentity.errors[:"email.address"]and read naturally in a form. A non-dirty virtual component is not validated. - Method delegation (RFC-0005). Component methods and attribute accessors are
callable on the entity —
user.send_welcome_email,user.address = "x". Name clashes between two components raiseDelegationConflictat load time;except:/only:are the escape hatch. - Lazy / virtual components (RFC-0006).
entity.emailalways returns anEmail, nevernil— a missing row yields an in-memory component with every attribute at its database default.entity.savecascades to the components you touched, inserting a row only for those that are dirty, in one transaction. Reading a component costs aSELECTand nothing else. - The
componentDSL (RFC-0004).component Nameon an entity declares what it is composed from, generates the reader, and wires thehas_one. ApplicationComponent(RFC-0003) and entity subclass resolution — a loaded entity comes back as its real subclass (User, notApplicationEntity).ApplicationEntity(RFC-0001) — immutable identity rows in one sharedentitiestable, discriminated bymodel.- The component registry (RFC-0002), reload-safe by keying on class name.
ecs_rails:installandecs_rails:componentgenerators (RFC-0008).- Gem scaffold, MIT licence, and RSpec + PostgreSQL test harness.