Rails Rumble
Sep 02
Lada chwila zacznie się rejestracja na Rails Rumble. Jest to konkurs polegający na tym, by w ciągu 48 godzin stworzyć od zera w Railsach, lub dowolnym innym Rubiowym frameworku opartym o Racka, działającą aplikację. Organizatorzy zapewniają hosting (który należy sobie skonfigurować) oraz prywatne konto na githubie. Zespół może liczyć maksymalnie cztery osoby i wymagane są co najmniej dwa komity dziennie, aby udowodnić postęp prac. Można korzystać z dowolnych pluginów i gemów, pod warunkiem, że nie jest w nich zaszyta większość głównej funkcjonalności tworzonego serwisu.
Nagrody
Zwycięzcy, oprócz 5 minut sławy i lansu, mogą liczyć na prawdziwy Rumble Championship Belt! Serwisy oceniane będą przez panel ekspertów i społeczność Internetową.
Poprzednie edycje
W poprzednich edycjach uczestnikom udało się odpalić kilkaset interesujących serwisów, między innymi:
- hi.im – aplikacja gromadząca w jendym miejscu twoją działalność w różnych serwisach społecznościowych
- howsmycode.com – narzędzie do rewizji kodu w rozproszonych zespołach
- hurl.it – internetowa wersja CURL’a
- electriccheckbook.com – zarządzanie finansami
- tastyplanner.com – danie dnia
- meetinbetween.us – serwis wyznaczający dla dwóch lokalizacji miejsce spotkania
- trackclass.com – aplikacja usprawniająca organizację zajęć związanych ze szkołą, lub studiami
- remindr.info/ – prosty przypominacz, który może wysłać ci o zadanej godzinie wysłać wiadomośc za pomocą smsa, jabbera poczty, lub twittera
Co prawda nie wszystkie z tych projektów nadal działają, ale są one jednak dowodem na to, że można zrobić coś z niczego w zaledwie jeden weekend.
Terminy
Rejestracja miała trwać przez tydzień od 1 do 7 września. Niestety wystąpiły pewne
opóźnienia i organizatorzy zapowiadają, że wkrótce uruchomią rejestrację. Sam konkurs odbędzie się w weekend 16 — 17 października. My już szykujemy listę ciekawych pomysłów do realizacji.
więcej...
Ruby on Rails 3
Aug 312 lata developmentu, tysiące commitów od ponad 1600 autorów, 4 wersje beta, 2 release candidate i wreszcie jest… Gemy finalnej wersji Rails 3.0 są do pobrania już od wczoraj. Nie obyło się bez opóźnień – pierwsza wersja RC była planowana jeszcze na czerwiec (w trakcie lub tuż po RailsConf 2010), lecz ukazała się dopiero z końcem lipca. Na finiszu jednak team Rails nie zawiódł i zapowiedź wersji finalnej na ten tydzień nie była pustą obietnicą. Licząc na to, że autorzy wynieśli nauczkę z faux pas z wersją 2.3.6, to jest naprawdę to, na co wielu developerów zaciera ręce.
Dla niecierpliwych
Już w tej chwili możesz wywołać
gem install rails
i pobierze się najnowsza wersja RoR. Wraz z Rails finalne wersje 1.0 osiągnęły dwa kluczowe elementy frameworku – Bundler i ARel. One również zainstalują się wraz z instalacją nowych Railsów.
Trochę bardziej cierpliwym polecam skorzystanie z rvm, który również zaledwie tydzień temu osiąnął „dojrzałość” w postaci wersji 1.0. Dzięki temu narzędziu można nieinwazyjnie przygotować optymalne, odizolowane środowisko dla developmentu. A warto wiedzieć, że Rails 3 wspierają tylko wersje Ruby’ego 1.8.7 i 1.9.2. Jeśli możesz przeboleć niezgodność niektórych gemów z Rubym 1.9, polecam korzystanie z tej ostatniej wersji. Dzięki rvm przesiadka będzie całkowicie bezbolesna.
Jak to podejść?
Czy wybrać już Rails 3 do najnowszego projektu? A może lepiej przeczekać jeszcze kilka miesięcy z 2.3.x? Główna obawa dotyczyć może zgodności zewnętrznych gemów z nową wersją frameworku. Na szczęście sytuacja wygląda całkiem optymistycznie. Developerzy mieli sporo czasu na zapoznanie się z nowym API i dzięki temu wiele poważniejszych gemów jest już dziś gotowa do współpracy z Rails 3. Na myśl przychodzą choćby haml, authlogic, will_paginate, hoptoad_notifier. Wszystkie pluginy ekipy Thoughtbot (paperclip, shoulda, factory_girl) były już w czerwcu kompatybilne z ówczesną betą, teraz można co najwyżej czekać na oficjalne wersje zgodne z finalną wersją Rails 3.
Wątpliwości i obawy co do kompatybilności bibliotek pomoże rozwiać strona http://www.railsplugins.org, a dla korzystających z Ruby 1.9.2 dodatkowo http://isitruby19.com
Dla mnie osobiście nie ma już dyskusji. Zobaczyłem światło (czyt. ARela i nowy ActionMailer) i nie zamierzam się oglądać wstecz przy nowych projektach.
Niektórych może też kusić migracja istniejącego projektu z 2.x do 3.0. Dla większości średnich i dużych projektów, które wyszły już z etapu dynamicznego developmentu, gra nie będzie warta świeczki. Choć powstało narzędzie automatyzujące część procesu migracji (rails_upgrade), w wielu przypadkach i tak będzie to droga przez mękę, a korzyść z tego niewielka. Co innego jeśli mowa o mniejszych lub hobbystycznych projektach. Ale w takiej sytuacji i tak nikogo przekonywać nie muszę…
Dokumentacja
Już dziś poradniki na stronie Rails Guides są aktualne do wersji 3.0. Kluczowe różnice względem wersji 2.3 można znaleźć w sekcjach:
- A Guide to The Rails Command Line
- Active Record Query Interface
- Rails Routing from the Outside In
- Action Mailer Basics
Oprócz oficjalnych poradników warte uwagi dla adeptów Rails 3 będą:
- http://railstutorial.org – kompletna książka-tutorial oparta na Rails 3. Może się okazać świetna dla początkujących.
- http://rubyonrails.org/screencasts/rails3 – screencasty poświęcone najnowszej wersji
Po inne materiały (a jest ich już całkiem sporo) odsyłam do niedawnego wpisu na oficjalnym blogu rubyonrails.org.
Warto wiedzieć, że sporo książek poświęconych Rails 3 jest już na ukończeniu i w niedługim czasie powinny być dostępne. Odświeżony klasyk „Rails Way” ma się ukazać z końcem października, a inne tytuły nawet jeszcze we wrześniu. Jak będzie z polskimi wersjami książek? Strony polskich wydawców na razie milczą. Na pierwsze tytuły o Rails 3 przyjdzie nam zapewne sporo poczekać.
W końcu
Skończyły się wymówki. Ruby on Rails 3.0 już jest, teraz nic nie stoi na drodze byś zbudował(-a) The Next Big Thing. Na co czekasz?!? gem install rails i do roboty!
więcej...
Aplikacja działająca na dwóch serwerach
Aug 30
Zazwyczaj aplikacje, które piszemy, nie mają użytkowników i zbyt wiele ruchu. Czasem coś jednak pójdzie nie tak, pojawią się użytkownicy i jeden serwer na którym nasza aplikacja działa, przestaje wystarczać. Poniższy przewodnik opowiada jak skonfigurować dwa (lub więcej) serwerów do obsługiwania naszej aplikacji. Może się on przydać, również osobom, chcącym połączyć się z bazą danych umieszczoną na innym serwerze niż aplikacja.
Założenia
- Zakładam, że korzystasz z Capistrano. Jeśli nie, to prawdopodobnie powinieneś.
- Posiadasz Railsową aplikację zdeployowaną na jednym serwerze produkcyjnym.
- Korzystasz z Nginxa, jak serwera WWW z zainstalowanym Phusion Passengerem. (Konfiguracja dla Apacha będzie podobna)
- Jako baza danych wykorzystywany jest MySQL. Instrukcje udostępniania połączenia dla innych rodzajów baz danych mogą odbiegać od tego co jest opisane w tym przewodniku.
- Serwer w tym przypadku to Debian, ale na wszystkich systemach *nixowych powinno to wyglądać podobnie.
Schemat konfiguracji
Master serwer – główny serwer, który będzie przyjmował żądania i je rozdzielał (część przekaże na inne serwery, część obsłuży sam). Na nim znajduje się baza i procesy chodzące w tle (backgroundrb, delayed_job, sphinx itp…)
Slave serwer – drugi serwer, służący wyłącznie jako serwer aplikacji, łączy się z bazą znajdującą się na master serwerze.
Taka konfiguracja umożliwia dodanie więcej niż jednego równoległego slave serwera. W pewnym momencie warto również pomyśleć o przeniesieniu bazy na osobny serwer.
Ustawienia capistrano
Capistrano oparte jest o system ról, z których skorzystamy. Różnym serwerom możemy przypisać różne role, a poszczególnym rolom różne zadania. Dzięki temu możemy sprawić, by zadania związane z bazą i procesy chodzące w tle pracowały tylko na maszynie z rolą: :db. Jeśli mamy już skonfigurowane Capistrano, to powinniśmy do roli :app przypisać adres ip drugiego serwera.
role :app, "master_serwer_ip", "slave_serwer_ip" role :db, "master_serwer_ip"
Zadania, które powinny być wykonywane wyłącznie na serwerze głównym należy oznaczyć :roles => :db.
task :restart, :roles => :db do run "cd #{release_path} && RAILS_ENV=production rake ts:rebuild &"
end
Tworzenie usera na slave
Na slave serwerze należy stworzyć użytkownika z takim samym hasłem i nazwą jak na serwerze master. Załóżmy, że posiadamy użytkownika capistrano z hasłem some_password. Logujemy się jako root na slave serwer:
useradd capistrano -p some_password cd /home mkdir capistrano chown -R capistrano capistrano chrgp -R capistrano capistrano
I mamy stworzonego użytkownika.
Przygotowanie deploya
Należy na serwerze slave stworzyć katalog do którego będziemy robić deploye. Najwygodniej nam będzie, jeśli struktura katalogów będzie identyczna jak na serwerze master. Jeśli nasza aplikacja nazywa się facebook_killer wykonujemy:
mdkir /var/www/facebook_killer chown capistrano /var/www/facebook_killer/ chgrp capistrano /var/www/facebook_killer/
Lokalnie capistrano powinno nam przygotować niezbędne katalogi. Wywołujemy więc
cap production deploy:setup
Robimy deploy i ewentualnie poprawiamy co trzeba (instalujemy gemy, linkujemy katalogi).
cap production deploy
Pliki konfiguracyjne
Pora przygotować pliki konfiguracyjne. Wszystkie pliki oprócz database.yml powinny być identyczne jak na masterze. Należy więc dostosować ich zawartość, po czym zajmujemy się plikiem database.yml:
vim /var/www/facebook_killer/shared/config/database.yml
Ponieważ chcemy się połączyć z bazą na masterze, należy ustawić wszystko tak jak na masterze i dodać dwie linijki w sekcji production (port i adres serwera z bazą danych):
adapter: mysql encoding: utf8 database: facebook_killer_prd username: database_user password: database_password host: master_serwer_ip port: 3306
Odpalamy serwer ręcznie w trybie produkcyjnym, żeby zobaczyć czy działa. (W razie potrzeby należy doinstalować brakujące gemy):
cd /var/www/facebook_killer/current/ RAILS_ENV=production script/server production
Jeśli nie możemy teraz połączyć się bazą danych, znaczy to, że mamy odpowiednio zabezpieczony serwer produkcyjny. Wystarczy teraz tylko udostępnić bazę slave serwerowi. Jeśli jednak połączyliśmy się bez problemów, znaczy, to że zbyt łatwo każda osoba znająca hasło do bazy może się z nią zdalnie połączyć. Wnioski co to znaczy, należy wyciągnąć samemu.
Zdalny dostęp do bazy
Na serwerze z bazą (master serwer) należy ustawić dostęp do bazy danych. Poniższe podpunkt to ekstrakt ze świetnego przewodnika jak umożliwić zdalne łączenie się z serwerem bazy danych (w języku angielskim).
Logujemy się na master serwer i szukamy pliku configuracyjnego MySQL: my.cnf. (W zależności od dystrybucji linuxa może być w w różnych katalogach.)
vim /etc/mysql/my.cnf
Należy zmodyfikować sekcję [mysqld], upewaniając się, że linia skip-networking jest wykomentowana, a bind-address wskazuje na adres master serwera.
[mysqld] user = mysql pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock port = 3306 basedir = /usr datadir = /var/lib/mysql tmpdir = /tmp bind-address = master_serwer_ip # skip-networking
Następnie należy zapisać plik i zrestartować serwer:
/etc/init.d/mysql restart
Kolejną rzeczą jest nadanie uprawnień użytkownikowi bazy danych łączącemu się z slave serwera. Łączymy się z bazą danych:
mysql -u root -p mysql
I nadajemy niezbędne uprawnienia.
GRANT ALL ON facebook_killer_prd.* TO database_user@'slave_server_ip' IDENTIFIED BY 'database_password'; exit
Należy jeszcze otworzyć port 3306. Bezpieczniej jest to zrobić umożliwiając wejścia tylko spod adresu ip slave serwera.
/sbin/iptables -A INPUT -i eth0 -s slave_serwer_ip -p tcp --destination-port 3306 -j ACCEPT service iptables save
Warto teraz odpalić serwer ręcznie i zobaczyć czy poprawnie łączymy się z bazą. Jeśli, występują jakieś problemy, polecam zajrzeć do oryginalnej instrukcji.
Ustawienie load balancingu
Pora na najważniejszą rzecz. Skorzystamy z dyrektywy upstream.
Na master serwerze ustawiamy:
# rozdziela żądania upstream www.facebook_killer.com { server 127.0.0.1:81; server slave_serwer_ip; } # nasłuchuje żądań i je przekazuje server { listen 80; server_name www.facebook_killer.com; location / { proxy_pass http://www.facebook_killer.com; }
} # uruchamia aplikację server { listen 81; server_name www.facebook_killer.com; root /var/www/facebook_killer/current/public; passenger_enabled on;
}
W skrócie chodzi o to, że pierwsza dyrektywa server nasłuchuje i przekazuje żądania do upstream, które rozdziela je między serwer zdalny (slave_serwer) oraz lokalny (zdefiniowany w drugiej dyrektywie) na porcie 81. Warto zapoznać się z dokumentacją dyrektywy upstream.
Przydatna uwaga: Upstream musi się nazywać tak samo jak domena (bez http), inaczej powyższa konfiguracja nie zadziała.
Na drugim serwerze konfiguracja jest dużo prostsza:
server { listen 80: server_name www.facebook_killer.com; root /var/www/facebook_killer/current/public; passenger_enabled on;
}
Czyli tak naprawdę jest to zwykła konfiguracja nginx. Domena facebook_killer powinna wkazywać oczywiście na adres master serwera. Restartujemy nginxa na obu serwerach i gotowe. Możemy szykować się na wykop effect.
Uwagi końcowe
Domyślnie nginx, wysyła przychodzące żądania do serwerwów po równo, lub zgodnie z wagami, które można przypisać poszczególnym serwerom. Rozwiązanie to może spowodować, że jeden z serwerów dostanie za duże obciążenie, w czasie gdy inny będzie wolny. Warto zatem rozważyć użycie Global Queue. Działa ona w ten sposób, że tworzona jest jedna globalna kolejka żądań i są one przekazywane do serwerów dopiero wtedy, gdy serwery są w stanie obsłużyć kolejny request.
Oczywiście to jest najprostsza wersja takiej konfiguracji, która działa. Myślę, że powyższe wskazówki są dobrym punktem wyjścia do uruchomienia aplikacji działającej na kilku serwerach. Dodanie kolejnego serwera, po jego skonfigurowaniu to już tylko dodatkowa linjka w upstream.
więcej...
ArgumentError: marshal data too short when loading session data
Aug 20I was stuck for a while when application maintained by me have started to throw ArgumentError: marshal data too short errors in random places When user have encountered that problem then it was unable to use application at all.

