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