Skalowanie RoR

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

ruby script/server
można użyć mongrela:
mongrel_rails start

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  | Tagi  | 15 comments

Django, Rails i współdzielenie danych

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.

# plik: app/controllers/application.rb
class ApplicationController < ActionController::Base
  before_filter :defaults
  def initialize
    @name = "Jarek"
  end
  def defaults
    @msg = "Hello!"
  end
end

# plik: app/controllers/home_controller.rb
class HomeController < ApplicationController 
end

# plik: app/controllers/about_controller.rb
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:

# wpierw wciągamy RequestContext
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 ,  | Tagi ,  | 2 comments

Django i Rails biją PHP

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 , ,  | Tagi , ,  | 19 comments

Dlaczego Ruby on Rails jest wyjątkowy?

Opublikowane przez Jarosław Zabiełło Sun, 14 May 2006 04:22:00 GMT

Niektórym osobom stykającym się z po raz pierwszy z Railsami wydaje się, że jest to tylko jakieś kolejne, tradycyjne środowisko developerskie pracujące wg wzorca projektowego MVC (model-widok-kontroler). Przywiązani do swoich języków i frameworków czasami się dziwią, dlaczego temat Railsów wywołuje od jakiegoś czasu tyle emocji i komentarzy. Na pewno po częsci jest tak pewnie dlatego, że RoR jest bardzo dobrze wypromowany. Dobra strona główna, dobra dokumentacja, książki, filmy, bardzo aktywna społeczność – nic tylko naśladować. Z drugiej strony, trzeba też przyznać, że jest to środowisko świetnie zaprojektowane – w Railsach sie pracuje po prostu komfortowo.

Nie dziwią więc ciągłe próby naśladowania Railsów w innych językach (PHP, Python, Java, C# itp) Jednakże między nimi a Railsami będzie ciągle pewna, trudna do osiągnięcia, jeśli nie w ogóle niemożliwa – bariera. Railsy posiadają nie tylko wszystko, co potrzeba do bardzo produktywnego tworzenia aplikacji internetowych, ale są przy tym równocześnie bardzo eleganckie i czytelne. Eleganckie i czytelne czyli łatwe do nauki. Tak, nauka Rubiego nie jest przeszkodą. Przekona się o tym każdy, kto trochę bliżej przyjrzy się jak działa RoR.

Miałem okazję porównywać ze sobą kilka różnych frameworków. Gdy chciałem przekonać się do któregoś z nich, zawsze ostatecznie wracałem z powrotem do RoR. Po prostu żaden z nich nie jest jak elegancki i prosty w użyciu1. Co jest główną przyczyną takiego wrażenia? Twórca Railsów, David Heinemeier Hansson powiedział kiedyś, że gdyby nie Ruby to nie powstałby Ruby on Rails. Powiedział także, że uważa iż w żadnym innym języku nie da się napisać tak eleganckiego i pięknego kodu. Zatem można powiedzieć, że prawdziwą siłą Railsów jest Ruby. Razem tworzą nierozłączną parę i ta łączność nie dotyczy bynajmniej tylko nazwy. ;) Ruby posiada bowiem pewne unikalne cechy, które pozwoliły stworzyć RoR w postaci, która jest raczej mało nieosiągalna dla innych języków. Już to wyjaśniam.

Panuje powszechnie mniemanie, że Ruby to połączenie cech Pythona i Perla. Ruby (podobnie jak Perl) posiada np. wbudowaną w składnię obsługe wyrażeń regularnych. Posiada także (podobnie jak Python) pełną obiektowość i bardzo elegancką, czytelną (choć nie taką samą) składnię. Pewnym odkryciem było dla mnie to, że językie, do którego Ruby ma najwięcej podobieństw to innego języka – Smalltalk. Lektura cech, filozofii (i nawet do pewnego stopnia składni) Smalltalka pokazuje zdumiewające podobieństwo do Rubiego. Można wręcz odnieść wrażenie, że Ruby to swego rodzaju przeróbka Smalltalka2. Podobieństw jest bardzo wiele. Od słów kluczowych po symbole, sposób tworzenia instancji klas, bloki kodu (ang. closures), kontynuacje3 itp. Jak ktoś jeszcze nie rozumie filozofii Rubiego, jego modelu obiektowego i powodu istnienia otwartych klas, powinien poczytać sobie trochę o Smalltalku.

Ruby jak i Smalltalk posiadają bardzo podobny model obiektowy. Wszystkie obiekty (na drodze dziedziczenia) wywodzą się z jednej, ostateczniej klasy Object. Oba języki mają zaimplementowaną pełną obiektowość. Nie ma (tak ja w Javie) podziału na prymitywy i typy referencyjne. Wszystko jest obiektem i wszystko posiada metody. Dotyczy to nie tylko liczb i napisów ale także obiektu nil, true czy false. Każdy obiekt można przeciążyć i/lub zmodyfikować wewnetrznie (dynamicznie dodając lub usuwając jego metody w trakcie pracy programu)

