stateful_enum 0.5.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fdcc7c193f8597cec775968ecf6cfae38dfe09299d024b877b5da5fae7e5e5ca
4
- data.tar.gz: 20558b138fd9df10e915e40ce6ff197b436a3fe4ce4717f935207dae21d7254e
3
+ metadata.gz: 6d0d82f2cf13496a059030aac0a34a998e47997bcbf6605637fa488c5363454d
4
+ data.tar.gz: 69fc206ccfc3d4290ad0961cac0d011e92ed1fa5421c0064bd881213d52082d5
5
5
  SHA512:
6
- metadata.gz: 1f89d7a82fc8264fbd93b3bd85bb320607409fa662f7726f5b79f04b65da88ee3f080ff39a9ba8d5a2a14c128a1b52fdbf7c773ac6f9cbad8df61cff7052a9e8
7
- data.tar.gz: b2bb43940cffd1be568fff1903920f569585fc3e56a257b9f9d824427ec42de5b2aeaf675faeab48404794ea8179d671dc2a661b98f379e5a7f3f679d6900121
6
+ metadata.gz: e3f5cec3415b7401c36acf28d5dda810d75818cc88b38a768e721757aaf508714ee0613b7926bb0b5e05832df83e6d7deadc3cc0b816eb3e7ab9a6286f6b1399
7
+ data.tar.gz: 6a8d8196552d59c696c42872ae3a9decb62ddef4e75b42566af8e45cd2d3887e3fe6688808ac230f3d35ba7501f4e06328a4fad93462d3e8aee4fca985da4720
@@ -15,6 +15,9 @@ addons:
15
15
 
16
16
  matrix:
17
17
  include:
18
+ - rvm: 2.6.0
19
+ gemfile: gemfiles/Gemfile-rails.6.0.x
20
+
18
21
  - rvm: 2.6.0
19
22
  gemfile: gemfiles/Gemfile-rails.5.2.x
20
23
  - rvm: 2.6.0
data/README.md CHANGED
@@ -122,6 +122,38 @@ end
122
122
 
123
123
  You can define `before` and `after` event hooks inside of an `event` block.
124
124
 
125
+ ### Inspecting All Defined Events And Current Possible Events
126
+
127
+ You can get the list of defined events from the model class:
128
+
129
+ ```ruby
130
+ Bug.stateful_enum.events
131
+ #=> an Array of all defined StatefulEnum::Machine::Event objects
132
+ ```
133
+
134
+ And you can get the list of possible event definitions from the model instance:
135
+
136
+ ```ruby
137
+ Bug.new(status: :assigned).stateful_enum.possible_events
138
+ #=> an Array of StatefulEnum::Machine::Event objects that are callable from the receiver object
139
+ ```
140
+
141
+ Maybe what you really need for your app is the list of possible event "names":
142
+
143
+ ```ruby
144
+ Bug.new(status: :assigned).stateful_enum.possible_event_names
145
+ #=> [:resolve, :close]
146
+ ```
147
+
148
+ You can get the list of next possible state names as well:
149
+
150
+ ```ruby
151
+ Bug.new(status: :assigned).stateful_enum.possible_states
152
+ #=> [:resolved, :closed]
153
+ ```
154
+
155
+ These features would help some kind of metaprogramming over state transitions.
156
+
125
157
 
126
158
  ## Generating State Machine Diagrams
127
159
 
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '..'
4
+
5
+ gem 'rails', '~> 6.0.0.rc1'
6
+ gem 'selenium-webdriver'
@@ -65,7 +65,6 @@ module StatefulEnum
65
65
  end
66
66
  end
67
67
 
68
- class Item < Struct.new(:from, :to, :label)
69
- end
68
+ Item = Struct.new(:from, :to, :label)
70
69
  end
71
70
  end
@@ -10,13 +10,13 @@ module StatefulEnum
10
10
  # end
11
11
  # end
12
12
  def enum(definitions, &block)
13
- prefix, suffix = definitions[:_prefix], definitions[:_suffix] if Rails::VERSION::STRING >= '5'
13
+ prefix, suffix = definitions[:_prefix], definitions[:_suffix] if Rails::VERSION::MAJOR >= 5
14
14
  enum = super definitions
