hanami 2.0.0.alpha6 → 2.0.0.alpha8

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.
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require "hanami/action/configuration"
5
+ require_relative "actions/cookies"
6
+ require_relative "actions/sessions"
7
+ require_relative "actions/content_security_policy"
8
+ require_relative "../application/view_name_inferrer"
9
+
10
+ module Hanami
11
+ class Configuration
12
+ # Hanami actions configuration
13
+ #
14
+ # @since 2.0.0
15
+ class Actions
16
+ include Dry::Configurable
17
+
18
+ setting :cookies, default: {}, constructor: -> options { Cookies.new(options) }
19
+ setting :sessions, constructor: proc { |storage, *options| Sessions.new(storage, *options) }
20
+ setting :csrf_protection
21
+
22
+ setting :name_inference_base, default: "actions"
23
+ setting :view_context_identifier, default: "view.context"
24
+ setting :view_name_inferrer, default: Application::ViewNameInferrer
25
+ setting :view_name_inference_base, default: "views"
26
+
27
+ attr_accessor :content_security_policy
28
+
29
+ attr_reader :base_configuration
30
+ private :base_configuration
31
+
32
+ def initialize(*, **options)
33
+ super()
34
+
35
+ @base_configuration = Hanami::Action::Configuration.new
36
+ @content_security_policy = ContentSecurityPolicy.new do |csp|
37
+ if assets_server_url = options[:assets_server_url]
38
+ csp[:script_src] += " #{assets_server_url}"
39
+ csp[:style_src] += " #{assets_server_url}"
40
+ end
41
+ end
42
+
43
+ configure_defaults
44
+ end
45
+
46
+ def finalize!
47
+ # A nil value for `csrf_protection` means it has not been explicitly configured
48
+ # (neither true nor false), so we can default it to whether sessions are enabled
49
+ self.csrf_protection = sessions.enabled? if csrf_protection.nil?
50
+
51
+ if content_security_policy
52
+ default_headers["Content-Security-Policy"] = content_security_policy.to_str
53
+ end
54
+ end
55
+
56
+ # Returns the list of available settings
57
+ #
58
+ # @return [Set]
59
+ #
60
+ # @since 2.0.0
61
+ # @api private
62
+ def settings
63
+ base_configuration.settings + self.class.settings
64
+ end
65
+
66
+ private
67
+
68
+ # Apply defaults for base configuration settings
69
+ def configure_defaults
70
+ self.default_request_format = :html
71
+ self.default_response_format = :html
72
+
73
+ self.default_headers = {
74
+ "X-Frame-Options" => "DENY",
75
+ "X-Content-Type-Options" => "nosniff",
76
+ "X-XSS-Protection" => "1; mode=block"
77
+ }
78
+ end
79
+
80
+ def method_missing(name, *args, &block)
81
+ if config.respond_to?(name)
82
+ config.public_send(name, *args, &block)
83
+ elsif base_configuration.respond_to?(name)
84
+ base_configuration.public_send(name, *args, &block)
85
+ else
86
+ super
87
+ end
88
+ end
89
+
90
+ def respond_to_missing?(name, _incude_all = false)
91
+ config.respond_to?(name) || base_configuration.respond_to?(name) || super
92
+ end
93
+ end
94
+ end
95
+ end
@@ -11,9 +11,9 @@ module Hanami
11
11
  class Logger
12
12
  include Dry::Configurable
13
13
 
14
- protected :config
14
+ attr_reader :application_name
15
15
 
16
- setting :application_name
16
+ protected :config
17
17
 
18
18
  setting :level
19
19
 
@@ -30,7 +30,6 @@ module Hanami
30
30
  setting :logger_class, default: Hanami::Logger
31
31
 
32
32
  def initialize(env:, application_name:)
33
- @env = env
34
33
  @application_name = application_name
35
34
 
36
35
  config.level = case env
@@ -58,12 +57,16 @@ module Hanami
58
57
  end
59
58
  end
60
59
 
61
- def finalize!
62
- config.application_name = @application_name.call
63
- end
64
-
65
60
  def instance
66
- logger_class.new(application_name, *options, stream: stream, level: level, formatter: formatter, filter: filters, colorizer: colors)
61
+ logger_class.new(
62
+ application_name.name,
63
+ *options,
64
+ stream: stream,
65
+ level: level,
66
+ formatter: formatter,
67
+ filter: filters,
68
+ colorizer: colors
69
+ )
67
70
  end
68
71
 
