2011-12-28 15 views
11

Mam do czynienia z aplikacją internetową, która korzysta z własnego systemu szablonowego, który pozwala na osadzanie kodu Perl w kodzie HTML. Te instrukcje są wykonywane przez analizator szablonów w czasie wykonywania przy użyciu eval EXPR.Szybsza alternatywa dla eval?

Jest to bardzo elastyczne, ale te instrukcje są rozproszone wszędzie i są wykonywane jako część. eval EXPR (w przeciwieństwie do eval BLOCK) wymaga od Perla odpalania tłumacza za każdym razem, a moje profilowanie ujawnia, że ​​są one dość istotnym źródłem spowolnienia.

Wiele wbudowanych instrukcji Perl jest bardzo prostych. Na przykład, szablon może mieć linię:

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

czyli

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

Oznacza to, że są one po prostu wywołanie metody obiektu. Ale są też bardziej skomplikowane.

Mam nadzieję, że zoptymalizuję to i do tej pory mam dwie pomysły, obie są okropne. Pierwszym z nich jest przepisanie wszystkich szablonów w celu zastąpienia prostych wywołań tokenami takimi jak USER:NAME i USER:GENERATETICKETNUMBER, które parser mógłby następnie wyszukać i wywołać odpowiednią metodę obiektu. Ale zamiast zajmować się szablonami łączącymi HTML i Perl, mam szablony łączące HTML, Perl i tokeny.

Drugą ideą jest próba przeanalizowania osadzonego perla, dowiedzieć się, co chce zrobić instrukcja, a jeśli jest to wystarczająco proste, wywołać metodę odpowiedniego obiektu za pomocą odniesienia symbolicznego. To oczywiście szalone.

Czy jest jakieś logiczne rozwiązanie, które przeoczyłem?

+1

+1 za "To jest oczywiście szalone." –

Odpowiedz

9

spróbuj biorąc podejście podobne do tego, który mod_perl używa do kompilowania CGI:

  1. Konwersja szablonu do kodu Perla. Na przykład, pierwszym przykładem może konwertować do czegoś podobnego:

    print "<p>Welcome, "; 
    print $user->query('name'); 
    print ".\n"; 
    
  2. owinąć sub { ... } wokół tego kodu, wraz z kodem do rozpakowania argumentów (np, na takie rzeczy jak $user w próbce).

  3. eval ten kod. Zwróć uwagę, że zwraca on kod coderef.

  4. Zadzwoń do tego pliku coderef wielokrotnie. :)

+5

Podobnym podejściem byłoby zapisanie wygenerowanego kodu do pliku '.pm', a następnie załadowanie tego modułu. W ten sposób szablon będzie wymagał ponownej naprawy dopiero po jego zmianie. Który sposób jest lepszy, zależy od szczegółów, w jaki sposób aplikacja jest zaimplementowana. – cjm

+0

Wow, to genialne! Pozwala mi to rozwiązać problem bez konieczności przerabiania szablonów gazillion. Wypróbuję to, dziękuję! – parsim

+0

To właśnie robi [Template Toolkit] (http://template-toolkit.org). –

2

Być może zechcesz rzucić okiem na wnętrzności Text::MicroTemplate. Realistycznie, możesz chcieć użyć użyć Text :: MicroTemplate, ponieważ prawdopodobnie pasuje do twoich potrzeb. Tworzy podprocedurę, która łączy łańcuchy w razie potrzeby, podobnie jak sugerował zmierzch. Oto wynik build_mt('hello, <?= $_[0] ?>') w 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

Dzięki za pomoc! Mam nadzieję, że uda mi się uciec od tego bez konieczności wymiany obecnego systemu szablonowego, ponieważ byłoby to dość dużą pracą. Ale jeśli stanie się to zbyt wielkim problemem, przyjrzę się MicroTemplate. – parsim

3

Możecie przyjrzeć Mojolicious. Ma on kod templating engine, który pozwala na składnię zbliżoną do tego, z której korzystasz.Możesz go przełączyć, aby go użyć lub spojrzeć na jego źródło (kliknij źródło po lewej stronie poprzedniego linku), aby sprawdzić, czy możesz narysować kilka pomysłów.

FYI Składnia szablonów silnika Mojolcious umożliwia następujące formy wymieszane z HTML odpowiednio

<% 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

Dzięki! Jak napisałem do Josha, mam nadzieję, że nie będę musiał wymieniać całego systemu szablonowego, tylko dlatego, że jest to 10-letnie oprogramowanie i trudno je modyfikować. Ale słyszałem dobre rzeczy o Mojoliciu. – parsim

+0

Całkowicie rozumiem! Być może jego kod może ci pomóc z kilkoma pomysłami. –

+1

@ JoelBerger wszędzie, gdzie to pytanie otrzyma odpowiedź "CPI idiot use". +1 za bycie wspaniałym moim przyjacielem. – Hawken

0

Nie należy używać „eval” wywołać metody w szablonie. Przykro nam, że brzmi to szorstko, ale punktem oddzielonego widoku jest usunięcie kodu przetwarzania z warstwy widoku. Systemy szablonów opisane powyżej wraz z szablonem Toolkit po prostu przekazują obiekt/hash, abyś mógł uzyskać do niego dostęp.

dlaczego nie przekazać $ użytkownika jako hashref jak:

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

ten pozwoli Ci uzyskać dostęp do „name” z:

$user->{'name'}; 

Inaczej, jest prawdopodobne, że masz, że jesteś coś takiego:

  1. wywołania szablonu $ user-> query();
  2. metoda nazywa DB, aby uzyskać wartość metoda
  3. zwraca wartość

to baaaardzo dużo droższe, aby zapytania do bazy danych, niż przekazać odwołanie hash/przedmiot do szablonu. Możesz wypróbować niektóre narzędzia do profilowania kodu, takie jak Devel :: NYTProf, aby zobaczyć, jaka część wykonywania kodu naprawdę spowalnia Cię. Sceptycznie podchodzę do tego, że eval tak niszczy twój program, że musisz zoptymalizować eval. Wygląda na to, że kod wewnątrz eval jest tym, co cię spowalnia.

+0

Uruchomiłem NYTProf i wydaje się, że eval samo w sobie jest znaczącym źródłem spowolnienia. Zajmuje tylko 5-10% całkowitego czasu serwera, ale jest to także największy punkt. Naprawianie połączeń $ user-> query ($ value) za pomocą $ user -> {$ value} nie jest opłacalne, ponieważ sub-zapytanie często naprawdę robi coś poza prostym sprawdzeniem wartości. To tylko jeden z wielu połączeń. – parsim

+0

parsim, dlaczego wywołujesz podprogramy do pracy w warstwie widoku? Czy przed wykonaniem wyniku nie należy wykonać całej tej pracy? Może powinieneś rozważyć zbudowanie innego typu API? Jeśli dzwonisz z powodu interakcji użytkownika, to prawdopodobnie czas na niektóre połączenia po stronie serwera za pośrednictwem AJAX. –

+0

Odpowiedź jest taka, że ​​aplikacja internetowa nie ma architektury MVC. (Czy wspomniałem, że to prawie 10 lat?) Szablony skutecznie napędzają aplikację. Zmiana tego byłaby ogromna, więc utknąłem z tym. – parsim