hanami 2.0.0.alpha1 → 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 +4 -4
- data/CHANGELOG.md +306 -5
- data/FEATURES.md +9 -1
- data/LICENSE.md +1 -1
- data/README.md +9 -6
- data/hanami.gemspec +12 -11
- data/lib/hanami/application/autoloader/inflector_adapter.rb +22 -0
- data/lib/hanami/application/container/boot/inflector.rb +7 -0
- data/lib/hanami/application/container/boot/logger.rb +7 -0
- data/lib/hanami/application/container/boot/rack_logger.rb +19 -0
- data/lib/hanami/application/container/boot/rack_monitor.rb +12 -0
- data/lib/hanami/application/container/boot/routes_helper.rb +9 -0
- data/lib/hanami/application/container/boot/settings.rb +7 -0
- data/lib/hanami/application/router.rb +59 -0
- data/lib/hanami/application/routes.rb +55 -0
- data/lib/hanami/application/routes_helper.rb +34 -0
- data/lib/hanami/application/routing/middleware/stack.rb +89 -0
- data/lib/hanami/application/routing/resolver/node.rb +50 -0
- data/lib/hanami/application/routing/resolver/trie.rb +59 -0
- data/lib/hanami/application/routing/resolver.rb +87 -0
- data/lib/hanami/application/routing/router.rb +36 -0
- data/lib/hanami/application/settings/dotenv_store.rb +60 -0
- data/lib/hanami/application/settings.rb +93 -0
- data/lib/hanami/application.rb +330 -34
- data/lib/hanami/assets/application_configuration.rb +63 -0
- data/lib/hanami/assets/configuration.rb +54 -0
- data/lib/hanami/boot/source_dirs.rb +44 -0
- data/lib/hanami/boot.rb +1 -2
- data/lib/hanami/cli/application/cli.rb +40 -0
- data/lib/hanami/cli/application/command.rb +47 -0
- data/lib/hanami/cli/application/commands/console.rb +81 -0
- data/lib/hanami/cli/application/commands.rb +16 -0
- data/lib/hanami/cli/base_command.rb +48 -0
- data/lib/hanami/cli/commands/command.rb +4 -4
- data/lib/hanami/cli/commands.rb +3 -2
- data/lib/hanami/configuration/logger.rb +84 -0
- data/lib/hanami/configuration/middleware.rb +4 -4
- data/lib/hanami/configuration/null_configuration.rb +14 -0
- data/lib/hanami/configuration/router.rb +52 -0
- data/lib/hanami/configuration/sessions.rb +5 -5
- data/lib/hanami/configuration/source_dirs.rb +42 -0
- data/lib/hanami/configuration.rb +122 -131
- data/lib/hanami/init.rb +5 -0
- data/lib/hanami/setup.rb +9 -0
- data/lib/hanami/slice.rb +189 -0
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +96 -0
- data/lib/hanami.rb +17 -30
- metadata +116 -50
- data/bin/hanami +0 -8
- data/lib/hanami/configuration/cookies.rb +0 -24
- data/lib/hanami/configuration/security.rb +0 -141
- data/lib/hanami/container.rb +0 -107
- data/lib/hanami/frameworks.rb +0 -28
- data/lib/hanami/routes.rb +0 -31
data/lib/hanami/application.rb
CHANGED
@@ -1,70 +1,366 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "dry/system/container"
|
4
|
+
require "dry/system/loader/autoloading"
|
3
5
|
require "hanami/configuration"
|
4
|
-
require "
|
5
|
-
require "
|
6
|
+
require "pathname"
|
7
|
+
require "rack"
|
8
|
+
require "zeitwerk"
|
9
|
+
require_relative "slice"
|
10
|
+
require_relative "application/autoloader/inflector_adapter"
|
6
11
|
|
7
12
|
module Hanami
|
8
|
-
# Hanami application
|
13
|
+
# Hanami application class
|
9
14
|
#
|
10
15
|
# @since 2.0.0
|
11
16
|
class Application
|
12
17
|
@_mutex = Mutex.new
|
13
18
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
+
class << self
|
20
|
+
def inherited(klass)
|
21
|
+
@_mutex.synchronize do
|
22
|
+
klass.class_eval do
|
23
|
+
@_mutex = Mutex.new
|
24
|
+
@_configuration = Hanami::Configuration.new(application_name: name, env: Hanami.env)
|
19
25
|
|
20
|
-
|
21
|
-
|
22
|
-
end
|
26
|
+
extend ClassMethods
|
27
|
+
end
|
23
28
|
|
24
|
-
|
29
|
+
klass.send :prepare_base_load_path
|
30
|
+
|
31
|
+
Hanami.application = klass
|
32
|
+
end
|
25
33
|
end
|
26
34
|
end
|
27
35
|
|
28
|
-
#
|
36
|
+
# Application class interface
|
29
37
|
#
|
30
|
-
#
|
38
|
+
# rubocop:disable Metrics/ModuleLength
|
31
39
|
module ClassMethods
|
40
|
+
def self.extended(klass)
|
41
|
+
klass.class_eval do
|
42
|
+
@inited = @booted = false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
32
46
|
def configuration
|
33
47
|
@_configuration
|
34
48
|
end
|
35
49
|
|
36
50
|
alias config configuration
|
37
51
|
|
38
|
-
def
|
52
|
+
def init # rubocop:disable Metrics/MethodLength
|
53
|
+
return self if inited?
|
54
|
+
|
55
|
+
configuration.finalize!
|
56
|
+
|
57
|
+
@autoloader = Zeitwerk::Loader.new
|
58
|
+
autoloader.inflector = Autoloader::InflectorAdapter.new(inflector)
|
59
|
+
|
60
|
+
load_settings
|
61
|
+
|
62
|
+
@container = prepare_container
|
63
|
+
@deps_module = prepare_deps_module
|
64
|
+
|
65
|
+
load_slices
|
66
|
+
slices.values.each(&:init)
|
67
|
+
slices.freeze
|
68
|
+
|
69
|
+
autoloader.setup
|
70
|
+
|
71
|
+
@inited = true
|
72
|
+
self
|
73
|
+
end
|
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
|
+
|
92
|
+
def inited?
|
93
|
+
@inited
|
94
|
+
end
|
95
|
+
|
96
|
+
def booted?
|
97
|
+
@booted
|
98
|
+
end
|
99
|
+
|
100
|
+
def autoloader
|
101
|
+
raise "Application not init'ed" unless defined?(@autoloader)
|
102
|
+
|
103
|
+
@autoloader
|
104
|
+
end
|
105
|
+
|
106
|
+
def container
|
107
|
+
raise "Application not init'ed" unless defined?(@container)
|
108
|
+
|
109
|
+
@container
|
110
|
+
end
|
111
|
+
|
112
|
+
def deps
|
113
|
+
raise "Application not init'ed" unless defined?(@deps_module)
|
114
|
+
|
115
|
+
@deps_module
|
116
|
+
end
|
117
|
+
|
118
|
+
def router
|
119
|
+
raise "Application not init'ed" unless inited?
|
120
|
+
|
39
121
|
@_mutex.synchronize do
|
40
|
-
|
41
|
-
|
122
|
+
@_router ||= load_router
|
123
|
+
end
|
124
|
+
end
|
42
125
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
126
|
+
def rack_app
|
127
|
+
@rack_app ||= router.to_rack_app
|
128
|
+
end
|
129
|
+
|
130
|
+
def slices
|
131
|
+
@slices ||= {}
|
132
|
+
end
|
133
|
+
|
134
|
+
def register_slice(name, **slice_args)
|
135
|
+
raise "Slice +#{name}+ already registered" if slices.key?(name.to_sym)
|
136
|
+
|
137
|
+
slice = Slice.new(self, name: name, **slice_args)
|
138
|
+
slice.namespace.const_set :Slice, slice if slice.namespace # rubocop:disable Style/SafeNavigation
|
139
|
+
slices[name.to_sym] = slice
|
140
|
+
end
|
141
|
+
|
142
|
+
def register(*args, **opts, &block)
|
143
|
+
container.register(*args, **opts, &block)
|
144
|
+
end
|
145
|
+
|
146
|
+
def register_bootable(*args, **opts, &block)
|
147
|
+
container.boot(*args, **opts, &block)
|
148
|
+
end
|
149
|
+
|
150
|
+
def init_bootable(*args)
|
151
|
+
container.init(*args)
|
152
|
+
end
|
153
|
+
|
154
|
+
def start_bootable(*args)
|
155
|
+
container.start(*args)
|
156
|
+
end
|
157
|
+
|
158
|
+
def key?(*args)
|
159
|
+
container.key?(*args)
|
160
|
+
end
|
161
|
+
|
162
|
+
def keys
|
163
|
+
container.keys
|
164
|
+
end
|
165
|
+
|
166
|
+
def [](*args)
|
167
|
+
container[*args]
|
168
|
+
end
|
169
|
+
|
170
|
+
def resolve(*args)
|
171
|
+
container.resolve(*args)
|
172
|
+
end
|
173
|
+
|
174
|
+
def settings
|
175
|
+
@_settings ||= load_settings
|
176
|
+
end
|
177
|
+
|
178
|
+
def namespace
|
179
|
+
configuration.namespace
|
180
|
+
end
|
181
|
+
|
182
|
+
def namespace_name
|
183
|
+
namespace.name
|
184
|
+
end
|
185
|
+
|
186
|
+
def namespace_path
|
187
|
+
inflector.underscore(namespace)
|
188
|
+
end
|
189
|
+
|
190
|
+
def application_name
|
191
|
+
configuration.application_name
|
192
|
+
end
|
193
|
+
|
194
|
+
def root
|
195
|
+
configuration.root
|
196
|
+
end
|
197
|
+
|
198
|
+
def inflector
|
199
|
+
configuration.inflector
|
200
|
+
end
|
201
|
+
|
202
|
+
# @api private
|
203
|
+
def component_provider(component)
|
204
|
+
raise "Hanami.application must be inited before detecting providers" unless inited?
|
205
|
+
|
206
|
+
# [Admin, Main, MyApp] or [MyApp::Admin, MyApp::Main, MyApp]
|
207
|
+
providers = slices.values + [self]
|
208
|
+
|
209
|
+
component_class = component.is_a?(Class) ? component : component.class
|
210
|
+
component_name = component_class.name
|
211
|
+
|
212
|
+
return unless component_name
|
213
|
+
|
214
|
+
providers.detect { |provider| component_name.include?(provider.namespace.to_s) }
|
215
|
+
end
|
216
|
+
|
217
|
+
private
|
218
|
+
|
219
|
+
def prepare_base_load_path
|
220
|
+
base_path = File.join(root, "lib")
|
221
|
+
$LOAD_PATH.unshift base_path unless $LOAD_PATH.include?(base_path)
|
222
|
+
end
|
223
|
+
|
224
|
+
def prepare_container
|
225
|
+
define_container.tap do |container|
|
226
|
+
configure_container container
|
47
227
|
end
|
48
228
|
end
|
49
|
-
end
|
50
229
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
def
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
230
|
+
def prepare_deps_module
|
231
|
+
define_deps_module
|
232
|
+
end
|
233
|
+
|
234
|
+
def define_container
|
235
|
+
require "#{application_name}/container"
|
236
|
+
namespace.const_get :Container
|
237
|
+
rescue LoadError, NameError
|
238
|
+
namespace.const_set :Container, Class.new(Dry::System::Container)
|
239
|
+
end
|
240
|
+
|
241
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
242
|
+
def configure_container(container)
|
243
|
+
container.use :env, inferrer: -> { Hanami.env }
|
244
|
+
container.use :notifications
|
245
|
+
|
246
|
+
container.configure do |config|
|
247
|
+
config.inflector = configuration.inflector
|
248
|
+
|
249
|
+
config.root = configuration.root
|
250
|
+
config.bootable_dirs = [
|
251
|
+
"config/boot",
|
252
|
+
Pathname(__dir__).join("application/container/boot").realpath,
|
253
|
+
]
|
60
254
|
|
61
|
-
|
255
|
+
config.component_dirs.loader = Dry::System::Loader::Autoloading
|
256
|
+
config.component_dirs.add_to_load_path = false
|
62
257
|
end
|
258
|
+
|
259
|
+
# Autoload classes defined in lib/[app_namespace]/
|
260
|
+
if root.join("lib", namespace_path).directory?
|
261
|
+
autoloader.push_dir(root.join("lib", namespace_path), namespace: namespace)
|
262
|
+
end
|
263
|
+
|
264
|
+
# Add lib/ to to the $LOAD_PATH so other files there (outside the app namespace)
|
265
|
+
# are require-able
|
266
|
+
container.add_to_load_path!("lib") if root.join("lib").directory?
|
267
|
+
|
268
|
+
container
|
269
|
+
end
|
270
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
271
|
+
|
272
|
+
def define_deps_module
|
273
|
+
require "#{application_name}/deps"
|
274
|
+
namespace.const_get :Deps
|
275
|
+
rescue LoadError, NameError
|
276
|
+
namespace.const_set :Deps, container.injector
|
277
|
+
end
|
278
|
+
|
279
|
+
def load_slices
|
280
|
+
Dir[File.join(slices_path, "*")]
|
281
|
+
.select(&File.method(:directory?))
|
282
|
+
.each(&method(:load_slice))
|
283
|
+
end
|
284
|
+
|
285
|
+
def slices_path
|
286
|
+
File.join(root, config.slices_dir)
|
287
|
+
end
|
288
|
+
|
289
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
290
|
+
def load_slice(slice_path)
|
291
|
+
slice_path = Pathname(slice_path)
|
292
|
+
|
293
|
+
slice_name = slice_path.relative_path_from(Pathname(slices_path)).to_s
|
294
|
+
slice_const_name = inflector.camelize(slice_name)
|
295
|
+
|
296
|
+
if config.slices_namespace.const_defined?(slice_const_name)
|
297
|
+
slice_module = config.slices_namespace.const_get(slice_const_name)
|
298
|
+
|
299
|
+
raise "Cannot use slice +#{slice_const_name}+ since it is not a module" unless slice_module.is_a?(Module)
|
300
|
+
else
|
301
|
+
slice_module = Module.new
|
302
|
+
config.slices_namespace.const_set inflector.camelize(slice_name), slice_module
|
303
|
+
end
|
304
|
+
|
305
|
+
register_slice(
|
306
|
+
slice_name,
|
307
|
+
namespace: slice_module,
|
308
|
+
root: slice_path.realpath
|
309
|
+
)
|
310
|
+
end
|
311
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
312
|
+
|
313
|
+
def load_settings
|
314
|
+
require_relative "application/settings"
|
315
|
+
|
316
|
+
prepare_base_load_path
|
317
|
+
require File.join(configuration.root, configuration.settings_path)
|
318
|
+
settings_class = autodiscover_application_constant(configuration.settings_class_name)
|
319
|
+
settings_class.new(configuration.settings_store)
|
320
|
+
rescue LoadError
|
321
|
+
Settings.new
|
322
|
+
end
|
323
|
+
|
324
|
+
MODULE_DELIMITER = "::"
|
325
|
+
private_constant :MODULE_DELIMITER
|
326
|
+
|
327
|
+
def autodiscover_application_constant(constants)
|
328
|
+
inflector.constantize([namespace_name, *constants].join(MODULE_DELIMITER))
|
329
|
+
end
|
330
|
+
|
331
|
+
def load_router
|
332
|
+
require_relative "application/router"
|
333
|
+
|
334
|
+
Router.new(
|
335
|
+
routes: load_routes,
|
336
|
+
resolver: router_resolver,
|
337
|
+
**configuration.router.options,
|
338
|
+
) do
|
339
|
+
use Hanami.application[:rack_monitor]
|
340
|
+
|
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 {}
|
63
355
|
end
|
64
356
|
|
65
|
-
def
|
66
|
-
|
357
|
+
def router_resolver
|
358
|
+
config.router.resolver.new(
|
359
|
+
slices: slices,
|
360
|
+
inflector: inflector
|
361
|
+
)
|
67
362
|
end
|
68
363
|
end
|
364
|
+
# rubocop:enable Metrics/ModuleLength
|
69
365
|
end
|
70
366
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hanami/assets/configuration"
|
4
|
+
require "dry/configurable"
|
5
|
+
|
6
|
+
module Hanami
|
7
|
+
module Assets
|
8
|
+
# @since 2.0.0
|
9
|
+
# @api public
|
10
|
+
class ApplicationConfiguration
|
11
|
+
include Dry::Configurable
|
12
|
+
|
13
|
+
setting :server_url, default: "http://localhost:8080"
|
14
|
+
|
15
|
+
# @since 2.0.0
|
16
|
+
# @api private
|
17
|
+
def initialize(*)
|
18
|
+
super
|
19
|
+
|
20
|
+
@base_configuration = Assets::Configuration.new
|
21
|
+
end
|
22
|
+
|
23
|
+
# @since 2.0.0
|
24
|
+
# @api private
|
25
|
+
def finalize!
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the list of available settings
|
29
|
+
#
|
30
|
+
# @return [Set]
|
31
|
+
#
|
32
|
+
# @since 2.0.0
|
33
|
+
# @api private
|
34
|
+
def settings
|
35
|
+
base_configuration.settings + self.class.settings
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# @since 2.0.0
|
41
|
+
# @api private
|
42
|
+
attr_reader :base_configuration
|
43
|
+
|
44
|
+
# @since 2.0.0
|
45
|
+
# @api private
|
46
|
+
def method_missing(name, *args, &block)
|
47
|
+
if config.respond_to?(name)
|
48
|
+
config.public_send(name, *args, &block)
|
49
|
+
elsif base_configuration.respond_to?(name)
|
50
|
+
base_configuration.public_send(name, *args, &block)
|
51
|
+
else
|
52
|
+
super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# @since 2.0.0
|
57
|
+
# @api private
|
58
|
+
def respond_to_missing?(name, _incude_all = false)
|
59
|
+
config.respond_to?(name) || base_configuration.respond_to?(name) || super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/configurable"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
module Assets
|
7
|
+
# @since 2.0.0
|
8
|
+
# @api public
|
9
|
+
class Configuration
|
10
|
+
include Dry::Configurable
|
11
|
+
|
12
|
+
# Initialize the Configuration
|
13
|
+
#
|
14
|
+
# @yield [config] the configuration object
|
15
|
+
#
|
16
|
+
# @return [Configuration]
|
17
|
+
#
|
18
|
+
# @since 2.0.0
|
19
|
+
# @api private
|
20
|
+
def initialize(*)
|
21
|
+
super
|
22
|
+
yield self if block_given?
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the list of available settings
|
26
|
+
#
|
27
|
+
# @return [Set]
|
28
|
+
#
|
29
|
+
# @since 2.0.0
|
30
|
+
# @api private
|
31
|
+
def settings
|
32
|
+
self.class.settings
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# @since 2.0.0
|
38
|
+
# @api private
|
39
|
+
def method_missing(name, *args, &block)
|
40
|
+
if config.respond_to?(name)
|
41
|
+
config.public_send(name, *args, &block)
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# @since 2.0.0
|
48
|
+
# @api private
|
49
|
+
def respond_to_missing?(name, _incude_all = false)
|
50
|
+
config.respond_to?(name) || super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
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
|
data/lib/hanami/boot.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hanami/cli"
|
4
|
+
require_relative "commands"
|
5
|
+
|
6
|
+
module Hanami
|
7
|
+
class CLI
|
8
|
+
module Application
|
9
|
+
# Hanami application CLI
|
10
|
+
class CLI < Hanami::CLI
|
11
|
+
attr_reader :application
|
12
|
+
|
13
|
+
def initialize(application: nil, commands: Commands)
|
14
|
+
super(commands)
|
15
|
+
@application = application
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# TODO: we should make a prepare_command method upstream
|
21
|
+
def parse(result, out)
|
22
|
+
command, arguments = super
|
23
|
+
|
24
|
+
if command.respond_to?(:with_application)
|
25
|
+
# Set HANAMI_ENV before the application inits to ensure all aspects
|
26
|
+
# of the boot process respect the provided env
|
27
|
+
ENV["HANAMI_ENV"] = arguments[:env] if arguments[:env]
|
28
|
+
|
29
|
+
require "hanami/init"
|
30
|
+
application = Hanami.application
|
31
|
+
|
32
|
+
[command.with_application(application), arguments]
|
33
|
+
else
|
34
|
+
[command, arguments]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../base_command"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
class CLI
|
7
|
+
module Application
|
8
|
+
# Hanami application CLI command (intended to run inside application directory)
|
9
|
+
class Command < BaseCommand
|
10
|
+
def self.inherited(klass)
|
11
|
+
super
|
12
|
+
|
13
|
+
klass.option :env, aliases: ["-e"], default: nil, desc: "Application environment"
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :application
|
17
|
+
|
18
|
+
def initialize(application: nil, **opts)
|
19
|
+
super(**opts)
|
20
|
+
@application = application
|
21
|
+
end
|
22
|
+
|
23
|
+
def with_application(application)
|
24
|
+
self.class.new(
|
25
|
+
command_name: @command_name,
|
26
|
+
application: application,
|
27
|
+
out: out,
|
28
|
+
inflector: application.inflector,
|
29
|
+
files: files,
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def run_command(klass, *args)
|
36
|
+
klass.new(
|
37
|
+
command_name: klass.name,
|
38
|
+
application: application,
|
39
|
+
out: out,
|
40
|
+
inflector: application.inflector,
|
41
|
+
files: files,
|
42
|
+
).call(*args)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|