module Merb module RenderMixin # universal render method. Template handlers are registered # by template extension. So you can use the same render method # for any kind of template that implements an adapter module. # out of the box Merb support Erubis, Markaby and Builder templates # # Erubis template ext: .herb .jerb .erb # Markaby template ext: .mab # Builder template ext: .rxml .builder .xerb # # Examples: # # render # looks for views/controllername/actionname.* and renders # the template with the proper engine based on its file extension. # # render :layout => :none # renders the current template with no layout. XMl Builder templates # are exempt from layout by default. # # render :action => 'foo' # renders views/controllername/foo.* # # render :nothing => 200 # renders nothing with a status of 200 # # render :js => "$('some-div').toggle();" # if the right hand side of :js => is a string then the proper # javascript headers will be set and the string will be returned # verbatim as js. # # render :js => :spinner # when the rhs of :js => is a Symbol, it will be used as the # action/template name so: views/controllername/spinner.jerb # will be rendered as javascript # # render :js => true # this will just look for the current controller/action tenmplate # with the .jerb extension and render it as javascript # # render :xml => @posts.to_xml # render :xml => "Hi!" # this will set the appropriate xml headers and render the rhs # of :xml => as a string. SO you can pass any xml string to this # to be rendered. # def render(opts={}, &blk) action = opts[:action] || params[:action] opts[:layout] ||= ancestral_trait[:layout] case when status = opts[:nothing] return render_nothing(status) when partial = opts[:partial] template = find_partial(partial, opts) opts[:layout] = :none when js = opts[:js] headers['Content-Type'] = "text/javascript" opts[:layout] = :none if String === js return js elsif Symbol === js template = find_template(:action => js, :ext => 'jerb') else template = find_template(:action => action, :ext => 'jerb') end when xml = opts[:xml] headers['Content-Type'] = 'application/xml' headers['Encoding'] = 'UTF-8' return xml else template = find_template(:action => action) end engine = engine_for(template) options = { :file => template, :view_context => (opts[:clean_context] ? clean_view_context : _view_context), :opts => opts } content = engine.transform(options) if engine.exempt_from_layout? || opts[:layout] == :none content else wrap_layout(content, opts) end end # this returns a ViewContext object populated with all # the instance variables in your controller. This is used # as the view context object for the Erubis templates. def _view_context @_view_context_cache ||= ViewContext.new(self) end def clean_view_context ViewContext.new(self) end # does a render with no layout. Also sets the # content type header to text/javascript. Use # this when you want to render a template with # .jerb extension. def render_js(template=nil) render :js => true, :action => (template || params[:action]) end # renders nothing but sets the status, defaults # to 200. does send one ' ' space char, this is for # safari and flash uploaders to work. def render_nothing(status=200) @status = status return " " end def render_no_layout(opts={}) render opts.update({:layout => :none}) end # This is merb's partial render method. You name your # partials _partialname.* , and then call it like # partial(:partialname). If there is no '/' character # in the argument passed in it will look for the partial # in the view directory that corresponds to the current # controller name. If you pass a string with a path in it # you can render partials in other view directories. So # if you create a views/shared directory then you can call # partials that live there like partial('shared/foo') def partial(template) render :partial => template end private def wrap_layout(content, opts={}) if opts[:layout] != :application layout_choice = find_template(:layout => opts[:layout]) else if name = find_template(:layout => self.class.name.snake_case) layout_choice = name else layout_choice = find_template(:layout => :application) end end _view_context.instance_variable_set('@_layout_content', content) engine = engine_for(layout_choice) options = { :file => layout_choice, :view_context => _view_context, :opts => opts } engine.transform(options) end # OPTIMIZE : combine find_template and find_partial ? def find_template(opts={}) if action = opts[:action] path = File.expand_path(MERB_ROOT / "dist/app/views" / self.class.name.snake_case / action) elsif layout = opts[:layout] path = ancestral_trait[:layout_root] / layout else raise "called find_template without an :action or :layout" end extensions = [ancestral_trait[:template_extensions].keys].flatten.uniq glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}" Dir[glob].first end def find_partial(template, opts={}) if template =~ /\// t = template.split('/') template = t.pop path = ancestral_trait[:template_root] / t.join('/') / "_#{template}" else path = ancestral_trait[:template_root] / self.class.name.snake_case / "_#{template}" end extensions = [ancestral_trait[:template_extensions].keys].flatten.uniq glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}" Dir[glob].first end end end