hanami 2.0.3 → 2.1.0.beta1

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 (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
@@ -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,14 @@
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
+ end
12
+ end
13
+ end
14
+ 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