rom 5.0.0 and rom-sql 3.0.0 released

We're happy to announce the release of rom 5.0.0 and rom-sql 3.0.0. Core libraries have been upgraded to work with dry-types 1.0.0 and as a result, schema types can now benefit from all the new dry-types features. Many significant improvements have been introduced in rom-sql, let's take a look at some of the new features.

Support for arbitrary join conditions

It is now possible to set arbitrary join conditions via new DSL:

users.join(tasks) { |users:, tasks:|
  tasks[:user_id].is(users[:id]) & users[:name].is('John')
}

This will produce the following SQL:

SELECT "users"."id", "users"."name"
FROM "users"
INNER JOIN "tasks" ON (("tasks"."user_id" = "users"."id") AND ("users"."name" = 'John'))
ORDER BY "users"."id"

Support for CASE statements

We've also made it simple to construct CASE statements, using the following DSL:

users.select_append {
  id.case(1 => string('one'), else: string('something else')).as(:one_or_else)
}

This will produce the following SQL:

SELECT
  "users"."id",
  "users"."name",
  (CASE "users"."id" WHEN 1 THEN 'one' ELSE 'something else' END) AS "one_or_else"
FROM "users"
ORDER BY "users"."id"

Support for exists in Projection DSL

Support for Relation#exists was added in 2.0.0, now it is also available within the Projection DSL. Here's an example:

users.select_append { |posts: |
  exists(posts.where(posts[:user_id] => id)).as(:has_posts)
}

This will produce the following SQL:

SELECT
  "users"."id",
  "users"."name",
  (EXISTS (SELECT "tasks"."id", "tasks"."user_id", "tasks"."title" FROM "tasks" WHERE ("tasks"."user_id" = "users"."id") ORDER BY "tasks"."id")) AS "has_tasks"
FROM "users"
ORDER BY "users"."id"

Improved pluck support

You can now select more than one attribute when using Relation#pluck:

users.pluck(:id, :name)
# [[1, "Jane"], [2, "Joe"], [3, "Jane"], [4, "John"]]

What's next?

Originally we had an ambitious roadmap for 5.0.0 release, but its scope was simplified so that we could ship it faster, otherwise people would be blocked with upgrading for too long. This means that everything else that didn't get into 5.0.0 will be either implemented in 5.x series, or 6.0.0.

You can also expect new releases of rom-elasticsearch, rom-http and rom-yaml adapters soon.

Release information and upgrading

This is a major release with breaking changes. Please refer to the upgrade guide for more information. As part of 5.0.0, following gems have been released:

If you're having problems with the upgrade, please ask questions on discussion forum or our community chat.


Announcing rom-factory

We're happy to announce a new project that we've been working on - rom-factory. The project was originally started by Jānis Miezītis back in 2016, and then it was moved to rom-rb organization in March 2017. As you can probably guess, rom-factory is a data generator library, similar to FactoryBot (previously known as FactoryGirl) or Fabrication. It's built on top of rom-rb and has a sweet integration with Faker gem.

Let's see how it looks like.

Factories

In rom-factory you can define as many factories as you want, we do not store them under one singleton object. After you got rom container set up, you can easily configure a factory:

MyFactory = ROM::Factory.configure do |c|
  c.rom = your_rom_container
end

AnotherFactory = ROM::Factory.configure do |c|
  c.rom = your_rom_container
end

After setting up a factory, you can start defining your builders:

MyFactory.define(:user) do |f|
  f.name "Jane"
  f.email "jane@doe.org"
end

You can ask for an in-memory struct, in which case a primary key value will be auto-generated:

MyFactory.structs[:user]
# #<ROM::Struct::User id=1 name="Jane" email="jane@doe.org"

...or you can ask for a struct which will be persisted in your database:

MyFactory[:user]
# #<ROM::Struct::User id=1 name="Jane" email="jane@doe.org"

Dynamic values with sequences, re-using other values and faker

Having static values is often not enough, which is why rom-factory has a couple of neat features which allow you to define dynamic values. The first one is sequencing:

MyFactory.define(:user) do |f|
  f.sequence(:email) { |n| "user-#{n}@rom-rb.org" }
end

You can also re-use values from other attributes:

MyFactory.define(:user) do |f|
  f.name "Jane"
  f.email { |name| "#{name}@rom-rb.org" }
end

We also added support for faker, which makes defining builders more concise:

MyFactory.define(:user) do |f|
  f.name { fake(:name) }
  f.email { fake(:internet, :email) }
  f.age { fake(:number, :between, 10, 100) }
end

Associations

Currently has_many, has_one and belongs_to are supported. Here are a couple examples:

MyFactory.define(:user) do |f|
  f.name { fake(:name) }
  f.email { fake(:internet, :email) }

  # this will use :group builder to create a group for a user
  f.association(:group)

  # this will create 2 posts for a user
  f.association(:posts, count: 2)
end

Extending existing builders

You can define a builder by extending another one, for example you may have a user and an admin, which sets admin attribute to true:

MyFactory.define(:user) do |f|
  f.name { fake(:name) }
  f.email { fake(:internet, :email) }
  f.age { fake(:number, :between, 10, 100) }
  f.admin false
end

MyFactory.define(admin: :user) do |f|
  f.admin true
end

Status & Roadmap

This is still in beta phase, current release is 0.5.0. We're planning to turn this into a pure data generator which doesn't assume any specific persistence backend (currently it uses and requires rom-core). On top of this, we want to add support for persistence backends. Once this is done, we'll have 1.0.0 ready.

For now, give it a try and tell us what you think. If you have any questions or problems, reach out on our discussion forum.

Useful links:


  • 2 of 9