state-fu 0.12.3 → 0.13.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.
data/lib/persistence.rb CHANGED
@@ -20,7 +20,7 @@ module StateFu
20
20
  # end
21
21
  #
22
22
  # def write_attribute( string_value )
23
- # Logger.debug "magnetising ( #{field_name} => #{string_value} on #{object.inspect}"
23
+ # Logging.debug "magnetising ( #{field_name} => #{string_value} on #{object.inspect}"
24
24
  # object.send "magnetised_#{field_name}=", string_value
25
25
  # end
26
26
  # end
@@ -56,7 +56,7 @@ module StateFu
56
56
  persister_class = class_for klass, field_name
57
57
  prepare_field( klass, field_name, persister_class)
58
58
  returning persister_class.new( binding, field_name ) do |persister|
59
- Logger.debug( "#{persister_class}: method #{binding.method_name} as field #{persister.field_name}" )
59
+ Logging.debug( "#{persister_class}: method #{binding.method_name} as field #{persister.field_name}" )
60
60
  end
61
61
  end
62
62
 
@@ -4,7 +4,7 @@ module StateFu
4
4
 
5
5
  def self.prepare_field( klass, field_name )
6
6
  _field_name = field_name
7
- Logger.debug("Preparing ActiveRecord field #{klass}.#{field_name}")
7
+ Logging.debug("Preparing ActiveRecord field #{klass}.#{field_name}")
8
8
 
9
9
  # this adds a before_save hook to ensure that the field is initialized
10
10
  # (and the initial state set) before create.
@@ -20,12 +20,12 @@ module StateFu
20
20
  # Attribute version, so just do the simplest thing we can.
21
21
 
22
22
  def read_attribute
23
- Logger.debug "Read attribute #{field_name}, got #{object.send(:read_attribute,field_name)} for #{object.inspect}"
23
+ Logging.debug "Read attribute #{field_name}, got #{object.send(:read_attribute,field_name)} for #{object.inspect}"
24
24
  object.send( :read_attribute, field_name )
25
25
  end
26
26
 
27
27
  def write_attribute( string_value )
28
- Logger.debug "Write attribute #{field_name} to #{string_value} for #{object.inspect}"
28
+ Logging.debug "Write attribute #{field_name} to #{string_value} for #{object.inspect}"
29
29
  object.send( :write_attribute, field_name, string_value )
30
30
  end
31
31
 
@@ -5,7 +5,7 @@ module StateFu
5
5
  def self.prepare_field( klass, field_name )
6
6
  # ensure getter exists
7
7
  unless klass.instance_methods.map(&:to_sym).include?( field_name.to_sym )
8
- Logger.debug "Adding attr_reader :#{field_name} for #{klass}"
8
+ Logging.debug "Adding attr_reader :#{field_name} for #{klass}"
9
9
  _field_name = field_name
10
10
  klass.class_eval do
11
11
  private
@@ -15,7 +15,7 @@ module StateFu
15
15
 
16
16
  # ensure setter exists
17
17
  unless klass.instance_methods.map(&:to_sym).include?( :"#{field_name}=" )
18
- Logger.debug "Adding attr_writer :#{field_name}= for #{klass}"
18
+ Logging.debug "Adding attr_writer :#{field_name}= for #{klass}"
19
19
  _field_name = field_name
20
20
  klass.class_eval do
21
21
  private
@@ -32,13 +32,13 @@ module StateFu
32
32
 
33
33
  def read_attribute
34
34
  string = object.send( field_name )
35
- Logger.debug "Read attribute #{field_name}, got #{string.inspect} for #{object.inspect}"
35
+ Logging.debug "Read attribute #{field_name}, got #{string.inspect} for #{object.inspect}"
36
36
  string
37
37
  end
38
38
 
39
39
  def write_attribute( string_value )
40
40
  writer_method = "#{field_name}="
41
- Logger.debug "Writing attribute #{field_name} -> #{string_value.inspect} for #{object.inspect}"
41
+ Logging.debug "Writing attribute #{field_name} -> #{string_value.inspect} for #{object.inspect}"
42
42
  object.send( writer_method, string_value )
43
43
  end
44
44
 
@@ -8,25 +8,9 @@ module StateFu
8
8
 
9
9
  attr_reader :binding, :field_name, :current_state
10
10
 
