hanami 2.0.0.alpha1 → 2.0.0.alpha5

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +306 -5
  3. data/FEATURES.md +9 -1
  4. data/LICENSE.md +1 -1
  5. data/README.md +9 -6
  6. data/hanami.gemspec +12 -11
  7. data/lib/hanami/application/autoloader/inflector_adapter.rb +22 -0
  8. data/lib/hanami/application/container/boot/inflector.rb +7 -0
  9. data/lib/hanami/application/container/boot/logger.rb +7 -0
  10. data/lib/hanami/application/container/boot/rack_logger.rb +19 -0
  11. data/lib/hanami/application/container/boot/rack_monitor.rb +12 -0
  12. data/lib/hanami/application/container/boot/routes_helper.rb +9 -0
  13. data/lib/hanami/application/container/boot/settings.rb +7 -0
  14. data/lib/hanami/application/router.rb +59 -0
  15. data/lib/hanami/application/routes.rb +55 -0
  16. data/lib/hanami/application/routes_helper.rb +34 -0
  17. data/lib/hanami/application/routing/middleware/stack.rb +89 -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/routing/resolver.rb +87 -0
  21. data/lib/hanami/application/routing/router.rb +36 -0
  22. data/lib/hanami/application/settings/dotenv_store.rb +60 -0
  23. data/lib/hanami/application/settings.rb +93 -0
  24. data/lib/hanami/application.rb +330 -34
  25. data/lib/hanami/assets/application_configuration.rb +63 -0
  26. data/lib/hanami/assets/configuration.rb +54 -0
  27. data/lib/hanami/boot/source_dirs.rb +44 -0
  28. data/lib/hanami/boot.rb +1 -2
  29. data/lib/hanami/cli/application/cli.rb +40 -0
  30. data/lib/hanami/cli/application/command.rb +47 -0
  31. data/lib/hanami/cli/application/commands/console.rb +81 -0
  32. data/lib/hanami/cli/application/commands.rb +16 -0
  33. data/lib/hanami/cli/base_command.rb +48 -0
  34. data/lib/hanami/cli/commands/command.rb +4 -4
  35. data/lib/hanami/cli/commands.rb +3 -2
  36. data/lib/hanami/configuration/logger.rb +84 -0
  37. data/lib/hanami/configuration/middleware.rb +4 -4
  38. data/lib/hanami/configuration/null_configuration.rb +14 -0
  39. data/lib/hanami/configuration/router.rb +52 -0
  40. data/lib/hanami/configuration/sessions.rb +5 -5
  41. data/lib/hanami/configuration/source_dirs.rb +42 -0
  42. data/lib/hanami/configuration.rb +122 -131
  43. data/lib/hanami/init.rb +5 -0
  44. data/lib/hanami/setup.rb +9 -0
  45. data/lib/hanami/slice.rb +189 -0
  46. data/lib/hanami/version.rb +1 -1
  47. data/lib/hanami/web/rack_logger.rb +96 -0
  48. data/lib/hanami.rb +17 -30
  49. metadata +116 -50
  50. data/bin/hanami +0 -8
  51. data/lib/hanami/configuration/cookies.rb +0 -24
  52. data/lib/hanami/configuration/security.rb +0 -141
  53. data/lib/hanami/container.rb +0 -107
  54. data/lib/hanami/frameworks.rb +0 -28
  55. data/lib/hanami/routes.rb +0 -31
@@ -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,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,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
@@ -15,7 +15,7 @@ module Hanami
15
15
  # Abstract command
16
16
  #
17
17
  # @since 1.1.0
18
- class Command < Hanami::CLI::Command
18
+ class Command < Dry::CLI::Command
19
19
  # @since 1.1.0
20
20
  # @api private
21
21
  def self.inherited(component)
@@ -72,9 +72,9 @@ module Hanami
72
72
  def call(**options)
73
73
  # FIXME: merge ENV vars (like HANAMI_ENV) into **options
74
74
  super(options)
75
- rescue StandardError => e
76
- warn e.message
77
- warn e.backtrace.join("\n\t")
75
+ rescue StandardError => exception
76
+ warn exception.message
77
+ warn exception.backtrace.join("\n\t")
78
78
  exit(1)
79
79
  end
80
80
  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"
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require "hanami/logger"
5
+
6
+ module Hanami
7
+ class Configuration
8
+ # Hanami logger configuration
9
+ #
10
+ # @since 2.0.0
11
+ class Logger
12
+ include Dry::Configurable
13
+
14
+ protected :config
15
+
16
+ setting :application_name
17
+
18
+ setting :level
19
+
20
+ setting :stream
21
+
22
+ setting :formatter
23
+
24
+ setting :colors
25
+
26
+ setting :filters, default: %w[_csrf password password_confirmation].freeze
27
+
28
+ setting :options, default: [], constructor: ->(value) { Array(value).flatten }, cloneable: true
29
+
30
+ setting :logger_class, default: Hanami::Logger
31
+
32
+ def initialize(env:, application_name:)
33
+ @env = env
34
+ @application_name = application_name
35
+
36
+ config.level = case env
37
+ when :production
38
+ :info
39
+ else
40
+ :debug
41
+ end
42
+
43
+ config.stream = case env
44
+ when :test
45
+ File.join("log", "#{env}.log")
46
+ else
47
+ $stdout
48
+ end
49
+
50
+ config.formatter = case env
51
+ when :production
52
+ :json
53
+ end
54
+
55
+ config.colors = case env
56
+ when :production, :test
57
+ false
58
+ end
59
+ end
60
+
61
+ def finalize!
62
+ config.application_name = @application_name.call
63
+ end
64
+
65
+ def instance
66
+ logger_class.new(application_name, *options, stream: stream, level: level, formatter: formatter, filter: filters, colorizer: colors)
67
+ end
68
+
69
+ private
70
+
71
+ def method_missing(name, *args, &block)
72
+ if config.respond_to?(name)
73
+ config.public_send(name, *args, &block)
74
+ else
75
+ super
76
+ end
77
+ end
78
+
79
+ def respond_to_missing?(name, _incude_all = false)
80
+ config.respond_to?(name) || super
81
+ end
82
+ end
83
+ end
84
+ end
@@ -6,15 +6,15 @@ module Hanami
6
6
  #
