5

我试图以 Hpricot/Nokogiri 类型的方式解析而不是评估 rails ERB 文件。我尝试解析的文件包含与使用 ERB(标准 Rails 视图文件)生成的动态内容混合的 HTML 片段ERB 符号、<%、<%= 等,就好像它们是 html/xml 标记一样。

理想情况下,我会得到一个类似 DOM 的结构,其中 <%、<%= 等符号将作为它们自己的节点类型包含在内。

我知道可以使用正则表达式一起破解某些东西,但我正在寻找更可靠的东西,因为我正在开发一个工具,我需要在一个非常大的视图代码库上运行,其中 html 内容和 erb 内容是重要的。

例如,内容如:

等等等等等等
<div>我的好文<%= my_dynamic_expression %></div>

将返回一个树结构,如:

根
 - text_node(等等等等)
 - 元素(div)
    - text_node(我的好文)
        - erb_node (<%=)
4

2 回答 2

5

我最终通过使用 RLex 解决了这个问题,http: //raa.ruby-lang.org/project/ruby-lex/是 lex 的 ruby​​ 版本,语法如下:

%{

#define 数字 257

#define OPTOK 258
#define IDENT 259
#define OPETOK 260
#define CLSTOK 261
#定义CLTOK 262
#define 浮点数 263
#define FIXNUM 264
#定义字265
#define STRING_DOUBLE_QUOTE 266
#define STRING_SINGLE_QUOTE 267

#define TAG_START 268
#定义 TAG_END 269
#define TAG_SELF_CONTAINED 270
#define ERB_BLOCK_START 271
#define ERB_BLOCK_END 272
#define ERB_STRING_START 273
#define ERB_STRING_END 274
#define TAG_NO_TEXT_START 275
#define TAG_NO_TEXT_END 276
#define WHITE_SPACE 277
%}

数字 [0-9]
空白的 [ ]
字母 [A-Z-Z]
name1 [A-Za-z_]
名称2 [A-Za-z_0-9]
valid_tag_character [A-Za-z0-9"'=@_():/ ]
忽略标签样式|脚本
%%

{空白}+"\n" { 返回 [ WHITE_SPACE, yytext ] }
"\n"{空白}+ { 返回 [ WHITE_SPACE, yytext ] }
{空白}+"\n"{空白}+ { 返回 [ WHITE_SPACE, yytext ] }

"\r" { 返回 [ WHITE_SPACE, yytext ] }
"\n" { return[ yytext[0], yytext[0..0] ] };
"\t" { return[ yytext[0], yytext[0..0] ] };

^{空白}+ { 返回 [ WHITE_SPACE, yytext ] }

{空白}+$ { 返回 [ WHITE_SPACE, yytext ] };

"" { 返回 [TAG_NO_TEXT_START, yytext] }
"" { 返回 [TAG_NO_TEXT_END, yytext] }
“”{返回[TAG_SELF_CONTAINED,yytext]}
“”{返回[TAG_SELF_CONTAINED,yytext]}
"" { 返回 [ TAG_START, yytext ] }
"" { 返回 [TAG_END, yytext ] }

"" { 返回 [ERB_BLOCK_END, yytext ] }
"" { 返回 [ERB_STRING_END, yytext] }


{字母}+ { 返回 [ WORD, yytext ] }


\".*\" { 返回 [ STRING_DOUBLE_QUOTE, yytext ] }
'.*' { 返回 [ STRING_SINGLE_QUOTE, yytext ] }
. { 返回 [ yytext[0], yytext[0..0] ] }

%%

这不是一个完整的语法,但出于我的目的,定位和重新发送文本,它有效。我将那个语法与一小段代码结合起来:

    text_handler = MakeYourOwnCallbackHandler.new

    l = Erblex.new
    l.yyin = File.open(file_name, "r")

    循环做
      a,v = l.yylex
      如果 a == 0 则中断

      如果(一个<字)
        text_handler.character(v.to_s, a)
      别的
        案例一
        当 WORD
          text_handler.text(v.to_s)
        当 TAG_START
          text_handler.start_tag(v.to_s)
        当TAG_END
          text_handler.end_tag(v.to_s)
        当空白
          text_handler.white_space(v.to_s)
        当 ERB_BLOCK_START
          text_handler.erb_block_start(v.to_s)
        当 ERB_BLOCK_END
          text_handler.erb_block_end(v.to_s)      
        当 ERB_STRING_START
          text_handler.erb_string_start(v.to_s)
        当 ERB_STRING_END
          self.text_handler.erb_string_end(v.to_s)
        当 TAG_NO_TEXT_START
          text_handler.ignorable_tag_start(v.to_s)
        当TAG_NO_TEXT_END
          text_handler.ignorable_tag_end(v.to_s)
        当 STRING_DOUBLE_QUOTE
          text_handler.string_double_quote(v.to_s)
        当 STRING_SINGLE_QUOTE
          text_handler.string_single_quote(v.to_s)
        当 TAG_SELF_CONTAINED
          text_handler.tag_self_contained(v.to_s)
        结尾
      结尾  
    结尾
于 2010-04-09T22:46:41.780 回答
2

我最近遇到了类似的问题。我采用的方法是编写一个小脚本 (erblint.rb) 进行字符串替换以将 ERB 标记 (<% %><%= %>) 转换为 XML 标记,然后使用 Nokogiri 进行解析。

请参阅以下代码以了解我的意思:

#!/usr/bin/env ruby
require 'rubygems'
require 'nokogiri'

# This is a simple program that reads in a Ruby ERB file, and parses
# it as an XHTML file. Specifically, it makes a decent attempt at
# converting the ERB tags (<% %> and <%= %>) to XML tags (<erb-disp/>
# and <erb-eval/> respectively.
#
# Once the document has been parsed, it will be validated and any
# error messages will be displayed.
#
# More complex option and error handling is left as an exercise to the user.

abort 'Usage: erb.rb <filename>' if ARGV.empty?

filename = ARGV[0]

begin
  doc = ""
  File.open(filename) do |file|
    puts "\n*** Parsing #{filename} ***\n\n"
    file.read(nil, s = "")

    # Substitute the standard ERB tags to convert them to XML tags
    #   <%= ... %> for <erb-disp> ... </erb-disp>
    #   <% ... %>  for <erb-eval> ... </erb-eval>
    #
    # Note that this won't work for more complex expressions such as:
    #   <a href=<% @some_object.generate_url -%> >link text</a>
    # Of course, this is not great style, anyway...
    s.gsub!(/<%=(.+?)%>/m, '<erb-disp>\1</erb-disp>')
    s.gsub!(/<%(.+?)%>/m, '<erb-eval>\1</erb-eval>')
    doc = Nokogiri::XML(s) do |config|
      # put more config options here if required
      # config.strict
    end
  end

  puts doc.to_xhtml(:indent => 2, :encoding => 'UTF-8')
  puts "Huzzah, no errors!" if doc.errors.empty?

  # Otherwise, print each error message
  doc.errors.each { |e| puts "Error at line #{e.line}: #{e}" }
rescue
  puts "Oops! Cannot open #{filename}"
end

我已将此作为要点发布在 Github 上:https ://gist.github.com/787145

于 2011-01-20T00:08:41.670 回答