dry-view 0.5.3 → 0.6.0
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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +18 -0
- data/.travis.yml +15 -10
- data/.yardopts +5 -0
- data/CHANGELOG.md +60 -1
- data/Gemfile +15 -5
- data/README.md +38 -13
- data/bin/setup +5 -0
- data/bin/setup_helpers.rb +27 -0
- data/dry-view.gemspec +8 -9
- data/lib/dry-view.rb +3 -1
- data/lib/dry/view.rb +503 -2
- data/lib/dry/view/context.rb +80 -0
- data/lib/dry/view/decorated_attributes.rb +81 -0
- data/lib/dry/view/exposure.rb +15 -2
- data/lib/dry/view/exposures.rb +15 -5
- data/lib/dry/view/part.rb +154 -61
- data/lib/dry/view/part_builder.rb +136 -0
- data/lib/dry/view/path.rb +22 -5
- data/lib/dry/view/render_environment.rb +62 -0
- data/lib/dry/view/render_environment_missing.rb +44 -0
- data/lib/dry/view/rendered.rb +55 -0
- data/lib/dry/view/renderer.rb +22 -19
- data/lib/dry/view/scope.rb +146 -14
- data/lib/dry/view/scope_builder.rb +98 -0
- data/lib/dry/view/tilt.rb +78 -0
- data/lib/dry/view/tilt/erb.rb +26 -0
- data/lib/dry/view/tilt/erbse.rb +21 -0
- data/lib/dry/view/tilt/haml.rb +26 -0
- data/lib/dry/view/version.rb +5 -2
- metadata +50 -88
- data/benchmarks/templates/button.html.erb +0 -1
- data/benchmarks/view.rb +0 -24
- data/lib/dry/view/controller.rb +0 -159
- data/lib/dry/view/decorator.rb +0 -45
- data/lib/dry/view/missing_renderer.rb +0 -15
- data/spec/fixtures/templates/_hello.html.slim +0 -1
- data/spec/fixtures/templates/controller_renderer_options.html.erb +0 -3
- data/spec/fixtures/templates/decorated_parts.html.slim +0 -4
- data/spec/fixtures/templates/edit.html.slim +0 -11
- data/spec/fixtures/templates/empty.html.slim +0 -1
- data/spec/fixtures/templates/greeting.html.slim +0 -2
- data/spec/fixtures/templates/hello.html.slim +0 -1
- data/spec/fixtures/templates/layouts/app.html.slim +0 -6
- data/spec/fixtures/templates/layouts/app.txt.erb +0 -3
- data/spec/fixtures/templates/parts_with_args.html.slim +0 -3
- data/spec/fixtures/templates/parts_with_args/_box.html.slim +0 -3
- data/spec/fixtures/templates/shared/_index_table.html.slim +0 -2
- data/spec/fixtures/templates/shared/_shared_hello.html.slim +0 -1
- data/spec/fixtures/templates/tasks.html.slim +0 -3
- data/spec/fixtures/templates/user.html.slim +0 -2
- data/spec/fixtures/templates/users.html.slim +0 -5
- data/spec/fixtures/templates/users.txt.erb +0 -3
- data/spec/fixtures/templates/users/_row.html.slim +0 -2
- data/spec/fixtures/templates/users/_tbody.html.slim +0 -5
- data/spec/fixtures/templates/users_with_count.html.slim +0 -5
- data/spec/fixtures/templates/users_with_count_inherit.html.slim +0 -6
- data/spec/fixtures/templates_override/_hello.html.slim +0 -1
- data/spec/fixtures/templates_override/users.html.slim +0 -5
- data/spec/integration/decorator_spec.rb +0 -80
- data/spec/integration/exposures_spec.rb +0 -392
- data/spec/integration/part/decorated_attributes_spec.rb +0 -193
- data/spec/integration/view_spec.rb +0 -133
- data/spec/spec_helper.rb +0 -46
- data/spec/unit/controller_spec.rb +0 -83
- data/spec/unit/decorator_spec.rb +0 -61
- data/spec/unit/exposure_spec.rb +0 -227
- data/spec/unit/exposures_spec.rb +0 -103
- data/spec/unit/part_spec.rb +0 -104
- data/spec/unit/renderer_spec.rb +0 -57
- data/spec/unit/scope_spec.rb +0 -53
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/cache"
|
4
|
+
require "dry/equalizer"
|
5
|
+
require_relative "part"
|
6
|
+
|
7
|
+
module Dry
|
8
|
+
class View
|
9
|
+
# Decorates exposure values with matching parts
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class PartBuilder
|
13
|
+
extend Dry::Core::Cache
|
14
|
+
include Dry::Equalizer(:namespace)
|
15
|
+
|
16
|
+
attr_reader :namespace
|
17
|
+
attr_reader :render_env
|
18
|
+
|
19
|
+
# Returns a new instance of PartBuilder
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
def initialize(namespace: nil, render_env: nil)
|
23
|
+
@namespace = namespace
|
24
|
+
@render_env = render_env
|
25
|
+
end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
def for_render_env(render_env)
|
29
|
+
return self if render_env == self.render_env
|
30
|
+
|
31
|
+
self.class.new(namespace: namespace, render_env: render_env)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Decorates an exposure value
|
35
|
+
#
|
36
|
+
# @param name [Symbol] exposure name
|
37
|
+
# @param value [Object] exposure value
|
38
|
+
# @param options [Hash] exposure options
|
39
|
+
#
|
40
|
+
# @return [Dry::View::Part] decorated value
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
def call(name, value, **options)
|
44
|
+
builder = value.respond_to?(:to_ary) ? :build_collection_part : :build_part
|
45
|
+
|
46
|
+
send(builder, name, value, **options)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def build_part(name, value, **options)
|
52
|
+
klass = part_class(name: name, **options)
|
53
|
+
|
54
|
+
klass.new(
|
55
|
+
name: name,
|
56
|
+
value: value,
|
57
|
+
render_env: render_env,
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def build_collection_part(name, value, **options)
|
62
|
+
collection_as = collection_options(name: name, **options)[:as]
|
63
|
+
item_name, item_as = collection_item_options(name: name, **options).values_at(:name, :as)
|
64
|
+
|
65
|
+
arr = value.to_ary.map { |obj|
|
66
|
+
build_part(item_name, obj, **options.merge(as: item_as))
|
67
|
+
}
|
68
|
+
|
69
|
+
build_part(name, arr, **options.merge(as: collection_as))
|
70
|
+
end
|
71
|
+
|
72
|
+
def collection_options(name:, **options)
|
73
|
+
collection_as = options[:as].is_a?(Array) ? options[:as].first : nil
|
74
|
+
|
75
|
+
options.merge(as: collection_as)
|
76
|
+
end
|
77
|
+
|
78
|
+
def collection_item_options(name:, **options)
|
79
|
+
singular_name = inflector.singularize(name).to_sym
|
80
|
+
singular_as =
|
81
|
+
if options[:as].is_a?(Array)
|
82
|
+
options[:as].last if options[:as].length > 1
|
83
|
+
else
|
84
|
+
options[:as]
|
85
|
+
end
|
86
|
+
|
87
|
+
if singular_as && !singular_as.is_a?(Class)
|
88
|
+
singular_as = inflector.singularize(singular_as.to_s)
|
89
|
+
end
|
90
|
+
|
91
|
+
options.merge(
|
92
|
+
name: singular_name,
|
93
|
+
as: singular_as,
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
def part_class(name:, fallback_class: Part, **options)
|
98
|
+
name = options[:as] || name
|
99
|
+
|
100
|
+
if name.is_a?(Class)
|
101
|
+
name
|
102
|
+
else
|
103
|
+
fetch_or_store(namespace, name, fallback_class) do
|
104
|
+
resolve_part_class(name: name, fallback_class: fallback_class)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def resolve_part_class(name:, fallback_class:)
|
110
|
+
return fallback_class unless namespace
|
111
|
+
|
112
|
+
name = inflector.camelize(name.to_s)
|
113
|
+
|
114
|
+
# Give autoloaders a chance to act
|
115
|
+
begin
|
116
|
+
klass = namespace.const_get(name)
|
117
|
+
rescue NameError
|
118
|
+
end
|
119
|
+
|
120
|
+
if !klass && namespace.const_defined?(name, false)
|
121
|
+
klass = namespace.const_get(name)
|
122
|
+
end
|
123
|
+
|
124
|
+
if klass && klass < Part
|
125
|
+
klass
|
126
|
+
else
|
127
|
+
fallback_class
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def inflector
|
132
|
+
render_env.inflector
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/lib/dry/view/path.rb
CHANGED
@@ -1,19 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "pathname"
|
4
|
+
require "dry/core/cache"
|
2
5
|
|
3
6
|
module Dry
|
4
|
-
|
7
|
+
class View
|
8
|
+
# @api private
|
5
9
|
class Path
|
10
|
+
extend Dry::Core::Cache
|
6
11
|
include Dry::Equalizer(:dir, :root)
|
7
12
|
|
8
13
|
attr_reader :dir, :root
|
9
14
|
|
10
|
-
def
|
15
|
+
def self.[](path)
|
16
|
+
if path.is_a?(self)
|
17
|
+
path
|
18
|
+
else
|
19
|
+
new(path)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(dir, root: dir)
|
11
24
|
@dir = Pathname(dir)
|
12
|
-
@root =
|
25
|
+
@root = root
|
13
26
|
end
|
14
27
|
|
15
|
-
def lookup(name, format)
|
16
|
-
|
28
|
+
def lookup(name, format, include_shared: true)
|
29
|
+
fetch_or_store(dir, root, name, format) do
|
30
|
+
template?(name, format) ||
|
31
|
+
(include_shared && template?("shared/#{name}", format)) ||
|
32
|
+
!root? && chdir("..").lookup(name, format)
|
33
|
+
end
|
17
34
|
end
|
18
35
|
|
19
36
|
def chdir(dirname)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/equalizer"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
class View
|
7
|
+
# @api private
|
8
|
+
class RenderEnvironment
|
9
|
+
def self.prepare(renderer, config, context)
|
10
|
+
new(
|
11
|
+
renderer: renderer,
|
12
|
+
inflector: config.inflector,
|
13
|
+
context: context,
|
14
|
+
scope_builder: config.scope_builder.new(namespace: config.scope_namespace),
|
15
|
+
part_builder: config.part_builder.new(namespace: config.part_namespace),
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
include Dry::Equalizer(:renderer, :inflector, :context, :scope_builder, :part_builder)
|
20
|
+
|
21
|
+
attr_reader :renderer, :inflector, :context, :scope_builder, :part_builder
|
22
|
+
|
23
|
+
def initialize(renderer:, inflector:, context:, scope_builder:, part_builder:)
|
24
|
+
@renderer = renderer
|
25
|
+
@inflector = inflector
|
26
|
+
@context = context.for_render_env(self)
|
27
|
+
@scope_builder = scope_builder.for_render_env(self)
|
28
|
+
@part_builder = part_builder.for_render_env(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def format
|
32
|
+
renderer.format
|
33
|
+
end
|
34
|
+
|
35
|
+
def part(name, value, **options)
|
36
|
+
part_builder.(name, value, **options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def scope(name = nil, locals)
|
40
|
+
scope_builder.(name, locals)
|
41
|
+
end
|
42
|
+
|
43
|
+
def template(name, scope, &block)
|
44
|
+
renderer.template(name, scope, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def partial(name, scope, &block)
|
48
|
+
renderer.partial(name, scope, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
def chdir(dirname)
|
52
|
+
self.class.new(
|
53
|
+
renderer: renderer.chdir(dirname),
|
54
|
+
inflector: inflector,
|
55
|
+
context: context,
|
56
|
+
scope_builder: scope_builder,
|
57
|
+
part_builder: part_builder,
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/inflector"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
class View
|
7
|
+
# @api private
|
8
|
+
class RenderEnvironmentMissing
|
9
|
+
class MissingEnvironmentError < StandardError
|
10
|
+
def message
|
11
|
+
"a +render_env+ must be provided"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def format
|
16
|
+
raise MissingEnvironmentError
|
17
|
+
end
|
18
|
+
|
19
|
+
def context
|
20
|
+
raise MissingEnvironmentError
|
21
|
+
end
|
22
|
+
|
23
|
+
def part(name, value, **options)
|
24
|
+
raise MissingEnvironmentError
|
25
|
+
end
|
26
|
+
|
27
|
+
def scope(name = nil, locals)
|
28
|
+
raise MissingEnvironmentError
|
29
|
+
end
|
30
|
+
|
31
|
+
def template(name, scope, &block)
|
32
|
+
raise MissingEnvironmentError
|
33
|
+
end
|
34
|
+
|
35
|
+
def partial(name, scope, &block)
|
36
|
+
raise MissingEnvironmentError
|
37
|
+
end
|
38
|
+
|
39
|
+
def inflector
|
40
|
+
@inflector ||= Dry::Inflector.new
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/equalizer"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
class View
|
7
|
+
# Output of a View rendering
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class Rendered
|
11
|
+
include Dry::Equalizer(:output, :locals)
|
12
|
+
|
13
|
+
# Returns the rendered view
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
attr_reader :output
|
19
|
+
|
20
|
+
# Returns the hash of locals used to render the view
|
21
|
+
#
|
22
|
+
# @return [Hash[<Symbol, Dry::View::Part>] locals hash
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
attr_reader :locals
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
def initialize(output:, locals:)
|
29
|
+
@output = output
|
30
|
+
@locals = locals
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the local corresponding to the key
|
34
|
+
#
|
35
|
+
# @param name [Symbol] local key
|
36
|
+
#
|
37
|
+
# @return [Dry::View::Part]
|
38
|
+
#
|
39
|
+
# @api public
|
40
|
+
def [](name)
|
41
|
+
locals[name]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the rendered view
|
45
|
+
#
|
46
|
+
# @return [String]
|
47
|
+
#
|
48
|
+
# @api public
|
49
|
+
def to_s
|
50
|
+
output
|
51
|
+
end
|
52
|
+
alias_method :to_str, :to_s
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/dry/view/renderer.rb
CHANGED
@@ -1,27 +1,29 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/cache"
|
4
|
+
require "dry/equalizer"
|
5
|
+
require_relative "tilt"
|
3
6
|
|
4
7
|
module Dry
|
5
|
-
|
8
|
+
class View
|
9
|
+
# @api private
|
6
10
|
class Renderer
|
7
|
-
PARTIAL_PREFIX = "_"
|
8
|
-
PATH_DELIMITER = "/"
|
11
|
+
PARTIAL_PREFIX = "_"
|
12
|
+
PATH_DELIMITER = "/"
|
9
13
|
|
10
|
-
|
14
|
+
extend Dry::Core::Cache
|
11
15
|
|
12
|
-
|
16
|
+
include Dry::Equalizer(:paths, :format, :engine_mapping, :options)
|
13
17
|
|
14
|
-
|
18
|
+
TemplateNotFoundError = Class.new(StandardError)
|
15
19
|
|
16
|
-
|
17
|
-
@__engines__ ||= {}
|
18
|
-
end
|
20
|
+
attr_reader :paths, :format, :engine_mapping, :options
|
19
21
|
|
20
|
-
def initialize(paths, format:, **options)
|
22
|
+
def initialize(paths, format:, engine_mapping: nil, **options)
|
21
23
|
@paths = paths
|
22
24
|
@format = format
|
25
|
+
@engine_mapping = engine_mapping || {}
|
23
26
|
@options = options
|
24
|
-
@tilts = self.class.tilts
|
25
27
|
end
|
26
28
|
|
27
29
|
def template(name, scope, &block)
|
@@ -46,12 +48,13 @@ module Dry
|
|
46
48
|
def chdir(dirname)
|
47
49
|
new_paths = paths.map { |path| path.chdir(dirname) }
|
48
50
|
|
49
|
-
self.class.new(new_paths, format: format)
|
51
|
+
self.class.new(new_paths, format: format, **options)
|
50
52
|
end
|
51
53
|
|
52
54
|
def lookup(name)
|
53
|
-
paths.inject(false) { |
|
54
|
-
result
|
55
|
+
paths.inject(false) { |_, path|
|
56
|
+
result = path.lookup(name, format, include_shared: false)
|
57
|
+
break result if result
|
55
58
|
}
|
56
59
|
end
|
57
60
|
|
@@ -59,12 +62,12 @@ module Dry
|
|
59
62
|
|
60
63
|
def name_for_partial(name)
|
61
64
|
name_segments = name.to_s.split(PATH_DELIMITER)
|
62
|
-
|
65
|
+
name_segments[0..-2].push("#{PARTIAL_PREFIX}#{name_segments[-1]}").join(PATH_DELIMITER)
|
63
66
|
end
|
64
67
|
|
65
68
|
def tilt(path)
|
66
|
-
|
67
|
-
|
69
|
+
fetch_or_store(:engine, path, engine_mapping, options) {
|
70
|
+
Tilt[path, engine_mapping, **options]
|
68
71
|
}
|
69
72
|
end
|
70
73
|
end
|
data/lib/dry/view/scope.rb
CHANGED
@@ -1,43 +1,175 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/equalizer"
|
4
|
+
require "dry/core/constants"
|
5
|
+
require_relative "render_environment_missing"
|
2
6
|
|
3
7
|
module Dry
|
4
|
-
|
8
|
+
class View
|
9
|
+
# Evaluation context for templates (including layouts and partials) and
|
10
|
+
# provides a place to encapsulate view-specific behaviour alongside a
|
11
|
+
# template and its locals.
|
12
|
+
#
|
13
|
+
# @abstract Subclass this and provide your own methods adding view-specific
|
14
|
+
# behavior. You should not override `#initialize`
|
15
|
+
#
|
16
|
+
# @see https://dry-rb.org/gems/dry-view/templates/
|
17
|
+
# @see https://dry-rb.org/gems/dry-view/scopes/
|
18
|
+
#
|
19
|
+
# @api public
|
5
20
|
class Scope
|
6
|
-
|
21
|
+
# @api private
|
22
|
+
CONVENIENCE_METHODS = %i[format context locals].freeze
|
23
|
+
|
24
|
+
include Dry::Equalizer(:_name, :_locals, :_render_env)
|
25
|
+
|
26
|
+
# The scope's name
|
27
|
+
#
|
28
|
+
# @return [Symbol]
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
attr_reader :_name
|
7
32
|
|
33
|
+
# The scope's locals
|
34
|
+
#
|
35
|
+
# @overload _locals
|
36
|
+
# Returns the locals
|
37
|
+
# @overload locals
|
38
|
+
# A convenience alias for `#_format.` Is available unless there is a
|
39
|
+
# local named `locals`
|
40
|
+
#
|
41
|
+
# @return [Hash[<Symbol, Object>]
|
42
|
+
#
|
43
|
+
# @api public
|
8
44
|
attr_reader :_locals
|
9
|
-
attr_reader :_context
|
10
|
-
attr_reader :_renderer
|
11
45
|
|
12
|
-
|
46
|
+
# The current render environment
|
47
|
+
#
|
48
|
+
# @return [RenderEnvironment] render environment
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
attr_reader :_render_env
|
52
|
+
|
53
|
+
# Returns a new Scope instance
|
54
|
+
#
|
55
|
+
# @param name [Symbol, nil] scope name
|
56
|
+
# @param locals [Hash<Symbol, Object>] template locals
|
57
|
+
# @param render_env [RenderEnvironment] render environment
|
58
|
+
#
|
59
|
+
# @return [Scope]
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
def initialize(name: nil, locals: Dry::Core::Constants::EMPTY_HASH, render_env: RenderEnvironmentMissing.new)
|
63
|
+
@_name = name
|
13
64
|
@_locals = locals
|
14
|
-
@
|
15
|
-
|
65
|
+
@_render_env = render_env
|
66
|
+
end
|
67
|
+
|
68
|
+
# @overload render(partial_name, **locals, &block)
|
69
|
+
# Renders a partial using the scope
|
70
|
+
#
|
71
|
+
# @param partial_name [Symbol, String] partial name
|
72
|
+
# @param locals [Hash<Symbol, Object>] partial locals
|
73
|
+
# @yieldreturn [String] string content to include where the partial calls `yield`
|
74
|
+
#
|
75
|
+
# @overload render(**locals, &block)
|
76
|
+
# Renders a partial (named after the scope's own name) using the scope
|
77
|
+
#
|
78
|
+
# @param locals[Hash<Symbol, Object>] partial locals
|
79
|
+
# @yieldreturn [String] string content to include where the partial calls `yield`
|
80
|
+
#
|
81
|
+
# @return [String] the rendered partial output
|
82
|
+
#
|
83
|
+
# @api public
|
84
|
+
def render(partial_name = nil, **locals, &block)
|
85
|
+
partial_name ||= _name
|
86
|
+
raise ArgumentError, "+partial_name+ must be provided for unnamed scopes" unless partial_name
|
87
|
+
partial_name = _inflector.underscore(_inflector.demodulize(partial_name.to_s)) if partial_name.is_a?(Class)
|
88
|
+
|
89
|
+
_render_env.partial(partial_name, _render_scope(locals), &block)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Build a new scope using a scope class matching the provided name
|
93
|
+
#
|
94
|
+
# @param name [Symbol, Class] scope name (or class)
|
95
|
+
# @param locals [Hash<Symbol, Object>] scope locals
|
96
|
+
#
|
97
|
+
# @return [Scope]
|
98
|
+
#
|
99
|
+
# @api public
|
100
|
+
def scope(name = nil, **locals)
|
101
|
+
_render_env.scope(name, locals)
|
16
102
|
end
|
17
103
|
|
18
|
-
|
19
|
-
|
104
|
+
# The template format for the current render environment.
|
105
|
+
#
|
106
|
+
# @overload _format
|
107
|
+
# Returns the format.
|
108
|
+
# @overload format
|
109
|
+
# A convenience alias for `#_format.` Is available unless there is a
|
110
|
+
# local named `format`
|
111
|
+
#
|
112
|
+
# @return [Symbol] format
|
113
|
+
#
|
114
|
+
# @api public
|
115
|
+
def _format
|
116
|
+
_render_env.format
|
117
|
+
end
|
118
|
+
|
119
|
+
# The context object for the current render environment
|
120
|
+
#
|
121
|
+
# @overload _context
|
122
|
+
# Returns the context.
|
123
|
+
# @overload context
|
124
|
+
# A convenience alias for `#_context`. Is available unless there is a
|
125
|
+
# local named `context`.
|
126
|
+
#
|
127
|
+
# @return [Context] context
|
128
|
+
#
|
129
|
+
# @api public
|
130
|
+
def _context
|
131
|
+
_render_env.context
|
20
132
|
end
|
21
133
|
|
22
134
|
private
|
23
135
|
|
136
|
+
# Handles missing methods, according to the following rules:
|
137
|
+
#
|
138
|
+
# 1. If there is a local with a name matching the method, it returns the
|
139
|
+
# local.
|
140
|
+
# 2. If the `context` responds to the method, then it will be sent the
|
141
|
+
# method and all its arguments.
|
24
142
|
def method_missing(name, *args, &block)
|
25
143
|
if _locals.key?(name)
|
26
144
|
_locals[name]
|
27
145
|
elsif _context.respond_to?(name)
|
28
146
|
_context.public_send(name, *args, &block)
|
147
|
+
elsif CONVENIENCE_METHODS.include?(name)
|
148
|
+
__send__(:"_#{name}", *args, &block)
|
29
149
|
else
|
30
150
|
super
|
31
151
|
end
|
32
152
|
end
|
33
153
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
154
|
+
def respond_to_missing?(name, include_private = false)
|
155
|
+
_locals.key?(name) || _render_env.context.respond_to?(name) || CONVENIENCE_METHODS.include?(name) || super
|
156
|
+
end
|
157
|
+
|
158
|
+
def _render_scope(**locals)
|
159
|
+
if locals.none?
|
38
160
|
self
|
161
|
+
else
|
162
|
+
self.class.new(
|
163
|
+
# FIXME: what about `name`?
|
164
|
+
locals: locals,
|
165
|
+
render_env: _render_env,
|
166
|
+
)
|
39
167
|
end
|
40
168
|
end
|
169
|
+
|
170
|
+
def _inflector
|
171
|
+
_render_env.inflector
|
172
|
+
end
|
41
173
|
end
|
42
174
|
end
|
43
175
|
end
|