hanami-view 1.3.3 → 2.0.0.alpha6

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 +18 -9
  3. data/LICENSE +20 -0
  4. data/README.md +17 -835
  5. data/hanami-view.gemspec +27 -16
  6. data/lib/hanami/view/application_configuration.rb +77 -0
  7. data/lib/hanami/view/application_context.rb +57 -0
  8. data/lib/hanami/view/application_view.rb +89 -0
  9. data/lib/hanami/view/context.rb +98 -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 +121 -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 -281
  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,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
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hanami
4
- module View
5
- module Rendering
6
- # Rendering options
7
- #
8
- # @since 1.1.1
9
- # @api private
10
- class Options
11
- # @since 1.1.1
12
- # @api private
13
- def self.build(options, locals, format)
14
- options.dup.tap do |opts|
15
- opts[:format] ||= format
16
- opts[:locals] = locals
17
- opts[:locals].merge!(options.fetch(:locals) { ::Hash.new })
18
- opts[:locals].merge!(format: opts.fetch(:format, format))
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,31 +0,0 @@
1
- require 'hanami/view/rendering/partial_finder'
2
-
3
- module Hanami
4
- module View
5
- module Rendering
6
- # Rendering partial
7
- #
8
- # It's used when a template wants to render a partial.
9
- #
10
- # @api private
11
- # @since 0.1.0
12
- #
13
- # @see Hanami::View::Rendering::Template
14
- # @see Hanami::View::Rendering::LayoutScope#render
15
- #
16
- # @example
17
- # # We have an application template (templates/application.html.erb)
18
- # # that uses the following line:
19
- #
20
- # <%= render partial: 'shared/sidebar' %>
21
- class Partial < Template
22
- protected
23
- # @api private
24
- # @since 0.1.0
25
- def template
26
- PartialFinder.new(@view.class, @options).find
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,29 +0,0 @@
1
- module Hanami
2
- module View
3
- module Rendering
4
- # @since 0.7.0
5
- # @api private
6
- class PartialFile
7
- # @since 0.7.0
8
- # @api private
9
- attr_reader :key
10
-
11
- # @since 0.7.0
12
- # @api private
13
- attr_reader :format
14
-
15
- # @since 0.7.0
16
- # @api private
17
- attr_reader :template
18
-
19
- # @since 0.7.0
20
- # @api private
21
- def initialize(key, format, template)
22
- @key = key
23
- @format = format
24
- @template = template
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,75 +0,0 @@
1
- require 'hanami/view/rendering/template_finder'
2
-
3
- module Hanami
4
- module View
5
- module Rendering
6
- # Find a partial for the current view context.
7
- # It's used when a template wants to render a partial.
8
- #
9
- # @see Hanami::View::Rendering::Partial
10
- # @see Hanami::View::Rendering::TemplateFinder
11
- #
12
- # @api private
13
- # @since 0.1.0
14
- class PartialFinder < TemplateFinder
15
- # Template file name prefix.
16
- # By convention a partial file name starts with this prefix.
17
- #
18
- # @api private
19
- # @since 0.1.0
20
- #
21
- # @example
22
- # "_sidebar.html.erb"
23
- PREFIX = '_'.freeze
24
-
25
- # Find a template for a partial. Initially it will look for the
26
- # partial template in the framework configuration where it may
27
- # already be cached. Failing that it will look under the
28
- # directory of the parent directory view template, if not found
29
- # it will search recursively from the view root.
30
- #
31
- # @return [Hanami::View::Template] the requested template
32
- #
33
- # @see Hanami::View::Rendering::TemplateFinder#find
34
- #
35
- # @since 0.4.3
36
- # @api private
37
- def find
38
- Hanami::View::Configuration.for(@view).
39
- find_partial(relative_partial_path, template_name, format)
40
- end
41
-
42
- protected
43
-
44
- # @since 0.7.0
45
- # @api private
46
- def relative_partial_path
47
- [view_template_dir, template_name].join(separator)
48
- end
49
-
50
- # @since 0.4.3
51
- # @api private
52
- def view_template_dir
53
- *all, _ = @view.template.split(separator)
54
- all.join(separator)
55
- end
56
-
57
- # @api private
58
- def template_name
59
- *all, last = partial_name.split(separator)
60
- all.push( last.prepend(prefix) ).join(separator)
61
- end
62
-
63
- # @api private
64
- def partial_name
65
- @options[:partial]
66
- end
67
-
68
- # @api private
69
- def prefix
70
- PREFIX
71
- end
72
- end
73
- end
74
- end
75
- end
@@ -1,73 +0,0 @@
1
- require 'hanami/view/template'
2
- require 'hanami/view/rendering/partial_file'
3
-
4
- module Hanami
5
- module View
6
- module Rendering
7
- # Find partial templates in the file system
8
- #
9
- # @api private
10
- # @since 0.7.0
11
- #
12
- # @see View::Template
13
- class PartialTemplatesFinder
14
- # Search pattern for partial file names
15
- #
16
- # @api private
17
- # @since 0.7.0
18
- PARTIAL_PATTERN = '_*'.freeze
19
-
20
- # @api private
21
- # @since 0.7.0
22
- PARTIAL_PARTS_SEPARATOR = '.'.freeze
23
-
24
- # @api private
25
- # @since 0.7.0
26
- attr_reader :configuration
27
-
28
- # Initializes a new PartialTemplatesFinder
29
- #
30
- # @param configuration [Configuration] the configuration object
31
- #
32
- # @since 0.7.0
33
- # @api private
34
- def initialize(configuration)
35
- @configuration = configuration
36
- end
37
-
38
- # Find partials under the given path
39
- #
40
- # @return [Array] array of PartialFinder objects
41
- #
42
- # @since 0.7.0
43
- # @api private
44
- def find
45
- _find_partials(configuration.root).map do |template|
46
- partial_path, partial_base_name = Pathname(template).relative_path_from(configuration.root).split
47
- partial_base_parts = partial_base_name.to_s.split(PARTIAL_PARTS_SEPARATOR)
48
-
49
- PartialFile.new(
50
- "#{partial_path}#{::File::SEPARATOR}#{partial_base_parts[0]}",
51
- partial_base_parts[1],
52
- View::Template.new(template, configuration.default_encoding)
53
- )
54
- end
55
- end
56
-
57
- private
58
-
59
- # Find partial template file paths
60
- #
61
- # @param path [String] the path under which we should search for partials
62
- #
63
- # @return [Array] an array of strings for each matching partial template file found
64
- #
65
- # @since 0.7.0
66
- # @api private
67
- def _find_partials(path)
68
- Dir.glob("#{ [path, TemplatesFinder::RECURSIVE, PARTIAL_PATTERN].join(::File::SEPARATOR) }.#{TemplatesFinder::FORMAT}.#{TemplatesFinder::ENGINES}")
69
- end
70
- end
71
- end
72
- end
73
- end
@@ -1,134 +0,0 @@
1
- require 'hanami/view/rendering/layout_registry'
2
- require 'hanami/view/rendering/view_finder'
3
-
4
- module Hanami
5
- module View
6
- module Rendering
7
- # Holds all the references of all the registered subclasses of a view.
8
- # We have one registry for each superclass view.
9
- #
10
- # @api private
11
- # @since 0.1.0
12
- #
13
- # @see Hanami::View::Rendering::LayoutRegistry
14
- # @see Hanami::View::Rendering#registry
15
- #
16
- # @example
17
- # require 'hanami/view'
18
- #
19
- # module Articles
20
- # class Index
21
- # include Hanami::View
22
- # end
23
- #
24
- # class Show
25
- # include Hanami::View
26
- # end
27
- #
28
- # class JsonShow < Show
29
- # format :json
30
- # end
31
- #
32
- # class XmlShow < Show
33
- # format :xml
34
- #
35
- # def render
36
- # ArticleSerializer.new(article).to_xml
37
- # end
38
- # end
39
- # end
40
- #
41
- # # We have the following templates:
42
- # #
43
- # # * articles/index.html.erb
44
- # # * articles/index.atom.erb
45
- # # * articles/show.html.erb
46
- # # * articles/show.json.erb
47
- #
48
- # # One registry per superclass view
49
- # Articles::Index.send(:registry).object_id # => 70135342862240
50
- #
51
- # Articles::Show.send(:registry).object_id # => 70135342110540
52
- # Articles::XmlShow.send(:registry).object_id # => 70135342110540
53
- # Articles::JsonShow.send(:registry).object_id # => 70135342110540
54
- #
55
- #
56
- #
57
- # # It holds the references for all the templates and the views
58
- # Articles::Index.send(:registry).inspect
59
- # # => { :all => [Articles::Index, nil],
60
- # # :atom => [Articles::Index, #<Hanami::View::Template ... @file="/path/to/templates/articles/index.atom.erb"],
61
- # # :html => [Articles::Index, #<Hanami::View::Template ... @file="/path/to/templates/articles/index.html.erb"] }
62
- #
63
- # Articles::Show.send(:registry).inspect
64
- # # => { :all => [Articles::Show, nil],
65
- # # :html => [Articles::Show, #<Hanami::View::Template ... @file="/path/to/templates/articles/show.html.erb"],
66
- # # :json => [Articles::JsonShow, #<Hanami::View::Template ... @file="/path/to/templates/articles/show.json.erb"],
67
- # # :xml => [Articles::XmlShow, nil] }
68
- class Registry < LayoutRegistry
69
- # Default format for views without an explicit format.
70
- #
71
- # @api private
72
- # @since 0.1.0
73
- #
74
- # @see Hanami::View::Dsl#format
75
- DEFAULT_FORMAT = :all
76
-
77
- # Returns the view for the given context.
78
- #
79
- # @param context [Hash] the rendering context
80
- # @option context [Symbol] :format the requested format
81
- #
82
- # @return [Hanami::View] the view associated with the given context
83
- #
84
- # @raise [Hanami::View::MissingFormatError] if the given context doesn't
85
- # have the :format key
86
- #
87
- # @api private
88
- # @since 0.1.0
89
- #
90
- # @see Hanami::View::Rendering#render
91
- def resolve(context)
92
- view, template = @registry.fetch(format(context)) { @registry[DEFAULT_FORMAT] }
93
- view.new(template, **context)
94
- end
95
-
96
- private
97
- # @api private
98
- def prepare!
99
- prepare_views!
100
- prepare_templates!
101
- end
102
-
103
- # @api private
104
- def prepare_views!
105
- views.each do |view|
106
- @registry.merge! view.format || DEFAULT_FORMAT => [ view, template_for(view) ]
107
- end
108
- end
109
-
110
- # @api private
111
- def prepare_templates!
112
- templates.each do |template|
113
- @registry.merge! template.format => [ view_for(template), template ]
114
- end
115
- end
116
-
117
- # @api private
118
- def views
119
- @view.subclasses + [ @view ]
120
- end
121
-
122
- # @api private
123
- def view_for(template)
124
- ViewFinder.new(@view).find(template)
125
- end
126
-
127
- # @api private
128
- def template_for(view)
129
- templates.find {|template| template.format == view.format }
130
- end
131
- end
132
- end
133
- end
134
- end
@@ -1,108 +0,0 @@
1
- require 'hanami/utils/escape'
2
- require 'hanami/view/rendering/layout_scope'
3
- require 'hanami/view/rendering/template'
4
- require 'hanami/view/rendering/partial'
5
-
6
- module Hanami
7
- module View
8
- module Rendering
9
- # Rendering view scope
10
- #
11
- # @since 0.1.0
12
- #
13
- # @see Hanami::View::Rendering::LayoutScope
14
- class Scope < LayoutScope
15
- # Initialize the scope
16
- #
17
- # @param view [Class] the view
18
- # @param locals [Hash] a set of objects available during the rendering
19
- # @option locals [Symbol] :format the requested format
20
- #
21
- # @api private
22
- # @since 0.1.0
23
- def initialize(view, locals = {})
24
- @view = view
25
- @locals = locals
26
- @layout = layout
27
- @scope = nil
28
- end
29
-
30
- # Returns an inspect String
31
- #
32
- # @return [String] inspect String (contains classname, objectid in hex, available ivars)
33
- #
34
- # @since 0.3.0
35
- def inspect
36
- base = "#<#{ self.class }: #{'%x' % (self.object_id << 1)}"
37
- base << " @view=\"#{@view}\"" if @view
38
- base << " @locals=\"#{@locals}\"" if @locals
39
- base << ">"
40
- end
41
-
42
- # Returns the requested format.
43
- #
44
- # @return [Symbol] the requested format (eg. :html, :json, :xml, etc..)
45
- #
46
- # @since 0.1.0
47
- def format
48
- locals[:format]
49
- end
50
-
51
- # Implements "respond to" logic
52
- #
53
- # @return [TrueClass,FalseClass]
54
- #
55
- # @since 0.3.0
56
- # @api private
57
- #
58
- # @see http://ruby-doc.org/core/Object.html#method-i-respond_to_missing-3F
59
- def respond_to_missing?(m, include_all)
60
- # FIXME: this isn't compatible with Hanami 2.0, as it extends a view
61
- # that we want to be frozen in the future
62
- #
63
- # See https://github.com/hanami/view/issues/130#issuecomment-319326236
64
- @view.respond_to?(m, include_all) ||
65
- @locals.key?(m)
66
- end
67
-
68
- protected
69
-
70
- # @api private
71
- def method_missing(m, *args, &block)
72
- ::Hanami::View::Escape.html(
73
- # FIXME: this isn't compatible with Hanami 2.0, as it extends a view
74
- # that we want to be frozen in the future
75
- #
76
- # See https://github.com/hanami/view/issues/130#issuecomment-319326236
77
- if @view.respond_to?(m, true)
78
- @view.__send__ m, *args, &block
79
- elsif @locals.key?(m)
80
- @locals[m]
81
- else
82
- super
83
- end
84
- )
85
- end
86
-
87
- private
88
-
89
- # @since 1.1.1
90
- # @api private
91
- def _options(options)
92
- current_locals = locals.reject { |key, _| @view.respond_to?(key) }
93
- Options.build(options, current_locals, format)
94
- end
95
-
96
- # @since 0.4.2
97
- # @api private
98
- def layout
99
- if @view.class.respond_to?(:layout)
100
- @view.class.layout.new(self, "")
101
- else
102
- nil
103
- end
104
- end
105
- end
106
- end
107
- end
108
- end
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "hanami/view/rendering/scope"
4
- require "hanami/view/rendering/options"
5
-
6
- module Hanami
7
- module View
8
- module Rendering
9
- # Rendering subscope
10
- #
11
- # @since 1.1.1
12
- # @api private
13
- #
14
- # @see Hanami::View::Rendering::Scope
15
- class Subscope < Scope
16
- # Implements "respond to" logic
17
- #
18
- # @return [TrueClass,FalseClass]
19
- #
20
- # @since 1.1.1
21
- # @api private
22
- #
23
- # @see http://ruby-doc.org/core/Object.html#method-i-respond_to_missing-3F
24
- def respond_to_missing?(m, _include_all)
25
- @locals.key?(m)
26
- end
27
-
28
- protected
29
-
30
- # @since 1.1.1
31
- # @api private
32
- def method_missing(m, *args, &block)
33
- ::Hanami::View::Escape.html(
34
- # FIXME: this isn't compatible with Hanami 2.0, as it extends a view
35
- # that we want to be frozen in the future
36
- #
37
- # See https://github.com/hanami/view/issues/130#issuecomment-319326236
38
- if @locals.key?(m)
39
- @locals[m]
40
- else
41
- super
42
- end
43
- )
44
- end
45
-
46
- private
47
-
48
- # @since 1.1.1
49
- # @api private
50
- def _options(options)
51
- Options.build(options, locals, format)
52
- end
53
- end
54
- end
55
- end
56
- end
@@ -1,69 +0,0 @@
1
- require 'hanami/view/rendering/template_finder'
2
-
3
- module Hanami
4
- module View
5
- module Rendering
6
- # Rendering template
7
- #
8
- # It's used when a template wants to render another template.
9
- #
10
- # @api private
11
- # @since 0.1.0
12
- #
13
- # @see Hanami::View::Rendering::LayoutScope#render
14
- #
15
- # @example
16
- # # We have an application template (templates/application.html.erb)
17
- # # that uses the following line:
18
- #
19
- # <%= render template: 'articles/show' %>
20
- class Template
21
- # Initialize a template
22
- #
23
- # @param view [Hanami::View] the current view
24
- # @param options [Hash] the rendering informations
25
- # @option options [Symbol] :format the current format
26
- # @option options [Hash] :locals the set of objects available within
27
- # the rendering context
28
- #
29
- # @api private
30
- # @since 0.1.0
31
- def initialize(view, options)
32
- @view, @options = view, options
33
- end
34
-
35
- # Render the template.
36
- #
37
- # @return [String] the output of the rendering process.
38
- #
39
- # @raise [Hanami::View::MissingTemplateError] if template can't be found
40
- #
41
- # @api private
42
- # @since 0.1.0
43
- def render(&block)
44
- (template or raise_missing_template_error).render(scope, &block)
45
- end
46
-
47
- protected
48
- # @api private
49
- def template
50
- TemplateFinder.new(@view.class, @options).find
51
- end
52
-
53
- # @api private
54
- def scope
55
- Subscope.new(@view, @options[:locals])
56
- end
57
-
58
- # @since 0.5.0
59
- # @api private
60
- def raise_missing_template_error
61
- raise MissingTemplateError.new(
62
- @options.fetch(:template) { @options.fetch(:partial, nil) },
63
- @options[:format]
64
- )
65
- end
66
- end
67
- end
68
- end
69
- end
@@ -1,55 +0,0 @@
1
- require 'hanami/view/rendering/templates_finder'
2
-
3
- module Hanami
4
- module View
5
- module Rendering
6
- # Find a template for the current view context.
7
- # It's used when a template wants to render another template.
8
- #
9
- # @see Hanami::View::Rendering::Template
10
- # @see Hanami::View::Rendering::TemplatesFinder
11
- #
12
- # @api private
13
- # @since 0.1.0
14
- class TemplateFinder < TemplatesFinder
15
- # Initialize a finder
16
- #
17
- # @param view [Class] a view
18
- # @param options [Hash] the informations about the context
19
- # @option options [String] :template the template file name
20
- # @option options [Symbol] :format the requested format
21
- #
22
- # @api private
23
- # @since 0.1.0
24
- def initialize(view, options)
25
- super(view)
26
- @options = options
27
- end
28
-
29
- # Find a template for the current view context
30
- #
31
- # @return [Hanami::View::Template] the requested template
32
- #
33
- # @api private
34
- # @since 0.1.0
35
- #
36
- # @see Hanami::View::Rendering::TemplatesFinder#find
37
- # @see Hanami::View::Rendering::Template#render
38
- def find
39
- super.first
40
- end
41
-
42
- protected
43
- # @api private
44
- def template_name
45
- @options[:template]
46
- end
47
-
48
- # @api private
49
- def format
50
- @options[:format]
51
- end
52
- end
53
- end
54
- end
55
- end