In last week’s tutorial we looked at defining our first Operation for creating new users.

A Trailblazer Operation encapsulates a given action of the application and takes responsibility for accepting data from the request, validating that data, and creating or updating the database.

It’s often the case that the create and update actions of a given resource are similar, but not quite the same. For example, each action will typically have the same properties, and perhaps many of the same validation rules.

But the create and update actions are usually not exactly the same.

In today’s tutorial we will be taking a closer look at the Contract aspect of the Operation.

What is the Contract?

An important part of last week’s tutorial was defining the Contract of the Operation:

require "reform/form/validation/unique_validator.rb"

class User < ActiveRecord::Base  
class Create < Trailblazer::Operation  
include Model; model User, :create

contract do  
property :email  
property :password, virtual: true

validates :email, presence: true, email: true, unique: true  
validates :password, presence: true  
end  
end  
end  

The Contract defines the properties and validation rules of the Operation.

But what is the Contract?

If you remember back to Trailblazer – A New Architecture For Rails [Review] I mentioned that the Operation, whilst on the surface seems like it has a lot of responsibility, actually is more of a conductor for the underlying composed collaborators.

The Contract is actually Reform in disguise! We’ve previously looked at Reform in Using Form Objects in Ruby on Rails with Reform.

So the Contract functionality of your Operations is actually implemented with Reform.

Separating out the Contract

In last week’s tutorial we defined the Contract inside of the Operation. This is a perfectly acceptable way of defining the Contract as it keeps everything in one file.

However, it is also possible to define the Contract as a separate class, and then set that class as the Contract for the Operation.

I find this makes it easier to see the properties and validation rules for a set of Operations that share many of the same properties.

To separate out the Contract, first create a new file called contract.rb under the concepts/user directory:

module User::Contract

end  

First I’m going to define all of the shared properties and rules in a Base class that can be extended. Ruby does not have the idea of an abstract class, but this is essentially an abstract class because I won’t be using it directly, but instead, only using the children of this class:

require "reform/form/validation/unique_validator.rb"

module User::Contract  
class Base < Reform::Form  
model User  
property :email  
property :password, virtual: true

validates :email, email: true, unique: true  
validates :password, length: { minimum: 8 }  
end  
end  

As you can see, the Base class should inherit from Reform::Form. Finally we see how the Contract is actually just an instance of Reform in disguise!

Next I can basically just cut and paste the rules from the Operation that will always apply and add them to this class.

I’ve also added a password length validation just to show that business rules that should always be satisfied should be placed in this base class.

Next I can add the Create Contract:

require "reform/form/validation/unique_validator.rb"

module User::Contract  
class Base < Reform::Form  
model User  
property :email  
property :password, virtual: true

validates :email, email: true, unique: true  
validates :password, length: { minimum: 8 }  
end

class Create < Base  
validates :email, presence: true  
validates :password, presence: true  
end  
end  

The Create class inherits from the Base class so we do not need to define the properties and the validation rules again. In this class we can simply add the requirements that the email and password must be present.

Finally I can add the Update class:

require "reform/form/validation/unique_validator.rb"

module User::Contract  
class Base < Reform::Form  
model User  
property :email  
property :password, virtual: true

validates :email, email: true, unique: true  
validates :password, length: { minimum: 8 }  
end

class Create < Base  
validates :email, presence: true  
validates :password, presence: true  
end

class Update < Base

end  
end  

In this example I haven’t added any additional validation rules for the update action, but as you can probably imagine, in a more complex scenario you might have additional validation rules that must be satisfied to update a given resource. In that case you could add these rules here.

With the Contract defined, we can now add it to the Operation like this:

require "bcrypt"

class User < ActiveRecord::Base  
class Create < Trailblazer::Operation  
include Model; model User, :create

contract User::Contract::Create

def process(params)  
validate(params[:user]) do |f|  
generate_digest  
f.save  
end  
end

def generate_digest  
model.password_digest = BCrypt::Password.create(contract.password)  
end  
end  
end  

As you can see, I can remove all of the Contract specific code and replace it with following line to set the Contract on the Operation:

contract User::Contract::Create  

If you run the tests again you should see that they all still pass!

Conclusion

If you are already familiar with Reform, it should come as no surprise that Operation classes in Trailblazer are really powerful. Once I understood that the Contract of an Operation was Reform, the pieces really started to fall into place.

The ability to define a “contract” and use inheritance really makes it easy to build out your business rules for a particular set of actions.

This is much easier than trying to do it in a Model class, especially if the rules are slightly different for create and update actions!