<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feed.ghandal.net/~d/styles/itemcontent.css"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>GhandaL i jego świat</title><link>http://blog.ghandal.net/</link><description>Wpisy z dziennika internetowego Jogger, wspomaganego przez Jabbera</description><lastBuildDate>Fri, 30 Jul 2010 00:10:43 +0200</lastBuildDate><generator>JoggerPL</generator><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feed.ghandal.net/ghandal" /><feedburner:info uri="ghandal" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><title>RuPy - dzień 2 i podsumowanie</title><link>http://feed.ghandal.net/~r/ghandal/~3/UAkQ32vc2wA/</link><description>&lt;p&gt;W &lt;a href="http://ghandal.jogger.pl/2009/11/07/rupy-krotko-o-dniu-pierwszym/"&gt;poprzednim wpisie&lt;/a&gt; było trochę o pierwszym dniu &lt;a href="http://rupy.eu"&gt;RuPy&lt;/a&gt;. W tym dalszy ciąg programu.&lt;/p&gt;
&lt;p&gt;Drugi dzień zapowiadał się lepiej, niż pierwszy. I chyba byłby taki, gdyby nie odwołane prezentacje.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Paolo Negri :: RabbitMQ&lt;/strong&gt; — ciekawa prezentacja nt. wykorzystania erlangowego &lt;a href="http://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt; oraz &lt;a href="http://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol"&gt;AMQP&lt;/a&gt; w aplikacjach pisanych w językach Ruby i Python. Momentami szybko prowadzona przez co zdarzało mi się zastanawiać, jaki język słyszę — angielski czy włoski, ale mimo wszystko podobało mi się. Pierwotnie miałem w tym czasie iść na &lt;strong&gt;Mastering git&lt;/strong&gt;, ale ze względu na zmiany agendy udało się zobaczyć obie prezentacje (to jest chyba jedyny plus zmian agendy bez wcześniejszego powiadomienia via. strona konferencji, tweeter, blip, flaker, whatever...)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scott Chacon :: Mastering Git&lt;/strong&gt; — bezkonkurencyjnie najlepsza prezentacja, a w zasadzie najlepsze show na konferencji. Git Ninja Training boski, wiele nowego czeka na zastosowanie (git bisect, git cherry-pick, itp.). No i nareszcie zdobyłem &lt;a href="http://github.com"&gt;GitHub&lt;/a&gt;owe naklejki :)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Marcin Raczkowski :: Replacing REGEX with TreeTop&lt;/strong&gt; — idea zastąpienia wyrażeń regularnych parserami jest ciekawa, szczególnie, że nie wszystkie problemy da się rozwiązać Regexpami. Jednak cały czas mam pewne wątpliwości co do wydajności takiego rozwiązania względem regexpów w problemach, które można rozwiązać zarówno jednym, jak i drugim sposobem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lightning Talks Session&lt;/strong&gt; — nie wszystkie prezentacje pamiętam. Ale szczególnie podobała mi się prezentacja &lt;a href="http://blog.headius.com/"&gt;Charlesa&lt;/a&gt; na temat języka &lt;a href="http://kenai.com/projects/duby"&gt;Duby&lt;/a&gt;, czyli języka ze składnią Ruby i statycznym typowaniem. Kod działa na JVM i jest kompilowany do plików *.class (można też wygenerować kod Java ze źródeł w duby).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Serge Smetana :: Advanced Performance Optimization of Rails Applications&lt;/strong&gt; — na tej prezentacji, szczerze mówiąc, nieco się wyłączyłem (krótki sen robi swoje), więc niewiele jestem w stanie napisać...&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Charles Nutter :: JRuby: Pushing the Boundaries&lt;/strong&gt; — świetne wprowadzenie w świat JRuby i jego coraz większych możliwości. Myślę, że JRuby staje się dużym graczem w świecie Ruby i naprawdę warto się z nim zapoznać i obserwować jego rozwój. Przykłady, prezentowane podczas prezentacji, można pobrać z &lt;a href="http://github.com/headius/jruby-demos"&gt;Githuba&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Na Google Wave Hackathon nie zostałem. Przede wszystkim dlatego, że nie mam jeszcze konta na Google Wave. Mam tylko nadzieję, że hackathon w ogóle się odbył ;-)&lt;/p&gt;
&lt;p&gt;Na koniec parę słów o organizacji samej konferencji. Znów nie było t-shirtów dla wszystkich — i drugi rok z rzędu wróciłem bez koszulki (t-shirt można było złapać, wygrać w konkursach lub dostać za zadawane pytania). Lokalizacja konferencji jest świetna — kampus UAM spisał się idealnie. WiFi momentami nie wyrabiało, ale drugiego dnia było zdecydowanie lepiej.&lt;/p&gt;
&lt;p&gt;Catering również wywiązał się całkiem nieźle — można było liczyć na śniadanie, obiad i przekąski (ciastka i słodkie bułki). Kawa, herbata i soki bez limitów.&lt;/p&gt;
&lt;p&gt;Pewne zastrzeżenia można mieć jednak do Organizatorów, nie mówię tutaj o samym fakcie odwołanych prezentacji, bo tego rzeczywiście nie zawsze da się przewidzieć, ale o sposobie informowania o tym. Przecież jest strona konferencji, jest twitter, dlatego dziwi mnie, że informacja o odwołanych prezentacjach była przekazywana w zasadzie jedynie na miejscu, chwilę przed planowanym wykładem. Najbardziej szkoda niedzielnej prezentacji o MongoDB — tak, jak pisałem poprzednio czuje się nieprzekonany do tej technologii i odwołany speech nie pomógł mi wcale :) Jednak, mimo wszystko, warto było jechać do Poznania na te dwa dni. Myślę, że i w przyszłym roku pojawie się w Poznaniu.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/UAkQ32vc2wA" height="1" width="1"/&gt;</description><pubDate>Mon, 09 Nov 2009 12:22:28 +0100</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2009/11/09/rupy-dzien-2-i-podsumowanie/</guid><category>Komputerowo-internetowo</category><category>Python</category><category>Ruby</category><category>Techblog</category><category>rupy</category><category>ruby</category><category>python</category><feedburner:origLink>http://blog.ghandal.net/2009/11/09/rupy-dzien-2-i-podsumowanie/</feedburner:origLink></item><item><title>RuPy - krótko o dniu pierwszym</title><link>http://feed.ghandal.net/~r/ghandal/~3/rK5iJspNwPg/</link><description>&lt;p&gt;Szybkie, krótkie podsumowanie dzisiejszego dnia konferencji &lt;a href="http://rupy.eu"&gt;RuPy&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Michael Dirolf :: An Introduction to MongoDB&lt;/strong&gt; — ciekawy wstęp w świat Document-Oriented Databases. Pomimo wielu niewątpliwych zalet, ja cały czas mam więcej wiele wątpliwości, czy warto tego używać. Wiem na pewno, że Mongo nie jest lekarstwem na wszystko — razi przede wszystkim brak obiektów (!) - zamiast tego jakiś tam wymyślony BSON, brak transakcji, brak obsługi relacji wiele-wiele (jak dla mnie denormalizacja nie jest tutaj rozwiązaniem, a dokładniej jest rozwiązaniem tylko dla niewielu przypadków, o manual join nie wspomne). Jak dla mnie całe MongoDB to troche bardziej rozbudowany key/value store. Może jutro zobaczę coś więcej, co mnie przekona. Zobaczymy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;David Goodger :: Community Oriented-Talk keynote&lt;/strong&gt; — po trochę przydługim wstępie parę słów o oczywistych (dla mnie przynajmniej) rzeczach dotyczących społeczności (bądź aktywny, nie bądź palantem, itp.). Troche smutno, że David czytał z kartek, bo sama prezentacja bardzo dobrze przygotowana.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lightning Talks Session&lt;/strong&gt; — szczerze mówiąc, nie wiele pamiętam z tej sesji... Może później sobie coś przypomnę.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Michał Łomnicki/Nick Sutterer :: Cells and Apotomo – Rails Plugins for Creating Reusable Web Components&lt;/strong&gt; — dwa rozwiązania dla wprowadzenia komponentów do aplikacji &lt;a href="http://www.rubyonrails.com"&gt;Rails&lt;/a&gt;, rozwiązania ciekawe, ale — znów — nie wszędzie stosowalne. Przykładowe zastosowanie zaproponowane przez Nicka, to wszelkiego rodzaju dashboardy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Julian Fischer :: Enterprise Ruby and Python Hosting&lt;/strong&gt;— momentami bardzo ogólna, ale też bardzo przekrojowa prezentacja o hostowaniu dużych aplikacji i o tym, na co powinno się zwrócić uwagę (backupy, monitoring, architektura, itp.).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Tyle na dzisiaj, przede mną Geek Party i dzień 2.&lt;/p&gt;
&lt;p&gt;Stay tuned!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/rK5iJspNwPg" height="1" width="1"/&gt;</description><pubDate>Sat, 07 Nov 2009 20:01:38 +0100</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2009/11/07/rupy-krotko-o-dniu-pierwszym/</guid><category>Komputerowo-internetowo</category><category>Python</category><category>Ruby</category><category>Techblog</category><category>rupy</category><category>ruby</category><category>python</category><feedburner:origLink>http://blog.ghandal.net/2009/11/07/rupy-krotko-o-dniu-pierwszym/</feedburner:origLink></item><item><title>Instalacja Ruby on Rails, Apache, mySQL i Passengera pod kontrolą REE na serwerze pod kontrolą Debiana 4.0 etch</title><link>http://feed.ghandal.net/~r/ghandal/~3/2GzYu9VEH5I/</link><description>&lt;p&gt;Ostatnio dane mi było instalować pełen stos programów potrzebnych do uruchomienia aplikacji w &lt;a href="http://www.rubyonrails.com"&gt;Railsach&lt;/a&gt; na serwerze pod kontrolą systemu &lt;a href="http://www.debian.org"&gt;Debian 4.0 etch minimal&lt;/a&gt; w wersji 64-bitowej. Ponieważ proces ten jest dosyć długi, opisuję go poniżej.&lt;/p&gt;
&lt;h3&gt;Wymagania wstępne i założenia&lt;/h3&gt;
&lt;p&gt;Opis nie będzie zawierał prawie żadnych instrukcji jak zabezpieczyć serwer, przy okazji zakładam kilka rzeczy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;jesteśmy zalogowani (np. przez &lt;abbr title="Secure SHell"&gt;SSH&lt;/abbr&gt;) na koncie &lt;tt&gt;root&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;mamy do dyspozycji "gołą" instalację Debiana&lt;/li&gt;
&lt;li&gt;pracujemy na domyślnych źródłach &lt;tt&gt;apt&lt;/tt&gt; wersji minimal&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Czynności wstępne&lt;/h3&gt;
&lt;p&gt;Na początku warto upewnić się, że mamy aktualną listę pakietów. W tym celu warto wykonać następujące czynności:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ apt-get update
$ apt-get upgrade
&lt;/pre&gt;
&lt;p&gt;Instalujemy też potrzebne pakiety:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ apt-get install apt-utils build-essential ruby1.8-dev imagemagick libmysqlclient15off \
 libmysqlclient15-dev mysql-server mysql-common mysql-client memcached libxslt1-dev libpcre3-dev \
 zlib1g-dev unzip gzip libssl-dev libreadline5-dev libmysql-ruby sqlite3 imagemagick man-db cron wget lynx curl bind9 -y
&lt;/pre&gt;
&lt;p&gt;Od razu możemy zmienić hasło roota MySQL:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ mysqladmin -u root password NEWPASSWORD
&lt;/pre&gt;
&lt;h3&gt;Instalacja Ruby&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Żeby korzystać z jedynie Ruby Enterprise Edition można opuścić ten punkt.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ruby instalujemy ze źrodeł. W momencie tworzenia opisu w oficjalnych repozytoriach Debiana znalazłem wersję 1.8.5, a najnowszą stabilną wersją Ruby na stronie &lt;a href="http://www.ruby-lang.org"&gt;Ruby-Lang&lt;/a&gt; była wersja 1.8.6-p286. Pobieramy i rozpakowujemy źródła Ruby:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ cd /usr/src
$ wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p286.tar.gz
$ tar zxf ruby-1.8.6-p286.tar.gz
$ cd ruby-1.8.6-p286
&lt;/pre&gt;
&lt;p&gt;Przed konfiguracją edytujemy plik &lt;tt&gt;ext/Setup&lt;/tt&gt; w celu dołączenia potrzebnych rozszerzeń. Powyższy plik może wyglądać tak:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
 #Win32API
 #bigdecimal
 curses
 #dbm
 digest
 digest/md5
 #digest/rmd160
 digest/sha1
 digest/sha2
 dl
 enumerator
 #etc
 #fcntl
 #gdbm
 iconv
 #io/wait
 #nkf
 #pty
 openssl
 #racc/cparse
 readline
 #sdbm
 socket
 stringio
 strscan
 syck
 syslog
 #tcltklib
 thread
 #tk
 #win32ole
 zlib
&lt;/pre&gt;
&lt;p&gt;Zawartość pliku oznacza, że chcemy skompilować Ruby wraz z rozszerzeniami &lt;tt&gt;curses, digest (md5, sha1/2), dl, enumerator, iconv, openssl, readline, socket, stringio, strscan, syck, syslog, thread, zlib&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Przyszła kolej na konfigurację, kompilację i instalację:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ ./configure --prefix=/usr/local --with-openssl-dir=/usr \
 --with-readline-dir=/usr --with-zlib-dir=/usr
$ make
$ make install
$ make install-doc
&lt;/pre&gt;
&lt;p&gt;Ostatnia linijka jest opcjonalna i odpowiedzialna za wygenerowanie dokumentacji.&lt;/p&gt;
&lt;p&gt;Po zainstalowaniu interpretera języka Ruby przychodzi kolej na Rubygems:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ cd /usr/src
$ wget http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz
$ tar zxf rubygems-1.3.1.tgz
$ cd rubygems-1.3.1/
$ ruby setup.rb
$ gem isntall rubygems-update
$ update_rubygems
$ gem source --add http://gems.github.com
&lt;/pre&gt;
&lt;p&gt;Ostatnia linijka dodaje do źródeł gemów repozytorium &lt;a href="http://github.com"&gt;Github&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pora na instalacje potrzebnych gemów, moja proponowana lista:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
gem install actionmailer actionpack actionwebservice active_graph \
 activerecord activeresource activesupport addressable archive-tar-minitar \ 
 bcrypt-ruby BlueCloth builder capistrano capistrano-ext cgi_multipart_eof_fix \
 chronic color columnize daemons eventmachine extlib fastercsv fastthread \ 
 feedtools gem_plugin haml highline hoe hpricot image_science \
 InlineAttachment json libxml-ruby linecache memcache-client metaid \
 mime-types mini_magick mislav-will_paginate mongrel mongrel_cluster \
 mongrel_proctitle needle net-scp net-sftp net-ssh net-ssh-gateway nokogiri \
 oauth passenger progressbar rack rails rake rcov RedCloth rmagick rspec \
 rspec-rails rspec_hpricot_matchers ruby-debug ruby-debug-base rubyforge \
 RubyInline rubyzip thin thor tlsmail transaction-simple uuid uuidtools \
 webrat wirble xml-simple youtube ZenTest
&lt;/pre&gt;
&lt;h3&gt;Instalacja &lt;a href="http://www.rubyenterpriseedition.com"&gt;Ruby Enterprise Edition&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ponieważ będziemy uruchamiać aplikację Rails z użyciem &lt;a href="http://www.modrails.com"&gt;Passengera&lt;/a&gt;, możemy zredukować zużycie pamięci korzystając ze specjalnie przygotowanej wersji interpretera. Instalujemy go ze źródeł:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ cd /usr/src
$ wget http://rubyforge.org/frs/download.php/50087/ruby-enterprise-1.8.6-20090113.tar.gz
$ tar zxf ruby-enterprise-1.8.6-20090113.tar.gz
$ cd ruby-enterprise-1.8.6-20090113/
$ ./installer
&lt;/pre&gt;
&lt;p&gt;Wraz z &lt;abbr title="Ruby Enterprise Edition"&gt;REE&lt;/abbr&gt; zostaną zainstalowane &lt;tt&gt;rubygems&lt;/tt&gt; oraz pewne &lt;tt&gt;gemy&lt;/tt&gt;. Dodatkowe gemy instalujemy jak wyżej, z tym, że korzystamy ze zmodyfikowanej wersji Ruby:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
/opt/ruby-enterprise-1.8.6-20090113/bin/ruby -S /opt/ruby-enterprise-1.8.6-20090113/bin/gem install capistrano  # ... i inne gemy
&lt;/pre&gt;
&lt;h3&gt;Instalacja Apache&lt;/h3&gt;
&lt;p&gt;Zainstalowałem &lt;tt&gt;apache2&lt;/tt&gt; wraz z pakietami &lt;tt&gt;apache2-mpm-worker, apache2-utils&lt;/tt&gt; oraz &lt;tt&gt;apache2-prefork-dev&lt;/tt&gt;&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
 apt-get install apache2 apache2-mpm-worker apache2-utils apache2-prefork-dev
&lt;/pre&gt;
&lt;h3&gt;Instalacja i konfiguracja Passengera obsługiwanego przez REE&lt;/h3&gt;
&lt;p&gt;Ponieważ &lt;tt&gt;gem&lt;/tt&gt; Passengera został zainstalowany wcześniej pozostaje wygenerować odpowiedni moduł dla &lt;tt&gt;apache2&lt;/tt&gt;:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
 /opt/ruby-enterprise-1.8.6-20090113/bin/passenger-install-apache2-module
&lt;/pre&gt;
&lt;p&gt;Pora na konfigurację. Wykorzystamy modułowość &lt;tt&gt;apache2&lt;/tt&gt; oraz program &lt;tt&gt;a2enmod&lt;/tt&gt; do załadowania modułu. Tworzymy pliki &lt;tt&gt;/etc/apache2/mods-available/passenger.load&lt;/tt&gt; z zawartością taką, jak wygenerowała nam instalacja modułu Passengera, np.:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
  LoadModule passenger_module /opt/ruby-enterprise-1.8.6-20090113/lib/ruby/gems/1.8/gems/passenger-2.0.6/ext/apache2/mod_passenger.so
&lt;/pre&gt;
&lt;p&gt;i &lt;tt&gt;/etc/apache2/mods-available/passenger.conf&lt;/tt&gt;:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
 PassengerRoot /opt/ruby-enterprise-1.8.6-20090113/lib/ruby/gems/1.8/gems/passenger-2.0.6
 PassengerRuby /opt/ruby-enterprise-1.8.6-20090113/bin/ruby
&lt;/pre&gt;
&lt;p&gt;Po zapisaniu tych plików ładujemy moduł:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ a2enmod passenger
&lt;/pre&gt;
&lt;p&gt;I to już praktycznie wszystko, jedyne, co zostaje do zrobienia, to utworzenie konfiguracji &lt;tt&gt;vhosta&lt;/tt&gt; dla konkretnej aplikacji, np. w &lt;tt&gt;/etc/apache2/sites-available&lt;/tt&gt;, uaktywnienie jej programem &lt;tt&gt;a2ensite&lt;/tt&gt; i przeładowanie &lt;tt&gt;apache&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Mam nadzieję, że powyższy opis pozwoli komuś zaoszczędzić trochę czasu.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/2GzYu9VEH5I" height="1" width="1"/&gt;</description><pubDate>Fri, 30 Jan 2009 18:07:59 +0100</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2009/01/30/instalacja-ruby-on-rails-apache-mysql-i-passengera-pod-kontr/</guid><category>Komputerowo-internetowo</category><category>Ruby</category><category>Ruby on Rails</category><category>Techblog</category><category>ruby</category><category>rails</category><category>ree</category><category>apache</category><category>debian</category><category>etch</category><feedburner:origLink>http://blog.ghandal.net/2009/01/30/instalacja-ruby-on-rails-apache-mysql-i-passengera-pod-kontr/</feedburner:origLink></item><item><title>Dziwne zachowanie config.gem w połączeniu z rspec-rails w Rails 2.2</title><link>http://feed.ghandal.net/~r/ghandal/~3/ylJ3UnP73oo/</link><description>&lt;p&gt;Pracując ostatnio nad projektem zauważyłem dziwne rzeczy dziejące się w aplikacji. Po pierwsze w trybie deweloperskim po jakimkolwiek błędzie nie wyświetlał się &lt;em&gt;stack trace&lt;/em&gt;. Jedyne, co się wyświetlało, to error code 500.&lt;/p&gt;
&lt;p&gt;Już sam ten fakt był dla mnie dość uciążliwy, chociaż w tym przypadku zawsze można było odwołać się do logów aplikacji. Dużo gorszą rzeczą było nadpisanie przez coś wartości stałej RAILS_ENV i ustawienie jej na 'test'. Co ciekawe, wartość ENV['RAILS_ENV'] nadal wskazywała na poprawne środowisko.&lt;/p&gt;
&lt;p&gt;Kiedy po zainstalowaniu &lt;a href="http://rpm.newrelic.com"&gt;RPM&lt;/a&gt; aplikacja nie chciała z nim współpracować (oczywiście działo się tak przez źle wskazujące RAILS_ENV) trzeba było znaleźć winnego. Po nitce do kłębka znalazłem wpis dotyczący &lt;a href="http://railsforum.com/viewtopic.php?pid=82597"&gt;znikających stack trace'ów&lt;/a&gt;. Okazało się, że wycięcie z pliku &lt;strong&gt;config/environment.rb&lt;/strong&gt; linijki:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
config.gem 'rspec-rails', :lib =&amp;gt; 'spec/rails'
&lt;/pre&gt;
&lt;p&gt;rozwiązało oba problemy!&lt;/p&gt;
&lt;p&gt;Winowajcą jest linijka&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
silence_warnings { RAILS_ENV = "test" }
&lt;/pre&gt;
&lt;p&gt;w pliku &lt;em&gt;lib/spec/rails.rb&lt;/em&gt; gema rspec-rails.&lt;/p&gt;
&lt;p&gt;Powyższy problem zdaje się nie występować w przypadku korzystania z rspeca w formie pluginu.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/ylJ3UnP73oo" height="1" width="1"/&gt;</description><pubDate>Fri, 16 Jan 2009 17:23:13 +0100</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2009/01/16/dziwne-zachowanie-config-gem-w-polaczeniu-z-rspec-rails-w-ra/</guid><category>Komputerowo-internetowo</category><category>Ruby</category><category>Ruby on Rails</category><category>Techblog</category><category>rspec</category><category>rails</category><category>stack trace</category><feedburner:origLink>http://blog.ghandal.net/2009/01/16/dziwne-zachowanie-config-gem-w-polaczeniu-z-rspec-rails-w-ra/</feedburner:origLink></item><item><title>Małe podsumowanie Off Festival 2008</title><link>http://feed.ghandal.net/~r/ghandal/~3/9zde65bvuFk/</link><description>&lt;p&gt;W tym roku po raz pierwszy miałem przyjemność uczestniczyć w &lt;a href="http://www.2008.off-festival.pl/"&gt;Off Festivalu&lt;/a&gt; w Mysłowicach.&lt;/p&gt;
&lt;h2&gt;Mysłowice i organizacja festiwalu&lt;/h2&gt;
&lt;p style="clear: both"&gt;Jeżeli chodzi o miasto oraz organizację festiwalu, to mam kilka rzeczy, które mi się całkowicie nie podobały. Po pierwsze i chyba najważniejsze, pole namiotowe jest za daleko od scen i całego Słupna Park. Owszem, można skrócić sobie drogę przechodząc nieoficjalną drogą przez tory i nasyp kolejowy, ale nie jest to rozwiązanie dobre. A 20-minutowa podróż z biwaku na teren festiwalu to jednak dla mnie trochę za dużo. Przydałoby się poszukać innego miejsca, może na terenie samego parku?&lt;/p&gt;
&lt;p&gt;Kolejną sprawą było oznakowanie. Chociaż słyszałem, że ok. 15 w piątek było oznakowanie na dworcu PKP, o tyle ja, przyjeżdzając do Mysłowic dopiero w okolicach godziny 21 nie zauważyłem żadnych znaków kierujących mnie krótszą drogą na pole namiotowe, w związku z czym nadkładałem drogi i zamiast 10 minut spędziłem ok. 30 na maszerowaniu z osprzętem turystycznym.&lt;/p&gt;
&lt;p&gt;Co do gastronomii, nie była ona rewelacyjna. Po pierwsze kolejki, kolejki, kolejki. Poza tym sam asortyment też nie był zbyt szeroki.&lt;/p&gt;
&lt;p&gt;Pochwalić mogę namioty z płytami — całkiem niezłe zaopatrzenie i duży plus za płytę &lt;a htef="http://myspace.com/polpomotel"&gt;Polpo Motel&lt;/a&gt;. Namiot &lt;a href="http://polskieradio.pl/trojka"&gt;trójkowy&lt;/a&gt; zrobił promocję na Offensywę vol. 3, co też się chwali.&lt;/p&gt;
&lt;p&gt;Jeżeli chodzi o sceny i sam festiwal, to uważam, że scen było w sam raz, nie były też zbytnio od siebie oddalone, chociaż myślę, że scena leśna była nieco za blisko sceny głównej, co powodowało niekiedy nakładanie się dźwięków koncertu na scenie leśnej z dźwiękami z prób na scenie głównej.&lt;/p&gt;
&lt;h2&gt;Muzyka na Off-ie&lt;/h2&gt;
&lt;p style="clear:both"&gt;Przejdźmy do najważniejszej części — muzyki, której było dużo i dobrze. Bardzo żałuję, że nie mogłem przyjechać w piątek rano, bo ominęły mnie koncerty zespołów, na których zobaczeniu i usłyszeniu mi zależało — New York Crasnals, Kawałek Kulki, Lutosphere, Dick4Dick, Budyn i Sprawcy Rzepaku czy of Montreal. W zasadzie pierwszy dzień festiwalu rozpocząłem od koncertu zespołu Hey. Poniżej kilka słów o najciekawszych moim zdaniem zespołach.&lt;/p&gt;
&lt;h3&gt;Hey&lt;/h3&gt;
&lt;p&gt;Myślę, że nie ma co dużo mówić o Kasi i zespole. Jedyne, co mogę podkreślić i co mi się bardzo podobało, to fakt, że mogłem usłyszeć piosenki, których nie słychać na koncertach (np. Morf&amp;amp;Na). Bardzo ciekawe aranżacje (chociaż po koncercie unplugged w Romie nic już nie będzie takie samo). Jedyny mankament dla mnie, to zbyt duża ilość piosenek z mojego nie najbardziej ulubionego albumu "Music, Music", chociaż, jak to Kasia określiła, grali to, co lubią i chwała im za to!&lt;/p&gt;
&lt;h3&gt;Caribou&lt;/h3&gt;
&lt;p&gt;Jestem niezmiernie zaniepokojony faktem, że praktycznie nie pamiętam tego koncertu, a jak to wiele ludzi podkreśla był to jeden z najlepszych koncertów tegorocznego Off-u. Pamiętam tylko tyle, że muzyka zespołu Caribou podobała mi się...&lt;/p&gt;
&lt;h3&gt;Mogwai&lt;/h3&gt;
&lt;p&gt;Tutaj się bardzo zawiodłem. Nie znałem niczego z repertuaru tego zespołu. Spodziewałem się cudownej dla uszu muzyki, a dostałem smętne kawałki, do tego wszystkie podobne do siebie i grane praktycznie w jednym tempie. Nie wysiedziałem do końca, bo nie mogłem więcej słuchać. Technicznie ok, ale jakoś mnie nie porwali.&lt;/p&gt;
&lt;h3&gt;The Poise Rite&lt;/h3&gt;
&lt;p&gt;Świetni! Zlokalizowałem już ich płytę i będzie moja! (dla chętnych jest dostępna w &lt;a href="http://merlin.pl/Passagalia_The-Poise-Rite/browse/product/4,517811.html"&gt;Merlinie&lt;/a&gt;). Granie the Poise Rite przypomina mi nieco Interpol. Dużo gitary, przyjemny wokal.&lt;/p&gt;
&lt;h3&gt;Baaba&lt;/h3&gt;
&lt;p&gt;Duże zaskoczenie. Słyszałem wcześniej ich jeden kawałek na Offsesjach. Dużo improwizacji na scenie, ciekawy zestaw instrumentów i zabawa dźwiękiem. Trzeba będzie zaopatrzyć się w płytę i wyszukiwać ich koncertów w okolicy.&lt;/p&gt;
&lt;h3&gt;Czesław Śpiewa&lt;/h3&gt;
&lt;p&gt;Miałem nie pisać o Czesławie, jednak napiszę. Grał tym razem bez akordeonu, który nie dojechał z powodu opóźnienia pociągu. Bardzo zaskoczyły mnie tłumy i reakcje publiczności na Czesława. Szczerze mówiąc jest to dla mnie fenomen. Sam koncert ciekawy, też ze względu na brak akordeonu i więcej aranżacji na klawisze. Czesław przez cały czas popijał piwko, co odbiło się, niestety na jego grze, szczególnie na scenie MySpace.&lt;/p&gt;
&lt;h3&gt;James Chance and Les Contortions&lt;/h3&gt;
&lt;p&gt;Kolejny świetny show. Pan James nie jest już nastolatkiem, ale to nie przeszkadzało mu w szaleństwach na scenie. Bardzo ciekawe podejście do jazzu. Troche funk, trochę punk. Kto nie słuchał, niech żałuje!&lt;/p&gt;
&lt;h3&gt;Lao Che&lt;/h3&gt;
&lt;p&gt;Do tego koncertu byłem trochę sceptycznie nastawiony. Jednak już po pierwszych taktach Lao Che porwało mnie i do samego końca koncertu (który nieco się przedłużył) bawiłem się wybornie. Myślę, że muzyki Lao Che nie trzeba przedstawiać (w sumie było to jeden z mniej offowych zespołów na off-ie).&lt;/p&gt;
&lt;h3&gt;L.U.C &amp;amp; Rahim&lt;/h3&gt;
&lt;p&gt;Na koniec nieco psychodeliczny kąsek w wydaniu dwóch genialnych raperów. Świetne połączenie elektroniki ze smyczkami, dobrze zarapowane, a do tego ciekawa, momentami mocna oprawa wizualna sprawiły, że pomimo późnej godziny czułem się świetnie, nawet pomimo faktu, że powstałem dzięki ekskramentom sinic.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Podsumowując, bardzo się cieszę, że pojechałem na Off. Poznałem dużo świetnych zespołów i posłuchałem dobrej muzyki. W przyszłym roku też raczej się pojawię. Miejmy tylko nadzieję, że następny Off będzie przebiegał w nieco bardziej przyjemnych warunkach pogodowych.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/9zde65bvuFk" height="1" width="1"/&gt;</description><pubDate>Wed, 13 Aug 2008 00:20:51 +0200</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/08/13/male-podsumowanie-off-festival-2008/</guid><category>Muzyka</category><category>Przemyślenia</category><category>Z życia</category><category>off</category><category>festiwal</category><category>festival</category><category>mysłowice</category><feedburner:origLink>http://blog.ghandal.net/2008/08/13/male-podsumowanie-off-festival-2008/</feedburner:origLink></item><item><title>Blokowe helpery w Ruby on Rails</title><link>http://feed.ghandal.net/~r/ghandal/~3/GoVdmxtZC0A/</link><description>&lt;p&gt;Bardzo przydatną rzeczą, którą możemy zrobić podczas refaktoryzacji kodu widoków jest dopisanie helperów akceptujących bloki kodu.&lt;/p&gt;
&lt;p&gt;W &lt;a href="http://rubyonrails.com"&gt;Railsach&lt;/a&gt; standardowo mamy co najmniej kilka takich helperów, m.in. &lt;em&gt;form_for&lt;/em&gt;, czy &lt;em&gt;link_to&lt;/em&gt; (póki co w wersji EDGE). Nic nie stoi na przeszkodzie, aby dopisać swoje własne metody akceptujące bloki, które w widoczny sposób poprawiają czytelność kodu.&lt;/p&gt;
&lt;p&gt;Jako przykład podam implementację &lt;em&gt;rounded-box&lt;/em&gt; jako helper akceptujący blok. Końcowy efekt wyglądał będzie następująco:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
&lt;%- rounded_box do %&gt;
Treść, która powinna znaleźć się w boksie
&lt;% end -%&gt;
&lt;/pre&gt;
&lt;p&gt;Napisanie tego typu metod jest bardzo proste i opiera się na wykorzystaniu metody &lt;a href="http://rails-doc.org/docs/ActionView/Helpers/TextHelper/concat"&gt;&lt;em&gt;concat&lt;/em&gt;&lt;/a&gt;. Zakładam, że cały kod naszego boksu na postać (styl &lt;abbr title="Cascading Style Sheets"&gt;CSS&lt;/abbr&gt; zostawmy w spokoju):&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
&amp;lt;div class="rnd_container"&amp;gt;
  &amp;lt;b class="rnd_top"&amp;gt;
    &amp;lt;b class="rnd_b1"&amp;gt;&amp;lt;/b&amp;gt;
    &amp;lt;b class="rnd_b2"&amp;gt;&amp;lt;/b&amp;gt;
    &amp;lt;b class="rnd_b3"&amp;gt;&amp;lt;/b&amp;gt;
    &amp;lt;b class="rnd_b4"&amp;gt;&amp;lt;/b&amp;gt;
  &amp;lt;/b&amp;gt;
&amp;lt;div class="rnd_content"&amp;gt; 
  &amp;lt;h2&amp;gt;&amp;lt;p&amp;gt;Treść w boksie&amp;lt;/p&amp;gt;&amp;lt;/h2&amp;gt;
&amp;lt;/div&amp;gt;
  &amp;lt;b class="rnd_bottom"&amp;gt;
    &amp;lt;b class="rnd_b4"&amp;gt;&amp;lt;/b&amp;gt;
    &amp;lt;b class="rnd_b3"&amp;gt;&amp;lt;/b&amp;gt;
    &amp;lt;b class="rnd_b2"&amp;gt;&amp;lt;/b&amp;gt;
    &amp;lt;b class="rnd_b1"&amp;gt;&amp;lt;/b&amp;gt;
  &amp;lt;/b&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Powyższy kod można najprościej przetransportować do osobnego partiala i zapisac, np. w katalogu &lt;em&gt;app/views/shared/_rounded_box.html.erb&lt;/em&gt; w lekko zmodyfikowanej wersji:&lt;/p&gt;
&lt;pre name="code" class="html"&gt;
  &amp;lt;div class="rnd_container"&amp;gt;
    &amp;lt;b class="rnd_top"&amp;gt;
      &amp;lt;b class="rnd_b1"&amp;gt;&amp;lt;/b&amp;gt;
      &amp;lt;b class="rnd_b2"&amp;gt;&amp;lt;/b&amp;gt;
      &amp;lt;b class="rnd_b3"&amp;gt;&amp;lt;/b&amp;gt;
      &amp;lt;b class="rnd_b4"&amp;gt;&amp;lt;/b&amp;gt;
    &amp;lt;/b&amp;gt;
  &amp;lt;div class="rnd_content"&amp;gt; 
    &amp;lt;h2&amp;gt;&amp;lt;%= body %&amp;gt;&amp;lt;/h2&amp;gt;
  &amp;lt;/div&amp;gt;
    &amp;lt;b class="rnd_bottom"&amp;gt;
      &amp;lt;b class="rnd_b4"&amp;gt;&amp;lt;/b&amp;gt;
      &amp;lt;b class="rnd_b3"&amp;gt;&amp;lt;/b&amp;gt;
      &amp;lt;b class="rnd_b2"&amp;gt;&amp;lt;/b&amp;gt;
      &amp;lt;b class="rnd_b1"&amp;gt;&amp;lt;/b&amp;gt;
    &amp;lt;/b&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Sama metoda helpera wygląda następująco:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
  def rounded_box(options={}, &amp;amp;block)
    options.merge!(:body =&amp;gt; capture(&amp;amp;block))
    concat(render(:partial =&amp;gt; "shared/rounded_box", :locals =&amp;gt; options), block.binding)
  end
&lt;/pre&gt;
&lt;p&gt;Jak widać oprócz bloku możemy przekazać do partiala swoje zmienne podając je jako parametry wywołania helpera. Cała magia polega na przechwyceniu zawartości bloku wywołaniem &lt;em&gt;capture(&amp;amp;block)&lt;/em&gt; i skonkatenowaniem jej z naszym boksem.&lt;/p&gt;
&lt;p&gt;Rozwiązanie szybkie, proste i jakże czytelne!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/GoVdmxtZC0A" height="1" width="1"/&gt;</description><pubDate>Tue, 08 Jul 2008 10:28:11 +0200</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/07/08/blokowe-helpery-w-ruby-on-rails/</guid><category>Komputerowo-internetowo</category><category>Ruby</category><category>Ruby on Rails</category><category>Techblog</category><category>helper</category><category>rails</category><category>blok</category><feedburner:origLink>http://blog.ghandal.net/2008/07/08/blokowe-helpery-w-ruby-on-rails/</feedburner:origLink></item><item><title>Manipulowanie czasem w testach w aplikacjach Ruby</title><link>http://feed.ghandal.net/~r/ghandal/~3/8lFvYVZ8oCg/</link><description>&lt;p&gt;Czasem, szczególnie podczas pisania testów, zachodzi potrzeba manipulowania czasem, np. podczas testowania metod, które mają zwracać posortowane względem czasu utworzenia obiekty ActiveRecord.&lt;/p&gt;
&lt;p&gt;W ostatnim projekcie podczas pisania &lt;a href="http://rspec.info"&gt;spec-ów&lt;/a&gt; modeli korzystałem z takiego tworu:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
describe SomeClass do
  describe "by_date" do
    it "should return objects sorted by creation date" do
      Time.advancing_by_days(-1) do
        @earlier = SomeClass.new(:some =&amp;gt; "values")
      end
      @normal = SomeClass.new
      Time.advancing_by_days(1) do
        @later = SomeClass.new(:some =&amp;gt; "other value")
      end
      SomeClass.by_date.should == [@later, @normal, @earlier]
    end
  end
end
&lt;/pre&gt;
&lt;p&gt;O ile mi wiadomo, &lt;a href="http://www.rubyonrails.com"&gt;Railsy&lt;/a&gt; nie mają standardowo wbudowanych klas do manipulowania czasem, czy metodą &lt;strong&gt;Time.now&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Stworzenie metod realizujących powyższą funkcjonalność jest bardzo proste. Wystarczy stworzyć plik, np. &lt;em&gt;time_extensions.rb&lt;/em&gt; z poniższą zawartością:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
require 'time'

if !Time.respond_to?('old_now')
  Time.class_eval {
    @@advance_by_days = 0
    @@advance_by_minutes = 0
    cattr_accessor :advance_by_days, :advance_by_minutes

    class &amp;lt;&amp;lt; Time
      alias old_now now
      def now
        if Time.advance_by_days != 0
          return Time.at(old_now.to_i + Time.advance_by_days * 60 * 60 * 24 + 1)
        elsif Time.advance_by_minutes != 0
          return Time.at(old_now.to_i + Time.advance_by_minutes * 60)
        else
          old_now
        end
      end
      def advancing_by_days(days=0)
        Time.advance_by_days = days
        yield
        Time.advance_by_days = 0
      end
      def advancing_by_minutes(minutes=0)
        Time.advance_by_minutes = minutes
        yield
        Time.advance_by_minutes = 0
      end
    end
  }
end
&lt;/pre&gt;
&lt;p&gt;Jak widać korzystamy z możliwości, jakie daje &lt;a href="http://en.wikipedia.org/wiki/Monkey_patch"&gt;&lt;em&gt;monkey pathing&lt;/em&gt;&lt;/a&gt; i modyfikujemy standardową metodę &lt;strong&gt;Time.now&lt;/strong&gt;, uprzednio tworząc do niej alias. W powyższym kodzie oprócz zmiany czasu o zadaną liczbę dni, możemy zmieniać go o zadaną liczbę minut.&lt;/p&gt;
&lt;p&gt;Aby korzystać z powyższych metod w spec-ach aplikacji railsowej, wystarczy wrzucić plik z powyższym kodem do katalogu &lt;em&gt;spec&lt;/em&gt; aplikacji i dopisać do pliku &lt;em&gt;spec_helper.rb&lt;/em&gt; linijkę:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
require 'time_extensions'
&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/8lFvYVZ8oCg" height="1" width="1"/&gt;</description><pubDate>Sat, 05 Jul 2008 00:43:31 +0200</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/07/05/manipulowanie-czasem-w-testach-w-aplikacjach-ruby/</guid><category>Komputerowo-internetowo</category><category>Ruby</category><category>Ruby on Rails</category><category>Techblog</category><category>rails</category><category>tips</category><category>time</category><feedburner:origLink>http://blog.ghandal.net/2008/07/05/manipulowanie-czasem-w-testach-w-aplikacjach-ruby/</feedburner:origLink></item><item><title>Dwie przydatne rzeczy przy pracy z RubyGems i Rake</title><link>http://feed.ghandal.net/~r/ghandal/~3/TRHiFC1O0u4/</link><description>&lt;p&gt;Ostatnio cierpię na brak czasu, stąd trochę zaniedbałem bloga. Dzisiaj krótka rzecz, która ułatwi pracę z gemami oraz z Rake. Przyznam się, że poniższe skrypty nie są mojego autorstwa, ja jedynie popularyzuję je.&lt;/p&gt;
&lt;h5&gt;Automatyczne otwieranie dokumentacji gemów w przeglądarce&lt;/h5&gt;
&lt;p&gt;Tworzymy funkcję w bashu &lt;em&gt;gemdoc&lt;/em&gt;, która otworzy nam w przeglądarce dokumentacje RDoc podanego gema. Oczywiście obsłużymy autocomplete. Poniższy kod wklejamy do naszego pliku &lt;em&gt;.bash_profile&lt;/em&gt;, po czym przeładowujemy ów plik poleceniem&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ source ~/.bash_profile
&lt;/pre&gt;
&lt;pre name="code" class="bash"&gt;
export GEMDIR=`gem env gemdir`

gemdoc() {
  open $GEMDIR/doc/`$(which ls) $GEMDIR/doc | grep $1 | sort | tail -1`/rdoc/index.html
}
_gemdocomplete() {
  COMPREPLY=($(compgen -W '$(`which ls` $GEMDIR/doc)' -- ${COMP_WORDS[COMP_CWORD]}))
  return 0
}
complete -o default -o nospace -F _gemdocomplete gemdoc
&lt;/pre&gt;
&lt;p&gt;Funkcja pochodzi z bloga &lt;a href="http://stephencelis.com/archive/2008/6/bashfully-yours-gem-shortcuts"&gt;Stephena&lt;/a&gt;.&lt;/p&gt;
&lt;h5&gt;Automatyczne dopełnianie tasków Rake&lt;/h5&gt;
&lt;p&gt;Drugą przydatną funkcją jest automatyczne dopełnianie tasków Rake. Kod wklejamy jak powyżej do naszego &lt;em&gt;.bash_profile&lt;/em&gt;.&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
export COMP_WORDBREAKS=${COMP_WORDBREAKS/\:/}