Marshal CC http://www.flickr.com/photos/qmnonic/
Logs were showing that it happens when Rails was trying to create session object. Session store was in ActiveRecord and sessions table was not corrupted.
After watching that for a while it have shown that places in code were this exception was thrown were random but there was pattern. Page visited before was common in each case.
It have turned out that application was storing in session whole ActiveRecord object. Like:
session[:some_info] = @variable
And later we were trying to use that way:
@variable = Model.find session[:some_info]
Due to Rails magic AcitiveRecord’s find when provided with AR object will return that object (of course if is the same model). Code was working well (maybe not very effectively since You should avoid storing large objects in session) until object stored that way started to grow. Application was collecting some data and amount of data stored have grown to that point that after Marshal.dump size of string was more than 64 kB. And this is default size of text field used to store session data in MySQL.
When You try to store too much data in text field in MySQL, excessive data is being truncated, so Marshal.load throws that exception.
To have that error solved is enough to store just object id in session (session[:some_info] = @variable.id).
więcej...
why the lucky stiff
Aug 16
Dawno, dawno temu, za oceanem, Ruby był jeszcze egzotycznym językiem, a większość blogów pisana była po Japońsku. Jednym z pierwszych popularyzatorów tego języka, był człowiek znany wyłącznie pod pseudonimem why the lucky stiff, w skrócie _why. Ewangelizował, programował, pisał, śpiewał, rysował i uczył dzieci programować. Serio.
Większości osób był znany jako autor Why’s (Poignant) Guide to Ruby, najbardziej kolorowego i zabawnego podręcznika do Ruby. W zasadzie było to skrzyżowanie komiksu i książki do nauki programowania. Obrazek obok pochodzi właśnie z Why’s (Poignant) Guide to Ruby. Jeśli jeszcze jej nie widzieliście, to na prawdę polecam choć na chwilę zajrzeć jak ta książka wygląda.
Aby uświadomić sobie w ilu przedsięwzięciach brał udział można zajrzeć na przygotowaną listę spuścizny _why’a. Jest tam kilkadziesiąt projektów Open Source, między innymi hpricot, shoes, czy interaktywna konsola Ruby w przeglądarce, czyli Try Ruby, komiksy, wystąpienia, a nawet dokonania jego zespołu muzycznego. Lista jest naprawdę imponująca.
Rzeczy które robił, były zabawne, zaskakujące i ładne. Nic dziwnego, że zainspirował wielu programistów i wywarł duży wpływ na tworzącę się community. Ale zaraz, zaraz, dlaczego piszemy o nim w czasie przeszłym?
Zniknięcie
19 sierpnia 2009 roku, _why zniknął z sieci. Usunął większość swoich stron, konta na serwisach społecznościowych, a nawet repozytoria na githubie. Wydarzenie to nie przeszło bez echa i pokazało jak bardzo społeczność lubiła _why‘a i jak za nim tęskni.
_whyday
W ten sposób narodziła się idea whyday. Chodzi o to, by 19 sierpnia uczcić wkład why the lucky stiff w nasz ekosytem, poprzez hackowanie dla zabawy. Jeśli masz dość pisania CMS’ów i innowacyjnych serwisów społecznościowych, w czwartek jest okazja, byś poprogramował dla przyjemności. Weź laptop, idź do kawiarni lub parku i pobaw się dowolnym językiem programowania.
W Warszawie takie spotkanie organizuje w czwartek ekipa WRUG’a, szczegóły (dokładne miejsce i czas) są ustalane na forum. Mamy zamiar tego popołudnia stworzyć coś ciekawego.
Jeśli wiecie o _whyday w innych miastach dajcie znać.
Więcej o samym _why przeczytacie w naprawdę obszernym i wyczerpującym artykule, lub skrótowo w podsumowanie na githubie.
Zapraszamy do zabawy.
więcej...
Warte uwagi: Bundler
Aug 03Wraz z nadchodzącymi wielkimi krokami Rails 3 sporo mówi się o jednym konkretnym gemie. Ma ułatwić zarządzanie zależnościami w aplikacjach. Ma uprościć pracę przy tworzeniu i korzystaniu z gemów. Generalnie ma zbawić świat, uzależnić wszystkich developerów od siebie, a następnie odpalić cap deploy:skynet i po sprawie… OK, może bez tego ostatniego.
Warto jednak wiedzieć, że Bundler nie jest jedynie częścią Rails 3. Jest niezależnym, potężnym narzędziem, które można – i warto – używać przy tworzeniu wszelkich aplikacji w Rubym.
Dlaczego?
Wszystko zaczęło się w 428 r. p.n.e… nie wróć to znowu nie to. Budowa aplikacji, choćby najmniejszych, wymaga od programisty korzystania z wielu narzędzi. Dzięki dobrodziejstwom open-source mamy do dyspozycji tysiące bibliotek napisanych przez tysiące innych developerów. Każda z nich to żyjący własnym życiem twór, który w trakcie rozwoju i działania naszego projektu może przechodzić przez multum zmian. Jak zapewnić, że zmiana w zewnętrznym module nie wpłynie negatywnie na działanie naszej aplikacji? Jak zadbać o to, by każdy developer w zespole miał identyczne środowisko? Railsy radziły sobie do tej pory korzystając z wpisów config.gem w pliku environment.rb – możliwość określenia specyficznej wersji i źródła gema daje pewne zabezpieczenie, choć mocno ogranicza.
Ale świat nie kończy się na Rails, rozbudowane zależności mogą mieć wszystkie aplikacje w Rubym. Co więcej, same gemy zależą nierzadko od innych gemów i tu zaczynają się poważne schody. Developerzy wynajdowali patenty na zapewnienie zgodności, m.in. hardkodując je w źródle swoich bibliotek. Takie potworki zamiast sprawę rozwiązywać, tylko ją komplikowały.
Bundler ma na celu wszystkie te problemy rozwiązać.
Jak?
Weźmy na warsztat prosty skrypt z jedną zależnością:
require "rubygems"
require "nokogiri"
require "open-uri" doc = Nokogiri::HTML(open('http://www.google.com/search?q=lukasz+adamczak'))
link = doc.css('h3.r a.l').first
puts link.content
Jeśli w systemie mamy nokogiri, program powinien wypisać:
lans musi być
Jeśli nie, przeczytamy całkiem słuszny komunikat:
no such file to load -- nokogiri
Idąc tradycyjną ścieżką, moglibyśmy teraz zainstalować gem install nokogiri i problem rozwiązany. Pojdźmy jednak The Bundler Way. Instalujemy bundlera (na dzień dzisiejszy aktualna wersja to 1.0.0.rc.2, więc trzeba użyć --pre)
gem install bundler --pre
Otrzymujemy bardzo potężne polecenie bundle, którego możemy użyć już teraz. bundle init utworzy w bieżącym katalogu prosty wzorcowy plik Gemfile, który będzie opisywał wszystkie zależności naszego projektu. Drugi krok to użycie Bundlera w kodzie. Zmieniamy układ require‘ów na:
require "rubygems" require "bundler/setup" require "nokogiri" require "open-uri"
require "bundler/setup" gwarantuje, że projekt będzie wykorzystywał do zarządzania zależnościami właśnie bundlera. W tym momencie cały projekt stał się odizolowaną „bańką”. Całe środowisko jest opisane przez Gemfile. Nie mają znaczenia gemy jakie masz w systemie. Tylko to, co wyraźnie zaznaczysz w Gemfile‘u będzie należało do projektu. Aby się przekonać zainstaluj:
sudo gem install nokogiri
Mogłoby się wydawać, że teraz skrypt wykona się poprawnie. Błąd! Bundler sprawdzi plik Gemfile (domyślnie pusty) i uzna, że nie wie co to nokogiri:
no such file to load -- nokogiri (LoadError)
Dodaj więc do Gemfile linię:
gem "nokogiri"
Teraz skrypt wykonuje się poprawnie. Standardowa kolejność pracy z Bundlerem powinna jednak być odwrotna. Po dodaniu lub zmianie czegokolwiek w Gemfile, pamiętaj o odpaleniu polecenia bundle install. Zajmie się ono instalacją brakujących gemów, w razie potrzeby prosząc nawet o hasło do sudo. Zainstalowane w ten sposób gemy trafiają domyślnie w to samo miejsce gdzie instalowane przez gem install.
bundle ma też kilka innych ciekawych opcji. check do sprawdzenia czy posiadamy wszystko co wymagane.
$ bundle check Could not find gem 'nokogiri (= 1.4.3.1, runtime)' in any of the gem sources.
(a po instalacji)
$ bundle check The Gemfile's dependencies are satisfied
console uruchamia irb z wczytanym kompletnym środowiskiem:
$ bundle console ruby-1.8.7-p249 > Nokogiri => Nokogiri
bundle exec umożliwia uruchomienie wykonywalnego skryptu z gema – z tej konkretnej wersji, którą podaliśmy w Gemfile:
$ bundle exec nokogiri http://rubysfera.pl Your document is stored in @doc... ruby-1.8.7-p249 >
show i open pozwalają znaleźć i otworzyć katalog ze źródłem gema:
$ bundle show Gems included by the bundle: * bundler (1.0.0.rc.2) * nokogiri (1.4.1) $ bundle show nokogiri /Users/Czak/.rvm/gems/ruby-1.8.7-p249/gems/nokogiri-1.4.1 $ bundle open nokogiri # otwiera powyższy katalog w domyślnym edytorze
Aha!
Bundler podczas pracy tworzy automatycznie plik Gemfile.lock na bazie Gemfile oraz stanu Twojego aktualnego środowiska. Jeśli w Gemfile nie określisz wymaganej wersji nokogiri, a w systemie masz zainstalowaną 1.4.1, wtedy do Gemfile.lock trafi:
GEM remote: http://rubygems.org/ specs: nokogiri (1.4.1)
Należy pamiętać aby Gemfile.lock dodać do repozytorium wraz z źródłowym Gemfile. To zagwarantuje, że cały zespół będzie pracował na identycznym środowisku. Jeśli w którymś momencie wymusisz w Gemfile wersję:
gem "nokogiri", "1.4.3.1"
wtedy Gemfile.lock zostanie uaktualniony automatycznie przez bundlera i obie zmiany należy wcommitować. Po każdej zmianie w Gemfile (a właściwie jak często się da, np. po git pull) warto uruchamiać bundle install aby zsynchronizować oba pliki i – w razie potrzeby – uaktualnić lokalne środowisko.
Na koniec
Takim krótkim tekstem ledwo musnąłem powierzchnię Bundlera. Może jednak przekonałem kogoś, że jest to narzędzie warte uwagi i jednocześnie łatwe w użyciu. Warto już teraz wyrobić sobie nawyk wywoływania bundle init zaraz po git init, niezależnie od rodzaju budowanej aplikacji. Bundler może w niedługim czasie wprowadzić trochę ładu do Rubinowego światka, na czym skorzysta każdy developer.
Aby dowiedzieć się więcej:
- http://gembundler.com/v1.0/index.html
- http://github.com/carlhuda/bundler
- http://railscasts.com/episodes/201-bundler
- http://asciicasts.com/episodes/201-bundler
- http://yehudakatz.com/2010/07/26/whats-new-in-bundler-1-0-0-rc-1/
więcej...
Brakujące reguły pluralizacji w Rails
Aug 02Połączenie sił wbudowanego mechanizmu I18n oraz pluginu Globalize daje całkiem duże możliwości do budowania wielojęzycznych aplikacji w Rails. Globalize służy do tłumaczenia danych trzymanych w bazie. Zaś moduł I18n do typowych prac lokalizacyjnych związanych z interfejsem aplikacji (włącznie ze wszystkimi komunikatami wyświetlanymi przez sam framework Ruby on Rails). Jedynym problemem jaki spotkałem jest brak reguł pluralizacji dla języków innych od języka angielskiego.
Jak wiadomo, jęz. angielskim istnieją dwie formy: liczba pojedyńcza i mnoga (1 book, 2 or more books). Ale już w języku polskim – trzy (1 książka, 2,3,4 książki, 5 i więcej książek). To co mnie ostatnio zdziwiło podczas prac z Rails 3 to kompletny brak wbudowanych reguł pluralizacji dla języków innych od angielskiego. Fakt, że Rails mechanizm I18n opiera na gemie i18n, i to w sumie wina tego gemu że posiada zdefiniowaną metodę pluralizacji tylko dla języka angielskiego. Jest też napisane, że można to rozszerzyć o dodatkową logikę, ale nigdzie nie podano szczegółowych informacji o tym jak to zrobić.
Po przebadaniu źródeł (i raczej bezskutecznym poszukiwaniu rozwiązania w internecie) napisalem kod który rozszerza Rails o dodatkowe reguły pluralizacji. Poniższy kod można dodać gdzieś do inicjalizatorów, np. do pliku config/initializers/pluralization.rb. Reguły pluralizacji zostały skopiowane z Gettexta.
# config/initializers/pluralization.rb module I18n::Backend::Pluralization # rules taken from : http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html def pluralize(locale, entry, n) return entry unless entry.is_a?(Hash) && n if n == 0 && entry.has_key?(:zero) key = :zero else key = case locale when :pl # Polish n==1 ? :one : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? :few : :other when :cs, :sk # Czech, Slovak n==1 ? :one : (n>=2 && n<=4) ? :few : :other when lt # Lithuanian n%10==1 && n%100!=11 ? :one : n%10>=2 && (n%100<10 || n%100>=20) ? :few : :other when :lv # Latvian n%10==1 && n%100!=11 ? :one : n != 0 ? :few : :other when :ru, :uk, :sr, :hr # Russian, Ukrainian, Serbian, Croatian n%10==1 && n%100!=11 ? :one : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? :few : :other when :sl # Slovenian n%100==1 ? :one : n%100==2 ? :few : n%100==3 || n%100==4 ? :many : :other when :ro # Romanian n==1 ? :one : (n==0 || (n%100 > 0 && n%100 < 20)) ? :few : :other when :gd # Gaeilge n==1 ? :one : n==2 ? :two : :other; # add another language if you like... else n==1 ? :one : :other # default :en end end raise InvalidPluralizationData.new(entry, n) unless entry.has_key?(key) entry[key] end end I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)
Teraz wystarczy w którymś z plików lokalizacyjnych, np. config/locale/pl.yaml dodać
pl: :book zero: brak książek one: 1 książka few: %{count} książki other: %{count} książek
i wywołać to w szablonie
<% (0..5).each do |i| %> <%= t(:book, :count => i) %>, < % end %>
To samo można uzyskać w konsoli Rails:
tmpl = { :zero => 'brak książek', :one => '1 książka', :few => '%{count} książki', :other => '%{count} książek'
}
I18n.locale = :pl
I18n.backend.store_translations :pl, :book => tmpl
(0..5).each{|i| puts I18n.t(:book, :count => i)}
Wynik:
brak książek 1 książka 2 książki 3 książki 4 książki 5 książekNa koniec jeszcze jedna ciekawostka związana z interpolacją zmiennych. Jest to przykład z konkretnego kodu jaki ostatnio pisałem. Wpierw plik z tłumaczeniem:
# plik config/locales/controllers/search/pl.yml
pl: controllers: search: paging: "wyświetl co %{step} wersetów" Jak widać z komentarza, plik znajduje się głębiej w strukturze katalogu. Aby Rails ładował wszystkie pliki *.yml i *.rb z dowolnego podkatalogu w config/locales, należy do pliku config/application.rb dodać wpis:
# inside class Application < Rails::Application block
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
Sam szablon (używany tu jest format Haml) wygląda następująco:
%div - html = capture do %select#step{:name => 'step'} - [10, 20, 50, 100, 200, 500].each do |step| %option{:value => step}= step %div= raw t(:'controllers.search.paging', :step => html)
Za pomocą funkcji capture cały kod HTM znajdujący się w zasięgu bloku kodu zostaje przypisany do zmiennej i następnie jako parametr do I18n. Jak wiadomo, Rails 3 wyświetlane wartości w szablonach przepuszcza przez znany z Rails 2.x helper h. Koniecznie jest zatem dodanie na początku metody raw aby Rails 3 nie modyfikował HTML’a.
UPDATE
Twórcy gemu i18n wyszli najwyraźniej z założenia, że każdy musi sobie samemu dodać reguły pluralizacji dla pozostałych języków. A zatem krok po kroku, jeszcze raz, tym razem wg zasad twórców.
Jeśli aplikacja ma obsługiwać wiele języków, najwygodniej, jest stworzyć po jednym pliku YAML na język w ramach wydzielonego podkatalogu (wsadzenie wszystkich reguł do jednego pliku nie działa, framework ogólnie nie lubi jak w pliku jest więcej niż jeden główny klucz)
config locales pluralization pl.rb ru.rb ....
Aby wszyskie pliki lokalizacyne były ładowane z dowolnego podkatalogu wewnątrz config/locales/ trzeba dodać do config/application.rb poniższy wpis:
# config/application.rb
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
Reguły lokalizacji dla przykładowych języków:
# config/locales/pluralization/pl.rb
key = lambda{|n| n==1 ? :one : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? :few : :other}
{:pl => {:i18n => {:plural => {:keys => [:one, :few, :other], :rule => key}}}}
# config/locales/pluralization/ru.rb
key = lambda{|n| n%10==1 && n%100!=11 ? :one : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? :few : :other}
{:ru => {:i18n => {:plural => {:keys => [:one, :few, :other], :rule => key}}}}
Na końcu należy załadować moduł gemu i18n który załaduje obsługę dla powyższych plików:
# config/initializers/pluralization.rb I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)...
więcej...
Distributed programming with Ruby
Jul 29Ostatnio piszę o książkach. Właściwie tylko i wyłącznie o książkach. Dziś nie będzie inaczej - kontunuję książkowy temat. Dziś kilka moich spostrzeżeń o książce Distributed Programming with Ruby Marka Batesa. Wspomniana ksiązka wydana jest przez wydawnictwo Addison-Wesley. Wcześniej opisywałem kilka książek z tego wydawnictwa Refactoring. Ruby Edition i Ruby. Wzorce Projektowe Miałem również kontakt z innymi ksiązkami tego wydawnictwa takimi jak The Ruby Way i The Rails Way, które według mnie są na wysokim poziomie. Niestety Distributed Programming with Ruby to najsłabsza książka z wyżej wymienionych. Jeśli tamte mogłem oceniać na 4.5/5 lub nawet 5/5 to ta książka może liczyć na 3/5.
Książka to fajny przekrojowy materiał przez kilka bibliotek, które można wykorzystać. Ale mam wrażenie, że gdyby wyciąć połowę z tych 250 stron to książka nic by nie straciła. Książka ogólnie bardzo pomagała mi w zaśnięciu. Szczególnie druga część, gdzie były omawiane takie biblioteki jak:
- RingyDingy - brzmi jak imie dla pasa, ostatni commit 15.12.2006.
- Starfish - ostatni commit 06.12.2007
- Distribunaut - autorstwa tego samego Pana co cała książka
- Politics - też wielkiej kariery ten gem nie zrobił
Najciekawszą częścią książki było omówienie Starlinga i AMQP.
Jeśli jesteś programistą na dorobku to odpuść sobie - nie warto kupować tej książki za (o zgrozo!) $ 39.99. Jeśli zamawiasz książki do firmowej biblioteczki to można to dorzucić, pewnie i tak będzie w rabacie z czymś lepszym.
...więcej...
Serwer continuous integration w Rubym – Integrity
Jul 28Jeśli zawsze chciałeś mieć serwer Continuous integration, a właśnie teraz masz przerwę na kawę, to świetny moment by go zainstalować i zacząć używać w swoim projekcie. Jeśli nie wiesz dlaczego potrzebujesz serwera Continuous Integration (a potrzebujesz), poczytaj o tym na wikipedii, lub obejrzyj prezentację twórcy Integrity z tegorocznego EuRuKo.
Potrzebne bedą:
- przerwa na kawę
- ruby
- git
- jakiś stary komputer (może być też ten na którym pracujesz i tak, może być nowy)
O Integrity słów kilka
Integrity to zgrabna aplikacja napisana w Sinatrze, pełniąca rolę serwera Continuous Integration. Potrafi ona pobrać kod z repozytorium gita i uruchomić na nim dowolne komendy. Gotowi? Zatem zaczynamy.
Instalacja Integrity
Integrity, do zarządzania zależnościami korzysta z bundlera. Jeśli jeszcze z niego nie korzystaliście, po prosty zainstalujcie go i podążajcie za poleceniami. Pewnie w krótce przyjrzymy się mu bliżej w osobnym poście.
gem install bundler
Integrity najlepiej pobrać z githuba i użyć ostatniej stabilnej wersji (v22):
git clone git://github.com/integrity/integrity git checkout -b deploy v22
Następnie każemy bundlerowi zainstalować wszystykie potrzebne gemy i idziemy wstawić wodę na kawę/herbatę/yerba matę.
bundle install bundle lock
Przygotujemy bazę danych.
rake db
Na chwilę obecną dostaniemy jakieś ostrzeżenia odnośnie datamappera, nie przeszkadzają nam one jednak uruchomić Integrity.
bundle exec rackup
Wystarczy teraz zajrzeć pod adres: http://localhost:9292/ i utworzyć swój pierwszy projekt.
Po podaniu nazwy, adresu repozytorium i komendy do wykonania (spec spec), jesteśmy gotowi przeprowadzić pierwszy build, natykamy się tu jednak na komunikat:
Integrity/.bundle/environment.rb:126:in `gem': rspec is not part of the bundle. Add it to Gemfile. (Gem::LoadError)
Oznacza to, że wszystkie potrzebne gemy musimy dorzucić do bundlera. Otwieramy zatem plik Gemfile i dopisujemy na jego końcu następującą linijkę:
gem "rspec"
Jeśli twoje testy wymagają innych gemów, musisz je również podać w pliku Gemfile. Następnie każemy bundlerowi zainstalować brakujący gem i idziemy zalać wrzątkiem kawę/herbatę/yerba matę.
bundle install --relock
Restartujemy naszą aplikację i odpalamy build jeszcze raz. Wszystkie testy przechodzą, a build nabiera zielonego koloru. Mamy działający serwer continuous integration. Prawda że szybko?
Skąd Integrity wie, że testy przechodzą?
Integrity korzysta z prostej zasady, że jeżeli unixowy program zakończył się bez błędów, to zwracana jest wartość 0, a jeśli błędy wystąpiły, wynik jest od zera różny. Ponieważ większość, jeśli nie wszystkie narzędzia do testowania w Rubym (test unit, rspec, cucumber) są zgodne z tymi założeniami, Integrity „wie”, czy testy znalazły błedy, czy też nie. Dzięki temu możliwe jest dodanie dowolnych narzędzi o ile zwracają one odpowiedni kod na wyjściu.
Co dalej?
Następnymi krokami powinno być uruchomienie Integrity na swoim ulubionym web serwerze i zautomatyzowaniu procesu. Buildy mogą być przygotowywane zarówno cyklicznie (np co godzinę), jak i z wykorzystaniem Post-Receive Hook. Można również rozważyć ustawienie powiadomień na wypadek wystąpienia błędów. Jak to wszystko zrobić opisane zostało na stronie Integrity, ale to już temat do samodzielnego zgłębienia w czasie kolejnej przerwy na kawę.
więcej...
Announcing Coloration, editor color scheme converter
Jul 23Without further ado I'm introducing Coloration - editor/IDE color scheme converter. It is an evolution of tm2jed tool and at the moment it can convert Textmate color themes (in XML plist format) to Vim, JEdit and Kate/KWrite/KDevelop color schemes. So if you are Textmate->Vim convert or you just envy Textmate users for their good looking, dark themes now you have no excuse to not try out Coloration.
Here's how Vim with Sunburst theme looks like:
If you want to give Coloration a try you have two options. Either you can use online version at coloration.sickill.net or you can install ruby gem coloration:
gem install coloration
It will give you tm2vim, tm2jedit and tm2katepart commands. Note it requires ruby 1.9.
Let me know if you find it useful. Source code is available at github.com/sickill/coloration.
Enjoy!
...więcej...