69
72
  private
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require "hanami/view"
5
+
6
+ module Hanami
7
+ class Configuration
8
+ # Hanami actions configuration
9
+ #
10
+ # @since 2.0.0
11
+ class Views
12
+ include Dry::Configurable
13
+
14
+ setting :parts_path, default: "view/parts"
15
+
16
+ attr_reader :base_configuration
17
+ private :base_configuration
18
+
19
+ def initialize(*)
20
+ super
21
+
22
+ @base_configuration = Hanami::View.config.dup
23
+
24
+ configure_defaults
25
+ end
26
+
27
+ # Returns the list of available settings
28
+ #
29
+ # @return [Set]
30
+ #
31
+ # @since 2.0.0
32
+ # @api private
33
+ def settings
34
+ self.class.settings + View.settings - NON_FORWARDABLE_METHODS
35
+ end
36
+
37
+ def finalize!
38
+ return self if frozen?
39
+
40
+ base_configuration.finalize!
41
+
42
+ super
43
+ end
44
+
45
+ private
46
+
47
+ def configure_defaults
48
+ self.paths = ["templates"]
49
+ self.template_inference_base = "views"
50
+ self.layout = "application"
51
+ end
52
+
53
+ # An inflector for views is not configurable via `config.views.inflector` on an
54
+ # `Hanami::Application`. The application-wide inflector is already configurable
55
+ # there as `config.inflector` and will be used as the default inflector for views.
56
+ #
57
+ # A custom inflector may still be provided in an `Hanami::View` subclass, via
58
+ # `config.inflector=`.
59
+ NON_FORWARDABLE_METHODS = %i[inflector inflector=].freeze
60
+ private_constant :NON_FORWARDABLE_METHODS
61
+
62
+ def method_missing(name, *args, &block)
63
+ return super if NON_FORWARDABLE_METHODS.include?(name)
64
+
65
+ if config.respond_to?(name)
66
+ config.public_send(name, *args, &block)
67
+ elsif base_configuration.respond_to?(name)
68
+ base_configuration.public_send(name, *args, &block)
69
+ else
70
+ super
71
+ end
72
+ end
73
+
74
+ def respond_to_missing?(name, _include_all = false)
75
+ return false if NON_FORWARDABLE_METHODS.include?(name)
76
+
77
+ config.respond_to?(name) || base_configuration.respond_to?(name) || super
78
+ end
79
+ end
80
+ end
81
+ end
@@ -13,21 +13,20 @@ require_relative "configuration/middleware"
13
13
  require_relative "configuration/router"
14
14
  require_relative "configuration/sessions"
15
15
  require_relative "configuration/source_dirs"
16
+ require_relative "constants"
16
17
 
17
18
  module Hanami
18
19
  # Hanami application configuration
19
20
  #
20
21
  # @since 2.0.0
21
- #
22
- # rubocop:disable Metrics/ClassLength
23
22
  class Configuration
24
23
  include Dry::Configurable
25
24
 
26
25
  DEFAULT_ENVIRONMENTS = Concurrent::Hash.new { |h, k| h[k] = Concurrent::Array.new }
27
26
  private_constant :DEFAULT_ENVIRONMENTS
28
27
 
29
- MODULE_DELIMITER = "::"
30
- private_constant :MODULE_DELIMITER
28
+ attr_reader :application_name
29
+ attr_reader :env
31
30
 
32
31
  attr_reader :actions
33
32
  attr_reader :middleware
@@ -37,57 +36,41 @@ module Hanami
37
36
  attr_reader :environments
38
37
  private :environments
39
38
 
39
+ # rubocop:disable Metrics/AbcSize
40
40
  def initialize(application_name:, env:)
41
- @namespace = application_name.split(MODULE_DELIMITER)[0..-2].join(MODULE_DELIMITER)
41
+ @application_name = application_name
42
42
 
43
43
  @environments = DEFAULT_ENVIRONMENTS.clone
44
- config.env = env
44
+ @env = env
45
45
 
46
46
  # Some default setting values must be assigned at initialize-time to ensure they
47
47
  # have appropriate values for the current application
48
48
  self.root = Dir.pwd
49
49
  self.settings_store = Application::Settings::DotenvStore.new.with_dotenv_loaded
50
50
 
51
- config.logger = Configuration::Logger.new(env: env, application_name: method(:application_name))
51
+ config.logger = Configuration::Logger.new(env: env, application_name: application_name)
52
52
 
53
- @assets = begin
54
- require_path = "hanami/assets/application_configuration"
55
- require require_path
53
+ @assets = load_dependent_config("hanami/assets/application_configuration") {
56
54
  Hanami::Assets::ApplicationConfiguration.new
57
- rescue LoadError => e
58
- raise e unless e.path == require_path
59
- require_relative "configuration/null_configuration"
60
- NullConfiguration.new
61
- end
55
+ }
62
56
 
