hanami 2.0.0.alpha1 → 2.0.0.alpha5

Sign up to get free protection for your applications and to get access to all the features.
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