Opublikowane przez Jarosław Zabiełło
Sat, 27 Jan 2007 03:33:00 GMT
W związku z przestawieniem się całkowicie na najnowszą wersję Railsów (1.2.1), postanowiłem przy zrobić także upgrade oprogramowania do mego bloga (który w wersji jaką miałem nie współpracował z RoR 1.2.1). Na szczęście, dzięki railsowym migracjom proces aktualizacji skryptów jak i struktury baz przebiegł bez problemów i niedestrukcyjnie dla danych w bazie. Typo”:http://trac.typosphere.org/ zawsze był kodem trochę awangardowym, wprowadzającym zaawansowane mechanizmy Rubiego i najnowsze pomysły Railsów. Przekonałem się o tym podczas próby modyfikacji paru szablonów. Typo zamiast dotychczasowego formatu ERb (*.rhtml) Typo używa już szablony nowej generacji – Haml. Chcąc niechcąc, musiałem przejść szybki kurs posługiwania się nimi aby zmodyfikować szablony w swoim blogu.
Czytaj dalej...
Posted in Ruby on Rails | Tagi erb, haml, rails, templates | 12 comments
Opublikowane przez Jarosław Zabiełło
Mon, 25 Dec 2006 00:14:00 GMT
W wypadku treści wyświetlanych w szablonach najlepiej zachować zasadę ograniczonego zaufania. Np. jeśli wyświetlamy zawartość komentarzy które ktoś wysłał z formularza, to przy odrobinie złośliwości i/lub głupoty taki użyszkodnik może nam wysłać kod HTML lub JavaScript, który popsuje spójność naszej strony. Generalnie istnieje kilka metod aby się przed tym zabezpieczyć.
- Wszelkie treści podejrzane o możliwość wysłania kodu HTML wyświetlaj przez funkcję filtrującą html_escape() (lub w skrócie: h) Czyli zamiast <= jakies_dane %> należy używać <=h jakies_dane %> i dobrze aby ten zwyczaj wszedł nam w krew. Dzięki temu helperowi wszelkie dane zawierające znaczniki zostaną wymienione na encje HTML (np. zamiast < będzie < co w efekcie uniemożliwi interpretację takich znaków jako tagów HTML)]
- W wypadku kiedy potrzebujemy kodu HTML, ale nie chcemy aby zawierał ukryte wstawki w języku JavaScript, dane filtrujemy przez funkcję sanitize(). Wszystkie akcje onXXX oraz linki zaczynające się od javascript: powinny zostać usunięte.
- W wypadku kiedy chcemy udostępnić użyszkodnikom możliwość wprowadzania treści z możliwością formatowania tekstu, zamiast HTML można udostępnić im możliwość wprowadzania tekstu w formacie Markdown (BlueCloth) lub Textile (RedCloth). To specjalny, uproszczony sposób formatowania tekstu, który jest zamieniany na bezpieczny i dobrze sformułowany kod HTML. Rails posiada wbudowane helpery do ich obsługi (są zdefiniowane w module ActionView::Helpers::TextHelper)
- Ostatecznie można wyciąć wszystkie znaczniki HTML za pomocą helpera strip_tags().
Aby Rails mógł używać helpery textilize() i markdown() trzeba je doinstalować
gem install RedCloth
gem install BlueCloth
Następnie w pliku config/environment.rb dodać kod:
require_gem 'RedCloth'
require_gem 'BlueCloth'
Przykład użycia:
<%= textilize '"Polskie forum":http://forum.rubyonrails.pl dla RoR.' %>
<%= markdown 'Strona Rubiego [po polsku](http://ruby-lang.org/pl).' %>
Wygenerowany kod HTML:
<p><a href="http://forum.rubyonrails.pl">Polskie forum</a> dla RoR.</p>
<p>Strona Rubiego <a href="http://ruby-lang.org/pl">po polsku</a>.</p>
Posted in Ruby on Rails | Tagi rails | 1 comment
Opublikowane przez Jarosław Zabiełło
Sun, 24 Dec 2006 01:51:00 GMT
W Rails domyślnym miejscem do ustawienia swoich dodatkowych konfiguracji jest plik conf/environment.rb. Jednak wygodniejszym sposobem jest użycie formatu YAML. Np. załóżmy, że pliku config/defaults.yml mamy następujące ustawienia:
default: &defaults
paging: 10
development:
show_debugs: true
paging: 20
<<: *defaults
production:
show_debugs: false
<<: *defaults
Wykorzystałem tu zasadę DRY (unikania powtarzania kodu) stąd sekcja ‘default’ która zawiera wspólne ustawienia dla Railsów pracujących zarówno w trybie roboczym (developerskim) jak i produkcyjnym. Aby ten plik był wciągany i to zgodnie z bieżącym trybem pracy Railsów, do pliku config/environment.rb należy dodać:
require 'ostruct'
yml = YAML.load_file RAILS_ROOT + '/config/defaults.yml'
$defaults = OpenStruct.new(yml).send RAILS_ENV
Do naszej aplikacji RoR zostanie dodana zmienna globalna $defaults, W wypadku pracy w trybie produkcyjnym, zmienna $defaults zawiera hasz z wartościami:
{"paging"=>10, "show_debugs"=>false}
Zamiast użycia zmiennej globalnej, można użyć
::AppConfig = OpenStruct.new(yml).send RAILS_ENV
dzięki temu mamy dostęp do poszczególnych parametrów za pomocą notacji kropkowej:
Posted in Ruby on Rails | Tagi rails | brak comments
Opublikowane przez Jarosław Zabiełło
Thu, 14 Dec 2006 23:48:00 GMT
Nowy Skype 3 wprowadza małą rewolucję w stos. do poprzedniej wersji. Można nie tylko rozmawiać, ale pograć w szachy, kółko i krzyżyk i inne gry (są b. ładnie zrobione we Flashu 9). Można nagrywać rozmowy na dysk. Można tworzyć publiczne czaty. I właśnie w tej sprawie piszę ten tekst bo
stworzyłem polski czat dla miłośników frameworków Ruby on Rails, Django i Pylons.
Wygodniej jest czasem skonsultować coś w czasie rzeczywistym niż na grupie czy forum dyskusyjnym.
Posted in Pylons, Django (en), Ruby, Django, Ruby on Rails, Python | Tagi django, pylons, rails | 17 comments
Opublikowane przez Jarosław Zabiełło
Fri, 01 Dec 2006 16:17:00 GMT
11 grudnia w Warszawie odbędzie się kolejne spotkanie dla osób zainteresowanych Ruby on Rails, rozwojem internetu i e-biznesem. Szczegóły tutaj.
Posted in Ruby on Rails | Tagi meetings, rails | brak comments
Opublikowane przez Jarosław Zabiełło
Wed, 29 Nov 2006 03:45:00 GMT