63
- # Config for actions (same for views, below) may not be available if the gem isn't
64
- # loaded; fall back to a null config object if it's missing
65
- @actions = begin
66
- require_path = "hanami/action/application_configuration"
67
- require require_path
68
- Hanami::Action::ApplicationConfiguration.new(assets_server_url: assets.server_url)
69
- rescue LoadError => e
70
- raise e unless e.path == require_path
71
- require_relative "configuration/null_configuration"
72
- NullConfiguration.new
73
- end
57
+ @actions = load_dependent_config("hanami/action") {
58
+ require_relative "configuration/actions"
59
+ Actions.new
60
+ }
74
61
 
75
62
  @middleware = Middleware.new
76
63
 
77
64
  @router = Router.new(self)
78
65
 
79
- @views = begin
80
- require_path = "hanami/view/application_configuration"
81
- require require_path
82
- Hanami::View::ApplicationConfiguration.new
83
- rescue LoadError => e
84
- raise e unless e.path == require_path
85
- require_relative "configuration/null_configuration"
86
- NullConfiguration.new
87
- end
66
+ @views = load_dependent_config("hanami/view") {
67
+ require_relative "configuration/views"
68
+ Views.new
69
+ }
88
70
 
89
71
  yield self if block_given?
90
72
  end
73
+ # rubocop:enable Metrics/AbcSize
91
74
 
92
75
  def environment(env_name, &block)
93
76
  environments[env_name] << block
@@ -109,21 +92,6 @@ module Hanami
109
92
  super
110
93
  end
111
94
 
112
- def namespace
113
- inflector.constantize(@namespace)
114
- end
115
-
116
- def application_name
117
- inflector.underscore(@namespace).to_sym
118
- end
119
-
120
- setting :env
121
-
122
- def env=(new_env)
123
- config.env = env
124
- apply_env_config(new_env)
125
- end
126
-
127
95
  setting :root, constructor: -> path { Pathname(path) }
128
96
 
129
97
  setting :inflector, default: Dry::Inflector.new
@@ -148,20 +116,8 @@ module Hanami
148
116
 
149
117
  setting :settings_store, default: Application::Settings::DotenvStore
150
118
 
151
- setting :slices_dir, default: "slices"
152
-
153
- setting :slices_namespace, default: Object
154
-
155
- # TODO: convert into a dedicated object with explicit behaviour around blocks per
156
- # slice, etc.
157
- setting :slices, default: {}, constructor: :dup.to_proc
158
-
159
119
  setting :source_dirs, default: Configuration::SourceDirs.new, cloneable: true
160
120
 
161
- def slice(slice_name, &block)
162
- slices[slice_name] = block
163
- end
164
-
165
121
  setting :base_url, default: "http://0.0.0.0:2300", constructor: -> url { URI(url) }
166
122
 
167
123
  def for_each_middleware(&blk)
@@ -181,6 +137,16 @@ module Hanami
181
137
  end
182
138
  end
183
139
 
140
+ def load_dependent_config(require_path, &block)
141
+ require require_path
142
+ yield
143
+ rescue LoadError => e
144
+ raise e unless e.path == require_path
145
+
146
+ require_relative "configuration/null_configuration"
147
+ NullConfiguration.new
148
+ end
149
+
184
150
  def method_missing(name, *args, &block)
185
151
  if config.respond_to?(name)
186
152
  config.public_send(name, *args, &block)
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ CONTAINER_KEY_DELIMITER = "."
5
+ private_constant :CONTAINER_KEY_DELIMITER
6
+
7
+ # @api private
8
+ MODULE_DELIMITER = "::"
9
+ private_constant :MODULE_DELIMITER
10
+
11
+ PATH_DELIMITER = "/"
12
+ private_constant :PATH_DELIMITER
13
+
14
+ # @api private
15
+ CONFIG_DIR = "config"
16
+ private_constant :CONFIG_DIR
17
+
18
+ # @api private
19
+ SLICES_DIR = "slices"
20
+ private_constant :SLICES_DIR
21
+
22
+ # @api private
23
+ LIB_DIR = "lib"
24
+ private_constant :LIB_DIR
25
+
26
+ # @api private
27
+ RB_EXT = ".rb"
28
+ private_constant :RB_EXT
29
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ # @since 2.0.0
5
+ Error = Class.new(StandardError)
6
+
7
+ # @since 2.0.0
8
+ ApplicationLoadError = Class.new(Error)
9
+
10
+ # @since 2.0.0
11
+ SliceLoadError = Class.new(Error)
12
+
13
+ # @since 2.0.0
14
+ ComponentLoadError = Class.new(Error)
15
+ end