_rakecomplete() {
  COMPREPLY=($(compgen -W "`rake -s -T | awk '{{print $2}}'`" -- ${COMP_WORDS[COMP_CWORD]}))
  return 0
}
complete -o default -o nospace -F _rakecomplete rake
&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/TRHiFC1O0u4" height="1" width="1"/&gt;</description><pubDate>Sat, 21 Jun 2008 11:48:59 +0200</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/06/21/dwie-przydatne-rzeczy-przy-pracy-z-rubygems-i-rake/</guid><category>Komputerowo-internetowo</category><category>Ruby</category><category>Techblog</category><category>bash</category><category>rake</category><category>gem</category><feedburner:origLink>http://blog.ghandal.net/2008/06/21/dwie-przydatne-rzeczy-przy-pracy-z-rubygems-i-rake/</feedburner:origLink></item><item><title>deferred?(env) w Thin, Ebb i Merb</title><link>http://feed.ghandal.net/~r/ghandal/~3/JNWMjXrmJKU/</link><description>&lt;p&gt;Jak donosi &lt;a href="http://brainspl.at/articles/2008/04/18/deferred-requests-with-merb-ebb-and-thin"&gt;Ezra Zygmuntowicz&lt;/a&gt; &lt;a href="http://merbivore.com"&gt;Merb&lt;/a&gt; doczekał się wsparcia dla &lt;em&gt;deferred requests&lt;/em&gt;. Ale o co chodzi?&lt;/p&gt;
&lt;p&gt;Porównując serwery operujące na zdarzeniach &lt;em&gt;(event driven)&lt;/em&gt; z operującymi na wątkach &lt;em&gt;(threaded)&lt;/em&gt; można dojść do wniosków, iż te operujące na zdarzeniach są o wiele szybsze przy serwowaniu krótkich żądań. Dzieje się tak, gdyż nie ma narzutu na odpalenie nowego wątku i przełączaniu kontekstu. &lt;a href="http://tinyclouds.org/"&gt;Ry Dahl&lt;/a&gt; wspominał o tym w swojej prezentacji na &lt;a href="http://rupy.eu"&gt;RuPy&lt;/a&gt;. Jednak co z długimi requestami, takimi jak upload pliku, czy skomplikowane wyliczenia? Jak się okazuje jedne z najpopularniejszych i najszybszych serwerów do frameworków w Rubym, które wspierają interfejs &lt;a href="http://rack.rubyforge.org/"&gt;Rack&lt;/a&gt; — &lt;a href="http://code.macournoyer.com/thin/"&gt;Thin&lt;/a&gt; oraz &lt;a href="http://ebb.rubyforge.org/"&gt;Ebb&lt;/a&gt; mają metodę &lt;em&gt;deferred?(env)&lt;/em&gt; w ich &lt;a href="http://rack.rubyforge.org"&gt;Rackowym&lt;/a&gt; interfejsie. Oba serwery będą odpalały tę metodę na objekcie aplikacji przed jego wywołaniem. Taki trik pozwala adapterowi &lt;a href="http://rack.rubyforge.org"&gt;Rack&lt;/a&gt; zadecydować czy request powinien być obsłużony przez osobny wątek, czy w pętli zdarzeń. Aby przyspieszyć działanie serwera, wszystkie wolne akcje powinny być oznaczone flagą &lt;em&gt;deferred&lt;/em&gt; i wywoływane w osobnych wątkach.&lt;/p&gt;
&lt;p&gt;O ile nic nie wiadomo mi o wsparciu &lt;em&gt;deferred?(env)&lt;/em&gt; w &lt;a href="http://www.rubyonrails.com"&gt;Railsach&lt;/a&gt;, o tyle, jak wspomniałem na początku, &lt;a href="http://merbivore.com"&gt;Merb&lt;/a&gt; doczekał się właśnie takiej funkcjonalności. Jeżeli chcemy poinformować nasz serwer, że jakieś akcje są wolne, do pliku &lt;em&gt;init.rb&lt;/em&gt; powinniśmy dodać następującą linijkę:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
Merb::Config[:deferred_actions] = ["/uploads/create", "reports/longaction"]
&lt;/pre&gt;
&lt;p&gt;Powyższa linijka sprawi, że wszystkie akcje oprócz wymienionych będą obsługiwane przez &lt;em&gt;event loop&lt;/em&gt;. Każdy request do jednej z powyższych akcji spowoduje odpalenie nowego wątku dla niej i nie będzie spowalniać naszej aplikacji.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Co ważne&lt;/strong&gt;, aby korzystać z dobrodziejstw &lt;em&gt;deferred?(env)&lt;/em&gt;, należy zaopatrzyć się w najnowsze developerskie wersje zarówno serwerów, jak i Merba. Wszystko można znaleźć oczywiście na &lt;a href="http://github.com"&gt;GitHubie&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/JNWMjXrmJKU" height="1" width="1"/&gt;</description><pubDate>Fri, 18 Apr 2008 11:04:35 +0200</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/04/18/deferred-env-w-thin-ebb-i-merb/</guid><category>Komputerowo-internetowo</category><category>Merb</category><category>Ruby</category><category>Techblog</category><category>thin</category><category>ebb</category><category>rack</category><category>deferred</category><category>merb</category><feedburner:origLink>http://blog.ghandal.net/2008/04/18/deferred-env-w-thin-ebb-i-merb/</feedburner:origLink></item><item><title>RuPy — dzień drugi</title><link>http://feed.ghandal.net/~r/ghandal/~3/G0tv4z_Bgi0/</link><description>&lt;p&gt;&lt;a href="http://blog.ghandal.net/2008/04/15/rupy-krotkie-podsumowanie-dnia-pierwszego/"&gt;Wczoraj&lt;/a&gt; podsumowałem pierwszy dzień &lt;a href="http://rupy.eu"&gt;RuPy&lt;/a&gt;, dzisiaj pora na drugi.&lt;/p&gt;
&lt;p&gt;Drugi dzień zaczął się od prezentacji &lt;em&gt;Rails, Amazon Web Services and you&lt;/em&gt; wygłoszonej przez &lt;a href="http://szafranek.net/"&gt;Krzysztofa Szafranka&lt;/a&gt;. Generalnie &lt;a href="http://aws.amazon.com"&gt;Amazon Web Services&lt;/a&gt; to świetna sprawa dla ludzi potrzebujących dobrej skalowalności i wielkich powierzchni za niewygórowaną cenę. Dowiedziałem się jednak czegoś o innej usłudze — &lt;a href="http://www.amazon.com/Mechanical-Turk-AWS-home-page/b?ie=UTF8&amp;amp;node=15879911"&gt;AWS Mechanical Turk&lt;/a&gt;. W skrócie to miejsce, gdzie można wynająć sobie prawdziwych ludzi do zrobienia dla nas określonych rzeczy. Dokładniej możemy zadawać pytania w formacie XML, na które wynajęci przez nas Workerzy będą starali się odpowiedzieć. Pytania nie muszą wymagać odpowiedzi "Tak" lub "Nie", możemy na przykład "zapytać" o nową notkę na nasz blog. Jednym z większych zastosowań tego mechanizmu, który na razie jest w fazie BETA, było poszukiwanie na samolotu Steeve'a Fosseta. Niestety aby skorzystać z AWS Mechanical Turk musimy posiadać prawidłowy adres z USA oraz aktywną kartę kredytową tam wydaną. &lt;a href="http://szafranek.net"&gt;Krzysztof&lt;/a&gt; zdołał również zareklamować swój serwis — &lt;a href="http://natrasie.pl"&gt;natrasie.pl&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Następną prezentacją była prezentacja "Rabiego" i "Rabistak", czyli &lt;em&gt;Ruby on Rails deployment with RubyStack&lt;/em&gt; Daniela Liszki. Idea RubyStack jest ciekawa i warta poświęcenia chwili uwagi. RubyStack to instalator całego środowiska uruchomieniowego dla Ruby/Railsów, spakowane do jednego pliku wraz z instalatorem graficznym. Można go uruchamiać pod wieloma systemami — aktualnie Windows, Linux, MacOS X, w planach jest obsługa Solarisa. Jednym kliknięciem możemy zainstalować np. Ruby, Rubygems, Apache wraz z potrzebnymi modułami, Railsy i kilka must-have gemów. W przyszłości ma pojawić się narzędzie do dostosowywania do swoich potrzeb paczek. Prezentacja zakończyła się małą polemiką słuchaczy na temat tego, iż &lt;em&gt;/bin/sh&lt;/em&gt; nie musi wcale wskazywać na &lt;em&gt;/bin/bash&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Po RubyStack przyszła kolej na krótką prezentację sponsora — &lt;a href="http://megiteam.pl"&gt;Megiteam&lt;/a&gt;. Z rozmów kuluarowych dowiedziałem się nowej, ciekawej rzeczy, która prawdopodobnie skusi mnie do skorzystania z usług Megiteam, chodzi o nowy sposób rozliczania usług. Nie ma już rozliczania per proces, zamiast tego dostajemy gwarantowany RAM na swoje procesy + dodatkowe 20MB do krótkoterminowego użycia. To sprawia, że ich oferta staje się bardziej atrakcyjna, co więcej nie trzeba martwić się o konsumpcję pamięci przez serwer mySQL.&lt;/p&gt;
&lt;p&gt;Następną prezentacją, na której byłem, była pierwsza część próby napisania IMDB-like aplikacji w Railsach w dosyć krótkim czasie, czyli &lt;em&gt;Is Rails as agile as advertise&lt;/em&gt; by &lt;a href="http://www.ridaalbarazi.com/blog/"&gt;Rida Al Barazi&lt;/a&gt;. Prezentacja całkiem udana, chociaż po drodze było trochę błędów, które zgłaszała sala. Nie zostałem jednak na drugiej części, która chyba była ciekawsza, ponieważ skupiała się na bardziej zaawansowanych funkcjonalnościach oraz dopracowywaniu graficznej strony aplikacji.&lt;/p&gt;
&lt;p&gt;W niedzielę nie zostałem do końca prezentacji i ostatnią, na której byłem był &lt;em&gt;A need of REST&lt;/em&gt; Łukasza Piestrzeniewicza. Z powodu "pythonowej" sali i większej części "pythonistów" została pominięta część dotycząca Railsów, Łukasz skupił się bardziej na założeniach REST i jego wykorzystaniu. Ciekawa prezentacja, szczególnie dla ludzi, którzy nie znali lub nie rozumieli do końca idei REST&lt;/p&gt;
&lt;p&gt;Podsumowując konferencję, muszę przyznać, że warto było na nią przyjechać, nie tylko ze względu na ciastka i obiadki. Aczkolwiek muszę przyznać, że w tym roku RuPy powinno nazywać się RailsPy — w zasadzie jedna lub dwie prezentacje w części Ruby nie dotyczyły Railsów. Oczywiście nie mam nikomu tego za złe, w końcu sam pracuję w Railsach. Mam tylko nadzieję, że w przyszłym roku pojawi się więcej prezentacji dotyczących samego Rubiego albo innych frameworków (np. &lt;a href="http://merbivore.com"&gt;Merba&lt;/a&gt;). Tegoroczne prezentacje mogą utwierdzać niektórych w mylnym przekonaniu, że Ruby = Rails.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/G0tv4z_Bgi0" height="1" width="1"/&gt;</description><pubDate>Wed, 16 Apr 2008 14:30:19 +0200</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/04/16/rupy-dzien-drugi/</guid><category>Komputerowo-internetowo</category><category>Python</category><category>Ruby</category><category>rupy</category><category>ruby</category><category>rails</category><category>python</category><category>konferencja</category><feedburner:origLink>http://blog.ghandal.net/2008/04/16/rupy-dzien-drugi/</feedburner:origLink></item><item><title>RuPy — krótkie podsumowanie dnia pierwszego</title><link>http://feed.ghandal.net/~r/ghandal/~3/e1QrqpLEsxs/</link><description>&lt;p&gt;Miałem przyjemność uczestniczyć w tegorocznym &lt;a href="http://rupy.eu"&gt;RuPy&lt;/a&gt;. Generalnie impreza bardzo przydatna i ciekawa. Chciałem dorzucić swoje trzy groszę i przekazać moje wrażenia.&lt;/p&gt;
&lt;h3&gt;Dzień 1&lt;/h3&gt;
&lt;p&gt;Dzień pierwszy zaczął się, jak to na konferencjach bywa, od rejestracji uczestników. Ponieważ na pytanie &lt;em&gt;"Ruby czy Python?"&lt;/em&gt; odpowiadam &lt;strong&gt;Ruby&lt;/strong&gt;, można mnie było zobaczyć w zasadzie tylko w sali A, gdzie odbywały się prezentacje związane z Rubym.&lt;/p&gt;
&lt;p&gt;Na pierwszy ogień poszła prezentacja &lt;a href="http://www.stifflog.com"&gt;Jarosława Rzeszótko&lt;/a&gt; o metaprogramowaniu w Ruby. Prezentacja fajna, części trików nie znałem, więc na pewno czegoś się nauczyłem. Jednak sama prezentacja była dosyć szybko przeprowadzona, na slajdach było momentami dużo kodu, co mogło trochę skołować uczestników. Przydałoby się więcej jakiegoś live-codingu zamiast kilkudziesięciu linijek kodu na slajdach.&lt;/p&gt;
&lt;p&gt;Następnym prelegentem był &lt;a href="http://tinyclouds.org/"&gt;Ry Dahl&lt;/a&gt;. Opowiedział o swoim serwerze do uruchamiania aplikacji napisanych w Railsach, Merbie i innych frameworkach opartych o Rack, a w przyszłości także Pythonowych aplikacji WSGI — Ebb. Przedstawił po krótce architekturę serwera, wyjaśnił, dlaczego jest on szybszy od innych oraz, co może niektórych zasmucić, powiedział, że nie planuje wersji Ebb pracującej pod kontrolą JRuby.&lt;/p&gt;
&lt;p&gt;Następną prezentacją było największe show na &lt;a href="http://rupy.eu"&gt;RuPy 2008&lt;/a&gt; — Correlations and Conclusions by &lt;a href="http://zedshaw.com"&gt;Zed Shaw&lt;/a&gt;. Sama prezentacja przedstawiała bardziej statystyczne podejście do testowania swoich hipotez i wyciągania wniosków z wyników badań. Przy okazji &lt;a href="http://zedshaw.com"&gt;Zed&lt;/a&gt; pochwalił się swoimi projektami w Pythonie, które pomogły mu przygotować prezentację, włączając w to ciekawy &lt;a href="http://www.zedshaw.com/projects/rupy2008/scripts/yml2beamer.py"&gt;konwerter Yaml do prezentacji LaTeX-owej w Beamerze&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Po dawce dobrego show nadszedł czas na posiłek. Nie będę tutaj opisywał lunchu, bo i po co. Po obiadku prezentował się &lt;a href="http://hosting365.com"&gt;Hosting 365&lt;/a&gt;, ja w tym czasie ładowałem baterię na korytarzu...&lt;/p&gt;
&lt;p&gt;Po sesji ładowania baterii nadszedł czas na TDD w Railsach. &lt;a href="http://andrzejkrzywda.com"&gt;Andrzej&lt;/a&gt; w ciekawy sposób przedstawił swoje spostrzeżenia na temat TDD jak i BDD. Opisał swoje doświadczenia z wyżej wymienionymi procesami, jak również zafundował uczestnikom małą sesję live-coding. Prezentacja oczywiście na plus, parę rzeczy mi się rozjaśniło, będę na pewno starał się być pragmatycznym w testowaniu i chyba zrezygnuję z bardzo rozbudowanych testów unitowych.&lt;/p&gt;
&lt;p&gt;Następnym prelegentem był &lt;a href="http://www.jayfields.com/"&gt;Jay Fields&lt;/a&gt;, który opowiedział o "DSL dla biznesu", czyli "Business Natural Languages". Bardzo ciekawie zaprezentowany bardzo ciekawy temat. Może tylko przykładowa aplikacja zaprezentowana podczas prezentacji była nadto prosta, co mogło nasuwać niektórym na myśl sensowność wytaczania takiej armaty, jaką jest DSL do tak prostych zastosowań, jednak z pewnością Business Natural Languages są w stanie polepszyć użyteczność aplikacji i zachęcić mniej technicznych ludzi do pracy z komputerem. W wielkim skrócie Business Natural Languages w założeniu mają uprościć proces wytwarzania aplikacji z modelu Business --&amp;gt; Developers --&amp;gt; QA do Business --&amp;gt; Business przez zastosowanie Descriptive and maintainable phrases (DAMP) w taki sposób, by managerowie mogli sami definiować reguły aplikacji posługując się językiem naturalnym (prawie).&lt;/p&gt;
&lt;p&gt;Kolejnym prelegentem był &lt;a href="http://www.reinventar.com/"&gt;Pedro Sousa&lt;/a&gt;. Niestety jego &lt;em&gt;acts_as_problem_solved&lt;/em&gt; była najsłabszą częścią dnia pierwszego, przynajmniej jeśli chodzi o część dotyczącą Ruby (o Pythonie się nie wypowiadam, bo nie uczestniczyłem w żadnej prezentacji go dotyczącej). Tak naprawdę taką prezentację mógłby wygłosić każdy, kto napisał 5 projektów w Railsach i korzystał z kilku pluginów. Niezadowolenie było widać na sali, część uczestników wyszła po kilku minutach. Ja zająłem się przeglądaniem &lt;a href="http://blipcast.pl/rupy"&gt;Blipcasta&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Na zakończenie dnia pierwszego o cache-owaniu aplikacji Railsowych opowiadał Wiktor Schmidt z &lt;a href="http://netguru.pl"&gt;Netguru&lt;/a&gt;. Wszystko byłoby fajnie, gdyby nie trochę niedopracowany live-coding, prawdopodobnie przez świeżo nabytego MacBooka i małe doświadczenie w pracy z TextMatem.&lt;/p&gt;
&lt;p&gt;Podsumowując, pierwszy dzień konferencji minął na ciekawych prezentacjach, szkoda, że nie dotarłem w końcu na RuPy Geek Party, choć, &lt;a href="http://jan.jogger.pl/2008/04/14/rupy-subiektywne-podsumowanie/"&gt;od znajomych&lt;/a&gt; wiem, że klub był trochę za mały na pomieszczenie konferencyjnej ekipy.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/e1QrqpLEsxs" height="1" width="1"/&gt;</description><pubDate>Tue, 15 Apr 2008 11:03:24 +0200</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/04/15/rupy-krotkie-podsumowanie-dnia-pierwszego/</guid><category>Komputerowo-internetowo</category><category>Python</category><category>Ruby</category><category>rupy</category><category>ruby</category><category>python</category><category>konferencja</category><feedburner:origLink>http://blog.ghandal.net/2008/04/15/rupy-krotkie-podsumowanie-dnia-pierwszego/</feedburner:origLink></item><item><title>Google App Engine — oddaj głos na wsparcie dla Ruby</title><link>http://feed.ghandal.net/~r/ghandal/~3/XoSFNvr_i9k/</link><description>&lt;p&gt;Jak już zapewne większość z Was zauważyło, &lt;a href="http://www.google.com"&gt;Google&lt;/a&gt; zrobiło konkurencję dla &lt;a href="http://aws.amazon.com/"&gt;Amazon Web Services&lt;/a&gt;. &lt;a href="http://code.google.com/appengine/docs/whatisgoogleappengine.html"&gt;Google App Engine&lt;/a&gt; jest póki co w fazie beta i na razie obsługuje jedynie aplikacje napisane w &lt;a href="http://www.python.org"&gt;Pythonie&lt;/a&gt;, jednak niebawem ma się pojawić wsparcie dla innych języków. Dlatego też warto już teraz dodać gwiazdkę i skomentować chęć dołączenia języka &lt;a href="http://www.ruby-lang.org"&gt;Ruby&lt;/a&gt; do języków wspieranych przez &lt;a href="http://code.google.com/appengine/docs/whatisgoogleappengine.html"&gt;Google App Engine&lt;/a&gt;. Można to zrobić komentując &lt;a href="http://code.google.com/p/googleappengine/issues/detail?id=29"&gt;Issue 29&lt;/a&gt;. Tak więć &lt;em&gt;&lt;a href="http://code.google.com/p/googleappengine/issues/detail?id=29"&gt;"star" that ticket&lt;/a&gt;&lt;/em&gt;!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/XoSFNvr_i9k" height="1" width="1"/&gt;</description><pubDate>Thu, 10 Apr 2008 13:48:00 +0200</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/04/10/google-app-engine-oddaj-glos-na-wsparcie-dla-ruby/</guid><category>Komputerowo-internetowo</category><category>Ruby</category><category>Techblog</category><category>ruby</category><category>gae</category><category>google app engine</category><feedburner:origLink>http://blog.ghandal.net/2008/04/10/google-app-engine-oddaj-glos-na-wsparcie-dla-ruby/</feedburner:origLink></item><item><title>Modelujemy znajomych w Ruby on Rails</title><link>http://feed.ghandal.net/~r/ghandal/~3/mWUbdKOs4cg/</link><description>&lt;p&gt;Ponieważ jestem w trakcie pisania małego serwisu społecznościowego w ramach pracy dyplomowej, nadszedł czas na wymodelowanie sieci znajomych. Przeglądałem trochę książek, szukałem trochę po przeróżnych blogach. W końcu stworzyłem hybrydę z rzeczy, które znalazłem.&lt;/p&gt;
&lt;p&gt;Piszę w &lt;a href="http://www.rubyonrails.com"&gt;Railsach 2.0.2&lt;/a&gt;, chciałem więc w pełni wykorzystać &lt;abbr title="Representational State Transfer"&gt;REST&lt;/abbr&gt;. Pierwsze pytania pojawiły się już na poziomie projektowania bazy danych: czy lepiej podwajać krotki i trzymać w nich jakieś dodatkowe atrybuty? Czy może lepiej oszczędzić miejsce redukując rozmiar tabeli do jednej krotki na znajomość kosztem bardziej złożonego zapytania? Stanęło na tym, że każda "znajomość" ma dwie krotki w tabeli pośredniej, a znajomych przeglądamy przeglądając atrubuty "friend_id" krotek wybranych po atrybucie "user_id". Zatem dostaliśmy relację wiele-wiele pomiędzy tabelą &lt;em&gt;Users&lt;/em&gt; a nią samą. Nasz model usera zaczął wyglądać więc mniej więcej tak:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
# app/models/user.rb
class User &amp;lt; ActiveRecord::Base
  has_many :friendships, :dependent =&amp;gt; :destroy
