| | 5 | # universal render method. Template handlers are registered |
|---|
| | 6 | # by template extension. So you can use the same render method |
|---|
| | 7 | # for any kind of template that implements an adapter module. |
|---|
| | 8 | # out of the box Merb support Erubis, Markaby and Builder templates |
|---|
| | 9 | # |
|---|
| | 10 | # Erubis template ext: .herb .jerb .erb |
|---|
| | 11 | # Markaby template ext: .mab |
|---|
| | 12 | # Builder template ext: .rxml .builder .xerb |
|---|
| | 13 | # |
|---|
| | 14 | # Examples: |
|---|
| | 15 | # |
|---|
| | 16 | # render |
|---|
| | 17 | # looks for views/controllername/actionname.* and renders |
|---|
| | 18 | # the template with the proper engine based on its file extension. |
|---|
| | 19 | # |
|---|
| | 20 | # render :layout => :none |
|---|
| | 21 | # renders the current template with no layout. XMl Builder templates |
|---|
| | 22 | # are exempt from layout by default. |
|---|
| | 23 | # |
|---|
| | 24 | # render :action => 'foo' |
|---|
| | 25 | # renders views/controllername/foo.* |
|---|
| | 26 | # |
|---|
| | 27 | # render :nothing => 200 |
|---|
| | 28 | # renders nothing with a status of 200 |
|---|
| | 29 | # |
|---|
| | 30 | # render :js => "$('some-div').toggle();" |
|---|
| | 31 | # if the right hand side of :js => is a string then the proper |
|---|
| | 32 | # javascript headers will be set and the string will be returned |
|---|
| | 33 | # verbatim as js. |
|---|
| | 34 | # |
|---|
| | 35 | # render :js => :spinner |
|---|
| | 36 | # when the rhs of :js => is a Symbol, it will be used as the |
|---|
| | 37 | # action/template name so: views/controllername/spinner.jerb |
|---|
| | 38 | # will be rendered as javascript |
|---|
| | 39 | # |
|---|
| | 40 | # render :js => true |
|---|
| | 41 | # this will just look for the current controller/action tenmplate |
|---|
| | 42 | # with the .jerb extension and render it as javascript |
|---|
| | 43 | # |
|---|
| | 44 | # render :xml => @posts.to_xml |
|---|
| | 45 | # render :xml => "<foo><bar>Hi!</bar></foo>" |
|---|
| | 46 | # this will set the appropriate xml headers and render the rhs |
|---|
| | 47 | # of :xml => as a string. SO you can pass any xml string to this |
|---|
| | 48 | # to be rendered. |
|---|
| | 49 | # |
|---|
| 26 | | end |
|---|
| 27 | | |
|---|
| 28 | | def wrap_layout(content, opts={}) |
|---|
| 29 | | return content if ((opts[:layout] == :none) || (ancestral_trait[:layout] == :none)) |
|---|
| 30 | | |
|---|
| 31 | | if ancestral_trait[:layout] != :application |
|---|
| 32 | | layout_choice = find_template(:layout => ancestral_trait[:layout]) |
|---|
| 33 | | else |
|---|
| 34 | | if name = find_template(:layout => self.class.name.snake_case) |
|---|
| 35 | | layout_choice = name |
|---|
| 36 | | else |
|---|
| 37 | | layout_choice = find_template(:layout => :application) |
|---|
| 38 | | end |
|---|
| 39 | | end |
|---|
| 40 | | |
|---|
| 41 | | _view_context.instance_variable_set('@_layout_content', content) |
|---|
| 42 | | engine = engine_for(layout_choice) |
|---|
| 43 | | options = { |
|---|
| 44 | | :file => layout_choice, |
|---|
| 45 | | :view_context => _view_context, |
|---|
| 46 | | :opts => opts |
|---|
| 47 | | } |
|---|
| 48 | | engine.transform(options) |
|---|
| 49 | | end |
|---|
| 50 | | |
|---|
| 51 | | |
|---|
| 52 | | def find_template(opts={}) |
|---|
| 53 | | if action = opts[:action] |
|---|
| 54 | | path = |
|---|
| 55 | | File.expand_path(MERB_ROOT / "dist/app/views" / self.class.name.snake_case / action) |
|---|
| 56 | | elsif layout = opts[:layout] |
|---|
| 57 | | path = ancestral_trait[:layout_root] / layout |
|---|
| 58 | | else |
|---|
| 59 | | raise "called find_template without an :action or :layout" |
|---|
| 60 | | end |
|---|
| 61 | | extensions = [ancestral_trait[:template_extensions].keys].flatten.uniq |
|---|
| 62 | | glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}" |
|---|
| 63 | | Dir[glob].first |
|---|
| 64 | | end |
|---|
| 65 | | |
|---|
| 66 | | def find_partial(template, opts={}) |
|---|
| 67 | | if template =~ /\// |
|---|
| 68 | | t = template.split('/') |
|---|
| 69 | | template = t.pop |
|---|
| 70 | | path = ancestral_trait[:template_root] / t.join('/') / "_#{template}" |
|---|
| 71 | | else |
|---|
| 72 | | path = ancestral_trait[:template_root] / self.class.name.snake_case / "_#{template}" |
|---|
| 73 | | end |
|---|
| 74 | | extensions = [ancestral_trait[:template_extensions].keys].flatten.uniq |
|---|
| 75 | | glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}" |
|---|
| 76 | | Dir[glob].first |
|---|
| 122 | | if template =~ /\// |
|---|
| 123 | | t = template.split('/') |
|---|
| 124 | | template = t.pop |
|---|
| 125 | | tmpl = new_eruby_obj(template_dir(t.join('/')) / "/_#{template}.#{template_extension_for(:html)}") |
|---|
| 126 | | else |
|---|
| 127 | | tmpl = new_eruby_obj(template_dir(self.class.name.snake_case) / "/_#{template}.#{template_extension_for(:html)}") |
|---|
| | 129 | render :partial => template |
|---|
| | 130 | end |
|---|
| | 131 | |
|---|
| | 132 | private |
|---|
| | 133 | |
|---|
| | 134 | def wrap_layout(content, opts={}) |
|---|
| | 135 | return content if ((opts[:layout] == :none) || (ancestral_trait[:layout] == :none)) |
|---|
| | 136 | |
|---|
| | 137 | if ancestral_trait[:layout] != :application |
|---|
| | 138 | layout_choice = find_template(:layout => ancestral_trait[:layout]) |
|---|
| | 139 | else |
|---|
| | 140 | if name = find_template(:layout => self.class.name.snake_case) |
|---|
| | 141 | layout_choice = name |
|---|
| | 142 | else |
|---|
| | 143 | layout_choice = find_template(:layout => :application) |
|---|
| | 144 | end |
|---|
| | 145 | end |
|---|
| | 146 | |
|---|
| | 147 | _view_context.instance_variable_set('@_layout_content', content) |
|---|
| | 148 | engine = engine_for(layout_choice) |
|---|
| | 149 | options = { |
|---|
| | 150 | :file => layout_choice, |
|---|
| | 151 | :view_context => _view_context, |
|---|
| | 152 | :opts => opts |
|---|
| | 153 | } |
|---|
| | 154 | engine.transform(options) |
|---|
| 129 | | tmpl.evaluate(_view_context) |
|---|
| 130 | | end |
|---|
| 131 | | |
|---|
| 132 | | |
|---|
| 133 | | # this is the xml builder render method. This method |
|---|
| 134 | | # builds the Builder::XmlMarkup object for you and adds |
|---|
| 135 | | # the xml headers and encoding. Then it evals your template |
|---|
| 136 | | # in the context of the xml object. So your .xerb templates |
|---|
| 137 | | # will look like this: |
|---|
| 138 | | # xml.foo {|xml| |
|---|
| 139 | | # xml.bar "baz" |
|---|
| 140 | | # } |
|---|
| 141 | | def render_xml(template=current_method_name(1)) |
|---|
| 142 | | _xml_body = IO.read( File.expand_path(MERB_ROOT / "dist/app/views" / self.class.name.snake_case / "#{template}.xerb") ) |
|---|
| 143 | | headers['Content-Type'] = 'application/xml' |
|---|
| 144 | | headers['Encoding'] = 'UTF-8' |
|---|
| 145 | | _view_context.instance_eval %{ |
|---|
| 146 | | xml = Builder::XmlMarkup.new :indent => 2 |
|---|
| 147 | | xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8" |
|---|
| 148 | | #{_xml_body} |
|---|
| 149 | | return xml.target! |
|---|
| 150 | | } |
|---|
| 151 | | end |
|---|
| 152 | | |
|---|
| 153 | | # render using chunked encoding |
|---|
| 154 | | # def stream |
|---|
| 155 | | # prefix = '<p>' |
|---|
| 156 | | # suffix = "</p>\r\n" |
|---|
| 157 | | # render_chunked do |
|---|
| 158 | | # IO.popen("cat /tmp/test.log") do |io| |
|---|
| 159 | | # done = false |
|---|
| 160 | | # until done == true do |
|---|
| 161 | | # sleep 0.3 |
|---|
| 162 | | # line = io.gets.chomp |
|---|
| 163 | | # if line == 'EOF' |
|---|
| 164 | | # done = true |
|---|
| 165 | | # else |
|---|
| 166 | | # send_chunk(prefix + line + suffix) |
|---|
| 167 | | # end |
|---|
| 168 | | # end |
|---|
| 169 | | # end |
|---|
| 170 | | # end |
|---|
| 171 | | # end |
|---|
| 172 | | def render_chunked(&blk) |
|---|
| 173 | | headers['Transfer-Encoding'] = 'chunked' |
|---|
| 174 | | Proc.new { |
|---|
| 175 | | response.send_status_no_connection_close(0) |
|---|
| 176 | | response.send_header |
|---|
| 177 | | blk.call |
|---|
| 178 | | response.write("0\r\n\r\n") |
|---|
| 179 | | } |
|---|
| 180 | | end |
|---|
| 181 | | |
|---|
| 182 | | def send_chunk(data) |
|---|
| 183 | | response.write('%x' % data.size + "\r\n") |
|---|
| 184 | | response.write(data + "\r\n") |
|---|
| 185 | | end |
|---|
| | 156 | |
|---|
| | 157 | |
|---|
| | 158 | def find_template(opts={}) |
|---|
| | 159 | if action = opts[:action] |
|---|
| | 160 | path = |
|---|
| | 161 | File.expand_path(MERB_ROOT / "dist/app/views" / self.class.name.snake_case / action) |
|---|
| | 162 | elsif layout = opts[:layout] |
|---|
| | 163 | path = ancestral_trait[:layout_root] / layout |
|---|
| | 164 | else |
|---|
| | 165 | raise "called find_template without an :action or :layout" |
|---|
| | 166 | end |
|---|
| | 167 | extensions = [ancestral_trait[:template_extensions].keys].flatten.uniq |
|---|
| | 168 | glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}" |
|---|
| | 169 | Dir[glob].first |
|---|
| | 170 | end |
|---|
| | 171 | |
|---|
| | 172 | def find_partial(template, opts={}) |
|---|
| | 173 | if template =~ /\// |
|---|
| | 174 | t = template.split('/') |
|---|
| | 175 | template = t.pop |
|---|
| | 176 | path = ancestral_trait[:template_root] / t.join('/') / "_#{template}" |
|---|
| | 177 | else |
|---|
| | 178 | path = ancestral_trait[:template_root] / self.class.name.snake_case / "_#{template}" |
|---|
| | 179 | end |
|---|
| | 180 | extensions = [ancestral_trait[:template_extensions].keys].flatten.uniq |
|---|
| | 181 | glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}" |
|---|
| | 182 | Dir[glob].first |
|---|
| | 183 | end |
|---|