hanami 2.0.0.alpha1 → 2.0.0.alpha2
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 +70 -5
- data/FEATURES.md +9 -1
- data/LICENSE.md +1 -1
- data/README.md +4 -5
- data/hanami.gemspec +11 -11
- data/lib/hanami.rb +19 -18
- data/lib/hanami/application.rb +322 -26
- 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 +8 -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/settings.rb +7 -0
- data/lib/hanami/application/router.rb +59 -0
- data/lib/hanami/application/routing/middleware/stack.rb +89 -0
- data/lib/hanami/application/routing/resolver.rb +82 -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/settings.rb +23 -0
- data/lib/hanami/application/settings/definition.rb +26 -0
- data/lib/hanami/application/settings/loader.rb +97 -0
- data/lib/hanami/application/settings/struct.rb +65 -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.rb +16 -0
- data/lib/hanami/cli/application/commands/console.rb +81 -0
- data/lib/hanami/cli/base_command.rb +48 -0
- data/lib/hanami/cli/commands.rb +3 -2
- data/lib/hanami/cli/commands/command.rb +4 -4
- data/lib/hanami/configuration.rb +129 -64
- data/lib/hanami/configuration/middleware.rb +2 -2
- data/lib/hanami/configuration/router.rb +50 -0
- data/lib/hanami/init.rb +5 -0
- data/lib/hanami/setup.rb +9 -0
- data/lib/hanami/slice.rb +138 -0
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +96 -0
- metadata +92 -54
- 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
@@ -15,7 +15,7 @@ module Hanami
|
|
15
15
|
# Abstract command
|
16
16
|
#
|
17
17
|
# @since 1.1.0
|
18
|
-
class Command <
|
18
|
+
class Command < Dry::CLI::Command
|
19
19
|
# @since 1.1.0
|
20
20
|
# @api private
|
21
21
|
def self.inherited(component)
|
@@ -72,9 +72,9 @@ module Hanami
|
|
72
72
|
def call(**options)
|
73
73
|
# FIXME: merge ENV vars (like HANAMI_ENV) into **options
|
74
74
|
super(options)
|
75
|
-
rescue StandardError =>
|
76
|
-
warn
|
77
|
-
warn
|
75
|
+
rescue StandardError => exception
|
76
|
+
warn exception.message
|
77
|
+
warn exception.backtrace.join("\n\t")
|
78
78
|
exit(1)
|
79
79
|
end
|
80
80
|
end
|
data/lib/hanami/configuration.rb
CHANGED
@@ -4,6 +4,8 @@ require "uri"
|
|
4
4
|
require "concurrent/hash"
|
5
5
|
require "concurrent/array"
|
6
6
|
require "dry/inflector"
|
7
|
+
require "pathname"
|
8
|
+
require "zeitwerk"
|
7
9
|
|
8
10
|
module Hanami
|
9
11
|
# Hanami application configuration
|
@@ -12,40 +14,72 @@ module Hanami
|
|
12
14
|
#
|
13
15
|
# rubocop:disable Metrics/ClassLength
|
14
16
|
class Configuration
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
require "hanami/configuration/security"
|
17
|
+
require_relative "configuration/middleware"
|
18
|
+
require_relative "configuration/router"
|
19
|
+
require_relative "configuration/sessions"
|
19
20
|
|
20
|
-
|
21
|
+
attr_reader :actions
|
22
|
+
attr_reader :views
|
23
|
+
|
24
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
21
25
|
def initialize(env:)
|
22
26
|
@settings = Concurrent::Hash.new
|
23
27
|
|
28
|
+
self.autoloader = Zeitwerk::Loader.new
|
29
|
+
|
24
30
|
self.env = env
|
25
31
|
self.environments = DEFAULT_ENVIRONMENTS.clone
|
26
32
|
|
33
|
+
self.root = Dir.pwd
|
34
|
+
self.slices_dir = DEFAULT_SLICES_DIR
|
35
|
+
settings[:slices] = {}
|
36
|
+
|
37
|
+
self.settings_path = DEFAULT_SETTINGS_PATH
|
38
|
+
self.settings_loader_options = {}
|
39
|
+
|
27
40
|
self.base_url = DEFAULT_BASE_URL
|
28
41
|
|
29
42
|
self.logger = DEFAULT_LOGGER.clone
|
30
|
-
self.
|
31
|
-
self.cookies = DEFAULT_COOKIES
|
43
|
+
self.rack_logger_filter_params = DEFAULT_RACK_LOGGER_FILTER_PARAMS.clone
|
32
44
|
self.sessions = DEFAULT_SESSIONS
|
33
45
|
|
34
|
-
self.
|
35
|
-
self.default_response_format = DEFAULT_RESPONSE_FORMAT
|
36
|
-
|
46
|
+
self.router = Router.new(base_url)
|
37
47
|
self.middleware = Middleware.new
|
38
|
-
self.security = Security.new
|
39
48
|
|
40
49
|
self.inflections = Dry::Inflector.new
|
50
|
+
|
51
|
+
@actions = begin
|
52
|
+
require_path = "hanami/action/application_configuration"
|
53
|
+
require require_path
|
54
|
+
Hanami::Action::ApplicationConfiguration.new
|
55
|
+
rescue LoadError => e
|
56
|
+
raise e unless e.path == require_path
|
57
|
+
Object.new
|
58
|
+
end
|
59
|
+
|
60
|
+
@views = begin
|
61
|
+
require_path = "hanami/view/application_configuration"
|
62
|
+
require require_path
|
63
|
+
Hanami::View::ApplicationConfiguration.new
|
64
|
+
rescue LoadError => e
|
65
|
+
raise e unless e.path == require_path
|
66
|
+
Object.new
|
67
|
+
end
|
41
68
|
end
|
42
|
-
# rubocop:enable Metrics/MethodLength
|
69
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
43
70
|
|
44
71
|
def finalize
|
45
72
|
environment_for(env).each do |blk|
|
46
73
|
instance_eval(&blk)
|
47
74
|
end
|
48
75
|
|
76
|
+
# Finalize nested configuration
|
77
|
+
#
|
78
|
+
# TODO: would be good to just create empty configurations for actions/views
|
79
|
+
# instead of plain objects
|
80
|
+
actions.finalize! if actions.respond_to?(:finalize!)
|
81
|
+
views.finalize! if views.respond_to?(:finalize!)
|
82
|
+
|
49
83
|
self
|
50
84
|
end
|
51
85
|
|
@@ -53,6 +87,14 @@ module Hanami
|
|
53
87
|
environment_for(name).push(blk)
|
54
88
|
end
|
55
89
|
|
90
|
+
def autoloader=(loader)
|
91
|
+
settings[:autoloader] = loader || nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def autoloader
|
95
|
+
settings.fetch(:autoloader)
|
96
|
+
end
|
97
|
+
|
56
98
|
def env=(value)
|
57
99
|
settings[:env] = value
|
58
100
|
end
|
@@ -61,6 +103,65 @@ module Hanami
|
|
61
103
|
settings.fetch(:env)
|
62
104
|
end
|
63
105
|
|
106
|
+
def root=(root)
|
107
|
+
settings[:root] = Pathname(root)
|
108
|
+
end
|
109
|
+
|
110
|
+
def root
|
111
|
+
settings.fetch(:root)
|
112
|
+
end
|
113
|
+
|
114
|
+
def slices_dir=(dir)
|
115
|
+
settings[:slices_dir] = dir
|
116
|
+
end
|
117
|
+
|
118
|
+
def slices_dir
|
119
|
+
settings.fetch(:slices_dir)
|
120
|
+
end
|
121
|
+
|
122
|
+
def slices_namespace=(namespace)
|
123
|
+
settings[:slices_namespace] = namespace
|
124
|
+
end
|
125
|
+
|
126
|
+
def slices_namespace
|
127
|
+
settings.fetch(:slices_namespace) { Object }
|
128
|
+
end
|
129
|
+
|
130
|
+
def slice(slice_name, &block)
|
131
|
+
settings[:slices][slice_name] = block
|
132
|
+
end
|
133
|
+
|
134
|
+
def slices
|
135
|
+
settings[:slices]
|
136
|
+
end
|
137
|
+
|
138
|
+
def settings_path=(value)
|
139
|
+
settings[:settings_path] = value
|
140
|
+
end
|
141
|
+
|
142
|
+
def settings_path
|
143
|
+
settings.fetch(:settings_path)
|
144
|
+
end
|
145
|
+
|
146
|
+
def settings_loader=(loader)
|
147
|
+
settings[:settings_loader] = loader
|
148
|
+
end
|
149
|
+
|
150
|
+
def settings_loader
|
151
|
+
settings.fetch(:settings_loader) {
|
152
|
+
require "hanami/application/settings/loader"
|
153
|
+
settings[:settings_loader] = Application::Settings::Loader
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
def settings_loader_options=(options)
|
158
|
+
settings[:settings_loader_options] = options
|
159
|
+
end
|
160
|
+
|
161
|
+
def settings_loader_options
|
162
|
+
settings[:settings_loader_options]
|
163
|
+
end
|
164
|
+
|
64
165
|
def base_url=(value)
|
65
166
|
settings[:base_url] = URI.parse(value)
|
66
167
|
end
|
@@ -77,20 +178,20 @@ module Hanami
|
|
77
178
|
settings.fetch(:logger)
|
78
179
|
end
|
79
180
|
|
80
|
-
def
|
81
|
-
settings[:
|
181
|
+
def rack_logger_filter_params=(params)
|
182
|
+
settings[:rack_logger_filter_params] = params
|
82
183
|
end
|
83
184
|
|
84
|
-
def
|
85
|
-
settings
|
185
|
+
def rack_logger_filter_params
|
186
|
+
settings[:rack_logger_filter_params]
|
86
187
|
end
|
87
188
|
|
88
|
-
def
|
89
|
-
settings[:
|
189
|
+
def router=(value)
|
190
|
+
settings[:router] = value
|
90
191
|
end
|
91
192
|
|
92
|
-
def
|
93
|
-
settings.fetch(:
|
193
|
+
def router
|
194
|
+
settings.fetch(:router)
|
94
195
|
end
|
95
196
|
|
96
197
|
def sessions=(*args)
|
@@ -101,34 +202,10 @@ module Hanami
|
|
101
202
|
settings.fetch(:sessions)
|
102
203
|
end
|
103
204
|
|
104
|
-
def default_request_format=(value)
|
105
|
-
settings[:default_request_format] = value
|
106
|
-
end
|
107
|
-
|
108
|
-
def default_request_format
|
109
|
-
settings.fetch(:default_request_format)
|
110
|
-
end
|
111
|
-
|
112
|
-
def default_response_format=(value)
|
113
|
-
settings[:default_response_format] = value
|
114
|
-
end
|
115
|
-
|
116
|
-
def default_response_format
|
117
|
-
settings.fetch(:default_response_format)
|
118
|
-
end
|
119
|
-
|
120
205
|
def middleware
|
121
206
|
settings.fetch(:middleware)
|
122
207
|
end
|
123
208
|
|
124
|
-
def security=(value)
|
125
|
-
settings[:security] = value
|
126
|
-
end
|
127
|
-
|
128
|
-
def security
|
129
|
-
settings.fetch(:security)
|
130
|
-
end
|
131
|
-
|
132
209
|
def inflections(&blk)
|
133
210
|
if blk.nil?
|
134
211
|
settings.fetch(:inflections)
|
@@ -137,16 +214,7 @@ module Hanami
|
|
137
214
|
end
|
138
215
|
end
|
139
216
|
|
140
|
-
|
141
|
-
bu = base_url
|
142
|
-
|
143
|
-
{
|
144
|
-
scheme: bu.scheme,
|
145
|
-
host: bu.host,
|
146
|
-
port: bu.port,
|
147
|
-
inflector: inflections
|
148
|
-
}
|
149
|
-
end
|
217
|
+
alias inflector inflections
|
150
218
|
|
151
219
|
def for_each_middleware(&blk)
|
152
220
|
stack = middleware.stack.dup
|
@@ -178,27 +246,24 @@ module Hanami
|
|
178
246
|
DEFAULT_ENVIRONMENTS = Concurrent::Hash.new { |h, k| h[k] = Concurrent::Array.new }
|
179
247
|
private_constant :DEFAULT_ENVIRONMENTS
|
180
248
|
|
249
|
+
DEFAULT_SLICES_DIR = "slices"
|
250
|
+
private_constant :DEFAULT_SLICES_DIR
|
251
|
+
|
181
252
|
DEFAULT_BASE_URL = "http://0.0.0.0:2300"
|
182
253
|
private_constant :DEFAULT_BASE_URL
|
183
254
|
|
184
255
|
DEFAULT_LOGGER = { level: :debug }.freeze
|
185
256
|
private_constant :DEFAULT_LOGGER
|
186
257
|
|
187
|
-
|
188
|
-
private_constant :
|
258
|
+
DEFAULT_RACK_LOGGER_FILTER_PARAMS = %w[_csrf password password_confirmation].freeze
|
259
|
+
private_constant :DEFAULT_RACK_LOGGER_FILTER_PARAMS
|
189
260
|
|
190
|
-
|
191
|
-
private_constant :
|
261
|
+
DEFAULT_SETTINGS_PATH = File.join("config", "settings")
|
262
|
+
private_constant :DEFAULT_SETTINGS_PATH
|
192
263
|
|
193
264
|
DEFAULT_SESSIONS = Sessions.null
|
194
265
|
private_constant :DEFAULT_SESSIONS
|
195
266
|
|
196
|
-
DEFAULT_REQUEST_FORMAT = :html
|
197
|
-
private_constant :DEFAULT_REQUEST_FORMAT
|
198
|
-
|
199
|
-
DEFAULT_RESPONSE_FORMAT = :html
|
200
|
-
private_constant :DEFAULT_RESPONSE_FORMAT
|
201
|
-
|
202
267
|
attr_reader :settings
|
203
268
|
end
|
204
269
|
# rubocop:enable Metrics/ClassLength
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
class Configuration
|
5
|
+
# Hanami router configuration
|
6
|
+
#
|
7
|
+
# @since 2.0.0
|
8
|
+
# @api private
|
9
|
+
class Router
|
10
|
+
# @api private
|
11
|
+
# @since 2.0.0
|
12
|
+
attr_writer :routes
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
# @since 2.0.0
|
16
|
+
attr_reader :routes
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
# @since 2.0.0
|
20
|
+
attr_writer :resolver
|
21
|
+
|
22
|
+
# @api private
|
23
|
+
# @since 2.0.0
|
24
|
+
def initialize(base_url, routes: DEFAULT_ROUTES)
|
25
|
+
@base_url = base_url
|
26
|
+
@routes = routes
|
27
|
+
end
|
28
|
+
|
29
|
+
# @api private
|
30
|
+
# @since 2.0.0
|
31
|
+
def resolver
|
32
|
+
@resolver ||= begin
|
33
|
+
require_relative "../application/routing/resolver"
|
34
|
+
Application::Routing::Resolver
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @api private
|
39
|
+
# @since 2.0.0
|
40
|
+
def options
|
41
|
+
{ base_url: @base_url }
|
42
|
+
end
|
43
|
+
|
44
|
+
# @api private
|
45
|
+
# @since 2.0.0
|
46
|
+
DEFAULT_ROUTES = File.join("config", "routes")
|
47
|
+
private_constant :DEFAULT_ROUTES
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/hanami/init.rb
ADDED
data/lib/hanami/setup.rb
ADDED
data/lib/hanami/slice.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/system/container"
|
4
|
+
require "pathname"
|
5
|
+
|
6
|
+
module Hanami
|
7
|
+
# Distinct area of concern within an Hanami application
|
8
|
+
#
|
9
|
+
# @since 2.0.0
|
10
|
+
class Slice
|
11
|
+
attr_reader :application, :name, :namespace, :root
|
12
|
+
|
13
|
+
def initialize(application, name:, namespace: nil, root: nil, container: nil)
|
14
|
+
@application = application
|
15
|
+
@name = name.to_sym
|
16
|
+
@namespace = namespace
|
17
|
+
@root = root ? Pathname(root) : root
|
18
|
+
@container = container || define_container
|
19
|
+
end
|
20
|
+
|
21
|
+
def inflector
|
22
|
+
application.inflector
|
23
|
+
end
|
24
|
+
|
25
|
+
def namespace_path
|
26
|
+
@namespace_path ||= inflector.underscore(namespace.to_s)
|
27
|
+
end
|
28
|
+
|
29
|
+
def init
|
30
|
+
container.import application: application.container
|
31
|
+
|
32
|
+
slice_block = application.configuration.slices[name]
|
33
|
+
instance_eval(&slice_block) if slice_block
|
34
|
+
end
|
35
|
+
|
36
|
+
def boot
|
37
|
+
container.finalize! do
|
38
|
+
container.config.env = application.container.config.env
|
39
|
+
end
|
40
|
+
|
41
|
+
@booted = true
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
# rubocop:disable Style/DoubleNegation
|
46
|
+
def booted?
|
47
|
+
!!@booted
|
48
|
+
end
|
49
|
+
# rubocop:enable Style/DoubleNegation
|
50
|
+
|
51
|
+
def container
|
52
|
+
@container ||= define_container
|
53
|
+
end
|
54
|
+
|
55
|
+
def import(*slice_names)
|
56
|
+
raise "Cannot import after booting" if booted?
|
57
|
+
|
58
|
+
slice_names.each do |slice_name|
|
59
|
+
container.import slice_name.to_sym => application.slices.fetch(slice_name.to_sym).container
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def register(*args, &block)
|
64
|
+
container.register(*args, &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
def register_bootable(*args, &block)
|
68
|
+
container.boot(*args, &block)
|
69
|
+
end
|
70
|
+
|
71
|
+
def init_bootable(*args)
|
72
|
+
container.init(*args)
|
73
|
+
end
|
74
|
+
|
75
|
+
def start_bootable(*args)
|
76
|
+
container.start(*args)
|
77
|
+
end
|
78
|
+
|
79
|
+
def key?(*args)
|
80
|
+
container.key?(*args)
|
81
|
+
end
|
82
|
+
|
83
|
+
def keys
|
84
|
+
container.keys
|
85
|
+
end
|
86
|
+
|
87
|
+
def [](*args)
|
88
|
+
container[*args]
|
89
|
+
end
|
90
|
+
|
91
|
+
def resolve(*args)
|
92
|
+
container.resolve(*args)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
98
|
+
def define_container
|
99
|
+
container = Class.new(Dry::System::Container)
|
100
|
+
container.use :env
|
101
|
+
|
102
|
+
container.configure do |config|
|
103
|
+
config.name = name
|
104
|
+
config.inflector = application.configuration.inflector
|
105
|
+
|
106
|
+
if application.configuration.autoloader
|
107
|
+
require "dry/system/loader/autoloading"
|
108
|
+
config.component_dirs.loader = Dry::System::Loader::Autoloading
|
109
|
+
config.component_dirs.add_to_load_path = false
|
110
|
+
end
|
111
|
+
|
112
|
+
if root&.directory?
|
113
|
+
config.root = root
|
114
|
+
config.bootable_dirs = ["config/boot"]
|
115
|
+
|
116
|
+
if root.join("lib").directory?
|
117
|
+
config.component_dirs.add "lib" do |dir|
|
118
|
+
dir.default_namespace = namespace_path.tr(File::SEPARATOR, config.namespace_separator)
|
119
|
+
end
|
120
|
+
|
121
|
+
application.configuration.autoloader&.push_dir(root.join("lib"))
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Force after configure hook to run
|
127
|
+
container.configure do; end
|
128
|
+
|
129
|
+
if namespace
|
130
|
+
namespace.const_set :Container, container
|
131
|
+
namespace.const_set :Deps, container.injector
|
132
|
+
end
|
133
|
+
|
134
|
+
container
|
135
|
+
end
|
136
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
137
|
+
end
|
138
|
+
end
|