Active Record a automatické findery
Jako další pěknou vlastnost Active Record vypíchnu automatické findery.
Zděděním modelu (Ruby třídy, která reprezentuje “business object”) od ActiveRecord::Base získáváme automatické findery na všechny atributy, načtené reflexí z databázové tabulky i včetně jejich kombinací.
Příklad:
Stejně jako v minulém příspěvku použijeme třídu Contact:
class Contact < ActiveRecord::Base
#to je vsechno
end
A její tabulku contacts:
CREATE TABLE contacts (
id integer NOT NULL PRIMARY KEY,
name varchar(255),
address varchar(255)
);
Atribut id je default primární klíč (ve třídě jsme tohle nikde neurčili, je to default chování Active Record). Pak můžeme kontakt najít jednoduše dle id:
c = Contact.find(1) #najde kontakt s id 1
Metodu find třídy Contact jsme nikde nenapsali, je zděděna z ActiveRecord::Base. Zajímavé jsou však další “automatické findery” dle atributů tabulky, aniž bychom je kdekoli definovali:
c = Contact.find_by_name('Jirka') #metodu find_by_name jsem nikde nepsal
c = Contact.find_by_address('Valhalla') #tuhle jsem taky nikde nepsal
Fungují dokonce kombinace atributů. Je jedno v jakém pořadí, všimněte si spojky “and” mezi atributy:
c = Contact.find_by_name_and_address('Jirka', 'Valhalla') #nejdriv jmeno, pak adresa
c = Contact.find_by_address_and_name('Valhalla', 'Jirka') #naopak
Tohle je vývojářův sen. Žádné rozhraní, žádné implementace, při změně atributů v tabulce nemusíme findery měnit či připisovat nové. Změníme pouze schéma tabulky a nové findery a jejich kombinace máme okamžitě k dispozici. Samozřejmě bez redeploy aplikace či restartu serveru.
Vypadá to jako magie, ale Active Record využívá Ruby metodu method_missing, která se zavolá vždy, když metoda v objektu neexistuje. V tomto případě si method_missing sáhne do schématu tabulku a za běhu přidá novou metodu dle kombinace atributů.
Protože máme logiku i data pěkně pohromadě, pak se nemusíme rozpomínat či studovat, jaké rozhraní používat pro hledání, jaké pro ukládání, co pro změnu či mazání, apod. Prostě potřebuju pracovat s kontaktem, tak použiju třídu Contact či některou z jejich instancí.
Příklad:
c1 = Contact.new(:name=>'Hradil')
c1.save #ulozime novy kontakt
c1.name = 'Jirka Hradil' #zmenime jmeno
c1.save #ulozime zmeny, provede se UPDATE, protoze zaznam uz byl persistovan a ma id
c2 = Contact.find_by_name('Jirka Hradil') #najdeme si kontakt
c2.delete #smazeme kontakt, provede se DELETE
Zkuste si to a uvidíte, jak neradi se budete vracet k Hibernate :).