hanami-view 1.3.1 → 2.0.0.alpha3

Sign up to get free protection for your applications and to get access to all the features.
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"