aux 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7ab27a48b02884baff3dedd1a770d02a71be57471b40aaabfd1213068dfd317
4
- data.tar.gz: 1e38945b37770dc63d2280c1b000cff18b96d06b6544430480c815ab571213ac
3
+ metadata.gz: 1cf122c1dc9b99cf9e0b875586c740617087f7d59c06b50e31a9b9f632874dcc
4
+ data.tar.gz: 185dd787c6d3ee866e9c67c8ffc4135a28565eb63167bd19f85c1e63e9bb397f
5
5
  SHA512:
6
- metadata.gz: 05c2d14e99306c482ca584ca330e0808e653bab12082c5726dfd8495370f1109646afa0a7985c948d180715ce4aa074d4c0eb9cb8f58d409dab30305fb2fcdc3
7
- data.tar.gz: 116352323a5886ac2e2c8eeafe0a55687bd5e6e37d29e4eb66b4b4c76eb7ad5e7ba5eefbdaec0a7c25cc6afe77318713cef01fcab95aeee957dbc3726b15c986
6
+ metadata.gz: a393a2f312dfc8dc83bfe74ac4e8b77d0525158d6d7a34e2a9889aae8d269478fa2c16c0e44ecfefc236408696e5ceaa007944d4f486e7c2d39595b23f63e67a
7
+ data.tar.gz: '00679c6c77e2a668f7ee39accb292e19495ffaae3118232f1dd122bc3c31f3250d8517b66cbd863603cbd490b9a35cf292b66181a95de6069e071a0bf6503b28'
data/aux.gemspec CHANGED
@@ -10,11 +10,11 @@ Gem::Specification.new do |specification|
10
10
  specification.email = ['eboyko@eboyko.ru']
11
11
  specification.homepage = 'https://github.com/eboyko/aux'
12
12
 
13
+ specification.add_dependency 'concurrent-ruby', '~> 1.2', '>= 1.2.3'
13
14
  specification.add_dependency 'activemodel', '>= 6.1', '< 8'
14
- specification.add_dependency 'dry-container', '>= 0.9.0', '<= 0.11'
15
15
 
16
- specification.add_development_dependency 'zeitwerk', '~> 2.5'
17
- specification.add_development_dependency 'rubocop', '~> 1.36.0'
16
+ specification.add_development_dependency 'zeitwerk', '~> 2.6', '>= 2.6.13'
17
+ specification.add_development_dependency 'rubocop', '~> 1.62', '>= 1.62.1'
18
18
 
19
19
  specification.required_ruby_version = '>= 2.7.1'
20
20
  specification.metadata['rubygems_mfa_required'] = 'true'
@@ -52,24 +52,19 @@ module Aux
52
52
 
53
53
  # @param initialize [TrueClass, FalseClass]
54
54
  # @param memoize [TrueClass, FalseClass]
55
- # @param scope [TrueClass, Symbol, String, nil]
55
+ # @param scope [Symbol, String, TrueClass, nil]
56
56
  # @param as [Symbol, String, nil]
57
57
  def register(initialize: false, memoize: false, scope: true, as: nil)
58
- @_pluggable.register(
59
- initialization_required: initialize,
60
- memoization_required: memoize,
61
- namespace: scope,
62
- code: as
63
- )
58
+ @_pluggable.register(initialize, memoize, scope, as)
64
59
  end
65
60
 
66
61
  # @param code [Symbol, String]
67
- # @param initialization_block [Block]
62
+ # @param initialization_block [Proc, nil]
68
63
  # @param scope [TrueClass, Symbol, String, nil]
69
64
  # @param private [TrueClass, FalseClass]
70
65
  # @param as [Symbol, String, nil]
71
66
  def resolve(code, initialization_block = nil, scope: true, private: true, as: nil)
72
- @_pluggable.resolve(code, initialization_block, namespace: scope, private: private, as: as)
67
+ @_pluggable.resolve(code, scope, private, as, initialization_block)
73
68
  end
74
69
  end
75
70
  end
@@ -3,32 +3,26 @@
3
3
  module Aux
4
4
  module Pluggable
5
5
  # Describes the bridge between a pluggable class and the registry
6
+ # @!visibility private
6
7
  class Connector
7
8
  # @!attribute [r] dependencies
8
9
  # @return [Array<Dependency>]
9
10
  attr_reader :dependencies
10
11
 
11
12
  # @param subject [Class]
