simple_states 0.0.1 → 0.0.2

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/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: []