transitions 0.0.18 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ # 1.0.0
2
+
3
+ (troessner) Remove suppport for multipe state machines
4
+
5
+ Here is the reasoning:
6
+
7
+ 1.) If you really need multiple state machines for one model, you probably need multiple models.
8
+ So far, I have not seen a valid use case for multiple state machines, which was not better expressed
9
+ by using multiple models.
10
+
11
+ 2.) The current transitions semantics and API is not suited for multiple state machines:
12
+ Right now events are just plain methods defined on the models itself.
13
+ Consider you had multiple state machines, on named `day` and one named `night`, both with differing events.
14
+ What to do when you switch to state machine `day` but trigger an event defined on `night`?
15
+ You can either allow it, but this would make multiple state machines pointless if there is no enforced
16
+ separation of concerns. Or you disallow it, in which case `events` would either have to be bound
17
+ to state machines, not to the object itself, which would be tedious and ugly, or the `events` themselves
18
+ would need to check if they are allowed to be called. The last solution seems like a clean solution
19
+ but it would require a decent amount of rewriting existing code - and I just do not see the benefit for this.
20
+
21
+ 3.) Kind of a weak point, but right now the functionality is broken anyway throughout the gem. This functionality
22
+ is not even documented, so the side effects on existing projects should be minimal.
23
+
24
+ On the plus side, removing the possibility of having multiple state machines will streamline and improve existing
25
+ code a lot.
26
+
1
27
  # 0.0.18 (2012-05-18)
2
28
 
3
29
  * (troessner) Remove `define_state_query_method` from public API
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = Travis Build Status
2
2
 
