Skip to content

Coming from Rails

ORM::ActiveRecord ports Active Record to Raku concept-for-concept. If you know Rails, most of what you know carries over once you adjust for two Raku rules about method names.

The two naming rules

Raku method names cannot end in ! or ?, and the ecosystem prefers hyphens to underscores:

  • A bang method drops the ! and gains a -bang suffix: save!save-bang.
  • A predicate drops the ? and gains an is- prefix: valid?is-valid, persisted?is-persisted, new_record?is-new-record.
  • Everything else swaps _ for -: find_byfind-by, has_manyhas-many, update_allupdate-all.

That is most of the translation. The tables below cover the rest.

Models and tables

Rails ORM::ActiveRecord
class User < ApplicationRecord class User is Model { }
self.table_name = 'people' method table-name { 'people' }
belongs_to :author self.belongs-to: author => class-name => 'Author'
has_many :posts self.has-many: posts => class-name => 'Post'
has_one :profile self.has-one: profile => class-name => 'Profile'
has_and_belongs_to_many :tags self.has-and-belongs-to-many: tags => class-name => 'Tag'
has_many :through self.has-many: magazines => %(through => :subscriptions, ...)

Associations and validations are declared in submethod BUILD. See Models and the association cookbook.

Creating and updating

Rails ORM::ActiveRecord
User.create(attrs) User.create({ ... })
User.create!(attrs) User.create-bang({ ... })
user.save / user.save! user.save / user.save-bang
user.update(attrs) / update! user.update({ ... }) / update-bang
user.destroy / User.destroy_all user.destroy / User.destroy-all
user.persisted? / new_record? user.is-persisted / user.is-new-record
user.reload user.reload
user.touch user.touch

See Persistence.

Querying

Rails ORM::ActiveRecord
User.find(id) User.find(id)
User.find_by(name: 'x') User.find-by({ name => 'x' })
User.find_by!(...) User.find-by-bang({ ... })
User.where(active: true) User.where({ active => True })
.where.not(...) .not({ ... })
.order(:name) .order('name')
.limit(10).offset(20) .limit(10).offset(20)
.first / .last .first / .last
.count / .sum(:col) .count / .sum('col')
.pluck(:name) .pluck('name')
.exists? .exists
.update_all(...) / .delete_all .update-all(...) / .delete-all

A relation is lazy; it runs when you call .all / .perform, iterate it, or ask for a count. See Finders and Queries.

Eager loading

Rails ORM::ActiveRecord
.preload(:posts) .preload(:posts)
.includes(:posts) .includes(:posts)
.eager_load(:posts) .eager-load(:posts)
.references(:posts) .references(:posts)
.includes(posts: :comments) .preload(posts => :comments)

See the eager-loading cookbook.

Validations

Rails ORM::ActiveRecord
validates :name, presence: true self.validate: 'name', { :presence }
validates :age, numericality: { ... } self.validate: 'age', { numericality => { ... } }
validates_with MyValidator self.validates-with(MyValidator.new)
validates_each :a, :b do ... end self.validates-each: <a b>, -> $rec, $attr, $value { ... }
record.valid? / invalid? record.is-valid / record.is-invalid
record.errors.full_messages record.errors.full-messages

See Validations and the custom-validator cookbook.

Callbacks

Rails ORM::ActiveRecord
before_save :m self.before-save: -> { ... } or self.before-save: 'method-name'
after_create, after_update, ... self.after-create, self.after-update, ...
after_commit, after_rollback self.after-commit, self.after-rollback

See Callbacks.

Transactions

Rails ORM::ActiveRecord
ActiveRecord::Base.transaction { } DB.shared.transaction({ })
Model.transaction { } Model.transaction({ })
requires_new: true :requires-new
raise ActiveRecord::Rollback die X::Rollback.new
isolation: :read_committed :isolation<read_committed>

See the transactions cookbook.

Higher-level features

Rails ORM::ActiveRecord
enum status: { ... } self.enum: 'status', { ... } (Enums)
serialize :prefs, JSON self.serialize('prefs', JsonCoder.new) (Attribute types)
normalizes :email, with: ... self.normalizes('email', :with(-> $v { ... })) (Normalisation)
encrypts :ssn self.encrypts('ssn') (Encryption)
has_secure_password self.has-secure-password (Tokens & secure data)
STI type column Single-table inheritance
discard gem Soft deletes

Tooling

Rails ORM::ActiveRecord
rails db:migrate active-record db:migrate
rails db:rollback active-record db:rollback
rails db:seed active-record db:seed
rails g model User name:string active-record generate model User name:string
rails console / runner active-record console / active-record runner
rails dbconsole active-record dbconsole

See Database tasks, Generators, and Runtime tasks.

What's different

  • Hashes are passed explicitly: User.where({ active => True }), not bareword keyword arguments.
  • True / False / Nil replace true / false / nil.
  • A model class binds itself into GLOBAL (GLOBAL::<User> := User;) so that associations resolve by class name — the generators emit this for you.
  • There is no autoloading: use the modules and models you reference.