I'm trying out http://rom-rb.org/ and can't figure out how to get a presence validation to pass in the presence of multiple source models. I would expect the following script to save a new event and organiser, but instead it says that event_name
is not present.
What am I missing?
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'rom'
gem 'rom-sql'
gem 'rom-rails'
gem 'activemodel'
gem 'sqlite3'
gem 'activesupport'
end
require 'rom'
require 'rom-rails'
`rm -Rf /tmp/romtest.sqlite`
ROM.setup(:sql, 'sqlite:///tmp/romtest.sqlite')
class Events < ROM::Relation[:sql]
end
class Organisers < ROM::Relation[:sql]
end
class CreateEvent < ROM::Commands::Create[:sql]
relation :events
register_as :create
result :one
associates :organiser, key: [:organiser_id, :id]
end
class CreateOrganiser < ROM::Commands::Create[:sql]
relation :organisers
register_as :create
result :one
end
class CreateEventWithOrganiser < ROM::Model::Form
commands organisers: :create, events: :create
input do
attribute :email
attribute :event_name
end
validations do
validates :event_name, presence: true
end
def commit!
command = organisers.create.with(
email: email,
) >> events.create.with(
name: event_name,
)
command.transaction do
command.call
end
end
end
ROM.finalize
rom = ROM.env
gateway = rom.gateways.fetch(:default)
migration = gateway.migration do
change do
create_table :organisers do
primary_key :id
column :email, String, null: false
end
create_table :events do
primary_key :id
column :name, String, null: false
column :organiser_id, Integer, null: false
end
end
end
migration.apply(gateway.connection, :up)
f = CreateEventWithOrganiser.build(
email: 'test@example.com',
event_name: 'Test Event'
)
# Unexpectedly fails
f.save
puts f.errors.full_messages
# => "Event name can't be blank"
Here's an updated version of your script which works:
require 'rom'
require 'rom-rails'
`rm -Rf /tmp/romtest.sqlite`
ROM.setup(:sql, 'sqlite:///tmp/romtest.sqlite')
class Events < ROM::Relation[:sql]
end
class Organisers < ROM::Relation[:sql]
end
class CreateEvent < ROM::Commands::Create[:sql]
relation :events
register_as :create
result :one
associates :organiser, key: [:organiser_id, :id]
end
class CreateOrganiser < ROM::Commands::Create[:sql]
relation :organisers
register_as :create
result :one
end
class CreateEventWithOrganiser < ROM::Model::Form
inject_commands_for :organisers, :events
input do
attribute :email
attribute :event_name
end
validations do
validates :event_name, presence: true
end
def commit!
validate!
return if errors.any?
command = organisers.create.with(
email: email
) >> events.create.with(
name: event_name
)
command.transaction do
command.call
end
end
end
ROM.finalize
rom = ROM.env
gateway = rom.gateways.fetch(:default)
migration = gateway.migration do
change do
create_table :organisers do
primary_key :id
column :email, String, null: false
end
create_table :events do
primary_key :id
column :name, String, null: false
column :organiser_id, Integer, null: false
end
end
end
migration.apply(gateway.connection, :up)
f = CreateEventWithOrganiser.build(
email: 'test@example.com',
event_name: 'Test Event'
)
puts f.save.result.inspect
# #<ROM::Commands::Result::Success:0x007fa92b589ea0 @value={:id=>1, :name=>"Test Event", :organiser_id=>1}>
The reason why it didn't work with commands
is because this method will generate command objects for your form and set provided validations for each command, which will only work correctly if you used a single command. Otherwise same validator is used for each command which doesn't make sense. When you use inject_commands_for
it will grab your own commands where validators are not set so you are free to handle validations yourself.
I think we should stop setting validators on commands which would make your original sample work but notice that you need to call validate!
yourself.
I hope this helps.
I also created a gist showing how to do the same without a form: https://gist.github.com/solnic/3b68342482cf1414f719