/
output_helpers.rb
204 lines (192 loc) · 6.17 KB
/
output_helpers.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
module Padrino
module Helpers
##
# Helpers related to buffer output for various template engines.
#
module OutputHelpers
def self.included(base)
base.send(:include, SinatraCurrentEngine) unless base.method_defined?(:current_engine)
end
##
# Module used to detect the current engine in vanilla Sinatra apps.
#
module SinatraCurrentEngine
attr_reader :current_engine
def render(engine, *)
@current_engine, engine_was = engine, @current_engine
output = super
@current_engine = engine_was
output
end
end
##
# Captures the html from a block of template code for any available handler.
#
# Be aware that trusting the html is up to the caller.
#
# @param [Object] *args
# Objects yield to the captured block.
# @param [Proc] &block
# Template code to capture as HTML.
#
# @return [String] Captured HTML resulting from the block.
#
# @example
# capture_html(&block) => "...html..."
# capture_html(object_for_block, &block) => "...html..."
#
# @example
# ActiveSupport::SafeBuffer.new + capture_html { "<foo>" }
# # => "<foo>"
# ActiveSupport::SafeBuffer.new.safe_concat(capture_html { "<foo>" })
# # => "<foo>"
#
def capture_html(*args, &block)
if handler = find_proper_handler
handler.capture_from_template(*args, &block)
else
block.call(*args)
end
end
alias :capture :capture_html
##
# Outputs the given text to the templates buffer directly.
#
# The output might be subject to escaping, if it is not marked as safe.
#
# @param [String, SafeBuffer] text
# Text to concatenate to the buffer.
#
# @example
# concat_content("This will be output to the template buffer")
#
def concat_content(text="")
if handler = find_proper_handler
handler.concat_to_template(text)
else
text
end
end
alias :concat :concat_content
##
# Outputs the given text to the templates buffer directly,
# assuming that it is safe.
#
# @param [String] text
# Text to concatenate to the buffer.
#
# @example
# concat_safe_content("This will be output to the template buffer")
#
def concat_safe_content(text="")
concat_content text.html_safe
end
##
# Returns true if the block is from a supported template type; false otherwise.
# Used to determine if html should be returned or concatenated to the view.
#
# @param [Block] block
# Determine if this block is a view template.
#
# @example
# block_is_template?(block) => true
#
# @return [Boolean] True if the block is a template; false otherwise.
#
def block_is_template?(block)
handler = find_proper_handler
block && handler && handler.engine_matches?(block)
end
##
# Capture a block or text of content to be rendered at a later time.
# Your blocks can also receive values, which are passed to them by <tt>yield_content</tt>.
#
# @overload content_for(key, content)
# @param [Symbol] key Name of your key for the content yield.
# @param [String] content Text to be stored for this key.
# @overload content_for(key, &block)
# @param [Symbol] key Name of your key for the content yield.
# @param [Proc] block Block to be stored as content for this key.
#
# @example
# content_for(:name) { ...content... }
# content_for(:name) { |name| ...content... }
# content_for(:name, "I'm Jeff")
#
def content_for(key, content = nil, &block)
content_blocks[key.to_sym] << (block_given? ? block : Proc.new { content })
end
##
# Is there a content block for a given key?
#
# @param [Symbol] key
# Name of content to yield.
#
# @return [TrueClass,FalseClass] Result html for the given +key+
#
# @example
# content_for? :header => true
#
def content_for?(key)
content_blocks[key.to_sym].present?
end
##
# Render the captured content blocks for a given key.
# You can also pass values to the content blocks by passing them
# as arguments after the key.
#
# @param [Symbol] key
# Name of content to yield.
# @param *args
# Values to pass to the content block.
#
# @return [String] Result HTML for the given +key+.
#
# @example
# yield_content :include
# yield_content :head, "param1", "param2"
# yield_content(:title) || "My page title"
#
def yield_content(key, *args)
blocks = content_blocks[key.to_sym]
return nil if blocks.empty?
mark_safe(blocks.map { |content| capture_html(*args, &content) }.join)
end
protected
##
# Retrieves content_blocks stored by content_for or within yield_content.
#
# @example
# content_blocks[:name] => ['...', '...']
#
def content_blocks
@content_blocks ||= Hash.new { |h,k| h[k] = [] }
end
##
# Retrieves the template handler for the given output context.
# Can handle any output related to capturing or concatenating in a given template.
#
# @example
# find_proper_handler => <OutputHelpers::HamlHandler>
#
def find_proper_handler
handler_class = OutputHelpers.handlers[current_engine]
handler_class && handler_class.new(self)
end
##
# Marks a String or a collection of Strings as safe. `nil` is accepted
# but ignored.
#
# @param [String, Array<String>] the values to be marked safe.
#
# @return [ActiveSupport::SafeBuffer, Array<ActiveSupport::SafeBuffer>]
def mark_safe(value)
if value.respond_to? :map!
value.map!{|v| v.html_safe if v }
else
value.html_safe if value
end
end
end
end
end