aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGibheer <gibheer@gmail.com>2012-11-22 07:32:17 +0100
committerGibheer <gibheer@gmail.com>2012-11-26 21:41:09 +0100
commit9e062519bae4fa85ef10c942be5e0e07b23aba50 (patch)
tree3d97bf4172ca8c38aa6bbaf45504ebe519907fec
parent37a4a28deec224f7cd9a5a124e22258e868913e4 (diff)
first commit for the new renderer
This part handles the building of the template tree, which then gets used later by the containers.
-rw-r--r--lib/zero/renderer.rb143
-rw-r--r--spec/unit/renderer/read_template_path_spec.rb42
-rw-r--r--spec/unit/renderer/template_path.rb8
-rw-r--r--spec/unit/renderer/transform_spec.rb32
-rw-r--r--spec/unit/renderer/type_map_spec.rb13
5 files changed, 121 insertions, 117 deletions
diff --git a/lib/zero/renderer.rb b/lib/zero/renderer.rb
index 40a9bed..3e9f790 100644
--- a/lib/zero/renderer.rb
+++ b/lib/zero/renderer.rb
@@ -1,98 +1,85 @@
module Zero
- class FileNotFoundError < IOError; end
- # This class helps with rendering of content.
+ # the base renderer for getting render containers
#
- # The purpose of this class is to render templates. All variables pushed into
- # the renderer should be already processed, so that the raw data can be used.
+ # This class handles templates and render coontainers, which can be used for
+ # the actual rendering.
#
- # The workflow of this class is like the following.
+ # To use this renderer you have to give it a template path and optionally
+ # a map of shorthand type descriptions to fully types. This will then be used
+ # to extend the internal map of templates to possible formats in a way, that
+ # you will be able to answer xhtml and html requests with the same template.
#
- # * setup the type mapping
- # * create a new instance of the class to prepare rendering
- # * call #render to process the template
+ # When the object is initialized and you are sure, everything is loaded, call
+ # #read_template_path! and the template tree will be built. Without this step,
+ # you will probably don't get any output.
#
- # The call to #render will return the String representation of the template
- # with all data given.
+ # After the setup, the renderer can be used to build render containers, which
+ # then can be used to actually render something.
class Renderer
- class << self
- # set a base path for template search
- # @param path [String] the path to the template base dir
- def template_path=(path)
- @@path = path + '/'
- end
-
- # save a mapping hash for the type
- #
- # With that it is possible to map long and complex contant types to simpler
- # representations. These get then used in the finding process for the best
- # fitting template.
- #
- # @example
- # Zero::Renderer.map = {'text/html' => 'html'}
- #
- # @param map [Hash] maps the content type to a simple representation
- def type_map=(map)
- @@map = map
- end
-
- # returns the type map
- # @return [Hash] the mapping for types
- def type_map
- @@map ||= {}
- end
- end
-
- # take the path and render the template within the context
- # @param path [String] the relative path to the template
- # @param context [Object] the object to process on
- # @param accept_types
- def initialize(path, context, accept_types)
- accept_types ||= Request::Accept.new('text/html')
- @path = find_template(path, accept_types)
- @context = context
- end
-
- # render the template within the context
- # @return [String] the rendered template
- def render
- Tilt.new(@path).render(@context)
+ # initializes a new Renderer
+ #
+ # This method takes a path to the base template directory and a type map.
+ # 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 renderer with a small map
+ # Renderer.new('app', {
+ # 'html' => ['text/html', 'application/html+xml'],
+ # 'json' => ['application/json', 'application/aweomse+json']
+ # })
+ #
+ # @param [String] a string to templates
+ def initialize(template_path, type_map = {})
+ @template_path = template_path + '/'
+ @type_map = type_map
end
- private
-
- # check if the template does exist
+ # returns the hash of type conversions
+ # @return [Hash] type conversion
+ attr_reader :type_map
+ # get the path to the templates
+ # @return [String] the base template path
+ attr_reader :template_path
+ # get the tree of templates
# @api private
- # @param template_path [String] the relative path to the template
- # @param types [Array] a sorted list of types to search for
- # @return [String] a file name to use
- def find_template(template_path, types)
- types.each do |type|
- Dir[@@path + template_path + '.' + transform(type) + '.*'].each do |file|
- return file
+ # @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.
+ def read_template_path!
+ @templates = Hash.new do |hash, key|
+ subtree = {}
+ search_files(key).each do |file|
+ parts = file.split('.')
+ read_type(parts[2]).each do |type|
+ subtree[type] = file
+ end
end
+ hash[key] = subtree
end
- raise FileNotFoundError.new("Template '#{template_path}' not found!")
+ self
end
- # @see transform
- # @api private
- def transform(string)
- self.class.transform(string)
+ private
+
+ def search_files(template_name)
+ Dir[template_path + template_name + '**/*.*']
end
- # transform a type into a simpler representation
- # @api private
- # @param string [String] the original type name
- # @return [String] the shorter representation or the original
- def self.transform(string)
- return type_map[string] if type_map.has_key?(string)
- string
+ def read_type(short_notation)
+ to_type_list(type_map[short_notation] || short_notation)
end
- # an alias to Renderer.map
- # @api private
- def map
- self.class.map
+ def to_type_list(original_map)
+ return original_map if original_map.respond_to?(:each)
+ [original_map]
end
end
end
diff --git a/spec/unit/renderer/read_template_path_spec.rb b/spec/unit/renderer/read_template_path_spec.rb
new file mode 100644
index 0000000..8522007
--- /dev/null
+++ b/spec/unit/renderer/read_template_path_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+
+describe Zero::Renderer, 'read_template_path!' do
+ subject { Zero::Renderer.new(template_path, type_map) }
+ let(:template_path) { 'foo' }
+ let(:file_list) { ['./foo/welcome/index.html.erb'] }
+
+ before :each do
+ subject.stub(:search_files).and_return(file_list)
+ end
+
+ shared_examples_for 'a template loader' do
+ it 'creates a template tree' do
+ subject.read_template_path!
+ subject.templates['welcome/index'].should eq(result)
+ end
+ end
+
+ context 'without mapping' do
+ let(:type_map) { {} }
+ let(:result) { { 'html' => './foo/welcome/index.html.erb' } }
+
+ it_behaves_like 'a template loader'
+ end
+
+ context 'with a single mapping' do
+ let(:type_map) { {'html' => 'text/html' } }
+ let(:result) { { 'text/html' => './foo/welcome/index.html.erb' } }
+
+ it_behaves_like 'a template loader'
+ end
+
+ context 'with multiple mappings' do
+ let(:type_map) { {'html' => ['text/html', 'text/xml'] } }
+ let(:result) { {
+ 'text/html' => './foo/welcome/index.html.erb',
+ 'text/xml' => './foo/welcome/index.html.erb'
+ } }
+
+ it_behaves_like 'a template loader'
+ end
+end
diff --git a/spec/unit/renderer/template_path.rb b/spec/unit/renderer/template_path.rb
new file mode 100644
index 0000000..261faa8
--- /dev/null
+++ b/spec/unit/renderer/template_path.rb
@@ -0,0 +1,8 @@
+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
diff --git a/spec/unit/renderer/transform_spec.rb b/spec/unit/renderer/transform_spec.rb
deleted file mode 100644
index fce6730..0000000
--- a/spec/unit/renderer/transform_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require 'spec_helper'
-
-describe Zero::Renderer, '.transform' do
- subject { Zero::Renderer }
- let(:map) {{ 'text/html' => 'html' }}
-
- shared_examples_for 'a transformer' do
- before :each do
- Zero::Renderer.type_map = map
- end
-
- after :each do
- Zero::Renderer.type_map = {}
- end
-
- it "transforms a string" do
- subject.transform(original).should eq(result)
- end
- end
-
- context "with a shortable type" do
- let(:original) { 'text/html' }
- let(:result) { 'html' }
- it_behaves_like 'a transformer'
- end
-
- context "with an unshortable type" do
- let(:original) { 'application/json' }
- let(:result) { 'application/json' }
- it_behaves_like 'a transformer'
- end
-end
diff --git a/spec/unit/renderer/type_map_spec.rb b/spec/unit/renderer/type_map_spec.rb
index f9839e7..f0b86ad 100644
--- a/spec/unit/renderer/type_map_spec.rb
+++ b/spec/unit/renderer/type_map_spec.rb
@@ -1,10 +1,9 @@
require 'spec_helper'
-describe Zero::Renderer, '.type_map' do
- subject { Zero::Renderer }
- let(:mapping) {{ 'test/foo' => 'foo' }}
- it "saves the map" do
- subject.type_map = mapping
- subject.type_map.should be(mapping)
- end
+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) }
end