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_STEPSconstant). You shouldn't need to make any changes to theinitializermethod.
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
#callmethod can be anything. The first parameter is positional and is always either thechange_setor 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_argsmethod. 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_setand 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_argsat all. - If a step does have additional parameters, it is listed in
.with_step_argsand 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