Z tego wynika, że właściwie to można modyfikować sam język. Daje to możliwości zupełnie nieosiągalne nawet dla tak dobrego i obiektowego języka jakim jest Python. Ruby pozwala na łatwe dodawanie nowych metod do liczb czy napisów. Pozwala na taką modyfikację samego siebie, aby optymalnie nadawał się do realizacji pewnych, specyficznych zadań. Ruby umożliwia zatem tworzenie tego, co się określa mianem języków domenowych (Domain-specific Programming Languages). Są to języki, które w przeciwieństwie do języków ogólnego zastostosowania, zostały zaprojektowane do wykonywania specyficznego zadania/zadań. Zarówno Smalltalk jak i Ruby pozwalają na tworzenie języków domenowych.

Ruby on Rails to nic innego jak framework napisany za pomocą Rubiego zmodyfikowanego w celu uzyskania wysoce produktywnego środowiska do tworzenia nowoczesnych aplikacji internetowych.

Przykładowe helpery dostępne w Rails, które korzystają z nowych metod nie będących standardową częścią Rubiego. Poniższe przykłady pochodzą z 1-g wydania książki Agile Web Development in Rails.

puts 20.bytes #=> 20
puts 20.kilobytes #=> 20480
puts 20.megabytes #=> 20971520
puts 20.gigabytes #=> 21474836480
puts 20.terabytes #=> 21990232555520
puts 20.minutes.ago #=> Tue May 10 16:43:43 CDT 2005
puts 20.hours.from_now #=> Wed May 11 13:03:43 CDT 2005
puts 20.weeks.from_now #=> Tue Sep 27 17:03:43 CDT 2005
puts 20.months.ago #=> Thu Sep 18 17:03:43 CDT 2003
now = Time.now
puts now #=> Tue May 10 17:15:59 CDT 2005
puts now.ago(3600) #=> Tue May 10 16:15:59 CDT 2005
puts now.at_beginning_of_day #=> Tue May 10 00:00:00 CDT 2005
puts now.at_beginning_of_month #=> Sun May 01 00:00:00 CDT 2005
puts now.at_beginning_of_week #=> Mon May 09 00:00:00 CDT 2005
puts now.at_beginning_of_year #=> Sat Jan 01 00:00:00 CST 2005
puts now.at_midnight #=> Tue May 10 00:00:00 CDT 2005
puts now.change(:hour => 13) #=> Tue May 10 13:00:00 CDT 2005
puts now.last_month #=> Sun Apr 10 17:15:59 CDT 2005
puts now.last_year #=> Mon May 10 17:15:59 CDT 2004
puts now.midnight #=> Tue May 10 00:00:00 CDT 2005
puts now.monday #=> Mon May 09 00:00:00 CDT 2005
puts now.months_ago(2) #=> Thu Mar 10 17:15:59 CST 2005
puts now.months_since(2) #=> Sun Jul 10 17:15:59 CDT 2005
puts now.next_week #=> Mon May 16 00:00:00 CDT 2005
puts now.next_year #=> Wed May 10 17:15:59 CDT 2006
puts now.seconds_since_midnight #=> 62159.215938
puts now.since(7200) #=> Tue May 10 19:15:59 CDT 2005
puts now.tomorrow #=> Wed May 11 17:15:59 CDT 2005
puts now.years_ago(2) #=> Sat May 10 17:15:59 CDT 2003
puts now.years_since(2) #=> Thu May 10 17:15:59 CDT 2007
puts now.yesterday #=> Mon May 09 17:15:59 CDT 2005
puts "cat".pluralize #=> cats
puts "cats".pluralize #=> cats
puts "erratum".pluralize #=> errata
puts "cats".singularize #=> cat
puts "errata".singularize #=> erratum

Z racji tego, że Railsy zmodyfikowały Rubiego do realizacji swoich zadań, uważam, że próba sklonowania tego środowiska w jakimś innym języku (poza Smalltalkiem), jest z góry skazana na niepowodzenie. Po prostu nigdy to nie będzie tak piękny kod jak Ruby dla Railsów. Na otarcie łez dla pythonistas, mogę powiedzieć, że przy wszystkich zaletach Rubiego, nadal uważam że Python (jako język) jest nie tylko łatwiejszy do opanowania ale także bardziej produktywny. Ale jeśli chodzi o frameworki, to Rails jest nie do pobicia jeśli chodzi o komfort, możliwości i DRY[4].

1 Zupełnie identyczne wrażenia prostoty jak z Railsami miałem w przypadku szukania dobrego systemu CMS’a. Taki np. pythonowy Plone w porównaniu do ezPublish i całej masy innych, pehapowych rozwiązań jest nie tylko poteżniejszy ale także niezrównanie prostszy i wygodny (po 5 minutach od instalacji, praktycznie bez czytania dokumentacji i bez znajomości Pythona, można stworzyć prosty serwis o całkiem przyzwoitej, podstawowej funkcjonalności)