7
7
  # @since 2.0.0
8
8
  class Middleware
9
+ attr_reader :stack
10
+
9
11
  def initialize
10
12
  @stack = []
11
13
  end
12
14
 
13
- def use(middleware, *args)
14
- stack.push([middleware, *args])
15
+ def use(middleware, *args, &block)
16
+ stack.push([middleware, *args, block].compact)
15
17
  end
16
-
17
- attr_reader :stack
18
18
  end
19
19
  end
20
20
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+
5
+ module Hanami
6
+ class Configuration
7
+ # NullConfiguration can serve as a fallback configuration object when out-of-gem
8
+ # configuration objects are not available (specifically, when the hanami-controller or
9
+ # hanami-view gems are not loaded)
10
+ class NullConfiguration
11
+ include Dry::Configurable
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require_relative "../application/routing/resolver"
5
+
6
+ module Hanami
7
+ class Configuration
8
+ # Hanami router configuration
9
+ #
10
+ # @since 2.0.0
11
+ # @api private
12
+ class Router
13
+ include Dry::Configurable
14
+
15
+ # Base configuration is provided so router config can include the `base_url`
16
+ attr_reader :base_configuration
17
+ private :base_configuration
18
+
19
+ # @api private
20
+ # @since 2.0.0
21
+ def initialize(base_configuration)
22
+ @base_configuration = base_configuration
23
+ end
24
+
25
+ setting :routes_path, default: File.join("config", "routes")
26
+
27
+ setting :routes_class_name, default: "Routes"
28
+
29
+ setting :resolver, default: Application::Routing::Resolver
30
+
31
+ # @api private
32
+ # @since 2.0.0
33
+ def options
34
+ {base_url: base_configuration.base_url}
35
+ end
36
+
37
+ private
38
+
39
+ def method_missing(name, *args, &block)
40
+ if config.respond_to?(name)
41
+ config.public_send(name, *args, &block)
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ def respond_to_missing?(name, _include_all = false)
48
+ config.respond_to?(name) || super
49
+ end
50
+ end
51
+ end
52
+ end
@@ -9,8 +9,11 @@ module Hanami
9
9
  #
10
10
  # @since 2.0.0
11
11
  class Sessions
12
+ NULL_SESSION_OPTION = :null
13
+ private_constant :NULL_SESSION_OPTION
14
+
12
15
  def self.null
13
- NULL_STORAGE
16
+ self.class.new(NULL_SESSION_OPTION)
14
17
  end
15
18
 
16
19
  attr_reader :storage, :options
@@ -23,7 +26,7 @@ module Hanami
23
26
  end
24
27
 
25
28
  def enabled?
26
- storage != NULL_STORAGE
29
+ storage != NULL_SESSION_OPTION
27
30
  end
28
31
 
29
32
  def middleware
@@ -32,9 +35,6 @@ module Hanami
32
35
 
33
36
  private
34
37
 
35
- NULL_STORAGE = :null
36
- private_constant :NULL_STORAGE
37
-
38
38
  def storage_middleware
39
39
  require_storage
40
40
 
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require "dry/system/config/component_dirs"
5
+
6
+ module Hanami
7
+ class Configuration
8
+ # Configuration for slice source dirs
9
+ #
10
+ # @since 2.0.0
11
+ class SourceDirs
12
+ DEFAULT_COMPONENT_DIR_PATHS = %w[lib actions repositories views].freeze
13
+ private_constant :DEFAULT_COMPONENT_DIR_PATHS
14
+
15
+ include Dry::Configurable
16
+
17
+ setting :component_dirs,
18
+ default: Dry::System::Config::ComponentDirs.new.tap { |dirs|
19
+ DEFAULT_COMPONENT_DIR_PATHS.each do |path|
20
+ dirs.add path
21
+ end
22
+ },
23
+ cloneable: true
24
+
25
+ setting :autoload_paths, default: %w[entities]
26
+
27
+ private
28
+
29
+ def method_missing(name, *args, &block)
30
+ if config.respond_to?(name)
31
+ config.public_send(name, *args, &block)
32
+ else
33
+ super
34
+ end
35
+ end
36
+
37
+ def respond_to_missing?(name, _include_all = false)
38
+ config.respond_to?(name) || super
39
+ end
40
+ end
41
+ end
42
+ end