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.
- checksums.yaml +7 -0
- data/.github/workflows/build.yml +46 -0
- data/.gitignore +3 -0
- data/.travis.yml +17 -0
- data/Changelog.rdoc +16 -0
- data/Gemfile +7 -0
- data/README.rdoc +154 -99
- data/examples/conversation.rb +5 -5
- data/examples/lamp.rb +5 -5
- data/examples/relationship.rb +8 -8
- data/examples/traffic_light.rb +3 -3
- data/examples/user.rb +8 -4
- data/gemfiles/Gemfile.activerecord-5.2.x +9 -0
- data/gemfiles/Gemfile.activerecord-6.0.x +9 -0
- data/gemfiles/Gemfile.activerecord-6.1.x +8 -0
- data/gemfiles/Gemfile.activerecord-main.x +9 -0
- data/gemfiles/Gemfile.basic +6 -0
- data/lib/simple_state_machine/.DS_Store +0 -0
- data/lib/simple_state_machine/active_record.rb +2 -64
- data/lib/simple_state_machine/decorator/active_record.rb +68 -0
- data/lib/simple_state_machine/decorator/default.rb +91 -0
- data/lib/simple_state_machine/railtie.rb +1 -1
- data/lib/simple_state_machine/simple_state_machine.rb +8 -251
- data/lib/simple_state_machine/state_machine.rb +88 -0
- data/lib/simple_state_machine/state_machine_definition.rb +72 -0
- data/lib/simple_state_machine/tools/graphviz.rb +21 -0
- data/lib/simple_state_machine/tools/inspector.rb +44 -0
- data/lib/simple_state_machine/transition.rb +40 -0
- data/lib/simple_state_machine/version.rb +1 -1
- data/lib/simple_state_machine.rb +13 -3
- data/lib/tasks/graphviz.rake +31 -0
- data/simple_state_machine.gemspec +14 -24
- data/spec/.DS_Store +0 -0
- data/spec/active_record_spec.rb +216 -179
- data/spec/{decorator_spec.rb → decorator/default_spec.rb} +32 -32
- data/spec/examples_spec.rb +17 -17
- data/spec/mountable_spec.rb +26 -14
- data/spec/simple_state_machine_spec.rb +128 -92
- data/spec/spec_helper.rb +18 -5
- data/spec/state_machine_definition_spec.rb +48 -34
- data/spec/state_machine_spec.rb +36 -2
- data/spec/tools/graphviz_spec.rb +30 -0
- data/spec/tools/inspector_spec.rb +70 -0
- metadata +54 -128
- data/autotest/discover.rb +0 -1
- data/lib/tasks/graphiz.rake +0 -13
- data/rails/graphiz.rake +0 -16
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4cf708bed33ca00037b1fbb2562b2a4c351666d82a4555b84078600b9e90ad03
|
4
|
+
data.tar.gz: 251e24c03f5c59313aec334045670ed024c2720630c188d17bfed1eced1827be
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 482b1ba0a9e91260dfe56153453b636640dcd7c37916fd692e13d8607f59ee141d7161c9db6e9a50cdbb14b21b2895227129edf1c4f35dbcb733caceafeb8beb
|
7
|
+
data.tar.gz: 5c3b05eb49e792a4289e85c5be1f10f9239020a0147e0ab5b617bba58d7fac4b727a47f45564c472e25e4079e8d1fb7d52838f9a8af9dd610a90e2580d6a9132
|
@@ -0,0 +1,46 @@
|
|
1
|
+
name: Build
|
2
|
+
on:
|
3
|
+
- push
|
4
|
+
- pull_request
|
5
|
+
|
6
|
+
jobs:
|
7
|
+
build:
|
8
|
+
name: Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }}
|
9
|
+
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
|
10
|
+
strategy:
|
11
|
+
fail-fast: false
|
12
|
+
matrix:
|
13
|
+
ruby:
|
14
|
+
- "3.0"
|
15
|
+
- "2.7"
|
16
|
+
- "2.6"
|
17
|
+
- "2.5"
|
18
|
+
rails:
|
19
|
+
- "5.2"
|
20
|
+
- "6.0"
|
21
|
+
- "6.1"
|
22
|
+
- main
|
23
|
+
exclude:
|
24
|
+
- ruby: 2.5
|
25
|
+
rails: main
|
26
|
+
- ruby: 2.6
|
27
|
+
rails: main
|
28
|
+
- ruby: 3.0
|
29
|
+
rails: "5.1"
|
30
|
+
- ruby: 3.0
|
31
|
+
rails: "5.2"
|
32
|
+
|
33
|
+
runs-on: 'ubuntu-latest'
|
34
|
+
|
35
|
+
env:
|
36
|
+
BUNDLE_GEMFILE: gemfiles/Gemfile.activerecord-${{ matrix.rails }}.x
|
37
|
+
|
38
|
+
steps:
|
39
|
+
- uses: actions/checkout@v2
|
40
|
+
- uses: ruby/setup-ruby@v1
|
41
|
+
with:
|
42
|
+
ruby-version: ${{ matrix.ruby }}
|
43
|
+
- name: Setup project
|
44
|
+
run: bundle install
|
45
|
+
- name: Run test
|
46
|
+
run: bundle exec rspec spec
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.5
|
4
|
+
- 2.7
|
5
|
+
- 3.0
|
6
|
+
- jruby-9.2.16.0
|
7
|
+
script: bundle exec rspec spec
|
8
|
+
|
9
|
+
gemfile:
|
10
|
+
- gemfiles/Gemfile.activerecord-6.0.x
|
11
|
+
- gemfiles/Gemfile.activerecord-6.1.x
|
12
|
+
- gemfiles/Gemfile.basic
|
13
|
+
|
14
|
+
matrix:
|
15
|
+
exclude:
|
16
|
+
- rvm: jruby
|
17
|
+
gemfile: gemfiles/Gemfile.activerecord-4.2.x
|
data/Changelog.rdoc
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
== 0.6.0
|
2
|
+
=== Enhancements
|
3
|
+
- Define decorated methods as #{event_name}_with_managed_state instead of with_managed_state_#{event_name} [Petrik]
|
4
|
+
- Added raised_error method to access caught Errors [Petrik]
|
5
|
+
- Added support for a default error state [Marek de Heus]
|
6
|
+
- Removed transaction support, you can still add it manually, see active_record_spec.rb [Marek de Heus]
|
7
|
+
- Allow catching errors for subclasses of the error [Petrik]
|
8
|
+
- Added gem install instructions [Marek de Heus]
|
9
|
+
- Added define_event method to simplify mountable state machine definitions [Petrik]
|
10
|
+
- Added end_states method so we can check the state machine correctness [Petrik]
|
11
|
+
- Added begin_states method so we can check the state machine correctness [Petrik]
|
12
|
+
|
13
|
+
=== Bugfixes
|
14
|
+
- Fixed bug in Inspector, from state can now be a Error class [Marek de Heus]
|
15
|
+
- Make sure from and end states are uniq [Petrik]
|
16
|
+
- fixed typos (graphiz -> graphviz) (redmar-master) [rjk]
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -1,43 +1,29 @@
|
|
1
1
|
= SimpleStateMachine
|
2
2
|
|
3
|
+
{<img src="https://github.com/mdh/ssm/actions/workflows/build.yml/badge.svg" />}[https://github.com/mdh/ssm/actions?query=workflow%3A.github%2Fworkflows%2Fbuild.yml+branch%3Amaster++]
|
4
|
+
|
3
5
|
A simple DSL to decorate existing methods with state transition guards.
|
4
6
|
|
5
7
|
Instead of using a DSL to define events, SimpleStateMachine decorates methods
|
6
8
|
to help you encapsulate state and guard state transitions.
|
7
9
|
|
8
|
-
It supports exception rescuing, google chart visualization and mountable
|
9
|
-
|
10
|
-
== Basic example
|
11
|
-
|
12
|
-
class LampSwitch
|
13
|
-
|
14
|
-
extend SimpleStateMachine
|
10
|
+
It supports exception rescuing, google chart visualization and mountable state machines.
|
15
11
|
|
16
|
-
|
17
|
-
self.state = 'off'
|
18
|
-
end
|
12
|
+
== Usage
|
19
13
|
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
event :push_switch, :off => :on,
|
24
|
-
:on => :off
|
14
|
+
Define an event and specify how the state should transition. If we want the state to change
|
15
|
+
from *pending* to *active* we write:
|
25
16
|
|
26
|
-
|
27
|
-
|
28
|
-
lamp = LampSwitch.new
|
29
|
-
lamp.state # => 'off'
|
30
|
-
lamp.off? # => true
|
31
|
-
lamp.push_switch # => 'pushed switch'
|
32
|
-
lamp.state # => 'on'
|
33
|
-
lamp.on? # => true
|
34
|
-
lamp.push_switch # => 'pushed switch'
|
35
|
-
lamp.off? # => true
|
17
|
+
event :activate_account, :pending => :active
|
36
18
|
|
19
|
+
That's it. You can now call *activate_account* and the state will automatically change.
|
20
|
+
If the state change is not allowed, a SimpleStateMachine::IllegalStateTransitionError is
|
21
|
+
raised.
|
37
22
|
|
38
|
-
|
23
|
+
=== Methods with arguments
|
39
24
|
|
40
|
-
|
25
|
+
If you want to pass arguments and call other methods before the state transition, define your
|
26
|
+
event as a method.
|
41
27
|
|
42
28
|
def activate_account(activation_code)
|
43
29
|
# call other methods, no need to add these in callbacks
|
@@ -45,119 +31,187 @@ Define your event as a method, arguments are allowed:
|
|
45
31
|
end
|
46
32
|
|
47
33
|
Now mark the method as an event and specify how the state should transition
|
48
|
-
when the method is called.
|
34
|
+
when the method is called.
|
49
35
|
|
50
36
|
event :activate_account, :pending => :active
|
51
37
|
|
52
|
-
That's it!
|
53
|
-
You can now call activate_account and the state will automatically change.
|
54
|
-
If the state change is not allowed, a SimpleStateMachine::IllegalStateTransitionError is raised.
|
55
38
|
|
56
|
-
=== Using ActiveRecord / ActiveModel validations
|
57
|
-
When using ActiveRecord / ActiveModel you can add an error to the errors object.
|
58
|
-
This will prevent the state from being changed.
|
59
|
-
|
60
|
-
def activate_account(activation_code)
|
61
|
-
if activation_code_invalid?(activation_code)
|
62
|
-
errors.add(:activation_code, 'Invalid')
|
63
|
-
end
|
64
|
-
end
|
65
39
|
|
66
|
-
|
40
|
+
== Basic example
|
67
41
|
|
68
|
-
|
69
|
-
|
42
|
+
class LampSwitch
|
43
|
+
|
44
|
+
extend SimpleStateMachine
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
self.state = 'off'
|
48
|
+
end
|
49
|
+
|
50
|
+
event :push_switch, :off => :on,
|
51
|
+
:on => :off
|
70
52
|
|
71
|
-
def download_data
|
72
|
-
Service.download_data
|
73
53
|
end
|
74
|
-
event :download_data, :pending => :downloaded,
|
75
|
-
Service::ConnectionError => :download_failed
|
76
54
|
|
77
|
-
|
78
|
-
state # =>
|
55
|
+
lamp = LampSwitch.new
|
56
|
+
lamp.state # => 'off'
|
57
|
+
lamp.off? # => true
|
58
|
+
lamp.push_switch #
|
59
|
+
lamp.state # => 'on'
|
60
|
+
lamp.on? # => true
|
61
|
+
lamp.push_switch #
|
62
|
+
lamp.off? # => true
|
79
63
|
|
80
|
-
=== Catching all from states
|
81
|
-
If an event should transition from all states you can use :all
|
82
64
|
|
83
|
-
|
65
|
+
== ActiveRecord
|
84
66
|
|
85
|
-
|
67
|
+
For ActiveRecord methods are decorated with state transition guards _and_ persistence.
|
68
|
+
Methods marked as events behave like ActiveRecord *save* and *save!*.
|
86
69
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
70
|
+
=== Example
|
71
|
+
|
72
|
+
To add a state machine to an ActiveRecord class, you will have to:
|
73
|
+
* extend SimpleStateMachine::ActiveRecord,
|
74
|
+
* set the initial state in after_initialize,
|
75
|
+
* turn methods into events
|
91
76
|
|
92
77
|
class User < ActiveRecord::Base
|
93
|
-
|
78
|
+
|
94
79
|
extend SimpleStateMachine::ActiveRecord
|
95
80
|
|
96
|
-
|
97
|
-
self.
|
98
|
-
# if you get an ActiveRecord::MissingAttributeError
|
99
|
-
# you'll probably need to do (http://bit.ly/35q23b):
|
100
|
-
# write_attribute(:ssm_state, "pending") unless read_attribute(:ssm_state)
|
81
|
+
after_initialize do
|
82
|
+
self.state ||= 'pending'
|
101
83
|
end
|
102
|
-
|
84
|
+
|
103
85
|
def invite
|
104
86
|
self.activation_code = Digest::SHA1.hexdigest("salt #{Time.now.to_f}")
|
105
|
-
#send_activation_email
|
106
87
|
end
|
107
88
|
event :invite, :pending => :invited
|
108
|
-
|
109
|
-
def confirm_invitation activation_code
|
110
|
-
if self.activation_code != activation_code
|
111
|
-
errors.add 'activation_code', 'is invalid'
|
112
|
-
end
|
113
|
-
end
|
114
|
-
event :confirm_invitation, :invited => :active
|
115
89
|
end
|
116
90
|
|
117
|
-
|
118
|
-
|
119
|
-
|
91
|
+
user = User.new
|
92
|
+
user.pending? # => true
|
93
|
+
user.invite # => true
|
94
|
+
user.invited? # => true
|
95
|
+
user.activation_code # => 'SOMEDIGEST'
|
120
96
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
- active?
|
97
|
+
For the invite method this generates the following event methods
|
98
|
+
* *invite* (behaves like ActiveRecord save )
|
99
|
+
* *invite!* (behaves like ActiveRecord save!)
|
125
100
|
|
126
101
|
If you want to be more verbose you can also use:
|
127
|
-
|
128
|
-
|
102
|
+
* *invite_and_save* (alias for invite)
|
103
|
+
* *invite_and_save!* (alias for invite!)
|
129
104
|
|
130
|
-
== Mountable Example
|
131
105
|
|
132
|
-
|
106
|
+
=== Using ActiveRecord / ActiveModel validations
|
107
|
+
|
108
|
+
When using ActiveRecord / ActiveModel you can add an error to the errors object.
|
109
|
+
This will prevent the state from being changed.
|
110
|
+
|
111
|
+
If we add an activate_account method to User
|
133
112
|
|
134
|
-
class
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
113
|
+
class User < ActiveRecord::Base
|
114
|
+
...
|
115
|
+
def activate_account(activation_code)
|
116
|
+
if activation_code_invalid?(activation_code)
|
117
|
+
errors.add(:activation_code, 'Invalid')
|
118
|
+
end
|
139
119
|
end
|
120
|
+
event :activate_account, :invited => :confirmed
|
121
|
+
...
|
140
122
|
end
|
141
123
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
124
|
+
user.confirm_invitation!('INVALID') # => raises ActiveRecord::RecordInvalid,
|
125
|
+
# "Validation failed: Activation code is invalid"
|
126
|
+
user.confirmed? # => false
|
127
|
+
user.confirm_invitation!('VALID')
|
128
|
+
user.confirmed? # => true
|
146
129
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
130
|
+
== Mountable StateMachines
|
131
|
+
|
132
|
+
If you like to separate your state machine from your model class, you can do so as following:
|
133
|
+
|
134
|
+
class MyStateMachine < SimpleStateMachine::StateMachineDefinition
|
135
|
+
|
136
|
+
event :invite, :new => :invited
|
137
|
+
event :confirm_invitation, :invited => :active
|
138
|
+
|
139
|
+
def decorator_class
|
140
|
+
SimpleStateMachine::Decorator::Default
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class User < ActiveRecord::Base
|
145
|
+
|
146
|
+
extend SimpleStateMachine::Mountable
|
147
|
+
mount_state_machine MyStateMachine
|
148
|
+
|
149
|
+
after_initialize do
|
150
|
+
self.state ||= 'new'
|
151
151
|
end
|
152
152
|
|
153
|
+
end
|
154
|
+
|
153
155
|
|
154
|
-
==
|
156
|
+
== Transitions
|
155
157
|
|
156
|
-
|
158
|
+
=== Catching all from states
|
159
|
+
If an event should transition from all other defined states, you can use the *:all* state:
|
160
|
+
|
161
|
+
event :suspend, :all => :suspended
|
162
|
+
|
163
|
+
|
164
|
+
=== Catching exceptions
|
165
|
+
|
166
|
+
You can let the state machine handle exceptions by specifying the failure state for an Error:
|
167
|
+
|
168
|
+
def download_data
|
169
|
+
raise Service::ConnectionError, "Uhoh"
|
170
|
+
end
|
171
|
+
event :download_data, Service::ConnectionError => :download_failed
|
157
172
|
|
173
|
+
download_data # catches Service::ConnectionError
|
174
|
+
state # => "download_failed"
|
175
|
+
state_machine.raised_error # "Uhoh"
|
176
|
+
|
177
|
+
|
178
|
+
=== Default error state
|
179
|
+
|
180
|
+
To automatically catch all exceptions to a default error state use default_error_state:
|
181
|
+
|
182
|
+
state_machine_definition.default_error_state = :failed
|
183
|
+
|
184
|
+
== Transactions
|
185
|
+
|
186
|
+
If you want to run events in transactions run them in a transaction block:
|
187
|
+
|
188
|
+
user.transaction { user.invite! }
|
189
|
+
|
190
|
+
== Tools
|
191
|
+
|
192
|
+
===Generating state diagrams
|
193
|
+
|
194
|
+
When using Rails/ActiveRecord you can generate a state diagram of the state machine via the
|
195
|
+
built in rake tasks.
|
196
|
+
For details run:
|
197
|
+
|
198
|
+
rake -T ssm
|
199
|
+
|
200
|
+
A Googlechart example:
|
201
|
+
http://tinyurl.com/79xztr6
|
202
|
+
|
203
|
+
== Installation
|
204
|
+
|
205
|
+
Use gem install:
|
206
|
+
|
207
|
+
gem install simple_state_machine
|
208
|
+
|
209
|
+
Or add it to your Gemfile:
|
210
|
+
|
211
|
+
gem 'simple_state_machine'
|
158
212
|
|
159
213
|
== Note on Patches/Pull Requests
|
160
|
-
|
214
|
+
|
161
215
|
* Fork the project.
|
162
216
|
* Make your feature addition or bug fix.
|
163
217
|
* Add tests for it. This is important so I don't break it in a
|
@@ -166,6 +220,7 @@ If your using rails you get rake tasks for generating a graphiz google chart of
|
|
166
220
|
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
167
221
|
* Send me a pull request. Bonus points for topic branches.
|
168
222
|
|
223
|
+
|
169
224
|
== Copyright
|
170
225
|
|
171
226
|
Copyright (c) 2010 Marek & Petrik. See LICENSE for details.
|
data/examples/conversation.rb
CHANGED
@@ -2,19 +2,19 @@
|
|
2
2
|
#
|
3
3
|
# class Conversation
|
4
4
|
# include AASM
|
5
|
-
#
|
5
|
+
#
|
6
6
|
# aasm_column :current_state # defaults to aasm_state
|
7
|
-
#
|
7
|
+
#
|
8
8
|
# aasm_initial_state :unread
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# aasm_state :unread
|
11
11
|
# aasm_state :read
|
12
12
|
# aasm_state :closed
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# aasm_event :view do
|
15
15
|
# transitions :to => :read, :from => [:unread]
|
16
16
|
# end
|
17
|
-
#
|
17
|
+
#
|
18
18
|
# aasm_event :close do
|
19
19
|
# transitions :to => :closed, :from => [:read, :unread]
|
20
20
|
# end
|
data/examples/lamp.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
class Lamp
|
2
|
-
|
2
|
+
|
3
3
|
extend SimpleStateMachine
|
4
4
|
|
5
5
|
def initialize
|
6
6
|
self.state = 'off'
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def push_button1
|
10
10
|
puts "click1: #{state}"
|
11
11
|
end
|
12
|
-
event :push_button1, :off => :on,
|
12
|
+
event :push_button1, :off => :on,
|
13
13
|
:on => :off
|
14
|
-
|
14
|
+
|
15
15
|
def push_button2
|
16
16
|
puts "click2: #{state}"
|
17
17
|
end
|
18
18
|
event :push_button2, :off => :on,
|
19
19
|
:on => :off
|
20
20
|
|
21
|
-
end
|
21
|
+
end
|
data/examples/relationship.rb
CHANGED
@@ -2,23 +2,23 @@
|
|
2
2
|
#
|
3
3
|
# class Relationship
|
4
4
|
# include AASM
|
5
|
-
#
|
5
|
+
#
|
6
6
|
# aasm_column :status
|
7
|
-
#
|
7
|
+
#
|
8
8
|
# aasm_initial_state Proc.new { |relationship| relationship.strictly_for_fun? ? :intimate : :dating }
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# aasm_state :dating, :enter => :make_happy, :exit => :make_depressed
|
11
11
|
# aasm_state :intimate, :enter => :make_very_happy, :exit => :never_speak_again
|
12
12
|
# aasm_state :married, :enter => :give_up_intimacy, :exit => :buy_exotic_car_and_wear_a_combover
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# aasm_event :get_intimate do
|
15
15
|
# transitions :to => :intimate, :from => [:dating], :guard => :drunk?
|
16
16
|
# end
|
17
|
-
#
|
17
|
+
#
|
18
18
|
# aasm_event :get_married do
|
19
19
|
# transitions :to => :married, :from => [:dating, :intimate], :guard => :willing_to_give_up_manhood?
|
20
20
|
# end
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# def strictly_for_fun?; end
|
23
23
|
# def drunk?; end
|
24
24
|
# def willing_to_give_up_manhood?; end
|
@@ -32,7 +32,7 @@
|
|
32
32
|
|
33
33
|
class Relationship
|
34
34
|
extend SimpleStateMachine
|
35
|
-
|
35
|
+
|
36
36
|
def initialize
|
37
37
|
self.state = relationship.strictly_for_fun? ? get_intimate : start_dating
|
38
38
|
end
|
@@ -84,4 +84,4 @@ class Relationship
|
|
84
84
|
def never_speak_again; end
|
85
85
|
def give_up_intimacy; end
|
86
86
|
def buy_exotic_car_and_wear_a_combover; end
|
87
|
-
end
|
87
|
+
end
|
data/examples/traffic_light.rb
CHANGED
data/examples/user.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
require 'digest/sha1'
|
2
2
|
class User < ActiveRecord::Base
|
3
|
-
|
3
|
+
|
4
4
|
validates_presence_of :name
|
5
|
-
|
5
|
+
|
6
6
|
extend SimpleStateMachine::ActiveRecord
|
7
7
|
|
8
|
+
after_initialize :after_initialize
|
8
9
|
def after_initialize
|
9
10
|
self.state ||= 'new'
|
11
|
+
# if you get an ActiveRecord::MissingAttributeError
|
12
|
+
# you'll probably need to do (http://bit.ly/35q23b):
|
13
|
+
# write_attribute(:ssm_state, "pending") unless read_attribute(:ssm_state)
|
10
14
|
end
|
11
15
|
|
12
16
|
def invite
|
@@ -23,13 +27,13 @@ class User < ActiveRecord::Base
|
|
23
27
|
event :confirm_invitation, :invited => :active
|
24
28
|
|
25
29
|
#event :log_send_activation_code_failed, :new => :send_activation_code_failed
|
26
|
-
#
|
30
|
+
#
|
27
31
|
# def reset_password(new_password)
|
28
32
|
# self.password = new_password
|
29
33
|
# end
|
30
34
|
# # do not change state, but ensure that we are in proper state
|
31
35
|
# event :reset_password, :active => :active
|
32
|
-
|
36
|
+
|
33
37
|
def send_activation_email(code)
|
34
38
|
true
|
35
39
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# A sample Gemfile
|
2
|
+
source "http://rubygems.org"
|
3
|
+
|
4
|
+
group :test do
|
5
|
+
gem "rspec"
|
6
|
+
gem "activerecord", git: "https://github.com/rails/rails.git", branch: "main"
|
7
|
+
gem "sqlite3", :platform => [:ruby, :mswin, :mingw]
|
8
|
+
gem "activerecord-jdbcsqlite3-adapter", :platform => :jruby
|
9
|
+
end
|
Binary file
|