Advanced Topics » Standalone Commands
Writing records to a persistence solution is done through Commands. Commands are objects that encapsulate datastore-specific modification operations. They receive a relation and execute their operation using that relation.
ROM adapters can provide three basic Command types:
- ROM::Commands::Create
- ROM::Commands::Update
- ROM::Commands::Delete
Some adapters provide more for special operations - see your adapter’s documentation for details.
Defining Commands
In your setup, define available commands with the commands statement, passing
it a symbol of the relation's name:
require 'rom-repository'
# Assumes a database with a users table
ROM.container(:sql, 'sqlite::memory') do |config|
 # Remember that many adapters can infer relations, so we can often skip defining it.
 # Otherwise, add something like:
 #
 # config.relation(:users)
 config.commands(:users) do
    # declares that we can create, update, and delete users.
    define(:create)
    define(:update)
    define(:delete)
 end
end
Single Results vs Many Results
Ordinarily, a command will return an Array of Hash objects. You can
customize this to return a single result by supplying result :one to the
command definition.
ROM.container(:sql, 'sqlite::memory') do |config|
  config.commands(:users) do
    define(:create) do
       result :one
    end
    # ... etc
  end
end
Executing Commands
Every registered command is accessible through the environment container:
rom_container = ROM.container(:sql, 'sqlite::memory') do |config|
  config.relation(:users) do
    def by_id(id)
      where(id: id)
    end
    def by_first_name(name)
      where(first_name: name)
    end
  end
  config.commands(:users) do
    define(:create)
  end
end
# fetch the command object by type
create_user = rom_container.commands[:users][:create]
# a command object won’t do anything until it’s called with input tuples:
new_users = [
  { name: 'Jane' },
  { name: 'Joe' }
]
create_user.call(new_users) # saves the user tuples
Commands are backed by a relation, which you can use to specify which records to Update or Delete.
# operate on a single result
delete_user = rom_container.commands[:users][:delete]
delete_user.by_id(2).call
# or many
delete_users = rom_container.commands[:users][:delete]
delete_users.by_first_name('Lawrence').call
# update is the same, and takes the new data as a parameter to #call
update_user = rom_container.command[:users][:update]
update_user.by_id(7).call(first_name: 'Kaylee', last_name: 'Frye')
Full Example
This short example demonstrates defining and using the three basic commands.
# app.rb
class MyApp
  def self.run(rom_container)
    user_commands = rom_container.commands[:users]
    # Create...
    user = user_commands[:create].call(first_name: 'Natalia', last_name: 'Romanova')
    puts rom_container.relation(:users).to_a.inspect
    # => [{:first_name=>"Natalia", :last_name=>"Romanova"}]
    # Update...
    user_commands[:update].by_id(user[:id]).call(first_name: 'Natasha', last_name: 'Romanoff')
    puts rom_container.relation(:users).to_a.inspect
    # => [{:first_name=>"Natasha", :last_name=>"Romanoff"}]
    # And Delete!
    user_commands[:delete].by_id(user[:id]).call
    puts rom_container.relation(:users).to_a.inspect
    # => []
  end
end
# command_demo.rb
require 'rom-sql'
# Assumes a database with a users table
rom_container = ROM.container(:sql, 'sqlite::memory') do |config|
  config.use :macros
  config.relation(:users) do
    def by_id(id)
      restrict(id: id)
    end
  end
  config.commands(:users) do
    define(:create) do
       result :one
    end
    define(:update)
    define(:delete)
  end
end
MyApp.run(rom_container)
Run it and see the three states print out.
ruby command_demo.rb