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
@@ -0,0 +1,22 @@
|
|
1
|
+
module Hanami
|
2
|
+
class Application
|
3
|
+
module Autoloader
|
4
|
+
# Allows the Hanami standard inflector (from dry-inflector) to be used with Zeitwerk
|
5
|
+
class InflectorAdapter
|
6
|
+
def initialize(inflector)
|
7
|
+
@inflector = inflector
|
8
|
+
end
|
9
|
+
|
10
|
+
def camelize(basename, _abspath)
|
11
|
+
# Discard unused `_abspath` argument before calling our own inflector's
|
12
|
+
# `#camelize` (which takes only one argument)
|
13
|
+
inflector.camelize(basename)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :inflector
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Hanami.application.register_bootable :rack_logger do |container|
|
4
|
+
start do
|
5
|
+
require "hanami/web/rack_logger"
|
6
|
+
|
7
|
+
use :logger
|
8
|
+
use :rack_monitor
|
9
|
+
|
10
|
+
rack_logger = Hanami::Web::RackLogger.new(
|
11
|
+
container[:logger],
|
12
|
+
filter_params: Hanami.application.configuration.rack_logger_filter_params
|
13
|
+
)
|
14
|
+
|
15
|
+
rack_logger.attach container[:rack_monitor]
|
16
|
+
|
17
|
+
register :rack_logger, rack_logger
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Hanami.application.register_bootable :rack_monitor do |container|
|
4
|
+
start do
|
5
|
+
require "dry/monitor"
|
6
|
+
require "dry/monitor/rack/middleware"
|
7
|
+
|
8
|
+
middleware = Dry::Monitor::Rack::Middleware.new(container[:notifications])
|
9
|
+
|
10
|
+
register :rack_monitor, middleware
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hanami/router"
|
4
|
+
require "hanami/application/routing/middleware/stack"
|
5
|
+
|
6
|
+
module Hanami
|
7
|
+
class Application
|
8
|
+
# Hanami application router
|
9
|
+
# @since 2.0.0
|
10
|
+
class Router < ::Hanami::Router
|
11
|
+
# @since 2.0.0
|
12
|
+
# @api private
|
13
|
+
def initialize(routes:, stack: Routing::Middleware::Stack.new, **kwargs, &blk)
|
14
|
+
@stack = stack
|
15
|
+
instance_eval(&blk)
|
16
|
+
super(**kwargs, &routes)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @since 2.0.0
|
20
|
+
# @api private
|
21
|
+
def freeze
|
22
|
+
return self if frozen?
|
23
|
+
|
24
|
+
remove_instance_variable(:@stack)
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
# @since 2.0.0
|
29
|
+
# @api private
|
30
|
+
def use(middleware, *args, &blk)
|
31
|
+
@stack.use(middleware, *args, &blk)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @since 2.0.0
|
35
|
+
# @api private
|
36
|
+
def scope(*args, &blk)
|
37
|
+
@stack.with(args.first) do
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @since 2.0.0
|
43
|
+
def slice(name, at:, &blk)
|
44
|
+
path = prefixed_path(at)
|
45
|
+
@resolver.register_slice_at_path(name, path)
|
46
|
+
|
47
|
+
scope(path, &blk)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @since 2.0.0
|
51
|
+
# @api private
|
52
|
+
def to_rack_app
|
53
|
+
return self if @stack.empty?
|
54
|
+
|
55
|
+
@stack.to_rack_app(self)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack/builder"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
class Application
|
7
|
+
module Routing
|
8
|
+
# Hanami::Applicatione::Router middleware stack
|
9
|
+
#
|
10
|
+
# @since 2.0.0
|
11
|
+
# @api private
|
12
|
+
module Middleware
|
13
|
+
# Middleware stack
|
14
|
+
#
|
15
|
+
# @since 2.0.0
|
16
|
+
# @api private
|
17
|
+
class Stack
|
18
|
+
# @since 2.0.0
|
19
|
+
# @api private
|
20
|
+
ROOT_PREFIX = "/"
|
21
|
+
private_constant :ROOT_PREFIX
|
22
|
+
|
23
|
+
# @since 2.0.0
|
24
|
+
# @api private
|
25
|
+
def initialize
|
26
|
+
@prefix = ROOT_PREFIX
|
27
|
+
@stack = Hash.new { |hash, key| hash[key] = [] }
|
28
|
+
end
|
29
|
+
|
30
|
+
# @since 2.0.0
|
31
|
+
# @api private
|
32
|
+
def use(middleware, *args, &blk)
|
33
|
+
@stack[@prefix].push([middleware, args, blk])
|
34
|
+
end
|
35
|
+
|
36
|
+
# @since 2.0.0
|
37
|
+
# @api private
|
38
|
+
def with(path)
|
39
|
+
prefix = @prefix
|
40
|
+
@prefix = path
|
41
|
+
yield
|
42
|
+
ensure
|
43
|
+
@prefix = prefix
|
44
|
+
end
|
45
|
+
|
46
|
+
# @since 2.0.0
|
47
|
+
# @api private
|
48
|
+
def to_rack_app(app) # rubocop:disable Metrics/MethodLength
|
49
|
+
s = self
|
50
|
+
|
51
|
+
Rack::Builder.new do
|
52
|
+
s.each do |prefix, stack|
|
53
|
+
s.mapped(self, prefix) do
|
54
|
+
stack.each do |middleware, args, blk|
|
55
|
+
use(middleware, *args, &blk)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
run app
|
60
|
+
end
|
61
|
+
end.to_app
|
62
|
+
end
|
63
|
+
|
64
|
+
# @since 2.0.0
|
65
|
+
# @api private
|
66
|
+
def empty?
|
67
|
+
@stack.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
# @since 2.0.0
|
71
|
+
# @api private
|
72
|
+
def each(&blk)
|
73
|
+
@stack.each(&blk)
|
74
|
+
end
|
75
|
+
|
76
|
+
# @since 2.0.0
|
77
|
+
# @api private
|
78
|
+
def mapped(builder, prefix, &blk)
|
79
|
+
if prefix == ROOT_PREFIX
|
80
|
+
builder.instance_eval(&blk)
|
81
|
+
else
|
82
|
+
builder.map(prefix, &blk)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
class Application
|
5
|
+
module Routing
|
6
|
+
# Hanami application router endpoint resolver
|
7
|
+
#
|
8
|
+
# @since 2.0.0
|
9
|
+
class Resolver
|
10
|
+
require_relative "resolver/trie"
|
11
|
+
|
12
|
+
# @since 2.0.0
|
13
|
+
class NotCallableEndpointError < StandardError
|
14
|
+
def initialize(endpoint)
|
15
|
+
super("#{endpoint.inspect} is not compatible with Rack. Please make sure it implements #call.")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
# @since 2.0.0
|
21
|
+
def initialize(slices:, inflector:)
|
22
|
+
@slices = slices
|
23
|
+
@inflector = inflector
|
24
|
+
@slices_registry = Trie.new
|
25
|
+
end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
# @since 2.0.0
|
29
|
+
#
|
30
|
+
# rubocop:disable Metrics/MethodLength
|
31
|
+
def call(path, identifier)
|
32
|
+
endpoint =
|
33
|
+
case identifier
|
34
|
+
when String
|
35
|
+
resolve_string_identifier(path, identifier)
|
36
|
+
when Class
|
37
|
+
identifier.respond_to?(:call) ? identifier : identifier.new
|
38
|
+
else
|
39
|
+
identifier
|
40
|
+
end
|
41
|
+
|
42
|
+
unless endpoint.respond_to?(:call) # rubocop:disable Style/IfUnlessModifier
|
43
|
+
raise NotCallableEndpointError.new(endpoint)
|
44
|
+
end
|
45
|
+
|
46
|
+
endpoint
|
47
|
+
end
|
48
|
+
# rubocop:enable Metrics/MethodLength
|
49
|
+
|
50
|
+
# @api private
|
51
|
+
# @since 2.0.0
|
52
|
+
def register_slice_at_path(name, path)
|
53
|
+
slices_registry.add(path, name)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# @api private
|
59
|
+
# @since 2.0.0
|
60
|
+
attr_reader :slices
|
61
|
+
|
62
|
+
# @api private
|
63
|
+
# @since 2.0.0
|
64
|
+
attr_reader :inflector
|
65
|
+
|
66
|
+
# @api private
|
67
|
+
# @since 2.0.0
|
68
|
+
attr_reader :slices_registry
|
69
|
+
|
70
|
+
# @api private
|
71
|
+
# @since 2.0.0
|
72
|
+
def resolve_string_identifier(path, identifier)
|
73
|
+
slice_name = slices_registry.find(path) or raise "missing slice for #{path.inspect} (#{identifier.inspect})"
|
74
|
+
slice = slices[slice_name]
|
75
|
+
action_key = "actions.#{identifier.gsub(/[#\/]/, '.')}"
|
76
|
+
|
77
|
+
slice[action_key]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
class Application
|
5
|
+
module Routing
|
6
|
+
class Resolver
|
7
|
+
# Endpoint resolver node to register slices in a tree
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
# @since 2.0.0
|
11
|
+
class Node
|
12
|
+
# @api private
|
13
|
+
# @since 2.0.0
|
14
|
+
attr_reader :slice
|
15
|
+
|
16
|
+
# @api private
|
17
|
+
# @since 2.0.0
|
18
|
+
def initialize
|
19
|
+
@slice = nil
|
20
|
+
@children = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
# @since 2.0.0
|
25
|
+
def put(segment)
|
26
|
+
@children[segment] ||= self.class.new
|
27
|
+
end
|
28
|
+
|
29
|
+
# @api private
|
30
|
+
# @since 2.0.0
|
31
|
+
def get(segment)
|
32
|
+
@children.fetch(segment) { self if leaf? }
|
33
|
+
end
|
34
|
+
|
35
|
+
# @api private
|
36
|
+
# @since 2.0.0
|
37
|
+
def leaf!(slice)
|
38
|
+
@slice = slice
|
39
|
+
end
|
40
|
+
|
41
|
+
# @api private
|
42
|
+
# @since 2.0.0
|
43
|
+
def leaf?
|
44
|
+
@slice
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hanami/application/routing/resolver/node"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
class Application
|
7
|
+
module Routing
|
8
|
+
class Resolver
|
9
|
+
# Endpoint resolver trie to register slices
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
# @since 2.0.0
|
13
|
+
class Trie
|
14
|
+
# @api private
|
15
|
+
# @since 2.0.0
|
16
|
+
def initialize
|
17
|
+
@root = Node.new
|
18
|
+
end
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
# @since 2.0.0
|
22
|
+
def add(path, name)
|
23
|
+
node = @root
|
24
|
+
for_each_segment(path) do |segment|
|
25
|
+
node = node.put(segment)
|
26
|
+
end
|
27
|
+
|
28
|
+
node.leaf!(name)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @api private
|
32
|
+
# @since 2.0.0
|
33
|
+
def find(path)
|
34
|
+
node = @root
|
35
|
+
|
36
|
+
for_each_segment(path) do |segment|
|
37
|
+
break unless node
|
38
|
+
|
39
|
+
node = node.get(segment)
|
40
|
+
end
|
41
|
+
|
42
|
+
return node.slice if node&.leaf?
|
43
|
+
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
# @since 2.0.0
|
51
|
+
def for_each_segment(path, &blk)
|
52
|
+
_, *segments = path.split(/\//)
|
53
|
+
segments.each(&blk)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/constants"
|
4
|
+
require_relative "settings/definition"
|
5
|
+
require_relative "settings/struct"
|
6
|
+
|
7
|
+
module Hanami
|
8
|
+
class Application
|
9
|
+
# Application settings
|
10
|
+
#
|
11
|
+
# @since 2.0.0
|
12
|
+
module Settings
|
13
|
+
Undefined = Dry::Core::Constants::Undefined
|
14
|
+
|
15
|
+
def self.build(loader, loader_options, &definition_block)
|
16
|
+
definition = Definition.new(&definition_block)
|
17
|
+
settings = loader.new(**loader_options).call(definition.settings)
|
18
|
+
|
19
|
+
Struct[settings.keys].new(settings)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|