Showing posts with label ruby on rails. Show all posts
Showing posts with label ruby on rails. Show all posts

Friday, April 17, 2009

Pluginy Ruby on Rails z których najprawdopodobniej będziesz chciał skorzystać

Na RailsLab w ukazał się ciekawy artykuł, w którym znaleźć możemy zestawienie procentowe projektów wykorzystujących poszczególne wersje Ruby on Rails. Ciekwszym aspektem omawianego artykułu jest jednak zestawienie najczęściej wykorzystywanych pluginów (zrobione w oparciu o dane pochodzące z GitHuba). W pierwszej piątce znajdują się:


  1. exception_notification

  2. restful_authentication

  3. will_paginate

  4. attachment_fu

  5. acts_as_list



W dalszej kolejności znajdują się:


  1. hoptoad_notifer

  2. rspec

  3. acts_as_tree

  4. acts_as_state_machine

  5. auto_complete

  6. paperclip

  7. ssl_requirement

  8. haml

  9. rspec-rails

  10. annotate_models

  11. actice_merchant

  12. acts_as_taggable_on_steroids

  13. asset_packer

  14. calendar_date_select

  15. jrails

  16. in_place_editing

  17. responds_to_parent

Thursday, April 02, 2009

Wykorzystanie Helperów w Kontrolerach i Modelu

Wykorzystanie dostępnych w Railsach Helperów bezpośrednio w modelu, lub kontrolerach nie jest wspierane bezpośrednio przez środowisko. Przyczyn tego faktu należu upatrywać w chęci zapobiegania wykorzystania kodu Widoku w niższych warstwach, dzięki czemu wzrasta przejrzystość całej aplikacji. Niestety w pewnych specyficznych przypadkach bardzo pomocne staje się wykorzystanie bezpośrednio w modelu którejś z metod np. ActionView::Helpers::TextHelper.

Poniżej metoda, jaką możemy zastosować aby wywołać helpery (źródło)

Tworzymy plik np. helpery.rb w katalogu 'lib'


def help
Helper.instance
end

class Helper
include Singleton
include ActionView::Helpers::TextHelper
end


Teraz wystarczy w modelu lub kontrollerze dodać nagłówek:

require 'lib/helpers'


Wywołanie metody helpera sprowadza się wówczas do wywołania:

# help.name_of_helper
help.pluralize 10, "person"

Thursday, December 04, 2008

Wyświetlanie Daty


strftime:

%a - The abbreviated weekday name (``Sun'')
%A - The full weekday name (``Sunday'')
%b - The abbreviated month name (``Jan'')
%B - The full month name (``January'')
%c - The preferred local date and time representation
%d - Day of the month (01..31)
%H - Hour of the day, 24-hour clock (00..23)
%I - Hour of the day, 12-hour clock (01..12)
%j - Day of the year (001..366)
%m - Month of the year (01..12)
%M - Minute of the hour (00..59)
%p - Meridian indicator (``AM'' or ``PM'')
%S - Second of the minute (00..60)
%U - Week number of the current year,
starting with the first Sunday as the first
day of the first week (00..53)
%W - Week number of the current year,
starting with the first Monday as the first
day of the first week (00..53)
%w - Day of the week (Sunday is 0, 0..6)
%x - Preferred representation for the date alone, no time
%X - Preferred representation for the time alone, no date
%y - Year without a century (00..99)
%Y - Year with century
%Z - Time zone name
%% - Literal ``%'' character

t = Time.now
t.strftime("Printed on %m/%d/%Y") #=> "Printed on 04/09/2003"
t.strftime("at %I:%M%p") #=> "at 08:56AM"

Monday, November 03, 2008

RubyGems 1.3.1 na Ubuntu 8.10

Wraz z pojawieniem się nowej wersji Ubuntu 8.10 - nie obeszło się bez problemów. Domyślnie dla tej wersji apt-get instaluje rubygemy w wersji 1.2. Niestety polecenie:


sudo update_rubygems


Wydane po instalacji "gem install rubygems-update" się nie wywołuje.

