padrino-helpers 0.13.3 → 0.13.3.1
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/lib/padrino-helpers/asset_tag_helpers.rb +7 -7
- data/lib/padrino-helpers/form_builder/abstract_form_builder.rb +6 -6
- data/lib/padrino-helpers/form_helpers.rb +3 -3
- data/lib/padrino-helpers/form_helpers/errors.rb +8 -8
- data/lib/padrino-helpers/form_helpers/options.rb +1 -1
- data/lib/padrino-helpers/format_helpers.rb +1 -1
- data/lib/padrino-helpers/number_helpers.rb +5 -8
- data/lib/padrino-helpers/output_helpers.rb +4 -4
- data/lib/padrino-helpers/output_helpers/abstract_handler.rb +1 -1
- data/lib/padrino-helpers/render_helpers.rb +3 -3
- data/lib/padrino-helpers/tag_helpers.rb +1 -1
- data/lib/padrino/core_ext/output_safety.rb +25 -0
- data/lib/padrino/rendering.rb +6 -5
- data/lib/padrino/rendering/erb_template.rb +1 -1
- data/lib/padrino/rendering/erubis_template.rb +2 -2
- data/lib/padrino/rendering/slim_template.rb +17 -1
- data/lib/padrino/safe_buffer.rb +118 -0
- data/test/helper.rb +2 -2
- data/test/test_format_helpers.rb +5 -5
- data/test/test_render_helpers.rb +10 -0
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5dd540d3a9472d8c134bab1741faf3f69ae62166
|
4
|
+
data.tar.gz: 036a523946eefb56ec7892ead4e6df2c5f200d87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 734ebfd2d7817d8feb9221f9037df2da53923552cc961b7a6a60573539410dab665fbcc99f40296492f8cca611147c0aef6d64b533ba27d93f974f34c0e378b8
|
7
|
+
data.tar.gz: f8b6b1a10621c16c53786bc20b3494508f07e160bf94b788020a55e407ac443ebcb779e33c7282d7a1b47238e8b9f111b36c306eb1bbc8cd04d3250bfd1d5b1c
|
@@ -32,9 +32,9 @@ module Padrino
|
|
32
32
|
def flash_tag(*args)
|
33
33
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
34
34
|
bootstrap = options.delete(:bootstrap) if options[:bootstrap]
|
35
|
-
args.inject(
|
36
|
-
|
37
|
-
|
35
|
+
args.inject(SafeBuffer.new) do |html,kind|
|
36
|
+
next html unless flash[kind]
|
37
|
+
flash_text = SafeBuffer.new << flash[kind]
|
38
38
|
flash_text << content_tag(:button, '×'.html_safe, {:type => :button, :class => :close, :'data-dismiss' => :alert}) if bootstrap
|
39
39
|
html << content_tag(:div, flash_text, { :class => kind }.update(options))
|
40
40
|
end
|
@@ -243,8 +243,8 @@ module Padrino
|
|
243
243
|
options = {
|
244
244
|
:rel => 'stylesheet',
|
245
245
|
:type => 'text/css'
|
246
|
-
}.update(sources.last.is_a?(Hash) ? sources.pop
|
247
|
-
sources.flatten.inject(
|
246
|
+
}.update(sources.last.is_a?(Hash) ? Utils.symbolize_keys(sources.pop) : {})
|
247
|
+
sources.flatten.inject(SafeBuffer.new) do |all,source|
|
248
248
|
all << tag(:link, { :href => asset_path(:css, source) }.update(options))
|
249
249
|
end
|
250
250
|
end
|
@@ -266,8 +266,8 @@ module Padrino
|
|
266
266
|
def javascript_include_tag(*sources)
|
267
267
|
options = {
|
268
268
|
:type => 'text/javascript'
|
269
|
-
}.update(sources.last.is_a?(Hash) ? sources.pop
|
270
|
-
sources.flatten.inject(
|
269
|
+
}.update(sources.last.is_a?(Hash) ? Utils.symbolize_keys(sources.pop) : {})
|
270
|
+
sources.flatten.inject(SafeBuffer.new) do |all,source|
|
271
271
|
all << content_tag(:script, nil, { :src => asset_path(:js, source) }.update(options))
|
272
272
|
end
|
273
273
|
end
|
@@ -155,7 +155,7 @@ module Padrino
|
|
155
155
|
include_index = default_collection.respond_to?(:each)
|
156
156
|
|
157
157
|
nested_options = { :parent => self, :association => child_association }
|
158
|
-
Array(collection).each_with_index.inject(
|
158
|
+
Array(collection).each_with_index.inject(SafeBuffer.new) do |all,(child_instance,index)|
|
159
159
|
nested_options[:index] = options[:index] || (include_index ? index : nil)
|
160
160
|
all << @template.fields_for(child_instance, { :nested => nested_options, :builder => self.class }, &block) << "\n"
|
161
161
|
end
|
@@ -196,7 +196,7 @@ module Padrino
|
|
196
196
|
# field_name(:street) => "user[addresses_attributes][0][street]"
|
197
197
|
def field_name(field=nil)
|
198
198
|
result = field_name_fragment
|
199
|
-
result << "[#{field}]"
|
199
|
+
result << "[#{field}]" if field
|
200
200
|
result
|
201
201
|
end
|
202
202
|
|
@@ -209,8 +209,8 @@ module Padrino
|
|
209
209
|
def field_id(field=nil, value=nil)
|
210
210
|
result = (namespace && !is_nested) ? "#{namespace}_" : ''
|
211
211
|
result << field_id_fragment
|
212
|
-
result << "_#{field}"
|
213
|
-
result << "_#{value}"
|
212
|
+
result << "_#{field}" if field
|
213
|
+
result << "_#{value}" if value
|
214
214
|
result
|
215
215
|
end
|
216
216
|
|
@@ -242,7 +242,7 @@ module Padrino
|
|
242
242
|
options = { :id => field_id(field), :selected => field_value(field) }.update(options)
|
243
243
|
options.update(error_class(field)){ |_,*values| values.compact.join(' ') }
|
244
244
|
selected_values = resolve_checked_values(field, options)
|
245
|
-
variants_for_group(options).inject(
|
245
|
+
variants_for_group(options).inject(SafeBuffer.new) do |html, (caption,value)|
|
246
246
|
variant_id = "#{options[:id]}_#{value}"
|
247
247
|
attributes = { :value => value, :id => variant_id, :checked => selected_values.include?(value) }
|
248
248
|
caption = yield(attributes) << ' ' << caption
|
@@ -308,7 +308,7 @@ module Padrino
|
|
308
308
|
|
309
309
|
def error_class(field)
|
310
310
|
error = @object.errors[field] if @object.respond_to?(:errors)
|
311
|
-
error.
|
311
|
+
error.nil? || error.empty? ? {} : { :class => 'invalid' }
|
312
312
|
end
|
313
313
|
|
314
314
|
def default_options(field, options, defaults={})
|
@@ -121,7 +121,7 @@ module Padrino
|
|
121
121
|
# hidden_form_method_field('delete')
|
122
122
|
#
|
123
123
|
def hidden_form_method_field(desired_method)
|
124
|
-
return
|
124
|
+
return SafeBuffer.new if desired_method.nil? || desired_method.to_s =~ /get|post/i
|
125
125
|
hidden_field_tag(:_method, :value => desired_method)
|
126
126
|
end
|
127
127
|
|
@@ -144,7 +144,7 @@ module Padrino
|
|
144
144
|
#
|
145
145
|
def field_set_tag(*args, &block)
|
146
146
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
147
|
-
legend_html = args.empty? ?
|
147
|
+
legend_html = args.empty? ? SafeBuffer.new : content_tag(:legend, args.first)
|
148
148
|
concat_content content_tag(:fieldset, legend_html << capture_html(&block), options)
|
149
149
|
end
|
150
150
|
|
@@ -168,7 +168,7 @@ module Padrino
|
|
168
168
|
#
|
169
169
|
def label_tag(name, options={}, &block)
|
170
170
|
options = { :caption => "#{name.to_s.humanize}: ", :for => name }.update(options)
|
171
|
-
caption_text =
|
171
|
+
caption_text = SafeBuffer.new << options.delete(:caption)
|
172
172
|
caption_text << "<span class='required'>*</span> ".html_safe if options.delete(:required)
|
173
173
|
|
174
174
|
if block_given?
|
@@ -37,10 +37,10 @@ module Padrino
|
|
37
37
|
# error_messages_for :user
|
38
38
|
#
|
39
39
|
def error_messages_for(*objects)
|
40
|
-
options = objects.last.is_a?(Hash) ? objects.pop
|
40
|
+
options = objects.last.is_a?(Hash) ? Utils.symbolize_keys(objects.pop) : {}
|
41
41
|
objects = objects.map{ |obj| resolve_object(obj) }.compact
|
42
42
|
count = objects.inject(0){ |sum, object| sum + object.errors.count }
|
43
|
-
return
|
43
|
+
return SafeBuffer.new if count.zero?
|
44
44
|
|
45
45
|
content_tag(:div, error_contents(objects, count, options), error_html_attributes(options))
|
46
46
|
end
|
@@ -78,7 +78,7 @@ module Padrino
|
|
78
78
|
# @api public
|
79
79
|
def error_message_on(object, field, options={})
|
80
80
|
error = Array(resolve_object(object).errors[field]).first
|
81
|
-
return
|
81
|
+
return SafeBuffer.new unless error
|
82
82
|
options = { :tag => :span, :class => :error }.update(options)
|
83
83
|
tag = options.delete(:tag)
|
84
84
|
error = [options.delete(:prepend), error, options.delete(:append)].compact.join(" ")
|
@@ -90,7 +90,7 @@ module Padrino
|
|
90
90
|
def error_contents(objects, count, options)
|
91
91
|
object_name = options[:object_name] || objects.first.class.to_s.underscore.gsub(/\//, ' ')
|
92
92
|
|
93
|
-
contents =
|
93
|
+
contents = SafeBuffer.new
|
94
94
|
contents << error_header_tag(options, object_name, count)
|
95
95
|
contents << error_body_tag(options)
|
96
96
|
contents << error_list_tag(objects, object_name)
|
@@ -98,7 +98,7 @@ module Padrino
|
|
98
98
|
|
99
99
|
def error_list_tag(objects, object_name)
|
100
100
|
errors = objects.inject({}){ |all,object| all.update(object.errors) }
|
101
|
-
error_messages = errors.inject(
|
101
|
+
error_messages = errors.inject(SafeBuffer.new) do |all, (field, message)|
|
102
102
|
field_name = I18n.t(field, :default => field.to_s.humanize, :scope => [:models, object_name, :attributes])
|
103
103
|
all << content_tag(:li, "#{field_name} #{message}")
|
104
104
|
end
|
@@ -110,19 +110,19 @@ module Padrino
|
|
110
110
|
model_name = I18n.t(:name, :default => object_name.humanize, :scope => [:models, object_name], :count => 1)
|
111
111
|
I18n.t :header, :count => count, :model => model_name, :locale => options[:locale], :scope => [:models, :errors, :template]
|
112
112
|
end
|
113
|
-
content_tag(options[:header_tag] || :h2, header_message)
|
113
|
+
content_tag(options[:header_tag] || :h2, header_message) unless header_message.empty?
|
114
114
|
end
|
115
115
|
|
116
116
|
def error_body_tag(options)
|
117
117
|
body_message = options[:message] || I18n.t(:body, :locale => options[:locale], :scope => [:models, :errors, :template])
|
118
|
-
content_tag(:p, body_message)
|
118
|
+
content_tag(:p, body_message) unless body_message.empty?
|
119
119
|
end
|
120
120
|
|
121
121
|
def error_html_attributes(options)
|
122
122
|
[:id, :class, :style].each_with_object({}) do |key,all|
|
123
123
|
if options.include?(key)
|
124
124
|
value = options[key]
|
125
|
-
all[key] = value
|
125
|
+
all[key] = value if value
|
126
126
|
else
|
127
127
|
all[key] = 'field-errors' unless key == :style
|
128
128
|
end
|
@@ -55,7 +55,7 @@ module Padrino
|
|
55
55
|
# Returns the options tags for a select based on the given option items.
|
56
56
|
#
|
57
57
|
def options_for_select(option_items, state = {})
|
58
|
-
return [] if option_items.
|
58
|
+
return [] if option_items.count == 0
|
59
59
|
option_items.map do |caption, value, attributes|
|
60
60
|
html_attributes = { :value => value || caption }.merge(attributes||{})
|
61
61
|
html_attributes[:selected] ||= option_is_selected?(html_attributes[:value], caption, state[:selected])
|
@@ -216,7 +216,7 @@ module Padrino
|
|
216
216
|
def highlight(text, words, *args)
|
217
217
|
options = { :highlighter => '<strong class="highlight">\1</strong>' }.update(args.last.is_a?(Hash) ? args.pop : {})
|
218
218
|
|
219
|
-
if text.
|
219
|
+
if text.empty? || words.empty?
|
220
220
|
text
|
221
221
|
else
|
222
222
|
match = Array(words).map { |p| Regexp.escape(p) }.join('|')
|
@@ -41,7 +41,7 @@ module Padrino
|
|
41
41
|
# # => 1234567890,50 £
|
42
42
|
#
|
43
43
|
def number_to_currency(number, options = {})
|
44
|
-
options.symbolize_keys
|
44
|
+
options = Utils.symbolize_keys(options)
|
45
45
|
|
46
46
|
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
|
47
47
|
currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :raise => true) rescue {}
|
@@ -89,7 +89,7 @@ module Padrino
|
|
89
89
|
# number_to_percentage(302.24398923423, :precision => 5) # => 302.24399%
|
90
90
|
#
|
91
91
|
def number_to_percentage(number, options = {})
|
92
|
-
options.symbolize_keys
|
92
|
+
options = Utils.symbolize_keys(options)
|
93
93
|
|
94
94
|
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
|
95
95
|
percentage = I18n.translate(:'number.percentage.format', :locale => options[:locale], :raise => true) rescue {}
|
@@ -134,8 +134,7 @@ module Padrino
|
|
134
134
|
# # => 98 765 432,98
|
135
135
|
#
|
136
136
|
def number_with_delimiter(number, *args)
|
137
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
138
|
-
options.symbolize_keys!
|
137
|
+
options = args.last.is_a?(Hash) ? Utils.symbolize_keys(args.pop) : {}
|
139
138
|
|
140
139
|
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
|
141
140
|
|
@@ -178,8 +177,7 @@ module Padrino
|
|
178
177
|
# # => 1.111,23
|
179
178
|
#
|
180
179
|
def number_with_precision(number, *args)
|
181
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
182
|
-
options.symbolize_keys!
|
180
|
+
options = args.last.is_a?(Hash) ? Utils.symbolize_keys(args.pop) : {}
|
183
181
|
|
184
182
|
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
|
185
183
|
precision_defaults = I18n.translate(:'number.precision.format', :locale => options[:locale],
|
@@ -239,8 +237,7 @@ module Padrino
|
|
239
237
|
def number_to_human_size(number, *args)
|
240
238
|
return nil if number.nil?
|
241
239
|
|
242
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
243
|
-
options.symbolize_keys!
|
240
|
+
options = args.last.is_a?(Hash) ? Utils.symbolize_keys(args.pop) : {}
|
244
241
|
|
245
242
|
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
|
246
243
|
human = I18n.translate(:'number.human.format', :locale => options[:locale], :raise => true) rescue {}
|
@@ -53,9 +53,9 @@ module Padrino
|
|
53
53
|
# capture_html(object_for_block, &block) => "...html..."
|
54
54
|
#
|
55
55
|
# @example
|
56
|
-
#
|
56
|
+
# SafeBuffer.new + capture_html { "<foo>" }
|
57
57
|
# # => "<foo>"
|
58
|
-
#
|
58
|
+
# SafeBuffer.new.safe_concat(capture_html { "<foo>" })
|
59
59
|
# # => "<foo>"
|
60
60
|
#
|
61
61
|
def capture_html(*args, &block)
|
@@ -181,7 +181,7 @@ module Padrino
|
|
181
181
|
def yield_content(key, *args)
|
182
182
|
blocks = content_blocks[key.to_sym]
|
183
183
|
return nil if blocks.empty?
|
184
|
-
blocks.inject(
|
184
|
+
blocks.inject(SafeBuffer.new){ |all,content| all << capture_html(*args, &content) }
|
185
185
|
end
|
186
186
|
|
187
187
|
protected
|
@@ -213,7 +213,7 @@ module Padrino
|
|
213
213
|
#
|
214
214
|
# @param [String, Array<String>] the values to be marked safe.
|
215
215
|
#
|
216
|
-
# @return [
|
216
|
+
# @return [SafeBuffer, Array<SafeBuffer>]
|
217
217
|
def mark_safe(value)
|
218
218
|
if value.respond_to? :map!
|
219
219
|
value.map!{|v| v.html_safe if v }
|
@@ -29,7 +29,7 @@ module Padrino
|
|
29
29
|
# @handler.capture_from_template(&block) => "...html..."
|
30
30
|
#
|
31
31
|
def capture_from_template(*args, &block)
|
32
|
-
self.output_buffer, _buf_was =
|
32
|
+
self.output_buffer, _buf_was = SafeBuffer.new, self.output_buffer
|
33
33
|
raw = yield(*args)
|
34
34
|
captured = template.instance_variable_get(:@_out_buf)
|
35
35
|
self.output_buffer = _buf_was
|
@@ -34,8 +34,8 @@ module Padrino
|
|
34
34
|
options = { :locals => {}, :layout => false }.update(options)
|
35
35
|
explicit_engine = options.delete(:engine)
|
36
36
|
|
37
|
-
path, name =
|
38
|
-
template_path =
|
37
|
+
path, _, name = template.to_s.rpartition(File::SEPARATOR)
|
38
|
+
template_path = path.empty? ? :"_#{name}" : :"#{path}#{File::SEPARATOR}_#{name}"
|
39
39
|
item_name = name.partition('.').first.to_sym
|
40
40
|
|
41
41
|
items, counter = if options[:collection].respond_to?(:inject)
|
@@ -45,7 +45,7 @@ module Padrino
|
|
45
45
|
end
|
46
46
|
|
47
47
|
locals = options[:locals]
|
48
|
-
items.each_with_object(
|
48
|
+
items.each_with_object(SafeBuffer.new) do |item,html|
|
49
49
|
locals[item_name] = item if item
|
50
50
|
locals["#{item_name}_counter".to_sym] = counter += 1 if counter
|
51
51
|
content =
|
@@ -124,7 +124,7 @@ module Padrino
|
|
124
124
|
|
125
125
|
options = parse_data_options(name, options)
|
126
126
|
attributes = tag_attributes(options)
|
127
|
-
output =
|
127
|
+
output = SafeBuffer.new
|
128
128
|
output.safe_concat "<#{name}#{attributes}>"
|
129
129
|
if content.respond_to?(:each) && !content.is_a?(String)
|
130
130
|
content.each{ |item| output.concat item; output.safe_concat NEWLINE }
|
@@ -0,0 +1,25 @@
|
|
1
|
+
if defined?(ActiveSupport::SafeBuffer)
|
2
|
+
SafeBuffer = ActiveSupport::SafeBuffer
|
3
|
+
else
|
4
|
+
require 'padrino/safe_buffer'
|
5
|
+
|
6
|
+
SafeBuffer = Padrino::SafeBuffer
|
7
|
+
|
8
|
+
class String
|
9
|
+
def html_safe
|
10
|
+
SafeBuffer.new(self)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Object
|
15
|
+
def html_safe?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Numeric
|
21
|
+
def html_safe?
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/padrino/rendering.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'padrino/core_ext/output_safety'
|
1
2
|
require 'padrino-support'
|
2
3
|
require 'pathname'
|
3
4
|
|
@@ -206,7 +207,7 @@ module Padrino
|
|
206
207
|
|
207
208
|
# Cleanup the template.
|
208
209
|
@current_engine, engine_was = engine, @current_engine
|
209
|
-
@_out_buf, buf_was =
|
210
|
+
@_out_buf, buf_was = SafeBuffer.new, @_out_buf
|
210
211
|
|
211
212
|
# Pass arguments to Sinatra render method.
|
212
213
|
super(engine, data, with_layout(options), locals, &block)
|
@@ -276,7 +277,7 @@ module Padrino
|
|
276
277
|
template_candidates = glob_templates(layouts_path, template_path)
|
277
278
|
selected_template = select_template(template_candidates, *rendering_options)
|
278
279
|
|
279
|
-
fail TemplateNotFound, "Layout '#{template_path}' not found in '#{layouts_path}'" if !selected_template && layout
|
280
|
+
fail TemplateNotFound, "Layout '#{template_path}' not found in '#{layouts_path}'" if !selected_template && layout
|
280
281
|
selected_template
|
281
282
|
end
|
282
283
|
end
|
@@ -287,7 +288,7 @@ module Padrino
|
|
287
288
|
return options if layout == false
|
288
289
|
|
289
290
|
layout = @layout if !layout || layout == true
|
290
|
-
return options if settings.templates.has_key?(:layout) && layout
|
291
|
+
return options if settings.templates.has_key?(:layout) && !layout
|
291
292
|
|
292
293
|
if layout.kind_of?(String) && Pathname.new(layout).absolute?
|
293
294
|
layout_path, _, layout = layout.rpartition('/')
|
@@ -300,8 +301,8 @@ module Padrino
|
|
300
301
|
|
301
302
|
def glob_templates(views_path, template_path)
|
302
303
|
parts = []
|
303
|
-
parts << views_path if views_path
|
304
|
-
if respond_to?(:request) && request.respond_to?(:controller) && request.controller
|
304
|
+
parts << views_path if views_path
|
305
|
+
if respond_to?(:request) && request.respond_to?(:controller) && request.controller && Pathname.new(template_path).relative?
|
305
306
|
parts << "{,#{request.controller}}"
|
306
307
|
end
|
307
308
|
parts << template_path.chomp(File.extname(template_path)) + '.*'
|
@@ -16,7 +16,7 @@ module Padrino
|
|
16
16
|
def set_eoutvar(compiler, eoutvar = '_erbout')
|
17
17
|
compiler.put_cmd = "#{eoutvar}.safe_concat"
|
18
18
|
compiler.insert_cmd = "#{eoutvar}.concat"
|
19
|
-
compiler.pre_cmd = ["#{eoutvar} =
|
19
|
+
compiler.pre_cmd = ["#{eoutvar} = SafeBuffer.new"]
|
20
20
|
compiler.post_cmd = ["#{eoutvar}.force_encoding(__ENCODING__)"]
|
21
21
|
end
|
22
22
|
end
|
@@ -2,7 +2,7 @@ module Padrino
|
|
2
2
|
module Rendering
|
3
3
|
##
|
4
4
|
# SafeBufferEnhancer is an Erubis Enhancer that compiles templates that
|
5
|
-
# are fit for using
|
5
|
+
# are fit for using SafeBuffer as a Buffer.
|
6
6
|
#
|
7
7
|
# @api private
|
8
8
|
module SafeBufferEnhancer
|
@@ -53,7 +53,7 @@ module Padrino
|
|
53
53
|
def precompiled_preamble(locals)
|
54
54
|
original = super
|
55
55
|
return original unless @is_padrino_app
|
56
|
-
"__in_erb_template = true\n" << original.rpartition("\n").first << "#{@outvar} = _buf =
|
56
|
+
"__in_erb_template = true\n" << original.rpartition("\n").first << "#{@outvar} = _buf = SafeBuffer.new\n"
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
@@ -1,5 +1,21 @@
|
|
1
1
|
module Padrino
|
2
2
|
module Rendering
|
3
|
+
class SlimOutputBuffer < Temple::Generators::StringBuffer
|
4
|
+
define_options :buffer_class => 'SafeBuffer'
|
5
|
+
|
6
|
+
def call(exp)
|
7
|
+
[preamble, compile(exp), postamble].flatten.compact.join('; '.freeze)
|
8
|
+
end
|
9
|
+
|
10
|
+
def create_buffer
|
11
|
+
"#{buffer} = #{options[:buffer_class]}.new"
|
12
|
+
end
|
13
|
+
|
14
|
+
def concat(str)
|
15
|
+
"#{buffer}.safe_concat((#{str}))"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
3
19
|
class SlimTemplate < Slim::Template
|
4
20
|
include SafeTemplate
|
5
21
|
|
@@ -13,7 +29,7 @@ end
|
|
13
29
|
Tilt.prefer(Padrino::Rendering::SlimTemplate, :slim)
|
14
30
|
|
15
31
|
Padrino::Rendering.engine_configurations[:slim] = {
|
16
|
-
:generator =>
|
32
|
+
:generator => Padrino::Rendering::SlimOutputBuffer,
|
17
33
|
:buffer => "@_out_buf",
|
18
34
|
:use_html_safe => true,
|
19
35
|
:disable_capture => true,
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Padrino
|
2
|
+
##
|
3
|
+
# Padrino::SafeBuffer is based on ActiveSupport::SafeBuffer
|
4
|
+
#
|
5
|
+
class SafeBuffer < String
|
6
|
+
UNSAFE_STRING_METHODS = %w(
|
7
|
+
capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
|
8
|
+
slice squeeze strip sub succ swapcase tr tr_s upcase
|
9
|
+
)
|
10
|
+
|
11
|
+
alias_method :original_concat, :concat
|
12
|
+
private :original_concat
|
13
|
+
|
14
|
+
class SafeConcatError < StandardError
|
15
|
+
def initialize
|
16
|
+
super "Could not concatenate to the buffer because it is not html safe."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](*args)
|
21
|
+
if args.size < 2
|
22
|
+
super
|
23
|
+
else
|
24
|
+
if html_safe?
|
25
|
+
new_safe_buffer = super
|
26
|
+
|
27
|
+
if new_safe_buffer
|
28
|
+
new_safe_buffer.instance_variable_set :@html_safe, true
|
29
|
+
end
|
30
|
+
|
31
|
+
new_safe_buffer
|
32
|
+
else
|
33
|
+
to_str[*args]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def safe_concat(value)
|
39
|
+
raise SafeConcatError unless html_safe?
|
40
|
+
original_concat(value)
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(str = "")
|
44
|
+
@html_safe = true
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize_copy(other)
|
49
|
+
super
|
50
|
+
@html_safe = other.html_safe?
|
51
|
+
end
|
52
|
+
|
53
|
+
def clone_empty
|
54
|
+
self[0, 0]
|
55
|
+
end
|
56
|
+
|
57
|
+
def concat(value)
|
58
|
+
super(html_escape_interpolated_argument(value))
|
59
|
+
end
|
60
|
+
alias << concat
|
61
|
+
|
62
|
+
def prepend(value)
|
63
|
+
super(html_escape_interpolated_argument(value))
|
64
|
+
end
|
65
|
+
|
66
|
+
def +(other)
|
67
|
+
dup.concat(other)
|
68
|
+
end
|
69
|
+
|
70
|
+
def %(args)
|
71
|
+
case args
|
72
|
+
when Hash
|
73
|
+
escaped_args = Hash[args.map { |k,arg| [k, html_escape_interpolated_argument(arg)] }]
|
74
|
+
else
|
75
|
+
escaped_args = Array(args).map { |arg| html_escape_interpolated_argument(arg) }
|
76
|
+
end
|
77
|
+
|
78
|
+
self.class.new(super(escaped_args))
|
79
|
+
end
|
80
|
+
|
81
|
+
def html_safe?
|
82
|
+
defined?(@html_safe) && @html_safe
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_param
|
90
|
+
to_str
|
91
|
+
end
|
92
|
+
|
93
|
+
def encode_with(coder)
|
94
|
+
coder.represent_object nil, to_str
|
95
|
+
end
|
96
|
+
|
97
|
+
UNSAFE_STRING_METHODS.each do |unsafe_method|
|
98
|
+
if unsafe_method.respond_to?(unsafe_method)
|
99
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
100
|
+
def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
|
101
|
+
to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
|
102
|
+
end # end
|
103
|
+
|
104
|
+
def #{unsafe_method}!(*args) # def capitalize!(*args)
|
105
|
+
@html_safe = false # @html_safe = false
|
106
|
+
super # super
|
107
|
+
end # end
|
108
|
+
EOT
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def html_escape_interpolated_argument(arg)
|
115
|
+
(!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/test/helper.rb
CHANGED
@@ -44,8 +44,8 @@ class MiniTest::Spec
|
|
44
44
|
FileUtils.mkdir_p(File.dirname(__FILE__) + "/views")
|
45
45
|
FileUtils.mkdir_p(File.dirname(__FILE__) + "/views/layouts")
|
46
46
|
path = "/views/#{name}"
|
47
|
-
path += ".#{options.delete(:locale)}" if options[:locale]
|
48
|
-
path += ".#{options[:format]}" if options[:format]
|
47
|
+
path += ".#{options.delete(:locale)}" if options[:locale]
|
48
|
+
path += ".#{options[:format]}" if options[:format]
|
49
49
|
path += ".erb" unless options[:format].to_s =~ /erb|slim|haml|rss|atom|builder|liquid/
|
50
50
|
path += ".builder" if options[:format].to_s =~ /rss|atom/
|
51
51
|
file = File.dirname(__FILE__) + path
|
data/test/test_format_helpers.rb
CHANGED
@@ -209,23 +209,23 @@ describe "FormatHelpers" do
|
|
209
209
|
describe 'for #js_escape_html method' do
|
210
210
|
it 'should escape double quotes' do
|
211
211
|
assert_equal "\\\"hello\\\"", js_escape_html('"hello"')
|
212
|
-
assert_equal "\\\"hello\\\"", js_escape_html(
|
212
|
+
assert_equal "\\\"hello\\\"", js_escape_html(SafeBuffer.new('"hello"'))
|
213
213
|
end
|
214
214
|
it 'should escape single quotes' do
|
215
215
|
assert_equal "\\'hello\\'", js_escape_html("'hello'")
|
216
|
-
assert_equal "\\'hello\\'", js_escape_html(
|
216
|
+
assert_equal "\\'hello\\'", js_escape_html(SafeBuffer.new("'hello'"))
|
217
217
|
end
|
218
218
|
it 'should escape html tags and breaks' do
|
219
219
|
assert_equal "\\n\\n<p>hello<\\/p>\\n", js_escape_html("\n\r<p>hello</p>\r\n")
|
220
|
-
assert_equal "\\n\\n<p>hello<\\/p>\\n", js_escape_html(
|
220
|
+
assert_equal "\\n\\n<p>hello<\\/p>\\n", js_escape_html(SafeBuffer.new("\n\r<p>hello</p>\r\n"))
|
221
221
|
end
|
222
222
|
it 'should escape data-confirm attribute' do
|
223
223
|
assert_equal "<data-confirm=\\\"are you sure\\\">", js_escape_html("<data-confirm=\"are you sure\">")
|
224
|
-
assert_equal "<data-confirm=\\\"are you sure\\\">", js_escape_html(
|
224
|
+
assert_equal "<data-confirm=\\\"are you sure\\\">", js_escape_html(SafeBuffer.new("<data-confirm=\"are you sure\">"))
|
225
225
|
end
|
226
226
|
it 'should keep html_safe content html_safe' do
|
227
227
|
assert_equal false, js_escape_html('"hello"').html_safe?
|
228
|
-
assert_equal true, js_escape_html(
|
228
|
+
assert_equal true, js_escape_html(SafeBuffer.new('"hello"')).html_safe?
|
229
229
|
end
|
230
230
|
end
|
231
231
|
end
|
data/test/test_render_helpers.rb
CHANGED
@@ -253,5 +253,15 @@ describe "RenderHelpers" do
|
|
253
253
|
end
|
254
254
|
assert_equal 'existing', Standalone3.new.partial('none')
|
255
255
|
end
|
256
|
+
|
257
|
+
it 'should not add "./" to partial template name' do
|
258
|
+
class Standalone4
|
259
|
+
def render(_, file, *)
|
260
|
+
file.to_s
|
261
|
+
end
|
262
|
+
include Padrino::Helpers::RenderHelpers
|
263
|
+
end
|
264
|
+
assert_equal '_none', Standalone4.new.partial('none')
|
265
|
+
end
|
256
266
|
end
|
257
267
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: padrino-helpers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.13.3
|
4
|
+
version: 0.13.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Padrino Team
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2016-08-
|
14
|
+
date: 2016-08-30 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: padrino-support
|
@@ -19,14 +19,14 @@ dependencies:
|
|
19
19
|
requirements:
|
20
20
|
- - '='
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.13.3
|
22
|
+
version: 0.13.3.1
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - '='
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0.13.3
|
29
|
+
version: 0.13.3.1
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: tilt
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -120,11 +120,13 @@ files:
|
|
120
120
|
- lib/padrino-helpers/render_helpers.rb
|
121
121
|
- lib/padrino-helpers/tag_helpers.rb
|
122
122
|
- lib/padrino-helpers/translation_helpers.rb
|
123
|
+
- lib/padrino/core_ext/output_safety.rb
|
123
124
|
- lib/padrino/rendering.rb
|
124
125
|
- lib/padrino/rendering/erb_template.rb
|
125
126
|
- lib/padrino/rendering/erubis_template.rb
|
126
127
|
- lib/padrino/rendering/haml_template.rb
|
127
128
|
- lib/padrino/rendering/slim_template.rb
|
129
|
+
- lib/padrino/safe_buffer.rb
|
128
130
|
- padrino-helpers.gemspec
|
129
131
|
- test/fixtures/apps/.components
|
130
132
|
- test/fixtures/apps/.gitignore
|
@@ -245,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
245
247
|
version: 1.3.6
|
246
248
|
requirements: []
|
247
249
|
rubyforge_project: padrino-helpers
|
248
|
-
rubygems_version: 2.
|
250
|
+
rubygems_version: 2.5.1
|
249
251
|
signing_key:
|
250
252
|
specification_version: 4
|
251
253
|
summary: Helpers for padrino
|