2011-12-28 23 views
11

Estoy lidiando con una aplicación web que usa un sistema de creación de plantillas propio que permite que el código Perl se incruste en HTML. Estas declaraciones las ejecuta el analizador de plantillas en tiempo de ejecución usando eval EXPR.¿Una alternativa más rápida a eval?

Esto es muy flexible, pero estas declaraciones están dispersas en todas partes, y se ejecuta un lote. eval EXPR (a diferencia de eval BLOCK) requiere Perl para encender el intérprete cada vez, y mi perfil revela que son una fuente razonablemente importante de desaceleración.

Muchas de las sentencias Perl incrustadas son muy simples. Por ejemplo, una plantilla podría tener una línea como esta:

<p>Welcome, <!--E: $user->query('name') -->. 

O:

<p>Ticket number <!--E: $user->generate_ticket_number() --> has been generated. 

Es decir, sólo están llamando a métodos de objeto. Aunque hay otros más complicados también.

Espero optimizar esto, y hasta ahora tengo dos ideas, ambas son terribles. El primero es reescribir todas las plantillas para reemplazar las llamadas simples con tokens como USER:NAME y USER:GENERATETICKETNUMBER, que el analizador podría entonces buscar e invocar el método de objeto apropiado. Pero luego, en lugar de lidiar con plantillas que combinan HTML y Perl, tendría plantillas que combinan HTML, Perl y tokens.

La segunda idea es intentar analizar el Perl incrustado, descubrir qué quiere hacer la afirmación y, si es lo suficientemente simple, llamar al método de objeto apropiado mediante una referencia simbólica. Esto es obviamente una locura.

¿Hay alguna solución lógica que tenga en cuenta?

+1

+1 para "Esto es obviamente una locura". –

Respuesta

9

trate de tomar un enfoque similar a la que mod_perl utiliza para compilar los CGI:

  1. convertir el código de plantilla en Perl. Por ejemplo, el primer ejemplo podría convertir en algo como:

    print "<p>Welcome, "; 
    print $user->query('name'); 
    print ".\n"; 
    
  2. Envuelva un sub { ... } alrededor de ese código, junto con algo de código para desempaquetar argumentos (por ejemplo, para cosas como $user en la muestra).

  3. eval ese código. Tenga en cuenta que devuelve un valor de código.

  4. Llamar a ese coderef repetidamente. :)

+5

Un enfoque relacionado sería escribir el código generado en un archivo '.pm' y luego cargar ese módulo. De esta forma, la plantilla solo debería repararse cuando cambie. Qué camino es mejor depende de los detalles de cómo se implementa la aplicación. – cjm

+0

Guau, eso es genial! Me permite solucionar el problema sin tener que volver a escribir un montón de plantillas. Voy a probarlo, gracias! – parsim

+0