2 Smalltalk posiada tylko 5 słów kluczowych i proste zasady: wszystko jest obiektem, wszystkie operacje polegają na przesyłaniu metod (nazywanych tu: wiadomościami) między obiektami. Moim zdaniem Smalltalk trochę przesadził z tą zasadą, bo nawet bo zamiast do sterowania kontrolą kodu używane są także wiadomości. Może jest jest spójne, ale trochę dziwnie wygląda. Osobiście bardziej podobają mi się możliwości Smalltalka ale wyrażone w składni …Rubiego. :) Zobacz też Ruby Instead of Smalltalk

3 Kontynuacje są jedną z wbudowanych cech Rubiego o której może być za jakiś czas głośno. Umożliwiają bowiem budowanie tzw. internetowych serwerów kontynuacyjnych, które w pełni zachowują stan pomiędzy requestami i ogromnie upraszczają prace programistów. W tej chwili najlepiej opracowanym frameworkiem tego typu jest smalltalkowy Seaside.

4 DRY to skrót od ang. Dont’t Repeat Yourself. Railsy zostały napisane z wręcz obsesyjną ;) cechą unikania powtarzania kodu. Mniej powtórzeń to nie tylko mniej niepotrzebnej, dodatkowej pracy ale także mniejsze ryzyko popełniania błędu.

Posted in , ,  | Tagi , ,  | 32 comments

Modularyzacja RoR - komponenty

Opublikowane przez Jarosław Zabiełło Wed, 19 Apr 2006 21:11:00 GMT

RoR pozwala na modularyzację projektu za pomocą wtyczek, zagnieżdżonych miniaplikacji (rails engines) oraz komponentów.

Używany tutaj blog jest oparty na Typo, aplikacji napisanej w RoR. Nie znalazłem w niej jednak gotowego, wbudowanego mechanizmu aby dodać galerię fotek. Są co prawda linki do zewnętrznego serwisu flickr.com, ale w wersji darmowej można umieścić stosunkowo niewiele fotek.

Aplikacja bloga chodzi na lighttpd i fastcgi. Wszelkie modyfikacje kodu wymagają restartu serwera. Mało to raczej wygodne. Postanowiłem więc stworzyć sobie oddzielny projekt RoR. A zatem standardowo: rails fotki; cd fotki. Potem modyfikacja config/database.yml (ustawilem polaczenie do dowolnej bazy, i tak nie bede uzywal). Do pracy roboczej wystarczy mi webrick. A zatem ruby scripts/serwer i pod adresem http://localhost:3000 dziala nowy projekt.

Starając się trzymać zasady DRY (czyli unikać powtarzania kodu). Działający kod zamieniłem następnie na komponent. Czyli procedura była taka: wpierw tworzę sobie aplikację w izolowanym projekcie RoR. Na stępnie przenoszę ją do komponentu (nadal w tym samym projekcie). Testuję. Jak działa, to łączę komponent z aplikacją docelową. Dzięki użyciu komponentu, unikam zaśmiecania kodu aplikacji docelowej, ograniczając do minimum ilość zmian jakie muszę dodać.

Komponenty

Komponenty w RoR tworzy się (podobnie jak inne rzeczy) stosunkowo łatwo i przyjemnie. Kazda świeża instalacja RoR tworzy domyślnie pusty folder components. Należy tam przejść i stworzyć swój podfolder, np. fotki.

Następnie przenosimy tam plik z kontrolerem. W tym wypadku było to plik miniaturki_controlle.rb. Wiadomo gdzie był: apps/controllers/. Przenosimy go zatem do components/fotki/. Musimy także dokonać w nim paru zmian. Przede wszystkim klasa kontrolera nie może dziedziczyć bezpośrednio po ApplicationController. Musimy uwzględnić to, że kontroler leży w innym miejscu.

Trzeba także zmienić definicje klasy i dodać informację, że szablony będą leżeć w innym miejscu niż domyślnie app/views/. Kontroler więc wyglądać może tak:

class Fotki::MiniaturkiController < ApplicationController
  uses_component_template_root

  def mysio_lista
    filenames(File.dirname(__FILE__)+'/../../public/fotki/mysio', '*.jpg')
    @subfolder = 'mysio/'
    render :template => 'fotki/miniaturki/lista'
  end

  def mysio_fotka
    filenames(File.dirname(__FILE__)+'/../../public/fotki/mysio', '*.jpg')
    @subfolder = 'mysio/'
    render :template => 'fotki/miniaturki/fotka'
  end

  protected

  def filenames(path, filter)
    @filenames = []
    Dir.glob(path+File::SEPARATOR+filter).each do |f|
      @filenames << File.basename(f).sub(/.*^\D+(\d+).+$/, '\1')
    end
    @filenames.sort!
  end
end

Metoda filenames wciaga liste plików i przypisuje do zmiennej instancji @filenames (zmienne tego typu są widziane w szablonach).

Przenieśliśmy kontroler, pora na szablony. Tworzymy zatem kolejny podfolder components/fotki/miniaturki/ i przenosimy tam wszystkie nasze szablony z app/views/miniaturki/. Szablon lista.rhtml służy do wyrzucenia na ekran miniaturek z obrazkami:

