simple_states 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,5 +1,68 @@
1
1
  # simple\_states
2
2
 
3
- A super-slim (~200 loc) statemachine-like support library focussed on use in Travis CI.
3
+ A super-slim (~200 loc) statemachine-like support library focussed on use in
4
+ Travis CI.
4
5
 
5
- More docs coming soon, checkout the tests.
6
+ ## Usage
7
+
8
+ Define states and events like this:
9
+
10
+ class Foo
11
+ include SimpleStates
12
+
13
+ states :created, :started, :finished
14
+
15
+ event :start, :from => :created, :to => :started, :if => :startable?
16
+ event :finish, :to => :finished, :after => :cleanup
17
+
18
+ attr_accessor :state
19
+
20
+ def start
21
+ # start foo
22
+ end
23
+ end
24
+
25
+ Including the SimpleStates module to your class is currently required. We'll add
26
+ hooks for ActiveRecord etc later.
27
+
28
+ SimpleStates expects your model to support attribute accessors for `:state`.
29
+
30
+ Event options have the following well-known meanings:
31
+
32
+ :from # valid states to transition from
33
+ :to # target state to transition to
34
+ :if # only proceed if the given method returns true
35
+ :unless # only proceed if the given method returns false
36
+ :before # run the given method before running `super` and setting the new state
37
+ :after # run the given method at the very end
38
+
39
+ All of these options except for `:to` can be given as a single symbol or string or
40
+ as an Array of symbols or strings.
41
+
42
+ Calling `event` will effectively add methods to a proxy module which is
43
+ included to the singleton class of your class' instances. E.g. declaring `event
44
+ :start` in the example above will add a method `start` to a module included to
45
+ the singleton class of instances of `Foo`.
46
+
47
+ This method will
48
+
49
+ 1. check if `:if`/`:except` conditions apply (if given) and just return from the method otherwise
50
+ 2. check if the object currently is in a valid `:from` state (if given) and raise an exception otherwise
51
+ 3. run `:before` callbacks (if given)
52
+ 4. call `super` if Foo defines the current method (i.e. call `start` but not `finish` in the example above)
53
+ 5. add the object's current state to its `past_states` history
54
+ 6. set the object's `state` to the target state given as `:to`
55
+ 7. set the object's `[state]_at` attribute to `Time.now` if the object defines a writer for it
56
+ 8. run `:after` callbacks (if given)
57
+
58
+ You can define options for all events like so:
59
+
60
+ event :finish, :to => :finished, :after => :cleanup
61
+ event :all, :after => :notify
62
+
63
+ This will call :cleanup first and then :notify on :finish.
64
+
65
+ If no target state was given for an event then SimpleStates will try to derive
66
+ it from the states list. I.e. for an event `start` it will check the states
67
+ list for a state `started` and use it. If it can not find a target state this
68
+ way then it will raise an exception.
@@ -43,16 +43,12 @@ module SimpleStates
43
43
  def skip?(object, args)
44
44
  result = false
45
45
  result ||= !send_methods(object, options.if, args) if options.if?
46
- result ||= send_methods(object, options.except, args) if options.except?
46
+ result ||= send_methods(object, options.unless, args) if options.unless?
47
47
  result
48
48
  end
49
49
 
50
50
  def can_transition?(object)
51
- object.state && Array(options.from).include?(object.state)
52
- end
53
-
54
- def raise_invalid_transition(object)
55
- raise TransitionException, "#{object.inspect} can not receive event #{name.inspect} while in state #{object.state.inspect}."
51
+ !options.from || object.state && Array(options.from).include?(object.state)
56
52
  end
57
53
 
58
54
  def run_callbacks(object, type, args)
@@ -60,7 +56,7 @@ module SimpleStates
60
56
  end
61
57
 
62
58
  def set_state(object)
63
- if state = options.to
59
+ if state = target_state(object)
64
60
  object.past_states << object.state if object.state
65
61
  object.state = state.to_sym
66
62
  object.send(:"#{state}_at=", Time.now) if object.respond_to?(:"#{state}_at=")
@@ -68,6 +64,12 @@ module SimpleStates
68
64
  end
69
65
  end
70
66
 
67
+ def target_state(object)
68
+ options.to || :"#{name}ed".tap do |state|
69
+ raise_unknown_target_state(object) unless object.class.states.include?(state)
70
+ end
71
+ end
72
+
71
73
  def send_methods(object, methods, args)
72
74
  Array(methods).inject(false) { |result, method| result | send_method(object, method, args) } if methods
73
75
  end
@@ -83,5 +85,13 @@ module SimpleStates
83
85
  def arity(object, method)
84
86
  object.class.instance_method(method).arity rescue 0
85
87
  end
88
+
89
+ def raise_invalid_transition(object)
90
+ raise TransitionException, "#{object.inspect} can not receive event #{name.inspect} while in state #{object.state.inspect}."
91
+ end
92
+
93
+ def raise_unknown_target_state(object)
94
+ raise TransitionException, "can not find target state for #{object.inspect} for event #{name.inspect}."
95
+ end
86
96
  end
87
97
  end
@@ -1,3 +1,3 @@
1
1
  module SimpleStates
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_states
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2011-08-04 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &70306211479800 !ruby/object:Gem::Requirement
16
+ requirement: &70366831755540 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70306211479800
24
+ version_requirements: *70366831755540
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: hashr
27
- requirement: &70306211479060 !ruby/object:Gem::Requirement
27
+ requirement: &70366831754780 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.0.10
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70306211479060
35
+ version_requirements: *70366831754780
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: test_declarative
38
- requirement: &70306211478360 !ruby/object:Gem::Requirement
38
+ requirement: &70366831754080 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70306211478360
46
+ version_requirements: *70366831754080
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: mocha
49
- requirement: &70306211477140 !ruby/object:Gem::Requirement
49
+ requirement: &70366831752860 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70306211477140
57
+ version_requirements: *70366831752860
58
58
  description: ! '[description]'
59
59
  email: svenfuchs@artweb-design.de
60
60
  executables: []