3
- {<img src="https://secure.travis-ci.org/troessner/transitions.png"/>}[http://travis-ci.org/troessner/transitions]
3
+ {<img src="https://secure.travis-ci.org/troessner/transitions.png?branch=master"/>}[http://travis-ci.org/troessner/transitions]
4
4
 
5
5
  = Synopsis
6
6
 
@@ -24,10 +24,8 @@ This goes into your Gemfile:
24
24
 
25
25
  = Using transitions
26
26
 
27
- require 'transitions'
28
-
29
27
  class Product
30
- include Transitions
28
+ include ActiveModel::Transitions
31
29
 
32
30
  state_machine do
33
31
  state :available # first one is initial state
@@ -46,6 +44,13 @@ This goes into your Gemfile:
46
44
  end
47
45
  end
48
46
 
47
+ In this example we assume that you are in a rails project using Bundler, which would automitcally require `transitions`.
48
+ If this is not the case for you you have to add
49
+
50
+ require 'transitions'
51
+
52
+ whereever you load your dependencies in your application.
53
+
49
54
  = Features
50
55
 
51
56
  == Events
@@ -119,6 +124,19 @@ the name of the timestamp column.
119
124
  transitions :from => :exploded, :to => :rebuilt
120
125
  end
121
126
 
127
+ == `event_fired` and `event_failed`
128
+
129
+ In case you define `event_fired` and / or `event_failed`, `transitions` will use those callbacks correspondingly.
130
+ You can use those callbacks like this:
131
+
132
+ def event_fired(current_state, new_state, event)
133
+ MyLogger.info "Event fired #{event.inspect}"
134
+ end
135
+
136
+ def event_failed(event)
137
+ MyLogger.warn "Event failed #{event.inspect}"
138
+ end
139
+
122
140
  == Listing all the available states
123
141
 
124
142
  You can easily get a listing of all available states:
@@ -133,9 +151,6 @@ In case you have multiple state machines you can also pass the state machine nam
133
151
  = Documentation, Guides & Examples
134
152
 
135
153
  - {Online API Documentation}[http://rdoc.info/github/troessner/transitions/master/Transitions]
136
- - Krzysiek Heród (aka {Netizer}[http://github.com/netizer]) wrote a nice
137
- {blog post}[http://dev.netizer.pl/transitions-state-machine-for-rails-3.html]
138
- about using Transitions in ActiveRecord.
139
154
 
140
155
  = Copyright
141
156
 
@@ -36,39 +36,38 @@ module ActiveModel
36
36
  # exclusive row lock.
37
37
  def reload(options = nil)
38
38
  super.tap do
39
- self.class.state_machines.values.each do |sm|
40
- remove_instance_variable(sm.current_state_variable) if instance_variable_defined?(sm.current_state_variable)
41
- end
39
+ sm = self.class.get_state_machine
40
+ remove_instance_variable(sm.current_state_variable) if instance_variable_defined?(sm.current_state_variable)
42
41
  end
43
42
  end
44
43
 
45
44
  protected
46
45
 
47
- def write_state(state_machine, state)
48
- prev_state = current_state(state_machine.name)
49
- write_state_without_persistence(state_machine, state)
46
+ def write_state(state)
47
+ prev_state = current_state
48
+ write_state_without_persistence(state)
50
49
  save!
51
50
  rescue ActiveRecord::RecordInvalid
52
- write_state_without_persistence(state_machine, prev_state)
51
+ write_state_without_persistence(prev_state)
53
52
  raise
54
53
  end
55
54
 
56
- def write_state_without_persistence(state_machine, state)
57
- ivar = state_machine.current_state_variable
55
+ def write_state_without_persistence(state)
56
+ ivar = self.class.get_state_machine.current_state_variable
58
57
  instance_variable_set(ivar, state)
59
58
  self.state = state.to_s
60
59
  end
61
60
 
62
- def read_state(state_machine)
61
+ def read_state
63
62
  self.state && self.state.to_sym
64
63
  end
65
64
 
66
65
  def set_initial_state
67
- self.state ||= self.class.state_machine.initial_state.to_s if self.has_attribute?(:state)
66
+ self.state ||= self.class.get_state_machine.initial_state.to_s if self.has_attribute?(:state)
68
67
  end
69
68
 
70
69
  def state_inclusion
71
- unless self.class.state_machine.states.map{|s| s.name.to_s }.include?(self.state.to_s)
70
+ unless self.class.get_state_machine.states.map{|s| s.name.to_s }.include?(self.state.to_s)
72
71
  self.errors.add(:state, :inclusion, :value => self.state)
73
72
  end
74
73
  end
data/lib/transitions.rb CHANGED
@@ -33,30 +33,23 @@ module Transitions
33
33
  module ClassMethods
34
34
  def inherited(klass)
35
35
  super # Make sure we call other callbacks possibly defined upstream the ancestor chain.
36
- klass.state_machines = state_machines
37
- end
38
-
39
- def state_machines
40
- @state_machines ||= {}
36
+ klass.state_machine = state_machine
41
37
  end
42
38
 
43
39
  # The only reason we need this method is for the inherited callback.
44
- def state_machines=(value)
45
- @state_machines = value ? value.dup : nil
40
+ def state_machine=(value)
41
+ @state_machine = value.dup
46
42
  end
47
43
 
48
- def state_machine(name = nil, options = {}, &block)
49
- if name.is_a?(Hash)
50
- options = name
51
- name = nil
52
- end
53
- name ||= :default
54
- state_machines[name] ||= Machine.new(self, name)
55
- block ? state_machines[name].update(options, &block) : state_machines[name]
44
+ def state_machine(options = {}, &block)
45
+ @state_machine ||= Machine.new self
46
+ block ? @state_machine.update(options, &block) : @state_machine
56
47
  end
57
48
 
58
- def available_states(name = :default)
59
- state_machines[name].states.map(&:name).sort_by {|x| x.to_s}
49
+ def get_state_machine; @state_machine; end
50
+
51
+ def available_states
52
+ @state_machine.states.map(&:name).sort_by {|x| x.to_s}
60
53
  end
61
54
  end
62
55
 
@@ -65,16 +58,16 @@ module Transitions
65
58
  end
66
59
 
67
60
  # TODO Do we need this method really? Also, it's not a beauty, refactor at least.
68
- def current_state(name = nil, new_state = nil, persist = false)
69
- sm = self.class.state_machine(name)
61
+ def current_state(new_state = nil, persist = false)
62
+ sm = self.class.get_state_machine
70
63
  ivar = sm.current_state_variable
71
- if name && new_state
64
+ if new_state
72
65
  if persist && respond_to?(:write_state)
73
- write_state(sm, new_state)
66
+ write_state(new_state)
74
67
  end
75
68
 
76
69
  if respond_to?(:write_state_without_persistence)
77
- write_state_without_persistence(sm, new_state)
70
+ write_state_without_persistence(new_state)
78
71
  end
79
72
 
80
73
  instance_variable_set(ivar, new_state)
@@ -84,7 +77,7 @@ module Transitions
84
77
  return value if value
85
78
 
86
79
  if respond_to?(:read_state)
87
- value = instance_variable_set(ivar, read_state(sm))
80
+ value = instance_variable_set(ivar, read_state)
88
81
  end
89
82
 
90
83
  value || sm.initial_state
@@ -39,7 +39,7 @@ module Transitions
39
39
  end
40
40
 
41
41
  def fire(obj, to_state = nil, *args)
42
- transitions = @transitions.select { |t| t.from == obj.current_state(@machine ? @machine.name : nil) }
42
+ transitions = @transitions.select { |t| t.from == obj.current_state }
43
43
  raise InvalidTransition, error_message_for_invalid_transitions(obj, to_state) if transitions.size == 0
44
44
 
45
45
  next_state = nil
@@ -24,10 +24,10 @@ module Transitions
24
24
  class Machine
25
25
  attr_writer :initial_state
26
26
  attr_accessor :states, :events, :state_index
27
- attr_reader :klass, :name, :auto_scopes
27
+ attr_reader :klass, :auto_scopes
28
28
 
29
- def initialize(klass, name, options = {}, &block)
30
- @klass, @name, @states, @state_index, @events = klass, name, [], {}, {}
29
+ def initialize(klass, options = {}, &block)
30
+ @klass, @states, @state_index, @events = klass, [], {}, {}
31
31
  update(options, &block)
32
32
  end
33
33
 
@@ -44,25 +44,25 @@ module Transitions
44
44
  end
45
45
 
46
46
  def fire_event(event, record, persist, *args)
47
- state_index[record.current_state(@name)].call_action(:exit, record)
47
+ state_index[record.current_state].call_action(:exit, record)
48
48
  begin
49
49
  if new_state = @events[event].fire(record, nil, *args)
50
50
  state_index[new_state].call_action(:enter, record)
51
51
 
52
- if record.respond_to?(event_fired_callback)
53
- record.send(event_fired_callback, record.current_state, new_state, event)
52
+ if record.respond_to?(:event_fired)
53
+ record.send(:event_fired, record.current_state, new_state, event)
54
54
  end
55
55
 
56
- record.current_state(@name, new_state, persist)
56
+ record.current_state(new_state, persist)
57
57
  @events[event].success.call(record) if @events[event].success
58
58
  return true
59
59
  else
60
- record.send(event_fired_callback, event) if record.respond_to?(event_failed_callback)
60
+ record.send(:event_failed, event) if record.respond_to?(:event_failed)
61
61
  return false
62
62
  end
63
63
  rescue => e
64
- if record.respond_to?(event_failed_callback)
65
- record.send(event_failed_callback, event)
64
+ if record.respond_to?(:event_failed)
65
+ record.send(:event_failed, event)
66
66
  return false
67
67
  else
68
68
  raise e
@@ -80,27 +80,24 @@ module Transitions
80
80
  end
81
81
 
82
82
  def current_state_variable
83
- "@#{@name}_current_state"
83
+ "@current_state"
84
84
  end
85
85
 
86
86
  private
87
87
 
88
88
  def state(name, options = {})
89
- @states << (state_index[name] ||= State.new(name, :machine => self)).update(options)
89
+ unless @state_index.key? name # Just ignore duplicates
90
+ state = State.new(name, :machine => self)
91
+ state.update options
92
+ @state_index[name] = state
93
+ @states << state
94
+ end
90
95
  end
91
96
 
92
97
  def event(name, options = {}, &block)
93
98
  (@events[name] ||= Event.new(self, name)).update(options, &block)
94
99
  end
95
100
 
96
- def event_fired_callback
97
- @event_fired_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_fired'
98
- end
99
-
100
- def event_failed_callback
101
- @event_failed_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_failed'
102
- end
103
-
104
101
  def include_scopes
105
102
  @states.each do |state|
106
103
  state_name = state.name.to_s
@@ -1,3 +1,3 @@
1
1
  module Transitions
2
- VERSION = "0.0.18"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -137,7 +137,7 @@ class TestActiveRecord < Test::Unit::TestCase
137
137
  end
138
138
 
139
139
  test "transition with wrong state will not validate" do
140
- for s in @light.class.state_machine.states
140
+ for s in @light.class.get_state_machine.states
141
141
  @light.state = s.name
142
142
  assert @light.valid?
143
143
  end
@@ -3,25 +3,15 @@ require "helper"
3
3
  class Bender
4
4
  include Transitions
5
5
 
6
- state_machine :default do
6
+ state_machine do
7
7
  state :drinking
8
8
  state :smoking
9
9
  state :gambling
10
10
  end
11
-
12
- state_machine :maintenance do
13
- state :wip
14
- state :finished
15
- end
16
11
  end
17
12
 
18
13
  class TestAvailableStatesListing < Test::Unit::TestCase
19
14
  test 'available_states should return the states for the default state machine if no state machine is specified' do
20
15
  assert_equal [:drinking, :gambling, :smoking], Bender.available_states
21
16
  end
22
-
23
- test 'available_states should return the states for a given state' do
24
- assert_equal [:finished, :wip], Bender.available_states(:maintenance)
25
- assert_equal [:drinking, :gambling, :smoking], Bender.available_states(:default)
26
- end
27
17
  end
data/test/test_machine.rb CHANGED
@@ -3,12 +3,10 @@ require "helper"
3
3
  class MachineTestSubject
4
4
  include Transitions
5
5
 
6
- state_machine do
6
+ state_machine :initial => :closed do
7
7
  state :open
8
8
  state :closed
9
- end
10
9
 
11
- state_machine :initial => :foo do
12
10
  event :shutdown do
13
11
  transitions :from => :open, :to => :closed
14
12
  end
@@ -17,26 +15,19 @@ class MachineTestSubject
17
15
  transitions :from => :open, :to => :closed
18
16
  end
19
17
  end
20
-
21
- state_machine :extra, :initial => :bar do
22
- end
23
18
  end
24
19
 
25
20
  class TransitionsMachineTest < Test::Unit::TestCase
26
- test "allows reuse of existing machines" do
27
- assert_equal 2, MachineTestSubject.state_machines.size
28
- end
29
-
30
21
  test "sets #initial_state from :initial option" do
31
- assert_equal :bar, MachineTestSubject.state_machine(:extra).initial_state
22
+ assert_equal :closed, MachineTestSubject.get_state_machine.initial_state
32
23
  end
33
24
 
34
- test "accesses non-default state machine" do
35
- assert_kind_of Transitions::Machine, MachineTestSubject.state_machine(:extra)
25
+ test "`get_state_machine` returns Transitions::Machine" do
26
+ assert_kind_of Transitions::Machine, MachineTestSubject.get_state_machine
36
27
  end
37
28
 
38
29
  test "finds events for given state" do
39
- events = MachineTestSubject.state_machine.events_for(:open)
30
+ events = MachineTestSubject.get_state_machine.events_for(:open)
40
31
  assert events.include?(:shutdown)
41
32
  assert events.include?(:timeout)
42
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: transitions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.18
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-05-18 00:00:00.000000000Z
13
+ date: 2012-06-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
17
- requirement: &76418870 !ruby/object:Gem::Requirement
17
+ requirement: !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,10 +22,15 @@ dependencies:
22
22
  version: '1'
23
23
  type: :development
24
24
  prerelease: false
25
- version_requirements: *76418870
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: '1'
26
31
  - !ruby/object:Gem::Dependency
27
32
  name: test-unit
28
- requirement: &76418470 !ruby/object:Gem::Requirement
33
+ requirement: !ruby/object:Gem::Requirement
29
34
  none: false
30
35
  requirements:
31
36
  - - ~>
@@ -33,10 +38,15 @@ dependencies:
33
38
  version: '2.2'
34
39
  type: :development
35
40
  prerelease: false
36
- version_requirements: *76418470
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: '2.2'
37
47
  - !ruby/object:Gem::Dependency
38
48
  name: mocha
39
- requirement: &76418120 !ruby/object:Gem::Requirement
49
+ requirement: !ruby/object:Gem::Requirement
40
50
  none: false
41
51
  requirements:
42
52
  - - ! '>='
@@ -44,10 +54,15 @@ dependencies:
44
54
  version: '0'
45
55
  type: :development
46
56
  prerelease: false
47
- version_requirements: *76418120
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
48
63
  - !ruby/object:Gem::Dependency
49
64
  name: rake
50
- requirement: &76417650 !ruby/object:Gem::Requirement
65
+ requirement: !ruby/object:Gem::Requirement
51
66
  none: false
52
67
  requirements:
53
68
  - - ! '>='
@@ -55,10 +70,15 @@ dependencies:
55
70
  version: '0'
56
71
  type: :development
57
72
  prerelease: false
58
- version_requirements: *76417650
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
59
79
  - !ruby/object:Gem::Dependency
60
80
  name: random_data
61
- requirement: &76417170 !ruby/object:Gem::Requirement
81
+ requirement: !ruby/object:Gem::Requirement
62
82
  none: false
63
83
  requirements:
64
84
  - - ! '>='
@@ -66,10 +86,15 @@ dependencies:
66
86
  version: '0'
67
87
  type: :development
68
88
  prerelease: false
69
- version_requirements: *76417170
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
70
95
  - !ruby/object:Gem::Dependency
71
96
  name: sqlite3
72
- requirement: &76416820 !ruby/object:Gem::Requirement
97
+ requirement: !ruby/object:Gem::Requirement
73
98
  none: false
74
99
  requirements:
75
100
  - - ! '>='
@@ -77,10 +102,15 @@ dependencies:
77
102
  version: '0'
78
103
  type: :development
79
104
  prerelease: false
80
- version_requirements: *76416820
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
81
111
  - !ruby/object:Gem::Dependency
82
112
  name: activerecord
83
- requirement: &76416030 !ruby/object:Gem::Requirement
113
+ requirement: !ruby/object:Gem::Requirement
84
114
  none: false
85
115
  requirements:
86
116
  - - ~>
@@ -88,7 +118,12 @@ dependencies:
88
118
  version: '3'
89
119
  type: :development
90
120
  prerelease: false
91
- version_requirements: *76416030
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ~>
125
+ - !ruby/object:Gem::Version
126
+ version: '3'
92
127
  description: Lightweight state machine extracted from ActiveModel
93
128
  email: timo.roessner@googlemail.com
94
129
  executables: []
@@ -140,7 +175,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
140
175
  version: '0'
141
176
  segments:
142
177
  - 0
143
- hash: -1015670387
178
+ hash: -659970477
144
179
  required_rubygems_version: !ruby/object:Gem::Requirement
145
180
  none: false
146
181
  requirements:
@@ -149,7 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
184
  version: 1.3.6
150
185
  requirements: []
151
186
  rubyforge_project: transitions
152
- rubygems_version: 1.8.10
187
+ rubygems_version: 1.8.24
153
188
  signing_key:
154
189
  specification_version: 3
155
190
  summary: State machine extracted from ActiveModel