Drugie wydanie kultowej książki na temat Railsów jest już w druku. Wersja papierowa powinna być gotowa na 15 grudnia. Pierwsze wydanie zostało uznane za najlepszą książką techniczną na świecie. Drugie jest jeszcze ciekawsze, bo opisuje wszystkie nowości jakie mają się pojawić w Railsach 1.2. Właściciele wersji PDF mogą kupić wersję papierową ze zniżką.
Posted in Ruby on Rails | Tagi rails | brak comments
Opublikowane przez Jarosław Zabiełło
Sat, 25 Nov 2006 13:52:00 GMT
Nadchodzi wielkimi krokami Ruby on Rails 1.2. Właśnie udostępniono wersję RC1. Wersja finalna powinna pojawić się w najbliższych tygodniach. Wprowadzono szereg istotnych usprawnień w działaniu Railsów powiększając tym samym jeszcze bardziej komfort pracy i dystans w stosunku do pozostałych frameworków.
Np. dodano dodatkowy parametr “format” do metody respond_to. Aby z tego korzystać, należy w routes.rb dodać regułę:
map.connect ':controller/:action/:id.:format'.
Nie trzeba już więcej bawić się w analizę nagłówków HTTP.
class WeblogController < ActionController::Base
def index
@posts = Post.find :all
respond_to do |format|
format.html
format.xml { render :xml => @posts.to_xml }
format.rss { render :action => "feed.rxml" }
end
end
end
GET /weblog
GET /weblog.xml
GET /weblog.rss
Inną, istotną zmianą jest dodanie Multibyte. Nie jest to co prawda tak zaawansowane jak w Pythonie, ale do czasu pojawienie się Ruby 2 (który ma mieć pełne wsparcie dla Unicode), Multibyte poprawia trochę sytuację. programisty. Generalnie Ruby jakoś dawał sobie radę z UTF-8, ale w niektórych sytuacjach pojawiały się problemy. Np. metoda size() zwraca ilość bajtów a nie znaków. Multibyte wprowadza metodę proxy dla napisów – chars. Np.
Napisałeś <%= @post.body.chars.length %> znaków.
Jedną z bardziej rewolucyjnych zmian, jest dodanie obsługi REST. (która zakłada, że każdy zasób sieci powinien być jednoznacznie identyfikowany za pomocą samego adresu URL). W praktyce oznacza, to znacznie skrócenie kontrolerów w Railsach po przez wysłanie na ten sam adres różnych komend, tzn,. nie tylko GET i POST ale także PUT i DELETE. Mimo, że te polecenia są cześcią standardu protokołu HTTP, większość przeglądarek implementuje tylko GET I POST. W takim wypadku Rails emuluje działanie tych komend poprzez dodawanie do adresu znaku średnika i polecenia.
Plugin Simply RESTful już działa i można go używać. Np. wygeneruj sobie kod nowego, REST’owego scaffoldingu:
ruby script/generate scaffold_resource
Oczywiście, musisz mieć odpowiednią najnowszą wersję Railsów, którą najlepiej zainstalować za pomocą gemsów:
gem install rails --source http://gems.rubyonrails.org -y
Nie przejmuj się jak wyświetli ci się coś w stylu
Successfully installed rails-1.1.6.5618
To jest tymczasowe oznaczenie dla wersji RC1. Jak wyjdzie wersja finalna to będzie wyświetlać się numer 1.2.
Trwają jeszcze końcowe prace nad Active Resource, modułem obsługującym REST i nowy sposób mapowania.
I ostatnia wiadomość, nadchodzące w grudniu, drugie wydanie książki Agile Web Development in Rails powinno większość tych zmian uwzględniać, bo książka jest pisana z założeniem że używany jest Edge Rails (czyli najnowsza wersja z repozytorium SVN).
Posted in Ruby on Rails | Tagi rails | 2 comments
Opublikowane przez Jarosław Zabiełło
Tue, 07 Nov 2006 23:47:00 GMT
W ostatnim artykule (Railsy: Lighttpd czy Apache 2.2.x?) porównywałem najbardziej popularne serwery HTTP dla Railsów. Zaintrygowany paroma wpisami w blogu postanowiłem przyjrzeć się dosyć mało znanemu serwerowi HTTP który zaczyna zdobywać coraz więcej uwagi na Zachodzie. Chodzi o ultraszybki serwer nginx napisany przez rosyjskiego programistę Igora Sysojewa.
Na pierwszy bój poszedł prosty test wyświetlenia “Hello World!” Na używanym przeze mnie serwerze dedykowanym (Athlon 64 3000+, 1GB RAM, Linux Ubuntu 64bit) dla 100 tys. zapytań (musiałem użyć aż tyle, bo serwer jest za szybki na mniejszą liczbę zapytań) uzyskałem następujące wyniki (dla polecenia “ab -n 100000 -c 1 http://localhost”):
- Apache 2.2.3 = 4439 req/s
- Lighttpd 1.4.11 = 7150 req/s
- Nginx 0.4.12 = 8700 req/s
Co prawda Nginx wykazuje miażdzącą przewagę wydajności nad Apachem ale, z racji tego, że używam Lighttpd, który co prawda odstaje od Nginxa ale nie aż tak, postanowiłem na razie zaczekać z ewentualną migracją.
Okazało się jednak, że będę musiał przeprowadzić taką migrację szybciej niż bym chciał. Coś złego zaczęło się dziać z Ligthttpd. Po paru godzinach pracy, przestawał odpowiadać na zapytania a nawet w ogóle proces znikał z pamięci. Ki diabeł? Trudno powiedzieć, nie mam czasu aby analizować dokładniej problem. Jedyne co pomagało to regularny restart Lighttpd. Trochę głupie rozwiązanie. Postanowiłem zatem zrobić wcześniejszą migrację do Nginxa. Wg statystyk Netcraftu z Nginxa korzysta już ponad 90 tys. domen. Wydaje się to wystarczającą ilością aby można było uznać ten serwer za stabilny.
Jednakże moja migracja ma pewną trudność. Używam bowiem równocześnie PHP, Django, Rails i Zope (ściślej: Plone). Czyli całkiem niezła mieszanka aplikacji. Z PHP i Railsami było najmniej problemów, bo przykłady konfiguracji są z grubsza podane w Wiki.
Z Plone było troszkę gorzej. Musiałem bowiem znaleźć odpowiednik mniej więcej takiego kodu w Apache:
RewriteRule "^/(.*)$"
"http://88.198.15.160:6001/VirtualHostBase/http/apologetyka.com:80/app/VirtualHostRoot/$1" [P,L]
To nie jest zwykły rewrite, to jest połączenie proxy z rewrite.
W Lighttpd (też się swego czasu namęczyłem aby to uzyskać) uzyskuje się to tak:
url.rewrite-once = (
"^/(.*)$" => "/VirtualHostBase/http/apologetyka.com:80/plone/VirtualHostRoot/$1"
)
proxy.server = (
"/VirtualHostBase/" => (
( "host" => "88.198.15.160" , "port" => 6001 ) )
)
Trochę prób i się udało. Nginx potrzebował takiego wpisu:
location / {
rewrite ^/(.*)$ /VirtualHostBase/http/apologetyka.com:80/plone/VirtualHostRoot/$1;
}
location /VirtualHostBase/ {
include /opt/nginx/conf/proxy.conf;
proxy_pass http://88.198.15.160:6001;
}
Najtrudniej było z konfiguracją Django bo opisu do Django na Nginx nie ma ani w dokumentacji do Django, ani w dokumentacji do NGinxa. Zmęczony eksperymentowaniem napisałem na listę dyskusyjną Django i dostałem całkiem pożyteczną wskazówkę odnośnie strony http://www.python.rk.edu.pl/w/p/django-pod-serwerem-nginx/. Niestety miałem pecha, bo akurat trafiłem na zmianę wpisów w DNS i artykuł był niedostępny. Udało mi się na szczęście wyłuskać jego kopię z cache Googli. Autor międzyczasie podesłał mi też pliki z artykułami. Zauważyłem że napotkał pewne problemy ze zmuszeniem Django do wyświetlania statycznej treści. Trochę podłubałem w kodzie i problem rozwiązałem. :)
Zauważyłem że Django wyświetlał mi pliki statyczne w trybie debug. Wynikało to pewnie z tego, że w urls.py stosuję zawsze taki wpis:
...
if DEBUG:
urlpatterns += patterns('',
(r'^images/(?P<path>.*)$', 'django.views.static.serve', {'document_root': MEDIA_ROOT+'/images', 'show_indexes': True}),
(r'^stylesheets/(?P<path>.*)$', 'django.views.static.serve', {'document_root': MEDIA_ROOT+'/stylesheets', 'show_indexes': True}),
(r'^javascripts/(?P<path>.*)$', 'django.views.static.serve', {'document_root': MEDIA_ROOT+'/javascripts', 'show_indexes': True}),
)
Dla trybu produkcyjnego (settings.DEBUG=False) trzeba zmusić serwer HTTP aby się zajmował plikami statycznymi. Django ma tylko przetwarzać Pythona. Mozna to zrobić np. tak:
...
location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|mov) {
access_log off; # po co mi logi obrazków :)
expires 30d;
}
location / {
include /opt/nginx/conf/fastcgi.conf;
fastcgi_pass 127.0.0.1:6002;
fastcgi_pass_header Authorization;
fastcgi_intercept_errors off;
}
Gdzie plik fastcgi.conf:
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
fastcgi_param PATH_INFO $fastcgi_script_name;
Plik startowy napisałem sobie już w Pythonie:
import os, sys, time
DEBUG = True
path = '/home/app/django'
projects = {
'biblia' : {
'project': 'searchers',
'port':6002,
'pidfile': '/var/run/django_searchers.pid',
'children': 4,
},
'koran': {
'project': 'django_project',
'port':6003,
'pidfile': '/var/run/django_koran.pid',
'children': 4
},
}
def start(name):
project = projects[name]['project']
os.chdir('%s/%s/' % (path, project))
appl = './manage.py runfcgi host=127.0.0.1'
cmd = '%s port=%s minspare=1 maxspare=%s pidfile=%s --settings=%s.settings' \
% (appl, projects[name]['port'], projects[name]['children'], projects[name]['pidfile'], project)
if DEBUG: print cmd
os.system(cmd)
def stop(name):
pidfile = projects[name]['pidfile']
if os.path.exists(pidfile):
cmd = '/bin/kill -TERM %s' % open(pidfile).read()
if DEBUG: print cmd
os.system(cmd)
os.unlink(pidfile)
def restart(name):
stop(name)
time.sleep(1)
start(name)
if __name__ == '__main__':
try:
action, project = sys.argv[1], sys.argv[2]
if action in ['start','stop', 'restart'] and project in projects:
if action == 'start':
start(project)
elif action == 'stop':
stop(project)
elif action == 'restart':
restart(project)
else:
raise IndexError
else:
raise IndexError
except IndexError:
print "Usage: %s {start|stop|restart} {%s}" % (sys.argv[0], '|'.join(projects.keys()))
Migracja się udała. Plone, PHP, Django i Rails śmigają mi teraz na ultraszybkim (i zajmującym mało pamięci!) serwerze Nginx. Acha, zapomniałem dodać: Nginx to nie tylko duża wydajność i oszczędność pamięci. Nginx ma dużo modułów. Może nie tyle, co Apache, ale znacznie lepiej niż Lighttpd.
BTW, ciekawie wygląda także serwer Cherokee. Nginx działa tylko pod systemami POSIX (Unix, MacOS-X, Linux, FreeBSD). Cherokee natomiast posiada… binarną instalację pod Windows! Ale o tym może napiszę coś innym razem. :)
Posted in Django, Ruby on Rails | Tagi cherokee, django, fastcgi, nginx, rails | 16 comments
Opublikowane przez Jarosław Zabiełło
Tue, 07 Nov 2006 23:13:00 GMT
RadRails, zintegrowany edytor dla Rubiego i Railsów zaczyna coraz bardziej niwelować wcześniejsze zachwyty nad komercyjnym edytorem TextMate. Nie dość, że jest darmowy i działa na Windows, MacOS-X i Linuksie (TextMate tylko na MacOS-X), to na dodatek uzyskał to, co do tej pory było główną atrakcją TextMate – makra przyśpieszające pisanie kodu, czyli tzw. snippety. RadRaila ma już przygotowanych 199 snippetów do Rubiego i 50 do RHTML!
Do tego wystarczy dorzucić kolorowanie kodu a’la TextMate i RadRails ma już wszystko (no może do pełni doskonałości brakuje mu uzupełniania kodu tak, jak to robi komercyjny edytor Komodo)
Posted in Ruby, Ruby on Rails | Tagi edytory, radrails, rails, ruby | brak comments
Opublikowane przez Jarosław Zabiełło
Sun, 29 Oct 2006 14:53:00 GMT
Odnośnie użycia Railsów w celu produkcyjnym, dużo się zmieniło od czasu pojawienia się Mongrela – małego, ale dosyć szybkiego serwera napisanego w Ruby i C. Następuje generalne odejście od podpinania kilku procesów FastCGI do Rubiego z różnych powodów. W drugim wydaniu Agile Web Development in Rails autorzy przyznali, że próbowali różnych opcji użycia modułu FastCGI. Zawsze ostatecznie były jakieś problemy.
Aktualnie zalecanym podejściem jest użycie szybkiego serwera HTTP (Lighttpd, Apache lub LiteSpeed) na froncie do obsługi statycznych plików (głównie obrazki, style kaskadowe i pliki Javascript). Aplikację RoR należy uruchomić za pomocą kilku procesów Mongrela (nie jak wcześniej: kilka procesów FastCGI). A tym, co zepnie je z serwerem HTTP jest moduł rozkładający ruch (load balancing).
Niestety, okazało się że w wypadku Lighttpd ten moduł zawiera błędy. Z kolei nowy Apache 2.2 posiada dobrze działający moduł load balancingu. Spowodowało to zrezygnowanie przez wielu z Lighttpd na rzecz nowego Apache 2.2.x.
Z racji tego, że mam trochę swobody w doborze technologii w pracy, postawiłem serwer na nowym Apache’u. Aplikacje Railsów podpinamy za pomocą mongrela i wbudowanego w Apache modułu load_balancer. Działa faktycznie sprawnie.
Czy to znaczy, że Lighttpd nie ma sensu używać? Skąd! W wersji 1.5 mają już mieć naprawiony moduł load balancingu. Ale nawet i bez tego, istnieje bardzo dobry, mały i szybki load balancer – Pound. Skuszony jednak potrzebą eksperymentowania oraz nabytym niedawno serwerem dedykowanym (w miejsce wcześniejszego VPS’a) postanowiłem przenieść wszystkie swoje prywatne serwisy (a jest tego sporo) z Lighttpd do Apache 2.2.3. Musiałem przenieść dwie aplikacje Rails, dwie aplikacje Django, dwie aplikacje PHP 5.1 oraz jedną duża aplikację Zope/Plone. I jak wrażenia?
Otóż, po 2 tygodniach używania, stwierdziłem, że to nie ma sensu. Apache pożera za dużo pamięci. Dzieje się dokładnie tak, jak pisałem wcześniej. Nie mogłem użyć trybu wielowątkowego, nie tyle ze względu na PHP ale ze względy na Django! Napisali bowiem, że nie należy tego trybu używać w wypadku ich frameworka. Django nie jest wielowątkowy. Musiałem więc przełączyć Apache z trybu worker na prefork. Zgodnie z obawami, każdy fork musiał marnować pamięć na moduł mod_php i mod_python nawet jak to nie ma sensu (np. podczas obsługi obrazków). Na dodatek nie mogłem coś uruchomić jednej aplikacji Django. Kompletnie nie wiem dlaczego. Druga była prawie identycznie skonfigurowana i działała.
Wróciłem zatem z powrotem do Lighttpd , Pounda i Mongrela (dla RoR) i FastCGI (dla Django i PHP). Efekty były bardzo widoczne: zwolniło mi się ponad 200 MB pamięci. Może to nie ma znaczenia w pracy, gdzie mamy 2GB ale ja mam tylko 1GB i muszę jeszcze uruchomić zasobożerny Plone.
Dokładniej wszystkie sprawdzone i działające konfiguracje opiszę w powstającej książce o Railach. Moja ostateczna konlkluzja jest taka: jeśli zależy ci na oszczędzeniu pamięci – wybieraj Lighttpd. Jeśli nie jest to kluczowa sprawa, a potrzebujesz jakieś specjalne, dodatkowe moduły (których Apache ma pełno) nowy Apache 2.2 spełni twe oczekiwania.
Posted in Ruby on Rails | Tagi apache, fastcgi, lighttpd, mod_python, rails | 9 comments
Opublikowane przez Jarosław Zabiełło
Thu, 12 Oct 2006 14:55:31 GMT
Poza polskim forum mamyod niedawna kanał #rubyonrails.pl na serwerze irc.freenode.net gdzie można bardziej interaktywnie wymieniać się doświadczeniem na temat Rubiego i/lub Railsów. Na razie nie ma wiele osób, ale może się bardziej rozkręci.
Posted in Ruby, Ruby on Rails | 5 comments
Opublikowane przez Jarosław Zabiełło
Wed, 04 Oct 2006 15:01:00 GMT
Większość osób, które zetknęły się z frameworkiem Ruby on Rails zauważyła, że domyślne ustawienia są tam tak dobrane, aby maksymalnie uprościć i zminimalizować ilość ręcznej pracy przy ustawieniach. Jeśli więc ktoś trzyma się flozofii i konwencji nazw (np. dla tabel i ich pól) będzie miał znacznie mniej dodatkowej roboty z konfiguracją frameworka do pracy. Typowa definicja modelu wyglądać może nawet tak prosto:
class User < ActiveRecord::Base
end
Powyższa definicja zakłada, że tabela w bazie nosi nazwę “users”, a jej primary key nazywa się “id”. W przeciwieństwie do innych ORM’ów, railsowy ActiveRecord nie wymaga żadnego wcześniejszego definiowania pól. Ruby bez problemu sam sobie wyciagnie wszystkie nazwy pól w sposób dynamiczny wtedy, kiedy będzie mu to potrzebne.
Nietypowe struktury
Niestety, nie zawsze jesteśmy w takiej komfortowej sytuacji, że mamy wpływ na nazewnictwo tabel i ich pól. Jednak to nie jest większym problemem, bo Rails sobie z tym radzi równie prosto.
class User < ActiveRecord::Base
set_table_name 'uzytkownicy'
set_primary_key 'IdUzytkownika'
end
Jak widać, dużo pracy nam nie przybyło. W tym wypadku musieliśmy tylko jawnie podać jak się nazywa nasza tabela i jak się nazywa jej klucz główny.
Korzystanie z wielu baz
W momencie tworzenia projektu aplikacji RoR generowany jest automatycznie plik konfiguracyjny zawierający połączenie do bazy danych. Przykładowy plik config/database.yml mogłby wyglądać np. tak:
default: &defaults
adapter: mysql
host: localhost
encoding: utf8
username: jakis_login
password: jakies_haslo
development:
database: bookstore_dev
<<: *defaults
test:
database: bookstore_test
port: 3307
<<: *defaults
production:
database: bookstore
socket: /var/run/mysqld/mysqld.sock
<<: *defaults
Co robi powyższy kod? Definiuje 3 bazy: produkcyjną, roboczą i testową. (Ta ostatnia jest używana tylko do testów jednostkowych więc nie powinno się tam wstawiać żadnych istotnych danych) W w/w konfiguracji zdecydowaliśmy, że wszystkie bazy będą korzystać z MySQL >=4.1 z ustawionym wew. kodowaniem UTF-8 (zalecane). Baza produkcyjna działać będzie na Linuksie, więc zamiast portu TCP, podano uniksowy socket. Baza testowa działa na porcie 3307, a robocza na domyślnym (3306). Główna sekcja “defaults” jest wspólna dla wszystkich trzech baz zgodnie z zasadą DRY (Don’t Repeat Yourself).
Załóżmy jednak, że chcemy korzystać z dodatkowej bazy i chcemy aby z niej korzystały niektóre modele. Żaden problem. Do pliku dopisujemy tylko dodatkową definicję połączenia do innej bazy:
other:
adapter: mysql
host: 192.168.0.123
database: customers
<<: *defaults
Jak widać, możemy tą metodą tworzyć aplikację korzystającą z rozproszonych baz. (W tym wypadku baza customers leży na zupełnie innym serwerze, ale korzysta z tego samego usera i hasła.)
No dobrze, ale jak modele mają rozpoznać z jakiego połączenia korzystają? To też prosta sprawa. Domyślnym połączeniem jest to, co zdefiniowano w sekcji domyślnej. Jedynie modele, które korzystają z innego połączenia, muszą to mieć jawnie zadeklarowane w definicji, np.
class Client
establish_connection :other
end
Model Client oraz wszystkie modele utworzone z niego na drodze dziedziczenia, będą korzystać z innego połączenia niż domyślne.
BTW, to jedna z bardziej brakujących mi cech we frameworku Django (Pylons nie ma z tym żadnego problemu). Mam nadzieję, że wkrótce to poprawią i w Django będzie można bez problemu korzystać z wielu baz.
Wspolne modele dla wielu aplikacji Rails
W tym scenariuszu mamy za zadanie stworzyć szereg aplikacji webowych korzystających z oddzielnych baz o tej samej strukturze tabel. Chcemy uniknąć powielania kodu niezbędnego dla modeli ORM. Nie mamy też czasu (ani ochoty) aby siedzieć i stworzyć definicje do wszystkich tabel wraz z wyposażeniem ich w nasze własne, dodatkowe metody uwzględniające każdą, możliwą potrzebę w przyszłości. Zamiast tego, wolimy tworzyć i rozbudowywać obiektowy opis naszej bazy dla wszystkich aplikacji w miarę ich rozwoju i wzrostu złożoności. Chcemy oby nasz model biznesowy, opisywany przez ActiveRecord, rósł stopniowo wraz ze wzrostem naszych aplikacji RoR.
Do takiego zadania, najlepiej aby wszystkie aplikacje RoR korzystały ze wspólnego repozytorium modeli. W wypadku systemów POSIX (Linux, czy MacOS-X) najprościej to rozwiązać za pomocą linków symbolicznych. Jednakże, jeśli chcemy aby nasz kod był bardziej przenośny i działał także pod Windowsami musimy inaczej podejść do tego problemu: wyniesiemy definicje naszych modeli do zewnętrznych modułów Rubiego aby domieszkowały one klasy poszczególnych modeli Rails.
Wpierw musimy dla każdej aplikacji RoR wskazać gdzie leży repozytorium z wpołdzielonymi modułami. Najlepiej wstawić do pliku conf/environment.rb zmienną globalną Rubiego o nazwie, np. $SHARED.
$SHARED = File.dirname(__FILE__)+'/../../shared/'
Konstrukcja File.dirname… jest zalecana, gdyż daje pewny i jednoznaczny dostęp do pliku bez względy na to gdzie przekopiujemy cały nasz projekt. (Identycznie należy robić pod PHP aby uniknąć problemów z ustawieniami zmiennej include_dirs. Większość pehapowego lamerstwa używa zapisu require(“plik.php”) i potem się dziwi że coś im nie działa)
Załóżmy że wcześniej mieliśmy model zdefiniowany następująco:
class Book
set_table_name 'ksiazki'
belongs_to :author
def self.commented(user_id)
self.find(:all, :conditions=>['user_id=?',user_id])
end
end
Mamy tu więc nie tylko nazwę tabeli niezgodną z konwencją Railsów, ale także dodatkową metodę klasową. Wpierw zróbmy z tego moduł:
module BookModule
def self.extended(c)
c.set_table_name 'ksiazki'
c.belongs_to :author
end
def commented(user_id)
self.find(:all, :conditions=>['user_id=?',user_id])
end
end
Kod korzystający z tego modułu przyjmie postać:
if RAILS_ENV == "development"
load $SHARED + 'models/book.rb'
else
require $SHARED + 'models/book.rb'
end
class Book< ActiveRecord::Base
include BookModule
extend BookModule
end
Górny fragment jest potrzebny jeśli chcemy aby Webrick/Mongrel w trybie development się przeładowywał podczas zmiany kodu w Ruby. “include” włącza metody instancji. Zaś “extend” włącza wszystkie emetody modułu _BookModule _do klasy jako jej metody klasowe. Zwróć uwagę, że w module nie definiowaliśmy ich jako z prefiksem self. Co prawda, efektem ubocznym tego przykładu jest to, że metoda commented() będzie możliwa do uruchomienia jako metoda zarówno instancji jak i klasy, ale to raczej nie ma żadnego znaczenia.
Posted in Ruby on Rails | Tagi activerecord, orm, rails | 11 comments
Opublikowane przez Jarosław Zabiełło
Mon, 14 Aug 2006 15:02:00 GMT
Polska grupa RoR postanowiła się po raz drugi spotkać na żywo. Spotkanie ustalono na 18 sierpnia o godzinie 18:00 w sali konferencyjnej firmy Sec-Bos w Warszawie.
Spotkanie odbedzie się na 99% w przypadku wystąpienia problemów z salą niezwłocznie zostanie dołączona adnotacja.
Polska społeczność RoR chciałaby również podziękować firmie Sec-Bos za pomoc w organizacji spotkania.
Szczegóły na forum http://forum.rubyonrails.pl .
Posted in Ruby on Rails | Tagi rails | brak comments
Opublikowane przez Jarosław Zabiełło
Sun, 06 Aug 2006 12:54:00 GMT
Mit o rzekomej niskiej wydajności Railsów wydaje się być przesadzony. Interpreter Rubiego jest co prawda wolniejszy od interpretera Pythona (i tym samym Django jest szybsze od Railsów). Ale już porównanie Django, Railsów i pehapowego Symphony pokazuje że Rails jest wyraźnie szybszy od Symphony (który używa PHP5 i akceleratora!). Skoro PHP jest wolniejszy, to z szybkością Railsów nie jest aż tak źle, prawda? Ale to nie wszystko, Rails posiada wygodny mechanizm który pozwala bez trudu powalić na kolana typową aplikację Django.
Siła skalowania poziomego.
Gdy mamy na myśli wydajność całej aplikacji to nie sposób pominąć ważny jej element: skalowalność. Aplikację webową można skalować pionowo (dokładając więcej RAM’u, mocniejszy procesor) lub poziomo (dokładając po prostu kolejny serwer). Ta ostatnia opcja jest szczególnie interesująca, bo dołożenie mocniejszego procesora nie można ciągnąć bez końca.
Ruby on Rails świetnie skaluje się poziomo. Zamiast klasycznego zestawu Lighttpd + kilka procesów fasctgi polecam znacznie lepszy zestaw oparty na procesach mongrel.
Scenariusz 1:
Lighttpd na froncie obsługuje wszystkie statyczne pliki. Pozostałe kieruje do Pound (szybkiego i lekkiego load balancera1), który z kolei kieruje ruch do kilku procesów mongrela (każdy z nich może być nawet na innej maszynie).
Scenariusz 2:
Apache 2.2 na froncie z wbudowanym modułem load balancera rozkłada ruch na kilka procesów mongrela.
Przykład.
Zakładam, że użyję Lighttpd + Pound + 4 procesy mongrela z rozłożonym ruchem na 2 serwery. Fragment pliku konfiguracyjnego dla serwera Lighttpd:
$HTTP["host"] == "domena.host" {
$HTTP["url"] !~ "^/(cgi-bin|images|stylesheets|javascripts)" {
proxy.server = ( "/" => ( ( "host" => "127.0.0.1" , "port" => 9000 ) ))
}
server.document-root = "/home/httpd/domena.host/public"
server.indexfiles = ("dispatch.fcgi")
server.error-handler-404 = "/dispatch.fcgi"
}
Lighttpd obsługuje wszystkie pliki statyczne. Robi to wyjątkowo szybko, lepiej niż Apache. Cały pozostały ruch jest spięty za pomocą proxy z procesem na porcie 9000. Na tym porcie będzie nasłuchiwał Pound, który zajmie się rozkładaniem ruchu na pozostałe serwery. Plik konfiguracyjny dla Pound’a może wyglądać:
User "nobody"
ListenHTTP
Address 0.0.0.0
Port 9000
Service
BackEnd
Address 127.0.0.1
Port 9001
End
BackEnd
Address 127.0.0.2
Port 9002
End
BackEnd
Address 127.0.0.3
Port 9003
End
BackEnd
Address 127.0.0.4
Port 9004
End
End
End
Pozostało tylko uruchomić mongrela. Po zainstalowaniu (gem install mongrel), przechodzimy do folderu z naszą aplikacją railsów i odpalamy
mongrel_rails cluster::configure -e production \
-p 9000 -N 4 -c /opt/ror/mojprojekt -a 127.0.0.1 \
--user mongrel --group mongrel
(Parametr -a chroni nas przed możliwością zewnętrznego odpalenia w przeglądarce Railsów działającej bezpośrednio na którymś z procesów mongrela).
Powinien powstać plik: config/mongrel_cluster.yml
---
port: "9000"
environment: production
address: 127.0.0.1
pid_file: log/mongrel.pid
servers: 4
Cluster odpalamy za pomocą
mongrel_rails cluster::start
Dodatkową zaletą używania mongrela jest możliwość wygodnego restartowania dowolnej aplikacji Railsów (a można ich mieć przecież wiele) bez konieczności restartowania serwera HTTP (mongrel_rails cluster::restart)
Poza tym mongrel świetnie nadaje się do zastąpienia Webricka, standardowego serwera HTTP zalecanego do tej pory do pracy developerskiej. Zamiast
można użyć mongrela:
Domyślnie podniesie się serwer HTTP na porcie 3000 i w trybie development (czyli identycznie jak Webrick). Mongrel jest jednak znacznie szybszy!
Dla użytkowników windozy też jest dobra wiadomość. Mongrel można zainstalować jako usługę windowsów i zarządzać standardowo tak jak inne usługi. Można np. odpalić sobie na jednym porcie Rails w trybie produkcyjnym, a na drugim – w trybie developerskim. W tym drugim wypadku nie trzeba restartować serwera bo zmiany w kodzie automatycznie przeładowują serwerek (tak jak Webrick)
Oczywiście to wszystko ślicznie działa z Capistrano, railsowym systemem automatycznie synchronizującym zmiany na n-serwerach.
Jak dla mnie, mongrel wnosi nową jakość do pracy z Railsami i warto przyjrzeć się mu dokładniej. Nie zdziwiłbym się, jakby następne wersje Railsów standardowo zalecały używanie mongrela, a nie Webricka.
Tak jak siłą Google są tysiące mniejszych maszyn a nie jedna – super szybka, tak nawet najszybszy serwer wyposażony w Apache i mod_python (który używa domyślnie Django) nie ma szans w starciu z clusterem Railsów opartym na procesach mongrel i korzystających z kilku serwerów.