Oto metoda jak sobie z tym poradzić znaleziona na blogu (http://roninonrails.blogspot.com/2008/11/setting-up-ubuntu-810-for-ruby-on-rails.html):


wget http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz
cd rubygems-1.3.1
ruby setup.rb
ln -s /usr/bin/gem1.8 /usr/bin/gem

Thursday, October 16, 2008

Ruby on Rails pod maską Ogólnopolskiej Informacji Medycznej

Firma Delta Software oficjalnie uruchomiła serwis Ogólnopolskiej Informacji Medycznej. Cały projekt zrealizowany został w technologii Ruby on Rails. Technologia ta umożliwiła wytworzenie produktu, który stać się ma największą konkurencją dla dotychczasowych portali medycznych. W chwili obecnej system działa w podstawowej funkcjonalności, jednakże bardzo szybko uruchamiane są dodatkowe moduły. W ostatecznej wersji serwis wykorzystywać będzie również algorytmy sztucznej inteligencji.

Thursday, July 10, 2008

Instalacja Ruby on Rails 2.1 pod Ubuntu 8.04

Informacje zaczerpnięte są ze strony "Installing Ruby & Rails on Ubuntu 8.04 Hardy Heron", ale jednocześnie starałem się je uzupełnić o rozwiązania problemów na jakie można trafić w trakcie instalacji (dodane elementy starałem się wyróżniać kolorem czerwonym).

Krok 1:
Instalacja pakietów podstawowych


sudo apt-get install ruby irb ri rdoc ruby1.8-dev
libzlib-ruby libyaml-ruby libreadline-ruby libncurses-ruby libcurses-ruby libruby
libruby-extras libfcgi-ruby1.8 build-essential libopenssl-ruby libdbm-ruby libdbi-ruby
libdbd-sqlite3-ruby sqlite3 libsqlite3-dev libsqlite3-ruby libxml-ruby libxml2-dev
ruby rubygems


Krok 2:
Instalacja najnowszej wersji gem


sudo gem update --system


Może się zdażyć, że pojawi się komunikat następującej treści:

uninitialized constant Gem::GemRunner

Rozwiązanie można znaleźć na tej stronie, ale ponieważ jest krótkie pozwoliłem sobie je zamieścić poniżej:

sudo gedit /usr/bin/gem

A następnie przed linią:

require 'rubygems'

Dodajemy:

require 'rubygems/gem_runner'


3. Krok
Dalsza aktualizacja gema:

gem install rubygems-update
sudo update_rubygems


4. Krok
Instalacja railsa

sudo gem install rails


To zainstaluje nam najnowszą wersję railsa. Gdybyśmy chcieli zainstalować również wcześniejszą wersję należy wywołać następującą komendę (zainstaluje ona railsa w wersji 2.0.2)

sudo gem install rails -v 2.0.2


5. Krok
Instalacja bazy danych i narzędzi pomocniczych

sudo apt-get install mysql-server mysql-client libdbd-mysql-ruby mysql-query-browser mysql-admin


6. Krok
Instalacja gema mysql

sudo gem install mysql


W przypadku problemów z isntalacją warto przeczytać tego posta.

6. Krok
Instalacja dodatkowych przydatnych gemow:


sudo gem install mongrel sqlite3-ruby capistrano mini_magick mislav-will_paginate

Tuesday, July 08, 2008

Instalacja gema mysql pod ubuntu

Jeżeli w czasie wywołania:


sudo gem install mysql


Pojawia się następujący błąd:


/usr/bin/ruby1.8 extconf.rb install mysql
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lm... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lz... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lsocket... no
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lnsl... yes
checking for mysql_query() in -lmysqlclient... no
*** extconf.rb failed ***


Należy zainstalować następującą bibliotekę:


sudo apt-get install libmysqlclient15-dev


A następnie wywołać:


sudo gem install mysql

Friday, June 27, 2008

Migracje w rails 2.1

W nowym wydaniu Railsa w wersji 2.1 w nasze ręce oddana została poprawiona funkcjonalność związana z zarządzaniem migracjami. Poprawione zostały dwie rzeczy. Pierwsza dotyczy generowania samych migracji, które przestały być już oznaczane kolejnymi numerami wersji (stanowiło to czasami nie lada wyzwanie kiedy w projekcie kilka osób jednocześnie pracowało z modelem), a druga możliwości określania zmian w tabelach.

Od wersji rails 2.1 migracje numerowane są tak zwanym stemplem czasowym. W praktyce oznacza to, że wygenerowanie migracji za pomocą script/generate migration nowa_migracja spowoduje utworzenie pliku o nazwie zbliżonej do tej: 20080627102918_nowa_migracja.rb

Wcześniej jeżeli dwie osoby jednocześnie wygenerowały migracje trzeba było cofać wprowadzone zmiany (najlepiej przed pobraniem zmian z SVNa - ponieważ tylko wtedy odbywało się to w miarę bezproblemowo), zmieniać nazwy plików i dopiero wówczas migrować model. Teraz proces ten przeprowadzany jest automatycznie. W momencie w którym rake db:migrate stwierdzi, że w katalogu znajdują się migracje z wcześniejszym stemplem czasowym, cofa wszystkie zmiany, a następnie migruje model z uwzględnieniem wcześniejszej migracji.

Druga zmiana w Railsie 2.1 dotyczy sposobu w jaki możemy definiować zmiany tabel przyjrzyjmy się poniższemu fragmentowi kodu:


change_table :news do |:tabela|
tabela.belongs_to :issue # Doda kolumnę issue_id
tabela.string :naglowek, :autor # Doda dwie kolumny typu string
tabela.remove :header, :author #Usunie dwie kolumny
end



Do dyspozycji mamy teraz następujace modyfikatory dostępne w bloku kodu metody change_table:


  1. tabela.index - dodaje indeks

  2. tabela.change - zmienia definicję kolumny np. tabela.change :nazwa, :string, :limit => 200

  3. tabela.change_default - zmienia wartość domyślną dla kolumny

  4. tabela.rename - zmienia nazwę kolumny

  5. tabela.remove - usuwa kolumnę

  6. tabela.remove_belongs_to - usuwa kolumnę z kluczem obcym

  7. tabela.remove_timestams - stemple czasowe

  8. dodatkowo możemy używać (references, string, text, integer, float, decimal, datetime, timestamps, time, date, binary, boolean)

Błąd: no such file to load -- mkmf

W przypadku kiedy chcemy zainstalować Mongrela, a pojawia się nam poniższy błąd:


Building native extensions. This could take a while...
ERROR: Error installing mongrel:
ERROR: Failed to build gem native extension.

/usr/bin/ruby1.8 extconf.rb install mongrel
extconf.rb:1:in `require': no such file to load -- mkmf (LoadError)
from extconf.rb:1



należy zainstalować developerską wersję ruby


sudo apt-get install ruby1.8-dev


To powinno rozwiązać problem.

Problem z zależnościami rubygems w ubuntu 8.04

Korzystając z nowego railsa 2.1 w ubuntu 8.04 możemy natknąć się na następujący problem


/usr/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/rails/plugin/locator.rb:91:in `add': wrong number of arguments (2 for 1) (ArgumentError)
from /usr/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/rails/plugin/locator.rb:91:in `plugins'
from /usr/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/rails/plugin/loader.rb:63:in `locate_plugins'
from /usr/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/rails/plugin/loader.rb:62:in `map'
from /usr/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/rails/plugin/loader.rb:62:in `locate_plugins'
from /usr/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/rails/plugin/loader.rb:27:in `all_plugins'
from /usr/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/rails/plugin/loader.rb:22:in `plugins'
from /usr/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/rails/plugin/loader.rb:45:in `add_plugin_load_paths'
from /usr/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/initializer.rb:235:in `add_plugin_load_paths'


Występuje on w momencie kiedy dodamy do pliku konfiguracyjnego informację wiążącą naszą aplikację z konkretnymi gemami (i to o dziwo nie wszystkimi bo will_paginate działa prawidłowo) np.:


config.gem 'mini_magick', :version => '~>1.2.3'


Googlując za rozwiązaniem udało mi się znaleźć witrynę http://rails.lighthouseapp.com/projects/8994/tickets/293-gem-dependencies-broken-in-2-1-0, gdzie z opisu w kolejnych postach można wywnioskować, że usunięcie pakietu rubygems i zainstalowanie go ze źródeł powinno pomóc. Niestety w moim przypadku nie dało to oczekiwanego rezultatu.

Thursday, June 26, 2008

Ściągawki dla programistów ruby on rails

Natknąłem się dzisiaj na bardzo ciekawy, a zarazem szeroki zbiór krótkich ściągawek dla programistów ruby on railsa.

Warte polecenia:

http://radarek.jogger.pl/2008/03/12/zestaw-ponad-40-sciagawek-dla-programistow-ruby-on-rails-i-n/

Paginacja w rails 2.1 (gem will_paginate)

W railsie 2.0.2 do paginacji można było używać will_paginate instalowanego jako plugin. Po przejściu do railsa 2.1 okazało się, że widoki zawierające paginatory przestały działać. Nie będę opisywał tutaj walki z usunięciem tego błędu - bo skutkowała wieloma mało zrozumiałymi komunikatami.

Aby wykorzystać paginacje w railsie 2.1 należy:


  1. usunąć katalog will_paginate z /vendor/plugins/

  2. do pliku environment.rb dodać nastepującą linijkę:


    config.gem 'mislav-will_paginate',
    :version => '~> 2.3.2',
    :lib => 'will_paginate',
    :source => 'http://gems.github.com'


    Uwaga:na sieci znalazłem kilka wersji sposobu dodania will_paginate do environment.rb - niestety nie wszystie chciały działać!

  3. Na końcu pliku environment.rb należy dopisać (za słowem kluczowym end):


    require 'will_paginate'



  4. wywołać task raka:

    sudo rake gems:install


    Uwaga: instalacja gemów wymaga uprawnień administratora



Teraz możemy cieszyć się prawidłowo działającą aplikacją.

Różnica pomiędzy count, length a size

W ruby on rails mamy do dyspozycji trzy metody określające ilość elementów w kolekcji - length, count, size. Aby zoptymalizować działanie naszej aplikacji warto jednak wiedzieć w jaki sposób działa każda z nich:


  • length (np. obiekt.kolekcja.length) - pobiera całą kolekcje, a nastepnie zwraca liczbę elementów

  • count (np. obiekt.kolekcja.count) - zwraca liczbę elementów nie pobierając kolekcji do pamięci (wykorzystuje SQLową instrukcję count)

  • size (np. obiekt.kolekcja.size) - metoda ta jest połączeniem działania dwóch poprzednich metod. Jeżeli obiekt.kolekcja został wcześniej pobrany to działanie jest podobne do instrukcji length, jeżeli natomiast kolekcja nie została pobrana wywołana zostanie instrukcja count



Mam nadzieję, że to krótkie wyjaśnienie pozwoli zrozumieć różnicę pomiędzy tymi trzema instrukcjami i jednocześnie pozwoli tworzyć bardziej efektywny kod.

Tuesday, June 24, 2008

Bezpieczny to_param

Wykorzystując Ruby on Rails chcielibyśmy tworzyć bardziej przyjazne/opisowe adresy url. Weźmy za przykład biuletyn rynku New Connect. W biuletynie tym znajdują się linki do bieżących lub archiwalnych artykułów. Byłoby lepiej, żeby zamiast wywołania www.ncbiuletyn.pl/czytaj/1, zobaczyć www.ncbiuletyn.pl/czytaj/1-debiuty_sierpniowe.html

Aby osiągnąć taki efekt nie musimy za wiele robić wystarczy nadpisać w klasie modelu metodę to_param. Możemy to zrobić w taki sposób:


def to_param
"#{self.id}-#{self.tytul}"
end


Musimy jednak pamiętać, że nie każdy ciąg znaków będzie prawidłowym adresem url i niektóre artukuły mogą nam się po prostu przestać wyświetlać. Musimy zatem zabezpieczyć przed tym naszą aplikację i usuwać z pola niedozwolone znaki. Możemy to osiągnąć w poniższy sposób:


def to_param
"#{self.id}-#{self.tytul.gsub(/(\W)/,"_")}"
end

Dodanie counter cache do istniejącej tabeli

Dodanie counter cacha do isteniejącej tabeli (dla relacji jeden- wiele) rozpocząć powinniśmy od stworzenia migracji dodającej do tabeli (w obiekcie agregującym) dodatkowej kolumny. Załóżmy w tym przypadku, że w naszej aplikacji posiadamy tabele Miasta oraz tabelę powiaty. Obie są w relacji jeden-do-wielu.


class Miasto < ActiveRecord::Base
validates_presence_of :nazwa, :message => "jest wymagana"
belongs_to :powiat
end

class Powiat < ActiveRecord::Base
validates_presence_of :nazwa, :message => "jest wymagana"
has_many :miasta
end


Uwaga: jeżeli chcesz używać polskich nazw w modelach przeczytaj ten artykuł.

Zanim przejdziemy do modyfikacji tabel zaznaczmy w modelu, że chcemy używać kolumny counter_cache.


class Miasto < ActiveRecord::Base
validates_presence_of :nazwa, :message => "jest wymagana"
belongs_to :powiat, :counter_cache => true;
end


Następnie dodajmy migrację:


script/generate migration powiaty_add_counter_cache


W migracji wpisujemy następujący kod


add_column :powiaty, :miasta_count, :integer, :default => 0
Powiat.find(:all).each do |p|
Powiat.update_counters p.id, :miasta_count => p.miasta.length
end


W tym momencie nasza aplikacja będzie miała prawidłowo ustawione liczniki.

Counter Cache w ruby on rails dla relacji many-to-many

Rails dostarcza wiele cennych narzędzi, które w nieprawdopodobny sposób potrafią przyspieszyć proces tworzenia aplikacji. Jednym z bardzo użytecznych mechanizmów jest wsparcie dla mechanizmu counter cache.

Aby zrozumieć jak działa ten mechanizm wyobraźmy sobie, że mamy relacje typu jeden do wielu. Dla przykładu w każdym powiecie znajduje się wiele miast.



Jeżeli chcielibyśmy w naszej aplikacji wyświetlać przy każdym powiecie informacje o liczbie miast jaka sie w nim znajduje musielibyśmy za każdym razem wywołać:


Select count(*) from miasta where powiat_id = id


Łatwo się zorientować, że jeżeli liczba miast i powiatów jest duża to takie rozwiązanie jest wysoce nieefektywne. Rozwiązaniem staje się wprowadzenie dodatkowej kolumny do tabeli powiaty zawierającą policzoną wcześniej liczbę miast. Teraz pozostaje jedynie pilnowanieaby przy każdym dodaniu lub usunięciu relacji powiat - miasto informacja w dodatkowej kolumnie została zaktualizowana.

W przypadku przedstawionej tutaj relacji sam Rails czuwa nad tym aby liczba ta pozostała prawidłowa wystarczy, że zrobimy następujące kroki:

1.Zdefiniujemy migracje:


class CreateRelacja < ActiveRecord::Migration
def self.up

create_table :miasta do |t|
t.string :nazwa, :limit => 35
t.integer :miasto_id
t.timestamps
end

create_table :powiaty do |t|
t.string :nazwa, :limit => 35
t.integer :miasta_count, :default => 0
t.timestamps
end

end
end

Uwaga: bardzo ważne jest aby kolumna miasta_count zainicjowana została domyślną wartością 0 – w przeciwnym przypadu licznik nie będzie działał (jeżeli chcesz dodać counter_cache w istniejącej tabeli zobacz tego posta (http://mstanek.blogspot.com/2008/06/dodanie-counter-cache-do-istniejcej.html)).

2.W następnym kroku należy zmodyfikować klasy modelu:


class Miasto < ActiveRecord::Base
validates_presence_of :nazwa, :message => "jest wymagana"
belongs_to :powiat,:counter_cache => true;

end
class Powiat < ActiveRecord::Base
validates_presence_of :nazwa, :message => "jest wymagana"
has_many :miasta
end

Takie rozwiązanie jest szybkie i całkowicie wspierane przez Railsa. Niestety wsparcia takiego nie otrzymujemy dla relacji wiele do wiele.

W tym momencie za model posłuży nam baza usług medycznych informacja-madyczna.pl. Występuje tam związek pomiędzy specjalizacjami a lekarzami. Jest wielu lekarzy posiadających daną specjalizację, a każdy z nich może posiadać więcej niż jedną specjalizację. Jest to ewidentnie związek wiele do wiele.



Nasz model wygląda zatem w następujący sposób:


class Lekarz < ActiveRecord::Base
has_and_belongs_to_many :specjalizacje
end

class Specjalizacja < ActiveRecord::Base
has_and_belongs_to_many :lekarze
end



Aby załóżmy zatem, że chcemy mieć informacje dotyczącą ilości lekarzy z daną specjalizacją. W kodzie informację taką można uzyskać w nastepujący sposób:


Specjalizacja.find_by_nazwa('ginekologia').lekarze.length


Niestety wyciągnięcie w ten sposób informacji dla każdej specjalizacji jest bardzo czasochłonne. Dodajmy zatem kolumnę licznika w tabeli specjalizacje, tak abyśmy mogli wyliczoną w powyższy sposób liczbę zachować. Tworzymy zatem migrację a w migracji dodajemy następujący kod:


add_column :specjalizacje, :lekarze_count, :integer, :default => 0
Specjalizacja.find(:all).each do |s|
s.lekarze_count = s.lekarze.length
s.save
end


Proszę zauważyć, że aby zachować jednolity zapis z wbudowanym mechanizmem couter_cache kolumna została nazwana w analogiczny sposób.

W tym momencie posiadamy już informację o liczności każdego związku – jest ona przechowywana w klasie Specjalizacja. Teraz pozostaje najtrudniejsza część, należy mianowicie zadbać o to, żeby w czasie dodawania bądź usuwania obiektów z relacji licznik ulegał zmianie. Należy tutaj zwrócić uwagę, że modyfikaje tego typu możemy wprowadzać po obu stronach relacji. Pokaże to na przykładzie dodania nowej specjalizacji lekarzowi, którą możemy wykonać na dwa sposoby:


lekarz = Lekarz.find_by_imie_and_nazwisko('Jan','Kowalski')
specjalizacja = Specjalizacja.find_by_nazwa('ginekologia')


1. sposób:

lekarz.specjalizacje << specjalizacja
lekarz.save


2. sposób:

specjalizacja.lekarze << lekarz
specjalizacja.save


Z dokumentacji relacji has_many dowiadujemy się, że posiada ona dwa bardzo pomocne nam modyfikatory :before_add oraz :before_remove. Pozwolą nam one na śledzenie zmian, które powinny zostać odzwierciedlone w wartości licznika. Rozszerzmy zatem naszą klasę Lekarz o śledzenie zmian i poprawę licznika:


class Lekarz < ActiveRecord::Base
has_and_belongs_to_many :specjalizacje,
:before_add => :counter_inc,
:before_remove => :counter_dec


def counter_inc(t)
counter_change(t, 1)
end

def counter_dec(t)
counter_change(t, -1)
end

private
def counter_change(object, amount)
object.lekarze_count = object.lekarze_count+amount;
object.save
end
end


A następnie klasę Specjalizacja


class Specjalizacja < ActiveRecord::Base
has_and_belongs_to_many :lekarze,
:before_add => :counter_inc,
:before_remove => :counter_dec


def counter_inc(t)
t.counter_inc(self)
end

def counter_dec(t)
t.counter_dec(self)
end
end


W tym momencie udało nam się stworzyć mechanizm counter_cache dla relacji wiele do wiele (many-to-many). Oczywiście pozostaje nam napisanie odpowiednich testów, ale to zadanie pozostawiam już czytelnikowi.

Monday, June 16, 2008

Poprawione wyrażenie regularne dla e-maila

Ponieważ wyrażenie regularne służące do weryfikacji poprawności e-maila podane w dokumentacji rubiego nie jest doskonałe (zakłada konieczność wprowadzenia e-maila) oraz umożliwia wstawienie w nim znaku '?' poniżej podaje zmodyfikowane wyrażenie nie posiadający w.w. wad:


validates_format_of :email,
:with => /(^([^@\s?]+)@((?:[-_a-z0-9]+\.)+[a-z]{2,})$)|(^$)/i,
:message => "ma błędny format (przykładowy poprawny email przyklad@domena.pl)"


Korzystając z okazji podam również wyrażenie regularne które może służyć do weryfikacji poprawności wpisanego numeru telefonu:


validates_format_of :telefon,
:with => /^[+]{0,1}[-0-9()\s]+$/,
:message => "ma błędny format (przykłady poprawnych: +48 071 123 123, 012-12-332)"

Why ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association arrise?

Tworząc zależności wiele do wiele możemy wykorzystać przełącznik through i zapisać w danej asocjacji dodatkowe informacje np. relacja Czytelnik - Artykul, może zawierać np. ocenę wystawioną danemu artykułowi. Nie wchodząc w szczegóły jak tworzyć takie związki (na sieci znajduje się kilka dość dobrych tutoriali które łątwo wygooglować), czasami napotkać na poniższy wyjątek:


ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association


Aby rozwiązać ten problem należy pamiętać, że klasa będąca złączeniem musi być jawnie dodana do zależności w klasach bazowych. Na przykładzie:


class Lekarz < ActiveRecord::Base
has_many :placowki, :through => :pracownicy
has_many :pracownicy
end

class Placowka < ActiveRecord::Base
has_many :lekarze, :through => :pracownicy
has_many :pracownicy
end

class Pracownik < ActiveRecord::Base
belongs_to :lekarz
belongs_to :placowka
end

Monday, June 09, 2008

Przekazanie bloku kodu do metody helpera

Korzystanie z dobrodziejstwa rubiego jakim jest przekazywanie bloku kodu do metody można wykorzystywać projektując metody w klasach Helperów. Aby wykorzystać ten mechanizm należy jednak zastosować specjalną kontrukcję, którą pokrótce postaram się wyjaśnić.

Załóżmy, że projektowana przez nas witryna składa się z wielu modułów, z których każdy posiada nagłówek oraz treść. Za każdym razem te dwie rzeczy opakowywane są w serię divów z przypisanymi odpowidnimi klasami. Nasze zadanie polegać będzie na tym aby napisać metodę w klasie ApplicationHelper umożliwiającą wstawienie wspomnianego modułu w dowolnym widoku.

Zacznijmy więc od definicji metody:


def modul_tematyczny(tytul, &block)
end


Następnie w widoku umieśćmy następujące wywołanie:


<div ....> .... </div>
<% modul_tematyczny("Wstawiony modul") do %>
<ul>
<% @wiadomosci.each do |wiadomosc|%>
<li> <%= wiadomosc.temat-%> - <%= wiadomosc.updated_at %></li>
<% end %>
</ul>
<% end%>
<div ....> .... </div>


Powróćmy w do helpera i dodajmy odpowiedni kod, tak aby wszystko razem zaczęło działać:


def modul_tematyczny(tytul, &block)
blok = capture(&block)

res = <<HTML
<div class="modul">
<h1 class="tytul">#{tytul}</h1>
<div class='zawartosc'>
#{blok}
</div>
</div>
HTML

concat(res, block.binding)
end


Jak nie trudno zauważyć całą magię stanowią dwa wywołania, które wiążą blok kodu ze zmienną, a następnie zwracają wynik. Zastosowany przykład jest wysoce niedoskonały, ale daje pogląd na możliwości jakie daje nam definiowanie helperów wykorzystujących bloki kodu.

Friday, March 28, 2008

Rails a upload plików z klasą polimorficzną (Attachment_fu)

Polecam zainstalowanie plug-inu Attachment_fu


script/plugin install http://svn.techno-weenie.net/projects/plugins/attachment_fu


Następnie tworzymy model np. File /lub plik - tworząc odpowiednie inflections/, czyli


scrip/generate model Plik


w stworzona migracja powinna wyglądać następująco:


class CreatePliki < ActiveRecord::Migration
def self.up
create_table :pliki do |t|
t.column :filename, :string
t.column :size, :integer
t.column :content_type, :string
t.column :thumbnail, :string
t.column :parent_id, :integer
t.column :height, :integer
t.column :width, :integer

# Obsługa polimorfizmu
t.column :zasob_id, :integer
t.column :zasob_type, :string

t.timestamps
end
end

def self.down
drop_table :pliki
end
end


A nasz plik modelu powinien zawierać następujący wpis:


class Plik < ActiveRecord::Base
belongs_to :zasob, :polymorphic => true # Polimorfizm

has_attachment :content_type => :image,
:storage => :file_system,
:max_size => 2.megabytes,
:resize_to => '300x300>',
:size => 0..2.megabyte,
:thumbnails => {
:small => '50x50>',
:medium => '100x100>',
:big => '200x200>'
},
:processor => :MiniMagick;

validates_as_attachment
end


Zdefiniowane zostały tutaj opcje zapisu zdjęcia uwzględniające tworzenie miniaturek za pomocą biblioteki MiniMagic. Aby ją zainstalować powinniśmy wpisać w konsoli:

sudo gem install mini_magick

Teraz wiążemy nasze zdjęcie z jakąś istniejącą klasą modelu. W naszym przypadku niech będzie nią klasa produkt:


class Produkt < ActiveRecord::Base

has_and_belongs_to_many :kategorie
belongs_to :producent

has_one :plik, :as => :zasob #,:class_name => 'Plik'

validates_presence_of :nazwa, :message => "nie może być pusta"
validates_presence_of :cena, :message => "nie może być pusta"

validates_length_of :nazwa, :minimum=>2, :message=> "musi składać się przynajmniej z dwóch znaków"
validates_numericality_of :cena, :greater_than_or_equal_to => 0, :message => "nie może być ujemna"

end


W następnej kolejności musimy uzupełnić widoki o możliwość dodawania oraz wyświetlania naszego obrazka. W formularzu dla akcji new oraz edit powinniśmy mieć następujący wpis


<% form_for(@produkt, :html => { :multipart => true }) do |f| %>
....
<%= file_field :plik, :uploaded_data %>
....
<% end %>


Musimy również uzupełnić nasz kotroler o zapis załączonego pliku. W akcji update oraz create należy dodać następujący kod:


unless params[:plik].blank?
@produkt.plik = Plik.new(params[:plik])
p.save
end


Wyświetlić nasz załączony plik możemy w następujący sposób:


<%= image_tag(@produkt.plik.public_filename(:big)) if @produkt.plik%>


gdzie :big to nazwa thumbnailsa podana w modelu. Jeżeli nie podamy tego parametru wyświetlony będzie oryginalny obrazek.

Przedstawiłem podstawowe kroki niezbędne do dodania możliwości przechowywania plików związanych z naszymi klasami modelu. Po więcej informacji odsyłam do:

http://www.railslodge.com/plugins/108-attachment-fu
http://clarkware.com/cgi/blosxom/2007/02/24
http://blog.defv.be/2007/12/19/attachment_fu-with-polymorphic-association
http://khamsouk.souvanlasy.com/2007/5/1/ajax-file-uploads-in-rails-using-attachment_fu-and-responds_to_parent