transitionable 0.1.1 → 0.2.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 +4 -4
- data/Gemfile.lock +9 -3
- data/README.md +62 -1
- data/lib/transitionable/version.rb +1 -1
- data/lib/transitionable.rb +32 -15
- data/transitionable.gemspec +1 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 952f7bc9e470346c073f9e6ea3a57dfe99cdd95c
|
4
|
+
data.tar.gz: c3160957e7b0ab4084c17ca8df4e23eb2ab53717
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8179578162e736f2a85b4a24d0e8ad7f5d8b7662b0f4581ed8cf5cf962111d262e103d4867403a829e0aa5781fbdc5bbbc724f6f896c1cd06db1a68fb477fa39
|
7
|
+
data.tar.gz: 9dba3437433e2ffd449623790055c8427d3a343d6cc99bc0bfd1854ca13a6e2cb558fbacf567e685e137421def9f140328e952d3d6c63a2e09a598bce2fe460f
|
data/Gemfile.lock
CHANGED
@@ -1,22 +1,27 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
transitionable (0.
|
4
|
+
transitionable (0.2.0)
|
5
5
|
activesupport (~> 5.2)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activesupport (5.2.
|
10
|
+
activesupport (5.2.1)
|
11
11
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
12
12
|
i18n (>= 0.7, < 2)
|
13
13
|
minitest (~> 5.1)
|
14
14
|
tzinfo (~> 1.1)
|
15
|
+
coderay (1.1.2)
|
15
16
|
concurrent-ruby (1.0.5)
|
16
17
|
diff-lcs (1.3)
|
17
|
-
i18n (1.
|
18
|
+
i18n (1.1.1)
|
18
19
|
concurrent-ruby (~> 1.0)
|
20
|
+
method_source (0.9.2)
|
19
21
|
minitest (5.11.3)
|
22
|
+
pry (0.12.0)
|
23
|
+
coderay (~> 1.1.0)
|
24
|
+
method_source (~> 0.9.0)
|
20
25
|
rake (10.5.0)
|
21
26
|
rspec (3.8.0)
|
22
27
|
rspec-core (~> 3.8.0)
|
@@ -40,6 +45,7 @@ PLATFORMS
|
|
40
45
|
|
41
46
|
DEPENDENCIES
|
42
47
|
bundler (~> 1.16)
|
48
|
+
pry
|
43
49
|
rake (~> 10.0)
|
44
50
|
rspec (~> 3.7)
|
45
51
|
transitionable!
|
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
A convention-based state machine that complements your models without stealing the show.
|
4
4
|
|
5
|
+
Multiple state machines are supported.
|
6
|
+
|
5
7
|
## Installation
|
6
8
|
|
7
9
|
Add this line to your application's Gemfile:
|
@@ -16,10 +18,12 @@ And then execute:
|
|
16
18
|
|
17
19
|
## Usage
|
18
20
|
|
21
|
+
### Single state machine in model
|
22
|
+
|
19
23
|
```ruby
|
20
24
|
class Event
|
21
25
|
|
22
|
-
#
|
26
|
+
# By default, Transitionable assumes including class defines the following constants BEFORE including this module:
|
23
27
|
#
|
24
28
|
# * STATES
|
25
29
|
# * TRANSITIONS
|
@@ -61,6 +65,63 @@ event.validate_transition!(target_state: 'new_state')
|
|
61
65
|
# => returns true or raises Transitionable::InvalidStateTransition exception
|
62
66
|
```
|
63
67
|
|
68
|
+
### Multiple state machines in model
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
class Event
|
72
|
+
|
73
|
+
DELIVERY_STATES = {
|
74
|
+
STAGED: 'staged',
|
75
|
+
STARTED: 'started',
|
76
|
+
COMPLETED: 'completed'
|
77
|
+
}.freeze
|
78
|
+
|
79
|
+
PREP_STATES = {
|
80
|
+
COOKING: 'cooking',
|
81
|
+
COOKED: 'cooked'
|
82
|
+
}.freeze
|
83
|
+
|
84
|
+
DELIVERY_TRANSITIONS = [
|
85
|
+
{ from: DELIVERY_STATES[:STAGED], to: DELIVERY_STATES[:STARTED] },
|
86
|
+
{ from: DELIVERY_STATES[:STARTED], to: DELIVERY_STATES[:COMPLETED] }
|
87
|
+
].freeze
|
88
|
+
|
89
|
+
PREP_TRANSITIONS = [
|
90
|
+
{ from: PREP_STATES[:COOKING], to: PREP_STATES[:COOKED] }
|
91
|
+
]
|
92
|
+
|
93
|
+
include Transitionable
|
94
|
+
|
95
|
+
transition :delivery_state, DELIVERY_STATES, DELIVERY_TRANSITIONS
|
96
|
+
transition :prep_state, PREP_STATES, PREP_TRANSITIONS
|
97
|
+
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
Provides the following helpers
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
event.staged?
|
105
|
+
event.started?
|
106
|
+
event.completed?
|
107
|
+
event.cooking?
|
108
|
+
event.cooked?
|
109
|
+
```
|
110
|
+
|
111
|
+
Provides 2 validation methods
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
event.validate_transition(target_state: 'new_state')
|
115
|
+
# => returns true or false
|
116
|
+
|
117
|
+
event.validate_transition!(target_state: 'new_state')
|
118
|
+
# => returns true or raises Transitionable::InvalidStateTransition exception
|
119
|
+
```
|
120
|
+
|
121
|
+
#### Important assumptions with multiple state machines:
|
122
|
+
* Each state must be unique across all state machines in the model. Otherwise, `InvalidStateTransition` exception will thrown as Transitionable attempts to define the same helper method multiple times.
|
123
|
+
* For the same reason, `"#{state}?"` must not be defined in your model for each of your state.
|
124
|
+
|
64
125
|
## Development
|
65
126
|
|
66
127
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/transitionable.rb
CHANGED
@@ -8,39 +8,56 @@ module Transitionable
|
|
8
8
|
|
9
9
|
class InvalidStateTransition < StandardError
|
10
10
|
def initialize(from_state, to_state)
|
11
|
-
msg =
|
11
|
+
msg = from_state ?
|
12
|
+
"Can't transition from #{from_state} to #{to_state}." :
|
13
|
+
"Can't transition to #{to_state}."
|
12
14
|
super(msg)
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
16
18
|
module ClassMethods
|
17
|
-
attr_accessor :
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
self
|
19
|
+
attr_accessor :state_machines
|
20
|
+
|
21
|
+
# This assumes states is a hash
|
22
|
+
def transition(name, states = self::STATES, transitions = self::TRANSITIONS)
|
23
|
+
self.state_machines ||= {}
|
24
|
+
self.state_machines[name] = { states: states.values, transitions: transitions }
|
25
|
+
self.state_machines[name][:states].each do |this_state|
|
26
|
+
raise 'Method already defined' if self.new.respond_to? this_state
|
22
27
|
define_method "#{this_state}?" do
|
23
|
-
|
28
|
+
current_state_based_on(this_state) == this_state
|
24
29
|
end
|
25
30
|
end
|
26
31
|
end
|
27
32
|
end
|
28
33
|
|
29
|
-
def transitionable_state
|
30
|
-
self.send(self.class.trans_column)
|
31
|
-
end
|
32
|
-
|
33
34
|
def validate_transition!(target_state:)
|
35
|
+
current_state = current_state_based_on(target_state)
|
34
36
|
unless validate_transition(target_state: target_state)
|
35
|
-
raise InvalidStateTransition.new(
|
37
|
+
raise InvalidStateTransition.new(current_state, target_state)
|
36
38
|
end
|
37
39
|
true
|
38
40
|
end
|
39
41
|
|
40
42
|
def validate_transition(target_state:)
|
41
|
-
self.class
|
42
|
-
|
43
|
-
|
43
|
+
self.class.state_machines.each do |machine_name, machine|
|
44
|
+
next unless machine[:states].include?(target_state)
|
45
|
+
current_state = send(machine_name)
|
46
|
+
matched_transition = machine[:transitions].detect do |transition|
|
47
|
+
transition[:from] == current_state && transition[:to] == target_state
|
48
|
+
end
|
49
|
+
return matched_transition.present?
|
50
|
+
end
|
51
|
+
raise InvalidStateTransition.new(nil, target_state)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def current_state_based_on(target_state)
|
57
|
+
self.class.state_machines.each do |machine_name, machine|
|
58
|
+
return send(machine_name) if machine[:states].include?(target_state)
|
59
|
+
end
|
60
|
+
raise InvalidStateTransition.new(nil, target_state)
|
44
61
|
end
|
45
62
|
|
46
63
|
end
|
data/transitionable.gemspec
CHANGED
@@ -35,5 +35,6 @@ Gem::Specification.new do |spec|
|
|
35
35
|
spec.add_development_dependency "bundler", "~> 1.16"
|
36
36
|
spec.add_development_dependency "rake", "~> 10.0"
|
37
37
|
spec.add_development_dependency "rspec", "~> 3.7"
|
38
|
+
spec.add_development_dependency 'pry'
|
38
39
|
spec.add_dependency "activesupport", "~>5.2"
|
39
40
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: transitionable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- zino
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-11-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.7'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: activesupport
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|