an open-source
persistence and mapping
toolkit for Ruby built for
speed and simplicity
Slide 10
Slide 10 text
Database-agnostic
Slide 11
Slide 11 text
Flexible
Slide 12
Slide 12 text
Extendible
Slide 13
Slide 13 text
Fast
Slide 14
Slide 14 text
Simple
Slide 15
Slide 15 text
Sounds like some crazy object relational mapper, eh?
Slide 16
Slide 16 text
rom-rb
is not
an ORM
Slide 17
Slide 17 text
Typical ORM
!
that we DO NOT HAVE
→ Relying on mutable objects
→ Complex association model
→ Leaky abstractions
→ [ActiveRecord] tight coupling with database schema
→ [ActiveRecord] n+1 query problem
→ [ActiveRecord] callbacks
→ [ActiveRecord] huge API
Slide 18
Slide 18 text
Why does it even exist?
Slide 19
Slide 19 text
A TRUE ALTERNATIVE to Active Record
Slide 20
Slide 20 text
rom-rb provides a way to
separate persistence concerns from
application domain
Slide 21
Slide 21 text
What's the
deal with
rom-rb 4.0?
(aka: serious business)
Slide 22
Slide 22 text
Big, BIG,
challenge
Slide 23
Slide 23 text
Make rom-rb simple to use
(comparable with Active Record)
Slide 24
Slide 24 text
What's so awesome about Active Record?
Slide 25
Slide 25 text
No boilerplate
class User < ActiveRecord::Base
end
Slide 26
Slide 26 text
Persistence, so simple
user = User.create(name: "Jane")
Slide 27
Slide 27 text
Queries, so simple
User.where(name: "Jane")
Slide 28
Slide 28 text
Making changes, so simple
User.where(name: "Jane").update_all(name: "Jane Doe")
Slide 29
Slide 29 text
Ease of use
→ Little code to write to get started
→ A lot of functionality OOTB
→ No boilerplate
Slide 30
Slide 30 text
This was a real challenge for rom-rb!
Slide 31
Slide 31 text
Dynamic query interface
not tied to the database schema
Slide 32
Slide 32 text
Explicit representation
of data structures returned by relations
Slide 33
Slide 33 text
Mapping to struct objects
decoupled from the database
Slide 34
Slide 34 text
No concept of lazy-loadable associations
Slide 35
Slide 35 text
This is fine
Slide 36
Slide 36 text
We made it
(⊃ꙏ•́‿•̀
ꙏ)⊃━☆ꚍ.*ꙓꙏꚍ
Slide 37
Slide 37 text
Relations and Structs
Slide 38
Slide 38 text
class Users < ROM::Relation[:sql]
schema(infer: true)
end
class Users < ROM::Relation[:sql]
schema(infer: true) do
associations do
has_many :tasks
end
end
end
Slide 60
Slide 60 text
users.combine(:tasks).first
# => #
# ]>
Slide 61
Slide 61 text
module Entities
class User < ROM::Struct
def has_tasks?
!tasks.empty?
end
end
end
user = users.combine(:tasks).first
user.has_tasks?
# true
Slide 62
Slide 62 text
user = users.first
user.has_tasks?
# ROM::Struct::MissingAttribute:
# undefined method `tasks' for
# # (attribute not loaded?)
Slide 63
Slide 63 text
What does this mean, really?
Slide 64
Slide 64 text
→ Dynamic query interface is maintained
→ Custom methods are maintained
→ Objects decoupled from the database are
maintained too
Slide 65
Slide 65 text
So, this is awesome
Slide 66
Slide 66 text
Ability to start simple
without even defining any struct classes
Slide 67
Slide 67 text
Ability to provide your own methods
that rely on various attributes in various contexts
Slide 68
Slide 68 text
AR-like convenience without tight coupling
with the database
Slide 69
Slide 69 text
BUT...
Slide 70
Slide 70 text
This is not enforced by the library!
Slide 71
Slide 71 text
At any point in time, you can define structs
with explicit attributes
and ask rom-rb to load them
Slide 72
Slide 72 text
You can establish different models
based on various domain concepts
Slide 73
Slide 73 text
However...this requires time
and good understanding of
the application domain
Slide 74
Slide 74 text
It's a process and rom-rb fully supports it
Slide 75
Slide 75 text
show-off
mode: on
Slide 76
Slide 76 text
10 things you can do with rom-rb
Slide 77
Slide 77 text
…that Active Record can't do
Slide 78
Slide 78 text
1. Use it with non-SQL databases
Slide 79
Slide 79 text
class Users < ROM::Relation[:mongo]
schema do
attribute :_id, Types::ObjectID
attribute :name, Types::String
end
end
Slide 80
Slide 80 text
2. Define cross-database associations
Slide 81
Slide 81 text
class Users < ROM::Relation[:sql]
schema(infer: true) do
associations do
has_many :tasks, override: true, view: :for_users
end
end
end
class Tasks < ROM::Relation[:yaml]
gateway :external
schema(infer: true)
def for_users(users)
tasks.restrict(UserId: users.pluck(:id))
end
end
Slide 82
Slide 82 text
class Users < ROM::Relation[:sql]
schema(infer: true) do
associations do
has_many :tasks, override: true, view: :for_users
end
end
end
class Tasks < ROM::Relation[:yaml]
gateway :external
schema(infer: true)
def for_users(users)
tasks.restrict(UserId: users.pluck(:id))
end
end
Slide 83
Slide 83 text
3. Map to any model
Slide 84
Slide 84 text
class MyUser < MySuperModelLibrary
end
users.map_to(MyUser)
Slide 85
Slide 85 text
4. Project data easily and get back convenient structs
Slide 86
Slide 86 text
posts.
select(:title).
select_append { meta.get_text(:comment_count, :integer).as(:comments) }.
first
# #
8. Use it with legacy schemas
without yelling at your screen too much
Slide 94
Slide 94 text
class Users < ROM::Relation[:sql]
schema(:SomehorriblyNamedUseRtable, as: :users) do
attribute :UserIdentifier, Serial.meta(alias: :id)
attribute :UserName, String.meta(alias: :name)
end
end
users.first
# #
Slide 95
Slide 95 text
9. Define custom data mappers
Slide 96
Slide 96 text
class EncryptionMapper < ROM::Mapper
register_as :encryption
def call(relation)
relation.map { |tuple|
# do whatever you want
}
end
end
users.map_with(:encryption)
Slide 97
Slide 97 text
10. Use changesets to transform data
before passing it to the database
Slide 98
Slide 98 text
class NewUser < ROM::Changeset::Create
map do
rename_keys user_name: :name
end
end
users.changeset(NewUser, user_name: "Jane").commit
# #