lib/merb/mixins/render_mixin.rb in merb-0.3.4 vs lib/merb/mixins/render_mixin.rb in merb-0.3.7

- old
+ new

@@ -1,59 +1,105 @@ module Merb module RenderMixin - + + def self.included(base) + base.class_eval { + class_inheritable_accessor :_template_root, + :_layout + + @@template_extensions = { } + self._layout = :application + self._template_root = File.expand_path(MERB_VIEW_ROOT) + # This method is called by templating-engines to register themselves with + # a list of extensions that will be looked up on #render of an action. + def self.register_engine(engine, *extensions) + [extensions].flatten.uniq.each do |ext| + @@template_extensions[ext] = engine + end + end + + } + end + # 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 + # out of the box Merb support Erubis, Haml, Markaby and Builder templates # # Erubis template ext: .herb .jerb .erb # Markaby template ext: .mab # Builder template ext: .rxml .builder .xerb + # Haml template ext: .haml # # Examples: # - # render - # looks for views/controllername/actionname.* and renders + # 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 + # 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 :action => 'foo' # - # render :nothing => 200 - # renders nothing with a status of 200 + # Renders views/controllername/foo.* # - # render :template => 'shared/message' - # renders views/shared/message + # render :nothing => 200 # - # render :js => "$('some-div').toggle();" - # if the right hand side of :js => is a string then the proper + # Renders nothing with a status of 200 + # + # render :template => 'shared/message' + # + # Renders views/shared/message + # + # 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 + # 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 + # render :js => true + # + # This will just look for the current controller/action template # with the .jerb extension and render it as javascript # - # render :xml => @posts.to_xml - # render :xml => "<foo><bar>Hi!</bar></foo>" - # this will set the appropriate xml headers and render the rhs + # XML can be rendered with the same options as Javascript. + # + # render :xml => @posts.to_xml + # render :xml => "<foo><bar>Hi!</bar></foo>" + # + # 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. # + # render :xml => :hello + # + # Renders the hello.xrb template for the current controller. + # + # render :xml => true + # render :xml => true, :action => "buffalo" + # + # Renders the buffalo.xerb template for the current controller. + # def render(opts={}, &blk) - action = opts[:action] || params[:action] + + action = if kind_of?(Merb::ControllerExceptions::Base) + self.class.name.snake_case.split('::').last + else + opts[:action] || params[:action] + end + opts[:layout] ||= _layout case when status = opts[:nothing] return render_nothing(status) @@ -68,27 +114,36 @@ end opts[:clean_context] = true when js = opts[:js] headers['Content-Type'] = "text/javascript" opts[:layout] = :none - if String === js + case js + when String return js - elsif Symbol === js + when Symbol template = find_template(:action => js, :ext => 'jerb') - else + else template = find_template(:action => action, :ext => 'jerb') end when xml = opts[:xml] headers['Content-Type'] = 'application/xml' headers['Encoding'] = 'UTF-8' - return xml + # TODO This is the same as the js logic above. Can it be refactored? + case xml + when String + return xml + when Symbol + template = find_template(:action => xml, :ext => 'rxml,xerb,builder') + else + template = find_template(:action => action, :ext => 'rxml,xerb,builder') + end when template = opts[:template] template = find_template(:template => template) else template = find_template(:action => action) end - + engine = engine_for(template) options = { :file => template, :view_context => (opts[:clean_context] ? clean_view_context : cached_view_context), :opts => opts @@ -99,21 +154,12 @@ 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 cached_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) @@ -122,14 +168,18 @@ # 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 + @_status = status return " " end + def set_status(status) + @_status = status + end + def render_no_layout(opts={}) render opts.update({:layout => :none}) end # This is merb's partial render method. You name your @@ -142,18 +192,36 @@ # if you create a views/shared directory then you can call # partials that live there like partial('shared/foo') def partial(template, locals={}) render :partial => template, :locals => locals end + + # +catch_content+ catches the thrown content from another template + # So when you throw_content(:foo) {...} you can catch_content :foo + # in another view or the layout. + def catch_content(name) + cached_view_context.instance_variable_get("@_#{name}_content") + end private + # 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 cached_view_context + @_view_context_cache ||= ViewContext.new(self) + end + + def clean_view_context + ViewContext.new(self) + end + 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) + if name = find_template(:layout => self.class.name.snake_case.split('::').join('/')) layout_choice = name else layout_choice = find_template(:layout => :application) end end @@ -170,32 +238,46 @@ # OPTIMIZE : combine find_template and find_partial ? def find_template(opts={}) if template = opts[:template] path = _template_root / template + elsif opts[:action] and kind_of?(Merb::ControllerExceptions::Base) + path = _template_root / 'exceptions' / opts[:action] elsif action = opts[:action] - path = _template_root / self.class.name.snake_case / action + segment = self.class.name.snake_case.split('::').join('/') + path = _template_root / segment / action elsif _layout = opts[:layout] - path = _layout_root / _layout + path = _template_root / 'layout' / _layout else raise "called find_template without an :action or :layout" - end - extensions = [_template_extensions.keys].flatten.uniq + end + extensions = [@@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 = _template_root / t.join('/') / "_#{template}" else - path = _template_root / self.class.name.snake_case / "_#{template}" + segment = self.class.name.snake_case.split('::').join('/') + path = _template_root / segment / "_#{template}" end - extensions = [_template_extensions.keys].flatten.uniq + extensions = [@@template_extensions.keys].flatten.uniq glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}" Dir[glob].first end + # lookup the template_extensions for the extname of the filename + # you pass. Answers with the engine that matches the extension, Template::Erubis + # is used if none matches. + def engine_for(file) + extension = File.extname(file)[1..-1] + @@template_extensions[extension] + rescue + ::Merb::Template::Erubis + end + end end \ No newline at end of file