Builder Rendering Helper (.builder templates) w/ doco and tests.

This commit is contained in:
Ryan Tomayko 2008-03-08 20:40:01 -05:00
parent 327c6ece36
commit caf4857024
6 changed files with 200 additions and 1 deletions

View File

@ -405,7 +405,98 @@ module Sinatra
end
end
# Generating conservative XML content using Builder templates.
#
# Builder templates can be inline by passing a block to the builder method, or in
# external files with +.builder+ extension by passing the name of the template
# to the +builder+ method as a Symbol.
#
# === Inline Rendering
#
# If the builder method is given a block, the block is called directly with an
# +XmlMarkup+ instance and the result is returned as String:
# get '/who.xml' do
# builder do |xml|
# xml.instruct!
# xml.person do
# xml.name "Francis Albert Sinatra",
# :aka => "Frank Sinatra"
# xml.email 'frank@capitolrecords.com'
# end
# end
# end
#
# Yields the following XML:
# <?xml version='1.0' encoding='UTF-8'?>
# <person>
# <name aka='Frank Sinatra'>Francis Albert Sinatra</name>
# <email>Frank Sinatra</email>
# </person>
#
# === Builder Template Files
#
# Builder templates can be stored in separate files with a +.builder+
# extension under the view path. An +XmlMarkup+ object named +xml+ is automatically
# made available to template.
#
# Example:
# get '/bio.xml' do
# builder :bio
# end
#
# The "views/bio.builder" file might contain the following:
# xml.instruct! :xml, :version => '1.1'
# xml.person do
# xml.name "Francis Albert Sinatra"
# xml.aka "Frank Sinatra"
# xml.aka "Ol' Blue Eyes"
# xml.aka "The Chairman of the Board"
# xml.born 'date' => '1915-12-12' do
# xml.text! "Hoboken, New Jersey, U.S.A."
# end
# xml.died 'age' => 82
# end
#
# And yields the following output:
# <?xml version='1.1' encoding='UTF-8'?>
# <person>
# <name>Francis Albert Sinatra</name>
# <aka>Frank Sinatra</aka>
# <aka>Ol&apos; Blue Eyes</aka>
# <aka>The Chairman of the Board</aka>
# <born date='1915-12-12'>Hoboken, New Jersey, U.S.A.</born>
# <died age='82' />
# </person>
#
# NOTE: Builder must be installed or a LoadError will be raised the first time an
# attempt is made to render a builder template.
#
# See http://builder.rubyforge.org/ for comprehensive documentation on Builder.
module Builder
def builder(content=nil, options={}, &block)
options, content = content, nil if content.is_a?(Hash)
content = Proc.new { block } if content.nil?
render(:builder, content, options)
end
private
def render_builder(content, &b)
require 'builder'
xml = ::Builder::XmlMarkup.new(:indent => 2)
case content
when String
eval(content, binding, '<BUILDER>', 1)
when Proc
content.call(xml)
end
xml.target!
end
end
class EventContext
include ResponseHelpers
@ -413,6 +504,7 @@ module Sinatra
include RenderingHelpers
include Erb
include Haml
include Builder
attr_accessor :request, :response

101
test/builder_test.rb Normal file
View File

@ -0,0 +1,101 @@
require File.dirname(__FILE__) + '/helper'
context "Builder" do
setup do
Sinatra.application = nil
end
context "without layouts" do
setup do
Sinatra.application = nil
end
specify "should render" do
get '/no_layout' do
builder 'xml.instruct!'
end
get_it '/no_layout'
should.be.ok
body.should == %(<?xml version="1.0" encoding="UTF-8"?>\n)
end
specify "should render inline block" do
get '/no_layout_and_inlined' do
@name = "Frank & Mary"
builder do |xml|
xml.couple @name
end
end
get_it '/no_layout_and_inlined'
should.be.ok
body.should == %(<couple>Frank &amp; Mary</couple>\n)
end
end
context "Templates (in general)" do
setup do
Sinatra.application = nil
end
specify "are read from files if Symbols" do
get '/from_file' do
@name = 'Blue'
builder :foo, :views_directory => File.dirname(__FILE__) + "/views"
end
get_it '/from_file'
should.be.ok
body.should.equal %(<exclaim>You rock Blue!</exclaim>\n)
end
specify "use layout.ext by default if available" do
get '/' do
builder :foo, :views_directory => File.dirname(__FILE__) + "/views/layout_test"
end
get_it '/'
should.be.ok
body.should.equal "<layout>\n<this>is foo!</this>\n</layout>\n"
end
specify "renders without layout" do
get '/' do
builder :no_layout, :views_directory => File.dirname(__FILE__) + "/views/no_layout"
end
get_it '/'
should.be.ok
body.should.equal "<foo>No Layout!</foo>\n"
end
specify "raises error if template not found" do
get '/' do
builder :not_found
end
lambda { get_it '/' }.should.raise(Errno::ENOENT)
end
end
end

1
test/views/foo.builder Normal file
View File

@ -0,0 +1 @@
xml.exclaim "You rock #{@name}!"

View File

@ -0,0 +1 @@
xml.this "is foo!"

View File

@ -0,0 +1,3 @@
xml.layout do
xml << yield
end

View File

@ -0,0 +1 @@
xml.foo "No Layout!"