end
&lt;/pre&gt;
&lt;p&gt;Model samej relacji "być znajomym" nabrał takiego kształtu:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
# app/models/friendship.rb
class Friendship &amp;lt; ActiveRecord::Base
  belongs_to :user
  belongs_to :friend, :class_name =&amp;gt; 'User', :foreign_key =&amp;gt; 'friend_id'
end
&lt;/pre&gt;
&lt;p&gt;Przyjęliśmy, że każde "zaproszenie" może mieć jeden z trzech stanów:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wysłane&lt;/strong&gt; — taki status otrzymuje zaproszenie po wyłaniu przez nas, a przed zaakceptowaniem przez drugą stronę&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Oczekujące&lt;/strong&gt; — gdy wyślemy do kogoś zaproszenie, pojawi się u niego jako oczekujące na akceptację bądź odrzucenie&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zaakceptowane&lt;/strong&gt; — gdy obie strony zgodzą się na pokazanie światu, że się znają&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stan zaproszenia przechowywany więc jest w tabeli pośredniej relacji wiele-wiele. Chcemy również w łatwy sposób docierać do naszych znajomych z poziomu usera. Z pomocą przychodzi nam metoda &lt;em&gt;has_many :through&lt;/em&gt;, którą wykorzystujemy w modelu &lt;em&gt;User&lt;/em&gt; (dla uproszczenia stany zaproszeń wyrażane są za pomocą stringów, innym sposobem, w moim mniemaniu lepszym, jest użycie, zamiast napisów, np. jakiś stałych liczbowych):&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
# app/models/user.rb
class User &amp;lt; ActiveRecord::Base
# (...)  
  has_many :friendships, :dependent =&amp;gt; :destroy
  
  has_many :friends,
    :through =&amp;gt; :friendships,
    :conditions =&amp;gt; ["status = ?", 'zaakceptowane']
  
  has_many :requested_friends,
    :through =&amp;gt; :friendships,
    :source =&amp;gt; :friend,
    :conditions =&amp;gt; ["status = ?", 'wyslane']
  
  has_many :pending_friends,
    :through =&amp;gt; :friendships,
    :source =&amp;gt; :friend,
    :conditions =&amp;gt; ["status = ?", 'oczekujace']
  
  has_many :any_friends,
    :through =&amp;gt; :friendships,
    :source =&amp;gt; :friend
# (...)
end
&lt;/pre&gt;
&lt;p&gt;Teraz dobrze jest zapewnić jakieś metody do zarządzania znajomościami. W tym celu utworzymy metody klasy &lt;em&gt;Friendship&lt;/em&gt;, które będą wysyłały, akceptowały i odrzucały zaproszenia do znajomości:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
# app/models/friendship.rb
class Friendship &amp;lt; ActiveRecord::Base

  belongs_to :user
  belongs_to :friend, :class_name =&amp;gt; 'User', :foreign_key =&amp;gt; 'friend_id'
  
  validates_presence_of :user_id, :friend_id
  
  def self.exists?(user, friend)
    not find_by_user_id_and_friend_id(user, friend).nil?
  end
  
  def self.request(user, friend)
    unless user == friend or Friendship.exists?(user, friend)
      transaction do
        create(:user =&amp;gt; user, :friend =&amp;gt; friend, :status =&amp;gt; 'wyslane')
        create(:user =&amp;gt; friend, :friend =&amp;gt; user, :status =&amp;gt; 'oczekujace')
      end
    end
  end
  
  def self.accept(user, friend)
    transaction do
      accepted_at = Time.now
      accept_one_side(user, friend, accepted_at)
      accept_one_side(friend, user, accepted_at)
    end
  end
  
  def self.breakup(user, friend)
    transaction do
      destroy(find_by_user_id_and_friend_id(user, friend))
      destroy(find_by_user_id_and_friend_id(friend, user))
    end
  end
  
  private
  def self.accept_one_side(user, friend, accepted_at)
    request = find_by_user_id_and_friend_id(user, friend)
    request.status = 'zaakceptowane'
    request.accepted_at = accepted_at
    request.save!
  end
end
&lt;/pre&gt;
&lt;p&gt;Jak widać kluczowe akcje modelu wywoływane są w bloku metody &lt;em&gt;transaction&lt;/em&gt;. To zapewnia nam transakcyjne wykonanie kodu, co zabda o spójność danych i nie dopuści do powstania sytuacji, w której podczas wysyłania zaproszenia od użytkownika A do użytkownika B w bazie pojawi się tylko krotka (A,B,'wyslane').&lt;/p&gt;
&lt;p&gt;Można powiedzieć, że model jest już gotowy, pora zatem na kontroler, który będzie oferował podzbiór standardowych akcji: &lt;em&gt;create&lt;/em&gt;, &lt;em&gt;index&lt;/em&gt;, &lt;em&gt;show&lt;/em&gt; oraz &lt;em&gt;destroy&lt;/em&gt;. Akcja &lt;em&gt;index&lt;/em&gt; zajmie się wyświetlaniem listy znajomych danego użytkownika, &lt;em&gt;show&lt;/em&gt; przekieruje nas na profil użytkownika, &lt;em&gt;create&lt;/em&gt; będzie odpowiedzialna za wysłanie lub zaakceptowanie zaproszenia, a &lt;em&gt;destroy&lt;/em&gt; pozwoli na usunięcie kogoś z listy znajomych, jak również anulowanie wysłanego zaproszenia czy odrzucenie zaproszenia od niechcianej osoby. Całość umieściłem za filtrem &lt;em&gt;:login_required&lt;/em&gt; (korzystam z pluginu &lt;a href="http://agilewebdevelopment.com/plugins/restful_authentication"&gt;restful_authentication&lt;/a&gt;) aby zarządzanie znajomymi było dostępne tylko po zalogowaniu.&lt;/p&gt;
&lt;p&gt;Poszczególne akcje kontrolera nie są jakoś bardzo wyszukane, dla przykładu wkleję akcję &lt;em&gt;destroy&lt;/em&gt;:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
# app/controllers/friendships_controller.rb
class FriendshipsController &amp;lt; ApplicationController
# (...)
  def destroy
    # current_user zwraca aktualnie zalogowanego uzytkownika
    @user = current_user # dbamy o to, aby nikt nie usunal za nas naszych znajomych
    @friend = User.find(params[:id]) rescue nil
    if @friend
      if @user.any_friends.include?(@friend)
        Friendship.breakup(@user,@friend)
        flash[:notice] = 'Zaproszenie zostało usunięte'
      else
        flash[:notice] = 'Nie ma takiego zaproszenia'
      end
    else
      flash[:notice] = 'Nie ma takiego użytkownika'
    end
    redirect_to user_friendships_url(@user)
  end
# (...)
&lt;/pre&gt;
&lt;p&gt;Zasób &lt;em&gt;Friendships&lt;/em&gt; zagnieżdzony został przeze mnie w zasobie &lt;em&gt;Users&lt;/em&gt;, co zapewnia taki fragment &lt;em&gt;routes.rb&lt;/em&gt;:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
# config/routes.rb
# (...)
 map.resources :users do |u|
    u.resources :friendships
  end
