Commands
A command is custom class that manages the logic between your input, usually a form object, an object and an user.
Usually, a command is used to handle the interaction between the controller and the model.
Commands are located in the app/commands/decidim/<my_module>
directory, and named: <action>_<my_resource>.rb
.
Because most of the time, a command is used to create, update a resource, we are shipping 3 commands by default, which you can inherit in your own commands:
-
Decidim::Commands::CreateResource
- for creating resources -
Decidim::Commands::UpdateResource
- for updating resources -
Decidim::Commands::DestroyResource
- for destroying resources
Decidim::Commands::CreateResource
In order to create a resource, you need to provide the form with the values you want to create.
This is a custom implementation for demonstration purposes:
# frozen_string_literal: true
# app/commands/decidim/my_module/create_my_resource.rb
module Decidim
module MyModule
class CreateMyResource < Decidim::Commands::CreateResource
# Tell the parent class which fields are going to be
# copied from the form
fetch_form_attributes :title, :description, :component
# Tell the parent class which files are going to be
# extracted from the form and attached to the resource
fetch_file_attributes :logo, :banner
# This is the class initializer. It can be ignored if you do not need
# to do anything special.
def initialize(form)
@form = form
end
private
# Tell the parent what is the ActiveRecord class needed
def resource_class = Decidim::MyModule::MyResource
# This method allows you to customize the log visibility
def extra_params = { visibility: "all" }
# The command has a before hook that you can use:
def run_before_hooks
# do something
end
# The command has an after hook that you can use:
def run_after_hooks
# do something
end
# Additionally, you can override the `attributes` method
# to add more attributes to the resource
def attributes
super.merge({
my_custom_attribute: form.my_custom_attribute
})
end
# You can also provide a custom form validation
# by overriding the `invalid?` method
def invalid?
return true if form.my_custom_attribute.blank?
super
end
end
end
end
Decidim::Commands::UpdateResource
In order to update a resource, you need to provide the resource you want to update, and the form with the new values.
This is a custom implementation for demonstration purposes:
# frozen_string_literal: true
# app/commands/decidim/my_module/update_my_resource.rb
module Decidim
module MyModule
class UpdateMyResource < Decidim::Commands::UpdateResource
# Tell the parent class which fields are going to be
# copied from the form
fetch_form_attributes :title, :description, :component
# Tell the parent class which files are going to be
# extracted from the form and attached to the resource
fetch_file_attributes :logo, :banner
# This is the class initializer. It can be ignored if you do not need
# to do anything special.
def initialize(form, resource)
@form = form
@resource = resource
end
private
# Tell the parent what is the ActiveRecord class needed
def resource_class = Decidim::MyModule::MyResource
# This method allows you to customize the log visibility
def extra_params = { visibility: "all" }
# The command has a before hook that you can use:
def run_before_hooks
# do something
end
# The command has an after hook that you can use:
def run_after_hooks
# do something
end
# Additionally, you can override the `attributes` method
# to add more attributes to the resource
def attributes
super.merge({
my_custom_attribute: form.my_custom_attribute
})
end
# You can also provide a custom form validation
# by overriding the `invalid?` method
def invalid?
return true if form.my_custom_attribute.blank?
super
end
end
end
end
Decidim::Commands::DestroyResource
If you do not need to do anything special, you can just call this command for any resource you want to destroy.
If you still want to customize the command, you can do it like this:
# frozen_string_literal: true
# app/commands/decidim/my_module/destroy_my_resource.rb
module Decidim
module MyModule
class DestroyMyResource < Decidim::Commands::DestroyResource
# This is the class initializer, that can be safely ignored if you do not perform additional actions
def initialize(resource, current_user)
@resource = resource
@current_user = current_user
end
private
# This method allows you to customize the log visibility
def extra_params = { visibility: "all" }
# The command has a before hook that you can use:
def run_before_hooks
# do something
end
# The command has an after hook that you can use:
def run_after_hooks
# do something
end
# You can also provide a custom validation by overriding the `invalid?` method
def invalid? = false
end
end
end
Advanced usage
In the below example, you will be able to see an advanced example on how you can write your custom command (CreateMyResource
), events (Decidim::MyModule::MyResourceEvent
) and jobs (Decidim::MyModule::MyCustomJob
) can be used.
# frozen_string_literal: true
# app/commands/decidim/my_module/create_my_resource.rb
module Decidim
module MyModule
# A command with the business logic to create a resource.
class CreateMyResource < Decidim::Command
# Public: Initializes the command.
#
def initialize(form, resource)
@form = form
@resource = resource
end
def call
return broadcast(:invalid) if form.invalid?
transaction do
create_resource
dispatch_event
process_jobs
end
broadcast(:ok)
end
private
attr_reader :form, :resource
def process_jobs
Decidim::MyModule::MyCustomJob.perform_later(resource)
end
def dispatch_event
Decidim::EventsManager.publish(
event: "decidim.events.my_module.my_resource_created",
event_class: Decidim::MyModule::MyResourceEvent,
resource:
)
end
def create_resource
@resource = Decidim.traceability.create!(
resource,
form.current_user,
**attributes,
visibility: "public-only"
)
end
# this is mapping of
# ActiveRecord::attribute => form.attribute
def attributes
{
title: form.title,
description: form.description,
resource: form.resource
}
end
end
end
end
Overriding Decidim commands
Sometimes you may need to extend a Decidim
supplied command, then you can either override the attributes
method, either extend it with a super
call.
# frozen_string_literal: true
# app/lib/overrides/commands/create_my_resource.rb
module Decidim
module Overrides
module Commands
module CreateMyResource
def attributes
super.merge(
{
my_custom_attribute: form.my_custom_attribute
}
)
end
end
end
end
end
Decidim::MyModule::CreateMyResource.prepend(Decidim::Overrides::Commands::CreateMyResource)
More information
-
Decidim::Command
is an internalization of Rectify gem created by Andy Pike