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,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/utils/basic_object"
4
+
5
+ module Hanami
6
+ class Application
7
+ module Settings
8
+ # Application settings definition DSL
9
+ #
10
+ # @since 2.0.0
11
+ # @api private
12
+ class Definition < Hanami::Utils::BasicObject
13
+ attr_reader :settings
14
+
15
+ def initialize(&block)
16
+ @settings = []
17
+ instance_eval(&block) if block
18
+ end
19
+
20
+ def setting(name, *args)
21
+ @settings << [name, args]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ class Application
5
+ module Settings
6
+ # Default application settings loader. Uses dotenv (if available) to load
7
+ # .env files and then loads settings from ENV.
8
+ #
9
+ # @since 2.0.0
10
+ # @api private
11
+ class Loader
12
+ InvalidSettingsError = Class.new(StandardError) do
13
+ def initialize(errors)
14
+ @errors = errors
15
+ end
16
+
17
+ def to_s
18
+ <<~STR.strip
19
+ Could not initialize settings. The following settings were invalid:
20
+
21
+ #{@errors.map { |setting, message| "#{setting}: #{message}" }.join("\n")}
22
+ STR
23
+ end
24
+ end
25
+
26
+ UnsupportedSettingArgumentError = Class.new(StandardError) do
27
+ def initialize(setting_name, arguments)
28
+ @setting_name = setting_name
29
+ @arguments = arguments
30
+ end
31
+
32
+ def to_s
33
+ <<~STR.strip
34
+ Unsupported arguments #{@arguments.inspect} for setting +#{@setting_name}+
35
+ STR
36
+ end
37
+ end
38
+
39
+ def initialize(*)
40
+ end
41
+
42
+ def call(defined_settings)
43
+ load_dotenv
44
+
45
+ settings, errors = load_settings(defined_settings)
46
+
47
+ raise InvalidSettingsError, errors if errors.any?
48
+
49
+ settings
50
+ end
51
+
52
+ private
53
+
54
+ def load_dotenv
55
+ require "dotenv"
56
+ Dotenv.load(*dotenv_files) if defined?(Dotenv)
57
+ rescue LoadError # rubocop:disable Lint/SuppressedException
58
+ end
59
+
60
+ def dotenv_files
61
+ [
62
+ ".env.#{Hanami.env}.local",
63
+ (".env.local" unless Hanami.env?(:test)),
64
+ ".env.#{Hanami.env}",
65
+ ".env"
66
+ ].compact
67
+ end
68
+
69
+ def load_settings(defined_settings) # rubocop:disable Metrics/MethodLength
70
+ defined_settings.each_with_object([{}, {}]) { |(name, args), (settings, errors)|
71
+ begin
72
+ settings[name] = resolve_setting(name, args)
73
+ rescue => exception # rubocop:disable Style/RescueStandardError
74
+ if exception.is_a?(UnsupportedSettingArgumentError) # rubocop: disable Style/GuardClause
75
+ raise exception
76
+ else
77
+ errors[name] = exception
78
+ end
79
+ end
80
+ }
81
+ end
82
+
83
+ def resolve_setting(name, args)
84
+ value = ENV.fetch(name.to_s.upcase) { Undefined }
85
+
86
+ if args.none?
87
+ value
88
+ elsif args[0]&.respond_to?(:call)
89
+ args[0].call(value)
90
+ else
91
+ raise UnsupportedSettingArgumentError.new(name, args)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ class Application
5
+ module Settings
6
+ # Application settings struct
7
+ #
8
+ # When the application loads settings, a struct subclass is created for
9
+ # the settings defined specifically for the application, then initialized
10
+ # with those settings and their values
11
+ #
12
+ # @since 2.0.0
13
+ # @api public
14
+ class Struct
15
+ class << self
16
+ def [](names)
17
+ Class.new(self) do
18
+ @setting_names = names
19
+
20
+ define_singleton_method(:setting_names) do
21
+ @setting_names
22
+ end
23
+
24
+ define_readers
25
+ end
26
+ end
27
+
28
+ def reserved?(name)
29
+ instance_methods.include?(name)
30
+ end
31
+
32
+ private
33
+
34
+ def define_readers
35
+ setting_names.each do |name|
36
+ next if reserved?(name)
37
+
38
+ define_method(name) do
39
+ @settings[name]
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def initialize(settings)
46
+ @settings = settings.freeze
47
+ end
48
+
49
+ def [](name)
50
+ raise ArgumentError, "Unknown setting +#{name}+" unless self.class.setting_names.include?(name)
51
+
52
+ if self.class.reserved?(name)
53
+ @settings[name]
54
+ else
55
+ public_send(name)
56
+ end
57
+ end
58
+
59
+ def to_h
60
+ @settings.to_h
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
data/lib/hanami/boot.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/setup"
4
- require "hanami"
3
+ require_relative "setup"
5
4
 