# (...)
&lt;/pre&gt;
&lt;p&gt;Wydaje mi się, że takie rozwiązanie jest jednym z najprostszych, a mimo to zapewnia nam podstawowe rzeczy, które mogą okazać się przydatne, np. to, kto był stroną wysyłającą zaproszenie, czy kiedy zostało ono zaakceptowane.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/mWUbdKOs4cg" height="1" width="1"/&gt;</description><pubDate>Sat, 05 Apr 2008 17:51:05 +0200</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/04/05/modelujemy-znajomych-w-ruby-on-rails/</guid><category>Komputerowo-internetowo</category><category>Ruby</category><category>Ruby on Rails</category><category>Techblog</category><category>rails</category><category>ror</category><feedburner:origLink>http://blog.ghandal.net/2008/04/05/modelujemy-znajomych-w-ruby-on-rails/</feedburner:origLink></item><item><title>Dynamiczne ładowanie plików z formularza w Ruby on Rails</title><link>http://feed.ghandal.net/~r/ghandal/~3/85yGa988W-o/</link><description>&lt;h3&gt;Wstęp&lt;/h3&gt;
&lt;p&gt;Wyobraźmy sobie, że chcemy utworzyć bazę ogłoszeń, do których chcemy w łatwy i przyjemny sposób dodawać zdjęcia. W tej notce pokażę jak w dosyć prosty sposób wykorzystać &lt;a href="http://www.rubyonrails.com"&gt;Railsy&lt;/a&gt; do stworzenia prostego panelu do zarządzania takimi ogłoszeniami. Stworzymy formularz dodawania ogłoszeń, w którym oprócz możliwości dodawania ogłoszeń umieścimy prosty formularz, który umożliwi załadowanie zdjęcia na serwer i podpięcie go pod dodawane ogłoszenie. Oczywiście udostępnimy możliwość wyboru zdjęć już obecnych na serwerze, dla uproszczenia zakładamy, że zdjęcie może należeć tylko do jednego ogłoszenia.&lt;/p&gt;
&lt;h3&gt;Wymagania&lt;/h3&gt;
&lt;p&gt;Wszystko, czego potrzebujemy to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.rubyonrails.com"&gt;Ruby on Rails&lt;/a&gt; — ja używam wersji 2.0.2&lt;/li&gt;
&lt;li&gt;Pluginu &lt;a href="http://agilewebdevelopment.com/plugins/attachment_fu"&gt;attachment_fu&lt;/a&gt; do uploadu zdjęć na serwer&lt;/li&gt;
&lt;li&gt;Pluginu &lt;a href="http://agilewebdevelopment.com/plugins/responds_to_parent"&gt;responds_to_parent&lt;/a&gt; do dynamicznej aktualizacji formularza dodawania ogłoszeń&lt;/li&gt;
&lt;li&gt;Plugin &lt;a href="http://agilewebdevelopment.com/plugins/attachment_fu"&gt;attachment_fu&lt;/a&gt; do pracy z obrazami i do ich zmniejszania wymaga jednego z gemów:
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://seattlerb.rubyforge.org/ImageScience.html"&gt;ImageScience&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://rmagick.rubyforge.org/"&gt;RMagick&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://rubyforge.org/projects/mini-magick/"&gt;mini-magick&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Zaczynamy&lt;/h3&gt;
&lt;p&gt;Zacznijmy od stworzenia nowego projektu. Nie będę skupiał się na podstawach, na potrzeby tego opisu wykorzystam domyślną konfigurację &lt;a href="http://www.rubyonrails.com"&gt;railsów&lt;/a&gt; z bazą SQLite. W linii poleceń tworzymy nowy projekt:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ rails demo
$ cd demo
&lt;/pre&gt;
&lt;p&gt;Od razu stworzymy bazę danych, aby potem nie trzeba było do tego wracać:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ rake db:create
&lt;/pre&gt;
&lt;p&gt;Mając przygotowany projekt, zaopatrzmy się w odpowiednie pluginy. W tym celu odpalamy następujące polecenia:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ script/plugin install http://svn.techno-weenie.net/projects/plugins/attachment_fu/
$ script/plugin install http://responds-to-parent.googlecode.com/svn/trunk
&lt;/pre&gt;
&lt;p&gt;Środowisko mamy już przygotowane. Pora na utworzenie modeli. Ograniczymy się do najbardziej podstawowych rzeczy. Utworzymy model Advertisement, który będzie miał tytuł oraz opis. Potrzebny będzie również model Photo, który będzie miał atrybuty wymagane przez plugin &lt;a href="http://agilewebdevelopment.com/plugins/attachment_fu"&gt;attachment_fu&lt;/a&gt;. Wklepujemy zatem w konsoli:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ script/generate model Advertisement name:string description:text
&lt;/pre&gt;
&lt;p&gt;W przypadku modelu Photo nie będziemy podawać żadnych parametrów dla generatora:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ script/generate model Photo
&lt;/pre&gt;
&lt;p&gt;Po wygenerowaniu modeli, zanim zmigrujemy bazę danych, wyedytujemy plik migracji &lt;em&gt;db/migrate/002_create_photos.rb&lt;/em&gt;, wypełniając go jak poniżej:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
class CreatePhotos &amp;lt; ActiveRecord::Migration
  def self.up
    create_table :photos do |t|
      t.column :parent_id,  :integer
      t.column :content_type, :string
      t.column :filename, :string    
      t.column :thumbnail, :string 
      t.column :size, :integer
      t.column :width, :integer
      t.column :height, :integer
      t.column :advertisement_id, :integer
    end
  end

  def self.down
    drop_table :photos
  end
end
&lt;/pre&gt;
&lt;p&gt;W tej chwili możemy już zmigrować bazę danych&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ rake db:migrate
&lt;/pre&gt;
&lt;h3&gt;Konfiguracja modeli&lt;/h3&gt;
&lt;p&gt;Pora na skonfigurowanie modeli, utworzenie relacji jeden-wiele między ogłoszeniami i zdjęciami oraz przygotowanie modelu Photo do pracy z pluginem &lt;a href="http://agilewebdevelopment.com/plugins/attachment_fu"&gt;attachment_fu&lt;/a&gt; (wykorzystanie relacji wiele-wiele pozostawiam jako ćwiczenie). W tym celu edytujemy pliki modeli jak poniżej&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
# app/models/advertisement.rb
class Advertisement &amp;lt; ActiveRecord::Base
  has_many :photos
end
&lt;/pre&gt;
&lt;pre name="code" class="rails"&gt;
# app/models/photo.rb
class Photo &amp;lt; ActiveRecord::Base
  belongs_to :advertisement
  has_attachment :content_type =&amp;gt; :image,   # zalacznikiem jest obrazek
                 :storage =&amp;gt; :file_system,  # trzymamy go w systemie plikow
                 :max_size =&amp;gt; 2.megabytes,  # ograniczamy rozmiar do 2 MB
                 :resize_to =&amp;gt; '1200x800&amp;gt;', # zmniejszamy plik wejsciowy
                 :thumbnails =&amp;gt; {:thumb =&amp;gt; '200x200&amp;gt;'} # tworzymy jedna miniature
  validates_as_attachment # walidujemy plik po zaladowaniu na serwer, przed zapisem do bazy danych
  # z powyzsza walidacja mialem problemy pod Windowsem, posiadaczom tego systemu radze poki co zakomentowac
  # powyzsza linijke
end
&lt;/pre&gt;
&lt;p&gt;W tej chwili mamy już skonfigurowaną relację oraz przygotowany model pod umieszczanie na serwerze plików. Pora na następną część&lt;/p&gt;
&lt;h3&gt;Konfiguracja kontrolerów&lt;/h3&gt;
&lt;p&gt;Do prawidłowej pracy będziemy potrzebować dwa kontrolery. Jeden z nich, nazwijmy go &lt;em&gt;advertisements&lt;/em&gt; będzie odpowiedzialny za zarządzanie ogłoszeniami, drugi — &lt;em&gt;photos&lt;/em&gt; — będzie zajmował się zdjęciami. Wykorzystamy mechanizm &lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"&gt;REST&lt;/a&gt;. W tym celu tworzymy kontrolery z linii poleceń:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ script/generate controller Advertisements index show new edit create update destroy
$ script/generate controller Photos index new edit create destroy
&lt;/pre&gt;
&lt;p&gt;Aby skorzystać z &lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"&gt;REST&lt;/a&gt; musimy jeszcze dodać odpowiednie wpisy w pliku &lt;em&gt;config/routes.rb&lt;/em&gt;:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
ActionController::Routing::Routes.draw do |map|
  map.resources :advertisements
  map.resources :photos
  
  # ponizsze wpisy pozostawiamy
  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'
end
&lt;/pre&gt;
&lt;p&gt;Nadeszła pora na zaimplementowanie akcji potrzebnych do zarządzania naszymi zasobami. Są to standardowe akcje, pokażę zatem jedynie zawartość kontrolerów:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
# app/controllers/advertisements_controller.rb
class AdvertisementsController &amp;lt; ApplicationController

  def index
    @advertisements = Advertisement.find(:all)
    respond_to do |format|
      format.html {}
      format.xml  { render :xml =&amp;gt; @advertisements.to_xml }
    end
  end

  def show
    @advertisement = Advertisement.find(params[:id])
    respond_to do |format|
      format.html {}
      format.xml  { render :xml =&amp;gt; @advertisement.to_xml }
    end
  end

  def new
    @advertisement = Advertisement.new
  end

  def edit
    @advertisement = Advertisement.find(params[:id])
  end

  def create
    @advertisement = Advertisement.new(params[:advertisement])
    respond_to do |format|
      if @advertisement.save
        format.html { redirect_to advertisement_url(@advertisement) }
        format.xml  { render :xml =&amp;gt; @advertisement.to_xml }
      else
        format.html { render :action =&amp;gt; 'new'}
        format.xml  { render :xml =&amp;gt; @advertisement.errors.to_xml }
      end
    end
  end

  def update
    @advertisement = Advertisement.find(params[:id])
    respond_to do |format|
      if @advertisement.update_attributes(params[:advertisement])
        format.html { redirect_to advertisement_url(@advertisement) }
        format.xml  { render :xml =&amp;gt; @advertisement.to_xml }
      else
        format.html { redirect_to edit_advertisement_url(@advertisement) }
        format.xml  { render :xml =&amp;gt; @advertisement.errors.to_xml }
      end
    end
  end

  def destroy
    @advertisement = Advertisement.find(params[:id])
    respond_to do |format|
      if @advertisement.destroy
        format.html { redirect_to advertisements_url }
        format.xml  { head :ok }
      else
        format.html { redirect_to advertisement_url(@advertisement) }
        format.xml  { redirect_to @advertisement.errors.to_xml }
      end
    end
  end
end
&lt;/pre&gt;
&lt;pre name="code" class="rails"&gt;
# app/controllers/photos_controller.rb
class PhotosController &amp;lt; ApplicationController

  def index
    @photos = Photo.find(:all, :conditions =&amp;gt; 'parent_id IS NULL') # wybieramy tylko zdjecia w oryginalnych rozmiarach
    respond_to do |format|
      format.html {}
      format.xml  { render :xml =&amp;gt; @photos.to_xml }
    end
  end

  def new
    @photo = Photo.new
    @advertisements = Advertisement.find(:all)
  end

  def create
    @photo = Photo.new(params[:photo])
    respond_to do |format|
      if @photo.save
        format.html { redirect_to photos_url }
        format.xml  { render :xml =&amp;gt; @photo.to_xml }
      else
        format.html { render :action =&amp;gt; 'new' }
        format.xml  { render :xml =&amp;gt; @photo.errors.to_xml }
      end
    end
  end

  def destroy
    @photo = Photo.find(params[:id])
    respond_to do |format|
      if @photo.destroy
        flash[:notice] = 'Zdjęcie zostało usunięte'
        format.html { redirect_to photos_url }
        format.xml  { head :ok }
      else
        flash[:error] = 'Wystąpił błąd podczas usuwania zdjęcia'
        format.html { redirect_to photos_url }
        format.xml  { render :xml =&amp;gt; @photo.errors.to_xml }
      end
    end
  end
end
&lt;/pre&gt;
&lt;p&gt;Warstwa modeli i kontrolerów jest już prawie gotowa. Pozostało dodanie widoków, za pomocą których będziemy mogli odczuć, czy to, co stworzyliśmy w ogóle działa.&lt;/p&gt;
&lt;h3&gt;Pora na widoki&lt;/h3&gt;
&lt;p&gt;W warstwie widoków ograniczę się do najprostrzego wyświetlania elementów, bez żadnych upiększaczy (przynajmniej na razie). Poniżej wklejam najprostsze możliwe widoki, które nie oferują nic poza podstawowymi opcjami. Widoki dla kontrolera &lt;em&gt;advertisements_controller&lt;/em&gt;:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
# app/views/advertisements/index.html.erb
&amp;lt;h1&amp;gt;Ogłoszenia&amp;lt;/h1&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;% for advertisement in @advertisements %&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;%= link_to h(advertisement.name), advertisement_url(advertisement) %&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;%= truncate(h(advertisement.description), 20) %&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;%= link_to 'Edytuj', edit_advertisement_url(advertisement) %&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;%= link_to 'Usuń', advertisement_url(advertisement), :method =&amp;gt; :delete, :confirm =&amp;gt; 'Czy jesteś pewien?' %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;%= link_to 'Dodaj nowe', new_advertisement_url %&amp;gt;&amp;lt;/p&amp;gt;

# app/views/advertisements/new.html.erb
&amp;lt;h1&amp;gt;Nowe ogłoszenie&amp;lt;/h1&amp;gt;
&amp;lt;%= error_messages_for :advertisement %&amp;gt;

&amp;lt;% form_for @advertisement do |f| %&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;label for="advertisement_name"&amp;gt;Nazwa:&amp;lt;/label&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.text_field :name %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;label for="advertisement_description"&amp;gt;Opis:&amp;lt;/label&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.text_area :description, :cols =&amp;gt; 60, :rows =&amp;gt; 20 %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= submit_tag 'Zapisz', :disable_with =&amp;gt; "Proszę czekać..." %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;%= link_to 'Powrót', advertisements_url %&amp;gt;&amp;lt;/p&amp;gt;

# app/views/advertisements/show.html.erb
&amp;lt;h1&amp;gt;&amp;lt;%=h @advertisement.name %&amp;gt;&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;%=h @advertisement.description %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;%= link_to 'Edytuj', edit_advertisement_url(@advertisement) %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;%= link_to 'Usuń', advertisement_url(@advertisement), :method =&amp;gt; :delete, :confirm =&amp;gt; 'Czy jesteś pewien?' %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;%= link_to 'Powrót', advertisements_url %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;hr/&amp;gt;
&amp;lt;% unless @advertisement.photos.empty? %&amp;gt;
&amp;lt;p&amp;gt;Zdjęcia:&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;% for photo in @advertisement.photos %&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;%= link_to image_tag(photo.public_filename(:thumb)), photo.public_filename %&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;% end %&amp;gt;

# app/views/advertisements/edit.html.erb
&amp;lt;h1&amp;gt;Edycja ogłoszenia&amp;lt;/h1&amp;gt;
&amp;lt;%= error_messages_for :advertisement %&amp;gt;

&amp;lt;% form_for @advertisement do |f| %&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;label for="advertisement_name"&amp;gt;Nazwa:&amp;lt;/label&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.text_field :name %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;label for="advertisement_description"&amp;gt;Opis:&amp;lt;/label&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.text_area :description, :cols =&amp;gt; 60, :rows =&amp;gt; 20 %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= submit_tag 'Zapisz', :disable_with =&amp;gt; "Proszę czekać..." %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;%= link_to 'Powrót', advertisements_url %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Widoki dla kontrolera &lt;em&gt;photos_controller&lt;/em&gt;:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
# app/views/photos/index.html.erb
&amp;lt;h1&amp;gt;Zdjęcia&amp;lt;/h1&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;% for photo in @photos %&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;%= link_to image_tag(photo.public_filename(:thumb)), photo.public_filename %&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;%= link_to 'Usuń', photo_url(photo), :method =&amp;gt; :delete, :confirm =&amp;gt; 'Czy jesteś pewien?' %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;%= link_to 'Dodaj nowe', new_photo_url %&amp;gt;&amp;lt;/p&amp;gt;

# app/views/photos/new.html.erb
&amp;lt;h1&amp;gt;Nowe zdjęcie&amp;lt;/h1&amp;gt;
&amp;lt;%= error_messages_for :photo %&amp;gt;

