dry-system 0.20.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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