hanami 2.0.3 → 2.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +25 -9
  5. data/hanami.gemspec +2 -2
  6. data/lib/hanami/config/actions.rb +0 -4
  7. data/lib/hanami/config/views.rb +0 -4
  8. data/lib/hanami/config.rb +54 -0
  9. data/lib/hanami/extensions/action/slice_configured_action.rb +15 -7
  10. data/lib/hanami/extensions/action.rb +4 -4
  11. data/lib/hanami/extensions/router/errors.rb +58 -0
  12. data/lib/hanami/extensions/view/context.rb +129 -60
  13. data/lib/hanami/extensions/view/part.rb +26 -0
  14. data/lib/hanami/extensions/view/scope.rb +26 -0
  15. data/lib/hanami/extensions/view/slice_configured_context.rb +0 -2
  16. data/lib/hanami/extensions/view/slice_configured_helpers.rb +44 -0
  17. data/lib/hanami/extensions/view/slice_configured_view.rb +106 -21
  18. data/lib/hanami/extensions/view/standard_helpers.rb +14 -0
  19. data/lib/hanami/extensions.rb +10 -3
  20. data/lib/hanami/helpers/form_helper/form_builder.rb +1391 -0
  21. data/lib/hanami/helpers/form_helper/values.rb +75 -0
  22. data/lib/hanami/helpers/form_helper.rb +213 -0
  23. data/lib/hanami/middleware/public_errors_app.rb +75 -0
  24. data/lib/hanami/middleware/render_errors.rb +93 -0
  25. data/lib/hanami/slice.rb +27 -2
  26. data/lib/hanami/slice_configurable.rb +3 -2
  27. data/lib/hanami/version.rb +1 -1
  28. data/lib/hanami/web/rack_logger.rb +1 -1
  29. data/lib/hanami.rb +1 -1
  30. data/spec/integration/action/view_rendering/view_context_spec.rb +221 -0
  31. data/spec/integration/action/view_rendering_spec.rb +0 -18
  32. data/spec/integration/rack_app/middleware_spec.rb +23 -23
  33. data/spec/integration/rack_app/rack_app_spec.rb +5 -1
  34. data/spec/integration/view/config/default_context_spec.rb +149 -0
  35. data/spec/integration/view/{inflector_spec.rb → config/inflector_spec.rb} +1 -1
  36. data/spec/integration/view/config/part_class_spec.rb +147 -0
  37. data/spec/integration/view/config/part_namespace_spec.rb +103 -0
  38. data/spec/integration/view/config/paths_spec.rb +119 -0
  39. data/spec/integration/view/config/scope_class_spec.rb +147 -0
  40. data/spec/integration/view/config/scope_namespace_spec.rb +103 -0
  41. data/spec/integration/view/config/template_spec.rb +38 -0
  42. data/spec/integration/view/context/request_spec.rb +3 -7
  43. data/spec/integration/view/helpers/form_helper_spec.rb +174 -0
  44. data/spec/integration/view/helpers/part_helpers_spec.rb +124 -0
  45. data/spec/integration/view/helpers/scope_helpers_spec.rb +84 -0
  46. data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +162 -0
  47. data/spec/integration/view/helpers/user_defined_helpers/scope_helpers_spec.rb +119 -0
  48. data/spec/integration/view/slice_configuration_spec.rb +9 -9
  49. data/spec/integration/web/render_detailed_errors_spec.rb +90 -0
  50. data/spec/integration/web/render_errors_spec.rb +240 -0
  51. data/spec/spec_helper.rb +1 -1
  52. data/spec/support/matchers.rb +32 -0
  53. data/spec/unit/hanami/config/actions/default_values_spec.rb +0 -4
  54. data/spec/unit/hanami/config/render_detailed_errors_spec.rb +25 -0
  55. data/spec/unit/hanami/config/render_errors_spec.rb +25 -0
  56. data/spec/unit/hanami/config/views_spec.rb +0 -18
  57. data/spec/unit/hanami/extensions/view/context_spec.rb +59 -0
  58. data/spec/unit/hanami/helpers/form_helper_spec.rb +2826 -0
  59. data/spec/unit/hanami/router/errors/not_allowed_error_spec.rb +27 -0
  60. data/spec/unit/hanami/router/errors/not_found_error_spec.rb +22 -0
  61. data/spec/unit/hanami/slice_configurable_spec.rb +18 -0
  62. data/spec/unit/hanami/version_spec.rb +1 -1
  63. data/spec/unit/hanami/web/rack_logger_spec.rb +1 -1
  64. metadata +65 -33
  65. data/spec/integration/action/view_integration_spec.rb +0 -165
  66. data/spec/integration/view/part_namespace_spec.rb +0 -96
  67. data/spec/integration/view/path_spec.rb +0 -56
  68. data/spec/integration/view/template_spec.rb +0 -68
  69. data/spec/isolation/hanami/application/already_configured_spec.rb +0 -19
  70. data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +0 -10
  71. data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +0 -14
  72. data/spec/isolation/hanami/application/not_configured_spec.rb +0 -9
  73. data/spec/isolation/hanami/application/routes/configured_spec.rb +0 -44
  74. data/spec/isolation/hanami/application/routes/not_configured_spec.rb +0 -16
  75. data/spec/isolation/hanami/boot/success_spec.rb +0 -50
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e4e96c0b8ba475c9f256fa14b1af20a353804df3f0db3847b814899f7cdd394
4
- data.tar.gz: 8bc463f8095b0bff1ac433fe1869f90e5d7a98e88198546e2fd78ac0caf13639
3
+ metadata.gz: 5b7770e05814c2c09affa6bb627a563f56f64601e2587c474f5139c42a17d25c
4
+ data.tar.gz: 67e7f5b426b6e50023f5c3baad3b91d422b1c82dcc25213f4d6fe4efab0ff681
5
5
  SHA512:
6
- metadata.gz: 5fc16cc328fc1756b03875d8e6ce6383771b432d85deab7c9fb55c77f9e9a573c0ed449312456c322dd202c0402dd40f5eb126d25b3d6c2b6666e6ff4d9d69c1
7
- data.tar.gz: 903eaccd76b79b10d1ff1197fa6f12d99ce824d41a66be2579549884a9d45d25d3d186fc6f56e33b7badafec427b64da1455443ff674488f2bfb5e3502ca65f4
6
+ metadata.gz: eeb3695b89718d12ca4d6376b997af52a21fe39e39e8b8e2e932422d291314a9b8bc1f11ade96279737847283234a783039e8e7e0364fc488def2ab18f8efaf5
7
+ data.tar.gz: 63cf7e291a0b6789eb16430ce0c1945ce945787fab250c8e804869276428045404d0f98205fdf481c9a80e48cf89b38a243bff00b52a900349fcffef5cc62254
data/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  The web, with simplicity.
4
4
 
5
+ ## v2.1.0.beta1 - 2023-06-29
6
+
7
+ ### Added
8
+
9
+ - [Tim Riley] Introduce `Hanami::View` extensions to use alongside hanami-view v2.1.0.beta1 (#1292)
10
+ - [Tim Riley] Enable standard helpers (from hanami-view) for all view parts and scopes (#1303,
11
+ #1307)
12
+ - [Tim Riley] Add `Hanami::Helpers::FormHelper` and include in all view parts and scopes (#1305,
13
+ #1307)
14
+ - [Tim Riley] Include user defined helpers (at `MyApp::Views::Helpers` or `MySlice::Views::Helpers`)
15
+ in all view parts and scopes (#1307)
16
+ - [Tim Riley] Introduce `Hanami::Middleware::RenderErrors` middleware to render error responses (in HTML or JSON) when errors are raised. For HTML responses, the error pages are expected to be found in `public/{404,500}.html` (#1309)
17
+ - [Tim Riley] Use hanami-webconsole (if bundled) to render detailed error pages in development mode (#1311).
18
+
19
+ ### Fixed
20
+
21
+ - [Tim Riley] Prevent matching incorrect slice names (due to partial matches) in `Hanami::SliceConfigurable`. This ensures appropriate config is applied to actions and views within their respective slices. (#1302)
22
+ - [Masanori Ohnishi] Ensure content-length properly appears in rack logs (#1306)
23
+
5
24
  ## v2.0.3 - 2023-02-01
6
25
 
7
26
  ### Added
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright © 2014-2021 Luca Guidi
1
+ Copyright © 2014 Hanami Team
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -82,26 +82,42 @@ We strive for an inclusive and helpful community. We have a [Code of Conduct](ht
82
82
 
83
83
  In addition to contributing code, you can help to triage issues. This can include reproducing bug reports, or asking for vital information such as version numbers or reproduction instructions. If you would like to start triaging issues, one easy way to get started is to [subscribe to hanami on CodeTriage](https://www.codetriage.com/hanami/hanami).
84
84
 
85
- ### Development Requirements
85
+ ### Tests
86
86
 
87
- * Ruby >= 3.0
88
- * Bundler
89
- * Node.js (MacOS)
87
+ To run all test suite:
88
+
89
+ ```shell
90
+ $ bundle exec rake
91
+ ```
90
92
 
91
- ### Testing
93
+ To run all the unit tests:
92
94
 
93
- In order to simulate installed gems on developers' computers, the build installs all the gems locally in `vendor/cache`, including `hanami` code from `lib/`.
95
+ ```shell
96
+ $ bundle exec rspec spec/unit
97
+ ```
94
98
 
95
- **Before running a test, please make sure you have a fresh version of the code:**
99
+ To run all the integration tests:
96
100
 
97
101
  ```shell
98
- bundle exec rspec spec/path/to/file_spec.rb
102
+ $ bundle exec rspec spec/integration
99
103
  ```
100
104
 
105
+ To run a single test:
106
+
107
+ ```shell
108
+ $ bundle exec rspec path/to/spec.rb
109
+ ```
110
+
111
+ ### Development Requirements
112
+
113
+ * Ruby >= 3.0
114
+ * Bundler
115
+ * Node.js (MacOS)
116
+
101
117
  ## Versioning
102
118
 
103
119
  __Hanami__ uses [Semantic Versioning 2.0.0](http://semver.org)
104
120
 
105
121
  ## Copyright
106
122
 
107
- Copyright © 2014-2022 Hanami Team – Released under MIT License.
123
+ Copyright © 2014 Hanami Team – Released under MIT License.
data/hanami.gemspec CHANGED
@@ -38,8 +38,8 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency "dry-monitor", "~> 1.0", ">= 1.0.1", "< 2"
39
39
  spec.add_dependency "dry-system", "~> 1.0", "< 2"
40
40
  spec.add_dependency "dry-logger", "~> 1.0", "< 2"
41
- spec.add_dependency "hanami-cli", "~> 2.0"
42
- spec.add_dependency "hanami-utils", "~> 2.0"
41
+ spec.add_dependency "hanami-cli", "~> 2.1.beta"
42
+ spec.add_dependency "hanami-utils", "~> 2.1.beta"
43
43
  spec.add_dependency "zeitwerk", "~> 2.6"
44
44
 
45
45
  spec.add_development_dependency "rspec", "~> 3.8"
@@ -82,10 +82,6 @@ module Hanami
82
82
  # @api private
83
83
  setting :name_inference_base, default: "actions"
84
84
 
85
- # @!attribute [rw] view_context_identifier
86
- # @api private
87
- setting :view_context_identifier, default: "views.context"
88
-
89
85
  # @!attribute [rw] view_name_inferrer
90
86
  # @api private
91
87
  setting :view_name_inferrer, default: Slice::ViewNameInferrer
@@ -13,8 +13,6 @@ module Hanami
13
13
  class Views
14
14
  include Dry::Configurable
15
15
 
16
- setting :parts_path, default: "views/parts"
17
-
18
16
  attr_reader :base_config
19
17
  protected :base_config
20
18
 
@@ -46,8 +44,6 @@ module Hanami
46
44
  private
47
45
 
48
46
  def configure_defaults
49
- self.paths = ["templates"]
50
- self.template_inference_base = "views"
51
47
  self.layout = "app"
52
48
  end
53
49
 
data/lib/hanami/config.rb CHANGED
@@ -127,6 +127,58 @@ module Hanami
127
127
  # @since 2.0.0
128
128
  setting :base_url, default: "http://0.0.0.0:2300", constructor: ->(url) { URI(url) }
129
129
 
130
+ # @!attribute [rw] render_errors
131
+ # Sets whether to catch exceptions and render error pages.
132
+ #
133
+ # For HTML responses, these error pages are in `public/{404,500}.html`.
134
+ #
135
+ # Defaults to `true` in production mode, `false` in all others.
136
+ #
137
+ # @return [Boolean]
138
+ #
139
+ # @api public
140
+ # @since 2.1.0
141
+ setting :render_errors, default: false
142
+
143
+ # @!attribute [rw] render_detailed_errors
144
+ # Sets whether to catch exceptions and render detailed, interactive error pages.
145
+ #
146
+ # Requires the hanami-webconsole gem to be available.
147
+ #
148
+ # Defaults to `false` in production mode, `true` in all others.
149
+ #
150
+ # @return [Boolean]
151
+ #
152
+ # @api public
153
+ # @since 2.1.0
154
+ setting :render_detailed_errors, default: false
155
+
156
+ # @!attribute [rw] render_error_responses
157
+ # Sets a mapping of exception class names (as strings) to symbolic response status codes used
158
+ # for rendering error responses.
159
+ #
160
+ # The response status codes will be passed to `Rack::Utils.status_code`.
161
+ #
162
+ # In ordinary usage, you should not replace this hash. Instead, add keys and values for the
163
+ # errors you want handled.
164
+ #
165
+ # @example
166
+ # config.render_error_responses
167
+ # # => {"Hanami::Router::NotFoundError" => :not_found}
168
+ #
169
+ # config.render_error_responses["MyNotFoundError"] = :not_found
170
+ #
171
+ # @return [Hash{String => Symbol}]
172
+ #
173
+ # @see #render_errors
174
+ #
175
+ # @api public
176
+ # @since 2.1.0
177
+ setting :render_error_responses, default: Hash.new(:internal_server_error).merge!(
178
+ "Hanami::Router::NotAllowedError" => :not_found,
179
+ "Hanami::Router::NotFoundError" => :not_found,
180
+ )
181
+
130
182
  # Returns the app or slice's {Hanami::SliceName slice_name}.
131
183
  #
132
184
  # This is useful for default config values that depend on this name.
@@ -217,6 +269,8 @@ module Hanami
217
269
 
218
270
  # Apply default values that are only knowable at initialize-time (vs require-time)
219
271
  self.root = Dir.pwd
272
+ self.render_errors = (env == :production)
273
+ self.render_detailed_errors = (env != :production)
220
274
  load_from_env
221
275
 
222
276
  @logger = Config::Logger.new(env: env, app_name: app_name)
@@ -121,18 +121,26 @@ module Hanami
121
121
  slice: slice,
122
122
  )
123
123
 
124
- view_identifiers.detect do |identifier|
125
- break slice[identifier] if slice.key?(identifier)
124
+ view_identifiers.each do |identifier|
125
+ return slice[identifier] if slice.key?(identifier)
126
126
  end
127
+
128
+ nil
127
129
  end
128
130
 
129
131
  def resolve_view_context
130
- identifier = actions_config.view_context_identifier
132
+ if Hanami.bundled?("hanami-view")
133
+ return Extensions::View::Context.context_class(slice).new
134
+ end
135
+
136
+ # If hanami-view isn't bundled, try and find a possible third party context class with the
137
+ # same `Views::Context` name (but don't fall back to automatically defining one).
138
+ if slice.namespace.const_defined?(:Views)
139
+ views_namespace = slice.namespace.const_get(:Views)
131
140
 
132
- if slice.key?(identifier)
133
- slice[identifier]
134
- elsif slice.app.key?(identifier)
135
- slice.app[identifier]
141
+ if views_namespace.const_defined?(:Context)
142
+ views_namespace.const_get(:Context).new
143
+ end
136
144
  end
137
145
  end
138
146
 
@@ -101,13 +101,13 @@ module Hanami
101
101
  end
102
102
 
103
103
  # @api private
104
- def view_options(req, res)
105
- {context: view_context&.with(**view_context_options(req, res))}.compact
104
+ def view_options(request, response)
105
+ {context: view_context&.with(**view_context_options(request, response))}.compact
106
106
  end
107
107
 
108
108
  # @api private
109
- def view_context_options(req, res)
110
- {request: req, response: res}
109
+ def view_context_options(request, response) # rubocop:disable Lint:UnusedMethodArgument
110
+ {request: request}
111
111
  end
112
112
 
113
113
  # Returns true if a view should automatically be rendered onto the response body.
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/router"
4
+
5
+ module Hanami
6
+ class Router
7
+ # Error raised when a request is made for a missing route.
8
+ #
9
+ # Raised only when using hanami-router as part of a full Hanami app. When using hanami-router
10
+ # standalone, the behavior for such requests is to return a "Not Found" response.
11
+ #
12
+ # @api public
13
+ # @since 2.1.0
14
+ class NotFoundError < Hanami::Router::Error
15
+ # @return [Hash] the Rack environment for the request
16
+ #
17
+ # @api public
18
+ # @since 2.1.0
19
+ attr_reader :env
20
+
21
+ def initialize(env)
22
+ @env = env
23
+
24
+ message = "No route found for #{env["REQUEST_METHOD"]} #{env["PATH_INFO"]}"
25
+ super(message)
26
+ end
27
+ end
28
+
29
+ # Error raised when a request is made for a route using a HTTP method not allowed on the route.
30
+ #
31
+ # Raised only when using hanami-router as part of a full Hanami app. When using hanami-router
32
+ # standalone, the behavior for such requests is to return a "Method Not Allowed" response.
33
+ #
34
+ # @api public
35
+ # @since 2.1.0
36
+ class NotAllowedError < Hanami::Router::Error
37
+ # @return [Hash] the Rack environment for the request
38
+ #
39
+ # @api public
40
+ # @since 2.1.0
41
+ attr_reader :env
42
+
43
+ # @return [Array<String>] the allowed methods for the route
44
+ #
45
+ # @api public
46
+ # @since 2.1.0
47
+ attr_reader :allowed_methods
48
+
49
+ def initialize(env, allowed_methods)
50
+ @env = env
51
+ @allowed_methods = allowed_methods
52
+
53
+ message = "Only #{allowed_methods.join(', ')} requests are allowed at #{env["PATH_INFO"]}"
54
+ super(message)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "hanami/view"
4
- require "hanami/view/context"
5
3
  require_relative "../../errors"
6
4
 
7
5
  module Hanami
@@ -13,88 +11,159 @@ module Hanami
13
11
  #
14
12
  # @api private
15
13
  module Context
16
- def self.included(context_class)
17
- super
18
-
19
- context_class.extend(Hanami::SliceConfigurable)
20
- context_class.extend(ClassMethods)
21
- context_class.prepend(InstanceMethods)
22
- end
14
+ class << self
15
+ # Returns a context class for the given slice. If a context class is not defined, defines
16
+ # a class named `Views::Context` within the slice's namespace.
17
+ #
18
+ # @api private
19
+ def context_class(slice)
20
+ views_namespace = views_namespace(slice)
21
+
22
+ if views_namespace.const_defined?(:Context)
23
+ return views_namespace.const_get(:Context)
24
+ end
23
25
 
24
- module ClassMethods
25
- def configure_for_slice(slice)
26
- extend SliceConfiguredContext.new(slice)
26
+ views_namespace.const_set(:Context, Class.new(context_superclass(slice)).tap { |klass|
27
+ klass.configure_for_slice(slice)
28
+ })
27
29
  end
28
- end
29
30
 
30
- module InstanceMethods
31
- # @see SliceConfiguredContext#define_new
32
- def initialize(**kwargs)
33
- defaults = {content: {}}
31
+ private
34
32
 
35
- super(**kwargs, **defaults)
33
+ def context_superclass(slice)
34
+ return Hanami::View::Context if Hanami.app.equal?(slice)
35
+
36
+ begin
37
+ slice.inflector.constantize(
38
+ slice.inflector.camelize("#{slice.app.slice_name.name}/views/context")
39
+ )
40
+ rescue NameError => e
41
+ raise e unless %i[Views Context].include?(e.name)
42
+
43
+ Hanami::View::Context
44
+ end
36
45
  end
37
46
 
38
- def inflector
39
- _options.fetch(:inflector)
47
+ # TODO: this could be moved into the top-level Extensions::View
48
+ def views_namespace(slice)
49
+ if slice.namespace.const_defined?(:Views)
50
+ slice.namespace.const_get(:Views)
51
+ else
52
+ slice.namespace.const_set(:Views, Module.new)
53
+ end
40
54
  end
55
+ end
41
56
 
42
- def routes
43
- _options.fetch(:routes)
57
+ module ClassExtension
58
+ def self.included(context_class)
59
+ super
60
+
61
+ context_class.extend(Hanami::SliceConfigurable)
62
+ context_class.extend(ClassMethods)
63
+ context_class.prepend(InstanceMethods)
44
64
  end
45
65
 
46
- def settings
47
- _options.fetch(:settings)
66
+ module ClassMethods
67
+ def configure_for_slice(slice)
68
+ extend SliceConfiguredContext.new(slice)
69
+ end
48
70
  end
49
71
 
50
- def assets
51
- unless _options[:assets]
52
- raise Hanami::ComponentLoadError, "hanami-assets gem is required to access assets"
72
+ module InstanceMethods
73
+ attr_reader :inflector
74
+
75
+ attr_reader :settings
76
+
77
+ # @see SliceConfiguredContext#define_new
78
+ def initialize( # rubocop:disable Metrics/ParameterLists
79
+ inflector: nil,
80
+ settings: nil,
81
+ routes: nil,
82
+ assets: nil,
83
+ request: nil,
84
+ **args
85
+ )
86
+ @inflector = inflector
87
+ @settings = settings
88
+ @routes = routes
89
+ @assets = assets
90
+ @request = request
91
+
92
+ @content_for = {}
93
+
94
+ super(**args)
53
95
  end
54
96
 
55
- _options[:assets]
56
- end
97
+ def initialize_copy(source)
98
+ # The standard implementation of initialize_copy will make shallow copies of all
99
+ # instance variables from the source. This is fine for most of our ivars.
100
+ super
57
101
 
58
- def content_for(key, value = nil, &block)
59
- content = _options[:content]
60
- output = nil
102
+ # Dup any objects that will be mutated over a given rendering to ensure no leakage of
103
+ # state across distinct view renderings.
104
+ @content_for = source.instance_variable_get(:@content_for).dup
105
+ end
61
106
 
62
- if block
63
- content[key] = yield
64
- elsif value
65
- content[key] = value
66
- else
67
- output = content[key]
107
+ def with(**args)
108
+ self.class.new(
109
+ inflector: @inflector,
110
+ settings: @settings,
111
+ assets: @assets,
112
+ routes: @routes,
113
+ request: @request,
114
+ **args
115
+ )
68
116
  end
69
117
 
70
- output
71
- end
118
+ def assets
119
+ unless @assets
120
+ raise Hanami::ComponentLoadError, "the hanami-assets gem is required to access assets"
121
+ end
72
122
 
73
- def current_path
74
- request.fullpath
75
- end
123
+ @assets
124
+ end
76
125
 
77
- def csrf_token
78
- request.session[Hanami::Action::CSRFProtection::CSRF_TOKEN]
79
- end
126
+ def request
127
+ unless @request
128
+ raise Hanami::ComponentLoadError, "only views rendered from Hanami::Action instances have a request"
129
+ end
80
130
 
81
- def request
82
- _options.fetch(:request)
83
- end
131
+ @request
132
+ end
84
133
 
85
- def session
86
- request.session
87
- end
134
+ def routes
135
+ unless @routes
136
+ raise Hanami::ComponentLoadError, "the hanami-router gem is required to access routes"
137
+ end
88
138
 
89
- def flash
90
- response.flash
91
- end
139
+ @routes
140
+ end
92
141
 
93
- private
142
+ def content_for(key, value = nil)
143
+ if block_given?
144
+ @content_for[key] = yield
145
+ elsif value
146
+ @content_for[key] = value
147
+ else
148
+ @content_for[key]
149
+ end
150
+ end
151
+
152
+ def current_path
153
+ request.fullpath
154
+ end
155
+
156
+ def csrf_token
157
+ request.session[Hanami::Action::CSRFProtection::CSRF_TOKEN]
158
+ end
159
+
160
+ def session
161
+ request.session
162
+ end
94
163
 
95
- # TODO: create `Request#flash` so we no longer need the `response`
96
- def response
97
- _options.fetch(:response)
164
+ def flash
165
+ request.flash
166
+ end
98
167
  end
99
168
  end
100
169
  end
@@ -102,4 +171,4 @@ module Hanami
102
171
  end
103
172
  end
104
173
 
105
- Hanami::View::Context.include(Hanami::Extensions::View::Context)
174
+ Hanami::View::Context.include(Hanami::Extensions::View::Context::ClassExtension)
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Extensions
5
+ module View
6
+ # @api private
7
+ module Part
8
+ def self.included(part_class)
9
+ super
10
+
11
+ part_class.extend(Hanami::SliceConfigurable)
12
+ part_class.include(StandardHelpers)
13
+ part_class.extend(ClassMethods)
14
+ end
15
+
16
+ module ClassMethods
17
+ def configure_for_slice(slice)
18
+ extend SliceConfiguredHelpers.new(slice)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ Hanami::View::Part.include(Hanami::Extensions::View::Part)
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Extensions
5
+ module View
6
+ # @api private
7
+ module Scope
8
+ def self.included(scope_class)
9
+ super
10
+
11
+ scope_class.extend(Hanami::SliceConfigurable)
12
+ scope_class.include(StandardHelpers)
13
+ scope_class.extend(ClassMethods)
14
+ end
15
+
16
+ module ClassMethods
17
+ def configure_for_slice(slice)
18
+ extend SliceConfiguredHelpers.new(slice)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ Hanami::View::Scope.include(Hanami::Extensions::View::Scope)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "hanami/view"
4
-
5
3
  module Hanami
6
4
  module Extensions
7
5
  module View
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Extensions
5
+ module View
6
+ # @api private
7
+ class SliceConfiguredHelpers < Module
8
+ attr_reader :slice
9
+
10
+ def initialize(slice)
11
+ super()
12
+ @slice = slice
13
+ end
14
+
15
+ def extended(klass)
16
+ include_helpers(klass)
17
+ end
18
+
19
+ def inspect
20
+ "#<#{self.class.name}[#{slice.name}]>"
21
+ end
22
+
23
+ private
24
+
25
+ def include_helpers(klass)
26
+ if mod = helpers_module(slice.app)
27
+ klass.include(mod)
28
+ end
29
+
30
+ if mod = helpers_module(slice)
31
+ klass.include(mod)
32
+ end
33
+ end
34
+
35
+ def helpers_module(slice)
36
+ return unless slice.namespace.const_defined?(:Views)
37
+ return unless slice.namespace.const_get(:Views).const_defined?(:Helpers)
38
+
39
+ slice.namespace.const_get(:Views).const_get(:Helpers)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end