<% for f in @filenames %>
  <% if @subfolder%>
    <%= link_to(image_tag("/fotki/#{@subfolder}thumbnails/prev_pict#{f}.jpg",:border => "1"), "/#{@subfolder}#{f}") %>
  <% else %>
    <%= link_to(image_tag("/fotki/#{@subfolder}thumbnails/prev_pict#{f}.jpg",:border => "1"), "/fotka/#{f}") %>
  <% end %>
<% end %>

Natomiast szablon fotka.rhtml wyświetla jedno zdjęcie w pełnym formacie:

<div align="center">
  <div> 
    <%= render :partial => 'menu' %>
    <%= image_tag "/fotki/#{@subfolder}pict#{params[:id]}.jpg" %>
    <%= render :partial => 'menu' %>
</div>

Dobrze byłoby także, aby można było nawigować między obrazkami. Dodałem więc proste menu u góry i na dole. Aby się nie powtarzać, szablon z treścią menu wyniosłem do szablonu cząstkowego (partial). Plik z takim szablonem musi mieć obowiązkowo znak podkreślnika na początku nazywy. Szablon _menu.rhtml wygląda tak:

  <div style="margin-top:10px; margin-bottom: 10px"> 
    [ 
      <% if @filenames.first != params[:id] %>
        <% if @subfolder %>
          <a href="/<%= @subfolder %><%= @filenames[@filenames.index(@params[:id]) - 1] %>">&lt;&lt;</a>
        <% else %>
          <a href="/fotka/<%= @filenames[@filenames.index(@params[:id]) - 1] %>">&lt;&lt;</a>
        <% end %>
        |
      <% end %>
      <% if @subfolder %>
        <a href="/<%= @subfolder %>">miniaturki</a> 
      <% else %>
        <a href="/fotki">miniaturki</a> 
      <% end %>
      <% if @filenames.last != params[:id] %>
        |
        <% if @subfolder %>
          <a href="/<%= @subfolder %><%= @filenames[@filenames.index(@params[:id]) + 1] %>">&gt;&gt;</a>
        <% else %>
          <a href="/fotka/<%= @filenames[@filenames.index(@params[:id]) + 1] %>">&gt;&gt;</a>
        <% end %>
      <% end %>
    ]
  </div>

Jest troszkę rozbudowany z dwóch powodów. Chciałem aby przejścia do następnej lub wcześniejszej fotki były dostępne tylko wtedy kiedy to ma sens. Poza tym, kod uwzględnia tworzenie podfolderów ze zdjęciami. Stąd we wszystkich szablonach dodatkowa zmienna @subfolder.

Gdybym chciał umieścić wywołanie komponentu w szablonie aplikacji, to musiałbym użyć funkcji render_component. Jednak, w tym wypadku nie chciałem grzebać w szablonach Typo. Zatem ostatnią rzeczą jaka została, to zdefiniwanie odpowiedniej reguły dla resolvera adresów. Chcę aby lista pokazała się pod adresem /mysio a konkretne fotki pod adresem /mysio/nrfotki. Reguły rozwiązywania adresów URL znajdują się w pliku config/routes.rb:

ActionController::Routing::Routes.draw do |map|
  # reszta kodu
  map.connect 'mysio', 
              :controller => 'fotki/miniaturki',
              :action     => 'mysio_lista'              
  map.connect 'mysio/:id',
              :controller => 'fotki/miniaturki',
              :action     => 'mysio_fotka',
              :id         => /\d+/
  # blok kończy domyślna reguła:
  map.connect ':controller/:action/:id'
end

To prawie wszystko. Prawie, bo musiałem także napisać kod do przeskalowania obrazków (domyślnie były w rozdzielczości 1280×1024) oraz wygenerowania miniaturek. W związku z tym, że chciałem wszystko już napisac w Ruby, zabrałem się za pisanie skryptu w tym języku. Przeskalowanie fotek do rozdzielczości 1024×768:

#!/usr/bin/ruby
require 'RMagick'
default_path = '/home/rubyonrails/typo/public/fotki'
path = $1 || default_path
Dir.chdir(path)
Dir.glob('*.jpg').each do |f|    
  img = Magick::Image::read(f).first
  if img.columns > 1024
    print f
    img.change_geometry("1024x768") { |cols, rows, im| im.resize!(cols, rows) }
    img.write f
    puts " przeskalowane"
  end
end

Oraz generowanie miniaturek:

#!/usr/bin/ruby
require 'RMagick'
default_path = '/home/rubyonrails/typo/public/fotki'
path = $1 || default_path
outpath = path + File::SEPARATOR + 'thumbnails'
Dir.mkdir(outpath) if not File.exists?(outpath)
Dir.chdir(path)
Dir.glob('*.JPG').each do |f|  
  if f != f.downcase
    File.rename(f, f.downcase)
    f.downcase!
  end
  img = Magick::Image::read(f).first
  img.change_geometry("128x128") { |cols, rows, im| im.resize!(cols, rows) }
  img.write "#{outpath}#{File::SEPARATOR}prev_#{f}"
  p f
end

Niestety, tu spotkała mnie przykra niespodzianka. Biblioteka RMagic wywalała się z komunikatem o braku pamięci. Kod jest poprawny. To jest błąd w tej bibliotece. Chcąc, nie chcąc, przepisałem kod do Pythona:

#!/usr/bin/env python2.4
'''
Skaluje obrazki do 1020x768 oraz generuje miniatiurki
Operuje na plikach JPEG.
'''
import glob, Image, os, sys
scale = (1024,768)

try:
    path, pattern, prefix = sys.argv[1], sys.argv[2], sys.argv[3]
except:
    print "Skladnia: sciezka wzorzec prefix"
    sys.exit(1)
os.chdir(path)
thumbnails_path = path + os.sep + 'thumbnails'
if not os.path.exists(thumbnails_path):
    os.makedirs(thumbnails_path)
for f in glob.glob(pattern):
  print f,
  img = Image.open(f)
  size = img.size
  if max(size) > 1024:
    print "%s (%sx%s)" % (f, size[0], size[1]),
    img = img.resize(scale, Image.ANTIALIAS)
    img.save(f, 'JPEG')
    size = img.size
    print ' przeskalowane do %sx%s' % (size[0], size[1]),
  print " przeskalowywanie do 128x128",
  img.thumbnail((128, 128), Image.ANTIALIAS)
  img.save('thumbnails/%s_%s' % (prefix, f), 'JPEG')
  print

Tym razem wszystko przeszło gładko i szybko. Kod Pythona wykorzystujący biblioteke PIL był poza tym jakieś 2x szybszy. Sprawdza się zatem to, że współcześni programiści nie mogą ograniczać się tylko do jednego języka. Czasami warto mieć w zapasie drugi. Python jest doskonałym uzupełnieniem dla Rubiego. Ruby ma lepszy framework, ale Python ma dojrzalszą bibliotekę graficzna.

Jedyną rzeczą którą nie udało mi się rozwiązać, to umieszczenie treści komponentu w ramach aplikacji Typo (tak jak widać np,. ten artykuł). Typo jest dosyć skomplikowaną aplikacją i nie mogłem się połapac w jego plikach z layoutem. Niektórzy żartują że Typo to przykład kodowania sztucznej inteligencji. :) Przykład działających fotek z moim ulubionym futrzakiem można zobaczyć: tutaj.

Posted in  | Tagi  | 3 comments

Arachno IDE 0.6.9

Opublikowane przez Jarosław Zabiełło Thu, 06 Apr 2006 21:21:00 GMT

Została udostępniona kolejna wersja beta edytora Arachno IDE. Z najważniejszych zmian jest pełna obsługa UTF-8! Program wraz z plikiem zawierającym klucz autoryzującym na 3 miesiące można pobrać z forum . Zachęcam użytkowników Rubiego i Rails do testów.

Stworzono też odzielne forum dla beta testerów. Można na nim pobrać wersję beta (aktualnie jest już wersja 0.6.10)

Tagi , ,  | brak comments

RoR 1.1 i problemy z Typo

Opublikowane przez Jarosław Zabiełło Wed, 05 Apr 2006 07:18:00 GMT

Niektóre aplikacje nie są jeszcze w pełni dostosowane do pracy z najnowszą wersją Railsów. Np. taką aplikacją jest Typo (obsługująca zresztą ten blog) Wpierw należy sprawdzić jaką wersją Railsów dysponujemy:
rails -v
Jeśli jest nowsza od wersji 1.0, to należy się cofnąć:
gem install -r rails -v "=1.0.0"
Następnie wszystkie podejrzane aplikacje RoR “zamrażamy”. Wystarczy przejść do folderu gdzie mamy aplikację w Rails i z poziomu konsoli wykonać następującą komendę:
rake freeze_edge REVISION=3303

Dopiero teraz możemy zrobić upgrade Railsów:

gem install -r rails --include-dependencies

Powinno wszystko działać (w Railsach 1.1.1 wprowadzono dodatkowe nowości zamrażania). Poza tym dobrze wiedzieć, że trwają prace nad Typo aby działał z RoR 1.1

Posted in  | Tagi ,  | brak comments

RoR 1.1 wersja finalna!

Opublikowane przez Jarosław Zabiełło Wed, 29 Mar 2006 03:18:00 GMT

Wyszła oficjalna wersja Ruby on Rails 1.1. To bardzo ważna aktualizacja. Zawiera ponad 500 zmian i dodatków.

M.in. wprowadzono nowy typ szablonów jeszcze bardziej ułatwiających korzystanie z AJAX’a. RoR posiada zatem 3 podstawowe rodzaje szablonów: główne RHTML, RXML (do XML i kanałów RSS) oraz RJS do AJAX’a. Poza tym można podpiąć inne, np. wzorowany na pythonowym DjangoLiquid.