Esto es exactamente lo que hace [Template Toolkit] (http://template-toolkit.org). –

2

Es posible que desee mirar las entrañas de Text::MicroTemplate. De manera realista, es posible que desee usar Text :: MicroTemplate, ya que es probable que se ajuste a sus necesidades. Construye una subrutina que concatena cadenas según sea necesario, al igual que sugirió el atolladero. Aquí está el resultado de build_mt('hello, <?= $_[0] ?>') en re.pl:

$CODE1 = sub { 
     package Devel::REPL::Plugin::Packages::DefaultScratchpad; 
     use warnings; 
     use strict 'refs'; 
     local $SIG{'__WARN__'} = sub { 
     print STDERR $_mt->_error(shift(), 4, $_from); 
     } 
     ; 
     Text::MicroTemplate::encoded_string(sub { 
     my $_mt = ''; 
     local $_MTREF = \$_mt; 
     my $_from = ''; 
     $_mt .= 'hello, '; 
     $_from = $_[0]; 
     $_mt .= ref $_from eq 'Text::MicroTemplate::EncodedString' ? $$_from : do { 
      $_from =~ s/([&><"'])/$Text::MicroTemplate::_escape_table{$1};/eg; 
      $_from 
     }; 
     return $_mt; 
     } 
     ->(@_)); 
    }; 
+0

¡Gracias por la ayuda! Espero escapar de esto sin tener que reemplazar el sistema de plantillas actual, ya que sería un trabajo bastante grande. Pero si se convierte en un problema, miraré MicroTemplate. – parsim

3

Es posible echar un vistazo a Mojolicious. Tiene un templating engine que permite una sintaxis cercana a la que está utilizando.Posiblemente pueda cambiar para usarlo o ver su fuente (haga clic en la fuente a la izquierda del enlace anterior) para ver si puede dibujar algunas ideas.

FYI sintaxis del motor de plantillas Mojolcious permite las siguientes formas entremezcladas con HTML apropiada

<% Perl code %> 
<%= Perl expression, replaced with result %> 
<%== Perl expression, replaced with XML escaped result %> 
<%# Comment, useful for debugging %> 
<%% Replaced with "<%", useful for generating templates %> 
% Perl code line, treated as "<% line =%>" 
%= Perl expression line, treated as "<%= line %>" 
%== Perl expression line, treated as "<%== line %>" 
%# Comment line, treated as "<%# line =%>" 
%% Replaced with "%", useful for generating templates 
+0

Gracias! Como publiqué a Josh, espero evitar tener que reemplazar todo el sistema de plantillas, solo porque es un software de 10 años de antigüedad y es difícil de modificar. Pero he escuchado cosas buenas sobre Mojolicious. – parsim

+0

¡Lo entiendo totalmente! Aún así, quizás su código podría ayudarlo con algunas ideas. –

+1

@JoelBerger en cualquier otro lugar esta pregunta obtendría una respuesta de "CPAN de uso idiota". +1 por ser increíble mi amigo. – Hawken

0

No se debe utilizar 'eval' para llamar a métodos en su plantilla. Lamento sonar duro, pero el objetivo de una vista separada es eliminar el código de procesamiento de la capa de visualización. Los sistemas de plantillas descritos anteriormente junto con Template Toolkit simplemente transfieren un objeto/hash para que pueda acceder a él.

por qué no pase de $ usuario como hashref como:

$user = { 
     'name' => 'John', 
     'id' => '3454' 
     }; 

esto le permitirá acceder a 'nombre' con:

$user->{'name'}; 

De lo contrario, lo más probable es que usted tiene que eres haciendo algo como:

  1. plantilla llama $ user-> query();
  2. método
  3. llama DB para obtener el valor
  4. devuelve el método valoran

Eso es tan mucho más caro para hacer una consulta a la base de pasar la referencia a un hash/objeto a la plantilla. Es posible que desee consultar algunas herramientas de creación de perfiles de código como Devel :: NYTProf para ver qué parte de la ejecución del código realmente le está ralentizando. Soy escéptico de que la evaluación esté empantanando tanto su programa que necesite optimizar su evaluación. Parece que el código dentro de eval es lo que te está ralentizando.

+0

Ejecuté NYTProf, y parece indicar que la evaluación en sí es una fuente importante de desaceleración. Solo consume del 5 al 10% del tiempo total del servidor, pero también es el punto más importante. No es realmente viable reemplazar las llamadas $ user-> query ($ value) con $ user -> {$ value} ya que el sub & query a menudo realmente hace algo más que simplemente buscar el valor. Además, esa es solo una llamada entre muchas. – parsim

+0

parsim, ¿por qué estás llamando a subrutinas para trabajar en la capa de vista? ¿No debería hacerse todo ese trabajo antes de enviar el resultado? ¿Quizás necesites considerar construir un tipo diferente de API? Si llama debido a la interacción del usuario, es probable que sea el momento para algunas llamadas del lado del servidor a través de AJAX. –

+0

La respuesta es que la aplicación web no tiene una arquitectura MVC. (¿Mencioné que tiene casi 10 años?) Las plantillas dirigen la aplicación de manera efectiva. Sería un trabajo enorme cambiar esto, así que estoy atascado con eso. – parsim

Cuestiones relacionadas