12
- # @param registry [Dry::Container]
13
+ # @param registry [Aux::Registry]
13
14
  def initialize(subject, registry)
14
15
  @subject = subject
15
16
  @registry = registry
16
17
  @dependencies = []
17
-
18
- configure
19
18
  end
20
19
 
21
- # @param initialization_required [Boolean] whether the subject requires initialization before use
22
- # @param memoization_required [Boolean] whether the subject should be memoized
23
- # @param namespace [Symbol] the namespace to register the subject
24
- # @param code [Symbol] an alternate name
25
- def register(initialization_required: nil, memoization_required: nil, namespace: nil, code: nil)
26
- configure(
27
- initialization_required: initialization_required,
28
- memoization_required: memoization_required,
29
- namespace: namespace,
30
- code: code
31
- )
20
+ # @param initialization_required [TrueClass, FalseClass] whether the subject requires initialization before use
21
+ # @param memoization_required [TrueClass, FalseClass] whether the subject should be memoized
22
+ # @param namespace [Symbol, String, TrueClass, nil] the namespace to register the subject
23
+ # @param code [Symbol, String, nil] an alternate name
24
+ def register(initialization_required, memoization_required, namespace, code)
25
+ configure(initialization_required, memoization_required, namespace, code)
32
26
 
33
27
  # Register the subject in the registry using the provided options
34
28
  @registry.register(@cipher, memoize: @memoization_required) do
@@ -37,16 +31,16 @@ module Aux
37
31
  end
38
32
 
39
33
  # @param code [Symbol, String] the name of the dependency
40
- # @param initialization_block [Proc] an optional block used to initialize the dependency
41
- # @param namespace [TrueClass, Symbol, String, nil] whether to resolve the dependency in the same namespace
34
+ # @param namespace [Symbol, String, TrueClass, nil] whether to resolve the dependency in the same namespace
42
35
  # @param private [TrueClass, FalseClass] whether to make the dependency private
43
- # @param as [Symbol] an internal alias name for the dependency
44
- # rubocop:disable Layout/LineLength, Metrics/AbcSize, Metrics/MethodLength
45
- def resolve(code, initialization_block = nil, namespace: true, private: true, as: nil)
46
- cipher = Utilities.dependency_cipher(@subject.name, scope: namespace, code: code)
47
- load_class(cipher) unless @registry.key?(cipher)
36
+ # @param as [Symbol, String, nil] an internal alias name for the dependency
37
+ # @param initialization_block [Proc, nil] an optional block used to initialize the dependency
38
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
39
+ def resolve(code, namespace, private, as, initialization_block = nil)
40
+ cipher = Utilities.dependency_cipher(@subject.name, namespace, code)
41
+ load_class(cipher)
48
42
 
49
- dependency = Dependency.new(@registry.resolve(cipher), initialization_block, pointer: as || code, private: private)
43
+ dependency = Dependency.new(@registry.resolve(cipher), as || code, private, initialization_block)
50
44
  @dependencies.push(dependency)
51
45
 
52
46
  if @initialization_required
@@ -62,16 +56,16 @@ module Aux
62
56
  end
63
57
  end
64
58
  end
65
- # rubocop:enable Layout/LineLength, Metrics/AbcSize, Metrics/MethodLength
59
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
66
60
 
67
61
  private
68
62
 
69
- # @param initialization_required [TrueClass, FalseClass, nil]
70
- # @param memoization_required [TrueClass, FalseClass, nil]
71
- # @param namespace [Symbol, String, nil]
63
+ # @param initialization_required [TrueClass, FalseClass]
64
+ # @param memoization_required [TrueClass, FalseClass]
65
+ # @param namespace [TrueClass, Symbol, String, nil]
72
66
  # @param code [Symbol, String, nil]
73
- def configure(initialization_required: nil, memoization_required: nil, namespace: nil, code: nil)
74
- @cipher = Utilities.dependency_cipher(@subject.name, scope: namespace, code: code)
67
+ def configure(initialization_required, memoization_required, namespace, code)
68
+ @cipher = Utilities.dependency_cipher(@subject.name, namespace, code)
75
69
  @initialization_required = initialization_required
76
70
  @memoization_required = memoization_required
77
71
  @namespace = namespace
@@ -85,7 +79,11 @@ module Aux
85
79
  # @return [Class] the loaded class
86
80
  def load_class(cipher)
87
81
  cipher.split('.').reduce(Object) do |namespace, class_name|