Ulepszono także ActiveRecord (dodano m.in. powiązania polimorficzne, oraz “migracje”, czyli wersjonowanie struktury bazy, które są teraz zintegrowane z generatorami modeli.

Do kontrolerów dodano metodę respond_to ułatwiająca pisanie stron działających równocześnie zarówno z jak i bez AJAX’a. Wprowadzono dodatkowy rodzaj testów integracji i wiele, wiele innych cech, w sumie ponad 500 zmian i dodatków zebranych przez grupę ponad 100 kontrybutorów.

Posted in  | Tagi  | 2 comments

RadRails najlepsze narzędzie do RoR?

Opublikowane przez Jarosław Zabiełło Thu, 23 Mar 2006 05:53:00 GMT

RadRails to wysokiej klasy edytor klasy IDE dedykowany do pracy z Ruby on Rails. Jest on przeróbką kodu słynnego Eclipse. RadRails otrzymał nagrodę najlepszego narzędzia developerskiego pod Eclipse: “Eclipse Best Developer Tool”.

Najnowsza wersja RadRails (0.6.1) wprowadza kolejne udogodnienia w pracy, więcej generatorów, dodano obsługę unit testów, lepsze uzupełnianie kodu (hmm, przypomina coraz bardziej TextMate).

Dzięki RadRails, nie trzeba w ogóle wchodzić do konsoli aby uruchamiać skrypty Railsów. RadRails generuje (lub usuwa kontrolery), modele, restartuje webrick’a (wbudowany serwer www dedykowany do pracy developerskiej), ma wbudowany podgląd przeglądarki, . W sumie wszystko w jednym.

Posted in ,  | Tagi , ,  | 9 comments

Ruby on Rails najlepszym frameworkiem roku

Opublikowane przez Jarosław Zabiełło Thu, 16 Mar 2006 11:55:00 GMT

Ruby on Rails dostał nagrodę jako najlepsze narzędzie do budowania aplikacji internetowych. Wzorcowa promocja RoR robi swoje. Tylko naśladować.

Posted in  | Tagi , ,  | brak comments

Pylons - czyli siła Myghty i wygoda RubyonRails

Opublikowane przez Jarosław Zabiełło Fri, 03 Mar 2006 23:51:00 GMT

Coraz bardziej ciekawie rozwija sie pythonowy framework do szybkiego budowania aplikacji internetowych: Pylons. W przeciwieństwie do TurboGears Pylons jest zbudowany na bazie Myghty a nie CherryPy. Myghty jest bardzo szybkim, stabilnym i dobrze napisanym frameworkiem. Pracuje jako cgi, mod_python, wielowątkowo lub wieloforkowo, dzięki WSGI. Posiada doskonały, elastyczny cache, obsługę AJAX’a, słowem prawie wszystko co trzeba dla współczesnej aplikacji internetowej. Jednak posiada za dużo cech które w branży pythonowej określa się jaki “perlish” (Myghty był wzorowany na perlowym Masonie)

I tu z pomocą przychodzi Pylons. Jest on czymś w rodzaju megaframeworku, czyli frameworku zbudowanym na bazie innego frameworku, w tym wypadku: Myghty. (Na podobnej zasadzie TurboGears jest megaframeworkiem opartym na CherryPy).

Pylons różni się jednak tym od TurboGears że jest oparty na frameworku znacznie bardziej solidnym i znacznie lepiej naśladuje framework bedący natchnieniem dla większości innych, czyli Ruby on Rails. Pylons czerpie pełnymi garściami z najlepszych pomysłów Railsów dodając do tego większą wydajność Pythona, elastyczność i prostotę składni oraz siłę jego bibliotek (np. Python ma doskonale zaimplementowaną obsługę Unicode, co jest w Ruby potraktowane trochę po macoszemu, a w PHP to w ogóle tragedia)

Zobaczmy przykładowo, co nam daje Pylons. Np. rozwiązywanie adresów. Adresy URL są rozwiązywane za pomoca bardzo ciekawej biblioteki Routes w sposób podobny do Railsów. Np. załóżmy że bieżący request zwraca następujący słownik: {‘controller’: ‘blog’, ‘action’: ‘view’, ‘id’: 2 (gdzie rozwiązywanie adresów url odbywa się wg definicji podobnej do Railsów: ’:controller/:action/:id’). Uzyskamy następujące wyniki:

url_for(id=4)                    =>  '/blog/view/4',
url_for(controller='/admin')     =>  '/admin',
url_for(controller='admin')      =>  '/admin/index/4'
url_for(action='edit')           =>  '/blog/post/4',
url_for(action='list', id=None)  =>  '/blog/list'

Pylons “przeportował do Pythona” całą masę użytecznych helperów znanych w Rails. Zajmuje się tym biblioteka RailsHelpers. Przykładowo wygląda to tak:

link_to("Delete this page", url(action="destroy", id=4), confirm="Are you sure?")
link_to("Help", url(action="help"), popup=True)
link_to("Busy loop", url(action="busy"), popup=['new_window', 'height=300,width=600'])
link_to("Destroy account", url(action="destroy"), confirm="Are you sure?", post => True)

Jaką to daje nam korzyść? Ogromną! Wyobraźmy sobie że chcemy zmienic sposób rozwiązywania adresów url. Co z milionami stron, które miały linki do różnych wewnętrznych części naszego serwisu? Koszmar! Trzeba ręcznie je poprawiać. Dzięki Pylon, nie musimy bezpośrednio wklepywać adres url, ale używamy do tego helperów. Zmiana zasad rozwiązywania adresów automatycznie poprawi adresy we wszystkich miejscach. Korzyść jest niewymierna!

RailsHelpers zawiera także więcej helperów. Np. można na nie zrzucić budowę formularzy.

Można też uzyskać efekty specjalne połączone z AJAX’em w identyczny sposób jak w Railsach. Pylons bowiem impelmentuje w Pythonie tą samą, znakomitą bibliotekę JavaScript: scriptaculous

Nie sposób nie wspomnieć także o interaktywnym debuggerze. To poprostu jest nowa jakość. Można interaktywnie prześledzić stan obiektów aplikacji internetowej. Świetny pomysł.

Jak dla mnie, Pylons staje się głównym rywalem Django do tytułu najlepszego frameworka dla języka Python. Jest znacznie bardziej elastyczny, posiada wysoką wydajność i możliwości Myghty oraz w łatwiejszy sposób rozwiązuje adresy url i ma wygodniejsze helpery, bo wzorowane na Ruby on Rails. Więcej na temat Pylons można śledzić także na stronie http://groovie.org/

Posted in ,  | Tagi ,  | 2 comments

A Railsy pędzą do przodu...

Opublikowane przez Jarosław Zabiełło Fri, 03 Feb 2006 14:40:00 GMT

Gdy w środowisku Pythona trwa wciąż zamieszanie i dyskusja wokół rywalizujących ze sobą frameworków (moim zdaniem największe szanse na zwycięstwo ma Django) tego problemu nie ma dla języka Ruby. Mimo, że istnieją także inne ciekawe frameworki, to Ruby on Rails zdecydowanie dominuje i pędzi do przodu. Framework jest dojrzały i okrzepnięty. Testowany przez tysiące developerów od wielu miesięcy.

Dokumentacja, API, filmy, FAQ, recepty, Wiki i książki powinny być wystarczające dla każdego kto by chciał spróbować możliwości tego frameworka.

RoR jest wzorcowym przykładem także tego, jak należy promować swój produkt. Oficjalna strona RoR jest bardzo czytelna i pomocna. Istnieje także polska strona Railsów dla osób które chciałyby wymienić się doświadczeniem w jęz. polskim.

Typowe obawy

Częstym argumentem przeciwko zainteresowaniu się Railsami jest to, że są one oparte na języku Ruby, którego się nie zna. Ja też miałem takie obawy na początku. Tym bardziej, że nie wydawało mi się, aby Ruby miał coś zdecydowanie lepszego do zaoferowania w stos. do Pythona.

W praktyce okazalo się, że Ruby nie jest wcale jakoś mocno trudniejszy od Pythona. Okazuje się bowiem, że do praktycznego posługiwania się Railsami nie trzeba wcale znać doskonale Rubiego. Dziwne, ale prawdziwe. Agile Web Development in Rails na końcu zawiera zwięzłe wprowadzenie do Rubiego na użytek Railsów. I jest w miarę wystarczające aby rozpocząć korzystanie z RoR.

Edytor do Railsów

Do wygodnej pracy z RoR, warto sobie zainstalować jakiś dobry edytor. Jest całkiem sporo opcji. Moim faworytem jest Arachno IDE i RadRails (do Railsów), oraz RDE do czystego kodowania w Rubym. Bardzo zachwalany przez wielu jest TextMate. (Niestety działa tylko pod systemem OS-X. Ale znajduje takie uznanie, że wiele osób dla tego edytora kupuje sobie Mini Maki. :)

Dokumentacja

Railsy posiadają bardzo dobrą dokumentację. Na stronie domowej są dostępne filmy (warto obejrzeć!), manuale, recepty, FAQ, WIKI, mnóstwo przydatnych materiałów. No i jak komuś mało, to są dostępne książki. Przede wszystkim kultowa Agile Web Development with Rails oraz PixAxe2 czyli Programming Ruby napisana przez twórcę jęz. Ruby. Poza tym w drodze jest cała grupa nowych książek.

Posted in  | Tagi  | 2 comments

Guido van Rossum o frameworkach

Opublikowane przez Jarosław Zabiełło Fri, 27 Jan 2006 04:38:00 GMT

Twórca języka Python – Guido van Rossum z racji tego, że aktualnie pracuje dla Google, zainteresował się trochę więcej sprawą budowania aplikacji webowych w Pythonie. :) W swym artykule opisuje swoje wstępne wrażenia m.in. z kontaktu z Django oraz Ruby on Rails.

Zdaniem Guido, Django posiada ładny resolver adresów URL. Nie podoba mu się za to język szablonów, który zdaniem Guido jest zbyt “niepythonowy” (jakoś trudno mi sobie wyobrazić jak system szablonów może być w ogóle “pythonowy” :) No i to, że szablony Django kojarzą mu się z PHP, pokazuje że przyjrzał im się zbyt powierzchownie (ja tam nie widzę żadnego podobieństwa).

System szablonów Django jest ciekawym połaczeniem obiektowości pythonowego Cheetah i cech pehapowego Smarty. Może się podobać, lub nie, ale generalnie jest szybki i spełnia swoją rolę: jest adresowany do nieprogramistów.

Z kolei RoR posiada swój system szablonów który jest b. prosty: to po prostu połączenie Rubiego z HTML (b. podobnie do PHP czy JSP). To podejście ma także zalety jak i wady (np. trzeba znać Rubiego ale z 2-j strony nie trzeba uczyć się kolejnego pseudojęzyka do szablonów). Ostatecznie, jak komuś to nie odpowiada, to dla RoR istnieje alternatywny system szablonów zwany Liquid , system, który składnią przypomina szablony Django.

Szkoda, że Guido nie przyjrzał się bibliotece Pylons, który używa Myghty (niedawno wyszła wersja 1.0) a do rozwiązywania adresów używa b. ciekawej biblioteki – Routes.

Posted in  | Tagi , , , ,  | brak comments

Komentarz do konferencji Snakes & Rubies

Opublikowane przez Jarosław Zabiełło Thu, 19 Jan 2006 20:08:00 GMT

DHH, twórca Ruby on Rails w swoim blogu opisał różnicę między Django i RoR Generalnie podsumował swoje stanowisko, które wyraził na konferencji. Szkoda, że Adrian (ani nikt z developerów Django) nie napisał w swoim blogu żadnego komentarza. :(

Pod artykułem DHH jest sporo ciekawych komentarzy. Warto je poczytać.

Generalnie DHH został zaatakowany za niedocenianie roli umiędzynarodowienia RoR. Ludzie tego potrzebują i dobrze by było aby do RoR zostało dodane i18n. Jest kilka gotowych modułów, które do tego celu się nadają, np. Globalize (Być może DHH powstrzymuje wdrożenie i18n z powodu słabej implementacji UTF-8 (Unicode w ogólności) w Ruby? W przeciwieństwie do Rubiego Python ma wzorcową obsługę Unicode, więc wszelkie konwersje znaków nie są żadnym problemem.) Oczywiście nie ma nawet co mówić o kompletnie nędznym supporcie Unicode i UTF-8 w PHP gdzie nie ma nawet jak przeprowadzić konwersji z latin2 do utf8 a podpieranie się kulawym modułem iconv tylko ucina teksty w wypadku znaku który nie da się poddać konwersji. Ale co ja będę kopał leżącego… ;)

Nie mogłem się powstrzymać ;) i dodałem także swój komentarz do tego tekstu i w ogóle do kwestii rzekomej wyższości Rubiego nad Pythonem. Wiekszość osób które wpisały się do blogu Davida najwyraźniej ma mikre pojęcie o Pythonie. To prawda, że Ruby ma kilka cech lepszych od Pythona, ale prawdą jest także że Python posiada kilka cech lepszych od Rubiego. Postaram się później to jakoś podsumować w oddzielnym tekście.

