dry-system 0.20.0 → 0.21.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.
@@ -13,42 +13,29 @@ module Dry
13
13
  #
14
14
  # @api public
15
15
  class Identifier
16
- include Dry::Equalizer(:identifier, :namespace, :separator)
16
+ include Dry::Equalizer(:key, :separator)
17
17
 
18
- # @return [String] the identifier string
18
+ # @return [String] the identifier's string key
19
19
  # @api public
20
- attr_reader :identifier
21
-
22
- # @return [String, nil] the namespace for the component
23
- # @api public
24
- attr_reader :namespace
20
+ attr_reader :key
25
21
 
26
22
  # @return [String] the configured namespace separator
27
23
  # @api public
28
24
  attr_reader :separator
29
25
 
30
26
  # @api private
31
- def initialize(identifier, namespace: nil, separator: DEFAULT_SEPARATOR)
32
- @identifier = identifier.to_s
33
- @namespace = namespace
27
+ def initialize(key, separator: DEFAULT_SEPARATOR)
28
+ @key = key.to_s
34
29
  @separator = separator
35
30
  end
36
31
 
37
- # @!method key
38
- # Returns the identifier string
39
- #
40
- # @return [String]
41
- # @see #identifier
42
- # @api public
43
- alias_method :key, :identifier
44
-
45
32
  # @!method to_s
46
- # Returns the identifier string
33
+ # Returns the identifier string key
47
34
  #
48
35
  # @return [String]
49
- # @see #identifier
36
+ # @see #key
50
37
  # @api public
51
- alias_method :to_s, :identifier
38
+ alias_method :to_s, :key
52
39
 
53
40
  # Returns the root namespace segment of the identifier string, as a symbol
54
41
  #
@@ -62,31 +49,11 @@ module Dry
62
49
  segments.first.to_sym
63
50
  end
64
51
 
65
- # Returns a path-delimited representation of the identifier, with the namespace
66
- # incorporated. This path is intended for usage when requiring the component's
67
- # source file.
68
- #
69
- # @example
70
- # identifier.key # => "articles.operations.create"
71
- # identifier.namespace # => "admin"
52
+ # Returns true if the given leading namespaces are a leading part of the
53
+ # identifier's key
72
54
  #
73
- # identifier.path # => "admin/articles/operations/create"
74
- #
75
- # @return [String] the path
76
- # @api public
77
- def path
78
- @require_path ||= identifier.gsub(separator, PATH_SEPARATOR).yield_self { |path|
79
- if namespace
80
- namespace_path = namespace.to_s.gsub(separator, PATH_SEPARATOR)
81
- "#{namespace_path}#{PATH_SEPARATOR}#{path}"
82
- else
83
- path
84
- end
85
- }
86
- end
87
-
88
- # Returns true if the given namespace prefix is part of the identifier's leading
89
- # namespaces
55
+ # Also returns true if nil is given (technically, from nothing everything is
56
+ # wrought)
90
57
  #
91
58
  # @example
92
59
  # identifier.key # => "articles.operations.create"
@@ -94,64 +61,74 @@ module Dry
94
61
  # identifier.start_with?("articles.operations") # => true
95
62
  # identifier.start_with?("articles") # => true
96
63
  # identifier.start_with?("article") # => false
64
+ # identifier.start_with?(nil) # => true
97
65
  #
98
66
  # @param leading_namespaces [String] the one or more leading namespaces to check
99
67
  # @return [Boolean]
100
68
  # @api public
101
69
  def start_with?(leading_namespaces)
102
- identifier.start_with?("#{leading_namespaces}#{separator}") ||
103
- identifier.eql?(leading_namespaces)
70
+ leading_namespaces.nil? ||
71
+ key.start_with?("#{leading_namespaces}#{separator}") ||
72
+ key.eql?(leading_namespaces)
104
73
  end
105
74
 
106
- # Returns a copy of the identifier with the given leading namespaces removed from
107
- # the identifier string.
108
- #
109
- # Additional options may be provided, which are passed to #initialize when
110
- # constructing the new copy of the identifier
111
- #
112
- # @param leading_namespace [String] the one or more leading namespaces to remove
113
- # @param options [Hash] additional options for initialization
75
+ # Returns the key with its segments separated by the given separator
114
76
  #
115
- # @return [Dry::System::Identifier] the copy of the identifier
77
+ # @example
78
+ # identifier.key # => "articles.operations.create"
79
+ # identifier.key_with_separator("/") # => "articles/operations/create"
116
80
  #
117
- # @see #initialize
81
+ # @return [String] the key using the separator
118
82
  # @api private
119
- def dequalified(leading_namespaces, **options)
120
- new_identifier = identifier.gsub(
121
- /^#{Regexp.escape(leading_namespaces)}#{Regexp.escape(separator)}/,
122
- EMPTY_STRING
123
- )
124
-
125
- return self if new_identifier == identifier
126
-
127
- self.class.new(
128
- new_identifier,
129
- namespace: namespace,
130
- separator: separator,
131
- **options
132
- )
83
+ def key_with_separator(separator)
84
+ segments.join(separator)
133
85
  end
