hanami 1.3.5 → 2.0.0.alpha1

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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -41
  3. data/FEATURES.md +2 -12
  4. data/LICENSE.md +1 -1
  5. data/README.md +5 -8
  6. data/bin/hanami +6 -4
  7. data/hanami.gemspec +24 -28
  8. data/lib/hanami/application.rb +44 -176
  9. data/lib/hanami/boot.rb +6 -0
  10. data/lib/hanami/cli/commands/command.rb +21 -29
  11. data/lib/hanami/cli/commands/server.rb +63 -14
  12. data/lib/hanami/cli/commands.rb +5 -42
  13. data/lib/hanami/configuration/cookies.rb +24 -0
  14. data/lib/hanami/configuration/middleware.rb +8 -28
  15. data/lib/hanami/configuration/security.rb +141 -0
  16. data/lib/hanami/configuration/sessions.rb +50 -0
  17. data/lib/hanami/configuration.rb +181 -191
  18. data/lib/hanami/container.rb +107 -0
  19. data/lib/hanami/frameworks.rb +24 -18
  20. data/lib/hanami/routes.rb +17 -205
  21. data/lib/hanami/server.rb +5 -69
  22. data/lib/hanami/version.rb +3 -1
  23. data/lib/hanami.rb +39 -243
  24. metadata +36 -233
  25. data/lib/hanami/action/csrf_protection.rb +0 -211
  26. data/lib/hanami/action/routing_helpers.rb +0 -40
  27. data/lib/hanami/app.rb +0 -96
  28. data/lib/hanami/application_configuration.rb +0 -1495
  29. data/lib/hanami/application_name.rb +0 -108
  30. data/lib/hanami/application_namespace.rb +0 -14
  31. data/lib/hanami/assets/asset.rb +0 -72
  32. data/lib/hanami/assets/static.rb +0 -102
  33. data/lib/hanami/cli/commands/assets/precompile.rb +0 -42
  34. data/lib/hanami/cli/commands/assets.rb +0 -16
  35. data/lib/hanami/cli/commands/console.rb +0 -95
  36. data/lib/hanami/cli/commands/db/apply.rb +0 -32
  37. data/lib/hanami/cli/commands/db/console.rb +0 -44
  38. data/lib/hanami/cli/commands/db/create.rb +0 -32
  39. data/lib/hanami/cli/commands/db/drop.rb +0 -32
  40. data/lib/hanami/cli/commands/db/migrate.rb +0 -39
  41. data/lib/hanami/cli/commands/db/prepare.rb +0 -32
  42. data/lib/hanami/cli/commands/db/rollback.rb +0 -59
  43. data/lib/hanami/cli/commands/db/version.rb +0 -32
  44. data/lib/hanami/cli/commands/db.rb +0 -32
  45. data/lib/hanami/cli/commands/destroy/action.rb +0 -137
  46. data/lib/hanami/cli/commands/destroy/app.rb +0 -159
  47. data/lib/hanami/cli/commands/destroy/mailer.rb +0 -74
  48. data/lib/hanami/cli/commands/destroy/migration.rb +0 -51
  49. data/lib/hanami/cli/commands/destroy/model.rb +0 -84
  50. data/lib/hanami/cli/commands/destroy.rb +0 -26
  51. data/lib/hanami/cli/commands/generate/action/action.erb +0 -7
  52. data/lib/hanami/cli/commands/generate/action/action_spec.minitest.erb +0 -11
  53. data/lib/hanami/cli/commands/generate/action/action_spec.rspec.erb +0 -9
  54. data/lib/hanami/cli/commands/generate/action/action_without_view.erb +0 -7
  55. data/lib/hanami/cli/commands/generate/action/view.erb +0 -7
  56. data/lib/hanami/cli/commands/generate/action/view_spec.minitest.erb +0 -12
  57. data/lib/hanami/cli/commands/generate/action/view_spec.rspec.erb +0 -10
  58. data/lib/hanami/cli/commands/generate/action.rb +0 -321
  59. data/lib/hanami/cli/commands/generate/app/application.erb +0 -313
  60. data/lib/hanami/cli/commands/generate/app/favicon.ico +0 -0
  61. data/lib/hanami/cli/commands/generate/app/gitkeep.erb +0 -0
  62. data/lib/hanami/cli/commands/generate/app/layout.erb +0 -7
  63. data/lib/hanami/cli/commands/generate/app/layout_spec.minitest.erb +0 -10
  64. data/lib/hanami/cli/commands/generate/app/layout_spec.rspec.erb +0 -10
  65. data/lib/hanami/cli/commands/generate/app/routes.erb +0 -5
  66. data/lib/hanami/cli/commands/generate/app/template.erb.erb +0 -10
  67. data/lib/hanami/cli/commands/generate/app/template.haml.erb +0 -7
  68. data/lib/hanami/cli/commands/generate/app/template.slim.erb +0 -8
  69. data/lib/hanami/cli/commands/generate/app.rb +0 -243
  70. data/lib/hanami/cli/commands/generate/mailer/mailer.erb +0 -9
  71. data/lib/hanami/cli/commands/generate/mailer/mailer_spec.minitest.erb +0 -7
  72. data/lib/hanami/cli/commands/generate/mailer/mailer_spec.rspec.erb +0 -5
  73. data/lib/hanami/cli/commands/generate/mailer.rb +0 -104
  74. data/lib/hanami/cli/commands/generate/migration/migration.erb +0 -4
  75. data/lib/hanami/cli/commands/generate/migration.rb +0 -41
  76. data/lib/hanami/cli/commands/generate/model/entity.erb +0 -2
  77. data/lib/hanami/cli/commands/generate/model/entity_spec.minitest.erb +0 -5
  78. data/lib/hanami/cli/commands/generate/model/entity_spec.rspec.erb +0 -3
  79. data/lib/hanami/cli/commands/generate/model/migration.erb +0 -10
  80. data/lib/hanami/cli/commands/generate/model/repository.erb +0 -5
  81. data/lib/hanami/cli/commands/generate/model/repository_spec.minitest.erb +0 -5
  82. data/lib/hanami/cli/commands/generate/model/repository_spec.rspec.erb +0 -3
  83. data/lib/hanami/cli/commands/generate/model.rb +0 -125
  84. data/lib/hanami/cli/commands/generate/secret.rb +0 -48
  85. data/lib/hanami/cli/commands/generate.rb +0 -28
  86. data/lib/hanami/cli/commands/new/.env.development.erb +0 -3
  87. data/lib/hanami/cli/commands/new/.env.test.erb +0 -3
  88. data/lib/hanami/cli/commands/new/.gitkeep.erb +0 -0
  89. data/lib/hanami/cli/commands/new/Gemfile.erb +0 -57
  90. data/lib/hanami/cli/commands/new/README.md.erb +0 -33
  91. data/lib/hanami/cli/commands/new/config/boot.erb +0 -2
  92. data/lib/hanami/cli/commands/new/config/environment.erb +0 -49
  93. data/lib/hanami/cli/commands/new/config.ru.erb +0 -3
  94. data/lib/hanami/cli/commands/new/gitignore.erb +0 -4
  95. data/lib/hanami/cli/commands/new/gitignore_with_sqlite.erb +0 -5
  96. data/lib/hanami/cli/commands/new/hanamirc.erb +0 -3
  97. data/lib/hanami/cli/commands/new/lib/project.erb +0 -2
  98. data/lib/hanami/cli/commands/new/minitest/Rakefile.erb +0 -12
  99. data/lib/hanami/cli/commands/new/minitest/features_helper.erb +0 -11
  100. data/lib/hanami/cli/commands/new/minitest/spec_helper.erb +0 -7
  101. data/lib/hanami/cli/commands/new/rspec/Rakefile.erb +0 -9
  102. data/lib/hanami/cli/commands/new/rspec/capybara.erb +0 -8
  103. data/lib/hanami/cli/commands/new/rspec/features_helper.erb +0 -12
  104. data/lib/hanami/cli/commands/new/rspec/rspec.erb +0 -2
  105. data/lib/hanami/cli/commands/new/rspec/spec_helper.erb +0 -103
  106. data/lib/hanami/cli/commands/new/schema.sql.erb +0 -0
  107. data/lib/hanami/cli/commands/new.rb +0 -578
  108. data/lib/hanami/cli/commands/project.rb +0 -421
  109. data/lib/hanami/cli/commands/routes.rb +0 -21
  110. data/lib/hanami/cli/commands/templates.rb +0 -31
  111. data/lib/hanami/cli/commands/version.rb +0 -19
  112. data/lib/hanami/common_logger.rb +0 -109
  113. data/lib/hanami/components/app/assets.rb +0 -59
  114. data/lib/hanami/components/app/controller.rb +0 -74
  115. data/lib/hanami/components/app/routes.rb +0 -59
  116. data/lib/hanami/components/app/view.rb +0 -44
  117. data/lib/hanami/components/component.rb +0 -182
  118. data/lib/hanami/components/components.rb +0 -479
  119. data/lib/hanami/components/routes_inspector.rb +0 -72
  120. data/lib/hanami/components.rb +0 -156
  121. data/lib/hanami/config/cookies.rb +0 -69
  122. data/lib/hanami/config/framework_configuration.rb +0 -43
  123. data/lib/hanami/config/load_paths.rb +0 -46
  124. data/lib/hanami/config/mapper.rb +0 -47
  125. data/lib/hanami/config/routes.rb +0 -20
  126. data/lib/hanami/config/security.rb +0 -110
  127. data/lib/hanami/config/sessions.rb +0 -119
  128. data/lib/hanami/configuration/app.rb +0 -21
  129. data/lib/hanami/early_hints.rb +0 -129
  130. data/lib/hanami/env.rb +0 -69
  131. data/lib/hanami/environment.rb +0 -541
  132. data/lib/hanami/environment_application_configurations.rb +0 -37
  133. data/lib/hanami/hanamirc.rb +0 -169
  134. data/lib/hanami/mailer/glue.rb +0 -27
  135. data/lib/hanami/middleware_stack.rb +0 -172
  136. data/lib/hanami/rake_helper.rb +0 -78
  137. data/lib/hanami/rake_tasks.rb +0 -3
  138. data/lib/hanami/rendering_policy.rb +0 -94
  139. data/lib/hanami/routing/default.rb +0 -32
  140. data/lib/hanami/setup.rb +0 -3
  141. data/lib/hanami/static.rb +0 -63
  142. data/lib/hanami/templates/default.html.erb +0 -30
  143. data/lib/hanami/templates/welcome.html.erb +0 -52
  144. data/lib/hanami/views/default.rb +0 -37
  145. data/lib/hanami/views/default_template_finder.rb +0 -22
  146. data/lib/hanami/views/null_view.rb +0 -15
  147. data/lib/hanami/welcome.rb +0 -41
@@ -1,69 +0,0 @@
1
- module Hanami
2
- # @api private
3
- module Config
4
- # Cookies configuration
5
- #
6
- # @since 0.3.0
7
- # @api private
8
- class Cookies
9
-
10
- # Return the routes for this application
11
- #
12
- # @return [Hash] options for cookies
13
- #
14
- # @since 0.3.0
15
- # @api private
16
- attr_reader :default_options
17
-
18
- # Cookies configuration
19
- #
20
- # httponly option enabled by default.
21
- # Prevent attackers to steal cookies via JavaScript,
22
- # Eg. alert(document.cookie) will fail
23
- #
24
- # @param options [Hash, TrueClass, FalseClass] optional cookies options
25
- # @param configuration [Hanami::Configuration] the application configuration
26
- #
27
- # @since 0.3.0
28
- # @api private
29
- #
30
- # @see https://github.com/rack/rack/blob/master/lib/rack/utils.rb #set_cookie_header!
31
- # @see https://www.owasp.org/index.php/HttpOnly
32
- #
33
- # @example Enable cookies with boolean
34
- # module Web
35
- # class Application < Hanami::Application
36
- # configure do
37
- # # ...
38
- # cookies true
39
- # end
40
- # end
41
- # end
42
- #
43
- # @example Enable cookies with options
44
- # module Web
45
- # class Application < Hanami::Application
46
- # configure do
47
- # # ...
48
- # cookies max_age: 300
49
- # end
50
- # end
51
- # end
52
- def initialize(configuration, options = {})
53
- @options = options
54
- @default_options = { httponly: true, secure: configuration.ssl? }
55
- @default_options.merge!(options) if options.is_a?(::Hash)
56
- end
57
-
58
- # Return if cookies are enabled
59
- #
60
- # @return [TrueClass, FalseClass] enabled cookies
61
- #
62
- # @since 0.3.0
63
- # @api private
64
- def enabled?
65
- @options.respond_to?(:empty?) ? !@options.empty? : !!@options
66
- end
67
- end
68
- end
69
- end
@@ -1,43 +0,0 @@
1
- module Hanami
2
- # @api private
3
- module Config
4
- # Collects all the settings for a given framework configuration and then
5
- # forwards them when the application is loaded.
6
- #
7
- # @since 0.2.0
8
- # @api private
9
- class FrameworkConfiguration < BasicObject
10
- # @since 0.2.0
11
- # @api private
12
- def initialize(&blk)
13
- @blocks = [blk || ::Proc.new { }]
14
- @settings = []
15
- end
16
-
17
- # @since 0.2.0
18
- # @api private
19
- def __apply(configuration)
20
- @blocks.compact.each do |blk|
21
- configuration.instance_eval(&blk)
22
- end
23
-
24
- @settings.each do |(m, args, blk)|
25
- configuration.public_send(m, *args, &blk)
26
- end
27
- end
28
-
29
- # @since 0.6.0
30
- # @api private
31
- def __add(&blk)
32
- @blocks << blk
33
- self
34
- end
35
-
36
- # @since 0.2.0
37
- # @api private
38
- def method_missing(m, *args, &blk)
39
- @settings.push([m, args, blk])
40
- end
41
- end
42
- end
43
- end
@@ -1,46 +0,0 @@
1
- require 'hanami/utils'
2
-
3
- module Hanami
4
- # @api private
5
- module Config
6
- # Define the load paths where the application should load
7
- #
8
- # @since 0.1.0
9
- # @api private
10
- class LoadPaths < Utils::LoadPaths
11
- # Overrides Utils::LoadPath initialize method
12
- #
13
- # @see Hanami::Utils::LoadPaths#initialize
14
- #
15
- # @since 0.1.0
16
- # @api private
17
- def initialize(root)
18
- super()
19
- @root = root
20
- end
21
-
22
- # Requires relative @pats [Utils::Kernel.Array] variable via each method
23
- #
24
- # @see Hanami::Utils::LoadPaths#each
25
- #
26
- # @since 0.1.0
27
- # @api private
28
- def load!
29
- each do |path|
30
- Utils.require!(path)
31
- end
32
- end
33
-
34
- protected
35
-
36
- # Overrides Utils::LoadPath realpath method
37
- #
38
- # @see Hanami::Utils::LoadPaths#realpath
39
- #
40
- # @api private
41
- def realpath(path)
42
- @root.join(path).realpath
43
- end
44
- end
45
- end
46
- end
@@ -1,47 +0,0 @@
1
- require 'hanami/utils/kernel'
2
-
3
- module Hanami
4
- # @since 0.1.0
5
- # @api private
6
- module Config
7
- # Block or file mapper
8
- #
9
- # @since 0.1.0
10
- # @api private
11
- class Mapper
12
- # @api private
13
- EXTNAME = '.rb'
14
-
15
- # @since 0.1.0
16
- # @api private
17
- def initialize(root, path, &blk)
18
- @path, @blk = path, blk
19
- @path = root.join(path) if root && path
20
- end
21
-
22
- # @since 0.1.0
23
- # @api private
24
- def to_proc
25
- return @blk if @blk
26
-
27
- code = realpath.read
28
- Proc.new { eval(code) }
29
- end
30
-
31
- private
32
- # @since 0.1.0
33
- # @api private
34
- def realpath
35
- Utils::Kernel.Pathname("#{ @path }#{ EXTNAME }").realpath
36
- rescue Errno::ENOENT
37
- raise ArgumentError, error_message
38
- end
39
-
40
- # @since 0.1.0
41
- # @api private
42
- def error_message
43
- 'You must specify a block or a file.'
44
- end
45
- end
46
- end
47
- end
@@ -1,20 +0,0 @@
1
- require 'hanami/config/mapper'
2
-
3
- module Hanami
4
- # @since 0.1.0
5
- # @api private
6
- module Config
7
- # Defines a route set
8
- #
9
- # @since 0.1.0
10
- # @api private
11
- class Routes < Mapper
12
- private
13
- # @since 0.1.0
14
- # @api private
15
- def error_message
16
- 'You must specify a block or a file for routes definitions.'
17
- end
18
- end
19
- end
20
- end
@@ -1,110 +0,0 @@
1
- module Hanami
2
- # @since 0.3.0
3
- # @api private
4
- module Config
5
- # Security policies are stored here.
6
- #
7
- # @since 0.3.0
8
- class Security
9
- # @since 0.3.0
10
- # @api private
11
- X_FRAME_OPTIONS_HEADER = 'X-Frame-Options'.freeze
12
-
13
- # @since 0.8.0
14
- # @api private
15
- X_CONTENT_TYPE_OPTIONS_HEADER = 'X-Content-Type-Options'.freeze
16
-
17
- # @since 0.8.0
18
- # @api private
19
- X_XSS_PROTECTION_HEADER = 'X-XSS-Protection'.freeze
20
-
21
- # @since 0.3.0
22
- # @api private
23
- CONTENT_SECURITY_POLICY_HEADER = 'Content-Security-Policy'.freeze
24
-
25
- # @since 0.8.0
26
- # @api private
27
- SEPARATOR = ';'.freeze
28
-
29
- # @since 0.8.0
30
- # @api private
31
- SPACED_SEPARATOR = "#{ SEPARATOR } ".freeze
32
-
33
- # X-Frame-Options headers' value
34
- #
35
- # @overload x_frame_options(value)
36
- # Sets the given value
37
- # @param value [String] for X-Frame-Options header.
38
- #
39
- # @overload x_frame_options
40
- # Gets the value
41
- # @return [String] X-Frame-Options header's value
42
- #
43
- # @since 0.3.0
44
- def x_frame_options(value = nil)
45
- if value.nil?
46
- @x_frame_options
47
- else
48
- @x_frame_options = value
49
- end
50
- end
51
-
52
- # X-Content-Type-Options headers' value
53
- #
54
- # @overload x_content_type_options(value)
55
- # Sets the given value
56
- # @param value [String] for X-Content-Type-Options header.
57
- #
58
- # @overload x_content_type_options
59
- # Gets the value
60
- # @return [String] X-Content-Type-Options header's value
61
- #
62
- # @since 0.8.0
63
- def x_content_type_options(value = nil)
64
- if value.nil?
65
- @x_content_type_options
66
- else
67
- @x_content_type_options = value
68
- end
69
- end
70
-
71
- # X-XSS-Protection headers' value
72
- #
73
- # @overload x_xss_protection(value)
74
- # Sets the given value
75
- # @param value [String] for X-XSS-Protection header.
76
- #
77
- # @overload x_xss_protection
78
- # Gets the value
79
- # @return [String] X-XSS-Protection header's value
80
- #
81
- # @since 0.8.0
82
- def x_xss_protection(value = nil)
83
- if value.nil?
84
- @x_xss_protection
85
- else
86
- @x_xss_protection = value
87
- end
88
- end
89
-
90
- # Content-Policy-Security headers' value
91
- #
92
- # @overload content_security_policy(value)
93
- # Sets the given value
94
- # @param value [String] for Content-Security-Policy header.
95
- #
96
- # @overload content_security_policy
97
- # Gets the value
98
- # @return [String] Content-Security-Policy header's value
99
- #
100
- # @since 0.3.0
101
- def content_security_policy(value = nil)
102
- if value.nil?
103
- @content_security_policy
104
- else
105
- @content_security_policy = value.split(SEPARATOR).map(&:strip).join(SPACED_SEPARATOR)
106
- end
107
- end
108
- end
109
- end
110
- end
@@ -1,119 +0,0 @@
1
- require 'ipaddr'
2
- require 'hanami/utils/string'
3
-
4
- module Hanami
5
- # @since 0.2.0
6
- # @api private
7
- module Config
8
- # Sessions configuration
9
- #
10
- # @since 0.2.0
11
- # @api private
12
- class Sessions
13
-
14
- # Ruby namespace for Rack session adapters
15
- #
16
- # @since 0.2.0
17
- # @api private
18
- RACK_NAMESPACE = 'Rack::Session::%s'.freeze
19
-
20
- # Localhost string for detecting localhost host configuration
21
- #
22
- # @since 0.2.0
23
- # @api private
24
- BLACKLISTED_DOMAINS = %w(localhost).freeze
25
-
26
- # HTTP sessions configuration
27
- #
28
- # @param adapter [Symbol,String,Class] the session adapter
29
- # @param options [Hash] the optional session options
30
- # @param configuration [Hanami::Configuration] the application configuration
31
- #
32
- # @since 0.2.0
33
- # @api private
34
- #
35
- # @see http://www.rubydoc.info/github/rack/rack/Rack/Session/Abstract/ID
36
- # @see https://www.rubydoc.info/github/rack/rack/Rack/Session/Cookie
37
- def initialize(adapter = nil, options = {}, configuration = nil)
38
- @adapter = adapter
39
- @options = options
40
- @configuration = configuration
41
- end
42
-
43
- # Check if the sessions are enabled
44
- #
45
- # @return [FalseClass,TrueClass] the result of the check
46
- #
47
- # @since 0.2.0
48
- # @api private
49
- def enabled?
50
- !!@adapter
51
- end
52
-
53
- # Returns the Rack middleware and the options
54
- #
55
- # @return [Array] Rack middleware and options
56
- #
57
- # @since 0.2.0
58
- # @api private
59
- def middleware
60
- middleware = case @adapter
61
- when Symbol
62
- RACK_NAMESPACE % Utils::String.classify(@adapter)
63
- else
64
- @adapter
65
- end
66
-
67
- [middleware, options]
68
- end
69
-
70
- private
71
-
72
- # @since 0.2.0
73
- # @api private
74
- def options
75
- default_options.merge(@options)
76
- end
77
-
78
- # @since 0.2.0
79
- # @api private
80
- def default_options
81
- result = if @configuration
82
- { domain: domain, secure: @configuration.ssl? }
83
- else
84
- {}
85
- end
86
-
87
- if s = cookies_adapter_serializer
88
- result[:coder] = s
89
- end
90
-
91
- result
92
- end
93
-
94
- # @since 0.2.0
95
- # @api private
96
- def domain
97
- domain = @configuration.host
98
- if !BLACKLISTED_DOMAINS.include?(domain) && !ip_address?(domain)
99
- domain
100
- end
101
- end
102
-
103
- # @since 0.2.0
104
- # @api private
105
- def ip_address?(string)
106
- !!IPAddr.new(string) rescue false
107
- end
108
-
109
- # @since 1.3.5
110
- # @api private
111
- def cookies_adapter_serializer
112
- return nil unless @adapter == :cookie
113
-
114
- require "rack/session/cookie"
115
- Rack::Session::Cookie::Base64::JSON.new
116
- end
117
- end
118
- end
119
- end
@@ -1,21 +0,0 @@
1
- require "delegate"
2
-
3
- module Hanami
4
- # @api private
5
- class Configuration
6
- # @api private
7
- class App < SimpleDelegator
8
- # @api private
9
- attr_reader :path_prefix
10
- # @api private
11
- attr_reader :host
12
-
13
- # @api private
14
- def initialize(app, options = {})
15
- super(app)
16
- @path_prefix = options[:at]
17
- @host = options[:host]
18
- end
19
- end
20
- end
21
- end
@@ -1,129 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hanami
4
- # HTTP/2 Early Hints Rack middleware
5
- #
6
- # It sends extra responses **before** the main response is sent.
7
- # These extra responses are HTTP/2 Early Hints (103).
8
- # They specify the web assets (javascripts, stylesheets, etc..) to be "pushed",
9
- # so modern browsers pre-fetch them in parallel with the main HTTP response.
10
- #
11
- # @see https://tools.ietf.org/html/draft-ietf-httpbis-early-hints-05
12
- #
13
- # @since 1.2.0
14
- # @api private
15
- class EarlyHints
16
- # @since 1.2.0
17
- # @api private
18
- class NotSupportedByServerError < ::StandardError
19
- # @since 1.2.0
20
- # @api private
21
- def initialize
22
- super("Current Ruby server doesn't support Early Hints.\nPlease make sure to use a web server with Early Hints enabled (only Puma for now).")
23
- end
24
- end
25
-
26
- # @since 1.2.0
27
- # @api private
28
- def initialize(app)
29
- @app = app
30
- end
31
-
32
- # @param env [Hash] Rack env
33
- #
34
- # @return [Array,Rack::Response] a Rack response
35
- #
36
- # @raise [Hanami::EarlyHints::NotSupportedByServerError] if the current Ruby
37
- # server doesn't support Early Hints
38
- #
39
- # @since 1.2.0
40
- # @api private
41
- def call(env)
42
- @app.call(env).tap do
43
- send_early_hints(env)
44
- end
45
- end
46
-
47
- private
48
-
49
- # Pushing a lot of assets may exceed the limit of HTTP headers of a single
50
- # Early Hints (103) response.
51
- #
52
- # For this reason we send multiple Early Hints (103) responses for each `n`
53
- # assets. We call this `n` number `BATCH_SIZE`.
54
- #
55
- # If the current page needs to push 23 assets, it will send 3 Early Hints
56
- # (103) responses:
57
- #
58
- # 1. Response #1: 10 assets
59
- # 2. Response #2: 10 assets
60
- # 3. Response #3: 3 assets
61
- #
62
- # @since 1.2.0
63
- # @api private
64
- BATCH_SIZE = 10
65
-
66
- # Rack servers that support Early Hints (only Puma for now),
67
- # inject an object into the Rack env to send multiple Early Hints (103)
68
- # responses.
69
- #
70
- # @since 1.2.0
71
- # @api private
72
- #
73
- # @see https://github.com/puma/puma/pull/1403
74
- RACK_EARLY_HINTS_ENV_KEY = "rack.early_hints"
75
-
76
- # This cache key is used by `hanami-assets` to collect the assets that are
77
- # eligible to be pushed.
78
- #
79
- # It stores these values in a thread-local variable.
80
- #
81
- # NOTE: if changing this key here, it MUST be changed into `hanami-assets` as well
82
- #
83
- # @since 1.2.0
84
- # @api private
85
- CACHE_KEY = :__hanami_assets
86
-
87
- # Tries to send multiple Early Hints (103) HTTP responses, if there are
88
- # assets eligible.
89
- #
90
- # @param env [Hash] Rack env
91
- #
92
- # @raise [Hanami::EarlyHints::NotSupportedByServerError] if the current Ruby
93
- # server doesn't support Early Hints
94
- #
95
- # @since 1.2.0
96
- # @api private
97
- def send_early_hints(env)
98
- return if Thread.current[CACHE_KEY].nil?
99
-
100
- Thread.current[CACHE_KEY].each_slice(BATCH_SIZE) do |slice|
101
- link = slice.map do |asset, options|
102
- ret = %(<#{asset}>; rel=preload)
103
- ret += "; as=#{options[:as]}" unless options[:as].nil?
104
- ret += "; crossorigin" if options[:crossorigin]
105
- ret
106
- end.join("\n")
107
-
108
- send_early_hints_response(env, link)
109
- end
110
- end
111
-
112
- # Tries to send an Early Hints (103) HTTP response for a batch of assets
113
- #
114
- # @param env [Hash] Rack env
115
- # @param link [String] the serialized HTTP `Link` headers
116
- #
117
- # @raise [Hanami::EarlyHints::NotSupportedByServerError] if the current Ruby
118
- # server doesn't support Early Hints
119
- #
120
- # @since 1.2.0
121
- # @api private
122
- def send_early_hints_response(env, link)
123
- env[RACK_EARLY_HINTS_ENV_KEY].call("Link" => link)
124
- rescue NoMethodError => exception
125
- raise exception if env.key?(RACK_EARLY_HINTS_ENV_KEY)
126
- raise NotSupportedByServerError
127
- end
128
- end
129
- end
data/lib/hanami/env.rb DELETED
@@ -1,69 +0,0 @@
1
- begin
2
- require 'dotenv/parser'
3
- rescue LoadError # rubocop:disable Lint/HandleExceptions
4
- end
5
-
6
- module Hanami
7
- # Encapsulate access to ENV
8
- #
9
- # @since 0.9.0
10
- # @api private
11
- class Env
12
- # Create a new instance
13
- #
14
- # @param env [#[],#[]=] a Hash like object. It defaults to ENV
15
- #
16
- # @return [Hanami::Env]
17
- #
18
- # @since 0.9.0
19
- # @api private
20
- def initialize(env: ENV)
21
- @env = env
22
- end
23
-
24
- # Return a value, if found
25
- #
26
- # @param key [String] the key
27
- #
28
- # @return [String,NilClass] the value, if found
29
- #
30
- # @since 0.9.0
31
- # @api private
32
- def [](key)
33
- @env[key]
34
- end
35
-
36
- # Sets a value
37
- #
38
- # @param key [String] the key
39
- # @param value [String] the value
40
- #
41
- # @since 0.9.0
42
- # @api private
43
- def []=(key, value)
44
- @env[key] = value
45
- end
46
-
47
- # Loads a dotenv file and updates self
48
- #
49
- # @param path [String, Pathname] the path to the dotenv file
50
- #
51
- # @return void
52
- #
53
- # @since 0.9.0
54
- # @api private
55
- def load!(path)
56
- return unless defined?(Dotenv::Parser)
57
-
58
- contents = ::File.open(path, "rb:bom|utf-8", &:read)
59
- parsed = Dotenv::Parser.call(contents)
60
-
61
- parsed.each do |k, v|
62
- next if @env.has_key?(k)
63
-
64
- @env[k] = v
65
- end
66
- nil
67
- end
68
- end
69
- end