88
- namespace.const_get(class_name.split('_').map(&:capitalize).join)
82
+ if defined?(ActiveSupport::Inflector)
83
+ namespace.const_get(class_name.camelcase)
84
+ else
85
+ namespace.const_get(class_name.split('_').map(&:capitalize).join)
86
+ end
89
87
  end
90
88
  end
91
89
  end
@@ -3,6 +3,7 @@
3
3
  module Aux
4
4
  module Pluggable
5
5
  # Describes the dependency and its preferences
6
+ # @!visibility private
6
7
  class Dependency
7
8
  # @!attribute [r] target
8
9
  # @return [Object]
@@ -10,14 +11,14 @@ module Aux
10
11
  # @return [Symbol, String]
11
12
  # @!attribute [r] private
12
13
  # @return [Boolean]
13
- attr_reader :pointer, :target, :private
14
+ attr_reader :target, :pointer, :private
14
15
 
15
16
  # @param target [Object]
16
- # @param initialization_block [Proc, nil]
17
17
  # @param pointer [Symbol, String]
18
18
  # @param private [Boolean]
19
- def initialize(target, initialization_block = nil, pointer:, private:)
20
- @target = initialization_block ? initialization_block.call(target) : target
19
+ # @param initialization_block [Proc, nil]
20
+ def initialize(target, pointer, private, initialization_block = nil)
21
+ @target = initialization_block&.call(target) || target
21
22
  @pointer = pointer
22
23
  @private = private
23
24
  end
@@ -3,28 +3,29 @@
3
3
  module Aux
4
4
  module Pluggable
5
5
  # Random methods for internal usage
6
+ # @!visibility private
6
7
  module Utilities
7
8
  # First, we need to determine the appropriate namespace (also called the scope) in which to resolve something.
8
9
  # By default, we assume that the developers want to resolve a dependency from the same namespace as the
9
10
  # referencing class. Another approach is to allow developers to set the correct scope themselves.
10
11
  #
11
- # @param subject [ClassName]
12
- # @param scope [TrueClass, Symbol, String, nil]
13
- # @param code [TrueClass, Symbol, String, nil]
12
+ # @param subject [String]
13
+ # @param scope [Symbol, String, TrueClass, nil]
14
+ # @param code [Symbol, String, nil]
14
15
  # @return [String]
15
- def self.dependency_cipher(subject, scope: nil, code: nil)
16
+ def self.dependency_cipher(subject, scope, code)
16
17
  native_cipher = dependency_native_cipher(subject)
17
18
  native_cipher_partitions = native_cipher.rpartition('.')
18
- scope = scope == true ? native_cipher_partitions.first : scope
19
- code = code.nil? ? native_cipher_partitions.last : code
19
+ scope = native_cipher_partitions.first if scope == true
20
+ code = native_cipher_partitions.last if code.nil?
20
21
 
21
22
  [scope, code].reject { |part| part.nil? || part.empty? }.join('.')
22
23
  end
23
24
 
24
- # @param subject [ClassName]
25
+ # @param subject [String]
25
26
  # @return [String]
26
27
  def self.dependency_native_cipher(subject)
27
- subject.dup.gsub(/::/, '.').gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
28
+ subject.dup.gsub('::', '.').gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
28
29
  end
29
30
  end
30
31
  end
data/lib/aux/pluggable.rb CHANGED
@@ -4,11 +4,14 @@ require 'aux/pluggable/class_methods'
4
4
  require 'aux/pluggable/connector'
5
5
  require 'aux/pluggable/dependency'
6
6
  require 'aux/pluggable/utilities'
7
+ require 'aux/registry'
7
8
 
8
9
  module Aux
9
10
  # Describes interface that makes any class able to register itself as well as resolve dependencies
10
11
  # rubocop:disable Style/ClassVars
11
12
  module Pluggable
13
+ @@registry = Aux::Registry.new
14
+
12
15
  # Extends the including class with ClassMethods and initializes a new Connector instance
13
16
  # @param base [Class] the class that includes this module
14
17
  def self.included(base)
@@ -19,12 +22,19 @@ module Aux
19
22
  end
20
23
  end
21
24
 
22
- # @param registry [Dry::Container]
25
+ # @yield configure the module
26
+ # @yieldparam pluggable [Module<Aux::Pluggable>]
27
+ # @yieldparam registry [Aux::Registry]
28
+ def self.configure
29
+ yield(self, @@registry) if block_given?
30
+ end
31
+
32
+ # @param registry [Aux::Registry]
23
33
  def self.registry=(registry)
