tennpipes-helper 3.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.rdoc +239 -0
  4. data/Rakefile +1 -0
  5. data/lib/tennpipes-helper.rb +62 -0
  6. data/lib/tennpipes-helper/asset_tag_helpers.rb +394 -0
  7. data/lib/tennpipes-helper/form_builder/abstract_form_builder.rb +279 -0
  8. data/lib/tennpipes-helper/form_builder/standard_form_builder.rb +40 -0
  9. data/lib/tennpipes-helper/form_helpers.rb +639 -0
  10. data/lib/tennpipes-helper/form_helpers/errors.rb +138 -0
  11. data/lib/tennpipes-helper/form_helpers/options.rb +98 -0
  12. data/lib/tennpipes-helper/form_helpers/security.rb +70 -0
  13. data/lib/tennpipes-helper/format_helpers.rb +372 -0
  14. data/lib/tennpipes-helper/locale/cs.yml +103 -0
  15. data/lib/tennpipes-helper/locale/da.yml +91 -0
  16. data/lib/tennpipes-helper/locale/de.yml +81 -0
  17. data/lib/tennpipes-helper/locale/en.yml +103 -0
  18. data/lib/tennpipes-helper/locale/es.yml +103 -0
  19. data/lib/tennpipes-helper/locale/fr.yml +79 -0
  20. data/lib/tennpipes-helper/locale/hu.yml +103 -0
  21. data/lib/tennpipes-helper/locale/it.yml +89 -0
  22. data/lib/tennpipes-helper/locale/ja.yml +103 -0
  23. data/lib/tennpipes-helper/locale/lv.yml +103 -0
  24. data/lib/tennpipes-helper/locale/nl.yml +82 -0
  25. data/lib/tennpipes-helper/locale/no.yml +91 -0
  26. data/lib/tennpipes-helper/locale/pl.yml +95 -0
  27. data/lib/tennpipes-helper/locale/pt_br.yml +103 -0
  28. data/lib/tennpipes-helper/locale/ro.yml +103 -0
  29. data/lib/tennpipes-helper/locale/ru.yml +103 -0
  30. data/lib/tennpipes-helper/locale/sv.yml +103 -0
  31. data/lib/tennpipes-helper/locale/tr.yml +103 -0
  32. data/lib/tennpipes-helper/locale/uk.yml +103 -0
  33. data/lib/tennpipes-helper/locale/zh_cn.yml +103 -0
  34. data/lib/tennpipes-helper/locale/zh_tw.yml +103 -0
  35. data/lib/tennpipes-helper/number_helpers.rb +283 -0
  36. data/lib/tennpipes-helper/output_helpers.rb +226 -0
  37. data/lib/tennpipes-helper/output_helpers/abstract_handler.rb +61 -0
  38. data/lib/tennpipes-helper/output_helpers/erb_handler.rb +27 -0
  39. data/lib/tennpipes-helper/output_helpers/haml_handler.rb +25 -0
  40. data/lib/tennpipes-helper/output_helpers/slim_handler.rb +18 -0
  41. data/lib/tennpipes-helper/render_helpers.rb +63 -0
  42. data/lib/tennpipes-helper/tag_helpers.rb +294 -0
  43. data/lib/tennpipes-helper/translation_helpers.rb +36 -0
  44. data/lib/tennpipes/rendering.rb +369 -0
  45. data/lib/tennpipes/rendering/erb_template.rb +40 -0
  46. data/lib/tennpipes/rendering/erubis_template.rb +66 -0
  47. data/lib/tennpipes/rendering/haml_template.rb +26 -0
  48. data/lib/tennpipes/rendering/slim_template.rb +20 -0
  49. data/test/fixtures/apps/render.rb +25 -0
  50. data/test/fixtures/apps/views/article/comment/show.slim +1 -0
  51. data/test/fixtures/apps/views/blog/post.erb +1 -0
  52. data/test/fixtures/apps/views/layouts/specific.erb +1 -0
  53. data/test/fixtures/apps/views/test/post.erb +1 -0
  54. data/test/fixtures/layouts/layout.erb +1 -0
  55. data/test/fixtures/markup_app/app.rb +87 -0
  56. data/test/fixtures/markup_app/views/button_to.erb +8 -0
  57. data/test/fixtures/markup_app/views/button_to.haml +5 -0
  58. data/test/fixtures/markup_app/views/button_to.slim +6 -0
  59. data/test/fixtures/markup_app/views/capture_concat.erb +14 -0
  60. data/test/fixtures/markup_app/views/capture_concat.haml +12 -0
  61. data/test/fixtures/markup_app/views/capture_concat.slim +12 -0
  62. data/test/fixtures/markup_app/views/content_for.erb +23 -0
  63. data/test/fixtures/markup_app/views/content_for.haml +19 -0
  64. data/test/fixtures/markup_app/views/content_for.slim +19 -0
  65. data/test/fixtures/markup_app/views/content_tag.erb +13 -0
  66. data/test/fixtures/markup_app/views/content_tag.haml +11 -0
  67. data/test/fixtures/markup_app/views/content_tag.slim +11 -0
  68. data/test/fixtures/markup_app/views/current_engine.erb +5 -0
  69. data/test/fixtures/markup_app/views/current_engine.haml +5 -0
  70. data/test/fixtures/markup_app/views/current_engine.slim +5 -0
  71. data/test/fixtures/markup_app/views/fields_for.erb +20 -0
  72. data/test/fixtures/markup_app/views/fields_for.haml +15 -0
  73. data/test/fixtures/markup_app/views/fields_for.slim +15 -0
  74. data/test/fixtures/markup_app/views/form_for.erb +72 -0
  75. data/test/fixtures/markup_app/views/form_for.haml +59 -0
  76. data/test/fixtures/markup_app/views/form_for.slim +59 -0
  77. data/test/fixtures/markup_app/views/form_tag.erb +95 -0
  78. data/test/fixtures/markup_app/views/form_tag.haml +78 -0
  79. data/test/fixtures/markup_app/views/form_tag.slim +79 -0
  80. data/test/fixtures/markup_app/views/link_to.erb +5 -0
  81. data/test/fixtures/markup_app/views/link_to.haml +4 -0
  82. data/test/fixtures/markup_app/views/link_to.slim +4 -0
  83. data/test/fixtures/markup_app/views/mail_to.erb +3 -0
  84. data/test/fixtures/markup_app/views/mail_to.haml +3 -0
  85. data/test/fixtures/markup_app/views/mail_to.slim +3 -0
  86. data/test/fixtures/markup_app/views/meta_tag.erb +3 -0
  87. data/test/fixtures/markup_app/views/meta_tag.haml +3 -0
  88. data/test/fixtures/markup_app/views/meta_tag.slim +3 -0
  89. data/test/fixtures/markup_app/views/partials/_erb.erb +1 -0
  90. data/test/fixtures/markup_app/views/partials/_haml.haml +1 -0
  91. data/test/fixtures/markup_app/views/partials/_slim.slim +1 -0
  92. data/test/fixtures/markup_app/views/simple_partial.erb +1 -0
  93. data/test/fixtures/markup_app/views/simple_partial.haml +1 -0
  94. data/test/fixtures/markup_app/views/simple_partial.slim +1 -0
  95. data/test/fixtures/render_app/app.rb +110 -0
  96. data/test/fixtures/render_app/views/_deep.erb +3 -0
  97. data/test/fixtures/render_app/views/_deep.haml +2 -0
  98. data/test/fixtures/render_app/views/_deep.slim +2 -0
  99. data/test/fixtures/render_app/views/_partial_block_erb.erb +10 -0
  100. data/test/fixtures/render_app/views/_partial_block_haml.haml +7 -0
  101. data/test/fixtures/render_app/views/_partial_block_slim.slim +7 -0
  102. data/test/fixtures/render_app/views/_unsafe.html.builder +2 -0
  103. data/test/fixtures/render_app/views/_unsafe_object.html.builder +2 -0
  104. data/test/fixtures/render_app/views/current_engine.haml +5 -0
  105. data/test/fixtures/render_app/views/current_engines/_erb.erb +1 -0
  106. data/test/fixtures/render_app/views/current_engines/_haml.haml +1 -0
  107. data/test/fixtures/render_app/views/current_engines/_slim.slim +1 -0
  108. data/test/fixtures/render_app/views/dive_inner_erb.erb +3 -0
  109. data/test/fixtures/render_app/views/dive_inner_haml.haml +2 -0
  110. data/test/fixtures/render_app/views/dive_inner_slim.slim +2 -0
  111. data/test/fixtures/render_app/views/dive_outer_erb.erb +3 -0
  112. data/test/fixtures/render_app/views/dive_outer_haml.haml +2 -0
  113. data/test/fixtures/render_app/views/dive_outer_slim.slim +2 -0
  114. data/test/fixtures/render_app/views/double_capture_erb.erb +3 -0
  115. data/test/fixtures/render_app/views/double_capture_haml.haml +2 -0
  116. data/test/fixtures/render_app/views/double_capture_slim.slim +2 -0
  117. data/test/fixtures/render_app/views/erb/test.erb +1 -0
  118. data/test/fixtures/render_app/views/explicit_engine.haml +5 -0
  119. data/test/fixtures/render_app/views/haml/test.haml +1 -0
  120. data/test/fixtures/render_app/views/render_block_erb.erb +5 -0
  121. data/test/fixtures/render_app/views/render_block_haml.haml +4 -0
  122. data/test/fixtures/render_app/views/render_block_slim.slim +4 -0
  123. data/test/fixtures/render_app/views/ruby_block_capture_erb.erb +1 -0
  124. data/test/fixtures/render_app/views/ruby_block_capture_haml.haml +1 -0
  125. data/test/fixtures/render_app/views/ruby_block_capture_slim.slim +1 -0
  126. data/test/fixtures/render_app/views/template/_user.haml +7 -0
  127. data/test/fixtures/render_app/views/template/haml_template.haml +1 -0
  128. data/test/fixtures/render_app/views/template/some_template.haml +2 -0
  129. data/test/fixtures/render_app/views/wrong_capture_erb.erb +3 -0
  130. data/test/fixtures/render_app/views/wrong_capture_haml.haml +2 -0
  131. data/test/fixtures/render_app/views/wrong_capture_slim.slim +2 -0
  132. data/test/helper.rb +88 -0
  133. data/test/test_asset_tag_helpers.rb +401 -0
  134. data/test/test_form_builder.rb +1216 -0
  135. data/test/test_form_helpers.rb +1056 -0
  136. data/test/test_format_helpers.rb +251 -0
  137. data/test/test_helpers.rb +10 -0
  138. data/test/test_locale.rb +20 -0
  139. data/test/test_number_helpers.rb +142 -0
  140. data/test/test_output_helpers.rb +157 -0
  141. data/test/test_render_helpers.rb +225 -0
  142. data/test/test_rendering.rb +706 -0
  143. data/test/test_rendering_extensions.rb +14 -0
  144. data/test/test_tag_helpers.rb +131 -0
  145. metadata +299 -0