6
5
  Hanami.boot
@@ -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
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/cli/registry"
4
+
5
+ module Hanami
6
+ class CLI
7
+ module Application
8
+ # Hanami application CLI commands registry
9
+ module Commands
10
+ extend Hanami::CLI::Registry
11
+
12
+ require_relative "commands/console"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/cli"
4
+ require "hanami/cli/application/command"
5
+ require "hanami/console/context"
6
+
7
+ module Hanami
8
+ class CLI
9
+ module Application
10
+ module Commands # rubocop:disable Style/Documentation
11
+ # Hanami application `console` CLI command
12
+ class Console < Command
13
+ REPL =
14
+ begin
15
+ require "pry"
16
+ Pry
17
+ rescue LoadError
18
+ require "irb"
19
+ IRB
20
+ end
21
+
22
+ desc "Open interactive console"
23
+
24
+ def call(**)
25
+ measure "#{prompt_prefix} booted in" do
26
+ out.puts "=> starting #{prompt_prefix} console"
27
+ application.init
28
+ end
29
+
30
+ start_repl
31
+ end
32
+
33
+ private
34
+
35
+ def start_repl
36
+ context = Hanami::Console::Context.new(application)
37
+
38
+ case REPL.name.to_sym
39
+ when :Pry
40
+ start_pry(context)
41
+ when :IRB
42
+ start_irb(context)
43
+ end
44
+ end
45
+
46
+ def start_pry(context)
47
+ # https://github.com/pry/pry/pull/1877
48
+ if Gem.loaded_specs["pry"].version >= Gem::Version.new("0.13.0")
49
+ Pry.prompt = Pry::Prompt.new(:hanami, "Hanami Console prompt.", prompt_procs)
50
+ Pry.start(context)
51
+ else
52
+ Pry.start(context, prompt_procs)
53
+ end
54
+ end
55
+
56
+ def start_irb(context)
57
+ IRB.start(context, prompt_procs)
58
+ end
59
+
60
+ def prompt_procs
61
+ [proc { default_prompt }, proc { indented_prompt }]
62
+ end
63
+
64
+ def default_prompt
65
+ "#{prompt_prefix}> "
66
+ end
67
+
68
+ def indented_prompt
69
+ "#{prompt_prefix}* "
70
+ end
71
+
72
+ def prompt_prefix
73
+ "#{inflector.underscore(application.application_name)}[#{application.config.env}]"
74
+ end
75
+ end
76
+
77
+ register "console", Console
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/inflector"
4
+ require "hanami/cli/command"
5
+ require "hanami/utils/files"
6
+
7
+ module Hanami
8
+ class CLI
9
+ # Base class for Hanami application CLI commands
10
+ class BaseCommand < Hanami::CLI::Command
11
+ attr_reader :out
12
+ attr_reader :inflector
13
+ attr_reader :files
14
+
15
+ def initialize(
16
+ command_name:,
17
+ out: $stdout,
18
+ inflector: Dry::Inflector.new,
19
+ files: Hanami::Utils::Files
20
+ )
21
+ super(command_name: command_name)
22
+
23
+ @out = out
24
+ @inflector = inflector
25
+ @files = files
26
+ end
27
+
28
+ private
29
+
30
+ def run_command(klass, *args)
31
+ klass.new(
32
+ command_name: klass.name,
33
+ application: application,
34
+ out: out,
35
+ files: files,
36
+ ).call(*args)
37
+ end
38
+
39
+ def measure(desc)
40
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
41
+ yield
42
+ stop = Process.clock_gettime(Process::CLOCK_MONOTONIC)
43
+
44
+ out.puts "=> #{desc} in #{(stop - start).round(1)}s"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "hanami/cli"
3
+ require "dry/cli"
4
+ require "ostruct"
4
5
 
5
6
  module Hanami
6
7
  # Hanami CLI
@@ -55,7 +56,7 @@ module Hanami
55
56
  # @since 1.1.0
56
57
  # @api private
57
58
  module Commands
58
- extend Hanami::CLI::Registry
59
+ extend Dry::CLI::Registry
59
60
 
60
61
  require "hanami/cli/commands/command"
61
62
  require "hanami/cli/commands/server"