hanami 2.0.0.alpha4 → 2.0.0.alpha7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|