diff options
| author | Gibheer <gibheer@gmail.com> | 2012-11-22 07:32:17 +0100 | 
|---|---|---|
| committer | Gibheer <gibheer@gmail.com> | 2012-11-26 21:41:09 +0100 | 
| commit | 9e062519bae4fa85ef10c942be5e0e07b23aba50 (patch) | |
| tree | 3d97bf4172ca8c38aa6bbaf45504ebe519907fec | |
| parent | 37a4a28deec224f7cd9a5a124e22258e868913e4 (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.rb | 143 | ||||
| -rw-r--r-- | spec/unit/renderer/read_template_path_spec.rb | 42 | ||||
| -rw-r--r-- | spec/unit/renderer/template_path.rb | 8 | ||||
| -rw-r--r-- | spec/unit/renderer/transform_spec.rb | 32 | ||||
| -rw-r--r-- | spec/unit/renderer/type_map_spec.rb | 13 | 
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 | 
