state-fu 0.11.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.
Files changed (85) hide show
  1. data/LICENSE +40 -0
  2. data/README.textile +293 -0
  3. data/Rakefile +114 -0
  4. data/lib/binding.rb +292 -0
  5. data/lib/event.rb +192 -0
  6. data/lib/executioner.rb +120 -0
  7. data/lib/hooks.rb +39 -0
  8. data/lib/interface.rb +132 -0
  9. data/lib/lathe.rb +538 -0
  10. data/lib/machine.rb +184 -0
  11. data/lib/method_factory.rb +243 -0
  12. data/lib/persistence.rb +116 -0
  13. data/lib/persistence/active_record.rb +34 -0
  14. data/lib/persistence/attribute.rb +47 -0
  15. data/lib/persistence/base.rb +100 -0
  16. data/lib/persistence/relaxdb.rb +23 -0
  17. data/lib/persistence/session.rb +7 -0
  18. data/lib/sprocket.rb +58 -0
  19. data/lib/state-fu.rb +56 -0
  20. data/lib/state.rb +48 -0
  21. data/lib/support/active_support_lite/array.rb +9 -0
  22. data/lib/support/active_support_lite/array/access.rb +60 -0
  23. data/lib/support/active_support_lite/array/conversions.rb +202 -0
  24. data/lib/support/active_support_lite/array/extract_options.rb +21 -0
  25. data/lib/support/active_support_lite/array/grouping.rb +109 -0
  26. data/lib/support/active_support_lite/array/random_access.rb +13 -0
  27. data/lib/support/active_support_lite/array/wrapper.rb +25 -0
  28. data/lib/support/active_support_lite/blank.rb +67 -0
  29. data/lib/support/active_support_lite/cattr_reader.rb +57 -0
  30. data/lib/support/active_support_lite/keys.rb +57 -0
  31. data/lib/support/active_support_lite/misc.rb +59 -0
  32. data/lib/support/active_support_lite/module.rb +1 -0
  33. data/lib/support/active_support_lite/module/delegation.rb +130 -0
  34. data/lib/support/active_support_lite/object.rb +9 -0
  35. data/lib/support/active_support_lite/string.rb +38 -0
  36. data/lib/support/active_support_lite/symbol.rb +16 -0
  37. data/lib/support/applicable.rb +41 -0
  38. data/lib/support/arrays.rb +197 -0
  39. data/lib/support/core_ext.rb +90 -0
  40. data/lib/support/exceptions.rb +106 -0
  41. data/lib/support/has_options.rb +16 -0
  42. data/lib/support/logger.rb +165 -0
  43. data/lib/support/methodical.rb +17 -0
  44. data/lib/support/no_stdout.rb +55 -0
  45. data/lib/support/plotter.rb +62 -0
  46. data/lib/support/vizier.rb +300 -0
  47. data/lib/tasks/spec_last.rake +55 -0
  48. data/lib/tasks/state_fu.rake +57 -0
  49. data/lib/transition.rb +338 -0
  50. data/lib/transition_query.rb +224 -0
  51. data/spec/custom_formatter.rb +49 -0
  52. data/spec/features/binding_and_transition_helper_mixin_spec.rb +111 -0
  53. data/spec/features/method_missing_only_once_spec.rb +28 -0
  54. data/spec/features/not_requirements_spec.rb +118 -0
  55. data/spec/features/plotter_spec.rb +97 -0
  56. data/spec/features/shared_log_spec.rb +7 -0
  57. data/spec/features/singleton_machine_spec.rb +39 -0
  58. data/spec/features/state_and_array_options_accessor_spec.rb +47 -0
  59. data/spec/features/transition_boolean_comparison_spec.rb +101 -0
  60. data/spec/helper.rb +13 -0
  61. data/spec/integration/active_record_persistence_spec.rb +202 -0
  62. data/spec/integration/binding_extension_spec.rb +41 -0
  63. data/spec/integration/class_accessor_spec.rb +117 -0
  64. data/spec/integration/event_definition_spec.rb +74 -0
  65. data/spec/integration/example_01_document_spec.rb +133 -0
  66. data/spec/integration/example_02_string_spec.rb +88 -0
  67. data/spec/integration/instance_accessor_spec.rb +97 -0
  68. data/spec/integration/lathe_extension_spec.rb +67 -0
  69. data/spec/integration/machine_duplication_spec.rb +101 -0
  70. data/spec/integration/relaxdb_persistence_spec.rb +97 -0
  71. data/spec/integration/requirement_reflection_spec.rb +270 -0
  72. data/spec/integration/state_definition_spec.rb +163 -0
  73. data/spec/integration/transition_spec.rb +1033 -0
  74. data/spec/spec.opts +9 -0
  75. data/spec/spec_helper.rb +132 -0
  76. data/spec/state_fu_spec.rb +948 -0
  77. data/spec/units/binding_spec.rb +192 -0
  78. data/spec/units/event_spec.rb +214 -0
  79. data/spec/units/exceptions_spec.rb +82 -0
  80. data/spec/units/lathe_spec.rb +570 -0
  81. data/spec/units/machine_spec.rb +229 -0
  82. data/spec/units/method_factory_spec.rb +366 -0
  83. data/spec/units/sprocket_spec.rb +69 -0
  84. data/spec/units/state_spec.rb +59 -0
  85. metadata +171 -0
@@ -0,0 +1,90 @@
1
+ # if ActiveSupport is absent, install a very small subset of it for
2
+ # some convenience methods
3
+ unless Object.const_defined?('ActiveSupport') #:nodoc
4
+ Dir[File.join(File.dirname( __FILE__), 'active_support_lite','**' )].sort.each do |lib|
5
+ lib = File.expand_path lib
6
+ next unless File.file?( lib )
7
+ require lib
8
+ end
9
+
10
+ class Hash #:nodoc
11
+ include ActiveSupport::CoreExtensions::Hash::Keys
12
+ end
13
+ end
14
+
15
+ # ruby1.9 style symbol comparability for ruby1.8
16
+ if RUBY_VERSION < "1.9"
17
+ class Symbol #:nodoc
18
+ unless instance_methods.include?(:'<=>')
19
+ def <=> other
20
+ self.to_s <=> other.to_s
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ module ArrayToHash
27
+ def to_h
28
+ if RUBY_VERSION >= '1.8.7'
29
+ Hash[self]
30
+ else
31
+ inject({}) { |h, a| h[a.first] = a.last; h }
32
+ end
33
+ end
34
+ end
35
+
36
+ class Array
37
+ include ArrayToHash unless instance_methods.include?(:to_h)
38
+ end
39
+
40
+ class Object
41
+
42
+ def self.__define_method( method_name, &block )
43
+ self.class.class_eval do
44
+ define_method method_name, &block
45
+ end
46
+ end
47
+
48
+ def __define_singleton_method( method_name, &block )
49
+ (class << self; self; end).class_eval do
50
+ define_method method_name, &block
51
+ end
52
+ end
53
+
54
+
55
+ def with_methods_on(other)
56
+ (class << self; self; end).class_eval do
57
+ # we need some accounting to ensure that everything behaves itself when
58
+ # .with_methods_on is called more than once.
59
+ @_with_methods_on ||= []
60
+ if !@_with_methods_on.include?("method_missing_before_#{other.__id__}")
61
+ alias_method "method_missing_before_#{other.__id__}", :method_missing
62
+ end
63
+ @_with_methods_on << "method_missing_before_#{other.__id__}"
64
+
65
+ define_method :method_missing do |method_name, *args|
66
+ if _other.respond_to?(method_name, true)
67
+ _other.__send__( method_name, *args )
68
+ else
69
+ send "method_missing_before_#{other.__id__}", method_name, *args
70
+ end
71
+ end
72
+ end
73
+
74
+ result = yield
75
+
76
+ (class << self; self; end).class_eval do
77
+ # heal the damage
78
+ if @_with_methods_on.pop != "method_missing_before_#{other.__id__}"
79
+ raise "there is no god"
80
+ end
81
+ if !@_with_methods_on.include?("method_missing_before_#{other.__id__}")
82
+ alias_method :method_missing, "method_missing_before_#{other.__id__}"
83
+ undef_method "method_missing_before_#{other.__id__}"
84
+ end
85
+ end
86
+
87
+ result
88
+ end # with_methods_on
89
+ end
90
+
@@ -0,0 +1,106 @@
1
+ module StateFu
2
+
3
+ class MagicMethodError < NoMethodError
4
+ end
5
+
6
+ class Error < ::StandardError
7
+ attr_reader :binding, :options
8
+
9
+ def initialize binding, message=nil, options={}
10
+ @binding = binding
11
+ @options = options
12
+ super message
13
+ end
14
+
15
+ end
16
+
17
+ class TransitionNotFound < Error
18
+ attr_reader :valid_transitions
19
+ attr_reader :valid_destinations
20
+ DEFAULT_MESSAGE = "Transition could not be determined"
21
+
22
+ def initialize(binding, valid_transitions, message=DEFAULT_MESSAGE, options={})
23
+ @valid_transitions = valid_transitions
24
+ @valid_destinations = valid_transitions.map(&:destination)
25
+ super(binding, message, options)
26
+ end
27
+
28
+ def inspect
29
+ "<#{self.class.to_s} #{message} available=[#{valid_destinations.inspect}]>"
30
+ end
31
+
32
+ end
33
+
34
+ class TransitionError < Error
35
+ # TODO default message
36
+ attr_reader :transition
37
+
38
+ def initialize transition, message=nil, options={}
39
+ raise caller.inspect unless transition.is_a?(Transition)
40
+ @transition = transition
41
+ super transition.binding, message, options
42
+ end
43
+
44
+ delegate :origin, :to => :transition
45
+ delegate :target, :to => :transition
46
+ delegate :event, :to => :transition
47
+ delegate :args, :to => :transition
48
+
49
+ # TODO capture these on initialization
50
+ delegate :unmet_requirements, :to => :transition
51
+ delegate :unmet_requirement_messages, :to => :transition
52
+ delegate :requirement_errors, :to => :transition
53
+
54
+ def inspect
55
+ origin_name = origin && origin.name
56
+ target_name = target && target.name
57
+ event_name = event && event.name
58
+ "<#{self.class.to_s} #{message} #{origin_name.inspect}=[#{event_name.inspect}]=>#{target_name.inspect}>"
59
+ end
60
+ end
61
+
62
+ class UnknownTarget < TransitionError
63
+ end
64
+
65
+ class TransitionAlreadyFired < TransitionError
66
+ end
67
+
68
+ class RequirementError < TransitionError
69
+ include Enumerable
70
+
71
+ delegate :each, :to => :to_h
72
+ delegate :length, :to => :to_h
73
+ delegate :empty?, :to => :to_h
74
+
75
+ def to_a
76
+ unmet_requirement_messages
77
+ end
78
+
79
+ def to_h
80
+ requirement_errors
81
+ end
82
+
83
+ def to_s
84
+ inspect
85
+ end
86
+
87
+ def inspect
88
+ "<#{self.class.to_s}::#{__id__} :#{transition.origin.to_sym}-[#{transition.event.to_sym}]->:#{transition.target.to_sym} unmet_requirements=#{to_a.inspect}>"
89
+ end
90
+ end
91
+
92
+ class TransitionHalted < TransitionError
93
+ end
94
+
95
+ # deprecated?
96
+ # class Invalid Transition < TransitionError
97
+
98
+ class IllegalTransition < TransitionError
99
+ attr_reader :legal_transitions
100
+
101
+ def initialize transition, message=nil, valid_transitions=nil, options={}
102
+ @valid_transitions = valid_transitions
103
+ super transition, message, options
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,16 @@
1
+ module HasOptions
2
+
3
+ def self.included(base)
4
+ base.class_eval do
5
+ attr_accessor :options
6
+ end
7
+ end
8
+
9
+ def []v
10
+ options[v]
11
+ end
12
+
13
+ def []=v,k
14
+ options[v]=k
15
+ end
16
+ end
@@ -0,0 +1,165 @@
1
+
2
+ require 'logger'
3
+ module StateFu
4
+ #
5
+ # TODO - spec coverage
6
+ #
7
+ # Provide logging facilities, including the ability to use a shared logger.
8
+ # Use Rails' log if running as a rails plugin; allow independent control of
9
+ # StateFu log level.
10
+
11
+ class Logger
12
+ cattr_accessor :prefix # prefix for log messages
13
+ cattr_accessor :suppress # set true to send messages to /dev/null
14
+ cattr_accessor :shared
15
+ cattr_accessor :suppress
16
+
17
+ DEBUG = 0
18
+ INFO = 1
19
+ WARN = 2
20
+ ERROR = 3
21
+ FATAL = 4
22
+ UNKNOWN = 5
23
+
24
+ ENV_LOG_LEVEL = 'STATEFU_LOGLEVEL'
25
+ DEFAULT_LEVEL = INFO
26
+
27
+ DEFAULT_SHARED_LOG_PREFIX = '[StateFu] '
28
+
29
+ @@prefix = DEFAULT_SHARED_LOG_PREFIX
30
+ @@logger = nil
31
+ @@suppress = false
32
+ @@shared = false
33
+ @@log_level = nil
34
+
35
+ def self.new( logger = $stdout, options={} )
36
+ self.suppress = false
37
+ self.logger = logger, options
38
+ self
39
+ end
40
+
41
+ def self.initial_log_level
42
+ if env_level = ENV[ENV_LOG_LEVEL]
43
+ parse_log_level( env_level )
44
+ else
45
+ DEFAULT_LEVEL
46
+ end
47
+ end
48
+
49
+ def self.level
50
+ @@log_level ||= initial_log_level
51
+ end
52
+
53
+ def self.level=( new_level )
54
+ @@log_level = parse_log_level(new_level)
55
+ end
56
+
57
+ def self.shared?
58
+ !! @@shared
59
+ end
60
+
61
+ def self.prefix
62
+ shared? ? @@prefix : nil
63
+ end
64
+
65
+ def self.logger= logger
66
+ set_logger logger
67
+ end
68
+
69
+ def self.instance
70
+ @@logger ||= set_logger(Logger.default_logger)
71
+ end
72
+
73
+ def self.suppress!
74
+ self.suppress = true
75
+ end
76
+
77
+ def self.suppressed?(severity = DEBUG)
78
+ suppress == true || severity < level
79
+ end
80
+
81
+ def self.add(severity, message = nil, progname = nil, &block)
82
+ severity = parse_log_level( severity )
83
+ return if suppressed?( severity )
84
+ message = [prefix, (message || (block && block.call) || progname).to_s].compact.join
85
+ # message = "#{message}\n" unless message[-1] == ?\n
86
+ instance.add( severity, message )
87
+ end
88
+
89
+ def self.debug progname = nil, &block; add DEBUG, progname, &block; end
90
+ def self.info progname = nil, &block; add INFO, progname, &block; end
91
+ def self.warn progname = nil, &block; add WARN, progname, &block; end
92
+ def self.error progname = nil, &block; add ERROR, progname, &block; end
93
+ def self.fatal progname = nil, &block; add FATAL, progname, &block; end
94
+ def self.unknown progname = nil, &block; add UNKNOWN, progname, &block; end
95
+
96
+ #
97
+ # TODO fix these crappy methods
98
+ #
99
+
100
+ # setter for logger instance
101
+ def self.set_logger( logger, options = { :shared => false } )
102
+ case logger
103
+ when String
104
+ file = File.open(logger, File::WRONLY | File::APPEND)
105
+ @@logger = Logger.activesupport_logger_available? ? ActiveSupport::BufferedLogger.new(file) : ::Logger.new(file)
106
+ when ::Logger
107
+ @@logger = logger
108
+ when Logger.activesupport_logger_available? && ActiveSupport::BufferedLogger
109
+ @@logger = logger
110
+ else
111
+ raise ArgumentError.new
112
+ end
113
+ self.shared = !!options.symbolize_keys![:shared]
114
+ if shared?
115
+ @@prefix = options[:prefix] || DEFAULT_SHARED_LOG_PREFIX
116
+ puts "shared :: #{@@prefix} #{prefix}"
117
+ end
118
+ if lvl = options[:level] || options[:log_level]
119
+ self.level = lvl
120
+ end
121
+ instance
122
+ end
123
+
124
+ private
125
+
126
+ def self.get_logger( logr = $stdout )
127
+ if Object.const_defined?( "RAILS_DEFAULT_LOGGER" )
128
+ set_logger RAILS_DEFAULT_LOGGER, :shared => true
129
+ else
130
+ if Object.const_defined?( 'ActiveSupport' ) && ActiveSupport.const_defined?('BufferedLogger')
131
+ set_logger( ActiveSupport::BufferedLogger.new( logr ))
132
+ else
133
+ set_logger ::Logger.new( logr )
134
+ end
135
+ end
136
+ end
137
+
138
+ def self.activesupport_logger_available?
139
+ Object.const_defined?( 'ActiveSupport' ) && ActiveSupport.const_defined?('BufferedLogger')
140
+ end
141
+
142
+ def self.default_logger
143
+ if Object.const_defined?( "RAILS_DEFAULT_LOGGER" )
144
+ RAILS_DEFAULT_LOGGER
145
+ else
146
+ $stdout
147
+ end
148
+ end
149
+
150
+ def self.parse_log_level(input)
151
+ case input
152
+ when String, Symbol
153
+ const_get( input )
154
+ when 0,1,2,3,4,5
155
+ input
156
+ when nil
157
+ level
158
+ else
159
+ raise ArgumentError
160
+ end
161
+ end
162
+
163
+ end
164
+ end
165
+
@@ -0,0 +1,17 @@
1
+ module StateFu
2
+ module Methodical
3
+
4
+ def self.__define_method( method_name, &block )
5
+ self.class.class_eval do
6
+ define_method method_name, &block
7
+ end
8
+ end
9
+
10
+ def __define_singleton_method( method_name, &block )
11
+ (class << object; self; end).class_eval do
12
+ define_method method_name, &block
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,55 @@
1
+ require 'stringio'
2
+
3
+ # a module for suppressing or capturing STDOUT or STDERR.
4
+ # useful when shelling out to "noisy" applications or to suppress
5
+ # output during tests.
6
+ module NoStdout #:nodoc:all
7
+ module InstanceMethods
8
+
9
+ # Suppresses or redirects STDOUT inside the given block.
10
+ # supply an IO of your own to capture STDOUT, otherwise it's put
11
+ # in a new StringIO object.
12
+ def no_stdout ( to = StringIO.new('','r+'), &block )
13
+ orig_stdout = $stdout
14
+ $stdout = @alt_stdout = to
15
+ result = yield
16
+ $stdout = orig_stdout
17
+ result
18
+ end
19
+
20
+ # returns the contents of STDOUT from the previous usage of
21
+ # no_stdout, or nil
22
+ def last_stdout
23
+ return nil unless @alt_stdout
24
+ @alt_stdout.rewind
25
+ @alt_stdout.read
26
+ end
27
+
28
+ ## COPIED FROM ABOVE ####
29
+
30
+ # Suppresses or redirects STDERR inside the given block.
31
+ # supply an IO of your own to capture STDERR, otherwise it's put
32
+ # in a new StringIO object.
33
+ def no_stderr ( to = StringIO.new('','r+'), &block )
34
+ orig_stderr = $stderr
35
+ $stderr = @alt_stderr = to
36
+ result = yield
37
+ $stderr = orig_stderr
38
+ result
39
+ end
40
+
41
+ # returns the contents of STDERR from the previous usage of
42
+ # no_stderr, or nil
43
+ def last_stderr
44
+ return nil unless @alt_stderr
45
+ @alt_stderr.rewind
46
+ @alt_stderr.read
47
+ end
48
+ end
49
+
50
+ def self.included klass
51
+ klass.class_eval do
52
+ include InstanceMethods
53
+ end
54
+ end
55
+ end