Jakiś czas temu, zagłębiając się w Sinatre stanąłem przed problemem jak uzyskać dostęp do zmiennych instancyjnych z poziomu CoffeeScriptu?. Czytając dokumentację zauważyłem, że szablony są przetwarzane w tym
w tym samym kontekście, metody obsługujące połączenie (ang:
route handlers) więc od strony "technicznej" nie powinno być problemu, pozostało tylko pytanie jak przetworzyć kod rubiego (a tym samym zmienne) w pliku CoffeeScritp?
Odpowiedz okazałą się zaskakująca, a mianowicie zważywszy na fakt, że wybierająca silnik, tj.: haml, coffee, erb do przetworzenia templetu, oprócz klucza wskazującego plik z katalogu "views", przyjmuje również także zwykły ciąg znaków (ang
embedded template)! Więc wystarczy napisać coś w stylu:
server.rb:
require 'sinatra'
get '/script.js' do
@msg = " 'Dynamicly created JS! Yea!' "
coffee( erb(:script) )
end
views/script.erb:
i=10
alert <%= @msg %>
i odpowiedz, jaką dostaniemy po skierowaniu przeglądarki na adres
/script.js to:
(function() {
var i;
i = 10;
alert('Dynamicly created JS! Yea!');}).call(this);
Ha! A więc działa! Tyle tylko, że jest to bardzo brzydkie rozwiązanie, bo żądamy pliku
js, przetwarzamy plik
script.erb, który tak naprawdę jest plikiem CoffeeScript...
Rozwiązaniem było by zmuszenie Sinatry do rozpoznawania plików
coffee.erb jako szablonu z wbudowanym kodem rubiego i przetworzeniem go najpierw za pomocą silnika
erb, lub dodaniem nowej opcji wymuszającej przewożenie templetu za pomocą silnika
erb, tj. żeby było możliwe wywołanie templetu
coffee z dodatkową opcją:
coffee :script, :preprocess => :erb
W dalszej części skupmy się na tej drugiej opcji, gdyż wydaje się programistycznie ciekawsza ;)
Słowem wstępu, zaznaczyć należy, że wszystkie templety dostępne w Sinatrze, tj:
haml, erb, coffee, itdmają taką samą listę przyjmowanych argumentów (na przykładzie
erb ):
erb :template_key, options={}, locals={}
Co nie znaczy, że Hash
locals, który powinien być wykorzystany do przekazania zmiennych do templety jest zawsze wykorzystywany/zaimplementowany;) Tak też właśnie jest w przypadku silnika
coffee
Przeglądając kod Sinatry zauważyłem, że żeby dodać nową opcję do Hasha
optionswystarczy zmodyfikować metodę
coffee (okolice linijki 510 w pliku
base.rb).
Nowa, zmodyfikowana wersja wygląda następująco:
def coffee(template, options={}, locals={})
options.merge! :layout => false, :default_content_type => :js
if options.has_key? :preprocess
@preferred_extension = "coffee." + options[:preprocess].to_s
data = render options[:preprocess], template, options, locals
options.merge! :layout => false, :default_content_type => :js
render :coffee, data, options, locals
else
render :coffee, template, options, locals
end
end
Jeszcze tylko jedno, skoro zmieniliśmy rozszerzenie wyszukiwanego pliku (z .coffee na .coffee.erb) za pomocą zmiennej
@preferred_extension musimy zabronić ponownemu ustawieniu jej w metodzie
compite_template w okolicach linijki 584 pliku
sinatra/base.rb, tj:
@preferred_extension = engine.to_s unless options.has_key? :preprocess
Dzięki tym modyfikacją mogłem przepisać kod z pliku
server.rb do postaci:
require 'sinatra'
get '/script.js' do
@msg = " 'Dynamicly created JS! Yea!' "
coffee :script, :preprocess => :erb
end
Plik, w którym teraz znajduje się CoffeeScript z dodanym kodem rubiego znajduje się w views/script.coffee.erb czyli jest tak jak być powinno:)
Dla wszystkich zainteresowanych zmodyfikowaną wersją Sinatry, kod dostępny jest na
GitHubie.
PS: Jest to mój pierwszy post, więc proszę o odrobinę wyrozumiałości, oraz wszelkie możliwe rady :)