Class: Clamsy::Tenjin::Engine

Inherits:
Object
  • Object
show all
Defined in:
lib/clamsy/tenjin.rb

Overview

engine class for templates

Engine class supports the followings.

  • template caching

  • partial template

  • layout template

  • capturing (experimental)

ex. file ‘ex_list.rbhtml’

<ul>
{? for item in @items ?}
  <li>#{item}</li>
{? end ?}
</ul>

ex. file ‘ex_layout.rbhtml’

<html>
 <body>
  <h1>${@title}</li>
#{@_content}
{? import 'footer.rbhtml' ?}
 </body>
</html>

ex. file ‘main.rb’

require 'tenjin'
options = {:prefix=>'ex_', :postfix=>'.rbhtml', :layout=>'ex_layout.rbhtml'}
engine = Tenjin::Engine.new(options)
context = {:title=>'Tenjin Example', :items=>['foo', 'bar', 'baz']}
output = engine.render(:list, context)  # or 'ex_list.rbhtml'
print output

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Engine

initializer of Engine class.

options:

:prefix

prefix string for template name (ex. ‘template/’)

:postfix

postfix string for template name (ex. ‘.rbhtml’)

:layout

layout template name (default nil)

:path

array of directory name (default nil)

:cache

save converted ruby code into file or not (default true)

:path

list of directory (default nil)

:preprocess

flag to activate preprocessing (default nil)

:templateclass

template class object (default Tenjin::Template)



829
830
831
832
833
834
835
836
837
838
839
# File 'lib/clamsy/tenjin.rb', line 829

def initialize(options={})
  @prefix  = options[:prefix]  || ''
  @postfix = options[:postfix] || ''
  @layout  = options[:layout]
  @cache   = options.fetch(:cache, true)
  @path    = options[:path]
  @preprocess = options.fetch(:preprocess, nil)
  @templateclass = options.fetch(:templateclass, Template)
  @init_opts_for_template = options
  @templates = {}   # filename->template
end

Instance Method Details

#cachename(filename) ⇒ Object



881
882
883
# File 'lib/clamsy/tenjin.rb', line 881

def cachename(filename)
  return (filename + '.cache').untaint
end

#create_template(filename, _context = nil) ⇒ Object

create template object from file



886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
# File 'lib/clamsy/tenjin.rb', line 886

def create_template(filename, _context=nil)
  template = @templateclass.new(nil, @init_opts_for_template)
  template.timestamp = Time.now()
  cache_filename = cachename(filename)
  _context = hook_context(Context.new) if _context.nil?
  if !@cache
    input = read_template_file(filename, _context)
    template.convert(input, filename)
  elsif !test(?f, cache_filename) || File.mtime(cache_filename) < File.mtime(filename)
    #$stderr.puts "*** debug: load original"
    input = read_template_file(filename, _context)
    template.convert(input, filename)
    store_cachefile(cache_filename, template)
  else
    #$stderr.puts "*** debug: load cache"
    template.filename = filename
    load_cachefile(cache_filename, template)
  end
  return template
end

#find_template_file(template_name) ⇒ Object

find template filename

Raises:

  • (Errno::ENOENT)


848
849
850
851
852
853
854
855
856
857
858
859
# File 'lib/clamsy/tenjin.rb', line 848

def find_template_file(template_name)
  filename = to_filename(template_name)
  if @path
    for dir in @path
      filepath = "#{dir}#{File::SEPARATOR}#{filename}"
      return filepath if test(?f, filepath.untaint)
    end
  else
    return filename if test(?f, filename.dup.untaint)  # dup is required for frozen string
  end
  raise Errno::ENOENT.new("#{filename} (path=#{@path.inspect})")
end

#get_template(template_name, _context = nil) ⇒ Object

get template object



926
927
928
929
930
931
932
933
934
935
# File 'lib/clamsy/tenjin.rb', line 926

def get_template(template_name, _context=nil)
  template = @templates[template_name]
  t = template
  unless t && t.timestamp && t.filename && t.timestamp >= File.mtime(t.filename)
    filename = find_template_file(template_name)
    template = create_template(filename, _context)  # _context is passed only for preprocessor
    register_template(template_name, template)
  end
  return template
end

#hook_context(context) ⇒ Object



963
964
965
966
967
968
969
970
971
972
# File 'lib/clamsy/tenjin.rb', line 963

def hook_context(context)
  if !context
    context = Context.new
  elsif context.is_a?(Hash)
    context = Context.new(context)
  end
  context._engine = self
  context._layout = nil
  return context
end

#load_cachefile(cache_filename, template) ⇒ Object

load template from cache file



917
918
919
920
921
922
923
# File 'lib/clamsy/tenjin.rb', line 917

def load_cachefile(cache_filename, template)
  s = File.read(cache_filename)
  if s.sub!(/\A\#\@ARGS (.*?)\r?\n/, '')
    template.args = $1.split(',')
  end
  template.script = s
end

#read_template_file(filename, _context) ⇒ Object

read template file and preprocess it



862
863
864
865
866
867
868
869
870
871
872
873
# File 'lib/clamsy/tenjin.rb', line 862

def read_template_file(filename, _context)
  return File.read(filename) if !@preprocess
  _context ||= {}
  _context = hook_context(_context) if _context.is_a?(Hash) || _context._engine.nil?
  _buf = _context._buf
  _context._buf = ""
  begin
    return Preprocessor.new(filename).render(_context)
  ensure
    _context._buf = _buf
  end
end

#register_template(template_name, template) ⇒ Object

register template object



876
877
878
879
# File 'lib/clamsy/tenjin.rb', line 876

def register_template(template_name, template)
  #template.timestamp = Time.new unless template.timestamp
  @templates[template_name] = template
end

#render(template_name, context = Context.new, layout = true) ⇒ Object

get template object and evaluate it with context object. if argument ‘layout’ is true then default layout file (specified at initializer) is used as layout template, else if false then no layout template is used. if argument ‘layout’ is string, it is regarded as layout template name.



942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
# File 'lib/clamsy/tenjin.rb', line 942

def render(template_name, context=Context.new, layout=true)
  #context = Context.new(context) if context.is_a?(Hash)
  context = hook_context(context)
  while true
    template = get_template(template_name, context)  # context is passed only for preprocessor
    _buf = context._buf
    output = template.render(context)
    context._buf = _buf
    unless context._layout.nil?
      layout = context._layout
      context._layout = nil
    end
    layout = @layout if layout == true or layout.nil?
    break unless layout
    template_name = layout
    layout = false
    context.instance_variable_set('@_content', output)
  end
  return output
end

#store_cachefile(cache_filename, template) ⇒ Object

store template into cache file



908
909
910
911
912
913
914
# File 'lib/clamsy/tenjin.rb', line 908

def store_cachefile(cache_filename, template)
  s = template.script
  s = "\#@ARGS #{template.args.join(',')}\n#{s}" if template.args
  tmp_filename = "#{cache_filename}.#{rand()}"
  File.open(tmp_filename, 'w') {|f| f.write(s) }
  File.rename(tmp_filename, cache_filename)
end

#to_filename(template_name) ⇒ Object

convert short name into filename (ex. ‘:list’ => ‘template/list.rb.html’)



842
843
844
845
# File 'lib/clamsy/tenjin.rb', line 842

def to_filename(template_name)
  name = template_name
  return name.is_a?(Symbol) ? "#{@prefix}#{name}#{@postfix}" : name
end