Transactions Development Pattern - samvera/hyrax GitHub Wiki
Back to Patterns
<NOTE: At this writing, all links are to code in Release 3.3.0.
For a walk through of transactions, see https://github.com/samvera-labs/transitioning-to-valkyrie-workshop and the transaction exercise solution.
Transactions
General Overview
Transactions provide a way to run multiple steps in a predetermined order. Steps in a transactions can modify values in a change set or in the resource associated with the change set once the change set is saved.
Typically, the transaction is initiated with a change set when an action saving a form is executed in a controller. One of the steps will save the change set, which syncs it with a resource. From that point on, the steps operate on the resource.
NOTE: Transactions are not reversible. But if the steps stop anywhere before the change set is saved, then the transaction will be a no-op.
Terminology
Term | Description |
---|---|
Container Registry | Register each transaction and step under a name space for the type of object to which they will be applied. |
Transaction | Defines a series of steps applied to a change set and/or its associated resource in a predetermined order. Typically a transaction is defined to perform steps required for an action in a controller (e.g. collection_create transaction is used by the create action in the CollectionController ). |
Step | Code that has a single responsibility. These are often used by multiple transactions (e.g. set_user_as_depositor step is used in collection_create and work_create transactions. |
Change Set | is_a Valkyrie::ChangeSet See Understanding change sets |
Resource | is_a Valkyrie::Resource See Understanding resources |
Hyrax Specifics
Defining Transactions and Steps
Container Registry
/lib/hyrax/transactions/container.rb - registers transactions and steps
Key parts of the container registration file:
- require the files where each transaction and step are defined
- each step is registered under the namespace of the type of object it acts on (e.g. If it acts on the change_set, register it under
namespace 'change_set'
. If it acts on works and collections, register it under bothnamespace 'work_resource'
andnamespace 'collection_resource'
.)
Transaction Definition
/lib/hyrax/transactions - other files in the transactions directory are defining multi-step transactions. NOTE: This is not required. The location of the transaction is registered in the container registry.
Key parts of a transaction file:
DEFAULT_STEPS
- the steps to run for this transaction. If the step acts on a change_set, then prefix the step name withchange_set.
(e.g.change_set.set_user_as_depositor
). If the step acts on a resource, then prefix the name with resource type defined in the Container registry (e.g.collection_resource.save_acl
). Steps can be a shared step (e.g.add_to_collections
) or another transaction (e.g.apply
).#initialize
- this is the same for all transactions. It receives the container registry (defaulting to the container registry described above) and steps (defaulting to the steps defined in theDEFAULT_STEPS
constant). You shouldn't need to make any changes to theinitializer
method.
Shared Steps
/lib/hyrax/transactions/steps - single responsibility steps are defined in the steps directory
Key parts of a step file:
#call
- this is the method that gets called to execute the transaction- parameters - The parameters for the
#call
method can be anything. The first parameter is positional and is always either thechange_set
or one of the resources (e.g.work_resource
,collection_resource
, etc.). All other parameters are named and must have default values. The additional parameters are passed to the transaction step when the transaction is started using the#with_step_args
method. See Calling a Transaction for more information on passing in values. Success
- When the step completes successfully, the last thing to do is callSuccess(value)
. Value is often the first parameter. The return value doesn't have to be the object passed in (e.g. change_set or resource), but it typically is as a way to pass along any modifications that were made to the change_set or resource. An example where it is not the passed in object is the save step which syncs the passed inchange_set
and returns the savedresource
.Failure
- If the step fails, callFailure([err_msg, first_param])
and pass an array including an error message and the first parameter. All processing of the transaction steps stops once a failure occurs. Again, The caller can process the
- parameters - The parameters for the
Calling a Transaction
Example from #valkyrie_create in CollectionController.
@collection = transactions['change_set.create_collection']
.with_step_args(
'change_set.set_user_as_depositor' => { user: current_user },
'change_set.add_to_collections' => { collection_ids: Array(params[:parent_id]) },
'collection_resource.apply_collection_type_permissions' => { user: current_user }
)
- If a step doesn’t have additional parameters beyond the first parameter that is the object (e.g. change_set or resource), it is not in the list for method
.with_step_args
. Some transactions have no steps with additional methods and will not call.with_step_args
at all. - If a step does have additional parameters, it is listed in
.with_step_args
and the additional parameters for the step are passed as a hash. - Steps are called in the order listed in the transaction's
DEFAULT_STEPS
. - Processing of a transaction will stop if a failure happens or once all steps have complete successfully
An Example
TBD
Testing
TBD