hanami 2.0.0.alpha4 → 2.0.0.alpha5

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: 2188bc3aee5a164b6610c6bc97fad9f0bba0384bf11a80be27228e7f19dad4e0
4
- data.tar.gz: 7fa39987132cf6bfda0b0986a36894d08297c1df06b31354cfbeae934d73a813
3
+ metadata.gz: 9c8875d3320057cffe3af1a7bb2ff781cd8ad2145c446fc9e9190d344a4a48e6
4
+ data.tar.gz: c003bc2117815e10f2c15f458fb3bc6d94b480dfe9f2ec98e6458804f8a0d06d
5
5
  SHA512:
6
- metadata.gz: f7b1aad0186fcdaa94593ba4ff5d01cf4f8d4431aaffba173c3c795c2c0c76993a27da3aa503b0ce59fd67467615c86c50a4ab227986e38dd0986e91c30766a5
7
- data.tar.gz: cd2d8b82bc7f98e906686fdab7299282b1c1be4b29278c9ee5b0f9b3cefbb7d31fa83013a840b2b9fa2b8e2b2f3f8c0582ca3587b35ced91f65db6f853d67cf7
6
+ metadata.gz: 7b133de3c728132ef6255480b10068482d58cf1e2708df395f00cc5981cdccac70cbecdb2c8325cd822298d7c92c0ee0253ab1f8f9d95ddf84b0d6de556e7f3f
7
+ data.tar.gz: 42b525e3163e7bb73938b5e2fa444ccc15cc38def3e74225086f0e90bb0d805979e0a88a47703fcc73956d68598260ab366a1c155eb94b7114515b8ffc56671e
data/CHANGELOG.md CHANGED
@@ -1,6 +1,126 @@
1
1
  # Hanami
2
2
  The web, with simplicity.
3
3
 
4
+ ## v2.0.0.alpha5 - 2022-01-12
5
+ ### Changed
6
+ - [Luca Guidi] Sensible default configuration for application logger, with per-environment defaults:
7
+
8
+ The defaults are:
9
+
10
+ - In **production**, log for level `info`, send logs to `$stdout` in JSON format without colours
11
+ - In **development**, log for level `debug`, send logs to `$stdout` in single-line format with colours
12
+ - In **test**, log for level `debug`, send logs to `log/test.log` in single-line format without colours
13
+
14
+ To configure the logger:
15
+
16
+ ```ruby
17
+ module MyApp
18
+ class Application < Hanami::Application
19
+ config.logger.level = :info
20
+
21
+ config.logger.stream = $stdout
22
+ config.logger.stream = "/path/to/file"
23
+ config.logger.stream = StringIO.new
24
+
25
+ config.logger.format = :json
26
+ config.logger.format = MyCustomFormatter.new
27
+
28
+ config.logger.color = false # disable coloring
29
+ config.logger.color = MyCustomColorizer.new
30
+
31
+ config.logger.filters << "secret" # add
32
+ config.logger.filters += ["yet", "another"] # add
33
+ config.logger.filters = ["foo"] # replace
34
+
35
+ # See https://ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger.html
36
+ config.logger.options = ["daily"] # time based log rotation
37
+ config.logger.options = [0, 1048576] # size based log rotation
38
+ end
39
+ end
40
+ ```
41
+
42
+ To configure the logger for specific environments:
43
+
44
+ ```ruby
45
+ module MyApp
46
+ class Application < Hanami::Application
47
+ config.environment(:staging) do
48
+ config.logger.level = :info
49
+ end
50
+ end
51
+ end
52
+ ```
53
+
54
+ To assign a custom replacement logger object:
55
+
56
+ ```ruby
57
+ module MyApp
58
+ class Application < Hanami::Application
59
+ config.logger = MyCustomLogger.new
60
+ end
61
+ end
62
+ ```
63
+ - [Tim Riley] Comprehensive `config.source_dirs` setting
64
+
65
+ This replaces the previous `component_dir_paths` setting, and contains two nested settings:
66
+
67
+ - `config.source_dirs.component_dirs` (backed by `Dry::System::Config::ComponentDirs`), for directories of source files intended to be registered as components
68
+ - `config.source_dirs.autoload_paths`, for directories of source files not intended for registration as components, but still to be made accessible by the autoloader
69
+
70
+ To add and configure your own additional component dirs:
71
+
72
+ ```ruby
73
+ module MyApp
74
+ class Application < Hanami::Application
75
+ # Adding a simple component dir
76
+ config.source_dirs.component_dirs.add "serializers"
77
+
78
+ # Adding a component dir with custom configuration
79
+ config.source_dirs.component_dirs.add "serializers" do |dir|
80
+ dir.auto_register = proc { |component|
81
+ !component.identifier.start_with?("structs")
82
+ }
83
+ end
84
+ end
85
+ end
86
+ ```
87
+
88
+ To customize the configuration of the default component dirs ("lib", "actions", "repositories", "views"):
89
+
90
+ ```ruby
91
+ module MyApp
92
+ class Application < Hanami::Application
93
+ # Customising a default component dir
94
+ config.source_dirs.component_dirs.dir("lib").auto_register = proc { |component|
95
+ !component.identifier.start_with?("structs")
96
+ }
97
+
98
+ # Setting default config to apply to all component dirs
99
+ config.source_dirs.component_dirs.auto_register = proc { |component|
100
+ !component.identifier.start_with?("entities")
101
+ }
102
+
103
+ # Removing a default component dir
104
+ config.source_dirs.component_dirs.delete("views")
105
+ end
106
+ end
107
+ ```
108
+
109
+ To configure the autoload paths (defaulting to `["entities"]`):
110
+
111
+ ```ruby
112
+ module MyApp
113
+ class Application < Hanami::Application
114
+ # Adding your own autoload paths
115
+ config.source_dirs.autoload_paths << "structs"
116
+
117
+ # Or providing a full replacement
118
+ config.source_dirs.autoload_paths = ["structs"]
119
+ end
120
+ end
121
+ ```
122
+ - [Tim Riley] Application router is lazy loaded (not requiring application to be fully booted) and now available via `Hanami.rack_app` or `Hanami.application.rack_app`, instead of the previous `Hanami.app` (which required the app to be booted first).
123
+
4
124
  ## v2.0.0.alpha4 - 2021-12-07
