lib/roda/plugins/render.rb in roda-2.29.0 vs lib/roda/plugins/render.rb in roda-3.0.0

- old
+ new

@@ -22,65 +22,59 @@ # # The +render+ and +view+ methods just return strings, they do not have # side effects (unless the templates themselves have side effects). # As Roda uses the routing block return value as the body of the response, # in most cases you will call these methods as the last expression in a - # routing block # to have the response body be the result of the template + # routing block to have the response body be the result of the template # rendering. # # Because +render+ and +view+ just return strings, you can call them inside # templates (i.e. for subtemplates/partials), or multiple times in the # same route and combine the results together: # # route do |r| # r.is 'foo-bars' do - # @bars = Bar.where(:foo).map{|b| render(:bar, :locals=>{:bar=>b})}.join + # @bars = Bar.where(:foo).map{|b| render(:bar, locals: {bar: b})}.join # view('foo') # end # end # # You can provide options to the plugin method: # - # plugin :render, :engine=>'haml', :views=>'admin_views' + # plugin :render, engine: 'haml', views: 'admin_views' # # = Plugin Options # # The following plugin options are supported: # - # :allowed_paths :: Set the template paths to allow if +:check_paths+ is true. - # Defaults to the +:views+ directory. - # :cache :: nil/false to disallow template caching completely. + # :allowed_paths :: Set the template paths to allow. Attempts to render paths outside + # of this directory will raise an error. Defaults to the +:views+ directory. + # :cache :: nil/false to disable template caching by default. By default, caching + # is disabled by default if RACK_ENV is development. # :cache_class :: A class to use as the template cache instead of the default. - # :check_paths :: Check template paths start with one of the entries in +:allowed_paths+, - # and raise a RodaError if the path doesn't. + # :check_paths :: Can be set to false to turn off template path checking. # :engine :: The tilt engine to use for rendering, also the default file extension for # templates, defaults to 'erb'. - # :escape :: Use Roda's Erubis escaping support, which makes <tt><%= %></tt> escape output, - # <tt><%== %></tt> not escape output, and handles postfix conditions inside - # <tt><%= %></tt> tags. Can have a value of :erubi to use Erubi escaping support. - # :explicit_cache :: Only use the template cache if the :cache option is provided when rendering - # (useful for development). Defaults to true if RACK_ENV is development, allowing explicit - # caching of specific templates, but not caching by default. - # :inherit_cache :: Whether to create a dup of the cache in subclasses. The default is false, which - # starts subclasses with an empty cache. + # :escape :: Use Erubi as the ERB template engine, and enable escaping by default, + # which makes <tt><%= %></tt> escape output and <tt><%== %></tt> not escape output. # :layout :: The base name of the layout file, defaults to 'layout'. This can be provided as a hash # with the :template or :inline options. # :layout_opts :: The options to use when rendering the layout, if different from the default options. # :template_opts :: The tilt options used when rendering all templates. defaults to: - # <tt>{:outvar=>'@_out_buf', :default_encoding=>Encoding.default_external}</tt>. + # <tt>{outvar: '@_out_buf', default_encoding: Encoding.default_external}</tt>. # :engine_opts :: The tilt options to use per template engine. Keys are # engine strings, values are hashes of template options. # :views :: The directory holding the view files, defaults to the 'views' subdirectory of the # application's :root option (the process's working directory by default). # # = Render/View Method Options # # Most of these options can be overridden at runtime by passing options # to the +view+ or +render+ methods: # - # view('foo', :engine=>'html.erb') - # render('foo', :views=>'admin_views') + # view('foo', engine: 'html.erb') + # render('foo', views: 'admin_views') # # There are additional options to +view+ and +render+ that are # available at runtime: # # :cache :: Set to false to not cache this template, even when @@ -106,14 +100,14 @@ # ignored when using :inline. Disables caching of the # template by default. # :template_class :: Provides the template class to use, inside of using # Tilt or <tt>Tilt[:engine]</tt>. # - # Here's how those options are used: + # Here's an example of using these options: # - # view(:inline=>'<%= @foo %>') - # render(:path=>'/path/to/template.erb') + # view(inline: '<%= @foo %>') + # render(path: '/path/to/template.erb') # # If you pass a hash as the first argument to +view+ or +render+, it should # have either +:template+, +:inline+, +:path+, or +:content+ (for +view+) as # one of the keys. # @@ -126,84 +120,52 @@ # # If your application uses a unique template per path, in that the same # path never uses more than one template, you can use the +view_options+ plugin # and do: # - # set_view_options :cache_key=>r.path_info + # set_view_options cache_key: r.path_info # # at the top of your route block. You can even do this if you do have paths # that use more than one template, as long as you specify +:cache_key+ # specifically when rendering in those paths. # # If you use a single layout in your application, you can also make layout # rendering faster by specifying +:cache_key+ inside the +:layout_opts+ # plugin option. module Render - OPTS={}.freeze - RodaPlugins.deprecate_constant(self, :OPTS) - - # RODA3: Remove - def self.load_dependencies(app, opts=RodaPlugins::OPTS) - if opts[:escape] && opts[:escape] != :erubi - app.plugin :_erubis_escaping - end - end - # Setup default rendering options. See Render for details. - def self.configure(app, opts=RodaPlugins::OPTS) + def self.configure(app, opts=OPTS) if app.opts[:render] orig_cache = app.opts[:render][:cache] opts = app.opts[:render][:orig_opts].merge(opts) end app.opts[:render] = opts.dup app.opts[:render][:orig_opts] = opts opts = app.opts[:render] - if opts[:ext] && !opts[:engine] - RodaPlugins.warn "The :ext render plugin option is deprecated and will be removed in Roda 3. Switch to using the :engine option." - end - opts[:engine] = (opts[:engine] || opts[:ext] || "erb").dup.freeze + opts[:engine] = (opts[:engine] || "erb").dup.freeze opts[:views] = app.expand_path(opts[:views]||"views").freeze opts[:allowed_paths] ||= [opts[:views]].freeze opts[:allowed_paths] = opts[:allowed_paths].map{|f| app.expand_path(f, nil)}.uniq.freeze + opts[:check_paths] = true unless opts.has_key?(:check_paths) - if opts.fetch(:cache, true) - if orig_cache - opts[:cache] = orig_cache - elsif cache_class = opts[:cache_class] - opts[:cache] = cache_class.new + unless opts.has_key?(:explicit_cache) + opts[:explicit_cache] = if opts.fetch(:cache, true) + ENV['RACK_ENV'] == 'development' else - opts[:cache] = app.thread_safe_cache + true end end - opts[:explicit_cache] = ENV['RACK_ENV'] == 'development' unless opts.has_key?(:explicit_cache) + opts[:cache] = orig_cache || (opts[:cache_class] || RodaCache).new opts[:layout_opts] = (opts[:layout_opts] || {}).dup + opts[:layout_opts][:_is_layout] = true if opts[:layout_opts][:views] opts[:layout_opts][:views] = app.expand_path(opts[:layout_opts][:views]).freeze end - # RODA3: Remove - opts[:layout_opts][:_is_layout] = true - if opts[:locals] - RodaPlugins.warn "The :locals render plugin option is deprecated and will be removed in Roda 3. Locals should now be specified on a per-call basis, or you can use the render_locals plugin." - end - - if opts[:layout_opts][:locals] - RodaPlugins.warn "The :layout_opts=>:locals render plugin option is deprecated and will be removed in Roda 3. Locals should now be specified on a per-call basis, or you can use the render_locals plugin." - end - - if opts[:layout_opts][:merge_locals] - RodaPlugins.warn "The :layout_opts=>:merge_locals render plugin option is deprecated and will be removed in Roda 3. You can use the render_locals plugin for merging locals." - end - - # RODA3: Remove - if opts[:layout_opts][:merge_locals] && opts[:locals] - opts[:layout_opts][:locals] = opts[:locals].merge(opts[:layout_opts][:locals] || {}) - end - if layout = opts.fetch(:layout, true) opts[:layout] = true unless opts.has_key?(:layout) case layout when Hash @@ -216,25 +178,17 @@ end opts[:layout_opts].freeze template_opts = opts[:template_opts] = (opts[:template_opts] || {}).dup template_opts[:outvar] ||= '@_out_buf' - if RUBY_VERSION >= "1.9" && !template_opts.has_key?(:default_encoding) + unless template_opts.has_key?(:default_encoding) template_opts[:default_encoding] = Encoding.default_external end - # RODA3: Make :escape assume erubi, remove erubis support - if opts[:escape] == :erubi + + if opts[:escape] require 'tilt/erubi' template_opts[:escape] = true - elsif opts[:escape] - template_opts[:engine_class] = ErubisEscaping::Eruby - - opts[:escaper] ||= if opts[:escape_safe_classes] - ErubisEscaping::UnsafeClassEscaper.new(opts[:escape_safe_classes]) - else - ::Erubis::XmlHelper - end end template_opts.freeze engine_opts = opts[:engine_opts] = (opts[:engine_opts] || {}).dup engine_opts.to_a.each do |k,v| @@ -250,21 +204,11 @@ # them as necessary to prevent changes in the subclass # affecting the parent class. def inherited(subclass) super opts = subclass.opts[:render] = subclass.opts[:render].dup - - if opts[:cache] - opts[:cache] = if opts[:inherit_cache] - opts[:cache] = opts[:cache].dup - elsif cache_class = opts[:cache_class] - opts[:cache] = cache_class.new - else - opts[:cache] = thread_safe_cache - end - end - + opts[:cache] = opts[:cache].dup opts.freeze end # Return the render options for this class. def render_opts @@ -272,26 +216,24 @@ end end module InstanceMethods # Render the given template. See Render for details. - def render(template, opts = RodaPlugins::OPTS, &block) + def render(template, opts = OPTS, &block) opts = render_template_opts(template, opts) - retrieve_template(opts).render((opts[:scope]||self), (opts[:locals]||RodaPlugins::OPTS), &block) + retrieve_template(opts).render((opts[:scope]||self), (opts[:locals]||OPTS), &block) end - # Return the render options for the instance's class. While this - # is not currently frozen, it may be frozen in a future version, - # so you should not attempt to modify it. + # Return the render options for the instance's class. def render_opts self.class.render_opts end # Render the given template. If there is a default layout # for the class, take the result of the template rendering # and render it inside the layout. See Render for details. - def view(template, opts=RodaPlugins::OPTS) + def view(template, opts=OPTS) opts = parse_template_opts(template, opts) content = opts[:content] || render_template(opts) if layout_opts = view_layout_opts(opts) content = render_template(layout_opts){content} @@ -302,26 +244,22 @@ private # Convert template options to single hash when rendering templates using render. def render_template_opts(template, opts) - opts = parse_template_opts(template, opts) - - # RODA3: Remove - merge_render_locals(opts) if render_plugin_handle_locals? - - opts + parse_template_opts(template, opts) end # Private alias for render. Should be used by other plugins when they want to render a template # without a layout, as plugins can override render to use a layout. alias render_template render # If caching templates, attempt to retrieve the template from the cache. Otherwise, just yield # to get the template. def cached_template(opts, &block) - if (!render_opts[:explicit_cache] || opts[:cache]) && (cache = render_opts[:cache]) && (key = opts[:cache_key]) + if (!render_opts[:explicit_cache] || opts[:cache]) && (key = opts[:cache_key]) + cache = render_opts[:cache] unless template = cache[key] template = cache[key] = yield end template else @@ -331,14 +269,11 @@ # Given the template name and options, set the template class, template path/content, # template block, and locals to use for the render in the passed options. def find_template(opts) render_opts = render_opts() - if opts[:ext] && !opts[:engine] - RodaPlugins.warn "The :ext render plugin option is deprecated and will be removed in Roda 3. Switch to using the :engine option." - end - engine_override = opts[:engine] ||= opts[:ext] + engine_override = opts[:engine] engine = opts[:engine] ||= render_opts[:engine] if content = opts[:inline] path = opts[:path] = content template_class = opts[:template_class] ||= ::Tilt[engine] opts[:template_block] = Proc.new{content} @@ -347,45 +282,30 @@ path = opts[:path] ||= template_path(opts) template_class = opts[:template_class] opts[:template_class] ||= ::Tilt end - if render_opts[:cache] - if (cache = opts[:cache]).nil? - cache = content || !opts[:template_block] - end + if (cache = opts[:cache]).nil? + cache = content || !opts[:template_block] + end - if cache - template_block = opts[:template_block] unless content - template_opts = opts[:template_opts] + if cache + template_block = opts[:template_block] unless content + template_opts = opts[:template_opts] - opts[:cache_key] ||= if template_class || engine_override || template_opts || template_block - [path, template_class, engine_override, template_opts, template_block] - else - path - end + opts[:cache_key] ||= if template_class || engine_override || template_opts || template_block + [path, template_class, engine_override, template_opts, template_block] else - opts.delete(:cache_key) + path end - elsif opts[:cache] - RodaPlugins.warn ":cache render/view method option used when caching explicitly disabled via :cache=>nil/false plugin option. Caching this template will be skipped for backwards compatibility. Starting in Roda 3, the :cache render/view method option will force caching even if the plugin defaults to not caching." + else + opts.delete(:cache_key) end opts end - # RODA3: Remove - def merge_render_locals(opts) - if !opts[:_is_layout] && (r_locals = render_opts[:locals]) - opts[:locals] = if locals = opts[:locals] - Hash[r_locals].merge!(locals) - else - r_locals - end - end - end - # Return a single hash combining the template and opts arguments. def parse_template_opts(template, opts) opts = Hash[opts] if template.is_a?(Hash) opts.merge!(template) @@ -425,68 +345,27 @@ end # The template path for the given options. def template_path(opts) path = "#{opts[:views]}/#{template_name(opts)}.#{opts[:engine]}" - full_path = self.class.expand_path(path) if opts.fetch(:check_paths){render_opts[:check_paths]} + full_path = self.class.expand_path(path) unless render_opts[:allowed_paths].any?{|f| full_path.start_with?(f)} raise RodaError, "attempt to render path not in allowed_paths: #{full_path} (allowed: #{render_opts[:allowed_paths].join(', ')})" end - elsif !opts.has_key?(:check_paths) && !render_opts.has_key?(:check_paths) - unless render_opts[:allowed_paths].any?{|f| full_path.start_with?(f)} - RodaPlugins.warn "The :check_paths render/view method option and :check_paths render plugin option were not specified, and the path used for the template (#{full_path.inspect}) is not in the allowed paths (#{render_opts[:allowed_paths].inspect}). Allowing the template render anyway for backwards compatibility, but an error will be raised starting in Roda 3. Specify the :allowed_paths render plugin option to include the path, or use the :check_paths=>false render plugin option to explicitly disable path checking." - end end path end - # RODA3: Remove - def render_plugin_handle_locals? - true - end - # If a layout should be used, return a hash of options for # rendering the layout template. If a layout should not be # used, return nil. def view_layout_opts(opts) if layout = opts.fetch(:layout, render_opts[:layout]) layout_opts = render_layout_opts - # RODA3: Remove - merge_locals = layout_opts[:merge_locals] - method_layout_opts = opts[:layout_opts] - - # RODA3: Remove - if render_plugin_handle_locals? - if method_layout_opts - method_layout_locals = method_layout_opts[:locals] - if method_layout_opts.has_key?(:merge_locals) - RodaPlugins.warn "The :layout_opts=>:merge_locals view option is deprecated and will be removed in Roda 3. You can use the render_locals plugin for merging locals." - merge_locals = method_layout_opts[:merge_locals] - end - end - - locals = {} - if merge_locals && (plugin_locals = render_opts[:locals]) - locals.merge!(plugin_locals) - end - if layout_locals = layout_opts[:locals] - locals.merge!(layout_locals) - end - if merge_locals && (method_locals = opts[:locals]) - locals.merge!(method_locals) - end - if method_layout_locals - locals.merge!(method_layout_locals) - end - end - layout_opts.merge!(method_layout_opts) if method_layout_opts - - # RODA3: Remove - layout_opts[:locals] = locals if render_plugin_handle_locals? && !locals.empty? case layout when Hash layout_opts.merge!(layout) when true