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,128 +0,0 @@
1
- require 'hanami/utils/string'
2
- require 'hanami/utils/class'
3
- require 'hanami/view/rendering/null_layout'
4
-
5
- module Hanami
6
- module View
7
- module Rendering
8
- # Defines the logic to find a layout
9
- #
10
- # @api private
11
- # @since 0.1.0
12
- #
13
- # @see Hanami::Layout
14
- class LayoutFinder
15
- # Layout class name suffix
16
- #
17
- # @api private
18
- # @since 0.1.0
19
- SUFFIX = 'Layout'.freeze
20
-
21
- # Find a layout from the given name.
22
- #
23
- # @param layout [Symbol,String,NilClass] layout name or nil if you want
24
- # to fallback to the framework defaults (see `Hanami::View.layout`).
25
- #
26
- # @param namespace [Class,Module] a Ruby namespace where to lookup
27
- #
28
- # @return [Hanami::Layout] the layout for the given name or
29
- # `Hanami::View.layout`
30
- #
31
- # @api private
32
- # @since 0.1.0
33
- #
34
- # @example With given name
35
- # require 'hanami/view'
36
- #
37
- # Hanami::View::Rendering::LayoutFinder.find(:article) # =>
38
- # ArticleLayout
39
- #
40
- # @example With a class
41
- # require 'hanami/view'
42
- #
43
- # Hanami::View::Rendering::LayoutFinder.find(ArticleLayout) # =>
44
- # ArticleLayout
45
- #
46
- # @example With namespace
47
- # require 'hanami/view'
48
- #
49
- # Hanami::View::Rendering::LayoutFinder.find(:application, CardDeck) # =>
50
- # CardDeck::ApplicationLayout
51
- #
52
- # @example With nil
53
- # require 'hanami/view'
54
- #
55
- # Hanami::View::Rendering::LayoutFinder.find(nil) # =>
56
- # Hanami::View::Rendering::NullLayout
57
- #
58
- # @example With unknown layout
59
- # require 'hanami/view'
60
- #
61
- # Hanami::View::Rendering::LayoutFinder.find(:unknown) # =>
62
- # Hanami::View::Rendering::NullLayout
63
- #
64
- def self.find(layout, namespace = Object)
65
- case layout
66
- when Symbol, String
67
- # TODO Move this low level logic into a Hanami::Utils solution
68
- class_name = "#{ Utils::String.classify(layout) }#{ SUFFIX }"
69
- namespace = Utils::Class.load!(namespace)
70
- namespace.const_get(class_name)
71
- when Class
72
- layout
73
- end || NullLayout
74
- end
75
-
76
- # Initialize the finder
77
- #
78
- # @param view [Class, #layout]
79
- #
80
- # @api private
81
- # @since 0.1.0
82
- def initialize(view)
83
- @view = view
84
- end
85
-
86
- # Find the layout for the view
87
- #
88
- # @return [Hanami::Layout] the layout associated to the view
89
- #
90
- # @see Hanami::View::Rendering::LayoutFinder.find
91
- # @see Hanami::View::Rendering::LayoutFinder#initialize
92
- #
93
- # @api private
94
- # @since 0.1.0
95
- #
96
- # @example With layout
97
- # require 'hanami/view'
98
- #
99
- # module Articles
100
- # class Show
101
- # include Hanami::View
102
- # layout :article
103
- # end
104
- # end
105
- #
106
- # Hanami::View::Rendering::LayoutFinder.new(Articles::Show) # =>
107
- # ArticleLayout
108
- #
109
- # @example Without layout
110
- # require 'hanami/view'
111
- #
112
- # module Dashboard
113
- # class Index
114
- # include Hanami::View
115
- # end
116
- # end
117
- #
118
- # Hanami::View.layout # => :application
119
- #
120
- # Hanami::View::Rendering::LayoutFinder.new(Dashboard::Index) # =>
121
- # ApplicationLayout
122
- def find
123
- self.class.find(@view.layout)
124
- end
125
- end
126
- end
127
- end
128
- end
@@ -1,69 +0,0 @@
1
- require 'hanami/view/rendering/null_template'
2
- require 'hanami/view/rendering/templates_finder'
3
-
4
- module Hanami
5
- module View
6
- module Rendering
7
- # Holds the references of all the registered layouts.
8
- # As now the registry is unique at the level of the framework.
9
- #
10
- # @api private
11
- # @since 0.1.0
12
- #
13
- # @see Hanami::Layout::ClassMethods#registry
14
- class LayoutRegistry
15
- # Initialize the registry
16
- #
17
- # @param view [Class] the view
18
- #
19
- # @api private
20
- # @since 0.1.0
21
- def initialize(view)
22
- @registry = {}
23
- @view = view
24
- prepare!
25
- end
26
-
27
- # Returns the layout for the given context.
28
- #
29
- # @param context [Hash] the rendering context
30
- # @option context [Symbol] :format the requested format
31
- #
32
- # @return [Hanami::Layout, Hanami::View::Rendering::NullTemplate]
33
- # the layout associated with the given context or a `NullTemplate` if
34
- # it can't be found.
35
- #
36
- # @raise [Hanami::View::MissingFormatError] if the given context doesn't
37
- # have the :format key
38
- #
39
- # @api private
40
- # @since 0.1.0
41
- def resolve(context)
42
- @registry.fetch(format(context)) { NullTemplate.new }
43
- end
44
-
45
- protected
46
- # @api private
47
- # @since 0.1.0
48
- def prepare!
49
- templates.each do |template|
50
- @registry.merge! template.format => template
51
- end
52
- @registry.any? or raise MissingTemplateLayoutError.new(@view)
53
- end
54
-
55
- # @api private
56
- # @since 0.1.0
57
- def templates
58
- TemplatesFinder.new(@view).find
59
- end
60
-
61
- # @api private
62
- # @since 0.1.0
63
- def format(context)
64
- context.fetch(:format) { raise MissingFormatError }
65
- end
66
- end
67
- end
68
- end
69
- end
@@ -1,274 +0,0 @@
1
- require 'hanami/view/rendering/null_local'
2
- require 'hanami/view/rendering/options'
3
- require 'hanami/utils/escape'
4
-
5
- module Hanami
6
- module View
7
- module Rendering
8
- # List of render types that exactly one of must be included when calling `#render`.
9
- # For example, when calling `<%= render something: 'my_thing', locals: {} %>`,
10
- # 'something' must be one of the values listed here.
11
- #
12
- # @since 1.1.0
13
- # @api private
14
- KNOWN_RENDER_TYPES = [:partial, :template].freeze
15
-
16
- # Scope for layout rendering
17
- #
18
- # @since 0.1.0
19
- class LayoutScope < BasicObject
20
- # Initialize the scope
21
- #
22
- # @param layout [Hanami::Layout] the layout to render
23
- # @param scope [Hanami::View::Rendering::Scope] the scope of the current
24
- # view
25
- #
26
- # @api private
27
- # @since 0.1.0
28
- def initialize(layout, scope)
29
- @layout = layout
30
- @scope = scope
31
- @view = nil
32
- @locals = nil
33
- end
34
-
35
- # Returns the classname as string
36
- #
37
- # @return classname
38
- #
39
- # @since 0.3.0
40
- def class
41
- (class << self; self end).superclass
42
- end
43
-
44
- # Returns an inspect String
45
- #
46
- # @return [String] inspect String (contains classname, objectid in hex, available ivars)
47
- #
48
- # @since 0.3.0
49
- def inspect
50
- base = "#<#{ self.class }:#{'%x' % (self.object_id << 1)}"
51
- base << " @layout=\"#{@layout.inspect}\"" if @layout
52
- base << " @scope=\"#{@scope.inspect}\"" if @scope
53
- base << ">"
54
- end
55
-
56
- # Render a partial or a template within a layout template.
57
- #
58
- # @param options [Hash]
59
- # @option options [String] :partial the partial template to render
60
- # @option options [String] :template the template to render
61
- #
62
- # @return [String] the output of the rendering process
63
- #
64
- # @raise [Hanami::Error::UnknownRenderTypeError] if the given type to
65
- # be rendered is unknown
66
- #
67
- # @since 0.1.0
68
- #
69
- # @example Rendering partial
70
- # # Given a partial under:
71
- # # templates/shared/_sidebar.html.erb
72
- # #
73
- # # In the layout template:
74
- # # templates/application.html.erb
75
- # #
76
- # # Use like this:
77
- # <%= render partial: 'shared/sidebar' %>
78
- #
79
- # @example Rendering template
80
- # # Given a template under:
81
- # # templates/articles/index.html.erb
82
- # #
83
- # # In the layout template:
84
- # # templates/application.html.erb
85
- # #
86
- # # Use like this:
87
- # <%= render template: 'articles/index' %>
88
- #
89
- # @example Rendering partial, using optional :locals
90
- # # Given a partial under:
91
- # # templates/shared/_sidebar.html.erb
92
- # #
93
- # # In the layout template:
94
- # # templates/application.html.erb
95
- # #
96
- # # Use like this:
97
- # <%= render partial: 'shared/sidebar', { user: current_user } %>
98
- #
99
- # #
100
- # # `user` will be available in the scope of the sidebar rendering
101
- def render(options)
102
- renderer(options).render
103
- end
104
-
105
- # Returns the requested format.
106
- #
107
- # @return [Symbol] the requested format (eg. :html, :json, :xml, etc..)
108
- #
109
- # @since 0.1.0
110
- def format
111
- @scope.format
112
- end
113
-
114
- # The current view.
115
- #
116
- # @return [Hanami::View] the current view
117
- #
118
- # @since 0.1.0
119
- def view
120
- @view || @scope.view
121
- end
122
-
123
- # The current locals.
124
- #
125
- # @return [Hash] the current locals
126
- #
127
- # @since 0.1.0
128
- def locals
129
- (@locals || @scope.locals).dup
130
- end
131
-
132
- # It tries to invoke a method for the view or a local for the given key.
133
- # If the lookup fails, it returns a null object.
134
- #
135
- # @return [Object,Hanami::View::Rendering::NullLocal] the returning value
136
- #
137
- # @since 0.7.0
138
- #
139
- # @example Safe method navigation
140
- # <% if local(:plan).overdue? %>
141
- # <h2>Your plan is overdue.</h2>
142
- # <% end %>
143
- #
144
- # @example Optional Contents
145
- # # Given the following layout template
146
- #
147
- # <!doctype HTML>
148
- # <html>
149
- # <!-- ... -->
150
- # <body>
151
- # <!-- ... -->
152
- # <%= local :footer %>
153
- # </body>
154
- # </html>
155
- #
156
- # # Case 1:
157
- # # Products::Index doesn't respond to #footer, local will return nil
158
- # #
159
- # # Case 2:
160
- # # Products::Show responds to #footer, local will send back
161
- # # #footer returning value
162
- #
163
- # module Products
164
- # class Index
165
- # include Hanami::View
166
- # end
167
- #
168
- # class Show
169
- # include Hanami::View
170
- #
171
- # def footer
172
- # "contents for footer"
173
- # end
174
- # end
175
- # end
176
- def local(key)
177
- if respond_to?(key)
178
- __send__(key)
179
- else
180
- locals.fetch(key) { NullLocal.new(key) }
181
- end
182
- end
183
-
184
- # Implements "respond to" logic
185
- #
186
- # @return [TrueClass,FalseClass]
187
- #
188
- # @since 0.3.0
189
- # @api private
190
- #
191
- # @see http://ruby-doc.org/core/Object.html#method-i-respond_to-3F
192
- def respond_to?(m, include_all = false)
193
- respond_to_missing?(m, include_all)
194
- end
195
-
196
- # Implements "respond to" logic
197
- #
198
- # @return [TrueClass,FalseClass]
199
- #
200
- # @since 0.3.0
201
- # @api private
202
- #
203
- # @see http://ruby-doc.org/core/Object.html#method-i-respond_to_missing-3F
204
- def respond_to_missing?(m, include_all)
205
- @layout.respond_to?(m, include_all) ||
206
- @scope.respond_to?(m, include_all)
207
- end
208
-
209
- protected
210
-
211
- # Forward all the missing methods to the view scope or to the layout.
212
- #
213
- # @api private
214
- # @since 0.1.0
215
- #
216
- # @see Hanami::View::Rendering::Scope
217
- # @see Hanami::Layout
218
- #
219
- # @example
220
- # # In the layout template:
221
- # # templates/application.html.erb
222
- # #
223
- # # Use like this:
224
- # <title><%= article.title %></title>
225
- #
226
- # # `article` will be looked up in the view scope first.
227
- # # If not found, it will be searched within the layout.
228
- def method_missing(m, *args, &blk)
229
- # FIXME: this isn't compatible with Hanami 2.0, as it extends a view
230
- # that we want to be frozen in the future
231
- #
232
- # See https://github.com/hanami/view/issues/130#issuecomment-319326236
233
- if @scope.respond_to?(m, true) && @scope.locals.has_key?(m) && layout.respond_to?(m, true)
234
- layout.__send__(m, *args, &blk)
235
- elsif @scope.respond_to?(m, true)
236
- @scope.__send__(m, *args, &blk)
237
- elsif layout.respond_to?(m, true)
238
- layout.__send__(m, *args, &blk)
239
- else
240
- ::Hanami::View::Escape.html(super)
241
- end
242
- end
243
-
244
- # @api private
245
- def renderer(options)
246
- if options[:partial]
247
- Rendering::Partial
248
- elsif options[:template]
249
- Rendering::Template
250
- else
251
- ::Kernel.raise UnknownRenderTypeError.new(KNOWN_RENDER_TYPES, options)
252
- end.new(view, _options(options))
253
- end
254
-
255
- private
256
-
257
- # @api private
258
- def _options(options)
259
- current_locals = locals.reject do |key, _|
260
- @scope.respond_to?(key, true) &&
261
- (layout.respond_to?(key, true) || @scope.view.respond_to?(:name, true))
262
- end
263
- Options.build(options, current_locals, format)
264
- end
265
-
266
- # @since 0.4.2
267
- # @api private
268
- def layout
269
- @layout || @layout.class.layout.new(@scope, "")
270
- end
271
- end
272
- end
273
- end
274
- end
@@ -1,52 +0,0 @@
1
- module Hanami
2
- module View
3
- module Rendering
4
- # Null Object pattern for Layout.
5
- # It's used when a view doesn't require a layout.
6
- #
7
- # @api private
8
- # @since 0.1.0
9
- #
10
- # @example
11
- # require 'hanami/view'
12
- #
13
- # module Articles
14
- # class Show
15
- # include Hanami::View
16
- # layout false
17
- # end
18
- # end
19
- #
20
- # # In this scenario we will use a `NullLayout`.
21
- class NullLayout
22
-
23
- # Initialize a layout
24
- #
25
- # @param scope [Hanami::View::Rendering::Scope] view rendering scope
26
- # @param rendered [String] the output of the view rendering process
27
- #
28
- # @api private
29
- # @since 0.1.0
30
- #
31
- # @see Hanami::Layout#initialize
32
- # @see Hanami::View::Rendering#render
33
- def initialize(scope, rendered)
34
- @rendered = rendered
35
- end
36
-
37
- # Render the layout
38
- #
39
- # @return [String] the output of the rendering process
40
- #
41
- # @api private
42
- # @since 0.1.0
43
- #
44
- # @see Hanami::Layout#render
45
- # @see Hanami::View::Rendering#render
46
- def render
47
- @rendered
48
- end
49
- end
50
- end
51
- end
52
- end
@@ -1,82 +0,0 @@
1
- require 'hanami/utils/basic_object'
2
-
3
- module Hanami
4
- module View
5
- module Rendering
6
- # Null local
7
- #
8
- # @since 0.7.0
9
- #
10
- # @see Hanami::View::Rendering#local
11
- class NullLocal < Utils::BasicObject
12
- # @since 0.7.0
13
- # @api private
14
- TO_STR = "".freeze
15
-
16
- # @since 0.7.0
17
- # @api private
18
- def initialize(local)
19
- @local = local
20
- end
21
-
22
- # @since 0.7.0
23
- # @api private
24
- def all?
25
- false
26
- end
27
-
28
- # @since 0.7.0
29
- # @api private
30
- def any?
31
- false
32
- end
33
-
34
- # @since 0.7.0
35
- # @api private
36
- def empty?
37
- true
38
- end
39
-
40
- # @since 0.7.0
41
- # @api private
42
- def nil?
43
- true
44
- end
45
-
46
- # @since 0.7.0
47
- # @api private
48
- def to_str
49
- TO_STR
50
- end
51
-
52
- # @since 0.8.0
53
- # @api private
54
- alias to_s to_str
55
-
56
- # @since 0.7.0
57
- # @api private
58
- def method_missing(m, *)
59
- if m.match(/\?\z/)
60
- false
61
- else
62
- self.class.new("#{ @local }.#{ m }")
63
- end
64
- end
65
-
66
- private
67
-
68
- # @since 0.7.0
69
- # @api private
70
- def respond_to_missing?(method_name, include_all)
71
- true
72
- end
73
-
74
- # @since 0.7.0
75
- # @api private
76
- def __inspect
77
- " :#{ @local }"
78
- end
79
- end
80
- end
81
- end
82
- end
@@ -1,83 +0,0 @@
1
- module Hanami
2
- module View
3
- module Rendering
4
- # Null Object pattern for layout template
5
- #
6
- # It's used when a layout doesn't have an associated template.
7
- #
8
- # A common scenario is for non-html requests.
9
- # Usually we have a template for the application layout
10
- # (eg `templates/application.html.erb`), but we don't use to have the
11
- # template for JSON requests (eg `templates/application.json.erb`).
12
- # Because most of the times, we only return the output of the view.
13
- #
14
- # @api private
15
- # @since 0.1.0
16
- #
17
- # @example
18
- # require 'hanami/view'
19
- #
20
- # # We have an ApplicationLayout (views/application_layout.rb):
21
- # class ApplicationLayout
22
- # include Hanami::Layout
23
- # end
24
- #
25
- # # Our layout has a template for HTML requests, located at:
26
- # # templates/application.html.erb
27
- #
28
- # # We set it as global layout
29
- # Hanami::View.layout = :application
30
- #
31
- # # We have two views for HTML and JSON articles.
32
- # # They have a template each:
33
- # #
34
- # # * templates/articles/show.html.erb
35
- # # * templates/articles/show.json.erb
36
- # module Articles
37
- # class Show
38
- # include Hanami::View
39
- # format :html
40
- # end
41
- #
42
- # class JsonShow < Show
43
- # format :json
44
- # end
45
- # end
46
- #
47
- # # We initialize the framework
48
- # Hanami::View.load!
49
- #
50
- #
51
- #
52
- # # When we ask for a HTML rendering, it will use `Articles::Show` and
53
- # # ApplicationLayout. The output will be a composition of:
54
- # #
55
- # # * templates/articles/show.html.erb
56
- # # * templates/application.html.erb
57
- #
58
- # # When we ask for a JSON rendering, it will use `Articles::JsonShow`
59
- # # and ApplicationLayout. Since, the layout doesn't have any associated
60
- # # template for JSON, the output will be a composition of:
61
- # #
62
- # # * templates/articles/show.json.erb
63
- class NullTemplate
64
- # Render the layout template
65
- #
66
- # @param scope [Hanami::View::Scope] the rendering scope
67
- # @param locals [Hash] a set of objects available during the rendering
68
- # @yield [Proc] yields the given block
69
- #
70
- # @return [String] the output of the rendering process
71
- #
72
- # @api private
73
- # @since 0.1.0
74
- #
75
- # @see Hanami::Layout#render
76
- # @see Hanami::View::Rendering#render
77
- def render(scope, locals = {})
78
- yield
79
- end
80
- end
81
- end
82
- end
83
- end
@@ -1,26 +0,0 @@
1
- module Hanami
2
- module View
3
- module Rendering
4
- # Null Object pattern for view
5
- #
6
- # It's used when a layout is rendered direcly for testing purposes
7
- #
8
- # @api private
9
- # @since 1.2.1
10
- class NullView
11
- # Render the layout template
12
- #
13
- # @return [String] an empty string
14
- #
15
- # @api private
16
- # @since 1.2.1
17
- #
18
- # @see Hanami::Layout#render
19
- # @see Hanami::View::Rendering#render
20
- def render
21
- ""
22
- end
23
- end
24
- end
25
- end
26
- end