Posted in ,  | Tagi ,  | 5 comments

Symfony - kolejny naśladowca RoR

Opublikowane przez Jarosław Zabiełło Tue, 10 Jan 2006 06:30:00 GMT

Pojawia się coraz więcej naśladowców drogi jaką podążają frameworki Ruby on Rails i Django. Bardzo ciekawie wygląda pehapowy framework Symfony. Polecam przyjrzeć się udostępnionemu na stronie głównej filmowi.

Posted in  | Tagi ,  | 3 comments

Ruby on Rails 1.0, Django 0.9

Opublikowane przez Jarosław Zabiełło Thu, 05 Jan 2006 10:36:00 GMT

Wyszła finalna wersja Ruby on Rails 1.0. Django jest zaś dostępne w wersji 0.9.

Posted in ,  | Tagi ,  | brak comments

Komentarze i filmy z konferencji Django + Ruby on Rails

Opublikowane przez Jarosław Zabiełło Thu, 05 Jan 2006 10:26:00 GMT

Zostały udostępnione nagrania wideo z ostatniej konferencji Snakes and Rubies organizowanej przez developerów Django oraz Ruby on Rails. Wiele osób umieściło także komentarze na temat tej konferencji w swoich blogach.

Posted in ,  | Tagi ,  | brak comments

Rails i Django organizują wspólną konferencję!

Opublikowane przez Jarosław Zabiełło Thu, 17 Nov 2005 20:00:00 GMT

3 grudnia developerzy Rubiego i Pythona mają się spotkać razem na uniwersytecie DePaul w Chicago. Będzie można usłyszeć dwóch liderów aplikacji webowych drugiej generacji. Przemawiać będzie twórca Railsów: David Heinemeier Hansson oraz twórca Django: Adrian Holovaty. Można wysłać za pomocą internetu pytania do obu mówców na które będą odpowiadać podczas konferencji. Zobacz http://snakesandrubies.com/event Mam nadzieję że będzie z tego dostępny jakiś film albo chociaż transkrypcja.

Posted in ,  | Tagi ,  | brak comments

Optymalizacja użycia railsów

Opublikowane przez Jarosław Zabiełło Sun, 13 Nov 2005 20:43:00 GMT

Optymalizacja użycia Railsów: http://weblog.textdrive.com/article/175/rails-optimizing-resource-usage

Posted in  | Tagi  | 5 comments

Starsze wpisy: 1 ... 3 4 5