niedziela, 25 września 2011

Faker i bezpieczny email.

Kiedyś używałem taki fajny gem Faker, który służy do generowania fałszywych danych, bardzo przydatna sprawa przy wypełnianiu wszelkiego rodzaju pól, formularzy, czy też przy pisaniu automatycznych testów.
No i tu pojawia się problem, a mianowicie przy pomocy Fakera można wygenerować fałszywego użytkownika, tzn: fałszywe nazwisko, telefon, adres zamieszkania i email...który może się zdarzyć zupełnie przypadkowo prawdziwy.

Sprawa może być tym bardziej przykra jeśli wysyłamy w programie na te adresy jakiś mail (np: podtwierzający rejestrację użytkownika w serwisie), czasami wysłanie emaila może przebiegać automatycznie, wywoływane jako callback w modelu podczas zapisu użytkownika do bazy danych. Jeden nowy programista w zespole lub chwila zamyślenia no i email zapraszający naszego super tajnego projektu poleciał do jakiegoś pana za oceanem...

Właśnie dlatego postanowiłem napisać rozszerzanie do Fakera, które będzie generować tylko i wyłącznie bezpieczne adresy email, z domeny example.{com|org|net}. Jak zawsze szczegóły można zobaczyć na moim koncie GitHuba . Tym razem wyjątkowo skromnie, raptem kilka linijek kodu, licząc z testami ;)

Użycie też bardzo proste:
require 'faker'
p Faker::Internet.safe_email




Faker od wersji 1.0.1 posiada metodę safe_email :D

poniedziałek, 19 września 2011

Sinatra, a zmienne instancyjne w CoffeeScript.

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 :)