stator 0.3.1 → 0.3.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ea9ce5a04f3d128aecd8f78788c7a06a49d2785dc3669a7c54a2c973fb01f2b
4
- data.tar.gz: c638206a0fb9dedf3e53403d160425054be95b1f08223e6436239f500afb9eb7
3
+ metadata.gz: c4db54f443ba466ae3fc6f79857816b8719e0cbacf0615f8c014ae0c71c32e81
4
+ data.tar.gz: 53d72d27aafa1a340c02d2d5459235488bf5e12df9198312ffe1d5121fc24912
5
5
  SHA512:
6
- metadata.gz: e32e3867650801979aefd648ebc2184e0a9cf9357c0fa08146a963308c35ec610b3c5ac4a9439149acfa086b98957dd1006484310b60ab0fb18fad56a26bfe51
7
- data.tar.gz: b8fa99e1f0e0bdf6a3832395d9de0730ee3a990994509c2dc8b30aa6f5ee06ca1af3114b4f9c00899cb83b15515cca142841a521ee4f0532bc863ba4d03532fe
6
+ metadata.gz: a2e76eb8e8bbe0e1dab6d8fbdbb6516f12cff56c26c3d10d974d4aadce08fbcabdcd49dbba710d0f904a1a04ea4c489125b18475a119de5e625a9cbb6e9406a4
7
+ data.tar.gz: 0fef985f07d95f3d8801f4e3534f478feb516fea223532e1e562cc85bebdc54c09222ee0087517d4c3f396f4d094ad80a9404f7e0cb69829ffa6c5ddc40bb297
data/Gemfile CHANGED
@@ -5,6 +5,6 @@ gem 'activerecord', '5.2.3'
5
5
 
6
6
  gemspec
7
7
 
8
+ gem 'activerecord-nulldb-adapter', '~> 0.4.0', require: false
8
9
  gem 'rake'
9
- gem 'activerecord-nulldb-adapter', '~> 0.4.0', :require => false, :git => 'git@github.com:nulldb/nulldb.git'
10
10
  gem 'rspec'
@@ -7,6 +7,9 @@ module Stator
7
7
  delegate :transitions, to: :@machine
8
8
  delegate :namespace, to: :@machine
9
9
 
10
+ attr_reader :skip_validations
11
+ attr_reader :skip_transition_tracking
12
+
10
13
  def initialize(machine, record)
11
14
  @machine = machine
12
15
  @record = record
@@ -38,7 +41,7 @@ module Stator
38
41
 
39
42
  def validate_transition
40
43
  return unless state_changed?
41
- return if @machine.skip_validations
44
+ return if skip_validations
42
45
 
43
46
  was = state_was
44
47
  is = state
@@ -60,7 +63,7 @@ module Stator
60
63
  end
61
64
 
62
65
  def track_transition
63
- return if @machine.skip_transition_tracking
66
+ return if skip_transition_tracking
64
67
 
65
68
  attempt_to_track_state(state)
66
69
  attempt_to_track_state_changed_timestamp
@@ -115,6 +118,22 @@ module Stator
115
118
  @machine.states.reverse.detect { |s| in_state_at?(s, t) }
116
119
  end
117
120
 
121
+ def without_validation
122
+ was = @skip_validations
123
+ @skip_validations = true
124
+ yield @record
125
+ ensure
126
+ @skip_validations = was
127
+ end
128
+
129
+ def without_transition_tracking
130
+ was = @skip_transition_tracking
131
+ @skip_transition_tracking = true
132
+ yield @record
133
+ ensure
134
+ @skip_transition_tracking = was
135
+ end
136
+
118
137
  protected
119
138
 
120
139
  def attempt_to_track_state(state_to_track)
@@ -7,16 +7,14 @@ module Stator
7
7
  attr_reader :transitions
8
8
  attr_reader :states
9
9
  attr_reader :namespace
10
- attr_reader :skip_validations
11
- attr_reader :skip_transition_tracking
12
-
13
10
 
14
11
  def initialize(klass, options = {})
15
- @class_name = klass.name
16
- @field = options[:field] || :state
17
- @namespace = options[:namespace] || nil
12
+ @class_name = klass.name
13
+ @field = options[:field] || :state
14
+ @namespace = options[:namespace]
18
15
 
19
- @initial_state = options[:initial] && options[:initial].to_s
16
+ @initial_state = options[:initial] && options[:initial].to_s
17
+ @tracking_enabled = options[:track] || false
20
18
 
21
19
  @transitions = []
22
20
  @aliases = []
@@ -26,7 +24,6 @@ module Stator
26
24
  @states = [@initial_state].compact
27
25
 
28
26
  @options = options
29
-
30
27
  end
31
28
 
32
29
  def integration(record)
@@ -38,7 +35,6 @@ module Stator
38
35
  end
39
36
 
40
37
  def transition(name, &block)
41
-
42
38
  t = ::Stator::Transition.new(@class_name, name, @namespace)