11
- def self.prepare_class( klass )
12
- unless klass.instance_methods.include?( :method_missing_before_state_fu )
13
- alias_method :method_missing_before_state_fu, :method_missing
14
- klass.class_eval do
15
- def method_missing( method_name, *args, &block )
16
- state_fu!
17
- begin
18
- send( method_name, *args, &block )
19
- rescue NoMethodError => e
20
- method_missing_before_state_fu( method_name, *args, &block )
21
- end
22
- end
23
- end
24
- end
25
- end
26
-
27
11
  # define this method in subclasses to do any preparation
28
12
  def self.prepare_field( klass, field_name )
29
- Logger.warn("Abstract method in #{self}.prepare_field called. Override me!")
13
+ Logging.warn("Abstract method in #{self}.prepare_field called. Override me!")
30
14
  end
31
15
 
32
16
  def initialize( binding, field_name )
@@ -36,11 +20,11 @@ module StateFu
36
20
  @current_state = find_current_state()
37
21
 
38
22
  if current_state.nil?
39
- Logger.warn("undefined state for binding #{binding} on #{object} with field_name #{field_name.inspect}")
40
- Logger.warn("Machine for #{object} has no states: #{machine}") if machine.states.empty?
23
+ Logging.warn("undefined state for binding #{binding} on #{object} with field_name #{field_name.inspect}")
24
+ Logging.warn("Machine for #{object} has no states: #{machine}") if machine.states.empty?
41
25
  else
42
26
  persist!
43
- Logger.debug("#{object} resumes #{binding.method_name} at #{current_state.name}")
27
+ Logging.debug("#{object} resumes #{binding.method_name} at #{current_state.name}")
44
28
  end
45
29
  end
46
30
 
data/lib/state-fu.rb CHANGED
@@ -15,12 +15,14 @@
15
15
  # extend the core features.
16
16
  #
17
17
  # It is also delightfully elegant and easy to use for simple things.
18
+ %w( support support/active_support_lite ).each do |path|
19
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), path)))
20
+ end
18
21
 
