rom 5.4.1 → 6.0.0.alpha1

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.
Files changed (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -65
  3. data/LICENSE +1 -1
  4. data/README.md +7 -6
  5. data/lib/rom/array_dataset.rb +46 -0
  6. data/lib/rom/associations/abstract.rb +217 -0
  7. data/lib/rom/associations/definitions/abstract.rb +150 -0
  8. data/lib/rom/associations/definitions/many_to_many.rb +29 -0
  9. data/lib/rom/associations/definitions/many_to_one.rb +14 -0
  10. data/lib/rom/associations/definitions/one_to_many.rb +14 -0
  11. data/lib/rom/associations/definitions/one_to_one.rb +14 -0
  12. data/lib/rom/associations/definitions/one_to_one_through.rb +14 -0
  13. data/lib/rom/associations/definitions.rb +7 -0
  14. data/lib/rom/associations/many_to_many.rb +128 -0
  15. data/lib/rom/associations/many_to_one.rb +65 -0
  16. data/lib/rom/associations/one_to_many.rb +65 -0
  17. data/lib/rom/associations/one_to_one.rb +13 -0
  18. data/lib/rom/associations/one_to_one_through.rb +13 -0
  19. data/lib/rom/associations/through_identifier.rb +41 -0
  20. data/lib/rom/attribute.rb +425 -0
  21. data/lib/rom/auto_curry.rb +70 -0
  22. data/lib/rom/cache.rb +87 -0
  23. data/lib/rom/changeset/associated.rb +110 -0
  24. data/lib/rom/changeset/create.rb +18 -0
  25. data/lib/rom/changeset/delete.rb +15 -0
  26. data/lib/rom/changeset/extensions/relation.rb +26 -0
  27. data/lib/rom/changeset/pipe.rb +81 -0
  28. data/lib/rom/changeset/pipe_registry.rb +27 -0
  29. data/lib/rom/changeset/stateful.rb +285 -0
  30. data/lib/rom/changeset/update.rb +81 -0
  31. data/lib/rom/changeset.rb +185 -0
  32. data/lib/rom/command.rb +351 -0
  33. data/lib/rom/command_compiler.rb +201 -0
  34. data/lib/rom/command_proxy.rb +36 -0
  35. data/lib/rom/commands/class_interface.rb +236 -0
  36. data/lib/rom/commands/composite.rb +55 -0
  37. data/lib/rom/commands/create.rb +15 -0
  38. data/lib/rom/commands/delete.rb +16 -0
  39. data/lib/rom/commands/graph/class_interface.rb +64 -0
  40. data/lib/rom/commands/graph/input_evaluator.rb +94 -0
  41. data/lib/rom/commands/graph.rb +88 -0
  42. data/lib/rom/commands/lazy/create.rb +35 -0
  43. data/lib/rom/commands/lazy/delete.rb +39 -0
  44. data/lib/rom/commands/lazy/update.rb +46 -0
  45. data/lib/rom/commands/lazy.rb +106 -0
  46. data/lib/rom/commands/update.rb +16 -0
  47. data/lib/rom/commands.rb +5 -0
  48. data/lib/rom/compat/auto_registration.rb +115 -0
  49. data/lib/rom/compat/auto_registration_strategies/base.rb +29 -0
  50. data/lib/rom/compat/auto_registration_strategies/custom_namespace.rb +84 -0
  51. data/lib/rom/compat/auto_registration_strategies/no_namespace.rb +33 -0
  52. data/lib/rom/compat/auto_registration_strategies/with_namespace.rb +29 -0
  53. data/lib/rom/compat/command.rb +74 -0
  54. data/lib/rom/compat/components/dsl/schema.rb +130 -0
  55. data/lib/rom/compat/components.rb +91 -0
  56. data/lib/rom/compat/global.rb +17 -0
  57. data/lib/rom/compat/mapper.rb +22 -0
  58. data/lib/rom/compat/registries.rb +47 -0
  59. data/lib/rom/compat/relation.rb +40 -0
  60. data/lib/rom/compat/schema/dsl.rb +260 -0
  61. data/lib/rom/compat/setting_proxy.rb +44 -0
  62. data/lib/rom/compat/setup.rb +151 -0
  63. data/lib/rom/compat/transformer.rb +49 -0
  64. data/lib/rom/compat.rb +22 -0
  65. data/lib/rom/components/association.rb +26 -0
  66. data/lib/rom/components/command.rb +24 -0
  67. data/lib/rom/components/core.rb +148 -0
  68. data/lib/rom/components/dataset.rb +60 -0
  69. data/lib/rom/components/dsl/association.rb +47 -0
  70. data/lib/rom/components/dsl/command.rb +60 -0
  71. data/lib/rom/components/dsl/core.rb +126 -0
  72. data/lib/rom/components/dsl/dataset.rb +33 -0
  73. data/lib/rom/components/dsl/gateway.rb +14 -0
  74. data/lib/rom/components/dsl/mapper.rb +70 -0
  75. data/lib/rom/components/dsl/relation.rb +49 -0
  76. data/lib/rom/components/dsl/schema.rb +150 -0
  77. data/lib/rom/components/dsl/view.rb +82 -0
  78. data/lib/rom/components/dsl.rb +255 -0
  79. data/lib/rom/components/gateway.rb +50 -0
  80. data/lib/rom/components/mapper.rb +29 -0
  81. data/lib/rom/components/provider.rb +160 -0
  82. data/lib/rom/components/registry.rb +154 -0
  83. data/lib/rom/components/relation.rb +41 -0
  84. data/lib/rom/components/schema.rb +61 -0
  85. data/lib/rom/components/view.rb +55 -0
  86. data/lib/rom/components.rb +55 -0
  87. data/lib/rom/configuration_dsl.rb +4 -0
  88. data/lib/rom/constants.rb +135 -0
  89. data/lib/rom/container.rb +182 -0
  90. data/lib/rom/core.rb +125 -0
  91. data/lib/rom/data_proxy.rb +97 -0
  92. data/lib/rom/enumerable_dataset.rb +70 -0
  93. data/lib/rom/gateway.rb +232 -0
  94. data/lib/rom/global.rb +56 -0
  95. data/lib/rom/header/attribute.rb +190 -0
  96. data/lib/rom/header.rb +198 -0
  97. data/lib/rom/inferrer.rb +55 -0
  98. data/lib/rom/initializer.rb +80 -0
  99. data/lib/rom/lint/enumerable_dataset.rb +56 -0
  100. data/lib/rom/lint/gateway.rb +120 -0
  101. data/lib/rom/lint/linter.rb +79 -0
  102. data/lib/rom/lint/spec.rb +22 -0
  103. data/lib/rom/lint/test.rb +98 -0
  104. data/lib/rom/loader.rb +161 -0
  105. data/lib/rom/mapper/attribute_dsl.rb +480 -0
  106. data/lib/rom/mapper/dsl.rb +107 -0
  107. data/lib/rom/mapper/model_dsl.rb +61 -0
  108. data/lib/rom/mapper.rb +99 -0
  109. data/lib/rom/mapper_compiler.rb +84 -0
  110. data/lib/rom/memory/associations/many_to_many.rb +12 -0
  111. data/lib/rom/memory/associations/many_to_one.rb +12 -0
  112. data/lib/rom/memory/associations/one_to_many.rb +12 -0
  113. data/lib/rom/memory/associations/one_to_one.rb +12 -0
  114. data/lib/rom/memory/associations.rb +6 -0
  115. data/lib/rom/memory/commands.rb +60 -0
  116. data/lib/rom/memory/dataset.rb +127 -0
  117. data/lib/rom/memory/gateway.rb +66 -0
  118. data/lib/rom/memory/mapper_compiler.rb +10 -0
  119. data/lib/rom/memory/relation.rb +91 -0
  120. data/lib/rom/memory/schema.rb +32 -0
  121. data/lib/rom/memory/storage.rb +61 -0
  122. data/lib/rom/memory/types.rb +11 -0
  123. data/lib/rom/memory.rb +7 -0
  124. data/lib/rom/model_builder.rb +103 -0
  125. data/lib/rom/open_struct.rb +112 -0
  126. data/lib/rom/pipeline.rb +111 -0
  127. data/lib/rom/plugin.rb +130 -0
  128. data/lib/rom/plugins/class_methods.rb +37 -0
  129. data/lib/rom/plugins/command/schema.rb +45 -0
  130. data/lib/rom/plugins/command/timestamps.rb +149 -0
  131. data/lib/rom/plugins/dsl.rb +53 -0
  132. data/lib/rom/plugins/relation/changeset.rb +97 -0
  133. data/lib/rom/plugins/relation/instrumentation.rb +66 -0
  134. data/lib/rom/plugins/relation/registry_reader.rb +36 -0
  135. data/lib/rom/plugins/schema/timestamps.rb +59 -0
  136. data/lib/rom/plugins.rb +100 -0
  137. data/lib/rom/processor/composer.rb +37 -0
  138. data/lib/rom/processor/transformer.rb +415 -0
  139. data/lib/rom/processor.rb +30 -0
  140. data/lib/rom/registries/associations.rb +26 -0
  141. data/lib/rom/registries/commands.rb +11 -0
  142. data/lib/rom/registries/container.rb +12 -0
  143. data/lib/rom/registries/datasets.rb +21 -0
  144. data/lib/rom/registries/gateways.rb +8 -0
  145. data/lib/rom/registries/mappers.rb +21 -0
  146. data/lib/rom/registries/nestable.rb +32 -0
  147. data/lib/rom/registries/relations.rb +8 -0
  148. data/lib/rom/registries/root.rb +203 -0
  149. data/lib/rom/registries/schemas.rb +44 -0
  150. data/lib/rom/registries/views.rb +11 -0
  151. data/lib/rom/relation/class_interface.rb +61 -0
  152. data/lib/rom/relation/combined.rb +160 -0
  153. data/lib/rom/relation/commands.rb +65 -0
  154. data/lib/rom/relation/composite.rb +53 -0
  155. data/lib/rom/relation/curried.rb +129 -0
  156. data/lib/rom/relation/graph.rb +107 -0
  157. data/lib/rom/relation/loaded.rb +136 -0
  158. data/lib/rom/relation/materializable.rb +62 -0
  159. data/lib/rom/relation/name.rb +122 -0
  160. data/lib/rom/relation/wrap.rb +64 -0
  161. data/lib/rom/relation.rb +625 -0
  162. data/lib/rom/repository/class_interface.rb +162 -0
  163. data/lib/rom/repository/relation_reader.rb +48 -0
  164. data/lib/rom/repository/root.rb +75 -0
  165. data/lib/rom/repository/session.rb +60 -0
  166. data/lib/rom/repository.rb +179 -0
  167. data/lib/rom/schema/associations_dsl.rb +222 -0
  168. data/lib/rom/schema/inferrer.rb +106 -0
  169. data/lib/rom/schema.rb +471 -0
  170. data/lib/rom/settings.rb +141 -0
  171. data/lib/rom/setup.rb +297 -0
  172. data/lib/rom/struct.rb +99 -0
  173. data/lib/rom/struct_compiler.rb +114 -0
  174. data/lib/rom/support/configurable.rb +213 -0
  175. data/lib/rom/support/inflector.rb +31 -0
  176. data/lib/rom/support/memoizable.rb +61 -0
  177. data/lib/rom/support/notifications.rb +238 -0
  178. data/lib/rom/transaction.rb +26 -0
  179. data/lib/rom/transformer.rb +46 -0
  180. data/lib/rom/types.rb +74 -0
  181. data/lib/rom/version.rb +1 -1
  182. data/lib/rom-changeset.rb +4 -0
  183. data/lib/rom-core.rb +3 -0
  184. data/lib/rom-repository.rb +4 -0
  185. data/lib/rom.rb +3 -3
  186. metadata +302 -23
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/lint/linter"
4
+
5
+ module ROM
6
+ module Lint
7
+ # Ensures that a [ROM::Gateway] extension provides datasets through the
8
+ # expected methods
9
+ #
10
+ # @api public
11
+ class Gateway < ROM::Lint::Linter
12
+ # The gateway identifier e.g. +:memory+
13
+ #
14
+ # @api public
15
+ attr_reader :identifier
16
+
17
+ # The gateway class
18
+ #
19
+ # @api public
20
+ attr_reader :gateway
21
+
22
+ # The optional URI
23
+ #
24
+ # @api public
25
+ attr_reader :uri
26
+
27
+ # Gateway instance used in lint tests
28
+ #
29
+ # @api private
30
+ attr_reader :gateway_instance
31
+
32
+ # Create a gateway linter
33
+ #
34
+ # @param [Symbol] identifier
35
+ # @param [Class] gateway
36
+ # @param [String] uri optional
37
+ def initialize(identifier, gateway, uri = nil)
38
+ @identifier = identifier
39
+ @gateway = gateway
40
+ @uri = uri
41
+ @gateway_instance = setup_gateway_instance
42
+ end
43
+
44
+ # Lint: Ensure that +gateway+ setups up its instance
45
+ #
46
+ # @api public
47
+ def lint_gateway_setup
48
+ return if gateway_instance.instance_of? gateway
49
+
50
+ complain <<-STRING
51
+ #{gateway}.setup must return a gateway instance but
52
+ returned #{gateway_instance.inspect}
53
+ STRING
54
+ end
55
+
56
+ # Lint: Ensure that +gateway_instance+ responds to +[]+
57
+ #
58
+ # @api public
59
+ def lint_dataset_reader
60
+ return if gateway_instance.respond_to? :[]
61
+
62
+ complain "#{gateway_instance} must respond to []"
63
+ end
64
+
65
+ # Lint: Ensure that +gateway_instance+ responds to +dataset?+
66
+ #
67
+ # @api public
68
+ def lint_dataset_predicate
69
+ return if gateway_instance.respond_to? :dataset?
70
+
71
+ complain "#{gateway_instance} must respond to dataset?"
72
+ end
73
+
74
+ # Lint: Ensure +gateway_instance+ supports +transaction+ interface
75
+ #
76
+ # @api public
77
+ def lint_transaction_support
78
+ result = gateway_instance.transaction { 1 }
79
+
80
+ complain "#{gateway_instance} must return the result of a transaction block" if result != 1
81
+
82
+ gateway_instance.transaction do |t|
83
+ t.rollback!
84
+
85
+ complain "#{gateway_instance} must interrupt a transaction on rollback"
86
+ end
87
+ end
88
+
89
+ # Lint: Ensure +gateway_instance+ returns adapter name
90
+ def lint_adapter_reader
91
+ if gateway_instance.adapter != identifier
92
+ complain "#{gateway_instance} must have the adapter identifier set to #{identifier.inspect}"
93
+ end
94
+ rescue MissingAdapterIdentifierError
95
+ complain "#{gateway_instance} is missing the adapter identifier"
96
+ end
97
+
98
+ private
99
+
100
+ # Setup gateway instance
101
+ #
102
+ # @api private
103
+ def setup_gateway_instance
104
+ if uri
105
+ ROM::Gateway.setup(identifier, uri)
106
+ else
107
+ ROM::Gateway.setup(identifier)
108
+ end
109
+ end
110
+
111
+ # Run Gateway#disconnect
112
+ #
113
+ # @api private
114
+ def after_lint
115
+ super
116
+ gateway_instance.disconnect
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ROM
4
+ module Lint
5
+ # Base class for building linters that check source code
6
+ #
7
+ # Linters are used by authors of ROM adapters to verify that their
8
+ # integration complies with the ROM api.
9
+ #
10
+ # Most of the time, authors won't need to construct linters directly
11
+ # because the provided test helpers will automatically run when required
12
+ # in tests and specs.
13
+ #
14
+ # @example
15
+ # require 'rom/lint/spec'
16
+ #
17
+ #
18
+ # @api public
19
+ class Linter
20
+ # A failure raised by +complain+
21
+ Failure = Class.new(StandardError)
22
+
23
+ # Iterate over all lint methods
24
+ #
25
+ # @yield [String, ROM::Lint]
26
+ #
27
+ # @api public
28
+ def self.each_lint
29
+ return to_enum unless block_given?
30
+
31
+ lints.each { |lint| yield lint, self }
32
+ end
33
+
34
+ # Run a lint method
35
+ #
36
+ # @param [String] name
37
+ #
38
+ # @raise [ROM::Lint::Linter::Failure] if linting fails
39
+ #
40
+ # @api public
41
+ def lint(name)
42
+ before_lint
43
+ public_send name
44
+ after_lint
45
+ true # for assertions
46
+ end
47
+
48
+ # Return a list a lint methods
49
+ #
50
+ # @return [String]
51
+ #
52
+ # @api private
53
+ def self.lints
54
+ public_instance_methods(true).grep(/^lint_/).map(&:to_s)
55
+ end
56
+
57
+ private
58
+
59
+ # Raise a failure if a lint verification fails
60
+ #
61
+ # @raise [ROM::Lint::Linter::Failure]
62
+ #
63
+ # @api private
64
+ def complain(*args)
65
+ raise Failure, *args
66
+ end
67
+
68
+ # Hook method executed before each lint method run
69
+ #
70
+ # @api private
71
+ def before_lint; end
72
+
73
+ # Hook method executed after each lint method run
74
+ #
75
+ # @api private
76
+ def after_lint; end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/lint/gateway"
4
+ require "rom/lint/enumerable_dataset"
5
+
6
+ RSpec.shared_examples "a rom gateway" do
7
+ ROM::Lint::Gateway.each_lint do |name, linter|
8
+ it name do
9
+ result = linter.new(identifier, gateway, uri).lint(name)
10
+ expect(result).to be_truthy
11
+ end
12
+ end
13
+ end
14
+
15
+ RSpec.shared_examples "a rom enumerable dataset" do
16
+ ROM::Lint::EnumerableDataset.each_lint do |name, linter|
17
+ it name do
18
+ result = linter.new(dataset, data).lint(name)
19
+ expect(result).to be_truthy
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rom/lint/gateway"
4
+ require "rom/lint/enumerable_dataset"
5
+
6
+ module ROM
7
+ module Lint
8
+ # A module that helps to define test methods
9
+ module Test
10
+ # Defines a test method converting lint failures to assertions
11
+ #
12
+ # @param [String] name
13
+ #
14
+ # @api private
15
+ def define_test_method(name, &block)
16
+ define_method "test_#{name}" do
17
+ instance_eval(&block)
18
+ rescue ROM::Lint::Linter::Failure => e
19
+ raise Minitest::Assertion, e.message
20
+ end
21
+ end
22
+ end
23
+
24
+ # This is a simple lint-test for gateway class to ensure the
25
+ # basic interfaces are in place
26
+ #
27
+ # @example
28
+ #
29
+ # class MyGatewayTest < Minitest::Test
30
+ # include ROM::Lint::TestGateway
31
+ #
32
+ # def setup
33
+ # @gateway = MyGateway
34
+ # @uri = "super_db://something"
35
+ # end
36
+ # end
37
+ #
38
+ # @api public
39
+ module TestGateway
40
+ extend ROM::Lint::Test
41
+
42
+ # Returns the gateway identifier e.g. +:memory+
43
+ #
44
+ # @api public
45
+ attr_reader :identifier
46
+
47
+ # Returns the gateway class
48
+ #
49
+ # @api public
50
+ attr_reader :gateway
51
+
52
+ # Returns gateway's URI e.g. "super_db://something"
53
+ #
54
+ # @api public
55
+ attr_reader :uri
56
+
57
+ ROM::Lint::Gateway.each_lint do |name, linter|
58
+ define_test_method name do
59
+ assert linter.new(identifier, gateway, uri).lint(name)
60
+ end
61
+ end
62
+ end
63
+
64
+ # This is a simple lint-test for a gateway dataset class to ensure the
65
+ # basic behavior is correct
66
+ #
67
+ # @example
68
+ #
69
+ # class MyDatasetLintTest < Minitest::Test
70
+ # include ROM::Lint::TestEnumerableDataset
71
+ #
72
+ # def setup
73
+ # @data = [{ name: 'Jane', age: 24 }, { name: 'Joe', age: 25 }]
74
+ # @dataset = MyDataset.new(@data, [:name, :age])
75
+ # end
76
+ # end
77
+ # @api public
78
+ module TestEnumerableDataset
79
+ extend ROM::Lint::Test
80
+
81
+ # Returns the dataset instance
82
+ #
83
+ # @api public
84
+ attr_reader :dataset
85
+
86
+ # Returns the expected data
87
+ #
88
+ # @api public
89
+ attr_reader :data
90
+
91
+ ROM::Lint::EnumerableDataset.each_lint do |name, linter|
92
+ define_test_method name do
93
+ assert linter.new(dataset, data).lint(name)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
data/lib/rom/loader.rb ADDED
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "zeitwerk"
5
+
6
+ require "rom/support/inflector"
7
+
8
+ require "rom/types"
9
+ require "rom/initializer"
10
+ require "rom/loader"
11
+
12
+ module ROM
13
+ # AutoRegistration is used to load component files automatically from the provided directory path
14
+ #
15
+ # @api public
16
+ class Loader
17
+ extend Initializer
18
+
19
+ NamespaceType = Types::Strict::Bool | Types.Instance(Module)
20
+
21
+ PathnameType = Types.Constructor(Pathname, &Kernel.method(:Pathname))
22
+
23
+ InflectorType = Types.Interface(:camelize)
24
+
25
+ ComponentDirs = Types::Strict::Hash.constructor { |hash| hash.transform_values(&:to_s) }
26
+
27
+ DEFAULT_MAPPING = {
28
+ relations: "relations",
29
+ mappers: "mappers",
30
+ commands: "commands"
31
+ }.freeze
32
+
33
+ # @!attribute [r] directory
34
+ # @return [Pathname] The root path
35
+ param :root_directory, type: PathnameType.optional
36
+
37
+ # @!attribute [r] auto_load
38
+ # @return [Boolean]
39
+ # Whether files should be auto-loadable
40
+ option :auto_load, type: Types::Strict::Bool, default: -> { false }
41
+
42
+ # @!attribute [r] namespace
43
+ # @return [Boolean,String]
44
+ # The name of the top level namespace or true/false which
45
+ # enables/disables default top level namespace inferred from the dir name
46
+ option :namespace, type: NamespaceType, default: -> { true }
47
+
48
+ # @!attribute [r] component_dirs
49
+ # @return [Hash] component => dir-name map
50
+ option :component_dirs, type: ComponentDirs, default: -> { DEFAULT_MAPPING }
51
+
52
+ # @!attribute [r] inflector
53
+ # @return [Dry::Inflector] String inflector
54
+ # @api private
55
+ option :inflector, type: InflectorType, default: -> { Inflector }
56
+
57
+ # @!attribute [r] components
58
+ # @return [ROM::Components]
59
+ # @api private
60
+ option :components
61
+
62
+ # Load components
63
+ #
64
+ # @api private
65
+ def call
66
+ return if @loaded || root_directory.nil?
67
+
68
+ setup
69
+
70
+ backend.eager_load unless auto_load
71
+
72
+ @loaded = true
73
+ end
74
+
75
+ # @api private
76
+ def auto_load_component_file(type, key)
77
+ return unless component_dirs.include?(type)
78
+
79
+ const_parts = key.split(".").map { |name| inflector.camelize(name) }
80
+ const_parts.unshift(namespace_const_name) if namespace_const_name
81
+ const_name = const_parts.join("::")
82
+
83
+ inflector.constantize(const_name)
84
+ end
85
+
86
+ private
87
+
88
+ # @api private
89
+ def namespace_const_name
90
+ case namespace
91
+ when true
92
+ inflector.classify(root_directory.basename)
93
+ end
94
+ end
95
+
96
+ # @api private
97
+ def backend
98
+ @backend ||= Zeitwerk::Loader.new
99
+ end
100
+
101
+ # @api private
102
+ # rubocop:disable Metrics/AbcSize
103
+ def setup
104
+ backend.inflector = inflector
105
+
106
+ case namespace
107
+ when true
108
+ top_directory = root_directory.join("..").realpath
109
+
110
+ backend.push_dir(top_directory)
111
+
112
+ others = top_directory.children.select(&:directory?).reject { |dir| dir == root_directory }
113
+
114
+ others.each do |dir|
115
+ backend.ignore(dir)
116
+ end
117
+ when false
118
+ backend.push_dir(root_directory)
119
+ else
120
+ backend.push_dir(root_directory, namespace: namespace)
121
+ end
122
+
123
+ excluded_dirs.each do |dir|
124
+ backend.ignore(dir)
125
+ end
126
+
127
+ backend.on_load do |_, constant, path|
128
+ if (type = path_to_component_type(path))
129
+ begin
130
+ components.add(type, constant: constant, config: constant.config.component)
131
+ rescue StandardError => e
132
+ raise "Failed to load #{constant} from #{path}: #{e.message}"
133
+ end
134
+ end
135
+ end
136
+
137
+ backend.setup
138
+ end
139
+ # rubocop:enable Metrics/AbcSize
140
+
141
+ # @api private
142
+ def path_to_component_type(path)
143
+ return unless File.file?(path)
144
+
145
+ component_dirs
146
+ .detect { |_, dir|
147
+ path.start_with?(root_directory.join(dir).to_s)
148
+ }
149
+ &.first
150
+ end
151
+
152
+ # @api private
153
+ def excluded_dirs
154
+ root_directory
155
+ .children
156
+ .select(&:directory?)
157
+ .reject { |dir| component_dirs.values.include?(dir.basename.to_s) }
158
+ .map { |name| root_directory.join(name) }
159
+ end
160
+ end
161
+ end