simple_state_machine 0.5.2 → 0.6.1

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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build.yml +46 -0
  3. data/.gitignore +3 -0
  4. data/.travis.yml +17 -0
  5. data/Changelog.rdoc +16 -0
  6. data/Gemfile +7 -0
  7. data/README.rdoc +154 -99
  8. data/examples/conversation.rb +5 -5
  9. data/examples/lamp.rb +5 -5
  10. data/examples/relationship.rb +8 -8
  11. data/examples/traffic_light.rb +3 -3
  12. data/examples/user.rb +8 -4
  13. data/gemfiles/Gemfile.activerecord-5.2.x +9 -0
  14. data/gemfiles/Gemfile.activerecord-6.0.x +9 -0
  15. data/gemfiles/Gemfile.activerecord-6.1.x +8 -0
  16. data/gemfiles/Gemfile.activerecord-main.x +9 -0
  17. data/gemfiles/Gemfile.basic +6 -0
  18. data/lib/simple_state_machine/.DS_Store +0 -0
  19. data/lib/simple_state_machine/active_record.rb +2 -64
  20. data/lib/simple_state_machine/decorator/active_record.rb +68 -0
  21. data/lib/simple_state_machine/decorator/default.rb +91 -0
  22. data/lib/simple_state_machine/railtie.rb +1 -1
  23. data/lib/simple_state_machine/simple_state_machine.rb +8 -251
  24. data/lib/simple_state_machine/state_machine.rb +88 -0
  25. data/lib/simple_state_machine/state_machine_definition.rb +72 -0
  26. data/lib/simple_state_machine/tools/graphviz.rb +21 -0
  27. data/lib/simple_state_machine/tools/inspector.rb +44 -0
  28. data/lib/simple_state_machine/transition.rb +40 -0
  29. data/lib/simple_state_machine/version.rb +1 -1
  30. data/lib/simple_state_machine.rb +13 -3
  31. data/lib/tasks/graphviz.rake +31 -0
  32. data/simple_state_machine.gemspec +14 -24
  33. data/spec/.DS_Store +0 -0
  34. data/spec/active_record_spec.rb +216 -179
  35. data/spec/{decorator_spec.rb → decorator/default_spec.rb} +32 -32
  36. data/spec/examples_spec.rb +17 -17
  37. data/spec/mountable_spec.rb +26 -14
  38. data/spec/simple_state_machine_spec.rb +128 -92
  39. data/spec/spec_helper.rb +18 -5
  40. data/spec/state_machine_definition_spec.rb +48 -34
  41. data/spec/state_machine_spec.rb +36 -2
  42. data/spec/tools/graphviz_spec.rb +30 -0
  43. data/spec/tools/inspector_spec.rb +70 -0
  44. metadata +54 -128
  45. data/autotest/discover.rb +0 -1
  46. data/lib/tasks/graphiz.rake +0 -13
  47. data/rails/graphiz.rake +0 -16