19
22
  [ 'support/core_ext',
20
- 'support/logger',
23
+ 'support/logging',
21
24
  'support/applicable',
22
25
  'support/arrays',
23
- 'support/methodical',
24
26
  'support/has_options',
25
27
  'support/vizier',
26
28
  'support/plotter',
@@ -38,53 +38,31 @@ class Array
38
38
  end
39
39
 
40
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
41
+ unless defined? instance_exec # 1.9
42
+ module InstanceExecMethods #:nodoc:
72
43
  end
44
+ include InstanceExecMethods
73
45
 
74
- result = yield
46
+ # Evaluate the block with the given arguments within the context of
47
+ # this object, so self is set to the method receiver.
48
+ #
49
+ # From Mauricio's http://eigenclass.org/hiki/bounded+space+instance_exec
50
+ def instance_exec(*args, &block)
51
+ begin
52
+ old_critical, Thread.critical = Thread.critical, true
53
+ n = 0
54
+ n += 1 while respond_to?(method_name = "__instance_exec#{n}")
55
+ InstanceExecMethods.module_eval { define_method(method_name, &block) }
56
+ ensure
57
+ Thread.critical = old_critical
58
+ end
75
59
 
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
60
+ begin
61
+ send(method_name, *args)
62
+ ensure
63
+ InstanceExecMethods.module_eval { remove_method(method_name) } rescue nil
64
+ end
85
65
  end
86
-
87
- result
88
- end # with_methods_on
66
+ end
89
67
  end
90
68
 
@@ -7,7 +7,7 @@ module StateFu
7
7
  # Use Rails' log if running as a rails plugin; allow independent control of
8
8
  # StateFu log level.
9
9
 
10
- class Logger
10
+ class Logging
11
11
  cattr_accessor :prefix # prefix for log messages
12
12
  cattr_accessor :suppress # set true to send messages to /dev/null
13
13
  cattr_accessor :shared
@@ -101,10 +101,10 @@ module StateFu
101
101
  case logger
102
102
  when String
103
103
  file = File.open(logger, File::WRONLY | File::APPEND)
104
- @@logger = Logger.activesupport_logger_available? ? ActiveSupport::BufferedLogger.new(file) : ::Logger.new(file)
105
- when ::Logger
104
+ @@logger = activesupport_logger_available? ? ActiveSupport::BufferedLogger.new(file) : Logger.new(file)
105
+ when Logger
106
106
  @@logger = logger
107
- when Logger.activesupport_logger_available? && ActiveSupport::BufferedLogger
107
+ when activesupport_logger_available? && ActiveSupport::BufferedLogger
108
108
  @@logger = logger
109
109
  else
110
110
  default_logger
@@ -134,7 +134,7 @@ module StateFu
134
134
  ActiveSupport::BufferedLogger.new(target)
135
135
  end
136
136
  else
137
- ::Logger.new(target)
137
+ Logger.new(target)
138
138
  end
139
139
  end
140
140
 
data/lib/transition.rb CHANGED
@@ -165,7 +165,7 @@ module StateFu
165
165
  # halt a transition with a message
166
166
  # can be used to back out of a transition inside eg a state entry hook
167
167
  def halt! message
168
- raise TransitionHalted.new( self, message )
168
+ raise StateFu::TransitionHalted.new( self, message )
169
169
  end
170
170
 
171
171
  #
@@ -184,10 +184,10 @@ module StateFu
184
184
  StateFu::Hooks::ALL_HOOKS.map do |owner, slot|
185
185
  [ [owner, slot], send(owner).hooks[slot] ]
186
186
  end.each do |address, hooks|
187
- Logger.info("running #{address.inspect} hooks for #{object.class} #{object}")
187
+ Logging.info("running #{address.inspect} hooks for #{object.class} #{object}")
188
188
  owner,slot = *address
189
189
  hooks.each do |hook|
190
- Logger.info("running hook #{hooks} for #{object.class} #{object}")
190
+ Logging.info("running hook #{hooks} for #{object.class} #{object}")
191
191
  @current_hook_slot = address
192
192
  @current_hook = hook
193
193
  run_hook hook
@@ -195,14 +195,14 @@ module StateFu
195
195
  if slot == :entry
196
196
  @accepted = true
197
197
  @binding.persister.current_state = @target
198
- Logger.info("State is now :#{@target.name} for #{object.class} #{object}")
198
+ Logging.info("State is now :#{@target.name} for #{object.class} #{object}")
199
199
  end
200
200
  end
201
201
  # transition complete
202
202
  @current_hook_slot = nil
203
203
  @current_hook = nil
204
204
  rescue TransitionHalted => e
205
- Logger.info("Transition halted for #{object.class} #{object}: #{e.inspect}")
205
+ Logging.info("Transition halted for #{object.class} #{object}: #{e.inspect}")
206
206
  @errors << e
207
207
  end
208
208
  self
@@ -292,17 +292,15 @@ module StateFu
292
292
  s
293
293
  end
294
294
 
295
+ def evaluate(method_name_or_proc)
296
+ executioner.evaluate(method_name_or_proc)
297
+ end
298
+ alias_method :call, :evaluate
299
+
295
300
  private
296
301
 
297
302
  def executioner
298
- @executioner ||= Executioner.new( self ) do |ex|
299
- machine.inject_helpers_into( ex )
300
- machine.inject_methods_into( ex )
301
- end
302
- end
303
-
304
- def evaluate(method_name_or_proc)
305
- executioner.evaluate(method_name_or_proc)
303
+ @executioner ||= Executioner.new( self )
306
304
  end
307
305
 
308
306
  def evaluate_requirement_message( name, revalidate=false)
@@ -0,0 +1,22 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ describe "machine(:autosave => true)" do
4
+ before(:all) do
5
+ reset!
6
+ prepare_active_record() do
7
+ def self.up
8
+ create_table :example_records do |t|
9
+ t.string :name, :null => false
10
+ t.string :state_fu_field, :null => false
11
+ t.string :description
12
+ t.string :status
13
+ t.timestamps
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ it "should automatically save the record when a transition is complete" do
20
+ pending
21
+ end
22
+ end
@@ -1,57 +1,32 @@
1
1
  require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
2
 
3
3
  module RequirementFeatureHelper
4
- def account_expired?
5
- !! account_expired
6
- end
7
4
 
8
5
  def valid_password?
9
6
  !! valid_password
10
7
  end
11
8
  end
12
9
 
13
- # it_should_behave_like "!" do
14
- shared_examples_for "not requirements" do
15
- describe "requirements with names beginning with no[t]_" do
16
-
17
- it "should return the opposite of the requirement name without not_" do
18
- # @obj.current_state.should == :guest
19
- @obj.stfu.teleport! :anonymous
20
- @obj.valid_password = false
21
- @binding.can_has_valid_password?.should == false
22
- @binding.can_has_not_valid_password?.should == true
23
- @binding.can_has_no_valid_password?.should == true
24
- @obj.valid_password = true
25
- @binding.can_has_valid_password?.should == true
26
- @binding.can_has_not_valid_password?.should == false
27
- @binding.can_has_no_valid_password?.should == false
28
- end
29
-
30
- it "should call the method directly if one exists" do
31
- @obj.valid_password = true
32
- (class << @obj; self; end).class_eval do
33
- define_method( :no_valid_password? ) { true }
34
- end
35
- @binding.can_has_valid_password?.should == true
36
- @binding.can_has_not_valid_password?.should == false
37
- @binding.can_has_no_valid_password?.should == true
38
- end
39
-
40
- end
41
-
42
- end
43
-
44
10
  describe "requirements" do
11
+
45
12
  before(:all) do
46
13
  reset!
47
14
  make_pristine_class('Klass')
15
+
48
16
  Klass.class_eval do
49
17
  attr_accessor :valid_password
50
18
  attr_accessor :account_expired
19
+
20
+ def account_expired?
21
+ !! account_expired
22
+ end
51
23
  end
24
+
52
25
  @machine = StateFu::Machine.new do
53
26
  initial_state :guest
54
27
 
28
+ define(:valid_password?) { !! valid_password }
29
+
55
30
  event :has_valid_password, :from => :anonymous, :to => :logged_in do
56
31
  requires :valid_password?
57
32
  end
@@ -62,9 +37,12 @@ describe "requirements" do
62
37
 
63
38
  event :has_no_valid_password, :from => :anonymous, :to => :suspect do
64
39
  requires :no_valid_password?
65
- end
66
-
40
+ end
67
41
  end
42
+
43
+ @machine.bind!(Klass, :default)
44
+ @obj = Klass.new
45
+ @binding = @obj.state_fu
68
46
  end
69
47
 
70
48
  before :each do
@@ -72,47 +50,31 @@ describe "requirements" do
72
50
  @obj.account_expired = false
73
51
  end
74
52
 
75
- describe "requirements defined with a machine helper" do
76
- before :all do
77
- @machine.lathe { helper RequirementFeatureHelper }
78
- @machine.bind!(Klass, :default)
79
- @obj = Klass.new
80
- @binding = @obj.state_fu
81
- end
82
-
83
- it_should_behave_like "not requirements"
84
-
85
- it "should not have methods on the object" do
86
- @obj.respond_to?(:valid_password?).should == false
87
- @obj.respond_to?(:account_expired?).should == false
88
- end
89
-
90
- it "should have methods on the binding" do
91
- # this is a little misleading because theyre not evaluated on the binding ..
92
- @binding.respond_to?(:valid_password?).should == true
93
- @binding.respond_to?(:account_expired?).should == true
94
- @binding.respond_to?(:not_valid_password?).should == false
95
- @binding.respond_to?(:not_account_expired?).should == false
96
- end
53
+ it "should have methods ...." do
54
+ @obj.should respond_to(:account_expired?)
55
+ @machine.named_procs.keys.should include(:valid_password?)
56
+ end
57
+
58
+ it "should return the opposite of the requirement name without not_" do
59
+ @obj.stfu.teleport! :anonymous
60
+ @obj.valid_password = false
61
+ @binding.can_has_valid_password?.should == false
62
+ @binding.can_has_not_valid_password?.should == true
63
+ @binding.can_has_no_valid_password?.should == true
64
+ @obj.valid_password = true
65
+ @binding.can_has_valid_password?.should == true
66
+ @binding.can_has_not_valid_password?.should == false
67
+ @binding.can_has_no_valid_password?.should == false
97
68
  end
98
69
 
99
- describe "requirements defined on the object" do
100
- before :all do
101
- @machine.bind!(Klass, :default)
102
- @obj = Klass.new
103
- @binding = @obj.state_fu
104
- Klass.class_eval do
105
- include RequirementFeatureHelper
106
- end
70
+ it "should call the method directly if one exists" do
71
+ @obj.valid_password = true
72
+ (class << @obj; self; end).class_eval do
73
+ define_method( :no_valid_password? ) { true }
107
74
  end
75
+ @binding.can_has_valid_password?.should == true
76
+ @binding.can_has_not_valid_password?.should == false
77
+ @binding.can_has_no_valid_password?.should == true
78
+ end
108
79
 
109
- it_should_behave_like "not requirements"
110
-
111
- it "should have methods on the object" do
112
- @obj.respond_to?(:valid_password?).should == true
113
- @obj.respond_to?(:not_valid_password?).should == false
114
- @obj.respond_to?(:account_expired?).should == true
115
- @obj.respond_to?(:not_account_expired?).should == false
116
- end
117
- end
118
80
  end