&amp;lt;% form_for @photo, :html =&amp;gt; {:multipart =&amp;gt; true} do |f| %&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;label for="photo_uploaded_data"&amp;gt;Plik ze zdjęciem:&amp;lt;/label&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.file_field :uploaded_data %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;label for="photo_advertisement_id"&amp;gt;Ogłoszenie:&amp;lt;/label&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.collection_select :advertisement_id, @advertisements, :id, :name, {:prompt =&amp;gt; true} %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.submit "Załaduj" %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;%= link_to 'Powrót', photos_url %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;
&lt;p&gt;W tej chwili możemy już zobaczyć, jak to wszystko działa. Wystarczy odpalić serwer:&lt;/p&gt;
&lt;pre name="code" class="bash"&gt;
$ script/server
&lt;/pre&gt;
&lt;p&gt;Po odpaleniu serwera wpisując w adresie przeglądarki adres &lt;a href="http://localhost:3000/advertisements"&gt;http://localhost:3000/advertisements&lt;/a&gt; możemy administrować ogłoszeniami, natomiast pod adresem &lt;a href="http://localhost:3000/photos"&gt;http://localhost:3000/photos&lt;/a&gt; możemy zarządzać zdjęciami.&lt;/p&gt;
&lt;p&gt;No dobrze, ale na początku pisałem o dynamicznym ładowaniu zdjęć podczas pisania ogłoszenia oraz o możliwości wybierania zdjęć. Na pierwszy rzut pójdzie możliwość wyboru zdjęć spośród załadowanych i nie dołączonych do innych ogłoszeń&lt;/p&gt;
&lt;h3&gt;Wybór zdjęć przy dodawaniu ogłoszenia&lt;/h3&gt;
&lt;p&gt;Aby umożliwić dołączanie zdjęć podczas dodawania czy edycji ogłoszenia, skorzystamy z możliwości, jakie dostarcza metoda &lt;em&gt;has_many&lt;/em&gt; w modelu ogłoszeń, a dokładnie z settera &lt;em&gt;photo_ids&lt;/em&gt;. W tym celu należy dodać kilka rzeczy do kontrolera ogłoszeń i zmodyfikować odpowiednio widoki.&lt;/p&gt;
&lt;p&gt;W metodach &lt;em&gt;new&lt;/em&gt; oraz &lt;em&gt;edit&lt;/em&gt; dodajemy następującą linijkę (oczywiście przeczymy tutaj zasadzie &lt;abbr title="Don't Repeat Yourself"&gt;DRY&lt;/abbr&gt;, jednak chodzi tutaj o jak najprostsze pokazanie, o co chodzi):&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
@photos = Photo.find(:all, :conditions =&amp;gt; 'parent_id IS NULL')
&lt;/pre&gt;
&lt;p&gt;W tej chwili możemy już pokazać w formularzu dodawania/edycji ogłoszeń dostępne zdjęcia wraz z check boxami do ich zaznaczania. W tym celu dodajemy do formularza w plikach &lt;em&gt;app/views/advertisements/{new,edit}&lt;/em&gt; gdzieś w bloku &lt;em&gt;form_for&lt;/em&gt;, najlepiej po text area:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
&amp;lt;div id="photos"&amp;gt;
&amp;lt;% for photo in @photos %&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= image_tag photo.public_filename(:thumb) %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= check_box_tag 'advertisement[photo_ids][]', photo.id, @advertisement.photos.include?(photo) %&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Od tej pory będziemy mogli zaznaczać zdjęcia, które będziemy chcieli dodać do ogłoszenia (pamiętajmy o tym, że mamy relację jeden-wiele, więc jeżeli tworząc lub edytując jakieś ogłoszenie zaznaczymy zdjęcie wcześniej zajęte, zostanie ono przypisane tylko do nowo tworzonego/edytowanego ogłoszenia — aby to zmienić należy zamienić relację z jeden-wiele na wiele-wiele). Zostaje tylko jeden problem. Kiedy będziemy chcieli usunąć z jakiegoś ogłoszenia wszystkie zdjęcia i odznaczymy wszystkie dostępne w formularzu, po kliknięciu 'Dodaj' lub 'Edytuj' nic się nie stanie. Dzieje się tak dlatego, że w tym przypadku Railsy nie przekażą do kontrolera zawartości parametru &lt;em&gt;advertisement[photo_ids][]&lt;/em&gt;. Jednak jest bardzo prosty sposób, by to zmienić. Do kontrolera &lt;em&gt;advertisements_controller&lt;/em&gt; w akcji &lt;em&gt;update&lt;/em&gt; dodajemy na początku następującą linijkę:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
params[:advertisement][:photo_ids] ||= []
&lt;/pre&gt;
&lt;p&gt;Spowoduje ona, że w przypadku, gdy odznaczymy wszystkie zdjęcia podczas edycji ogłoszenia, zostaną rzeczywiście usunięte z relacji.&lt;/p&gt;
&lt;h3&gt;Dynamiczne dodawanie plików + &lt;em&gt;a pinch of AJAX&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;Do zrobienia została nam jeszcze jedna część — dynamiczne ładowanie zdjęcia podczas dodawania lub edycji ogłoszenia i uaktualnianie formularza nowym zdjęciem. Nie da się tego zrobić przy pomocy samego AJAX-a, ponieważ ze względów bezpieczeństwa AJAX nie pozwala na przesyłanie lokalnych plików. Istnieje jednak prosty sposób, aby tę niedogodność ominąć. W tym celu utworzymy na stronie niewodoczny &lt;em&gt;iframe&lt;/em&gt; i ustawimy na niego atrybut &lt;em&gt;target&lt;/em&gt; formularza ładującego zdjęcie. Następnie, wykorzystując plugin &lt;strong&gt;responds_to_parent&lt;/strong&gt;, uaktualnimy listę zdjęć w formularzu dotyczącym ogłoszenia.&lt;/p&gt;
&lt;p&gt;Na początku zdefiniujmy sobie prosty layout oraz dołączmy potrzebne skrypty JavaScript. W tym celu tworzymy plik &lt;em&gt;app/views/layouts/application.erb&lt;/em&gt; o nastepującej zawartości:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta http-equiv="Content-type" content="text/html; charset=utf-8"/&amp;gt;
    &amp;lt;title&amp;gt;Demonstracja dynamicznego ładowania plików, &amp;amp;copy; 2008 GhandaL&amp;lt;/title&amp;gt;
    &amp;lt;%= javascript_include_tag :defaults %&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;% if flash[:notice] %&amp;gt;
      &amp;lt;div style="border: 1px green"&amp;gt;&amp;lt;%= flash[:notice] %&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;% end %&amp;gt;
    &amp;lt;% if flash[:error] %&amp;gt;
      &amp;lt;div style="border: 1px red"&amp;gt;&amp;lt;%= flash[:error] %&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;% end %&amp;gt;
    &amp;lt;%= yield :layout %&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;W celu ładowania pliku posłużymy się fragmentami kodu widoków, tzw. &lt;em&gt;partials&lt;/em&gt;. Zanim jednak dopracujemy widoki, zmodyfikujemy odpowiednio kontroler &lt;em&gt;photos_controller&lt;/em&gt;, aby uaktualniał stronę, która wywołała akcję &lt;em&gt;create&lt;/em&gt;. W tym celu modyfikujemy akcję &lt;em&gt;create&lt;/em&gt; w pliku &lt;em&gt;app/controllers/photos_controller.rb&lt;/em&gt; tak, by wyglądała jak poniżej:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
  def create
    @photo = Photo.new(params[:photo])
    respond_to do |format|
      if @photo.save
        format.html { redirect_to photos_url }
        format.xml  { render :xml =&amp;gt; @photo.to_xml }
        format.js do
          responds_to_parent do
            render :update do |page|
              page.insert_html :bottom, "photos", :partial =&amp;gt; 'photo', :object =&amp;gt; @photo
            end
          end
        end
      else
        format.html { render :action =&amp;gt; 'new' }
        format.xml  { render :xml =&amp;gt; @photo.errors.to_xml }
        format.js do
          responds_to_parent do
            render :update do |page|
              # jakis kod uaktualniajacy, np. bledem, strone
            end
          end
        end
      end
    end
  end
&lt;/pre&gt;
&lt;p&gt;Teraz możemy się zająć stworzeniem odpowiednich &lt;em&gt;partials&lt;/em&gt;. Po pierwsze stworzymy kod, który wyświetli nowo załadowane zdjęcie. Tworzymy plik &lt;em&gt;app/views/photos/_photo.erb&lt;/em&gt; o zawartości:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
&amp;lt;p&amp;gt;&amp;lt;%= image_tag photo.public_filename(:thumb) %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;%= check_box_tag 'advertisement[photo_ids][]', photo.id, true %&amp;gt;&amp;lt;/p&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Do zrobienia została nam jeszcze jedynie modyfikacja widoków akcji &lt;em&gt;new&lt;/em&gt; i &lt;em&gt;edit&lt;/em&gt; kontrolera &lt;em&gt;advertisements_controller&lt;/em&gt;. Gdzieś na końcu plików &lt;em&gt;app/views/advertisements/{new,edit}&lt;/em&gt; dopisujemy następujący kod:&lt;/p&gt;
&lt;pre name="code" class="rails"&gt;
&amp;lt;iframe name="dummy" id="dummy" style="width:1px;height:1px;border:0"&amp;gt;&amp;lt;/iframe&amp;gt;
&amp;lt;% form_for :photo, :url =&amp;gt; {:controller =&amp;gt; :photos, :action =&amp;gt; :create, :format =&amp;gt; 'js'}, :html =&amp;gt; {:target =&amp;gt; 'dummy', :multipart =&amp;gt; true} do |f| %&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;label for="photo_uploaded_data"&amp;gt;Plik ze zdjęciem:&amp;lt;/label&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.file_field :uploaded_data %&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;%= f.submit 'Dodaj' %&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;I to już wszystko. Od tej pory dodając i edytując ogłoszenia można już ładować zdjęcia bez potrzeby przeładowywania strony.&lt;/p&gt;
&lt;h3&gt;Konkluzja&lt;/h3&gt;
&lt;p&gt;Oczywiście powyższe rozwiązanie ma pewne wady. Przede wszystkim relacja jeden-wiele jest bardzo prymitywna w tym przypadku i zabrania dołączania jednego zdjęcia do kilku ogłoszeń. Jednak modyfikacja tego nie jest trudna, nie o tym jest również ta notka. Kod demonstracyjnej aplikacji możesz ściągnąć &lt;a href="http://studia.ghandal.net/Rails/demo.zip"&gt;mojej strony&lt;/a&gt;. Mam nadzieję, że powyższy opis komuś się przyda. Jeśli zauważyłeś jakiś błąd lub chciałbyś wyrazić opinię o moim rozwiązaniu, zapraszam do komentowania.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/85yGa988W-o" height="1" width="1"/&gt;</description><pubDate>Sun, 23 Mar 2008 00:38:32 +0100</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/03/23/dynamiczne-ladowanie-plikow-z-formularza-w-ruby-on-rails/</guid><category>Komputerowo-internetowo</category><category>Ruby</category><category>Ruby on Rails</category><category>Techblog</category><category>rails</category><category>ruby</category><category>ajax</category><category>iframe</category><feedburner:origLink>http://blog.ghandal.net/2008/03/23/dynamiczne-ladowanie-plikow-z-formularza-w-ruby-on-rails/</feedburner:origLink></item><item><title>Mój pierwszy VPS</title><link>http://feed.ghandal.net/~r/ghandal/~3/7NBQZQW6_vM/</link><description>&lt;p&gt;Ponieważ zaczęły mnie denerwować timeouty w przekazywaniu żądań z Apache do mongreli na &lt;a href="http://rootnode.pl"&gt;Rootnode&lt;/a&gt; zacząłem rozglądać się za czymś lepszym. Pod uwagę brałem VPS-y w cenie do ok. 60zł za miesiąc. Postanowiłem w najbliższych miesiącach przetestować kilka z nich. Na pierwszy ogień poszedł &lt;a href="http://provps.pl"&gt;ProVPS&lt;/a&gt;. Za tym rozwiązaniem przemawia lokalizacja serwerów w Polsce, w Gdańsku, a co za tym idzie niskie pingi, w granicach 35-45ms. Jednak na tym plusy się kończą (porównując do reszty ofert branych pod uwagę). Za 49 zł netto miesięcznie dostajemy gwarantowane 128 MB RAM-u, rozszerzane do 512 MB, 5 GB powierzchni dyskowej, 25 GB transferu miesięcznego oraz 1 adres IP. Na tle konkurencji trochę blado.&lt;/p&gt;
&lt;p&gt;Wczoraj aktywowano mi VPS i spędziłem kilka ładnych godzin na konfigurowaniu poszczególnych części systemu. Postanowiłem uruchomić minimum usług, głównie po to, by zmieścić się w drakońskich limitach pamięci. Niestety musiałem zrezygnować z mySQL, ponieważ nie znalazłem sposobu na ograniczenie konsumpcji pamięci do akceptowalnego na tym serwerze poziomu — po uruchomieniu mysqld konsumował ok. 100 MB pamięci operacyjnej pomimo przeróżnych konfiguracji znalezionych w sieci i w dokumentacji mySQL. Zaminiłem mySQL na PostgreSQL i tutaj małe zaskoczenie — domyślna instalacja postgresa zużywa ponad połowę mniej pamięci niż mySQL!&lt;/p&gt;
&lt;p&gt;Gdy baza danych była już gotowa, nadszedł czas na serwer WWW. Wybór oczywiście padł na &lt;a href="http://nginx.net"&gt;nginx&lt;/a&gt;, o wiele lżejszy od Apache'a serwer plus load balancer. Na VPS-ie będą uruchamiane jedynie statyczne pliki oraz aplikacje w &lt;a href="http://www.rubyonrails.com"&gt;Railsach&lt;/a&gt; i &lt;a href="http://www.merbivore.com"&gt;Merbie&lt;/a&gt; (w liczbie max. 3 — patrz pamięć). Z konfiguracją nginxa nie było żadnych problemów, świetną konfigurację dla aplikacji railsowych w obsługą cache przygotował &lt;a href="http://brainspl.at"&gt;Ezra Zygmuntowicz&lt;/a&gt;. Konfiguracja ta dostępna jest na jego &lt;a href="http://brainspl.at/nginx.conf.txt"&gt;stronie&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Następną rzeczą do zrobienia była kompilacja Ruby, Rubygems, ImageMagick oraz instalacja Subversion. Oczywiście po drodze były mniejsze komplikacje — a to zabrakło biblioteki zlib, a to nie zainstalowałem libsqlite3-dev. W każdym razie po mniej więcej 5 godzinach miałem już działającego Postgresa, nginxa, SVN, Ruby+Rails i całą resztę potrzebnych do życia aplikacji.&lt;/p&gt;
&lt;p&gt;Nadszedł czas na sprawdzenie, czy praca nie poszła na marne. Zgrałem pisaną ostatnio aplikację, zmigrowałem bazę, ustawiłem dwie instancje serwera &lt;a href="http://code.macournoyer.com/thin/"&gt;thin&lt;/a&gt; i uruchomiłem je. Et voilà! Wszystko ruszyło i działa jak należy i, co najważniejsze, naprawdę dużo szybciej niż na &lt;a href="http://rootnode.pl"&gt;Rootnode&lt;/a&gt;, gdzie aplikacja stała poprzednio.&lt;/p&gt;
&lt;p&gt;Nieprzespana noc z konfiguracją serwera za mną, ale za miesiąc prawdopodobnie czeka mnie następna, ponieważ, jak pisałem na wstępie, mam w planie przetestowanie kilku dostawców, którzy mają mniejszy miesięczny abonament i gwarantują 2x więcej pamięci, co dla mnie jest rzeczą ważną. Zastanawiam się, czy sensowne będzie kupowanie VPS-a w USA. Na mojej liście jest &lt;a href="http://www.slicehost.com"&gt;Slicehost&lt;/a&gt; oraz &lt;a href="http://www.silverrack.om"&gt;SilverRack&lt;/a&gt; z całkiem niezłą ofertą za niewielkie pieniądze. Z drugiej strony za zachodnią granicą mamy &lt;a href="http://www.hosteurope.de"&gt;Host Europe&lt;/a&gt;, również z ciekawą ofertą i niskimi pingami.&lt;/p&gt;
&lt;p&gt;Pomimo dużo większych nakładów czasowych myślę, że nie wrócę już do dzielonego hostingu. VPS i serwer dedykowany daje tę wolność, jakiej nie ma na żadnym dzielonym hostingu (Rootnode jest bardzo liberalne, ale, np. nie mogłem doprosić się o aktualizację Ruby do wersji 1.8.6, ponieważ ma "za dużo zależności"). Pamiętać trzeba oczywiście o tym, że samemu trzeba dbać o stabliność i bezpieczeństwo danych na takim serwerze, ale to z kolei wymusza pewną pracę i naukę, która później nie zginie.&lt;/p&gt;
&lt;p&gt;PS. Jakby ktoś z czytających miał wiedzę na temat tego, jak ograniczyć zużycie pamięci przez mySQL, proszę o kontakt.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/7NBQZQW6_vM" height="1" width="1"/&gt;</description><pubDate>Tue, 11 Mar 2008 14:43:37 +0100</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/03/11/moj-pierwszy-vps/</guid><category>Komputerowo-internetowo</category><category>Ruby</category><category>Ruby on Rails</category><category>Techblog</category><category>Web Design</category><category>vps</category><category>serwer</category><category>ruby</category><feedburner:origLink>http://blog.ghandal.net/2008/03/11/moj-pierwszy-vps/</feedburner:origLink></item><item><title>Ebb — szybciej, coraz szybciej!</title><link>http://feed.ghandal.net/~r/ghandal/~3/qZ3QboRYYOY/</link><description>&lt;p&gt;Na horyzoncie pojawił się nowy gracz na rynku serwerów do obsługi frameworków w języku &lt;a href="http://www.ruby-lang.org"&gt;Ruby&lt;/a&gt; i Python (w przyszłości) — jest nim &lt;a href="http://ebb.rubyforge.org/"&gt;Ebb&lt;/a&gt;. Serwer ten &lt;a href="http://www.rubyinside.com/ebb-web-framework-http-server-786.html"&gt;bije konkurentów na głowę&lt;/a&gt;, przynajmniej w szybkości i ilości requestów na sekundę. Ja zauważyłem, że zużywa troche więcej pamięci niż np. &lt;a href="http://mongrel.rubyforge.org/"&gt;Mongrel&lt;/a&gt;. W swojej małej aplikacji używałem już &lt;a href="http://mongrel.rubyforge.org/"&gt;Mongrela&lt;/a&gt; oraz młodszego &lt;a href="http://code.macournoyer.com/thin/"&gt;Thina&lt;/a&gt; — nadszedł czas na przetestowanie &lt;a href="http://ebb.rubyforge.org/"&gt;Ebb&lt;/a&gt;. Na pierwszy rzut oka rzeczywiście wzrost wydajności widoczny jest niemal natychmiast.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://ebb.rubyforge.org/"&gt;Ebb&lt;/a&gt; jest na razie młodym serwerem i brak mu dokumentacji, a przynajmniej ja nie stwierdziłem takowej. Jednak aby odpalić aplikację w &lt;a href="http://www.rubyonrails.com"&gt;Railsach&lt;/a&gt; wystarczy wykonać kilka prostych poleceń:&lt;/p&gt;
&lt;blockquote&gt;$ gem install ebb&lt;/blockquote&gt;
&lt;p&gt;Następnie przejść do katalogu aplikacji &lt;a href="http://www.rubyonrails.com"&gt;Railsowej&lt;/a&gt; i odpalić&lt;/p&gt;
&lt;blockquote&gt;$ ebb_rails start&lt;/blockquote&gt;
&lt;p&gt;Pełna lista opcji, jakie oferuje Ebb jest następująca:&lt;/p&gt;
&lt;pre&gt;
Usage: ebb_rails [options] start|stop