134
86
 
135
- # Returns a copy of the identifier with the given options applied
87
+ # Returns a copy of the identifier with the key's leading namespace(s) replaced
88
+ #
89
+ # @example Changing a namespace
90
+ # identifier.key # => "articles.operations.create"
91
+ # identifier.namespaced(from: "articles", to: "posts").key # => "posts.commands.create"
92
+ #
93
+ # @example Removing a namespace
94
+ # identifier.key # => "articles.operations.create"
95
+ # identifier.namespaced(from: "articles", to: nil).key # => "operations.create"
136
96
  #
137
- # @param namespace [String, nil] a new namespace to be used
97
+ # @example Adding a namespace
98
+ # identifier.key # => "articles.operations.create"
99
+ # identifier.namespaced(from: nil, to: "admin").key # => "admin.articles.operations.create"
100
+ #
101
+ # @param from [String, nil] the leading namespace(s) to replace
102
+ # @param to [String, nil] the replacement for the leading namespace
138
103
  #
139
104
  # @return [Dry::System::Identifier] the copy of the identifier
140
105
  #
141
106
  # @see #initialize
142
107
  # @api private
143
- def with(namespace:)
144
- self.class.new(
145
- identifier,
146
- namespace: namespace,
147
- separator: separator
148
- )
108
+ def namespaced(from:, to:)
109
+ return self if from == to
110
+
111
+ separated_to = "#{to}#{separator}" if to
112
+
113
+ new_key =
114
+ if from.nil?
115
+ "#{separated_to}#{key}"
116
+ else
117
+ key.sub(
118
+ /^#{Regexp.escape(from.to_s)}#{Regexp.escape(separator)}/,
119
+ separated_to || EMPTY_STRING
120
+ )
121
+ end
122
+
123
+ return self if new_key == key
124
+
125
+ self.class.new(new_key, separator: separator)
149
126
  end
150
127
 
151
128
  private
152
129
 
153
130
  def segments
154
- @segments ||= identifier.split(separator)
131
+ @segments ||= key.split(separator)
155
132
  end
156
133
  end
157
134
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/equalizer"
4
+
5
+ module Dry
6
+ module System
7
+ # An indirect component is a component that cannot be directly from a source file
8
+ # directly managed by the container. It may be component that needs to be loaded
9
+ # indirectly, either via a manual registration file or an imported container
10
+ #
11
+ # Indirect components are an internal abstraction and, unlike ordinary components, are
12
+ # not exposed to users via component dir configuration hooks.
13
+ #
14
+ # @see Container#load_component
15
+ # @see Container#find_component
16
+ #
17
+ # @api private
18
+ class IndirectComponent
19
+ include Dry::Equalizer(:identifier)
20
+
21
+ # @!attribute [r] identifier
22
+ # @return [String] the component's unique identifier
23
+ attr_reader :identifier
24
+
25
+ # @api private
26
+ def initialize(identifier)
27
+ @identifier = identifier
28
+ end
29
+
30
+ # Returns false, indicating that the component is not directly loadable from the
31
+ # files managed by the container
32
+ #
33
+ # This is the inverse of {Component#loadable?}
34
+ #
35
+ # @return [FalseClass]
36
+ #
37
+ # @api private
38
+ def loadable?
39
+ false
40
+ end
41
+
42
+ # Returns the component's unique key
43
+ #
44
+ # @return [String] the key
45
+ #
46
+ # @see Identifier#key
47
+ #
48
+ # @api private
49
+ def key
50
+ identifier.to_s
51
+ end
52
+
53
+ # Returns the root namespace segment of the component's key, as a symbol
54
+ #
55
+ # @see Identifier#root_key
56
+ #
57
+ # @return [Symbol] the root key
58
+ #
59
+ # @api private
60
+ def root_key
61
+ identifier.root_key
62
+ end
63
+ end
64
+ end
65
+ end
@@ -28,7 +28,7 @@ module Dry
28
28
  #
29
29
  # @api public
30
30
  def require!(component)
31
- require(component.path) if component.file_exists?
31
+ require(component.require_path)
32
32
  self
33
33
  end
34
34
 
@@ -61,8 +61,7 @@ module Dry
61
61
  # @api public
62
62
  def constant(component)
63
63
  inflector = component.inflector
64
-
65
- inflector.constantize(inflector.camelize(component.path))
64
+ inflector.constantize(inflector.camelize(component.const_path))
66
65
  end
67
66
 
68
67
  private
@@ -30,16 +30,12 @@ module Dry
30
30
  end
31
31
 
32
32
  # @api private
