Different contexts like “admin user” vs. “signed in” can be handled in the same code asset along with countless
elses. Or, you can use polymorphism as it is encouraged by Trailblazer.
The easiest way is to use normal inheritance for context-specific operations.
class Thing::Create < Trailblazer::Operation # generic code, contracts, etc. class SignedIn < self # specific code end end
builds block allows to let the operation class take care of the instantiation process.
class Thing::Create < Trailblazer::Operation builds -> (params) do return SignedIn if params[:current_user] end
When running the top-level operation, the builder will instantiate the correct subclass according to the
op = Thing::Create.(current_user: admin) op.class #=> Thing::Create::SignedIn
builds block doesn’t return a constant, the original constant will be used for instantiation. In our example, this would resolve to
builds blocks are not inherited. You can copy them to other classes, though.
class Thing::Update < Trailblazer::Operation self.builder_class = Create.builder_class
Be careful about constant resolving here: the block you copied has to have runtime evaluation of constants.
class Thing::Create < Trailblazer::Operation builds -> (params) do return self::SignedIn if # ... end
Now, the block can safely be copied to other classes where
SignedIn will be resolved in the new context.
A resolver allows you to use both the operation model and the policy in the builder.
class Thing::Create < Trailblazer::Operation include Resolver policy Thing::Policy, :create? model Thing, :create builds -> (model, policy, params) do return Admin if policy.admin? return SignedIn if params[:current_user] end
Please note that the
builds block is run in class context, no operation instance is available, yet. It is important to understand that
Resolver also changes the way the operation’s model is created/found. This, too, happens on the class layer, now.
You have to configure the CRUD module using
::model so the operation can instantiate the correct model for the builder.
If you want to change the way the model is created, you have to do so on the class level.
class Thing::Create < Trailblazer::Operation include Resolver # .. def self.model!(params) Thing.find_by(slug: params[:slug]) end
Do not include
CRUD when using