rom-rb form validation when using multiple relations

612 Views Asked by At

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:      '[email protected]',
  event_name: 'Test Event'
)

# Unexpectedly fails
f.save
puts f.errors.full_messages
# => "Event name can't be blank"
1

There are 1 best solutions below

1
On BEST ANSWER

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:      '[email protected]',
  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