15
15
 
16
16
  if block
17
17
  definitions.each_key do |column|
18
18
  states = enum[column]
19
- StatefulEnum::Machine.new self, column, (states.is_a?(Hash) ? states.keys : states), prefix, suffix, &block
19
+ (@_defined_stateful_enums ||= []) << StatefulEnum::Machine.new(self, column, (states.is_a?(Hash) ? states.keys : states), prefix, suffix, &block)
20
20
  end
21
21
  end
22
22
  end
@@ -2,8 +2,10 @@
2
2
 
3
3
  module StatefulEnum
4
4
  class Machine
5
+ attr_reader :events
6
+
5
7
  def initialize(model, column, states, prefix, suffix, &block)
6
- @model, @column, @states, @event_names = model, column, states, []
8
+ @model, @column, @states, @events = model, column, states, []
7
9
  @prefix = if prefix
8
10
  prefix == true ? "#{column}_" : "#{prefix}_"
9
11
  end
@@ -20,40 +22,41 @@ module StatefulEnum
20
22
  end
21
23
 
22
24
  def event(name, &block)
23
- raise ArgumentError, "event: :#{name} has already been defined." if @event_names.include? name
24
- Event.new @model, @column, @states, @prefix, @suffix, name, &block
25
- @event_names << name
25
+ raise ArgumentError, "event: :#{name} has already been defined." if @events.map(&:name).include? name
26
+ @events << Event.new(@model, @column, @states, @prefix, @suffix, name, &block)
26
27
  end
27
28
 
28
29
  class Event
30
+ attr_reader :name, :value_method_name
31
+
29
32
  def initialize(model, column, states, prefix, suffix, name, &block)
30
33
  @states, @name, @transitions, @before, @after = states, name, {}, [], []
31
34
 
32
35
  instance_eval(&block) if block
33
36
 
34
37
  transitions, before, after = @transitions, @before, @after
35
- new_method_name = "#{prefix}#{name}#{suffix}"
38
+ @value_method_name = value_method_name = :"#{prefix}#{name}#{suffix}"
36
39
 
37
40
  # defining event methods
38
41
  model.class_eval do
39
42
  # def assign()
40
- detect_enum_conflict! column, new_method_name
43
+ detect_enum_conflict! column, value_method_name
41
44
 
42
45
  # defining callbacks
43
- define_callbacks new_method_name
46
+ define_callbacks value_method_name
44
47
  before.each do |before_callback|
45
- model.set_callback new_method_name, :before, before_callback
48
+ model.set_callback value_method_name, :before, before_callback
46
49
  end
47
50
  after.each do |after_callback|
48
- model.set_callback new_method_name, :after, after_callback
51
+ model.set_callback value_method_name, :after, after_callback
49
52
  end
50
53
 
51
- define_method new_method_name do
54
+ define_method value_method_name do
52
55
  to, condition = transitions[send(column).to_sym]
53
56
  #TODO better error
54
57
  if to && (!condition || instance_exec(&condition))
55
58
  #TODO transaction?
56
- run_callbacks new_method_name do
59
+ run_callbacks value_method_name do
57
60
  original_method = self.class.send(:_enum_methods_module).instance_method "#{prefix}#{to}#{suffix}!"
58
61
  original_method.bind(self).call
59
62
  end
@@ -63,14 +66,14 @@ module StatefulEnum
63
66
  end
64
67
 
65
68
  # def assign!()
66
- detect_enum_conflict! column, "#{new_method_name}!"
67
- define_method "#{new_method_name}!" do
68
- send(new_method_name) || raise('Invalid transition')
69
+ detect_enum_conflict! column, "#{value_method_name}!"
70
+ define_method "#{value_method_name}!" do
71
+ send(value_method_name) || raise('Invalid transition')
69
72
  end
70
73
 
71
74
  # def can_assign?()
72
- detect_enum_conflict! column, "can_#{new_method_name}?"
73
- define_method "can_#{new_method_name}?" do
75
+ detect_enum_conflict! column, "can_#{value_method_name}?"
76
+ define_method "can_#{value_method_name}?" do
74
77
  state = send(column).to_sym
