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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +79 -0
- data/lib/hanami/application/action/slice_configured_action.rb +103 -0
- data/lib/hanami/application/action.rb +72 -0
- data/lib/hanami/application/routing/router.rb +36 -0
- data/lib/hanami/application/slice_registrar.rb +106 -0
- data/lib/hanami/application/view/context.rb +95 -0
- data/lib/hanami/application/view/slice_configured_context.rb +71 -0
- data/lib/hanami/application/view/slice_configured_view.rb +101 -0
- data/lib/hanami/application/view.rb +24 -0
- data/lib/hanami/application/view_name_inferrer.rb +63 -0
- data/lib/hanami/application.rb +64 -147
- data/lib/hanami/configuration/actions/content_security_policy.rb +118 -0
- data/lib/hanami/configuration/actions/cookies.rb +29 -0
- data/lib/hanami/configuration/actions/sessions.rb +46 -0
- data/lib/hanami/configuration/actions.rb +95 -0
- data/lib/hanami/configuration/logger.rb +11 -8
- data/lib/hanami/configuration/views.rb +81 -0
- data/lib/hanami/configuration.rb +28 -62
- data/lib/hanami/constants.rb +29 -0
- data/lib/hanami/errors.rb +15 -0
- data/lib/hanami/slice.rb +157 -99
- data/lib/hanami/slice_configurable.rb +75 -0
- data/lib/hanami/slice_name.rb +111 -0
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami.rb +13 -5
- metadata +21 -3
@@ -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
|
-
|
14
|
+
attr_reader :application_name
|
15
15
|
|
16
|
-
|
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(
|
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
|
data/lib/hanami/configuration.rb
CHANGED
@@ -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
|
-
|
30
|
-
|
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
|
-
@
|
41
|
+
@application_name = application_name
|
42
42
|
|
43
43
|
@environments = DEFAULT_ENVIRONMENTS.clone
|
44
|
-
|
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:
|
51
|
+
config.logger = Configuration::Logger.new(env: env, application_name: application_name)
|
52
52
|
|
53
|
-
@assets =
|
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
|
-
|
58
|
-
raise e unless e.path == require_path
|
59
|
-
require_relative "configuration/null_configuration"
|
60
|
-
NullConfiguration.new
|
61
|
-
end
|
55
|
+
}
|
62
56
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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 =
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|