浏览 4716 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-06-30
1,action_view.rb $:.unshift(File.dirname(__FILE__) + "/action_view/vendor") require 'action_view/base' require 'action_view/partials' ActionView::Base.class_eval do include ActionView::Partials end ActionView::Base.load_helpers(File.dirname(__FILE__) + "/action_view/helpers/") 还是require base.rb/partials.rb,然后helpers目录下有许多helpers模块都include进来 2,base.rb module ActionView class Base module CompiledTemplates end include CompiledTemplates class ObjectWrapper < Struct.new(:value) end def self.load_helpers(helper_dir) Dir.entries(helper_dir).sort.each do |helper_file| next unless helper_file =~ /^([a-z][a-z_]*_helper).rb$/ require File.join(helper_dir, $1) helper_module_name = $1.camelize class_eval("include ActionView::Helpers::#{helper_module_name}") if Helpers.const_defined?(helper_module_name) end end def render(options = {}, old_local_assigns = {}, &block) if options.is_a?(String) render_file(options, true, old_local_assigns) elsif options == :update update_page(&block) elsif options.is_a?(Hash) options[:locals] ||= {} options[:use_full_path] = options[:use_full_path].nil? ? true : options[:use_full_path] if options[:file] render_file(options[:file], options[:use_full_path], options[:locals]) elsif options[:partial] && options[:collection] render_partial_collection(options[:partial], options[:collection], options[:spacer_template], options[:locals]) elsif options[:partial] render_partial(options[:partial], ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals]) elsif options[:inline] render_template(options[:type] || :rhtml, options[:inline], nil, options[:locals] || {}) end end end def render_file(template_path, use_full_path = true, local_assigns = {}) @first_render ||= template_path if use_full_path template_path_without_extension, template_extension = path_and_extension(template_path) if template_extension template_file_name = full_template_path(template_path_without_extension, template_extension) else template_extension = pick_template_extension(template_path).to_s template_file_name = full_template_path(template_path, template_extension) end else template_file_name = template_path template_extension = template_path.split('.').last end template_source = nil begin render_template(template_extension, template_source, template_file_name, local_assigns) rescue Exception => e if TemplateError === e e.sub_template_of(template_file_name) raise e else raise TemplateError.new(@base_path, template_file_name, @assigns, template_source, e) end end end def render_template(template_extension, template, file_path = nil, local_assigns = {}) if handler = @@template_handlers[template_extension] template ||= read_template_file(file_path, template_extension) delegate_render(handler, template, local_assigns) else compile_and_render_template(template_extension, template, file_path, local_assigns) end end def compile_and_render_template(extension, template = nil, file_path = nil, local_assigns = {}) local_assigns = local_assigns.symbolize_keys if @@local_assigns_support_string_keys if compile_template?(template, file_path, local_assigns) template ||= read_template_file(file_path, extension) compile_template(extension, template, file_path, local_assigns) end method_name = @@method_names[file_path || template] evaluate_assigns send(method_name, local_assigns) do |*name| instance_variable_get "@content_for_#{name.first || 'layout'}" end end private def read_template_file(template_path, extension) File.read(template_path) end def create_template_source(extension, template, render_symbol, locals) if template_requires_setup?(extension) body = case extension.to_sym when :rxml "controller.response.content_type ||= 'application/xml'\n" + "xml = Builder::XmlMarkup.new(:indent => 2)\n" + template when :rjs "controller.response.content_type ||= 'text/javascript'\n" + "update_page do |page|\n#{template}\nend" end else body = ERB.new(template, nil, @@erb_trim_mode).src end @@template_args[render_symbol] ||= {} locals_keys = @@template_args[render_symbol].keys | locals @@template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h } locals_code = "" locals_keys.each do |key| locals_code << "#{key} = local_assigns[:#{key}]\n" end "def #{render_symbol}(local_assigns)\n#{locals_code}#{body}\nend" end def compile_template(extension, template, file_name, local_assigns) render_symbol = assign_method_name(extension, template, file_name) render_source = create_template_source(extension, template, render_symbol, local_assigns.keys) line_offset = @@template_args[render_symbol].size if extension case extension.to_sym when :rxml, :rjs line_offset += 2 end end begin unless file_name.blank? CompiledTemplates.module_eval(render_source, file_name, -line_offset) else CompiledTemplates.module_eval(render_source, 'compiled-template', -line_offset) end rescue Exception => e if logger logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" logger.debug "Function body: #{render_source}" logger.debug "Backtrace: #{e.backtrace.join("\n")}" end raise TemplateError.new(@base_path, file_name || template, @assigns, template, e) end @@compile_time[render_symbol] = Time.now end end end 我们看到create_template_source方法中,对rxml后缀的模板使用Builder::XmlMarkup解析,对rjs后缀的模板使用update_page语句解析,而对rhtml则使用erb来解析 而compile_template方法则是对render_source调用CompiledTemplates.module_eval,由于render_source都是一些Ruby语句,所以module_eval会解析这些语句 render方法则根据options为:file、:partial、:partial collection、:inline来分发render方法 3,erb.rb class ERB class Compiler Scanner.default_scanner = TrimScanner class Scanner SplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/ def scan; end end class TrimScanner < Scanner TrimSplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>\n)|(%>)|(\n)/ def scan(&block) @stag = nil if @percent @src.each do |line| percent_line(line, &block) end else @src.each do |line| @scan_line.call(line, &block) end end nil end def percent_line(line, &block) if @stag || line[0] != ?% return @scan_line.call(line, &block) end line[0] = '' if line[0] == ?% @scan_line.call(line, &block) else yield(PercentLine.new(line.chomp)) end end def scan_line(line) line.split(SplitRegexp).each do |token| next if token.empty? yield(token) end end end def compile(s) out = Buffer.new(self) content = '' scanner = make_scanner(s) scanner.scan do |token| if scanner.stag.nil? case token when PercentLine out.push("#{@put_cmd} #{content.dump}") if content.size > 0 content = '' out.push(token.to_s) out.cr when :cr out.cr when '<%', '<%=', '<%#' scanner.stag = token out.push("#{@put_cmd} #{content.dump}") if content.size > 0 content = '' when "\n" content << "\n" out.push("#{@put_cmd} #{content.dump}") out.cr content = '' when '<%%' content << '<%' else content << token end else case token when '%>' case scanner.stag when '<%' if content[-1] == ?\n content.chop! out.push(content) out.cr else out.push(content) end when '<%=' out.push("#{@insert_cmd}((#{content}).to_s)") when '<%#' # out.push("# #{content.dump}") end scanner.stag = nil content = '' when '%%>' content << '%>' else content << token end end end out.push("#{@put_cmd} #{content.dump}") if content.size > 0 out.close out.script end end end class ERB def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout') @safe_level = safe_level compiler = ERB::Compiler.new(trim_mode) set_eoutvar(compiler, eoutvar) @src = compiler.compile(str) @filename = nil end def result(b=TOPLEVEL_BINDING) if @safe_level th = Thread.start { $SAFE = @safe_level eval(@src, b, (@filename || '(erb)'), 1) } return th.value else return eval(@src, b, (@filename || '(erb)'), 1) end end end class ERB module Util public def html_escape(s) s.to_s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/</, "<") end alias h html_escape module_function :h module_function :html_escape def url_encode(s) s.to_s.gsub(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) } end alias u url_encode module_function :u module_function :url_encode end end 我们看到ERB初始化时调用了compiler.compile,compile方法首先调用默认的TrimScanner对模板scan并针对不同的标签进行处理,最后将结果写入Buffer 最后ERB的result方法使用Kernel#eval来处理模板文件并返回处理后的结果 Util模块定义了html_escape和url_encode方法,alias为h和u 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |