hanami 2.0.0.alpha4 → 2.0.0.alpha7
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 +4 -4
- data/CHANGELOG.md +256 -0
- data/README.md +3 -7
- data/hanami.gemspec +5 -4
- data/lib/hanami/application/container/{boot → providers}/inflector.rb +1 -1
- data/lib/hanami/application/container/{boot → providers}/logger.rb +1 -1
- data/lib/hanami/application/container/providers/rack_logger.rb +15 -0
- data/lib/hanami/application/container/{boot → providers}/rack_monitor.rb +2 -2
- data/lib/hanami/application/container/{boot → providers}/routes_helper.rb +1 -1
- data/lib/hanami/application/container/{boot → providers}/settings.rb +1 -1
- data/lib/hanami/application/router.rb +7 -7
- data/lib/hanami/application/routing/router.rb +36 -0
- data/lib/hanami/application/slice_registrar.rb +107 -0
- data/lib/hanami/application.rb +124 -202
- data/lib/hanami/boot/source_dirs.rb +44 -0
- data/lib/hanami/cli/application/cli.rb +1 -1
- data/lib/hanami/configuration/actions.rb +90 -0
- data/lib/hanami/configuration/logger.rb +49 -5
- data/lib/hanami/configuration/source_dirs.rb +42 -0
- data/lib/hanami/configuration.rb +19 -24
- data/lib/hanami/constants.rb +23 -0
- data/lib/hanami/errors.rb +12 -0
- data/lib/hanami/{init.rb → prepare.rb} +1 -1
- data/lib/hanami/slice.rb +198 -112
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +27 -51
- data/lib/hanami.rb +19 -29
- metadata +23 -16
- data/lib/hanami/application/autoloader/inflector_adapter.rb +0 -22
- data/lib/hanami/application/container/boot/rack_logger.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8a7910a3dd4b6e7d82711df4f181de1e8c63fbb91981542d2eb067d2cfe06b4
|
4
|
+
data.tar.gz: 2d09e35743119e1e630f196780df68cf7b7685c2d6e3208fe0c7804816ccb45f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51ecc6d32c036bfc2fae43498ca6f88bb937317e610d24e499a069b02ff4c8a49b253684ba4fe47d6f67e2c2fc2b0a989460fc1462b397ddd1a5ec539ace2b7c
|
7
|
+
data.tar.gz: aa3a1f17f9304605135391c3cb915f61cabbd5cbb2e4351e70be122353d36a85af979bbf4710ffdbfa27e1e77aea023f3de7ddda2b547399e1d96b396134da9c
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,262 @@
|
|
1
1
|
# Hanami
|
2
2
|
The web, with simplicity.
|
3
3
|
|
4
|
+
## v2.0.0.alpha7 - 2020-03-08
|
5
|
+
|
6
|
+
## Added
|
7
|
+
- [Tim Riley] Introduced `Hanami::ApplicationLoadError` and `Hanami::SliceLoadError` exceptions to represent errors encountered during application and slice loading.
|
8
|
+
- [Tim Riley] `Hanami::Slice.shutdown` can be used to stop all the providers in a slice
|
9
|
+
|
10
|
+
## Changed
|
11
|
+
- [Tim Riley] Slices are now represented as concrete classes (such as `Main::Slice`) inheriting from `Hanami::Slice`, as opposed to _instances_ of `Hanami::Slice`. You may create your own definitions for these slices in `config/slices/[slice_name].rb`, which you can then use for customising per-slice config and behavior, e.g.
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
# config/slices/main.rb:
|
15
|
+
|
16
|
+
module Main
|
17
|
+
class Slice < Hanami::Slice
|
18
|
+
# slice config here
|
19
|
+
end
|
20
|
+
end
|
21
|
+
```
|
22
|
+
- [Tim Riley] Application-level `config.slice(slice_name, &block)` setting has been removed in favour of slice configuration within concrete slice class definitions
|
23
|
+
- [Tim Riley] You can configure your slice imports inside your slice classes, e.g.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
# config/slices/main.rb:
|
27
|
+
|
28
|
+
module Main
|
29
|
+
class Slice < Hanami::Slice
|
30
|
+
# Import all exported components from "search" slice
|
31
|
+
import from: :search
|
32
|
+
end
|
33
|
+
end
|
34
|
+
```
|
35
|
+
- [Tim Riley] You can configure your slice exports inside your slice classes, e.g.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# config/slices/search.rb:
|
39
|
+
|
40
|
+
module Search
|
41
|
+
class Slice < Hanami::Slice
|
42
|
+
# Export the "index_entity" component only
|
43
|
+
export ["index_entity"]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
```
|
47
|
+
- [Tim Riley] For advanced cases, you can configure your slice's container via a `prepare_container` block:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# config/slices/search.rb:
|
51
|
+
|
52
|
+
module Search
|
53
|
+
class Slice < Hanami::Slice
|
54
|
+
prepare_container do |container|
|
55
|
+
# `container` object is available here, with
|
56
|
+
# slice-specific configuration already applied
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
```
|
61
|
+
- [Tim Riley] `Hanami::Application.shutdown` will now also shutdown all registered slices
|
62
|
+
|
63
|
+
## v2.0.0.alpha6 - 2022-02-10
|
64
|
+
### Added
|
65
|
+
- [Luca Guidi] Official support for Ruby: MRI 3.1
|
66
|
+
- [Tim Riley] Introduce partial Slice imports and exports. It allows to selectively export a functionality from a slice and import into another.
|
67
|
+
|
68
|
+
Import from `search` slice, uses `search` as the imported key namespace:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
# config/application.rb
|
72
|
+
|
73
|
+
module MyApp
|
74
|
+
class Application < Hanami::Application
|
75
|
+
config.slice(:admin) do
|
76
|
+
import(from: :search)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
Import from `search` slice with custom namespace:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# config/application.rb
|
86
|
+
|
87
|
+
module MyApp
|
88
|
+
class Application < Hanami::Application
|
89
|
+
config.slice(:admin) do
|
90
|
+
import(from: :search, as: :search_engine)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
Import specific keys from `search` slice
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
# config/application.rb
|
100
|
+
|
101
|
+
module MyApp
|
102
|
+
class Application < Hanami::Application
|
103
|
+
config.slice(:admin) do
|
104
|
+
import(keys: ["run_query"], from: :search)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
Export only specific keys from `search` slice, and import them in `admin`
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
# config/application.rb
|
114
|
+
|
115
|
+
module MyApp
|
116
|
+
class Application < Hanami::Application
|
117
|
+
config.slice(:admin) do
|
118
|
+
import(from: :search)
|
119
|
+
end
|
120
|
+
|
121
|
+
config.slice(:search) do
|
122
|
+
container.config.exports = %w[run_query index_item]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
```
|
127
|
+
|
128
|
+
### Fixed
|
129
|
+
- [Luca Guidi] Ensure request logger to respect logger formatter option.
|
130
|
+
|
131
|
+
### Changed
|
132
|
+
- [Luca Guidi] Drop support for Ruby: MRI 2.6 and 2.7.
|
133
|
+
- [Tim Riley] `Hanami.init` => `Hanami.prepare` and `hanami/init` => `hanami/prepare`
|
134
|
+
- [Tim Riley] `Hanami.register_bootable` => `Hanami.register_provider`
|
135
|
+
- [Tim Riley] `Hanami.start_bootable` => `Hanami.start`
|
136
|
+
- [Tim Riley] `Hanami::Slice#init` => `Hanami::Slice#prepare`
|
137
|
+
- [Tim Riley] `Hanami::Slice#register_bootable` => `Hanami::Slice#register_provider`
|
138
|
+
- [Tim Riley] `Hanami::Slice#start_bootable` => `Hanami::Slice#start`
|
139
|
+
|
140
|
+
## v2.0.0.alpha5 - 2022-01-12
|
141
|
+
### Changed
|
142
|
+
- [Luca Guidi] Sensible default configuration for application logger, with per-environment defaults:
|
143
|
+
|
144
|
+
The defaults are:
|
145
|
+
|
146
|
+
- In **production**, log for level `info`, send logs to `$stdout` in JSON format without colours
|
147
|
+
- In **development**, log for level `debug`, send logs to `$stdout` in single-line format with colours
|
148
|
+
- In **test**, log for level `debug`, send logs to `log/test.log` in single-line format without colours
|
149
|
+
|
150
|
+
To configure the logger:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
module MyApp
|
154
|
+
class Application < Hanami::Application
|
155
|
+
config.logger.level = :info
|
156
|
+
|
157
|
+
config.logger.stream = $stdout
|
158
|
+
config.logger.stream = "/path/to/file"
|
159
|
+
config.logger.stream = StringIO.new
|
160
|
+
|
161
|
+
config.logger.format = :json
|
162
|
+
config.logger.format = MyCustomFormatter.new
|
163
|
+
|
164
|
+
config.logger.color = false # disable coloring
|
165
|
+
config.logger.color = MyCustomColorizer.new
|
166
|
+
|
167
|
+
config.logger.filters << "secret" # add
|
168
|
+
config.logger.filters += ["yet", "another"] # add
|
169
|
+
config.logger.filters = ["foo"] # replace
|
170
|
+
|
171
|
+
# See https://ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger.html
|
172
|
+
config.logger.options = ["daily"] # time based log rotation
|
173
|
+
config.logger.options = [0, 1048576] # size based log rotation
|
174
|
+
end
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
To configure the logger for specific environments:
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
module MyApp
|
182
|
+
class Application < Hanami::Application
|
183
|
+
config.environment(:staging) do
|
184
|
+
config.logger.level = :info
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
```
|
189
|
+
|
190
|
+
To assign a custom replacement logger object:
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
module MyApp
|
194
|
+
class Application < Hanami::Application
|
195
|
+
config.logger = MyCustomLogger.new
|
196
|
+
end
|
197
|
+
end
|
198
|
+
```
|
199
|
+
- [Tim Riley] Comprehensive `config.source_dirs` setting
|
200
|
+
|
201
|
+
This replaces the previous `component_dir_paths` setting, and contains two nested settings:
|
202
|
+
|
203
|
+
- `config.source_dirs.component_dirs` (backed by `Dry::System::Config::ComponentDirs`), for directories of source files intended to be registered as components
|
204
|
+
- `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
|
205
|
+
|
206
|
+
To add and configure your own additional component dirs:
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
module MyApp
|
210
|
+
class Application < Hanami::Application
|
211
|
+
# Adding a simple component dir
|
212
|
+
config.source_dirs.component_dirs.add "serializers"
|
213
|
+
|
214
|
+
# Adding a component dir with custom configuration
|
215
|
+
config.source_dirs.component_dirs.add "serializers" do |dir|
|
216
|
+
dir.auto_register = proc { |component|
|
217
|
+
!component.identifier.start_with?("structs")
|
218
|
+
}
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
```
|
223
|
+
|
224
|
+
To customize the configuration of the default component dirs ("lib", "actions", "repositories", "views"):
|
225
|
+
|
226
|
+
```ruby
|
227
|
+
module MyApp
|
228
|
+
class Application < Hanami::Application
|
229
|
+
# Customising a default component dir
|
230
|
+
config.source_dirs.component_dirs.dir("lib").auto_register = proc { |component|
|
231
|
+
!component.identifier.start_with?("structs")
|
232
|
+
}
|
233
|
+
|
234
|
+
# Setting default config to apply to all component dirs
|
235
|
+
config.source_dirs.component_dirs.auto_register = proc { |component|
|
236
|
+
!component.identifier.start_with?("entities")
|
237
|
+
}
|
238
|
+
|
239
|
+
# Removing a default component dir
|
240
|
+
config.source_dirs.component_dirs.delete("views")
|
241
|
+
end
|
242
|
+
end
|
243
|
+
```
|
244
|
+
|
245
|
+
To configure the autoload paths (defaulting to `["entities"]`):
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
module MyApp
|
249
|
+
class Application < Hanami::Application
|
250
|
+
# Adding your own autoload paths
|
251
|
+
config.source_dirs.autoload_paths << "structs"
|
252
|
+
|
253
|
+
# Or providing a full replacement
|
254
|
+
config.source_dirs.autoload_paths = ["structs"]
|
255
|
+
end
|
256
|
+
end
|
257
|
+
```
|
258
|
+
- [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).
|
259
|
+
|
4
260
|
## v2.0.0.alpha4 - 2021-12-07
|
5
261
|
### Added
|
6
262
|
- [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
@@ -36,7 +36,7 @@ These components are designed to be used independently or together in a Hanami a
|
|
36
36
|
|
37
37
|
## Installation
|
38
38
|
|
39
|
-
__Hanami__ supports Ruby (MRI)
|
39
|
+
__Hanami__ supports Ruby (MRI) 3.0+
|
40
40
|
|
41
41
|
```shell
|
42
42
|
gem install hanami
|
@@ -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/
|
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
|
|
@@ -136,8 +136,4 @@ __Hanami__ uses [Semantic Versioning 2.0.0](http://semver.org)
|
|
136
136
|
|
137
137
|
## Copyright
|
138
138
|
|
139
|
-
Released under MIT License.
|
140
|
-
|
141
|
-
This project was formerly known as Lotus (`lotusrb`).
|
142
|
-
|
143
|
-
Copyright © 2014-2021 Luca Guidi.
|
139
|
+
Copyright © 2014-2022 Hanami Team – Released under MIT License.
|
data/hanami.gemspec
CHANGED
@@ -4,7 +4,7 @@ lib = File.expand_path("../lib", __FILE__)
|
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
5
|
require "hanami/version"
|
6
6
|
|
7
|
-
Gem::Specification.new do |spec|
|
7
|
+
Gem::Specification.new do |spec|
|
8
8
|
spec.name = "hanami"
|
9
9
|
spec.version = Hanami::VERSION
|
10
10
|
spec.authors = ["Luca Guidi"]
|
@@ -14,10 +14,11 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
|
|
14
14
|
spec.homepage = "http://hanamirb.org"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
17
|
-
spec.files = `git ls-files -c -o --exclude-standard -z -- lib/* bin/* LICENSE.md README.md CODE_OF_CONDUCT.md CHANGELOG.md FEATURES.md hanami.gemspec`.split("\x0")
|
17
|
+
spec.files = `git ls-files -c -o --exclude-standard -z -- lib/* bin/* LICENSE.md README.md CODE_OF_CONDUCT.md CHANGELOG.md FEATURES.md hanami.gemspec`.split("\x0") # rubocop:disable Layout/LineLength
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
|
-
spec.
|
20
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
21
|
+
spec.required_ruby_version = ">= 3.0"
|
21
22
|
|
22
23
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
23
24
|
|
@@ -26,7 +27,7 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
|
|
26
27
|
spec.add_dependency "dry-core", "~> 0.4"
|
27
28
|
spec.add_dependency "dry-inflector", "~> 0.2", ">= 0.2.1"
|
28
29
|
spec.add_dependency "dry-monitor"
|
29
|
-
spec.add_dependency "dry-system", "~> 0.
|
30
|
+
spec.add_dependency "dry-system", "~> 0.23", ">= 0.23.0"
|
30
31
|
spec.add_dependency "hanami-cli", "~> 2.0.alpha"
|
31
32
|
spec.add_dependency "hanami-utils", "~> 2.0.alpha"
|
32
33
|
spec.add_dependency "zeitwerk", "~> 2.4"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Hanami.application.register_provider :rack_logger do
|
4
|
+
start do
|
5
|
+
require "hanami/web/rack_logger"
|
6
|
+
|
7
|
+
target.start :logger
|
8
|
+
target.start :rack_monitor
|
9
|
+
|
10
|
+
rack_logger = Hanami::Web::RackLogger.new(target[:logger])
|
11
|
+
rack_logger.attach target[:rack_monitor]
|
12
|
+
|
13
|
+
register :rack_logger, rack_logger
|
14
|
+
end
|
15
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
Hanami.application.
|
3
|
+
Hanami.application.register_provider :rack_monitor do
|
4
4
|
start do
|
5
5
|
require "dry/monitor"
|
6
6
|
require "dry/monitor/rack/middleware"
|
7
7
|
|
8
|
-
middleware = Dry::Monitor::Rack::Middleware.new(
|
8
|
+
middleware = Dry::Monitor::Rack::Middleware.new(target[:notifications])
|
9
9
|
|
10
10
|
register :rack_monitor, middleware
|
11
11
|
end
|
@@ -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:,
|
14
|
-
@
|
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(:@
|
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
|
-
@
|
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
|
-
@
|
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 @
|
53
|
+
return self if @middleware_stack.empty?
|
54
54
|
|
55
|
-
@
|
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_provider :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
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../constants"
|
4
|
+
require_relative "../slice"
|
5
|
+
|
6
|
+
module Hanami
|
7
|
+
class Application
|
8
|
+
# @api private
|
9
|
+
class SliceRegistrar
|
10
|
+
attr_reader :application, :slices
|
11
|
+
private :application, :slices
|
12
|
+
|
13
|
+
def initialize(application)
|
14
|
+
@application = application
|
15
|
+
@slices = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def register(name, slice_class = nil, &block)
|
19
|
+
if slices.key?(name.to_sym)
|
20
|
+
raise SliceLoadError, "Slice '#{name}' is already registered"
|
21
|
+
end
|
22
|
+
|
23
|
+
# TODO: raise error unless name meets format (i.e. single level depth only)
|
24
|
+
|
25
|
+
slices[name.to_sym] = slice_class || build_slice(name, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](name)
|
29
|
+
slices.fetch(name) do
|
30
|
+
raise SliceLoadError, "Slice '#{name}' not found"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def freeze
|
35
|
+
slices.freeze
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
def load_slices
|
40
|
+
slice_configs = Dir[root.join(CONFIG_DIR, "slices", "*#{RB_EXT}")]
|
41
|
+
.map { |file| File.basename(file, RB_EXT) }
|
42
|
+
|
43
|
+
slice_dirs = Dir[File.join(root, SLICES_DIR, "*")]
|
44
|
+
.select { |path| File.directory?(path) }
|
45
|
+
.map { |path| File.basename(path) }
|
46
|
+
|
47
|
+
(slice_dirs + slice_configs).uniq.sort.each do |slice_name|
|
48
|
+
load_slice(slice_name)
|
49
|
+
end
|
50
|
+
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def each(&block)
|
55
|
+
slices.each_value(&block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_a
|
59
|
+
slices.values
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Attempts to load a slice class defined in `config/slices/[slice_name].rb`, then
|
65
|
+
# registers the slice with the matching class, if found.
|
66
|
+
def load_slice(slice_name)
|
67
|
+
slice_const_name = inflector.camelize(slice_name)
|
68
|
+
slice_require_path = root.join("config", "slices", slice_name).to_s
|
69
|
+
|
70
|
+
begin
|
71
|
+
require(slice_require_path)
|
72
|
+
rescue LoadError => e
|
73
|
+
raise e unless e.path == slice_require_path
|
74
|
+
end
|
75
|
+
|
76
|
+
slice_class =
|
77
|
+
begin
|
78
|
+
inflector.constantize("#{slice_const_name}::Slice")
|
79
|
+
rescue NameError => e
|
80
|
+
raise e unless e.name == :Slice
|
81
|
+
end
|
82
|
+
|
83
|
+
register(slice_name, slice_class)
|
84
|
+
end
|
85
|
+
|
86
|
+
def build_slice(slice_name, &block)
|
87
|
+
slice_module =
|
88
|
+
begin
|
89
|
+
slice_module_name = inflector.camelize(slice_name.to_s)
|
90
|
+
inflector.constantize(slice_module_name)
|
91
|
+
rescue NameError
|
92
|
+
Object.const_set(inflector.camelize(slice_module_name), Module.new)
|
93
|
+
end
|
94
|
+
|
95
|
+
slice_module.const_set(:Slice, Class.new(Hanami::Slice, &block))
|
96
|
+
end
|
97
|
+
|
98
|
+
def root
|
99
|
+
application.root
|
100
|
+
end
|
101
|
+
|
102
|
+
def inflector
|
103
|
+
application.inflector
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|