Version 4.0

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 file
  • rake db:migrate - runs pending migrations
  • rake db:clean - cleans the database
  • rake 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