Skip to content

Instantly share code, notes, and snippets.

@vasil
Created December 3, 2009 15:58
Show Gist options
  • Save vasil/248260 to your computer and use it in GitHub Desktop.
Save vasil/248260 to your computer and use it in GitHub Desktop.
module Sinatra
#class TemplateNotFound < Sinatra::NotFound
class TemplateNotFound < Sinatra::ShowExceptions
end
module Partials
# Render a partial template. [Adapted from Merb]
#
# ==== Parameters
# template<~to_s>::
# The path to the template, relative to the views dir;
# absolute path will work too.
# opts<Hash>:: A hash of options (see below)
#
# ==== Options (opts)
# :with<Object, Array>::
# An object or an array of objects that will be passed into the partial.
# :as<~to_sym>:: The local name of the :with Object inside of the partial.
# others::
# A Hash object names and values that will be the local names and values
# inside the partial.
#
# ==== Examples
# partial :foo, :hello => @object
#
# The "_foo" partial will be called, relative to the views dir,
# with a local variable of +hello+ inside of it, assigned to @object.
#
# partial :bar, :with => ['one', 'two', 'three']
#
# The "_bar" partial will be called once for each element of the array
# specified by :with for a total of three iterations. Each element
# of the array will be available in the partial via a local variable named
# +bar+. Additionally, there will be two extra local variables:
# +collection_index+ and +collection_size+. +collection_index+ is the index
# of the object currently referenced by +bar+ in the collection passed to
# the partial. +collection_size+ is the total size of the collection.
#
# By default, the object specified by :with will be available through a
# local variable with the same name as the partial template. However,
# this can be changed using the :as option.
#
# partial :bar, :with => "one", :as => :number
#
# In this case, "one" will be available in the partial through the local
# variable named +number+.
def partial(template, opts={})
# partial :foo becomes "./_foo"
# partial "foo/bar" becomes "foo/_bar"
template = template.to_s
if template =~ %r{^/}
template_path = File.dirname(template) / "_#{File.basename(template)}"
else
template_path = (m = template.match(/.*(?=\/)/)) ? m[0] : '.'
end
template = "_#{File.basename(template)}"
# This handles no :with as well
with = [opts.delete(:with)].flatten
as = (opts.delete(:as) || template.match(%r[(?:.*/)?_([^\./]*)])[1]).to_sym
# Ensure that as is in the locals hash even if it isn't passed in here
# so that it's included in the preamble.
locals = opts.merge(:collection_index => -1, :collection_size => with.size, as => opts[as])
template_full = File.join(template_path, template)
# this handles an edge-case where the name of the partial is _foo.* and your opts
# have :foo as a key.
named_local = opts.key?(as)
with.inject('') do |rendered, temp|
locals[as] = temp unless named_local
if File::exists?(File.join(Sinatra::Application.views, template_full))
locals[:collection_index] += 1
rendered << erb(:"#{template_full}", {}, locals)
else
raise TemplateNotFound, "Could not find template at #{template_path}.*"
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment