0
0
Fork 0

reworked the renderer

The renderer is now a bit smaller and asks the TemplateFinder for the
actual resources. That way it can just concentrate on rendering instead
of finding out, which stuff actually exists and which not.
This commit is contained in:
Gibheer 2013-02-27 22:27:02 +01:00
parent 714c540e4b
commit 7fd2f6b25b
8 changed files with 111 additions and 119 deletions

View File

@ -24,77 +24,68 @@ module Zero
# This type map is used to extend the possible renderings for different
# types, which the clients sends.
#
# @example create a simple renderer
# Renderer.new('app/templates')
# @example create a simple renderer with a mapping from html to text/html
# Renderer.new('app/templates', {'html' => 'text/html'})
#
# @example create a renderer with a small map
# Renderer.new('app', {
# 'html' => ['text/html', 'application/html+xml'],
# 'json' => ['application/json', 'application/aweomse+json']
# })
# @example create a renderer with a specific layout
# Renderer.new('app'. 'layouts/layout', {'html' => 'text/html'})
#
# @param template_path [String] a string to templates
# @param type_map [Hash] a map of simple types to complex ones
def initialize(template_path, type_map = {})
@template_path = template_path
@type_map = type_map
# @param path [String] the relative path to templates
# @param layout [String] the key of the layouts
# @param types [Hash{String => Array}] a map of short type names to long ones
def initialize(path, layout = 'layout', types)
@path = path
@layout = layout
@types = types
end
# returns the hash of type conversions
# @return [Hash] type conversion
attr_reader :type_map
attr_reader :types
# returns the key for layout files
# @return [String] the key for layouts
attr_reader :layout
# get the path to the templates
# @return [String] the base template path
attr_reader :template_path
attr_reader :path
# get the tree of templates
#
# This function returns all templates with their keys.
# @api private
# @return [Hash] the template tree
attr_reader :templates
# load the template tree
#
# This method gets all templates in the `template_path` and builds an
# internal tree structure, where templates and types direct the request to
# the wanted template.
# @return [Self] returns the object
def read_template_path!
@templates = TemplateFinder.new(template_path, @type_map).get_templates
def templates
@templates ||= TemplateFinder.new(path, types)
end
# render a template
#
# This method will render the given template, based on the type in the given
# context.
# @param name [String] the name of the template
# @param type [Array] a list of accept types used to find the template
# @param context [Object] the context in which to evaluate the template
# @return [String] the rendered content
def render(name, type, context)
template(name, type).render(context)
# This method will render the template according to the requested types.
# @param template [String] the template to render
# @param types [Array<String>] a list of types requested to render
# @param context [Object] any object to use for rendering
# @return [String] the result of rendering
def render(template, types, context)
unless templates.exist_for_types?(layout, types)
return load_template(template, types).render(context)
end
load_layout_template(types).render(context) do
load_template(template, types).render(context)
end
end
private
# get the prepared template for the name and type
# @api private
# @param name [String] the name of the template
# @param types [Array] the types for the template
# @return [Tilt::Template] a prepared tilt template
def template(name, types)
if templates.has_key? name
types.each do |type|
template = templates[name][type]
unless template.nil?
# TODO Will be implemented later
# return template if template.kind_of?(Tilt::Template)
return Tilt.new(template)
end
end
raise ArgumentError.new(
"No template found for any of this types #{types.join ', '}!"
)
end
raise ArgumentError.new "No template found for '#{name}'!"
def load_template(template, types)
templates.get(template, types)
end
def load_layout_template(types)
templates.get(layout, types)
end
end
end

View File

@ -0,0 +1,3 @@
special layout loaded
<%= yield %>

View File

@ -0,0 +1,11 @@
require 'spec_helper'
describe Zero::Renderer, '#initialize' do
subject { described_class.new(template_path, type_map) }
let(:template_path) { 'foo/' }
let(:type_map) { {'html' => ['text/html'] } }
its(:path) { should be(template_path) }
its(:layout) { should match(/layout/) }
its(:types) { should be(type_map) }
end

View File

