hanami-view 1.3.1 → 2.0.0.alpha3

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/LICENSE +20 -0
  4. data/README.md +17 -835
  5. data/hanami-view.gemspec +26 -16
  6. data/lib/hanami/view/application_configuration.rb +77 -0
  7. data/lib/hanami/view/application_context.rb +35 -0
  8. data/lib/hanami/view/application_view.rb +89 -0
  9. data/lib/hanami/view/context.rb +97 -0
  10. data/lib/hanami/view/context_helpers/content_helpers.rb +26 -0
  11. data/lib/hanami/view/decorated_attributes.rb +82 -0
  12. data/lib/hanami/view/errors.rb +31 -53
  13. data/lib/hanami/view/exposure.rb +126 -0
  14. data/lib/hanami/view/exposures.rb +74 -0
  15. data/lib/hanami/view/part.rb +217 -0
  16. data/lib/hanami/view/part_builder.rb +140 -0
  17. data/lib/hanami/view/path.rb +68 -0
  18. data/lib/hanami/view/render_environment.rb +62 -0
  19. data/lib/hanami/view/render_environment_missing.rb +44 -0
  20. data/lib/hanami/view/rendered.rb +55 -0
  21. data/lib/hanami/view/renderer.rb +79 -0
  22. data/lib/hanami/view/scope.rb +189 -0
  23. data/lib/hanami/view/scope_builder.rb +98 -0
  24. data/lib/hanami/view/standalone_view.rb +400 -0
  25. data/lib/hanami/view/tilt/erb.rb +26 -0
  26. data/lib/hanami/view/tilt/erbse.rb +21 -0
  27. data/lib/hanami/view/tilt/haml.rb +26 -0
  28. data/lib/hanami/view/tilt.rb +78 -0
  29. data/lib/hanami/view/version.rb +5 -5
  30. data/lib/hanami/view.rb +208 -223
  31. data/lib/hanami-view.rb +3 -1
  32. metadata +120 -70
  33. data/LICENSE.md +0 -22
  34. data/lib/hanami/layout.rb +0 -190
  35. data/lib/hanami/presenter.rb +0 -98
  36. data/lib/hanami/view/configuration.rb +0 -504
  37. data/lib/hanami/view/dsl.rb +0 -347
  38. data/lib/hanami/view/escape.rb +0 -225
  39. data/lib/hanami/view/inheritable.rb +0 -54
  40. data/lib/hanami/view/rendering/layout_finder.rb +0 -128
  41. data/lib/hanami/view/rendering/layout_registry.rb +0 -69
  42. data/lib/hanami/view/rendering/layout_scope.rb +0 -274
  43. data/lib/hanami/view/rendering/null_layout.rb +0 -52
  44. data/lib/hanami/view/rendering/null_local.rb +0 -82
  45. data/lib/hanami/view/rendering/null_template.rb +0 -83
  46. data/lib/hanami/view/rendering/null_view.rb +0 -26
  47. data/lib/hanami/view/rendering/options.rb +0 -24
  48. data/lib/hanami/view/rendering/partial.rb +0 -31
  49. data/lib/hanami/view/rendering/partial_file.rb +0 -29
  50. data/lib/hanami/view/rendering/partial_finder.rb +0 -75
  51. data/lib/hanami/view/rendering/partial_templates_finder.rb +0 -73
  52. data/lib/hanami/view/rendering/registry.rb +0 -134
  53. data/lib/hanami/view/rendering/scope.rb +0 -108
  54. data/lib/hanami/view/rendering/subscope.rb +0 -56
  55. data/lib/hanami/view/rendering/template.rb +0 -69
  56. data/lib/hanami/view/rendering/template_finder.rb +0 -55
  57. data/lib/hanami/view/rendering/template_name.rb +0 -50
  58. data/lib/hanami/view/rendering/templates_finder.rb +0 -144
  59. data/lib/hanami/view/rendering/view_finder.rb +0 -37
  60. data/lib/hanami/view/rendering.rb +0 -294
  61. data/lib/hanami/view/template.rb +0 -57
data/lib/hanami/view.rb CHANGED
@@ -1,267 +1,252 @@
1
- require 'set'
2
- require 'pathname'
3
- require 'hanami/utils/class_attribute'
4
- require 'hanami/view/version'
5
- require 'hanami/view/configuration'
6
- require 'hanami/view/inheritable'
7
- require 'hanami/view/rendering'
8
- require 'hanami/view/escape'
9
- require 'hanami/view/dsl'
10
- require 'hanami/view/errors'
11
- require 'hanami/layout'
12
- require 'hanami/presenter'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require "dry/core/cache"
5
+ require "dry/core/equalizer"
6
+ require "dry/inflector"
7
+
8
+ require_relative "view/application_view"
9
+ require_relative "view/context"
10
+ require_relative "view/exposures"
11
+ require_relative "view/errors"
12
+ require_relative "view/part_builder"
13
+ require_relative "view/path"
14
+ require_relative "view/render_environment"
15
+ require_relative "view/rendered"
16
+ require_relative "view/renderer"
17
+ require_relative "view/scope_builder"
18
+ require_relative "view/standalone_view"
13
19
 
14
- # Hanami
15
- #
16
- # @since 0.1.0
17
20
  module Hanami
18
- # View
21
+ # A standalone, template-based view rendering system that offers everything
22
+ # you need to write well-factored view code.
19
23
  #
20
- # @since 0.1.0
21
- module View
22
- include Utils::ClassAttribute
23
- # Framework configuration
24
- #
25
- # @since 0.2.0
24
+ # This represents a single view, holding the configuration and exposures
25
+ # necessary for rendering its template.
26
+ #
27
+ # @abstract Subclass this and provide your own configuration and exposures to
28
+ # define your own view (along with a custom `#initialize` if you wish to
29
+ # inject dependencies into your subclass)
30
+ #
31
+ # @see https://dry-rb.org/gems/dry-view/
32
+ #
33
+ # @api public
34
+ class View
26
35
  # @api private
27
- class_attribute :configuration
28
- self.configuration = Configuration.new
36
+ DEFAULT_RENDERER_OPTIONS = {default_encoding: "utf-8"}.freeze
37
+
38
+ include Dry::Equalizer(:config, :exposures)
39
+
40
+ extend Dry::Core::Cache
41
+
42
+ extend Dry::Configurable
43
+
44
+ # @!group Configuration
29
45
 
30
- # Configure the framework.
31
- # It yields the given block in the context of the configuration
46
+ # @overload config.paths=(paths)
47
+ # Set an array of directories that will be searched for all templates
48
+ # (templates, partials, and layouts).
32
49
  #
33
- # @param blk [Proc] the configuration block
50
+ # These will be converted into Path objects and used for template lookup
51
+ # when rendering.
34
52
  #
35
- # @since 0.2.0
53
+ # This is a **required setting**.
36
54
  #
37
- # @see Hanami::View::Configuration
55
+ # @param paths [String, Path, Array<String, Path>] the paths
38
56
  #
39
- # @example
40
- # require 'hanami/view'
57
+ # @api public
58
+ # @!scope class
59
+ setting :paths, constructor: -> paths {
60
+ Array(paths).map { |path| Path[path] }
61
+ }
62
+
63
+ # @overload config.template=(name)
64
+ # Set the name of the template for rendering this view. Template name
65
+ # should be relative to the configured `paths`.
41
66
  #
42
- # Hanami::View.configure do
43
- # root '/path/to/root'
44
- # end
45
- def self.configure(&blk)
46
- configuration.instance_eval(&blk)
47
- end
67
+ # This is a **required setting**.
68
+ #
69
+ # @param name [String] template name
70
+ # @api public
71
+ # @!scope class
72
+ setting :template
48
73
 
49
- # Duplicate Hanami::View in order to create a new separated instance
50
- # of the framework.
74
+ # @overload config.template_inference_base=(base_path)
75
+ # Set the base path to strip away when when inferring a view's template
76
+ # names from its class name.
51
77
  #
52
- # The new instance of the framework will be completely decoupled from the
53
- # original. It will inherit the configuration, but all the changes that
54
- # happen after the duplication, won't be reflected on the other copies.
78
+ # **This setting only applies for views within an Hanami application.**
55
79
  #
