simple_state_machine 0.4.3 → 0.5.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +34 -0
- data/Rakefile +17 -34
- data/autotest/discover.rb +1 -0
- data/lib/simple_state_machine/active_record.rb +11 -6
- data/lib/simple_state_machine/simple_state_machine.rb +77 -27
- data/lib/simple_state_machine/version.rb +3 -0
- data/lib/simple_state_machine.rb +1 -1
- data/simple_state_machine.gemspec +13 -54
- data/spec/active_record_spec.rb +53 -24
- data/spec/decorator_spec.rb +179 -28
- data/spec/examples_spec.rb +1 -0
- data/spec/mountable_spec.rb +24 -0
- data/spec/simple_state_machine_spec.rb +73 -93
- data/spec/spec_helper.rb +0 -6
- data/spec/state_machine_definition_spec.rb +89 -0
- data/spec/state_machine_spec.rb +26 -0
- metadata +87 -24
- data/VERSION +0 -1
- data/spec/spec.opts +0 -3
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
simple_state_machine (1.5.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ZenTest (4.4.2)
|
10
|
+
activerecord (2.3.10)
|
11
|
+
activesupport (= 2.3.10)
|
12
|
+
activesupport (2.3.10)
|
13
|
+
diff-lcs (1.1.2)
|
14
|
+
rake (0.8.7)
|
15
|
+
rspec (2.5.0)
|
16
|
+
rspec-core (~> 2.5.0)
|
17
|
+
rspec-expectations (~> 2.5.0)
|
18
|
+
rspec-mocks (~> 2.5.0)
|
19
|
+
rspec-core (2.5.1)
|
20
|
+
rspec-expectations (2.5.0)
|
21
|
+
diff-lcs (~> 1.1.2)
|
22
|
+
rspec-mocks (2.5.0)
|
23
|
+
sqlite3-ruby (1.3.1)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
ZenTest
|
30
|
+
activerecord (~> 2.3.5)
|
31
|
+
rake
|
32
|
+
rspec
|
33
|
+
simple_state_machine!
|
34
|
+
sqlite3-ruby
|
data/Rakefile
CHANGED
@@ -1,38 +1,21 @@
|
|
1
|
-
require '
|
2
|
-
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
require 'spec/rake/spectask'
|
22
|
-
Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
-
spec.libs << 'lib' << 'spec'
|
24
|
-
spec.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
-
end
|
26
|
-
|
27
|
-
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
28
|
-
spec.libs << 'lib' << 'spec'
|
29
|
-
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
-
spec.rcov = true
|
31
|
-
end
|
32
|
-
|
33
|
-
task :spec => :check_dependencies
|
34
|
-
|
35
|
-
task :default => :spec
|
4
|
+
#require 'spec/rake/spectask'
|
5
|
+
#Spec::Rake::SpecTask.new(:spec) do |spec|
|
6
|
+
# spec.libs << 'lib' << 'spec'
|
7
|
+
# spec.spec_files = FileList['spec/**/*_spec.rb']
|
8
|
+
#end
|
9
|
+
#
|
10
|
+
#Spec::Rake::SpecTask.new(:rcov) do |spec|
|
11
|
+
# spec.libs << 'lib' << 'spec'
|
12
|
+
# spec.pattern = 'spec/**/*_spec.rb'
|
13
|
+
# spec.rcov = true
|
14
|
+
#end
|
15
|
+
#
|
16
|
+
#task :spec => :check_dependencies
|
17
|
+
#
|
18
|
+
#task :default => :spec
|
36
19
|
|
37
20
|
require 'rake/rdoctask'
|
38
21
|
Rake::RDocTask.new do |rdoc|
|
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
@@ -1,10 +1,8 @@
|
|
1
1
|
module SimpleStateMachine::ActiveRecord
|
2
2
|
|
3
|
-
include SimpleStateMachine::
|
4
|
-
|
5
|
-
|
6
|
-
Decorator.new subject
|
7
|
-
end
|
3
|
+
include SimpleStateMachine::Mountable
|
4
|
+
include SimpleStateMachine::Extendable
|
5
|
+
include SimpleStateMachine::Inheritable
|
8
6
|
|
9
7
|
class Decorator < SimpleStateMachine::Decorator
|
10
8
|
|
@@ -60,5 +58,12 @@ module SimpleStateMachine::ActiveRecord
|
|
60
58
|
def define_state_getter_method; end
|
61
59
|
|
62
60
|
end
|
63
|
-
|
61
|
+
|
62
|
+
def state_machine_definition
|
63
|
+
unless @state_machine_definition
|
64
|
+
@state_machine_definition = SimpleStateMachine::StateMachineDefinition.new
|
65
|
+
@state_machine_definition.lazy_decorator = lambda { Decorator.new(self) }
|
66
|
+
end
|
67
|
+
@state_machine_definition
|
68
|
+
end
|
64
69
|
end
|
@@ -1,50 +1,68 @@
|
|
1
1
|
module SimpleStateMachine
|
2
|
+
|
3
|
+
require 'cgi'
|
2
4
|
|
3
|
-
class
|
5
|
+
class IllegalStateTransitionError < ::RuntimeError
|
4
6
|
end
|
5
7
|
|
8
|
+
##
|
9
|
+
# Allows class to mount a state_machine
|
10
|
+
module Mountable
|
11
|
+
def state_machine_definition
|
12
|
+
unless @state_machine_definition
|
13
|
+
@state_machine_definition = StateMachineDefinition.new
|
14
|
+
@state_machine_definition.lazy_decorator = lambda { Decorator.new(self) }
|
15
|
+
end
|
16
|
+
@state_machine_definition
|
17
|
+
end
|
18
|
+
|
19
|
+
def state_machine_definition= state_machine_definition
|
20
|
+
@state_machine_definition = state_machine_definition
|
21
|
+
state_machine_definition.transitions.each do |transition|
|
22
|
+
state_machine_definition.decorator.decorate(transition)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
include Mountable
|
27
|
+
|
6
28
|
##
|
7
29
|
# Adds state machine methods to the extended class
|
8
|
-
module
|
30
|
+
module Extendable
|
9
31
|
|
10
32
|
# mark the method as an event and specify how the state should transition
|
11
33
|
def event event_name, state_transitions
|
12
34
|
state_transitions.each do |froms, to|
|
13
35
|
[froms].flatten.each do |from|
|
14
|
-
|
15
|
-
state_machine_decorator(self).decorate(transition)
|
36
|
+
state_machine_definition.add_transition(event_name, from, to)
|
16
37
|
end
|
17
38
|
end
|
18
39
|
end
|
19
40
|
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
def state_machine_definition= state_machine_definition
|
25
|
-
@state_machine_definition = state_machine_definition
|
26
|
-
state_machine_definition.transitions.each do |transition|
|
27
|
-
state_machine_decorator(self).decorate(transition)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def state_machine_decorator subject
|
32
|
-
Decorator.new subject
|
33
|
-
end
|
41
|
+
end
|
42
|
+
include Extendable
|
34
43
|
|
44
|
+
##
|
45
|
+
# Allows subclasses to inherit state machines
|
46
|
+
module Inheritable
|
35
47
|
def inherited(subclass)
|
36
48
|
subclass.state_machine_definition = state_machine_definition.clone
|
49
|
+
decorator = state_machine_definition.decorator
|
50
|
+
decorator.subject = subclass
|
51
|
+
subclass.state_machine_definition.decorator = decorator
|
37
52
|
super
|
38
53
|
end
|
39
54
|
end
|
40
|
-
|
41
|
-
include StateMachineMixin
|
55
|
+
include Inheritable
|
42
56
|
|
43
57
|
##
|
44
58
|
# Defines state machine transitions
|
45
59
|
class StateMachineDefinition
|
46
60
|
|
47
|
-
attr_writer :state_method
|
61
|
+
attr_writer :state_method, :decorator, :lazy_decorator
|
62
|
+
|
63
|
+
def decorator
|
64
|
+
@decorator ||= @lazy_decorator.call
|
65
|
+
end
|
48
66
|
|
49
67
|
def transitions
|
50
68
|
@transitions ||= []
|
@@ -53,12 +71,28 @@ module SimpleStateMachine
|
|
53
71
|
def add_transition event_name, from, to
|
54
72
|
transition = Transition.new(event_name, from, to)
|
55
73
|
transitions << transition
|
56
|
-
transition
|
74
|
+
decorator.decorate(transition)
|
57
75
|
end
|
58
76
|
|
59
77
|
def state_method
|
60
78
|
@state_method ||= :state
|
61
79
|
end
|
80
|
+
|
81
|
+
# Human readable format: old_state.event! => new_state
|
82
|
+
def to_s
|
83
|
+
transitions.map(&:to_s).join("\n")
|
84
|
+
end
|
85
|
+
|
86
|
+
# Graphiz dot format for rendering as a directional graph
|
87
|
+
def to_graphiz_dot
|
88
|
+
transitions.map { |t| t.to_graphiz_dot }.join(";")
|
89
|
+
end
|
90
|
+
|
91
|
+
# Generates a url that renders states and events as a directional graph.
|
92
|
+
# See http://code.google.com/apis/chart/docs/gallery/graphviz.html
|
93
|
+
def google_chart_url
|
94
|
+
"http://chart.googleapis.com/chart?cht=gv&chl=digraph{#{::CGI.escape to_graphiz_dot}}"
|
95
|
+
end
|
62
96
|
end
|
63
97
|
|
64
98
|
##
|
@@ -128,7 +162,7 @@ module SimpleStateMachine
|
|
128
162
|
|
129
163
|
# override with your own implementation, like setting errors in your model
|
130
164
|
def illegal_event_callback event_name
|
131
|
-
raise
|
165
|
+
raise IllegalStateTransitionError.new("You cannot '#{event_name}' when state is '#{@subject.send(state_method)}'")
|
132
166
|
end
|
133
167
|
|
134
168
|
end
|
@@ -153,6 +187,15 @@ module SimpleStateMachine
|
|
153
187
|
is_same_event?(event_name) && error.class == from
|
154
188
|
end
|
155
189
|
|
190
|
+
def to_s
|
191
|
+
"#{from}.#{event_name}! => #{to}"
|
192
|
+
end
|
193
|
+
|
194
|
+
def to_graphiz_dot
|
195
|
+
%("#{from}"->"#{to}"[label=#{event_name}])
|
196
|
+
end
|
197
|
+
|
198
|
+
|
156
199
|
private
|
157
200
|
|
158
201
|
def is_same_event?(event_name)
|
@@ -168,6 +211,7 @@ module SimpleStateMachine
|
|
168
211
|
# Decorates @subject with methods to access the state machine
|
169
212
|
class Decorator
|
170
213
|
|
214
|
+
attr_writer :subject
|
171
215
|
def initialize(subject)
|
172
216
|
@subject = subject
|
173
217
|
define_state_machine_method
|
@@ -191,7 +235,7 @@ module SimpleStateMachine
|
|
191
235
|
end
|
192
236
|
|
193
237
|
def define_state_helper_method state
|
194
|
-
unless
|
238
|
+
unless any_method_defined?("#{state.to_s}?")
|
195
239
|
@subject.send(:define_method, "#{state.to_s}?") do
|
196
240
|
self.send(self.class.state_machine_definition.state_method) == state.to_s
|
197
241
|
end
|
@@ -199,7 +243,7 @@ module SimpleStateMachine
|
|
199
243
|
end
|
200
244
|
|
201
245
|
def define_event_method event_name
|
202
|
-
unless
|
246
|
+
unless any_method_defined?("#{event_name}")
|
203
247
|
@subject.send(:define_method, "#{event_name}") {}
|
204
248
|
end
|
205
249
|
end
|
@@ -218,7 +262,7 @@ module SimpleStateMachine
|
|
218
262
|
end
|
219
263
|
|
220
264
|
def define_state_setter_method
|
221
|
-
unless
|
265
|
+
unless any_method_defined?("#{state_method}=")
|
222
266
|
@subject.send(:define_method, "#{state_method}=") do |new_state|
|
223
267
|
instance_variable_set(:"@#{self.class.state_machine_definition.state_method}", new_state)
|
224
268
|
end
|
@@ -226,10 +270,16 @@ module SimpleStateMachine
|
|
226
270
|
end
|
227
271
|
|
228
272
|
def define_state_getter_method
|
229
|
-
unless
|
273
|
+
unless any_method_defined?(state_method)
|
230
274
|
@subject.send(:attr_reader, state_method)
|
231
275
|
end
|
232
276
|
end
|
277
|
+
|
278
|
+
def any_method_defined?(method)
|
279
|
+
@subject.method_defined?(method) ||
|
280
|
+
@subject.protected_method_defined?(method) ||
|
281
|
+
@subject.private_method_defined?(method)
|
282
|
+
end
|
233
283
|
|
234
284
|
protected
|
235
285
|
|
data/lib/simple_state_machine.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
require 'simple_state_machine/simple_state_machine'
|
2
|
-
require 'simple_state_machine/active_record'
|
2
|
+
require 'simple_state_machine/active_record'
|
@@ -1,72 +1,31 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "simple_state_machine/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |s|
|
7
6
|
s.name = %q{simple_state_machine}
|
8
|
-
s.version
|
7
|
+
s.version = SimpleStateMachine::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
9
|
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
10
|
s.authors = ["Marek de Heus", "Petrik de Heus"]
|
12
|
-
s.date = %q{2010-09-14}
|
13
11
|
s.description = %q{A simple DSL to decorate existing methods with logic that guards state transitions.}
|
14
12
|
s.email = ["FIX@example.com"]
|
13
|
+
s.homepage = %q{http://github.com/mdh/ssm}
|
15
14
|
s.extra_rdoc_files = [
|
16
15
|
"LICENSE",
|
17
16
|
"README.rdoc"
|
18
17
|
]
|
19
|
-
s.files
|
20
|
-
|
21
|
-
"LICENSE",
|
22
|
-
"README.rdoc",
|
23
|
-
"Rakefile",
|
24
|
-
"VERSION",
|
25
|
-
"examples/conversation.rb",
|
26
|
-
"examples/lamp.rb",
|
27
|
-
"examples/relationship.rb",
|
28
|
-
"examples/traffic_light.rb",
|
29
|
-
"examples/user.rb",
|
30
|
-
"lib/simple_state_machine.rb",
|
31
|
-
"lib/simple_state_machine/active_record.rb",
|
32
|
-
"lib/simple_state_machine/simple_state_machine.rb",
|
33
|
-
"simple_state_machine.gemspec",
|
34
|
-
"spec/active_record_spec.rb",
|
35
|
-
"spec/decorator_spec.rb",
|
36
|
-
"spec/examples_spec.rb",
|
37
|
-
"spec/simple_state_machine_spec.rb",
|
38
|
-
"spec/spec.opts",
|
39
|
-
"spec/spec_helper.rb"
|
40
|
-
]
|
41
|
-
s.homepage = %q{http://github.com/mdh/ssm}
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
42
20
|
s.rdoc_options = ["--charset=UTF-8"]
|
43
21
|
s.require_paths = ["lib"]
|
44
22
|
s.rubygems_version = %q{1.3.7}
|
45
23
|
s.summary = %q{A statemachine that focuses on events instead of states}
|
46
|
-
s.test_files
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
"examples/conversation.rb",
|
53
|
-
"examples/lamp.rb",
|
54
|
-
"examples/relationship.rb",
|
55
|
-
"examples/traffic_light.rb",
|
56
|
-
"examples/user.rb"
|
57
|
-
]
|
58
|
-
|
59
|
-
if s.respond_to? :specification_version then
|
60
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
61
|
-
s.specification_version = 3
|
62
|
-
|
63
|
-
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
64
|
-
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
65
|
-
else
|
66
|
-
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
67
|
-
end
|
68
|
-
else
|
69
|
-
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
70
|
-
end
|
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"
|
71
30
|
end
|
72
31
|
|
data/spec/active_record_spec.rb
CHANGED
@@ -1,28 +1,29 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
-
require
|
4
|
-
|
3
|
+
require "rubygems"
|
4
|
+
require "bundler"
|
5
|
+
Bundler.require
|
6
|
+
#Bundler.setup(:test)#, :activerecord)
|
5
7
|
require 'active_record'
|
6
8
|
require 'examples/user'
|
7
9
|
|
8
|
-
ActiveRecord::Base.logger = Logger.new
|
9
|
-
ActiveRecord::Base.establish_connection(:adapter
|
10
|
+
ActiveRecord::Base.logger = Logger.new "test.log"
|
11
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3",
|
12
|
+
:database => ":memory:")
|
10
13
|
|
11
14
|
def setup_db
|
12
15
|
ActiveRecord::Schema.define(:version => 1) do
|
13
16
|
create_table :users do |t|
|
14
|
-
t.column :id,
|
15
|
-
t.column :name,
|
16
|
-
t.column :state,
|
17
|
-
t.column :activation_code,
|
18
|
-
t.column :created_at,
|
19
|
-
t.column :updated_at,
|
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
|
20
23
|
end
|
21
|
-
end
|
22
|
-
ActiveRecord::Schema.define(:version => 1) do
|
23
24
|
create_table :tickets do |t|
|
24
|
-
t.column :id,
|
25
|
-
t.column :ssm_state,
|
25
|
+
t.column :id, :integer
|
26
|
+
t.column :ssm_state, :string
|
26
27
|
end
|
27
28
|
end
|
28
29
|
end
|
@@ -87,8 +88,10 @@ describe ActiveRecord do
|
|
87
88
|
|
88
89
|
it "raises an error if an invalid state_transition is called" do
|
89
90
|
user = User.create!(:name => 'name')
|
90
|
-
|
91
|
-
|
91
|
+
expect {
|
92
|
+
user.confirm_invitation_and_save 'abc'
|
93
|
+
}.to raise_error(SimpleStateMachine::IllegalStateTransitionError,
|
94
|
+
"You cannot 'confirm_invitation' when state is 'new'")
|
92
95
|
end
|
93
96
|
|
94
97
|
it "returns false and keeps state if record is invalid" do
|
@@ -130,16 +133,20 @@ describe ActiveRecord do
|
|
130
133
|
|
131
134
|
it "raises an error if an invalid state_transition is called" do
|
132
135
|
user = User.create!(:name => 'name')
|
133
|
-
|
134
|
-
|
136
|
+
expect {
|
137
|
+
user.confirm_invitation_and_save! 'abc'
|
138
|
+
}.to raise_error(SimpleStateMachine::IllegalStateTransitionError,
|
139
|
+
"You cannot 'confirm_invitation' when state is 'new'")
|
135
140
|
end
|
136
141
|
|
137
142
|
it "raises a RecordInvalid and keeps state if record is invalid" do
|
138
143
|
user = User.new
|
139
144
|
user.should be_new
|
140
145
|
user.should_not be_valid
|
141
|
-
|
142
|
-
|
146
|
+
expect {
|
147
|
+
user.invite_and_save!
|
148
|
+
}.to raise_error(ActiveRecord::RecordInvalid,
|
149
|
+
"Validation failed: Name can't be blank")
|
143
150
|
user.should be_new
|
144
151
|
end
|
145
152
|
|
@@ -147,13 +154,34 @@ describe ActiveRecord do
|
|
147
154
|
user = User.create!(:name => 'name')
|
148
155
|
user.invite_and_save!
|
149
156
|
user.should be_invited
|
150
|
-
|
151
|
-
|
157
|
+
expect {
|
158
|
+
user.confirm_invitation_and_save!('x')
|
159
|
+
}.to raise_error(ActiveRecord::RecordInvalid,
|
160
|
+
"Validation failed: Activation code is invalid")
|
152
161
|
user.should be_invited
|
153
162
|
end
|
154
163
|
|
155
164
|
end
|
156
165
|
|
166
|
+
describe "event" do
|
167
|
+
|
168
|
+
it "does not persist transitions" do
|
169
|
+
user = User.create!(:name => 'name')
|
170
|
+
user.invite.should == true
|
171
|
+
User.find(user.id).should_not be_invited
|
172
|
+
User.find(user.id).activation_code.should be_nil
|
173
|
+
end
|
174
|
+
|
175
|
+
it "returns false and keeps state if record is invalid" do
|
176
|
+
user = User.new
|
177
|
+
user.should be_new
|
178
|
+
user.should_not be_valid
|
179
|
+
user.invite.should == false
|
180
|
+
user.should be_new
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
157
185
|
describe "event!" do
|
158
186
|
|
159
187
|
it "persists transitions" do
|
@@ -167,8 +195,7 @@ describe ActiveRecord do
|
|
167
195
|
user = User.new
|
168
196
|
user.should be_new
|
169
197
|
user.should_not be_valid
|
170
|
-
|
171
|
-
l.should raise_error(ActiveRecord::RecordInvalid, "Validation failed: Name can't be blank")
|
198
|
+
expect { user.invite! }.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Name can't be blank")
|
172
199
|
user.should be_new
|
173
200
|
end
|
174
201
|
|
@@ -191,4 +218,6 @@ describe ActiveRecord do
|
|
191
218
|
end
|
192
219
|
|
193
220
|
end
|
221
|
+
|
194
222
|
end
|
223
|
+
|