Getting Started » Rails Setup
Rails integration is provided by rom-rails project. Simply add it to your Gemfile:
gem 'rom-rails'
Configuring Railtie
Create a rom initializer:
# config/initializers/rom.rb
ROM::Rails::Railtie.configure do |config|
config.gateways[:default] = [:sql, ENV.fetch('DATABASE_URL')]
end
You can provide additional adapter-specific options, for example you can enable specific sql plugins for postgres:
# config/initializers/rom.rb
ROM::Rails::Railtie.configure do |config|
config.gateways[:default] = [:sql,
ENV.fetch('DATABASE_URL'), extensions: [:pg_hstore]
]
end
You can also provide a list of relations that should not be inferred from your schema automatically:
# config/initializers/rom.rb
ROM::Rails::Railtie.configure do |config|
config.gateways[:default] = [:sql,
ENV.fetch('DATABASE_URL'), not_inferrable_relations: [:schema_migrations]
]
end
Migration Tasks
The railtie provides rake tasks for managing your database schema. You need to
enable them in your Rakefile
:
require 'rom/sql/rake_task'
After that, you have access to following tasks:
rake db:create_migration[migration_name]
- creates a new migration filerake db:migrate
- runs pending migrationsrake db:clean
- cleans the databaserake db:reset
- drops tables and re-runs migrations
Accessing Container
In Rails environment ROM container is accessible via ROM.env
:
ROM.env # returns the container
In your controllers you can access ROM container by rom
variable:
class UsersController < ApplicationController
def show
@user = rom.relation[:users].by_id(params[:id]).one
end
end
Accessing the global container directly is considered as a bad practice. The recommended way is to use a DI mechanism to inject specific ROM components as dependencies into your objects.
For example you can use dry-container and dry-auto_inject to define your own application container and specify dependencies there to have them automatically injected.
See rom-rails-skeleton for an example of such setup.
Defining Relations
Relation class definitions are automatically loaded from app/relations
.
The following code defines a users
relation for the :sql
adapter:
class Users < ROM::Relation[:sql]
# some methods
end
# access registered relation via container
ROM.env.relations[:users]
Defining Commands
Command class definitions are automatically loaded from app/commands
.
The following code defines a command which inserts data into users
relation:
# app/commands/create_user.rb
class CreateUser < ROM::Commands::Create[:sql]
relation :users
register_as :create
result :one
end
# access registered relation via container
ROM.env.commands[:users][:create]
Defining Custom Mappers
If you want to use custom mappers you can place them under app/mappers
:
# app/mappers/user_mapper.rb
class UserMapper < ROM::Mapper
relation :users
# some mapping logic
end
Running alongside ActiveRecord
There might be some cases where you will want to run ROM alongside ActiveRecord. Since ROM is designed to work independently, you will need to take few additional steps.
ROM creates its own connections and Rails above version 5 won't allow you to drop the database since there are active connections on it.
# lib/tasks/db.rake
task :remove_rom_connection => [:environment] do
ROM.env && ROM.env.disconnect
end
Rake::Task["db:drop"].clear_prerequisites()
Rake::Task["db:drop"].enhance [:remove_rom_connection, :load_config, :check_protected_environments]
Rake::Task["db:reset"].clear_prerequisites()
Rake::Task["db:reset"].enhance [:remove_rom_connection, "db:drop", "db:setup"]
Since migrations (and other) tasks require environment, ROM will be loaded and will throw an exception, because relations will try to load tables before migrations have actually run. We know this is an ugly solution, but we are working hard to solve this case. This monkey patch will give you reasonable information to act upon if the necessity arises.
# config/initializers/rom_monkey.rb
module ROM
module Rails
class Railtie < ::Rails::Railtie
alias_method :create_container!, :create_container
def create_container
begin
create_container!
rescue => e
puts "Container failed to initialize because of #{e.inspect}"
puts "This message comes from the monkey patch in #{__FILE__}, if you are using rake, then this is fine"
end
end
end
end
end