hanami 2.0.3 → 2.1.0.beta2

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -2
  3. data/LICENSE.md +1 -1
  4. data/README.md +26 -10
  5. data/hanami.gemspec +2 -2
  6. data/lib/hanami/app.rb +5 -0
  7. data/lib/hanami/config/actions.rb +4 -11
  8. data/lib/hanami/config/assets.rb +84 -0
  9. data/lib/hanami/config/null_config.rb +3 -0
  10. data/lib/hanami/config/views.rb +0 -4
  11. data/lib/hanami/config.rb +71 -5
  12. data/lib/hanami/extensions/action/slice_configured_action.rb +15 -7
  13. data/lib/hanami/extensions/action.rb +8 -6
  14. data/lib/hanami/extensions/router/errors.rb +58 -0
  15. data/lib/hanami/extensions/view/context.rb +129 -60
  16. data/lib/hanami/extensions/view/part.rb +26 -0
  17. data/lib/hanami/extensions/view/scope.rb +26 -0
  18. data/lib/hanami/extensions/view/slice_configured_context.rb +0 -2
  19. data/lib/hanami/extensions/view/slice_configured_helpers.rb +44 -0
  20. data/lib/hanami/extensions/view/slice_configured_view.rb +106 -21
  21. data/lib/hanami/extensions/view/standard_helpers.rb +18 -0
  22. data/lib/hanami/extensions.rb +10 -3
  23. data/lib/hanami/helpers/assets_helper.rb +752 -0
  24. data/lib/hanami/helpers/form_helper/form_builder.rb +1391 -0
  25. data/lib/hanami/helpers/form_helper/values.rb +75 -0
  26. data/lib/hanami/helpers/form_helper.rb +213 -0
  27. data/lib/hanami/middleware/assets.rb +21 -0
  28. data/lib/hanami/middleware/public_errors_app.rb +75 -0
  29. data/lib/hanami/middleware/render_errors.rb +90 -0
  30. data/lib/hanami/providers/assets.rb +44 -0
  31. data/lib/hanami/rake_tasks.rb +19 -18
  32. data/lib/hanami/settings.rb +1 -1
  33. data/lib/hanami/slice.rb +48 -2
  34. data/lib/hanami/slice_configurable.rb +3 -2
  35. data/lib/hanami/version.rb +1 -1
  36. data/lib/hanami/web/rack_logger.rb +1 -1
  37. data/lib/hanami.rb +3 -3
  38. data/spec/integration/action/view_rendering/view_context_spec.rb +221 -0
  39. data/spec/integration/action/view_rendering_spec.rb +0 -18
  40. data/spec/integration/assets/assets_spec.rb +101 -0
  41. data/spec/integration/assets/serve_static_assets_spec.rb +152 -0
  42. data/spec/integration/logging/exception_logging_spec.rb +115 -0
  43. data/spec/integration/logging/notifications_spec.rb +68 -0
  44. data/spec/integration/logging/request_logging_spec.rb +128 -0
  45. data/spec/integration/rack_app/middleware_spec.rb +22 -22
  46. data/spec/integration/rack_app/rack_app_spec.rb +3 -220
  47. data/spec/integration/rake_tasks_spec.rb +107 -0
  48. data/spec/integration/view/config/default_context_spec.rb +149 -0
  49. data/spec/integration/view/{inflector_spec.rb → config/inflector_spec.rb} +1 -1
  50. data/spec/integration/view/config/part_class_spec.rb +147 -0
  51. data/spec/integration/view/config/part_namespace_spec.rb +103 -0
  52. data/spec/integration/view/config/paths_spec.rb +119 -0
  53. data/spec/integration/view/config/scope_class_spec.rb +147 -0
  54. data/spec/integration/view/config/scope_namespace_spec.rb +103 -0
  55. data/spec/integration/view/config/template_spec.rb +38 -0
  56. data/spec/integration/view/context/assets_spec.rb +3 -9
  57. data/spec/integration/view/context/request_spec.rb +3 -7
  58. data/spec/integration/view/helpers/form_helper_spec.rb +174 -0
  59. data/spec/integration/view/helpers/part_helpers_spec.rb +124 -0
  60. data/spec/integration/view/helpers/scope_helpers_spec.rb +84 -0
  61. data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +162 -0
  62. data/spec/integration/view/helpers/user_defined_helpers/scope_helpers_spec.rb +119 -0
  63. data/spec/integration/view/slice_configuration_spec.rb +9 -9
  64. data/spec/integration/web/render_detailed_errors_spec.rb +107 -0
  65. data/spec/integration/web/render_errors_spec.rb +242 -0
  66. data/spec/spec_helper.rb +1 -1
  67. data/spec/support/app_integration.rb +46 -2
  68. data/spec/support/matchers.rb +32 -0
  69. data/spec/unit/hanami/config/actions/content_security_policy_spec.rb +24 -36
  70. data/spec/unit/hanami/config/actions/csrf_protection_spec.rb +4 -3
  71. data/spec/unit/hanami/config/actions/default_values_spec.rb +3 -6
  72. data/spec/unit/hanami/config/render_detailed_errors_spec.rb +25 -0
  73. data/spec/unit/hanami/config/render_errors_spec.rb +25 -0
  74. data/spec/unit/hanami/config/views_spec.rb +0 -18
  75. data/spec/unit/hanami/env_spec.rb +11 -25
  76. data/spec/unit/hanami/extensions/view/context_spec.rb +59 -0
  77. data/spec/unit/hanami/helpers/assets_helper/asset_url_spec.rb +109 -0
  78. data/spec/unit/hanami/helpers/assets_helper/audio_tag_spec.rb +132 -0
  79. data/spec/unit/hanami/helpers/assets_helper/favicon_link_tag_spec.rb +91 -0
  80. data/spec/unit/hanami/helpers/assets_helper/image_tag_spec.rb +92 -0
  81. data/spec/unit/hanami/helpers/assets_helper/javascript_tag_spec.rb +143 -0
  82. data/spec/unit/hanami/helpers/assets_helper/stylesheet_link_tag_spec.rb +126 -0
  83. data/spec/unit/hanami/helpers/assets_helper/video_tag_spec.rb +132 -0
  84. data/spec/unit/hanami/helpers/form_helper_spec.rb +2826 -0
  85. data/spec/unit/hanami/router/errors/not_allowed_error_spec.rb +27 -0
  86. data/spec/unit/hanami/router/errors/not_found_error_spec.rb +22 -0
  87. data/spec/unit/hanami/slice_configurable_spec.rb +18 -0
  88. data/spec/unit/hanami/version_spec.rb +1 -1
  89. data/spec/unit/hanami/web/rack_logger_spec.rb +1 -1
  90. metadata +95 -35
  91. data/lib/hanami/assets/app_config.rb +0 -61
  92. data/lib/hanami/assets/config.rb +0 -53
  93. data/spec/integration/action/view_integration_spec.rb +0 -165
  94. data/spec/integration/view/part_namespace_spec.rb +0 -96
  95. data/spec/integration/view/path_spec.rb +0 -56
  96. data/spec/integration/view/template_spec.rb +0 -68
  97. data/spec/isolation/hanami/application/already_configured_spec.rb +0 -19
  98. data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +0 -10
  99. data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +0 -14
  100. data/spec/isolation/hanami/application/not_configured_spec.rb +0 -9
  101. data/spec/isolation/hanami/application/routes/configured_spec.rb +0 -44
  102. data/spec/isolation/hanami/application/routes/not_configured_spec.rb +0 -16
  103. data/spec/isolation/hanami/boot/success_spec.rb +0 -50