24
34
  @@registry = registry
25
35
  end
26
36
 
27
- # @return [Dry::Container]
37
+ # @return [Aux::Registry]
28
38
  def self.registry
29
39
  @@registry
30
40
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aux
4
+ class Registry
5
+ # Describes a registered dependency
6
+ # @!visibility private
7
+ class Entry
8
+ # @param constructor [Proc]
9
+ # @param memoization_required [TrueClass, FalseClass]
10
+ def initialize(constructor, memoization_required)
11
+ @constructor = constructor
12
+ @memoization_required = memoization_required
13
+
14
+ @mutex = Thread::Mutex.new
15
+ end
16
+
17
+ # @return [Object]
18
+ def call
19
+ return @constructor.call unless @memoization_required
20
+
21
+ @call ||= @mutex.synchronize do
22
+ @constructor.call
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aux/registry/entry'
4
+ require 'concurrent/map'
5
+
6
+ module Aux
7
+ # Registry for dependency injection
8
+ class Registry
9
+ def initialize
10
+ @prototypes = ::Concurrent::Map.new
11
+ end
12
+
13
+ # @param key [Symbol, String]
14
+ # @param memoize [TrueClass, FalseClass]
15
+ # @param constructor [Proc]
16
+ def register(key, memoize: false, &constructor)
17
+ @prototypes.put(key.to_s, Entry.new(constructor, memoize))
18
+ end
19
+
20
+ # @param key [Symbol, String]
21
+ # @return [Object]
22
+ def resolve(key)
23
+ @prototypes.fetch(key.to_s).call
24
+ end
25
+
26
+ # @param key [Symbol, String]
27
+ # @return [TrueClass, FalseClass]
28
+ def key?(key)
29
+ @prototypes.key?(key.to_s)
30
+ end
31
+ end
32
+ end
data/lib/aux/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aux
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
metadata CHANGED
@@ -1,83 +1,95 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aux
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgeny Boyko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-13 00:00:00.000000000 Z
11
+ date: 2024-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activemodel
14
+ name: concurrent-ruby
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '6.1'
20
- - - "<"
19
+ version: '1.2'
20
+ - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: '8'
22
+ version: 1.2.3
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - ">="
27
+ - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: '6.1'
30
- - - "<"
29
+ version: '1.2'
30
+ - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: '8'
32
+ version: 1.2.3
33
33
  - !ruby/object:Gem::Dependency
34
- name: dry-container
34
+ name: activemodel
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: 0.9.0
40
- - - "<="
39
+ version: '6.1'
40
+ - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: '0.11'
42
+ version: '8'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
- version: 0.9.0
50
- - - "<="
49
+ version: '6.1'
50
+ - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: '0.11'
52
+ version: '8'
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: zeitwerk
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
- version: '2.5'
59
+ version: '2.6'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 2.6.13
60
63
  type: :development
61
64
  prerelease: false
62
65
  version_requirements: !ruby/object:Gem::Requirement
63
66
  requirements:
64
67
  - - "~>"
65
68
  - !ruby/object:Gem::Version
66
- version: '2.5'
69
+ version: '2.6'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 2.6.13
67
73
  - !ruby/object:Gem::Dependency
68
74
  name: rubocop
69
75
  requirement: !ruby/object:Gem::Requirement
70
76
  requirements:
71
77
  - - "~>"
72
78
  - !ruby/object:Gem::Version
73
- version: 1.36.0
79
+ version: '1.62'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.62.1
74
83
  type: :development
75
84
  prerelease: false
76
85
  version_requirements: !ruby/object:Gem::Requirement
77
86
  requirements:
78
87
  - - "~>"
79
88
  - !ruby/object:Gem::Version
80
- version: 1.36.0
89
+ version: '1.62'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 1.62.1
81
93
  description:
82
94
  email:
83
95
  - eboyko@eboyko.ru
@@ -93,6 +105,8 @@ files:
93
105
  - lib/aux/pluggable/connector.rb
94
106
  - lib/aux/pluggable/dependency.rb
95
107
  - lib/aux/pluggable/utilities.rb
108
+ - lib/aux/registry.rb
109
+ - lib/aux/registry/entry.rb
96
110
  - lib/aux/validations.rb
97
111
  - lib/aux/validations/error.rb
98
112
  - lib/aux/validations/errors.rb