transitions 0.0.18 → 0.1.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/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