lib/merb/mixins/render_mixin.rb in merb-0.0.7 vs lib/merb/mixins/render_mixin.rb in merb-0.0.8

- old
+ new

@@ -1,91 +1,165 @@ module Merb module RenderMixin - + # shortcut to a template path based on name. def template_dir(loc) - File.expand_path(Merb::Server.config[:merb_root] + "/dist/app/views/#{loc}") + File.expand_path(Merb::Server.config[:merb_root] / "/dist/app/views/#{loc}") end # returns the current method name. Used for # auto discovery of which template to render # based on the action name. def current_method_name(depth=0) caller[depth] =~ /`(.*)'$/; $1 end + # given html, js and xml this method returns the template + # extension from the :template_ext map froom your app's + # configuration. defaults to .herb, .jerb & .xerb def template_extension_for(ext) Merb::Server.config[:template_ext][ext] 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 create_view_context + ViewContext.new(self) + end + # does a render with no layout. Also sets the - # content type header to text/javascript - def render_js(template=current_method_name(1), b=binding) + # content type header to text/javascript. Use + # this when you want to render a template with + # .jerb extension. + def render_js(template=current_method_name(1)) headers['Content-Type'] = "text/javascript" - template = Erubis::Eruby.new(IO.read( template_dir(self.class.name.snake_case) + "/#{template}.#{template_extension_for(:js)}" )) - template.result(b) + template = new_eruby_obj(template_dir(self.class.name.snake_case) / "/#{template}.#{template_extension_for(:js)}") + template.evaluate(create_view_context) end - # set the @layout. Use this right before a render to - # set the name of the layout to use minus the .rhtml - def layout(l) - @layout = l - end - - # renders nothing but sets the status + # renders nothing but sets the status, defaults + # to 200 def render_nothing(status=200) @status = status return "\n" end # renders the action without wrapping it in a layout. - def render_no_layout(template=current_method_name(1), b=binding) - template = Erubis::Eruby.new( IO.read( template_dir(self.class.name.snake_case) + "/#{template}.#{template_extension_for(:html)}" ) ) - template.result(b) + # call it without arguments if your template matches + # the name of the running action. Otherwise you can + # explicitely set the template name excluding the file + # extension + def render_no_layout(template=current_method_name(1)) + template = new_eruby_obj(template_dir(self.class.name.snake_case) / "/#{template}.#{template_extension_for(:html)}") + template.evaluate(create_view_context) end + # This is merb's partial render method. You name your + # partials _partialname.herb, 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) - template = Erubis::Eruby.new( IO.read( template_dir(self.class.name.snake_case) + "/_#{template}.#{template_extension_for(:html)}" ) ) - template.result(binding) + if template =~ /\// + t = template.split('/') + template = t.pop + tmpl = new_eruby_obj(template_dir(t.join('/')) / "/_#{template}.#{template_extension_for(:html)}") + else + tmpl = new_eruby_obj(template_dir(self.class.name.snake_case) / "/_#{template}.#{template_extension_for(:html)}") + end + tmpl.evaluate(create_view_context) end + # This creates and returns a new Erubis object populated + # with the template from path. If there is no matching + # template then we rescue the Errno::ENOENT exception + # and raise a no template found message + def new_eruby_obj(path) + begin + Erubis::MEruby.new(IO.read(path)) + rescue Errno::ENOENT + raise "No template found at path: #{path}" + end + end + + # this is the xml builder render method. This method + # builds the Builder::XmlMarkup object for you and adds + # the xml headers and encoding. Then it evals your template + # in the context of the xml object. So your .xerb templates + # will look like this: + # xml.foo {|xml| + # xml.bar "baz" + # } def render_xml(template=current_method_name(1)) xml = Builder::XmlMarkup.new :indent => 2 xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8" eval IO.read( template_dir(self.class.name.snake_case) + "/#{template}.#{template_extension_for(:xml)}" ) @headers['Content-Type'] = 'application/xml' @headers['Encoding'] = 'UTF-8' xml.target! end - # renders a template based on the current action name - # you can pass the name of a template if you want to - # render a template with a different name then then - # current action name. Wraps the rendered template in - # the layout. Uses layout/application.rhtml unless + # This is the main render method that handles layouts. + # render will use layout/application.rhtml unless # there is a layout named after the current controller - # or @layout has been set to another value. - def render(template=current_method_name(1), b=binding) + # or if self.layout= has been set to another value in + # the current controller. You can over-ride this setting + # by passing an options hash with a :layout => 'layoutname'. + # if you with to not render a layout then pass :layout => :none + # the first argument to render is the template name. if you do + # not pass a template name, it will set the template to + # views/controller/action automatically. + # examples: + # class Test < Merb::Controller + # # renders views/test/foo.herb + # # in layout application.herb + # def foo + # # code + # render + # end + # + # # renders views/test/foo.herb + # # in layout application.herb + # def bar + # # code + # render :foo + # end + # + # # renders views/test/baz.herb + # # with no layout + # def baz + # # code + # render :layout => :none + # end + def render(opts={}) + template = opts[:action] || params[:action] tmpl_ext = template_extension_for(:html) - MERB_LOGGER.info("Rendering template: #{template_dir(template)}..#{tmpl_ext}") + MERB_LOGGER.info("Rendering template: #{template}.#{tmpl_ext}") name = self.class.name.snake_case - template = Erubis::Eruby.new( IO.read( template_dir(name) + "/#{template}.#{tmpl_ext}" ) ) - layout_content = template.result(b) - return layout_content if (@layout.to_s == 'none') - if ['application', name].include?(@layout.to_s) + template = new_eruby_obj(template_dir(name) / "/#{template}.#{tmpl_ext}") + view_context = create_view_context + layout_content = template.evaluate(view_context) + self.layout = opts[:layout].to_sym if opts.has_key?(:layout) + return layout_content if (layout == :none) + if layout != :application + layout_choice = layout + else if File.exist?(template_dir("layout/#{name}.#{tmpl_ext}")) - layout = name + layout_choice = name else - layout = 'application' - end - else - layout = @layout.to_s - end - MERB_LOGGER.info("With Layout: #{template_dir('layout')}/#{layout}.#{tmpl_ext}") - @layout_content = layout_content - layout_tmpl = Erubis::Eruby.new( IO.read( "#{template_dir('layout')}/#{layout}.#{tmpl_ext}" ) ) - layout_tmpl.result(b) + layout_choice = layout + end + end + MERB_LOGGER.info("With Layout: #{layout_choice}.#{tmpl_ext}") + view_context.instance_eval { throw_content(:layout) { layout_content } } + layout_tmpl = new_eruby_obj("#{template_dir('layout')}/#{layout_choice}.#{tmpl_ext}") + layout_tmpl.evaluate(view_context) end end end \ No newline at end of file