hanami-view 1.3.0.beta1 → 2.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/LICENSE +20 -0
  4. data/README.md +20 -862
  5. data/hanami-view.gemspec +26 -16
  6. data/lib/hanami-view.rb +3 -1
  7. data/lib/hanami/view.rb +208 -223
  8. data/lib/hanami/view/application_configuration.rb +77 -0
  9. data/lib/hanami/view/application_context.rb +35 -0
  10. data/lib/hanami/view/application_view.rb +89 -0
  11. data/lib/hanami/view/context.rb +97 -0
  12. data/lib/hanami/view/context_helpers/content_helpers.rb +26 -0
  13. data/lib/hanami/view/decorated_attributes.rb +82 -0
  14. data/lib/hanami/view/errors.rb +19 -56
  15. data/lib/hanami/view/exposure.rb +126 -0
  16. data/lib/hanami/view/exposures.rb +74 -0
  17. data/lib/hanami/view/part.rb +217 -0
  18. data/lib/hanami/view/part_builder.rb +140 -0
  19. data/lib/hanami/view/path.rb +68 -0
  20. data/lib/hanami/view/render_environment.rb +62 -0
  21. data/lib/hanami/view/render_environment_missing.rb +44 -0
  22. data/lib/hanami/view/rendered.rb +55 -0
  23. data/lib/hanami/view/renderer.rb +79 -0
  24. data/lib/hanami/view/scope.rb +189 -0
  25. data/lib/hanami/view/scope_builder.rb +98 -0
  26. data/lib/hanami/view/standalone_view.rb +396 -0
  27. data/lib/hanami/view/tilt.rb +78 -0
  28. data/lib/hanami/view/tilt/erb.rb +26 -0
  29. data/lib/hanami/view/tilt/erbse.rb +21 -0
  30. data/lib/hanami/view/tilt/haml.rb +26 -0
  31. data/lib/hanami/view/version.rb +5 -5
  32. metadata +113 -63
  33. data/LICENSE.md +0 -22
  34. data/lib/hanami/layout.rb +0 -172
  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.rb +0 -294
  41. data/lib/hanami/view/rendering/layout_finder.rb +0 -128
  42. data/lib/hanami/view/rendering/layout_registry.rb +0 -69
  43. data/lib/hanami/view/rendering/layout_scope.rb +0 -274
  44. data/lib/hanami/view/rendering/null_layout.rb +0 -52
  45. data/lib/hanami/view/rendering/null_local.rb +0 -82
  46. data/lib/hanami/view/rendering/null_template.rb +0 -83
  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/template.rb +0 -57
data/hanami-view.gemspec CHANGED
@@ -1,28 +1,38 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'hanami/view/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
8
  spec.name = 'hanami-view'
8
- spec.version = Hanami::View::VERSION
9
- spec.authors = ['Luca Guidi']
10
- spec.email = ['me@lucaguidi.com']
11
- spec.description = %q{View layer for Hanami}
12
- spec.summary = %q{View layer for Hanami, with a separation between views and templates}
13
- spec.homepage = 'http://hanamirb.org'
9
+ spec.authors = ["Tim Riley", "Piotr Solnica"]
10
+ spec.email = ["tim@icelab.com.au", "piotr.solnica@gmail.com"]
14
11
  spec.license = 'MIT'
12
+ spec.version = Hanami::View::VERSION.dup
15
13
 
16
- spec.files = `git ls-files -- lib/* CHANGELOG.md LICENSE.md README.md hanami-view.gemspec`.split($/)
14
+ spec.summary = "A complete, standalone view rendering system that gives you everything you need to write well-factored view code"
15
+ spec.description = spec.summary
16
+ spec.homepage = 'https://dry-rb.org/gems/hanami-view'
17
+ spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "hanami-view.gemspec", "lib/**/*"]
18
+ spec.bindir = 'bin'
17
19
  spec.executables = []
18
- spec.test_files = spec.files.grep(%r{^(test)/})
19
20
  spec.require_paths = ['lib']
20
- spec.required_ruby_version = '>= 2.3.0'
21
21
 
22
- spec.add_runtime_dependency 'tilt', '~> 2.0', '>= 2.0.1'
23
- spec.add_runtime_dependency 'hanami-utils', '~> 1.3.beta'
22
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
23
+ spec.metadata['changelog_uri'] = 'https://github.com/hanami/view/blob/master/CHANGELOG.md'
24
+ spec.metadata['source_code_uri'] = 'https://github.com/hanami/view'
25
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/hanami/view/issues'
26
+
27
+ spec.required_ruby_version = ">= 2.4.0"
28
+
29
+ spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
30
+ spec.add_runtime_dependency "dry-configurable", "~> 0.1"
31
+ spec.add_runtime_dependency "dry-core", "~> 0.5", ">= 0.5"
32
+ spec.add_runtime_dependency "dry-inflector", "~> 0.1"
33
+ spec.add_runtime_dependency "tilt", "~> 2.0", ">= 2.0.6"
24
34
 
25
- spec.add_development_dependency 'bundler', '~> 1.5'
26
- spec.add_development_dependency 'rspec', '~> 3.7'
27
- spec.add_development_dependency 'rake', '~> 12'
35
+ spec.add_development_dependency "bundler"
36
+ spec.add_development_dependency "rake"
37
+ spec.add_development_dependency "rspec"
28
38
  end
data/lib/hanami-view.rb CHANGED
@@ -1 +1,3 @@
1
- require 'hanami/view'
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/view"
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
29
37
 
30
- # Configure the framework.
31
- # It yields the given block in the context of the configuration
32
- #
33
- # @param blk [Proc] the configuration block
38
+ include Dry::Equalizer(:config, :exposures)
39
+
40
+ extend Dry::Core::Cache
41
+
42
+ extend Dry::Configurable
43
+
44
+ # @!group Configuration
45
+
46
+ # @overload config.paths=(paths)
47
+ # Set an array of directories that will be searched for all templates
48
+ # (templates, partials, and layouts).
34
49
  #
35
- # @since 0.2.0
50
+ # These will be converted into Path objects and used for template lookup
51
+ # when rendering.
36
52
  #
37
- # @see Hanami::View::Configuration
53
+ # This is a **required setting**.
38
54
  #
39
- # @example
40
- # require 'hanami/view'
55
+ # @param paths [String, Path, Array<String, Path>] the paths
41
56
  #
42
- # Hanami::View.configure do
43
- # root '/path/to/root'
44
- # end
45
- def self.configure(&blk)
46
- configuration.instance_eval(&blk)
57
+ # @api public
58
+ # @!scope class
59
+ setting :paths do |paths|
60
+ Array(paths).map { |path| Path[path] }
47
61
  end
48
62
 
49
- # Duplicate Hanami::View in order to create a new separated instance
50
- # of the framework.
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`.
51
66
  #
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.
67
+ # This is a **required setting**.
55
68
  #
56
- # @return [Module] a copy of Hanami::View
69
+ # @param name [String] template name
70
+ # @api public
71
+ # @!scope class
72
+ setting :template
73
+
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.
57
77
  #
58
- # @since 0.2.0
59
- # @api private
78
+ # **This setting only applies for views within an Hanami application.**
60
79
  #
61
- # @example Basic usage
62
- # require '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".
63
83
  #
64
- # module MyApp
65
- # View = Hanami::View.dupe
66
- # end
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`.
67
93
  #
68
- # MyApp::View == Hanami::View # => false
94
+ # A false or nil value will use no layout. Defaults to `nil`.
69
95
  #
70
- # MyApp::View.configuration ==
71
- # Hanami::View.configuration # => false
96
+ # @param name [String, FalseClass, nil] layout name, or false to indicate no layout
97
+ # @api public
98
+ # @!scope class
99
+ setting :layout, 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, "layouts"
109
+
110
+ # @overload config.scope=(scope_class)
111
+ # Set the scope class to use when rendering the view's template.
72
112
  #
73
- # @example Inheriting configuration
74
- # require 'hanami/view'
113
+ # Configuring a custom scope class allows you to provide extra behaviour
114
+ # (alongside exposures) to the template.
75
115
  #
76
- # Hanami::View.configure do
77
- # root '/path/to/root'
78
- # end
116
+ # @see https://dry-rb.org/gems/dry-view/scopes/
79
117
  #
80
- # module MyApp
81
- # View = Hanami::View.dupe
82
- # end
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`
83
126
  #
84
- # module MyApi
85
- # View = Hanami::View.dupe
86
- # View.configure do
87
- # root '/another/root'
88
- # end
89
- # end
127
+ # Defaults to a frozen instance of `Hanami::View::Context`.
90
128
  #
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
129
+ # @see View#call
130
+ #
131
+ # @param context [Hanami::View::Context] context object
132
+ # @api public
133
+ # @!scope class
134
+ setting :default_context, Context.new.freeze
99
135
 
100
- # Duplicate the framework and generate modules for the target application
136
+ # @overload config.default_format=(format)
137
+ # Set the default format to use when rendering.
101
138
  #
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
139
+ # Defaults to `:html`.
106
140
  #
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
- }
141
+ # @param format [Symbol]
142
+ # @api public
143
+ # @!scope class
144
+ setting :default_format, :html
210
145
 
211
- duplicated.configure do
212
- namespace [mod, views].compact.join '::'
213
- end
146
+ # @overload config.scope_namespace=(namespace)
147
+ # Set a namespace that will be searched when building scope classes.
148
+ #
149
+ # @param namespace [Module, Class]
150
+ #
151
+ # @see Scope
152
+ #
153
+ # @api public
154
+ # @!scope class
155
+ setting :part_namespace
214
156
 
215
- duplicated.configure(&blk) if block_given?
216
- end
217
- 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, PartBuilder
218
166
 
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
167
+ # @overload config.scope_namespace=(namespace)
168
+ # Set a namespace that will be searched when building scope classes.
222
169
  #
223
- # @param base [Class] the target view
170
+ # @param namespace [Module, Class]
224
171
  #
225
- # @since 0.1.0
226
- # @api private
172
+ # @see Scope
173
+ #
174
+ # @api public
175
+ # @!scope class
176
+ setting :scope_namespace
177
+
178
+ # @overload config.scope_builder=(scope_builder)
179
+ # Set a custom scope builder class
180
+ #
181
+ # @see https://dry-rb.org/gems/dry-view/scopes/
182
+ #
183
+ # @param scope_builder [Class]
184
+ # @api public
185
+ # @!scope class
186
+ setting :scope_builder, 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, 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.
229
202
  #
230
- # @see Hanami::View::Dsl
231
- # @see Hanami::View::Inheritable
232
- # @see Hanami::View::Rendering
203
+ # Defaults to `{default_encoding: "utf-8"}`. Any options passed will be
204
+ # merged onto the defaults.
233
205
  #
234
- # @example
235
- # require 'hanami/view'
206
+ # @see https://github.com/rtomayko/tilt
236
207
  #
237
- # class IndexView
238
- # include Hanami::View
239
- # end
240
- def self.included(base)
241
- conf = self.configuration
242
- conf.add_view(base)
208
+ # @param options [Hash] renderer options
209
+ # @api public
210
+ # @!scope class
211
+ setting :renderer_options, DEFAULT_RENDERER_OPTIONS do |options|
212
+ DEFAULT_RENDERER_OPTIONS.merge(options.to_h).freeze
213
+ end
243
214
 
244
- base.class_eval do
245
- extend Inheritable
246
- extend Dsl
247
- extend Rendering
248
- extend Escape
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.
220
+ #
221
+ # @example
222
+ # config.renderer_engine_mapping = {erb: Tilt::ErubiTemplate}
223
+ #
224
+ # @see https://github.com/rtomayko/tilt
225
+ #
226
+ # @param mapping [Hash<Symbol, Class>] engine mapping
227
+ # @api public
228
+ # @!scope class
229
+ setting :renderer_engine_mapping
249
230
 
250
- include Utils::ClassAttribute
251
- class_attribute :configuration
231
+ # @!endgroup
252
232
 
253
- self.configuration = conf.duplicate
254
- end
233
+ include StandaloneView
255
234
 
256
- conf.copy!(base)
235
+ def self.inherited(subclass)
236
+ super
237
+
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