Skip to content
Mike Nelson edited this page Jan 29, 2020 · 1 revision

Ops have some fluff, but not much. The Subroutine::Op class' entire purpose in life is to validate user input and execute a series of operations. To enable this we filter input params, type cast inputs if desired, perform validations, and perform authorization. Only after these things are complete will the Op perform its operation.

Input Declaration

Inputs can be declared via the field method but are more commonly declared by type (integer, decimal, date).

class MyOp < ::Subroutine::Op
  field :first_name
  field :age, type: :integer
  field :subscribed, type: :boolean, default: false
  # ...
end

class MyOpWithTypes < ::Subroutine::Op
  string :first_name
  integer :age
  boolean :subscribed, default: false
  # ...
end
  • type - declares the data type which the input should be cast. Available types are declared in Subroutine::TypeCaster::TYPES. New types can be registered via ::Subroutine::TypeCaster.register
  • default - the default value of the input if not otherwise provided. If the provided default is callable, the result of that call will be used at runtime.
  • mass_assignable - whether an input can be passed via the constructor. Defaults to true.
  • field_reader - whether a reader method is created in the op
  • field_writer - whether a writer method is created in the op
  • groups - the param groups this field should be part of. Omission of this makes the field available in params.
  • aka - [deprecated] an alias (or aliases) that is checked when errors are inherited from other objects.

Since ops can use other ops, sometimes it's nice to explicitly state the inputs are valid. To "inherit" all the inputs from another op, simply use inputs_from. Do note this inherits the defaults, groups, etc, but does not bring over any validations you may declare against the fields.

class MyOp < ::Subroutine::Op
  string :token
  inputs_from MyOtherOp

  protected

  def perform
    verify_token!
    MyOtherOp.submit! params # will include any params relevant to MyOtherOp as well as `token`
  end

end

Validations

Since Ops include ActiveModel::Model, validations can be used just like any other ActiveModel object.

class MyOp < ::Subroutine::Op
  string :first_name

  validates :first_name, presence: true
end

Input Usage

Inputs are accessible within the op via field accessors and param accessors. You can see if an input was provided via the field_provided? method.

class MyOp < ::Subroutine::Op

  string :first_name
  validate :validate_first_name_is_not_bob

  protected

  def perform
   # whatever this op does
   true
  end

  def validate_first_name_is_not_bob
    if field_provided?(:first_name) && first_name.downcase == 'bob'
      errors.add(:first_name, 'should not be bob')
    end
  end
end

All provided params are accessible via the params accessor. All default values are accessible via the defaults accessor. The combination of the two is available via params_with_defaults.

class MyOp < ::Subroutine::Op
  string :name
  string :status, default: "browsing"

  def perform
    puts params.inspect
    puts defaults.inspect
    puts params_with_defaults.inspect
    puts respond_to?(:name)
    puts field_provided?(:status)
  end
end

MyOp.submit(name: "foobar", status: nil)
# => { name: "foobar" }
# => { status: "browsing" }
# => { name: "foobar", status: nil }
# => true
# => true

MyOp.submit(name: "foobar")
# => { name: "foobar" }
# => { status: "browsing" }
# => { name: "foobar", status: "browsing" }
# => true
# => false
Clone this wiki locally