Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/geekq/workflow-activerecord

ActiveRecord/Rails Integration for the Workflow library
https://github.com/geekq/workflow-activerecord

aasm activerecord rails rails5 ruby state-machine

Last synced: 6 days ago
JSON representation

ActiveRecord/Rails Integration for the Workflow library

Awesome Lists containing this project

README

        

[![Version](https://img.shields.io/gem/v/workflow-activerecord.svg)](https://rubygems.org/gems/workflow-activerecord)
[![Test](https://github.com/geekq/workflow-activerecord/actions/workflows/test.yml/badge.svg)](https://github.com/geekq/workflow-activerecord/actions/workflows/test.yml)
[![Code Climate](https://codeclimate.com/github/geekq/workflow-activerecord/badges/gpa.svg)](https://codeclimate.com/github/geekq/workflow-activerecord)
[![Test Coverage](https://codeclimate.com/github/geekq/workflow-activerecord/badges/coverage.svg)](https://codeclimate.com/github/geekq/workflow-activerecord/coverage)

# workflow-activerecord

**ActiveRecord/Rails Integration for the Workflow library**

Major+minor versions of workflow-activerecord are based on the oldest
compatible ActiveRecord API. To use [`workflow`][workflow] with
Rails/ActiveRecord 6.*, 7.* please use:

gem 'workflow-activerecord', '~> 6.0'

This will also automatically include the newest compatible version of
the core 'workflow' gem. But you can also choose a specific version:

gem 'workflow', '~> 2.0'
gem 'workflow-activerecord', '~> 4.1'

Please also have a look at [the sample application][]!

For detailed introduction into workflow DSL please read the
[`workflow` README][workflow]!

[workflow]: https://github.com/geekq/workflow
[the sample application]: https://github.com/geekq/workflow-rails-sample

State persistence with ActiveRecord
-----------------------------------

Workflow library can handle the state persistence fully automatically. You
only need to define a string field on the table called `workflow_state`
and include the workflow mixin in your model class as usual:

class Order < ApplicationRecord
include WorkflowActiverecord
workflow do
# list states and transitions here
end
end

On a database record loading all the state check methods e.g.
`article.state`, `article.awaiting_review?` are immediately available.
For new records or if the `workflow_state` field is not set the state
defaults to the first state declared in the workflow specification. In
our example it is `:new`, so `Article.new.new?` returns true and
`Article.new.approved?` returns false.

At the end of a successful state transition like `article.approve!` the
new state is immediately saved in the database.

You can change this behaviour by overriding `persist_workflow_state`
method.

### Scopes

Workflow library also adds automatically generated scopes with names based on
states names:

class Order < ApplicationRecord
include WorkflowActiverecord
workflow do
state :approved
state :pending
end
end

# returns all orders with `approved` state
Order.with_approved_state

# returns all orders with `pending` state
Order.with_pending_state

### Custom workflow database column

[meuble](http://imeuble.info/) contributed a solution for using
custom persistence column easily, e.g. for a legacy database schema:

class LegacyOrder < ApplicationRecord
include WorkflowActiverecord

workflow_column :foo_bar # use this legacy database column for
# persistence
end

### Single table inheritance

Single table inheritance is also supported. Descendant classes can either
inherit the workflow definition from the parent or override with its own
definition.

Custom Versions of Existing Adapters
------------------------------------

Other adapters (such as a custom ActiveRecord plugin) can be selected by adding a `workflow_adapter` class method, eg.

```ruby
class Example < ApplicationRecord
def self.workflow_adapter
MyCustomAdapter
end
include Workflow

# ...
end
```

(The above will include `MyCustomAdapter` *instead* of the default
`WorkflowActiverecord` adapter.)

Multiple Workflows
------------------

I am frequently asked if it's possible to represent multiple "workflows"
in an ActiveRecord class.

The solution depends on your business logic and how you want to
structure your implementation.

### Use Single Table Inheritance

One solution can be to do it on the class level and use a class
hierarchy. You can use [single table inheritance][STI] so there is only
single `orders` table in the database. Read more in the chapter "Single
Table Inheritance" of the [ActiveRecord documentation][ActiveRecord].
Then you define your different classes:

class Order < ActiveRecord::Base
include WorkflowActiverecord
end

class SmallOrder < Order
workflow do
# workflow definition for small orders goes here
end
end

class BigOrder < Order
workflow do
# workflow for big orders, probably with a longer approval chain
end
end

### Individual workflows for objects

Another solution would be to connect different workflows to object
instances via metaclass, e.g.

# Load an object from the database
booking = Booking.find(1234)

# Now define a workflow - exclusively for this object,
# probably depending on some condition or database field
if # some condition
class << booking
include WorkflowActiverecord
workflow do
state :state1
state :state2
end
end
# if some other condition, use a different workflow

You can also encapsulate this in a class method or even put in some
ActiveRecord callback. Please also have a look at [the full working
example][multiple_workflow_test]!

### on_transition

You can have a look at an advanced [`on_transition`][] example in
[this test file][advanced_hooks_and_validation_test].

[STI]: http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
[ActiveRecord]: http://api.rubyonrails.org/classes/ActiveRecord/Base.html
[multiple_workflow_test]: https://github.com/geekq/workflow-activerecord/blob/develop/test/multiple_workflows_test.rb
[`on_transition`]: https://github.com/geekq/workflow#on_transition
[advanced_hooks_and_validation_test]: http://github.com/geekq/workflow-activerecord/blob/develop/test/advanced_hooks_and_validation_test.rb

### Handling the state transition in a transaction

You might want to perform the state transition in a database transaction,
so that the state is persisted atomically with all the other attributes.
To do so, define a module:

```ruby
module TransitionTransaction
def process_event!(name, *, **)
transaction { super(name, *, **) }
end
end
```

and then prepend it in your model:

```ruby
class Task
workflow do
...
end

prepend TransitionTransaction
end
```

Changelog
---------

### New in the version 6.0.0

* GH-14 retire Ruby 2.6 and Rails 5.* and older since they have reached end of
live; please use workflow-activerecord 4.1.9, if you still depend on
those versions

### New in the version 4.1.9

* GH-13 Switch CI (continuous integration) from travis-CI to GitHub
* Tested Rails 7.0 support

Support
-------

### Reporting bugs

About
-----

Author: Vladimir Dobriakov,

Copyright (c) 2010-2024 Vladimir Dobriakov and Contributors

Copyright (c) 2008-2009 Vodafone

Copyright (c) 2007-2008 Ryan Allen, FlashDen Pty Ltd

Based on the work of Ryan Allen and Scott Barron

Licensed under MIT license, see the LICENSE file.