aasm 3.0.26 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +9 -1
- data/README.md +56 -4
- data/aasm.gemspec +1 -1
- data/lib/aasm/base.rb +21 -15
- data/lib/aasm/event.rb +15 -4
- data/lib/aasm/instance_base.rb +2 -0
- data/lib/aasm/persistence/active_record_persistence.rb +17 -1
- data/lib/aasm/transition.rb +13 -6
- data/lib/aasm/version.rb +1 -1
- data/spec/models/guardian.rb +48 -0
- data/spec/unit/api_spec.rb +12 -12
- data/spec/unit/callbacks_spec.rb +25 -25
- data/spec/unit/complex_example_spec.rb +15 -15
- data/spec/unit/event_spec.rb +44 -44
- data/spec/unit/guard_spec.rb +60 -0
- data/spec/unit/initial_state_spec.rb +3 -3
- data/spec/unit/inspection_spec.rb +36 -36
- data/spec/unit/localizer_spec.rb +12 -12
- data/spec/unit/new_dsl_spec.rb +2 -2
- data/spec/unit/persistence/active_record_persistence_spec.rb +107 -67
- data/spec/unit/persistence/mongoid_persistance_spec.rb +24 -24
- data/spec/unit/simple_example_spec.rb +20 -20
- data/spec/unit/state_spec.rb +16 -16
- data/spec/unit/subclassing_spec.rb +6 -6
- data/spec/unit/transition_spec.rb +55 -40
- metadata +8 -4
data/spec/unit/callbacks_spec.rb
CHANGED
@@ -4,15 +4,15 @@ describe 'callbacks for the new DSL' do
|
|
4
4
|
let(:callback) {CallbackNewDsl.new}
|
5
5
|
|
6
6
|
it "be called in order" do
|
7
|
-
callback.
|
8
|
-
callback.
|
9
|
-
callback.
|
10
|
-
callback.
|
11
|
-
callback.
|
12
|
-
callback.
|
13
|
-
callback.
|
14
|
-
callback.
|
15
|
-
callback.
|
7
|
+
expect(callback).to receive(:exit_open).once.ordered
|
8
|
+
expect(callback).to receive(:before).once.ordered
|
9
|
+
expect(callback).to receive(:before_exit_open).once.ordered # these should be before the state changes
|
10
|
+
expect(callback).to receive(:before_enter_closed).once.ordered
|
11
|
+
expect(callback).to receive(:enter_closed).once.ordered
|
12
|
+
expect(callback).to receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
|
13
|
+
expect(callback).to receive(:after_exit_open).once.ordered # these should be after the state changes
|
14
|
+
expect(callback).to receive(:after_enter_closed).once.ordered
|
15
|
+
expect(callback).to receive(:after).once.ordered
|
16
16
|
|
17
17
|
callback.close!
|
18
18
|
end
|
@@ -35,20 +35,20 @@ describe 'event callbacks' do
|
|
35
35
|
it "should run error_callback if an exception is raised and error_callback defined" do
|
36
36
|
def @foo.error_callback(e); end
|
37
37
|
|
38
|
-
@foo.
|
39
|
-
@foo.
|
38
|
+
allow(@foo).to receive(:enter).and_raise(e=StandardError.new)
|
39
|
+
expect(@foo).to receive(:error_callback).with(e)
|
40
40
|
|
41
41
|
@foo.safe_close!
|
42
42
|
end
|
43
43
|
|
44
44
|
it "should raise NoMethodError if exceptionis raised and error_callback is declared but not defined" do
|
45
|
-
@foo.
|
46
|
-
|
45
|
+
allow(@foo).to receive(:enter).and_raise(StandardError)
|
46
|
+
expect{@foo.safe_close!}.to raise_error(NoMethodError)
|
47
47
|
end
|
48
48
|
|
49
49
|
it "should propagate an error if no error callback is declared" do
|
50
|
-
@foo.
|
51
|
-
|
50
|
+
allow(@foo).to receive(:enter).and_raise("Cannot enter safe")
|
51
|
+
expect{@foo.close!}.to raise_error(StandardError, "Cannot enter safe")
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -59,18 +59,18 @@ describe 'event callbacks' do
|
|
59
59
|
end
|
60
60
|
|
61
61
|
it 'should call it for successful bang fire' do
|
62
|
-
@foo.
|
62
|
+
expect(@foo).to receive(:aasm_event_fired).with(:close, :open, :closed)
|
63
63
|
@foo.close!
|
64
64
|
end
|
65
65
|
|
66
66
|
it 'should call it for successful non-bang fire' do
|
67
|
-
@foo.
|
67
|
+
expect(@foo).to receive(:aasm_event_fired)
|
68
68
|
@foo.close
|
69
69
|
end
|
70
70
|
|
71
71
|
it 'should not call it for failing bang fire' do
|
72
|
-
@foo.aasm.
|
73
|
-
@foo.
|
72
|
+
allow(@foo.aasm).to receive(:set_current_state_with_persistence).and_return(false)
|
73
|
+
expect(@foo).not_to receive(:aasm_event_fired)
|
74
74
|
@foo.close!
|
75
75
|
end
|
76
76
|
end
|
@@ -82,18 +82,18 @@ describe 'event callbacks' do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
it 'should call it when transition failed for bang fire' do
|
85
|
-
@foo.
|
86
|
-
|
85
|
+
expect(@foo).to receive(:aasm_event_failed).with(:null, :open)
|
86
|
+
expect {@foo.null!}.to raise_error(AASM::InvalidTransition)
|
87
87
|
end
|
88
88
|
|
89
89
|
it 'should call it when transition failed for non-bang fire' do
|
90
|
-
@foo.
|
91
|
-
|
90
|
+
expect(@foo).to receive(:aasm_event_failed).with(:null, :open)
|
91
|
+
expect {@foo.null}.to raise_error(AASM::InvalidTransition)
|
92
92
|
end
|
93
93
|
|
94
94
|
it 'should not call it if persist fails for bang fire' do
|
95
|
-
@foo.aasm.
|
96
|
-
@foo.
|
95
|
+
allow(@foo.aasm).to receive(:set_current_state_with_persistence).and_return(false)
|
96
|
+
expect(@foo).to receive(:aasm_event_failed)
|
97
97
|
@foo.close!
|
98
98
|
end
|
99
99
|
end
|
@@ -4,12 +4,12 @@ describe 'on initialization' do
|
|
4
4
|
let(:auth) {AuthMachine.new}
|
5
5
|
|
6
6
|
it 'should be in the pending state' do
|
7
|
-
auth.aasm.current_state.
|
7
|
+
expect(auth.aasm.current_state).to eq(:pending)
|
8
8
|
end
|
9
9
|
|
10
10
|
it 'should have an activation code' do
|
11
|
-
auth.has_activation_code
|
12
|
-
auth.activation_code.
|
11
|
+
expect(auth.has_activation_code?).to be_true
|
12
|
+
expect(auth.activation_code).not_to be_nil
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -19,26 +19,26 @@ describe 'when being unsuspended' do
|
|
19
19
|
it 'should be able to be unsuspended' do
|
20
20
|
auth.activate!
|
21
21
|
auth.suspend!
|
22
|
-
auth.may_unsuspend
|
22
|
+
expect(auth.may_unsuspend?).to be_true
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'should not be able to be unsuspended into active' do
|
26
26
|
auth.suspend!
|
27
|
-
auth.may_unsuspend?(:active).
|
27
|
+
expect(auth.may_unsuspend?(:active)).not_to be_true
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'should be able to be unsuspended into active if polite' do
|
31
31
|
auth.suspend!
|
32
|
-
auth.may_wait?(:waiting, :please).
|
32
|
+
expect(auth.may_wait?(:waiting, :please)).to be_true
|
33
33
|
auth.wait!(nil, :please)
|
34
34
|
end
|
35
35
|
|
36
36
|
it 'should not be able to be unsuspended into active if not polite' do
|
37
37
|
auth.suspend!
|
38
|
-
auth.may_wait?(:waiting).
|
39
|
-
auth.may_wait?(:waiting, :rude).
|
40
|
-
|
41
|
-
|
38
|
+
expect(auth.may_wait?(:waiting)).not_to be_true
|
39
|
+
expect(auth.may_wait?(:waiting, :rude)).not_to be_true
|
40
|
+
expect {auth.wait!(nil, :rude)}.to raise_error(AASM::InvalidTransition)
|
41
|
+
expect {auth.wait!}.to raise_error(AASM::InvalidTransition)
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'should not be able to be unpassified' do
|
@@ -46,8 +46,8 @@ describe 'when being unsuspended' do
|
|
46
46
|
auth.suspend!
|
47
47
|
auth.unsuspend!
|
48
48
|
|
49
|
-
auth.may_unpassify
|
50
|
-
|
49
|
+
expect(auth.may_unpassify?).not_to be_true
|
50
|
+
expect {auth.unpassify!}.to raise_error(AASM::InvalidTransition)
|
51
51
|
end
|
52
52
|
|
53
53
|
it 'should be active if previously activated' do
|
@@ -55,14 +55,14 @@ describe 'when being unsuspended' do
|
|
55
55
|
auth.suspend!
|
56
56
|
auth.unsuspend!
|
57
57
|
|
58
|
-
auth.aasm.current_state.
|
58
|
+
expect(auth.aasm.current_state).to eq(:active)
|
59
59
|
end
|
60
60
|
|
61
61
|
it 'should be pending if not previously activated, but an activation code is present' do
|
62
62
|
auth.suspend!
|
63
63
|
auth.unsuspend!
|
64
64
|
|
65
|
-
auth.aasm.current_state.
|
65
|
+
expect(auth.aasm.current_state).to eq(:pending)
|
66
66
|
end
|
67
67
|
|
68
68
|
it 'should be passive if not previously activated and there is no activation code' do
|
@@ -70,6 +70,6 @@ describe 'when being unsuspended' do
|
|
70
70
|
auth.suspend!
|
71
71
|
auth.unsuspend!
|
72
72
|
|
73
|
-
auth.aasm.current_state.
|
73
|
+
expect(auth.aasm.current_state).to eq(:passive)
|
74
74
|
end
|
75
75
|
end
|
data/spec/unit/event_spec.rb
CHANGED
@@ -10,27 +10,27 @@ describe 'adding an event' do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'should set the name' do
|
13
|
-
event.name.
|
13
|
+
expect(event.name).to eq(:close_order)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'should set the success callback' do
|
17
|
-
event.options[:success].
|
17
|
+
expect(event.options[:success]).to eq(:success_callback)
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'should set the after callback' do
|
21
|
-
event.options[:after].
|
21
|
+
expect(event.options[:after]).to eq([:after_callback])
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'should set the before callback' do
|
25
|
-
event.options[:before].
|
25
|
+
expect(event.options[:before]).to eq([:before_callback])
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'should create transitions' do
|
29
29
|
transitions = event.transitions
|
30
|
-
transitions[0].from.
|
31
|
-
transitions[0].to.
|
32
|
-
transitions[1].from.
|
33
|
-
transitions[1].to.
|
30
|
+
expect(transitions[0].from).to eq(:open)
|
31
|
+
expect(transitions[0].to).to eq(:closed)
|
32
|
+
expect(transitions[1].from).to eq(:received)
|
33
|
+
expect(transitions[1].to).to eq(:closed)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -42,19 +42,19 @@ describe 'transition inspection' do
|
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'should support inspecting transitions from other states' do
|
45
|
-
event.transitions_from_state(:sleeping).map(&:to).
|
46
|
-
event.transitions_from_state?(:sleeping).
|
45
|
+
expect(event.transitions_from_state(:sleeping).map(&:to)).to eq([:running])
|
46
|
+
expect(event.transitions_from_state?(:sleeping)).to be_true
|
47
47
|
|
48
|
-
event.transitions_from_state(:cleaning).map(&:to).
|
49
|
-
event.transitions_from_state?(:cleaning).
|
48
|
+
expect(event.transitions_from_state(:cleaning).map(&:to)).to eq([])
|
49
|
+
expect(event.transitions_from_state?(:cleaning)).to be_false
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'should support inspecting transitions to other states' do
|
53
|
-
event.transitions_to_state(:running).map(&:from).
|
54
|
-
event.transitions_to_state?(:running).
|
53
|
+
expect(event.transitions_to_state(:running).map(&:from)).to eq([:sleeping])
|
54
|
+
expect(event.transitions_to_state?(:running)).to be_true
|
55
55
|
|
56
|
-
event.transitions_to_state(:cleaning).map(&:to).
|
57
|
-
event.transitions_to_state?(:cleaning).
|
56
|
+
expect(event.transitions_to_state(:cleaning).map(&:to)).to eq([])
|
57
|
+
expect(event.transitions_to_state?(:cleaning)).to be_false
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
@@ -63,7 +63,7 @@ describe 'firing an event' do
|
|
63
63
|
obj = double('object', :aasm => double('aasm', :current_state => 'open'))
|
64
64
|
|
65
65
|
event = AASM::Event.new(:event)
|
66
|
-
event.fire(obj).
|
66
|
+
expect(event.fire(obj)).to be_nil
|
67
67
|
end
|
68
68
|
|
69
69
|
it 'should return the state of the first matching transition it finds' do
|
@@ -73,7 +73,7 @@ describe 'firing an event' do
|
|
73
73
|
|
74
74
|
obj = double('object', :aasm => double('aasm', :current_state => :open))
|
75
75
|
|
76
|
-
event.fire(obj).
|
76
|
+
expect(event.fire(obj)).to eq(:closed)
|
77
77
|
end
|
78
78
|
|
79
79
|
it 'should call the guard with the params passed in' do
|
@@ -82,9 +82,9 @@ describe 'firing an event' do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
obj = double('object', :aasm => double('aasm', :current_state => :open))
|
85
|
-
obj.
|
85
|
+
expect(obj).to receive(:guard_fn).with('arg1', 'arg2').and_return(true)
|
86
86
|
|
87
|
-
event.fire(obj, nil, 'arg1', 'arg2').
|
87
|
+
expect(event.fire(obj, nil, 'arg1', 'arg2')).to eq(:closed)
|
88
88
|
end
|
89
89
|
|
90
90
|
end
|
@@ -101,7 +101,7 @@ describe 'should fire callbacks' do
|
|
101
101
|
}
|
102
102
|
|
103
103
|
model = ThisNameBetterNotBeInUse.new
|
104
|
-
model.
|
104
|
+
expect(model).to receive(:symbol_success_callback)
|
105
105
|
model.with_symbol!
|
106
106
|
end
|
107
107
|
|
@@ -115,7 +115,7 @@ describe 'should fire callbacks' do
|
|
115
115
|
}
|
116
116
|
|
117
117
|
model = ThisNameBetterNotBeInUse.new
|
118
|
-
model.
|
118
|
+
expect(model).to receive(:string_success_callback)
|
119
119
|
model.with_string!
|
120
120
|
end
|
121
121
|
|
@@ -129,8 +129,8 @@ describe 'should fire callbacks' do
|
|
129
129
|
}
|
130
130
|
|
131
131
|
model = ThisNameBetterNotBeInUse.new
|
132
|
-
model.
|
133
|
-
model.
|
132
|
+
expect(model).to receive(:success_callback1)
|
133
|
+
expect(model).to receive(:success_callback2)
|
134
134
|
model.with_array!
|
135
135
|
end
|
136
136
|
|
@@ -144,9 +144,9 @@ describe 'should fire callbacks' do
|
|
144
144
|
}
|
145
145
|
|
146
146
|
model = ThisNameBetterNotBeInUse.new
|
147
|
-
model.
|
148
|
-
model.
|
149
|
-
model.
|
147
|
+
expect(model).to receive(:success_callback1)
|
148
|
+
expect(model).to receive(:success_callback2)
|
149
|
+
expect(model).to receive(:proc_success_callback)
|
150
150
|
model.with_array_including_procs!
|
151
151
|
end
|
152
152
|
|
@@ -160,7 +160,7 @@ describe 'should fire callbacks' do
|
|
160
160
|
}
|
161
161
|
|
162
162
|
model = ThisNameBetterNotBeInUse.new
|
163
|
-
model.
|
163
|
+
expect(model).to receive(:proc_success_callback)
|
164
164
|
model.with_proc!
|
165
165
|
end
|
166
166
|
end
|
@@ -182,9 +182,9 @@ describe 'should fire callbacks' do
|
|
182
182
|
end
|
183
183
|
|
184
184
|
model = ThisNameBetterNotBeInUse.new
|
185
|
-
model.
|
186
|
-
model.
|
187
|
-
model.
|
185
|
+
expect(model).to receive(:do_one_thing_after).once.ordered
|
186
|
+
expect(model).to receive(:do_another_thing_after_too).once.ordered
|
187
|
+
expect(model).to receive(:do_third_thing_at_last).once.ordered
|
188
188
|
model.with_afters!
|
189
189
|
end
|
190
190
|
end
|
@@ -203,7 +203,7 @@ describe 'should fire callbacks' do
|
|
203
203
|
end
|
204
204
|
|
205
205
|
model = ThisNameBetterNotBeInUse.new
|
206
|
-
model.
|
206
|
+
expect(model).to receive(:do_something_before).once
|
207
207
|
model.before_as_proc!
|
208
208
|
end
|
209
209
|
end
|
@@ -221,8 +221,8 @@ describe 'should fire callbacks' do
|
|
221
221
|
end
|
222
222
|
|
223
223
|
model = ThisNameBetterNotBeInUse.new
|
224
|
-
model.
|
225
|
-
model.
|
224
|
+
expect(model).to receive(:do_something_before).once.ordered
|
225
|
+
expect(model).to receive(:do_something_after).once.ordered
|
226
226
|
model.in_right_order!
|
227
227
|
end
|
228
228
|
end
|
@@ -232,40 +232,40 @@ describe 'parametrised events' do
|
|
232
232
|
|
233
233
|
it 'should transition to specified next state (sleeping to showering)' do
|
234
234
|
pe.wakeup!(:showering)
|
235
|
-
pe.aasm.current_state.
|
235
|
+
expect(pe.aasm.current_state).to eq(:showering)
|
236
236
|
end
|
237
237
|
|
238
238
|
it 'should transition to specified next state (sleeping to working)' do
|
239
239
|
pe.wakeup!(:working)
|
240
|
-
pe.aasm.current_state.
|
240
|
+
expect(pe.aasm.current_state).to eq(:working)
|
241
241
|
end
|
242
242
|
|
243
243
|
it 'should transition to default (first or showering) state' do
|
244
244
|
pe.wakeup!
|
245
|
-
pe.aasm.current_state.
|
245
|
+
expect(pe.aasm.current_state).to eq(:showering)
|
246
246
|
end
|
247
247
|
|
248
248
|
it 'should transition to default state when on_transition invoked' do
|
249
249
|
pe.dress!(nil, 'purple', 'dressy')
|
250
|
-
pe.aasm.current_state.
|
250
|
+
expect(pe.aasm.current_state).to eq(:working)
|
251
251
|
end
|
252
252
|
|
253
253
|
it 'should call on_transition method with args' do
|
254
254
|
pe.wakeup!(:showering)
|
255
|
-
pe.
|
255
|
+
expect(pe).to receive(:wear_clothes).with('blue', 'jeans')
|
256
256
|
pe.dress!(:working, 'blue', 'jeans')
|
257
257
|
end
|
258
258
|
|
259
259
|
it 'should call on_transition proc' do
|
260
260
|
pe.wakeup!(:showering)
|
261
|
-
pe.
|
261
|
+
expect(pe).to receive(:wear_clothes).with('purple', 'slacks')
|
262
262
|
pe.dress!(:dating, 'purple', 'slacks')
|
263
263
|
end
|
264
264
|
|
265
265
|
it 'should call on_transition with an array of methods' do
|
266
266
|
pe.wakeup!(:showering)
|
267
|
-
pe.
|
268
|
-
pe.
|
267
|
+
expect(pe).to receive(:condition_hair)
|
268
|
+
expect(pe).to receive(:fix_hair)
|
269
269
|
pe.dress!(:prettying_up)
|
270
270
|
end
|
271
271
|
end
|
@@ -274,9 +274,9 @@ describe 'event firing without persistence' do
|
|
274
274
|
it 'should attempt to persist if aasm_write_state is defined' do
|
275
275
|
foo = Foo.new
|
276
276
|
def foo.aasm_write_state; end
|
277
|
-
foo.
|
277
|
+
expect(foo).to be_open
|
278
278
|
|
279
|
-
foo.
|
279
|
+
expect(foo).to receive(:aasm_write_state_without_persistence)
|
280
280
|
foo.close
|
281
281
|
end
|
282
282
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "per-transition guards" do
|
4
|
+
let(:guardian) { Guardian.new }
|
5
|
+
|
6
|
+
it "allows the transition if the guard succeeds" do
|
7
|
+
expect { guardian.use_one_guard_that_succeeds! }.to_not raise_error
|
8
|
+
expect(guardian).to be_beta
|
9
|
+
end
|
10
|
+
|
11
|
+
it "stops the transition if the guard fails" do
|
12
|
+
expect { guardian.use_one_guard_that_fails! }.to raise_error(AASM::InvalidTransition)
|
13
|
+
expect(guardian).to be_alpha
|
14
|
+
end
|
15
|
+
|
16
|
+
it "allows the transition if all guards succeeds" do
|
17
|
+
expect { guardian.use_guards_that_succeed! }.to_not raise_error
|
18
|
+
expect(guardian).to be_beta
|
19
|
+
end
|
20
|
+
|
21
|
+
it "stops the transition if the first guard fails" do
|
22
|
+
expect { guardian.use_guards_where_the_first_fails! }.to raise_error(AASM::InvalidTransition)
|
23
|
+
expect(guardian).to be_alpha
|
24
|
+
end
|
25
|
+
|
26
|
+
it "stops the transition if the second guard fails" do
|
27
|
+
expect { guardian.use_guards_where_the_second_fails! }.to raise_error(AASM::InvalidTransition)
|
28
|
+
expect(guardian).to be_alpha
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "event guards" do
|
33
|
+
let(:guardian) { Guardian.new }
|
34
|
+
|
35
|
+
it "allows the transition if the event guards succeed" do
|
36
|
+
expect { guardian.use_event_guards_that_succeed! }.to_not raise_error
|
37
|
+
expect(guardian).to be_beta
|
38
|
+
end
|
39
|
+
|
40
|
+
it "allows the transition if the event and transition guards succeed" do
|
41
|
+
expect { guardian.use_event_and_transition_guards_that_succeed! }.to_not raise_error
|
42
|
+
expect(guardian).to be_beta
|
43
|
+
end
|
44
|
+
|
45
|
+
it "stops the transition if the first event guard fails" do
|
46
|
+
expect { guardian.use_event_guards_where_the_first_fails! }.to raise_error(AASM::InvalidTransition)
|
47
|
+
expect(guardian).to be_alpha
|
48
|
+
end
|
49
|
+
|
50
|
+
it "stops the transition if the second event guard fails" do
|
51
|
+
expect { guardian.use_event_guards_where_the_second_fails! }.to raise_error(AASM::InvalidTransition)
|
52
|
+
expect(guardian).to be_alpha
|
53
|
+
end
|
54
|
+
|
55
|
+
it "stops the transition if the transition guard fails" do
|
56
|
+
expect { guardian.use_event_and_transition_guards_where_third_fails! }.to raise_error(AASM::InvalidTransition)
|
57
|
+
expect(guardian).to be_alpha
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|