Wykres z artykułu Scaling Rails with Apache 2.2, mod_proxy_balancer and Mongrel
-
1 Dlaczego nie wykorzystywany jest load balancer wbudowany w Lighttpd? Bo jest kiepsko zaimplementowany i są problemy z restartem procesów. Dlatego zalecany jest zewnętrzny load balancer lub użycie najnowszej wersji Apache 2.2 który ma ten moduł dobrze zaimplementowany.
Posted in Ruby on Rails | Tagi rails | 15 comments
Opublikowane przez Jarosław Zabiełło
Tue, 01 Aug 2006 03:09:00 GMT
Pojawiła się nowa wersja RadRails 0.7. Niestety, trochę się pośpieszono. Zawiera wiele błędów. Lepiej poczekać na następną wersję.
Pojawił się bardzo ciekawy edytor oparty na Eclipse: Aptana. Służy głównie do pracy z HTML, CSS i JavaScript. Warto jednak obejrzeć załączone filmy pokazujące w jaki sposób edytor podpowiada kod i uzupełnia metody, bo robi to lepiej niż Dramweaver 8.
Pojawiła się kolejna, stabilna wersja aplikacji do tworzenia bloga opartej na Ruby on Rails – Typo 4.
Posted in Ruby on Rails | Tagi eclipse, radrails, typo | brak comments
Opublikowane przez Jarosław Zabiełło
Wed, 19 Jul 2006 19:22:00 GMT
Osoby mające wcześniej do czynienia z innymi frameworkami, gdy sięgną do Django mogą być trochę zdezorientowane sposobem w jaki należy przekazywać zmienne do wspólnych części serwisu. Aby lepiej zrozumieć problem, podam wpierw jak jest on rozwiązywane w Railsach.
Otóż, Railsy zakładają, że każdy kontroler dziedziczy po wspólnym kontrolerze zwanym ApplicationControler. A że jest to normalna klasa Rubiego, więc nic dziwnego, że wszelkie jej metody i zmienne (instancji) są automatycznie dziedziczone we wszystkich jej klasach potomnych. To bardzo intuicyjne rozwiązanie. Jeśli chcemy przekazać jakieś wspólne dane do wszystkich kontrolerów i ich szablonów, wystarczy te dane zdefiniować w tym miejscu. Weźmy np. poniższy kod.
class ApplicationController < ActionController::Base
before_filter :defaults
def initialize
@name = "Jarek"
end
def defaults
@msg = "Hello!"
end
end
class HomeController < ApplicationController
end
class AboutController < ApplicationController
def index
@dodatkowa = "wartość"
end
end
Zakładając, że nie zmienialiśmy domyślnego działania resolvera adresów URL, wpisanie strony http://localhost:3000/home wywoła domyślną metodę index() z klasy HomeController. Jeśli jej nie ma (jak na naszym przykładzie), to Rails zakłada, że chcemy wywołać jej szablon tak, jakby ta metoda istniała. Wczytywany jest zatem plik app/views/home/index.rhtml. Dzięki temu, że w klasie ApplicationController zdefiniowaliśmy 2 zmienne instancji (@name i @msg) automatycznie są one dziedziczone przez klasę HomeController i tym samym dostępne w w/w szablonie. Identycznie będzie z drugą klasą, jej szablon również będzie miał obie, zdefiniowane wyżej, zmienne1.
W wypadku Django, sprawy wyglądają inaczej dlatego, że kontrolery3 nie są metodami klas Pythona! Są one metodami modułu Pythona. Co prawda moduł też jest obiektem, ma własną przetrzeń nazw itp. ale posiada jedną zasadniczą różnicę wobec klasy – nie można dziedziczyć modułu od modułu. Tym samym próba współdzielenia zmiennych musi być zrobiona zupełnie inną metodą.
Trochę z tym problemem się męczyłem, bo w dokumentacji mało na ten temat piszą. Dopiero na IRC podsunięto mi link do artykułu Django tips: Template context processors, który wszystko ładnie wyjaśnił2. Okazuje się, że jest kilka sposobów rozwiązania tego problemu. Napisanie własnego MiddleWare (ech, nie chciało mi się, to armata na muchę), napisanie własnego znacznika dla szablonów (w wypadku mojej małej aplikacji, to też byłaby przesada) lub wykorzystanie tzw. procesora kontekstu (Context Processor). I to było to!
Wystarczy stworzyć sobie plik (o nazwie np. context_processors.py) z funkcjami implementującymi to, co chcemy wrzucić do wszystkich szablonów.
def name(request):
return 'Jarek'
def msg(request):
return 'Hello!'
Następnie trzeba dodać w settings.py informację że będziemy tego używać:
TEMPLATE_CONTEXT_PROCESSORS = (
'myapp.context_processors.name',
'myapp.context_processors.msg',
)
W wypadku generic views to wszystko, bo one automatycznie wciągają RequestContext dla swoich szablonów. W wypadku naszych kontrolerów (zakładając, że używamy render_to_response() aby wyświetlać szablon) trzeba na końcu metody render_to_response() dodać jeden parametr ‘context_instance’. Czyli:
from django.template import RequestContext
def home(request):
return render_to_response(
'home.html',
context_instance=RequestContext(request))
def about(request):
return render_to_response(
'about.html',
{'dodatkowa': 'wartość'},
context_instance=RequestContext(request))
Tak coraz bardziej wgłębiając się w Rails i Django widzę, że Rails jest jednak bardziej intuicyjny i łatwiej się go nauczyć. Jednakże Django wydaje się mieć sporo potężnych mechanizmów, których odkrywanie (mam nadzieję) się uprości jak tylko jego twórcy opublikują pierwszą książkę na jego temat (ma być dostępna także w wersji online za darmo!)
-
1 W podanym wyżej przykładzie wykorzystałem dwa różne sposoby przekazania zmiennych. Za pomocą konstruktora i za pomocą metody ‘before_filter’. Wiadomo, że konstruktor nie nadaje się do przekazywania wszystkiego. Czasami musimy przekazać coś, co istnieje już po wywołaniu konstruktora. Wtedy przydaje się metoda ‘before_filter’. To jedna z wielu sztuczek jakie ma w swym arsenale Rails aby kod był czysty, klarowny i zgodny z zasadą DRY (nie powtarzania się).
2 Zobacz też artykuł How Django processes a request?.
3 W Django nazywane są one metodami widoku. Osobiście nie podoba mi się zamieszanie terminologiczne jakie Django zrobiło z modelem MVC. Kontroler to dla nich Widok (View), zaś Widok to Szablon. Trochę to bez sensu, bo nikt tak tych rzecz nie nazywa. Czyli djangowe metody widoku to nic innego jak akcje kontrolera dla Rails i reszty świata.
Posted in Django, Ruby on Rails | Tagi django, rails | 2 comments
Opublikowane przez Jarosław Zabiełło
Fri, 14 Jul 2006 15:34:00 GMT
Porównanie wydajności trzech frameworków: Symfony, Ruby on Rails i Django pokazuje że Rails jest znacznie szybszy od pehapowego Symfony, a Django znacznie szybszy od Railsów. Co ciekawe, PHP5 używał akceleratora.
Posted in PHP, Django, Ruby on Rails | Tagi django, php, rails | 19 comments
Opublikowane przez Jarosław Zabiełło
Fri, 23 Jun 2006 07:13:00 GMT
Są już oficjalne wyniki głosowania na rzecz stworzenia grupy dyskusyjnej pl.comp.lang.ruby. 96 głosów za, 4 wstrzymujące się i 6 nieważnych. Nowa grupa powinna być wkrótce dostępna na serwerach usenetowych.
Posted in Ruby, Ruby on Rails | Tagi ruby | 3 comments
Opublikowane przez Jarosław Zabiełło
Sat, 03 Jun 2006 14:03:00 GMT
Po długim okresie oczekiwań w końcu doczekaliśmy się rozpoczęcia głosowania nad stworzeniem grupy pl.comp.lang.ruby. Oczywistą zaletą grupy newsowej jest m.in. to, że jest indeksowana i archiwizowana przez Google oraz jest bardziej odporna na “pady” serwera, bo posty są replikowane po wielu serwerach. Formalna treść ogłoszenia (wraz z informacją o sposobie głosowania) jest dostępna tutaj…
Posted in Ruby, Ruby on Rails | Tagi ruby | brak comments
Opublikowane przez Jarosław Zabiełło
Sun, 28 May 2006 01:46:00 GMT
RadRails ma silną konkurencję. Edytor JEdit jest nie tylko szybszy i mniej zabiera zasobów komputera, to na dodatek, przy dodaniu odpowiednich pluginów doskonale nadaje się do pracy z Railsami. Wygląda to bardzo dobrze, wystarczy rzucić okiem na tę stronę.
Posted in Ruby, Ruby on Rails | Tagi edytory, jedit | 3 comments