Core » Mappers

Mappers are used to process relation data, this may involve merging results from multiple relations into nested data structures or instantiating custom objects. Relations generate their mappers automatically for most common use cases, but mappers are separated from relations, which means you can always define your own mappers, whenever you have the need.

Default relation mappers

Relations are configured to map automatically to plain hashes by default. When you're using relations via repostories, they are configured to map to ROM::Struct by default, and you can define custom struct namespace, if you want your own objects to be instantiated instead.

Here's how default mapping looks like, assuming you have a users relation available:

class Users < ROM::Relation[:sql]
  schema(infer: true) do
    has_many :tasks
  end
end

users.by_pk(1).one
=> {:id=>1, :name=>"Jane"}

users.by_pk(1).combine(:tasks).one
=> {:id=>1, :name=>"Jane", :tasks=>[{:id=>1, :user_id=>1, :title=>"One"}, {:id=>2, :user_id=>1, :title=>"Two"}]}

Using custom mappers

A mapper can be any object which responds to #call, which accepts a relation and return an array with results back. This means a simple proc will be just fine:

user_name_mapper = -> users { users.pluck(:name) }

user_names = users >> user_name_mapper

user_names.to_a
=> ["Jane", "John"]

Typically though, custom mappers will be used in more complex cases, when the underlying database doesn't provide enough functionality that's needed to get desired data structures. In such cases, you can define mapper classes and configure mapping there.

class MyMapper < ROM::Transformer
  relation :users
  register_as :my_mapper

  map_array do
    # define custom transformations here
  end
end

With a custom mapper configured, you can use Relation#map_with interface to send relation data through your mapper:

users.map_with(:my_mapper).to_a

ROM::Transformer is powered by transproc.

Learn more