simple_state_machine 0.5.2 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
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
-