75
78
  return false unless transitions.key? state
76
79
  _to, condition = transitions[state]
@@ -78,8 +81,8 @@ module StatefulEnum
78
81
  end
79
82
 
80
83
  # def assign_transition()
81
- detect_enum_conflict! column, "#{new_method_name}_transition"
82
- define_method "#{new_method_name}_transition" do
84
+ detect_enum_conflict! column, "#{value_method_name}_transition"
85
+ define_method "#{value_method_name}_transition" do
83
86
  transitions[send(column).to_sym].try! :first
84
87
  end
85
88
  end
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'stateful_enum/active_record_extension'
4
+ require 'stateful_enum/state_inspection'
4
5
 
5
6
  module StatefulEnum
6
7
  class Railtie < ::Rails::Railtie
7
- ActiveSupport.on_load :active_record do
8
- ::ActiveRecord::Base.extend StatefulEnum::ActiveRecordEnumExtension
8
+ initializer 'stateful_enum' do
9
+ ActiveSupport.on_load :active_record do
10
+ ::ActiveRecord::Base.extend StatefulEnum::ActiveRecordEnumExtension
11
+ ::ActiveRecord::Base.include StatefulEnum::StateInspection
12
+ end
9
13
  end
10
14
  end
11
15
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stateful_enum/machine'
4
+
5
+ module StatefulEnum
6
+ module StateInspection
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ def stateful_enum
11
+ @_defined_stateful_enums
12
+ end
13
+ end
14
+
15
+ def stateful_enum
16
+ StateInspector.new(self.class.stateful_enum, self)
17
+ end
18
+ end
19
+
20
+ class StateInspector
21
+ def initialize(defined_stateful_enums, model_instance)
22
+ @defined_stateful_enums, @model_instance = defined_stateful_enums, model_instance
23
+ end
24
+
25
+ # List of possible events from the current state
26
+ def possible_events
27
+ @defined_stateful_enums.flat_map {|se| se.events.select {|e| @model_instance.send("can_#{e.value_method_name}?") } }
28
+ end
29
+
30
+ # List of possible event names from the current state
31
+ def possible_event_names
32
+ possible_events.map(&:value_method_name)
33
+ end
34
+
35
+ # List of transitionable states from the current state
36
+ def possible_states
37
+ @defined_stateful_enums.flat_map do |stateful_enum|
38
+ col = stateful_enum.instance_variable_get :@column
39
+ pe = stateful_enum.events.select {|e| @model_instance.send("can_#{e.value_method_name}?") }
40
+ pe.flat_map {|e| e.instance_variable_get(:@transitions)[@model_instance.send(col).to_sym].first }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StatefulEnum
4
- VERSION = '0.5.0'
4
+ VERSION = '0.6.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stateful_enum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Akira Matsuda
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-02-07 00:00:00.000000000 Z
11
+ date: 2019-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -114,6 +114,7 @@ files:
114
114
  - gemfiles/Gemfile-rails.5.0.x
115
115
  - gemfiles/Gemfile-rails.5.1.x
116
116
  - gemfiles/Gemfile-rails.5.2.x
117
+ - gemfiles/Gemfile-rails.6.0.x
117
118
  - gemfiles/Gemfile-rails.edge
118
119
  - lib/generators/stateful_enum/graph_generator.rb
119
120
  - lib/generators/stateful_enum/plantuml_generator.rb
@@ -121,6 +122,7 @@ files:
121
122
  - lib/stateful_enum/active_record_extension.rb
122
123
  - lib/stateful_enum/machine.rb
123
124
  - lib/stateful_enum/railtie.rb
125
+ - lib/stateful_enum/state_inspection.rb
124
126
  - lib/stateful_enum/version.rb
125
127
  - stateful_enum.gemspec
126
128
  homepage: https://github.com/amatsuda/stateful_enum
@@ -142,8 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
144
  - !ruby/object:Gem::Version
143
145
  version: '0'
144
146
  requirements: []
145
- rubyforge_project:
146
- rubygems_version: 2.7.8
147
+ rubygems_version: 3.0.3
147
148
  signing_key:
148
149
  specification_version: 4
149
150
  summary: A state machine plugin on top of ActiveRecord::Enum