5
125
  ### Added
6
126
  - [Luca Guidi] Manage Content Security Policy (CSP) with "zero-defaults" policy. New API to change CSP values and to disable the feature.
data/README.md CHANGED
@@ -58,7 +58,7 @@ You can give back to Open Source, by supporting Hanami development via a [donati
58
58
 
59
59
  ### Supporters
60
60
 
61
- * [Trung Lê](https://github.com/joneslee85)
61
+ * [Trung Lê](https://github.com/runlevel5)
62
62
  * [James Carlson](https://github.com/jxxcarlson)
63
63
  * [Creditas](https://www.creditas.com.br/)
64
64
 
data/hanami.gemspec CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
26
26
  spec.add_dependency "dry-core", "~> 0.4"
27
27
  spec.add_dependency "dry-inflector", "~> 0.2", ">= 0.2.1"
28
28
  spec.add_dependency "dry-monitor"
29
- spec.add_dependency "dry-system", "~> 0.19", ">= 0.21.0"
29
+ spec.add_dependency "dry-system", "~> 0.22", ">= 0.22.0"
30
30
  spec.add_dependency "hanami-cli", "~> 2.0.alpha"
31
31
  spec.add_dependency "hanami-utils", "~> 2.0.alpha"
32
32
  spec.add_dependency "zeitwerk", "~> 2.4"
@@ -9,7 +9,7 @@ Hanami.application.register_bootable :rack_logger do |container|
9
9
 
10
10
  rack_logger = Hanami::Web::RackLogger.new(
11
11
  container[:logger],
12
- filter_params: Hanami.application.configuration.logger.filter_params
12
+ filter_params: Hanami.application.configuration.logger.filters
13
13
  )
14
14
 
15
15
  rack_logger.attach container[:rack_monitor]
@@ -10,8 +10,8 @@ module Hanami
10
10
  class Router < ::Hanami::Router
11
11
  # @since 2.0.0
12
12
  # @api private
13
- def initialize(routes:, stack: Routing::Middleware::Stack.new, **kwargs, &blk)
14
- @stack = stack
13
+ def initialize(routes:, middleware_stack: Routing::Middleware::Stack.new, **kwargs, &blk)
14
+ @middleware_stack = middleware_stack
15
15
  instance_eval(&blk)
16
16
  super(**kwargs, &routes)
17
17
  end
@@ -21,20 +21,20 @@ module Hanami
21
21
  def freeze
22
22
  return self if frozen?
23
23
 
24
- remove_instance_variable(:@stack)
24
+ remove_instance_variable(:@middleware_stack)
25
25
  super
26
26
  end
27
27
 
28
28
  # @since 2.0.0
29
29
  # @api private
30
30
  def use(middleware, *args, &blk)
31
- @stack.use(middleware, *args, &blk)
31
+ @middleware_stack.use(middleware, *args, &blk)
32
32
  end
33
33
 
34
34
  # @since 2.0.0
35
35
  # @api private
36
36
  def scope(*args, &blk)
37
- @stack.with(args.first) do
37
+ @middleware_stack.with(args.first) do
38
38
  super
39
39
  end
40
40
  end
@@ -50,9 +50,9 @@ module Hanami
50
50
  # @since 2.0.0
51
51
  # @api private
52
52
  def to_rack_app
53
- return self if @stack.empty?
53
+ return self if @middleware_stack.empty?
54
54
 
55
- @stack.to_rack_app(self)
55
+ @middleware_stack.to_rack_app(self)
56
56
  end
57
57
  end
58
58
  end
@@ -0,0 +1,36 @@
1
+ # # frozen_string_literal: true
2
+
3
+ # require "hanami/application/router"
4
+
5
+ # Hanami.application.register_bootable :router do
6
+ # start do
7
+ # configuration = Hanami.application.configuration
8
+
9
+ # routes = begin
10
+ # require File.join(configuration.root, configuration.router.routes_path)
11
+ # routes_class = Hanami.application.send(:autodiscover_application_constant, configuration.router.routes_class_name) # WIP private
12
+ # routes_class.routes
13
+ # rescue LoadError
14
+ # proc {}
15
+ # end
16
+
17
+ # resolver = configuration.router.resolver.new(
18
+ # slices: Hanami.application.slices,
19
+ # inflector: Hanami.application.inflector # TODO: use container[:inflector]?
20
+ # )
21
+
22
+ # router = Hanami::Application::Router.new(
23
+ # routes: routes,
24
+ # resolver: resolver,
25
+ # **configuration.router.options,
26
+ # ) do
27
+ # use Hanami.application[:rack_monitor]
28
+
29
+ # Hanami.application.config.for_each_middleware do |m, *args, &block|
30
+ # use(m, *args, &block)
31
+ # end
32
+ # end
33
+
34
+ # register :router, router
35
+ # end
36
+ # end
@@ -8,9 +8,6 @@ require "rack"
8
8
  require "zeitwerk"
9
9
  require_relative "slice"
10
10
  require_relative "application/autoloader/inflector_adapter"
11
- require_relative "application/router"
12
- require_relative "application/routes"
13
- require_relative "application/settings"
14
11
 
15
12
  module Hanami
16
13
  # Hanami application class
@@ -24,10 +21,9 @@ module Hanami
24
21
  @_mutex.synchronize do
25
22
  klass.class_eval do
26
23
  @_mutex = Mutex.new
27
- @_configuration = Hanami::Configuration.new(env: Hanami.env)
24
+ @_configuration = Hanami::Configuration.new(application_name: name, env: Hanami.env)
28
25
 
29
26
  extend ClassMethods
30
- include InstanceMethods
31
27
  end
32
28
 
33
29
  klass.send :prepare_base_load_path
@@ -76,10 +72,31 @@ module Hanami
76
72
  self
77
73
  end
78
74
 
75
+ def boot(&block)
76
+ return self if booted?
77
+
78
+ init
79
+
80
+ container.finalize!(&block)
81
+
82
+ slices.values.each(&:boot)
83
+
84
+ @booted = true
85
+ self
86
+ end
87
+
88
+ def shutdown
89
+ container.shutdown!
90
+ end
91
+
79
92
  def inited?
80
93
  @inited
81
94
  end
82
95
 
96
+ def booted?
97
+ @booted
98
+ end
99
+
83
100
  def autoloader
84
101
  raise "Application not init'ed" unless defined?(@autoloader)
85
102
 
@@ -98,6 +115,18 @@ module Hanami
98
115
  @deps_module
99
116
  end
100
117
 
118
+ def router
119
+ raise "Application not init'ed" unless inited?
120
+
121
+ @_mutex.synchronize do
122
+ @_router ||= load_router
123
+ end
124
+ end
125
+
126
+ def rack_app
127
+ @rack_app ||= router.to_rack_app
128
+ end
129
+
101
130
  def slices
102
131
  @slices ||= {}
103
132
  end
@@ -142,38 +171,12 @@ module Hanami
142
171
  container.resolve(*args)
143
172
  end
144
173
 
145
- def boot(&block)
146
- return self if booted?
147
-
148
- init
149
-
150
- load_router
151
-
152
- container.finalize!(&block)
153
-
154
- slices.values.each(&:boot)
155
-
156
- @booted = true
157
- self
158
- end
159
-
160
- def booted?
161
- @booted
162
- end
163
-
164
- def shutdown
165
- container.shutdown!
166
- end
167
-
168
174
  def settings
169
175
  @_settings ||= load_settings
170
176
  end
171
177
 
172
- MODULE_DELIMITER = "::"
173
- private_constant :MODULE_DELIMITER
174
-
175
178
  def namespace
176
- inflector.constantize(name.split(MODULE_DELIMITER)[0..-2].join(MODULE_DELIMITER))
179
+ configuration.namespace
177
180
  end
178
181
 
179
182
  def namespace_name
@@ -185,7 +188,7 @@ module Hanami
185
188
  end
186
189
 
187
190
  def application_name
188
- inflector.underscore(namespace).to_sym
191
+ configuration.application_name
189
192
  end
190
193
 
191
194
  def root
@@ -211,41 +214,6 @@ module Hanami
211
214
  providers.detect { |provider| component_name.include?(provider.namespace.to_s) }
212
215
  end
213
216
 
214
- def router
215
- @_mutex.synchronize do
216
- @_router ||= load_router
217
- end
218
- end
219
-
220
- def load_router
221
- Router.new(
222
- routes: routes,
223
- resolver: resolver,
224
- **configuration.router.options,
225
- ) do
226
- use Hanami.application[:rack_monitor]
227
-
228
- Hanami.application.config.for_each_middleware do |m, *args, &block|
229
- use(m, *args, &block)
230
- end
231
- end
232
- end
233
-
234
- def routes
235
- require File.join(configuration.root, configuration.router.routes_path)
236
- routes_class = autodiscover_application_constant(configuration.router.routes_class_name)
237
- routes_class.routes
238
- rescue LoadError
239
- proc {}
240
- end
241
-
242
- def resolver
243
- config.router.resolver.new(
244
- slices: slices,
245
- inflector: inflector
246
- )
247
- end
248
-
249
217
  private
250
218
 
251
219
  def prepare_base_load_path
@@ -343,6 +311,8 @@ module Hanami
343
311
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
344
312
 
345
313
  def load_settings
314
+ require_relative "application/settings"
315
+
346
316
  prepare_base_load_path
347
317
  require File.join(configuration.root, configuration.settings_path)
348
318
  settings_class = autodiscover_application_constant(configuration.settings_class_name)
@@ -351,27 +321,46 @@ module Hanami
351
321
  Settings.new
352
322
  end
353
323
 
324
+ MODULE_DELIMITER = "::"
325
+ private_constant :MODULE_DELIMITER
326
+
354
327
  def autodiscover_application_constant(constants)
355
328
  inflector.constantize([namespace_name, *constants].join(MODULE_DELIMITER))
356
329
  end
357
- end
358
- # rubocop:enable Metrics/ModuleLength
359
330
 
360
- # Application instance interface
361
- module InstanceMethods
362
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
363
- def initialize(application = self.class)
331
+ def load_router
364
332
  require_relative "application/router"
365
333
 
366
- application.boot
334
+ Router.new(
335
+ routes: load_routes,
336
+ resolver: router_resolver,
337
+ **configuration.router.options,
338
+ ) do
339
+ use Hanami.application[:rack_monitor]
367
340
 
368
- @app = application.router.to_rack_app
341
+ Hanami.application.config.for_each_middleware do |m, *args, &block|
342
+ use(m, *args, &block)
343
+ end
344
+ end
345
+ end
346
+
347
+ def load_routes
348
+ require_relative "application/routes"
349
+
350
+ require File.join(configuration.root, configuration.router.routes_path)
351
+ routes_class = autodiscover_application_constant(configuration.router.routes_class_name)
352
+ routes_class.routes
353
+ rescue LoadError
354
+ proc {}
369
355
  end
370
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
371
356
 
372
- def call(env)
373
- @app.call(env)
357
+ def router_resolver
358
+ config.router.resolver.new(
359
+ slices: slices,
360
+ inflector: inflector
361
+ )
374
362
  end
375
363
  end
364
+ # rubocop:enable Metrics/ModuleLength
376
365
  end
377
366
  end
@@ -0,0 +1,44 @@
1
+ module Hanami
2
+ module Boot
3
+ module SourceDirs
4
+ def self.setup_component_dir!(component_dir, slice, container)
5
+ # TODO: this `== "lib"` check should be codified into a method somewhere
6
+ if component_dir.path == "lib"
7
+ # Expect component files in the root of the lib
8
+ # component dir to define classes inside the slice's namespace.
9
+ #
10
+ # e.g. "lib/foo.rb" should define SliceNamespace::Foo, and will be
11
+ # registered as "foo"
12
+ component_dir.namespaces.root(key: nil, const: slice.namespace_path)
13
+
14
+ slice.application.autoloader.push_dir(slice.root.join("lib"), namespace: slice.namespace)
15
+
16
+ container.config.component_dirs.add(component_dir)
17
+ else
18
+ # Expect component files in the root of these component dirs to define
19
+ # classes inside a namespace matching the dir.
20
+ #
21
+ # e.g. "actions/foo.rb" should define SliceNamespace::Actions::Foo, and
22
+ # will be registered as "actions.foo"
23
+
24
+ dir_namespace_path = File.join(slice.namespace_path, component_dir.path)
25
+
26
+ autoloader_namespace = begin
27
+ slice.inflector.constantize(slice.inflector.camelize(dir_namespace_path))
28
+ rescue NameError
29
+ slice.namespace.const_set(slice.inflector.camelize(component_dir.path), Module.new)
30
+ end
31
+
32
+ # TODO: do we need to do something special to clear out any previously configured root namespace here?
33
+ component_dir.namespaces.root(const: dir_namespace_path, key: component_dir.path) # TODO: do we need to swap path delimiters for key delimiters here?
34
+ container.config.component_dirs.add(component_dir)
35
+
36
+ slice.application.autoloader.push_dir(
37
+ slice.root.join(component_dir.path),
38
+ namespace: autoloader_namespace
39
+ )
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -13,14 +13,58 @@ module Hanami
13
13
 
14
14
  protected :config
15
15
 
16
+ setting :application_name
17
+
18
+ setting :level
19
+
20
+ setting :stream
21
+
22
+ setting :formatter
23
+
24
+ setting :colors
25
+
26
+ setting :filters, default: %w[_csrf password password_confirmation].freeze
27
+
28
+ setting :options, default: [], constructor: ->(value) { Array(value).flatten }, cloneable: true
29
+
16
30
  setting :logger_class, default: Hanami::Logger
17
31
 
18
- setting :options, default: {level: :debug}
32
+ def initialize(env:, application_name:)
33
+ @env = env
34
+ @application_name = application_name
35
+
36
+ config.level = case env
37
+ when :production
38
+ :info
39
+ else
40
+ :debug
41
+ end
19
42
 
20
- # Currently used for logging of Rack requests only.
21
- #
22
- # TODO: incorporate this into the standard logging some way or another
23
- setting :filter_params, default: %w[_csrf password password_confirmation].freeze
43
+ config.stream = case env
44
+ when :test
45
+ File.join("log", "#{env}.log")
46
+ else
47
+ $stdout
48
+ end
49
+
50
+ config.formatter = case env
51
+ when :production
52
+ :json
53
+ end
54
+
55
+ config.colors = case env
56
+ when :production, :test
57
+ false
58
+ end
59
+ end
60
+
61
+ def finalize!
62
+ config.application_name = @application_name.call
63
+ end
64
+
65
+ def instance
66
+ logger_class.new(application_name, *options, stream: stream, level: level, formatter: formatter, filter: filters, colorizer: colors)
67
+ end
24
68
 
25
69
  private
26
70
 
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require "dry/system/config/component_dirs"
5
+
6
+ module Hanami
7
+ class Configuration
8
+ # Configuration for slice source dirs
9
+ #
10
+ # @since 2.0.0
11
+ class SourceDirs
12
+ DEFAULT_COMPONENT_DIR_PATHS = %w[lib actions repositories views].freeze
13
+ private_constant :DEFAULT_COMPONENT_DIR_PATHS
14
+
15
+ include Dry::Configurable
16
+
17
+ setting :component_dirs,
18
+ default: Dry::System::Config::ComponentDirs.new.tap { |dirs|
19
+ DEFAULT_COMPONENT_DIR_PATHS.each do |path|
20
+ dirs.add path
21
+ end
22
+ },
23
+ cloneable: true
24
+
25
+ setting :autoload_paths, default: %w[entities]
26
+
27
+ private
28
+
29
+ def method_missing(name, *args, &block)
30
+ if config.respond_to?(name)
31
+ config.public_send(name, *args, &block)
32
+ else
33
+ super
34
+ end
35
+ end
36
+
37
+ def respond_to_missing?(name, _include_all = false)
38
+ config.respond_to?(name) || super
39
+ end
40
+ end
41
+ end
42
+ end
@@ -12,6 +12,7 @@ require_relative "configuration/logger"
12
12
  require_relative "configuration/middleware"
13
13
  require_relative "configuration/router"
14
14
  require_relative "configuration/sessions"
15
+ require_relative "configuration/source_dirs"
15
16
 
16
17
  module Hanami
17
18
  # Hanami application configuration
@@ -25,6 +26,9 @@ module Hanami
25
26
  DEFAULT_ENVIRONMENTS = Concurrent::Hash.new { |h, k| h[k] = Concurrent::Array.new }
26
27
  private_constant :DEFAULT_ENVIRONMENTS
27
28
 
29
+ MODULE_DELIMITER = "::"
30
+ private_constant :MODULE_DELIMITER
31
+
28
32
  attr_reader :actions
29
33
  attr_reader :middleware
30
34
  attr_reader :router
@@ -33,7 +37,9 @@ module Hanami
33
37
  attr_reader :environments
34
38
  private :environments
35
39
 
36
- def initialize(env:)
40
+ def initialize(application_name:, env:)
41
+ @namespace = application_name.split(MODULE_DELIMITER)[0..-2].join(MODULE_DELIMITER)
42
+
37
43
  @environments = DEFAULT_ENVIRONMENTS.clone
38
44
  config.env = env
39
45
 
@@ -42,6 +48,8 @@ module Hanami
42
48
  self.root = Dir.pwd
43
49
  self.settings_store = Application::Settings::DotenvStore.new.with_dotenv_loaded
44
50
 
51
+ config.logger = Configuration::Logger.new(env: env, application_name: method(:application_name))
52
+
45
53
  @assets = begin
46
54
  require_path = "hanami/assets/application_configuration"
47
55
  require require_path
@@ -101,6 +109,14 @@ module Hanami
101
109
  super
102
110
  end
103
111
 
112
+ def namespace
113
+ inflector.constantize(@namespace)
114
+ end
115
+
116
+ def application_name
117
+ inflector.underscore(@namespace).to_sym
118
+ end
119
+
104
120
  setting :env
105
121
 
106
122
  def env=(new_env)
@@ -116,14 +132,14 @@ module Hanami
116
132
  self.inflector = Dry::Inflector.new(&block)
117
133
  end
118
134
 
119
- setting :logger, default: Configuration::Logger.new, cloneable: true
135
+ setting :logger, cloneable: true
120
136
 
121
137
  def logger=(logger_instance)
122
138
  @logger_instance = logger_instance
123
139
  end
124
140
 
125
141
  def logger_instance
126
- @logger_instance || logger.logger_class.new(**logger.options)
142
+ @logger_instance || logger.instance
127
143
  end
128
144
 
129
145
  setting :settings_path, default: File.join("config", "settings")
@@ -140,9 +156,7 @@ module Hanami
140
156
  # slice, etc.
141
157
  setting :slices, default: {}, constructor: :dup.to_proc
142
158
 
143
- # TODO: turn this into a richer "source dirs" setting that can support enabling
144
- # of container component loading as an opt in behvior
145
- setting :component_dir_paths, default: %w[actions repositories views]
159
+ setting :source_dirs, default: Configuration::SourceDirs.new, cloneable: true
146
160
 
147
161
  def slice(slice_name, &block)
148
162
  slices[slice_name] = block
data/lib/hanami/slice.rb CHANGED
@@ -111,47 +111,69 @@ module Hanami
111
111
  config.root = root
112
112
  config.bootable_dirs = ["config/boot"]
113
113
 
114
- # Add the "lib" component dir; all slices will load components from lib
115
- if root.join("lib").directory?
116
- config.component_dirs.add("lib") do |component_dir|
117
- # Expect component files in the root of the lib
118
- # component dir to define classes inside the slice's namespace.
119
- #
120
- # e.g. "lib/foo.rb" should define SliceNamespace::Foo, and will be
121
- # registered as "foo"
122
- component_dir.namespaces.root(key: nil, const: namespace_path)
114
+ # Add component dirs for each configured component path
115
+ application.configuration.source_dirs.component_dirs.each do |component_dir|
116
+ next unless root.join(component_dir.path).directory?
123
117
 
124
- application.autoloader.push_dir(root.join("lib"), namespace: namespace)
125
- end
126
- end
118
+ component_dir = component_dir.dup
127
119
 
128
- # Add component dirs for each configured component path
129
- application.configuration.component_dir_paths.each do |slice_dir|
130
- next unless root.join(slice_dir).directory?
120
+ # TODO: this `== "lib"` check should be codified into a method somewhere
121
+ if component_dir.path == "lib"
122
+ # Expect component files in the root of the lib/ component dir to define
123
+ # classes inside the slice's namespace.
124
+ #
125
+ # e.g. "lib/foo.rb" should define SliceNamespace::Foo, to be registered as
126
+ # "foo"
127
+ component_dir.namespaces.delete_root
128
+ component_dir.namespaces.add_root(key: nil, const: namespace_path)
129
+
130
+ config.component_dirs.add(component_dir)
131
131
 
132
- config.component_dirs.add(slice_dir) do |component_dir|
133
- # Expect component files in the root of these component dirs to define
134
- # classes inside a namespace matching the dir.
132
+ application.autoloader.push_dir(root.join("lib"), namespace: namespace)
133
+ else
134
+ # Expect component files in the root of non-lib/ component dirs to define
135
+ # classes inside a namespace matching that dir.
135
136
  #
136
- # e.g. "actions/foo.rb" should define SliceNamespace::Actions::Foo, and
137
- # will be registered as "actions.foo"
137
+ # e.g. "actions/foo.rb" should define SliceNamespace::Actions::Foo, to be
138
+ # registered as "actions.foo"
138
139
 
139
- dir_namespace_path = File.join(namespace_path, slice_dir)
140
+ dir_namespace_path = File.join(namespace_path, component_dir.path)
140
141
 
141
142
  autoloader_namespace = begin
142
143
  inflector.constantize(inflector.camelize(dir_namespace_path))
143
144
  rescue NameError
144
- namespace.const_set(inflector.camelize(slice_dir), Module.new)
145
+ namespace.const_set(inflector.camelize(component_dir.path), Module.new)
145
146
  end
146
147
 
147
- component_dir.namespaces.root(const: dir_namespace_path, key: slice_dir)
148
+ component_dir.namespaces.delete_root
149
+ component_dir.namespaces.add_root(const: dir_namespace_path, key: component_dir.path) # TODO: do we need to swap path delimiters for key delimiters here?
150
+
151
+ config.component_dirs.add(component_dir)
148
152
 
149
153
  application.autoloader.push_dir(
150
- container.root.join(slice_dir),
154
+ container.root.join(component_dir.path),
151
155
  namespace: autoloader_namespace
152
156
  )
153
157
  end
154
158
  end
159
+
160
+ # Pass configured autoload dirs to the autoloader
161
+ application.configuration.source_dirs.autoload_paths.each do |autoload_path|
162
+ next unless root.join(autoload_path).directory?
163
+
164
+ dir_namespace_path = File.join(namespace_path, autoload_path)
165
+
166
+ autoloader_namespace = begin
167
+ inflector.constantize(inflector.camelize(dir_namespace_path))
168
+ rescue NameError
169
+ namespace.const_set(inflector.camelize(autoload_path), Module.new)
170
+ end
171
+
172
+ application.autoloader.push_dir(
173
+ container.root.join(autoload_path),
174
+ namespace: autoloader_namespace
175
+ )
176
+ end
155
177
  end
156
178
  end
157
179
 
@@ -8,7 +8,7 @@ module Hanami
8
8
  module Version
9
9
  # @since 0.9.0
10
10
  # @api private
11
- VERSION = "2.0.0.alpha4"
11
+ VERSION = "2.0.0.alpha5"
12
12
 
13
13
  # @since 0.9.0
14
14
  # @api private
data/lib/hanami.rb CHANGED
@@ -31,20 +31,8 @@ module Hanami
31
31
  end
32
32
  end
33
33
 
34
- def self.app
35
- @_mutex.synchronize do
36
- raise "Hanami.app not configured" unless defined?(@_app)
37
-
38
- @_app
39
- end
40
- end
41
-
42
- def self.app=(app)
43
- @_mutex.synchronize do
44
- raise "Hanami.app already configured" if defined?(@_app)
45
-
46
- @_app = app
47
- end
34
+ def self.rack_app
35
+ application.rack_app
48
36
  end
49
37
 
50
38
  def self.env
@@ -63,14 +51,8 @@ module Hanami
63
51
  application.init
64
52
  end
65
53
 
66
- def self.boot(web: true)
67
- if defined?(@_app)
68
- @_app
69
- else
70
- application.boot
71
-
72
- @_app = application.new if web
73
- end
54
+ def self.boot
55
+ application.boot
74
56
  end
75
57
 
76
58
  def self.shutdown
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanami
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.alpha4
4
+ version: 2.0.0.alpha5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-07 00:00:00.000000000 Z
11
+ date: 2022-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -104,20 +104,20 @@ dependencies:
104
104
  requirements:
105
105
  - - "~>"
106
106
  - !ruby/object:Gem::Version
107
- version: '0.19'
107
+ version: '0.22'
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: 0.21.0
110
+ version: 0.22.0
111
111
  type: :runtime
112
112
  prerelease: false
113
113
  version_requirements: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '0.19'
117
+ version: '0.22'
118
118
  - - ">="
119
119
  - !ruby/object:Gem::Version
120
- version: 0.21.0
120
+ version: 0.22.0
121
121
  - !ruby/object:Gem::Dependency
122
122
  name: hanami-cli
123
123
  requirement: !ruby/object:Gem::Requirement
@@ -231,11 +231,13 @@ files:
231
231
  - lib/hanami/application/routing/resolver.rb
232
232
  - lib/hanami/application/routing/resolver/node.rb
233
233
  - lib/hanami/application/routing/resolver/trie.rb
234
+ - lib/hanami/application/routing/router.rb
234
235
  - lib/hanami/application/settings.rb
235
236
  - lib/hanami/application/settings/dotenv_store.rb
236
237
  - lib/hanami/assets/application_configuration.rb
237
238
  - lib/hanami/assets/configuration.rb
238
239
  - lib/hanami/boot.rb
240
+ - lib/hanami/boot/source_dirs.rb
239
241
  - lib/hanami/cli/application/cli.rb
240
242
  - lib/hanami/cli/application/command.rb
241
243
  - lib/hanami/cli/application/commands.rb
@@ -250,6 +252,7 @@ files:
250
252
  - lib/hanami/configuration/null_configuration.rb
251
253
  - lib/hanami/configuration/router.rb
252
254
  - lib/hanami/configuration/sessions.rb
255
+ - lib/hanami/configuration/source_dirs.rb
253
256
  - lib/hanami/init.rb
254
257
  - lib/hanami/server.rb
255
258
  - lib/hanami/setup.rb
@@ -276,7 +279,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
276
279
  - !ruby/object:Gem::Version
277
280
  version: 1.3.1
278
281
  requirements: []
279
- rubygems_version: 3.2.29
282
+ rubygems_version: 3.2.3
280
283
  signing_key:
281
284
  specification_version: 4
282
285
  summary: The web, with simplicity