stateful.rb 0.14.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []