estate 0.1.0 → 0.1.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 +4 -4
- data/README.md +2 -0
- data/lib/estate/configuration.rb +1 -1
- data/lib/estate/constants/orm.rb +10 -0
- data/lib/estate/estate.rb +14 -7
- data/lib/estate/logic/active_record/setup.rb +19 -0
- data/lib/estate/logic/active_record/specific_logic.rb +30 -0
- data/lib/estate/logic/common_logic.rb +26 -0
- data/lib/estate/logic/core.rb +19 -0
- data/lib/estate/logic/sequel/setup.rb +21 -0
- data/lib/estate/logic/sequel/specific_logic.rb +26 -0
- data/lib/estate/setup.rb +17 -0
- data/lib/estate/state_machine.rb +10 -14
- data/lib/estate/version.rb +1 -1
- data/lib/estate.rb +3 -1
- metadata +10 -5
- data/lib/estate/core.rb +0 -17
- data/lib/estate/db/active_record.rb +0 -47
- data/lib/estate/db/sequel.rb +0 -45
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0777265b6deae44f96eb4fbc8f150bb73b1f2d98849107b9750eeb0ac025ca49
|
|
4
|
+
data.tar.gz: 916c2a835c05ed793d87e25883cb6999ebf87687c260f39eba90e994cf0aa228
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 37e595f2f4451386df31a2b563af980037c90f91786760c9c88f9090418ffd3b640473bc5c2d16a1826c3646302d2041b151a0807d106e1dbc6c85dc126bbfbd
|
|
7
|
+
data.tar.gz: f939381283ace662a7d97169cc7e0bf26ad8d05a78eb40585f456e84768bda4474c1ae0501d5f7d232eb401d577341d2ce09f4fa3b02137adca8019e87c65114
|
data/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
1
3
|
# Estate Gem
|
|
2
4
|
|
|
3
5
|
Estate is a Ruby gem designed to simplify state management in both ActiveRecord and Sequel models. The primary focus of this gem is to provide a straightforward way to define states and transitions using a clean syntax.
|
data/lib/estate/configuration.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Estate
|
|
4
4
|
module Configuration
|
|
5
5
|
class << self
|
|
6
|
-
def init_config(column_name
|
|
6
|
+
def init_config(column_name, allow_empty_initial_state, raise_on_error)
|
|
7
7
|
@column_name = column_name
|
|
8
8
|
@allow_empty_initial_state = allow_empty_initial_state
|
|
9
9
|
@raise_on_error = raise_on_error
|
data/lib/estate/estate.rb
CHANGED
|
@@ -6,20 +6,21 @@ module Estate
|
|
|
6
6
|
|
|
7
7
|
Estate::Requirements.check_requirements(base)
|
|
8
8
|
Estate::StateMachine.create_store
|
|
9
|
-
Estate::
|
|
9
|
+
Estate::Setup.call(base)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
module ClassMethods
|
|
13
13
|
def estate(column: Estate::Configuration::Defaults::COLUMN_NAME,
|
|
14
14
|
empty_initial_state: Estate::Configuration::Defaults::ALLOW_EMPTY_INITIAL_STATE,
|
|
15
15
|
raise_on_error: Estate::Configuration::Defaults::RAISE_ON_ERROR)
|
|
16
|
-
Estate::Configuration.init_config(
|
|
17
|
-
raise_on_error: raise_on_error)
|
|
16
|
+
Estate::Configuration.init_config(column, empty_initial_state, raise_on_error)
|
|
18
17
|
|
|
19
18
|
yield if block_given?
|
|
20
19
|
end
|
|
21
20
|
|
|
22
|
-
def state(name)
|
|
21
|
+
def state(name = nil)
|
|
22
|
+
raise(StandardError, 'state must be a Symbol or a String') unless Estate::StateMachine.argument_valid?(name)
|
|
23
|
+
|
|
23
24
|
if Estate::StateMachine.state_exists?(name)
|
|
24
25
|
raise(StandardError, "state `:#{name}` is already defined")
|
|
25
26
|
else
|
|
@@ -27,16 +28,22 @@ module Estate
|
|
|
27
28
|
end
|
|
28
29
|
end
|
|
29
30
|
|
|
30
|
-
def transition(from
|
|
31
|
+
def transition(from: nil, to: nil)
|
|
32
|
+
unless Estate::StateMachine.argument_valid?(from)
|
|
33
|
+
raise(StandardError, 'argument `from` must be a Symbol or a String')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
raise(StandardError, 'argument `to` must be a Symbol or a String') unless Estate::StateMachine.argument_valid?(to)
|
|
37
|
+
|
|
31
38
|
raise(StandardError, "state `#{from}` is not defined") unless Estate::StateMachine.state_exists?(from)
|
|
32
39
|
|
|
33
40
|
raise(StandardError, "state `#{to}` is not defined") unless Estate::StateMachine.state_exists?(to)
|
|
34
41
|
|
|
35
|
-
if Estate::StateMachine.transition_exists?(from
|
|
42
|
+
if Estate::StateMachine.transition_exists?(from, to)
|
|
36
43
|
raise(StandardError, "`transition from: :#{from}, to: :#{to}` already defined")
|
|
37
44
|
end
|
|
38
45
|
|
|
39
|
-
Estate::StateMachine.register_transition(from
|
|
46
|
+
Estate::StateMachine.register_transition(from, to)
|
|
40
47
|
end
|
|
41
48
|
end
|
|
42
49
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Estate
|
|
4
|
+
module Logic
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module Setup
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def call(base)
|
|
10
|
+
base.class_eval do
|
|
11
|
+
public_send(:before_validation) do
|
|
12
|
+
Estate::Logic::Core.call(Estate::Constants::Orm::ACTIVE_RECORD, self)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'estate/logic/common_logic'
|
|
4
|
+
|
|
5
|
+
module Estate
|
|
6
|
+
module Logic
|
|
7
|
+
module ActiveRecord
|
|
8
|
+
module SpecificLogic
|
|
9
|
+
extend Estate::Logic::CommonLogic
|
|
10
|
+
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def add_error(instance, message, attribute: :base)
|
|
14
|
+
if Estate::Configuration.raise_on_error
|
|
15
|
+
exception_message = attribute == :base ? message : "#{attribute}: #{message}"
|
|
16
|
+
raise(StandardError, exception_message)
|
|
17
|
+
else
|
|
18
|
+
instance.errors.add(attribute, message) unless instance.errors[attribute].include?(message)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get_states(instance)
|
|
23
|
+
from_state = instance.public_send("#{Estate::Configuration.column_name}_was")
|
|
24
|
+
to_state = instance.public_send(Estate::Configuration.column_name)
|
|
25
|
+
[from_state, to_state]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Estate
|
|
4
|
+
module Logic
|
|
5
|
+
module CommonLogic
|
|
6
|
+
def validate_state_changes(instance, from_state, to_state)
|
|
7
|
+
if from_state == to_state
|
|
8
|
+
if from_state.nil? && !Estate::Configuration.allow_empty_initial_state
|
|
9
|
+
add_error(instance, "empty `#{Estate::Configuration.column_name}` is not allowed")
|
|
10
|
+
end
|
|
11
|
+
elsif to_state.nil?
|
|
12
|
+
add_error(instance, 'transition to empty state is not allowed')
|
|
13
|
+
elsif !Estate::StateMachine.state_exists?(to_state)
|
|
14
|
+
add_error(instance, "state `#{to_state}` is not defined")
|
|
15
|
+
elsif !transition_allowed?(from_state, to_state)
|
|
16
|
+
add_error(instance, "transition from `#{from_state}` to `#{to_state}` is not allowed",
|
|
17
|
+
attribute: Estate::Configuration.column_name)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def transition_allowed?(from_state, to_state)
|
|
22
|
+
from_state.nil? || Estate::StateMachine.transition_exists?(from_state, to_state)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Estate
|
|
4
|
+
module Logic
|
|
5
|
+
module Core
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def call(orm, instance)
|
|
9
|
+
require 'estate/logic/common_logic'
|
|
10
|
+
require File.join(File.dirname(__FILE__), orm, 'specific_logic')
|
|
11
|
+
|
|
12
|
+
extend Estate::Logic::CommonLogic
|
|
13
|
+
extend "Estate::Logic::#{orm.classify}::SpecificLogic".safe_constantize
|
|
14
|
+
|
|
15
|
+
validate_state_changes(instance, *get_states(instance))
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Estate
|
|
4
|
+
module Logic
|
|
5
|
+
module Sequel
|
|
6
|
+
module Setup
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def call(base)
|
|
10
|
+
base.class_eval do
|
|
11
|
+
def validate
|
|
12
|
+
super
|
|
13
|
+
|
|
14
|
+
Estate::Logic::Core.call(Estate::Constants::Orm::SEQUEL, self)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'estate/logic/common_logic'
|
|
4
|
+
|
|
5
|
+
module Estate
|
|
6
|
+
module Logic
|
|
7
|
+
module Sequel
|
|
8
|
+
module SpecificLogic
|
|
9
|
+
extend Estate::Logic::CommonLogic
|
|
10
|
+
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
# TODO: remove :base
|
|
14
|
+
def add_error(instance, message, attribute: :base)
|
|
15
|
+
instance.errors.add(attribute, message)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def get_states(instance)
|
|
19
|
+
from_state, = instance.column_change(Estate::Configuration.column_name)
|
|
20
|
+
to_state = instance.values[Estate::Configuration.column_name]
|
|
21
|
+
[from_state, to_state]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/estate/setup.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Estate
|
|
4
|
+
module Setup
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
def call(base)
|
|
8
|
+
if base.ancestors.map(&:to_s).include? 'ActiveRecord::Base'
|
|
9
|
+
require File.join(File.dirname(__FILE__), 'logic', 'active_record', 'setup')
|
|
10
|
+
Estate::Logic::ActiveRecord::Setup.call(base)
|
|
11
|
+
else
|
|
12
|
+
require File.join(File.dirname(__FILE__), 'logic', 'sequel', 'setup')
|
|
13
|
+
Estate::Logic::Sequel::Setup.call(base)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/estate/state_machine.rb
CHANGED
|
@@ -9,31 +9,27 @@ module Estate
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def state_exists?(state)
|
|
12
|
-
|
|
12
|
+
states.key?(state.to_sym)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def register_state(state)
|
|
16
|
-
|
|
17
|
-
when Symbol
|
|
18
|
-
states[state] = nil
|
|
19
|
-
when String
|
|
20
|
-
states[state.to_sym] = nil
|
|
21
|
-
else
|
|
22
|
-
raise(ArgumentError, 'State must be a Symbol or a String')
|
|
23
|
-
end
|
|
16
|
+
states[state.to_sym] = nil
|
|
24
17
|
end
|
|
25
18
|
|
|
26
|
-
def transition_exists?(
|
|
27
|
-
transition_key = { from:
|
|
19
|
+
def transition_exists?(from_state, to_state)
|
|
20
|
+
transition_key = { from: from_state.to_sym, to: to_state.to_sym }
|
|
28
21
|
transitions.key?(transition_key)
|
|
29
22
|
end
|
|
30
23
|
|
|
31
|
-
def register_transition(
|
|
32
|
-
|
|
33
|
-
transition_key = { from: from.to_sym, to: to.to_sym }
|
|
24
|
+
def register_transition(from_state, to_state)
|
|
25
|
+
transition_key = { from: from_state.to_sym, to: to_state.to_sym }
|
|
34
26
|
transitions[transition_key] = nil
|
|
35
27
|
end
|
|
36
28
|
|
|
29
|
+
def argument_valid?(argument)
|
|
30
|
+
argument.is_a?(Symbol) || argument.is_a?(String)
|
|
31
|
+
end
|
|
32
|
+
|
|
37
33
|
attr_reader :states, :transitions
|
|
38
34
|
end
|
|
39
35
|
end
|
data/lib/estate/version.rb
CHANGED
data/lib/estate.rb
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'estate/configuration'
|
|
4
|
-
require 'estate/
|
|
4
|
+
require 'estate/constants/orm'
|
|
5
5
|
require 'estate/estate'
|
|
6
|
+
require 'estate/logic/core'
|
|
6
7
|
require 'estate/requirements'
|
|
8
|
+
require 'estate/setup'
|
|
7
9
|
require 'estate/state_machine'
|
|
8
10
|
require 'estate/version'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: estate
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Igor Korepanov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-01-
|
|
11
|
+
date: 2024-01-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rubocop
|
|
@@ -91,11 +91,16 @@ files:
|
|
|
91
91
|
- README.md
|
|
92
92
|
- lib/estate.rb
|
|
93
93
|
- lib/estate/configuration.rb
|
|
94
|
-
- lib/estate/
|
|
95
|
-
- lib/estate/db/active_record.rb
|
|
96
|
-
- lib/estate/db/sequel.rb
|
|
94
|
+
- lib/estate/constants/orm.rb
|
|
97
95
|
- lib/estate/estate.rb
|
|
96
|
+
- lib/estate/logic/active_record/setup.rb
|
|
97
|
+
- lib/estate/logic/active_record/specific_logic.rb
|
|
98
|
+
- lib/estate/logic/common_logic.rb
|
|
99
|
+
- lib/estate/logic/core.rb
|
|
100
|
+
- lib/estate/logic/sequel/setup.rb
|
|
101
|
+
- lib/estate/logic/sequel/specific_logic.rb
|
|
98
102
|
- lib/estate/requirements.rb
|
|
103
|
+
- lib/estate/setup.rb
|
|
99
104
|
- lib/estate/state_machine.rb
|
|
100
105
|
- lib/estate/version.rb
|
|
101
106
|
homepage: https://github.com/igorkorepanov/estate
|
data/lib/estate/core.rb
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Estate
|
|
4
|
-
module Core
|
|
5
|
-
module_function
|
|
6
|
-
|
|
7
|
-
def setup(base)
|
|
8
|
-
if 'ActiveRecord::Base'.in? base.ancestors.map(&:to_s)
|
|
9
|
-
require File.join(File.dirname(__FILE__), 'db', 'active_record')
|
|
10
|
-
Estate::Db::ActiveRecord.setup_callbacks(base)
|
|
11
|
-
else
|
|
12
|
-
require File.join(File.dirname(__FILE__), 'db', 'sequel')
|
|
13
|
-
Estate::Db::Sequel.setup_callbacks(base)
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Estate
|
|
4
|
-
module Db
|
|
5
|
-
module ActiveRecord
|
|
6
|
-
module_function
|
|
7
|
-
|
|
8
|
-
def setup_callbacks(base)
|
|
9
|
-
base.class_eval do
|
|
10
|
-
public_send(:before_validation) do
|
|
11
|
-
from_state = public_send("#{Estate::Configuration.column_name}_was")
|
|
12
|
-
to_state = public_send(Estate::Configuration.column_name)
|
|
13
|
-
Estate::Db::ActiveRecord.validate_state_changes(self, from_state, to_state)
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def validate_state_changes(instance, from_state, to_state)
|
|
19
|
-
if from_state == to_state
|
|
20
|
-
if from_state.nil? && !Estate::Configuration.allow_empty_initial_state
|
|
21
|
-
add_error(instance: instance, message: "empty `#{Estate::Configuration.column_name}` is not allowed")
|
|
22
|
-
end
|
|
23
|
-
elsif to_state.nil?
|
|
24
|
-
add_error(instance: instance, message: 'transition to empty state is not allowed')
|
|
25
|
-
elsif !Estate::StateMachine.state_exists?(to_state)
|
|
26
|
-
add_error(instance: instance, message: "state `#{to_state}` is not defined")
|
|
27
|
-
elsif !transition_allowed?(from_state: from_state, to_state: to_state)
|
|
28
|
-
add_error(instance: instance, message: "transition from `#{from_state}` to `#{to_state}` is not allowed",
|
|
29
|
-
attribute: Estate::Configuration.column_name)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def add_error(instance:, message:, attribute: :base)
|
|
34
|
-
if Estate::Configuration.raise_on_error
|
|
35
|
-
exception_message = attribute == :base ? message : "#{attribute}: #{message}"
|
|
36
|
-
raise(StandardError, exception_message)
|
|
37
|
-
else
|
|
38
|
-
instance.errors.add(attribute, message) unless instance.errors[attribute].include?(message)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def transition_allowed?(from_state:, to_state:)
|
|
43
|
-
from_state.nil? || Estate::StateMachine.transition_exists?(from: from_state, to: to_state)
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
data/lib/estate/db/sequel.rb
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Estate
|
|
4
|
-
module Db
|
|
5
|
-
module Sequel
|
|
6
|
-
module_function
|
|
7
|
-
|
|
8
|
-
def setup_callbacks(base)
|
|
9
|
-
base.class_eval do
|
|
10
|
-
def validate
|
|
11
|
-
super
|
|
12
|
-
|
|
13
|
-
to_state = values[Estate::Configuration.column_name]
|
|
14
|
-
from_state, = column_change(Estate::Configuration.column_name)
|
|
15
|
-
Estate::Db::Sequel.validate_state_changes(self, from_state, to_state)
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def validate_state_changes(instance, from_state, to_state)
|
|
21
|
-
if from_state == to_state
|
|
22
|
-
if from_state.nil? && !Estate::Configuration.allow_empty_initial_state
|
|
23
|
-
add_error(instance: instance, message: "empty `#{Estate::Configuration.column_name}` is not allowed")
|
|
24
|
-
end
|
|
25
|
-
elsif to_state.nil?
|
|
26
|
-
add_error(instance: instance, message: 'transition to empty state is not allowed')
|
|
27
|
-
elsif !Estate::StateMachine.state_exists?(to_state)
|
|
28
|
-
add_error(instance: instance, message: "state `#{to_state}` is not defined")
|
|
29
|
-
elsif !transition_allowed?(from_state: from_state, to_state: to_state)
|
|
30
|
-
add_error(instance: instance, message: "transition from `#{from_state}` to `#{to_state}` is not allowed",
|
|
31
|
-
attribute: Estate::Configuration.column_name)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# TODO: remove base
|
|
36
|
-
def add_error(instance:, message:, attribute: :base)
|
|
37
|
-
instance.errors.add(attribute, message)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def transition_allowed?(from_state:, to_state:)
|
|
41
|
-
from_state.nil? || Estate::StateMachine.transition_exists?(from: from_state, to: to_state)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|