@ -1,16 +0,0 @@
require 'spec_helper'
describe Zero::Renderer, '#read_template_path!' do
subject { Zero::Renderer.new(template_path, type_map) }
let(:template_path) { 'spec/fixtures/templates/' }
let(:type_map) { {'html' => ['text/html']} }
let(:result) { {
'text/html' => template_path + 'index.html.erb',
'json' => template_path + 'index.json.erb'
} }
it "loads the templates" do
subject.read_template_path!
expect(subject.templates['index']).to eq(result)
end
end

View File

@ -1,7 +1,7 @@
require 'spec_helper'
describe Zero::Renderer, '#render' do
subject { Zero::Renderer.new(template_path, type_map) }
subject { described_class.new(template_path, type_map) }
let(:template_path) { 'spec/fixtures/templates/' }
let(:type_map) {{
'html' => ['text/html', 'text/xml', '*/*'],
@ -12,43 +12,54 @@ describe Zero::Renderer, '#render' do
let(:foo_types) { ['foo/bar', 'bar/foo'] }
let(:binding) { SpecTemplateContext.new('foo') }
before :each do
subject.read_template_path!
context 'with default layout' do
it 'returns a tilt template' do
subject.render('index', html_types, binding).should be_kind_of(String)
end
it 'renders html content' do
subject.render('index', html_types, binding).should match('success')
end
it 'returns a tilt template for different types' do
subject.render('index', json_types, binding).should be_kind_of(String)
end
it 'renders json content' do
subject.render('index', json_types, binding).should match("{text: 'success'}")
end
it 'returns an ArgumentError, if given template does not exist' do
expect {
subject.render('foobar', html_types, binding)
}.to raise_error(ArgumentError, /Template 'foobar' does not exist/)
end
it 'returns an ArgumentError, if no template fits types' do
expect {
subject.render('index', foo_types, binding)
}.to raise_error(
ArgumentError, /Template 'index' not found/)
end
it 'uses the context' do
subject.render('context', html_types, binding).should match('foo')
end
end
it 'returns a tilt template' do
subject.render('index', html_types, binding).should be_kind_of(String)
end
context 'with special layout' do
subject { described_class.new(template_path, layout, type_map) }
let(:layout) { 'special_layout' }
it 'renders html content' do
subject.render('index', html_types, binding).should match('success')
end
it 'returns a tilt template for different types' do
subject.render('index', json_types, binding).should be_kind_of(String)
end
it 'renders json content' do
subject.render('index', json_types, binding).should match("{text: 'success'}")
end
it 'returns an ArgumentError, if given template does not exist' do
expect {
subject.render('foobar', html_types, binding)
}.to raise_error(ArgumentError, "No template found for 'foobar'!")
end
it 'returns an ArgumentError, if no template fits types' do
expect {
subject.render('index', foo_types, binding)
}.to raise_error(
ArgumentError,
"No template found for any of this types #{foo_types.join ', '}!"
)
end
it 'uses the context' do
subject.render('context', html_types, binding).should match('foo')
it 'uses the layout for rendering' do
expect(subject.render('index', html_types, binding)
).to match(/layout loaded/)
end
it 'renders the template into the layout' do
expect(subject.render('index', html_types, binding)
).to match(/success/)
end
end
end

View File

@ -1,8 +0,0 @@
require 'spec_helper'
describe Zero::Renderer, '#template_path' do
subject { Zero::Renderer.new(template_path) }
let(:template_path) { 'foo' }
its(:type_map) { should be(template_path) }
end

View File

@ -0,0 +1,13 @@
require 'spec_helper'
describe Zero::Renderer, '#templates' do
let(:object) { described_class.new(template_path, types) }
subject { object.templates }
let(:template_path) { 'spec/fixtures/templates/' }
let(:types) { {'html' => ['text/html']} }
it 'loads the template tree' do
expect(subject).to be_kind_of(Zero::Renderer::TemplateFinder)
end
end

View File

@ -1,13 +0,0 @@
require 'spec_helper'
describe Zero::Renderer, '#type_map' do
subject { Zero::Renderer.new(template_path, type_map) }
let(:template_path) { 'foo' }
let(:type_map) { {'html' => ['text/html']} }
its(:type_map) { should be(type_map) }
it 'returns an empty Hash, if type_map is not set while initialization' do
Zero::Renderer.new(template_path).type_map.should eq({})
end
end