Operation

Operation: Contract

Last updated 02 December 2016 trailblazer v1.1 v2.0

Operation: Contract

The operation’s contract is one of the pivotal elements in Trailblazer. It integrates the deserialization and validation into your service object.

[DOCS STILL UNDER CONSTRUCTION]

Composition

Contracts don’t have to map to one model, only.

If your contract embraces several models using Composition, you can easily pass the composition hash via validate. Note that composition is a Reform feature. Do not forget the on: option when defining your compositional mapping.

class Create < Trailblazer::Operation
  contract do
    include Reform::Form::Composition

    property :id,   on: :comment
    property :name, on: :author
  end

  def process(params)
    comment = Comment.find(params[:id])
    author  = comment.author

    validate(params, comment: comment, author: author)
  end
end

The second argument to validate is the contract’s model. As per Reform’s API, for compositions this must be a hash.

Injection

It is possible to inject additional dependencies into the contract, which are not part of the original model(s). This often is a current_user or the operation instance itself.

Those dependencies have to be virtual properties in the contract. They can then be passed as the third argument to validate.

class Create < Trailblazer::Operation
  contract do
    property :current_user, virtual: true
    property :operation,    virtual: true
  end

  def process(params)
    validate(params, model,
      current_user: params[:current_user],
      operation:    self) do |f|

    end
  end
end

The same semantics apply to contract.

contract! Method

To automate injection or composition, you can override Operation#contract!. Note that you will still need to specify the virtual properties in the contract as above.

class Create < Trailblazer::Operation

private
  def contract!(model=nil, contract_class=nil)
    contract_class ||= self.class.contract_class
    @contract ||= contract_class.new(model,
      current_user: @current_user,
      operation: self)
  end

Or if you don’t require arguments when calling contract, simply use:

class Create < Trailblazer::Operation

private
  def contract! *args
    @contract ||= self.class.contract_class.new(nil, operation: self)
  end