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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5e27471f690e289d113e8e69a6c72ad1c063a20b57bdead1f97040b71cb101f
4
- data.tar.gz: 233b56c93ade0cc71d91d721bd58f207bc17bc320b9e2165d31ad2a4700786d7
3
+ metadata.gz: 0777265b6deae44f96eb4fbc8f150bb73b1f2d98849107b9750eeb0ac025ca49
4
+ data.tar.gz: 916c2a835c05ed793d87e25883cb6999ebf87687c260f39eba90e994cf0aa228
5
5
  SHA512:
6
- metadata.gz: c1330c6a3512171bdb6deaa89612fa4adf4d6aad6ef621e97aa456c0e792e48f21b1d7cac9ef47fe36a7a8b1806c6148da9b973200e1ff5f27e5434deac43c11
7
- data.tar.gz: 1452e5b9408d8cfd658ed983d11f1f8a0300e533400875285af65c84ee7a451edf6f29be6df4f779e991ce47a25fd6e230404b8949d37a15a1353414ef1be521
6
+ metadata.gz: 37e595f2f4451386df31a2b563af980037c90f91786760c9c88f9090418ffd3b640473bc5c2d16a1826c3646302d2041b151a0807d106e1dbc6c85dc126bbfbd
7
+ data.tar.gz: f939381283ace662a7d97169cc7e0bf26ad8d05a78eb40585f456e84768bda4474c1ae0501d5f7d232eb401d577341d2ce09f4fa3b02137adca8019e87c65114
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ ![CI](https://github.com/igorkorepanov/estate/actions/workflows/main.yml/badge.svg)
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.
@@ -3,7 +3,7 @@
3
3
  module Estate
4
4
  module Configuration
5
5
  class << self
6
- def init_config(column_name:, allow_empty_initial_state:, raise_on_error:)
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
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Estate
4
+ module Constants
5
+ module Orm
6
+ ACTIVE_RECORD = 'active_record'
7
+ SEQUEL = 'sequel'
8
+ end
9
+ end
10
+ end
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::Core.setup(base)
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(column_name: column, allow_empty_initial_state: empty_initial_state,
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:, to:)
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: from, to: to)
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: from, to: to)
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
@@ -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
@@ -9,31 +9,27 @@ module Estate
9
9
  end
10
10
 
11
11
  def state_exists?(state)
12
- !state.nil? && states.key?(state.to_sym)
12
+ states.key?(state.to_sym)
13
13
  end
14
14
 
15
15
  def register_state(state)
16
- case state
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?(from:, to:)
27
- transition_key = { from: from.to_sym, to: to.to_sym }
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(from:, to:)
32
- # TODO: validate from and to
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Estate
4
- VERSION = '0.1.0'
4
+ VERSION = '0.1.1'
5
5
  end
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/core'
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.0
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-27 00:00:00.000000000 Z
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/core.rb
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
@@ -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