Server options:
    -p, --port PORT        use PORT (default: 3000)
    -e, --env ENV          Rails environment 
                                     (default: development)
    -c, --chdir PATH       Rails root dir 
                                     (default: current dir)
    -d, --daemonize        Daemonize
    -l, --log-file FILE    File to redirect output
    -P, --pid-file FILE    File to store PID
    -t, --timeout SEC      Request or command timeout in sec
                                     (default: 60)

Common options:
    -h, --help             Show this message
    -v, --version          Show version
&lt;/pre&gt;
&lt;p&gt;W przypadku uruchamiania jako demon, konieczne jest podanie ścieżki do pliku z PID procesu.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/qZ3QboRYYOY" height="1" width="1"/&gt;</description><pubDate>Wed, 05 Mar 2008 09:37:26 +0100</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/03/05/ebb-szybciej-coraz-szybciej/</guid><category>Komputerowo-internetowo</category><category>Ruby</category><category>Techblog</category><category>ebb</category><category>ruby</category><category>rails</category><category>merb</category><category>serwer</category><feedburner:origLink>http://blog.ghandal.net/2008/03/05/ebb-szybciej-coraz-szybciej/</feedburner:origLink></item><item><title>Zmiana adresu kanału RSS.</title><link>http://feed.ghandal.net/~r/ghandal/~3/4UOFeiPueAI/</link><description>&lt;p&gt;Zdecydowałem się założyć konto na &lt;a href="http://www.feedburner.com"&gt;FeedBurner&lt;/a&gt;, więc od dzisiaj kanał &lt;abbr title="Really Simple Syndication"&gt;RSS&lt;/abbr&gt; dla wpisów dostępny będzie przez serwis wymieniony wcześniej serwis pod adresem &lt;a href="http://feeds.feedburner.com/ghandal"&gt;http://feeds.feedburner.com/ghandal&lt;/a&gt;. Proszę o aktualizację czytników.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/4UOFeiPueAI" height="1" width="1"/&gt;</description><pubDate>Thu, 14 Feb 2008 13:46:52 +0100</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/02/14/zmiana-adresu-kanalu-rss/</guid><category>Komputerowo-internetowo</category><feedburner:origLink>http://blog.ghandal.net/2008/02/14/zmiana-adresu-kanalu-rss/</feedburner:origLink></item><item><title>pygglib 0.2 released</title><link>http://feed.ghandal.net/~r/ghandal/~3/Jqj1mMMQa3k/</link><description>&lt;p&gt;Można powiedzieć, że dzisiaj światło dzienne ujrzała wersja 0.2 &lt;a href="http://trac2.assembla.com/pygglib"&gt;pygglib&lt;/a&gt; — biblioteki do obsługi protokołu &lt;a href="http://www.gadu-gadu.pl"&gt;Gadu-Gadu&lt;/a&gt; napisanej w &lt;a href="http://www.python.org"&gt;pythonie&lt;/a&gt;. Wersja ta implementuje już większą część protokołu opisanego na &lt;a href="http://ekg.chmurka.net/docs/protocol.html"&gt;stronach EKG&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Przy okazji pisania testów i implementacji okazało się, że panowie z &lt;a href="http://www.gadu-gadu.pl"&gt;Gadu-Gadu&lt;/a&gt; znowu mieszają coś w protokole, poza tym opis na stronach EKG jest już trochę nieaktualny (szczegóły zostały wysłane na &lt;a href="http://lists.ziew.org/mailman/listinfo/ekg-devel"&gt;listę dyskusyjną ekg-devel&lt;/a&gt;). W każdym razie dzisiaj miała miejsce prezentacja projektu na zajęciach i muszę przyznać, że większość prac, która została przewidziana, została zaimplementowana. Niestety zabrakło czasu na obsługę bezpośrednich połączeń oraz takich rzeczy jak połączenia konferencyjne, czy rozmowy głosowe, jednak jestem zadowolony z tego, co zostało napisane.&lt;/p&gt;
&lt;p&gt;Przy okazji muszę ponarzekać na &lt;a href="http://www.python.org"&gt;pythona&lt;/a&gt;... Przyzwyczajony byłem do tej pory do składni języka &lt;a href="http://www.ruby-lang.org"&gt;Ruby&lt;/a&gt; i trochę trudno było mi się przestawić na wcięcia i dwukropki. Poza tym podobno &lt;a href="http://www.python.org"&gt;python&lt;/a&gt; jest językiem obiektowym, więc tym bardziej denerwowało mnie na przykład to, że lista nie ma metody "len" bądź "length", tylko jej długość możemy sprawdzić funkcją &lt;i&gt;len(lista)&lt;/i&gt;, co już, według mnie, za bardzo obiektowe nie jest.&lt;/p&gt;
&lt;p&gt;Na osobną notkę zasługuje również coś, co według mnie powinno zostać naprawione wieki temu — w &lt;a href="http://www.python.org"&gt;pythonie&lt;/a&gt; nie da się (pod Windowsem i w sposób najbardziej naturalny) przechwytywać sygnału SIGINT po wciśnięciu Ctrl+C (czyli wyjątku KeyboardInterrupt) w programach wielowątkowych! Jest to absolutna kompromitacja. W programie demonstrującym działanie &lt;a href="http://trac2.assembla.com/pygglib"&gt;pygglib&lt;/a&gt; chciałem stworzyć proste echo, które byłoby wyłączane przez wciśnięcie Ctrl+C, jednak, ponieważ biblioteka korzysta z listenera i obsługuje zdarzenia, przez co jest wielowątkowa, stało się to niemożliwe. Pod linuxem można ten błąd w dosyć prosty sposób ominąć poprzez zastosowanie &lt;i&gt;os.fork()&lt;/i&gt;, jednak pod Windowsem takiej opcji nie ma.&lt;/p&gt;
&lt;p&gt;To tyle narzekania na &lt;a href="http://www.python.org"&gt;Pythona&lt;/a&gt;. Zainteresowanych zapraszam do &lt;a href="http://ghandal.net/pygglib/pygglib-0.2.1.tar.gz"&gt;pobrania&lt;/a&gt; źródeł biblioteki oraz testowania. Ewentualnie błędy i sugestie proszę zgłaszać na &lt;a href="http://trac2.assembla.com/pygglib"&gt;Trac&lt;/a&gt; projektu.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;EDIT:&lt;/b&gt; Wypuściliśmy wersję 0.2.1, w której poprawiony został odczyt wiadomości wysyłanych z niektórych wersji Gadu-Gadu, na końcu których występowały czasem śmieci. Link do źródeł został zaktualizowany.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/Jqj1mMMQa3k" height="1" width="1"/&gt;</description><pubDate>Wed, 16 Jan 2008 18:37:35 +0100</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2008/01/16/pygglib-0-2-released/</guid><category>Komputerowo-internetowo</category><category>Python</category><category>Studia</category><category>Techblog</category><category>pygglib</category><category>python</category><category>gadu-gadu</category><category>gg</category><feedburner:origLink>http://blog.ghandal.net/2008/01/16/pygglib-0-2-released/</feedburner:origLink></item><item><title>Rails 2.0 podano do stołu.</title><link>http://feed.ghandal.net/~r/ghandal/~3/TQcYEwxU1xc/</link><description>&lt;p&gt;Dzisiaj nareszcie wydano nową wersję &lt;a href="http://www.rubyonrails.org"&gt;Railsów&lt;/a&gt;, których przedstawiać pewnie nie trzeba. Instrukcja instalacji oraz nowości, których jest wiele, znajduje się na &lt;a href="http://weblog.rubyonrails.com/2007/12/7/rails-2-0-it-s-done"&gt;oficjalnym blogu&lt;/a&gt; frameworka. Ciekaw jestem, jak wypadną benchmarki nowej wersji Railsów. Mam nadzieję, że lepiej, niż poprzednich.&lt;/p&gt;
&lt;p&gt;Na horyzoncie pojawiła się jeszcze jedna, bardzo ciekawa, bo wielowątkowa, komponentowa i zorientowana na wydajność propozycja dla programistów aplikacji internetowych w &lt;a href="http://www.ruby-lang.org"&gt;Rubym&lt;/a&gt; — &lt;a href="http://www.merbivore.com"&gt;Merb&lt;/a&gt;. Framework ten nie ma wbudowanego ORM-a, może jednak korzystać bezproblemowo zarówno z Railsowym ActiveRecordem, jak i z Data-mapperem, czy Sequelem. Szkoda tylko, że nie póki co ciężko go uruchomić pod Windowsem.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/TQcYEwxU1xc" height="1" width="1"/&gt;</description><pubDate>Fri, 07 Dec 2007 19:08:21 +0100</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2007/12/07/rails-2-0-podano-do-stolu/</guid><category>Komputerowo-internetowo</category><category>Ruby</category><category>Ruby on Rails</category><category>ruby</category><category>rails</category><category>merb</category><feedburner:origLink>http://blog.ghandal.net/2007/12/07/rails-2-0-podano-do-stolu/</feedburner:origLink></item><item><title>GalleryMaker 0.2</title><link>http://feed.ghandal.net/~r/ghandal/~3/ySfHHMrJCZI/</link><description>&lt;p&gt;W miarę wolnego czasu kontynuuję pisanie swojej małej biblioteki w języku &lt;a href="http://www.ruby-lang.org"&gt;Ruby&lt;/a&gt; do tworzenia indeksu zdjęć z miniaturkami. Dzisiaj światło dzienne ujrzała wersja 0.2, a wraz z nią możliwość dzielenia indeksu na kilka plików, co było dosyć dużą bolączką biblioteki (ładowanie kilkuset miniaturek trochę trwa).&lt;/p&gt;
&lt;p&gt;Cały czas w TODO został system szablonów, w tej chwili nie mam czasu go zrobić. Nie wybrałem nawet koncepcji zarządzania tymi szablonami. W głowie mam kilka pomysłów — XML, Yaml, może Haml lub ERb. Na co się zdecyduję, jeszcze nie wiem.&lt;/p&gt;
&lt;p&gt;Jeśli ktoś chciałby pobrać aktualną wersję (nie daję gwarancji, że cokolwiek zadziała, chociaż testowałem przez chwilę zarówno pod Windowsem, jak i pod Linuxem — Debian 4.0 i Ubuntu 7.10), zapraszam do &lt;a href="http://svn2.assembla.com/svn/gallerycreator/trunk/"&gt;repozytorium&lt;/a&gt;. Do zrobienia jest jeszcze dużo, począwszy od refaktoryzacji. Wszelkie uwagi i pomysły mile widziane.&lt;/p&gt;
&lt;p&gt;Zapomniałem dodać, że do poprawnego działania wymagane jest posiadanie ImageMagick oraz biblioteki RMagick.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/ghandal/~4/ySfHHMrJCZI" height="1" width="1"/&gt;</description><pubDate>Fri, 07 Dec 2007 18:59:40 +0100</pubDate><guid isPermaLink="false">http://blog.ghandal.net/2007/12/07/gallerymaker-0-2/</guid><category>Komputerowo-internetowo</category><category>Ruby</category><category>Techblog</category><category>gallerymaker</category><category>ruby</category><feedburner:origLink>http://blog.ghandal.net/2007/12/07/gallerymaker-0-2/</feedburner:origLink></item></channel></rss>
