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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +70 -5
  3. data/FEATURES.md +9 -1
  4. data/LICENSE.md +1 -1
  5. data/README.md +4 -5
  6. data/hanami.gemspec +11 -11
  7. data/lib/hanami.rb +19 -18
  8. data/lib/hanami/application.rb +322 -26
  9. data/lib/hanami/application/autoloader/inflector_adapter.rb +22 -0
  10. data/lib/hanami/application/container/boot/inflector.rb +7 -0
  11. data/lib/hanami/application/container/boot/logger.rb +8 -0
  12. data/lib/hanami/application/container/boot/rack_logger.rb +19 -0
  13. data/lib/hanami/application/container/boot/rack_monitor.rb +12 -0
  14. data/lib/hanami/application/container/boot/settings.rb +7 -0
  15. data/lib/hanami/application/router.rb +59 -0
  16. data/lib/hanami/application/routing/middleware/stack.rb +89 -0
  17. data/lib/hanami/application/routing/resolver.rb +82 -0
  18. data/lib/hanami/application/routing/resolver/node.rb +50 -0
  19. data/lib/hanami/application/routing/resolver/trie.rb +59 -0
  20. data/lib/hanami/application/settings.rb +23 -0
  21. data/lib/hanami/application/settings/definition.rb +26 -0
  22. data/lib/hanami/application/settings/loader.rb +97 -0
  23. data/lib/hanami/application/settings/struct.rb +65 -0
  24. data/lib/hanami/boot.rb +1 -2
  25. data/lib/hanami/cli/application/cli.rb +40 -0
  26. data/lib/hanami/cli/application/command.rb +47 -0
  27. data/lib/hanami/cli/application/commands.rb +16 -0
  28. data/lib/hanami/cli/application/commands/console.rb +81 -0
  29. data/lib/hanami/cli/base_command.rb +48 -0
  30. data/lib/hanami/cli/commands.rb +3 -2
  31. data/lib/hanami/cli/commands/command.rb +4 -4
  32. data/lib/hanami/configuration.rb +129 -64
  33. data/lib/hanami/configuration/middleware.rb +2 -2
  34. data/lib/hanami/configuration/router.rb +50 -0
  35. data/lib/hanami/init.rb +5 -0
  36. data/lib/hanami/setup.rb +9 -0
  37. data/lib/hanami/slice.rb +138 -0
  38. data/lib/hanami/version.rb +1 -1
  39. data/lib/hanami/web/rack_logger.rb +96 -0
  40. metadata +92 -54
  41. data/bin/hanami +0 -8
  42. data/lib/hanami/configuration/cookies.rb +0 -24
  43. data/lib/hanami/configuration/security.rb +0 -141
  44. data/lib/hanami/container.rb +0 -107
  45. data/lib/hanami/frameworks.rb +0 -28
  46. 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ Hanami.application.register_bootable :inflector do
4
+ start do
5
+ register :inflector, Hanami.application.inflector
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ Hanami.application.register_bootable :logger do
4
+ start do
5
+ require "hanami/logger"
6
+ register :logger, Hanami::Logger.new(**Hanami.application.configuration.logger)
7
+ end
8
+ 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ Hanami.application.register_bootable :settings do
4
+ start do
5
+ register :settings, Hanami.application.settings
6
+ end
7
+ 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