Composing Rails models, in four steps
This demo is a bulletin board and a small marketplace built with ECS Rails. Every model is an identity row composed from standard components, and every table came from one install migration. Follow the steps; each opens a live page. You are acting as Ada Lovelace, and can switch at the top.
-
1 · Compose
A person is a list of parts
No table was written for
User. Each line is a component from the catalogue, stored in a table it shares with every other entity. A prefix adds a second copy under a named slot.class User < ApplicationEntity component Name component Email component Address, prefix: :shipping component Address, prefix: :billing component Geolocation, prefix: :shipping marker :moderator has_many :orders, via: :customer endOpen Ada Lovelace's profile and fill in an address. Step 4 picks it up.
-
2 · Use
Shop with the same parts
A product reuses
Text,Counterand the rest under its own slots. The market's filters and sorting query by those components.class Product < ApplicationEntity component Text, prefix: :title component Money, prefix: :price component Counter, prefix: :stock component Rating relates_to :seller, Company endOpen the market, add something to your basket and check out. The card is simulated: use
4242 4242 4242 4242. -
3 · Inspect
An invoice keeps copies, not links
Checkout copies the billing address and totals onto the invoice. Its
Addressis the same component a person has, so a later change to the person leaves the invoice as it was.class Invoice < ApplicationEntity component Identifier, prefix: :invoice_number component Money, prefix: :total component Address, prefix: :billing relates_to :order, Order, unique: true end -
4 · Extend
One system for every kind of entity
The geocoder reads the
addressestable, so it handles a seller's address and a customer's shipping address in one pass without naming either class. Declaring the pair is all an entity does to take part. No migration was needed.# in Demo::Geocoder: every address, whatever owns it Address.where.not(current.arel.exists) # in Company, beside its address component GeolocationRun the geocoder, then filter its map by entity type.
And the rest
The bulletin board has posts, comments and likes; groups use a join entity for membership; people carry moderator and admin markers. How it works explains each idea, and the source is on GitHub.