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.
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"