Skip to content
Kunal edited this page Jan 6, 2021 · 2 revisions

Every op must implement a perform method. This is the method which will be executed if all validations pass. When the the perform method is complete, the Op determines success based on whether errors is empty.

class MyFailingOp < ::Subroutine::Op
  string :first_name
  validates :first_name, presence: true

  protected

  def perform
    errors.add(:base, "This Op will never succeed")
    # but this line will execute
  end

end

Notice we do not declare perform as a public method. This is to ensure the "public" api of the op remains as submit or submit!.

You are able to fail fast within validations or perform if you invoke fail!. fail! can be invoked with no arguments or you can provide a field and message like errors.add.

class MyOp
  string :first_name

  protected
  def perform
    fail! :base, "What is going on, why would you do this?" if first_name == "Bob"
    fail! :first_name, "is invalid" if first_name == "Ralph"

    if first_name == "Douglas"
      errors.add(:first_name, "cannot be Douglas")
      fail!
    end
  end
end

You can also inherit errors from other objects that respond to errors in the same way:

class MyOp
  integer :user_id
  string :first_name

  protected
  def perform
    user = ::User.find(user_id)
    unless user.update(first_name: first_name)
      inherit_errors(user) 
    end
    # this line will still run because no `fail!` occurred and/or save! was not used
  end
end

Errors will be extracted from things like ActiveRecord objects is the resulting error is compatible:

class MyOp
  integer :user_id
  string :first_name

  protected
  def perform
    user = ::User.find(user_id)
    user.first_name = first_name
    user.save!
    # this line will not run if user is invalid
  end
end
Clone this wiki locally