@@ -0,0 +1,21 @@
1
+ module SimpleStateMachine
2
+ module Tools
3
+ require 'cgi'
4
+ module Graphviz
5
+ # Graphviz dot format for rendering as a directional graph
6
+ def to_graphviz_dot
7
+ "digraph G {\n" +
8
+ transitions.map { |t| t.to_graphviz_dot }.sort.join(";\n") +
9
+ "\n}"
10
+ end
11
+
12
+ # Generates a url that renders states and events as a directional graph.
13
+ # See http://code.google.com/apis/chart/docs/gallery/graphviz.html
14
+ def google_chart_url
15
+ graph = transitions.map { |t| t.to_graphviz_dot }.sort.join(";")
16
+ puts graph
17
+ "http://chart.googleapis.com/chart?cht=gv&chl=digraph{#{::CGI.escape graph}}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,44 @@
1
+ module SimpleStateMachine
2
+ module Tools
3
+ module Inspector
4
+ def begin_states
5
+ from_states - to_states
6
+ end
7
+
8
+ def end_states
9
+ to_states - from_states
10
+ end
11
+
12
+ def states
13
+ (to_states + from_states).uniq
14
+ end
15
+
16
+ private
17
+
18
+ def from_states
19
+ to_uniq_sym(sample_transitions.map(&:from))
20
+ end
21
+
22
+ def to_states
23
+ to_uniq_sym(sample_transitions.map(&:to))
24
+ end
25
+
26
+ def to_uniq_sym(array)
27
+ array.map { |state| state.is_a?(String) ? state.to_sym : state }.uniq
28
+ end
29
+
30
+ def sample_transitions
31
+ (@subject || sample_subject).state_machine_definition.send :transitions
32
+ end
33
+
34
+ def sample_subject
35
+ self_class = self.class
36
+ sample = Class.new do
37
+ extend SimpleStateMachine::Mountable
38
+ mount_state_machine self_class
39
+ end
40
+ sample
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,40 @@
1
+ module SimpleStateMachine
2
+ # Defines transitions for events
3
+ class Transition
4
+ attr_reader :event_name, :from, :to
5
+ def initialize(event_name, from, to)
6
+ @event_name = event_name.to_s
7
+ @from = from.is_a?(Class) ? from : from.to_s
8
+ @to = to.to_s
9
+ end
10
+
11
+ # returns true if it's a transition for event_name and subject_state
12
+ def is_transition_for?(event_name, subject_state)
13
+ is_same_event?(event_name) && is_same_from?(subject_state)
14
+ end
15
+
16
+ # returns true if it's a error transition for event_name and error
17
+ def is_error_transition_for?(event_name, error)
18
+ is_same_event?(event_name) && from.is_a?(Class) && error.is_a?(from)
19
+ end
20
+
21
+ def to_s
22
+ "#{from}.#{event_name}! => #{to}"
23
+ end
24
+
25
+ # TODO move to Graphiz module
26
+ def to_graphviz_dot
27
+ %("#{from}"->"#{to}"[label=#{event_name}])
28
+ end
29
+
30
+ private
31
+
32
+ def is_same_event?(event_name)
33
+ self.event_name == event_name.to_s
34
+ end
35
+
36
+ def is_same_from?(subject_from)
37
+ from.to_s == 'all' || subject_from.to_s == from.to_s
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module SimpleStateMachine
2
- VERSION = "0.5.2"
2
+ VERSION = "0.6.1"
3
3
  end
@@ -1,4 +1,14 @@
1
1
  require 'simple_state_machine/simple_state_machine'
2
- require 'simple_state_machine/active_record'
3
- require "simple_state_machine/railtie" if defined?(Rails::Railtie)
4
-
2
+ require 'simple_state_machine/state_machine'
3
+ require 'simple_state_machine/tools/graphviz'
4
+ require 'simple_state_machine/tools/inspector'
5
+ require 'simple_state_machine/state_machine_definition'
6
+ require 'simple_state_machine/transition'
7
+ require 'simple_state_machine/decorator/default'
8
+ if defined?(ActiveRecord)
9
+ require 'simple_state_machine/active_record'
10
+ require 'simple_state_machine/decorator/active_record'
11
+ end
12
+ if defined?(Rails::Railtie)
13
+ require "simple_state_machine/railtie"
14
+ end
@@ -0,0 +1,31 @@
1
+ namespace :ssm do
2
+ namespace :graph do
3
+
4
+ desc 'Outputs a dot file. You must specify class=ClassNAME'
5
+ task :dot => :environment do
6
+ if clazz = ENV['class']
7
+ puts clazz.constantize.state_machine_definition.to_graphviz_dot
8
+ else
9
+ puts "Missing argument: class. Please specify class=ClassName"
10
+ end
11
+ end
12
+
13
+ desc 'Generate a url for a google chart. You must specify class=ClassName'
14
+ task :url => :environment do
15
+ if clazz = ENV['class']
16
+ puts clazz.constantize.state_machine_definition.google_chart_url
17
+ else
18
+ puts "Missing argument: class. Please specify class=ClassName"
19
+ end
20
+ end
21
+
22
+ desc 'Opens the google chart in your browser. You must specify class=ClassNAME'
23
+ task :open => :environment do
24
+ if clazz = ENV['class']
25
+ `open '#{::CGI.unescape(clazz.constantize.state_machine_definition.google_chart_url)}'`
26
+ else
27
+ puts "Missing argument: class. Please specify class=ClassName"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -3,29 +3,19 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
  require "simple_state_machine/version"
4
4
 
5
5
  Gem::Specification.new do |s|
6
- s.name = %q{simple_state_machine}
7
- s.version = SimpleStateMachine::VERSION
8
- s.platform = Gem::Platform::RUBY
9
-
10
- s.authors = ["Marek de Heus", "Petrik de Heus"]
11
- s.description = %q{Simple State Machine is a state machine that focuses on events instead of states}
12
- s.email = ["FIX@example.com"]
13
- s.homepage = %q{http://github.com/mdh/ssm}
14
- s.extra_rdoc_files = [
15
- "LICENSE",
16
- "README.rdoc"
17
- ]
18
- s.files = `git ls-files`.split("\n")
19
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
- s.rdoc_options = ["--charset=UTF-8"]
21
- s.require_paths = ["lib"]
22
- s.rubygems_version = %q{1.3.7}
23
- s.summary = %q{A simple DSL to decorate existing methods with logic that guards state transitions.}
24
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
- s.add_development_dependency "rake"
26
- s.add_development_dependency "ZenTest"
27
- s.add_development_dependency "rspec"
28
- s.add_development_dependency "activerecord", "~>2.3.5"
29
- s.add_development_dependency "sqlite3-ruby"
6
+ s.name = %q{simple_state_machine}
7
+ s.version = SimpleStateMachine::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Marek de Heus", "Petrik de Heus"]
10
+ s.description = %q{A simple DSL to decorate existing methods with state transition guards.}
11
+ s.email = ["FIX@example.com"]
12
+ s.homepage = %q{http://github.com/mdh/ssm}
13
+ s.extra_rdoc_files = ["LICENSE","README.rdoc"]
14
+ s.files = `git ls-files`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.rdoc_options = ["--charset=UTF-8"]
17
+ s.require_paths = ["lib"]
18
+ s.summary = %q{A simple DSL to decorate existing methods with logic that guards state transitions.}
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
30
20
  end
31
21
 
data/spec/.DS_Store ADDED
Binary file
@@ -1,213 +1,250 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- require "rubygems"
4
- require "bundler"
5
- Bundler.require
6
- #Bundler.setup(:test)#, :activerecord)
7
- require 'active_record'
8
- require 'examples/user'
9
-
10
- ActiveRecord::Base.logger = Logger.new "test.log"
11
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3",
12
- :database => ":memory:")
13
-
14
- def setup_db
15
- ActiveRecord::Schema.define(:version => 1) do
16
- create_table :users do |t|
17
- t.column :id, :integer
18
- t.column :name, :string
19
- t.column :state, :string
20
- t.column :activation_code, :string
21
- t.column :created_at, :datetime
22
- t.column :updated_at, :datetime
1
+ require 'spec_helper'
2
+ require 'logger'
3
+
4
+ if defined? ActiveRecord
5
+ ActiveRecord::Base.logger = Logger.new "test.log"
6
+ ActiveRecord::Base.establish_connection('adapter' => RUBY_PLATFORM == 'java' ? 'jdbcsqlite3' : 'sqlite3', 'database' => ':memory:')
7
+
8
+ def setup_db
9
+ ActiveRecord::Schema.define(:version => 1) do
10
+ create_table :users do |t|
11
+ t.column :name, :string
12
+ t.column :state, :string
13
+ t.column :activation_code, :string
14
+ t.column :created_at, :datetime
15
+ t.column :updated_at, :datetime
16
+ end
17
+ create_table :tickets do |t|
18
+ t.column :ssm_state, :string
19
+ end
23
20
  end
24
- create_table :tickets do |t|
25
- t.column :id, :integer
26
- t.column :ssm_state, :string
27
- end
28
- end
29
- end
30
-
31
- def teardown_db
32
- ActiveRecord::Base.connection.tables.each do |table|
33
- ActiveRecord::Base.connection.drop_table(table)
34
- end
35
- end
36
-
37
- class Ticket < ActiveRecord::Base
38
- extend SimpleStateMachine::ActiveRecord
39
-
40
- state_machine_definition.state_method = :ssm_state
41
-
42
- def after_initialize
43
- self.ssm_state ||= 'open'
44
- end
45
-
46
- event :close, :open => :closed
47
- end
48
-
49
- describe ActiveRecord do
50
-
51
- before do
52
- setup_db
53
- end
54
-
55
- after do
56
- teardown_db
57
21
  end
58
22
 
59
- it "has a default state" do
60
- User.new.should be_new
61
- end
62
-
63
- # TODO needs nesting/grouping, seems to have some duplication
64
-
65
- describe "event_and_save" do
66
- it "persists transitions" do
67
- user = User.create!(:name => 'name')
68
- user.invite_and_save.should == true
69
- User.find(user.id).should be_invited
70
- User.find(user.id).activation_code.should_not be_nil
71
- end
72
-
73
- it "persist transitions even when state is attr_protected" do
74
- user_class = Class.new(User)
75
- user_class.instance_eval { attr_protected :state }
76
- user = user_class.create!(:name => 'name', :state => 'x')
77
- user.should be_new
78
- user.invite_and_save
79
- user.reload.should be_invited
80
- end
81
-
82
- it "persists transitions when using send and a symbol" do
83
- user = User.create!(:name => 'name')
84
- user.send(:invite_and_save).should == true
85
- User.find(user.id).should be_invited
86
- User.find(user.id).activation_code.should_not be_nil
23
+ def teardown_db
24
+ ActiveRecord::Base.connection.tables.each do |table|
25
+ ActiveRecord::Base.connection.drop_table(table)
87
26
  end
27
+ end
88
28
 
89
- it "raises an error if an invalid state_transition is called" do
90
- user = User.create!(:name => 'name')
91
- expect {
92
- user.confirm_invitation_and_save 'abc'
93
- }.to raise_error(SimpleStateMachine::IllegalStateTransitionError,
94
- "You cannot 'confirm_invitation' when state is 'new'")
95
- end
29
+ class Ticket < ActiveRecord::Base
30
+ extend SimpleStateMachine::ActiveRecord
96
31
 
97
- it "returns false and keeps state if record is invalid" do
98
- user = User.new
99
- user.should be_new
100
- user.should_not be_valid
101
- user.invite_and_save.should == false
102
- user.should be_new
103
- end
32
+ state_machine_definition.state_method = :ssm_state
104
33
 
105
- it "returns false, keeps state and keeps errors if event adds errors" do
106
- user = User.create!(:name => 'name')
107
- user.invite_and_save!
108
- user.should be_invited
109
- user.confirm_invitation_and_save('x').should == false
110
- user.should be_invited
111
- user.errors.entries.should == [['activation_code', 'is invalid']]
34
+ after_initialize :after_initialize
35
+ def after_initialize
36
+ self.ssm_state ||= 'open'
112
37
  end
113
38
 
39
+ event :close, :open => :closed
114
40
  end
115
41
 
116
- describe "event_and_save!" do
42
+ describe ActiveRecord do
117
43
 
118
- it "persists transitions" do
119
- user = User.create!(:name => 'name')
120
- user.invite_and_save!.should == true
121
- User.find(user.id).should be_invited
122
- User.find(user.id).activation_code.should_not be_nil
44
+ before do
45
+ setup_db
123
46
  end
124
47
 
125
- it "persist transitions even when state is attr_protected" do
126
- user_class = Class.new(User)
127
- user_class.instance_eval { attr_protected :state }
128
- user = user_class.create!(:name => 'name', :state => 'x')
129
- user.should be_new
130
- user.invite_and_save!
131
- user.reload.should be_invited
48
+ after do
49
+ teardown_db
132
50
  end
133
51
 
134
- it "raises an error if an invalid state_transition is called" do
135
- user = User.create!(:name => 'name')
136
- expect {
137
- user.confirm_invitation_and_save! 'abc'
138
- }.to raise_error(SimpleStateMachine::IllegalStateTransitionError,
139
- "You cannot 'confirm_invitation' when state is 'new'")
52
+ it "has a default state" do
53
+ expect(User.new).to be_new
140
54
  end
141
55
 
142
- it "raises a RecordInvalid and keeps state if record is invalid" do
143
- user = User.new
144
- user.should be_new
145
- user.should_not be_valid
146
- expect {
147
- user.invite_and_save!
148
- }.to raise_error(ActiveRecord::RecordInvalid,
149
- "Validation failed: Name can't be blank")
150
- user.should be_new
151
- end
152
-
153
- it "raises a RecordInvalid and keeps state if event adds errors" do
56
+ it "persists transitions when using send and a symbol" do
154
57
  user = User.create!(:name => 'name')
155
- user.invite_and_save!
156
- user.should be_invited
157
- expect {
158
- user.confirm_invitation_and_save!('x')
159
- }.to raise_error(ActiveRecord::RecordInvalid,
160
- "Validation failed: Activation code is invalid")
161
- user.should be_invited
162
- end
163
-
164
- end
165
-
166
- describe "event" do
58
+ expect(user.send(:invite_and_save)).to eq(true)
59
+ expect(User.find(user.id)).to be_invited
60
+ expect(User.find(user.id).activation_code).not_to be_nil
61
+ end
62
+
63
+ # TODO needs nesting/grouping, seems to have some duplication
64
+
65
+ describe "event_and_save" do
66
+ it "persists transitions" do
67
+ user = User.create!(:name => 'name')
68
+ expect(user.invite_and_save).to eq(true)
69
+ expect(User.find(user.id)).to be_invited
70
+ expect(User.find(user.id).activation_code).not_to be_nil
71
+ end
72
+
73
+ it "persists transitions when using send and a symbol" do
74
+ user = User.create!(:name => 'name')
75
+ expect(user.send(:invite_and_save)).to eq(true)
76
+ expect(User.find(user.id)).to be_invited
77
+ expect(User.find(user.id).activation_code).not_to be_nil
78
+ end
79
+
80
+ it "raises an error if an invalid state_transition is called" do
81
+ user = User.create!(:name => 'name')
82
+ expect {
83
+ user.confirm_invitation_and_save 'abc'
84
+ }.to raise_error(SimpleStateMachine::IllegalStateTransitionError,
85
+ "You cannot 'confirm_invitation' when state is 'new'")
86
+ end
87
+
88
+ it "returns false and keeps state if record is invalid" do
89
+ user = User.new
90
+ expect(user).to be_new
91
+ expect(user).not_to be_valid
92
+ expect(user.invite_and_save).to eq(false)
93
+ expect(user).to be_new
94
+ end
95
+
96
+ it "returns false, keeps state and keeps errors if event adds errors" do
97
+ user = User.create!(:name => 'name')
98
+ user.invite_and_save!
99
+ expect(user).to be_invited
100
+ expect(user.confirm_invitation_and_save('x')).to eq(false)
101
+ expect(user).to be_invited
102
+ expect(Array(user.errors[:activation_code])).to eq(['is invalid'])
103
+ end
104
+
105
+ it "rollsback if an exception is raised" do
106
+ user_class = Class.new(User)
107
+ user_class.instance_eval do
108
+ define_method :invite_without_managed_state do
109
+ User.create!(:name => 'name2') #this shouldn't be persisted
110
+ User.create! #this should raise an error
111
+ end
112
+ end
113
+ expect(user_class.count).to eq(0)
114
+ user = user_class.create!(:name => 'name')
115
+ expect {
116
+ user.transaction { user.invite_and_save }
117
+ }.to raise_error(ActiveRecord::RecordInvalid,
118
+ "Validation failed: Name can't be blank")
119
+ expect(user_class.count).to eq(1)
120
+ expect(user_class.first.name).to eq('name')
121
+ expect(user_class.first).to be_new
122
+ end
123
+
124
+ it "raises an error if an invalid state_transition is called" do
125
+ user = User.create!(:name => 'name')
126
+ expect {
127
+ user.confirm_invitation_and_save! 'abc'
128
+ }.to raise_error(SimpleStateMachine::IllegalStateTransitionError,
129
+ "You cannot 'confirm_invitation' when state is 'new'")
130
+ end
131
+ end
132
+
133
+ describe "event_and_save!" do
134
+
135
+ it "persists transitions" do
136
+ user = User.create!(:name => 'name')
137
+ expect(user.invite_and_save!).to eq(true)
138
+ expect(User.find(user.id)).to be_invited
139
+ expect(User.find(user.id).activation_code).not_to be_nil
140
+ end
141
+
142
+ it "raises an error if an invalid state_transition is called" do
143
+ user = User.create!(:name => 'name')
144
+ expect {
145
+ user.confirm_invitation_and_save! 'abc'
146
+ }.to raise_error(SimpleStateMachine::IllegalStateTransitionError,
147
+ "You cannot 'confirm_invitation' when state is 'new'")
148
+ end
149
+
150
+ it "raises a RecordInvalid and keeps state if record is invalid" do
151
+ user = User.new
152
+ expect(user).to be_new
153
+ expect(user).not_to be_valid
154
+ expect {
155
+ user.invite_and_save!
156
+ }.to raise_error(ActiveRecord::RecordInvalid,
157
+ "Validation failed: Name can't be blank")
158
+ expect(user).to be_new
159
+ end
160
+
161
+ it "raises a RecordInvalid and keeps state if event adds errors" do
162
+ user = User.create!(:name => 'name')
163
+ user.invite_and_save!
164
+ expect(user).to be_invited
165
+ expect {
166
+ user.confirm_invitation_and_save!('x')
167
+ }.to raise_error(ActiveRecord::RecordInvalid,
168
+ "Validation failed: Activation code is invalid")
169
+ expect(user).to be_invited
170
+ end
171
+
172
+ it "rollsback if an exception is raised" do
173
+ user_class = Class.new(User)
174
+ user_class.instance_eval do
175
+ define_method :invite_without_managed_state do
176
+ User.create!(:name => 'name2') #this shouldn't be persisted
177
+ User.create! #this should raise an error
178
+ end
179
+ end
180
+ expect(user_class.count).to eq(0)
181
+ user = user_class.create!(:name => 'name')
182
+ expect {
183
+ user.transaction { user.invite_and_save! }
184
+ }.to raise_error(ActiveRecord::RecordInvalid,
185
+ "Validation failed: Name can't be blank")
186
+ expect(user_class.count).to eq(1)
187
+ expect(user_class.first.name).to eq('name')
188
+ expect(user_class.first).to be_new
189
+ end
190
+
191
+ end
192
+
193
+ describe "event" do
194
+ it "persists transitions" do
195
+ user = User.create!(:name => 'name')
196
+ expect(user.invite).to eq(true)
197
+ expect(User.find(user.id)).to be_invited
198
+ expect(User.find(user.id).activation_code).not_to be_nil
199
+ end
200
+
201
+ it "returns false, keeps state and keeps errors if event adds errors" do
202
+ user = User.create!(:name => 'name')
203
+ user.invite_and_save!
204
+ expect(user).to be_invited
205
+ expect(user.confirm_invitation('x')).to eq(false)
206
+ expect(user).to be_invited
207
+ expect(Array(user.errors[:activation_code])).to eq(['is invalid'])
208
+ end
167
209
 
168
- it "raises an error" do
169
- user = User.create!(:name => 'name')
170
- expect { user.invite }.to raise_error(RuntimeError, "You cannot call invite. Use invite! instead")
171
210
  end
172
211
 
173
- end
212
+ describe "event!" do
174
213
 
175
- describe "event!" do
214
+ it "persists transitions" do
215
+ user = User.create!(:name => 'name')
216
+ expect(user.invite!).to eq(true)
217
+ expect(User.find(user.id)).to be_invited
218
+ expect(User.find(user.id).activation_code).not_to be_nil
219
+ end
176
220
 
177
- it "persists transitions" do
178
- user = User.create!(:name => 'name')
179
- user.invite!.should == true
180
- User.find(user.id).should be_invited
181
- User.find(user.id).activation_code.should_not be_nil
182
- end
221
+ it "raises a RecordInvalid and keeps state if record is invalid" do
222
+ user = User.new
223
+ expect(user).to be_new
224
+ expect(user).not_to be_valid
225
+ expect { user.invite! }.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Name can't be blank")
226
+ expect(user).to be_new
227
+ end
183
228
 
184
- it "raises a RecordInvalid and keeps state if record is invalid" do
185
- user = User.new
186
- user.should be_new
187
- user.should_not be_valid
188
- expect { user.invite! }.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Name can't be blank")
189
- user.should be_new
190
229
  end
191
230
 
192
- end
231
+ describe 'custom state method' do
193
232
 
194
- describe 'custom state method' do
233
+ it "persists transitions" do
234
+ ticket = Ticket.create!
235
+ expect(ticket.ssm_state).to eq('open')
236
+ expect(ticket.close!).to eq(true)
237
+ expect(ticket.ssm_state).to eq('closed')
238
+ end
195
239
 
196
- it "persists transitions" do
197
- ticket = Ticket.create!
198
- ticket.should be_open
199
- ticket.close!.should == true
200
- ticket.should be_closed
201
- end
240
+ it "persists transitions with !" do
241
+ ticket = Ticket.create!
242
+ expect(ticket.ssm_state).to eq('open')
243
+ ticket.close!
244
+ expect(ticket.ssm_state).to eq('closed')
245
+ end
202
246
 
203
- it "persists transitions with !" do
204
- ticket = Ticket.create!
205
- ticket.should be_open
206
- ticket.close!
207
- ticket.should be_closed
208
247
  end
209
248
 
210
249
  end
211
-
212
250
  end
213
-