56
- # @return [Module] a copy of Hanami::View
80
+ # For example, given a view `Main::Views::Articles::Index`, in the `Main`
81
+ # slice, and a template_inference_base of "views", the inferred template
82
+ # name will be "articles/index".
57
83
  #
58
- # @since 0.2.0
59
- # @api private
84
+ # @param base_path [String, nil] base templates path
85
+ # @api public
86
+ # @!scope class
87
+ setting :template_inference_base
88
+
89
+ # @overload config.layout=(name)
90
+ # Set the name of the layout to render templates within. Layouts will be
91
+ # looked up within the configured `layouts_dir`, within the configured
92
+ # `paths`.
60
93
  #
61
- # @example Basic usage
62
- # require 'hanami/view'
94
+ # A false or nil value will use no layout. Defaults to `nil`.
63
95
  #
64
- # module MyApp
65
- # View = Hanami::View.dupe
66
- # end
96
+ # @param name [String, FalseClass, nil] layout name, or false to indicate no layout
97
+ # @api public
98
+ # @!scope class
99
+ setting :layout, default: false
100
+
101
+ # @overload config.layouts_dir=(dir)
102
+ # Set the name of the directory (within the configured `paths`) holding
103
+ # the layouts. Defaults to `"layouts"`
104
+ #
105
+ # @param dir [String] directory name
106
+ # @api public
107
+ # @!scope class
108
+ setting :layouts_dir, default: "layouts"
109
+
110
+ # @overload config.scope=(scope_class)
111
+ # Set the scope class to use when rendering the view's template.
67
112
  #
68
- # MyApp::View == Hanami::View # => false
113
+ # Configuring a custom scope class allows you to provide extra behaviour
114
+ # (alongside exposures) to the template.
69
115
  #
70
- # MyApp::View.configuration ==
71
- # Hanami::View.configuration # => false
116
+ # @see https://dry-rb.org/gems/dry-view/scopes/
72
117
  #
73
- # @example Inheriting configuration
74
- # require 'hanami/view'
118
+ # @param scope_class [Class] scope class (inheriting from `Hanami::View::Scope`)
119
+ # @api public
120
+ # @!scope class
121
+ setting :scope
122
+
123
+ # @overload config.default_context=(context)
124
+ # Set the default context object to use when rendering. This will be used
125
+ # unless another context object is applied at render-time to `View#call`
75
126
  #
76
- # Hanami::View.configure do
77
- # root '/path/to/root'
78
- # end
127
+ # Defaults to a frozen instance of `Hanami::View::Context`.
79
128
  #
80
- # module MyApp
81
- # View = Hanami::View.dupe
82
- # end
129
+ # @see View#call
83
130
  #
84
- # module MyApi
85
- # View = Hanami::View.dupe
86
- # View.configure do
87
- # root '/another/root'
88
- # end
89
- # end
131
+ # @param context [Hanami::View::Context] context object
132
+ # @api public
133
+ # @!scope class
134
+ setting :default_context, default: Context.new.freeze
135
+
136
+ # @overload config.default_format=(format)
137
+ # Set the default format to use when rendering.
90
138
  #
91
- # Hanami::View.configuration.root # => #<Pathname:/path/to/root>
92
- # MyApp::View.configuration.root # => #<Pathname:/path/to/root>
93
- # MyApi::View.configuration.root # => #<Pathname:/another/root>
94
- def self.dupe
95
- dup.tap do |duplicated|
96
- duplicated.configuration = configuration.duplicate
97
- end
98
- end
139
+ # Defaults to `:html`.
140
+ #
141
+ # @param format [Symbol]
142
+ # @api public
143
+ # @!scope class
144
+ setting :default_format, default: :html
99
145
 
100
- # Duplicate the framework and generate modules for the target application
146
+ # @overload config.scope_namespace=(namespace)
147
+ # Set a namespace that will be searched when building scope classes.
148
+ #
149
+ # @param namespace [Module, Class]
101
150
  #
102
- # @param mod [Module] the Ruby namespace of the application
103
- # @param views [String] the optional namespace where the application's
104
- # views will live
105
- # @param blk [Proc] an optional block to configure the framework
151
+ # @see Scope
106
152
  #
