stator 0.9.0.beta → 0.9.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.
data/lib/stator/model.rb CHANGED
@@ -1,118 +1,113 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Stator
4
2
  module Model
5
- extend ActiveSupport::Concern
6
3
 
7
- included do
8
- class_attribute :_stators
9
- attr_accessor :_stator_integrations
4
+ def stator(options = {}, &block)
5
+
6
+ class_attribute :_stators unless respond_to?(:_stators)
10
7
 
11
- validate :_stator_validate_transition
8
+ include InstanceMethods unless self.included_modules.include?(InstanceMethods)
9
+ include TrackerMethods if options[:track] == true
12
10
 
13
11
  self._stators ||= {}
14
12
 
15
- before_save :_stator_maybe_track_transition, prepend: true
13
+ unless self.abstract_class?
14
+ f = options[:field] || :state
15
+ # rescue nil since the table may not exist yet.
16
+ initial = self.columns_hash[f.to_s].default rescue nil
17
+ options = options.merge(initial: initial) if initial
18
+ end
19
+
20
+ machine = (self._stators[options[:namespace].to_s] ||= ::Stator::Machine.new(self, options))
21
+
22
+ if block_given?
23
+ machine.instance_eval(&block)
24
+ machine.evaluate
25
+ end
26
+
27
+ machine
16
28
  end
17
29
 
18
- class_methods do
19
- def stator(namespace: nil, field: :state, initial: nil, track: true, &block)
20
- unless abstract_class?
21
- # Discover the default value (usually initial) from the table...
22
- # but rescue nil since the table may not exist yet.
23
- initial = _determine_initial_stator_state(field)
24
- end
30
+ def _stator(namespace)
31
+ self._stators[namespace.to_s]
32
+ end
25
33
 
26
- opts = { namespace: _stator_namespace(namespace), field: field.to_sym, initial: initial, track: track }
34
+ module TrackerMethods
27
35
 
28
- Stator::Machine.find_or_create(self, **opts).tap do |machine|
29
- machine.evaluate_dsl(&block) if block_given?
36
+ def self.included(base)
37
+ base.class_eval do
38
+ before_save :_stator_maybe_track_transition, prepend: true
30
39
  end
31
40
  end
32
41
 
33
- def _stator(namespace)
34
- self._stators[_stator_namespace(namespace)]
42
+ def in_state_at?(state, t, namespace = '')
43
+ _integration(namespace).in_state_at?(state, t)
35
44
  end
36
45
 
37
- def _stator_namespace(namespace = nil)
38
- namespace = nil if namespace.blank?
39
-
40
- (namespace || Stator.default_namespace).to_sym
46
+ def likely_state_at(t, namespace = '')
47
+ _integration(namespace).likely_state_at(t)
41
48
  end
42
49
 
43
- def _determine_initial_stator_state(field)
44
- columns_hash[field.to_s].default.to_sym
45
- rescue StandardError
46
- nil
50
+ def state_by?(state, t, namespace = '')
51
+ _integration(namespace).state_by?(state, t)
47
52
  end
48
- end
49
53
 
50
- def initialize_dup(other)
51
- @_stator_integrations = {}
52
- super
53
- end
54
+ protected
54
55
 
55
- def without_state_transition_validations(namespace = '')
56
- _stator_integration(namespace).without_validation do
57
- yield self
58
- end
59
- end
56
+ def _stator_maybe_track_transition
57
+ self._stators.each do |namespace, machine|
58
+ next unless machine.tracking_enabled?
60
59
 
61
- def without_state_transition_tracking(namespace = '')
62
- _stator_integration(namespace).without_transition_tracking do
63
- yield self
64
- end
65
- end
66
-
67
- def current_state
68
- _stator_integration.state&.to_sym
69
- end
60
+ _integration(namespace).track_transition
61
+ end
70
62
 
71
- def in_state_at?(state, t, namespace = '')
72
- _stator_integration(namespace).in_state_at?(state, t)
73
- end
63
+ true
64
+ end
74
65
 
75
- def likely_state_at(t, namespace = '')
76
- _stator_integration(namespace).likely_state_at(t)
77
66
  end
78
67
 
79
- def state_by?(state, t, namespace = '')
80
- _stator_integration(namespace).state_by?(state, t)
81
- end
68
+ module InstanceMethods
82
69
 
83
- private
70
+ def self.included(base)
71
+ base.class_eval do
72
+ validate :_stator_validate_transition
73
+ end
74
+ end
84
75
 
85
- # core methods
86
- def _stator(namespace = nil)
87
- self.class._stator(namespace)
88
- end
76
+ def initialize_dup(other)
77
+ @_integrations = {}
78
+ super
79
+ end
89
80
 
90
- def _stator_namespace(namespace = nil)
91
- self.class._stator_namespace(namespace)
92
- end
81
+ def without_state_transition_validations(namespace = '')
82
+ _integration(namespace).without_validation do
83
+ yield self
84
+ end
85
+ end
93
86
 
94
- def _stator_integration(namespace = nil)
95
- ns = _stator_namespace(namespace)
87
+ def without_state_transition_tracking(namespace = '')
88
+ _integration(namespace).without_transition_tracking do
89
+ yield self
90
+ end
91
+ end
96
92
 
97
- self._stator_integrations ||= {}
98
- self._stator_integrations[ns] ||= self.class._stator(ns).integration(self)
99
- end
93
+ protected
100
94
 
101
- # validation/transitional
102
- def _stator_validate_transition
103
- self._stators.each_key do |namespace|
104
- _stator_integration(namespace).validate_transition
95
+ def _stator_validate_transition
96
+ self._stators.each_key do |namespace|
97
+ _integration(namespace).validate_transition
98
+ end
105
99
  end
106
- end
107
100
 
108
- def _stator_maybe_track_transition
109
- self._stators.each do |namespace, machine|
110
- next unless machine.tracking_enabled?
101
+ def _stator(namespace = '')
102
+ self.class._stator(namespace)
103
+ end
111
104
 
112
- _stator_integration(namespace).track_transition
105
+ def _integration(namespace = '')
106
+ @_integrations ||= {}
107
+ @_integrations[namespace] ||= _stator(namespace).integration(self)
108
+ @_integrations[namespace]
113
109
  end
114
110
 
115
- true
116
111
  end
117
112
  end
118
113
  end
@@ -1,39 +1,44 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Stator
4
2
  class Transition
5
- attr_reader :namespace, :name, :attr_name, :from_states, :to_state, :class_name, :callbacks
3
+
4
+ ANY = '__any__'
5
+
6
+ attr_reader :name
7
+ attr_reader :full_name
6
8
 
7
9
  def initialize(class_name, name, namespace = nil)
8
- @class_name = class_name
9
- @name = name&.to_sym
10
- @namespace = namespace&.to_sym
11
- @from_states = []
12
- @to_state = nil
13
- @callbacks = {}
10
+ @class_name = class_name
11
+ @name = name
12
+ @namespace = namespace
13
+ @full_name = [@namespace, @name].compact.join('_') if @name
14
+ @froms = []
15
+ @to = nil
16
+ @callbacks = {}
14
17
  end
15
18
 
16
- def attr_name
17
- @attr_name ||= generate_attr_name
19
+ def from(*froms)
20
+ @froms |= froms.map{|f| f.try(:to_s) } # nils are ok
18
21
  end
19
22
 
20
- def from_states(*new_froms)
21
- @from_states |= new_froms
23
+ def to(to)
24
+ @to = to.to_s
22
25
  end
23
- alias from from_states
24
26
 
25
- def to(new_to)
26
- @to_state = new_to
27
+ def to_state
28
+ @to
27
29
  end
28
30
 
29
- def can?(current_state)
30
- from_states.include?(current_state) || from_states.include?(Stator::ANY) || current_state == Stator::ANY
31
+ def from_states
32
+ @froms
31
33
  end
32
34
 
33
- def valid?(from_check, to_check)
34
- from_check = from_check&.to_sym # coming from the database, i suspect
35
+ def can?(current_state)
36
+ @froms.include?(current_state) || @froms.include?(ANY) || current_state == ANY
37
+ end
35
38
 
36
- can?(from_check) && (to_check == to_state || to_check == ANY || to_state == ANY)
39
+ def valid?(from, to)
40
+ can?(from) &&
41
+ (@to == to || @to == ANY || to == ANY)
37
42
  end
38
43
 
39
44
  def conditional(options = {}, &block)
@@ -41,85 +46,79 @@ module Stator
41
46
  end
42
47
 
43
48
  def any
44
- Stator::ANY
49
+ ANY
45
50
  end
46
51
 
47
52
  def evaluate
48
- generate_methods if attr_name.present?
53
+ generate_methods unless @full_name.blank?
49
54
  end
50
55
 
51
- private
56
+ protected
52
57
 
53
58
  def klass
54
- class_name.constantize
55
- end
56
-
57
- def generate_attr_name
58
- if namespace == Stator.default_namespace
59
- name
60
- else
61
- [namespace, name].compact.join('_').to_sym
62
- end
59
+ @class_name.constantize
63
60
  end
64
61
 
65
62
  def callbacks(kind)
66
- callbacks[kind] || []
63
+ @callbacks[kind] || []
67
64
  end
68
65
 
69
66
  def conditional_block(options = {})
70
67
  options[:use_previous] ||= false
71
68
 
72
- _namespace = namespace
73
- _froms = from_states
74
- _to = to_state
75
-
76
- proc do
77
- integration = self.class._stator(_namespace).integration(self)
78
-
79
- integration.state_changed?(options[:use_previous]) &&
80
- _froms.include?(integration.state_was(options[:use_previous])) ||
81
- _froms.include?(Stator::ANY) &&
82
- integration.state == _to || _to == Stator::ANY
69
+ _namespace = @namespace
70
+ _froms = @froms
71
+ _to = @to
72
+
73
+ Proc.new do
74
+ (
75
+ self._stator(_namespace).integration(self).state_changed?(options[:use_previous])
76
+ ) && (
77
+ _froms.include?(self._stator(_namespace).integration(self).state_was(options[:use_previous])) ||
78
+ _froms.include?(::Stator::Transition::ANY)
79
+ ) && (
80
+ self._stator(_namespace).integration(self).state == _to ||
81
+ _to == ::Stator::Transition::ANY
82
+ )
83
83
  end
84
84
  end
85
85
 
86
86
  def generate_methods
87
87
  klass.class_eval <<-EV, __FILE__, __LINE__ + 1
88
- def #{attr_name}(should_save = true)
89
- integration = _stator_integration(:#{namespace})
88
+ def #{@full_name}(should_save = true)
89
+ integration = _integration(#{@namespace.to_s.inspect})
90
90
 
91
- unless can_#{attr_name}?
92
- integration.invalid_transition!(integration.state, :#{to_state}) if should_save
91
+ unless can_#{@full_name}?
92
+ integration.invalid_transition!(integration.state, #{@to.inspect}) if should_save
93
93
  return false
94
94
  end
95
95
 
96
- integration.state = :#{to_state}
97
-
96
+ integration.state = #{@to.inspect}
98
97
  self.save if should_save
99
98
  end
100
99
 
101
- def #{attr_name}!
102
- integration = _stator_integration(:#{namespace})
100
+ def #{@full_name}!
101
+ integration = _integration(#{@namespace.to_s.inspect})
103
102
 
104
- unless can_#{attr_name}?
105
- integration.invalid_transition!(integration.state, :#{to_state})
103
+ unless can_#{@full_name}?
104
+ integration.invalid_transition!(integration.state, #{@to.inspect})
106
105
  raise ActiveRecord::RecordInvalid.new(self)
107
106
  end
108
107
 
109
- integration.state = :#{to_state}
108
+ integration.state = #{@to.inspect}
110
109
  self.save!
111
110
  end
112
111
 
113
- def can_#{attr_name}?
114
- integration = _stator_integration(:#{namespace})
112
+ def can_#{@full_name}?
113
+ integration = _integration(#{@namespace.to_s.inspect})
115
114
  return true if integration.skip_validations
116
115
 
117
- machine = self._stator(:#{namespace})
118
- transition = machine.transitions.detect { |t| t.attr_name == :#{attr_name} }
119
-
116
+ machine = self._stator(#{@namespace.to_s.inspect})
117
+ transition = machine.transitions.detect{|t| t.full_name.to_s == #{@full_name.inspect}.to_s }
120
118
  transition.can?(integration.state)
121
119
  end
122
120
  EV
123
121
  end
122
+
124
123
  end
125
124
  end
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stator
4
+
4
5
  MAJOR = 0
5
6
  MINOR = 9
6
7
  PATCH = 0
7
- PRERELEASE = "beta"
8
+ PRERELEASE = nil
9
+
10
+ VERSION = [MAJOR, MINOR, PATCH, PRERELEASE].compact.join(".")
8
11
 
9
- VERSION = [MAJOR, MINOR, PATCH, PRERELEASE].compact.join('.')
10
12
  end
data/lib/stator.rb CHANGED
@@ -1,19 +1,6 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'stator/version'
4
2
  require 'stator/alias'
5
3
  require 'stator/integration'
6
4
  require 'stator/machine'
7
5
  require 'stator/model'
8
6
  require 'stator/transition'
9
-
10
- require 'active_support/concern'
11
- require 'debug'
12
-
13
- module Stator
14
- ANY = :__ANY__
15
-
16
- def self.default_namespace
17
- ENV.fetch('STATOR_NAMESPACE', :default).to_sym
18
- end
19
- end
data/stator.gemspec CHANGED
@@ -10,13 +10,24 @@ Gem::Specification.new do |gem|
10
10
  gem.email = ["mike@mikeonrails.com"]
11
11
  gem.description = %q{The simplest of ActiveRecord state machines. Intended to be lightweight and minimalistic.}
12
12
  gem.summary = %q{The simplest of ActiveRecord state machines}
13
- gem.homepage = "https://www.github.com/mnelson/stator"
13
+ gem.homepage = "https://github.com/guideline-tech/stator"
14
+ gem.license = "MIT"
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ gem.files = Dir["lib/**/*"] + Dir["*.gemspec"]
14
18
 
15
- gem.files = `git ls-files`.split($/)
16
19
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
20
  gem.require_paths = ["lib"]
19
21
 
20
- gem.add_dependency 'activerecord'
21
- gem.add_dependency 'activesupport'
22
+ gem.metadata["allowed_push_host"] = "https://rubygems.org"
23
+ gem.metadata["rubygems_mfa_required"] = "true"
24
+
25
+ gem.add_dependency 'base64'
26
+ gem.add_dependency 'benchmark'
27
+ gem.add_dependency 'bigdecimal'
28
+ gem.add_dependency 'logger'
29
+ gem.add_dependency 'mutex_m'
30
+ gem.add_dependency 'activerecord', ">= 6.0"
31
+
32
+ gem.required_ruby_version = ">= 3.2.0"
22
33
  end
metadata CHANGED
@@ -1,17 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0.beta
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Nelson
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-07-26 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: activerecord
13
+ name: base64
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: benchmark
15
28
  requirement: !ruby/object:Gem::Requirement
16
29
  requirements:
17
30
  - - ">="
@@ -25,7 +38,7 @@ dependencies:
25
38
  - !ruby/object:Gem::Version
26
39
  version: '0'
27
40
  - !ruby/object:Gem::Dependency
28
- name: activesupport
41
+ name: bigdecimal
29
42
  requirement: !ruby/object:Gem::Requirement
30
43
  requirements:
31
44
  - - ">="
@@ -38,6 +51,48 @@ dependencies:
38
51
  - - ">="
39
52
  - !ruby/object:Gem::Version
40
53
  version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: logger
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: mutex_m
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: activerecord
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '6.0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '6.0'
41
96
  description: The simplest of ActiveRecord state machines. Intended to be lightweight
42
97
  and minimalistic.
43
98
  email:
@@ -46,28 +101,6 @@ executables: []
46
101
  extensions: []
47
102
  extra_rdoc_files: []
48
103
  files:
49
- - ".github/workflows/build.yml"
50
- - ".gitignore"
51
- - ".rspec"
52
- - ".ruby-gemset"
53
- - ".ruby-version"
54
- - Appraisals
55
- - Gemfile
56
- - LICENSE.txt
57
- - README.md
58
- - Rakefile
59
- - gemfiles/activerecord_5.1.gemfile
60
- - gemfiles/activerecord_5.1.gemfile.lock
61
- - gemfiles/activerecord_5.2.gemfile
62
- - gemfiles/activerecord_5.2.gemfile.lock
63
- - gemfiles/activerecord_5.gemfile
64
- - gemfiles/activerecord_5.gemfile.lock
65
- - gemfiles/activerecord_6.0.gemfile
66
- - gemfiles/activerecord_6.0.gemfile.lock
67
- - gemfiles/activerecord_6.1.gemfile
68
- - gemfiles/activerecord_6.1.gemfile.lock
69
- - gemfiles/activerecord_7.0.gemfile
70
- - gemfiles/activerecord_7.0.gemfile.lock
71
104
  - lib/stator.rb
72
105
  - lib/stator/alias.rb
73
106
  - lib/stator/integration.rb
@@ -75,15 +108,13 @@ files:
75
108
  - lib/stator/model.rb
76
109
  - lib/stator/transition.rb
77
110
  - lib/stator/version.rb
78
- - spec/model_spec.rb
79
- - spec/spec_helper.rb
80
- - spec/support/models.rb
81
- - spec/support/schema.rb
82
111
  - stator.gemspec
83
- homepage: https://www.github.com/mnelson/stator
84
- licenses: []
85
- metadata: {}
86
- post_install_message:
112
+ homepage: https://github.com/guideline-tech/stator
113
+ licenses:
114
+ - MIT
115
+ metadata:
116
+ allowed_push_host: https://rubygems.org
117
+ rubygems_mfa_required: 'true'
87
118
  rdoc_options: []
88
119
  require_paths:
89
120
  - lib
@@ -91,19 +122,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
91
122
  requirements:
92
123
  - - ">="
93
124
  - !ruby/object:Gem::Version
94
- version: '0'
125
+ version: 3.2.0
95
126
  required_rubygems_version: !ruby/object:Gem::Requirement
96
127
  requirements:
97
- - - ">"
128
+ - - ">="
98
129
  - !ruby/object:Gem::Version
99
- version: 1.3.1
130
+ version: '0'
100
131
  requirements: []
101
- rubygems_version: 3.1.6
102
- signing_key:
132
+ rubygems_version: 3.6.8
103
133
  specification_version: 4
104
134
  summary: The simplest of ActiveRecord state machines
105
- test_files:
106
- - spec/model_spec.rb
107
- - spec/spec_helper.rb
108
- - spec/support/models.rb
109
- - spec/support/schema.rb
135
+ test_files: []
@@ -1,28 +0,0 @@
1
- name: build
2
- on:
3
- pull_request:
4
- push:
5
- branches:
6
- - master
7
- jobs:
8
- build:
9
- runs-on: ubuntu-latest
10
- continue-on-error: ${{ matrix.experimental }}
11
- strategy:
12
- fail-fast: false
13
- matrix:
14
- ruby-version: [2.7.5, 2.7.6]
15
- experimental: [false]
16
- include:
17
- - ruby-version: 3.0
18
- experimental: true
19
- - ruby-version: 3.1
20
- experimental: true
21
- steps:
22
- - uses: actions/checkout@v2
23
- - uses: ruby/setup-ruby@v1
24
- with:
25
- ruby-version: ${{ matrix.ruby-version }}
26
- bundler-cache: true # runs `bundle install` and caches installed gems automatically
27
- - run: bundle exec appraisal install
28
- - run: bundle exec appraisal rspec
data/.gitignore DELETED
@@ -1,18 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
18
- .envrc
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --color
2
- --format documentation
data/.ruby-gemset DELETED
@@ -1 +0,0 @@
1
- stator
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 2.7.5