@@ -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
@@ -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
@@ -11,6 +9,11 @@ module Hanami
11
9
  # @api private
12
10
  # @since 2.0.0
13
11
  class SliceConfiguredView < Module
12
+ TEMPLATES_DIR = "templates"
13
+ VIEWS_DIR = "views"
14
+ PARTS_DIR = "parts"
15
+ SCOPES_DIR = "scopes"
16
+
14
17
  attr_reader :slice
15
18
 
16
19
  def initialize(slice)
@@ -19,6 +22,7 @@ module Hanami
19
22
  end
20
23
 
21
24
  def extended(view_class)
25
+ load_app_view
22
26
  configure_view(view_class)
23
27
  define_inherited
24
28
  end
@@ -29,6 +33,19 @@ module Hanami
29
33
 
30
34
  private
31
35
 
36
+ # If the given view doesn't inherit from the app view, attempt to load it anyway, since
37
+ # requiring the app view is necessary for _its_ `SliceConfiguredView` hook to execute and
38
+ # define the app-level part and scope classes that we refer to here.
39
+ def load_app_view
40
+ return if app?
41
+
42
+ begin
43
+ slice.app.namespace.const_get(:View, false)
44
+ rescue NameError => e
45
+ raise unless e.name == :View
46
+ end
47
+ end
48
+
32
49
  # rubocop:disable Metrics/AbcSize
33
50
  def configure_view(view_class)
34
51
  view_class.settings.each do |setting|
@@ -84,12 +101,30 @@ module Hanami
84
101
  end
85
102
 
86
103
  view_class.config.inflector = inflector
87
- view_class.config.paths = prepare_paths(slice, view_class.config.paths)
104
+
105
+ # Configure the paths for this view if:
106
+ # - We are the app, and a user hasn't provided custom `paths` (in this case, we need to
107
+ # set the defaults)
108
+ # - We are a slice, and the view's inherited `paths` is identical to the parent's config
109
+ # (which would result in the view in a slice erroneously trying to find templates in
110
+ # the app)
111
+ if (!slice.parent && view_class.config.paths.empty?) ||
112
+ (slice.parent && view_class.config.paths.map(&:dir) == [templates_path(slice.parent)])
113
+ view_class.config.paths = templates_path(slice)
114
+ end
115
+
88
116
  view_class.config.template = template_name(view_class)
117
+ view_class.config.default_context = Extensions::View::Context.context_class(slice).new
89
118
 
