steady_state 1.0.0 → 1.1.0
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 +4 -4
- data/Rakefile +2 -0
- data/lib/steady_state/attribute/state.rb +2 -0
- data/lib/steady_state/attribute/state_machine.rb +2 -0
- data/lib/steady_state/attribute/transition_validator.rb +3 -1
- data/lib/steady_state/attribute.rb +10 -8
- data/lib/steady_state/version.rb +3 -1
- data/lib/steady_state.rb +2 -0
- metadata +9 -13
- data/spec/spec_helper.rb +0 -1
- data/spec/steady_state/attribute_spec.rb +0 -403
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02e5f7eb48f3ada429e0d38598535c77489ab57172d920c2dbb6083a19dc5466
|
4
|
+
data.tar.gz: bbc957eeed78c83b1a7e2c49395df741ba34eea99d3d4adaabdbbabe1298bb06
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2f28a60fae258f08f3bb57925419c86015d4dc5d738667fffe8e943223fe3603a0a90960653ff2b1d171dac4f077e50b113b4f5b5024da5cb5c8f58a950a096
|
7
|
+
data.tar.gz: 3fd2023e60bb8ff68c5ea40dcc4a263184fe0698ff3d3507dd7b02ecacf8417bcb9e9b0f44c1d789513994ac5010211b1c50716cdedc7f4eb39f21213a481f5f
|
data/Rakefile
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SteadyState
|
2
4
|
module Attribute
|
3
5
|
class TransitionValidator < ActiveModel::EachValidator
|
4
6
|
def validate_each(obj, attr_name, _value)
|
5
|
-
obj.errors.add(attr_name, :invalid) if obj.instance_variable_defined?("@last_valid_#{attr_name}")
|
7
|
+
obj.errors.add(attr_name, :invalid) if obj.instance_variable_defined?(:"@last_valid_#{attr_name}")
|
6
8
|
end
|
7
9
|
end
|
8
10
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'steady_state/attribute/state'
|
2
4
|
require 'steady_state/attribute/state_machine'
|
3
5
|
require 'steady_state/attribute/transition_validator'
|
@@ -17,26 +19,26 @@ module SteadyState
|
|
17
19
|
overrides = Module.new do
|
18
20
|
define_method :"validate_#{attr_name}_transition_to" do |next_value|
|
19
21
|
if public_send(attr_name).may_become?(next_value)
|
20
|
-
remove_instance_variable("@last_valid_#{attr_name}") if instance_variable_defined?("@last_valid_#{attr_name}")
|
21
|
-
elsif !instance_variable_defined?("@last_valid_#{attr_name}")
|
22
|
-
instance_variable_set("@last_valid_#{attr_name}", public_send(attr_name))
|
22
|
+
remove_instance_variable(:"@last_valid_#{attr_name}") if instance_variable_defined?(:"@last_valid_#{attr_name}")
|
23
|
+
elsif !instance_variable_defined?(:"@last_valid_#{attr_name}")
|
24
|
+
instance_variable_set(:"@last_valid_#{attr_name}", public_send(attr_name))
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
28
|
define_method :"#{attr_name}=" do |value|
|
27
|
-
unless instance_variable_defined?("@#{attr_name}_state_initialized")
|
28
|
-
instance_variable_set("@#{attr_name}_state_initialized", true)
|
29
|
+
unless instance_variable_defined?(:"@#{attr_name}_state_initialized")
|
30
|
+
instance_variable_set(:"@#{attr_name}_state_initialized", true)
|
29
31
|
end
|
30
32
|
public_send(:"validate_#{attr_name}_transition_to", value) if public_send(attr_name).present?
|
31
33
|
super(value)
|
32
34
|
end
|
33
35
|
|
34
36
|
define_method :"#{attr_name}" do |*args, &blk|
|
35
|
-
unless instance_variable_defined?("@#{attr_name}_state_initialized")
|
37
|
+
unless instance_variable_defined?(:"@#{attr_name}_state_initialized")
|
36
38
|
public_send(:"#{attr_name}=", state_machines[attr_name].start) if super(*args, &blk).blank?
|
37
|
-
instance_variable_set("@#{attr_name}_state_initialized", true)
|
39
|
+
instance_variable_set(:"@#{attr_name}_state_initialized", true)
|
38
40
|
end
|
39
|
-
last_valid_value = instance_variable_get("@last_valid_#{attr_name}") if instance_variable_defined?("@last_valid_#{attr_name}")
|
41
|
+
last_valid_value = instance_variable_get(:"@last_valid_#{attr_name}") if instance_variable_defined?(:"@last_valid_#{attr_name}")
|
40
42
|
state_machines[attr_name].new_state super(*args, &blk), last_valid_value
|
41
43
|
end
|
42
44
|
end
|
data/lib/steady_state/version.rb
CHANGED
data/lib/steady_state.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: steady_state
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Griffith
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -113,13 +113,11 @@ files:
|
|
113
113
|
- lib/steady_state/attribute/state_machine.rb
|
114
114
|
- lib/steady_state/attribute/transition_validator.rb
|
115
115
|
- lib/steady_state/version.rb
|
116
|
-
|
117
|
-
- spec/steady_state/attribute_spec.rb
|
118
|
-
homepage:
|
116
|
+
homepage:
|
119
117
|
licenses: []
|
120
118
|
metadata:
|
121
119
|
rubygems_mfa_required: 'true'
|
122
|
-
post_install_message:
|
120
|
+
post_install_message:
|
123
121
|
rdoc_options: []
|
124
122
|
require_paths:
|
125
123
|
- lib
|
@@ -127,17 +125,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
127
125
|
requirements:
|
128
126
|
- - ">="
|
129
127
|
- !ruby/object:Gem::Version
|
130
|
-
version:
|
128
|
+
version: '3.0'
|
131
129
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
130
|
requirements:
|
133
131
|
- - ">="
|
134
132
|
- !ruby/object:Gem::Version
|
135
133
|
version: '0'
|
136
134
|
requirements: []
|
137
|
-
rubygems_version: 3.
|
138
|
-
signing_key:
|
135
|
+
rubygems_version: 3.5.1
|
136
|
+
signing_key:
|
139
137
|
specification_version: 4
|
140
138
|
summary: Minimalist state management via "an enum with guard rails"
|
141
|
-
test_files:
|
142
|
-
- spec/spec_helper.rb
|
143
|
-
- spec/steady_state/attribute_spec.rb
|
139
|
+
test_files: []
|
data/spec/spec_helper.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'steady_state'
|
@@ -1,403 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe SteadyState::Attribute do
|
4
|
-
let(:steady_state_class) do
|
5
|
-
Class.new do
|
6
|
-
include ActiveModel::Model
|
7
|
-
include SteadyState
|
8
|
-
|
9
|
-
def self.model_name
|
10
|
-
ActiveModel::Name.new(self, nil, 'steady_state_class')
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
subject { steady_state_class.new }
|
15
|
-
|
16
|
-
shared_examples 'a basic state machine' do
|
17
|
-
it 'starts on initial state' do
|
18
|
-
expect(subject.state).to eq 'solid'
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'allows initialization to the initial state' do
|
22
|
-
expect(steady_state_class.new(state: 'solid')).to be_valid
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'allows initialization to other states' do
|
26
|
-
expect(steady_state_class.new(state: 'plasma')).to be_valid
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'adds validation errors when initializing to an invalid state' do
|
30
|
-
object = steady_state_class.new(state: 'banana')
|
31
|
-
expect(object).not_to be_valid
|
32
|
-
expect(object.errors[:state]).to match_array(['is not included in the list'])
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'allows valid transitions' do
|
36
|
-
expect(subject.state.may_become?('liquid')).to eq true
|
37
|
-
expect(subject.state.next_values).to match_array(['liquid'])
|
38
|
-
expect(subject.state.previous_values).to match_array([])
|
39
|
-
expect { subject.state = 'liquid' }.to change { subject.state }.from('solid').to('liquid')
|
40
|
-
expect(subject).to be_valid
|
41
|
-
|
42
|
-
expect(subject.state.may_become?('gas')).to eq true
|
43
|
-
expect(subject.state.next_values).to match_array(['gas'])
|
44
|
-
expect(subject.state.previous_values).to match_array(['solid'])
|
45
|
-
expect { subject.state = 'gas' }.to change { subject.state }.from('liquid').to('gas')
|
46
|
-
expect(subject).to be_valid
|
47
|
-
|
48
|
-
expect(subject.state.may_become?('plasma')).to eq true
|
49
|
-
expect(subject.state.next_values).to match_array(['plasma'])
|
50
|
-
expect(subject.state.previous_values).to match_array(['liquid'])
|
51
|
-
expect { subject.state = 'plasma' }.to change { subject.state }.from('gas').to('plasma')
|
52
|
-
expect(subject).to be_valid
|
53
|
-
expect(subject.state.next_values).to be_empty
|
54
|
-
expect(subject.state.previous_values).to match_array(['gas'])
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'adds validation errors for invalid transitions' do
|
58
|
-
expect(subject.state.may_become?('gas')).to eq false
|
59
|
-
expect { subject.state = 'gas' }.to change { subject.state }.from('solid').to('gas')
|
60
|
-
expect(subject).not_to be_valid
|
61
|
-
expect(subject.errors[:state]).to match_array(['is invalid'])
|
62
|
-
expect(subject.state.next_values).to match_array(['liquid'])
|
63
|
-
expect(subject.state.previous_values).to match_array([])
|
64
|
-
|
65
|
-
expect(subject.state.may_become?('plasma')).to eq false
|
66
|
-
expect { subject.state = 'plasma' }.to change { subject.state }.from('gas').to('plasma')
|
67
|
-
expect(subject).not_to be_valid
|
68
|
-
expect(subject.errors[:state]).to match_array(['is invalid'])
|
69
|
-
expect(subject.state.next_values).to match_array(['liquid'])
|
70
|
-
expect(subject.state.previous_values).to match_array([])
|
71
|
-
|
72
|
-
expect(subject.state.may_become?('solid')).to eq false
|
73
|
-
expect { subject.state = 'solid' }.to change { subject.state }.from('plasma').to('solid')
|
74
|
-
expect(subject).not_to be_valid
|
75
|
-
expect(subject.errors[:state]).to match_array(['is invalid'])
|
76
|
-
expect(subject.state.next_values).to match_array(['liquid'])
|
77
|
-
expect(subject.state.previous_values).to match_array([])
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
context 'with a single field and nothing fancy' do
|
82
|
-
before do
|
83
|
-
steady_state_class.module_eval do
|
84
|
-
attr_accessor :state
|
85
|
-
|
86
|
-
steady_state :state do
|
87
|
-
state 'solid', default: true
|
88
|
-
state 'liquid', from: 'solid'
|
89
|
-
state 'gas', from: 'liquid'
|
90
|
-
state 'plasma', from: 'gas'
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
it_behaves_like 'a basic state machine'
|
96
|
-
|
97
|
-
context 'with inheritance' do
|
98
|
-
let(:subclass) do
|
99
|
-
Class.new(steady_state_class) do
|
100
|
-
def initialize
|
101
|
-
# I do my own thing.
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
subject { subclass.new }
|
106
|
-
|
107
|
-
it_behaves_like 'a basic state machine'
|
108
|
-
end
|
109
|
-
|
110
|
-
context 'with an existing state value' do
|
111
|
-
before do
|
112
|
-
steady_state_class.module_eval do
|
113
|
-
def state
|
114
|
-
@state ||= 'liquid'
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
it 'starts on existing state' do
|
120
|
-
expect(subject.state).to eq 'liquid'
|
121
|
-
end
|
122
|
-
|
123
|
-
it 'does not allow initialization to an invalid next state' do
|
124
|
-
object = steady_state_class.new(state: 'solid')
|
125
|
-
expect(object).not_to be_valid
|
126
|
-
expect(object.errors[:state]).to match_array(['is invalid'])
|
127
|
-
end
|
128
|
-
|
129
|
-
it 'allows initialization to a valid next state' do
|
130
|
-
expect(steady_state_class.new(state: 'gas')).to be_valid
|
131
|
-
end
|
132
|
-
|
133
|
-
it 'adds validation errors when initializing to an invalid state' do
|
134
|
-
object = steady_state_class.new(state: 'banana')
|
135
|
-
expect(object).not_to be_valid
|
136
|
-
expect(object.errors[:state]).to match_array(['is invalid', 'is not included in the list'])
|
137
|
-
end
|
138
|
-
|
139
|
-
it 'allows valid transitions' do
|
140
|
-
expect(subject).to be_valid
|
141
|
-
expect(subject.state.may_become?('gas')).to eq true
|
142
|
-
expect(subject.state.next_values).to match_array(['gas'])
|
143
|
-
expect(subject.state.previous_values).to match_array(['solid'])
|
144
|
-
expect { subject.state = 'gas' }.to change { subject.state }.from('liquid').to('gas')
|
145
|
-
expect(subject).to be_valid
|
146
|
-
|
147
|
-
expect(subject.state.may_become?('plasma')).to eq true
|
148
|
-
expect(subject.state.next_values).to match_array(['plasma'])
|
149
|
-
expect(subject.state.previous_values).to match_array(['liquid'])
|
150
|
-
expect { subject.state = 'plasma' }.to change { subject.state }.from('gas').to('plasma')
|
151
|
-
expect(subject).to be_valid
|
152
|
-
expect(subject.state.next_values).to be_empty
|
153
|
-
expect(subject.state.previous_values).to match_array(['gas'])
|
154
|
-
end
|
155
|
-
|
156
|
-
it 'adds validation errors for invalid transitions' do
|
157
|
-
expect(subject.state.may_become?('plasma')).to eq false
|
158
|
-
expect { subject.state = 'plasma' }.to change { subject.state }.from('liquid').to('plasma')
|
159
|
-
expect(subject).not_to be_valid
|
160
|
-
expect(subject.errors[:state]).to match_array(['is invalid'])
|
161
|
-
expect(subject.state.next_values).to match_array(['gas'])
|
162
|
-
expect(subject.state.previous_values).to match_array(['solid'])
|
163
|
-
|
164
|
-
expect(subject.state.may_become?('solid')).to eq false
|
165
|
-
expect { subject.state = 'solid' }.to change { subject.state }.from('plasma').to('solid')
|
166
|
-
expect(subject).not_to be_valid
|
167
|
-
expect(subject.errors[:state]).to match_array(['is invalid'])
|
168
|
-
expect(subject.state.next_values).to match_array(['gas'])
|
169
|
-
expect(subject.state.previous_values).to match_array(['solid'])
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
context 'with a field reachable by multiple states' do
|
175
|
-
before do
|
176
|
-
steady_state_class.module_eval do
|
177
|
-
attr_accessor :step
|
178
|
-
|
179
|
-
steady_state :step do
|
180
|
-
state 'step-1', default: true
|
181
|
-
state 'step-2', from: 'step-1'
|
182
|
-
state 'cancelled', from: %w(step-1 step-2)
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
it 'allows transition from first state' do
|
188
|
-
expect(subject.step.may_become?('step-1')).to eq false
|
189
|
-
expect(subject.step.may_become?('step-2')).to eq true
|
190
|
-
expect(subject.step.may_become?('cancelled')).to eq true
|
191
|
-
expect(subject.step.next_values).to match_array(%w(cancelled step-2))
|
192
|
-
expect(subject.step.previous_values).to match_array([])
|
193
|
-
expect { subject.step = 'cancelled' }.to change { subject.step }.from('step-1').to('cancelled')
|
194
|
-
expect(subject.step.next_values).to match_array([])
|
195
|
-
expect(subject.step.previous_values).to match_array(%w(step-1 step-2))
|
196
|
-
expect(subject).to be_valid
|
197
|
-
end
|
198
|
-
|
199
|
-
it 'allows transition from second state' do
|
200
|
-
expect(subject.step.may_become?('step-1')).to eq false
|
201
|
-
expect(subject.step.may_become?('step-2')).to eq true
|
202
|
-
expect(subject.step.may_become?('cancelled')).to eq true
|
203
|
-
expect(subject.step.next_values).to match_array(%w(cancelled step-2))
|
204
|
-
expect(subject.step.previous_values).to match_array([])
|
205
|
-
expect { subject.step = 'step-2' }.to change { subject.step }.from('step-1').to('step-2')
|
206
|
-
expect(subject).to be_valid
|
207
|
-
|
208
|
-
expect(subject.step.may_become?('step-1')).to eq false
|
209
|
-
expect(subject.step.may_become?('step-2')).to eq false
|
210
|
-
expect(subject.step.may_become?('cancelled')).to eq true
|
211
|
-
expect(subject.step.next_values).to match_array(['cancelled'])
|
212
|
-
expect(subject.step.previous_values).to match_array(['step-1'])
|
213
|
-
expect { subject.step = 'cancelled' }.to change { subject.step }.from('step-2').to('cancelled')
|
214
|
-
expect(subject.step.next_values).to match_array([])
|
215
|
-
expect(subject.step.previous_values).to match_array(%w(step-1 step-2))
|
216
|
-
expect(subject).to be_valid
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
context 'with the predicates option' do
|
221
|
-
before do
|
222
|
-
options = opts
|
223
|
-
steady_state_class.module_eval do
|
224
|
-
attr_accessor :door
|
225
|
-
|
226
|
-
steady_state :door, **options do
|
227
|
-
state 'open', default: true
|
228
|
-
state 'closed', from: 'open'
|
229
|
-
state 'locked', from: 'closed'
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
context 'default' do
|
235
|
-
let(:opts) { {} }
|
236
|
-
|
237
|
-
it 'defines a predicate method for each state' do
|
238
|
-
expect(subject).to respond_to(:open?)
|
239
|
-
expect(subject).to respond_to(:closed?)
|
240
|
-
expect(subject).to respond_to(:locked?)
|
241
|
-
|
242
|
-
expect(subject.open?).to eq true
|
243
|
-
expect(subject.closed?).to eq false
|
244
|
-
expect(subject.locked?).to eq false
|
245
|
-
|
246
|
-
subject.door = 'closed'
|
247
|
-
expect(subject.open?).to eq false
|
248
|
-
expect(subject.closed?).to eq true
|
249
|
-
expect(subject.locked?).to eq false
|
250
|
-
|
251
|
-
subject.door = 'locked'
|
252
|
-
expect(subject.open?).to eq false
|
253
|
-
expect(subject.closed?).to eq false
|
254
|
-
expect(subject.locked?).to eq true
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
context 'enabled' do
|
259
|
-
let(:opts) { { predicates: true } }
|
260
|
-
|
261
|
-
it 'defines a predicate method for each state' do
|
262
|
-
expect(subject).to respond_to(:open?)
|
263
|
-
expect(subject).to respond_to(:closed?)
|
264
|
-
expect(subject).to respond_to(:locked?)
|
265
|
-
|
266
|
-
expect(subject.open?).to eq true
|
267
|
-
expect(subject.closed?).to eq false
|
268
|
-
expect(subject.locked?).to eq false
|
269
|
-
|
270
|
-
subject.door = 'closed'
|
271
|
-
expect(subject.open?).to eq false
|
272
|
-
expect(subject.closed?).to eq true
|
273
|
-
expect(subject.locked?).to eq false
|
274
|
-
|
275
|
-
subject.door = 'locked'
|
276
|
-
expect(subject.open?).to eq false
|
277
|
-
expect(subject.closed?).to eq false
|
278
|
-
expect(subject.locked?).to eq true
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
context 'disabled' do
|
283
|
-
let(:opts) { { predicates: false } }
|
284
|
-
|
285
|
-
it 'does not define predicate methods' do
|
286
|
-
expect(subject).not_to respond_to(:open?)
|
287
|
-
expect(subject).not_to respond_to(:closed?)
|
288
|
-
expect(subject).not_to respond_to(:locked?)
|
289
|
-
end
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
context 'with the states_getter option' do
|
294
|
-
let(:query_object) { double(where: []) } # rubocop:disable RSpec/VerifiedDoubles
|
295
|
-
|
296
|
-
before do
|
297
|
-
options = opts
|
298
|
-
steady_state_class.module_eval do
|
299
|
-
attr_accessor :car
|
300
|
-
|
301
|
-
steady_state :car, **options do
|
302
|
-
state 'driving', default: true
|
303
|
-
state 'stopped', from: 'driving'
|
304
|
-
state 'parked', from: 'stopped'
|
305
|
-
end
|
306
|
-
end
|
307
|
-
end
|
308
|
-
|
309
|
-
context 'default' do
|
310
|
-
let(:opts) { {} }
|
311
|
-
|
312
|
-
it 'defines states getter method' do
|
313
|
-
expect(steady_state_class.cars).to eq %w(driving stopped parked)
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
context 'disabled' do
|
318
|
-
let(:opts) { { states_getter: false } }
|
319
|
-
|
320
|
-
it 'does not define states getter method' do
|
321
|
-
expect { steady_state_class.cars }.to raise_error(NoMethodError, /undefined method `cars'/)
|
322
|
-
end
|
323
|
-
end
|
324
|
-
end
|
325
|
-
|
326
|
-
context 'with the scopes option' do
|
327
|
-
let(:query_object) { double(where: []) } # rubocop:disable RSpec/VerifiedDoubles
|
328
|
-
|
329
|
-
before do
|
330
|
-
options = opts
|
331
|
-
steady_state_class.module_eval do
|
332
|
-
attr_accessor :car
|
333
|
-
|
334
|
-
def self.defined_scopes
|
335
|
-
@defined_scopes ||= {}
|
336
|
-
end
|
337
|
-
|
338
|
-
def self.scope(name, callable)
|
339
|
-
defined_scopes[name] ||= callable
|
340
|
-
end
|
341
|
-
|
342
|
-
steady_state :car, **options do
|
343
|
-
state 'driving', default: true
|
344
|
-
state 'stopped', from: 'driving'
|
345
|
-
state 'parked', from: 'stopped'
|
346
|
-
end
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
context 'default' do
|
351
|
-
let(:opts) { {} }
|
352
|
-
|
353
|
-
it 'does not define scope methods' do
|
354
|
-
expect(steady_state_class.defined_scopes.keys).to eq []
|
355
|
-
end
|
356
|
-
|
357
|
-
context 'on an ActiveRecord' do
|
358
|
-
let(:steady_state_class) do
|
359
|
-
stub_const('ActiveRecord::Base', Class.new)
|
360
|
-
|
361
|
-
Class.new(ActiveRecord::Base) do
|
362
|
-
include ActiveModel::Model
|
363
|
-
include SteadyState
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
it 'defines a scope for each state' do
|
368
|
-
expect(steady_state_class.defined_scopes.keys).to eq %i(driving stopped parked)
|
369
|
-
|
370
|
-
expect(query_object).to receive(:where).with(car: 'driving')
|
371
|
-
query_object.instance_exec(&steady_state_class.defined_scopes[:driving])
|
372
|
-
expect(query_object).to receive(:where).with(car: 'stopped')
|
373
|
-
query_object.instance_exec(&steady_state_class.defined_scopes[:stopped])
|
374
|
-
expect(query_object).to receive(:where).with(car: 'parked')
|
375
|
-
query_object.instance_exec(&steady_state_class.defined_scopes[:parked])
|
376
|
-
end
|
377
|
-
end
|
378
|
-
end
|
379
|
-
|
380
|
-
context 'enabled' do
|
381
|
-
let(:opts) { { scopes: true } }
|
382
|
-
|
383
|
-
it 'defines a scope for each state' do
|
384
|
-
expect(steady_state_class.defined_scopes.keys).to eq %i(driving stopped parked)
|
385
|
-
|
386
|
-
expect(query_object).to receive(:where).with(car: 'driving')
|
387
|
-
query_object.instance_exec(&steady_state_class.defined_scopes[:driving])
|
388
|
-
expect(query_object).to receive(:where).with(car: 'stopped')
|
389
|
-
query_object.instance_exec(&steady_state_class.defined_scopes[:stopped])
|
390
|
-
expect(query_object).to receive(:where).with(car: 'parked')
|
391
|
-
query_object.instance_exec(&steady_state_class.defined_scopes[:parked])
|
392
|
-
end
|
393
|
-
end
|
394
|
-
|
395
|
-
context 'disabled' do
|
396
|
-
let(:opts) { { scopes: false } }
|
397
|
-
|
398
|
-
it 'does not define scope methods' do
|
399
|
-
expect(steady_state_class.defined_scopes.keys).to eq []
|
400
|
-
end
|
401
|
-
end
|
402
|
-
end
|
403
|
-
end
|