107
- # @return [Module] a copy of Hanami::View
108
- #
109
- # @since 0.2.0
110
- #
111
- # @see Hanami::View#dupe
112
- # @see Hanami::View::Configuration
113
- # @see Hanami::View::Configuration#namespace
114
- #
115
- # @example Basic usage
116
- # require 'hanami/view'
117
- #
118
- # module MyApp
119
- # View = Hanami::View.duplicate(self)
120
- # end
121
- #
122
- # # It will:
123
- # #
124
- # # 1. Generate MyApp::View
125
- # # 2. Generate MyApp::Layout
126
- # # 3. Generate MyApp::Presenter
127
- # # 4. Generate MyApp::Views
128
- # # 5. Configure MyApp::Views as the default namespace for views
129
- #
130
- # module MyApp::Views::Dashboard
131
- # class Index
132
- # include MyApp::View
133
- # end
134
- # end
135
- #
136
- # @example Compare code
137
- # require 'hanami/view'
138
- #
139
- # module MyApp
140
- # View = Hanami::View.duplicate(self) do
141
- # # ...
142
- # end
143
- # end
144
- #
145
- # # it's equivalent to:
146
- #
147
- # module MyApp
148
- # View = Hanami::View.dupe
149
- # Layout = Hanami::Layout.dup
150
- #
151
- # module Views
152
- # end
153
- #
154
- # View.configure do
155
- # namespace 'MyApp::Views'
156
- # end
157
- #
158
- # View.configure do
159
- # # ...
160
- # end
161
- # end
162
- #
163
- # @example Custom views module
164
- # require 'hanami/view
165
- #
166
- # module MyApp
167
- # View = Hanami::View.duplicate(self, 'Vs')
168
- # end
169
- #
170
- # defined?(MyApp::Views) # => nil
171
- # defined?(MyApp::Vs) # => "constant"
172
- #
173
- # # Developers can namespace views under Vs
174
- # module MyApp::Vs::Dashboard
175
- # # ...
176
- # end
177
- #
178
- # @example Nil views module
179
- # require 'hanami/view'
180
- #
181
- # module MyApp
182
- # View = Hanami::View.duplicate(self, nil)
183
- # end
184
- #
185
- # defined?(MyApp::Views) # => nil
186
- #
187
- # # Developers can namespace views under MyApp
188
- # module MyApp
189
- # # ...
190
- # end
191
- #
192
- # @example Block usage
193
- # require 'hanami/view'
194
- #
195
- # module MyApp
196
- # View = Hanami::View.duplicate(self) do
197
- # root '/path/to/root'
198
- # end
199
- # end
200
- #
201
- # Hanami::View.configuration.root # => #<Pathname:.>
202
- # MyApp::View.configuration.root # => #<Pathname:/path/to/root>
203
- def self.duplicate(mod, views = 'Views', &blk)
204
- dupe.tap do |duplicated|
205
- mod.module_eval %{ module #{ views }; end } if views
206
- mod.module_eval %{
207
- Layout = Hanami::Layout.dup
208
- Presenter = Hanami::Presenter.dup
209
- }
153
+ # @api public
154
+ # @!scope class
155
+ setting :part_namespace
210
156
 
211
- duplicated.configure do
212
- namespace [mod, views].compact.join '::'
213
- end
157
+ # @overload config.part_builder=(part_builder)
158
+ # Set a custom part builder class
159
+ #
160
+ # @see https://dry-rb.org/gems/dry-view/parts/
161
+ #
162
+ # @param part_builder [Class]
163
+ # @api public
164
+ # @!scope class
165
+ setting :part_builder, default: PartBuilder
214
166
 
215
- duplicated.configure(&blk) if block_given?
216
- end
217
- end
167
+ # @overload config.scope_namespace=(namespace)
168
+ # Set a namespace that will be searched when building scope classes.
169
+ #
170
+ # @param namespace [Module, Class]
171
+ #
172
+ # @see Scope
173
+ #
174
+ # @api public
175
+ # @!scope class
176
+ setting :scope_namespace
218
177
 
219
- # Override Ruby's hook for modules.
220
- # It includes basic Hanami::View modules to the given Class.
221
- # It sets a copy of the framework configuration
178
+ # @overload config.scope_builder=(scope_builder)
179
+ # Set a custom scope builder class
222
180
  #
223
- # @param base [Class] the target view
181
+ # @see https://dry-rb.org/gems/dry-view/scopes/
224
182
  #
225
- # @since 0.1.0
226
- # @api private
183
+ # @param scope_builder [Class]
184
+ # @api public
185
+ # @!scope class
186
+ setting :scope_builder, default: ScopeBuilder
187
+
188
+ # @overload config.inflector=(inflector)
189
+ # Set an inflector to provide to the part_builder and scope_builder.
190
+ #
191
+ # Defaults to `Dry::Inflector.new`.
227
192
  #
228
- # @see http://www.ruby-doc.org/core-2.1.2/Module.html#method-i-included
193
+ # @param inflector
194
+ # @api public
195
+ # @!scope class
196
+ setting :inflector, default: Dry::Inflector.new
197
+
198
+ # @overload config.renderer_options=(options)
199
+ # A hash of options to pass to the template engine. Template engines are
200
+ # provided by Tilt; see Tilt's documentation for what options your
201
+ # template engine may support.
202
+ #
203
+ # Defaults to `{default_encoding: "utf-8"}`. Any options passed will be
204
+ # merged onto the defaults.
205
+ #
206
+ # @see https://github.com/rtomayko/tilt
207
+ #
208
+ # @param options [Hash] renderer options
209
+ # @api public
210
+ # @!scope class
211
+ setting :renderer_options, default: DEFAULT_RENDERER_OPTIONS, constructor: -> options {
212
+ DEFAULT_RENDERER_OPTIONS.merge(options.to_h).freeze
213
+ }
214
+
215
+ # @overload config.renderer_engine_mapping=(mapping)
216
+ # A hash specifying the (Tilt-compatible) template engine class to use
217
+ # for a given format. Template engine detection is automatic based on
218
+ # format; use this setting only if you want to force a non-preferred
219
+ # engine.
229
220
  #
230
- # @see Hanami::View::Dsl
231
- # @see Hanami::View::Inheritable
232
- # @see Hanami::View::Rendering
221
+ # @example
222
+ # config.renderer_engine_mapping = {erb: Tilt::ErubiTemplate}
233
223
  #
234
- # @example
235
- # require 'hanami/view'
224
+ # @see https://github.com/rtomayko/tilt
236
225
  #
237
- # class IndexView
238
- # include Hanami::View
239
- # end
240
- def self.included(base)
241
- conf = self.configuration
242
- conf.add_view(base)
226
+ # @param mapping [Hash<Symbol, Class>] engine mapping
227
+ # @api public
228
+ # @!scope class
229
+ setting :renderer_engine_mapping
243
230
 
244
- base.class_eval do
245
- extend Inheritable
246
- extend Dsl
247
- extend Rendering
248
- extend Escape
231
+ # @!endgroup
249
232
 
250
- include Utils::ClassAttribute
251
- class_attribute :configuration
233
+ include StandaloneView
252
234
 
253
- self.configuration = conf.duplicate
254
- end
235
+ def self.inherited(subclass)
236
+ super
255
237
 
256
- conf.copy!(base)
238
+ # If inheriting directly from Hanami::View within an Hanami app, configure
239
+ # the view for the application
240
+ if subclass.superclass == View && (provider = application_provider(subclass))
241
+ subclass.include ApplicationView.new(provider)
242
+ end
257
243
  end
258
244
 
259
- # Load the framework
260
- #
261
- # @since 0.1.0
262
- # @api private
263
- def self.load!
264
- configuration.load!
245
+ def self.application_provider(subclass)
246
+ if Hanami.respond_to?(:application?) && Hanami.application?
247
+ Hanami.application.component_provider(subclass)
248
+ end
265
249
  end
250
+ private_class_method :application_provider
266
251
  end
267
252
  end
data/lib/hanami-view.rb CHANGED
@@ -1 +1,3 @@
1
- require 'hanami/view'
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/view"