33
- def call(name)
34
- name = name.respond_to?(:root_key) ? name.root_key.to_s : name
35
-
36
- require(root.join(config.registrations_dir, name))
33
+ def call(component)
34
+ require(root.join(config.registrations_dir, component.root_key.to_s))
37
35
  end
38
36
 
39
- def file_exists?(name)
40
- name = name.respond_to?(:root_key) ? name.root_key.to_s : name
41
-
42
- File.exist?(File.join(registrations_dir, "#{name}#{RB_EXT}"))
37
+ def file_exists?(component)
38
+ File.exist?(File.join(registrations_dir, "#{component.root_key}#{RB_EXT}"))
43
39
  end
44
40
 
45
41
  private
@@ -84,9 +84,7 @@ module Dry
84
84
  # Enables a plugin if not already enabled.
85
85
  # Raises error if plugin cannot be found in the plugin registry.
86
86
  #
87
- # Plugin identifier
88
- #
89
- # @param [Symbol] name The plugin identifier
87
+ # @param [Symbol] name The plugin name
90
88
  # @param [Hash] options Plugin options
91
89
  #
92
90
  # @return [self]
@@ -7,14 +7,14 @@ require "dry/system/components/bootable"
7
7
  module Dry
8
8
  module System
9
9
  class Provider
10
- attr_reader :identifier
10
+ attr_reader :name
11
11
 
12
12
  attr_reader :options
13
13
 
14
14
  attr_reader :components
15
15
 
16
- def initialize(identifier, options)
17
- @identifier = identifier
16
+ def initialize(name, options)
17
+ @name = name
18
18
  @options = options
19
19
  @components = Concurrent::Map.new
20
20
  end
@@ -35,9 +35,9 @@ module Dry
35
35
  boot_files.detect { |path| Pathname(path).basename(RB_EXT).to_s == name.to_s }
36
36
  end
37
37
 
38
- def component(name, options = {})
39
- identifier = options[:key] || name
40
- components.fetch(identifier).new(name, options)
38
+ def component(component_name, options = {})
39
+ component_key = options[:key] || component_name
40
+ components.fetch(component_key).new(component_name, options)
41
41
  end
42
42
 
43
43
  def load_components
@@ -15,12 +15,12 @@ module Dry
15
15
  items.each(&block)
16
16
  end
17
17
 
18
- def register(identifier, options)
19
- items << Provider.new(identifier, options)
18
+ def register(name, options)
19
+ items << Provider.new(name, options)
20
20
  end
21
21
 
22
- def [](identifier)
23
- detect { |provider| provider.identifier == identifier }
22
+ def [](name)
23
+ detect { |provider| provider.name == name }
24
24
  end
25
25
  end
26
26
  end
@@ -11,12 +11,9 @@ module Dry
11
11
  module System
12
12
  module Settings
13
13
  class DSL < BasicObject
14
- attr_reader :identifier
15
-
16
14
  attr_reader :schema
17
15
 
18
- def initialize(identifier, &block)
19
- @identifier = identifier
16
+ def initialize(&block)
20
17
  @schema = {}
21
18
  instance_eval(&block)
22
19
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dry
4
4
  module System
5
- VERSION = "0.20.0"
5
+ VERSION = "0.21.0"
6
6
  end
7
7
  end
data/lib/dry/system.rb CHANGED
@@ -8,17 +8,17 @@ module Dry
8
8
  # Register external component provider
9
9
  #
10
10
  # @api public
11
- def self.register_provider(identifier, options)
12
- providers.register(identifier, options)
13
- providers[identifier].load_components
11
+ def self.register_provider(name, options)
12
+ providers.register(name, options)
13
+ providers[name].load_components
14
14
  self
15
15
  end
16
16
 
17
17
  # Register an external component that can be booted within other systems
18
18
  #
19
19
  # @api public
20
- def self.register_component(identifier, provider:, &block)
21
- providers[provider].register_component(identifier, block)
20
+ def self.register_component(name, provider:, &block)
21
+ providers[provider].register_component(name, block)
22
22
  self
23
23
  end
24
24
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-system
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.0
4
+ version: 0.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-12 00:00:00.000000000 Z
11
+ date: 2021-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -197,11 +197,14 @@ files:
197
197
  - lib/dry/system/components/config.rb
198
198
  - lib/dry/system/config/component_dir.rb
199
199
  - lib/dry/system/config/component_dirs.rb
200
+ - lib/dry/system/config/namespace.rb
201
+ - lib/dry/system/config/namespaces.rb
200
202
  - lib/dry/system/constants.rb
201
203
  - lib/dry/system/container.rb
202
204
  - lib/dry/system/errors.rb
203
205
  - lib/dry/system/identifier.rb
204
206
  - lib/dry/system/importer.rb
207
+ - lib/dry/system/indirect_component.rb
205
208
  - lib/dry/system/lifecycle.rb
206
209
  - lib/dry/system/loader.rb
207
210
  - lib/dry/system/loader/autoloading.rb