90
- if (part_namespace = namespace_from_path(slice.config.views.parts_path))
119
+ view_class.config.part_class = part_class
120
+ view_class.config.scope_class = scope_class
121
+
122
+ if (part_namespace = namespace_from_path("#{VIEWS_DIR}/#{PARTS_DIR}"))
91
123
  view_class.config.part_namespace = part_namespace
92
124
  end
125
+ if (scope_namespace = namespace_from_path("#{VIEWS_DIR}/#{SCOPES_DIR}"))
126
+ view_class.config.scope_namespace = scope_namespace
127
+ end
93
128
  end
94
129
  # rubocop:enable Metrics/AbcSize
95
130
 
@@ -102,20 +137,12 @@ module Hanami
102
137
  end
103
138
  end
104
139
 
105
- def prepare_paths(slice, configured_paths)
106
- configured_paths.map { |path|
107
- if path.dir.relative?
108
- if slice.app.equal?(slice)
109
- # App-level templates are in app/
110
- slice.root.join(APP_DIR, path.dir)
111
- else
112
- # Other slice templates are in the root slice dir
113
- slice.root.join(path.dir)
114
- end
115
- else
116
- path
117
- end
118
- }
140
+ def templates_path(slice)
141
+ if slice.app.equal?(slice)
142
+ slice.root.join(APP_DIR, TEMPLATES_DIR)
143
+ else
144
+ slice.root.join(TEMPLATES_DIR)
145
+ end
119
146
  end
120
147
 
121
148
  def namespace_from_path(path)
@@ -134,16 +161,74 @@ module Hanami
134
161
  end
135
162
 
136
163
  def template_name(view_class)
137
- slice
138
- .inflector
164
+ inflector
139
165
  .underscore(view_class.name)
140
166
  .sub(/^#{slice.slice_name.path}\//, "")
141
- .sub(/^#{view_class.config.template_inference_base}\//, "")
167
+ .sub(/^#{VIEWS_DIR}\//, "")
142
168
  end
143
169
 
144
170
  def inflector
145
171
  slice.inflector
146
172
  end
173
+
174
+ def part_class
175
+ @part_class ||=
176
+ if views_namespace.const_defined?(:Part)
177
+ views_namespace.const_get(:Part)
178
+ else
179
+ views_namespace.const_set(:Part, Class.new(part_superclass).tap { |klass|
180
+ # Give the slice to `configure_for_slice`, since it cannot be inferred when it is
181
+ # called via `.inherited`, since the class is anonymous at this point
182
+ klass.configure_for_slice(slice)
183
+ })
184
+ end
185
+ end
186
+
187
+ def part_superclass
188
+ return Hanami::View::Part if app?
189
+
190
+ begin
191
+ inflector.constantize(inflector.camelize("#{slice.app.slice_name.name}/views/part"))
192
+ rescue NameError
193
+ Hanami::View::Part
194
+ end
195
+ end
196
+
197
+ def scope_class
198
+ @scope_class ||=
199
+ if views_namespace.const_defined?(:Scope)
200
+ views_namespace.const_get(:Scope)
201
+ else
202
+ views_namespace.const_set(:Scope, Class.new(scope_superclass).tap { |klass|
203
+ # Give the slice to `configure_for_slice`, since it cannot be inferred when it is
204
+ # called via `.inherited`, since the class is anonymous at this point
205
+ klass.configure_for_slice(slice)
206
+ })
207
+ end
208
+ end
209
+
210
+ def scope_superclass
211
+ return Hanami::View::Scope if app?
212
+
213
+ begin
214
+ inflector.constantize(inflector.camelize("#{slice.app.slice_name.name}/views/scope"))
215
+ rescue NameError
216
+ Hanami::View::Scope
217
+ end
218
+ end
219
+
220
+ def views_namespace
221
+ @slice_views_namespace ||=
222
+ if slice.namespace.const_defined?(:Views)
223
+ slice.namespace.const_get(:Views)
224
+ else
225
+ slice.namespace.const_set(:Views, Module.new)
226
+ end
227
+ end
228
+
229
+ def app?
230
+ slice.app == slice
231
+ end
147
232
  end
148
233
  end
149
234
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Extensions
5
+ module View
6
+ module StandardHelpers
7
+ include Hanami::View::Helpers::EscapeHelper
8
+ include Hanami::View::Helpers::NumberFormattingHelper
9
+ include Hanami::View::Helpers::TagHelper
10
+ include Helpers::FormHelper
11
+
12
+ if Hanami.bundled?("hanami-assets")
13
+ include Helpers::AssetsHelper
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,10 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  if Hanami.bundled?("hanami-controller")
4
- require_relative "./extensions/action"
4
+ require_relative "extensions/action"
5
5
  end
6
6
 
7
7
  if Hanami.bundled?("hanami-view")
8
- require_relative "./extensions/view"
9
- require_relative "./extensions/view/context"
8
+ require "hanami/view"
9
+ require_relative "extensions/view"
10
+ require_relative "extensions/view/context"
11
+ require_relative "extensions/view/part"
12
+ require_relative "extensions/view/scope"
13
+ end
14
+
15
+ if Hanami.bundled?("hanami-router")
16
+ require_relative "extensions/router/errors"
10
17
  end