stateful.rb 0.14.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '018501c0d47d3cb20bda391c184d7321e8fb090d9ef7042e5d949eadf2d655bf'
4
+ data.tar.gz: 7eee0d0b0dbd26a3d223b6b029b95cfdef37eec8a52d1d3248a136a71bf685c5
5
+ SHA512:
6
+ metadata.gz: 1237f18c8b2899e7fc6d79f9f7a72bf69a1ffa727fd5bbd44ef77c62b75d1a9b8195a50f7dd49d62b851e5c5a2e1590167f8e4b0648f279051aa6d9b288cc781
7
+ data.tar.gz: 617ffddbc3d9011a50e8908cdcc8f0019ee230a17eab96c85496a9555e9a27c7a775de9457947db1b117d93e34e09cf8b76fb0bc33597d3c7b26c18b3c33e5e7
@@ -0,0 +1,18 @@
1
+ # Array/shuffle.rb
2
+ # Array#shuffle
3
+
4
+ # 20130922
5
+ # 0.3.0
6
+
7
+ # Description: Reorders an array randomly.
8
+
9
+ # Changes since 0.2:
10
+ # 1. Removed the aliases to their own files.
11
+
12
+ class Array
13
+
14
+ def shuffle
15
+ sort_by{rand}
16
+ end
17
+
18
+ end
@@ -0,0 +1,61 @@
1
+ # Stateful.rb
2
+ # Stateful
3
+
4
+ # 20150206
5
+ # 0.14.0
6
+
7
+ require_relative File.join('Stateful', 'ClassMethods')
8
+ require_relative File.join('Stateful', 'InstanceMethods')
9
+
10
+ module Stateful
11
+
12
+ class << self
13
+
14
+ def set_column_name(klass)
15
+ if defined?(::ActiveRecord::Base) && klass < ::ActiveRecord::Base
16
+ unless klass.instance_variable_get(:@stateful_column_name)
17
+ klass.instance_variable_set(:@stateful_column_name, 'current_state')
18
+ end
19
+ else
20
+ unless klass.instance_variable_get(:@stateful_variable_name)
21
+ klass.instance_variable_set(:@stateful_variable_name, 'current_state')
22
+ end
23
+ end
24
+ end
25
+
26
+ def load_persistence_class_methods(klass)
27
+ if defined?(::ActiveRecord::Base) && klass < ::ActiveRecord::Base
28
+ require_relative File.join('Stateful', 'ActiveRecord')
29
+ klass.extend(Stateful::ActiveRecord::ClassMethods)
30
+ else
31
+ require_relative File.join('Stateful', 'Poro')
32
+ klass.extend(Stateful::Poro::ClassMethods)
33
+ end
34
+ end
35
+
36
+ def define_instance_methods(klass)
37
+ if defined?(::ActiveRecord::Base) && klass < ::ActiveRecord::Base
38
+ klass.define_stateful_column_name_setter_method
39
+ klass.define_stateful_column_name_getter_method
40
+ else
41
+ klass.define_stateful_variable_name_setter_method
42
+ klass.define_stateful_variable_name_getter_method
43
+ end
44
+ klass.define_next_state_method
45
+ klass.define_transitions_method
46
+ klass.define_initial_stateQ_method
47
+ klass.define_final_stateQ_method
48
+ end
49
+
50
+ def extended(klass)
51
+ klass.extend(Stateful::ClassMethods)
52
+ klass.send(:include, Stateful::InstanceMethods)
53
+ set_column_name(klass)
54
+ load_persistence_class_methods(klass)
55
+ define_instance_methods(klass)
56
+ end
57
+ alias_method :included, :extended
58
+
59
+ end # class << self
60
+
61
+ end
@@ -0,0 +1,39 @@
1
+ # Stateful/ActiveRecord.rb
2
+ # Stateful::ActiveRecord
3
+
4
+ require_relative File.join('..', 'Stateful')
5
+ require_relative File.join('ActiveRecord', 'ClassMethods')
6
+
7
+ module Stateful
8
+ module ActiveRecord
9
+
10
+ class << self
11
+
12
+ def set_column_name(klass)
13
+ unless klass.instance_variable_get(:@stateful_column_name)
14
+ klass.instance_variable_set(:@stateful_column_name, 'current_state')
15
+ end
16
+ end
17
+
18
+ def define_instance_methods(klass)
19
+ klass.define_stateful_column_name_setter_method
20
+ klass.define_stateful_column_name_getter_method
21
+ klass.define_next_state_method
22
+ klass.define_transitions_method
23
+ klass.define_initial_stateQ_method
24
+ klass.define_final_stateQ_method
25
+ end
26
+
27
+ def extended(klass)
28
+ klass.extend(Stateful::ClassMethods)
29
+ klass.send(:include, Stateful::InstanceMethods)
30
+ set_column_name(klass)
31
+ klass.extend(Stateful::ActiveRecord::ClassMethods)
32
+ define_instance_methods(klass)
33
+ end
34
+ alias_method :included, :extended
35
+
36
+ end # class << self
37
+
38
+ end
39
+ end
@@ -0,0 +1,87 @@
1
+ # Stateful/ActiveRecord/ClassMethods.rb
2
+ # Stateful::ActiveRecord::ClassMethods
3
+
4
+ module Stateful
5
+ module ActiveRecord
6
+ module ClassMethods
7
+
8
+ def stateful_column_name=(stateful_column_name)
9
+ @stateful_column_name = stateful_column_name
10
+ end
11
+
12
+ def stateful_column_name
13
+ @stateful_column_name
14
+ end
15
+
16
+ def define_event_method(transition)
17
+ stateful_column_name = self.instance_variable_get(:@stateful_column_name)
18
+ define_method(transition.event_name) do
19
+ next_state_name = self.send(stateful_column_name).next_state_name(transition.event_name)
20
+ next_state = self.class.stateful_states.find(next_state_name)
21
+ self.send("#{stateful_column_name}=", next_state)
22
+ end
23
+ end
24
+
25
+ def define_status_boolean_method(state_name)
26
+ stateful_column_name = self.instance_variable_get(:@stateful_column_name)
27
+ define_method("#{state_name}?") do
28
+ self.send(stateful_column_name).name == state_name
29
+ end
30
+ end
31
+
32
+ def define_stateful_column_name_setter_method
33
+ stateful_column_name = self.instance_variable_get(:@stateful_column_name)
34
+ define_method("#{stateful_column_name}=") do |state|
35
+ instance_variable_set("@#{stateful_column_name}", self.class.stateful_states.find(state))
36
+ write_attribute(stateful_column_name, instance_variable_get("@#{stateful_column_name}").name)
37
+ self.save
38
+ instance_variable_get("@#{stateful_column_name}")
39
+ end
40
+ end
41
+
42
+ def define_stateful_column_name_getter_method
43
+ stateful_column_name = self.instance_variable_get(:@stateful_column_name)
44
+ define_method(stateful_column_name) do
45
+ instance_variable_set("@#{stateful_column_name}", self.class.stateful_states.find(read_attribute(stateful_column_name)))
46
+ if state = instance_variable_get("@#{stateful_column_name}")
47
+ state
48
+ else
49
+ initial_state = self.class.stateful_states.initial_state
50
+ self.send("#{stateful_column_name}=", initial_state.name)
51
+ initial_state
52
+ end
53
+ end
54
+ end
55
+
56
+ def define_next_state_method
57
+ stateful_column_name = self.instance_variable_get(:@stateful_column_name)
58
+ define_method(:next_state) do |event_name|
59
+ next_state_name = self.send(stateful_column_name).next_state_name(event_name)
60
+ all_states.find(next_state_name)
61
+ end
62
+ end
63
+
64
+ def define_transitions_method
65
+ stateful_column_name = self.instance_variable_get(:@stateful_column_name)
66
+ define_method(:transitions) do
67
+ self.send(stateful_column_name).transitions
68
+ end
69
+ end
70
+
71
+ def define_initial_stateQ_method
72
+ stateful_column_name = self.instance_variable_get(:@stateful_column_name)
73
+ define_method(:initial_state?) do
74
+ self.send(stateful_column_name) == initial_state
75
+ end
76
+ end
77
+
78
+ def define_final_stateQ_method
79
+ stateful_column_name = self.instance_variable_get(:@stateful_column_name)
80
+ define_method(:final_state?) do
81
+ final_states.include?(self.send(stateful_column_name))
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,45 @@
1
+ # Stateful/ClassMethods.rb
2
+ # Stateful::ClassMethods
3
+
4
+ require_relative 'States'
5
+
6
+ module Stateful
7
+ module ClassMethods
8
+
9
+ def stateful_states
10
+ @stateful_states ||= Stateful::States.new(self)
11
+ end
12
+
13
+ # start DSL
14
+
15
+ def initial_state(state_name = nil, options = {}, &block)
16
+ stateful_states.initial_state(state_name, options, &block)
17
+ end
18
+
19
+ def final_state(*state_names)
20
+ stateful_states.final_state(*state_names)
21
+ end
22
+
23
+ def final_states(*state_names)
24
+ stateful_states.final_states(*state_names)
25
+ end
26
+
27
+ def state(state_name, options = {}, &block)
28
+ stateful_states.state(state_name, options, &block)
29
+ end
30
+
31
+ def stateful(options = {}, &block)
32
+ stateful_states.stateful(options, &block)
33
+ end
34
+
35
+ # end DSL
36
+
37
+ # boolean methods
38
+
39
+ def final_state?
40
+ !final_states.empty?
41
+ end
42
+ alias_method :has_final_state?, :final_state?
43
+
44
+ end
45
+ end
@@ -0,0 +1,30 @@
1
+ # Stateful/InstanceMethods.rb
2
+ # Stateful::InstanceMethods
3
+
4
+ module Stateful
5
+ module InstanceMethods
6
+
7
+ def all_states
8
+ self.class.stateful_states
9
+ end
10
+
11
+ def initial_state
12
+ self.class.initial_state
13
+ end
14
+
15
+ def final_state
16
+ self.class.final_state
17
+ end
18
+
19
+ def final_states
20
+ self.class.final_states
21
+ end
22
+
23
+ # boolean methods
24
+
25
+ def active?
26
+ !final_state?
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,39 @@
1
+ # Stateful/Poro.rb
2
+ # Stateful::Poro
3
+
4
+ require_relative File.join('..', 'Stateful')
5
+ require_relative File.join('Poro', 'ClassMethods')
6
+
7
+ module Stateful
8
+ module Poro
9
+
10
+ class << self
11
+
12
+ def set_variable_name(klass)
13
+ unless klass.instance_variable_get(:@stateful_variable_name)
14
+ klass.instance_variable_set(:@stateful_variable_name, 'current_state')
15
+ end
16
+ end
17
+
18
+ def define_instance_methods(klass)
19
+ klass.define_stateful_variable_name_setter_method
20
+ klass.define_stateful_variable_name_getter_method
21
+ klass.define_next_state_method
22
+ klass.define_transitions_method
23
+ klass.define_initial_stateQ_method
24
+ klass.define_final_stateQ_method
25
+ end
26
+
27
+ def extended(klass)
28
+ klass.extend(Stateful::ClassMethods)
29
+ klass.send(:include, Stateful::InstanceMethods)
30
+ set_variable_name(klass)
31
+ klass.extend(Stateful::Poro::ClassMethods)
32
+ define_instance_methods(klass)
33
+ end
34
+ alias_method :included, :extended
35
+
36
+ end # class << self
37
+
38
+ end
39
+ end
@@ -0,0 +1,83 @@
1
+ # Stateful/Poro/ClassMethods.rb
2
+ # Stateful::Poro::ClassMethods
3
+
4
+ module Stateful
5
+ module Poro
6
+ module ClassMethods
7
+
8
+ def stateful_variable_name=(stateful_variable_name)
9
+ @stateful_variable_name = stateful_variable_name
10
+ end
11
+
12
+ def stateful_variable_name
13
+ @stateful_variable_name
14
+ end
15
+
16
+ def define_event_method(transition)
17
+ stateful_variable_name = self.instance_variable_get(:@stateful_variable_name)
18
+ define_method(transition.event_name) do
19
+ next_state_name = self.send(stateful_variable_name).next_state_name(transition.event_name)
20
+ next_state = self.class.stateful_states.find(next_state_name)
21
+ self.send("#{stateful_variable_name}=", next_state)
22
+ end
23
+ end
24
+
25
+ def define_status_boolean_method(state_name)
26
+ stateful_variable_name = self.instance_variable_get(:@stateful_variable_name)
27
+ define_method("#{state_name}?") do
28
+ self.send(stateful_variable_name).name == state_name
29
+ end
30
+ end
31
+
32
+ def define_stateful_variable_name_setter_method
33
+ stateful_variable_name = self.instance_variable_get(:@stateful_variable_name)
34
+ define_method("#{stateful_variable_name}=") do |state|
35
+ instance_variable_set("@#{stateful_variable_name}", self.class.stateful_states.find(state))
36
+ end
37
+ end
38
+
39
+ def define_stateful_variable_name_getter_method
40
+ stateful_variable_name = self.instance_variable_get(:@stateful_variable_name)
41
+ define_method(stateful_variable_name) do
42
+ if state = instance_variable_get("@#{stateful_variable_name}")
43
+ state
44
+ else
45
+ initial_state = self.class.stateful_states.initial_state
46
+ self.send("#{stateful_variable_name}=", initial_state.name)
47
+ initial_state
48
+ end
49
+ end
50
+ end
51
+
52
+ def define_next_state_method
53
+ stateful_variable_name = self.instance_variable_get(:@stateful_variable_name)
54
+ define_method(:next_state) do |event_name|
55
+ next_state_name = self.send(stateful_variable_name).next_state_name(event_name)
56
+ all_states.find(next_state_name)
57
+ end
58
+ end
59
+
60
+ def define_transitions_method
61
+ stateful_variable_name = self.instance_variable_get(:@stateful_variable_name)
62
+ define_method(:transitions) do
63
+ self.send(stateful_variable_name).transitions
64
+ end
65
+ end
66
+
67
+ def define_initial_stateQ_method
68
+ stateful_variable_name = self.instance_variable_get(:@stateful_variable_name)
69
+ define_method(:initial_state?) do
70
+ self.send(stateful_variable_name) == initial_state
71
+ end
72
+ end
73
+
74
+ def define_final_stateQ_method
75
+ stateful_variable_name = self.instance_variable_get(:@stateful_variable_name)
76
+ define_method(:final_state?) do
77
+ final_states.include?(self.send(stateful_variable_name))
78
+ end
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,42 @@
1
+ # Stateful/State.rb
2
+ # Stateful::State
3
+
4
+ require_relative File.join('..', 'Array', 'shuffle')
5
+ require_relative 'Transition'
6
+
7
+ module Stateful
8
+ class State
9
+
10
+ attr_accessor :name
11
+ attr_accessor :transitions
12
+
13
+ def initialize(name, options = {})
14
+ @name = name
15
+ @options = options
16
+ @transitions = []
17
+ end
18
+
19
+ def event(event)
20
+ event_name, new_state = event.keys.first, event.values.first
21
+ transitions << Transition.new(event_name, new_state)
22
+ transitions.shuffle if non_deterministic_event_ordering?
23
+ transitions
24
+ end
25
+ alias_method :on, :event
26
+
27
+ def next_state_name(event_name)
28
+ if matching_transition = transitions.detect{|transition| transition.event_name == event_name}
29
+ matching_transition.next_state_name
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def non_deterministic_event_ordering?
36
+ @options[:non_deterministic_event_ordering] ||
37
+ @options[:non_deterministic] ||
38
+ (@options[:deterministic] && !@options[:deterministic])
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,101 @@
1
+ # Stateful/States.rb
2
+ # Stateful::States
3
+
4
+ require_relative 'State'
5
+
6
+ module Stateful
7
+ class States
8
+
9
+ attr_reader :all
10
+
11
+ def initialize(klass, options = {})
12
+ @klass = klass
13
+ @options = options
14
+ @all = []
15
+ @initial_state = nil
16
+ @final_states = []
17
+ end
18
+
19
+ def detect(candidate_state)
20
+ candidate_state_name = (
21
+ if candidate_state.is_a?(State)
22
+ candidate_state.name
23
+ else
24
+ candidate_state && candidate_state.to_sym
25
+ end
26
+ )
27
+ self.all.detect{|state| state.name == candidate_state_name}
28
+ end
29
+ alias_method :find, :detect
30
+
31
+ def missing?(state)
32
+ !detect(state)
33
+ end
34
+ alias_method :new_state?, :missing?
35
+
36
+ def present?(state)
37
+ !!detect(state)
38
+ end
39
+
40
+ def find_or_create(state_name, options = {})
41
+ find(state_name) || (
42
+ state = State.new(state_name, options)
43
+ all << state
44
+ state
45
+ )
46
+ end
47
+
48
+ def initial_state(state_name = nil, options = {}, &block)
49
+ if state_name
50
+ @initial_state = state(state_name, options, &block)
51
+ else
52
+ @initial_state
53
+ end
54
+ end
55
+
56
+ def final_state(*state_names)
57
+ final_states(*state_names).first
58
+ end
59
+ alias_method :final_state=, :final_state
60
+
61
+ def final_states(*state_names)
62
+ state_names = state_names.flatten
63
+ if !state_names.empty?
64
+ state_names.each do |state_name|
65
+ final_state = State.new(state_name)
66
+ @final_states << final_state
67
+ all << final_state
68
+ end
69
+ @final_states
70
+ else
71
+ @final_states
72
+ end
73
+ end
74
+
75
+ def state(state_name, options = {}, &block)
76
+ options.merge!(non_deterministic_event_ordering: global_non_deterministic_event_ordering?)
77
+ state = find_or_create(state_name, options)
78
+ state.instance_eval(&block) if block
79
+ state.transitions.each do |transition|
80
+ @klass.define_event_method(transition)
81
+ end
82
+ @klass.define_status_boolean_method(state_name)
83
+ state
84
+ end
85
+
86
+ def stateful(options = {}, &block)
87
+ @options = options
88
+ instance_eval(&block) if block
89
+ end
90
+
91
+ private
92
+
93
+ def global_non_deterministic_event_ordering?
94
+ @options[:global_non_deterministic_event_ordering] ||
95
+ @options[:non_deterministic_event_ordering] ||
96
+ @options[:non_deterministic] ||
97
+ (@options[:deterministic] && !@options[:deterministic])
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,15 @@
1
+ # Stateful/Transition.rb
2
+ # Stateful::Transition
3
+
4
+ module Stateful
5
+ class Transition
6
+
7
+ attr_reader :event_name
8
+ attr_reader :next_state_name
9
+
10
+ def initialize(event_name, next_state_name)
11
+ @event_name, @next_state_name = event_name, next_state_name
12
+ end
13
+
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stateful.rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.14.1
5
+ platform: ruby
6
+ authors:
7
+ - thoran
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-08-02 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Easily add state and manage it with Poro and ActiveRecord objects.
14
+ email: code@thoran.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/Array/shuffle.rb
20
+ - lib/Stateful.rb
21
+ - lib/Stateful/ActiveRecord.rb
22
+ - lib/Stateful/ActiveRecord/ClassMethods.rb
23
+ - lib/Stateful/ClassMethods.rb
24
+ - lib/Stateful/InstanceMethods.rb
25
+ - lib/Stateful/Poro.rb
26
+ - lib/Stateful/Poro/ClassMethods.rb
27
+ - lib/Stateful/State.rb
28
+ - lib/Stateful/States.rb
29
+ - lib/Stateful/Transition.rb
30
+ homepage: http://github.com/thoran/Stateful
31
+ licenses:
32
+ - MIT
33
+ metadata: {}
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.8.6
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubygems_version: 3.0.3
50
+ signing_key:
51
+ specification_version: 4
52
+ summary: A Ruby state machine.
53
+ test_files: []