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,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
@@ -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
|
data/lib/hanami/cli/commands.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
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
|
59
|
+
extend Dry::CLI::Registry
|
59
60
|
|
60
61
|
require "hanami/cli/commands/command"
|
61
62
|
require "hanami/cli/commands/server"
|