ROM 5.1 released
We're happy to announce the release of the rom suite 5.1.0! This release includes a couple of bug fixes and mostly focuses on improvements in the ROM::Transformer
API and adds plugin APIs to ROM::Repository
and ROM::Changeset
. Let's take a look at these nice additions!
Improved Transformer API
Currently ROM
has two different APIs for defining custom mappers. One, called ROM::Mapper
, is the old API that goes back to the very early days of the project. In version 4.0
a new API was introduced called ROM::Transformer
, with the intention to eventually replace ROM::Mapper
as Transformer API is more straight-forward and powerful than the original one.
In 5.1.0
we've made significant improvements that make using transformer-based mappers even better than before. Here's how it looks like in rom 5.1.0
with its new map
method:
class JSONSerializer < ROM::Transformer
map do
nest(:address, %i[city street zipcode])
deep_stringify_keys
end
end
You can easily check out how it works by simply creating an instance and calling it with some data as input:
json_serializer = JSONSerializer.new
json_serializer.(
[{ name: "Jane", city: "Cracow", street: "Street 1/2", zipcode: "12-345" }]
)
# => [{"name"=>"Jane",
# "address"=>{"city"=>"Cracow", "street"=>"Street 1/2", "zipcode"=>"12-345"}
# }]
Another big improvement is support for instance methods as mapping functions. Sometimes you need to access mapper's state in order to perform some mapping, this is where instance methods come in handy:
class JSONSerializer < ROM::Transformer
map do
# map zipcode using the corresponding instance method
map_value(:zipcode, &:normalize_zipcode)
nest(:address, %i[city street zipcode])
deep_stringify_keys
end
def normalize_zipcode(zipcode)
# do whatever you need
end
end
Transformers are typically registered within a rom container, this part has been simplified too and you can now do it in one line, which is consistent with other rom components. If we wanted to register our JSONSerializer
to be a mapper used by the users
relation, we could simply do this now:
class JSONSerializer < ROM::Transformer
relation :users, as: :json
map do
nest(:address, %i[city street zipcode])
deep_stringify_keys
end
end
Then the transformer will be available under :json
identifier:
# assuming `users` is our relation
users.map_with(:json).to_a
Plugin API for Repository and Changeset
Starting from rom 5.1.0
you can write plugins for repositories and changesets. This is a huge improvement that you should find useful whether you're integrating rom with your library or just want to DRY-up code in an application. Currently there are no built-in plugins for repositories or changesets, but it's very likely we'll be adding some in the near future.
Here's an example of a repository plugin that sets "default scope" for a repository root relation:
module DefaultScope
class ScopedRelation < Module
attr_reader :name, :view
def initialize(name, view)
@name = name
@view = view
define_relation_reader
end
private
def define_relation_reader
relation_view = view
define_method(name) do
super().public_send(relation_view)
end
end
end
def self.apply(repo, view:)
repo.prepend(ScopedRelation.new(repo.root, view))
end
end
ROM.plugins do
register :default_scope, DefaultScope, type: :repository
end
Now we can enable our plugin in a root repository:
class PostRepo < ROM::Repository[:posts]
use :default_scope, view: :published
end
That's it - the repository will always use posts scoped to its published
view.
Release information
This is a backward-compatible release, which means upgrading should not break your code. If you have any issues please report them. Please refer to 5.1.0 CHANGELOG for more information.