Operations
Operations are Lato's system for visually managing the execution of background Jobs.
Operations are handled by several components:
- The
Lato::Operationmodel initializes all operations and tracks their execution state. - The
Lato::OperationsControllercontroller manages user access to an ongoing operation. - The
lato_operationcomponent allows displaying the operation status in the front-end.
Converting a Job into an Operation
The easiest way to convert a Job into an operation is to simply make the Job a subclass of Lato::ApplicationJob.
Additionally, the perform() method must accept a single Hash parameter called params.
# Before
def MyJob < ApplicationJob
def perform(hello)
puts hello
end
end
# After
def MyJob < Lato::ApplicationJob
def perform(params = {})
puts params['hello']
end
end
Generating an Operation
Operations can be generated using the generate method of the Lato::Operation model.
Creating an operation requires three parameters: the name of the job to execute, a Hash of parameters to pass, and the ID of the user requesting the operation.
operation = Lato::Operation.generate('ExportProductsJob', { product_ids: [1, 2, 3] }, @session.user_id)
Once generated, the operation must be started using the start method.
Here is an example controller that starts an operation:
class CustomController < ApplicationController
def create_operation_action
@operation = Lato::Operation.generate('OperationExampleJob', {}, @session.user_id)
respond_to do |format|
if @operation.start
format.html { redirect_to lato.operation_path(@operation) }
format.json { render json: @operation }
else
format.html { render :index, status: :unprocessable_entity }
format.json { render json: @operation.errors, status: :unprocessable_entity }
end
end
end
end
Displaying the Outcome of an Operation
Each operation can be viewed at the route lato.operation_path(operation_id).
To ensure data security, an operation is only accessible to the user who initiated it.
Each operation can have four states: created, running, completed, failed.
The operation view, handled via the lato_operation component, shows the real-time status and output of the operation.
Use the link below to try out an operation:
<%= link_to 'Generate Operation',
main_app.custom_create_operation_action_path,
data: { turbo_method: :post }
%>
Generate Operation
Notes
To display the result of an operation directly in-page, you can use lato_action_controller features.
Example:
<%= link_to 'Generate Operation',
main_app.custom_create_operation_action_path,
data: { lato_action_target: 'trigger', turbo_method: :post, turbo_frame: 'lato_operation' }
%>
Generate Operation with Success
Handling Operation Outputs
An operation's output can be handled directly within the Job using a series of helper methods.
These functions can also be called even if the Job is executed without using the Lato::Operation model.
Set an output message
class MyJob < Lato::ApplicationJob
def perform(params = {})
save_operation_output_message("Operation succeeded with this message")
end
end
Set an output file
class MyJob < Lato::ApplicationJob
def perform(params = {})
file_path = Rails.root.join('tmp', 'myfile.csv')
save_operation_output_file(file_path)
end
end
Set an output action
class MyJob < Lato::ApplicationJob
def perform(params = {})
save_operation_output_message("Custom operation success message and action")
save_operation_output_action('Back to homepage', Rails.application.routes.url_helpers.root_path, target: '_blank')
end
end
Show execution error
class MyJob < Lato::ApplicationJob
def perform(params = {})
raise "This is the operation error message"
end
end
Update progress percentage
class MyJob < Lato::ApplicationJob
def perform(params = {})
10.times do |index|
sleep(1)
update_operation_percentage((index + 1) * 10)
end
end
end
Add logs during operation execution
class MyJob < Lato::ApplicationJob
def perform(params = {})
10.times do |index|
sleep(1)
add_operation_log("Custom log message #{index + 1}")
end
end
end
Replace logs during operation execution
class MyJob < Lato::ApplicationJob
def perform(params = {})
10.times do |index|
sleep(1)
replace_operation_log("Custom log message #{index + 1}")
end
end
end
Set an input file
<%= form_with
url: create_operation_action_path(type: 'file_input'),
data: {
lato_action_target: 'triggerSubmit',
turbo_frame: 'lato_operation',
controller: 'lato-form'
} do |form| %>
...
class CustomController < ApplicationController
def create_operation_action
@operation = Lato::Operation.generate('OperationExampleJob', {}, @session.user_id, params[:file])
# ...
end
end
class MyJob < Lato::ApplicationJob
def perform(params = {})
save_operation_output_message("You uploaded the file #{operation_input_file_attachment.filename}")
end
end
Utility Methods
Check if running inside an operation
The operation? method returns true if the job is running inside an operation context, false otherwise.
This is useful for writing jobs that can run both with and without operations:
class MyJob < Lato::ApplicationJob
def perform(params = {})
save_operation_output_message("Running inside operation") if operation?
# ... job logic works even without an operation
end
end
Clear operation logs
class MyJob < Lato::ApplicationJob
def perform(params = {})
add_operation_log("Step 1 started")
clear_operation_logs # removes all previous logs
add_operation_log("Restarted from scratch")
end
end
Operation Query Methods
The Lato::Operation model provides several query methods to inspect an operation's state and output:
operation = Lato::Operation.find(id) # State checks operation.finished? # returns true if completed or failed # Output checks operation.output_message? # returns true if an output message is set operation.output_message # returns the output message string operation.output_action? # returns true if an output action is set operation.output_action # returns the output action hash operation.output_error? # returns true if an output error is set operation.output_error # returns the output error string operation.output_file? # returns true if an output file is attached