has_state_machine 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +30 -0
- data/Rakefile +31 -0
- data/lib/has_state_machine.rb +10 -0
- data/lib/has_state_machine/core_ext/string.rb +16 -0
- data/lib/has_state_machine/definition.rb +69 -0
- data/lib/has_state_machine/railtie.rb +6 -0
- data/lib/has_state_machine/state.rb +112 -0
- data/lib/has_state_machine/state_helpers.rb +125 -0
- data/lib/has_state_machine/version.rb +5 -0
- metadata +140 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 180ea0ce6e3e597405acc08441fe8ee1c0f530ccfd176950e8683196e9232695
|
4
|
+
data.tar.gz: 97944638a358883f302463d830d506149fe169d9cf3a3c20d7ccd615724a4932
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c4e20717ca8de688ea7c766da83412b7bf0121d06d2cace551dfd1b5210632fd9840ca34d7293381431b2ff4afc27459a33bfd2711ef537ee3ca39c8598d8098
|
7
|
+
data.tar.gz: 973704bfb4403dae0b5ff00a3fe3b2d0d6d84f96a5e706bd0dea8cda113bd7dbfc7bac97ce84ae310b4fb6613f287f257a1dfedea41794e06f7afbd4b5af3834
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2020 Benjamin Hargett
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# HasStateMachine
|
2
|
+
|
3
|
+
[](https://github.com/bharget/has_state_machine/actions)
|
4
|
+
[](https://github.com/testdouble/standard)
|
5
|
+
|
6
|
+
## Usage
|
7
|
+
How to use my plugin.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'has_state_machine'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
```bash
|
18
|
+
$ bundle
|
19
|
+
```
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
```bash
|
23
|
+
$ gem install has_state_machine
|
24
|
+
```
|
25
|
+
|
26
|
+
## Contributing
|
27
|
+
Contribution directions go here.
|
28
|
+
|
29
|
+
## License
|
30
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
|
5
|
+
task default: %i[lint test]
|
6
|
+
|
7
|
+
namespace :test do
|
8
|
+
task :refresh do
|
9
|
+
sh "bin/appraisal clean"
|
10
|
+
sh "bin/appraisal generate"
|
11
|
+
end
|
12
|
+
|
13
|
+
task :all do
|
14
|
+
sh "bin/appraisal install"
|
15
|
+
sh "bin/appraisal bin/rake test"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
task :test do
|
20
|
+
sh "bin/test"
|
21
|
+
end
|
22
|
+
|
23
|
+
task :lint do
|
24
|
+
sh "bin/standardrb --no-fix"
|
25
|
+
end
|
26
|
+
|
27
|
+
namespace :changelog do
|
28
|
+
task :refresh do
|
29
|
+
sh "bin/refresh_changelog"
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "has_state_machine/railtie"
|
4
|
+
require "has_state_machine/core_ext/string"
|
5
|
+
require "has_state_machine/definition"
|
6
|
+
|
7
|
+
module HasStateMachine
|
8
|
+
end
|
9
|
+
|
10
|
+
ActiveRecord::Base.include HasStateMachine::Definition if defined?(ActiveRecord)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class String
|
4
|
+
##
|
5
|
+
# Adding our transition method to the default String class to prevent
|
6
|
+
# exceptions while transitioning from an invalid state. This method
|
7
|
+
# gets overwritten by valid HasStateMachine::State classes.
|
8
|
+
#
|
9
|
+
# @return [Boolean] false
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# "some random string".transition_to("draft") #=> false
|
13
|
+
def transition_to(_desired_state)
|
14
|
+
false
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "has_state_machine/state"
|
4
|
+
require "has_state_machine/state_helpers"
|
5
|
+
|
6
|
+
module HasStateMachine
|
7
|
+
module Definition
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
class_methods do
|
11
|
+
##
|
12
|
+
# Configures the state machine for the ActiveRecord object and adds some
|
13
|
+
# useful helper methods such as scopes, boolean checks, etc.
|
14
|
+
#
|
15
|
+
# @param states [Array<Symbol>] the list of possible states in a state machine
|
16
|
+
# @note the first state is used as the initial state
|
17
|
+
# @param options [Hash] a hash of additional options for the state machine
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# class Post < ApplicationRecord
|
21
|
+
# has_state_machine states: %i(draft published archived)
|
22
|
+
# end
|
23
|
+
def has_state_machine(states: [], **options)
|
24
|
+
raise ArgumentError, "Please define at least one state to use has_state_machine." if states.empty?
|
25
|
+
|
26
|
+
define_helper_methods(
|
27
|
+
states: states.map(&:to_s),
|
28
|
+
options: options.with_indifferent_access
|
29
|
+
)
|
30
|
+
|
31
|
+
include HasStateMachine::StateHelpers
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def define_helper_methods(states:, options:)
|
37
|
+
##
|
38
|
+
# The list of possible states in the state machine.
|
39
|
+
# Can be overwritten to use a different column name.
|
40
|
+
define_singleton_method "workflow_states" do
|
41
|
+
states
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Defines the column name for the attribute holding the current status.
|
46
|
+
# Can be overwritten to use a different column name.
|
47
|
+
define_singleton_method "state_attribute" do
|
48
|
+
options[:state_attribute]&.to_sym || :status
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Defines the namespace of the models possible states.
|
53
|
+
# Can be overwritten to use a different namespace.
|
54
|
+
define_singleton_method "workflow_namespace" do
|
55
|
+
(options[:workflow_namespace] || "Workflow::#{self}")
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Determines whether or not the state validations should be run
|
60
|
+
# as part of the object validations.
|
61
|
+
define_singleton_method "state_validations_on_object?" do
|
62
|
+
return true unless options.key?(:state_validations_on_object)
|
63
|
+
|
64
|
+
options[:state_validations_on_object]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string"
|
4
|
+
|
5
|
+
module HasStateMachine
|
6
|
+
class State < String
|
7
|
+
extend ActiveModel::Model
|
8
|
+
extend ActiveModel::Callbacks
|
9
|
+
include ActiveModel::Validations
|
10
|
+
|
11
|
+
attr_reader :object, :state, :options
|
12
|
+
|
13
|
+
##
|
14
|
+
# Defines the before_transition and after_transition callbacks
|
15
|
+
# for use on a HasStateMachine::State instance.
|
16
|
+
define_model_callbacks :transition, only: %i[before after]
|
17
|
+
|
18
|
+
##
|
19
|
+
# Retrieves the next available transitions for a given state.
|
20
|
+
delegate :possible_transitions, :state, to: "self.class"
|
21
|
+
|
22
|
+
##
|
23
|
+
# Add errors to the ActiveRecord object rather than the HasStateMachine::State
|
24
|
+
# class.
|
25
|
+
delegate :errors, to: :object
|
26
|
+
|
27
|
+
##
|
28
|
+
# Initializes the HasStateMachine::State instance.
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# state = HasStateMachine::State.new(post) #=> "draft"
|
32
|
+
# state.class #=> Workflow::Post::Draft
|
33
|
+
def initialize(object)
|
34
|
+
@object = object
|
35
|
+
|
36
|
+
super state
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Checks to see if the desired state is valid and then gives
|
41
|
+
# responsibility to the desired state's instance to make the
|
42
|
+
# transition.
|
43
|
+
#
|
44
|
+
# @param desired_state [String] the state to transition to
|
45
|
+
# @param options [Hash] a hash of additional options for
|
46
|
+
# transitioning the object
|
47
|
+
#
|
48
|
+
# @return [Boolean] whether or not the transition took place
|
49
|
+
def transition_to(desired_state, **options)
|
50
|
+
with_transition_options(options) do
|
51
|
+
return false unless valid_transition?(desired_state.to_s)
|
52
|
+
|
53
|
+
state_instance(desired_state.to_s).perform_transition!
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Makes the actual transition from one state to the next and
|
59
|
+
# runs the before and after transition callbacks.
|
60
|
+
def perform_transition!
|
61
|
+
run_callbacks :transition do
|
62
|
+
object.update("#{object.state_attribute}": state)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
##
|
69
|
+
# Determines if the given desired state exists in the predetermined
|
70
|
+
# list of allowed transitions.
|
71
|
+
def can_transition?(desired_state)
|
72
|
+
possible_transitions.include? desired_state
|
73
|
+
end
|
74
|
+
|
75
|
+
def state_instance(desired_state)
|
76
|
+
klass = "#{object.workflow_namespace}::#{desired_state.to_s.classify}".safe_constantize
|
77
|
+
klass&.new(object)
|
78
|
+
end
|
79
|
+
|
80
|
+
def valid_transition?(desired_state)
|
81
|
+
return true if options[:skip_validations]
|
82
|
+
|
83
|
+
object.valid? &&
|
84
|
+
can_transition?(desired_state) &&
|
85
|
+
state_instance(desired_state)&.valid?
|
86
|
+
end
|
87
|
+
|
88
|
+
def with_transition_options(options, &block)
|
89
|
+
@options = options
|
90
|
+
object.skip_state_validations = options[:skip_validations]
|
91
|
+
yield
|
92
|
+
object.skip_state_validations = false
|
93
|
+
end
|
94
|
+
|
95
|
+
class << self
|
96
|
+
def possible_transitions
|
97
|
+
@possible_transitions || []
|
98
|
+
end
|
99
|
+
|
100
|
+
def state
|
101
|
+
to_s.demodulize.underscore
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# Setter for the HasStateMachine::State classes to define the possible
|
106
|
+
# states the current state can transition to.
|
107
|
+
def transitions_to(states)
|
108
|
+
@possible_transitions = states.map(&:to_s)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HasStateMachine
|
4
|
+
module StateHelpers
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
attr_accessor :skip_state_validations
|
9
|
+
|
10
|
+
delegate \
|
11
|
+
:state_attribute,
|
12
|
+
:state_validations_on_object?,
|
13
|
+
:workflow_namespace,
|
14
|
+
:workflow_states,
|
15
|
+
to: "self.class"
|
16
|
+
|
17
|
+
##
|
18
|
+
# Sets the default value of the state method to the initial state
|
19
|
+
# defined in the state machine.
|
20
|
+
attribute state_attribute, :string, default: initial_state
|
21
|
+
|
22
|
+
##
|
23
|
+
# Validating any changes to the status attribute are represented by
|
24
|
+
# classes within the state machine and have a valid HasStateMachine::State class.
|
25
|
+
validates state_attribute, inclusion: {in: workflow_states}, presence: true
|
26
|
+
validate :state_class_defined?
|
27
|
+
validate :state_instance_validations, if: :should_validate_state?
|
28
|
+
|
29
|
+
##
|
30
|
+
# Overwrites the default getter for the state attribute to
|
31
|
+
# instantiate a HasStateMachine::State instance instead. If the state
|
32
|
+
# class does not exist, it simply returns a string.
|
33
|
+
#
|
34
|
+
# @return [HasStateMachine::State] the current state represented by a instance
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# post = Post.new(status: "draft")
|
38
|
+
# post.status.class #=> Workflow::Post::Draft
|
39
|
+
define_method state_attribute.to_s do
|
40
|
+
return state_class.new(self) if state_class.present?
|
41
|
+
|
42
|
+
current_state
|
43
|
+
end
|
44
|
+
|
45
|
+
workflow_states.each do |state|
|
46
|
+
##
|
47
|
+
# Defines scopes based on the state machine possible states
|
48
|
+
#
|
49
|
+
# @return [ActiveRecord_Relation]
|
50
|
+
# @example Retreiving a users published posts
|
51
|
+
# > Post.published.where(user: user)
|
52
|
+
# #=> [#<Post>]
|
53
|
+
scope state, -> { where("#{state_attribute} = ?", state) }
|
54
|
+
|
55
|
+
##
|
56
|
+
# Defines boolean helpers to determine if the active state matches
|
57
|
+
# the specified state.
|
58
|
+
#
|
59
|
+
# @return [Boolean] whether or not the active state matches the call
|
60
|
+
# @example Check if a post is published
|
61
|
+
# > post.published?
|
62
|
+
# #=> true
|
63
|
+
define_method "#{state}?" do
|
64
|
+
current_state == state
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
##
|
71
|
+
# Getter for the current state of the model based on the configured state
|
72
|
+
# attribute.
|
73
|
+
def current_state
|
74
|
+
self[state_attribute]
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Predicate method for determining whether or not the state validations
|
79
|
+
# should be run as part of the object validations.
|
80
|
+
def should_validate_state?
|
81
|
+
return false unless state_validations_on_object?
|
82
|
+
|
83
|
+
!skip_state_validations
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# Gets the HasStateMachine::State class that represents the current state
|
88
|
+
# of the model.
|
89
|
+
def state_class
|
90
|
+
return unless current_state.present?
|
91
|
+
|
92
|
+
"#{workflow_namespace}::#{current_state.classify}".safe_constantize
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# True unless unable to find the HasStateMachine::State class for the current
|
97
|
+
# state.
|
98
|
+
def state_class_defined?
|
99
|
+
return if state_class.present?
|
100
|
+
|
101
|
+
errors.add(state_attribute, :not_implemented, message: "class must be implemented")
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# Runs the validations defined on the current HasStateMachine::State when calling
|
106
|
+
# model.valid?
|
107
|
+
def state_instance_validations
|
108
|
+
return unless state_class.present?
|
109
|
+
|
110
|
+
public_send(state_attribute.to_s).valid?
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class_methods do
|
115
|
+
private
|
116
|
+
|
117
|
+
##
|
118
|
+
# The initial state of the workflow based on the first state defined in the model
|
119
|
+
# has_state_machine states array.
|
120
|
+
def initial_state
|
121
|
+
workflow_states.first
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: has_state_machine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Benjamin Hargett
|
8
|
+
- Jake Hurd
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2020-10-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '5.2'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '5.2'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: sqlite3
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: pry
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: pry-rails
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: standard
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: appraisal
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
description: HasStateMachine uses ruby classes to make creating a finite state machine
|
99
|
+
in your ActiveRecord models a breeze.
|
100
|
+
email:
|
101
|
+
- hargettbenjamin@gmail.com
|
102
|
+
- jake.hurd@gmail.com
|
103
|
+
executables: []
|
104
|
+
extensions: []
|
105
|
+
extra_rdoc_files: []
|
106
|
+
files:
|
107
|
+
- MIT-LICENSE
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- lib/has_state_machine.rb
|
111
|
+
- lib/has_state_machine/core_ext/string.rb
|
112
|
+
- lib/has_state_machine/definition.rb
|
113
|
+
- lib/has_state_machine/railtie.rb
|
114
|
+
- lib/has_state_machine/state.rb
|
115
|
+
- lib/has_state_machine/state_helpers.rb
|
116
|
+
- lib/has_state_machine/version.rb
|
117
|
+
homepage: https://www.github.com/encampment/has_state_machine
|
118
|
+
licenses:
|
119
|
+
- MIT
|
120
|
+
metadata: {}
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '2.5'
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
requirements: []
|
136
|
+
rubygems_version: 3.0.3
|
137
|
+
signing_key:
|
138
|
+
specification_version: 4
|
139
|
+
summary: Class based state machine for ActiveRecord models.
|
140
|
+
test_files: []
|