2010-04-06 13 views
3

Je tente d'analyser, et non d'évaluer, les rails de fichiers ERB de manière Hpricot/Nokogiri. Les fichiers que j'essaie d'analyser contiennent des fragments HTML mélangés avec du contenu dynamique généré à l'aide de ERB (rails standards view files). Je cherche une bibliothèque qui analysera non seulement le contenu environnant, mais aussi le traitement de Hpricot ou Nokogiri. les symboles ERB, <%, <% = etc, comme s'ils étaient des balises html/xml.Bibliothèque pour analyser les fichiers ERB

Idéalement, je récupérerais une structure de type DOM où les symboles <%, <% = etc. seraient inclus comme leurs propres types de nœuds. Je sais qu'il est possible de pirater quelque chose ensemble en utilisant des expressions régulières, mais je cherchais quelque chose d'un peu plus fiable car je développe un outil que je dois utiliser sur une base de code de vue très large où le contenu html et le contenu erb est important.

Par exemple, des contenus tels que:

 
blah blah blah 
<div>My Great Text <%= my_dynamic_expression %></div> 

renverrait une structure arborescente comme:

 
root 
- text_node (blah blah blah) 
- element (div) 
    - text_node (My Great Text) 
     - erb_node (<%=) 

Répondre

3

J'ai finalement fini par résoudre ce problème en utilisant RLex, http://raa.ruby-lang.org/project/ruby-lex/, la version rubis de lex avec le grammeur suivant:

 
%{ 

#define NUM 257 

#define OPTOK 258 
#define IDENT 259 
#define OPETOK 260 
#define CLSTOK 261 
#define CLTOK 262 
#define FLOAT 263 
#define FIXNUM 264 
#define WORD 265 
#define STRING_DOUBLE_QUOTE 266 
#define STRING_SINGLE_QUOTE 267 

#define TAG_START 268 
#define 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 
%} 

digit [0-9] 
blank [ ] 
letter [A-Za-z] 
name1 [A-Za-z_] 
name2 [A-Za-z_0-9] 
valid_tag_character [A-Za-z0-9"'[email protected]_():/ ] 
ignore_tags style|script 
%% 

{blank}+"\n"     { return [ WHITE_SPACE, yytext ] } 
"\n"{blank}+     { return [ WHITE_SPACE, yytext ] } 
{blank}+"\n"{blank}+     { return [ WHITE_SPACE, yytext ] } 

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

^{blank}+  { return [ WHITE_SPACE, yytext ] } 

{blank}+$  { return [ WHITE_SPACE, yytext ] }; 

"" { return [ TAG_NO_TEXT_START, yytext ] } 
"" { return [ TAG_NO_TEXT_END, yytext ] } 
""     { return [ TAG_SELF_CONTAINED, yytext ] } 
"" { return [ TAG_SELF_CONTAINED, yytext ] } 
"" { return [ TAG_START, yytext ] } 
"" { return [ TAG_END, yytext ] } 

"" { return [ ERB_BLOCK_END, yytext ] } 
"" { return [ ERB_STRING_END, yytext ] } 


{letter}+  { return [ WORD, yytext ] } 


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

%% 

Ceci n'est pas une complète grammer mais pour mes fins, localiser et ré-émettre du texte, cela a fonctionné. J'ai combiné ce grammaire avec ce petit morceau de code:

 
    text_handler = MakeYourOwnCallbackHandler.new 

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

    loop do 
     a,v = l.yylex 
     break if a == 0 

     if(a < WORD) 
     text_handler.character(v.to_s, a) 
     else 
     case a 
     when WORD 
      text_handler.text(v.to_s) 
     when TAG_START 
      text_handler.start_tag(v.to_s) 
     when TAG_END 
      text_handler.end_tag(v.to_s) 
     when WHITESPACE 
      text_handler.white_space(v.to_s) 
     when ERB_BLOCK_START 
      text_handler.erb_block_start(v.to_s) 
     when ERB_BLOCK_END 
      text_handler.erb_block_end(v.to_s)  
     when ERB_STRING_START 
      text_handler.erb_string_start(v.to_s) 
     when ERB_STRING_END 
      self.text_handler.erb_string_end(v.to_s) 
     when TAG_NO_TEXT_START 
      text_handler.ignorable_tag_start(v.to_s) 
     when TAG_NO_TEXT_END 
      text_handler.ignorable_tag_end(v.to_s) 
     when STRING_DOUBLE_QUOTE 
      text_handler.string_double_quote(v.to_s) 
     when STRING_SINGLE_QUOTE 
      text_handler.string_single_quote(v.to_s) 
     when TAG_SELF_CONTAINED 
      text_handler.tag_self_contained(v.to_s) 
     end 
     end 
    end 
+0

Avez-vous eu des problèmes avec la Lexer.rb/Erblex.rb généré par rlex étant incomplet? J'ai essayé à la fois dans OS X et Ubuntu, mais le lexer RB généré se termine soudainement au milieu d'un grand bloc 'case' /' when'. J'ai essayé juste "rlex grammar" et "rlex --output LexerClassName grammar", où "grammar" correspond à un fichier nommé "grammar.rl". J'ai Ruby 1.8.7. –

+0

Salut Sarah, j'ai effectivement eu ce problème. J'ai soumis une correction de bogue au propriétaire du rlex. Je peux vous envoyer le fichier patch si vous êtes intéressé mais c'est un bug que vous devez réparer ir rlex. –

+0

Pourriez-vous publier le patch en quelque sorte? – user43685

2

J'ai récemment eu un problème similaire. L'approche que j'ai prise était d'écrire un petit script (erblint.rb) faire une substitution de chaîne pour convertir les balises ERB (<% %> et <%= %>) en balises XML, puis analyser en utilisant Nokogiri.

Voir le code ci-dessous pour voir ce que je veux dire:

#!/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 

J'ai posté comme essentiel sur Github: https://gist.github.com/787145

+0

Love it! En fait, je l'ai mis dans un commentaire html pour éviter toute interférence entre tout ce qui se trouve à l'ERB qui réordonnait html 'value = value.gsub (/ <%=(.+?)%>/m, '')' – viniciusnz