43
39
  t.instance_eval(&block) if block_given?
44
40
 
@@ -66,6 +62,10 @@ module Stator
66
62
  end
67
63
  end
68
64
 
65
+ def tracking_enabled?
66
+ @tracking_enabled
67
+ end
68
+
69
69
  def conditional(*states, &block)
70
70
  _namespace = @namespace
71
71
 
@@ -88,22 +88,6 @@ module Stator
88
88
  @class_name.constantize
89
89
  end
90
90
 
91
- def without_validation
92
- was = @skip_validations
93
- @skip_validations = true
94
- yield
95
- ensure
96
- @skip_validations = was
97
- end
98
-
99
- def without_transition_tracking
100
- was = @skip_transition_tracking
101
- @skip_transition_tracking = true
102
- yield
103
- ensure
104
- @skip_transition_tracking = was
105
- end
106
-
107
91
  protected
108
92
 
109
93
  def verify_transition_validity(transition)
data/lib/stator/model.rb CHANGED
@@ -35,26 +35,25 @@ module Stator
35
35
 
36
36
  def self.included(base)
37
37
  base.class_eval do
38
- before_save :_stator_track_transition, prepend: true
38
+ before_save :_stator_maybe_track_transition, prepend: true
39
39
  end
40
40
  end
41
41
 
42
42
  def in_state_at?(state, t, namespace = '')
43
- machine = self._stator(namespace)
44
- machine.integration(self).in_state_at?(state, t)
43
+ _integration(namespace).in_state_at?(state, t)
45
44
  end
46
45
 
47
46
  def likely_state_at(t, namespace = '')
48
- machine = self._stator(namespace)
49
- machine.integration(self).likely_state_at(t)
47
+ _integration(namespace).likely_state_at(t)
50
48
  end
51
49
 
52
50
  protected
53
51
 
54
-
55
- def _stator_track_transition
52
+ def _stator_maybe_track_transition
56
53
  self._stators.each do |namespace, machine|
57
- machine.integration(self).track_transition
54
+ next unless machine.tracking_enabled?
55
+
56
+ _integration(namespace).track_transition
58
57
  end
59
58
 
60
59
  true
@@ -70,23 +69,28 @@ module Stator
70
69
  end
71
70
  end
72
71
 
72
+ def initialize_dup(other)
73
+ @_integrations = {}
74
+ super
75
+ end
76
+
73
77
  def without_state_transition_validations(namespace = '')
74
- self._stator(namespace).without_validation do
75
- yield
78
+ _integration(namespace).without_validation do
79
+ yield self
76
80
  end
77
81
  end
78
82
 
79
83
  def without_state_transition_tracking(namespace = '')
80
- self._stator(namespace).without_transition_tracking do
81
- yield
84
+ _integration(namespace).without_transition_tracking do
85
+ yield self
82
86
  end
83
87
  end
84
88
 
85
89
  protected
86
90
 
87
91
  def _stator_validate_transition
88
- self._stators.each do |namespace, machine|
89
- machine.integration(self).validate_transition
92
+ self._stators.each_key do |namespace|
93
+ _integration(namespace).validate_transition
90
94
  end
91
95
  end
92
96
 
@@ -94,6 +98,12 @@ module Stator
94
98
  self.class._stator(namespace)
95
99
  end
96
100
 
101
+ def _integration(namespace = '')
102
+ @_integrations ||= {}
103
+ @_integrations[namespace] ||= _stator(namespace).integration(self)
104
+ @_integrations[namespace]
105
+ end
106
+
97
107
  end
98
108
  end
99
109
  end
@@ -86,7 +86,7 @@ module Stator
86
86
  def generate_methods
87
87
  klass.class_eval <<-EV, __FILE__, __LINE__ + 1
88
88
  def #{@full_name}(should_save = true)
89
- integration = self._stator(#{@namespace.inspect}).integration(self)
89
+ integration = _integration(#{@namespace.to_s.inspect})
90
90
 
91
91
  unless can_#{@full_name}?
92
92
  integration.invalid_transition!(integration.state, #{@to.inspect}) if should_save
@@ -98,7 +98,7 @@ module Stator
98
98
  end
99
99
 
100
100
  def #{@full_name}!
101
- integration = self._stator(#{@namespace.inspect}).integration(self)
101
+ integration = _integration(#{@namespace.to_s.inspect})
102
102
 
103
103
  unless can_#{@full_name}?
104
104
  integration.invalid_transition!(integration.state, #{@to.inspect})
@@ -110,10 +110,10 @@ module Stator
110
110
  end
111
111
 
112
112
  def can_#{@full_name}?
113
- machine = self._stator(#{@namespace.inspect})
114
- return true if machine.skip_validations
113
+ integration = _integration(#{@namespace.to_s.inspect})
114
+ return true if integration.skip_validations
115
115
 
116
- integration = machine.integration(self)
116
+ machine = self._stator(#{@namespace.to_s.inspect})
117
117
  transition = machine.transitions.detect{|t| t.full_name.to_s == #{@full_name.inspect}.to_s }
118
118
  transition.can?(integration.state)
119
119
  end
@@ -4,7 +4,7 @@ module Stator
4
4
 
5
5
  MAJOR = 0
6
6
  MINOR = 3
7
- PATCH = 1
7
+ PATCH = 3
8
8
  PRERELEASE = nil
9
9
 
10
10
  VERSION = [MAJOR, MINOR, PATCH, PRERELEASE].compact.join(".")
data/spec/model_spec.rb CHANGED
@@ -160,6 +160,42 @@ describe Stator::Model do
160
160
  u.activated_state_at.should_not be_nil
161
161
  end
162
162
 
163
+ it "should skip tracking timestamps if opted out of with thread safety" do
164
+ threads = []
165
+ skip = User.new(email: "skip@example.com")
166
+ nope = User.new(email: "nope@example.com")
167
+
168
+ threads << Thread.new do
169
+ sleep 0.5
170
+ nope.semiactivate!
171
+ end
172
+ threads << Thread.new do
173
+ skip.without_state_transition_tracking do
174
+ sleep 1
175
+ skip.semiactivate!
176
+ end
177
+ end
178
+
179
+ threads.each(&:join)
180
+
181
+ nope.semiactivated_state_at.should_not be_nil
182
+ skip.semiactivated_state_at.should be_nil
183
+ end
184
+
185
+ it "should not inherit _integration cache on dup" do
186
+ u = User.new(email: "user@example.com")
187
+ u.save!
188
+
189
+ u_duped = u.dup
190
+
191
+ u.semiactivate!
192
+
193
+ u_duped_integration = u_duped.send(:_integration)
194
+
195
+ u_duped_integration.state.should_not eql(u.state)
196
+ u_duped_integration.instance_values["record"].should eq(u_duped)
197
+ end
198
+
163
199
  describe "helper methods" do
164
200
  it "should answer the question of whether the state is currently the one invoked" do
165
201
  a = Animal.new
@@ -254,6 +290,21 @@ describe Stator::Model do
254
290
  u.state.should eql("semiactivated")
255
291
  u.semiactivated_state_at.should eql(t)
256
292
  end
293
+
294
+ it "should allow opting into track by namespace" do
295
+ z = ZooKeeper.new(name: "Doug")
296
+ z.employment_state.should eql("hired")
297
+ z.employment_fire!
298
+ z.fired_employment_state_at.should_not be_nil
299
+
300
+ z.employment_hire!
301
+ z.hired_employment_state_at.should_not be_nil
302
+
303
+ z.working_start!
304
+ z.started_working_state_at.should be_nil
305
+ z.working_end!
306
+ z.ended_working_state_at.should be_nil
307
+ end
257
308
  end
258
309
 
259
310
  describe "aliasing" do
@@ -134,6 +134,33 @@ class Zoo < ActiveRecord::Base
134
134
  end
135
135
  end
136
136
 
137
+ class ZooKeeper < ActiveRecord::Base
138
+ extend Stator::Model
139
+
140
+ stator namespace: 'employment', field: 'employment_state', track: true do
141
+ transition :hire do
142
+ from nil, :fired
143
+ to :hired
144
+ end
145
+
146
+ transition :fire do
147
+ from :hired
148
+ to :fired
149
+ end
150
+ end
151
+
152
+ stator namespace: 'working', field: 'working_state', track: false do
153
+ transition :start do
154
+ from nil, :ended
155
+ to :started
156
+ end
157
+
158
+ transition :end do
159
+ from :started
160
+ to :ended
161
+ end
162
+ end
163
+ end
137
164
 
138
165
  class Farm < ActiveRecord::Base
139
166
  extend Stator::Model
@@ -22,6 +22,16 @@ ActiveRecord::Schema.define(:version => 20130628161227) do
22
22
  t.datetime "born_status_at"
23
23
  end
24
24
 
25
+ create_table "zoo_keepers", :force => true do |t|
26
+ t.string "name"
27
+ t.string "employment_state", :default => 'hired'
28
+ t.datetime "hired_employment_state_at"
29
+ t.datetime "fired_employment_state_at"
30
+ t.string "working_state"
31
+ t.datetime "started_working_state_at"
32
+ t.datetime "ended_working_state_at"
33
+ end
34
+
25
35
  create_table "zoos", :force => true do |t|
26
36
  t.string "name"
27
37
  t.string "state", :default => 'closed'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Nelson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-22 00:00:00.000000000 Z
11
+ date: 2021-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -75,8 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
75
  - !ruby/object:Gem::Version
76
76
  version: '0'
77
77
  requirements: []
78
- rubyforge_project:
79
- rubygems_version: 2.7.7
78
+ rubygems_version: 3.0.6
80
79
  signing_key:
81
80
  specification_version: 4
82
81
  summary: The simplest of ActiveRecord state machines