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
@@ -1,50 +0,0 @@
1
- require 'hanami/utils/string'
2
-
3
- module Hanami
4
- module View
5
- module Rendering
6
- # @since 0.2.0
7
- # @api private
8
- class TemplateName
9
- # @since 0.2.0
10
- # @api private
11
- NAMESPACE_SEPARATOR = '::'.freeze
12
-
13
- # @since 0.2.0
14
- # @api private
15
- def initialize(name, namespace)
16
- @name = name
17
- compile!(namespace)
18
- end
19
-
20
- # @since 0.2.0
21
- # @api private
22
- def to_s
23
- @name
24
- end
25
-
26
- private
27
- # @since 0.2.0
28
- # @api private
29
- def compile!(namespace)
30
- tokens(namespace) {|token| replace!(token) }
31
- @name = Utils::String.underscore(@name)
32
- end
33
-
34
- # @since 0.2.0
35
- # @api private
36
- def tokens(namespace)
37
- namespace.to_s.split(NAMESPACE_SEPARATOR).each do |token|
38
- yield token
39
- end
40
- end
41
-
42
- # @since 0.2.0
43
- # @api private
44
- def replace!(token)
45
- @name.gsub!(%r{\A#{ token }#{ NAMESPACE_SEPARATOR }}, '')
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,144 +0,0 @@
1
- require 'hanami/view/template'
2
-
3
- module Hanami
4
- module View
5
- module Rendering
6
- # Find templates for a view
7
- #
8
- # @api private
9
- # @since 0.1.0
10
- #
11
- # @see View::Template
12
- class TemplatesFinder
13
- # Default format
14
- #
15
- # @api private
16
- # @since 0.1.0
17
- FORMAT = '*'.freeze
18
-
19
- # Default template engines
20
- #
21
- # @api private
22
- # @since 0.1.0
23
- ENGINES = '*'.freeze
24
-
25
- # Recursive pattern
26
- #
27
- # @api private
28
- # @since 0.2.0
29
- RECURSIVE = '**'.freeze
30
-
31
- # Initialize a finder
32
- #
33
- # @param view [Class] the view
34
- #
35
- # @api private
36
- # @since 0.1.0
37
- def initialize(view)
38
- @view = view
39
- end
40
-
41
- # Find all the associated templates to the view.
42
- # It recursively looks for templates under the root path of the view,
43
- # that are matching the template name
44
- #
45
- # @return [Array<Hanami::View::Template>] the templates
46
- #
47
- # @api private
48
- # @since 0.1.0
49
- #
50
- # @see Hanami::View::Dsl#root
51
- # @see Hanami::View::Dsl#template
52
- #
53
- # @example
54
- # require 'hanami/view'
55
- #
56
- # module Articles
57
- # class Show
58
- # include Hanami::View
59
- # end
60
- # end
61
- #
62
- # Articles::Show.root # => "/path/to/templates"
63
- # Articles::Show.template # => "articles/show"
64
- #
65
- # # This view has a template:
66
- # #
67
- # # "/path/to/templates/articles/show.html.erb"
68
- #
69
- # Hanami::View::Rendering::TemplatesFinder.new(Articles::Show).find
70
- # # => [#<Hanami::View::Template:0x007f8a0a86a970 ... @file="/path/to/templates/articles/show.html.erb">]
71
- def find
72
- _find.map do |template|
73
- View::Template.new(template, @view.configuration.default_encoding)
74
- end
75
- end
76
-
77
- protected
78
-
79
- # @api private
80
- # @since 0.4.3
81
- #
82
- # Searches first for files at `../templates/articles/index.*.*`
83
- # If none are found, falls back to recursive search in `../templates/**/articles/index.*.*`
84
- #
85
- def _find(lookup = search_path)
86
- base_path = templates_path(root, template_name)
87
- if base_path.none?
88
- templates_path(root, lookup, template_name)
89
- else
90
- base_path
91
- end
92
- end
93
-
94
- # @api private
95
- # @since 0.7.0
96
- def templates_path(*parts)
97
- Dir.glob("#{ parts.join(separator) }.#{ format }.#{ engines }")
98
- end
99
-
100
- # @api private
101
- # @since 0.1.0
102
- def template_name
103
- @view.template
104
- end
105
-
106
- # @api private
107
- # @since 0.1.0
108
- def root
109
- @view.root
110
- end
111
-
112
- # @api private
113
- # @since 0.4.3
114
- def search_path
115
- recursive
116
- end
117
-
118
- # @api private
119
- # @since 0.2.0
120
- def recursive
121
- RECURSIVE
122
- end
123
-
124
- # @api private
125
- # @since 0.1.0
126
- def separator
127
- ::File::SEPARATOR
128
- end
129
-
130
- # @api private
131
- # @since 0.1.0
132
- def format
133
- FORMAT
134
- end
135
-
136
- # @api private
137
- # @since 0.1.0
138
- def engines
139
- ENGINES
140
- end
141
- end
142
- end
143
- end
144
- end
@@ -1,37 +0,0 @@
1
- module Hanami
2
- module View
3
- module Rendering
4
- # Find a view
5
- #
6
- # @api private
7
- # @since 0.1.0
8
- #
9
- # @see Hanami::View::Rendering::Registry
10
- class ViewFinder
11
- # Initialize a finder
12
- #
13
- # @param view [Class] the superclass view
14
- #
15
- # @api private
16
- # @since 0.1.0
17
- def initialize(view)
18
- @view = view
19
- end
20
-
21
- # Find a view for the given template.
22
- # It looks up for the current view and its subclasses.
23
- #
24
- # @param template [Hanami::View::Template] a template to be associated
25
- # to a view
26
- #
27
- # @return [Class] a view associated with the given template
28
- #
29
- # @api private
30
- # @since 0.1.0
31
- def find(template)
32
- @view.subclasses.find {|v| v.format == template.format } || @view
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,294 +0,0 @@
1
- require 'hanami/view/rendering/registry'
2
- require 'hanami/view/rendering/scope'
3
- require 'hanami/view/rendering/subscope'
4
- require 'hanami/view/rendering/null_local'
5
-
6
- module Hanami
7
- module View
8
- # Rendering methods
9
- #
10
- # @since 0.1.0
11
- #
12
- # @see Hanami::View::Rendering::InstanceMethods
13
- module Rendering
14
- # @since 0.1.0
15
- # @api private
16
- def self.extended(base)
17
- base.class_eval do
18
- include InstanceMethods
19
- end
20
- end
21
-
22
- module InstanceMethods
23
- # Initialize a view
24
- #
25
- # @param template [Hanami::View::Template] the template to render
26
- # @param locals [Hash] a set of objects available during the rendering
27
- # process.
28
- #
29
- # @since 0.1.0
30
- #
31
- # @see Hanami::View::Template
32
- #
33
- # @example
34
- # require 'hanami/view'
35
- #
36
- # class IndexView
37
- # include Hanami::View
38
- # end
39
- #
40
- # template = Hanami::View::Template.new('index.html.erb')
41
- # view = IndexView.new(template, {article: article})
42
- def initialize(template, **locals)
43
- @template = template
44
- @locals = locals
45
- @scope = Scope.new(self, @locals)
46
- end
47
-
48
- # Render the template by bounding the local scope.
49
- # If it uses a layout, it renders the template first and then the
50
- # control passes to the layout.
51
- #
52
- # Override this method for custom rendering policies.
53
- # For instance, when a serializer is used and there isn't the need of
54
- # a template.
55
- #
56
- # @return [String] the output of the rendering process
57
- #
58
- # @raise [Hanami::View::MissingTemplateError] if the template is nil
59
- #
60
- # @since 0.1.0
61
- #
62
- # @see Hanami::View::Layout
63
- #
64
- # @example with template
65
- # require 'hanami/view'
66
- #
67
- # class IndexView
68
- # include Hanami::View
69
- # end
70
- #
71
- # template = Hanami::View::Template.new('index.html.erb')
72
- # view = IndexView.new(template, {article: article})
73
- #
74
- # view.render # => <h1>Introducing Hanami::view</h1> ...
75
- #
76
- # @example with template and layout
77
- # require 'hanami/view'
78
- #
79
- # class ApplicationLayout
80
- # include Hanami::View::Layout
81
- # end
82
- #
83
- # class IndexView
84
- # include Hanami::View
85
- # layout :application
86
- # end
87
- #
88
- # template = Hanami::View::Template.new('index.html.erb')
89
- # view = IndexView.new(template, {article: article})
90
- #
91
- # view.render # => <html> ... <h1>Introducing Hanami::view</h1> ...
92
- #
93
- # @example with custom rendering
94
- # require 'hanami/view'
95
- #
96
- # class IndexView
97
- # include Hanami::View
98
- #
99
- # def render
100
- # ArticleSerializer.new(article).render
101
- # end
102
- # end
103
- #
104
- # view = IndexView.new(nil, {article: article})
105
- #
106
- # view.render # => {title: ...}
107
- def render
108
- layout.render
109
- end
110
-
111
- # It tries to invoke a method for the view or a local for the given key.
112
- # If the lookup fails, it returns a null object.
113
- #
114
- # @return [Object,Hanami::View::Rendering::NullLocal] the returning value
115
- #
116
- # @since 0.7.0
117
- #
118
- # @example
119
- # <% if local(:plan).overdue? %>
120
- # <h2>Your plan is overdue.</h2>
121
- # <% end %>
122
- def local(key)
123
- if respond_to?(key)
124
- __send__(key)
125
- else
126
- locals.fetch(key) { NullLocal.new(key) }
127
- end
128
- end
129
-
130
- protected
131
- # The output of the template rendering process.
132
- #
133
- # @return [String] the rendering output
134
- #
135
- # @raise [Hanami::View::MissingTemplateError] if the template is nil
136
- #
137
- # @api private
138
- # @since 0.1.0
139
- def rendered
140
- template.render @scope
141
- end
142
-
143
- # The layout.
144
- #
145
- # @return [Class, Hanami::View::Rendering::NullLayout]
146
- #
147
- # @see Hanami::View::Layout
148
- # @see Hanami::View.layout
149
- # @see Hanami::View::Dsl#layout
150
- #
151
- # @api private
152
- # @since 0.1.0
153
- def layout
154
- @layout ||= self.class.layout.new(@scope, rendered)
155
- end
156
-
157
- # The template.
158
- #
159
- # @return [Hanami::View::Template] the template
160
- #
161
- # @raise [Hanami::View::MissingTemplateError] if the template is nil
162
- #
163
- # @api private
164
- # @since 0.1.0
165
- def template
166
- @template or raise MissingTemplateError.new(self.class.template, @scope.format)
167
- end
168
-
169
- # A set of objects available during the rendering process.
170
- #
171
- # @return [Hash]
172
- #
173
- # @see Hanami::View#initialize
174
- #
175
- # @api private
176
- # @since 0.1.0
177
- def locals
178
- @locals
179
- end
180
-
181
- # Delegates missing methods to the scope.
182
- #
183
- # @see Hanami::View::Rendering::Scope
184
- #
185
- # @api private
186
- # @since 0.1.0
187
- #
188
- # @example
189
- # require 'hanami/view'
190
- #
191
- # class IndexView
192
- # include Hanami::View
193
- # end
194
- #
195
- # template = Hanami::View::Template.new('index.html.erb')
196
- # view = IndexView.new(template, {article: article})
197
- #
198
- # view.article # => #<Article:0x007fb0bbd3b6e8>
199
- def method_missing(m, *)
200
- @scope.__send__ m
201
- end
202
- end
203
-
204
- # Render the given context and locals with the appropriate template.
205
- # If there are registered subclasses, it choose the right class, according
206
- # to the requested format.
207
- #
208
- # @param context [Hash] the context for the rendering process
209
- # @option context [Symbol] :format the requested format
210
- #
211
- # @return [String] the output of the rendering process
212
- #
213
- # @raise [Hanami::View::MissingTemplateError] if it can't find a template
214
- # for the given context
215
- #
216
- # @raise [Hanami::View::MissingFormatError] if the given context doesn't
217
- # have the :format key
218
- #
219
- # @since 0.1.0
220
- #
221
- # @see Hanami::View#initialize
222
- # @see Hanami::View#render
223
- #
224
- # @example
225
- # require 'hanami/view'
226
- #
227
- # article = OpenStruct.new(title: 'Hello')
228
- #
229
- # module Articles
230
- # class Show
231
- # include Hanami::View
232
- #
233
- # def title
234
- # @title ||= article.title.upcase
235
- # end
236
- # end
237
- #
238
- # class JsonShow < Show
239
- # format :json
240
- #
241
- # def title
242
- # super.downcase
243
- # end
244
- # end
245
- # end
246
- #
247
- # Hanami::View.root = '/path/to/templates'
248
- # Hanami::View.load!
249
- #
250
- # Articles::Show.render(format: :html, article: article)
251
- # # => renders `articles/show.html.erb`
252
- #
253
- # Articles::Show.render(format: :json, article: article)
254
- # # => renders `articles/show.json.erb`
255
- #
256
- # Articles::Show.render(format: :xml, article: article)
257
- # # => raises Hanami::View::MissingTemplateError
258
- def render(context)
259
- registry.resolve(context).render
260
- end
261
-
262
- protected
263
-
264
- # Loading mechanism hook.
265
- #
266
- # @api private
267
- # @since 0.1.0
268
- #
269
- # @see Hanami::View.load!
270
- def load!
271
- super
272
- load_registry!
273
- end
274
-
275
- private
276
-
277
- # The registry that holds all the registered subclasses.
278
- #
279
- # @api private
280
- # @since 0.1.0
281
- #
282
- # @see Hanami::View::Rendering::Registry
283
- def registry
284
- @registry ||= Registry.new(self)
285
- end
286
-
287
- # @api private
288
- def load_registry!
289
- @registry = nil
290
- registry.freeze
291
- end
292
- end
293
- end
294
- end
@@ -1,57 +0,0 @@
1
- require 'tilt'
2
-
3
- module Hanami
4
- module View
5
- # A logic-less template.
6
- #
7
- # @since 0.1.0
8
- class Template
9
- def initialize(template, encoding = Encoding::UTF_8)
10
- options = { default_encoding: encoding }
11
-
12
- if slim?(template)
13
- # NOTE disable_escape: true is for Slim compatibility
14
- # See https://github.com/hanami/assets/issues/36
15
- options[:disable_escape] = true
16
- end
17
-
18
- @_template = Tilt.new(template, nil, options)
19
- end
20
-
21
- # Returns the format that the template handles.
22
- #
23
- # @return [Symbol] the format name
24
- #
25
- # @since 0.1.0
26
- #
27
- # @example
28
- # require 'hanami/view'
29
- #
30
- # template = Hanami::View::Template.new('index.html.erb')
31
- # template.format # => :html
32
- def format
33
- @_template.basename.match(/\.([^.]+)/).captures.first.to_sym
34
- end
35
-
36
- # Render the template within the context of the given scope.
37
- #
38
- # @param scope [Hanami::View::Scope] the rendering scope
39
- #
40
- # @return [String] the output of the rendering process
41
- #
42
- # @api private
43
- # @since 0.1.0
44
- #
45
- # @see Hanami::View::Scope
46
- def render(scope, &blk)
47
- @_template.render(scope, {}, &blk)
48
- end
49
-
50
- private
51
-
52
- def slim?(template)
53
- File.extname(template) == ".slim".freeze
54
- end
55
- end
56
- end
57
- end