@@ -0,0 +1,36 @@
1
+ module Tennpipes
2
+ module Helpers
3
+ ##
4
+ # Helpers related to locale i18n translation within templates.
5
+ #
6
+ module TranslationHelpers
7
+ ##
8
+ # Delegates to I18n.translate with no additional functionality.
9
+ #
10
+ # @param [Symbol] *args
11
+ # The keys to retrieve.
12
+ #
13
+ # @return [String]
14
+ # The translation for the specified keys.
15
+ #
16
+ def translate(*args)
17
+ I18n.translate(*args)
18
+ end
19
+ alias :t :translate
20
+
21
+ ##
22
+ # Delegates to I18n.localize with no additional functionality.
23
+ #
24
+ # @param [Symbol] *args
25
+ # The keys to retrieve.
26
+ #
27
+ # @return [String]
28
+ # The translation for the specified keys.
29
+ #
30
+ def localize(*args)
31
+ I18n.localize(*args)
32
+ end
33
+ alias :l :localize
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,369 @@
1
+ require 'tennpipes-assist'
2
+ require 'pathname'
3
+
4
+ module Tennpipes
5
+ ##
6
+ # Tennpipes enhances the Sinatra 'render' method to have support for
7
+ # automatic template engine detection, enhanced layout functionality,
8
+ # locale enabled rendering, among other features.
9
+ #
10
+ module Rendering
11
+ ##
12
+ # A SafeTemplate assumes that its output is safe.
13
+ #
14
+ module SafeTemplate
15
+ def render(*)
16
+ super.html_safe
17
+ end
18
+ end
19
+
20
+ ##
21
+ # Exception responsible for when an expected template did not exist.
22
+ #
23
+ class TemplateNotFound < RuntimeError
24
+ end
25
+
26
+ ##
27
+ # This is an array of file patterns to ignore. If your editor add a
28
+ # suffix during editing to your files please add it like:
29
+ #
30
+ # @example
31
+ # Tennpipes::Rendering::IGNORE_FILE_PATTERN << /~$/
32
+ #
33
+ IGNORE_FILE_PATTERN = [
34
+ /~$/ # This is for Gedit
35
+ ] unless defined?(IGNORE_FILE_PATTERN)
36
+
37
+ ##
38
+ # Default options used in the resolve_template-method.
39
+ #
40
+ DEFAULT_RENDERING_OPTIONS = { :strict_format => false, :raise_exceptions => true } unless defined?(DEFAULT_RENDERING_OPTIONS)
41
+
42
+ class << self
43
+ ##
44
+ # Default engine configurations for Tennpipes::Rendering.
45
+ #
46
+ # @return {Hash<Symbol,Hash>}
47
+ # The configurations, keyed by engine.
48
+ def engine_configurations
49
+ @engine_configurations ||= {}
50
+ end
51
+
52
+ def registered(app)
53
+ if defined?(Tennpipes::Application) && app == Tennpipes::Application
54
+ # this fail can be removed later when jRuby is not bugged and MRI19 is dropped
55
+ fail 'Please, do not use `register` on Tennpipes::Application object, use `.dup` or subclassing'
56
+ end
57
+ included(app)
58
+ engine_configurations.each do |engine, configs|
59
+ app.set engine, configs
60
+ end
61
+ end
62
+
63
+ def included(base)
64
+ base.send(:include, InstanceMethods)
65
+ base.extend(ClassMethods)
66
+ end
67
+ end
68
+
69
+ ##
70
+ # Class methods responsible for rendering templates as part of a request.
71
+ #
72
+ module ClassMethods
73
+ ##
74
+ # Use layout like rails does or if a block given then like sinatra.
75
+ # If used without a block, sets the current layout for the route.
76
+ #
77
+ # By default, searches in your:
78
+ #
79
+ # +app+/+views+/+layouts+/+application+.(+haml+|+erb+|+xxx+)
80
+ # +app+/+views+/+layout_name+.(+haml+|+erb+|+xxx+)
81
+ #
82
+ # If you define +layout+ :+custom+ then searches for your layouts in
83
+ # +app+/+views+/+layouts+/+custom+.(+haml+|+erb+|+xxx+)
84
+ # +app+/+views+/+custom+.(+haml+|+erb+|+xxx+)
85
+ #
86
+ # @param [Symbol] name (:layout)
87
+ # The layout to use.
88
+ #
89
+ # @yield []
90
+ #
91
+ def layout(name=:layout, &block)
92
+ return super(name, &block) if block_given?
93
+ @layout = name
94
+ end
95
+
96
+ ##
97
+ # Returns the cached layout path.
98
+ #
99
+ # @param [String, nil] given_layout
100
+ # The requested layout.
101
+ # @param [String, nil] layouts_path
102
+ # The directory where the layouts are located. Defaults to #views.
103
+ #
104
+ def fetch_layout_path(given_layout, layouts_path=views)
105
+ layout_name = (given_layout || @layout || :application).to_s
106
+ cache_layout_path(layout_name) do
107
+ if Pathname.new(layout_name).absolute? && Dir["#{layout_name}.*"].any? || Dir["#{layouts_path}/#{layout_name}.*"].any?
108
+ layout_name
109
+ else
110
+ File.join('layouts', layout_name)
111
+ end
112
+ end
113
+ end
114
+
115
+ def cache_layout_path(name)
116
+ @_cached_layout ||= {}
117
+ if !reload_templates? && path = @_cached_layout[name]
118
+ path
119
+ else
120
+ @_cached_layout[name] = yield(name)
121
+ end
122
+ end
123
+
124
+ def cache_template_path(options)
125
+ began_at = Time.now
126
+ @_cached_templates ||= {}
127
+ logging = defined?(settings) && settings.logging? && defined?(logger)
128
+ if !reload_templates? && path = @_cached_templates[options]
129
+ logger.debug :cached, began_at, path[0] if logging
130
+ else
131
+ path = @_cached_templates[options] = yield(name)
132
+ logger.debug :template, began_at, path[0] if path && logging
133
+ end
134
+ path
135
+ end
136
+ end
137
+
138
+ # Instance methods that allow enhanced rendering to function properly in Tennpipes.
139
+ module InstanceMethods
140
+ attr_reader :current_engine
141
+
142
+ ##
143
+ # Get/Set the content_type
144
+ #
145
+ # @param [String, nil] type
146
+ # The Content-Type to use.
147
+ #
148
+ # @param [Symbol, nil] type.
149
+ # Look and parse the given symbol to the matched Content-Type.
150
+ #
151
+ # @param [Hash] params
152
+ # Additional params to append to the Content-Type.
153
+ #
154
+ # @example
155
+ # case content_type
156
+ # when :js then do_some
157
+ # when :css then do_another
158
+ # end
159
+ #
160
+ # content_type :js
161
+ # # => set the response with 'application/javascript' Content-Type
162
+ # content_type 'text/html'
163
+ #
164
+ # # => set directly the Content-Type to 'text/html'
165
+ #
166
+ def content_type(type=nil, params={})
167
+ if type
168
+ super(type, params)
169
+ @_content_type = type
170
+ end
171
+ @_content_type
172
+ end
173
+
174
+ private
175
+
176
+ ##
177
+ # Enhancing Sinatra render functionality for:
178
+ #
179
+ # * Using layout similar to rails
180
+ # * Use render 'path/to/my/template' (without symbols)
181
+ # * Use render 'path/to/my/template' (with engine lookup)
182
+ # * Use render 'path/to/template.haml' (with explicit engine lookup)
183
+ # * Use render 'path/to/template', :layout => false
184
+ # * Use render 'path/to/template', :layout => false, :engine => 'haml'
185
+ #
186
+ def render(engine, data=nil, options={}, locals={}, &block)
187
+ # If engine is nil, ignore engine parameter and shift up all arguments
188
+ # render nil, "index", { :layout => true }, { :localvar => "foo" }
189
+ engine, data, options = data, options, locals if engine.nil? && data
190
+
191
+ # Data is a hash of options when no engine isn't explicit
192
+ # render "index", { :layout => true }, { :localvar => "foo" }
193
+ # Data is options, and options is locals in this case
194
+ data, options, locals = nil, data, options if data.is_a?(Hash)
195
+
196
+ # If data is unassigned then this is a likely a template to be resolved
197
+ # This means that no engine was explicitly defined
198
+ data, engine = resolve_template(engine, options) if data.nil?
199
+
200
+ ensure_rendering_engine(engine) || (options[:layout] ||= false)
201
+
202
+ # Cleanup the template.
203
+ @current_engine, engine_was = engine, @current_engine
204
+ @_out_buf, buf_was = ActiveSupport::SafeBuffer.new, @_out_buf
205
+
206
+ # Pass arguments to Sinatra render method.
207
+ super(engine, data, with_layout(options), locals, &block)
208
+ ensure
209
+ @current_engine = engine_was
210
+ @_out_buf = buf_was
211
+ end
212
+
213
+ ##
214
+ # Returns the template path and engine that match content_type (if present),
215
+ # I18n.locale.
216
+ #
217
+ # @param [String] template_path
218
+ # The path of the template.
219
+ #
220
+ # @param [Hash] options
221
+ # Additional options.
222
+ #
223
+ # @option options [Boolean] :strict_format (false)
224
+ # The resolved template must match the content_type of the request.
225
+ #
226
+ # @option options [Boolean] :raise_exceptions (false)
227
+ # Raises a {TemplateNotFound} exception if the template cannot be located.
228
+ #
229
+ # @return [Array<Symbol, Symbol>]
230
+ # The path and format of the template.
231
+ #
232
+ # @raise [TemplateNotFound]
233
+ # The template could not be found.
234
+ #
235
+ # @example
236
+ # get "/foo", :provides => [:html, :js] do; render 'path/to/foo'; end
237
+ # # If you request "/foo.js" with I18n.locale == :ru => [:"/path/to/foo.ru.js", :erb]
238
+ # # If you request "/foo" with I18n.locale == :de => [:"/path/to/foo.de.haml", :haml]
239
+ #
240
+ def resolve_template(template_path, options={})
241
+ template_path = template_path.to_s
242
+ controller_key = respond_to?(:request) && request.respond_to?(:controller) && request.controller
243
+ rendering_options = [template_path, content_type || :html, locale]
244
+
245
+ settings.cache_template_path(["#{controller_key}/#{template_path}", rendering_options[1], rendering_options[2]]) do
246
+ options = DEFAULT_RENDERING_OPTIONS.merge(options)
247
+ view_path = options[:views] || settings.views || "./views"
248
+
249
+ template_candidates = glob_templates(view_path, template_path)
250
+ selected_template = select_template(template_candidates, *rendering_options)
251
+ selected_template ||= template_candidates.first unless options[:strict_format]
252
+
253
+ fail TemplateNotFound, "Template '#{template_path}' not found in '#{view_path}'" if !selected_template && options[:raise_exceptions]
254
+ selected_template
255
+ end
256
+ end
257
+
258
+ ##
259
+ # Return the I18n.locale if I18n is defined.
260
+ #
261
+ def locale
262
+ I18n.locale if defined?(I18n)
263
+ end
264
+
265
+ def resolve_layout(layout, options={})
266
+ layouts_path = options[:layout_options] && options[:layout_options][:views] || options[:views] || settings.views || "./views"
267
+ template_path = settings.fetch_layout_path(layout, layouts_path)
268
+ rendering_options = [template_path, content_type || :html, locale]
269
+
270
+ settings.cache_template_path(rendering_options) do
271
+ template_candidates = glob_templates(layouts_path, template_path)
272
+ selected_template = select_template(template_candidates, *rendering_options)
273
+
274
+ fail TemplateNotFound, "Layout '#{template_path}' not found in '#{layouts_path}'" if !selected_template && layout.present?
275
+ selected_template
276
+ end
277
+ end
278
+
279
+ def with_layout(options)
280
+ options = options.dup
281
+ layout = options[:layout]
282
+ return options if layout == false
283
+
284
+ layout = @layout if !layout || layout == true
285
+ return options if settings.templates.has_key?(:layout) && layout.blank?
286
+
287
+ if layout.kind_of?(String) && Pathname.new(layout).absolute?
288
+ layout_path, _, layout = layout.rpartition('/')
289
+ options[:layout_options] ||= {}
290
+ options[:layout_options][:views] ||= layout_path
291
+ end
292
+ layout, layout_engine = resolve_layout(layout, options)
293
+ options.update(:layout => layout, :layout_engine => layout_engine)
294
+ end
295
+
296
+ def glob_templates(views_path, template_path)
297
+ parts = []
298
+ parts << views_path if views_path.present?
299
+ if respond_to?(:request) && request.respond_to?(:controller) && request.controller.present? && Pathname.new(template_path).relative?
300
+ parts << "{,#{request.controller}}"
301
+ end
302
+ parts << template_path.chomp(File.extname(template_path)) + '.*'
303
+ Dir.glob(File.join(parts)).inject([]) do |all,file|
304
+ next all if IGNORE_FILE_PATTERN.any?{ |pattern| file.to_s =~ pattern }
305
+ all << path_and_engine(file, views_path)
306
+ end
307
+ end
308
+
309
+ def select_template(templates, template_path, content_type, _locale)
310
+ simple_content_type = [:html, :plain].include?(content_type)
311
+ target_path, target_engine = path_and_engine(template_path)
312
+
313
+ templates.find{ |file,_| file.to_s == "#{target_path}.#{locale}.#{content_type}" } ||
314
+ templates.find{ |file,_| file.to_s == "#{target_path}.#{locale}" && simple_content_type } ||
315
+ templates.find{ |file,engine| engine == target_engine || File.extname(file.to_s) == ".#{target_engine}" } ||
316
+ templates.find{ |file,_| file.to_s == "#{target_path}.#{content_type}" } ||
317
+ templates.find{ |file,_| file.to_s == "#{target_path}" && simple_content_type }
318
+ end
319
+
320
+ def path_and_engine(path, relative=nil)
321
+ extname = File.extname(path)
322
+ engine = (extname[1..-1]||'none').to_sym
323
+ path = path.chomp(extname)
324
+ path.insert(0, '/') unless Pathname.new(path).absolute?
325
+ path = path.squeeze('/').sub(relative, '') if relative
326
+ [path.to_sym, engine.to_sym]
327
+ end
328
+
329
+ def ensure_rendering_engine(engine)
330
+ return true if settings.respond_to?(engine)
331
+ return nil unless engine == :erb
332
+ require 'erb'
333
+ rescue LoadError
334
+ else
335
+ require 'tennpipes/rendering/erb_template'
336
+ settings.set :erb, Tennpipes::Rendering.engine_configurations[:erb]
337
+ end
338
+ end
339
+ end
340
+ end
341
+
342
+ unless defined? Tennpipes::Rendering::HamlTemplate
343
+ begin
344
+ require 'haml'
345
+ require 'haml/helpers/xss_mods'
346
+ require 'haml/helpers/action_view_extensions'
347
+ rescue LoadError
348
+ else
349
+ require 'tennpipes/rendering/haml_template'
350
+ end
351
+ end
352
+
353
+ unless defined? Tennpipes::Rendering::ErubisTemplate
354
+ begin
355
+ require 'erubis'
356
+ rescue LoadError
357
+ else
358
+ require 'tennpipes/rendering/erubis_template'
359
+ end
360
+ end
361
+
362
+ unless defined? Tennpipes::Rendering::SlimTemplate
363
+ begin
364
+ require 'slim'
365
+ rescue LoadError
366
+ else
367
+ require 'tennpipes/rendering/slim_template'
368
+ end
369
+ end
@@ -0,0 +1,40 @@
1
+ module Tennpipes
2
+ module Rendering
3
+ class SafeERB < ::ERB
4
+ def set_eoutvar(compiler, eoutvar = '_erbout')
5
+ compiler.put_cmd = "#{eoutvar}.safe_concat"
6
+ compiler.insert_cmd = "#{eoutvar}.concat"
7
+ compiler.pre_cmd = ["#{eoutvar} = ActiveSupport::SafeBuffer.new"]
8
+ compiler.post_cmd = ["#{eoutvar}.force_encoding(__ENCODING__)"]
9
+ end
10
+ end
11
+
12
+ class ERBTemplate < Tilt::ERBTemplate
13
+ def render(*args)
14
+ app = args.first
15
+ app_class = app.class
16
+ @is_tennpipes_app = (defined?(Tennpipes::Application) && app.kind_of?(Tennpipes::Application)) ||
17
+ (app_class.respond_to?(:erb) && app_class.erb[:safe_buffer])
18
+ super
19
+ end
20
+
21
+ def prepare
22
+ @outvar = options[:outvar] || self.class.default_output_variable
23
+ options[:trim] = '<>' if !(options[:trim] == false) && (options[:trim].nil? || options[:trim] == true)
24
+ @engine = SafeERB.new(data, options[:safe], options[:trim], @outvar)
25
+ end
26
+
27
+ def precompiled_preamble(locals)
28
+ original = super
29
+ return original unless @is_tennpipes_app
30
+ "__in_erb_template = true\n" << original
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ Tilt.prefer(Tennpipes::Rendering::ERBTemplate, :erb)
37
+
38
+ Tennpipes::Rendering.engine_configurations[:erb] = {
39
+ :safe_buffer => true
40
+ }
@@ -0,0 +1,66 @@
1
+ module Tennpipes
2
+ module Rendering
3
+ ##
4
+ # SafeBufferEnhancer is an Erubis Enhancer that compiles templates that
5
+ # are fit for using ActiveSupport::SafeBuffer as a Buffer.
6
+ #
7
+ # @api private
8
+ module SafeBufferEnhancer
9
+ def add_expr_literal(src, code)
10
+ src << " #{@bufvar}.concat((" << code << ').to_s);'
11
+ end
12
+
13
+ def add_stmt(src, code)
14
+ code = code.sub('end', 'nil;end') if code =~ /\A\s*end\s*\Z/
15
+ src << code
16
+ src << ';' unless code[-1] == ?\n
17
+ end
18
+
19
+ def add_expr_escaped(src, code)
20
+ src << " #{@bufvar}.safe_concat " << code << ';'
21
+ end
22
+
23
+ def add_text(src, text)
24
+ src << " #{@bufvar}.safe_concat '" << escape_text(text) << "';" unless text.empty?
25
+ end
26
+ end
27
+
28
+ ##
29
+ # SafeBufferTemplate is the Eruby engine, augmented with SafeBufferEnhancer.
30
+ #
31
+ # @api private
32
+ class SafeEruby < ::Erubis::Eruby
33
+ include SafeBufferEnhancer
34
+ end
35
+
36
+ ##
37
+ # Modded ErubisTemplate that doesn't insist in an String as output
38
+ # buffer.
39
+ #
40
+ # @api private
41
+ class ErubisTemplate < Tilt::ErubisTemplate
42
+ def render(*args)
43
+ app = args.first
44
+ app_class = app.class
45
+ @is_tennpipes_app = (defined?(Tennpipes::Application) && app.kind_of?(Tennpipes::Application)) ||
46
+ (app_class.respond_to?(:erb) && app_class.erb[:engine_class] == Tennpipes::Rendering::SafeEruby)
47
+ super
48
+ end
49
+
50
+ ##
51
+ # In preamble we need a flag `__in_erb_template` and SafeBuffer for tennpipes apps.
52
+ #
53
+ def precompiled_preamble(locals)
54
+ original = super
55
+ return original unless @is_tennpipes_app
56
+ "__in_erb_template = true\n" << original.rpartition("\n").first << "#{@outvar} = _buf = ActiveSupport::SafeBuffer.new\n"
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ Tilt.prefer(Tennpipes::Rendering::ErubisTemplate, :erb)
63
+
64
+ Tennpipes::Rendering.engine_configurations[:erb] = {
65
+ :engine_class => Tennpipes::Rendering::SafeEruby,
66
+ }