module Crystal module Template DIRECTORY_NAME = "views" class << self inject( :logger => :logger, :config => :config, :environment => :environment ) def render *args, &block result, context = basic_render(parse_arguments(*args), &block) result end # def render_and_return_context *args, &block # basic_render(*parse_arguments(*args), &block) # end def render_layout *args options = parse_arguments(*args) options = options.stringify_keys! options.must.include :content options.must.include :context result, context = basic_render options do |*args| if args.empty? options.content else args.size.must_be == 1 variable_name = args.first.to_s self.context.content_variables[variable_name] end end result end def exist? tname, options = {} options = options.to_openobject !!find_template(tname, calculate_prefixes(options), options.format, options.current_dir) end def read tname, options = {} options = options.to_openobject file = find_template tname, calculate_prefixes(options), options.format, options.current_dir raise "No template '#{tname}'!" unless file File.read file end def template_name_with_prefix tname, prefix index = tname.rindex('/') index = index ? index + 1 : 0 tname = tname.clone tname.insert index, prefix end def parse_arguments *args options = args.extract_options!.to_openobject if args.size == 1 options.template = args.first else raise "Invalid input" if args.size != 0 end options end def basic_render options, &block options = options.clone.to_openobject with_context options do |context| context.content_block ||= block with_scope options, context do |scope| unless file = options.file file = find_template options.template!, calculate_prefixes(options), scope.format, scope.current_dir # p [tname, calculate_prefixes(options), scope.format, scope.current_dir] raise "No template '#{options.template!}'!" unless file end scope.current_dir = dirname(file) template = create_tilt_template file result = with_template context, template do render_template template, options, &context.content_block end return result, context end end end protected def context Thread.current[:render_context] end def calculate_prefixes options options.action ? [''] : options.prefixes end def with_context options, &block context = Thread.current[:render_context] || options.context || create_context(options) old = Thread.current[:render_context] begin Thread.current[:render_context] = context block.call context ensure Thread.current[:render_context] = old end end def with_scope options, context, &block initial = context.scope_variables old = context.scope_variables || OpenObject.new begin context.scope_variables = { :current_dir => options.current_dir || old.current_dir, :format => options.format || old.format # :prefixes => options.prefixes || old.prefixes }.to_openobject block.call context.scope_variables ensure context.scope_variables = old if initial end end def with_template context, template, &block old = context._tilt_template begin context._tilt_template = template block.call context ensure context._tilt_template = old end end def create_context options context_class = options.context_class || TemplateContext raise "#{context_class} must inherit from TemplateContext!" unless context_class.is? TemplateContext context = context_class.new context.options = options if ivs = options.instance_variables (ivs.is_a?(Array) ? ivs : [ivs]).each do |container| if container.is_a? Hash container.each do |name, value| context.instance_variable_set("@#{name}", value) end else container.instance_variables.each do |ivname| iv = container.instance_variable_get(ivname) context.instance_variable_set(ivname, iv) end end end end context end def dirname_without_cache path; File.dirname path end cache_method_with_params_in_production :dirname def find_template tname, prefixes, format, current_dir # splitted into two to optimize cache if tname =~ /^\// find_absolute_template tname, prefixes, format else find_relative_template tname, prefixes, format, current_dir end end def find_absolute_template_without_cache tname, prefixes, format prefixes = prefixes || config.default_template_prefixes! prefixes.each do |prefix| tname_with_prefix = template_name_with_prefix("/#{DIRECTORY_NAME}#{tname}", prefix) file = find_file(tname_with_prefix, format, environment.directories) return file if file end return nil end cache_method_with_params_in_production :find_absolute_template def find_relative_template_without_cache tname, prefixes, format, current_dir raise "You can't use relative template path '#{tname}' without :current_dir!" unless current_dir prefixes = prefixes || config.default_template_prefixes! prefixes.each do |prefix| tname_with_prefix = template_name_with_prefix("/#{tname}", prefix) file = find_file(tname_with_prefix, format, current_dir) return file if file end return nil end cache_method_with_params_in_production :find_relative_template def find_file tname, format, directories if tname.include? '.' _find_file(tname, directories) || _find_file("#{tname}.*", directories) else if format _find_file("#{tname}.#{format}.*", directories) or _find_file("#{tname}.*", directories, true) else _find_file("#{tname}.*", directories) end end end # with_one_extension - 'tname.*' matches not only 'tname.erb' but also 'tname.html.erb', # with this option enabled it will not match 'tname.html.erb', only 'tname.erb' def _find_file pattern, directories, with_one_extension = false files = environment.find_files_by_pattern_without_cache pattern, directories files = files.select{|f| f !~ /\.[^\.\/]+\.[^\.\/]+$/} if with_one_extension logger.warn "Multiple templates for '#{pattern}'!" if files.size > 1 files.first end def create_tilt_template path template_options = { :ugly => true, :outvar => "@output" } template = Tilt.new(path, nil, template_options) end def render_template template, options, &block locals = options.locals || {} locals[:object] = options.object if options.object? template.render context, locals, &block end end end end