Escolar Documentos
Profissional Documentos
Cultura Documentos
Tutorial Ruby
Tutorial Ruby
Copyright 2006 Ronaldo Melo Ferraz Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1. License.. Permiss/o 0 dada para copiar, distribuir e/ou modificar esse documento sob os termos da Licen1a de Documenta1/o Li"re GNU, Vers/o 1. ou 2ua!2uer outra "ers/o posterior da mesma pub!icada pe!a Free #oft$are Foundation% sem #e13es &n"ariantes, sem )e*tos de (apa Fronta!, e sem )e*tos de 4uarta (apa. Uma c5pia da !icen1a est6 inc!u7da na se1/o cu8o t7tu!o 0 .GNU Free Documentation License.. .9ai!s., .9uby on 9ai!s., e o !ogotipo do 9ai!s s/o marcas registradas de Da"id :einemeier :ansson, com todos os diretos reser"ados. ;s nomes e !ogotipo s/o usados a2ui com permiss/o. )odas as demais marcas registradas s/o propriedade de seus respecti"os donos, com todos os direitos reser"ados. or any !ater "ersion pub!ished by the Free #oft$are Foundation% $ith no &n"ariant #ections, no Front' (o"er )e*ts, and no +ac,'(o"er )e*ts. - copy of the !icense is inc!uded in the section entit!ed .GNU Free Documentation
Contedo
Rails para sua Diverso e Lucro......................................................................................................1 Aqui h rubis................................................................................................................................4 Ps e picaretas.............................................................................................................................5 Comean o a aplicao................................................................................................................! Primeiros passos......................................................................................................................! Con"i#uran o o banco e a os............................................................................................1$ %eran o mi#ra&es e mo elos e a os...............................................................................1' ()C.......................................................................................................................................** + primeiro controller.............................................................................................................*' La,outs..................................................................................................................................*! Roteamento...........................................................................................................................'* -ca""ol in#.............................................................................................................................'4 )ali a&es..............................................................................................................................41 .m se#un o controller..........................................................................................................44 /0ten en o um mo elo e a os..........................................................................................1* .san o helpers......................................................................................................................1' Relacionamentos...................................................................................................................12 3elon#s to..............................................................................................................................!5 4as man,...............................................................................................................................5! 4as one..................................................................................................................................52 4as man,6 throu#h................................................................................................................2$ Avanan o no Rails....................................................................................................................21 7iltros....................................................................................................................................21 .ploa s................................................................................................................................1$! Callbac8s.............................................................................................................................115 Plu#ins.................................................................................................................................112 4as an belon#s to man,....................................................................................................1*' /scopos................................................................................................................................1'4 7iltros aroun ......................................................................................................................141 9(L.....................................................................................................................................145 Autenticao 4::P..............................................................................................................15$ /nvian o e;mails.................................................................................................................155 Depuran o aplica&es.........................................................................................................11$ Associa&es polim<r"icas.....................................................................................................11* (< ulos...............................................................................................................................115
*
A=a0......................................................................................................................................1!$ A#in o como uma lista........................................................................................................125 :rans"orman o um mo elo e a os..................................................................................122 Continuan o com A=a0.........................................................................................................*$4 De#ra ao em A=a0............................................................................................................**5 Avanan o um pouco mais.......................................................................................................**! Roteamento avana o.........................................................................................................**5 Cachin#...............................................................................................................................*'' :ra uo6 .nico e e %lobali>ao......................................................................................*51 -e#urana............................................................................................................................*51 .nit :estin#.........................................................................................................................*1$ ?mplantao.........................................................................................................................*!5 Ambientes e Desenvolvimento...........................................................................................*!5 Ra8e a seu servio...............................................................................................................*!2 )iven o no limite.................................................................................................................*5$ + que "alta...........................................................................................................................*51 Concluso.................................................................................................................................*51 Ap@n ice AA %B. 7ree Documentation License......................................................................*5* $. PR/A(3L/......................................................................................................................*5* 1. APPL?CA3?L?:C ABD D/7?B?:?+B-...............................................................................*5' *. )/R3A:?( C+PC?B%......................................................................................................*54 '. C+PC?B% ?B D.AB:?:C.................................................................................................*54 4. (+D?7?CA:?+B-............................................................................................................*55 5. C+(3?B?B% D+C.(/B:-............................................................................................*5! 1. C+LL/C:?+B- +7 D+C.(/B:-...................................................................................*5! !. A%%R/%A:?+B E?:4 ?BD/P/BD/B: E+RF-.............................................................*5! 5. :RAB-LA:?+B................................................................................................................*55 2. :/R(?BA:?+B................................................................................................................*55 1$. 7.:.R/ R/)?-?+B- +7 :4?- L?C/B-/.....................................................................*55 4oG to use this License "or ,our ocuments.......................................................................*55
'
Aqui h rubis
4 pouco mais e ois anos atrs eu escobria o Rails. Procuran o por um Gi8i para uso pessoal6 eu topei com uma aplicao escrita em Rub,6 uma lin#ua#em pro#ramao queHapesar a minha pai0o pelo assuntoHeu conhecia apenas por meio e
e men&es
espor icas no blo# o um ami#o. A aplicao era o ?nsti8i e eu a instalei minutos epois e terminar e ler suas caracterIsticas6 que me aten iam per"eitamente. (ais tar e6 investi#an o o c< i#o o ?nsti8i6 eu escobri que ele "ora esenvolvi o em um novo "rameGor8
para aplica&es Eeb chama o Rub, on Rails6 ou simplesmente Rails. A espeito e meu interesse anti#o por to o e qualquer "rameGor8 Eeb6 "oi somente ap<s um m@s que eu me eci i veri"icar o que era o Rails. Dois anos epois6 quase to o o meu esenvolvimento Eeb atual J "eito em Rails e eu ain a uso o ?nsti8iH
ironicamente6 a mesma verso que eu bai0ei h tanto tempo atrs. Duan o comecei a usar o Rails6 uma as primeiras coisas que eu "i> "oi elaborar um tutorial para a=u ar as
pessoas que estavam curiosas sobre o assunto a se "amiliari>arem com o ambiente. + tutorial era bem simples e aten ia a um prop<sito bsicoA mostrar a al#uJm que = conhecia pro#ramao Eeb o que era esse novo "rameGor8 quanti a e o qual to o mun o estava "alan o e o que ele realmente po ia "a>er. Des e que os arquivos mais bai0a os o meu servi or e6 =ul#an o pela liberei esse tutorial6 ele continua sen o um cumpri o plenamente. + presente tutorial tem um novo ob=etivoA ir alJm o bsico e mostrar mais o que o Rails J capa> e "a>er6 consi eran o principalmente o que "oi intro u>i o a partir elabora o com base na verso $.2.' e a verso 1.1 o mesmo. + tutorial anterior "oi e es e ento muita coisa mu ouK vrias novas "acili a es "oram
e e;mails que eu ain a recebo sobre o mesmo6 acre ito que o ob=etivo proposto no mesmo "oi
intro u>i as tornan o o Rails ain a mais po eroso e "le0Ivel6 levan o o mesmo a um novo patamar pro utivi a e6 que inclui a possibili a e e aproveitar plenamente os recursos a to "ala a Eeb *.$. .m se#un o ob=etivo J que esse tutorial tambJm possa servir re"er@ncia em um curso avana o. Com base nisso6 o tutorial procura equilibrar uma intro uo completa ao Rails com uma panorLmica t<picos mais elabora os que incluem6 entre outros6 a construo e e0tens&es para o "rameGor86 o uso relacionamentos mais so"istica os e uma apresentao e assuntos relaciona os ao uso e A=a0. Como o tutorial possui esses elaborao bem mais completa ois ob=etivos istintos6 muito o que J a o no comeo e 5 a 11 horas6 tratan o ain a que inicialH os principais recursos e Rails e servin o
e base aos que quiserem utili>;lo com e alavanca para um conhecimento mais
e e
assim6 se voc@ = tem o conhecimento bsico necessrio e material novo e al#uns consi era&es a mais que po em ser relacionamentos entre mo elos e relacionamentos intro u>i os a partir a verso 1.1. Dentro e ambos os ob=etivos6 esse tutorial J volta o para e
ese=a pular o = sabe6 sinta;se M vonta e para o Rails6 al#uma se&es contJm e e al#uma valiaHum e0emplo J a seo sobre e al#uns tipos mais avana os
"a>er isso. /ntretanto6 como muitas coisas mu aram entre as vers&es a os6 que "ala tambJm
ambiente
que o esenvolve or = tenha conhecimento e pelo menos uma lin#ua#em e pro#ramao6 e um banco e a os relacional qualquer e que se=a capa> e estabelecer um ambiente para essa lin#ua#em e banco a os em sua plata"orma e escolha6 ou se=a6 que tenha o conhecimento tJcnico para acompanhar os passos a os no tutorial sem maiores problemas. + tutorial no assume6 porJm6 que o esenvolve or saiba "a>er o mesmo para o Rails e e0plica isso nos etalhes necessrios. .m certo conhecimento inteli#Ivel para qualquer o Rub, J requeri o6 embora a lin#ua#em se=a simples e po erosa o su"iciente ser esenvolve or com uma e0peri@ncia ra>ovel em outra lin#ua#em e alto nIvel.
Apesar isso6 esse tutorial po e no ser to "cil para esenvolve ores que este=am an o os seus primeiros passos em pro#ramao e em especial pro#ramao para a EebHembora eu acre ite esenvolve ores iniciantes possam se bene"iciar muito com o contato com uma lin#ua#em mais po erosa e com um
i"erenas para o Ein oGs sero nota as6 mas as conven&es e mu ar um ou ois etalhes na e0ecuo os
se#uiro o comum ao Linu0. Dito isso6 J per"eitamente possIvel aproveitar o tutorial em qualquer plata"orma o que a necessi a e
4einemeier 4ansson6 = isse em vrias ocasi&es que um os seus ob=etivos ao criar o Rails "oi evolver aos pro#rama ores a iria que ele conse#uiu isso. /nto6 se=a qual "oi o seu prop<sito ao ler esse tutorial6 aproveite bastante.
Ps e picaretas
Como ito acima6 o presente tutorial assume que voc@ tenha "amiliari a e com pro#ramao Eeb em #eral6 e com pelo menos um banco e a os relacional. /u usei o (,-DL na elaborao o tutorial por ele ser um banco e "cil instalao6 mas voc@ po e usar o que mais se a aptar ao seu ambiente6 principalmente a comple0i a e nesssa rea e que ele suporta os consi eran o que o Rails escon e a maior parte principais bancos e a os e0istentes no merca o. Para comear6 vamos assumir que voc@ tenha um banco e a os instala o e que este=a pronto para instalar o Rails e quaisquer outras bibliotecas necessrias. + primeiro passo ento J instalar o Rub,6 a lin#ua#em no
5
qual o mesmo "oi esenvolvi o. + Rub, J uma lin#ua#em e alto nIvel6 to completa quan o uma lin#ua#em po eria ser. -ua sinta0e J bem simples6 e lembra A a e /i""el6 com al#umas pita as voc@ leia o tutorial escrito pelo /ustquio e Perl em al#uns pontos. Como o ob=etivo Ran#el6 isponIvel no en ereo esse tutorial no J e0plicar ao Rub,6 se voc@ acha que precisa e uma intro uo M lin#ua#em6 eu recomen o que N:aDO se#uinteA http://eustaquiorangel.com/files#ruby. + :aD J conheci o e lon#a ata6 e seu tutorial J provavelmente a melhor intro uo ao Rub, em os primeiros livros sobre a
portu#u@s. Aproveitan o para "a>er uma mJ ia6 ele tambJm publicou um http://www.brasport.com.br/index.php?Escolha=8& i!ro= ""#8$. )oltan o M instalao o Rub,6 o processo J bem simples.
lin#ua#em em nosso i ioma. )oc@ po e encontrar mais in"orma&es sobre o livro no se#uinte en ereoA
Bo Ein oGs6 a melhor maneira J usar o Rub, +ne;Clic8 ?nstaller6 cu=a verso atual J a 1.5.5. /sse instala or po e ser bai0a o e http://rubyforge.org/pro%ects/rubyinstaller/ e basta ro ;lo para ter um ambiente Rub, completo para o Ein oGs6 incluin o ocumentao e e itores e te0to. /m istribui&es o Linu0 como o .buntu ou Debian6 com "acili a es para a instalao a pr<pria e aplicativos6 uma epen @ncias
necessrias. .m problema6 entretanto6 J que normalmente a verso o Rub, isponIvel nessas istribui&es J a 1.5.*6 que no ser mais suporta a pelo Rails em breve. + presente tutorial ro a per"eitamente sob essa verso mas "oi basicamente esenvolvi o sob a verso 1.5.4. Assim6 se voc@ est utili>an o o Linu06 eu recomen o que voc@ compile a sua pr<pria verso o Rub,6 se=a a 1.5.4 ou a 1.5.5. A verso 1.5.5 J a estvel atualmente. Para compil;la6 utili>e o proce imento abai0oA
wget ftp://ftp.ruby-lang.org/pub/ruby/ruby-1.8.5.tar.gz tar xvfz ruby-1.8.5.tar.gz d ruby-1.8.5 ./ onfigure --prefix!/u"r ma#e ma#e in"tall
Bo meu caso6 estou usan o a Pltima verso estvel a 1.5.4. .ma ve> que o Rub, este=a instala o6 J hora e "a>er o mesmo com o Rails. + proce imento J bem simplesA
1
e pacotes
o as o
"rente6 utili>aremos novamente o coman o para instalar outros pacotes que "acilitam a vi a esenvolve or Rails. + Pltimo parLmetro acima #arante que to as as instala as simultaneamente sem necessi a e e interveno. epen @ncias
Bovas vers&es o Rails so instala os por um processo similar. -e voc@ = bai0ou al#uma verso al#uma ve>6 use o mesmo coman o acima para atuali>ar a sua principalmente6 eve ser a Pnica usa a em um servi or pPblico. Com os passos acima "inali>a os e um banco esenvolvimento em Rails. e a os isponIvel6 voc@ est pronto para comear o istribuio para a verso mais recente6 lembran o6 e se#urana nas vers&es anteriores e e que a verso 1.1.1 corri#e um sJrio problema
Comeando a aplicao
PRIMEIROS PASSOS
)amos comear a trabalhar em uma aplicao bem simples. Como to o tutorial = pro u>i o na "ace a terra utili>a uma variao e blo#s6 lo=as virtuais6 a#en as6 livros e en ereos6 ou listas e coisas a "a>er6 vamos tentar al#o o %:D6 a meto olo#ia i"erenteA pro u>ir uma aplicao simples Allen. )i e usan o al#uns poucos conceitos e pro utivi a e cria a por Davi
+bviamente6 como o %:D J uma meto olo#ia comple0a em sua totali a e6 vamos nos "ocar somente em al#uns aspectos bsicos a mesma como conte0tos6 pro=etos6 e pr<0imas a&es. ?sso ser mais esses conceitos6 o resulta o "inal no estar6 J claro6 poli o para o uso o que irio. su"iciente para passarmos pela maioria os componentes o Rails. -en o o que estamos crian o meramente uma implementao li#eira Da mesma "orma6 voc@ no precisa enten er os processos vamos utili>ar so su"icientemente auto;e0plicativas. o %:D para se#uir o tutorialHas partes que
Lembre;se e que6 no Linu06 voc@ precisar e permiss&es su"icientes para e0ecutar o coman o acima6 possivelmente lo#an o como superusurioHa menos6 J claro6 que voc@ tenha instalan o o Rub, e o Rails em seu pr<prio iret<rio
home.
!
iret<rio
e trabalho
ronaldo@minerva:~/tmp$ rail" gtd reate reate app/ ontroller" reate app/,elper" reate app/model" reate app/view"/layout" reate onfig/environment" reate omponent" reate db reate do reate lib reate lib/ta"#" reate log reate publi /image" reate publi /-ava" ript" reate publi /"tyle",eet" reate " ript/performan e reate " ript/pro e"" reate te"t/fixture" reate te"t/fun tional reate te"t/integration reate te"t/mo #"/development reate te"t/mo #"/te"t reate te"t/unit reate vendor reate vendor/plugin" ...
+ coman o rails #era o esqueleto completo e uma aplicao6 pronta para ro ar. /sse coman o "oi cria o em seu sistema quan o voc@ instalou as bibliotecas necessrias. Caso voc@ no consi#a ro ;lo no Ein oGs6 J possIvel que as suas variveis iret<rio bin e ambiente no "oram atuali>a asHse=a porque a =anela e coman o em que voc@ est trabalhan o = estava aberta ou por al#um outro motivo qualquer. Besse caso6 #aranta que o a sua instalao Rub, este=a no caminho o Ein oGs6 a icionan o;o M sua varivel +,-*.+6 reinician o qualquer sesso e coman o con"orme o necessrio. Bo esqueleto #era o6 ca a parte a aplicao tem um local especI"ico para ser coloca o. ?sso eriva e uma as "iloso"ias por trs voc@ no precisa o Rails que po e ser escrita pela "rase Nconveno ao invJs e outra "orma6 teriam que se e con"i#uraoO. escritos em um Conveno6 nesse caso6 si#ni"ica que h um acor o entre o "rameGor8 e voc@6 o esenvolve or6 e mo o que e preocupar com certos etalhes que6 arquivo e con"i#urao. /ssa caracterIstica6 inclusive6 #erou uma pia a recorrente na comuni a e se#un o a qual muitas aplica&es Rails completas so menores consi eran o o monte o que um Pnico arquivo e con"i#urao e uma aplicao Qava. / e si#las que voc@ precisa conhecer para criar mesmo uma aplicao pequena em
qualquer "rameGor8 nessa Pltima lin#ua#em6 no J muito i"Icil acre itar que esse realmente se=a o caso. Bo Rails6 basta e0ecutar uma etermina a ao ou criar um etermina o arquivo e as coisas "uncionaro a maneira que voc@ ese=a e espera automaticamente6 sem necessi a e e qualquer trabalho a icional. A princIpio isso po e parecer um pouco restritivo6 mas na prtica "unciona per"eitamente. / sen o
5
e0tremamente "le0Ivel6 o Rails permite que voc@ mu e qualquer coisa que precise6 quan o precisar. Bo vamos entrar em etalhes sobre a estrutura e iret<rios a#ora porque ela "icar evi ente M me i a o tempo6
que avanarmos no tutorial. Bo se preocupeA ela J bem l<#ica e6 na maior parte automaticamente #erencia a pelo Rails. Para "ins o presente tutorial6 vamos se#uir a conveno
precisarmos
esenvolvimento
irio6 porJm6 e0istem ambientes especI"icos para o Rails com "acili a es pr<prias para a
lin#ua#em Rub, e para os etalhes o "rameGor8. 7alaremos um pouco mais sobre isso epois. A#ora que temos uma aplicao bsica6 vamos ro ;la e ver o resulta o. .ma as vanta#ens o Rails J que o ciclo e co i"icar;ro ar;e;testar J bem rpi o. 3asta "a>er uma e recarre#ar processos6 esperar a
alterao para ver ime iatamente o resulta o6 sem a necessi a e compilao que "a>er no Rails J reiniciar o servi or Eeb no caso
a aplicao ou envi;la para um servi or especialmente prepara o. + m0imo que voc@ ter e al#uma mu ana "un amental na con"i#urao
bsica a aplicao6 al#o que ocorre com pouca "reqR@ncia. Para "acilitar a vi a6 o Rails vem com seu pr<prio servi or Eeb6 utili>an o as bibliotecas Para ro ar o servi or6 basta usar um Sve=a o iret<rio script sob o esqueleto a aplicaoT. + coman o6 e0ecuta o entro o iret<rio a aplicao6 J o se#uinte*A o pr<prio Rub,.
ronaldo@minerva:~/tmp/gtd$ " ript/"erver !. /ooting 01/ri #... !. 2ail" appli ation "tarted on ,ttp://'.'.'.':3''' !. 4trl-4 to ",utdown "erver5 all wit, --,elp for option" )&''*-'6-&' 1':31:$'+ 789: 01/ri # 1.3.1 )&''*-'6-&' 1':31:$'+ 789: ruby 1.8.$ %&''5-1&-&$( )i$8*-linux+ )&''*-'6-&' 1':31:$'+ 789: 01/ri #::;<<=>erver?"tart: pid!8&*& port!3'''
Como voc@ po er ver6 o coman o inicia uma instLncia o servi or E/3ric86 capa> e servir aplica&es Rails sem necessi a e e qualquer con"i#urao a icional. + servi or ro a localmente e recebe requisi&es na e receber requisi&es porta '$$$. -en o um servi or simples e embuti o6 o E/3ric8 no J capa> simultLneas6 mas permite que testemos per"eitamente a nossa aplicao. Acessan o o en ereo a aplicao voc@ tem o se#uinteA
Bo Ein oGs6 ser necessrio pre"i0ar o coman o com uma invocao ao rub,6 script/ser!er. ?sso acontece porque6 no Ein oGs6 o script usa o no J a instalao o Rub, tiver si o "eita usan o o N+ne;Clic8 ?nstallerO. 2
que voc@ este=a usan o um ambiente como o C,#Gin. Arquivos com a e0tenso .rb6 porJm6 sero e0ecutveis6 quan o
Como mostra o acima6 temos uma aplicao inicial ro an o que6 inclusive6 mostra quais so os pr<0imos passos para continuar o esenvolvimento a mesma.
1$
/sse arquivo6 removen o os comentrios Sas linhas inicia as com UT6 tem o se#uinte "ormatoA
development: adapter: my"@l databa"e: gtdAdevelopment u"ername: root pa""word: ,o"t: lo al,o"t te"t: adapter: my"@l databa"e: gtdAte"t u"ername: root pa""word: ,o"t: lo al,o"t produ tion: adapter: my"@l databa"e: gtdAprodu tion u"ername: root pa""word: ,o"t: lo al,o"t
A e0tensao .,ml se re"ere ao "ormato o arquivo6 que utili>a uma lin#ua#em e omInio chama a CA(L. Por hora6 basta saber que J uma lin#ua#em e seriali>ao e a os "avoreci a por esenvolve ores Rub, e Rails. )oc@ po e encontrar mais in"orma&es sobre a mesma na ocumentao o"icial o Rub,. .ma aplicao Rails #eralmente utili>a tr@s bancos ambiente e esenvolvimento pa ro. .m banco e a os J utili>a o para o esenvolvimento6 on e to as as mu anas so aplica as. /sse banco e pro uo6 on e mo i"ica&es somente so aplica as uma ve> que e a os6 como emonstra o acima6 um para ca a
este=am completas. + arquivo permite con"i#urar6 inclusive6 um banco remoto para on e suas mo i"ica&es "inais sero re ireciona asHembora #eralmente se=a melhor utili>ar um outro mJto o e implantao6 como veremos mais a iante. + terceiro banco mostra o acima J um banco e testes6 utili>a o pelo Rails para a e0ecuo e unit testin#. /sse banco deve ser manti o necessariamente M parte = que to os os a os e tabelas presentes no mesmo so e0cluI os e recria os a ca a teste completo e"etua o na aplicao. )amos criar o banco a#oraA
ronaldo@minerva:~/tmp/gtd$ my"@l -u root -p 1nter pa""word: 0el ome to t,e By>CD monitor. 4ommand" end wit, 5 or Eg. Four By>CD onne tion id i" 8 to "erver ver"ion: 5.'.&&-GebianA'ubuntu*.'*.&-log <ype H,elp5H or HE,H for ,elp. <ype HE H to my"@l. reate databa"e gtd5 Cuery :IJ 1 row affe ted %'.5* "e ( lear t,e buffer.
11
my"@l. reate databa"e gtdAte"t5 Cuery :IJ 1 row affe ted %'.53 "e ( my"@l. u"e gtd5 Gataba"e ,anged my"@l.
development: adapter: my"@l databa"e: gtd u"ername: root pa""word: ,o"t: lo al,o"t "o #et: /var/run/my"@ld/my"@ld."o # te"t: adapter: my"@l databa"e: gtdAte"t u"ername: root pa""word: ,o"t: lo al,o"t "o #et: /var/run/my"@ld/my"@ld."o # produ tion: development
Bo caso acima6 eu con"i#urei os banco queremos testar uma aplicao nas tambJm acrescentar o mJto o
e pro uo e
= que o esenvolvimento est limita o M minha mquina. /ssa J uma estratJ#ia interessante a usar quan o uas situa&es localmente sem nos preocuparmos em copiar o banco a o que al#uns coman os. /u precisei e transporte soc8et M con"i#urao = que em minha mquina6 to o momentoHmesmo que o Rails permita isso com pouco mais
especi"icamente6 o (,-DL no permite cone0&es e re e em sua con"i#urao pa ro. +utros tipos bancos e a os so con"i#ura os e e maneira similar6 mu an o o parLmetro adapter e e #erar uma arquivo e
quaisquer outras con"i#ura&es necessrias. .ma maneira mais simples con"i#urao especI"ico para o banco esqueleto a aplicao passan o um parLmetro para isso. Por e0emploA
i"erena
e que o arquivo
database.yml ser um pouco i"erente para levar em conta as i"erenas o +racle. )oltan o M nossa aplicao6 como um arquivo e con"i#urao importante "oi mu a o6 o servi or Eeb
precisa ser reinicia o. ?sso acontece porque ele somente l@ essas con"i#ura&es no inIcio e e0ecuo. /sse J um os raros casos em que um servi or e esenvolvimento precisa ser reinicia o = que o Rails recarre#a
1*
praticamente qualquer mo i"icao "eita na aplicao quan o est no mo o e esenvolvimento. A#ora temos uma aplicao e um banco esenvolvimento propriamente ito. Para acesso ao banco e a os6 o Rails usa classes e a os conheci as como mo els6 ou mo elos e a os. /ssas classes mascaram os "cil e intuitiva ao etalhes Ns<r i osO o acesso ao banco e a os provi encian o uma inter"ace e a os para esenvolve or. /m qualquer aplicao6 teremos pelo menos um mo elo e a os con"i#ura o. Com isso = po emos iniciar o processo e
utili>ar na mesma. / a maneira mais "cil e "a>er isso J criar automaticamente a tabela no banco e a os6 utili>an o um os scripts o Rails. + Rails automaticamente enten e que to as as tabelas cria as no banco para uso em mo elos6 ou classes e a os6 satis"aro a uas con i&esA tero um nome plural em in#l@s e tero uma chave primria surro#a a inteira e auto;incrementa a chama a i . V possIvel mu ar ambas as con i&es6 mas inicialmente "icaremos com elas para no ter que mo i"icar o que o Rails #era e usa automaticamente. Para "acilitar o esenvolvimento6 a aplicao esenvolvi a est em in#l@s6 que espero se=a simples o omine plenamente este i ioma. Consi eran o a
o leitor que no
pro"uso e termos em in#l@s em nossa rea acho que isso no ser um problema. + uso o in#l@s evita que tenhamos que pensar em al#uns etalhes e tra uo e #lobali>ao enquanto estamos apren en o o Rails. (ais no "im o tutorial e0plicaremos como mu ar al#umas as coisas que o Rails por como pa ro.
esenvolvimento para o presente tutorial6 mas voc@ po e se#uir em qualquer ireo que achar melhor uma ve> que este=a esenvolven o suas pr<prias aplica&es. ?sso ito6 vamos continuar. Para manter portabili a e e "acilitar o controle e versionamento o banco e a os6 o Rails utili>a al#o e qualquer nature>a no
chama o mi#rations Smi#ra&esT. -o scripts em Rub, que utili>aremos a#ora. A primeira tabela que vamos criar ser a tabela
escreven o mo i"ica&es
banco e a os. (i#ra&es so a maneira mais "cil e acrescentar tabelas e classes e a os ao Rails e J o
ambientes on e uma ao ser e0ecuta a6 como6 por e0emplo6 casa6 escrit<rio6 e;mail6 tele"one6 etc. + nome J bem intuitivo e voc@ po e ler mais sobre o assunto o livro menciona o anteriormente. )amos #erar ento uma mi#rao para essa primeira mo i"icao no bancoA
reateA ontext"
1'
reate
+ script generate J uma as "erramentas cruciais o Rails que utili>aremos ao lon#o e to o nosso tutorial6 sen o usa o6 como o nome in ica6 para criar basicamente qualquer estrutura que precisamos em uma aplicao. + seu primeiro parLmetro J o tipo nome e ob=eto que estamos #eran o6 seu se#un o parLmetro J o e #erao. Bo caso acima6 esse ob=eto e quaisquer outros parLmetro a icionais in icam op&es
estamos #eran o uma mi#rao6 cu=o nome J create0contexts. Bo Rails6 e0istem muitas conven&es quanto ao uso e nomes6 que so se#ui as pelos coman os o mesmo6 com convers&es automticas quan o necessrio. Bo caso acima6 por e0emplo6 o nome que usamos ser automaticamente converti o em um nome alterna a6 como voc@ po er ver abai0o. + resulta o o coman o J um arquivo novo6 pre"i0a o pela verso a mi#rao6 que "oi cria o no iret<rio e classe6 que no Rub, so e0pressas com capitali>ao
db/migrate. /sse pre"i0o J usa o para controlar quais altera&es um banco precisa so"rer para che#ar a uma verso especI"ica. / itan o o arquivo6 temos o se#uinteA
la"" 4reate4ontext" K L tive2e ord::Bigration def "elf.up end def "elf.down end end
eriva a SWT
mJto os6 por estarem pre"i0a os por sel"6 in icam no Rub, que os mesmos po em ser chama os iretamente na classe SmJto os estticos na terminolo#ia instLncia. + mJto o self.up/J utili>a o para e"etuar as mo i"ica&es. + seu oposto6 self.down6 J usa o para es"a>er essas mo i"ica&es caso voc@ este=a reverten o para uma verso anterior o banco. Depen en o a "orma como os mJto os "orem escritos6 eles sero realmente complementares. +bviamente J possIvel que mo i"ica&es se=am e"etua as que no so reversIveis. Besse caso6 o Rails emitir um erro se uma mi#rao para uma verso mais bai0a "or tenta a. )amos e itar o arquivo a#ora para incluir a tabela que ese=amos criarA o QavaT6 sem necessi a e
14
t. olumn :nameJ :"tring end end def "elf.down dropAtable : ontext" end end
opera&es iretamente via -DL no banco6 mas isso no J recomen a o por quebrar essa abstrao. Bo caso acima6 o mJto o create0table que sero a iciona as. 3locos so uma as partes mais interessantes o Rub, e vale a estu ar um pouco o que eles po em "a>er = que o uso os mesmos J e0tenso em qualquer aplicao na lin#ua#em6 incluin o o Rails. Bo Rub,6 os blocos so valores e primeira classe6 que po e ser atribuI os e manipula os como qualquer outro ob=eto. .m outro etalhe a manter em mente J o uso e0tensivo e sImbolos no Rub,. Bo caso acima6 o pr<prio nome a tabela J mesmos. )oltan o ao nosso c< i#o6 como a tabela ter somente uma coluna inicialmente6 que J o nome o conte0to6 enota o por um sImbolo. -Imbolos so similares a strin#s6 com a i"erena "un amental que os so internali>a os e e0istem com uma Pnica instLncia que permite um uso transparente e e"iciente entro e self.up serve para especi"icar a tabela que ser cria a.
/sse mJto o recebe um bloco como parLmetro Sin ica o pela palavra;chave doT que especi"ica as colunas
somente precisamos e uma linha6 i enti"ican o o nome a coluna e seu tipo. (ais M "rente veremos outros tipos e op&es. + mJto o self.down6 como e0plican o6 reali>a a operao inversa estruin o a tabela. A#ora J hora J parte e aplicar essa mi#rao ao banco6 subin o sua verso. Para isso6 temos o utilitrio ra3e6 que o ma3e6 sen o capa> e reali>ar tare"as escritas em um
1a3efile6 a mesma "orma que o ma3e "a> uso e um 2a3efile. + Rails vem com seu pr<prio 1a3efile que permite a e0ecuo e vrias e suas tare"as necessrias6
incluin o db:migrate4 que aplica as mi#ra&es ain a no e"etua as a um banco. Ro amos o coman o a se#uinte "ormaA
ronaldo@minerva:~/tmp/gtd$ ra#e db:migrate %in /,ome/ronaldo/tmp/gtd( !! 4reate4ontext": migrating !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -- reateAtable%: ontext"( -. 1.&'86" !! 4reate4ontext": migrated %1.&'6&"( !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
15
ronaldo@minerva:~/tmp/gtd$ my"@l -u root -p gtd my"@l. ",ow table"5 N---------------N M <able"AinAgtd M N---------------N M ontext" M M " ,emaAinfo M N---------------N & row" in "et %'.'' "e (
Duas tabelas "oram cria as6 conte0ts e schemaYin"o. A primeira J a que especi"icamos. A se#un a in"orma&es o banco para o Rails6 para controlar o versionamento o mesmo. A tabela conte0ts "icou assimA
escreve
ronaldo@minerva:~/tmp/gtd$ my"@l -u root -p my"@l. de" ribe ontext"5 N-------N--------------N------N-----N---------N----------------N M 9ield M <ype M 8ull M Iey M Gefault M 1xtra M N-------N--------------N------N-----N---------N----------------N M id M int%11( M 8: M =27 M 8ODD M autoAin rement M M name M var ,ar%&55( M F1> M M 8ODD M M N-------N--------------N------N-----N---------N----------------N & row" in "et %'.'' "e (
Como voc@ po e ver6 no J preciso especi"icar a chave primria surro#a a6 que J automaticamente cria a pelo Rails. A tabela schemaYin"o possui os se#uintes a osA
ronaldo@minerva:~/tmp/gtd$ my"@l -u root -p gtd my"@l. "ele t P from " ,emaAinfo5 N---------N M ver"ion M N---------N M 1 M N---------N 1 row in "et %'.'' "e (
/sse nPmero e verso ser incrementa o ou ecrementa o epen en o a ireo a mi#rao. A mi#rao tambJm criou o arquivo db/schema.rb6 conten o uma representao po e ser usa a para recriar a estrutura outra tare"a o ra3e. + arquivo J auto;#era o e no eve ser mo i"ica o manualmente. -eu "ormato po e ser visto abai0oA o banco em Rub,6 que
11
? <,i" file i" autogenerated. 7n"tead of editing t,i" fileJ plea"e u"e t,e ? migration" feature of L tive2e ord to in rementally modify your databa"eJ and ? t,en regenerate t,i" " ,ema definition. L tive2e ord::> ,ema.define%:ver"ion !. 1( do reateAtable Q ontext"QJ :for e !. true do MtM t. olumn QnameQJ :"tring end end
Bote apenas que essa representao J limita a Ms tabelas em si e no inclui chaves estran#eiras6 tri##ers ou
ronaldo@minerva:~/tmp/gtd$ " ript/generate model ontext exi"t" app/model"/ exi"t" te"t/unit/ exi"t" te"t/fixture"/ reate app/model"/ ontext.rb reate te"t/unit/ ontextAte"t.rb reate te"t/fixture"/ ontext".yml exi"t" db/migrate Lnot,er migration i" already named reateA ontext": db/migrate/''1A reateA ontext".rb
+ Rails utili>a uma estratJ#ia e esenvolvimento Sconheci as como patterns e maneira #eralT chama a e ()C S(o el;)ieG;ControllerT. /ssa estratJ#ia separa os componentes a aplicao em partes istintas que no s< "acilitam o esenvolvimento como tambJm "acilitam a manuteno. + que estamos ven o no momento so os mo els6 que correspon em M cama a a os6 implementa a no Rails por um componente estrutura e acesso ao banco e
e suporte ao mo elo6 incluin o testes e mi#ra&es para o mesmo. Como #eramos a nossa
mi#rao inicial manualmente6 o Rails nos in"orma que = e0iste uma com o mesmo nome que ele preten ia #erar e que ele est pulan o esse passo. + arquivo responsvel pela implementao se#uinteA o mo elo est em app/model/context.rb e contJm apenas o
/ssas
uas linhas6 apoia as pelo Rails6 = provi enciam uma rique>a a os no banco6 e e0ecutar uma sJrie
1!
)amos criar a#ora um novo mo elo correspon en o M tabela mi#rao essa ve>. + coman o JA
e pro=etos. )amos
ronaldo@minerva:~/tmp/gtd$ " ript/generate model pro-e t exi"t" app/model"/ exi"t" te"t/unit/ exi"t" te"t/fixture"/ reate app/model"/pro-e t.rb reate te"t/unit/pro-e tAte"t.rb reate te"t/fixture"/pro-e t".yml exi"t" db/migrate reate db/migrate/''&A reateApro-e t".rb
la"" 4reate=ro-e t" K L tive2e ord::Bigration def "elf.up reateAtable :pro-e t" do MtM ? t. olumn :nameJ :"tring end end def "elf.down dropAtable :pro-e t" end end
la"" 4reate=ro-e t" K L tive2e ord::Bigration def "elf.up reateAtable :pro-e t" do MtM t. olumn :nameJ :"tring t. olumn :de" riptionJ :text end end def "elf.down dropAtable :pro-e t" end end
)oc@ no precisa se preocupar em criar to os os campos inicialmente. )oc@ po e sempre usar outra mi#rao para isso. Ro an o o coman o ra3e para carre#ar as mi#ra&es mais uma ve>6 temos o se#uinteA
ronaldo@minerva:~/tmp/gtd$ ra#e db:migrate %in /,ome/ronaldo/tmp/gtd( !! 4reate=ro-e t": migrating !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -- reateAtable%:pro-e t"( -. '.18*3" !! 4reate=ro-e t": migrated %'.18*5"( !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
15
ronaldo@minerva:~/tmp/gtd$ my"@l -u root -p gtd my"@l. ",ow table"5 N---------------N M <able"AinAgtd M N---------------N M ontext" M M pro-e t" M M " ,emaAinfo M N---------------N 3 row" in "et %'.'' "e ( my"@l. de" ribe pro-e t"5 N-------------N--------------N------N-----N---------N----------------N M 9ield M <ype M 8ull M Iey M Gefault M 1xtra M N-------------N--------------N------N-----N---------N----------------N M id M int%11( M 8: M =27 M 8ODD M autoAin rement M M name M var ,ar%&55( M F1> M M 8ODD M M M de" ription M text M F1> M M 8ODD M M N-------------N--------------N------N-----N---------N----------------N 3 row" in "et %'.'' "e ( my"@l. "ele t P from " ,emaAinfo5 N---------N M ver"ion M N---------N M & M N---------N 1 row in "et %'.'' "e (
? <,i" file i" autogenerated. 7n"tead of editing t,i" fileJ plea"e u"e t,e ? migration" feature of L tive2e ord to in rementally modify your databa"eJ and ? t,en regenerate t,i" " ,ema definition. L tive2e ord::> ,ema.define%:ver"ion !. &( do reateAtable Q ontext"QJ :for e !. true do MtM t. olumn QnameQJ :"tring end reateAtable Qpro-e t"QJ :for e !. true do MtM t. olumn QnameQJ :"tring t. olumn Qde" riptionQJ :text end end
ronaldo@minerva:~/tmp/gtd$ " ript/generate model a tion exi"t" app/model"/ exi"t" te"t/unit/ exi"t" te"t/fixture"/ reate app/model"/a tion.rb reate te"t/unit/a tionAte"t.rb reate te"t/fixture"/a tion".yml exi"t" db/migrate reate db/migrate/''3A reateAa tion".rb
12
la"" 4reateL tion" K L tive2e ord::Bigration def "elf.up reateAtable :a tion" do MtM t. olumn :de" riptionJ :"tring t. olumn :doneJ :boolean t. olumn : reatedAatJ :datetime t. olumn : ompletedAatJ :datetime t. olumn : ontextAidJ :integer t. olumn :pro-e tAidJ :integer end end def "elf.down dropAtable :a tion" end end
Com isso6 = temos a#ora o su"iciente em nosso banco aplicao. .m recurso muito Ptil
o Rails J o console6 que serve tanto para e0perimentar com as classes e outras "un&es Pteis e teste e manuteno
a os
A e0emplo
e um console
e coman o
o D+- ou
.. 4ontext. reate%:name !. H@;omeH( !. ?K4ontext:'xbR$fbb3 @newAre ord!fal"eJ @error"!?KL tive2e ord::1rror":'xbR$ f**8 @ba"e!?K4ontext:'xbR$fbb3 ....J @error"!ST.J @attribute"!SQnameQ!.Q@;omeQJ QidQ!.1T.
+ coman o acima cria e salva um novo conte0to6 receben o como parLmetro o nome
o mesmo6
escrito
por seu Pnico atributo. + resulta o6 visto lo#o epois o coman o6 J uma representao te0tual a classe. :o os ob=etos em Rub,6 e isso inclui representao te0tual. Ba maior parte es e nPmeros inteiros a classe comple0as6 possuem uma
classes que no especi"icam a sua pr<pria representao e J isso o que voc@ est ven o acima. /m uma aplicao6 voc@ provavelmente precisar criar um ob=eto e manipul;lo antes e salv;lo. ?sso
*$
.. ontext ! 4ontext.new !. ?K4ontext:'xbRR81bd' @newAre ord!trueJ @attribute"!SQnameQ!.nilT. .. ontext.name ! H@0or#H !. Q@0or#Q .. ontext."ave !. true
Bo e0emplo acima6 o ob=eto J cria o6 uma e suas proprie a e J atribuI a e epois isso o ob=eto J salvo. A persist@ncia e a os s< ocorre na invocao o mJto o sa!e. +utras opera&es so possIveisA
.. 4ontext.find%:all( !. )?K4ontext:'xbR*eR*d$ @attribute"!SQnameQ!.Q@;omeQJ QidQ!.Q1QT.J ?K4ontext:'xbR*eR*68 @attribute"!SQnameQ!.Q@0or#QJ QidQ!.Q&QT.+ .. 4ontext.find%&( !. ?K4ontext:'xbR*e&3&8 @attribute"!SQnameQ!.Q@0or#QJ QidQ!.Q&QT. .. 4ontext.find%:fir"tJ : ondition" !. )Hname li#e UHJ H@;omeH+( !. ?K4ontext:'xbR*d*b @attribute"!SQnameQ!.Q@;omeQJ QidQ!.Q1QT.
A primeira chama a acima retorna to os os re#istros correspon ente aos conte0tos. )emos que o resulta o J um arra,6 elimita o por 56. A se#un a chama a6 por sua ve>6 retorna o ob=eto especi"ica o pelo i passa o6 ou se=a6 por sua chave primria surro#a a. + terceiro mJto o6 por "im6 retorna o primeiro re#istro satis"a>en o as con i&es passa as. + mJto o find possui vrios outros parLmetros que servem para #erar queries comple0as sem es"oro6 or enan o o resulta o6 limitan o a quanti a e e itens retorna os6 e6 para usos mais avana os6 crian o
elas e0ploraremos
menos uma elasA mJto os e busca e criao automticos. Por e0emplo6 J possIvel e0ecutar o coman o abai0o para encontrar um conte0to pelo seu nomeA
+ mJto o find0by0name no e0istia atJ ser chama o. ?sso po e ser visto veri"ican o a e0ist@ncia o mJto o6 usan o respond0to?6 que J um mJto o presente em to as as classes Rub, e6 ao receber um sImbolo que especi"ica o nome o mJto o a ser testa o6 evolve uma valor in ican o se a classe suporta o mJto o ou
*1
noA
Como J "cil perceber6 a classe no possui aquele mJto o atJ que ele se=a invoca o. .ma classe Rails aceita combina&es quaisquer mJto os. e seus atributos e
a os e
.m e0emplo J find0or0create0by0name6 que procuraria um re#istro e o criaria caso no "osse encontra o6 com o nome passa o como parLmetro. -e a classe possuir mais atributos6 po erIamos muito bem usar al#o como find0or0create0by0name0and0description ou ain a find0by0description0and0completed0at. V claro que se usarmos isso para to as as chama as6 teremos mJto os com nomes absur amente lon#os como find0or0create0by0description0and0created0at0and0completed0at0and0done /que "icariam bem melhor como chama as separa as usan o con i&es por quest&es e le#ibili a e. + que estamos "a>en o aqui no console J basicamente o que "aremos em uma aplicao no que tan#e M manipulao e a os. Como voc@ po e ver6 na maior parte os casos6 o Rails no e0i#e que o as cenas6 com to a e"ici@ncia o mJto o esenvolve or escreva c< i#o -DL6 #eran o o c< i#o necessrio por trs possIvel. / caso se=a necessrio6 h ain a find6 que permitem a insero e "ra#mentos
como find0by0sql que permitem um e0ecuo ireta no banco com inte#rao plena com o Rails. .sar as "acili a es o Rails no si#ni"ica que o esenvolve or no precisa conhecer -DLK ao contrrio6 o o pr<prio Rails como para to as outras
conhecimento J "un amental6 tanto para evitar problemas no uso tare"as que e0istem alJm o Rails.
A#ora que = trabalhamos um pouco com o mo elo e a os6 vamos passar para a aplicao em si.
MVC
Como menciona o anteriormente6 o Rails utili>a o a estratJ#ia ()C. A parte correspon ente ao (o el e0iste nas classes e a os e J implementa a pelo ActiveRecor . .m se#un o componente o Rails6 o
**
4:(L6 ima#ens6 9(L6 ou qualquer outra saI a aceitvel a aplicao. As tr@s partes a estratJ#ia ()C so in epen entes no Rails como everiam ser em qualquer boa
scripts que no tem a ver com a aplicao Eeb em si6 como6 por e0emplo6 tare"as a#en a as. +s controllers
tambJm so in epen entes e po em ser usa o para simplesmente e"etuar a&es que nem mesmo retornam
muito senti o sem utili>arem a os #era os por mo elos e a os e controllers. Para i enti"icar qual controller ser responsvel por aten er uma Rails utili>a uma mecanismo e roteamento etermina a solicitao a aplicao6 o e con"i#ura&es
comple0as6 mas que po e tambJm ser customi>a o e acor o com as necessi a es e ca a aplicao. A re#ra principal entro a classe e i e roteamento no Rails "orma .RLs se#un o o pa ro /controller/action/id6 on e J um parLmetro opcional passa o para i enti"icar um ob=eto qualquer sobre o qual a ito anteriormente6 esse pa ro po e ser mo i"ica o e veremos mais sobre isso
controller J a classe responsvel por aten er a requisio especi"ica a por aquela .RL6 action J um mJto o
ao ser e"etua a. Como a iante no tutorial.
O PRIMEIRO CONTROLLER
)amos comear crian o um controller para li ar com a p#ina inicial e nossa aplicao. Para #erar um
ronaldo@minerva:~/tmp/gtd$ " ript/generate ontroller ,ome exi"t" app/ ontroller"/ exi"t" app/,elper"/ reate app/view"/,ome exi"t" te"t/fun tional/ reate app/ ontroller"/,omeA ontroller.rb reate te"t/fun tional/,omeA ontrollerAte"t.rb reate app/,elper"/,omeA,elper.rb
+ nome passa o para o coman o J home6 que ser usa o para compor to os os arquivos #era os pelo mesmo. Da mesma "orma que nos mo elos e a os6 o Rails cria unit tests para a classe #era a6 que po em ser
e0ecuta os com o coman o ra3e/tests. /sse coman o ro a to os os unit tests presentes na aplicao e qualquer boa aplicao esenvolvi a se#un o a meto olo#ia /0treme Pro#rammin# "a> uso e0tensivo essa tJcnica. )eremos esse assunto em mais etalhes em uma outra seo e nosso tutorial. -e voc@ acessar a .RL esse controller a#ora6 voc@ vai receber a se#uinte telaA
*'
Repare que6 nesse caso6 eu in"ormei somente /controller/ como a .RL6 sem passar nem a parte correspon ente a uma ao ou a um i . Besse caso6 o Rails assume que estamos invocan o a ao in e0 sem i . /sse J um mais e0emplo e conveno ao invJs e con"i#urao. Ao invJs e ter sempre que especi"icar uma ao pa ro em al#um lu#ar6 o Rails convenciona que a ao pa ro J in e06 poupan o o esenvolve or sem a"etar a aplicao. )amos olhar o arquivo app/controllers/home0controller.rb #era o por nosso coman o. Como = mencionamos6 o Rails se#ue uma estrutura "i0a Arquivos relaciona os ao banco vo em no relaciona o a ()C vai em app. Dentro e iret<rios6 poupan o mais uma ve> o esenvolve or. iret<rio db6 arquivos e con"i#urao em config e tu o iret<rios que contJm mais partes
especI"icas a aplicao. /m al#uns casos6 o Rails tambJm a iciona um su"i0o ao arquivo6 evitan o colis&es e nomes e problemas estranhos na aplicao6 como J o caso aqui. + arquivo o controller cria o contJm o se#uinteA
A classe .ome7ontroller
e"ine quem que ir respon er a requisi&es pa ro em Zhome. /la her a e"ini a no arquivo application.rb6 no mesmo
"amiliar para trabalhar sua aplicao6 um mo elo que J ao mesmo tempo simples e po eroso.
*4
+ princIpio que usamos acima J comum a tu o em Rails. 3asicamente tu o o que "a>emos em uma aplicao usan o o mesmo consiste em e0ten er al#uma classe por meio a a io e mJto os customi>a os. Recarre#an o a p#ina no nave#a or6 "icamos com o se#uinteA
)amos que6
essa ve>6 o Rails i enti"icou a ao a ser e0ecuta a6 mas6 como a aplicao no especi"icou
nenhum retorno6 houve um erro. ?sso aconteceu porque o Rails tentou aplicar automaticamente uma vieG para aquela ao o controller. Como a vieG ain a no e0iste6 temos o erro. Antes e nos aventurarmos em uma vieG usan o o mecanismo e templates o Rails6 vamos retornar al#um te0to por contra pr<priaA
la"" ;ome4ontroller K Lppli ation4ontroller def index render :text !. Q:lVWQ end end
isponIvel para qualquer controller ou vieG6 #era saI a na aplicao6 saI a esta que
e seus parLmetros. Bo caso acima6 estamos ren eri>an o te0to puro6 como in ica o pelo
*5
:emos a nossa primeira saI a em uma aplicao Rails. Como escrever manualmente to a a saI a no J o que queremos6 vamos criar a nossa primeira vieG. Para isso6 criamos o arquivo app/!iews/home/index.rhtml. A e0tenso .rhtml in ica que esse arquivo contJm 4:(L e c< i#o Rub,. .ma outra e0tenso possIvel seria .r=s para #erar retorno em Qava-cript6 comumente utili>a o em aplica&es A=a0. 4 ain a .r0ml6 para #erar saI a em 9(L. +utros mecanismos e template po em usar outras e0tens&es6 = que o Rails po e ser con"i#ura o para outros mecanismos alternativos. Por hora6 usaremos a lin#ua#em e templates pa ro o Rails. )amos criar o se#uinte arquivo6 entoA
Kp.:lVJ mundoWK/p.
-e recarre#amos a p#ina6 notaremos que na a mu ou. ?sso acontece porque estamos usan o render/ iretamente. Besse caso6 o Rails etecta que al#uma saI a = "oi #era a e no tenta #erar outra. 3asta6 ento6 remover a chama a a render/mJto o index6 voltan o o arquivo aA
*1
end
e especi"icar qualquer coisa6 o Rails est usan o o arquivo a equa o. )e=a que e"inimos um mJto o no mesmo6 e criamos um esses a
no con"i#uramos qualquer coisa. Criamos um controller6 arquivos representa uma ca eia aplicao "a> isso ou aquilo. Dualquer outra ao que "osse cria a seria associa o a um nome e arquivo entro entro
arquivo que contJm o que queremos que se=a retorna o por aquele mJto o. A simples e0ist@ncia
esse controller se#uiria o mesmo pa ro. + nome e um iret<rio em app/!iews cu=o nome seria o
a ao
o pr<prio
controller. + Rails tambJm J inteli#ente ao ponto e respon er a uma ao mesmo que o mJto o no e0ista6
es e que a vieG este=a presente no iret<rio correto.
LAYOUTS
Para "acilitar o esenvolvimento a parte visual e uma aplicao6 o Rails possui um conceito enomina o
la,outs.
Ba maioria as aplica&es Eeb6 as p#inas variam somente no seu conteP o principal6 possuin o e nave#ao em comum. +bviamente6 um "rameGor8 cu=o maior ob=etivo J esenvolve or no e0i#iria que o c< i#o para esses elementos tivesse que ser o
repeti o em ca a vieG. .m la,out "unciona como um arquivo rai>6 entro o qual o resulta o e uma vieG J
que J usa o automaticamente por qualquer vieG a no ser que ha=a especi"icao em contrrio. + Rails tambJm J inteli#ente o bastante para somente usar um la,out em vieGs com a mesma e0tenso. + la,out pa ro para a aplicao "ica em um arquivo chama o application.rhtml6 app/!iews/layouts6 que no e0iste ain a. -e voc@ olhar a#ora o c< i#o #era o pela p#ina que criamos atJ o momento6 voc@ ver o se#uinteA entro o iret<rio
Como voc@ po e notar6 o estamos #eran o nenhum os elementos 4:(L #eralmente vistos em uma p#ina comum. )amos criar o arquivo application.rhtml no iret<rio especi"ica o6 com o se#uinte conteP oA
e uso
entro
e uma vieG6
elimita o pelos
marca ores 8+ e +9. Aqueles "amiliari>a os com P4P e A-P reconhecero o estilo e marca ores6 com o uso
Bo caso acima6 o mJto o especial yield retorna o conteP o atual #era o pela ao6 se=a por meio
e uma
iretamente. + mJto o yield tem uma conotao especial no Rub,6 servin o para
*5
invocar o bloco associa o ao conte0to. ?nseri o em um la,out com seu conseqRente retorno e conteP o. A nossa p#ina recarre#a a a#ora "ica como mostra o abai0oA
o Rails6 o bloco
e"ine a e0ecuo
a ao6
Bo parece muita coisa6 mas6 olhan o o c< i#o6 voc@ ver o se#uinteA
+ que precisamos "a>er a#ora J e0ten er o nosso la,out para incluir al#umas ameni a es. + nosso arquivo application.rhtml po eria ser mu a o para o se#uinteA
*2
K,tml. K,ead. Ktitle.X<GK/title. KY! "tyle",eetAlin#Atag QdefaultQ Y. K/,ead. Kbody. KY! yield Y. K/body. K/,tml.
+ mJto o stylesheet0lin30tag/recebe o nome mesma. + nosso c< i#o #era o a#ora "icou assimA
Bote que mais uma ve> o Rails assumiu um caminho pa ro. Besse caso6 o arquivo J servi o iretamente a rai> a aplicao6 que J o iret<rio public. )oc@ po e criar um arquivo chama o default.css no iret<rio public/stylesheets para a icion;lo M aplicao. .m e0emplo isso seriaA
.ma coisa a manter em mente J que o la,out pa ro a aplicao no J6 e "orma al#uma6 o Pnico que po e ser #era o. )oc@ po e criar tantos la,outs quanto precisar6 colocan o;os no mesmo estaro acessIveis a to a aplicao. Para usar um la,out i"erente em um controler voc@ po eria "a>er al#o assimA iret<rio6 e on e
'$
la"" ;ome4ontroller K Lppli ation4ontroller layout Q,omeQ def index end end
+ la,out cu=o nome J home seria especi"ica o no arquivo app/!iews/layouts/home.rhtml6 com a mesma "uncionali a e o arquivo application.rhtml. .ma outra possibili a e que o Rails o"erence J sobrescrever um la,out para uma Pnica ao6 no mJto o render. .m e0emplo seriaA iretamente
la"" ;ome4ontroller K Lppli ation4ontroller layout Q,omeQ def index end def indexA"pe ial render :a tion !. Qli"tQJ :layout !. Q,omeA"pe ialQ end end
)oc@ po e tambJm suprimir inteiramente um la,out usan o :layout/=9/false. 7inalmente6 voc@ po e usar maneiras especI"icas e eterminar o la,out e uma p#ina. Por e0emploA
la"" ;ome4ontroller K Lppli ation4ontroller layout : ,oo"eAlayout def index end prote ted def ,oo"eAlayout % urrentAu"er.adminU( U Qadmini"trationQ : QnormalQ end end
Bo caso acima6
epen en o
o retorno
e"ini o no arquivo administration.rhtml/ou no arquivo normal.rhtml. Como po emos ver no h muitos limites para o que po e ser "eito.
'1
ROTEAMENTO
-e voc@ acessou a .RL rai> a aplicao6 voc@ ter nota o que ela no mu ou6 apesar o controller que criamos. ?sso acontece porque o Rails e"ine um arquivo index.html que serve como pa ro. -e removermos esse arquivo o iret<rio public4/"icaremos com a se#uinte p#inaA
/sse erro in ica que o Rails no conse#ue encontrar um roteamento que satis"aa a .RL que especi"icamos. Para resolvermos o problema6 vamos e itar o arquivo config/routes.rb6 que /sse arquivo contJm o se#uinte co i#oA e"ine esses roteamentos.
L tion4ontroller::2outing::2oute".draw do MmapM ? <,e priority i" ba"ed upon order of reation: fir"t
? >ample of regular route: ? map. onne t Hprodu t"/:idHJ : ontroller !. H atalogHJ :a tion !. HviewH ? Ieep in mind you an a""ign value" ot,er t,an : ontroller and :a tion ? >ample of named route: ? map.pur ,a"e Hprodu t"/:id/pur ,a"eHJ : ontroller !. H atalogHJ :a tion !. Hpur ,a"eH ? <,i" route an be invo#ed wit, pur ,a"eAurl%:id !. produ t.id( ? Fou an ,ave t,e root of your "ite routed by ,oo#ing up HH ? -- -u"t remember to delete publi /index.,tml. ? map. onne t HHJ : ontroller !. Qwel omeQ ? Lllow downloading 0eb >ervi e 0>GD a" a file wit, an exten"ion ? in"tead of a file named Hw"dlH map. onne t H: ontroller/"ervi e.w"dlHJ :a tion !. Hw"dlH ? 7n"tall t,e default route a" t,e lowe"t priority. map. onne t H: ontroller/:a tion/:idH end
Como os comentrios i>em6 esse arquivo e"ine roteamentos e .RLs para controllers. Ca a roteamento J
'*
e0amina o quan o uma .RL J processa a pelo Rails6 eclarao a e"inio e priori a e.
Como voc@ po e ver no arquivo acima6 a .RL pa ro que mencionamos6 /controller/action/id J a Pltima a ser e"ini a6 sen o a aplica a caso no ha=a instru&es em contrrio. + roteamento no Rails J bem rico e os conceitos o mesmo mais a iante. Bo momento6 queremos apenas resolver o trabalharemos al#uns
problema a p#ina inicial. /ssa p#ina J representa a no arquivo acima pela linha abai0o6 comenta a no momento.
Para mapear o controller home como nossa p#ina inicial6 basta mo i"icar a linha para i>er o se#uinteA
invoca a6 a requisio eve ser servi a pelo controller que e"inimos anteriormente. Bote a mistura que as aspas e aspas simples e aspas uas uplas nesse arquivo. Bo Rub,6 elas so relativamente e representar te0to nessa lin#ua#em. A i"erena J e variveis como veremos mais a iante no tutorial. /u ten o
intercambiveis e so apenas
as vrias "ormas
a "avorecer o uso e aspas uplas6 nas isso J uma opo pessoal. A#ora6 recarre#an o nossa p#ina inicial temosA
''
as possibili a es mais
simples que esto isponIveis com o roteamento o Rails. A#ora que temos um conhecimento bsico e mo els6 controllers e vieGs6 po emos combinar o tr@s.
SCAFFOLDING
Para a=u ar na rpi a prototipao e aplica&es6 o Rails possui al#o chama o e sca""ol in#. + sca""ol in# prov@ uma estrutura bsica para opera&es CR.D SCreate6 Retrieve6 .p ate an apoiar o esenvolvimento atJ que voc@ insira a co i"icao necessria. Para e0perimentar com isso e e0pan ir um pouco nossa aplicao6 vamos criar um novo controller6 que servir para a ministrar os nossos conte0tosA
DeleteT6 ou
se=a6 aquelas opera&es bsicas que temos na manipulao e a os6 #eran o inter"aces rpi as que po em
ronaldo@minerva:~/tmp/gtd$ " ript/generate ontroller ontext" exi"t" app/ ontroller"/ exi"t" app/,elper"/ reate app/view"/ ontext" exi"t" te"t/fun tional/ reate app/ ontroller"/ ontext"A ontroller.rb reate te"t/fun tional/ ontext"A ontrollerAte"t.rb reate app/,elper"/ ontext"A,elper.rb
?nicialmente6 esse controller J como o que criamos anteriormente6 para a home nenhuma ao prJ; e"ini a.
a aplicao6 e no possui
esse controller6
'4
'5
+ sca""ol in# J muito Ptil na #erao e controllers bsicos e a ministrao para ar apoio M prototipao e ao inIcio o esenvolvimento6 principalmente para ca astros6 permitin o que o esenvolve or se "oque no que J mais importante para a aplicao. + inconveniente J que as a&es #era as so bem limita as e no po em ser e ita as ou tra u>i as.
'1
Para e0pan ir um pouco a "uncionali a e6 e0iste outra "orma "le0ibili a e maior na e io p#inas "inais. Para isso6 use o coman o abai0o:
e #erao
as p#inas #era as6 que po em6 inclusive6 ser utili>a as como base para as
ronaldo@minerva:~/tmp/gtd$ " ript/generate " affold ontext exi"t" app/ ontroller"/ exi"t" app/,elper"/ exi"t" app/view"/ ontext" exi"t" te"t/fun tional/ dependen y model exi"t" app/model"/ exi"t" te"t/unit/ exi"t" te"t/fixture"/ identi al app/model"/ ontext.rb identi al te"t/unit/ ontextAte"t.rb identi al te"t/fixture"/ ontext".yml reate app/view"/ ontext"/Aform.r,tml reate app/view"/ ontext"/li"t.r,tml reate app/view"/ ontext"/",ow.r,tml reate app/view"/ ontext"/new.r,tml reate app/view"/ ontext"/edit.r,tml overwrite app/ ontroller"/ ontext"A ontroller.rbU )Fna@+ y for e app/ ontroller"/ ontext"A ontroller.rb overwrite te"t/fun tional/ ontext"A ontrollerAte"t.rbU )Fna@+ y for e te"t/fun tional/ ontext"A ontrollerAte"t.rb identi al app/,elper"/ ontext"A,elper.rb reate app/view"/layout"/ ontext".r,tml reate publi /"tyle",eet"/" affold. ""
e um sca""ol
o mo elo
a os J que
eve ser
passa o como parLmetro6 e no o nome o controller. + Rails J capa> e erivar um o outro. /sse coman o #era a classe o mo elo e a os6 o controller e vieGs para ca a ao CR.D necessria. Bo
caso acima6 como o mo el = e0istia6 ele no "oi cria o6 e "oi necessrio sobrescrever al#uns arquivos. 4 uas coisas a serem nota as aqui. Primeiro6 o sca""ol isso6 o la,out ori#inal #erou o seu pr<prio la,out6 com sua pr<pria ele6 e a icionar a st,lesheet
arquivo app/!iews/layouts/contexts.rhtml6 = que no precisamos limita as6 como veremos a iante. Abrin o o novo arquivo #era o o controller6 temos o se#uinteA
la"" 4ontext"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Hli"tH end
'!
? X1<" ",ould be "afe %"ee ,ttp://www.w3.org/&''1/tag/do /w,en<oO"eXet.,tml( verify :met,od !. :po"tJ :only !. ) :de"troyJ : reateJ :update +J :redire tAto !. S :a tion !. :li"t T def li"t @ ontextApage"J @ ontext" ! paginate : ontext"J :perApage !. 1' end def ",ow @ ontext ! 4ontext.find%param"):id+( end def new @ ontext ! 4ontext.new end def reate @ ontext ! 4ontext.new%param"): ontext+( if @ ontext."ave fla",):noti e+ ! H4ontext wa" "u e""fully redire tAto :a tion !. Hli"tH el"e render :a tion !. HnewH end end def edit @ ontext ! 4ontext.find%param"):id+( end def update @ ontext ! 4ontext.find%param"):id+( if @ ontext.updateAattribute"%param"): ontext+( fla",):noti e+ ! H4ontext wa" "u e""fully updated.H redire tAto :a tion !. H",owHJ :id !. @ ontext el"e render :a tion !. HeditH end end def de"troy 4ontext.find%param"):id+(.de"troy redire tAto :a tion !. Hli"tH end end
reated.H
+ resulta o "inal6 ao ser e0ecuta o6 J muito pareci o com o sen o os arquivos e #era os enquanto que6
i"erena
Rails. Al#umas ameni a es6 como pa#inao6 tambJm "oram intro u>i as. .ma consi erao eve ser "eita aquiA o sca""ol in# J apenas um apoio M prototipao. /le no eve ser
usa o como uma muleta6 #eran o p#inas para mo i"icao. -e voc@ quer a ministra&es prontas6 e0istem
plu#ins para o Rails que permitem a #erao e p#inas bem avana as. /sses plu#ins po em ser utili>a os
para criar a ministra&es prontas a e0emplo as #era as pelo D=an#o So "rameGor8 equivalente ao Rails e uma aplicao e no levam em para o P,thonT6 mas mesmo elas tem suas limita&es e in"le0ibili a es. A lio J sempre procurar o que J melhor para a sua aplicao. +s sca""ol in#s so uma viso muito linear contra inter"aces alternativas e melhor usabili a e. De qualquer "orma6 o sca""ol in# #era o J uma boa oportuni a e para ver o relacionamento entre
controllers6 vieGs e mo els. -e voc@ tomar uma ao qualquer como e0emplo6 ver que ela usa a classe e
'5
uma vieG6 evitan o que o c< i#o e apresentao se misture com a l<#ica e ne#<cio. (esmo elementos o pr<prio controller6 como pa#inao6 so inclui os nessa iscriminao. )e=a a vieG e lista#em6 por e0emplo6 escrita no arquivo list.rhtml/no iret<rio app/!iews/contextsA
K,1.Di"ting
ontext"K/,1.
Ktable. Ktr. KY for olumn in 4ontext. ontentA olumn" Y. Kt,.KY! olumn.,umanAname Y.K/t,. KY end Y. K/tr. KY for ontext in @ ontext" Y. Ktr. KY for olumn in 4ontext. ontentA olumn" Y. Ktd.KY!, ontext."end% olumn.name( Y.K/td. KY end Y. Ktd.KY! lin#Ato H>,owHJ :a tion !. H",owHJ :id !. ontext Y.K/td. Ktd.KY! lin#Ato H1ditHJ :a tion !. HeditHJ :id !. ontext Y.K/td. Ktd.KY! lin#Ato HGe"troyHJ S :a tion !. Hde"troyHJ :id !. ontext TJ : onfirm !. HLre you "ureUHJ :po"t !. true Y.K/td. K/tr. KY end Y. K/table. KY! lin#Ato H=reviou" pageHJ S :page !. @ ontextApage". urrent.previou" T if @ ontextApage". urrent.previou" Y. KY! lin#Ato H8ext pageHJ S :page !. @ ontextApage". urrent.next T if @ ontextApage". urrent.next Y. Kbr /. KY! lin#Ato H8ew ontextHJ :a tion !. HnewH Y.
+ mJto o paginate6 usa o no controller escon e a comple0i a e a os6 ivi in o os itens retorna os pelo nPmero variveis que contJm6 respectivamente6 uma lista numerao p#inas. em "uncionamento6 embora o sca""ol
os uas
as p#inas Sque po e ser usa a na vieG para #erar a e 1$ itens6 ver isso e
A vieG acima tambJm e0ibe uma comple0i a e maior6 utili>an o vrios mJto os para a #erao #erao a .RL6 que po em ser combina os e vrias "ormas. Por e0emploA
o seu
conteP o. Bota amente6 o mJto o lin30to que recebe como o te0to o lin8 e parLmetros a icionais para a
KY! lin#Ato : ontroller !. Q,omeQ Y. KY! lin#Ato : ontroller !. Q ontext"QJ :a tion !. QeditQJ :id !. & Y. KY! lin#Ato : ontroller !. QloginQJ :u"ing !. Q oo#ie"Q Y.
Bo primeiro caso acima6 o mJto o #era a .RL rai> para um controller6 que como vimos J mapea a para a ao index. Bo se#un o caso6 uma .RL completa J retorna a. / no terceiro6 um parLmetro usin# J a iciona o M requisio6 #eran o a se#uinte .RLA /login?using=coo3ies.
'2
+ mJto o lin30to possui vrias outras capaci a es entre as quais #erar con"irma&es em Qava-cript e criar
estrutivamente seus
nos pr<prios arquivos #ea os nesse sca""ol . .ma olha a na esclarecimento maior essas op&es.
.m outro conceito interessante apresenta o nesse sca""ol J o e partials SparciaisT6 que so "ra#mentos e p#inas que po em ser compartilha os entre vieGs6 a mesma "orma que um la,out po e usar vrias vieGs. )e=a6 por e0emplo6 a relao entre os ois arquivos abai0oA Primeiro6 a vieG para criao e um novo re#istroA
K,1.8ew
ontextK/,1.
KY! "tartAformAtag :a tion !. H reateH Y. KY! render :partial !. HformH Y. KY! "ubmitAtag Q4reateQ Y. KY! endAformAtag Y. KY! lin#Ato H/a #HJ :a tion !. Hli"tH Y.
K,1.1diting
ontextK/,1.
KY! "tartAformAtag :a tion !. HupdateHJ :id !. @ ontext Y. KY! render :partial !. HformH Y. KY! "ubmitAtag H1ditH Y. KY! endAformAtag Y. KY! lin#Ato H>,owHJ :a tion !. H",owHJ :id !. @ ontext Y. M KY! lin#Ato H/a #HJ :a tion !. Hli"tH Y.
Bote que o c< i#o #era o J muito pareci o S e "ato os ois arquivos e suas a&es relaciona as po eriam ser combina os em um Pnico ponto e acessoT e em ambos os arquivos h uma chama a ao mJto o render6 iret<rio. Arquivos que o arquivo J usan o um partial. /sse partial se encontra no arquivo/ 0form.rhtml6 no mesmo simplesA
e"inem partials sempre comeam com Y para especi"icar que so "ra#mentos. + conteP o
KY! errorAme""age"Afor H ontextH Y. KW--)form: ontext+--. Kp.Klabel for!Q ontextAnameQ.8ameK/label.Kbr/. KY! textAfield H ontextHJ HnameH Y.K/p. KW--)eoform: ontext+--.
Besse caso6 a parte o "ormulrio que J comum tanta M insero e um conte0to quanto M sua e io.
4$
Partials so uma as #ran es "acili a es o Rails6 sen o utili>a os principalmente em aplica&es A=a0 para
#erar somente os "ra#mentos o c< i#o necessrios para atuali>a&es e partes e uma p#ina. 4o=e6 com o uso o templates RQ-6 eles se tornaram ain a mais importantes. -e voc@ utili>ar o sca""ol #era o6 ver que o c< i#o J um pouco mais poli o6 com mensa#ens i"erena o que po eria ser "eito com uma Pnica linha e que sca""ol s so utJis6 mas no e sucesso e
anteriormente. Por isso6 a observao anterior re#ra na aplicao. .m #ran e e0emplo as tabelas possibili a e e customi>ao. e lista#ens6 "a>en o invoca&es
Ktr. KY for olumn in 4ontext. ontentA olumn" Y. Kt,.KY! olumn.,umanAname Y.K/t,. KY end Y. K/tr.
a tabela
e conteP o e
e a os em questo6 que no incluem colunas avana as e relacionamento e nem so capa>es e e colunas que contenham valores booleanos6 enumera os e tipos a os essas o banco. AlJm isso6 o mJto o human0name J volta o para o i ioma in#l@s e embora e0ista a e plu#ins e outros mJto os6 as limita&es e l<#ica na aplicao6
tJcnicas so lo#o aparentes e6 se cui a o no "or toma o6 po em levar a problemas tornan o a mesma esnecessariamente comple0a e violan o os princIpios bsicos quais o Rails "oi construI o.
e simplici a e sobre os
Aproveitan o momentaneamente o c< i#o #era o6 vamos avanar um pouco em nossa aplicao6 "a>en o uso e mais uma caracterIstica no Rails.
VALIDAES
)ali a&es6 como o pr<prio nome in ica6 so um mo o e #arantir a inte#ri a e os a os em uma aplicao. + mo elo e vali a&es que o Rails "ornece J bastante completo e po e ser "acilmente e0pan i o pelo esenvolve or caso ele necessite e al#o no "orneci o por pa ro nas bibliotecas. Bo caso a nossa classe e a os inicial6 precisamos vali ar pelo menos o "ato o usurio ter in"orma o o
nome o conte0to ao ca astr;lo. Para isso6 vamos abrir o arquivo vali ao simplesA a classe6 que est em app/model/context.rb6 e e it;lo6 inserin o uma
41
A menos que o usurio in"orme o nome o conte0to6 o Rails no permitir que o ob=eto se=a salvo. Bote que a vali ao J "eita no mo elo e a os e re"leti a na vieG atravJs os mJto os a mesma. + mJto o error0messages0for recolhe to as as mensa#ens e erro #era as urante uma vali ao e #era os itens e "ormulrio
o 4:(L necessrio para e0ibi;las6 que6 no caso acima6 ain a J "ormata o pela st,lesheet scaffold.css/ cria a anteriormente. Da mesma "orma6 os mJto os responsveis pela #erao Scomo text0field6 text0area6 select e datetime0select6 entre outrosT so capa>es campo a que se re"erem no "alhou em al#uma vali ao e encapsular a e0ibio in icao e erro. (Pltiplas vali a&es po em ser e"etua as em um mesmo campo. Por e0emplo6 para prevenir a insero nomes uplica os6 a se#uinte con io po eria ser coloca a na classeA e e veri"icar se o o campo em uma
4*
la"" 4ontext K L tive2e ord::/a"e validate"Apre"en eAof :name validate"Auni@uene""Aof :name end
+ resulta o seriaA
As mensa#ens
se#uinte "ormatoA
la"" 4ontext K L tive2e ord::/a"e validate"Apre"en eAof :name validate"Auni@uene""Aof :nameJ :me""age !. Qmu"t be uni@ueQ end
Bote que o mJto o error0messages0for J volta o para o in#l@s6 "orman o "rases que "a>em mais senti o nesse i ioma6 enquanto o nosso portu#u@s pre"ere "rases mais elabora as. V "acil substituir o mJto o acima por uma implementao mais interessante a iante6 em outra seo o tutorial.
4'
para usar o la,out que criamos. Para isso6 remova o arquivo iret<rio app/!iews/layoutsT para o
K,tml. K,ead. Ktitle.X<GK/title. KY! "tyle",eetAlin#Atag QdefaultQ Y. KY! "tyle",eetAlin#Atag Q" affoldQ Y. K/,ead. Kbody. KY! yield Y. K/body. K/,tml.
A mu ana no J #ran e6 mas si#ni"ica que nosso sca""ol a#ora usa o la,out a aplicao6 receben o to as as mo i"ica&es provenientes o mesmo. Cabe uma nota aqui sobre as a&es #era as no sca""ol e6 por e0tenso6 qualquer outra ao a ser cria a em uma aplicao. .m movimento entro a comuni a e o Rails ho=e J o uso e mJto os R/-:6 que representam uma
interpretao e um con=unto e pa r&es e "iloso"ias e esenvolvimento Eeb. /sses mJto os usam verbos 4::P mais especI"icos como P.: e D/L/:/ para e"etuar suas a&es6 ao invJs e usar somente os usuais %/: e P+-:. A pr<0ima verso o Rails6 ao que tu o in ica6 vir com um suporte bem "orte para esse tipo e uso. + presente tutorial no entrar nesses etalhes sobre isso6 mas ei0o ao leitor al#uns recursos para que ele possa pesquisar mais sobre o assuntoA http://www.xml.com/pub/a/;""</##/";/rest'on'rails.html http://pe=ra.barelyenough.org/blog/;"">/":/another'rest'controller'for'rails/ http://www.agilewebde!elopment.com/plugins/simplyrestful A#ora que vimos com o bsico "unciona no Rails6 po emos partir para tentar a nossa pr<pria implementao6 apren en o mais sobre com as coisas "uncionam.
UM SEGUNDO CONTROLLER
)amos criar o ca astro e pro=etosHque tambJm J bem simplesH e "orma mais manual para enten ermos a os. Para "acilitar6 vamos combinar a criao e e io e um como uma aplicao comum processa seus re#istro em uma Pnica ao. + primeiro passo J atuali>ar o mo elo e a os para e"etuar vali a&es. Bo caso no nosso mo elo e a os
44
para pro=etos Sque est no arquivo app/models/pro%ect.rbT6 precisamos A classe "icaria6 assumin o que o atributo description J opcional6 assimA
la"" =ro-e t K L tive2e ord::/a"e validate"Apre"en eAof :name validate"Auni@uene""Aof :name end
.m se#un o passo J #erar um controller para li ar com as requisi&es relaciona as ao ca astro e pro=etos. Dessa ve>6 vamos usar o coman o automaticamente. 3asicamente6 queremos as a&es in e06 list6 e it e elete. A ao in e0 e0iste para no precisarmos e uma re#ra e roteamento e0plIcita mas ser meramente uma invocao a ao list. / ao e it resumir to as as a&es e criao e insero6 simplican o o c< i#o. + coman o para #erar controller J o se#uinteA e #erao o controllers especi"ican o = as a&es que ese=amos criar
ronaldo@minerva:~/tmp/gtd$ " ript/generate ontroller pro-e t" index li"t edit delete exi"t" app/ ontroller"/ exi"t" app/,elper"/ reate app/view"/pro-e t" exi"t" te"t/fun tional/ reate app/ ontroller"/pro-e t"A ontroller.rb reate te"t/fun tional/pro-e t"A ontrollerAte"t.rb reate app/,elper"/pro-e t"A,elper.rb reate app/view"/pro-e t"/index.r,tml reate app/view"/pro-e t"/li"t.r,tml reate app/view"/pro-e t"/edit.r,tml reate app/view"/pro-e t"/delete.r,tml
la"" =ro-e t"4ontroller K Lppli ation4ontroller def index end def li"t end def edit end def delete end end
45
B<s a#ora temos um controller que respon e per"eitamente Ms nossas a&es6 com vieGs = associa as6 prontas para o uso. Como a ao in e0 J somente uma invocao simplesA a ao list6 po emos remover o arquivo a vieG a mesma
la"" =ro-e t"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t end def edit end def delete end end
+ que essas uas linhas nos i>em soA primeiro6 invoque o mJto o list6 e epois ren eri>e a vieG a ao
e um mJto o comum
o Rails que o torna uma ao6 e6 sen o assim6 ele po e ser invoca o normalmente6 como qualquer e i enti"icao
procuraria a vieG relaciona a a index e no a list. Precisamos6 ento6 e0plicitar a que ser usa a pela
41
aplicao. Antes e construirmos a ao list6 precisamos provi enciar um mo o e inserirmos re#istros o banco. Para isso6 precisamos criar a nossa ao e it. :emos uas situa&es possIveisA ou o usurio est crian o um novo re#istro6 ou ele est atuali>an o um re#istro e0istente. +bviamente6 a Pnica um ob=eto J e0ibir o "ormulrio o mesmo para preenchimento. Como a&es relaciona as somente a e0ibio #eralmente so associa as ao mJto o 4::P %/: enquanto a&es que mo i"icam a os e epen em e "ormulrios so invoca as a partir o mJto o P+-:6 vamos usar essa i"erenciao para i enti"icar se estamos salvan o o ob=eto ou somente e0ibin o o "ormulrio para e io. / mais6 vamos usar a e0ist@ncia ou no o parLmetro i para i enti"icar se estamos li an o com um novo ob=eto ou com um ob=eto = e0istente. A primeira verso o nosso mJto o "icaria assim6 entoA i"erena entre esses ois mJto os J a e0ist@ncia ou no o ob=eto. / a primeira coisa a ser "eita tanto na criao quanto na e io e
la"" =ro-e t"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t end def edit if param"):id+ @pro-e t ! =ro-e t.find%param"):id+( el"e @pro-e t ! =ro-e t.new end if re@ue"t.po"tU end end def delete end end
+ primeiro teste o mJto o veri"ica se um parLmetro i retornar ver a eiro. -e voc@ quiser =o#ar pelo la o params5:id6.nil?6 com uma inverso
como na lin#ua#em C. -e o ob=eto "or nuloHou nil em Rub,Ho teste retornar "also. Caso contrrio6 a le#ilibi a e6 a linha po eria ser converti a para if/ as con i&es. +u voc@ po e usar unless/params5:id6.nil? /sem
inverter as con i&es6 = que essa eclarao testa o contrrio e uma eclarao if. A verso o teste que usamos acima J um pouquinho mais e"iciente6 mas no o su"iciente para "a>er
qualquer i"erena #eral a aplicao e voc@ po e pre"erir a le#ibili a e maior. Bo c< i#o acima6 epen en o a e0ist@ncia ou no o parLmetro6 tentamos carre#ar o ob=eto o banco ou
4!
+ se#un o teste
anteriormente6 J isso que vamos usar para saber se estamos simplesmente e0ibin o o "ormulrio Sse=a va>io ou noT6 ou se est na hora e persistir o ob=eto para o banco. (ais M "rente colocaremos esse c< i#o. Caso voc@ ache que o nome e it para esse mJto o J meio en#anoso quan o um novo re#istro est sen o cria o6 voc@ po e usar re#ras eciso que e roteamento #enJricas para especi"icar que invoca&es a a&es chama as o que voc@ est "a>en o no momento. A ten @ncia com o uso e R/-:6
create6 up ate6 e save so re ireciona as para a mesma ao e it. -eparar ou no as suas a&es J um
epen e obviamente inclusive6 J e a&es bem especI"icas6 que respon em a somente um tipo e verbo 4::P. A#ora que o nosso mJto o comeou a tomar "orma6 precisamos e itar a vieG edit.html para e0ibir o nosso "ormulrio. + arquivo presente est assimA
Precisamos in icar ao usurio se estamos e itan o um re#istro e0istente ou a icionan o um novo re#istro no pr<prio cabealho a p#ina. Para isso po emos usar o se#uinte c< i#oA
K,1.KY if @pro-e t.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. =ro-e tK/,1.
Bote que estamos usan o a varivel ?pro%ect que criamos no mJto o edit. + ? in ica uma varivel instLncia6 que o Rails automaticamente propa#a para qualquer vieG sen o processa a a partir que a varivel "oi e"ini a. Ba linha acima6 estamos usan o o mJto o/new0record?6 presente em to as as classes e a os
a ao em
eriva as
o ActiveRecor para i enti"icar se estamos li an o com um novo re#istro ou com um re#istro = e0istente. /sse mJto o retorna ver a eiro enquanto a classe no "or salva pela primeira ve>. Com base nisso6 e0ibimos o nosso cabealho. -e voc@ ro ar a p#ina6 invocan o iretamente a .RL /pro%ects/edit6 ver que = temos al#o "uncionan oA
45
+ pr<0imo passo a#ora J criar o "ormulrio e a os. Para isso6 vamos e itar a nossa vieGA
K,1.KY if @pro-e t.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. =ro-e tK/,1. KY! "tartAformAtag :a tion !. QeditQJ :id !. @pro-e t Y. KY! endAformAtag Y.
Duas coisas so interessantes nesse chama aA primeiro6 o Rails J inteli#ente o bastante para perceber que voc@ est passan o o ob=eto como se "osse o seu i qualquer teste. + outro mJto o chama o6 end0form0tag6 simplesmente #era o "echamento o elemento form na p#ina. Com o cabealho um nome e um o nosso "ormulrio #era o6 po emos a#ora criar campos para e io. .m pro=eto possui escrio. + primeiro atributo J um te0to simples6 e uma linha somente6 enquanto o e #erar o c< i#o necessrio automaticamenteK se#un o6 sem que voc@ precise "a>er caso o ob=eto no tenha si o salvo6 o Rails suprimir automaticamente o i
se#un o J um te0to que po e ter mPltiplas linhas. / itan o o nosso arquivo6 teremos o se#uinteA
42
K,1.KY if @pro-e t.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. =ro-e tK/,1. KY! "tartAformAtag :a tion !. QeditQJ :id !. @pro-e t Y. Kp. Klabel for!Qpro-e tAnameQ.8ame:K/label.Kbr. KY! textAfield Qpro-e tQJ QnameQ Y. K/p. Kp. Klabel for!Qpro-e tAde" riptionQ.Ge" ription:K/label.Kbr. KY! textAarea Qpro-e tQJ Qde" riptionQJ :row" !. 5 Y. K/p. KY! endAformAtag Y.
os elementos
e "ormulrio vistos
acima. + Rails possui e>enas esses mJto os6 capa>es e #erar vrias combina&es possIveis para os tipos a os suporta os automaticamente e permitir e0tens&es se voc@ precisar e al#o mais customi>a o. / voc@ sempre po e #erar o seu c< i#o manualmente em situa&es especiais. )e=a que ca a mJto o recebe um ob=eto e um atributo a ser #era o. Bo caso precisa usar o ? iretamenteA basta passar o nome que est sen o e0ecuta o. -e voc@ olhar o 4:(L #era o6 ver o se#uinteA esses mJto os6 voc@ no o controller
5$
Kp. Klabel for!Qpro-e tAnameQ.8ame:K/label.Kbr. Kinput id!Qpro-e tAnameQ name!Qpro-e t)name+Q "ize!Q3'Q type!QtextQ /. K/p. Kp. Klabel for!Qpro-e tAde" riptionQ.Ge" ription:K/label.Kbr. Ktextarea ol"!Q$'Q id!Qpro-e tAde" riptionQ name!Qpro-e t)de" ription+Q row"!Q5Q.K/textarea. K/p.
especI"icos para os a os
"acilitar a vi a o esenvolve or. + atributo i po e ser associa o a um elemento label como emonstra o e o atributo name J o que J envia o ao Rails6 #eran o automaticamente uma tabela hash submeti os pelo "ormulrio que a ao po e usar. Dentro e um controller voc@ ser capa> e usar params5:pro%ect6 para acessar iretamente essa tabela
K,1.KY if @pro-e t.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. =ro-e tK/,1. KY! "tartAformAtag :a tion !. QeditQJ :id !. @pro-e t Y. Kp. Klabel for!Qpro-e tAnameQ.8ame:K/label.Kbr. KY! textAfield Qpro-e tQJ QnameQ Y. K/p. Kp. Klabel for!Qpro-e tAde" riptionQ.Ge" ription:K/label.Kbr. KY! textAarea Qpro-e tQJ Qde" riptionQJ :row" !. 5 Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#Ato Q4an elQJ :a tion !. Qli"tQ Y. K/p. KY! endAformAtag Y.
o que isso6 o mJto o acima evita que tenhamos que e "orar uma mistura
pressiona o em nosso "ormulrio. /mbora o c< i#o para isso se=a bem simples6 repetI;lo em ca a "ormulrio se tornaria rapi amente te ioso6 alJm evitar a to o custo. /sse J um pa ro que voc@ ver repeti amente em aplica&es atuais e que "a> bastante senti o o ponto e vista e usabili a e. + resulta o seriaA e apresentao com l<#ica que queremos
51
:emos a#ora um "ormulrio completo "uncionan o. Precisamos simplesmente salvar o que ser submeti o. )oltan o ao nosso controller6 "icamos com o se#uinteA
la"" =ro-e t"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t end def edit if param"):id+ @pro-e t ! =ro-e t.find%param"):id+( el"e @pro-e t ! =ro-e t.new end if re@ue"t.po"tU @pro-e t.attribute" ! param"):pro-e t+ if @pro-e t."ave redire tAto :a tion !. Qli"tQ end end end def delete end end
5*
:o o ob=eto que representa uma classe representa uma coleo para preenchimento Rails na submisso as colunas
a tabela equivalentes. /sse atributo aceita receber uma tabela hash ireta no ob=eto. Ca a item
e seus valores. Como menciona o anteriormente6 J aqui que os o "ormulrio vem a calhar6 servin o para associao
e0istente em params5:pro%ect6 no c< i#o acima6 como6 por e0emplo6 params5:pro%ect65:name66 preencher seu item correspon ente sem necessi a e e c< i#o a icional. Continuan o o c< i#o6 tentamos salvar o ob=eto. -e isso tem sucesso Sou se=a6 se nenhuma vali ao "alhaT re irecionamos para a p#ina e lista#em. Caso contrrio6 e0ibimos o "ormulrio novamente. Besse caso6 note que o Rails usar a mesma vieG6 se#uin o o comportamento pa ro6 e"etivamente mostran o o "ormulrio preenchi o com os a os posta os. ?sso acontece porque os mJto os text0field e text0area Se os emaisT preenchem automaticamente os elementos e "ormulrio com os valores e0istentes no ob=eto que receberam. ?sso torna a nossa tare"a bem simples. -e voc@ tentar salvar um "ormulrio sem preencher na a6 ver o se#uinte a#oraA
Botamos que a vali ao est "uncionan o6 mas nenhum erro J e0ibi o. Para isso6 precisamos mo i"icao em nossa vieGA
a se#uinte
5'
K,1.KY if @pro-e t.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. =ro-e tK/,1. KY! errorAme""age"Afor Qpro-e tQ Y. KY! "tartAformAtag :a tion !. QeditQJ :id !. @pro-e t Y. Kp. Klabel for!Qpro-e tAnameQ.8ame:K/label.Kbr. KY! textAfield Qpro-e tQJ QnameQ Y. K/p. Kp. Klabel for!Qpro-e tAde" riptionQ.Ge" ription:K/label.Kbr. KY! textAarea Qpro-e tQJ Qde" riptionQJ :row" !. 5 Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#Ato Q4an elQJ :a tion !. Qli"tQY. K/p. KY! endAformAtag Y.
+ que nos A
54
-e voc@ salvar um ob=eto a#ora6 ver que somos re ireciona os para a ao list que ain a precisamos criar. .ma lista#em J al#o bem simples e queremos e0ibir6 inicialmente6 somente o nome vamos criar al#o bem bsicoA o pro=eto. Para isto6
K,1.=ro-e t"K/,1. Ktable border!Q1Q Ktr. Kt,.8ameK/t,. K/tr. K/table. ellpadding!Q5Q ell"pa ing!Q1Q.
?sso nos um tabela simples. A#ora6 queremos e0ibir os re#istros cria os. Precisamos6 obviamente6 buscar esses ob=etos no banco. (o i"ican o o controller "icamos com al#o assimA
la"" =ro-e t"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @pro-e t" ! =ro-e t.find :allJ :order !. QnameQ end def edit if param"):id+ @pro-e t ! =ro-e t.find%param"):id+( el"e @pro-e t ! =ro-e t.new end if re@ue"t.po"tU @pro-e t.attribute" ! param"):pro-e t+ if @pro-e t."ave redire tAto :a tion !. Qli"tQ end end end def delete end end
mJto o find6 = trata o anteriormente6 recebe como primeiro parLmetro uma especi"icao o que retornar ou um i especI"ico. Bo caso acima6 queremos to os re#istros SallT. + se#un o parLmetro6 nomea o6 recebe a especi"icao e or enao. .m etalhe a chama a acima. (uitas ve>es6 em c< i#o Rub,6 voc@ ver um seqR@ncia e uma tabela hash com Pltimo parLmetro e parLmetros
nomea os no "im e uma chama a. + Rub, no possui parLmetros nomea os em si6 mas J capa> e simular isso com o uso e um mJto o. Duan o um mJto o usa essa tJcnica6 o Rub, automaticamente permite que os parLmetros se=am eclara os e "orma livre6 pelo nome6 e
55
a chama a em uma tabela hash que J ento passa a como parLmetro. Assim6 o acima J um sImbolo e o se#un o parLmetro J uma tabela hash cu=o
o mJto o "in
Pnico elemento J um par e"ini o por um sImbolo e uma strin#. A#ora6 = po emos usar esses ob=etos em uma vieGA
K,1.=ro-e t"K/,1. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. K/tr. KY @pro-e t".ea , do Mpro-e tM Y.Ktable border!Q1Q Ktr. Ktd.KY! pro-e t.name Y.K/td. K/tr. KY end Y. K/table.
ellpadding!Q5Q
ell"pa ing!Q1Q.
Bo caso acima6 o mJto o find retorna um arra, que po e ou no conter itens. .ma coisa a manter em mente aqui J que6 para caso e uso esse mJto o6 o retorno po e ser i"erente. -e voc@ estiver usan o find/com/:first6 o retorno ser um ob=eto ou o valor nil. -e voc@ estiver passan o um
i especI"ico6 um ob=eto ser retorna o ou um erro ser #era o. Lembre;se isso sempre que usar o mJto o
em suas varia&es. As varia&es servem para cobrir as situa&es mais comuns6 e voc@ sempre po e escrever seus pr<prios mJto os usan o as combina&es acima para obter o resulta o que ese=a. Com ois pro=etos inseri os6 o resulta o JA
Po emos a#ora mo i"icar a nossa vieG para permitir al#umas a&es sobre esses ob=etos. Por e0emploA
51
K,1.=ro-e t"K/,1. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. Kt,.L tion"K/t,. K/tr. KY @pro-e t".ea , do Mpro-e tM Y. Ktr. Ktd.KY! pro-e t.name Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. pro-e t Y. or KY! lin#Ato QGeleteQJ :a tion !. QdeleteQJ :id !. pro-e t Y. K/td. K/tr. KY end Y. K/table.
Como voc@ ever ter nota o pelas chama as a lin8Yto na vieG acima e em outros pontos o c< i#o = e0ibi o6 o Rails J capa> e erivar os elementos a uma .RL os parLmetros que esto sen o usa os no momento. + resulta o a aplicao a vieG acima J visto abai0oA
5!
K,1.=ro-e t"K/,1. Kp.KY! lin#Ato Q8ew =ro-e tQJ :a tion !. QeditQ Y.K/p. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. Kt,.L tion"K/t,. K/tr. KY @pro-e t".ea , do Mpro-e tM Y. Ktr. Ktd.KY! pro-e t.name Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. pro-e t Y. or KY! lin#Ato QGeleteQJ :a tion !. QdeleteQJ :id !. pro-e t Y. K/td. K/tr. KY end Y. K/table.
.m os problemas em ei0ar que um re#istro se=a removi o com um simples invocao J que o usurio no tem como reverter a ao. Po emos a icionar um mInimo e proteo com a se#uinte alteraoA
55
K,1.=ro-e t"K/,1. Kp.KY! lin#Ato Q8ew =ro-e tQJ :a tion !. QeditQ Y.K/p. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. Kt,.L tion"K/t,. K/tr. KY @pro-e t".ea , do Mpro-e tM Y. Ktr. Ktd.KY! pro-e t.name Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. pro-e t Y. or KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. pro-e t TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y. K/table.
e a icionar chaves
o se#un o para que o Rub, no e0era o seu comportamento pa ro e concatene to os os a chama a como o terceiro so
parLmetros livres em um s<. Bo caso acima6 tanto o se#un o parLmetro tabelas hash6 embora somente a se#un a este=a e0plicitamente in ica a. + resulta o pro=etoA
A ao
elete tambJm po e ser prote#i a e acesso %/: iretos com o uso e outra caracterIstica o Rails
o sca""ol que "oi #era o anteriormente para o mo elo e
que J a trans"ormao e lin8s em "ormulrios caso a ao se=a bem simples. + resulta o visual no J muito interessante e voc@ po e observ;lo no c< i#o a os e conte0tos.
52
la"" =ro-e t"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @pro-e t" ! =ro-e t.find :allJ :order !. QnameQ end def edit if param"):id+ @pro-e t ! =ro-e t.find%param"):id+( el"e @pro-e t ! =ro-e t.new end if re@ue"t.po"tU @pro-e t.attribute" ! param"):pro-e t+ if @pro-e t."ave redire tAto :a tion !. Qli"tQ end end end def delete =ro-e t.find%param"):id+(.de"troy redire tAto :a tion !. Qli"tQ end end
Como essa ao no possui nenhuma vieG6 o arquivo #era o delete.rhtml tambJm po e ser e0cluI o. .ma outra alterao que po e ser "eita J a e0ibio vamos usar uma outra caracterIstica e sesso6 mas somente persistem e mensa#ens e a os para al#umas a&es. Para isso6
e uma p#ina para outra. .ma ve> que a requisio para a qual "oram
propa#a as acaba6 elas so automaticamente removi as o conte0to e e0ecuo. )amos mo i"icar nosso controller mais uma ve>A
la"" =ro-e t"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @pro-e t" ! =ro-e t.find :allJ :order !. QnameQ end def edit if param"):id+ @pro-e t ! =ro-e t.find%param"):id+( el"e @pro-e t ! =ro-e t.new end if re@ue"t.po"tU
1$
@pro-e t.attribute" ! param"):pro-e t+ if @pro-e t."ave fla",):noti e+ ! Q<,e pro-e t wa" "u redire tAto :a tion !. Qli"tQ end end end def delete =ro-e t.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e pro-e t wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully "avedQ
e""fully deletedQ
Po emos a#ora usar essas variveis em nossa vieG para a ao list6 que J para on e as questo retornamA
uas a&es em
K,1.=ro-e t"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew =ro-e tQJ :a tion !. QeditQ Y.K/p. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. Kt,.L tion"K/t,. K/tr. KY @pro-e t".ea , do Mpro-e tM Y. Ktr. Ktd.KY! pro-e t.name Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. pro-e t Y. or KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. pro-e t TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y. K/table.
e sImbolos ao invJs
instLncia e po erem ser otimi>a os pelo interpreta or. + Rails #eralmente permite as mJto os e acesso e manter um pa ro em relao ao mesmo. ?sso "acilita a le#ibili a e
acesso para suas variveis internas representa as com tabelas hash6 mas J sempre bom escolher um evita o aparecimento e erros proveniente a mistura os ois tipos e acesso. + resulta o J o se#uinte6 ap<s uma ao e e ioA
a aplicao e
11
ronaldo@minerva:~/tmp/gtd$ " ript/generate migration addA"tatu"AtoApro-e t exi"t" db/migrate reate db/migrate/''$AaddA"tatu"AtoApro-e t.rb
a os. / itan o o
la"" Ldd>tatu"<o=ro-e t K L tive2e ord::Bigration def "elf.up addA olumn :pro-e t"J :a tiveJ :boolean =ro-e t.re"etA olumnAinformation =ro-e t.updateAall Qa tive ! 1Q end def "elf.down removeA olumn :pro-e t"J :a tive end end
Besse caso6 estamos a icionan o uma coluna e automaticamente atuali>an o para um valor que achamos
1*
mais in ica o. Como o nosso mo elo est sen o mo i"ica o6 precisamos usar o mJto o reset0column0information para que o Rails releia as tabelas e recarre#ue os mo elos ime iatamente. Ba ver a e6 isso no J estritamente necessrio pelo uso o mJto o update0all6 mas J interessante saber que esse uso po e ser necessrio em e -DL passa o como al#uns casos. + mJto o update0all/atuali>a to os os re#istros usan o o "ra#mento parLmetro.6 pulan o quaisquer vali a&es e0istentesHportanto6 use;o com cui a o. Ap<s ro ar o coman o ra8e db:migrate4 veremos o nosso banco atuali>a o completamente. Bote que6 nesse caso6 a mi#rao para um verso anterior simplesmente remove a coluna.
USANDO HELPERS
A#ora precisamos atuali>ar o nosso controller e nossas vieGs. + primeiro passo J atuali>ar a lista#em para e0ibir a situao o pro=eto. Para isso vamos utili>ar um recurso o Rails chama o e helpers.
4elpers so classes associa as a ca a controller que contJm "un&es utilitrias que so automaticamente
e0porta as para as vieGs associa as ao mesmo. Ca a controller tem o seu pr<prio helper e6 a e0emplo os
e"ini a no arquivo
talve> precisemos usar esse mJto o em outras vieGs que no as presente no iret<rio app/helpers. :erIamos6 ento6 al#o assimA
module Lppli ation;elper def ye"AorAnoU%value( value U QFe"Q : Q8oQ end end
K,1.=ro-e t"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew =ro-e tQJ :a tion !. QeditQ Y.K/p.
1'
Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. Kt,.L tiveK/t,. Kt,.L tion"K/t,. K/tr. KY @pro-e t".ea , do Mpro-e tM Y. Ktr. Ktd.KY! pro-e t.name Y.K/td. Ktd.KY! ye"AorAnoU%pro-e t.a tiveU( Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. pro-e t Y. or KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. pro-e t TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y. K/table.
Bote que po emos a#ora chamar o mJto o classe. .ma conveno especial
o Rails J que6 se um atributo representar um valor booleano6 voc@ po e e a os possuem campos booleanos nativos por pa ro. Bo caso
acrescentar um ponto e interro#ao ap<s o mJto o para que ele retorne ver a eiro ou "also iretamente. ?sso acontece porque nem to os bancos o (,-DL6 por e0emplo6 valores booleanos so representa os por campos inteiros conten o >ero ou um. + uso a interro#ao "acilita a visuali>ao e uso o campo. /m vrias situa&es6 o Rails J capa> le#Ivel campo. + resulta o a#ora JA e etectar automaticamente o valor booleano6 e o ponto i>er o tipo e e
interro#ao no J estritamente necessrio. (as J uma boa conveno "a>er isso. Bo s< o c< i#o "ica mais e ime iato6 como qualquer esenvolve or Rails ser capa> e a os arma>ena o no
14
+bviamente6 precisamos e itar o "ato o pro=eto estar ativo ou no. )amos alterar nossa vieG e e ioA
K,1.KY if @pro-e t.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. =ro-e tK/,1. KY! errorAme""age"Afor Qpro-e tQ Y. KY! "tartAformAtag :a tion !. QeditQJ :id !. @pro-e t Y. Kp. Klabel for!Qpro-e tAnameQ.8ame:K/label.Kbr. KY! textAfield Qpro-e tQJ QnameQ Y. K/p. Kp. Klabel for!Qpro-e tAde" riptionQ.Ge" ription:K/label.Kbr. KY! textAarea Qpro-e tQJ Qde" riptionQJ :row" !. 5 Y. K/p. Kp. Klabel for!Qpro-e tAa tiveQ.L tive:K/label.Kbr. KY! "ele t Qpro-e tQJ Qa tiveQJ ))QFe"QJ true+J )Q8oQJ fal"e++ Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#Ato Q4an elQJ :a tion !. Qli"tQY. K/p. KY! endAformAtag Y.
/ssa J uma as maneiras e e itar o atributo6 usan o um elemento select. + mJto o select #era automaticamente o c< i#o 4:(L necessrio6 receben o parLmetros similares aos outros mJto os e uma lista um arra, e valores a serem usa os como escolhas. /sses valores so in"orma os como escrio e o valor a ser atribuI o. Bo e arra,s on e ca a item representa um par6 conten o a
caso acima6 temos escri&es Ces e Bo6 correspon en o a valores true e "alse.
15
+ Rails
especiali>a os em #erar cole&es aninha as6 por e0emplo6 enquanto outros #eram ata. Antes e e0perimentar o mJto o #enJrico acima6 consulte a mJto o que lhe aten a.
Lembre;se que6 para valores booleanos6 para que a atribuio "uncione automaticamente6 os valores true e
"alse evem ser necessariamente usa os. Caso contrrio6 a converso automtica e tipos o Rub, entra em
ao e os a os no so processa os apropriamente. + resulta o po e ser visto abai0o e testan o voc@ ver que ele "unciona per"eitamente6 alteran o os valores e acor o com sua escolha sem necessi a e e qualquer co i"icao a icionalA
/0istem pelo menos uas outras iretas maneiras e "a>er a e io e um valor booleano. .ma seria usan o
ra io buttons e chec8bo0es. :estar essas maneiras "ica como um e0ercIcio para o leitor6 lembran o que o
princIpio J o mesmo. .ma Pltima coisa seria mo i"icar o controller para #erar pa#inao automtica. ?sso J "acilmente conse#ui o com uas atuali>a&es.
11
.ma no controllerA
la"" =ro-e t"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @pro-e tApage"J @pro-e t" ! paginate :pro-e t"J :perApage !. 5J :order !. QnameQ end def edit if param"):id+ @pro-e t ! =ro-e t.find%param"):id+( el"e @pro-e t ! =ro-e t.new end if re@ue"t.po"tU @pro-e t.attribute" ! param"):pro-e t+ if @pro-e t."ave fla",):noti e+ ! Q<,e pro-e t wa" "u redire tAto :a tion !. Qli"tQ end end end def delete =ro-e t.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e pro-e t wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully "avedQ
e""fully deletedQ
/ outra na vieGA
K,1.=ro-e t"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew =ro-e tQJ :a tion !. QeditQ Y.K/p. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. Kt,.L tiveK/t,. Kt,.L tion"K/t,. K/tr. KY @pro-e t".ea , do Mpro-e tM Y. Ktr. Ktd.KY! pro-e t.name Y.K/td. Ktd.KY! ye"AorAnoU%pro-e t.a tiveU( Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. pro-e t Y. or KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. pro-e t TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y. K/table. Kp.KY! paginationAlin#"%@pro-e tApage"( Y.K/p.
1!
A mo i"icao no controller usa um mJto o que = mencionamos anteriormente6 paginate6 para encapsular o processo e pa#inar os re#istros e #erar uma lista#em in"ormao retorna a pelo mJto o e pa#inao. +s mJto os usa os so ra>oavelmente simples6 mas6 in"eli>mente6 esto entre Rails e ois os mais ine"icientes e o e p#inas Saqui separa as em cinco re#istros por e p#inas com base na p#inaT. A mo i"icao na vieG6 por sua ve>6 simplesmente cria uma lista
a os que temos6
eles no che#am a representar um problema. + resulta o epois po e ser visto abai0o. )isuali>an o a primeira p#ina6 temosA
15
:erminamos um controller completoHsimples6 mas "uncionalHe6 inclusive6 alteramos o mo elo e a os por trs o mesmo e acor o com nossa necessi a e. A#ora = temos uma base para investi#armos o pr<0imo passo classes e a os. e uma aplicaoA relacionamentos entre
RELACIONAMENTOS
Antes e comearmos a ver os relacionamentos6 vamos unir o nosso pro=eto sob um la,out que pelo menos nos permita nave#ar mais "acilmente. Bosso novo arquivo app/!iews/layouts/application.rb/"icaria assimA
K,tml. K,ead. Ktitle.X<GK/title. KY! "tyle",eetAlin#Atag QdefaultQ Y. KY! "tyle",eetAlin#Atag Q" affoldQ Y. K/,ead. Kbody. K,1 id!Q,eaderQ.X<GK/,1. Kul id!QmenuQ. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 4ontext"QJ : ontroller !. Q ontext"Q Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 =ro-e t"QJ : ontroller !. Qpro-e t"Q Y.K/li. K/ul. Kdiv id!Q ontent"Q.KY! yield Y.K/div. K/body.
12
K/,tml.
body S font-family: ZerdanaJ LrialJ "an"-"erif5 font-"ize: 1''Y5 margin: '5 padding: '5 T ,1?,eader S ba #ground- olor: ? 5 olor: w,ite5 padding: 1'px 1'px 15px5 margin: '5 T ul?menu S padding: 5px 1'px5 margin: '5 border-bottom: 1px "olid ? T ul?menu li S di"play: inline5 margin-rig,t: 5px5 font-weig,t: bold5 T ul?menu a S text-de oration: none5 font-weig,t: normal5 T div? ontent" S margin: 1'px5 T div? ontent" ,1 S font-"ize: 13'Y5 font-"tyle: itali 5 T
!$
e al#uns
como po emos atuali>ar a aplicao sem me0er em qualquer #era as uma ve> que elas tenham si o bem plane=a as.
um pro=eto e J e0ecuta o em um etermina o conte0to. ?sso J su"iciente para comearmos o nosso ca astro. Atuali>amos primeiro o arquivo application.rhtml para incluir a nossa nova rea6 temosA
K,tml. K,ead. Ktitle.X<GK/title. KY! "tyle",eetAlin#Atag QdefaultQ Y. KY! "tyle",eetAlin#Atag Q" affoldQ Y.
!1
K/,ead. Kbody. K,1 id!Q,eaderQ.X<GK/,1. Kul id!QmenuQ. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 4ontext"QJ : ontroller !. Q ontext"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 =ro-e t"QJ : ontroller !. Qpro-e t"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 L tion"QJ : ontroller !. Qa tion"QJ :a tion !. Qli"tQ Y.K/li. K/ul. Kdiv id!Q ontent"Q.KY! yield Y.K/div. K/body. K/,tml.
/m se#un o lu#ar6 #eramos o nosso controller. )amos se#uir o mesmo estilo pro=etosA
ronaldo@minerva:~/tmp/gtd$ " ript/generate ontroller a tion" index li"t edit delete exi"t" app/ ontroller"/ exi"t" app/,elper"/ reate app/view"/a tion" exi"t" te"t/fun tional/ reate app/ ontroller"/a tion"A ontroller.rb reate te"t/fun tional/a tion"A ontrollerAte"t.rb reate app/,elper"/a tion"A,elper.rb reate app/view"/a tion"/index.r,tml reate app/view"/a tion"/li"t.r,tml reate app/view"/a tion"/edit.r,tml reate app/view"/a tion"/delete.r,tml
Para "acilitar6 vamos uplicar a "uncionali a e presente no ca astro e pro=etos. + nosso controller e a&es "icaria assim6 inicialmenteA
la"" L tion"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @a tionApage"J @a tion" ! paginate :a tion"J :perApage !. 5J :order !. Qde" riptionQ end def edit if param"):id+ @a tion ! L tion.find%param"):id+( el"e @a tion ! L tion.new end if re@ue"t.po"tU @a tion.attribute" ! param"):a tion+ if @a tion."ave fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ
e""fully "avedQ
!*
end end end def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
K,1.L tion"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew L tionQJ :a tion !. QeditQ Y.K/p. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.Ge" riptionK/t,. Kt,.GoneK/t,. Kt,.L tion"K/t,. K/tr. KY @a tion".ea , do Ma tionM Y. Ktr. Ktd.KY! a tion.de" ription Y.K/td. Ktd.KY! ye"AorAnoU%a tion.doneU( Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. a tion Y. or KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. a tion TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y. K/table. Kp.KY! paginationAlin#"%@a tionApage"( Y.K/p.
K,1.KY if @a tion.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. L tionK/,1. KY! errorAme""age"Afor Qa tionQ Y. KY! "tartAformAtag :a tion !. QeditQJ :id !. @a tion Y. Kp. Klabel for!Qa tionAde" riptionQ.Ge" ription:K/label.Kbr. KY! textAarea Qa tionQJ Qde" riptionQJ :row" !. 5 Y. K/p. Kp. Klabel for!Qa tionAdoneQ.Gone:K/label.Kbr. KY! "ele t Qa tionQJ QdoneQJ ))QFe"QJ true+J )Q8oQJ fal"e++ Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#Ato Q4an elQJ :a tion !. Qli"tQY. K/p.
!'
KY! endAformAtag Y.
Bo tente usar essa vieG para salvar uma ao ain a. 4 um erro sutil na mesma que a iante. As emais vieGs #era as Sindex.rhtml /e delete.rhtmlT po em ser removi as no sero usa as.
+bviamente6 a vieG acima no inclui to os os atributos presentes na ao. Para incluirmos esses atributos6 vamos consi erar al#umas coisas. + atributos created0at representa a AlJm isso6 atributos com os ata e criao a ao. Atributos termina os em 0at e 0on /so atas e atas e tempos6 respectivamente. updated0at e update0on sero created0on6
automaticamente atuali>a os pelo Rails e acor o com a necessi a e. :o os quan o o ob=eto "or cria o e os ois Pltimos sempre que o ob=eto "or salvo. Como o Rails obviamente no tem controle sobre o banco tipo caso e os termina os em 0on Stipos e a os e a os6 o esenvolve or J que eve escolher o
isso6 usar um tipo mais ou menos preciso no causa erros e Rails tentar convers&es sempre que necessrio. Bo nosso caso6 ento6 no precisamos ar qualquer valor ao atributo created0at /para que ele se=a salvo.
Ba ver a e6 como esse J um campo que everia ser salvo uma Pnica ve>6 vamos colocar al#uma in"ormao no mo elo para que o mesmo no possa ser atribuI o em "ormulrios6 mas somente em c< i#o ireto.
/sse
eclarao impe e que esse atributo se=a atuali>a o automaticamente por qualquer atribuio via o servi or. -omente uma atuali>ao ireta via c< i#o "uncionar e essa isso6 no temos que nos preocupar mais com esse atributo. + reverso
+ atributo completed0at/representa a ata em que a ao "oi completa a. :ambJm no precisamos e it;lo em nossa vieG6 = que ele po eria ser atribuI o quan o o usurio marcasse a ao como completa a. 7aremos ento a mesma coisa com esse atributo6 mu an o o nosso mo elo paraA
!4
Como o campo J atribuI o automaticamente6 o que precisamos "a>er J achar al#uma "orma esse atributo quan o o atributo done6 que representa o "ato ver a eiro. A soluo J sobrescrever o mJto o =ustamente para esse tipo e situaoA e escrita
e mo i"icar
a ao estar completa6 "or marca o como o atributo done6 al#o que o Rails permite
la"" L tion K L tive2e ord::/a"e attrAprote ted : reatedAatJ : ompletedAat def done!%value( if L tive2e ord::4onne tionLdapter"::4olumn.valueAtoAboolean%value( "elf. ompletedAat ! Gate<ime.now el"e "elf. ompletedAat ! nil end writeAattribute%QdoneQJ value( end end
Aqui6 estamos usan o o momento em que o atributo done J recebi o e e0ecutan o a&es e0tras. Bnote o sinal e i#ual6 in ican o que esse J um mJto o e atribuioK no Rub,6 qualquer mJto o termina o com o sinal po e ser automaticamente usa o como se "osse um atributo. + mJto o veri"ica se valor recebi o "oi ver a eiro usan o um mJto o interno o ActiveRecor . /ssa chama a J necessria porque6 e0ceto por nil e
"alse6 qualquer outro valor J consi era o ver a eiro em Rub,6 incluin o >ero e strin#s va>ias. -e sim6 o
atributo completed0at J atuali>a o com a ata atual. -e no6 o atributo J retorna o a um valor nulo. Depois isso6 o valor recebi o para o atributo done J passa o sem mo i"ica&es para a cama a e banco e a os6 usan o o mJto o write0attribute. (esmo que voc@ no precise mo i"icar campos em epen @ncia um o outro6 a tJcnica acima J util para
#uar ar valores converti os Spor e0emplo6 um valor que J in"orma o em #raus6 mas arma>ena o em ra ianos ou vice;versaT. + par e mJto os write0attributeZread0attribute po e ser usa o em con=uno com mJto os que sobrescrevem os mJto os manipulao e a os. 7inalmente6 temos os atributos context0id e pro%ect0id6 que representam as chaves estran#eiras que criamos no banco6 que usaremos para os nossos primeiros relacionamentos entre classes. e acesso que o Rails #era para permitir maior "le0ibili a e a
BELONGS TO
Bo caso esses ois atributos6 po erIamos i>er que uma ao pertence a um conte0to e que pertence a um pro=eto. Bo Rails6 representarIamos isso a se#uinte "ormaA
!5
la"" L tion K L tive2e ord::/a"e attrAprote ted : reatedAatJ : ompletedAat def done!%value( if L tive2e ord::4onne tionLdapter"::4olumn.valueAtoAboolean%value( "elf. ompletedAat ! Gate<ime.now el"e "elf. ompletedAat ! nil end writeAattribute%QdoneQJ value( end belong"Ato : ontext belong"Ato :pro-e t end
/ssas capa>
uas e
e "acili a es no Rails que inclui atribui&es e o que ele re"erencia. + Rails J a tabela pai ao su"i0o
buscas. )e=a que voc@ no precisou 0id. Caso voc@ tenha usan o al#o assimA
e u>ir a chave estran#eira quan o ela "or cria a concatenan o o nome a o outro nome6 voc@ po e tambJm
AlJm
e"inio
e0plora as na ocumentao6 incluin o outras con i&es limItro"es para o mesmo. Para ca a relacionamento cria o6 o Rails isponibili>a mJto os que permitem testar a e0ist@ncia e uma
associao6 e"ini;la6 remov@;la e busc;la. )amos usar o console para testar issoA
ronaldo@minerva:~/tmp/gtd$ " ript/ on"ole Doading development environment. .. a tion ! L tion. reate%:de" ription !. QL te"t a tion.Q( !. ?KL tion:'xbR$5383 @newAre ord!fal"eJ @error"!?KL tive2e ord::1rror":'xbR$&*$6' @ba"e!?KL tion:'xbR$5383 ....J @error"!ST.J @attribute"!SQ ontextAidQ!.nilJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.nilJ QdoneQ!.nilJ QidQ!.*J Qde" riptionQ!.QL te"t a tion.QJ Q reatedAatQ!.<,u >ep &1 15:&R:$3 /2< &''*T.
!1
Com o relacionamento6 em a io aos atributos vin os o banco e a os temos a#ora outros ois atributos que representam a relao. Po emos atribui;los normalmenteA
.. a tion.pro-e t ! =ro-e t.find%1( !. ?K=ro-e t:'xbRRa&da8 @attribute"!SQnameQ!.Q/uild a ;ou"eQJ QidQ!.Q1QJ Qde" riptionQ!.Q/uild a dream ,ou"e entirely planned by my"elfJ in luding a intere"ting atti wit, a ,uge library.QJ Qa tiveQ!.Q1QT. .. a tion.pro-e tAid !. 1
.. a tion. ontext ! 4ontext. reate%:name !. Q@=,oneQ( !. ?K4ontext:'xbRR6a1d' @newAre ord!fal"eJ @error"!?KL tive2e ord::1rror":'xbRR65aR @ba"e!?K4ontext:'xbRR6a1d' ....J @error"!ST.J @attribute"!SQnameQ!.Q@=,oneQJ QidQ!.3T.
Relacionamentos so bem Pteis6 mas precisamos ter um pouco e cui a o em como os usamos. Aqui entra a necessi a e e conhecimento e -DL que mencionamos anteriormente. )e=a o caso abai0o6 por e0emploA
>1D14< P 92:B a tion" 0;121 %a tion".id ! *( D7B7< 1 >1D14< P 92:B pro-e t" 0;121 %pro-e t".id ! 1( D7B7< 1
o pro=eto
a ao busca a6
uma elas completamente esnecessria. +bviamente6 se "i>ermos isso em um loop6 com mPltiplos ob=etos e relacionamentos6 o nIvel e ine"ici@ncia subir rapi amente.
!!
Para isso6 o Rails tem uma soluo que resolve a maior parte
.. L tion.find%*J :in lude !. ):pro-e tJ : ontext+( !. ?KL tion:'xbRR*6*f' @ ontext!?K4ontext:'xbRR*8818 @attribute"!SQnameQ!.Q@=,oneQJ QidQ!.Q3QT.J @pro-e t!?K=ro-e t:'xbRR*8a$8 @attribute"!SQnameQ!.Q/uild a ;ou"eQJ QidQ!.Q1QJ Qde" riptionQ!.Q/uild a dream ,ou"e entirely planned by my"elfJ in luding a intere"ting atti wit, a ,uge library.QJ Qa tiveQ!.Q1QT.J @attribute"!SQ ontextAidQ!.Q3QJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.Q1QJ QdoneQ!.nilJ QidQ!.Q*QJ Qde" riptionQ!.QL te"t a tion.QJ Q reatedAatQ!.Q&''*-'6-&1 15:&R:$3QT.
Como voc@ po e ver6 tanto o conte0to como o pro=eto "oram automaticamente recupera os. -e observamos o
L tion Doad 7n luding L""o iation" %'.&3&'R&( >1D14< a tion".\id\ L> t'Ar'J a tion".\de" ription\ L> t'Ar1J a tion".\done\ L> t'Ar&J a tion".\ reatedAat\ L> t'Ar3J a tion".\ ompletedAat\ L> t'Ar$J a tion".\ ontextAid\ L> t'Ar5J a tion".\pro-e tAid\ L> t'Ar*J pro-e t".\id\ L> t1Ar'J pro-e t".\name\ L> t1Ar1J pro-e t".\de" ription\ L> t1Ar&J pro-e t".\a tive\ L> t1Ar3J ontext".\id\ L> t&Ar'J ontext".\name\ L> t&Ar1 92:B a tion" D19< :O<12 ]:78 pro-e t" :8 pro-e t".id ! a tion".pro-e tAid D19< :O<12 ]:78 ontext" :8 ontext".id ! a tion". ontextAid 0;121 %a tion".id ! *(
Ao invJs
uas
centenas ou milhares e chama as ao banco em uma Pnica chama a. )e=a por e0emplo a i"erena entre as uas chama as abai0o6 epois e criarmos tr@s a&esA
.. L tion.find%:all(. olle t S MaM )a.pro-e t.nameJ a. ontext.name+ T !. ))Q/uild a ;ou"eQJ Q@;omeQ+J )Q=lant a <reeQJ Q@0or#Q+J )Q0rite a /oo#QJ Q@;omeQ++
+ ob=etivo
os nomes
os pro=etos e conte0tos
e ca a ao e0iste no
L tion Doad %'.''&R38( =ro-e t Doad %'.''&1*1( 4ontext Doad %'.'''83$( =ro-e t Doad %'.''1&&'( 4ontext Doad %'.''111*( =ro-e t Doad %'.''1&&8( 4ontext Doad %'.''1'RR(
>1D14< P 92:B a tion" >1D14< P 92:B pro-e t" >1D14< P 92:B ontext" >1D14< P 92:B pro-e t" >1D14< P 92:B ontext" >1D14< P 92:B pro-e t" >1D14< P 92:B ontext"
! ! ! ! ! !
1( 1( &( &( 3( 1(
1 1 1 1 1 1
.. L tion.find%:allJ :in lude !. ):pro-e tJ : ontext+(. olle t S MaM )a.pro-e t.nameJ a. ontext.name+ T !. ))Q/uild a ;ou"eQJ Q@;omeQ+J )Q=lant a <reeQJ Q@0or#Q+J )Q0rite a /oo#QJ Q@;omeQ++
!5
L tion Doad 7n luding L""o iation" %'.''$515( >1D14< a tion".\id\ L> t'Ar'J a tion".\de" ription\ L> t'Ar1J a tion".\done\ L> t'Ar&J a tion".\ reatedAat\ L> t'Ar3J a tion".\ ompletedAat\ L> t'Ar$J a tion".\ ontextAid\ L> t'Ar5J a tion".\pro-e tAid\ L> t'Ar*J pro-e t".\id\ L> t1Ar'J pro-e t".\name\ L> t1Ar1J pro-e t".\de" ription\ L> t1Ar&J pro-e t".\a tive\ L> t1Ar3J ontext".\id\ L> t&Ar'J ontext".\name\ L> t&Ar1 92:B a tion" D19< :O<12 ]:78 pro-e t" :8 pro-e t".id ! a tion".pro-e tAid D19< :O<12 ]:78 ontext" :8 ontext".id ! a tion". ontextAid
V "cil ver que essa chama a J bem melhor o que as emais e isso consi eran o al#uns poucos re#istros. /m casos e mo elos mais comple0os6 a partir o Rails 1.1 a eclarao inclu e J recursiva. )oc@ po eria
@order" ! :rdem.find :allJ :in lude !. ):item" !. S :produ tJ :di" ount TJ :"ale"per"on+
+ c< i#o acima buscaria to as as or ens ob=etos escreven o o pro uto associa o e o
a os6 carre#an o
manter a possibili a e em mente para aplic;la em suas pr<prias aplica&es. )amos a#ora intro u>ir nossa mo i"icao "inal em nosso mo elo e a os6 as vali a&esA
la"" L tion K L tive2e ord::/a"e attrAprote ted : reatedAatJ : ompletedAat def done!%value( if L tive2e ord::4onne tionLdapter"::4olumn.valueAtoAboolean%value( "elf. ompletedAat ! Gate<ime.now el"e "elf. ompletedAat ! nil end writeAattribute%QdoneQJ value( end belong"Ato : ontext belong"Ato :pro-e t validate"Apre"en eAof :de" ription validate"Apre"en eAof : ontextAid validate"Apre"en eAof :pro-e tAid end
A#ora que temos os nossos relacionamentos6 po emos atuali>ar o nosso controller e as vieGs #era as para o mesmo. Primeiro6 o controllerA
la"" L tion"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end
!2
def li"t @a tionApage"J @a tion" ! paginate :a tion"J :perApage !. 5J :order !. Qde" riptionQ end def edit @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if param"):id+ @a tion ! L tion.find%param"):id+( el"e @a tion ! L tion.new end if re@ue"t.po"tU @a tion.attribute" ! param"):a tion+ if @a tion."ave fla",):noti e+ ! Q<,e a tion wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
As
Precisamos esses arra,s para a nossa vieG e e io6 como vemos abai0oA
K,1.KY if @a tion.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. L tionK/,1. KY! errorAme""age"Afor Qa tionQ Y. KY! "tartAformAtag :a tion !. QeditQJ :id !. @a tion Y. Kp. Klabel for!Qa tionAde" riptionQ.Ge" ription:K/label.Kbr. KY! textAarea Qa tionQJ Qde" riptionQJ :row" !. 5 Y. K/p. Kp. Klabel for!Qa tionAdoneQ.Gone:K/label.Kbr. KY! "ele t Qa tionQJ QdoneQJ ))QFe"QJ true+J )Q8oQJ fal"e++ Y. K/p. Kp. Klabel for!Qa tionA ontextAidQ.4ontext:K/label.Kbr. KY! "ele t Qa tionQJ Q ontextAidQJ @ ontext"J :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. Klabel for!Qa tionApro-e tAidQ.=ro-e t:K/label.Kbr. KY! "ele t Qa tionQJ Qpro-e tAidQJ @pro-e t"J :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#Ato Q4an elQJ :a tion !. Qli"tQY. K/p. KY! endAformAtag Y.
5$
-elecionar os ob=etos e #erar as estruturas que precisamos no controller J um prtica recomen a a porque mantemos a nossa vieG limpa e po emos "a>er quaisquer or ena&es e "iltros sem nos preocuparmos com o
esi#n
a seleo aparea
A#ora voltamos ao pequeno problema que in icamos e0istir nesse controller anteriormente. -e voc@ tentar salvar uma ao a#ora6 ver que um erro aconteceA
51
/sse erro J causa o por um etalhe. Bo Rails6 a chave params5:action6 = J toma a pelo nome a ao que est sen o e0ecuta a e como temos um ob=eto com esse nome6 acabamos com um problema. /ntretanto6 a soluo J simplesA basta renomear o nosso ob=eto. Po emos6 por e0emplo6 cham;lo e item. Primeiro6 mu amos o controllerA
la"" L tion"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @a tionApage"J @a tion" ! paginate :a tion"J :perApage !. 5J :order !. Qde" riptionQ end def edit
5*
@ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if param"):id+ @item ! L tion.find%param"):id+( el"e @item ! L tion.new end if re@ue"t.po"tU @item.attribute" ! param"):item+ if @item."ave fla",):noti e+ ! Q<,e a tion wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
K,1.KY if @item.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. L tionK/,1. KY! errorAme""age"Afor QitemQ Y. KY! "tartAformAtag :a tion !. QeditQJ :id !. @item Y. Kp. Klabel for!QitemAde" riptionQ.Ge" ription:K/label.Kbr. KY! textAarea QitemQJ Qde" riptionQJ :row" !. 5 Y. K/p. Kp. Klabel for!QitemAdoneQ.Gone:K/label.Kbr. KY! "ele t QitemQJ QdoneQJ ))Q8oQJ fal"e+J )QFe"QJ true++ Y. K/p. Kp. Klabel for!QitemA ontextAidQ.4ontext:K/label.Kbr. KY! "ele t QitemQJ Q ontextAidQJ @ ontext"J :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. Klabel for!QitemApro-e tAidQ.=ro-e t:K/label.Kbr. KY! "ele t QitemQJ Qpro-e tAidQJ @pro-e t"J :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#Ato Q4an elQJ :a tion !. Qli"tQY. K/p. KY! endAformAtag Y.
/sse J um caso raro6 mas se voc@ vir al#um erro misterioso acontecen o "uncionan o6 veri"ique se no h um con"lito. Como um etalhe e0tra6 invertemos acima a or em
o Rails on e tu o
everia estar
que seriam uma ao ain a no concluI a por pa ro. Ro an o a#ora a ao6 temos o resulta o ese=a o.
5'
)amos a#ora mo i"icar o controller mais uma ve> em apoio M vieG se#uinteA
e lista#em
os
a os. :erIamos o
la"" L tion"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @a tionApage"J @a tion" ! paginate :a tion"J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ end def edit @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if param"):id+ @item ! L tion.find%param"):id+( el"e @item ! L tion.new end if re@ue"t.po"tU @item.attribute" ! param"):item+ if @item."ave fla",):noti e+ ! Q<,e a tion wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
A incluso os relacionamentos ir melhorar a nossa vieG. )e=a que precisamos a#ora especi"icar a con io e or enao mais claramente6 = que temos uma coluna chama a description em uas tabelas. Bossa vieG "icaria assimA
K,1.L tion"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew L tionQJ :a tion !. QeditQ Y.K/p. Ktable border!Q1Q ellpadding!Q5Q Ktr. Kt,.Ge" riptionK/t,. Kt,.4ompletedK/t,. Kt,.=ro-e tK/t,. Kt,.4ontextK/t,. Kt,.L tion"K/t,. K/tr. KY @a tion".ea , do Ma tionM Y. ell"pa ing!Q1Q.
54
Ktr. Ktd.KY! a tion.de" ription Y.K/td. Ktd.KY! ye"AorAnoU%a tion.doneU( Y.K/td. Ktd.KY! a tion.pro-e t.name Y.K/td. Ktd.KY! a tion. ontext.name Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. a tion Y. or KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. a tion TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y. K/table. Kp.KY! paginationAlin#"%@a tionApage"( Y.K/p.
-e quisermos6 po emos tambJm acrescentar um campo in"orman o quan o a ao "oi completa a6 "ormatan o a ata em que a mesma "oi completa a a maneira necessria M nossa aplicao6 ou e0ibin o um marca or caso contrrio. Bossa vieG po eria "icar como al#o assimA
K,1.L tion"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew L tionQJ :a tion !. QeditQ Y.K/p.
55
Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.Ge" riptionK/t,. Kt,.4ompletedK/t,. Kt,.0,enK/t,. Kt,.=ro-e tK/t,. Kt,.4ontextK/t,. Kt,.L tion"K/t,. K/tr. KY @a tion".ea , do Ma tionM Y. Ktr. Ktd.KY! a tion.de" ription Y.K/td. Ktd.KY! ye"AorAnoU%a tion.doneU( Y.K/td. Ktd.KY! %a tion.doneU( U a tion. ompletedAat."trftime%QYm/Yd/YyQ( : Q-Q Y.K/td. Ktd.KY! a tion.pro-e t.name Y.K/td. Ktd.KY! a tion. ontext.name Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. a tion Y. or KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. a tion TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y. K/table. Kp.KY! paginationAlin#"%@a tionApage"( Y.K/p.
51
HAS MANY
AlJm o relacionamento belongs0to4 o Rails tambJm possui um outro relacionamento chama o has0many. i> que um mo elo possui muitos e outros mo elos. /m issoA um conte0to possui muitas a&es e um pro=eto uas instLncias ime iatas Como o pr<prio nome in ica6 esse relacionamento nossa aplicao6 nos temos possui muitas a&es. (o i"ican o inicialmente o mo elo e conte0tos6 terIamosA
la"" 4ontext K L tive2e ord::/a"e validate"Apre"en eAof :name validate"Auni@uene""Aof :nameJ :me""age !. Qmu"t be uni@ueQ ,a"Amany :a tion" end
ronaldo@minerva:~/tmp/gtd$ " ript/ on"ole Doading development environment. .. ontext ! 4ontext.find%1( !. ?K4ontext:'xbR3ff1f8 @attribute"!SQnameQ!.Q@;omeQJ QidQ!.Q1QT. .. ontext.a tion". ount !. 1 .. ontext.a tion" !. )?KL tion:'xbR3f&6* @attribute"!SQ ontextAidQ!.Q1QJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.Q3QJ QdoneQ!.Q'QJ QidQ!.Q6QJ Qde" riptionQ!.Q>it down and writeJ every dayJ at lea"t two ,our" a day.QJ Q reatedAatQ!.Q&''*-'6-&1 1*:31:1$QT.+
.. a tion ! L tion. reate%:de" ription !. Q/uy a grammar to ,elp me wit, my 1ngli",.QJ : ontextAid !. 1J :pro-e tAid !. 3( !. ?KL tion:'xbR8f5$$8 @newAre ord!fal"eJ @error"!?KL tive2e ord::1rror":'xbR8e8*$$ @ba"e!?KL tion:'xbR8f5$$8 ....J @error"!ST.J @attribute"!SQ ontextAidQ!.1J Q ompletedAatQ!.nilJ Qpro-e tAidQ!.3J QdoneQ!.nilJ QidQ!.11J Qde" riptionQ!.Q/uy a grammar to ,elp me wit, my 1ngli",.QJ Q reatedAatQ!.<,u >ep &1 1*:5&:$' /2< &''*T. .. ontext.a tion" !. )?KL tion:'xbR3f&6* @attribute"!SQ ontextAidQ!.Q1QJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.Q3QJ QdoneQ!.Q'QJ QidQ!.Q6QJ Qde" riptionQ!.Q>it down and writeJ every dayJ at lea"t two ,our" a day.QJ Q reatedAatQ!.Q&''*-'6-&1 1*:31:1$QT.+ .. ontext.a tion".reload !. )?KL tion:'xbRRR8* @attribute"!SQ ontextAidQ!.Q1QJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.Q3QJ QdoneQ!.Q'QJ QidQ!.Q6QJ Qde" riptionQ!.Q>it down and writeJ every dayJ at lea"t two ,our" a day.QJ Q reatedAatQ!.Q&''*-'6-&1 1*:31:1$QT.J ?KL tion:'xbRRR8*6' @attribute"!SQ ontextAidQ!.Q1QJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.Q3QJ QdoneQ!.nilJ QidQ!.Q11QJ Qde" riptionQ!.Q/uy a grammar to ,elp me wit, my 1ngli",.QJ Q reatedAatQ!.Q&''*-'6-&1 1*:5&:$'QT.+
5!
Bote que6 como usan o um ob=eto = carre#a o6 a coleo atravJs e seu pr<prio constructor e no usan o os mJto os
nova ao "oi a iciona a. ?sso acontece porque o Rails "a> um cache para recarre#ar a coleo caso voc@ precise. .m outro e0emplo seriaA
.. ontext.a tion" KK L tion. reate%:de" ription !. QOpgrade my word pro e""or.QJ :pro-e tAid !. 3( !. )?KL tion:'xbRRR8* @attribute"!SQ ontextAidQ!.Q1QJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.Q3QJ QdoneQ!.Q'QJ QidQ!.Q6QJ Qde" riptionQ!.Q>it down and writeJ every dayJ at lea"t two ,our" a day.QJ Q reatedAatQ!.Q&''*-'6-&1 1*:31:1$QT.J ?KL tion:'xbRRR8*6' @attribute"!SQ ontextAidQ!.Q1QJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.Q3QJ QdoneQ!.nilJ QidQ!.Q11QJ Qde" riptionQ!.Q/uy a grammar to ,elp me wit, my 1ngli",.QJ Q reatedAatQ!.Q&''*-'6-&1 1*:5&:$'QT.J ?KL tion:'xbRR531*' @newAre ord!fal"eJ @error"!?KL tive2e ord::1rror":'xbRR513b' @ba"e!?KL tion:'xbRR531*' ....J @error"!ST.J @attribute"!SQ ontextAidQ!.1J Q ompletedAatQ!.nilJ Qpro-e tAidQ!.3J QdoneQ!.nilJ QidQ!.1&J Qde" riptionQ!.QOpgrade my word pro e""or.QJ Q reatedAatQ!.<,u >ep &1 1*:56:&1 /2< &''*T.+ .. ontext.a tion"."ize !. 3
que J pe#o automaticamente no ob=etoK se#un oA a coleo cresceu automaticamente6 = que estamos manipulan o os a os iretamente na mesma. Ao invJs e usar os mJto os acima para criar a ao6 voc@ po e usar iretamente os mJto os build e
create na coleo para a icionar novos a os. + primeiro "unciona para ob=etos = e0istentes na coleo e o se#un o para a icionar um novo ob=eto. .ma coleo tambJm possui um mJto o find para encontrar ob=etos mesmas re#ras o mJto o find usa o para classes e a os. Por "im6 por ra>&es valores. Bo caso e e"ici@ncia6 to a coleo eclara um mJto o o ob=eto pai para atribuio rpi a e entro a mesma6 e acor o com as
?sso removeria qualquer ao associa a Mquele conte0to6 substituin o;as pelas a&es i enti"ica as pelos i s acima.
55
+ conte0to acima teria to as as suas a&es substituI as pelas a&es associa as ao pro=eto cu=o i J 5. ?nvesti#an o a e0emplo ocumentao voc@ po er encontrar mais estruio automtica etalhes sobre como ca a mJto o "unciona. .m
isso J a
um ob=eto ter a "uncionali a e equivalente e uma eclarao casca e em um banco e a os. .ma coisa que eve ser nota a J que muitos esse mJto os causam o salvamento ime iato os ob=eto pai = est salvoT. Com a prtica6 voc@ ser capa> os casos. Bo vamos utili>ar esses mJto os em nossa aplicao no momento porque veremos uma aplicao similar mais M "rente com outro tipo e relacionamento pareci o. apropria amente caso queria outra ao6 embora o comportamento pa ro se=a o a os Sse o
HAS ONE
Dois outros relacionamentos e0istentes no Rails so has0and0belongs0to0many e has0one. + relacionamento has0one #eralmente e0pressa o oposto i#amos que temos as se#uintes classesA uas tabelasA uma conten o cart&es o relacionamento belongs0to. Por e0emplo6 e cart&es.
Ba tabela e carto e crJ ito terIamos uma chave estran#eira apontan o para o seu titular. :erIamos ento
la"" 4redit4ard K L tive2e ord::/a"e belong"Ato :a ountA,older end la"" L ount;older K L tive2e ord::/a"e ,a"Aone : reditA ard end
i"erena J que a chave estran#eira e0iste somente na tabela relaciona a M classe 7redit7ard6 sen o
automaticamente e u>i a no relacionamento inverso. /sse e0emplo no J muito bom = que6 em tese6 uma pessoa po eria ter vrios cart&es i Jia J essa. Ba prtica6 esse tipo relacionamentos o tipo has0many/ou has0many0and0belongs0to. + relacionamento has0many0and0belongs0to6 por sua ve>6 representa a interme iao entre uas tabelas. e crJ ito. (as a
52
esenvolve or po e estar
loca o em mPltiplos pro=etos e um pro=eto po e ter mPltiplos esenvolve ores. )eremos mais etalhes esse relacionamento a iante quan o "i>ermos mais mo i"ica&es em nossa aplicao para suportar outras caracterIsticas.
ronaldo@minerva:~/tmp/gtd$ " ript/ on"ole Doading development environment. .. pro-e t ! =ro-e t.find%3( !. ?K=ro-e t:'xbR8'83f @attribute"!SQnameQ!.Q0rite a /oo#QJ QidQ!.Q3QJ Qde" riptionQ!.QQJ Qa tiveQ!.Q1QT. .. pro-e t.a tion". olle t S MaM a. ontext.name T !. )Q@;omeQJ Q@;omeQJ Q@;omeQ+
Como voc@ po e ver6 o conte0to J o mesmo para to as as a&es associa as Mquele pro=eto e queremos os conte0tos sem repetio. .ma soluo simples seriaA
.. ontext" ! pro-e t.a tion". olle t S MaM a. ontext.name T !. )Q@;omeQJ Q@;omeQJ Q@;omeQ+ .. ontext".uni@ !. )Q@;omeQ+
e usar mais c< i#o6 essa soluo J muito ine"iciente por causa
as compara&es
que precisam ser reali>a as pelo mJto o uniq. Po emos resolver o problema usan o ento a se#uinte
la"" =ro-e t K L tive2e ord::/a"e validate"Apre"en eAof :name validate"Auni@uene""Aof :name ,a"Amany :a tion" ,a"Amany : ontext"J :t,roug, !. :a tion"J :"ele t !. Qdi"tin t end ontext".PQ
2$
.name T
Como voc@ po e ver6 muito mais "cil e interessante e6 em quase to os os casos6 mais e"iciente. Bo relacionamento acima6 temos os conte0tos e um pro=ecto obti os atravJs Sthrou#hT e suas a&es. -e
no "or necessrio preocupar com c<pias Scomo no e0emplo as notas "iscaisT po eremos inclusive escartar o parLmetro select que J um "ra#mento e -DL usa o no lu#ar o #era o automaticamente pelo Rails. Associa&es throu#h so muito po erosas e po em ser utili>a as em vrias situa&es para a#ili>ar o c< i#o e melhorar a le#ibili a e a aplicao (ais a iante6 veremos tambJm outra e0tenso e associa&es has0many conheci as como associa&es
polim<r"icas6 que tambJm po e ser usa as para mais combina&es interessantes e mo elos e a os. A#ora que = vimos o bsico para um aplicao6 J hora e "ocar em al#uns t<picos mais avana os.
Avanando no Rails
FILTROS
Bossa aplicao atJ o momento estaria permiti os. )amos6 ento6 a icionar um sistema rpi o al#uns conceitos e autenticao M aplicao6 aproveitan o para e0plorar mais e isponIvel para qualquer pessoa que ese=asse us;la. /m um e usurios cenrio normal6 provavelmente #ostarIamos e restrin#ir acesso para somente um #rupo
o Rails. .ma observao aqui J que e0istem plu#ins prontos com sistemas
autenticao "uncionais6 plenamente customi>veis. Como tu o no Rails6 a escolha J sua. A primeira coisa que temos que "a>er J criar uma tabela representar a nossa tabelaA e usurios para conter os lo#ins e senhas as
pessoas que po ero acessar o sistema. Para isso6 vamos se#uir o caminho "amiliar6 #eran o um mo el para
ronaldo@minerva:~/tmp/gtd$ " ript/generate model u"er exi"t" app/model"/ exi"t" te"t/unit/ exi"t" te"t/fixture"/ reate app/model"/u"er.rb reate te"t/unit/u"erAte"t.rb reate te"t/fixture"/u"er".yml
21
exi"t" reate
/ itan o a mi#rao #era a6 "icarIamos6 pensan o em al#o bem bsico6 com o se#uinteA
la"" 4reateO"er" K L tive2e ord::Bigration def "elf.up reateAtable :u"er" do MtM t. olumn :nameJ :"tring t. olumn :loginJ :"tringJ :limit !. 1' t. olumn :pa""wordJ :"tringJ :limit !. 1' end O"er.re"etA olumnAinformation O"er. reate%:name !. Q2onaldoQJ :login !. QronaldoQJ :pa""word !. Qte"tQ( end def "elf.down dropAtable :u"er" end end
o parLmetro limit para #erar campos te0to com um tamanho menor e que = estou crian o um epois e termos mo i"ica o a aplicao sem e criar um ca astro e usurios ime iatamente. /sse ca astro "ica como um e0ercIcio para o
leitor6 sen o uma boa oportuni a e para usar vali a&es como !alidates0confirmation0of4 que voc@ po e achar na ocumentao o Rails. + que precisamos a#ora J al#uma maneira e0ecuta os antes ou e0ecuta os antes epois e bloquear o acesso aos nossos controllers = cria os caso o e "iltros6 que so mJto os e "iltrosA os epois e uma e uma ao. + Rails possui tr@s tipos e uma ao e os e0ecuta os antes e
usurio no este=a autentica o. ?sso J conse#ui o com al#o que o Rails chama o processamento e uma ao6 os e0ecuta os epois
ao Saroun "ilters6 na terminolo#ia a ocumentaoT. Dualquer "iltro em Rails po e retornar um valor "also para bloquear o processamento posterior a ao. Bo nosso caso6 J e0atamente isso que precisamosA caso um usurio no este=a autentica o6 no permitiremos que ele acesse um controller. Como queremos "a>er isso para to os os nossos controllers e0istentes6 usaremos um "iltro no controller qual to os os outros so se o usurio est ou no autentica o6 usaremos uma varivel e sesso. (o i"ican o o nosso c< i#o6 inicialmente terIamos al#o assimA o
2*
Bo c< i#o acima6 a menos que tenhamos uma varIavel "also6 bloquean o o processamento
completamente va>ia ser retorna a6 sem nem mesmo o c< i#o o la,out. +bviamente6 o que precisamos J e uma "orma e permitir que o usurio "aa sua autenticao. )amos
mo i"icar o c< i#o acima para li ar com com essa possibili a e e enviar o usurio para um local on e ele possa "a>er autenticao caso ain a no tenha "eitoA
la"" Lppli ation4ontroller K L tion4ontroller::/a"e beforeAfilter :aut,enti ate prote ted def aut,enti ate unle"" "e""ion):u"er+ "e""ion):returnAto+ ! re@ue"t.re@ue"tAuri redire tAto : ontroller !. QloginQJ :a tion !. QloginQ return fal"e end return true end end
re irecionamos o usurio para um novo controller que "ara a sua autenticao. Besse caso6 retornamos "also para bloquear o resto a ao6 se=a qual "or. -e a varivel e0istir6 simplesmente retornamos ver a eiro para ei0ar que o c< i#o si#a seu caminho usual. .m toque a icional J salvar a .RL que o usurio tentou acessar para retornar a ela uma ve> que a autenticao se=a "eita. A#ora6 precisamos e nosso controller e autenticao. / alJm a autenticao6 po emos tambJm permitir epois que ele saiu a aplicao. -en o
que o usurio encerre sua sesso6 para que ela no "ique aberta
assim6 precisamos e uas a&esA lo#in e lo#out. De acor o com isso6 #eramos o controller abai0oA
ronaldo@minerva:~/tmp/gtd$ " ript/generate ontroller login login logout exi"t" app/ ontroller"/ exi"t" app/,elper"/ reate app/view"/login exi"t" te"t/fun tional/ reate app/ ontroller"/loginA ontroller.rb reate te"t/fun tional/loginA ontrollerAte"t.rb reate app/,elper"/loginA,elper.rb reate app/view"/login/login.r,tml reate app/view"/login/logout.r,tml
2'
-e voc@ ro ar uma p#ina qualquer a#ora ver que o servi or entra um loop in"inito. ?sso acontece por causa e um etalhe "un amentalA o controller que acabamos e criar tambJm est su=eito ao "iltro. A soluo J simples. 3asta a icionar uma linha ao controllerA
la"" Dogin4ontroller K Lppli ation4ontroller "#ipAbeforeAfilter :aut,enti ateJ :only !. ):login+ def login end def logout end end
e se#urana6 "a>en o e0clus&es somente quan o necessrio. Bo caso acima6 mesmo para a
ao lo#out terIamos que estar autentica os. Ba nossa implementao J provvel que essa ao no "aa e mais. /m um implementao real6 porJm6 ela po eria ser to importante quanto qualquer outra
Resolvi o esse problema6 po emos partir para a nossa implementao se=a capa> e "a>er sua autenticao6 precisamos responsvel pela autenticao6 app/!iews/login/login.rhtmlA
K,1.Dogin to >iteK/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: red5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. KY! "tartAformAtag :a tion !. QloginQ Y. Kp.=lea"eJ enter your login and pa""word to aut,enti ate to t,i" "ite:K/p. Kp. Klabel for!QloginQ.Dogin:K/label.Kbr. KY! textAfieldAtag QloginQJ param"):login+J :maxlengt, !. 1' Y. K/p. Kp. Klabel for!Qpa""wordQ.=a""word:K/label.Kbr. KY! pa""wordAfieldAtag Qpa""wordQJ QQJ :maxlengt, !. 1' Y. K/p. Kp. KY! "ubmitAtag Q1nterQ Y. K/p. KY! endAformAtag Y.
i"erentes e atribuir
os mJto os a os
24
e #erao
e campos
um valor a ser iniciali>a o. Bo caso6 usamos um lo#in previamente submeti o para preencher o campo
epois e "alhas
e uma "alha
e autenticao. Q
"lash.
:erIamos o se#uinte resulta oA
la"" Dogin4ontroller K Lppli ation4ontroller "#ipAbeforeAfilter :aut,enti ateJ :only !. ):login+ def login if re@ue"t.po"tU @u"er ! O"er.findAbyAloginAandApa""word%param"):login+J param"):pa""word+( if @u"er "e""ion):u"er+ ! @u"er.id if "e""ion):returnAto+ [[ W"e""ion):returnAto+.in ludeU%urlAfor%:a tion !. QloginQ(( redire tAto "e""ion):returnAto+ "e""ion):returnAto+ ! nil el"e redire tAto : ontroller !. Q,omeQ end el"e fla",):noti e+ ! Q7nvalid login and/or pa""word.Q end end end
25
.m lo#in vli o nos levaria para o controller que tentamos acessar6 se=a qual "or ele. + c< i#o J bem simples. Analisan o linha a linha6 terIamos uma seqR@ncia "cil. Primeiro6 testamos se o "ormulrio est sen o submeti o. -e estiver6 tentamos encontrar um usurio que aten a Mquele lo#in e senha. Aqui voc@ po e ver um uso os mJto os find0@/automaticamente #era os. -e no encontrarmos um re#istro que aten a esses critJrios6 simplesmente ren eri>amos a mesma p#ina6 in"orman o uma mensa#em ao usurio. Caso encontremos um usurio6 porJm6 re#istramos o seu i motivos para re#istrar somente o i so na varivel a sessoK e sesso que escolhemos. +s o que ois6 se a estrutura o ob=eto
mu ar ou6 por al#um motivo6 o Rails no encontrar a classe naquela requisio6 um erro ser #era o.
21
Ap<s re#istramos o i
ou no um re irecionamento e6 caso e0ista6 se no J para o controller que estamos a#ora. ?sso6 mais uma ve>6 evita erros e re ire&es in"initas. -e tivermos um valor vli o6 re irecionamos para o mesmo. -e no6 meramente acessamos a p#ina inicial a aplicao. Com isso6 temos um sistema simples e "uncional e autenticao que voc@ po e aumentar e acor o com
suas necessi a es6 a icionan o autenticao se#ura6 autenticao via hashes ou qualquer outra coisa que ese=e. Botamos mais uma ve> que e0istem plu#ins para a=u ar na implementao essas tare"as. Po emos aproveitar a in"ormao que temos "acili a e ao usurio. Primeiro6 precisamos e sesso e mo i"icar o nosso la,out para ar mais uma
module Lppli ation;elper def ye"AorAnoU%value( value U QFe"Q : Q8oQ end def "e""ionAu"er @"e""ionAu"er MM! O"er.find%:fir"tJ : ondition" !. )Hid ! UHJ "e""ion):u"er++( end end
A tJcnica acima J um emonstrao o que h e melhor o Rub,. Dueremos retornar o ob=eto associa o ao usurio autentica o. Po e acontecer e termos que usar esse ob=eto mais e um ve>. 7a>er um requisio no banco to a ve> em que precisarmos o ob=eto seria bem ine"iciente. + que "a>emos ento J criar uma varivel primeiro que no "or nulo. Como retorno ser o e instLncia que contenha o ob=eto retorna o. A sinta0e acima e sesso ser nula6 o
si#ni"ica que retornamos a varivel e instLncia ou S^^T o valor a chama a ao mJto o find a classe Aser6 o a primeira ve> que a chama a "or "eita a varivel a chama a. (as6 nesse momento6 atribuImos S^^_T o valor retorna o M varivel6 que ser
ento retorna o em to as as ve>es subseqRentes. Bote que usamos find com :first /para evitarmos um erro caso o usurio no e0ista. Besse caso6 queremos somente que um valor nulo se=a a o quan o a sesso no e0istir6 ao contrrio e um erro. A#ora6 mo i"icamos o la,out a aplicao6 em app/!iews/layouts/application.rhtmlA
2!
KY! "tyle",eetAlin#Atag Q" affoldQ Y. K/,ead. Kbody. K,1 id!Q,eaderQ.X<GK/,1. KY if "e""ionAu"er Y. Kp id!Qlogin-informationQ. Fou are logged in a" K"trong.KY! "e""ionAu"er.name Y.K/"trong.. KY! lin#Ato QDog outQJ : ontroller !. QloginQJ :a tion !. QlogoutQ Y.. K/p. KY end Y. Kul id!QmenuQ. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 4ontext"QJ : ontroller !. Q ontext"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 =ro-e t"QJ : ontroller !. Qpro-e t"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 L tion"QJ : ontroller !. Qa tion"QJ :a tion !. Qli"tQ Y.K/li. K/ul. Kdiv id!Q ontent"Q.KY! yield Y.K/div. K/body. K/,tml.
Por
"im6
acrescentamos
se#uinte
"ra#mento
estilo
st,lesheet
aplicao
em
public/stylesheets/default.cssA
?login-information S po"ition: ab"olute5 rig,t: 1'px5 top: 5px5 margin: '5 padding: '5 font-"ize: "mall5 T
+ resulta o J esseA
25
Po emos a#ora implementar a ao que remove a sesso o usurio6 que J bem simplesA
la"" Dogin4ontroller K Lppli ation4ontroller "#ipAbeforeAfilter :aut,enti ateJ :only !. ):login+ def login if re@ue"t.po"tU @u"er ! O"er.findAbyAloginAandApa""word%param"):login+J param"):pa""word+( if @u"er "e""ion):u"er+ ! @u"er.id if "e""ion):returnAto+ [[ W"e""ion):returnAto+.in ludeU%urlAfor%:a tion !. QloginQ(( redire tAto "e""ion):returnAto+ "e""ion):returnAto+ ! nil el"e redire tAto : ontroller !. Q,omeQ end el"e fla",):noti e+ ! Q7nvalid login and/or pa""word.Q end end end def logout "e""ion):u"er+ ! nil end end
22
7iltros tambJm po em ser usa os em mPltiplas combina&es. Para mo elo e a os e usurios para incluir tambJm o conceito Pnicos que po em ca astrar novos usurios. Comeamos #eran o uma mi#raoA
e a ministra ores
/ e itan o a mesmaA
la"" LddLdmini"trativeO"er" K L tive2e ord::Bigration def "elf.up addA olumn :u"er"J :adminJ :boolean O"er.re"etA olumnAinformation O"er.findAbyAname%QronaldoQ(.toggleW%QadminQ( end def "elf.down removeA olumn :u"er"J :admin end end
os outros
1$$
controllers que temos. -e voc@ #erou esse controller anteriormente6 o resulta o "inal eve ser assemelhar a
al#o assimA
Lembre;se e alterar o arquivo application.rhtml em app/!iews/layouts para re"letir o novo menu. Como a rea e usurio somente po e ser acessar por usurios a ministrativos6 precisamos e uma "orma
e recusar acesso M mesma caso o usurio no tenha esse per"il. 7aremos isso com o uso e um "iltro e0tra6 aplicvel somente aos controllers que estiverem sob
a ministrao6 que6 no momento6 J somente o pr<prio controller e usurios. /ntretan o6 como o "iltro po e ser aplica o a mais e um controller ei0aremos sua e"inio no controller rai>6 application.rbA
la"" Lppli ation4ontroller K L tion4ontroller::/a"e beforeAfilter :aut,enti ate prote ted def aut,enti ate unle"" "e""ion):u"er+ "e""ion):returnAto+ ! re@ue"t.re@ue"tAuri redire tAto : ontroller !. QloginQJ :a tion !. QloginQ return fal"e end return true end def aut,enti ateAadmini"tration unle"" "e""ionAu"er [[ "e""ionAu"er.adminU
1$1
redire tAto :a tion !. Qa return fal"e end return true end end
e""AdeniedQ
re irecionamos para uma ao que in"orma que o acesso "oi ne#a o. A#ora6 precisamos somente aplicar o
la"" O"er"4ontroller K Lppli ation4ontroller beforeAfilter :aut,enti ateAadmini"tration def index li"t render :a tion !. Qli"tQ end def li"t @u"erApage"J @u"er" ! paginate :u"er"J :perApage !. 5J :order !. QnameQ end def edit if param"):id+ @u"er ! O"er.find%param"):id+( el"e @u"er ! O"er.new end if re@ue"t.po"tU @u"er.attribute" ! param"):u"er+ if @u"er."ave fla",):noti e+ ! Q<,e u"er wa" "u redire tAto :a tion !. Qli"tQ end end end def delete O"er.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e u"er wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully "avedQ
e""fully deletedQ
Ao carre#armos o controller e usurio6 veremos que um erro acontece6 = que o mJto o session0user no e0iste nem nesse controller nem no controller rai>6 estan o presente somente no helper. A soluo J pelo pr<prio RailsA movemos o mJto o o helper para o controller e o manten o o acesso ao mesmo nas vieGs. + resulta o "inal6 epois e movermos o mJto o J o se#uinteA a a eclaramos l como um helper6
la"" Lppli ation4ontroller K L tion4ontroller::/a"e beforeAfilter :aut,enti ate ,elperAmet,od :"e""ionAu"er prote ted
1$*
def "e""ionAu"er @"e""ionAu"er MM! O"er.find%:fir"tJ : ondition" !. )Hid ! UHJ "e""ion):u"er++( end def aut,enti ate unle"" "e""ion):u"er+ "e""ion):returnAto+ ! re@ue"t.re@ue"tAuri redire tAto : ontroller !. QloginQJ :a tion !. QloginQ return fal"e end return true end def aut,enti ateAadmini"tration unle"" "e""ionAu"er [[ "e""ionAu"er.adminU redire tAto :a tion !. Qa e""AdeniedQ return fal"e end return true end end
K,tml. K,ead. Ktitle.X<GK/title. KY! "tyle",eetAlin#Atag QdefaultQ Y. KY! "tyle",eetAlin#Atag Q" affoldQ Y. K/,ead. Kbody. K,1 id!Q,eaderQ.X<GK/,1. KY if "e""ionAu"er Y. Kp id!Qlogin-informationQ. Fou are logged in a" K"trong.KY! "e""ionAu"er.name Y.K/"trong.. KY! lin#Ato QDog outQJ : ontroller !. QloginQJ :a tion !. QlogoutQ Y.. K/p. KY end Y. Kul id!QmenuQ. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 4ontext"QJ : ontroller !. Q ontext"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 =ro-e t"QJ : ontroller !. Qpro-e t"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 L tion"QJ : ontroller !. Qa tion"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 2e"our e"QJ : ontroller !. Qre"our e"QJ :a tion !. Qli"tQ Y.K/li. KY if "e""ionAu"er [[ "e""ionAu"er.adminU Y. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 O"er"QJ : ontroller !. Qu"er"QJ :a tion !. Qli"tQ Y.K/li. KY end Y. K/ul. Kdiv id!Q ontent"Q.KY! yield Y.K/div. K/body. K/,tml.
1$'
1$4
para o outro per"il6 teremos um re irecionamento. ?sso acontece porque a ao que "a> a ne#ao e acesso tambJm est su=eita ao "iltro. Resolvemos isso com a se#uinte mu ana em nosso controller primrioA
la"" Lppli ation4ontroller K L tion4ontroller::/a"e beforeAfilter :aut,enti ate "#ipAbeforeAfilter :aut,enti ateAadmini"trationJ :only !. ):a ,elperAmet,od :"e""ionAu"er prote ted def "e""ionAu"er @"e""ionAu"er MM! O"er.find%:fir"tJ : ondition" !. )Hid ! UHJ "e""ion):u"er++( end def aut,enti ate unle"" "e""ion):u"er+ "e""ion):returnAto+ ! re@ue"t.re@ue"tAuri redire tAto : ontroller !. QloginQJ :a tion !. QloginQ return fal"e end return true end def aut,enti ateAadmini"tration unle"" "e""ionAu"er [[ "e""ionAu"er.adminU redire tAto :a tion !. Qa e""AdeniedQ return fal"e end return true end end e""Adenied+
eclarao6 partin o
o "iltro presente no
controller base e que po emos e0cluir um "iltro para etermina as a&es mesmo antes e eclararmos o uso
Precisamos a#ora apenas criar a nossa ao accessY enie . Como essa J uma ao que ser compartilha a por to os os controllers6 tambJm vamos e"inila no mesmo arquivoA
la"" Lppli ation4ontroller K L tion4ontroller::/a"e beforeAfilter :aut,enti ate "#ipAbeforeAfilter :aut,enti ateAadmini"trationJ :only !. ):a ,elperAmet,od :"e""ionAu"er def a e""Adenied render :template !. Q",ared/a end prote ted def "e""ionAu"er @"e""ionAu"er MM! O"er.find%:fir"tJ : ondition" !. )Hid ! UHJ "e""ion):u"er++( e""AdeniedQ e""Adenied+
1$5
end def aut,enti ate unle"" "e""ion):u"er+ "e""ion):returnAto+ ! re@ue"t.re@ue"tAuri redire tAto : ontroller !. QloginQJ :a tion !. QloginQ return fal"e end return true end def aut,enti ateAadmini"tration unle"" "e""ionAu"er [[ "e""ionAu"er.adminU redire tAto :a tion !. Qa e""AdeniedQ return fal"e end return true end end
Bote que estamos usan o um template compartilha o tambJm. Precisamos isso porque6 como menciona o anteriormente6 o Rails acha vieGs pelo controller. .san o o mJto o acima6 #arantimos que o mesmo arquivo sempre ser chama o. De"inimos a#ora o arquivo6 que "icar o para o se#uinteA iret<rio app/!iews/shared6 com o nome access0denied.rhtml6
K,1.L
e"inir a
1$1
sua pr<pria ao ou sua pr<pria vieG. Aqui vamos que to os os conceitos "uncionam no Rails e que o "rameGor86 ao contrrio e"ini&es M parte a lin#ua#em. Bossa aplicao a#ora contJm um mecanismo bsico implementao e mais coisas interessantes na mesma.
UPLOADS
.ma as coisas que po emos "a>er e arquivos. e maneira relativamente simples com o Rails J #erenciar o uploa e
oGnloa
/m uma aplicao como a nossa6 ca a ao po eria6 por e0emplo6 estar associa a a um ou mais arquivos. 7a> senti o pensar tambJm que um arquivo po e estar associa o a mais po e precisar e um sobre a mesma. ?sso implicaria em um #erencia or e associ;los com nossas a&es. A primeira coisa que temos que "a>er a#ora6 ento6 J criar uma maneira aplicao. Para isto6 vamos comear com o nosso mo elo e a osA e enviar arquivos para a nossa e uma aoA por e0emplo6 voc@ ocumento para elaborar uma proposta e tambJm para conversar com o seu cliente e arquivos ou recursos em nossa aplicao e um mo o
ronaldo@minerva:~/tmp/gtd$ " ript/generate model re"our e exi"t" app/model"/ exi"t" te"t/unit/ exi"t" te"t/fixture"/ reate app/model"/re"our e.rb reate te"t/unit/re"our eAte"t.rb reate te"t/fixture"/re"our e".yml exi"t" db/migrate reate db/migrate/''RA reateAre"our e".rb
la"" 4reate2e"our e" K L tive2e ord::Bigration def "elf.up reateAtable :re"our e" do MtM t. olumn :nameJ :"tring t. olumn :filenameJ :"tring end end def "elf.down dropAtable :re"our e" end end
Bo comeo6 precisamos somente e arma>enar o nome o recurso e o nome o seu arquivo. -e precisarmos e mais al#uma coisa6 sempre po emos voltar e corri#ir o nosso mo elo com uma nova mi#rao
1$!
ronaldo@minerva:~/tmp/gtd$ " ript/generate ontroller re"our e" index li"t edit delete exi"t" app/ ontroller"/ exi"t" app/,elper"/ reate app/view"/re"our e" exi"t" te"t/fun tional/ reate app/ ontroller"/re"our e"A ontroller.rb reate te"t/fun tional/re"our e"A ontrollerAte"t.rb reate app/,elper"/re"our e"A,elper.rb reate app/view"/re"our e"/index.r,tml reate app/view"/re"our e"/li"t.r,tml reate app/view"/re"our e"/edit.r,tml reate app/view"/re"our e"/delete.r,tml
la"" 2e"our e K L tive2e ord::/a"e validate"Apre"en eAof :name validate"Apre"en eAof :filename end
K,tml. K,ead. Ktitle.X<GK/title. KY! "tyle",eetAlin#Atag QdefaultQ Y. KY! "tyle",eetAlin#Atag Q" affoldQ Y. K/,ead. Kbody. K,1 id!Q,eaderQ.X<GK/,1. KY if "e""ionAu"er Y. Kp id!Qlogin-informationQ. Fou are logged in a" K"trong.KY! "e""ionAu"er.name Y.K/"trong.. KY! lin#Ato QDog outQJ : ontroller !. QloginQJ :a tion !. QlogoutQ Y.. K/p. KY end Y. Kul id!QmenuQ. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 4ontext"QJ : ontroller !. Q ontext"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 =ro-e t"QJ : ontroller !. Qpro-e t"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 L tion"QJ : ontroller !. Qa tion"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 2e"our e"QJ : ontroller !. Qre"our e"QJ :a tion !. Qli"tQ Y.K/li. KY if "e""ionAu"er [[ "e""ionAu"er.adminU Y. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 O"er"QJ : ontroller !. Qu"er"QJ :a tion !. Qli"tQ Y.K/li. KY end Y. K/ul. Kdiv id!Q ontent"Q.KY! yield Y.K/div.
1$5
K/body. K/,tml.
la"" 2e"our e"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @re"our eApage"J @re"our e" ! paginate :re"our e"J :perApage !. 5J :order !. QnameQ end def edit if param"):id+ @re"our e ! 2e"our e.find%param"):id+( el"e @re"our e ! 2e"our e.new end if re@ue"t.po"tU @re"our e.attribute" ! param"):re"our e+ if @re"our e."ave fla",):noti e+ ! Q<,e re"our e wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def delete 2e"our e.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e re"our e wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
K,1.2e"our e"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew 2e"our eQJ :a tion !. QeditQ Y.K/p. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. Kt,.9ileK/t,. Kt,.L tion"K/t,. K/tr. KY @re"our e".ea , do Mre"our eM Y. Ktr. Ktd.KY! re"our e.name Y.K/td. Ktd.KY! re"our e.filename Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. re"our e Y. or KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. re"our e TJ : onfirm !. QLre you "ureUQ Y. K/td.
1$2
K,1.KY if @re"our e.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. 2e"our eK/,1. KY! errorAme""age"Afor Qre"our eQ Y. KY! "tartAformAtag%S:a tion !. QeditQJ :id !. @re"our eTJ S:multipart !. trueT( Y. Kp. Klabel for!Qre"our eAnameQ.8ame:K/label.Kbr. KY! textAfield Qre"our eQJ QnameQ Y. K/p. Kp. Klabel for!Qre"our eAfilenameQ.9ile:K/label.Kbr. KY! fileAfield Qre"our eQJ QfilenameQ Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#Ato Q4an elQJ :a tion !. Qli"tQY. K/p. KY! endAformAtag Y.
Bote
e0ten i o
nome o arquivo ao invJs e seus a os. /ssa mu ana J vista abai0o6 no 4:(L #era oA
11$
-e salvarmos um re#istro6 notaremos al#umas coisas estranhasA primeiro6 nenhum arquivo J envia o para qualquer iret<rio no servi orK se#un o6 a vali ao no "uncionaK e terceiro6 o campo no banco e a os com in"orma&es estranhas6 como po emos ver na tela abai0oA
111
o mesmo processar esses ob=etos. + que vemos acima J uma representao te0tual
Po emos usar os mJto os esse ob=eto para salvar o nosso arquivo em isco e "a>er o que mais precisamos. A maneira mais "cil e "a>er isso J mo i"icar o mo o como o mo elo processa os a os6 manten o o uso o atributo transparente para o usurio. Para isto6 vamos mo i"icar inicialmente a vieG para #ravar em um outro atributoA
K,1.KY if @re"our e.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. 2e"our eK/,1. KY! errorAme""age"Afor Qre"our eQ Y. KY! "tartAformAtag%S:a tion !. QeditQJ :id !. @re"our eTJ S:multipart !. trueT( Y. Kp. Klabel for!Qre"our eAnameQ.8ame:K/label.Kbr. KY! textAfield Qre"our eQJ QnameQ Y. K/p. Kp. Klabel for!Qre"our eAfileQ.9ile:K/label.Kbr. KY! fileAfield Qre"our eQJ QfileQ Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#Ato Q4an elQJ :a tion !. Qli"tQY. K/p. KY! endAformAtag Y.
la"" 2e"our e K L tive2e ord::/a"e attrAprote ted :filename validate"Apre"en eAof :name validate"Apre"en eAof :filename def file!%value( end end
e qualquer atribuio automtica e cria um novo atributo a os. :estan o o "ormulrio voc@ ver que a
vali ao voltou a "uncionar embora6 obviamente6 como no estamos atribuin o na a ao atributo "ilename6 o re#istro no ser salvo. + que precisamos a#ora J veri"icar se al#um a o "oi realmente envia o e salvar em isco se "or o casoA
11*
la"" 2e"our e K L tive2e ord::/a"e attrAprote ted :filename validate"Apre"en eAof :name validate"Apre"en eAof :filename def file!%value( if value."ize . ' "elf.filename ! 9ile.ba"ename%value.originalAfilename( filepat, ! Q?S2L7D>A2::<T/publi /upload"/?S"elf.filenameTQ 9ile.open%filepat,J QwbQ( do MfM f.write%value.read( end end end end
Antes
e criar um
ebai0o
iret<rio
public6 com permiss&es e escrita e leitura apropria as para o seu sistema. Bo c< i#o6 veri"icamos inicialmente se al#um arquivo ori#inal e recolhemos somente o nome filename6 e e"etivamente vali an o o mesmo. Depois isso6 #eramos um caminho local para o arquivo basea o na varivel 1-B )01CC*6 que J interna ao e sua aplicao on e to os os iret<rio esto locali>a os. )amos iretamente em um te0to6 e interpolaoA variveis aplica as a o "oi envia o. Depois6 recuperamos o nome completo o
Rails. /ssa varivel aponta para a rai> aqui tambJm o nosso primeiro e0emplo isco. Bote que escrever um arquivo em open
a classe File6 que recebe um bloco como parLmetro6 on e as opera&es so e"etua os. /m nosso a "orma acima6 sem parLmetros in icativos e tamanho6 #ravam ou l@em tu o o que
caso aqui6 escrevemos os a os li os a varivel o tipo )tringBC recebi a. +s mJto os write e read6 caso se=am usa os receberam. Com o uso o bloco6 o arquivo J automaticamente "echa o epois e sua utili>ao. + c< i#o acima tem uma "alha "un amental no senti o que sobrescreve arquivos = envia os se o nome "or o mesmo e outro re#istro. +bviamente6 sobrescrever os a os arbitrariamente no J uma coisa que queremos. /0istem vrias "ormas e resolver isso e veremos uma elas em breve. -e voc@ testar a aplicao a#ora6 ver que po e "a>er o uploa melhorar somente a visuali>ao lista#emA e qualquer arquivo. Precisamos a#ora
11'
K,1.2e"our e"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew 2e"our eQJ :a tion !. QeditQ Y.K/p. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. Kt,.9ileK/t,. Kt,.L tion"K/t,. K/tr. KY @re"our e".ea , do Mre"our eM Y. Ktr. Ktd.KY! re"our e.name Y.K/td. Ktd.KY! lin#Ato re"our e.filenameJ Q/upload"/?Sre"our e.filenameTQ Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. re"our e Y. or KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. re"our e TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y. K/table. Kp.KY! paginationAlin#"%@re"our eApage"( Y.K/p.
K,1.KY if @re"our e.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. 2e"our eK/,1.
114
KY! errorAme""age"Afor Qre"our eQ Y. KY! "tartAformAtag%S:a tion !. QeditQJ :id !. @re"our eTJ S:multipart !. trueT( Y. Kp. Klabel for!Qre"our eAnameQ.8ame:K/label.Kbr. KY! textAfield Qre"our eQJ QnameQ Y. K/p. KY unle"" @re"our e.newAre ordU Y. Kp. <,e urrent file i" KY! lin#Ato @re"our e.filenameJ Q/upload"/?S@re"our e.filenameTQ Y.. 4,oo"e a new file below to repla e it. K/p. KY end Y. Kp. Klabel for!Qre"our eAfileQ.9ile:K/label.Kbr. KY! fileAfield Qre"our eQJ QfileQ Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#Ato Q4an elQJ :a tion !. Qli"tQY. K/p. KY! endAformAtag Y.
o arquivo na aplicao.
-eria interessante e0trairmos isso em um mJto o que possa ser reusa o. 7a>emos isso em nosso mo eloA
115
la"" 2e"our e K L tive2e ord::/a"e attrAprote ted :filename validate"Apre"en eAof :name validate"Apre"en eAof :filename def file!%value( if value."ize . ' "elf.filename ! 9ile.ba"ename%value.originalAfilename( filepat, ! Q?S2L7D>A2::<T/publi /upload"/?S"elf.filenameTQ 9ile.open%filepat,J QwbQ( do MfM f.write%value.read( end end end def url Q/upload"/Q N "elf.filename unle"" "elf.filename.nilU end end
A#ora po emos usar esse atributo em nossas vieGs. Por e0emplo6 alteran o a vieG e lista#em e a osA
K,1.2e"our e"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew 2e"our eQJ :a tion !. QeditQ Y.K/p. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. Kt,.9ileK/t,. Kt,.L tion"K/t,. K/tr. KY @re"our e".ea , do Mre"our eM Y. Ktr. Ktd.KY! re"our e.name Y.K/td. Ktd.KY! lin#Ato re"our e.filenameJ re"our e.url Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. re"our e Y. or KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. re"our e TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y. K/table. Kp.KY! paginationAlin#"%@re"our eApage"( Y.K/p.
K,1.KY if @re"our e.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. 2e"our eK/,1. KY! errorAme""age"Afor Qre"our eQ Y. KY! "tartAformAtag%S:a tion !. QeditQJ :id !. @re"our eTJ S:multipart !. trueT( Y. Kp.
111
Klabel for!Qre"our eAnameQ.8ame:K/label.Kbr. KY! textAfield Qre"our eQJ QnameQ Y. K/p. KY unle"" @re"our e.newAre ordU Y. Kp. <,e urrent file i" KY! lin#Ato @re"our e.filenameJ @re"our e.url Y.. 4,oo"e a new file below to repla e it. K/p. KY end Y. Kp. Klabel for!Qre"our eAfileQ.9ile:K/label.Kbr. KY! fileAfield Qre"our eQJ QfileQ Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#Ato Q4an elQJ :a tion !. Qli"tQY. K/p. KY! endAformAtag Y.
.m outro e0cluI o
etalhe a tratar J o que "a>er quan o um arquivo J troca o. Besse caso precisamos remover o o banco. Para isso6 nossas solu&es esto to as no mo elo e a os. :rocar o arquivo J to
arquivo anti#o antes e salvar o novo. Precisamos tambJm remover o arquivo o isco quan o um re#istro J simples quanto o c< i#o abai0oA
la"" 2e"our e K L tive2e ord::/a"e attrAprote ted :filename validate"Apre"en eAof :name validate"Apre"en eAof :filename def file!%value( if value."ize . ' if "elf.filename filepat, ! Q?S2L7D>A2::<T/publi /upload"/?S"elf.filenameTQ if 9ile.exi"t"U%filepat,( 9ile.delete%filepat,( end end "elf.filename ! 9ile.ba"ename%value.originalAfilename( filepat, ! Q?S2L7D>A2::<T/publi /upload"/?S"elf.filenameTQ 9ile.open%filepat,J QwbQ( do MfM f.write%value.read( end end end def url Q/upload"/Q N "elf.filename unle"" "elf.filename.nilU end end
11!
CALLBACKS
Callbac8s so muito pareci os com os "iltros que vimos anteriormente6 mas se aplicam a mo elos e a os. -o mJto os que po em ser usa os para interceptar os momentos em que um ob=eto J persisti o para o banco e a os e e0ecutar al#uma ao nesse momento. -e voc@ consultar a ocumentao o ActiveRecor 6 ver que o ciclo e persist@ncia e um ob=eto que
nunca tenha si o salvo antes se#ue as se#uintes chama asA S;T save S;T vali ` S1T be"oreYvali ation S*T be"oreYvali ationYonYcreate S;T vali ate S;T vali ateYonYcreate S'T a"terYvali ation S4T a"terYvali ationYonYcreate S5T be"oreYsave S1T be"oreYcreate S;T create S!T a"terYcreate S5T a"terYsave Bo e0emplo acima6 os mJto os numera os so callbac8s6 ou se=a6 locais on e voc@ po e inter"erir no processamento as emais chama as. )oc@ po e inter"erir antes e epois a vali ao6 antes e epois o salvamento e ser to especi"Ico como quiser6 i>en o se quer "a>er isso somente na primeira ver que o
ob=eto "or salvo ou se para to as as emais ve>es em que o ob=eto "or persisti o. + mesmo J vli o para to as as outras opera&es )amos6 ento6 nos aproveitar e persist@ncia que po em se e"etua as em um ob=eto.
precisamento6 lo#o epois e que o mesmo tenha si o removi o. + nosso mo elo "icar assimA
la"" 2e"our e K L tive2e ord::/a"e attrAprote ted :filename validate"Apre"en eAof :name validate"Apre"en eAof :filename afterAde"troy :removeAfile def file!%value( if value."ize . ' if "elf.filename "elf.removeAfile end "elf.filename ! 9ile.ba"ename%value.originalAfilename(
115
9ile.open%"elf.filepat,J QwbQ( do MfM f.write%value.read( end end end def url Q/upload"/Q N "elf.filename unle"" "elf.filename.nilU end def filepat, Q?S2L7D>A2::<T/publi /upload"/?S"elf.filenameTQ end def removeAfile if 9ile.exi"t"U%"elf.filepat,( 9ile.delete%"elf.filepat,( end end end
?sso resolve o nosso problema. Bote que e"etuamos um pequeno re"atoramento para eliminar a necessi a e e repetir o c< i#o. Como mencionamos acima6 a maneira cui a os especiais. + Pnico problema e "a>er uploa s em Rails J bem prtica e no necessita e muitos ar a esse
o c< i#o acima J que terIamos que ser um pouco mais criteriosos e
veri"icar se al#um arquivo no est sen o sobrescrito e tratar isso. (as6 no precisamos nos trabalho. /0iste uma soluo muito mais "cil para casos e uploa s simples com esse.
PLUGINS
Plu#ins so uma "orma e aumentar a "uncionali a e o Rails sem me0er em qualquer e suas bibliotecas a lin#ua#em Rub,6 bsicas. + Rails possui um mecanismo e plu#ins to po eroso6 #raas M "le0ibili a e
que qualquer coisa po e ser mo i"ica a em sua estrutura. /0istem plu#ins6 para criar sistemas e autenticao e e vrios tipos Scomo inclusive = mencionamosTK e vali aoK plu#ins para "ormatar atas6 te0to6
comple0os entre outrosK plu#ins para criar novas "ormas locali>an o em httpAZZplu#ins.ra rails.or#Z.
#erar 4:(L6 9(L6 e e>enas e outras tare"as que voc@ po e analisar por si pr<prio visitan o o reposit<rio
.m plu#in em particular po e a=u ar a nossa tare"a com uploa s. Chama o capa> e reali>ar to o o processo e arquivos e uploa manipula&es no arquivo. /ntre essas manipula&es incluem;se a possibili a e re imensionamentos para o Rub,T J requeri a. ?nstalar um plu#in em Rails J bem "cil. /0istem
o mesmo e
escompat;lo
+ coman o ireto para isso possui caracterIsticas mais avana as que incluem a possibili a e e inte#rao com o sistema e controle e verso -ubversion6 e #eralmente J a opo mais in ica a. 3ai0ar o arquivo na mo po e ser vanta=oso para pro=etos em que mu anas quer e0ercer corre&es. -e=a qual "or a sua escolha6 a sinta0e o coman o J bem simples. )oc@ po e ver a a=u a o mesmo ro an o o coman o abai0oA e verso so ri#orosamente controla as e voc@ ecis&es manual inclusive sobre os plu#ins6 pre"erin o reali>ar suas pr<prias atuali>a&es e
Como voc@ po e ver6 ele J bem similar ao coman o #em. Para bai0ar o plu#in que precisamos aqui6 file0column6 voc@ po e usar o coman o se#uinteA
Bo nosso caso6 eu = tenho o plu#in instala o em outro pro=eto e vou simplesmente copi;lo para o iret<rio !endor/plugins6 #eran o a se#uinte estruturaA
ronaldo@minerva:~/tmp/gtd/vendor/plugin"/fileA olumn$ l" -l total &8 -rw-r--r-- 1 ronaldo ronaldo 3&$* &''*-'6-&& 1*:36 4;L8X1D:X -rw-r--r-- 1 ronaldo ronaldo 366 &''*-'6-&& 1*:36 init.rb drwxr-xr-x & ronaldo ronaldo $'6* &''*-'6-&& 1*:$' lib -rw-r--r-- 1 ronaldo ronaldo R8& &''*-'6-&& 1*:36 2a#efile -rw-r--r-- 1 ronaldo ronaldo 185* &''*-'6-&& 1*:36 21LGB1 drwxr-xr-x $ ronaldo ronaldo $'6* &''*-'6-&& 1*:$' te"t -rw-r--r-- 1 ronaldo ronaldo &$3 &''*-'6-&& 1*:36 <:G:
A#ora que temos esse plu#in6 po emos us;lo em nosso c< i#o. Bosso mo elo ser substituI o porA
la"" 2e"our e K L tive2e ord::/a"e validate"Apre"en eAof :name validate"Apre"en eAof :filename fileA olumn :filename end
1*$
iretamente
po emos utili>ar transparentemente em nossa aplicao. -e voc@ testou a vali ao com a nossa verso anterior o mo elo e a os e as vieGs ter percebi o que quan o o nome o ob=eto no J in"orma o6 causan o uma vali ao6 qualquer arquivo in"orma o ser o nosso plu#in6 ele toma conta isso per i o6 e0i#in o que o usurio o in"orme mais uma ve>. Bo caso
arma>enan o o arquivo temporariamente para que o usurio no tenha que conviver com um comportamento an[malo e nem e0i#in o que o esenvolve or se preocupe com etalhes como esses. )amos mo i"icar a#ora nossa vieG e e io paraA
K,1.KY if @re"our e.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. 2e"our eK/,1. KY! errorAme""age"Afor Qre"our eQ Y. KY! "tartAformAtag%S:a tion !. QeditQJ :id !. @re"our eTJ S:multipart !. trueT( Y. Kp. Klabel for!Qre"our eAnameQ.8ame:K/label.Kbr. KY! textAfield Qre"our eQJ QnameQ Y. K/p. KY unle"" @re"our e.newAre ordU Y. Kp. <,e urrent file i" KY! lin#Ato @re"our e.ba"eAfilenameJ urlAforAfileA olumn%Qre"our eQJ QfilenameQ( Y.. 4,oo"e a new file below to repla e it. K/p. KY end Y. Kp. Klabel for!Qre"our eAfilenameQ.9ile:K/label.Kbr. KY! fileA olumnAfield Qre"our eQJ QfilenameQ Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#Ato Q4an elQJ :a tion !. Qli"tQY. K/p. KY! endAformAtag Y.
K,1.2e"our e"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew 2e"our eQJ :a tion !. QeditQ Y.K/p. Ktable border!Q1Q ellpadding!Q5Q Ktr. Kt,.8ameK/t,. Kt,.9ileK/t,. Kt,.L tion"K/t,. ell"pa ing!Q1Q.
1*1
K/tr. KY @re"our e".ea , do Mre"our eM Y. Ktr. Ktd.KY! re"our e.name Y.K/td. Ktd. KY! lin#Ato re"our e.ba"eAfilenameJ urlAforAfileA olumn%re"our eJ QfilenameQ( Y. K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. re"our e Y. or KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. re"our e TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y. K/table. Kp.KY! paginationAlin#"%@re"our eApage"( Y.K/p.
Pelo "ato
mu ana acima na vieG e criamos o mJto o base0filename6 que voc@ po e ver abai0oA
la"" 2e"our e K L tive2e ord::/a"e validate"Apre"en eAof :name validate"Apre"en eAof :filename fileA olumn :filename def ba"eAfilename 9ile.ba"ename%"elf.filename( end end
e"ici@ncia
provavelmente ela ser sana a em al#uma verso "utura. Alis6 com os recursos po erIamos ter intro u>i o o mJto o c< i#o. (esmo com esse pequeno problema6 porJm6 po emos ver que o uso substancialmente o trabalho manti o. Consi ere6 por e0emplo6 uma aplicao com por n<s. )e=a que os arquivos esto em um e0emplo na lista#em abai0oA iret<rio sob public6 cu=o nome J a classe entro esse iret<rio6 um ouro e e>enas
o pro#rama or e a=u ar o c< i#o a "icar mais limpo e mais "cil e necessi a es
termos que li ar atributo por atributo com o problema6 po emos simplesmente ei0ar que o plu#in "aa isso
a os6 com um
iret<rio
(uito simples e intuitivo. Caso voc@ queira controlar os arquivos com maior ri#or6 autentican o o acesso aos
1**
-eria i"Icil conse#uir al#o mais simples o que isso. A#ora que temos os nossos recursos6 po emos associa&es entre associa&es has0and0belongs0to0many. emonstrar mais uma caracterIstica o Rails6 que so as
uas classes interme ia as por outra classe6 menciona as mais acima no te0toA
ois mo elos enquanto que nesta Pltima o mo elo interme irio J usa o somente para
colapsar os a os em uma a#re#ao. Como temos uma tabela interme iria representan o a relao6 o Rails tambJm se#ue uma conveno. A tabela eve se nomea a como a unio entre os nomes os ois mo elos que lhe o ori#em6 em or em isco ser chama a al"abJtica. Bo nosso caso6 a tabela que relaciona as a&es aos recursos em actions0resources. Bote o plural nos nomes. .m etalhe interessante J que caso essa tabela contenha outros campos6 eles sero re"leti os e c< i#o a icional. /6 mais o que isso6 no J necessrio
automaticamente na associao sem necessi a e #erar qualquer mo elo. )amos partir ento para a mi#rao necessriaA
ronaldo@minerva:~/tmp/gtd$ " ript/generate migration reateAa tion"Are"our e" reate db/migrate reate db/migrate/''8A reateAa tion"Are"our e".rb
/ a e itamosA
la"" 4reateL tion"2e"our e" K L tive2e ord::Bigration def "elf.up reateAtable :a tion"Are"our e"J :id !. fal"e do MtM t. olumn :a tionAidJ :integer t. olumn :re"our eAidJ :integer
1*'
Como no precisamos e uma coluna automtica i 6 in"ormamos M mi#rao que isso po e ser i#nora o. A#ora6 po emos mo i"ica os nossos mo elos. (o i"icaremos ambos porque po emos utili>ar a relao nas uas ire&es. Comeamos com o mo elo para as a&esA
la"" L tion K L tive2e ord::/a"e attrAprote ted : reatedAatJ : ompletedAat def done!%value( if L tive2e ord::4onne tionLdapter"::4olumn.valueAtoAboolean%value( "elf. ompletedAat ! Gate<ime.now el"e "elf. ompletedAat ! nil end writeAattribute%QdoneQJ value( end belong"Ato : ontext belong"Ato :pro-e t validate"Apre"en eAof :de" ription validate"Apre"en eAof : ontextAid validate"Apre"en eAof :pro-e tAid ,a"AandAbelong"AtoAmany :re"our e" end
la"" 2e"our e K L tive2e ord::/a"e validate"Apre"en eAof :name validate"Apre"en eAof :filename fileA olumn :filename def ba"eAfilename 9ile.ba"ename%"elf.filename( end ,a"AandAbelong"AtoAmany :a tion" end
.san o o console a#ora6 po emos manipular essas associa&es para enten emos um pouco melhor como
1*4
elas "uncionamA
ronaldo@minerva:~/tmp/gtd$ " ript/ on"ole Doading development environment. .. a tion ! L tion.find%1( !. ?KL tion:'xbRR8a6R8 @attribute"!SQ ontextAidQ!.Q1QJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.Q3QJ QdoneQ!.Q'QJ QidQ!.Q1QJ Qde" riptionQ!.Q/uy a grammar to ,elp me wit, my 1ngli",.QJ Q reatedAatQ!.Q&''*-'6-&& '6:16:3RQT. .. a tion.re"our e" !. )+
Como po emos ver6 nossa associao = est "uncionan o. :estan o um pouco maisA
.. a tion.re"our e" KK 2e"our e.find%R( !. )?K2e"our e:'xbRR3 ef8 @attribute"!SQnameQ!.Q/abelQJ QidQ!.QRQJ QfilenameQ!.Qbabel.do QT.+ .. a tion.re"our e" KK 2e"our e.find%8( !. )?K2e"our e:'xbRR3 ef8 @attribute"!SQnameQ!.Q/abelQJ QidQ!.QRQJ QfilenameQ!.Qbabel.do QT.J ?K2e"our e:'xbRR&fd68 @attribute"!SQnameQ!.Q9ir"t 9loorQJ QidQ!.Q8QJ QfilenameQ!.Q1"t-floor.pngQT.+ .. a tion.re"our e"."ize !. &
.. re"our e ! 2e"our e.find%R( !. ?K2e"our e:'xbRRe83 @attribute"!SQnameQ!.Q/abelQJ QidQ!.QRQJ QfilenameQ!.Qbabel.do QT. .. re"our e.a tion" !. )?KL tion:'xbRRa6 f8 @attribute"!SQ ontextAidQ!.Q1QJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.Q3QJ QdoneQ!.Q'QJ Qre"our eAidQ!.QRQJ QidQ!.Q1QJ Qde" riptionQ!.Q/uy a grammar to ,elp me wit, my 1ngli",.QJ Qa tionAidQ!.Q1QJ Q reatedAatQ!.Q&''*-'6-&& '6:16:3RQT.+
Bo caso
e associa&es com atributos na relao6 e0iste um mJto o especial que po e ser usa o para os mesmos. -uponha um relao entre uma tabela representan o um pe i o e uma o pe i o que se relaciona a um pro uto. +bviamente6 no item o pe i o o momento em que o pe i o "oi #era o. Po erIamos ter
a icionar os valores
order ! :rder.find%param"):orderAid+( produ t ! =rodu t.find%param"):produ tAid+( order.item".pu",Awit,Aattribute"%produ tJ :pri e !. produ t.pri eJ :@uantity !. 1(
order0id e product0id = con"i#ura os para os valores pr<prios os ob=etos relaciona os e com os campos price an quantity carre#a os com o valores passa os e0plicitamente para o mJto o. .ma ve> "eito isso6 os atributos po eriam ser e ita os com maior simplici a eA
1*5
a os sem necessi a e
qualquer es"oro a icional. Para to a a "uncionali a e #era a6 precisamos apenas e uas linhas e c< i#o. A ocumentao escreve uma sJrie e outros mJto os e parLmetros que po em ser usa os para li ar com
as situa&es mais inusita as que voc@ tiver. -e quisermos6 por e0emplo6 or enar nossas associa&es6 po emos mu ar um e nossos mo elos paraA
la"" L tion K L tive2e ord::/a"e attrAprote ted : reatedAatJ : ompletedAat def done!%value( if L tive2e ord::4onne tionLdapter"::4olumn.valueAtoAboolean%value( "elf. ompletedAat ! Gate<ime.now el"e "elf. ompletedAat ! nil end writeAattribute%QdoneQJ value( end belong"Ato : ontext belong"Ato :pro-e t validate"Apre"en eAof :de" ription validate"Apre"en eAof : ontextAid validate"Apre"en eAof :pro-e tAid ,a"AandAbelong"AtoAmany :re"our e"J :order !. QnameQ end
Para aproveitarmos essas mu anas em nosso mo elo6 vamos trabalhar mais uma ve> com o controller que #erencia o ca astro ca a ve>. Comeamos6 esta ve>6 mo i"ican o a ao e lista#em as a&esA e a&es. + nosso ob=etivo a#ora J permitir que o usurio associe recursos e arquivos e a uma ao qualquer. Para isto6 teremos um ca astro interno que nos permitir mPltiplas associa&es
K,1.L tion"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew L tionQJ :a tion !. QeditQ Y.K/p. Ktable border!Q1Q ellpadding!Q5Q Ktr. Kt,.Ge" riptionK/t,. Kt,.4ompletedK/t,. ell"pa ing!Q1Q.
1*1
Kt,.0,enK/t,. Kt,.=ro-e tK/t,. Kt,.4ontextK/t,. Kt,.L tion"K/t,. K/tr. KY @a tion".ea , do Ma tionM Y. Ktr. Ktd.KY! a tion.de" ription Y.K/td. Ktd.KY! ye"AorAnoU%a tion.doneU( Y.K/td. Ktd.KY! %a tion.doneU( U a tion. ompletedAat."trftime%QYm/Yd/YyQ( : Q-Q Y.K/td. Ktd.KY! a tion.pro-e t.name Y.K/td. Ktd.KY! a tion. ontext.name Y.K/td. Ktd nowrap. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. a tion Y. orKbr. KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. a tion TJ : onfirm !. QLre you "ureUQ Y. orKbr. KY! lin#Ato Q1dit 2e"our e"QJ :a tion !. Qre"our e"QJ :id !. a tion Y. K/td. K/tr. KY end Y. K/table. Kp.KY! paginationAlin#"%@a tionApage"( Y.K/p.
la"" L tion"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @a tionApage"J @a tion" ! paginate :a tion"J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ end def edit @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if param"):id+ @item ! L tion.find%param"):id+( el"e @item ! L tion.new end if re@ue"t.po"tU @item.attribute" ! param"):item+ if @item."ave fla",):noti e+ ! Q<,e a tion wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def re"our e" @a tion ! L tion.find%param"):id+( end def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
1*!
Com essa varivel6 = po emos criar uma vieG que inicialmente e0ibe os recursos associa os com um aoA
K,1.L tion 2e"our e"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp. K"trong.L tion Ge" ription:K/"trong.Kbr. KY! @a tion.de" ription Y. K/p. K,3.2e"our e"K/,3. KY if @a tion.re"our e"."ize . ' Y. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. Kt,.9ileK/t,. Kt,.L tion"K/t,. K/tr. KY @a tion.re"our e".ea , do Mre"our eM Y. Ktr. Ktd.KY! re"our e.name Y.K/td. Ktd. KY! lin#Ato re"our e.ba"eAfilenameJ urlAforAfileA olumn%re"our eJ QfilenameQ( Y. K/td. Ktd. KY! lin#Ato QGeleteQJ S :a tion !. QdeleteAre"our eQJ :id !. @a tionJ :re"our eAid !. re"our e TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y. K/table. KY el"e Y. Kp.Kem.8o re"our e wa" added to t,i" a tion yet.K/em.K/p. KY end Y.
+ resulta o JA
1*5
e uma "orma
K,1.L tion 2e"our e"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp. K"trong.L tion Ge" ription:K/"trong.Kbr. KY! @a tion.de" ription Y. K/p. K,3.2e"our e"K/,3. KY if @a tion.re"our e"."ize . ' Y. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. Kt,.9ileK/t,. Kt,.L tion"K/t,. K/tr. KY @a tion.re"our e".ea , do Mre"our eM Y. Ktr. Ktd.KY! re"our e.name Y.K/td. Ktd. KY @re"our e ! re"our e Y. KY! lin#Ato re"our e.ba"eAfilenameJ urlAforAfileA olumn%Qre"our eQJ QfilenameQ( Y. K/td. Ktd. KY! lin#Ato QGeleteQJ S :a tion !. QdeleteAre"our eQJ :id !. @a tionJ :re"our eAid !. re"our e TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y.
1*2
K/table. KY el"e Y. Kp.Kem.8o re"our e wa" added to t,i" a tion yet.K/em.K/p. KY end Y. KY! "tartAformAtag :a tion !. Qre"our e"Q Y. K,3.>ear , for 2e"our e" to LddK/,3. Kp. Klabel for!Q#eyword"Q.Ieyword":K/label.Kbr. KY! textAfieldAtag Q#eyword"QJ param"):#eyword"+ Y. K/p. Kp. KY! "ubmitAtag Q>ear ,Q Y. K/p. KY! endAformAtag Y. KY if @re"our e"."ize . ' Y. KY! "tartAformAtag :a tion !. QaddAre"our e"QJ :id !. @a tion Y. Kp.K"trong.2e"ult"K/"trong.K/p. Kul la""!Q"ear ,-re"ult"Q. KY @re"our e".ea , do Mre"our eM Y. Kli. KY! ,e #AboxAtag Qre"our e")+QJ re"our e.id Y. KY! re"our e.name Y.Kbr. K"pan.KY! lin#Ato re"our e.ba"eAfilenameJ urlAforAfileA olumn%re"our eJ QfilenameQ( Y.K/"pan. K/li. KY end Y. K/ul. Kp. KY! "ubmitAtag QLddQ Y. K/p. KY! endAformAtag Y. KY el"e Y. Kp.Kem.<,i" "ear , returned to mat ,e".K/em.K/p. KY end Y.
la"" L tion"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @a tionApage"J @a tion" ! paginate :a tion"J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ end def edit @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T
1'$
@pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if param"):id+ @item ! L tion.find%param"):id+( el"e @item ! L tion.new end if re@ue"t.po"tU @item.attribute" ! param"):item+ if @item."ave fla",):noti e+ ! Q<,e a tion wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def re"our e" @a tion ! L tion.find%param"):id+( @re"our e" ! )+ if re@ue"t.po"tU [[ Wparam"):#eyword"+.blan#U @re"our e" ! 2e"our e.find%:allJ : ondition" !. )Hname li#e U or filename li#e UHJ QY?Sparam"):#eyword"+TYQJ QY?Sparam"):#eyword"+TYQ+( end end def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
ul."ear ,-re"ult" S li"t-"tyle: none5 padding: '5 margin: '5 T ul."ear ,-re"ult" li S margin-bottom: 5px5 T ul."ear ,-re"ult" li "pan S font-"ize: "maller5 T
1'1
Precisamos a#ora realmente a icionar os recursos escolhi os M ao. Para isso6 criamos a ao que = especi"icamos na vieGA
la"" L tion"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @a tionApage"J @a tion" ! paginate :a tion"J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ end
1'*
def edit @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if param"):id+ @item ! L tion.find%param"):id+( el"e @item ! L tion.new end if re@ue"t.po"tU @item.attribute" ! param"):item+ if @item."ave fla",):noti e+ ! Q<,e a tion wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def re"our e" @a tion ! L tion.find%param"):id+( @re"our e" ! )+ if re@ue"t.po"tU [[ Wparam"):#eyword"+.blan#U @re"our e" ! 2e"our e.find%:allJ : ondition" !. )Hname li#e U or filename li#e UHJ QY?Sparam"):#eyword"+TYQJ QY?Sparam"):#eyword"+TYQ+( end end def addAre"our e" @a tion ! L tion.find%param"):id+( exi"tingAre"our eAid" ! @a tion.re"our e". olle t S Mre"our eM re"our e.id T newAre"our eAid" ! param"):re"our e"+.re-e t S MidM exi"tingAre"our eAid".in ludeU%id.toAi( T @a tion.re"our eAid" KK newAre"our eAid" redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
/ssa ao veri"ica os recursos e0istentes6 encontra os ain a no a iciona os Mquela ao6 os a iciona e6 por "im6 re ireciona para a lista#em mais uma ve>. )eri"icar recursos = a iciona os previne a incluso mPltiplos recursos na tabela interme iria. 7inalmente6 precisamos a ao que remove um re#istro associa o6 que J bem simplesA e
la"" L tion"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @a tionApage"J @a tion" ! paginate :a tion"J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ end def edit @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if param"):id+
1''
@item ! L tion.find%param"):id+( el"e @item ! L tion.new end if re@ue"t.po"tU @item.attribute" ! param"):item+ if @item."ave fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ end end end
e""fully "avedQ
def re"our e" @a tion ! L tion.find%param"):id+( @re"our e" ! )+ if re@ue"t.po"tU [[ Wparam"):#eyword"+.blan#U @re"our e" ! 2e"our e.find%:allJ : ondition" !. )Hname li#e U or filename li#e UHJ QY?Sparam"):#eyword"+TYQJ QY?Sparam"):#eyword"+TYQ+( end end def addAre"our e" @a tion ! L tion.find%param"):id+( exi"tingAre"our eAid" ! @a tion.re"our e". olle t S Mre"our eM re"our e.id T newAre"our eAid" ! param"):re"our e"+.re-e t S MidM exi"tingAre"our eAid".in ludeU%id.toAi( T @a tion.re"our e" KK 2e"our e.find%newAre"our eAid"( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def deleteAre"our e @a tion ! L tion.find%param"):id+( @a tion.re"our e".delete%2e"our e.find%param"):re"our eAid+(( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
e0ercIcio para o leitor limitar a busca com base nos re#istros = e0istentes e mo o que6 para conveni@ncia o usurio6 somente re#istros ain a no a iciona os apaream.
ESCOPOS
.ma outra caracterIstica interessante o Rails6 intro u>i a na verso 1.16 J o conceito e escopo sinttico e escopos para opera&es no banco e a os. + conceito J muito similar Ms eclar&es Gith presentes em vrias lin#ua#ens e pro#ramao6 que temporariamente aumentam o nIvel "acilitan o o uso as mesmas. + uso e escopos6 a e0emplo as eclara&es Gith6 po e simplicar bastante o uso o ActiveRecor 6 mas e uma ou mais variveis6
eve ser empre#a o com cui a o por possibilitar a intro uo i"Iceis e epurar. Para emonstrar o uso
possibili a e
e uso
a ministra or6 po ia acessar to as as a&es6 conte0tos e pro=etos #era os. +bviamente6 isso no J o que queremos. Com a a=u a e escopos6 po emos resolver isso rapi amente. Primeiramente6 vamos alterar o nosso mo elo e a os6 isolan o ca a ob=eto para conter tambJm a
ronaldo@minerva:~/tmp/gtd$ " ript/generate migration addAu"erAtoAob-e t" exi"t" db/migrate reate db/migrate/''6AaddAu"erAtoAob-e t".rb
/ a e itamosA
la"" LddO"er<o:b-e t" K L tive2e ord::Bigration def "elf.up addA olumn : ontext"J :u"erAidJ :integer addA olumn :pro-e t"J :u"erAidJ :integer addA olumn :a tion"J :u"erAidJ :integer addA olumn :re"our e"J :u"erAidJ :integer 4ontext.re"etA olumnAinformation =ro-e t.re"etA olumnAinformation L tion.re"etA olumnAinformation 2e"our e.re"etA olumnAinformation u"er ! O"er.findAbyAname%QronaldoQ( 4ontext.updateAall Qu"erAid ! ?Su"er.idTQ =ro-e t.updateAall Qu"erAid ! ?Su"er.idTQ L tion.updateAall Qu"erAid ! ?Su"er.idTQ 2e"our e.updateAall Qu"erAid ! ?Su"er.idTQ end def "elf.down removeA olumn removeA olumn removeA olumn removeA olumn end end : ontext"J :u"erAid :pro-e t"J :u"erAid :a tion"J :u"erAid :re"our e"J :u"erAid
Bo caso
e uma mi#rao como essa6 precisamos tambJm atuali>ar o nosso banco. / isso o que "a> essa isso6 encontramos o primeiro usurio que
criamos. / por "im6 utili>amos o mJto o update0all para "a>er atuali>a&es em massa em nosso re#istros. A#ora6 vamos e0perimentar com o controller responsvel pelos conte0tos6 utili>an o escopos. -e no estivJssemos usan o escopos e quisJssemos6 por e0emplo6 limitar a nossa ao list a somente re#istros um usurio6 terIamos al#o assimA e
la"" 4ontext"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t
1'5
@ ontextApage"J @ ontext" ! paginate : ontext"J : ondition" !. )Qu"erAid ! UQJ "e""ion):u"er++J :perApage !. 5J :order !. QnameQ end def edit if param"):id+ @ ontext ! 4ontext.find%param"):id+( el"e @ ontext ! 4ontext.new end if re@ue"t.po"tU @ ontext.attribute" ! param"): ontext+ if @ ontext."ave fla",):noti e+ ! Q<,e ontext wa" "u redire tAto :a tion !. Qli"tQ end end end def delete 4ontext.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e ontext wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully "avedQ
e""fully deletedQ
la"" 4ontext"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t 4ontext.wit,A" ope%:find !. S : ondition" !. )Qu"erAid ! UQJ "e""ion):u"er++ T( do @ ontextApage"J @ ontext" ! paginate : ontext"J :perApage !. 5J :order !. QnameQ end end def edit if param"):id+ @ ontext ! 4ontext.find%param"):id+( el"e @ ontext ! 4ontext.new end if re@ue"t.po"tU @ ontext.attribute" ! param"): ontext+ if @ ontext."ave fla",):noti e+ ! Q<,e ontext wa" "u redire tAto :a tion !. Qli"tQ end end end def delete 4ontext.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e ontext wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully "avedQ
e""fully deletedQ
+ mJto o with0scope recebe um sJrie e eclara&es que e"inem con i&es e atributos a serem aplica os. A princIpio po e parece mais es"oro6 mas o importante a enten er na chama a acima J que to as as
1'1
chama as ao banco
entro
cria o para o mesmo6 receben o automaticamente as con i&es especi"ica as. Bo que tan#e ao mJto o find6 basicamente qualquer e seus parLmetros po e ser especi"ica o6 como o""set e limit6 por e0emplo. Alteran o a ao e it terIamos al#o assimA
la"" 4ontext"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t 4ontext.wit,A" ope%:find !. S : ondition" !. )Qu"erAid ! UQJ "e""ion):u"er++ T( do @ ontextApage"J @ ontext" ! paginate : ontext"J :perApage !. 5J :order !. QnameQ end end def edit 4ontext.wit,A" ope%:find !. S : ondition" !. )Qu"erAid ! UQJ "e""ion):u"er++ TJ : reate !. S :u"erAid !. "e""ion):u"er+ T( do if param"):id+ @ ontext ! 4ontext.find%param"):id+( el"e @ ontext ! 4ontext.new end if re@ue"t.po"tU @ ontext.attribute" ! param"): ontext+ if @ ontext."ave fla",):noti e+ ! Q<,e ontext wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end end def delete 4ontext.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e ontext wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
+ escopo aqui no s< limita a busca6 como tambJm limita a criao6 in"orman o automaticamente o atributo user0id quan o necessrio. Para impe ir que o atributo "osse automaticamente in"orma o em um "ormulrio6 precisarIamos mu ar o nosso mo elo e a osA
la"" 4ontext K L tive2e ord::/a"e attrAprote ted :u"erAid validate"Apre"en eAof :name validate"Auni@uene""Aof :name validate"Apre"en eAof :u"erAid
1'!
Com o escopo que estabelecemos6 vemos que se um usurio tentar acessar iretamente um conte0to cria o por outro usurio6 teremos um erroA
/ vemos que6 em nosso arquivo log/de!elopment.log6 o -DL #era o continha a con io in"orma a no escopoA
1'5
=ro e""ing 4ontext"4ontroller?edit %for 1&R.'.'.1 at &''*-'6-&$ 1&:$':$5( )X1<+ >e""ion 7G: 1 381d R'663b55*'**5'*68Rd*35&18 =arameter": SQa tionQ!.QeditQJ QidQ!.Q1QJ Q ontrollerQ!.Q ontext"QT 4ontext Doad %'.''$R35( >1D14< P 92:B ontext" 0;121 %u"erAid ! &( L8G % ontext".id ! H1H( D7B7< 1
escopo no se aplicou ao mJto o sa!e6 e6 conseqRentemente6 o re#istro "oi salvo sem um atributo user0id. A e0plicao para isso po eria ser uplaA po erIamos i>er que o problema est no nosso uso e sa!e6 tanto para criao quanto para atuali>ao6 ou po erIamos tambJm i>er que h um problema no Rails6 = que6 se e0aminarmos o c< i#o ob=eto J cria o. /6 o mesmo6 veremos que o escopo no J aplica o em to as situa&es em que um e "ato6 e0istem al#uns problemas com o mJto o para os quais solu&es = "oram
propostas na base e c< i#o e esenvolvimento o Rails e po e ser estaro em suas vers&es "uturas. A soluo6 no momento6 J "a>er uma mo i"icao na ao editA
la"" 4ontext"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t 4ontext.wit,A" ope%:find !. S : ondition" !. )Qu"erAid ! UQJ "e""ion):u"er++ T( do @ ontextApage"J @ ontext" ! paginate : ontext"J :perApage !. 5J :order !. QnameQ end end def edit 4ontext.wit,A" ope%:find !. S : ondition" !. )Qu"erAid ! UQJ "e""ion):u"er++ TJ : reate !. S :u"erAid !. "e""ion):u"er+ T( do if re@ue"t.getU if param"):id+ @ ontext ! 4ontext.find%param"):id+( el"e @ ontext ! 4ontext.new end el"if re@ue"t.po"tU @ ontext ! param"):id+ U 4ontext.update%param"):id+J param"): ontext+( : 4ontext. reate%param"): ontext+( if @ ontext.validU fla",):noti e+ ! Q<,e ontext wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end en end end def delete 4ontext.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e ontext wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
o que i eal por e0i#ir c< i#o e0tra6 mas J o melhor que po emos
1'2
"a>er no momento. Bo c< i#o acima6 notamos que seria interessante ter o mesmo escopo aplica o a to as as a&es6 para prevenir qualquer "orma e acesso sem parLmetros. .ma soluo simples J criar um mJto o que nos retorne os escopos e aplicar isso a to os as a&es e maneira mais limpaA
la"" 4ontext"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t 4ontext.wit,A" ope%neededA" ope"( do @ ontextApage"J @ ontext" ! paginate : ontext"J :perApage !. 5J :order !. QnameQ end end def edit 4ontext.wit,A" ope%neededA" ope"( do if re@ue"t.getU if param"):id+ @ ontext ! 4ontext.find%param"):id+( el"e @ ontext ! 4ontext.new end el"if re@ue"t.po"tU @ ontext ! param"):id+ U 4ontext.update%param"):id+J param"): ontext+( : 4ontext. reate%param"): ontext+( if @ ontext.validU fla",):noti e+ ! Q<,e ontext wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end end def delete 4ontext.wit,A" ope%neededA" ope"( do 4ontext.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e ontext wa" "u redire tAto :a tion !. Qli"tQ end end prote ted def neededA" ope" @neededA" ope" MM! S :find !. S : ondition" !. )Qu"erAid ! UQJ "e""ion):u"er++ TJ : reate !. S :u"erAid !. "e""ion):u"er+ T T end end
e""fully deletedQ
ireto M "onte. /m especial6 queremos uma soluo que possa ser aplica a a
qualquer controller que precisarmos. Como nossa aplicao J bem simples6 po emos "a>er isso atravJs um "iltro especialmente cria o para isso.
14$
.m Pnica ressalva aqui J que estamos entran o em um territ<rio no Rails on e no h uma AP? estabeleci a6 e o nosso c< i#o J su=eito a mu anas e implementao que po em quebr;lo eventualmente. (as6 no momento6 po emos apren er al#o com o que vamos "a>er.
FILTROS AROUND
Como vamos precisar e uma implementao que se=a acessIvel a to os controllers6 e e nossa aplicao e maneira #eral a to a aplicao6 precisamos coloc;la em al#um lu#ar e mo o que ela se=a carre#a a
automaticamente. Po emos "a>er isso usan o o iret<rio extras6 que J "eito especialmente para isso. Primeiro precisamos habilitar esse iret<rio em nosso arquivo config/en!ironment.rb6 que J on e as
con"i#ura&es para a aplicao esto e"ini as6 e epois incluir a chama a ao arquivo que precisamosA
? /e "ure to re"tart your web "erver w,en you modify t,i" file. ? On omment below to for e 2ail" into produ tion mode w,en ? you donHt ontrol web/app "erver and anHt "et it t,e proper way ? 18Z)H2L7D>A18ZH+ MM! Hprodu tionH ? >pe ifie" gem ver"ion of 2ail" to u"e w,en vendor/rail" i" not pre"ent 2L7D>AX1BAZ12>7:8 ! H1.1.*H ? /oot"trap t,e 2ail" environmentJ framewor#"J and default re@uire 9ile.-oin%9ile.dirname%AA97D1AA(J HbootH( onfiguration
2ail"::7nitializer.run do M onfigM ? >etting" in onfig/environment"/P ta#e pre eden e t,o"e "pe ified ,ere ? >#ip framewor#" youHre not going to u"e %only wor#" if u"ing vendor/rail"( ? onfig.framewor#" -! ) :a tionAwebA"ervi eJ :a tionAmailer + ? Ldd additional load pat," for your own u"tom dir" onfig.loadApat," N! Y0% ?S2L7D>A2::<T/extra" ( ? 9or e all environment" to u"e t,e "ame logger level ? %by default produ tion u"e" :infoJ t,e ot,er" :debug( ? onfig.logAlevel ! :debug ? O"e t,e databa"e for "e""ion" in"tead of t,e file "y"tem ? % reate t,e "e""ion table wit, Hra#e db:"e""ion": reateH( ? onfig.a tionA ontroller."e""ionA"tore ! :a tiveAre ordA"tore ? O"e >CD in"tead of L tive 2e ordH" " ,ema dumper w,en reating t,e te"t databa"e. ? <,i" i" ne e""ary if your " ,ema anHt be ompletely dumped by t,e " ,ema dumperJ ? li#e if you ,ave on"traint" or databa"e-"pe ifi olumn type" ? onfig.a tiveAre ord." ,emaAformat ! :"@l ? L tivate ob"erver" t,at ",ould alway" be running ? onfig.a tiveAre ord.ob"erver" ! : a ,erJ :garbageA olle tor ? Ba#e L tive 2e ord u"e O<4-ba"e in"tead of lo al time ? onfig.a tiveAre ord.defaultAtimezone ! :ut ? >ee 2ail"::4onfiguration for more option" end ? Ldd new infle tion rule" u"ing t,e following format ? %all t,e"e example" are a tive by default(: ? 7nfle tor.infle tion" do Minfle tM ? infle t.plural /^%ox($/iJ HE1enH
141
? infle t."ingular /^%ox(en/iJ HE1H ? infle t.irregular Hper"onHJ HpeopleH ? infle t.un ountable Yw% fi", ",eep ( ? end ? 7n lude your appli ation re@uire Qu"erAfilter.rbQ onfiguration below
e con"i#ura&es que po em ser mo i"ica as para otimi>ar o e uma aplicao e e apoio se=am carre#a as6 re u>in o o uso
ou usar Geb services6 po e impe ir que as bibliotecas mem<ria e aumentan o a veloci a e e sua aplicao.
Bo arquivo acima6 = aproveitamos para incluir o nosso arquivo6 que ser carre#a o automaticamente =unto com a aplicao. A#ora6 s< precisamos cri;lo Slembre;se e criar o iret<rio tambJmTA
la"" O"er9ilter def initialize%targetA la""J targetAmet,od( @targetA la"" ! targetA la"" @targetAmet,od ! targetAmet,od end def before% ontroller( filter" ! ontroller."end%@targetAmet,od( @targetA la"".in"tan eAeval do " opedAmet,od" KK wit,A" ope%filter"( S end end def after% ontroller( @targetA la"".in"tan eAeval do " opedAmet,od".pop end end end
urrentA" opedAmet,od" T
A princIpio o arquivo acima po e parecer um pouco esotJrico6 mas J realmente bem simples. AserFilter J a primeira classe que estamos crian o em nossa aplicao que no se encai0a na estrutura pa ro o Rails. Como pensamos em utili>;la como um "iltro o tipo aroun 6 ou se=a6 e0ecuta o antes e epois e uma ao6 precisamos e"inir pelo menos ois mJto osA before e after6 que6 comos os nomes i>em6 ro am antes e epois a ao. Bo caso acima6 precisamos tambJm e um mJto o initiali>e porque vamos passar parLmetros e criao o mJto o
para a classe. /m Rub,6 o mJto o initiali=e J chama a quan o a classe J instancia a atravJs new. Duaisquer parLmetros passa os para new sero passa os tambJm para initiali=e.
Para o nosso "iltro6 precisamos e uas coisasA a classe M qual estaremos aplican o o escopo e qual o escopo a ser aplica o. Como "iltros aroun ro am em conte0tos especiais6 o melhor que po emos "a>er J passar o nome o mJto o que criamos para e"inir o "iltro e invoc;lo no momento em que precisarmos.
14*
Antes e estu armos a classe6 ve=a como "icou o controller epois a aplicao a mesmaA
la"" 4ontext"4ontroller K Lppli ation4ontroller aroundAfilter O"er9ilter.new%4ontextJ :neededA" ope"( def index li"t render :a tion !. Qli"tQ end def li"t @ ontextApage"J @ ontext" ! paginate : ontext"J :perApage !. 5J :order !. QnameQ end def edit if re@ue"t.getU if param"):id+ @ ontext ! 4ontext.find%param"):id+( el"e @ ontext ! 4ontext.new end el"if re@ue"t.po"tU @ ontext ! param"):id+ U 4ontext.update%param"):id+J param"): ontext+( : 4ontext. reate%param"): ontext+( if @ ontext.validU fla",):noti e+ ! Q<,e ontext wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def delete 4ontext.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e ontext wa" "u redire tAto :a tion !. Qli"tQ end prote ted def neededA" ope" @neededA" ope" MM! S :find !. S : ondition" !. )Qu"erAid ! UQJ "e""ion):u"er++ TJ : reate !. S :u"erAid !. "e""ion):u"er+ T T end end
e""fully deletedQ
iretas a with0scope
)amos enten er a#ora como a classe "unciona. Dois parLmetros so passa os ao "iltro em sua criaoA a classe que ser mo i"ica a e um mJto o pr<prio controller que simplesA escreve os "iltros a serem aplica os. + mJto o initiali=e o
a classe AserFilter J
def initialize%targetA la""J targetAmet,od( @targetA la"" ! targetA la"" @targetAmet,od ! targetAmet,od end
14'
Apenas #uar amos as variveis passa as na instLncia para serem utili>a as momento. Depois temos o mJto o beforeA
epois. Ba a
e especial atJ o
def before% ontroller( filter" ! ontroller."end%@targetAmet,od( @targetA la"".in"tan eAeval do " opedAmet,od" KK wit,A" ope%filter"( S end end
urrentA" opedAmet,od" T
que o mJto o "a> aqui J enviar uma mensa#em para o controller invocan o o mJto o que ob=eto. + mJto o send po e ser usa o em qualquer classe para invocar qualquer mJto o varivel filters aqui receber ento o resulta o necessrios. A se#un a linha usa o mJto o instance0e!al para reali>ar um pouco a invocao6 que so as
como conten o os escopos necessrios Sno caso aqui6 needed0scopesT. Lembre;se que tu o em Rub, J um e"ini&es
bloco que6 ao invJs e ser e0ecuta o no conte0to o mJto o atual6 J e0ecuta o no conte0to a instLncia sob o qual "oi invoca o6 no caso aqui6 a pr<pria classe 7ontext. As classes que so -cti!e1ecord::Gase6 ou se=a6 to as as classes que e"inem os nossos mo elos e a os6 possuem vrios
mJto os e variveis relaciona as a escopos. )emos tr@s acimaA with0scope6 scoped0methods e current0scoped0methods. Q trabalhamos com o primeiro e os uma varivel ois se#uintes escrevem respectivamente e instLncia que J arra, com to os os escopos ativos Sfind6 create6 etc.T e um mJto o que
retorna o Pltimo escopo aninha o = que escopos po em ser empilha os. A linha e c< i#o ento "a> al#o interessante. Primeiro6 ela entra em um escopo6 usan o o pr<prio mJto o e obter o controller6 e entro o bloco6 invoca o mJto o o
current0method0scopes para retornar o Pltimo escopo aninha o que "oi a iciona o. /sse J o retorno
bloco que ento J inseri o como mais um item na varivel scoped0methods6 e"etivamente ativan o um escopo na classe. )oc@ po e estar pensan oA mas isso no aplica o escopo uas ve>es` Bo6 pela m#ica os blocosA to lo#o o bloco e with0scope retorne6 o "iltro aplica o J removi o6 se#ui o pela a io o retorno. Aqui vai uma coinci @nciaA epois e ter elabora o a verso acima o c< i#o6 lembrei que e0istia um plu#in que "a>ia =ustamente isso6 e uma maneira bem mais avana a. )oc@ po e encontrar o plu#in e uma a tJcnica que usamos nesse ponto no en ereo se#uinteA escrio muito mais elabora a e completa
http://habtm.com/articles/;"">/";/;;/nested'with0scope. ?nclusive6 a linha acima J uma a aptao e uma linha o plu#in6 que e0ecuta e maneira mais completa o que eu estava "a>en o antes.
144
7inalmente6 o mJto o after remove o escopo cria o no mJto o before6 removen o;o scoped0methods6 se#uin o e0atamente o c< i#o interno o Rails. + resulta o J que6 aplicao o "iltro6 to as as chama as ao controller esto ebai0o os escopos que precisamos.
o arra, e
urante o tempo
-e voc@ acessar a aplicao a#ora6 com usurio i"erentes6 ver que o controller que manipula os conte0tos J capa> e "iltrar per"eitamente os re#istros e acor o com o usurio autentica o no momento6 sem qualquer inter"er@ncia no c< i#o o mesmo. /m nossa aplicao6 queremos que esse "iltro se=a aplica o a to os os controllers. Po erIamos ento mover suas chama as para nosso controller primrio6 que "icaria assimA
la"" Lppli ation4ontroller K L tion4ontroller::/a"e beforeAfilter :aut,enti ate "#ipAbeforeAfilter :aut,enti ateAadmini"trationJ :only !. ):a aroundAfilter aroundAfilter aroundAfilter aroundAfilter O"er9ilter.new%4ontextJ :neededA" ope"( O"er9ilter.new%=ro-e tJ :neededA" ope"( O"er9ilter.new%L tionJ :neededA" ope"( O"er9ilter.new%2e"our eJ :neededA" ope"( e""Adenied+
,elperAmet,od :"e""ionAu"er def a e""Adenied render :template !. Q",ared/a end prote ted def "e""ionAu"er @"e""ionAu"er MM! O"er.find%:fir"tJ : ondition" !. )Hid ! UHJ "e""ion):u"er++( end def aut,enti ate unle"" "e""ion):u"er+ "e""ion):returnAto+ ! re@ue"t.re@ue"tAuri redire tAto : ontroller !. QloginQJ :a tion !. QloginQ return fal"e end return true end def aut,enti ateAadmini"tration unle"" "e""ionAu"er [[ "e""ionAu"er.adminU redire tAto :a tion !. Qa e""AdeniedQ return fal"e end return true end def neededA" ope" @neededA" ope" MM! S :find !. S : ondition" !. )Qu"erAid ! UQJ "e""ion):u"er++ TJ : reate !. S :u"erAid !. "e""ion):u"er+ T T end end e""AdeniedQ
la"" 4ontext"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @ ontextApage"J @ ontext" ! paginate : ontext"J :perApage !. 5J :order !. QnameQ end def edit if re@ue"t.getU if param"):id+ @ ontext ! 4ontext.find%param"):id+( el"e @ ontext ! 4ontext.new end el"if re@ue"t.po"tU @ ontext ! param"):id+ U 4ontext.update%param"):id+J param"): ontext+( : 4ontext. reate%param"): ontext+( if @ ontext.validU fla",):noti e+ ! Q<,e ontext wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def delete 4ontext.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e ontext wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
a os6 percebemos que nossos controllers no as mu anas para usar os mJto os create e
update ao invJs e sa!e6 a o o problema que percebemos com a aplicao e escopos6 que "ica aqui como um e0ercIcio para o leitorT. -e ro armos al#uns e nossos controllers6 teremos al#uns problemas com as nossas con i&es6 por causa o nome ambI#uo e nosso coluna6 que aparece em vrias classes. .ma "orma rpi a e resolver isso J mu ar o c< i#o e nosso "iltro paraA
la"" O"er9ilter def initialize%targetA la""J targetAmet,od( @targetA la"" ! targetA la"" @targetAmet,od ! targetAmet,od end def before% ontroller( filter" ! ontroller."end%@targetAmet,odJ @targetA la""( @targetA la"".in"tan eAeval do " opedAmet,od" KK wit,A" ope%filter"( S urrentA" opedAmet,od" T end end def after% ontroller(
141
la"" Lppli ation4ontroller K L tion4ontroller::/a"e beforeAfilter :aut,enti ate "#ipAbeforeAfilter :aut,enti ateAadmini"trationJ :only !. ):a aroundAfilter aroundAfilter aroundAfilter aroundAfilter O"er9ilter.new%4ontextJ :neededA" ope"( O"er9ilter.new%=ro-e tJ :neededA" ope"( O"er9ilter.new%L tionJ :neededA" ope"( O"er9ilter.new%2e"our eJ :neededA" ope"( e""Adenied+
,elperAmet,od :"e""ionAu"er def a e""Adenied render :template !. Q",ared/a end prote ted def "e""ionAu"er @"e""ionAu"er MM! O"er.find%:fir"tJ : ondition" !. )Hid ! UHJ "e""ion):u"er++( end def aut,enti ate unle"" "e""ion):u"er+ "e""ion):returnAto+ ! re@ue"t.re@ue"tAuri redire tAto : ontroller !. QloginQJ :a tion !. QloginQ return fal"e end return true end def aut,enti ateAadmini"tration unle"" "e""ionAu"er [[ "e""ionAu"er.adminU redire tAto :a tion !. Qa e""AdeniedQ return fal"e end return true end def neededA" ope"%targetA la""( S :find !. S : ondition" !. )Q?StargetA la"".tableAnameT.u"erAid ! UQJ "e""ion):u"er++ TJ : reate !. S :u"erAid !. "e""ion):u"er+ T T end end e""AdeniedQ
.san o a pr<pria classe passa a como parLmetro6 especi"icamos melhor a con io e "iltro. ?sso resolve a maior parte os nossos problemas e encontramos uma maneira simples e "iltrar os re#istros que precisamos sem ter que espalhar con i&es por to a a nossa aplicao. Como menciona o anteriormente6 escopos eve ser usa os com cui a o6 mas o"erecem solu&es e0tremamente ele#antes para
14!
etermina os problemas.
XML
.ma as coisas que po emos "a>er em Rails J retornar 9(L para consumo e clientes em qualquer "ormato que precisamos. /m uma aplicao como a nossa6 um e0emplo e 9(L que po ermos querer pro u>ir J um
"ee R--.
/u ima#ino que a maioria e n<s este=a "amiliar com esse tipo e tecnolo#ia. Aqueles que no estiverem
po em encontrar mais in"orma&es em http://www.rssficado.com.br/. -eria interessante que os usurios a nossa aplicao "osse capa>es e acessarem um "ee R-- com as
que seria automaticamente atuali>a a caso um novo re#istro "osse inseri o na base e a os. Como mencionamos anteriormente6 o Rails po e pro u>ir no s< saI a 4:(L6 mas qualquer outra que se=a necessria. Bo caso aqui6 vamos pro u>ir saI a 9(L que J automaticamente suporta a pelo mecanismo templates o Rails. Para comear6 vamos #erar um novo controllerA e
ronaldo@minerva:~/tmp/gtd$ " ript/generate ontroller feed exi"t" app/ ontroller"/ exi"t" app/,elper"/ reate app/view"/feed exi"t" te"t/fun tional/ reate app/ ontroller"/feedA ontroller.rb reate te"t/fun tional/feedA ontrollerAte"t.rb reate app/,elper"/feedA,elper.rb
/sse controller ter somente uma ao que retornar o nosso "ee R--A
la"" 9eed4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.find :allJ : ondition" !. )Qdone ! 'Q+J :order !. Q reatedAat de" Q render :layout !. fal"e end end
ata
ecrescente
e criao.
Precisamos tambJm ren eri>ar a ao sem la,out = que a vieG associa a #erar tu o o que precisamos. A#ora6 precisamos criar a vieG relaciona a com essa ao. Besse caso6 ao invJs index.rhtml6 usaremos um arquivo chama o index.rxmlA e #erarmos um arquivo
xml.in"tru tW
145
xml.r"" Qver"ionQ !. Q&.'QJ Qxmln":d Q !. Q,ttp://purl.org/d /element"/1.1/Q do xml. ,annel do xml.title xml.lin# xml.pubGate xml.de" ription QGue L tion"Q urlAfor%:onlyApat, !. fal"eJ : ontroller !. Qa tion"Q( 4X7.rf 11&3Adate%@a tion".fir"t. reatedAat( if @a tion".anyU QGue a tion"Q
@a tion".ea , do Ma tionM xml.item do xml.title a tion.de" ription xml.lin# urlAfor%:onlyApat, !. fal"eJ : ontroller !. Qa tion"QJ :a tion !. QeditQJ :id !. a tion.id( xml.pubGate 4X7.rf 11&3Adate%a tion. reatedAat( xml.guid urlAfor%:onlyApat, !. fal"eJ : ontroller !. Qa tion"QJ :a tion !. QeditQJ :id !. a tion.id( end end end end
aplicao #erar 9(L quase como se estivesse escreven o c< i#o Rub,. Por meio e uma aplicao interessante as proprie a es a lin#ua#em6 a classe HmlGuilder intercepta as eclara&es automticas e elementos 9(L6 que
A tJcnica usa a J e0atamente a mesma #era a para criar os mJto os automticos que temos nas classes e a os6 no "ormato find0@6 crian o o necessrio no momento em que a primeira invocao J "eita que o esenvolve or possa utili>ar a classe como se to os os atributos anteriormente. + resulta o J o se#uinteA e mo o o arquivo 9(L e0istissem
142
escrito atravJs
e uma
mesmo simplesmente "alha porque6 sem autenticao6 tu o o que o que J retorna o J um re irecionamento para a p#ina e lo#in. Precisamos ento e um maneira alternativa e autenticar o nosso "ee .
AUTENTICAO HTTP
.ma "orma e "a>er isso J usar a autenticao = usa a por nave#a ores6 cu=os tipos principais so 3asic6 os nave#a ores em e0ist@ncia. A primeira J a Di#est e B:L(. As tr@s so suporta as pela maior parte proprietria6 sen o uma variao
"orma mais simples6 mas mais "acilmente interceptvel. A terceira J bem se#ura6 mas comple0a e o pa ro Ferberos usa a pela (icroso"t. /mbora suporta a por outros os a#re#a ores no a nave#a ores6 no J o que queremos implementar no momento = que a maioria
15$
suporta. 7icamos ento com a 3asic6 cu=o uso J relativamente simples e que J suporta a pelos principais leitores e R--. Para "a>er po ermos usar essa autenticao6 precisamos mu ar o nosso "iltro e lo#in. A autenticao 3asic "unciona basicamente com o uso envia os pelo nave#a or. 3asta recuperamos esses e um obscurecimento simples o lo#in e senha
la"" 9eed4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.find :allJ : ondition" !. )Qdone ! 'Q+J :order !. Q reatedAat de" Q render :layout !. fal"e end prote ted def aut,enti ate loginJ pa""word ! getAba"i Adata u"er ! O"er.findAbyAloginAandApa""word%loginJ pa""word( if u"er "e""ion):u"er+ ! u"er.id return true el"e ,eader")Q>tatu"Q+ ! QOnaut,orizedQ ,eader")Q000-Lut,enti ateQ+ ! Q/a"i realm!EQX<GEQQ render :text !. Q9ailed to aut,enti ate t,e provided login and pa""wordQJ :"tatu" !. Q$'1 Onaut,orizedQ return fal"e end end def getAba"i Adata loginJ pa""word ! QQJ QQ if re@ue"t.env.,a"A#eyU%Q_-;<<=ALO<;:27`L<7:8Q( data ! re@ue"t.env)Q_-;<<=ALO<;:27`L<7:8Q+.toA"."plit el"if re@ue"t.env.,a"A#eyU%Q;<<=ALO<;:27`L<7:8Q( data ! re@ue"t.env)Q;<<=ALO<;:27`L<7:8Q+.toA"."plit end if data and data)'+ !! Q/a"i Q loginJ pa""word ! /a"e*$.de ode*$%data)1+(."plit%Q:Q()'..1+ end return )loginJ pa""word+ end end
+ que "i>emos acima "oi mu ar o "iltro que "a> a autenticao. De autenticao basea a em uma p#ina
lo#in6 temos a#ora autenticao 3asic. Como nosso controller her a to os os seus mJto os
e somente criar um outro "iltro porque nossos escopos iniciali>a a nesse "iltro. -e a icionssemos outros6 os "iltros epen em e uma varivel
o controller
base6 para trocarmos a implementao e um mJto o6 basta re e"inI;lo. Precisamos "a>er isso aqui ao invJs e sesso que J e escopo "alhariam por estarem no controller
base e ro arem antes e qualquer "iltro e"ini o em outro classe. + novo mJto o authenticate J bem simples. /le veri"ica se os a os e lo#in recebi os correspon em a um
151
ver a eiro J retorna o. Caso contrrio6 cabealhos in ican o que o usurio no "oi autori>a o so e acor o com a especi"icao6 =unto com o valor "alse para bloquear qualquer processamento
+ mJto o get0basic0data6 por sua ve>6 tenta obter testa os em or em. Depois
a os
e autenticao
as variveis
e ambiente
a os po em estar em
eco i"ica os o "ormato 3ase146 usa o pelo nave#a or e retorna os. + resulta o J o se#uinteA
-e o usurio recusa a autenticao6 vemos al#o assim Sno ?nternet /0plorer po e "icar um pouco e acor o com as con"i#ura&es o mesmo6 mas o resulta o "inal J o mesmoTA
i"erente
15*
Precisamos a#ora somente e uma pequena alterao em nosso controller para "inali>;loA
la"" 9eed4ontroller K Lppli ation4ontroller "#ipAbeforeAfilter :aut,enti ate beforeAfilter :aut,enti ateAba"i def index @a tion" ! L tion.find :allJ : ondition" !. )Qdone ! 'Q+J :order !. Q reatedAat de" Q ,eader")Q4a ,e-4ontrolQ+ ! Qno- a ,eQ ,eader")Q4ontent-<ypeQ+ ! Qappli ation/xmlNr""Q render :layout !. fal"e end prote ted def aut,enti ateAba"i loginJ pa""word ! getAba"i Adata u"er ! O"er.findAbyAloginAandApa""word%loginJ pa""word( if u"er "e""ion):u"er+ ! u"er.id return true el"e ,eader")Q>tatu"Q+ ! QOnaut,orizedQ ,eader")Q000-Lut,enti ateQ+ ! Q/a"i realm!EQX<GEQQ render :text !. Q9ailed to aut,enti ate t,e provided login and pa""wordQJ :"tatu" !. Q$'1 Onaut,orizedQ return fal"e end end def getAba"i Adata loginJ pa""word ! QQJ QQ if re@ue"t.env.,a"A#eyU%Q_-;<<=ALO<;:27`L<7:8Q(
15'
data ! re@ue"t.env)Q_-;<<=ALO<;:27`L<7:8Q+.toA"."plit el"if re@ue"t.env.,a"A#eyU%Q;<<=ALO<;:27`L<7:8Q( data ! re@ue"t.env)Q;<<=ALO<;:27`L<7:8Q+.toA"."plit end if data and data)'+ !! Q/a"i Q loginJ pa""word ! /a"e*$.de ode*$%data)1+(."plit%Q:Q()'..1+ end return )loginJ pa""word+ end end
A primeira instruo J para que o nave#a or no use o seu cache interno e a se#un a in ica o tipo e a os que eve ser retorna o quan o usan o R--. A#ora6 po emos ver como o "ee "icaria em um aplicao R-- pr<priaA
154
ENVIANDO E-MAILS
+ Rails possui to a uma parte6 chama a e Action(ailer6 responsvel pelo envio e recebimento e e;mails. Para "ins esse tutorial6 vamos somente usar a parte e envio. -uponhamos que queremos enviar um e;mail para usurio recJm;cria os6 in"orman o;lhes o acesso que o e;mail o
eles a#ora tem ao sistema. Po emos "a>er isso "acilmente. Primeiramente6 J claro6 precisamos usurio6 que no temos em nosso mo elo. Po emos a icionar isso "acilmente com uma mi#raoA
la"" Ldd1mail<oO"er K L tive2e ord::Bigration def "elf.up addA olumn :u"er"J :emailJ :"tring end def "elf.down removeA olumn :u"er"J :email end end
la"" O"er K L tive2e ord::/a"e validate"Apre"en validate"Apre"en validate"Apre"en validate"Apre"en eAof eAof eAof eAof :name :login :pa""word :email
A vali ao acima somente veri"ica se o e;mail "oi in"orma o6 mas no se o mesmo J vali o. /0iste um
plu#in especI"ico que permite a vali ao real e e;mails que voc@ po e utili>ar nesse caso.
/ a lista#em e usurioA
K,1.O"er"K/,1. KY if fla",):noti e+ Y.
155
Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew O"erQJ :a tion !. QeditQ Y.K/p. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.8ameK/t,. Kt,.DoginK/t,. Kt,.Ldmini"tratorK/t,. Kt,.1-mailK/t,. Kt,.L tion"K/t,. K/tr. KY @u"er".ea , do Mu"erM Y. Ktr. Ktd.KY! u"er.name Y.K/td. Ktd.KY! u"er.login Y.K/td. Ktd.KY! ye"AorAnoU%u"er.adminU( Y.K/td. Ktd.KY! u"er.email Y.K/td. Ktd. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. u"er Y. or KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. u"er TJ : onfirm !. QLre you "ureUQ Y. K/td. K/tr. KY end Y. K/table. Kp.KY! paginationAlin#"%@u"erApage"( Y.K/p.
7inalmente6 a e ioA
K,1.KY if @u"er.newAre ordU Y.8ewKY el"e Y.1ditingKY end Y. O"erK/,1. KY! errorAme""age"Afor Qu"erQ Y. KY! "tartAformAtag :a tion !. QeditQJ :id !. @u"er Y. Kp. Klabel for!Qu"erAnameQ.8ame:K/label.Kbr. KY! textAfield Qu"erQJ QnameQ Y. K/p. Kp. Klabel for!Qu"erAloginQ.Dogin:K/label.Kbr. KY! textAfield Qu"erQJ QloginQ Y. K/p. Kp. Klabel for!Qu"erApa""wordQ.=a""word:K/label.Kbr. KY! pa""wordAfield Qu"erQJ Qpa""wordQ Y. K/p. Kp. Klabel for!Qu"erAadminQ.Ldmini"trator:K/label.Kbr. KY! "ele t Qu"erQJ QadminQJ ))Q8oQJ fal"e+J )QFe"QJ true++ Y. K/p. Kp. Klabel for!Qu"erAemailQ.1-mail:K/label.Kbr. KY! textAfield Qu"erQJ QemailQ Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#Ato Q4an elQJ :a tion !. Qli"tQY. K/p. KY! endAformAtag Y.
151
A#ora precisamos e uma "orma e enviar um e;mail para novos usurios. Para enviar e;mails6 o Rails utili>a
o mo el como
ronaldo@minerva:~/tmp/gtd$ " ript/generate mailer regi"trationAnotifier exi"t" app/model"/ reate app/view"/regi"trationAnotifier exi"t" te"t/unit/ reate te"t/fixture"/regi"trationAnotifier reate app/model"/regi"trationAnotifier.rb reate te"t/unit/regi"trationAnotifierAte"t.rb
Como voc@ po e ver6 esse coman o um arquivo em app/models6 que contJm o se#uinteA
V nessa classe que criaremos os mJto os que #eram o e;mail a ser envia o6 incluin o os seus
a os. /ssa
classe utili>a uma vieG associa o ao mJto o para #erar o conteP o o e;mail. Comearemos com o mJto oA
la"" 2egi"tration8otifier K L tionBailer::/a"e def regi"trationAnotifi ation%u"erJ "e""ionAu"er( re ipient" Q?Su"er.nameT K?Su"er.emailT.Q from Q?S"e""ionAu"er.nameT K?S"e""ionAu"er.emailT.Q "ub-e t Q2egi"trationQ body Qu"erQ !. u"er end end
o e;mail. +s
recipient6 o en ereo para o qual o e;mail ser envia oK from6 o en ereo isponIvel na mesma atravJs a associao. )amos aqui mais uma mostra
assunto o e;mailK e body6 o corpo a mensa#em6 que ser #era o a partir e uma vieG6 com a varivel user/ Rails6 atravJs o uso e mJto os que e"inem uma lin#ua#em e omInio especI"ica. Consultan o a ocumentao6 voc@ ver que po emos manipular outros cabealhos como cc6 bcc e sent0on6 representan o respectivamente os cabealhos c<pia;carbono6 c<pia;carbono invisIvel e ata e envio. )amos enviar um e;mail simples6 mas na ocumentao voc@ tambJm ver que J possIvel enviar mensa#ens 4:(L e6 inclusive6 mensa#ens com partes mPltiplas6 para uso com ane0os e outros a os. Para #erarmos o corpo acima6 "ican o o a mensa#em6 precisamos e uma vieG com o mesmo nome arquivo como registration0notification.rhtml6 o mJto o que criamos no iret<rio
locali>a o
app/!iews/registration0notifierA
15!
Gear KY! @u"er.name Y.: 0el ome to our X<G appli ation. <o a ,ttp://lo al,o"t:3''/,ome Fou login i" KY! @u"er.login Y.J and your pa""word i" KY! @u"er.pa""word Y.. <,an#" for your intere"t. 2egard"J <,e Ldmini"trator e"" itJ log in to:
Como voc@ po e ver6 um arquivo simples no mesmo estilo as vieGs que = #eramos6 com a Pnica i"erena e ser te0tual. Antes e enviar o nosso e;mail pelo controller6 precisamos e uma mo i"icao na con"i#urao e nossa
aplicaoA in"ormar o servi or pelo qual os e;mails sero envia os. ?sso somente J necessrio se voc@ no tiver um servi or local que respon a em localhost. De qualquer "orma6 como esse po e ser =ustamente o seu caso6 mostramos aqui a mo i"icao necessria6 que acontece no arquivo config/en!iroment.rbA
? /e "ure to re"tart your web "erver w,en you modify t,i" file. ? On omment below to for e 2ail" into produ tion mode w,en ? you donHt ontrol web/app "erver and anHt "et it t,e proper way ? 18Z)H2L7D>A18ZH+ MM! Hprodu tionH ? >pe ifie" gem ver"ion of 2ail" to u"e w,en vendor/rail" i" not pre"ent 2L7D>AX1BAZ12>7:8 ! H1.1.*H ? /oot"trap t,e 2ail" environmentJ framewor#"J and default re@uire 9ile.-oin%9ile.dirname%AA97D1AA(J HbootH( onfiguration
2ail"::7nitializer.run do M onfigM ? >etting" in onfig/environment"/P ta#e pre eden e t,o"e "pe ified ,ere ? >#ip framewor#" youHre not going to u"e %only wor#" if u"ing vendor/rail"( ? onfig.framewor#" -! ) :a tionAwebA"ervi eJ :a tionAmailer + ? Ldd additional load pat," for your own u"tom dir" onfig.loadApat," N! Y0% ?S2L7D>A2::<T/extra" ( ? 9or e all environment" to u"e t,e "ame logger level ? %by default produ tion u"e" :infoJ t,e ot,er" :debug( ? onfig.logAlevel ! :debug ? O"e t,e databa"e for "e""ion" in"tead of t,e file "y"tem ? % reate t,e "e""ion table wit, Hra#e db:"e""ion": reateH( ? onfig.a tionA ontroller."e""ionA"tore ! :a tiveAre ordA"tore ? O"e >CD in"tead of L tive 2e ordH" " ,ema dumper w,en reating t,e te"t databa"e. ? <,i" i" ne e""ary if your " ,ema anHt be ompletely dumped by t,e " ,ema dumperJ ? li#e if you ,ave on"traint" or databa"e-"pe ifi olumn type" ? onfig.a tiveAre ord." ,emaAformat ! :"@l ? L tivate ob"erver" t,at ",ould alway" be running ? onfig.a tiveAre ord.ob"erver" ! : a ,erJ :garbageA olle tor ? Ba#e L tive 2e ord u"e O<4-ba"e in"tead of lo al time ? onfig.a tiveAre ord.defaultAtimezone ! :ut
155
? >ee 2ail"::4onfiguration for more option" end ? ? ? ? ? ? ? ? Ldd new infle tion rule" u"ing t,e following format %all t,e"e example" are a tive by default(: 7nfle tor.infle tion" do Minfle tM infle t.plural /^%ox($/iJ HE1enH infle t."ingular /^%ox(en/iJ HE1H infle t.irregular Hper"onHJ HpeopleH infle t.un ountable Yw% fi", ",eep ( end onfiguration below
L tionBailer::/a"e."erverA"etting" ! S :addre"" !. Qmail.yourdomain. omQJ :domain !. Qyourdomain. omQJ :u"erAname !. Qu"er@yourdomain. omQJ :pa""word !. QPPPPPPQJ :aut,enti ation !. :login T
/ ite para os a os necessrios e reinicie o servi or para recarre#ar a nova con"i#urao. Por "im6 precisamos simplesmente mo i"icar o nosso controller responsvel pelos usurios para o envio mensa#em propriamente itoA a
la"" O"er"4ontroller K Lppli ation4ontroller beforeAfilter :aut,enti ateAadmini"tration def index li"t render :a tion !. Qli"tQ end def li"t @u"erApage"J @u"er" ! paginate :u"er"J :perApage !. 5J :order !. QnameQ end def edit if param"):id+ @u"er ! O"er.find%param"):id+( el"e @u"er ! O"er.new end if re@ue"t.po"tU @u"er.attribute" ! param"):u"er+ "endAemail ! @u"er.newAre ordU if @u"er."ave if "endAemail 2egi"tration8otifier.deliverAregi"trationAnotifi ation%@u"er( end fla",):noti e+ ! Q<,e u"er wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def delete O"er.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e u"er wa" "u
e""fully deletedQ
152
Ap<s salvar um usurio novo6 temos o se#uinte resulta o em nossa cai0a e e;mailA
+bviamente6 s< queremos enviar um e;mail para novos usurios. Por isso6 no c< i#o6 antes re#istro6 veri"icamos se o mesmo J novo. -e J6 epois
e salvarmos o
no6 o e;mail J i#nora o. Bote que voc@ no precisa instanciar qualquer classe. )oc@ precisa apenas a icionar deli!er0 ao mJto o que voc@ #erou e o e;mail ser cria o o envia o automaticamente. Como po emos ver6 a maneira e enviar e;mail no Rails J bem simples.
DEPURANDO APLICAES
.ma coisa que o Rails permite e que no vimos atJ o momento J a problemas. Para comearmos6 usaremos um novo coman o o RailsA epurao e aplica&es atravJs e seu e console. /ssa J uma caracterIstica muito simples6 mas e0tremamente Ptil na i enti"icao rpi a
11$
ronaldo@minerva:~/tmp/gtd$ " ript/brea#pointer 8o onne tion to brea#point "ervi e at druby://lo al,o"t:$&531 %G2b::G2b4onn1rror( <rie" to onne t will be made every & "e ond"...
epura or remoto
coman o inicia o6 po emos intro u>ir um brea8point em nossa aplicao. )amos colocar um em um lu#ar qualquer apenas para emonstraoA
la"" O"er"4ontroller K Lppli ation4ontroller beforeAfilter :aut,enti ateAadmini"tration def index li"t render :a tion !. Qli"tQ end def li"t @u"erApage"J @u"er" ! paginate :u"er"J :perApage !. 5J :order !. QnameQ brea#point end def edit if param"):id+ @u"er ! O"er.find%param"):id+( el"e @u"er ! O"er.new end if re@ue"t.po"tU @u"er.attribute" ! param"):u"er+ "endAemail ! @u"er.newAre ordU if @u"er."ave if "endAemail 2egi"tration8otifier.deliverAregi"trationAnotifi ation%@u"erJ "e""ionAu"er( end fla",):noti e+ ! Q<,e u"er wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def delete O"er.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e u"er wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
ronaldo@minerva:~/tmp/gtd$ " ript/brea#pointer 8o onne tion to brea#point "ervi e at druby://lo al,o"t:$&531 %G2b::G2b4onn1rror( <rie" to onne t will be made every & "e ond"... 1xe uting brea# point at ./" ript/../ onfig/../app/ ontroller"/u"er"A ontroller.rb:1& in \li"tH irb%?KO"er"4ontroller:'xbRR35e$ .(:''1:'.
111
irb%?KO"er"4ontroller:'xbRR35e$ .(:''1:'. @u"er" !. )?KO"er:'xbRR'31a$ @attribute"!SQnameQ!.QLle""andraQJ QadminQ!.Q'QJ QidQ!.Q1&QJ Qpa""wordQ!.Qte"tQJ QloginQ!.Qale""andraQJ QemailQ!.Qale""andra@refle tive"urfa e. omQT.J ?KO"er:'xbRR'3'8 @attribute"!SQnameQ!.QBar u"QJ QadminQ!.Q'QJ QidQ!.Q&QJ Qpa""wordQ!.Qte"tQJ QloginQ!.Qmar u"QJ QemailQ!.Qmar u"@refle tive"urfa e. omQT.J ?KO"er:'xbRR'&fR$ @attribute"!SQnameQ!.Q2onaldoQJ QadminQ!.Q1QJ QidQ!.Q1QJ Qpa""wordQ!.Qte"tQJ QloginQ!.QronaldoQJ QemailQ!.Qronaldo@refle tive"urfa e. omQT.+ irb%?KO"er"4ontroller:'xbRR35e$ .(:''&:'.
irb%?KO"er"4ontroller:'xbRR35e$ .(:'11:'. in"tan eAvariable" !. )Q@re"pon"eQJ Q@performedAredire tQJ Q@,eader"QJ Q@u"erApage"QJ Q@a tionAnameQJ Q@fla",QJ Q@templateQJ Q@ oo#ie"QJ Q@AAbpAfileQJ Q@urlQJ Q@performedArenderQJ Q@beforeAfilterA ,ainAabortedQJ Q@"e""ionAu"erQJ Q@a""ign"QJ Q@"e""ionQJ Q@param"QJ Q@re@ue"tAoriginQJ Q@variable"AaddedQJ Q@re@ue"tQJ Q@AAbpAlineQJ Q@u"er"Q+ irb%?KO"er"4ontroller:'xbRR35e$ .(:'1&:'. lo alAvariable" !. )QAQ+ irb%?KO"er"4ontroller:'xbRR35e$ .(:'13:'. re@ue"t.re@ue"tAuri !. Q/u"er"/li"tQ irb%?KO"er"4ontroller:'xbRR35e$ .(:'1$:'. !. Qu"er"Q ontrollerAname
:u o o que est
a linha
po e ser e0ecuta o tambJm. 7inalmente6 para sair6 use o coman o e0it6 que retorna o controle M aplicao. (Pltiplos brea8points po em ser usa os para para as seqRenciais. Besse caso6 e0it mover a e0ecuo para o pr<0imo brea8point. Como po emos ver6 a epurao e aplica&es J to simples em Rails quanto qualquer outra tare"a.
ASSOCIAES POLIMRFICAS
.m tipo e associao que no tivemos oportuni a e e e0plorar atJ o momento so associa&es e associao J mais uma as novi a es que "oram intro u>i as no Rails 1.1 e so polim<r"icas. /sse tipo
responsveis por uma "acili a e muito maior em certos tipos e aplica&es. )amos supor que6 em nossa aplicao6 tivJssemos a necessi a e e saber sobre ca a mo i"icao "eita a
um re#istro6 ou se=a6 au itar ca a operao e persist@ncia ao banco e a os. /ssa J uma necessi a e que eu = encontrei em al#umas aplica&es6 especialmente requisita a pelo usurio. Para arma>enar esses a os6 po erIamos criar um tabela que conteria o ata a operao6 o tipo e
operao6 o usurio que a reali>ou e o ob=eto sobre o qual a mesma "oi reali>a a. Bo caso aqui6 para emonstrar as nossas associa&es6 vamos i#norar um etalhe <bvioA a au itoria a remoo o ob=eto que6 pelo mJto o que vamos empre#ar6 no "uncionaria6 = que o ob=eto est sen o removi o o banco e a os e qualquer associao com o mesmo se tornaria invli a nesse instante. ?#noran o isso6 a primeira coisa precisamos "a>er J criar a tabela que conter nossos re#istros e criao e
11*
a os
ronaldo@minerva:~/tmp/gtd$ " ript/generate model audit exi"t" app/model"/ exi"t" te"t/unit/ exi"t" te"t/fixture"/ reate app/model"/audit.rb reate te"t/unit/auditAte"t.rb reate te"t/fixture"/audit".yml exi"t" db/migrate reate db/migrate/'11A reateAaudit".rb
la"" 4reateLudit" K L tive2e ord::Bigration def "elf.up reateAtable :audit" do MtM t. olumn :operationJ :"tring t. olumn :re ordedAatJ :datetime t. olumn :auditableAidJ :integer t. olumn :auditableAtypeJ :"tring t. olumn :u"erAidJ :"tring end end def "elf.down dropAtable :audit" end end
+ si#ni"ica o as uas colunas auditable0id e auditable0type se tornar claro em um instante. Com o mo elo em mos6 po emos e it;loA
/ssa linha J importante. /la in ica a inter"ace polim<r"ica que vamos a otar para nossa aplicao. + nome J arbitrrio mas6 uma ve> escolhi o6 ser usa o na in icao a associa&es. /scolhen o um mo elo qualquer6 po emos "a>er o se#uinte a#oraA
la"" 4ontext K L tive2e ord::/a"e attrAprote ted :u"erAid validate"Apre"en eAof :name validate"Auni@uene""Aof :name validate"Apre"en eAof :u"erAid
11'
A e"inio a associao a#ora possui um novo parLmetro que in ica a inter"ace polim<r"ica que e"inimos. / a#ora6 po emos usar a associao6 pelo consoleA
ronaldo@minerva:~/tmp/gtd$ " ript/ on"ole Doading development environment. .. ontext ! 4ontext.find%1( !. ?K4ontext:'xbR8''R*$ @attribute"!SQnameQ!.Q@;omeQJ QidQ!.Q1QJ Qu"erAidQ!.Q1QT. .. ontext.audit" !. )+ .. ontext.audit". reate%:operation !. Qin"ertQJ :re ordedAat !. Gate<ime.now( !. ?KLudit:'xbRReaR' @newAre ord!fal"eJ @error"!?KL tive2e ord::1rror":'xbRRe5 5 @ba"e!?KLudit:'xbRReaR' ....J @error"!ST.J @attribute"!SQre ordedAatQ!.?KGate<ime: 1'*'1&65$818&8*56R/$3&''''''''J-1/8J&&661*1.J QauditableAtypeQ!.Q4ontextQJ QidQ!.1J QauditableAidQ!.1J QoperationQ!.Qin"ertQT. .. ontext.audit")'+.auditableAid !. 1 .. ontext.audit")'+.auditableAtype !. Q4ontextQ
+ si#ni"ica o
o re#istro
relaciona o M associaoHno caso acima6 o conte0to com o i contJm o nome a classe relaciona aHno caso acima6 Conte0t. )amos testar com outro mo eloA
la"" =ro-e t K L tive2e ord::/a"e validate"Apre"en eAof :name validate"Auni@uene""Aof :name ,a"Amany :a tion" ,a"Amany : ontext"J :t,roug, !. :a tion"J :"ele t !. Qdi"tin t ,a"Amany :audit"J :a" !. :auditable end ontext".PQ
.. reloadW 2eloading... !. )LuditJ Lppli ation4ontrollerJ 4ontextJ =ro-e tJ L tionJ 2e"our e+ .. pro-e t ! =ro-e t.find%1(
114
!. ?K=ro-e t:'xbRR8*R&' @attribute"!SQnameQ!.Q/uild a ;ou"eQJ QidQ!.Q1QJ Qde" riptionQ!.QQJ Qu"erAidQ!.Q1QJ Qa tiveQ!.Q1QT. .. pro-e t.audit" !. )+ .. pro-e t.audit". reate%:operation !. Qin"ertQJ :re ordedAat !. Gate<ime.now( !. ?KLudit:'xbRRR*6*' @newAre ord!fal"eJ @error"!?KL tive2e ord::1rror":'xbRR*eb$8 @ba"e!?KLudit:'xbRRR*6*' ....J @error"!ST.J @attribute"!SQre ordedAatQ!.?KGate<ime: &1&'&56'66'R3$5*R/8*$'''''''J-1/8J&&661*1.J QauditableAtypeQ!.Q=ro-e tQJ QidQ!.&J QauditableAidQ!.1J QoperationQ!.Qin"ertQT. .. pro-e t.audit")'+.auditableAid !. 1 .. pro-e t.audit")'+.auditableAtype !. Q=ro-e tQ
Bote o uso e reloadI para recarre#ar os mo elos recJm;mu a os. / ve=a que o re#istro cria o para a nova classe que usamos mu a o tipo a associao automaticamente. Dualquer outra classe que usssemos "aria o mesmo. -elecionan o os a os no (,-DL6 vemos os a os emonstran o isso claramenteA
my"@l. "ele t P from audit"5 N----N-----------N---------------------N--------------N----------------N---------N M id M operation M re ordedAat M auditableAid M auditableAtype M u"erAid M N----N-----------N---------------------N--------------N----------------N---------N M 1 M in"ert M &''*-'6-&$ &3:13:56 M 1 M 4ontext M 8ODD M M & M in"ert M &''*-'6-&$ &3:1$:'$ M 1 M =ro-e t M 8ODD M N----N-----------N---------------------N--------------N----------------N---------N & row" in "et %'.'' "e (
Como po emos ver6 associa&es polim<r"icas tem esse nome porque elas so capa>es rela&es para ob=etos /ssas colunas so usa as pelo Rails para #erar
e representar
i"erentes por meio e colunas especiais que recebem seus valores automaticamente. eclara&es -DL especI"icas6 basea as na classe que est
"a>en o a chama a6 transparentemente para a aplicao. .ma ve> que temos essas associa&es6 po emos "a>er nossa au itoria vamos utili>ar m< ulos para incorporar essa "uncionali a e aos mo elos. e maneira automtica. Para isso6
MDULOS
Para usar m< ulos nesse caso6 vamos criar um arquivo especI"ico no auditing.rbA iret<rio extras6 chama o
module X<G module Luditing def "elf.appendAfeature"%ba"e( ba"e.afterA reate do Mob-e tM ob-e t.audit". reate%:operation !. Qin"ertQJ :re ordedAat !. Gate<ime.now(
115
end ba"e.afterAupdate do Mob-e tM ob-e t.audit". reate%:operation !. QupdateQJ :re ordedAat !. Gate<ime.now( end end end end
e"inir um m< ulo que ser incorpora o aos mo elos na aplicao. Para evitar
colis&es e nomes6 criamos o m< ulo entro e outro6 especI"ico para nossa aplicao. + mJto o append0features6 inerente a m< ulos6 J ro a o automaticamente pelo Rub, quan o um m< ulo J a iciona o a uma classe. Bo caso acima6 usamos o mJto o para a icionar ois "iltros Ms classes em questoA um ap<s a criao e um ap<s ca a operao um re#istro e au itoria aos e0istentes. Precisamos a#ora mu ar o arquivo en!iroment.rb/para requerer o arquivoA e salvamento. /sses mJto os simplesmente a icionam mais
? /e "ure to re"tart your web "erver w,en you modify t,i" file. ? On omment below to for e 2ail" into produ tion mode w,en ? you donHt ontrol web/app "erver and anHt "et it t,e proper way ? 18Z)H2L7D>A18ZH+ MM! Hprodu tionH ? >pe ifie" gem ver"ion of 2ail" to u"e w,en vendor/rail" i" not pre"ent 2L7D>AX1BAZ12>7:8 ! H1.1.*H ? /oot"trap t,e 2ail" environmentJ framewor#"J and default re@uire 9ile.-oin%9ile.dirname%AA97D1AA(J HbootH( onfiguration
2ail"::7nitializer.run do M onfigM ? >etting" in onfig/environment"/P ta#e pre eden e t,o"e "pe ified ,ere ? >#ip framewor#" youHre not going to u"e %only wor#" if u"ing vendor/rail"( ? onfig.framewor#" -! ) :a tionAwebA"ervi eJ :a tionAmailer + ? Ldd additional load pat," for your own u"tom dir" onfig.loadApat," N! Y0% ?S2L7D>A2::<T/extra" ( ? 9or e all environment" to u"e t,e "ame logger level ? %by default produ tion u"e" :infoJ t,e ot,er" :debug( ? onfig.logAlevel ! :debug ? O"e t,e databa"e for "e""ion" in"tead of t,e file "y"tem ? % reate t,e "e""ion table wit, Hra#e db:"e""ion": reateH( ? onfig.a tionA ontroller."e""ionA"tore ! :a tiveAre ordA"tore ? O"e >CD in"tead of L tive 2e ordH" " ,ema dumper w,en reating t,e te"t databa"e. ? <,i" i" ne e""ary if your " ,ema anHt be ompletely dumped by t,e " ,ema dumperJ ? li#e if you ,ave on"traint" or databa"e-"pe ifi olumn type" ? onfig.a tiveAre ord." ,emaAformat ! :"@l ? L tivate ob"erver" t,at ",ould alway" be running ? onfig.a tiveAre ord.ob"erver" ! : a ,erJ :garbageA olle tor ? Ba#e L tive 2e ord u"e O<4-ba"e in"tead of lo al time
111
? >ee 2ail"::4onfiguration for more option" end ? ? ? ? ? ? ? ? Ldd new infle tion rule" u"ing t,e following format %all t,e"e example" are a tive by default(: 7nfle tor.infle tion" do Minfle tM infle t.plural /^%ox($/iJ HE1enH infle t."ingular /^%ox(en/iJ HE1H infle t.irregular Hper"onHJ HpeopleH infle t.un ountable Yw% fi", ",eep ( end onfiguration below
L tionBailer::/a"e."erverA"etting" ! S :addre"" !. Qmail.yourdomain. omQJ :domain !. Qyourdomain. omQJ :u"erAname !. Qu"er@yourdomain. omQJ :pa""word !. QPPPPPPQJ :aut,enti ation !. :login T
la"" 4ontext K L tive2e ord::/a"e in lude X<G::Luditing attrAprote ted :u"erAid validate"Apre"en eAof :name validate"Auni@uene""Aof :name validate"Apre"en eAof :u"erAid ,a"Amany :a tion" ,a"Amany :audit"J :a" !. :auditable end
A#ora6 para qualquer operao "eita nesse mo elo e a os6 uma trilha e au itoria ser #rava a. Po emos comprovar isso6 salvan o um re#istro e0istente e crian o um novo6 o que resulta6 em nosso banco6 nos se#uintes re#istrosA
my"@l. "ele t P from audit"5 N----N-----------N---------------------N--------------N----------------N---------N M id M operation M re ordedAat M auditableAid M auditableAtype M u"erAid M N----N-----------N---------------------N--------------N----------------N---------N M 1 M in"ert M &''*-'6-&$ &3:13:56 M 1 M 4ontext M 8ODD M M & M in"ert M &''*-'6-&$ &3:1$:'$ M 1 M =ro-e t M 8ODD M M 3 M update M &''*-'6-&$ &3:16:'3 M 1 M 4ontext M 8ODD M M $ M in"ert M &''*-'6-&$ &3:&$:58 M 1& M 4ontext M 8ODD M N----N-----------N---------------------N--------------N----------------N---------N $ row" in "et %'.'' "e (
.ma mo i"icao que po emos "a>er em nosso m< ulo J permitir automaticamente a criao
11!
e uma
associaoA
module X<G module Luditing def "elf.appendAfeature"%ba"e( ba"e.afterA reate do Mob-e tM ob-e t.audit". reate%:operation !. Qin"ertQJ :re ordedAat !. Gate<ime.now( end ba"e.afterAupdate do Mob-e tM ob-e t.audit". reate%:operation !. QupdateQJ :re ordedAat !. Gate<ime.now( end ba"e.,a"Amany :audit"J :a" !. :auditable end end end
?sso permite que eliminemos a mesma chama a e ca a classe em que usarmos o m< ulo. (o i"icar o resto as classes para usar o m< ulo J um e0ercIcio para o leitor. +bviamente6 no c< i#o acima6 "alta uma coisaA re#istrar o usurio responsvel pelas chama as. A princIpio6 po erIamos pensar em simplesmente usar o atributo user0id presente em vrias classe. (as isso po e no ser vli o para uma situaoA o que acontece com classes que no tem esse atributo` Bo caso aplicao6 s< temos uma6 que J o mo elo e a os e nossa o pr<prio usurio e6 nesse caso no po emos utili>ar o
os re#istros a mesma porque no a ver com usurio que est "a>en o a mo i"icao. Bo po emos usar e sessoHque seria talve> o mJto o mais <bivoHpois sess&es no esto acessIveis a
A soluo J usar um estratJ#ia especial. /0iste um local que seria acessIvel a to os os mo elosA uma varivel e classe que to as outras classes po em acessar. Para isto6 vamos mo i"icar o mo elo e a os e usurios6 que J o local mais propIcio para issoA
la"" O"er K L tive2e ord::/a"e attrAa e""or : urrentAu"erAid eAof eAof eAof eAof :name :login :pa""word :email
atribuir um valor M mesma antes e qualquer uso as classes. Po emos "a>er isso em nosso controller rai>A
la"" Lppli ation4ontroller K L tion4ontroller::/a"e beforeAfilter :aut,enti ate beforeAfilter :"etA urrentAu"erAid "#ipAbeforeAfilter :aut,enti ateAadmini"trationJ :only !. ):a aroundAfilter aroundAfilter aroundAfilter aroundAfilter O"er9ilter.new%4ontextJ :neededA" ope"( O"er9ilter.new%=ro-e tJ :neededA" ope"( O"er9ilter.new%L tionJ :neededA" ope"( O"er9ilter.new%2e"our eJ :neededA" ope"( e""Adenied+
,elperAmet,od :"e""ionAu"er def a e""Adenied render :template !. Q",ared/a end prote ted def "etA urrentAu"erAid O"er. urrentAu"erAid ! "e""ion):u"er+ end def "e""ionAu"er @"e""ionAu"er MM! O"er.find%:fir"tJ : ondition" !. )Hid ! UHJ "e""ion):u"er++( end def aut,enti ate unle"" "e""ion):u"er+ "e""ion):returnAto+ ! re@ue"t.re@ue"tAuri redire tAto : ontroller !. QloginQJ :a tion !. QloginQ return fal"e end return true end def aut,enti ateAadmini"tration unle"" "e""ionAu"er [[ "e""ionAu"er.adminU redire tAto :a tion !. Qa e""AdeniedQ return fal"e end return true end def neededA" ope"%targetA la""( S :find !. S : ondition" !. )Q?StargetA la"".tableAnameT.u"erAid ! UQJ "e""ion):u"er++ TJ : reate !. S :u"erAid !. "e""ion):u"er+ T T end end e""AdeniedQ
Com isso "eito6 po emos "a>er a mo i"icao em nosso m< ulo e au itoriaA
module X<G module Luditing def "elf.appendAfeature"%ba"e( ba"e.afterA reate do Mob-e tM ob-e t.audit". reate%:operation !. Qin"ertQJ
112
:re ordedAat !. Gate<ime.nowJ :u"erAid !. O"er. urrentAu"erAid( end ba"e.afterAupdate do Mob-e tM ob-e t.audit". reate%:operation !. QupdateQJ :re ordedAat !. Gate<ime.nowJ :u"erAid !. O"er. urrentAu"erAid( end ba"e.,a"Amany :audit"J :a" !. :auditable end end end
Recarre#an o o nosso servi or e e0ecutan o al#uma operao em nossas classes au ita as6 teremos o se#uinte em nosso banco e a osA
my"@l. "ele t P from audit"5 N----N-----------N---------------------N--------------N----------------N---------N M id M operation M re ordedAat M auditableAid M auditableAtype M u"erAid M N----N-----------N---------------------N--------------N----------------N---------N M 1 M in"ert M &''*-'6-&$ &3:13:56 M 1 M 4ontext M 8ODD M M & M in"ert M &''*-'6-&$ &3:1$:'$ M 1 M =ro-e t M 8ODD M M 3 M update M &''*-'6-&$ &3:16:'3 M 1 M 4ontext M 8ODD M M $ M in"ert M &''*-'6-&$ &3:&$:58 M 1& M 4ontext M 8ODD M M 5 M update M &''*-'6-&$ &3:&*:35 M 1& M 4ontext M 8ODD M M * M update M &''*-'6-&$ &3:$':$& M 1& M 4ontext M 1 M M R M in"ert M &''*-'6-&$ &3:$':5* M 13 M 4ontext M 1 M N----N-----------N---------------------N--------------N----------------N---------N R row" in "et %'.'' "e (
/ssa J outra tJcnica que po emos usar em nossos m< ulos para acessar ob=etos ou valores que precisamos. Deve ser usa a com cui a o6 pois po e #erar c< i#o espa#uete6 mas J uma maneira "cil e interessante trans"erir a os e acor o com a necessi a e. e
AJAX
.ma as #ran es ten @ncias atuais no esenvolvimento e aplica&es Eeb J o que se tornou conheci o e apro0imar as aplica&es Eeb e suas e como Eeb *.$6 que J6 na a mais contrapartes o que uma tentantiva
inte#rao e usabili a e muito superiores Ms aplica&es que vinham sen o comumente esto se trans"orman o em uma re"er@ncia para o que eve ou no eve ser "eito na ?nternet. .ma as #ran es marcas esse mo elo e
esenvolvi as e
e e
recuperar
a os no servi or6 basea as em Qava-cript6 para re u>ir a necessi a e os "atores ominantes na e"inio
p#inas to tra icional em aplica&es Eeb. /6 embora essa no se=a a principal caracterIstica aplicao Eeb *.$6 isso J o que se tornou um ?nternet.
e um
1!$
Dentro
essa
e"inio6 a populari>ao
e "erramentas basea as na tJcnica conheci a como A=a0H a qual os "atores pre ominantes na
aceitao essas novas aplica&es. (arca6 mais o que "uncionali a e6 pre ominou inicialmente. Para aqueles que nunca ouviram "alar em A=a06 um ori#inalmente seria uma abreviao as e"ini&es mais comuns J a o pr<prio nome6 que
e As,nchronous Java-cript an
um uso avana o o que era conheci o como D4:(L6 o aumento <bvio o uso a tJcnica se eve ao "ato e que os nave#a ores mais usa os no merca o estabili>aram suas inter"aces Qava-cript6 4:(L6 9(L e C-- ao ponto e que esenvolver uma aplicao usan o tJcnicas avana as no J mais uma questo e esenvolver vers&es i"erentes a mesma coisa. .m os #ran es motivos a populari>ao o Rails nos Pltimos tempos tem si o o suporte o mesmo a
al#umas
essas bibliotecas que6 embora no se=a as mais avana as e completas6 esto to inte#ra as no
"rameGor8 que usar as mesmas se tornou al#o assumi o e "cil o su"iciente para bene"iciar mesmo o
esenvolve or iniciante. /m nosso tutorial vamos a#ora e0plorar al#umas uma apar@ncia e melhor tempo e resposta. +bviamente6 como qualquer outra tJcnica6 o A=a0 tem suas vanta#ens e i"icul a es6 por e0emplo6 J manter a acessibili a e com limita&es mostrar como resolver essas esvanta#ens. .ma as principais essas inte#ra&es mostran o como o A=a0 po e ser an o;lhes no s< uma usabili a e melhor como
i"icul a es. Procuramos aqui apenas mostrar rapi amente como a tJcnica isponIvel na ?nternet trabalhan o esses problemas e
po e ser empre#a a. Recomen amos que o leitor que opte por empre#ar qualquer tJcnica relaciona a em sua aplicao procure material que est plenamente apontan o possIveis solu&es. .m os outros #ran es problemas o A=a0 est no uploa e arquivos6 = que as requisi&es so "eitas e se#urana6 impe em
#eralmente via Qava-cript e limita&es que uploa s se=am processa os mais recentes
as cenas. /0istem solu&es parciais para esses o c< i#o o"icial. /m vers&es
problemas e atJ pouco tempo atrs o Rails suportava uma o Rails6 o suporte "oi retira o
emonstrao clara os problemas ain a e0istentes com a tJcnica. Problemas M parte6 a tJcnica J e0tremamente Ptil e suas aplica&es po ero #anhar muito usan o mesmo as implementa&es mais bsicas a mesma. Como menciona o acima6 essa J a nossa proposta aquiA mostrar o que po e ser "eito e permitir que o leitor evolua suas pr<prias solu&es6 apren en o a li ar com os problemas no uso prtico a tJcnica. Depois e to os esses comentrios e ressalvas6 po emos partir para nossa implementao. Para isto6 vamos
1!1
aproveitar a nossa p#ina home que atJ o momento estava convenientemente va>ia. Ba meto olo#ia %:D6 a coisa mais importante J a pr<0ima ao6 ou se=a6 quais a&es voc@ ain a precisa completar. 7a> senti o que a nossa p#ina home se=a um mo o e visuali>ar e trabalhar rapi amente com essas a&es. Para comear6 ento6 vamos simplesmente e0ibir nossas a&es na p#ina principal6 separa as pelo conte0to. )oltan o ao nosso controller6 terIamos o se#uinteA
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.find%:allJ : ondition" !. Qdone ! 'QJ :in lude !. ):u"erJ :pro-e tJ : ontext+J :order !. Qa tion".de" riptionQ( end end
Recuperamos as a&es ain a no completa as. Bote que o c< i#o acima assume que voc@ alterou o mo elo e a&es con"orme mostra o abai0o6 crian o uma associao entre usurio e aoA
la"" L tion K L tive2e ord::/a"e attrAprote ted : reatedAatJ : ompletedAat def done!%value( if L tive2e ord::4onne tionLdapter"::4olumn.valueAtoAboolean%value( "elf. ompletedAat ! Gate<ime.now el"e "elf. ompletedAat ! nil end writeAattribute%QdoneQJ value( end belong"Ato : ontext belong"Ato :pro-e t belong"Ato :u"er validate"Apre"en eAof :de" ription validate"Apre"en eAof : ontextAid validate"Apre"en eAof :pro-e tAid ,a"AandAbelong"AtoAmany :re"our e"J :order !. QnameQ end
A#ora que temos nossas a&es6 po emos criar a nossa vieG separan o as a&es por conte0toA
K,1.8ext L tion"K/,1. KY @a tion".groupAby%[: ontext(.ea , do M ontextJ a tion"M Y. Kdiv la""!QgroupQ. K,&.KY! Kul. ontext.name Y.K/,&.
1!*
KY a tion".ea , do Ma tionM Y. Kli.KY! a tion.de" ription Y.K/li. KY end Y. K/ul. K/div. KY end Y.
Ba vieG acima estamos usan o uma tJcnica interessante para separa os as a&es por conte0toA o mJto o group0by. /sse mJto o a#rupa um enumera o qualquer Scomo nossas a&es acimaT em con=untos cu=o critJrio e separao J a mensa#em passa a como parLmetro. /stu ar o mJto o group0by6 no c< i#o implementao inseri o tipo epen e o Rails6 J um bom e0ercIcio para enten er o po er o Rub,. A
a tJcnica usa a ser su"iciente para avanar ra>oavelmente seu conhecimento a lin#ua#em. + mJto o "oi iretamente no m< ulo que controla enumera&es no Rub,6 passan o a "uncionar para qualquer e"ini a em c< i#o. + parLmetro passa o J tambJm uma a io o o Rub,. /m uma lin#ua#em esttica isso simplesmente no seria e enumerao que venha a ser
possIvel ou seria muito i"Icil e implementar e maneira to simples. )oltan o ao c< i#o acima6 estamos separan o as nossas a&es pelo seu conte0to. + mJto o group0by/ retorna uma tabela hash separa a por conte0to que po e ser usa a em um loop6 como "a>emos acima. /ssa tabela hash possui como chaves o resulta o a mensa#em que passamos como parLmetro Sno caso acima6 o conte0toT e como valor um arra, os ob=etos que "iltramos Sno caso acima6 as a&esT. + resulta o inicial JA
1!'
os
a os
e maneira bem
clara e simpli"ica a. /ssa J uma as vanta#ens e usar uma lin#ua#em inLmica como o Rub,. A#ora6 a icionan o o "ra#mento apresentaoA e C-- abai0o ao nosso arquivo default.css6 melhoramos a nossa
div.group ,& S padding: 5px5 margin: 'px5 border-bottom: 1px "olid ? ba #ground- olor: ?ddd5 font-"ize: 11'Y5 T div.group ul S li"t-"tyle: none5 margin: '5 padding: '5 T div.group ul li S
1!4
+ resulta o J o se#uinteA
:emos a#ora uma apresentao bsica para trabalharmos. A primeira coisa que queremos J uma maneira e marcar a&es como completa as. -eria interessante para isso e0ibir pelo menos al#umas completa as para termos uma re"er@ncia. Po emos "a>er isso6 mo i"ican o o nosso controllerA as a&es =
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.find%:allJ : ondition" !. Qdone ! 'QJ :in lude !. ):u"erJ :pro-e tJ : ontext+J :order !. Qa tion".de" riptionQ( @ ompletedAa tion" ! L tion.find%:allJ : ondition" !. Qdone ! 1QJ :in lude !. ):u"erJ :pro-e tJ : ontext+J :order !. Qa tion".de" riptionQ(J :limit !. 3 end end
1!5
Po emos tambJm melhorar o nosso c< i#o acima re"atoran o as chama as em um mJto o mais "le0Ivel6 iretamente no mo elo e a os. .m e0emplo isse seriaA
la"" L tion K L tive2e ord::/a"e attrAprote ted : reatedAatJ : ompletedAat def done!%value( if L tive2e ord::4onne tionLdapter"::4olumn.valueAtoAboolean%value( "elf. ompletedAat ! Gate<ime.now el"e "elf. ompletedAat ! nil end writeAattribute%QdoneQJ value( end belong"Ato : ontext belong"Ato :pro-e t belong"Ato :u"er validate"Apre"en eAof :de" ription validate"Apre"en eAof : ontextAid validate"Apre"en eAof :pro-e tAid ,a"AandAbelong"AtoAmany :re"our e"J :order !. QnameQ def "elf.findAbyA"tatu"%doneJ limit ! nil( find :allJ : ondition" !. )Qdone ! UQJ done U 1 : '+J :in lude !. ):u"erJ :pro-e tJ : ontext+J :order !. Qa tion".de" riptionQJ :limit !. limit end end
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end end
K,1.8ext L tion"K/,1. KY @a tion".groupAby%[: ontext(.ea , do M ontextJ a tion"M Y. Kdiv la""!QgroupQ. K,&.KY! ontext.name Y.K/,&.
1!1
K,&.4ompleted L tion"K/,&. Kul. KY @ ompletedAa tion".ea , do Ma tionM Y. Kli.KY! a tion.de" ription Y.K/li. KY end Y. K/ul. K/div.
A#ora6 po emos intro u>ir al#uma "orma e marcar se a ao est completa a ou noA
1!!
K,1.8ext L tion"K/,1. KY @a tion".groupAby%[: ontext(.ea , do M ontextJ a tion"M Y. Kdiv la""!QgroupQ id!Q ontextAKY! ontext.id Y.Q. K,&.KY! ontext.name Y.K/,&.
Kul. KY a tion".ea , do Ma tionM Y. Kli id!Q ontextAa tionAKY! a tion.id Y.Q. KY! ,e #AboxAtag Q ompleted)?Sa tion.idT+QJ a tion.idJ fal"eJ :on li # !. Q?SremoteAfun tion%:url !. S :a tion !. Qmar#QJ :id !. a tion T(TQ Y. KY! a tion.de" ription Y. K/li. KY end Y. K/ul. K/div. KY end Y. Kdiv la""!QgroupQ.
K,&.4ompleted L tion"K/,&. Kul id!Q ompletedAa tion"Q. KY @ ompletedAa tion".ea , do Ma tionM Y. Kli.KY! a tion.de" ription Y.K/li. KY end Y. K/ul. K/div.
+ si#ni"ica o e to os esses i s con"eri os a elementos "icar aparente em breve. Por hora6 o importante J a chama a remota que vamos "a>er. Repare o "ra#mento e c< i#o que intro u>imosA
KY! ,e #AboxAtag Q ompleted)?Sa tion.idT+QJ a tion.idJ fal"eJ :on li # !. Q?SremoteAfun tion%:url !. Qmar#QJ :id !. a tion(TQ
Y.
/sse "ra#mento eclara um elemento e "ormulrio o tipo chec8bo06 chama a Protot,pe. Antes
e"ini o para uma ao em Qava-cript. Aqui entra o A=a0 inte#ra o ao Rails atravJs biblioteca M nossa aplicao. Por pa ro6 no momento em que o esqueleto biblioteca Sentre outrasT "oi coloca a no iret<rio.
e po ermos "a>er e0ecutar o c< i#o acima6 precisamos incorporar essa a aplicao "oi #era o6 essas iret<rio public/%a!ascripts6 como voc@ po e ver visuali>an o o
+ que precisamos J somente invoc;las6 o que conse#uimos com uma pequena mu ana em nosso la,out baseA
K,tml. K,ead. Ktitle.X<GK/title. KY! "tyle",eetAlin#Atag QdefaultQ Y. KY! "tyle",eetAlin#Atag Q" affoldQ Y. KY! -ava" riptAin ludeAtag :default" Y. K/,ead.
1!5
Kbody. K,1 id!Q,eaderQ.X<GK/,1. KY if "e""ionAu"er Y. Kp id!Qlogin-informationQ. Fou are logged in a" K"trong.KY! "e""ionAu"er.name Y.K/"trong.. KY! lin#Ato QDog outQJ : ontroller !. QloginQJ :a tion !. QlogoutQ Y.. K/p. KY end Y. Kul id!QmenuQ. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 4ontext"QJ : ontroller !. Q ontext"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 =ro-e t"QJ : ontroller !. Qpro-e t"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 L tion"QJ : ontroller !. Qa tion"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 2e"our e"QJ : ontroller !. Qre"our e"QJ :a tion !. Qli"tQ Y.K/li. KY if "e""ionAu"er [[ "e""ionAu"er.adminU Y. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 O"er"QJ : ontroller !. Qu"er"QJ :a tion !. Qli"tQ Y.K/li. KY end Y. K/ul. Kdiv id!Q ontent"Q.KY! yield Y.K/div. K/body. K/,tml.
+ resulta o essa chama a6 que po e ser visto visuali>an o;se a c< i#o "onte a p#ina #era a6 J a icionar uma sJrie e bibliotecas Qava-cript M p#ina. -omente "aa a invocao acima se realmente precisar = que elas representam quase *$$F3 e Qava-cript que teriam que ser bai0a os e interpreta os pelo nave#a or. +bviamente6 se o seu servi or estiver con"i#ura o apropria amente6 os arquivos somente ser bai0a os uma Pnica ve>6 sem causar mais o que uma emora na car#a inicial a p#ina. (esmo assim6 J interessante ter um certo cui a o em usar as bibliotecasHe quaisquer outras que "orem necessriasHpara no #erar p#inas que emoram emais a ser carre#a as e cu=a usabili a e J potenciamente re u>i a. A veloci a e e interpretao e Qava-cript varia Assim6 se "or possIvel6 ao invJs e nave#a or para nave#a or e po e ser realmente lenta e uma ve>6 melhor. e acor o com a
em al#uns6 mesmo entre os mais usa os. Por isso6 quanto menos Qava-cript carre#a o necessi a e e no as carre#ue em p#inas on e no "orem necessrias. A nossa p#ina a#ora est assimA
1!2
-e voc@ clicar em um
lo# a aplicao ver que uma ao est sen o chama a por trs as cenasHe obviamente "alhan o porque
no a e"inimos ain a. )oc@ po e tambJm visuali>ar o c< i#o a p#ina para ver o c< i#o que a chama a ao mJto o remote0function #erou. + que essa "uno "a>6 basicamente6 J invocar uma ao que passamos como parLmetro. /la no retorna valor a menos que implementemos a aoA
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( end
15$
end
as a&es e recarre#ar a
p#ina6 ver que ela espareceu o seu conte0to e a#ora est na lista e a&es completa as. + ob=etivo a#ora J "a>er a transio a ao visualmente e uma lista para a outra. )amos comear comear "a>en o a ao esaparecer. Para isto6 vamos usar pela primeira ve> um novi a e no Rails 1.1 que so os SRails Qava-criptT6
templates
RQ-
vieGs
escreven o
coman os
Qava-cript.
/ssas
vieGs6
no
surpreen entemente6 "uncionam e maneira similar Ms que escrevem elementos 9(L. Para usar uma vieG conten o um template RQ-6 vamos criar o arquivo mar3.r%s/no iret<rio app/!iews/homeA a vieG a ao mar86 chama o
Como voc@ po e ver6 o template J bem pareci o com o que usamos anteriormente para #erar 9(L. A i"erena J que esse #era coman os Qava-cript que so evolvi os para a aplicao e interpreta os assim que so retorna os. Bo caso acima6 um coman o J #era o para remover o elemento especi"ica o pela i passa o ao mJto o remo!e o ob=eto page6 que J automaticamente e"ini o para esse tipo e templates. -e voc@ e0ecutar a ao6 ver que o elemento a#ora aparecer em nossa lista somente a lista simplesmente criar um novo elemento na lista com a realmente JHmas tem a vanta#em esaparece escrio o seu conte0to. Precisamos "a>@;lo a#ora e "a>er issoA primeiro6 po emos a aoK e6 se#un o6 po emos ren eri>ar
uas maneiras
e a&es completa as novamente. + se#un o mJto o po e parecer mais custosoHe e permitir6 entre outras coisas6 mantermos a nossa apresentao
consistente6 como veremos lo#o abai0o. )amos i>er que6 eventualmente6 queiramos e0ibir mais o que somente a escrio a ao em nossas
listas. -e #eramos simplesmente as nossas listas em ca a lu#ar que precisamos6 veremos que to a ve> que "i>ermos uma mu ana em nossa apresentao6 teremos que mo i"icar to os locais que e0ibem a lista. + que po emos "a>er J usar partials para resolver esse problema. )amos mu ar a nossa vieG principal paraA
K,1.8ext L tion"K/,1. KY @a tion".groupAby%[: ontext(.ea , do M ontextJ a tion"M Y. Kdiv la""!QgroupQ id!Q ontextAKY! ontext.id Y.Q. K,&.KY! ontext.name Y.K/,&.
Kul. KY a tion".ea , do Ma tionM Y. Kli id!Q ontextAa tionAKY! a tion.id Y.Q. KY! ,e #AboxAtag Q ompleted)?Sa tion.idT+QJ a tion.idJ fal"eJ :on li # !. Q?SremoteAfun tion%:url !. S :a tion !. Qmar#QJ :id !. a tion T(TQ Y. KY! a tion.de" ription Y.
151
K,&.4ompleted L tion"K/,&. KY! render :partial !. Q ompletedAa tion"QJ :ob-e t !. @ ompletedAa tion" Y. K/div.
Kul id!Q ompletedAa tion"Q. KY @ ompletedAa tion".ea , do Ma tionM Y. Kli.KY! a tion.de" ription Y.K/li. KY end Y. K/ul.
+ resulta o visual J o mesmo6 mas h uma #ran e vanta#em. Po emos a#ora atuali>ar a nossa lista inLmicamente. Primeiro6 mu amos a ao mar8 em nosso controllerA
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end end
page.remove Q ontextAa tionA?S@a tion.idTQ page.repla e Q ompletedAa tion"QJ :partial !. Q ompletedAa tion"QJ :ob-e t !. @ ompletedAa tion"
A se#un a linha acima ren eri>a o partial que criamos S= po emos ver aqui os bene"Icios a ren eri>ao. + resulta o6 ap<s clicar em nossa primeira ao mostra a na tela anterior JA
essa separao
ao po ermos reusar o c< i#oT e substitui o elemento 4:(L in"orma o no primeiro parLmetro pelo resulta o
15*
entro
o mo elo normal
Rails6 em nenhum momento recarre#a a p#ina. :emos uma situao bem mais prtica para o usurio. Pela aplicao a mesma tJcnica po erIamos criar uma ao reversa unmar8 e remover um elemento a lista e completa as6 retornan o;o ao seu conte0to. .m probleminha que temos em nosso c< i#o J o que acontece quan o remover a Pltima ao conte0to6 ei0an o somente o tItulo o conte0to visIvel na p#ina. + resulta o J visualmente esa#ra vel como po emos ver abai0oA e um
15'
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end end
154
page.repla e Q ompletedAa tion"QJ :partial !. Q ompletedAa tion"QJ :ob-e t !. @ ompletedAa tion" if @removeA ontext page.remove Q ontextA?S@a tion. ontext.idTQ end
/stamos utili>an o mJto os novos o ActiveRecor em nossos e0emplos. Caso voc@ no tenha "amiliari a e com os mesmos Se0emplos acima so update0attribute e countT6 consulte a in"orma&es mais etalha as. A mu ana resolve nosso problema como voc@ po er ver testan o mais uma ve> a remoo a Pltima ao e um conte0to. .ma outra coisa que J muito utili>a a em aplica&es A=a0 so in ica&es visuais acontecen o. A primeira "orma e "a>er isso J atravJs e uma in icao e que uma ao est ocumentao para
terminouK a se#un a J in icar o resulta o a ao remota. )amos e0perimentar com ambas. Para a primeira precisamos mo i"ique o seu la,out bsicoA e uma ima#em e in icao. /scolha uma ima#em anima a qualquer e
K,tml. K,ead. Ktitle.X<GK/title. KY! "tyle",eetAlin#Atag QdefaultQ Y. KY! "tyle",eetAlin#Atag Q" affoldQ Y. KY! -ava" riptAin ludeAtag :default" Y. K/,ead. Kbody. K,1 id!Q,eaderQ.X<GK/,1. KY if "e""ionAu"er Y. Kp id!Qlogin-informationQ. Fou are logged in a" K"trong.KY! "e""ionAu"er.name Y.K/"trong.. KY! lin#Ato QDog outQJ : ontroller !. QloginQJ :a tion !. QlogoutQ Y.. K/p. KY end Y. KY! imageAtag%Qindi ator.gifQJ :id !. Qindi atorQJ :"tyle !. Qfloat: rig,t5 margin: 5px5 di"play: noneQ( Y. Kul id!QmenuQ. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 4ontext"QJ : ontroller !. Q ontext"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 =ro-e t"QJ : ontroller !. Qpro-e t"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 L tion"QJ : ontroller !. Qa tion"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 2e"our e"QJ : ontroller !. Qre"our e"QJ :a tion !. Qli"tQ Y.K/li. KY if "e""ionAu"er [[ "e""ionAu"er.adminU Y. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 O"er"QJ : ontroller !. Qu"er"QJ :a tion !. Qli"tQ Y.K/li. KY end Y. K/ul. Kdiv id!Q ontent"Q.KY! yield Y.K/div.
155
K/body. K/,tml.
/stamos usan o uma ima#em chama a/ indicator.gif4 que est no ima#em e a escon emos temporariamente.
locali>ao pa ro #era a pelo mJto o image0tag. .san o um pouco e estilo C--6 a=ustamos a posio a
K,1.8ext L tion"K/,1. KY @a tion".groupAby%[: ontext(.ea , do M ontextJ a tion"M Y. Kdiv la""!QgroupQ id!Q ontextAKY! ontext.id Y.Q. K,&.KY! ontext.name Y.K/,&.
Kul. KY a tion".ea , do Ma tionM Y. Kli id!Q ontextAa tionAKY! a tion.id Y.Q. KY! ,e #AboxAtag Q ompleted)?Sa tion.idT+QJ a tion.idJ fal"eJ :on li # !. Q?SremoteAfun tion%:url !. S :a tion !. Qmar#QJ :id !. a tion TJ :before !. Y%1lement.",ow%Qindi atorQ((J : omplete !. Y%1lement.,ide%Qindi atorQ(((TQ Y. KY! a tion.de" ription Y. K/li. KY end Y. K/ul. K/div. KY end Y. Kdiv la""!QgroupQ.
K,&.4ompleted L tion"K/,&. KY! render :partial !. Q ompletedAa tion"QJ :ob-e t !. @ ompletedAa tion" Y. K/div.
+ mJto o que #era a chama a remota aceita parLmetros a icionais que nos permitem manipular a chama a. Bo caso aqui6 estamos utili>an o e0ecuta a e0atamente antes ois elesA be"ore6 que recebe uma "uno Qava-cript que J a chama a remotaK e complete6 que recebe outra "uno Qava-cript que J uplas ou simples. Ba
e0ecuta a epois que a requisio termina6 tenha ela suce i o ou no. /stamos usan o uma "orma especial e strin#s no Rails6 usan o +JK4 para evitar ter que "icar escapan o aspas6 se=am "un&es6 usamos chama as e classe Qava-cript e0ibir e escon er o in ica or e ao. -e voc@ e0ecutar a chama a a#ora6 ver provavelmente que o in ica or nem pisca. ?sso acontece porque a chama a local J rpi a emais. Para testar a chama a melhor6 voc@ po e colocar o se#uinte c< i#o na aoA e"ini a na biblioteca Protot,pe para6 respectivamente6
151
def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' "leep%&( end end
e que sua requisio est sen o processa o6 que al#uma coisa est acontecen o6 como
.m outro tipo e in ica or visual J e0ibir al#uma marcao na pr<pria p#ina in ican o o elemento que "oi mo i"ica o. Bo caso e uma ao completa a6 por e0emplo6 po emos ilumin;la na p#ina usan o um e"eito
15!
e p#ina e"ini o na biblioteca script.aculo.us6 que tambJm vem com o Rails. Precisamos mu ar o nosso partial para associar um i a nossa ao. -em isso6 no seremos capa>es e usar o ob=eto 4:(L no Qava-cript e nosso template. (u an o o nosso partial6 temosA
Kul id!Q ompletedAa tion"Q. KY @ ompletedAa tion".ea , do Ma tionM Y. Kli id!Q ontextAa tionAKY! a tion.id Y.Q.KY! a tion.de" ription Y.K/li. KY end Y. K/ul.
page.remove Q ontextAa tionA?S@a tion.idTQ page.repla e Q ompletedAa tion"QJ :partial !. Q ompletedAa tion"QJ :ob-e t !. @ ompletedAa tion" page.vi"ualAeffe t :,ig,lig,tJ Q ontextAa tionA?S@a tion.idTQJ :duration !. 3.5 if @removeA ontext page.remove Q ontextA?S@a tion. ontext.idTQ end
+ resulta o JA
155
A in icao acima permite que o usurio tenha mais uma con"irmao que isso6 o que aconteceu.
.ma mu ana rpi a que po emos "a>er a#ora6 para "acilitar o c< i#o "uturo J compactar o nosso partial para se aplicar a qualquer situao em nossa p#ina. A nossa vieG "icariaA
K,1.8ext L tion"K/,1. KY @a tion".groupAby%[: ontext(.ea , do M ontextJ a tion"M Y. Kdiv la""!QgroupQ id!Q ontextAKY! ontext.id Y.Q. K,&.KY! ontext.name Y.K/,&.
KY! render :partial !. Qa tion"QJ :ob-e t !. a tion" Y. K/div. KY end Y. Kdiv la""!QgroupQ.
K,&.4ompleted L tion"K/,&. KY! render :partial !. Qa tion"QJ :ob-e t !. @ ompletedAa tion" Y. K/div.
Kul KY if a tion".fir"t.doneU Y.id!Q ompletedAa tion"QKY end Y.. KY a tion".ea , do Ma tionM Y. Kli id!Q ontextAa tionAKY! a tion.id Y.Q. KY unle"" a tion.doneU Y. KY! ,e #AboxAtag Q ompleted)?Sa tion.idT+QJ a tion.idJ fal"eJ :on li # !. Q?SremoteAfun tion%:url !. S :a tion !. Qmar#QJ :id !. a tion TJ :before !. Y%1lement.",ow%Qindi atorQ((J : omplete !. Y%1lement.,ide%Qindi atorQ(((TQ Y. KY end Y. KY! a tion.de" ription Y. K/li. KY end Y. K/ul.
page.remove Q ontextAa tionA?S@a tion.idTQ page.repla e Q ompletedAa tion"QJ :partial !. Qa tion"QJ :ob-e t !. @ ompletedAa tion" page.vi"ualAeffe t :,ig,lig,tJ Q ontextAa tionA?S@a tion.idTQJ :duration !. 3.5 if @removeA ontext page.remove Q ontextA?S@a tion. ontext.idTQ end
.ma
as vanta#ens
caso aqui6 quan o estamos li an o com a&es ain a no e0ecuta as que so e0ibi as a#rupa as e a&es = e0ecuta as6 que so e0ibi as sem #rupo6 J a possibili a e e manter o nosso c< i#o coeso6 sem nos repetir.
152
/sse J um os princIpios o Rub, e o Rails6 conheci o como DRC ou N Donat Repeat Yoursel"OHou se=a6 "aa qualquer coisa uma Pnica ve>K no copie na a. /sse J um princIpio s<li o evitar muita or e cabea na manuteno resultan o os in"ames usos e copiar e colar c< i#o. /sse J tambJm o princIpio por trs para evitar repeti&es trs os "iltros aroun que criamos e o que "i>emos acima6 mu an o um mJto o e mera con i&es e muitas outras instLncias e buscar para o mo elo6 e esenvolvimento que po e e bu#s a aplicao6 principalmente por evitar a proli"erao
Assim6 se precisarmos mu ar o nosso partial6 como no e0emplo abai0o6 J que veremos a vanta#em issoA
Kul KY if a tion".fir"t.doneU Y.id!Q ompletedAa tion"QKY end Y.. KY a tion".ea , do Ma tionM Y. Kli id!Q ontextAa tionAKY! a tion.id Y.Q. KY unle"" a tion.doneU Y. KY! ,e #AboxAtag Q ompleted)?Sa tion.idT+QJ a tion.idJ fal"eJ :on li # !. Q?SremoteAfun tion%:url !. S :a tion !. Qmar#QJ :id !. a tion TJ :before !. Y%1lement.",ow%Qindi atorQ((J : omplete !. Y%1lement.,ide%Qindi atorQ(((TQ Y. KY end Y. KY! a tion.de" ription Y.Kbr. K"pan. K"trong.=ro-e t:K/"trong. KY! a tion.pro-e t.name Y. KY if a tion.doneU Y. Kbr.K"trong.4ontext:K/"trong. KY! a tion. ontext.name Y. Kbr.K"trong.4ompleted Lt:K/"trong. KY! a tion. ompletedAat."trftime%QYm/Yd/YyQ( Y. KY end Y. K/"pan. K/li. KY end Y. K/ul.
12$
A#ora que temos as nossas listas6 po emos e0perimentar com mais um pouco e A=a0 e tambJm com al#uns outros aspectos o Rails. Di#amos que queremos a possibili a e e0ecutar entro e or enar as listas acima por priori a e e ao que queremos
entra outra implementao A=a0 presente no RailsA listas or enveis por meio e opera&es ra# an
rop.
Bo caso acima6 J bem "cil "a>er isso. 3asta mo i"icar o nosso partial mais uma ve>. Aproveitamos tambJm para resolver o caso e no termos nenhuma ao completa a6 ei0an o somente o #rupo visIvelA
121
KY if a tion".anyU Y. Kul id!QKY if a tion".fir"t.doneU Y. ompletedAa tion"KY el"e Y. ontextAa tion"AKY! a tion".fir"t. ontext.id Y.KY end Y.Q. KY a tion".ea , do Ma tionM Y. Kli id!Q ontextAa tionAKY! a tion.id Y.Q. KY unle"" a tion.doneU Y. KY! ,e #AboxAtag Q ompleted)?Sa tion.idT+QJ a tion.idJ fal"eJ :on li # !. Q?SremoteAfun tion%:url !. S :a tion !. Qmar#QJ :id !. a tion TJ :before !. Y%1lement.",ow%Qindi atorQ((J : omplete !. Y%1lement.,ide%Qindi atorQ(((TQ Y. KY end Y. KY! a tion.de" ription Y.Kbr. K"pan. K"trong.=ro-e t:K/"trong. KY! a tion.pro-e t.name Y. KY if a tion.doneU Y. Kbr.K"trong.4ontext:K/"trong. KY! a tion. ontext.name Y. Kbr.K"trong.4ompleted Lt:K/"trong. KY! a tion. ompletedAat."trftime%QYm/Yd/YyQ( Y. KY end Y. K/"pan. K/li. KY end Y. K/ul. KY unle"" a tion".fir"t.doneU Y. KY! "ortableAelement%Q ontextAa tion"A?Sa tion".fir"t. ontext.idTQ( Y. KY end Y. KY el"e Y. Kul id!Q ompletedAa tion"Q.K/ul. KY end Y.
que6 obviamente6 no precisamos nos importar mais com a&es = termina as. Depois e criamos um i para ca a um e nossas listas6 e0ecutamos a chama a ao mJto o
sortable0element. /sse mJto o J responsvel por #erar o c< i#o Qava-cript necessrio para que um elemento 4:(L representan o uma lista possa ser reor envel atravJs e opera&es ra# an
rop.
/0perimente a#ora clicar sobre um item a lista e mov@;lo. Como voc@ po e ver6 ao clicar em um elemento a seleo o mesmo se torna livre e ele po e ser arrasta a para qualquer outra posio na lista a que elimita a e elementos no po e ser troca os em nosso e0emplo Sembora pertence. Ca a lista tambJm J
isso se=a possIvel usan o uma li#eira mo i"icao o c< i#oT. + resulta o e um movimento po e ser visto abai0oA
12*
mo elos e a osA um campo que marque a posio o elemento na lista. %eramos uma mi#rao para istoA
ronaldo@minerva:~/tmp/gtd$ " ript/generate migration addApriorityAtoAa tion" exi"t" db/migrate reate db/migrate/'1&AaddApriorityAtoAa tion".rb
/ a e itamosA
12'
la"" Ldd=riority<oL tion" K L tive2e ord::Bigration def "elf.up addA olumn :a tion"J :po"itionJ :integer end def "elf.down removeA olumn :a tion"J :po"ition end end
A#ora que temos um campo6 po emos alterar o mJto o que estamos usan o para us;loA
la"" L tion K L tive2e ord::/a"e attrAprote ted : reatedAatJ : ompletedAat def done!%value( if L tive2e ord::4onne tionLdapter"::4olumn.valueAtoAboolean%value( "elf. ompletedAat ! Gate<ime.now el"e "elf. ompletedAat ! nil end writeAattribute%QdoneQJ value( end belong"Ato : ontext belong"Ato :pro-e t belong"Ato :u"er validate"Apre"en eAof :de" ription validate"Apre"en eAof : ontextAid validate"Apre"en eAof :pro-e tAid ,a"AandAbelong"AtoAmany :re"our e"J :order !. QnameQ def "elf.findAbyA"tatu"%doneJ limit ! nil( find :allJ : ondition" !. )Qdone ! UQJ done U 1 : '+J :in lude !. ):u"erJ :pro-e tJ : ontext+J :order !. Qa tion".po"itionJ a tion".de" riptionQJ :limit !. limit end end
?sso #arante que no tenhamos que nos preocupar com o uso a or enao quan o implementarmos o ao que precisamos no controller. A#ora6 precisamos mu ar o nosso partial para a icionar uma ao Ms nossas listasA
KY if a tion".anyU Y. Kul id!QKY if a tion".fir"t.doneU Y. ompletedAa tion"KY el"e Y. ontextAa tion"AKY! a tion".fir"t. ontext.id Y.KY end Y.Q. KY a tion".ea , do Ma tionM Y. Kli id!Q ontextAa tionAKY! a tion.id Y.Q. KY unle"" a tion.doneU Y. KY! ,e #AboxAtag Q ompleted)?Sa tion.idT+QJ a tion.idJ fal"eJ :on li # !. Q?SremoteAfun tion%:url !. S :a tion !. Qmar#QJ :id !. a tion TJ :before !. Y%1lement.",ow%Qindi atorQ((J : omplete !. Y%1lement.,ide%Qindi atorQ(((TQ Y. KY end Y. KY! a tion.de" ription Y.Kbr. K"pan.
124
K"trong.=ro-e t:K/"trong. KY! a tion.pro-e t.name Y. KY if a tion.doneU Y. Kbr.K"trong.4ontext:K/"trong. KY! a tion. ontext.name Y. Kbr.K"trong.4ompleted Lt:K/"trong. KY! a tion. ompletedAat."trftime%QYm/Yd/YyQ( Y. KY end Y. K/"pan. K/li. KY end Y. K/ul. KY unle"" a tion".fir"t.doneU Y. KY! "ortableAelement%Q ontextAa tion"A?Sa tion".fir"t. ontext.idTQJ :url !. S :a tion !. QorderQJ :id !. a tion".fir"t. ontext.id T( Y. KY end Y. KY el"e Y. Kul id!Q ompletedAa tion"Q.K/ul. KY end Y.
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end def order orderedAa tion" ! param")Q ontextAa tion"A?Sparam"):id+TQ+. olle t S MitemM item.g"ub%/)^'-6+/J QQ(.toAi T orderedAa tion".ea ,Aindex do MiM a tion ! L tion.find%orderedAa tion")i+(.updateAattribute%Qpo"itionQJ i N 1( end render :not,ing !. true end end
A ao J bem simples. + c< i#o Qava-cript seriali>a os elementos6 removen o tu o que no se=a um I#ito
a lista.
Recuperamos esse parLmetro6 que J meramente um arra, com os elementos em or em e coletamos esses os mesmos a "im e ca a um. /m se#ui a6 "a>emos um loop com esses i 6 recuperan o a ao e salvan o sua nova posio. Por "im6 no retornamos na a = que a ao visual = "oi e"etua a.
125
la"" L tion K L tive2e ord::/a"e a t"Aa"Ali"t :" ope !. H ontextAid ! ?S ontextAidT and done ! 'HJ : olumn !. Qpo"itionQ attrAprote ted : reatedAatJ : ompletedAat def done!%value( if L tive2e ord::4onne tionLdapter"::4olumn.valueAtoAboolean%value( "elf. ompletedAat ! Gate<ime.now el"e "elf. ompletedAat ! nil end writeAattribute%QdoneQJ value( end belong"Ato : ontext belong"Ato :pro-e t belong"Ato :u"er validate"Apre"en eAof :de" ription validate"Apre"en eAof : ontextAid validate"Apre"en eAof :pro-e tAid ,a"AandAbelong"AtoAmany :re"our e"J :order !. QnameQ def "elf.findAbyA"tatu"%doneJ limit ! nil( find :allJ : ondition" !. )Qdone ! UQJ done U 1 : '+J :in lude !. ):u"erJ :pro-e tJ : ontext+J :order !. Qa tion".po"itionJ a tion".de" riptionQJ :limit !. limit end end
A eclarao usa a no c< i#o acima cria uma sJrie e mJto os que permitem que a classe se comporte com uma lista quan o con=untos e elementos seus so retorna os. Bo e0emplo6 in icamos que a coluna no banco que in ica a posio J position6 que criamos anteriormente. ?n icamos tambJm um escopo para a operao queremos que a lista s< se=a vli a no escopo passamos para o parLmetro scope um "ra#mento a lista. A e0emplo o que entro e"inimos em A=a0 acima6 e um conte0to. Para isso6 a lista. e a&es no completa as
/sse "ra#mento ser usa o em to as opera&es e lista. Bote que e"inimos esse "ra#mento com aspas simples. ?sso acontece porque estamos usan o interpolao e no queremos que ela acontea ime iatamente como aconteceria se estivessemos usan o aspas uplas. + resulta o so mJto os que po emos usar caso precisamos acessar um #rupo lista. )e=a no console como isso "uncionariaA e a&es com se "osse uma
ronaldo@minerva:~/tmp/gtd$ " ript/ on"ole Doading development environment. .. a tion" ! L tion.findAallAbyA ontextAidAandAdone%*J fal"e( !. )?KL tion:'xbRR &ae$ @attribute"!SQ ontextAidQ!.Q*QJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.QRQJ QdoneQ!.Q'QJ QidQ!.Q5QJ Qde" riptionQ!.QL "imple te"t a tion.QJ Qu"erAidQ!.Q&QJ Qpo"itionQ!.Q1QJ
121
Q reatedAatQ!.Q&''*-'6-&$ 1$:53:5RQT.J ?KL tion:'xbRR &aa8 @attribute"!SQ ontextAidQ!.Q*QJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.QRQJ QdoneQ!.Q'QJ QidQ!.Q8QJ Qde" riptionQ!.QFet anot,er "imple te"t a tion.QJ Qu"erAidQ!.Q&QJ Qpo"itionQ!.Q&QJ Q reatedAatQ!.Q&''*-'6-&* 18:11:3'QT.+ .. a tion"."ize !. & .. a tion")'+.fir"tU !. true .. a tion")1+.la"tU !. true .. a tion")1+.fir"tU !. fal"e .. a tion")'+.la"tU !. fal"e .. a tion")'+.moveAlower !. true .. a tion")'+.la"tU !. true .. a tion")'+.fir"tU !. fal"e .. a tion")'+.,ig,erAitem !. ?KL tion:'xbRRRe*5' @attribute"!SQ ontextAidQ!.Q*QJ Q ompletedAatQ!.nilJ Qpro-e tAidQ!.QRQJ QdoneQ!.Q'QJ QidQ!.Q8QJ Qde" riptionQ!.QFet anot,er "imple te"t a tion.QJ Qu"erAidQ!.Q&QJ Qpo"itionQ!.Q1QJ Q reatedAatQ!.Q&''*-'6-&* 18:11:3'QT. .. a tion")'+.moveAtoAtop !. true
Como po emos ver pelos e0emplos acima6 temos mJto os bem prticos para a operao "acili a e e criar sub;lin#ua#ens eclarativas6 muito aproveita a pelo Rails6 J um positivos o Rub,. /0istem outros
a lista. /ssa
os maiores pontos
ois mJto os similares que vem com o RailsA acts0as0tree6 que trans"orma um mo elo em o mesmo tema que6 ao contrrio a primeira6
uma rvore aninha aK e acts0as0nested0set6 uma variao a icionam comportamentos similares. Como qualquer outra passam e "ra#mentos simplici a e J que est to o o po er o Rub, e o Rails
J muito mais e"iciente na busca e muito menos e"iciente na atuali>ao a rvore. /0istem ain a plu#ins que as tJcnicas que vimos atJ a#ora6 elas no e"ini a. / nessa e c< i#o que trans"ormam a classe enquanto ela est sen o
A#ora que temos a nossa lista "unciona o6 po emos partir para al#o um pouco mais avana oA e itar e inserir novas a&es via A=a0. /mbora insero e e io se=a li an o com A=a0 vamos an ar um pouco mais eva#ar. Para usar a e io6 vamos primeiro mo i"icar a nossa vieG principal para e0ibir um "ormulrio na mesmaA ois aspectos a mesma moe a6 como estamos
K,1.8ext L tion"K/,1.
12!
KY! render :partial !. QeditQ Y. KY @a tion".groupAby%[: ontext(.ea , do M ontextJ a tion"M Y. Kdiv la""!QgroupQ id!Q ontextAKY! ontext.id Y.Q. K,&.KY! ontext.name Y.K/,&.
KY! render :partial !. Qa tion"QJ :ob-e t !. a tion" Y. K/div. KY end Y. Kdiv la""!QgroupQ.
K,&.4ompleted L tion"K/,&. KY! render :partial !. Qa tion"QJ :ob-e t !. @ ompletedAa tion" Y. K/div.
V mais "cil usar um partial porque po emos ter que mo i"icar o item via A=a06 e isso separa o que precisamos e maneira mais tranqRila. + partial seria al#o assimA
Kdiv
la""!QeditQ id!QeditAformQ.
KY formAremoteAfor :itemJ @a tionJ :url !. S :a tion !. QeditQ T do MformM Y. Kp. Klabel for!QitemAde" riptionQ.Ge" ription:K/label.Kbr. KY! form.textAarea Qde" riptionQJ :row" !. 3 Y. K/p. Kp. Klabel for!QitemA ontextAidQ.4ontext:K/label.Kbr. KY! form."ele t Q ontextAidQJ @ ontext"J :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. Klabel for!QitemApro-e tAidQ.=ro-e t:K/label.Kbr. KY! form."ele t Qpro-e tAidQJ @pro-e t"J :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. K/p. KY end Y. K/div. K/div.
Bote o uso
o mJto o form0remote0for. AtJ o momento6 usamos o mJto o start0form0tag para nossos o "ormulrio em si6 precisan o e
"ormulrios. /sse J um mJto o simples6 que #era somente o ta# end0form0tag para "ech;lo.
/0iste um outro mJto o6 chama o form0for Scom sua verso remota form0remote0for6 mostra a acimaT6 que recebe um bloco e nesse bloco J capa> e #erar os campos necessrios6 usan o mJto os internos como
125
e #erao
e usar classes
au0iliares para "ormatar in ivi ualmente os campos a serem #era os. A verso remota tem a Pnica i"erena e usar A=a0 para enviar o campos6 e uma maneira similar a remote0function. Para "acilitar o nosso tutorial6 no vamos inserir em to as as nossa chama as A=a0 o c< i#o necessrio para mostrar e escon er o in ica or e ativi a e. ?ncluir o in ica or em to as as chama as J uma boa i Jia em qualquer aplicao A=a06 mas para "ins e e0emplo aqui vamos i#norar essa questo. / nosso controller "icaria assimA
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end def order orderedAa tion" ! param")Q ontextAa tion"A?Sparam"):id+TQ+. olle t S MitemM item.g"ub%/)^'-6+/J QQ(.toAi T orderedAa tion".ea ,Aindex do MiM a tion ! L tion.find%orderedAa tion")i+(.updateAattribute%Qpo"itionQJ i N 1( end render :not,ing !. true end end
Bote que6 temporariamente6 no estamos utili>an o nenhum ob=eto em nosso "ormulrio #era o.
select em "ormulrios. Com base no princIpio DRC6 seria interessante mover esse c< i#o para um local on e
o mesmo pu esse ser reusa o. .ma soluo J acrescentar um mJto o como o abai0o ao mo elo e a osA
def a"AdropAdown%order !. QnameQ( find%:allJ :order !. order(. olle t S MiM )i.nameJ i.id+ T end
-imples6 embora obviamente tenha que ser repeti a para ca a mo elo em que tivermos interesse em al#o similar. Po erIamos mov@;la para um helper6 com uma pequena mo i"icao e evitar a repetio.
122
.ma outra soluo6 melhor ain a6 seria criar uma espJcie implementar a se#uinte "ormaA
module X<G module L t" module GropGown def "elf.appendAfeature"%ba"e( ba"e.extend%4la""Bet,od"( end module 4la""Bet,od" def a t"Aa"AdropAdown%option" ! ST( la""Aeval KK-1:9 def "elf.a"AdropAdown find%:allJ :order !. Q?Soption"): olumn+TQ(. olle t S MiM )i.?Soption"): olumn+TJ i.id+ end 1:9 end end end end end L tive2e ord::/a"e. la""Aeval do in lude X<G::L t"::GropGown end
e"inin o um m< ulo &*L::-cts::LropLown. /sse m< ulo usa o mJto o e criar na classe
append0features6 que = e0plicamos antes6 para a icionar novas implementa&es a uma classe base. -e voc@ reparar as Pltimas tr@s linhas6 ver que estamos incluin o o m< ulo que acabamos base o ActiveRecor 6 a qual to os nossos mo elos erivam. + nosso mJto o append0features/no "a> mais na a classe base o ActiveRecor . /sses mJto os esto "a>6 por sua ve>6 J nossos mo elos e"ini os em um m< ulo interno6 chama o 7lass2ethods. + que esse m< ulo interno e"inir uma e0tenso chama a acts0as0drop0down6 que ao ser chama a6 trans"ormar ese=amos aqui6 ou se=a6 intro u>ir um mJto o "inal que po emos usar o que usar o mJto o extend6 presente em to as as
classes Rub,6 para incluir al#uns novos mJto os na classe que esta mo i"ican o que6 como vimos acima6 J a
a maneira que
para #era nossos a os. V uma ca eia um pouco complica a e se se#uir a princIpio6 mas estu an o o c< i#o voc@ ver que ela se
torna clara em pouco tempo. /sse mJto o6 acts0as0drop0down6 "a> uma coisa bem simples6 ele interpreta uma strin# no conte0to a classe6 que contJm a e"inio "inal e um mJto o6 com base nos parLmetros que passamos. V mais "cil usar uma strin# aqui para evitar as re#ras e conte0to o uso e um bloco.
*$$
+ mJto o que
e"inimos J o nosso as0drop0down6 visto anteriormente6 mas a#ora em uma verso #enJrica.
Duan o o mJto o class0e!al/ro a6 ele e0ecuta a strin# que lhe "oi passa a no conte0to a classe6 #eran o um mJto o "inal que contJm o c< i#o que precisamos. + mJto o acts0as0drop0down recebe um parLmetro nomea o6 column6 que J a coluna pela qual queremos or enar e que queremos e0ibir em nossos "ormulrios. Para "acilitar o enten imento6 ve=a o uso em um mo elo e a osA o m< ulo
la"" 4ontext K L tive2e ord::/a"e in lude X<G::Luditing a t"Aa"AdropAdown : olumn !. QnameQ attrAprote ted :u"erAid validate"Apre"en eAof :name validate"Auni@uene""Aof :name validate"Apre"en eAof :u"erAid ,a"Amany :a tion" end
Consi eran o o c< i#o acima6 so as tr@s linhas "inais que nos permitem usar acts0as0drop0down /sem precisar acts0as0audited. Bo momento em que a classe J e al#o como "i>emos na linha
iretamente acima
Po erIamos usar a mesma tJcnica para mu ar a linha include / &*L::-uditing para al#o como
mJto o as0drop0down/na classe6 que po emos usar em nosso controller a se#uinte maneiraA
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @ ontext" ! 4ontext.a"AdropAdown @pro-e t" ! =ro-e t.a"AdropAdown end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end def order orderedAa tion" ! param")Q ontextAa tion"A?Sparam"):id+TQ+. olle t S MitemM item.g"ub%/)^'-6+/J QQ(.toAi T orderedAa tion".ea ,Aindex do MiM a tion ! L tion.find%orderedAa tion")i+(.updateAattribute%Qpo"itionQJ i N 1(
*$1
Relembran o a ca eia6 temosA 1T + m< ulo %:DAAActsAADropDoGn J inclui o na classe base o ActiveRecor 6 -cti!e1ecord::Gase. *T Ao ser incluin o6 o m< ulo acima esten e a classe base a icionan o um mJto o e classe chama o acts0as0drop0down6 que po e ser invoca o para e"etuar outra mo i"icao na classe. Com isso conse#uimos que a mo i"icao s< se=a aplica a se o mJto o "or realmente invoca o6 o que "a>emos nos mo elos e nossa escolha. ?sso usa uma proprie a e o Rub, que J a mo i"icao inLmica e uma classe enquanto ela est sen o eclara a6 'T Caso o mJto o se=a invoca o6 ele e0ecuta uma eclarao no conte0to a classe que #era um e mJto o "inal e classe as0drop0down6 customi>a o especialmente para a classe. 4T /sse mJto o po e ser usa o livremente em controllers e vieGs6 "uncionan o sem a necessi a e qualquer outra "orma e con"i#urao. /ssa tJcnica e mo i"icao e classes J muito po erosa e po erIamos us;la e aument;la as maneiras pa ro e inumeras
maneiras. De "ato6 po e;se consi erar que basicamente to as as caracterIsticas usan o tJcnicas similares6 consi eran o que essa J uma classes no Rub,.
?sso acontece porque a lin#ua#em no possui6 proposita amente6 herana mPltipla. Assim6 a maior parte o que J "eito para incrementar uma classe J atravJs e m< ulos6 ou mi0ins na terminolo#ia o Rub,. + pr<prio mJto o group0by6 que usamos anteriormente6 J um mi0in a iciona o a outro mi0in que e"ine enumera&es. +bviamente6 antes e po emos ro ar o c< i#o precisamos mo i"icar nosso arquivo e con"i#urao
? /e "ure to re"tart your web "erver w,en you modify t,i" file. ? On omment below to for e 2ail" into produ tion mode w,en ? you donHt ontrol web/app "erver and anHt "et it t,e proper way ? 18Z)H2L7D>A18ZH+ MM! Hprodu tionH ? >pe ifie" gem ver"ion of 2ail" to u"e w,en vendor/rail" i" not pre"ent 2L7D>AX1BAZ12>7:8 ! H1.1.*H ? /oot"trap t,e 2ail" environmentJ framewor#"J and default re@uire 9ile.-oin%9ile.dirname%AA97D1AA(J HbootH( onfiguration
2ail"::7nitializer.run do M onfigM ? >etting" in onfig/environment"/P ta#e pre eden e t,o"e "pe ified ,ere ? >#ip framewor#" youHre not going to u"e %only wor#" if u"ing vendor/rail"( ? onfig.framewor#" -! ) :a tionAwebA"ervi eJ :a tionAmailer +
*$*
? Ldd additional load pat," for your own u"tom dir" onfig.loadApat," N! Y0% ?S2L7D>A2::<T/extra" ( ? 9or e all environment" to u"e t,e "ame logger level ? %by default produ tion u"e" :infoJ t,e ot,er" :debug( ? onfig.logAlevel ! :debug ? O"e t,e databa"e for "e""ion" in"tead of t,e file "y"tem ? % reate t,e "e""ion table wit, Hra#e db:"e""ion": reateH( ? onfig.a tionA ontroller."e""ionA"tore ! :a tiveAre ordA"tore ? O"e >CD in"tead of L tive 2e ordH" " ,ema dumper w,en reating t,e te"t databa"e. ? <,i" i" ne e""ary if your " ,ema anHt be ompletely dumped by t,e " ,ema dumperJ ? li#e if you ,ave on"traint" or databa"e-"pe ifi olumn type" ? onfig.a tiveAre ord." ,emaAformat ! :"@l ? L tivate ob"erver" t,at ",ould alway" be running ? onfig.a tiveAre ord.ob"erver" ! : a ,erJ :garbageA olle tor ? Ba#e L tive 2e ord u"e O<4-ba"e in"tead of lo al time ? onfig.a tiveAre ord.defaultAtimezone ! :ut ? >ee 2ail"::4onfiguration for more option" end ? ? ? ? ? ? ? ? Ldd new infle tion rule" u"ing t,e following format %all t,e"e example" are a tive by default(: 7nfle tor.infle tion" do Minfle tM infle t.plural /^%ox($/iJ HE1enH infle t."ingular /^%ox(en/iJ HE1H infle t.irregular Hper"onHJ HpeopleH infle t.un ountable Yw% fi", ",eep ( end
? 7n lude your appli ation onfiguration below re@uire Qu"erAfilter.rbQ re@uire Qauditing.rbQ re@uire Qa t"Aa"AdropAdown.rbQ L tionBailer::/a"e."erverA"etting" ! S :addre"" !. Qmail.yourdomain. omQJ :domain !. Qyourdomain. omQJ :u"erAname !. Qu"er@yourdomain. omQJ :pa""word !. QPPPPPPQJ :aut,enti ation !. :login T
e um
etalheA ser
portvel. -e quisermos us;la em outros pro=etos6 teremos copiar os arquivos e mo i"icar nossa con"i#urao. A soluo i eal seria empacotar o m< ulo com um plu#in6 que po eria ser simplesmente ei0a o no iret<rio apropria o6 "a>en o com que a mu ana estivesse automaticamente isponIvel para aplicao sem mo i"ica&es e0plIcitas e con"i#urao. + mJto o usa o tambJm J um pouco limita o no senti o o que o Pnico parLmetro que po e ser in"orma o J o nome e um atributo simples. /m um caso #eral6 haveria a possibili a e e usar um mJto o qualquer o ob=eto para te0to e outro mJto o para or enao6 con i&es6 limites6 e assim por iante. A icionar isso "ica como um e0ercIcio para o leitor.
*$'
div.group S margin-rig,t: &3'px5 T div.edit S border: 1px "olid ? float: rig,t5 widt,: &&'px5 T div.edit- ontent" S margin: 1'px5 T div.edit ,& S padding: 5px5 margin: 'px5 border-bottom: 1px "olid ? ba #ground- olor: ?ddd5 font-"ize: 11'Y5 T div.edit "ele tJ div.edit textarea S widt,: 16'px5 T
+ resulta o "inal
*$4
A#ora que temos o nosso "ormulrio6 po emos comear a pro#ramao A=a0 o mesmo. :emos que consi erar vrias situa&es. +lhan o a tela acima6 por e0emplo6 o que acontece quan o a icionarmos uma ao em um conte0to que no est sen o e0ibi o na p#ina` Como po emos e0ibir nossas vali a&es` Para resolvermos isso6 vamos ter que "a>er al#uma mo i"ica&es ra>oveis em nosso c< i#o. (as6 para comearmos6 vamos simplesmente consi erar a e0ist@ncia o conte0to na p#ina. Q temos o nosso "ormulrio remoto. Precisamos a#ora criar a nossa ao6 que salvar o nosso ob=eto e o e0ibir apropria amenteA
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @ ontext" ! 4ontext.a"AdropAdown @pro-e t" ! =ro-e t.a"AdropAdown
*$5
end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end def order orderedAa tion" ! param")Q ontextAa tion"A?Sparam"):id+TQ+. olle t S MitemM item.g"ub%/)^'-6+/J QQ(.toAi T orderedAa tion".ea ,Aindex do MiM a tion ! L tion.find%orderedAa tion")i+(.updateAattribute%Qpo"itionQJ i N 1( end render :not,ing !. true end def edit @a tion ! L tion. reate%param"):item+.merge%S :done !. fal"e T(( unle"" @a tion.validU render :not,ing !. true end end end
i#noramos o erro. Bo e0emplo acima6 a icionamos o atribute done como "also para que o mesmo receba uma valor no nulo6 = que ele no est sen o envia o o "ormulrio. A#ora precisamos e um template RQ- que nos permita a icionar uma ao recJm cria a M sua lista e
page.in"ertA,tml :bottomJ Q ontextAa tion"A?S@a tion. ontext.idTQJ :partial !. QitemQJ :ob-e t !. @a tion page.vi"ualAeffe t :,ig,lig,tJ Q ontextAa tionA?S@a tion.idTQJ :duration !. 3.5
/stamos mais uma ve> usan o um partial para nosso conteP o6 a icionan o 4:(L a um elemento = e0istente. Como mencionamos acima6 vamos li ar p#ina. /sse partial seria al#o assim6 e"ini o no arquivo 0item.rhtmlA epois com o caso e um conte0to que no est na
Kli id!Q ontextAa tionAKY! item.id Y.Q. KY unle"" item.doneU Y. KY! ,e #AboxAtag Q ompleted)?Sitem.idT+QJ item.idJ fal"eJ :on li # !. Q?SremoteAfun tion%:url !. S :a tion !. Qmar#QJ :id !. item TJ :before !. Y%1lement.",ow%Qindi atorQ((J : omplete !. Y%1lement.,ide%Qindi atorQ(((TQ Y. KY end Y. KY! item.de" ription Y.Kbr. K"pan. K"trong.=ro-e t:K/"trong. KY! item.pro-e t.name Y. KY if item.doneU Y. Kbr.K"trong.4ontext:K/"trong. KY! item. ontext.name Y. Kbr.K"trong.4ompleted Lt:K/"trong. KY! item. ompletedAat."trftime%QYm/Yd/YyQ( Y.
*$1
e"ini o no arquivo
KY if a tion".anyU Y. Kul id!QKY if a tion".fir"t.doneU Y. ompletedAa tion"KY el"e Y. ontextAa tion"AKY! a tion".fir"t. ontext.id Y.KY end Y.Q. KY! render :partial !. QitemQJ : olle tion !. a tion" Y. K/ul. KY unle"" a tion".fir"t.doneU Y. KY! "ortableAelement%Q ontextAa tion"A?Sa tion".fir"t. ontext.idTQJ :url !. S :a tion !. QorderQJ :id !. a tion".fir"t. ontext.id T( Y. KY end Y. KY el"e Y. Kul id!Q ompletedAa tion"Q.K/ul. KY end Y.
com isso no momento = que isso seria uma otimi>ao prematura. .m princIpio bom o esenvolvimento e aplica&es6 se=a em Rails ou no6 J "a>er o que seria mais ele#ante e mais le#Ivel primeiro e epois otimi>ar e acor o com a necessi a e. /m termos e otimi>ao6 e0istem e>enas e op&es que po eriam ser consi era as. (as6 quaisquer que
se=am s< evem ser utili>a as epois a i enti"icao e um problema real para evitar per as e tempo. Para a maior parte as aplica&es6 a veloci a e e qualquer p#ina ser boa o su"iciente porque nem a
car#a e a os nem a car#a e usurios sero altas emais para causar al#um problema. / mesmo antes e consi eramos a&es complica as para salvar um ou outro se#un o e per"ormance aqui e ali6 po em e0istir solu&es mais simples presentes no Rails. De qualquer "orma6 aplica&es A=a0 #eralmente balanam um #asto a ca a ve> que uma mu ana J "eita. Bo caso acima ob=eto e e nossa p#ina6 por e0emplo6 em muitas as a&es estaremos tratan o com apenas um e per"ormance maior por causa e
vrios partials com um tr"e#o menor e a os na p#ina6 por evitarem recarre#ar os a os completos to a
a os6 e tentar otimi>ar qualquer coisa relaciona a as a&es e"etua as em um Pnico ob=eto seria
provavelmente um imenso esper Icio e ener#ia. )oltan o M nossa aplicao6 o resulta o6 ao a icionarmos uma ao em um conte0to = e0istente JA
*$!
page.in"ertA,tml :bottomJ Q ontextAa tion"A?S@a tion. ontext.idTQJ :partial !. QitemQJ :ob-e t !. @a tion page.vi"ualAeffe t :,ig,lig,tJ Q ontextAa tionA?S@a tion.idTQJ :duration !. 3.5 page.repla e QeditAformQJ :partial !. QeditQ
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @ ontext" ! 4ontext.a"AdropAdown @pro-e t" ! =ro-e t.a"AdropAdown
*$5
end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end def order orderedAa tion" ! param")Q ontextAa tion"A?Sparam"):id+TQ+. olle t S MitemM item.g"ub%/)^'-6+/J QQ(.toAi T orderedAa tion".ea ,Aindex do MiM a tion ! L tion.find%orderedAa tion")i+(.updateAattribute%Qpo"itionQJ i N 1( end render :not,ing !. true end def edit @ ontext" ! 4ontext.a"AdropAdown @pro-e t" ! =ro-e t.a"AdropAdown @a tion ! L tion. reate%param"):item+.merge%S :done !. fal"e T(( unle"" @a tion.validU render :not,ing !. true end end end
Bo caso acima6 como encapsulamos a nossa #era&es os a os necessrios e conte0to e pro=eto6 no seria uma violao e c< i#o. + resulta o seria uma vieG assimA a estratJ#ia ()C mover esses chama as para entro a vieG6 economi>an o uma repetio
Kdiv
la""!QeditQ id!QeditAformQ.
KY formAremoteAfor :itemJ @a tionJ :url !. S :a tion !. QeditQ T do MformM Y. Kp. Klabel for!QitemAde" riptionQ.Ge" ription:K/label.Kbr. KY! form.textAarea Qde" riptionQJ :row" !. 3 Y. K/p. Kp. Klabel for!QitemA ontextAidQ.4ontext:K/label.Kbr. KY! form."ele t Q ontextAidQJ 4ontext.a"AdropAdownJ :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. Klabel for!QitemApro-e tAidQ.=ro-e t:K/label.Kbr. KY! form."ele t Qpro-e tAidQJ =ro-e t.a"AdropAdownJ :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. K/p. KY end Y.
*$2
K/div. K/div.
Com isso6 as linhas recJm;inseri as po eriam ser removi as o controller6 sem pre=uI>o para a manuteno e le#ibili a e. Continuan o em nossas melhorias6 J hora e intro u>ir vali ao em nosso "ormulrio. Po emos "a>er isso
Kdiv
la""!QeditQ id!QeditAformQ.
KY formAremoteAfor :itemJ @a tionJ :url !. S :a tion !. QeditQ T do MformM Y. Kp. Klabel for!QitemAde" riptionQ.Ge" ription:K/label.Kbr. KY! form.textAarea Qde" riptionQJ :row" !. 3 Y. K/p. Kp. Klabel for!QitemA ontextAidQ.4ontext:K/label.Kbr. KY! form."ele t Q ontextAidQJ 4ontext.a"AdropAdownJ :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. Klabel for!QitemApro-e tAidQ.=ro-e t:K/label.Kbr. KY! form."ele t Qpro-e tAidQJ =ro-e t.a"AdropAdownJ :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. K/p. KY end Y. K/div. K/div.
if @a tion.validU page.in"ertA,tml :bottomJ Q ontextAa tion"A?S@a tion. ontext.idTQJ :partial !. QitemQJ :ob-e t !. @a tion page.vi"ualAeffe t :,ig,lig,tJ Q ontextAa tionA?S@a tion.idTQJ :duration !. 3.5 @a tion ! nil end page.repla e QeditAformQJ :partial !. QeditQ
*1$
A chave aqui J que se tivermos um valor no ob=eto6 o partial ren eri>ar um "ormulrio preenchi o6 caso contrrio o "ormulrio vir va>io. Como estamos usan o uma varivel e ren eri>ar o "ormulrio em caso e instLncia6 >eramos a mesma antes e uma insero positiva. A#ora s< precisamos mu ar o nosso
controllerA
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end def order orderedAa tion" ! param")Q ontextAa tion"A?Sparam"):id+TQ+. olle t S MitemM item.g"ub%/)^'-6+/J QQ(.toAi T orderedAa tion".ea ,Aindex do MiM a tion ! L tion.find%orderedAa tion")i+(.updateAattribute%Qpo"itionQJ i N 1( end render :not,ing !. true end def edit value" ! param"):item+.merge%S :done !. fal"e T( value")Q ontextAidQ+ ! QQ if value")Q ontextAidQ+ [[ value")Q ontextAidQ+."tart"Awit,U%Q--Q( value")Qpro-e tAidQ+ ! QQ if value")Qpro-e tAidQ+ [[ value")Qpro-e tAidQ+."tart"Awit,U%Q--Q( @a tion ! L tion. reate%value"( end end
a biblioteca Protot,pe usa a para o Rails. Ao e elementos o tipo select6 se o item seleciona o o que nave#a ores "a>em
seriali>ar "ormulrios para envio via Qava-cript6 no caso normalmente. ?sso J o que tratamos no c< i#o acima.
Bo nosso "ormulrio6 isso acontece porque os elementos select que estamos usan o contJm um valor va>io para o primeiro elemento6 que J #era o pelo parLmetro prompt para nos ar uma usabili a e maior. .ma soluo para o problema seria mo i"icar a biblioteca para no ter esse comportamento por pa ro. + inconveniente seria ter que manter esse patch a ca a atuali>ao a mesma. .ma outra soluo seria criar um "iltro para automaticamente limpar os nossos parLmetros se "or o caso. / uma terceira soluo seria no usar o parLmetro prompt6 usan o include0blan3 ao invJs isso6 com uma pequena per a e usabili a e. + resulta o6 com a vali ao6 J o se#uinteA
*11
A#ora precisamos a icionar o caso em que um conte0to que no est sen o e0ibi o na p#ina6 por no possuir nenhuma ao no completa a6 recebe uma ao. (u amos a nossa vieG primriaA
*1*
KY @a tion".groupAby%[: ontext(.ea , do M ontextJ a tion"M Y. KY! render :partial !. Q ontextQJ :ob-e t !. ontextJ :lo al" !. S :a tion" !. a tion" T Y. KY end Y. K/div. Kdiv la""!QgroupQ.
K,&.4ompleted L tion"K/,&. KY! render :partial !. Qa tion"QJ :ob-e t !. @ ompletedAa tion" Y. K/div.
Kdiv
ontext.id Y.Q.
K,&.KY!
if @a tion.validU page KK Qif %typeof%$%H ontextA?S@a tion. ontext.idTH(( !! HundefinedH( SQ page.in"ertA,tml :topJ Q ontext"QJ :partial !. Q ontextQJ :ob-e t !. @a tion. ontextJ :lo al" !. S :a tion" !. @a tion. ontext.a tion" T page KK QT el"e SQ page.in"ertA,tml :bottomJ Q ontextAa tion"A?S@a tion. ontext.idTQJ :partial !. QitemQJ :ob-e t !. @a tion page KK QTQ page.vi"ualAeffe t :,ig,lig,tJ Q ontextAa tionA?S@a tion.idTQJ :duration !. 3.5 @a tion ! nil end page.repla e QeditAformQJ :partial !. QeditQ
Bo e0emplo acima6 precisamos saber se o conte0to = e0iste na p#ina. Para isso6 usamos um pouco Qava-cript ireto6 veri"ican o se h um ob=eto com o i
simplesmente a icionamos a ao. Caso contrrio6 ren eri>amos o conte0to completo e o inserimos no topo a lista #eral e conte0tos. + resulta o "inal J o que queremos6 com ois problemas simplesA + primeiro J que tanto as a&es como conte0tos inseri os esto potencialmente "ora acor o com os critJrios que estabelecemos = que estamos inserin o o resulta o se=a um pouco mais caro em termos vli a. + se#un o problema J termos que "orar a varivel ?action a assumir um valor nulo antes e usar uma varivel e e0ibirmos o e or enao e
e nossas chama as em
locais "i0os. A soluo para isso seria ren eri>amos as se&es completas que precisamos6 mesmo que isso e per"ormance. -e a or em "or muito importante6 essa J uma soluo
"ormulrio para o caso e queremos um "ormulrio va>io. + melhor seria usar a pr<pria conveno o Rails e"ini a para o pr<prio partial. + problema6 nesse caso6 J que estamos usan o
*1'
importLncia. (as para um aplicao real6 seria interessante uma re e"inio esse mJto o. + resulta o a a io e uma ao em um conte0to no e0istente na p#ina J o se#uinteA
-e voc@ tentou usar a lista para reor enar um elemento epois a a io e um item6 reparou que a mesma para e "uncionar. /sse J o compartamento normal e uma lista quan o um novo elemento J a iciona o M epois e uma mo i"icao na mesma6 corri#in o o mesma. Para corri#ir isso6 basta reiniciali>ar a lista
if @a tion.validU page KK Qif %typeof%$%H ontextA?S@a tion. ontext.idTH(( !! HundefinedH( SQ page.in"ertA,tml :topJ Q ontext"QJ :partial !. Q ontextQJ :ob-e t !. @a tion. ontextJ :lo al" !. S :a tion" !. @a tion. ontext.a tion" T page KK QT el"e SQ page.in"ertA,tml :bottomJ Q ontextAa tion"A?S@a tion. ontext.idTQJ :partial !. QitemQJ :ob-e t !. @a tion page KK QTQ page.vi"ualAeffe t :,ig,lig,tJ Q ontextAa tionA?S@a tion.idTQJ :duration !. 3.5 page."ortable Q ontextAa tion"A?S@a tion. ontext.idTQJ :url !. S :a tion !. QorderQJ :id !. @a tion. ontext.id T @a tion ! nil end page.repla e QeditAformQJ :partial !. QeditQ
/ uma Pltima melhoria que po emos "a>er antes "orma e cancelar a e io quan o o usurio e io6 em 0edit.rhtmlA
e partirmos para o nosso pr<0imo t<pico J ter al#uma e mo i"icar o nosso partial e
Kdiv
la""!QeditQ id!QeditAformQ.
KY formAremoteAfor :itemJ @a tionJ :url !. S :a tion !. QeditQ T do MformM Y. Kp. Klabel for!QitemAde" riptionQ.Ge" ription:K/label.Kbr. KY! form.textAarea Qde" riptionQJ :row" !. 3 Y. K/p. Kp. Klabel for!QitemA ontextAidQ.4ontext:K/label.Kbr. KY! form."ele t Q ontextAidQJ 4ontext.a"AdropAdownJ :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. Klabel for!QitemApro-e tAidQ.=ro-e t:K/label.Kbr. KY! form."ele t Qpro-e tAidQJ =ro-e t.a"AdropAdownJ :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#AtoAremote Q4an elQJ :url !. S :a tion !. Q an elQ T Y. K/p. KY end Y. K/div. K/div.
*15
def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end def order orderedAa tion" ! param")Q ontextAa tion"A?Sparam"):id+TQ+. olle t S MitemM item.g"ub%/)^'-6+/J QQ(.toAi T orderedAa tion".ea ,Aindex do MiM a tion ! L tion.find%orderedAa tion")i+(.updateAattribute%Qpo"itionQJ i N 1( end render :not,ing !. true end def edit value" ! param"):item+.merge%S :done !. fal"e T( value")Q ontextAidQ+ ! QQ if value")Q ontextAidQ+ [[ value")Q ontextAidQ+."tart"Awit,U%Q--Q( value")Qpro-e tAidQ+ ! QQ if value")Qpro-e tAidQ+ [[ value")Qpro-e tAidQ+."tart"Awit,U%Q--Q( @a tion ! L tion. reate%value"( end def end end an el
7inali>an o com um novo template RQ- para a ao6 #eran o uma vieG chama a cancel.r%sA
KY formAremoteAfor :itemJ @a tionJ :,tml !. S :a tion !. urlAfor%:a tion !. Q"aveQJ :id !. @a tion( :url !. S :a tion !. Q"aveQJ :id !. @a tion T do MformM Y. Kp. Klabel for!QitemAde" riptionQ.Ge" ription:K/label.Kbr. KY! form.textAarea Qde" riptionQJ :row" !. 3 Y. K/p.
*11
KY if W@a tion MM @a tion.newAre ordU Y. Kp. Klabel for!QitemA ontextAidQ.4ontext:K/label.Kbr. KY! form."ele t Q ontextAidQJ 4ontext.a"AdropAdownJ :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. Klabel for!QitemApro-e tAidQ.=ro-e t:K/label.Kbr. KY! form."ele t Qpro-e tAidQJ =ro-e t.a"AdropAdownJ :prompt !. Q-- 4,oo"e --Q Y. K/p. KY end Y. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#AtoAremote Q4an elQJ :update !. QeditAformA ontainerQJ :url !. S :a tion !. Q an elQ T Y. K/p. KY end Y. K/div. K/div. K/div.
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end def mar# @a tion ! L tion.find%param"):id+.g"ub%/)^'-6+/J QQ(( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end def order orderedAa tion" ! param")Q ontextAa tion"A?Sparam"):id+TQ+. olle t S MitemM item.g"ub%/)^'-6+/J QQ(.toAi T orderedAa tion".ea ,Aindex do MiM a tion ! L tion.find%orderedAa tion")i+(.updateAattribute%Qpo"itionQJ i N 1( end render :not,ing !. true end def edit @a tion ! param"):id+ U L tion.find%param"):id+( : L tion.new end def "ave value" ! param"):item+.merge%S :done !. fal"e T( value")Q ontextAidQ+ ! QQ if value")Q ontextAidQ+ [[ value")Q ontextAidQ+."tart"Awit,U%Q--Q( value")Qpro-e tAidQ+ ! QQ if value")Qpro-e tAidQ+ [[ value")Qpro-e tAidQ+."tart"Awit,U%Q--Q( @a tion ! param"):id+ U L tion.update%param"):id+J value"( : L tion. reate%value"( re"pondAto do Mwant"M want".-" want".,tml do if @a tion.validU
*1!
redire tAto :a tion !. QindexQ el"e index render :a tion !. QindexQ end end end end def an el render :partial !. QeditQ end def delete @a tion ! L tion.find%param"):id+( @a tion.de"troy @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end end
A tItulo e observao6 note que os e0emplos se#uintes desfazem essa mo i"icao. A maneira usa a acima "a> uso e uma caracterIstica as invoca&es A=a0 reali>a as no Rails que J a e"ine o elemento 4:(L que receber o
possibili a e e6 ao invJs e retornar Qava-cript6 retornar um "ra#mento e 4:(L e atuali>ar iretamente a p#ina com o mesmo. + parLmetro update6 no e0emplo acima6 conteP o. Precisamos e um elemento superior porque a substituio J "eita somente no conteP o e0terno.
+ parLmetro update po e tambJm iscriminar entre sucesso e "alha usan o a se#uinte con"i#uraoA
:update !. S :"u
Bo caso acima6 se a ao retornar um c< i#o 4::P pelo outro parLmetro J que ser atuali>a o.
parLmetro success J que receber o conteP o retorna oK caso contrrio6 o elemento cu=o i
As chama as remotas no Rails possuem vrias outras caracterIsticas que #astarIamos muito tempo para e0plorar aqui e cu=as escri&es po em ser encontra as na ocumentao e em outros tutoriais mais e parLmetros customi>a os para as a&es especI"icos. /ntre essas caracterIstiscas incluem;se a passa#em
no servi or6 acompanhamento o processo completo e requisio6 tratamento e con"irma&es e uma sJrie e outras possibili a es que nos permitem re"inar bastante a invocao o mJto o remoto. ?sso termina nossas melhorias. Depois e termos a insero "uncionan o6 po emos trabalhar com a e io e remoo e a&es e0istentes. Antes e partirmos para isso porJm6 uma icaA e0iste um plu#in para o Rails6 chama o .nobtrusive e #erar c< i#o Qava-cript
Qava-cript Shttp://www.u%sMrails.comT que po e a=u ar bastante na hora interpola o com uma p#ina para invocar lin8s6 "un&es e "ormulrios remotos.
*15
)amos comear com a e0cluso e um item6 que se#ue uma linha muito similar M
completo. (o i"icamos o nosso partial que #erar um item para incluir um lin8 remoto para a aoA
Kli id!Q ontextAa tionAKY! item.id Y.Q. KY unle"" item.doneU Y. KY! ,e #AboxAtag Q ompleted)?Sitem.idT+QJ item.idJ fal"eJ :on li # !. Q?SremoteAfun tion%:url !. S :a tion !. Qmar#QJ :id !. item TJ :before !. Y%1lement.",ow%Qindi atorQ((J : omplete !. Y%1lement.,ide%Qindi atorQ(((TQ Y. KY end Y. KY! item.de" ription Y. KY unle"" item.doneU Y. KY! lin#AtoAremote Q)G+QJ S :url !. S :a tion !. QdeleteQJ :id !. item TJ : onfirm !. QLre you "ureUQ TJ S : la"" !. Qa tion-lin#Q T Y. KY end Y. Kbr. K"pan. K"trong.=ro-e t:K/"trong. KY! item.pro-e t.name Y. KY if item.doneU Y. Kbr.K"trong.4ontext:K/"trong. KY! item. ontext.name Y. Kbr.K"trong.4ompleted Lt:K/"trong. KY! item. ompletedAat."trftime%QYm/Yd/YyQ( Y. KY end Y. K/"pan. K/li.
a.a tion-lin# S text-de oration: none5 font-"ize: "maller5 border-bottom: 1px da",ed ?6665 olor: ?***5 T
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end def order orderedAa tion" ! param")Q ontextAa tion"A?Sparam"):id+TQ+. olle t S MitemM item.g"ub%/)^'-6+/J QQ(.toAi T orderedAa tion".ea ,Aindex do MiM a tion ! L tion.find%orderedAa tion")i+(.updateAattribute%Qpo"itionQJ i N 1( end render :not,ing !. true end def edit value" ! param"):item+.merge%S :done !. fal"e T(
*12
value")Q ontextAidQ+ ! QQ if value")Q ontextAidQ+ [[ value")Q ontextAidQ+."tart"Awit,U%Q--Q( value")Qpro-e tAidQ+ ! QQ if value")Qpro-e tAidQ+ [[ value")Qpro-e tAidQ+."tart"Awit,U%Q--Q( @a tion ! L tion. reate%value"( end def end an el
def delete @a tion ! L tion.find%param"):id+( @a tion.de"troy @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end end
page.vi"ualAeffe t :fadeJ Q ontextAa tionA?S@a tion.idTQ page.delay%5( do page.remove Q ontextAa tionA?S@a tion.idTQ end if @removeA ontext page.remove Q ontextA?S@a tion. ontext.idTQ end
?ntro u>imos um pequeno ela, na e0ecuo a remoo para que o e"eito e esaparecimento possa ro ar primeiro. ?sso completa a nossa remoo. A#ora po emos passar para a nossa e io. Para a mesma6 vamos separar a ao que "a> a e io que salva realmente os a os. (u amos inicialmente o nosso controllerA a ao
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end def mar# @a tion ! L tion.find%param"):id+( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end def order orderedAa tion" ! param")Q ontextAa tion"A?Sparam"):id+TQ+. olle t S MitemM item.g"ub%/)^'-6+/J QQ(.toAi T orderedAa tion".ea ,Aindex do MiM a tion ! L tion.find%orderedAa tion")i+(.updateAattribute%Qpo"itionQJ i N 1( end render :not,ing !. true
**$
end def edit @a tion ! param"):id+ U L tion.find%param"):id+( : L tion.new end def "ave value" ! param"):item+.merge%S :done !. fal"e T( value")Q ontextAidQ+ ! QQ if value")Q ontextAidQ+ [[ value")Q ontextAidQ+."tart"Awit,U%Q--Q( value")Qpro-e tAidQ+ ! QQ if value")Qpro-e tAidQ+ [[ value")Qpro-e tAidQ+."tart"Awit,U%Q--Q( @a tion ! param"):id+ U L tion.update%param"):id+J value"( : L tion. reate%value"( end def end an el
def delete @a tion ! L tion.find%param"):id+( @a tion.de"troy @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end end
Depois
isso6 renomeamos a nossa vieG atualmente chama a edit.r%s para sa!e.r%s6 que termina com o
se#uinte conteP oA
if @a tion.validU page KK Qif %typeof%$%H ontextA?S@a tion. ontext.idTH(( !! HundefinedH( SQ page.in"ertA,tml :topJ Q ontext"QJ :partial !. Q ontextQJ :ob-e t !. @a tion. ontextJ :lo al" !. S :a tion" !. @a tion. ontext.a tion" T page KK QT el"e SQ if param"):id+ page.repla e Q ontextAa tionA?S@a tion.idTQJ :partial !. QitemQJ :ob-e t !. @a tion el"e page.in"ertA,tml :bottomJ Q ontextAa tion"A?S@a tion. ontext.idTQJ :partial !. QitemQJ :ob-e t !. @a tion end page KK QTQ page.vi"ualAeffe t :,ig,lig,tJ Q ontextAa tionA?S@a tion.idTQJ :duration !. 3.5 page."ortable Q ontextAa tion"A?S@a tion. ontext.idTQJ :url !. S :a tion !. QorderQJ :id !. @a tion. ontext.id T @a tion ! nil end page.repla e QeditAformQJ :partial !. QeditQ
Depois6 e itamos o nosso partial que #era um item6 que J o arquivo 0item.rhtmlA
Kli id!Q ontextAa tionAKY! item.id Y.Q. KY unle"" item.doneU Y. KY! ,e #AboxAtag Q ompleted)?Sitem.idT+QJ item.idJ fal"eJ :on li # !. Q?SremoteAfun tion%:url !. S :a tion !. Qmar#QJ :id !. item TJ :before !. Y%1lement.",ow%Qindi atorQ((J : omplete !. Y%1lement.,ide%Qindi atorQ(((TQ Y.
**1
KY end Y. KY! item.de" ription Y. KY unle"" item.doneU Y. KY! lin#AtoAremote Q)1+QJ S :url !. S :a tion !. QeditQJ :id !. item T TJ S : la"" !. Qa tion-lin#Q T Y. KY! lin#AtoAremote Q)G+QJ S :url !. S :a tion !. QdeleteQJ :id !. item TJ : onfirm !. QLre you "ureUQ TJ S : la"" !. Qa tion-lin#Q T Y. KY end Y. Kbr. K"pan. K"trong.=ro-e t:K/"trong. KY! item.pro-e t.name Y. KY if item.doneU Y. Kbr.K"trong.4ontext:K/"trong. KY! item. ontext.name Y. Kbr.K"trong.4ompleted Lt:K/"trong. KY! item. ompletedAat."trftime%QYm/Yd/YyQ( Y. KY end Y. K/"pan. K/li.
Kdiv
la""!QeditQ id!QeditAformQ.
KY formAremoteAfor :itemJ @a tionJ :url !. S :a tion !. Q"aveQJ :id !. @a tion T do MformM Y. Kp. Klabel for!QitemAde" riptionQ.Ge" ription:K/label.Kbr. KY! form.textAarea Qde" riptionQJ :row" !. 3 Y. K/p. KY if W@a tion MM @a tion.newAre ordU Y. Kp. Klabel for!QitemA ontextAidQ.4ontext:K/label.Kbr. KY! form."ele t Q ontextAidQJ 4ontext.a"AdropAdownJ :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. Klabel for!QitemApro-e tAidQ.=ro-e t:K/label.Kbr. KY! form."ele t Qpro-e tAidQJ =ro-e t.a"AdropAdownJ :prompt !. Q-- 4,oo"e --Q Y. K/p. KY end Y. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#AtoAremote Q4an elQJ :url !. S :a tion !. Q an elQ T Y. K/p. KY end Y. K/div. K/div.
Duan o estamos e itan o uma ao6 no queremos mu ar o seu conte0to ou pro=eto. Para isso6 bloqueamos esses campose em nosso e0emplo. +bviamente6 po erIamos querer e it;los tambJm em uma outra situao6 caso qual terIamos que
***
consi erar a possibili a e e mover itens e um local para o outro6 o que no seria muito complica o. 7a>er isso "ica como um e0ercIcio para o leitor6 consi eran o que to as as peas necessrias esto isponIveis. + resulta o e uma e io com base no c< i#o acima seriaA
-e quisermos mais uma mo i"icao A=a0 em nossa p#ina6 po erIamos acrescentar a possibili a e um usurio arraste uma ao para a lista e a&es completa as. Para isso6 precisarIamos6 inicialmente6 e mo i"icar uas
e que
**'
Kli id!Q ontextAa tionAKY! item.id Y.Q. KY unle"" item.doneU Y. KY! ,e #AboxAtag Q ompleted)?Sitem.idT+QJ item.idJ fal"eJ :on li # !. Q?SremoteAfun tion%:url !. S :a tion !. Qmar#QJ :id !. item TJ :before !. Y%1lement.",ow%Qindi atorQ((J : omplete !. Y%1lement.,ide%Qindi atorQ(((TQ Y. KY end Y. KY! item.de" ription Y. KY unle"" item.doneU Y. KY! lin#AtoAremote Q)1+QJ S :url !. S :a tion !. QeditQJ :id !. item T TJ S : la"" !. Qa tion-lin#Q T Y. KY! lin#AtoAremote Q)G+QJ S :url !. S :a tion !. QdeleteQJ :id !. item TJ : onfirm !. QLre you "ureUQ TJ S : la"" !. Qa tion-lin#Q T Y. KY end Y. Kbr. K"pan. K"trong.=ro-e t:K/"trong. KY! item.pro-e t.name Y. KY if item.doneU Y. Kbr.K"trong.4ontext:K/"trong. KY! item. ontext.name Y. Kbr.K"trong.4ompleted Lt:K/"trong. KY! item. ompletedAat."trftime%QYm/Yd/YyQ( Y. KY end Y. K/"pan. K/li. KY! draggableAelement Q ontextAa tionA?Sitem.idTQ unle"" item.doneU Y.
K,1.8ext L tion"K/,1. KY! render :partial !. QeditQ Y. Kdiv id!Q ontext"Q. KY @a tion".groupAby%[: ontext(.ea , do M ontextJ a tion"M Y. KY! render :partial !. Q ontextQJ :ob-e t !. ontextJ :lo al" !. S :a tion" !. a tion" T Y. KY end Y. K/div. Kdiv la""!QgroupQ id!Q ompletedAa tion"A ontainerQ.
K,&.4ompleted L tion"K/,&. KY! render :partial !. Qa tion"QJ :ob-e t !. @ ompletedAa tion" Y. KY! dropAre eivingAelement Q ompletedAa tion"A ontainerQJ :url !. S :a tion !. Qmar#Q T Y. K/div.
/ "inalmente6 mo i"ican o o nosso controller para receber marcar itens e acor o com a nova situaoA
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end def mar# @a tion ! L tion.find%param"):id+.g"ub%/)^'-6+/J QQ(( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end
**4
def order orderedAa tion" ! param")Q ontextAa tion"A?Sparam"):id+TQ+. olle t S MitemM item.g"ub%/)^'-6+/J QQ(.toAi T orderedAa tion".ea ,Aindex do MiM a tion ! L tion.find%orderedAa tion")i+(.updateAattribute%Qpo"itionQJ i N 1( end render :not,ing !. true end def edit @a tion ! param"):id+ U L tion.find%param"):id+( : L tion.new end def "ave value" ! param"):item+.merge%S :done !. fal"e T( value")Q ontextAidQ+ ! QQ if value")Q ontextAidQ+ [[ value")Q ontextAidQ+."tart"Awit,U%Q--Q( value")Qpro-e tAidQ+ ! QQ if value")Qpro-e tAidQ+ [[ value")Qpro-e tAidQ+."tart"Awit,U%Q--Q( @a tion ! param"):id+ U L tion.update%param"):id+J value"( : L tion. reate%value"( end def end an el
def delete @a tion ! L tion.find%param"):id+( @a tion.de"troy @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end end
a maneira com os parLmetros so envia os para o controller eve ter nota o6 a or enao a lista continua
ra# an
"uncionan o.
DEGRADAO EM AJAX
.ma coisa importante quan o li amos com A=a0 J no esabilita o. Para li ar com isso6 o Rails possui maneiras permite o uso ei0ar que tu o acontea somente por meio e #erar uma e#ra ao o Qava-cript6 = que e0istem situa&es em que um nave#a or no suporta a tecnolo#ia ou est com a mesma e "uncionali a e que e uma p#ina mesmo com o Qava-cript sem "uncionar. :oman o como e0emplo o nosso
"ormulrio e insero e a&es6 po emos "a>er um rpi o teste isso. Primeiro6 mo i"icamos o nosso partial e e ioA
Kdiv
la""!QeditQ id!QeditAformQ.
KY formAremoteAfor :itemJ @a tionJ :,tml !. S :a tion !. urlAfor%:a tion !. Q"aveQJ :id !. @a tion( :url !. S :a tion !. Q"aveQJ :id !. @a tion T do MformM Y.
**5
Kp. Klabel for!QitemAde" riptionQ.Ge" ription:K/label.Kbr. KY! form.textAarea Qde" riptionQJ :row" !. 3 Y. K/p. KY if W@a tion MM @a tion.newAre ordU Y. Kp. Klabel for!QitemA ontextAidQ.4ontext:K/label.Kbr. KY! form."ele t Q ontextAidQJ 4ontext.a"AdropAdownJ :prompt !. Q-- 4,oo"e --Q Y. K/p. Kp. Klabel for!QitemApro-e tAidQ.=ro-e t:K/label.Kbr. KY! form."ele t Qpro-e tAidQJ =ro-e t.a"AdropAdownJ :prompt !. Q-- 4,oo"e --Q Y. K/p. KY end Y. Kp. KY! "ubmitAtag Q>aveQ Y. or KY! lin#AtoAremote Q4an elQJ :url !. S :a tion !. Q an elQ T Y. K/p. KY end Y. K/div. K/div.
+ parLmetro html
la"" ;ome4ontroller K Lppli ation4ontroller def index @a tion" ! L tion.findAbyA"tatu"%fal"e( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( end def mar# @a tion ! L tion.find%param"):id+.g"ub%/)^'-6+/J QQ(( @a tion.updateAattribute%QdoneQJ true( @ ompletedAa tion" ! L tion.findAbyA"tatu"%trueJ 3( @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end def order orderedAa tion" ! param")Q ontextAa tion"A?Sparam"):id+TQ+. olle t S MitemM item.g"ub%/)^'-6+/J QQ(.toAi T orderedAa tion".ea ,Aindex do MiM a tion ! L tion.find%orderedAa tion")i+(.updateAattribute%Qpo"itionQJ i N 1( end render :not,ing !. true end def edit @a tion ! param"):id+ U L tion.find%param"):id+( : L tion.new end def "ave value" ! param"):item+.merge%S :done !. fal"e T( value")Q ontextAidQ+ ! QQ if value")Q ontextAidQ+ [[ value")Q ontextAidQ+."tart"Awit,U%Q--Q( value")Qpro-e tAidQ+ ! QQ if value")Qpro-e tAidQ+ [[ value")Qpro-e tAidQ+."tart"Awit,U%Q--Q( @a tion ! param"):id+ U L tion.update%param"):id+J value"( :
**1
L tion. reate%value"( re"pondAto do Mwant"M want".-" want".,tml do if @a tion.validU redire tAto :a tion !. QindexQ el"e index render :a tion !. QindexQ end end end end def end an el
def delete @a tion ! L tion.find%param"):id+( @a tion.de"troy @removeA ontext ! @a tion. ontext.a tion". ount%Qdone ! 'Q( !! ' end end
+ mJto o respond0to J uma a io o Rails 1.1 que permite que uma aplicao rea=a J ei0a o como est Sprimeira linha entro
e "ormas
i"erentes
e acor o com o tipo e resposta pe i o. Bo caso acima6 se o tipo e resposta pe i o J Qava-cript6 o retorno o blocoTK se6 ao contrrio6 4:(L J pe i o6 ren eri>amos a e p#ina novamente. Com o c< i#o que = inserimos no partial e com o c< i#o = e0istente nessa ao6 o ob=eto com que estivermos trabalhan o ser salvo tranqRilamente. / isso6 com apenas um pouco mais c< i#o. Assim6 rea#ir apropria amente e acor o com a situao J possIvel. V claro que a i"icul a e varia para caso. Bo nosso e0emplo6 marcas a&es com completa as seria um pouco mais leitor. Com isso terminamos a nossa viso rpi a o A=a0 em nosso tutorial. Bo vimos tu o o que J possIvel "a>erA por e0emplo6 uploa s6 inclusive = "oi menciona o anteriormente6 so um pouco problemticos usan o A=a0. A=a0 no J uma soluo m#ica para os seus problemas para aumentar a capaci a e e "le0ibili a e quem "a> a na aus@ncia i"erena J o A=a0 est =ustamente no nosso e0emplo "inal uma aplicao. e usabili a e. V uma "erramenta muito po erosa e e se "a>er e caso e
impossIvel. (o i"icar to as as a&es e nossa p#ina para "uncionar sem A=a0 "ica como um e0ercIcio para o
e suas aplica&es6 mas6 como qualquer outra "erramenta6 e como "a>er com que a p#ina continue a "uncionar mesmo o que qualquer outra coisa em
aplicao Rails. Besta seo vamos cobrir al#uns t<picos que nos permitem e0trair ain a mais "uncionali a e e nossas aplica&es. /m especial6 uma as se&es se#uintes trata e mo i"ica&es que permite um uso melhor o Rails em outros i iomas6 que provavelmente ser o seu caso6 esenvolven o aplica&es em portu#u@s6 com mensa#ens tra u>i as6 acentos e to a a para"ernlia necessria para uma aplicao locali>a a. (as6 antes e vermos isso6 vamos consi erar al#uns outros assuntosA
ROTEAMENTO AVANADO
AtJ o momento6 usamos somente o roteamento pa ro "orneci o pelo Rails6 com uma pequena e0ceo que J a p#ina inicial a aplicao6 re ireciona a anteriormente para o controller home. /m al#uns casos6 e usar .RLs especialmente con"i#ura as para a sua aplicaoHtanto por porJm6 voc@ ter necessi a e
quest&es e usabili a e e le#ibili a e como por quest&es e "acili a e e esenvolvimento. Bo arquivo routes.rb6 que vimos anteriormente6 J possIvel e"inir roteamentos i"erentes que sero
aplica os e acor o com critJrios e i enti"icao e"ini os por voc@. -uponhamos6 por e0emplo6 que queremos ver as nossas a&es separa as por com isso6 mo i"ican o o arquivo anteriormente menciona o a se#uinte "ormaA ia6 ao invJs a p#ina atual
que temos6 que simplesmente e0ibe uma lista#em. Po emos utili>ar um roteamento customi>a o para li ar
L tion4ontroller::2outing::2oute".draw do MmapM ? <,e priority i" ba"ed upon order of reation: fir"t
? >ample of regular route: ? map. onne t Qprodu t"/:idQJ : ontroller !. Q atalogQJ :a tion !. QviewQ ? Ieep in mind you an a""ign value" ot,er t,an : ontroller and :a tion ? >ample of named route: ? map.pur ,a"e Qprodu t"/:id/pur ,a"eQJ : ontroller !. Q atalogQJ :a tion !. Qpur ,a"eQ ? <,i" route an be invo#ed wit, pur ,a"eAurl%:id !. produ t.id( ? Fou an ,ave t,e root of your "ite routed by ,oo#ing up QQ ? -- -u"t remember to delete publi /index.,tml. map. onne t QQJ : ontroller !. Q,omeQ ? Lllow downloading 0eb >ervi e 0>GD a" a file wit, an exten"ion ? in"tead of a file named Qw"dlQ map. onne t Q: ontroller/"ervi e.w"dlQJ :a tion !. Qw"dlQ map. onne t Q:year/:mont,/:dayQJ : ontroller !. Qa tion"QJ :a tion !. QbyAdateQJ :re@uirement" !. S :year !. /EdS$T/J :mont, !. /EdS1J &T/J :day !. /EdS1J &T/ T ? 7n"tall t,e default route a" t,e lowe"t priority. map. onne t Q: ontroller/:a tion/:idQ end
**5
+ roteamento que estabelecemos acima in ica que esperamos tr@s itens como a composio chama os/ year6 month e day. /sses parLmetros sero passa os para a ao by0date nome J actions. + Pltimo parLmetro m@s e ia com um ou ois. A#ora po emos e"inir a nossa ao6 mo i"ican o o controller e0istenteA
e nossa .RL.
Duan o esse roteamento "or aciona o6 esses itens sero respectivamente envia os para parLmetros o controller cu=o
o roteamento in ica vali a&es que precisam ser e"etua as antes e"inimos que esperamos o ano com quatro I#itos e
la"" L tion"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @a tionApage"J @a tion" ! paginate :a tion"J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ end def byAdate @a tionApage"J @a tion" ! paginate :a tion"J : ondition" !. )Qa tion". reatedAat ! UQJ Gate.new%param"):year+.toAiJ param"):mont,+.toAiJ param"):day+.toAi(+J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ render :a tion !. Qli"tQ end def edit @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if re@ue"t.getU if param"):id+ @item ! L tion.find%param"):id+( el"e @item ! L tion.new end el"if re@ue"t.po"tU @item ! param"):id+ U L tion.update%param"):id+J param"):item+( : L tion. reate%param"):item+( if @item.validU fla",):noti e+ ! Q<,e a tion wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def re"our e" @a tion ! L tion.find%param"):id+( @re"our e" ! )+ if re@ue"t.po"tU [[ Wparam"):#eyword"+.blan#U @re"our e" ! 2e"our e.find%:allJ : ondition" !. )Hname li#e U or filename li#e UHJ QY?Sparam"):#eyword"+TYQJ QY?Sparam"):#eyword"+TYQ+( end end def addAre"our e" @a tion ! L tion.find%param"):id+( exi"tingAre"our eAid" ! @a tion.re"our e". olle t S Mre"our eM re"our e.id T newAre"our eAid" ! param"):re"our e"+.re-e t S MidM exi"tingAre"our eAid".in ludeU%id.toAi( T @a tion.re"our e" KK 2e"our e.find%newAre"our eAid"( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id
**2
end def deleteAre"our e @a tion ! L tion.find%param"):id+( @a tion.re"our e".delete%2e"our e.find%param"):re"our eAid+(( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ end end
e""fully deletedQ
A ao cria a acima "a> uma busca basea a nos parLmetros que passamos6 como voc@ po e ver pelo resulta o abai0o Snote a .RLTA
+ uso
e roteamentos similares a esse nos permite criar .RLs que po ero ser "acilmente i enti"ica as e e permalin8s6 ou se=a6 lin8s que i enti"icam
e u>i as pelo usurio. ?sso J especialmente Ptil na criao unicamente um recurso e no mu am. /sse tipo e customi>ao J especialmente Ptil para classes
e"inem posts em um blo#. /0iste ain a uma outra classe e roteamentos que so chama os e name
roteamento J chama o assim porque o resulta o J uma mJto o com um nome especI"ico que po e ser invoca o e controllers e vieGs. .m e0emplo seria mo i"icar o nosso arquivo e roteamentos paraA
L tion4ontroller::2outing::2oute".draw do MmapM ? <,e priority i" ba"ed upon order of reation: fir"t
? >ample of regular route: ? map. onne t Qprodu t"/:idQJ : ontroller !. Q atalogQJ :a tion !. QviewQ ? Ieep in mind you an a""ign value" ot,er t,an : ontroller and :a tion ? >ample of named route: ? map.pur ,a"e Qprodu t"/:id/pur ,a"eQJ : ontroller !. Q atalogQJ :a tion !. Qpur ,a"eQ ? <,i" route an be invo#ed wit, pur ,a"eAurl%:id !. produ t.id( ? Fou an ,ave t,e root of your "ite routed by ,oo#ing up QQ ? -- -u"t remember to delete publi /index.,tml. map.,ome QQJ : ontroller !. Q,omeQ ? Lllow downloading 0eb >ervi e 0>GD a" a file wit, an exten"ion ? in"tead of a file named Qw"dlQ map. onne t Q: ontroller/"ervi e.w"dlQJ :a tion !. Qw"dlQ map. onne t Q:year/:mont,/:dayQJ : ontroller !. Qa tion"QJ :a tion !. QbyAdateQJ :re@uirement" !. S :year !. /EdS$T/J :day !. /EdS1J&T/J :mont, !. /EdS1J&T/ T ? 7n"tall t,e default route a" t,e lowe"t priority. map. onne t Q: ontroller/:a tion/:idQ end
esse nome. Di#amos6 por e0emplo6 que queremos a#ora a icionar uma lin8 para a home nossa cabealho. Po erIamos "a>er al#o assim em nosso la,out primrioA
K,tml. K,ead. Ktitle.X<GK/title. KY! "tyle",eetAlin#Atag QdefaultQ Y. KY! "tyle",eetAlin#Atag Q" affoldQ Y. KY! -ava" riptAin ludeAtag :default" Y. K/,ead. Kbody. K,1 id!Q,eaderQ.KY! lin#Ato QX<GQJ ,omeAurl Y.K/,1. KY if "e""ionAu"er Y. Kp id!Qlogin-informationQ. Fou are logged in a" K"trong.KY! "e""ionAu"er.name Y.K/"trong.. KY! lin#Ato QDog outQJ : ontroller !. QloginQJ :a tion !. QlogoutQ Y.. K/p.
*'1
KY end Y. KY! imageAtag%Qindi ator.gifQJ :id !. Qindi atorQJ :"tyle !. Qfloat: rig,t5 margin: 5px5 di"play: noneQ( Y. Kul id!QmenuQ. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 4ontext"QJ : ontroller !. Q ontext"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 =ro-e t"QJ : ontroller !. Qpro-e t"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 L tion"QJ : ontroller !. Qa tion"QJ :a tion !. Qli"tQ Y.K/li. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 2e"our e"QJ : ontroller !. Qre"our e"QJ :a tion !. Qli"tQ Y.K/li. KY if "e""ionAu"er [[ "e""ionAu"er.adminU Y. Kli.KY! lin#AtoAunle""A urrent Q[ra@uo5 O"er"QJ : ontroller !. Qu"er"QJ :a tion !. Qli"tQ Y.K/li. KY end Y. K/ul. Kdiv id!Q ontent"Q.KY! yield Y.K/div. K/body. K/,tml.
Duan o
escreven o o mesmov
com o su"i0o 0url6 como voc@ po e ver acima. /ssa J mais uma "acili a e o"ereci a pelo "rameGor8 para "acilitar o trabalho o esenvolve or. Para completar a nossa mo i"icao6 basta inserir o se#uinte "ra#mento em nosso arquivo default.cssA
Po e no parecer muito a princIpio6 mas6 combina o isso com parLmetros6 temos al#o bastante so"istica o. )e=a o e0emplo abai0o6 mostran o um arquivo routes.rb hipotJticoA
L tion4ontroller::2outing::2oute".draw do map.wit,Aoption" : ontroller !. QblogQ blog.",ow QQJ :a tion blog.delete Qdelete/:idQJ :a tion blog.edit Qedit/:idQJ :a tion end map. onne t H: ontroller/:a tion/:view end
*'*
Como po emos ver6 isso realmente po e "acilitar a nossa vi a. + Pnico inconveniente J a poluio
e nosso
namespace com
e>enas
e mJto os
CACHING
Duan o mencionamos otimi>a&es anteriormente6 in icamos o "ato e que e0istem solu&es pr<prias no Rails para al#uns problemas comuns. .ma ve> que sua aplicao este=a termina a e voc@ tenha i enti"ica o pontos que po em se bene"iciar e a=ustes e per"ormance6 al#umas solu&es a as pelo Rails po em ser e bastante a=u a. .ma partes essas solu&es J o mecanismo e cachin# e0istente no Rails6 que permite que p#inas inteiras ou e
e p#inas se=am ren eri>a as e #uar a as como prontas para e0ibio "utura sem necessi a e
e0ecutar o c< i#o responsvel pela mesma novamente atJ que ela se=a invali a a por al#uma mu ana qualquer. + mecanismo p#ina. Apesar isso6 um os #ran es problemas e se usar cachin# em Rails J que J relativamente i"Icil acertar as op&es corretas sem testes cui a osos. /0istem tr@s tipos principais e caches no Rails e ca a um tem suas vanta#ens e esvanta#ens. A presente seo "ornece uma viso #eral e como voc@ po e usar esses mJto os sem muitas complica&es6 apontan o tambJm os problemas e ca a um. 4o=e6 com o crescimento o uso e e cachin# possui uma sJrie e "uncionali a es que permitem um bom #rau e um cache epen en o e "le0ibili a e
cache em aplica&es6 plu#ins esto sur#in o para corri#ir al#uns os problemas e0istentes o Rails6 mas h
ain a al#uns problemas que e0i#em uma apro0imao mais manual. Para testar essas op&es6 vamos comear "a>en o o cache list o controller actions como e0emplo inicial. Antes e precisamos habilit;lo no mo o e uma "ra#mento e p#ina. )amos usar a ao e usarmos o mecanismo e cachin#6 porJm6
? >etting" "pe ified ,ere will ta#e pre eden e over t,o"e in
onfig/environment.rb
? 7n t,e development environment your appli ationH" ode i" reloaded on ? every re@ue"t. <,i" "low" down re"pon"e time but i" perfe t for development ? "in e you donHt ,ave to re"tart t,e web"erver w,en you ma#e ode ,ange". onfig. a ,eA la""e" ! fal"e ? Dog error me""age" w,en you a onfig.w,inyAnil" ! true identally all met,od" on nil. onne t" to
? >,ow full error report" and di"able a ,ing onfig.a tionA ontroller. on"iderAallAre@ue"t"Alo al ! true
*''
onfig.a tionA ontroller.performA a ,ing onfig.a tionAview. a ,eAtemplateAexten"ion" onfig.a tionAview.debugAr-" ? GonHt are if t,e mailer anHt "end onfig.a tionAmailer.rai"eAdeliveryAerror" ! fal"e
Reinicie o servi or para que as mo i"ica&es tenham e"eito. A#ora po emos mo i"icar a nossa vieG para ter al#um cacheA
K,1.L tion"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew L tionQJ :a tion !. QeditQ Y.K/p. KY a ,e do Y. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.Ge" riptionK/t,. Kt,.4ompletedK/t,. Kt,.0,enK/t,. Kt,.=ro-e tK/t,. Kt,.4ontextK/t,. Kt,.L tion"K/t,. K/tr. KY @a tion".ea , do Ma tionM Y. Ktr. Ktd.KY! a tion.de" ription Y.K/td. Ktd.KY! ye"AorAnoU%a tion.doneU( Y.K/td. Ktd.KY! %a tion.doneU( U a tion. ompletedAat."trftime%QYm/Yd/YyQ( : Q-Q Y.K/td. Ktd.KY! a tion.pro-e t.name Y.K/td. Ktd.KY! a tion. ontext.name Y.K/td. Ktd nowrap. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. a tion Y. orKbr. KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. a tion TJ : onfirm !. QLre you "ureUQ Y. orKbr. KY! lin#Ato Q1dit 2e"our e"QJ :a tion !. Qre"our e"QJ :id !. a tion Y. K/td. K/tr. KY end Y. K/table. KY end Y. Kp.KY! paginationAlin#"%@a tionApage"( Y.K/p.
e um "ra#mento
iret<rio o
tmp/cache/sob nossa aplicao6 veremos que arquivos "oram cria os para conter o ren eri>ao pronta
-e a icionarmos um novo ob=eto ao banco a#ora6 veremos um "ato curiosoA ele no aparece em nossa lista#em. +utra coisa que notaremos J que6 se lo#armos como um outro usurio6 veremos as a&es primeiro. ?sso acontece porque o cache no "oi renova o no momento em que o banco so"reu mo i"ica&es. Po emos corri#ir o primeiro problema e0piran o o cache ao inserirmos6 mo i"icarmos ou e0cluirmos um o
*'4
ob=eto. .m arquivo
la"" L tion"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @a tionApage"J @a tion" ! paginate :a tion"J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ end def byAdate @a tionApage"J @a tion" ! paginate :a tion"J : ondition" !. )Qa tion". reatedAat ! UQJ Gate.new%param"):year+.toAiJ param"):mont,+.toAiJ param"):day+.toAi(+J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ render :a tion !. Qli"tQ end def edit @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if re@ue"t.getU if param"):id+ @item ! L tion.find%param"):id+( el"e @item ! L tion.new end el"if re@ue"t.po"tU @item ! param"):id+ U L tion.update%param"):id+J param"):item+( : L tion. reate%param"):item+( if @item.validU expireAfragment%:a tion !. Qli"tQ( fla",):noti e+ ! Q<,e a tion wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def re"our e" @a tion ! L tion.find%param"):id+( @re"our e" ! )+ if re@ue"t.po"tU [[ Wparam"):#eyword"+.blan#U @re"our e" ! 2e"our e.find%:allJ : ondition" !. )Hname li#e U or filename li#e UHJ QY?Sparam"):#eyword"+TYQJ QY?Sparam"):#eyword"+TYQ+( end end def addAre"our e" @a tion ! L tion.find%param"):id+( exi"tingAre"our eAid" ! @a tion.re"our e". olle t S Mre"our eM re"our e.id T newAre"our eAid" ! param"):re"our e"+.re-e t S MidM exi"tingAre"our eAid".in ludeU%id.toAi( T @a tion.re"our e" KK 2e"our e.find%newAre"our eAid"( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def deleteAre"our e @a tion ! L tion.find%param"):id+( @a tion.re"our e".delete%2e"our e.find%param"):re"our eAid+(( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def delete L tion.find%param"):id+(.de"troy
*'5
fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ expireAfragment%:a tion !. Qli"tQ( end end
e""fully deletedQ
/ssas mo i"ica&es "oram o cache a ser renova o quan o h mu anas nos a#ora J possIvel ver que a p#ina J atuali>a6 embora persista o problema para outro usurio. Para corri#ir o problema6 precisamos e uas mu anas. Primeiro em nossa nossa vieG6 on e terIamos al#o assimA e
K,1.L tion"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew L tionQJ :a tion !. QeditQ Y.K/p. KY a ,e%:a tion !. Qli"tQJ :id !. "e""ion):u"er+( do Y. Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.Ge" riptionK/t,. Kt,.4ompletedK/t,. Kt,.0,enK/t,. Kt,.=ro-e tK/t,. Kt,.4ontextK/t,. Kt,.L tion"K/t,. K/tr. KY @a tion".ea , do Ma tionM Y. Ktr. Ktd.KY! a tion.de" ription Y.K/td. Ktd.KY! ye"AorAnoU%a tion.doneU( Y.K/td. Ktd.KY! %a tion.doneU( U a tion. ompletedAat."trftime%QYm/Yd/YyQ( : Q-Q Y.K/td. Ktd.KY! a tion.pro-e t.name Y.K/td. Ktd.KY! a tion. ontext.name Y.K/td. Ktd nowrap. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. a tion Y. orKbr. KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. a tion TJ : onfirm !. QLre you "ureUQ Y. orKbr. KY! lin#Ato Q1dit 2e"our e"QJ :a tion !. Qre"our e"QJ :id !. a tion Y. K/td. K/tr. KY end Y. K/table. KY end Y. Kp.KY! paginationAlin#"%@a tionApage"( Y.K/p.
/0plicitan o o "ra#mento que estamos #ravan o em cache e in"orman o o i e nossas aplicao sem mais
capa> e #erar "ra#mentos customi>a os para ca a pessoa acessan o o sistema6 aumentan o a per"ormance o que al#umas pequenas mo i"ica&es. .ma lista#em Rails #rava o cache releva o se#uinte6 epois e al#umas invoca&esA
*'1
-rw-r--r-- 1 ronaldo ronaldo 165R &''*-1'-'1 '&:'& 1. a ,e -rw-r--r-- 1 ronaldo ronaldo 1188 &''*-1'-'1 '&:'1 &. a ,e
Como po emos ver6 h um arquivo para ca a usurio no cache. A#ora precisamos mu ar a "orma como e0piramos "ra#mentos6 alteran o nosso controllerA
la"" L tion"4ontroller K Lppli ation4ontroller def index li"t render :a tion !. Qli"tQ end def li"t @a tionApage"J @a tion" ! paginate :a tion"J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ end def byAdate @a tionApage"J @a tion" ! paginate :a tion"J : ondition" !. )Qa tion". reatedAat ! UQJ Gate.new%param"):year+.toAiJ param"):mont,+.toAiJ param"):day+.toAi(+J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ render :a tion !. Qli"tQ end def edit @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if re@ue"t.getU if param"):id+ @item ! L tion.find%param"):id+( el"e @item ! L tion.new end el"if re@ue"t.po"tU @item ! param"):id+ U L tion.update%param"):id+J param"):item+( : L tion. reate%param"):item+( if @item.validU expireAfragment%:a tion !. Qli"tQJ :id !. "e""ion):u"er+( fla",):noti e+ ! Q<,e a tion wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def re"our e" @a tion ! L tion.find%param"):id+( @re"our e" ! )+ if re@ue"t.po"tU [[ Wparam"):#eyword"+.blan#U @re"our e" ! 2e"our e.find%:allJ : ondition" !. )Hname li#e U or filename li#e UHJ QY?Sparam"):#eyword"+TYQJ QY?Sparam"):#eyword"+TYQ+( end end def addAre"our e" @a tion ! L tion.find%param"):id+( exi"tingAre"our eAid" ! @a tion.re"our e". olle t S Mre"our eM re"our e.id T newAre"our eAid" ! param"):re"our e"+.re-e t S MidM exi"tingAre"our eAid".in ludeU%id.toAi( T @a tion.re"our e" KK 2e"our e.find%newAre"our eAid"( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def deleteAre"our e @a tion ! L tion.find%param"):id+(
*'!
@a tion.re"our e".delete%2e"our e.find%param"):re"our eAid+(( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u e""fully deletedQ redire tAto :a tion !. Qli"tQ expireAfragment%:a tion !. Qli"tQJ :id !. "e""ion):u"er+( end end
acessvamos os a os com um usurio i"erente. -e voc@ a icionar mais o que um certo nPmero chama as. A soluo J similar tambJm. Primeiro6 mo i"icamos a nossa vieGA
para que a pa#inao aparea ver que a primeira lista#em em cache ser retorna a para to as as
K,1.L tion"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew L tionQJ :a tion !. QeditQ Y.K/p. KY a ,e%:a tion !. Qli"tQJ :id !. "e""ion):u"er+J :page !. page( do Y.
Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.Ge" riptionK/t,. Kt,.4ompletedK/t,. Kt,.0,enK/t,. Kt,.=ro-e tK/t,. Kt,.4ontextK/t,. Kt,.L tion"K/t,. K/tr. KY @a tion".ea , do Ma tionM Y. Ktr. Ktd.KY! a tion.de" ription Y.K/td. Ktd.KY! ye"AorAnoU%a tion.doneU( Y.K/td. Ktd.KY! %a tion.doneU( U a tion. ompletedAat."trftime%QYm/Yd/YyQ( : Q-Q Y.K/td. Ktd.KY! a tion.pro-e t.name Y.K/td. Ktd.KY! a tion. ontext.name Y.K/td. Ktd nowrap. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. a tion Y. orKbr. KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. a tion TJ : onfirm !. QLre you "ureUQ Y. orKbr. KY! lin#Ato Q1dit 2e"our e"QJ :a tion !. Qre"our e"QJ :id !. a tion Y. K/td. K/tr. KY end Y. K/table. Kp.KY! paginationAlin#"%@a tionApage"( Y.K/p. KY end Y.
a os
iret<rio
ronaldo@minerva:~/tmp/gtd/tmp/ a ,e/lo al,o"t.3'''/a tion"/li"t$ l" -l total 1& -rw-r--r-- 1 ronaldo ronaldo &'R* &''*-1'-'1 1':'R 1.page!1. a ,e -rw-r--r-- 1 ronaldo ronaldo *'& &''*-1'-'1 1':'R 1.page!&. a ,e -rw-r--r-- 1 ronaldo ronaldo 1&51 &''*-1'-'1 1':'R &.page!1. a ,e
A#ora s< precisamos alterar o nosso controller para e0pirar to as as p#inas relaciona as ao usurio se houver uma mu ana e intro u>ir o mJto o page que usamosA
la"" L tion"4ontroller K Lppli ation4ontroller ,elperAmet,od :page def index li"t render :a tion !. Qli"tQ end def li"t @a tionApage"J @a tion" ! paginate :a tion"J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ end def byAdate @a tionApage"J @a tion" ! paginate :a tion"J : ondition" !. )Qa tion". reatedAat ! UQJ Gate.new%param"):year+.toAiJ param"):mont,+.toAiJ param"):day+.toAi(+J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ render :a tion !. Qli"tQ end def edit @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if re@ue"t.getU if param"):id+ @item ! L tion.find%param"):id+( el"e @item ! L tion.new end el"if re@ue"t.po"tU @item ! param"):id+ U L tion.update%param"):id+J param"):item+( : L tion. reate%param"):item+( if @item.validU expireAfragment%/a tion"E/li"tE/?S"e""ion):u"er+T%.P(/( fla",):noti e+ ! Q<,e a tion wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def re"our e" @a tion ! L tion.find%param"):id+( @re"our e" ! )+ if re@ue"t.po"tU [[ Wparam"):#eyword"+.blan#U @re"our e" ! 2e"our e.find%:allJ : ondition" !. )Hname li#e U or filename li#e UHJ QY?Sparam"):#eyword"+TYQJ QY?Sparam"):#eyword"+TYQ+( end end def addAre"our e" @a tion ! L tion.find%param"):id+( exi"tingAre"our eAid" ! @a tion.re"our e". olle t S Mre"our eM re"our e.id T newAre"our eAid" ! param"):re"our e"+.re-e t S MidM exi"tingAre"our eAid".in ludeU%id.toAi( T @a tion.re"our e" KK 2e"our e.find%newAre"our eAid"(
*'2
redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def deleteAre"our e @a tion ! L tion.find%param"):id+( @a tion.re"our e".delete%2e"our e.find%param"):re"our eAid+(( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u e""fully deletedQ redire tAto :a tion !. Qli"tQ expireAfragment%/a tion"E/li"tE/?S"e""ion):u"er+T%.P(/( end prote ted def page param"):page+ MM 1 end end
.samos aqui uma e0presso re#ular6 customi>a a por usurio6 para remover qualquer arquivo e cache que este=a relaciona o ao mesmo. Bo po emos usar iretamente o parLmetro page porque o mJto o expire0fragment6 quan o usa o com parLmetros livres6 opera sobre "ra#mentos especI"icos. +bviamente6 se voc@ est customi>an o o seu cache a esse ponto6 tenha certe>a e que J isso o que voc@
precisa realmente porque6 caso contrrio6 ao invJs e iminuir a car#a no servi or6 voc@ estar aumentan o a mesma6 consi eran o que o Rails passa a ter que controlar e>enas ou centenas e p#inas em cache. De uma "orma #eral6 no J interessante "a>er cache basea o em parLmetros que mu am "reqRentementeH principalmente no caso e buscasHmas a anlise eve ser "eita caso a caso. A esvanta#em e usar cache por "ra#mentos J que ele se aplica somente a vieGs. -e voc@ observar o lo# o servi or urante a ren eri>ao a ao6 ver que a chama a ao banco ain a J "eita mesmo que a p#ina = este=a em cache.
=ro e""ing L tion"4ontroller?li"t %for 1&R.'.'.1 at &''*-1'-'1 11:'':''( )X1<+ >e""ion 7G: $$*5R$ *'56de16&$ 6b &b1 bR33R$e =arameter": SQa tionQ!.Qli"tQJ Q ontrollerQ!.Qa tion"QT L tion 4ount %'.''*R&3( >1D14< 4:O8<%G7><784< a tion".id( 92:B a tion" D19< :O<12 ]:78 pro-e t" :8 pro-e t".id ! a tion".pro-e tAid D19< :O<12 ]:78 ontext" :8 ontext".id ! a tion". ontextAid 0;121 %a tion".u"erAid ! 1( L tion 4olumn" %'.''&$8&( >;:0 971DG> 92:B a tion" =ro-e t 4olumn" %'.''&1R&( >;:0 971DG> 92:B pro-e t" 4ontext 4olumn" %'.''1*3&( >;:0 971DG> 92:B ontext" L tion Doad 7n luding L""o iation" %'.''&83'( >1D14< a tion".\id\ L> t'Ar'J a tion".\de" ription\ L> t'Ar1J a tion".\done\ L> t'Ar&J a tion".\ reatedAat\ L> t'Ar3J a tion".\ ompletedAat\ L> t'Ar$J a tion".\ ontextAid\ L> t'Ar5J a tion".\pro-e tAid\ L> t'Ar*J a tion".\u"erAid\ L> t'ArRJ a tion".\po"ition\ L> t'Ar8J pro-e t".\id\ L> t1Ar'J pro-e t".\name\ L> t1Ar1J pro-e t".\de" ription\ L> t1Ar&J pro-e t".\a tive\ L> t1Ar3J pro-e t".\u"erAid\ L> t1Ar$J ontext".\id\ L> t&Ar'J ontext".\name\ L> t&Ar1J ontext".\u"erAid\ L> t&Ar& 92:B a tion" D19< :O<12 ]:78 pro-e t" :8 pro-e t".id ! a tion".pro-e tAid D19< :O<12 ]:78 ontext" :8 ontext".id ! a tion". ontextAid 0;121 %a tion".u"erAid ! 1( :2G12 /F a tion".de" ription D7B7< 'J 5 2endering wit,in layout"/appli ation 2endering a tion"/li"t 9ragment read: lo al,o"t:3'''/a tion"/li"t/1Upage!1 %'.'''&1(
*4$
O"er Doad %'.''1''3( >1D14< P 92:B u"er" 0;121 %id ! 1( D7B7< 1 O"er 4olumn" %'.''3&'8( >;:0 971DG> 92:B u"er" 4ompleted in '.'$&'8 %&3 re@"/"e ( M 2endering: '.''6&& %&1Y( M G/: '.'&''5 %$RY( M &'' :I ),ttp://lo al,o"t/a tion"/li"t+
Como J "cil perceber6 mesmo com a leitura e um "ra#mento pelo mecanismo e cachin#6 o banco e a os continua a ser acessa o. Bo e0iste uma maneira pa ro e resolver isso6 especialmente quan o estamos li an o com "ra#mentos que epen em e vrias parLmetrosHcomo J o nosso caso no momento. .ma soluo possIvel ser customi>ar a invocao ao banco e mo o que ela s< ocorra quan o necessrio6 ou se=a6 quan o o "ra#mento no "or li o. .m e0emplo isso seria "a>er as mo i"ica&es vistas em se#ui a. Primeiro6 mo i"icamos a vieG para no usar variveis e instLncia6 mas usar mJto os6 permitin o um maior controle sobre a saI aA
K,1.L tion"K/,1. KY if fla",):noti e+ Y. Kp "tyle!Q olor: green5 font-"tyle: itali Q.KY! fla",):noti e+ Y.K/p. KY end Y. Kp.KY! lin#Ato Q8ew L tionQJ :a tion !. QeditQ Y.K/p. KY a ,e%:a tion !. Qli"tQJ :id !. "e""ion):u"er+J :page !. page( do Y.
Ktable border!Q1Q ellpadding!Q5Q ell"pa ing!Q1Q. Ktr. Kt,.Ge" riptionK/t,. Kt,.4ompletedK/t,. Kt,.0,enK/t,. Kt,.=ro-e tK/t,. Kt,.4ontextK/t,. Kt,.L tion"K/t,. K/tr. KY a tion".ea , do Ma tionM Y. Ktr. Ktd.KY! a tion.de" ription Y.K/td. Ktd.KY! ye"AorAnoU%a tion.doneU( Y.K/td. Ktd.KY! %a tion.doneU( U a tion. ompletedAat."trftime%QYm/Yd/YyQ( : Q-Q Y.K/td. Ktd.KY! a tion.pro-e t.name Y.K/td. Ktd.KY! a tion. ontext.name Y.K/td. Ktd nowrap. KY! lin#Ato Q1ditQJ :a tion !. QeditQJ :id !. a tion Y. orKbr. KY! lin#Ato QGeleteQJ S :a tion !. QdeleteQJ :id !. a tion TJ : onfirm !. QLre you "ureUQ Y. orKbr. KY! lin#Ato Q1dit 2e"our e"QJ :a tion !. Qre"our e"QJ :id !. a tion Y. K/td. K/tr. KY end Y. K/table. Kp.KY! paginationAlin#"%a tionApage"( Y.K/p. KY end Y.
*41
,elperAmet,od :page ,elperAmet,od :a tion"J :a tionApage" def index li"t render :a tion !. Qli"tQ end def li"t end def byAdate @a tionApage"J @a tion" ! paginate :a tion"J : ondition" !. )Qa tion". reatedAat ! UQJ Gate.new%param"):year+.toAiJ param"):mont,+.toAiJ param"):day+.toAi(+J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ render :a tion !. Qli"tQ end def edit @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if re@ue"t.getU if param"):id+ @item ! L tion.find%param"):id+( el"e @item ! L tion.new end el"if re@ue"t.po"tU @item ! param"):id+ U L tion.update%param"):id+J param"):item+( : L tion. reate%param"):item+( if @item.validU expireAfragment%/a tion"E/li"tE/?S"e""ion):u"er+T%.P(/( fla",):noti e+ ! Q<,e a tion wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def re"our e" @a tion ! L tion.find%param"):id+( @re"our e" ! )+ if re@ue"t.po"tU [[ Wparam"):#eyword"+.blan#U @re"our e" ! 2e"our e.find%:allJ : ondition" !. )Hname li#e U or filename li#e UHJ QY?Sparam"):#eyword"+TYQJ QY?Sparam"):#eyword"+TYQ+( end end def addAre"our e" @a tion ! L tion.find%param"):id+( exi"tingAre"our eAid" ! @a tion.re"our e". olle t S Mre"our eM re"our e.id T newAre"our eAid" ! param"):re"our e"+.re-e t S MidM exi"tingAre"our eAid".in ludeU%id.toAi( T @a tion.re"our e" KK 2e"our e.find%newAre"our eAid"( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def deleteAre"our e @a tion ! L tion.find%param"):id+( @a tion.re"our e".delete%2e"our e.find%param"):re"our eAid+(( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u e""fully deletedQ redire tAto :a tion !. Qli"tQ expireAfragment%/a tion"E/li"tE/?S"e""ion):u"er+T%.P(/( end prote ted
*4*
def a tion" loadAli"ting @a tion" end def a tionApage" loadAli"ting @a tionApage" end def loadAli"ting unle"" @a tion" @a tionApage"J @a tion" ! paginate :a tion"J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ end end def page param"):page+ MM 1 end
end
Bo c< i#o acima6 removemos as invoca&es mJto o J usa o internamente pelos outros
iretamente
a ao list
a os somente se isso no tinha aconteci o antes. /sse e"ini os para serem os helpers a serem
=ro e""ing L tion"4ontroller?li"t %for 1&R.'.'.1 at &''*-1'-'1 18:'R:&$( )X1<+ >e""ion 7G: ead833R3$'&f$e '8ed11*16'd*55f3f =arameter": SQa tionQ!.Qli"tQJ Q ontrollerQ!.Qa tion"QJ QpageQ!.Q1QT 2endering wit,in layout"/appli ation 2endering a tion"/li"t 9ragment read: lo al,o"t:3'''/a tion"/li"t/1Upage!1 %'.'''31( O"er Doad %'.''183&( >1D14< P 92:B u"er" 0;121 %id ! 1( D7B7< 1 O"er 4olumn" %'.''$116( >;:0 971DG> 92:B u"er" 4ompleted in '.'3&'8 %31 re@"/"e ( M 2endering: '.'16'$ %56Y( M G/: '.''565 %18Y( M &'' :I ),ttp://lo al,o"t/a tion"/li"tUpage!1+
Po emos ver a#ora que as chama as ao banco para carre#ar a&es no so mais invoca as e0ceto quan o o
cache J e0pira o.
A soluo que escrevemos aqui obviamente no eve ser usa a in iscrimina amente. /la J meramente
uma escrio e uma estratJ#ia possIvel. .ma outra maneira e "a>er o cache e p#inas6 cria a para resolver o problema e acesso ao banco
caches0action. +s ois "uncionam e maneira similar6 "a>en o o cache e to a a p#ina. A i"erena J que no caso o primeiro6 caches0page6 a requisio nem passa pelo Rails uma ve> que a p#ina tenha i o para o
e cache J servi o
iretamente
e0ecuta os6 callbac8s no ro am e assim por iante atJ que voc@ e0pire a p#ina no cache. +bviamente o comportamento e caches0page no J Ptil para a&es que
*4'
epen em
e customi>a&es em
tempo e e0ecuo basea as em parLmetros e autentica&es. (as po e ser e0tremamente Ptil para p#inas pPblicas on e o conteP o varia pouco6 especialmente quanto as mesmas so resulta o e acessos intensivos e banco. )amos e0perimentar a#ora o mJto o caches0action com uma ao que no se=a in ivi uali>a a por usurio6 como J o pr<prio ca astro e usurios. + mJto o J aplica o no pr<prio controller6 com a escrio a ao6 como po emos ver abai0oA
la"" O"er"4ontroller K Lppli ation4ontroller a ,e"Aa tion :li"t beforeAfilter :aut,enti ateAadmini"tration def index li"t render :a tion !. Qli"tQ end def li"t @u"erApage"J @u"er" ! paginate :u"er"J :perApage !. 5J :order !. QnameQ end def edit if param"):id+ @u"er ! O"er.find%param"):id+( el"e @u"er ! O"er.new end if re@ue"t.po"tU @u"er.attribute" ! param"):u"er+ "endAemail ! @u"er.newAre ordU if @u"er."ave expireAa tion%:a tion !. Qli"tQ( if "endAemail 2egi"tration8otifier.deliverAregi"trationAnotifi ation%@u"erJ "e""ionAu"er( end fla",):noti e+ ! Q<,e u"er wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def delete O"er.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e u"er wa" "u redire tAto :a tion !. Qli"tQ expireAa tion%:a tion !. Qli"tQ( end end
e""fully deletedQ
)emos tambJm que6 como em nosso e0emplo anterior6 precisamos e uma chama a para invali ar o cache. + c< i#o acima "unciona6 mas tem estamos "a>en o um cache ois problema sJrios. Primeiro6 quan o voc@ inclui6 altera ou e0clui um o arquivo em cache. ?sso acontece porque e um "ra#mento.
re#istro6 a mensa#em #era a na varivel "lash se torna parte -e#un o6 o problema com pa#inao tambJm acontece.
*44
Benhum
esses
o mJto o caches0action/ e
porque o mesmo no suporta parLmetros que permitem customi>ar as chama as. /sse J um e0emplo como voc@ tem que ser cui a oso no uso e cachin# para no provocar estranhos e"eitos colaterais. (esmo assim6 h solu&es para ambos os problemas.
A questo as variveis "lash6 por e0emplo6 po eria ser resolvi o como uma mo i"icao upla6 que somente escreveremos aquiA primeiro6 movemos o c< i#o que e0ibe a mensa#em "lash para o nosso la,out6 o que po eria ser "eito sem pre=uI>o para a aplicaoK se#un o6 "a>emos uma invocao completo. /sse plu#in e uma e0plicao etalha a e como ele "uncionam6 alJm o cache atravJs e instru&es e um e uso6
plu#in chama o contentYcache que po e usa o para salvar somente o conteP o a vieG e no a p#ina por
po em ser encontra as no en ereoA httpAZZblo#.co ahale.comZ*$$1Z$4Z1$Zcontent;onl,;cachin#;"or;railsZ. .ma outra soluo para o mesmo problema seria escrever o seu pr<prio "iltro. + mJto o caches0actions J na a mais es"oro. Para o problema e pa#inao6 a soluo seria criar um roteamento especiali>a a que nos permitiria #erar a .RL customi>a a para ca a p#ina a lista#em e mo o que elas aparecessem para o cache com a&es i"erentes. 3astaria acrescentar al#o assim ao arquivo routes.rbA o que uma invocao a um "iltro aroun e voc@ po eria escrever um similar com bem pouco
L tion4ontroller::2outing::2oute".draw do MmapM ? <,e priority i" ba"ed upon order of reation: fir"t
? >ample of regular route: ? map. onne t Qprodu t"/:idQJ : ontroller !. Q atalogQJ :a tion !. QviewQ ? Ieep in mind you an a""ign value" ot,er t,an : ontroller and :a tion ? >ample of named route: ? map.pur ,a"e Qprodu t"/:id/pur ,a"eQJ : ontroller !. Q atalogQJ :a tion !. Qpur ,a"eQ ? <,i" route an be invo#ed wit, pur ,a"eAurl%:id !. produ t.id( ? Fou an ,ave t,e root of your "ite routed by ,oo#ing up QQ ? -- -u"t remember to delete publi /index.,tml. map.,ome QQJ : ontroller !. Q,omeQ ? Lllow downloading 0eb >ervi e 0>GD a" a file wit, an exten"ion ? in"tead of a file named Qw"dlQ map. onne t Q: ontroller/"ervi e.w"dlQJ :a tion !. Qw"dlQ map. onne t Q:year/:mont,/:dayQJ : ontroller !. Qa tion"QJ :a tion !. QbyAdateQJ :re@uirement" !. S :year !. /EdS$T/J :day !. /EdS1J&T/J :mont, !. /EdS1J&T/ T map. onne t Qu"er"/li"t/:pageQJ : ontroller !. Qu"er"QJ :a tion !. Qli"tQ ? 7n"tall t,e default route a" t,e lowe"t priority. map. onne t Q: ontroller/:a tion/:idQ end
resolven o o problema e pa#inao6 embora no o problema as variveis "lash. .m outro inconveniente o usar cache J que precisamos controlar a e0pirao as p#inas6 um trabalho que po e "icar te ioso em pouco tempo. Para resolver o problema6 o Rails possui uma espJcie "ra#mentos e acor o com a necessi a e. )amos suport que estamos usan o a estratJ#ia ousa a para a&es em nossos outros controllers. Po eriamos criar uma classe )weeper #enJrica que nos permitisse e0pirar o cache automaticamente6 sem c< i#o e0tra no controller. Como queremos uma classe #enJrica6 vamos criar um arquivo novo no chama o list0sweeper.rbA iret<rio extras6 e classes chama as )weeper que po em ser usa a para automaticamente monitorar ob=etos e e0pirar a&es e
la"" Di"t>weeper K L tion4ontroller::4a ,ing::>weeper ob"erve =ro-e tJ 4ontextJ L tionJ 2e"our e def afterA"ave%re ord( expireAre ord%re ord( end def afterAde"troy%re ord( expireAre ord%re ord( end def expireAre ord%re ord( expireAfragment%/?Sre ord. la"".tableAnameTE/li"tE/?S"e""ion):u"er+T%.P(/( end end
uas
+ mJto o obser!e6 que usamos acima6 J e"ini o na classe Cbser!er6 a qual a classe )weeper J eriva a e serve para acompanhar e "orma automtica acessos ao banco e a os "eitos por uma classe atravJs os
controllers.
*41
? /e "ure to re"tart your web "erver w,en you modify t,i" file. ? On omment below to for e 2ail" into produ tion mode w,en ? you donHt ontrol web/app "erver and anHt "et it t,e proper way ? 18Z)H2L7D>A18ZH+ MM! Hprodu tionH ? >pe ifie" gem ver"ion of 2ail" to u"e w,en vendor/rail" i" not pre"ent 2L7D>AX1BAZ12>7:8 ! H1.1.*H ? /oot"trap t,e 2ail" environmentJ framewor#"J and default re@uire 9ile.-oin%9ile.dirname%AA97D1AA(J HbootH( onfiguration
2ail"::7nitializer.run do M onfigM ? >etting" in onfig/environment"/P ta#e pre eden e t,o"e "pe ified ,ere ? >#ip framewor#" youHre not going to u"e %only wor#" if u"ing vendor/rail"( ? onfig.framewor#" -! ) :a tionAwebA"ervi eJ :a tionAmailer + ? Ldd additional load pat," for your own u"tom dir" onfig.loadApat," N! Y0% ?S2L7D>A2::<T/extra" ( ? 9or e all environment" to u"e t,e "ame logger level ? %by default produ tion u"e" :infoJ t,e ot,er" :debug( ? onfig.logAlevel ! :debug ? O"e t,e databa"e for "e""ion" in"tead of t,e file "y"tem ? % reate t,e "e""ion table wit, Hra#e db:"e""ion": reateH( ? onfig.a tionA ontroller."e""ionA"tore ! :a tiveAre ordA"tore ? O"e >CD in"tead of L tive 2e ordH" " ,ema dumper w,en reating t,e te"t databa"e. ? <,i" i" ne e""ary if your " ,ema anHt be ompletely dumped by t,e " ,ema dumperJ ? li#e if you ,ave on"traint" or databa"e-"pe ifi olumn type" ? onfig.a tiveAre ord." ,emaAformat ! :"@l ? L tivate ob"erver" t,at ",ould alway" be running ? onfig.a tiveAre ord.ob"erver" ! : a ,erJ :garbageA olle tor ? Ba#e L tive 2e ord u"e O<4-ba"e in"tead of lo al time ? onfig.a tiveAre ord.defaultAtimezone ! :ut ? >ee 2ail"::4onfiguration for more option" end ? ? ? ? ? ? ? ? Ldd new infle tion rule" u"ing t,e following format %all t,e"e example" are a tive by default(: 7nfle tor.infle tion" do Minfle tM infle t.plural /^%ox($/iJ HE1enH infle t."ingular /^%ox(en/iJ HE1H infle t.irregular Hper"onHJ HpeopleH infle t.un ountable Yw% fi", ",eep ( end
? 7n lude your appli ation onfiguration below re@uire Qu"erAfilter.rbQ re@uire Qauditing.rbQ re@uire Qa t"Aa"AdropAdown.rbQ re@uire Qli"tA"weeper.rbQ L tionBailer::/a"e."erverA"etting" ! S :addre"" !. Qmail.yourdomain. omQJ :domain !. Qyourdomain. omQJ :u"erAname !. Qu"er@yourdomain. omQJ :pa""word !. QPPPPPPQJ :aut,enti ation !. :login
*4!
Reinicie o servi or para que as mo i"ica&es tenham e"eito. A#ora mo i"icamos o nosso controller primrio6 em application.rb6 para usar a classe acimaA
la"" Lppli ation4ontroller K L tion4ontroller::/a"e beforeAfilter :aut,enti ate beforeAfilter :"etA urrentAu"erAid "#ipAbeforeAfilter :aut,enti ateAadmini"trationJ :only !. ):a aroundAfilter aroundAfilter aroundAfilter aroundAfilter O"er9ilter.new%4ontextJ :neededA" ope"( O"er9ilter.new%=ro-e tJ :neededA" ope"( O"er9ilter.new%L tionJ :neededA" ope"( O"er9ilter.new%2e"our eJ :neededA" ope"( e""Adenied+
,elperAmet,od :"e""ionAu"er a ,eA"weeper :li"tA"weeperJ :only !. ) :editJ :delete + def a e""Adenied render :template !. Q",ared/a end prote ted def "etA urrentAu"erAid O"er. urrentAu"erAid ! "e""ion):u"er+ end def "e""ionAu"er @"e""ionAu"er MM! O"er.find%:fir"tJ : ondition" !. )Hid ! UHJ "e""ion):u"er++( end def aut,enti ate unle"" "e""ion):u"er+ "e""ion):returnAto+ ! re@ue"t.re@ue"tAuri redire tAto : ontroller !. QloginQJ :a tion !. QloginQ return fal"e end return true end def aut,enti ateAadmini"tration unle"" "e""ionAu"er [[ "e""ionAu"er.adminU redire tAto :a tion !. Qa e""AdeniedQ return fal"e end return true end def neededA" ope"%targetA la""( S :find !. S : ondition" !. )Q?StargetA la"".tableAnameT.u"erAid ! UQJ "e""ion):u"er++ TJ : reate !. S :u"erAid !. "e""ion):u"er+ T T end end e""AdeniedQ
A linha acima in ica que o controller eve re#istrar um sGeeper para as a&es edit e delete. Duan o essas a&es "orem e0ecuta as6 o re#istro ser mo i"ica o6
*45
la"" L tion"4ontroller K Lppli ation4ontroller ,elperAmet,od :page ,elperAmet,od :a tion"J :a tionApage" def index li"t render :a tion !. Qli"tQ end def li"t end def byAdate @a tionApage"J @a tion" ! paginate :a tion"J : ondition" !. )Qa tion". reatedAat ! UQJ Gate.new%param"):year+.toAiJ param"):mont,+.toAiJ param"):day+.toAi(+J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ render :a tion !. Qli"tQ end def edit @ ontext" ! 4ontext.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T @pro-e t" ! =ro-e t.find%:allJ :order !. QnameQ(. olle t S MiM )i.nameJ i.id+ T if re@ue"t.getU if param"):id+ @item ! L tion.find%param"):id+( el"e @item ! L tion.new end el"if re@ue"t.po"tU @item ! param"):id+ U L tion.update%param"):id+J param"):item+( : L tion. reate%param"):item+( if @item.validU fla",):noti e+ ! Q<,e a tion wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def re"our e" @a tion ! L tion.find%param"):id+( @re"our e" ! )+ if re@ue"t.po"tU [[ Wparam"):#eyword"+.blan#U @re"our e" ! 2e"our e.find%:allJ : ondition" !. )Hname li#e U or filename li#e UHJ QY?Sparam"):#eyword"+TYQJ QY?Sparam"):#eyword"+TYQ+( end end def addAre"our e" @a tion ! L tion.find%param"):id+( exi"tingAre"our eAid" ! @a tion.re"our e". olle t S Mre"our eM re"our e.id T newAre"our eAid" ! param"):re"our e"+.re-e t S MidM exi"tingAre"our eAid".in ludeU%id.toAi( T @a tion.re"our e" KK 2e"our e.find%newAre"our eAid"( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end def deleteAre"our e @a tion ! L tion.find%param"):id+( @a tion.re"our e".delete%2e"our e.find%param"):re"our eAid+(( redire tAto :a tion !. Qre"our e"QJ :id !. @a tion.id end
*42
def delete L tion.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e a tion wa" "u redire tAto :a tion !. Qli"tQ end prote ted def a tion" loadAli"ting @a tion" end def a tionApage" loadAli"ting @a tionApage" end
e""fully deletedQ
def loadAli"ting unle"" @a tion" @a tionApage"J @a tion" ! paginate :a tion"J :in lude !. ):pro-e tJ : ontext+J :perApage !. 5J :order !. Qa tion".de" riptionQ end end def page param"):page+ MM 1 end
end
Com isso concluImos a nossa passa#em sobre a parte muito po erosa mas que
e cachin#
cachin# em
para o se#uinteA
? >etting" "pe ified ,ere will ta#e pre eden e over t,o"e in
onfig/environment.rb
? 7n t,e development environment your appli ationH" ode i" reloaded on ? every re@ue"t. <,i" "low" down re"pon"e time but i" perfe t for development ? "in e you donHt ,ave to re"tart t,e web"erver w,en you ma#e ode ,ange". onfig. a ,eA la""e" ! fal"e ? Dog error me""age" w,en you a onfig.w,inyAnil" ! true identally all met,od" on nil. onne t" to
? >,ow full error report" and di"able a ,ing onfig.a tionA ontroller. on"iderAallAre@ue"t"Alo al ! true onfig.a tionA ontroller.performA a ,ing ! fal"e onfig.a tionAview. a ,eAtemplateAexten"ion" ! fal"e onfig.a tionAview.debugAr-" ! true ? GonHt are if t,e mailer anHt "end onfig.a tionAmailer.rai"eAdeliveryAerror" ! fal"e
*5$
locali>ar nossas aplica&es em uma certa me i a. .ma #eralmente su"iciente para que voc@ possa usar outro i ioma. .m caso bsico J o os nomes as tabelas6 que J
mJto os e constantes. Caso sua aplicao no precise "uncionar em vrios i iomas simultaneamente6 isso J
eriva o automaticamente
o nome
a classe
a os.
Caso voc@ no se sinta con"ortveis em usar nomes e mo elos em in#l@s6 pre"erin o nomes em portu#u@s6 voc@ po e "a>er al#um assimA
la"" 4ontexto K L tive2e ord::/a"e "etAtableAname Q ontexto"Q "etAprimaryA#ey Q odigoA ontextoQ end
Com essas
aplicam a qualquer chama a e no o"erecem quase nenhum problema em qualquer o Rails. Bo J necessrio se preocupar com o nome eriva os o banco e
+bviamente isso no J locali>ao nem internacionali>ao no senti o bsico ser um problema. .sar o in#l@s provavelmente representa menos necessi a e aplicao6 mas no J um motivo para i"icul a es.
/ntretanto6 como menciona o acima6 al#uns pontos o Rails que esperam nomes em in#l@s po em comear a apresentar al#uns problemas. .m e0emplo "echa o e c< i#o maneira no c< i#o o RailsA isso J o mJto o error0messages0for6 que #erar um bloco e"ini o a se#uinte escreven o os erros #era os por um mo elo. /sse mJto o J
*51
def errorAme""age"Afor%ob-e tAnameJ option" ! ST( option" ! option"."ymbolizeA#ey" ob-e t ! in"tan eAvariableAget%Q@?Sob-e tAnameTQ( if ob-e t [[ Wob-e t.error".emptyU ontentAtag%QdivQJ ontentAtag% option"):,eaderAtag+ MM Q,&QJ Q?Spluralize%ob-e t.error". ountJ QerrorQ(T pro,ibited t,i" ?Sob-e tAname.toA".g"ub%QAQJ Q Q(T from being "avedQ ( N ontentAtag%QpQJ Q<,ere were problem" wit, t,e following field":Q( N ontentAtag%QulQJ ob-e t.error".fullAme""age". olle t S Mm"gM ontentAtag%QliQJ m"g( T(J QidQ !. option"):id+ MM Qerror1xplanationQJ Q la""Q !. option"): la""+ MM Qerror1xplanationQ ( el"e QQ end end
Bote que e0istem vrios problemas com a implementaoA primeiro6 o te0to embuti o ireto
"rase que in"orma se h um ou mais erros. Como esse mJto o plurali>a bem somente palavras em in#l@s Se mesmo assim so"re e vrias e0cess&esT temos mais um ponto complica o. A soluo mais <bvia seria substituir completamente o mJto o6 usan o nossa pr<pria implementao para isso. Po erIamos6 por e0emplo6 a icionar a se#uinte implementao em nosso arquivo application0helper.rbA e helper primrip6
def errorAme""age"Afor%ob-e tAnameJ option" ! ST( option" ! option"."ymbolizeA#ey" ob-e t ! in"tan eAvariableAget%Q@?Sob-e tAnameTQ( if ob-e t [[ Wob-e t.error".emptyU ontentAtag%QdivQJ ontentAtag% option"):,eaderAtag+ MM Q,&QJ Q?Spluralize%ob-e t.error". ountJ Qerro impediuQJ Qerro" impediramQ(T @ue e""e regi"tro fo""e "alvo:Q ( N ontentAtag%QpQJ Q:" "eguinte" ampo" apre"entaram problema":Q( N ontentAtag%QulQJ ob-e t.error".fullAme""age". olle t S Mm"gM ontentAtag%QliQJ m"g( T(J QidQ !. option"):id+ MM Qerror1xplanationQJ Q la""Q !. option"): la""+ MM Qerror1xplanationQ ( el"e QQ end end
7a>en o uma vali ao qualquer6 isso resolver parcialmente o nosso problema. + cabealho in"ormao e vali ao ser corri#i o6 mas no as mensa#ens automaticamente pelo Rails. .ma "orma arquivo en!ironment.rbA e contornar isso seria re e"inir o con=unto que
e nossa escreve o
internamente essas mensa#ens. ?sso po eria ser "eito acrescentan o a se#uinte mo i"icao ao "inal
*5*
@@defaultAerrorAme""age" ! S :in lu"ion !. Qnao exi"te na li"taQJ :ex lu"ion !. Q-V exi"te na li"taQJ :invalid !. Qb invVlido.QJ : onfirmation !. Qnao onfere om "ua onfirmacaoQJ :a epted !. Qdeve "er a eitoQJ :empty !. Qnao pode "er vazioQJ :blan# !. Qnao pode e"tar em bran oQJ :tooAlong !. Qb muito longo %o mVximo deve "er Yd ara tere"(QJ :tooA",ort !. Qb muito urto %o mdnimo deve "er Yd ara tere"(QJ :wrongAlengt, !. Qpo""ui o omprimento errado %o taman,o deveria "er Yd :ta#en !. Q-V foi u"ando em outro regi"troQJ :notAaAnumber !. Qnao b um nemeroQ T end end
ara tere"(QJ
e seus campos
8ome nao pode "er vazio. >en,a nao onfere om "ua onfirmacao. Dogin -V foi u"ado em outro regi"tro.
+bviamente6 isso no J muito interessante. Po erIamos melhorar essas mensa#ens um pouco "a>en o uma outra mo i"icao em nosso mJto o error0messages0for6 ei0an o mesmo assimA
def errorAme""age"Afor%ob-e tAnameJ option" ! ST( option" ! option"."ymbolizeA#ey" ob-e t ! in"tan eAvariableAget%Q@?Sob-e tAnameTQ( unle"" ob-e t.error".emptyU item" ! )+ ob-e t.error".ea , S MattributeJ me""ageM item" KK ontentAtag%QliQJ me""age( T ontentAtag%QdivQJ ontentAtag% option"):,eaderAtag+ MM Q,&QJ Q?Spluralize%ob-e t.error". ountJ Qerro impediuQJ Qerro" impediramQ(T @ue e""e regi"tro fo""e "alvo:Q ( N ontentAtag%QpQJ Q:" "eguinte" ampo" apre"entaram problema":Q( N ontentAtag%QulQJ item".-oin%QQ((J QidQ !. option"):id+ MM Qerror1xplanationQJ Q la""Q !. option"): la""+ MM Qerror1xplanationQ ( end end
/ssa implementao e0ibe somente a mensa#em pura a iciona o M vali ao. Com o mJto o acima6 mo i"icarIamos um mo elo e0emplo abai0oA e a os qualquer para e0ibir mensa#ens completas na vali ao6 como no
*5'
validate"Apre"en eAof :nameJ :me""age !. Q: nome do ontexto deve "er informadoQ validate"Apre"en eAof :u"erAidJ :me""age !. Q: u"uVrio a""o iado ao ontexto deve "er informadoQ end
provavelmente J o mais interessante por permitir que a mensa#em se=a cui a osamente cunha a para ca a campo e vali ao6 aumentan o bastante o #rau e le#ibili a e as mesmas. As altera&es acima provavelmente resolvem a maior parte a necessi a e e tra uo em suas aplica&es. Apesar isso6 talve> voc@ tenha um outro problema no uso ecorrem o uso e "ormatos i"erente e caracteres acentua os. /sses problemas e te0to entre as vrias partes e #eralmente e arma>enamento
sua aplicao6 passan o es e a representao interna o Rub, atJ a persist@ncia "inal no banco e a os. A soluo provavelmente mais acerta a para o problema J usar uma co i"icao comum para to os6 o que epen en er uso e al#uns "atores como o servi or e banco e a os que voc@ est usan o. Besse ponto6 o e .nico e com o "ormato .:7;5 seria provavelmente a escolha mais interessante. (as temos um
#ran e problema no "ato e que o Rub, no suporta .nico e nativamente. Apesar isso6 e0istem al#umas passos que voc@ po e tomar para que sua aplicao basicamente "ale
.nico e sem que o Rub, precise saber isso na maior parte os casos. + primeiro passo6 obviamente6 seria escolher um banco e a os que suporte .nico e com .:7;5 a verso 4.1. ?nclusive6 a partir
nativamente. + (,-DL6 por e0emplo6 somente tem suporte a isso a partir po e usar al#o assim para #erar suas tabelas com suporte a .:7;5A
a verso 5.$6 a instalao pa ro habilita o .:7;5 como co i"icao pa ro. /m vers&es anteriores6 voc@
reate table %
ontext"
id int not null autoAin rementJ foo var ,ar%1''( not nullJ primary #ey %id( ( type!myi"am ,ara ter "et utf85
e um "iltro em nossa aplicao para "a>er com que o Rails retorne por pa ro na e a os. Po erIamos ter al#o assim em um
la"" Lppli ation4ontroller K L tion4ontroller::/a"e beforeAfilter :"etA ,ar"et def "etA ,ar"et @re"pon"e.,eader")Q4ontent-<ypeQ+ ! Qtext/,tml5 end end ,ar"et!utf-8Q
*54
+ c< i#o acima in ica ao nave#a or que o retorno a aplicao J .:7;5. Por "im6 precisamos mu ar o nosso arquivo e con"i#urao o banco e a os para "a>er com que a
comunicao com o banco e a os tambJm se=a em .:7;5. Para o (,-DL6 a con"i#urao seria assimA
+bviamente J importante tambJm que voc@ este=a usan o um e itor que salve os seus arquivos que voc@ est #eran o em .:7;5. Caso contrrio6 o que acontecer J que qualquer te0to problemtico Sleia;se com acentosT em seus controllers6 mo els e vieGs entrar em con"lito com o "ormato #erean o p#inas que misturam aplicao "alhe. +s passos acima so capa>es caracteres as varia&es e a=u ar a resolver a maior parte os problemas com co i"icao e que nenhum e os o resto os a os6 uas ou mais co i"ica&es6 conseqRentemente "a>en o com que sua
mJto os a classe )tring/ o Rub, suporta .nico e e que qualquer uso os mesmos em te0to que contenha e b,tes por caractere presentes no .:7;5 causar problemas. (esmo um mJto o simples como length ei0ar e "uncionar. .ma soluo quase completa para isso J usar bibliotecas para isso. Por e0emplo6 usan o o se#uinte c< i#o antes e qualquer coisa em sua aplicao Sno comeo o arquivo en!ironment.rb6 por e0emploT6 #arantir um suporte bsico ao .:7;5 no RailsA
e to as as "un&es
usan o uma outra biblioteca. Para us;la6 primeira a instalamos via gemA
ronaldo@minerva:~/tmp/gtd$ gem in"tall uni ode /ul# updating Xem "our e index for: ,ttp://gem".rubyforge.org ... >u e""fully in"talled uni ode-'.1
Com essa biblioteca instala a6 po emos usar os mJto os abai0o no lu#ar os usuaisA
*55
?sso resolve quase to os os problemas que po erIamos ter. (as6 atJ que uma verso com suporte nativo a .nico e6 como est previsto6 no J possIvel ter 1$$b e a os entre o c< i#o e as emais partes
sobre o assunto emA http://wi3i.rubyonrails.com/rails/pages/.ow*oAseAnicode)trings. ?sso6 entretanto6 no tem impe i o que o Rails se=a usa o para aplica&es que no esto em in#l@s. /u venho "a>en o isso h quase escritos acima. .m Pltimo t<pico antes em termos e terminarmos essa sesso J #lobali>ao6 ou se=a6 a a aptao e uma aplicao e ois anos = e ain a no tive qualquer problema se#ui o passos similares aos
acor o com pre"er@ncias e instalao ou e usurio. Para essas tare"as6 e0istem plu#ins para o Rails que "a>em um trabalho melhor nesse tutorial tentan o qualquer coisa manualmente. Dois esses plu#ins soA %lobali>e http://plugins.radrails.org/directory/show/<N %Loc http://plugins.radrails.org/directory/show/:# )isitan o a p#ina os mesmos po e enten er mais sobre como ca a um eles "unciona e quais so suas o que po erIamos "a>er
SEGURANA
+ Rails6 por pa ro6 = "a> um bom trabalho a os em uma aplicao. /ntretanto6 o e tentar evitar os problemas mais comuns etalha al#umas icas e se#urana e esenvolve or eve estar sempre atento para no intro u>ir e se#urana que po em
ser se#ui as para #arantir uma con"iabili a e maior ao seu c< i#o. .m os maiores problemas em aplica&es Eeb J a possibili a e a os em chama as ao banco e e in=eo e -DL6 que acontece quan o e "ontes inse#uras. .m
/sse c< i#o #era a se#uinte eclarao -DL6 ou al#o pareci o pelo menos6 para um parLmetro qualquerA
*51
"ele t P from u"er" w,ere name li#e HYte"tYH5 delete P from u"er" w,ere H1!1H
e to os nossos re#istros
ataques bastantes so"istica os po eriam acontecer. -e o -DL construI o "or su"icientemente verstil6 um pouco e testes po er revelar inclusive etalhes e lo#ins e senhas em um banco e a os. A soluo para o problema acima J usar sempre parLmetros nomea os. A busca ori#inal "icaria assimA
.san o parLmetros nomea os6 qualquer caractere potencialmente peri#oso ser trata o previamente. .ma outra prtica interessante J e0trair consultas "reqRentemente usa as para mJto os6 como "i>emos al#umas ve>es ao lon#o e nosso usurio. + mJto o acima6 por e0emplo6 po eria ser trans"orma o emA
la"" O"er K L tive2e ord::/a"e def "elf."ear ,AbyAname%#eyword"( find :allJ : ondition" !. )Qname li#e UQJ QY?S#eyword"TYQ+ end end
.m outro problema6 bem similar6 J usar parLmetros em templates RQ-6 su=eitan o a aplicao a ataques Qava-cript. Por e0emplo6 o c< i#o abai0o em uma vieG usan o RQ- J vulnervelA
KY! @param")Q#eyword"Q+ Y.
-e o c< i#o acima "or envia o em resposta a uma .RL como a abai0o6 voc@ ver um alert com o valor
os
o usurioA
*5!
KY!, @param")Q#eyword"Q+ Y.
+ mJto o h6 no Rails6 trata a sua entra a trans"orman o qualquer caractere problemtico em seu equivalente 4:(L6 prevenin o a maior partes os problemas. + c< i#o acima po e ser usa o tanto em
la"" O"er"4ontroller K Lppli ation4ontroller beforeAfilter :aut,enti ateAadmini"tration def index li"t render :a tion !. Qli"tQ end def li"t @u"erApage"J @u"er" ! paginate :u"er"J :perApage !. 5J :order !. QnameQ end def edit if param"):id+ @u"er ! O"er.find%param"):id+( el"e @u"er ! O"er.new end if re@ue"t.po"tU @u"er.attribute" ! param"):u"er+ "endAemail ! @u"er.newAre ordU if @u"er."ave expireAa tion%:a tion !. Qli"tQ( if "endAemail 2egi"tration8otifier.deliverAregi"trationAnotifi ation%@u"erJ "e""ionAu"er( end fla",):noti e+ ! Q<,e u"er wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end def delete O"er.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e u"er wa" "u redire tAto :a tion !. Qli"tQ expireAa tion%:a tion !. Qli"tQ( end end
e""fully deletedQ
a incluso
a osT6
o sistema. ?sso
*55
e0ibIssemos uma cai0a e seleo para esse atributo6 um atacante malicioso po eria simplesmente criar um p#ina que enviasse isso por ele6 evitan o tranquilamente qualquer o nosso pobre mJto o obscurecimento. A soluo J mo i"icar tanto o nosso mo elo para evitar atribui&es iretasA
la"" O"er K L tive2e ord::/a"e attrAa e""or : urrentAu"erAid eAof eAof eAof eAof :name :login :pa""word :email
Com essa mo i"icao6 nossa ao pPblica no seria mais capa> in icasse acesso a ministrativo ao banco para al#o assimA e
la"" O"er"4ontroller K Lppli ation4ontroller beforeAfilter :aut,enti ateAadmini"tration def index li"t render :a tion !. Qli"tQ end def li"t @u"erApage"J @u"er" ! paginate :u"er"J :perApage !. 5J :order !. QnameQ end def edit if param"):id+ @u"er ! O"er.find%param"):id+( el"e @u"er ! O"er.new end if re@ue"t.po"tU @u"er.attribute" ! param"):u"er+ @u"er.admin ! param"):u"er+):admin+ "endAemail ! @u"er.newAre ordU if @u"er."ave expireAa tion%:a tion !. Qli"tQ( if "endAemail 2egi"tration8otifier.deliverAregi"trationAnotifi ation%@u"erJ "e""ionAu"er( end fla",):noti e+ ! Q<,e u"er wa" "u e""fully "avedQ redire tAto :a tion !. Qli"tQ end end end
*52
def delete O"er.find%param"):id+(.de"troy fla",):noti e+ ! Q<,e u"er wa" "u redire tAto :a tion !. Qli"tQ expireAa tion%:a tion !. Qli"tQ( end end
e""fully deletedQ
:o as as mo i"ica&es que vimos atJ a#ora so bem simples6 mas o uso as mesmas po e evitar incontveis problemas no esenvolvimento e uma aplicao Eeb.
UNIT TESTING
/mbora tenhamos i#nora o esse assunto mais importantes urante to o o nosso tutorial6 in iscutivelmente uma as partes e qualquer aplicao Rails so os testes que evem ser manti os sobre a mesma. .nit
testin# J uma parte "un amental a meto olo#ia /0treme Pro#rammin#6 cu=as "iloso"ias "ormam a base e
muitas ecis&es toma as no Rails. A pe ra e esquina a meto olo#ia /0treme Pro#rammin# S9PT J o teste e uni a e Sunit testT6 ou como
al#uns o chamam6 teste unitrio. .m teste6 na meto olo#ia 9P J uma as maneiras e saber se o c< i#o que "oi escrito se con"orma aos requerimentos posteriormente M criao Por causa o mesmo e6 mais ain a6 se mu anas intro u>i as esenvolvimento a partir os o mesmo no invali aram c< i#o = e0istente6 intro u>in o bu#s na aplicao.
testes6 e"inin o as con i&es se#un o as quais o c< i#o a ser esenvolvi o eve ser cria o. + racional para isso J o "ato simples e que6 se um teste ra>oavelmente completo passa6 o c< i#o escrito provavelmente est correto. + ob=etivo esse tutorial no J o"erecer uma intro uo M meto olo#ia 9P6 mas mostrar6 pelo menos o esviar a nossa ateno
inicialmente6 como a mesma po e ser aplica a ao Rails no que concerne a testes. Para prop<sitos tutorial6 os testes "oram i#nora os atJ o momento porque intro u>i;los si#ni"icaria a emonstrao o Rails em si. -e voc@ recomen a osA http://en.wi3ipedia.org/wi3i/Anit0testing http://en.wi3ipedia.org/wi3i/*est'dri!en0de!elopment http://c;.com/cgi/wi3i?*estLri!enLe!elopment http://www.xprogramming.com/testfram.htm + Rails o"erecia6 atJ a verso 1.16 uas "ormas principais e testesA testes e
e uni a e6 = menciona os
associa&es e quaisquer outros a en os ao bsico "uncionam correstamente. +s Pltimos testam se a "uncionali a e bsica a aplicao6 como presente em controllers e vieGs6 est correta. .m novo tipo e testes "oi intro u>i o com o Rails 1.16 que so os testes
*1$
e partes conecta as e
os
a mesma "orma que os testes "uncionais so uma e0tenso e uni a e veri"icam se as classes
veri"icam se as inter epen @ncias entre as mesmas tambJm esto corretasHsen o que estas veri"ica&es so "eitas sobre controllers e vieGs tambJm.
Para "a>er uso e testes6 o Rails utili>a a biblioteca test/unit6 que vem por pa ro com o Rub,. A biblioteca J transparentemente incluI a no Rails e utili>a a tambJm e maneira bem transparente. A maioria os coman os e #erao que vimos atJ o momento cria automaticamente testes para ca a pea intro u>i a na aplicao. Para testar completamente uma aplicao em Rails6 usamos o coman o abai0oA
ra#e te"t
ronaldo@minerva:~/tmp/gtd$ ra#e te"t %in /,ome/ronaldo/tmp/gtd( /u"r/bin/ruby1.8 -7lib:te"t Q/u"r/lib/ruby/gem"/1.8/gem"/ra#e-'.R.1/lib/ra#e/ra#eAte"tAloader.rbQ Qte"t/unit/pro-e tAte"t.rbQ Qte"t/unit/a tionAte"t.rbQ Qte"t/unit/ ontextAte"t.rbQ Qte"t/unit/u"erAte"t.rbQ Qte"t/unit/re"our eAte"t.rbQ Qte"t/unit/regi"trationAnotifierAte"t.rbQ Qte"t/unit/auditAte"t.rbQ Doaded "uite /u"r/lib/ruby/gem"/1.8/gem"/ra#e-'.R.1/lib/ra#e/ra#eAte"tAloader >tarted ....... 9ini",ed in '.1'*8R3 "e ond". R te"t"J R a""ertion"J ' failure"J ' error" /u"r/bin/ruby1.8 -7lib:te"t Q/u"r/lib/ruby/gem"/1.8/gem"/ra#e-'.R.1/lib/ra#e/ra#eAte"tAloader.rbQ Qte"t/fun tional/,omeA ontrollerAte"t.rbQ Qte"t/fun tional/ ontext"A ontrollerAte"t.rbQ Qte"t/fun tional/pro-e t"A ontrollerAte"t.rbQ Qte"t/fun tional/a tion"A ontrollerAte"t.rbQ Qte"t/fun tional/loginA ontrollerAte"t.rbQ Qte"t/fun tional/re"our e"A ontrollerAte"t.rbQ Qte"t/fun tional/u"er"A ontrollerAte"t.rbQ Qte"t/fun tional/feedA ontrollerAte"t.rbQ Doaded "uite /u"r/lib/ruby/gem"/1.8/gem"/ra#e-'.R.1/lib/ra#e/ra#eAte"tAloader >tarted ........ 9ini",ed in '.'&81R "e ond". 8 te"t"J 8 a""ertion"J ' failure"J ' error" /u"r/bin/ruby1.8 -7lib:te"t Q/u"r/lib/ruby/gem"/1.8/gem"/ra#e-'.R.1/lib/ra#e/ra#eAte"tAloader.rbQ Doaded "uite /u"r/bin/ra#e >tarted 9ini",ed in '.'''3'8 "e ond". ' te"t"J ' a""ertion"J ' failure"J ' error"
a aplicao. Caso
ese=emos6
*11
Qte"t/unit/pro-e tAte"t.rbQ Qte"t/unit/a tionAte"t.rbQ Qte"t/unit/ ontextAte"t.rbQ Qte"t/unit/u"erAte"t.rbQ Qte"t/unit/re"our eAte"t.rbQ Qte"t/unit/regi"trationAnotifierAte"t.rbQ Qte"t/unit/auditAte"t.rbQ Doaded "uite /u"r/lib/ruby/gem"/1.8/gem"/ra#e-'.R.1/lib/ra#e/ra#eAte"tAloader >tarted ....... 9ini",ed in '.&*16$R "e ond". R te"t"J R a""ertion"J ' failure"J ' error" Doaded "uite /u"r/bin/ra#e >tarted 9ini",ed in '.'''31* "e ond". ' te"t"J ' a""ertion"J ' failure"J ' error" ronaldo@minerva:~/tmp/gtd$ ra#e te"t:fun tional" %in /,ome/ronaldo/tmp/gtd( /u"r/bin/ruby1.8 -7lib:te"t Q/u"r/lib/ruby/gem"/1.8/gem"/ra#e-'.R.1/lib/ra#e/ra#eAte"tAloader.rbQ Qte"t/fun tional/,omeA ontrollerAte"t.rbQ Qte"t/fun tional/ ontext"A ontrollerAte"t.rbQ Qte"t/fun tional/pro-e t"A ontrollerAte"t.rbQ Qte"t/fun tional/a tion"A ontrollerAte"t.rbQ Qte"t/fun tional/loginA ontrollerAte"t.rbQ Qte"t/fun tional/re"our e"A ontrollerAte"t.rbQ Qte"t/fun tional/u"er"A ontrollerAte"t.rbQ Qte"t/fun tional/feedA ontrollerAte"t.rbQ Doaded "uite /u"r/lib/ruby/gem"/1.8/gem"/ra#e-'.R.1/lib/ra#e/ra#eAte"tAloader >tarted ........ 9ini",ed in '.'$5*81 "e ond". 8 te"t"J 8 a""ertion"J ' failure"J ' error" Doaded "uite /u"r/bin/ra#e >tarted 9ini",ed in '.'''331 "e ond". ' te"t"J ' a""ertion"J ' failure"J ' error"
Como po emos ver pelos coman os usa os6 o que essa tare"a test/unit para ro ar ca a um que um mero teste retornan o ver a eiro. .m arquivo e caso e testes6 se=a "uncional ou a hierarquia e testes se#ue o se#uinte "ormatoA
:o os os arquivos e teste e uma aplicao "orma uma suite e testesK .ma suite e testes possui muitos casos e testeK .m caso e testes possui muitos testesK .m teste possui muitas asser&es. i>6 so a"irma&es sobre a vali a e e uma etermina a parte o e
c< i#o. .ma assero po eria6 por e0emplo6 vrias asser&es relaciona as.
Bo Rails6 J aqui que entra o banco e testes que con"i#uramos em nosso aplicao. Ao ro armos o suite testes a aplicao6 esse banco J estruI o e recria o limpo para que os testes possam operar em
a os
con"iveis e "uncionar corretamente. + banco e0iste para =ustamente no termos que nos preocupar com os
*1*
ambientes
e pro uo e
esenvolvimento6
estivermos "a>en o testes que envolvem a remoo e a os. -e voc@ observar o po er usarA iret<rio test que se encontra em sua aplicao6 ver uma sJrie e iret<rios que voc@
ronaldo@minerva:~/tmp/gtd/te"t$ l" -l total &8 drwxr-xr-x 3 ronaldo ronaldo $'6* &''*-'6-&$ drwxr-xr-x & ronaldo ronaldo $'6* &''*-'6-&$ drwxr-xr-x & ronaldo ronaldo $'6* &''*-'6-&& drwxr-xr-x $ ronaldo ronaldo $'6* &''*-'6-&& -rw-r--r-- 1 ronaldo ronaldo 131R &''*-'6-&& drwxr-xr-x 3 ronaldo ronaldo $'6* &''*-'6-&$ drwxr-xr-x & ronaldo ronaldo $'6* &''*-1'-'1
/ itaremos =ustamente arquivos nesses iret<rios para reali>ar os testes que precisamos. Dois os iret<rios acima so e particular interesse. Para testarmos se a "uncionali a e relativa aos e a os para isso. + iret<rio fixtures contJm =ustamente e a os para testes urante a a a os a serem carre#a os no banco
mo elos est correta6 J <bvio que pecisamos arquivos que nos permitem in"ormar e0ecuo os mesmos. Q o
aplicao no isponIveis urante testesHum e0emplo seria "uncionali a e que epen e e servios e re e que no po em ser usa os para homolo#ao. Q "alamos bastante sobre a teoria por trs os testes no Rails. )amos colocar isso em prtica crian o nossos primeiros testes e uni a e. Para comear6 vamos carre#ar al#uns iret<rio6 vemos uma sJrie a os em nosso banco usan o al#umas "i0tures. Dentro esse
ronaldo: id: 1 name: 2onaldo login: ronaldo pa""word: te"t admin: true email: ronaldo@refle tive"urfa e. om mar u": id: & name: Bar u"
*1'
login: mar u" pa""word: te"t admin: fal"e email: mar u"@refle tive"urfa e. om
+s
a os que acabamos
e testes quan o
precisarmos os mesmos. A#ora que temos al#uns a os6 vamos testar a "uncionali a e presente em nossos mo elos. Para isto6 vamos e itar o arquivo user0test.rb6 que est no iret<rio unit. /sse arquivo est e"ini o assim no momentoA
re@uire 9ile.dirname%AA97D1AA( N H/../te"tA,elperH la"" O"er<e"t K <e"t::Onit::<e"t4a"e fixture" :u"er" ? 2epla e t,i" wit, your real te"t". def te"tAtrut, a""ert true end end
Como po emos ver6 esse arquivo e"ine uma classe Aser*est que J um caso e teste6 como evi encia o pelo classe a qual J eriva a. /sse caso e teste contJm um Pnico teste no momento6 que simplesmente passa por testar al#o que sempre ser ver a eiro. )e=a que o arquivo = carre#a as "i0tures necessrias para o mesmo. V nesse momento que nossos a os e teste so carre#a os o banco. -e quisermos6 po emos carre#ar outras "i0tures simplesmente eclaran o;as com o mesmo coman o. + sImbolo passa o para a "uno representa o arquivo no iret<rio fixtures. B<s usamos esses arquivos e"inin o mJto os que representam testes. )amos comear com o bsico6
testan o se nossos a os po em ser recupera os o banco sem problemasA (o i"icamos o nosso arquivo e teste para o se#uinteA
re@uire 9ile.dirname%AA97D1AA( N H/../te"tA,elperH la"" O"er<e"t K <e"t::Onit::<e"t4a"e fixture" :u"er" def te"tAba"i A"ele tion" a""ertAe@ual Q2onaldoQJ u"er"%:ronaldo(.name a""ertAe@ual QBar u"QJ u"er"%:mar u"(.name a""ertAe@ual &J O"er. ount end end
Como po emos ver6 removemos o teste anterior e criamos o nosso pr<prio teste. + primeiro teste usa a
*14
assero assert0equal para comparar ois valores. Ba primeira as asser&es acima6 veri"icamos se temos realmente ois usurios no banco. Como eclaramos ois usurios em nossas "i0tures6 esse teste everia retornar corretamente. A se#un a e a terceira assero recuperam que usamos para ois usurios o banco e veri"icam se os seus nomes a os o banco. +s sImbolos
correspon em ao que esperamos. Bote a construo usa a para recuperar os eclarar nossos usurios no arquivo mesmos a os o banco e a os6 por atribuio. Ro an o os testes6 veremos se eles passamA
ronaldo@minerva:~/tmp/gtd$ ra#e te"t:unit" %in /,ome/ronaldo/tmp/gtd( /u"r/bin/ruby1.8 -7lib:te"t Q/u"r/lib/ruby/gem"/1.8/gem"/ra#e-'.R.1/lib/ra#e/ra#eAte"tAloader.rbQ Qte"t/unit/pro-e tAte"t.rbQ Qte"t/unit/a tionAte"t.rbQ Qte"t/unit/ ontextAte"t.rbQ Qte"t/unit/u"erAte"t.rbQ Qte"t/unit/re"our eAte"t.rbQ Qte"t/unit/regi"trationAnotifierAte"t.rbQ Qte"t/unit/auditAte"t.rbQ Doaded "uite /u"r/lib/ruby/gem"/1.8/gem"/ra#e-'.R.1/lib/ra#e/ra#eAte"tAloader >tarted ....... 9ini",ed in '.3'55*$ "e ond". R te"t"J 6 a""ertion"J ' failure"J ' error" Doaded "uite /u"r/bin/ra#e >tarted 9ini",ed in '.'''3'6 "e ond". ' te"t"J ' a""ertion"J ' failure"J ' error"
)emos que nos sete testes que temos6 com suas nove asser&es6 to as passam. .ma maneira mais "cil veri"icar se os testes especI"icos que estamos construin o esto passan o J invocar e testes6 como no e0emplo abai0oA
iretamente o arquivo
ronaldo@minerva:~/tmp/gtd$ ruby te"t/unit/u"erAte"t.rb Doaded "uite te"t/unit/u"erAte"t >tarted . 9ini",ed in '.'*$1$R "e ond". 1 te"t"J 3 a""ertion"J ' failure"J ' error"
Ca a arquivo e testes J auto;conti o e po e ser testa o corretamente. Como esperamos6 temos um teste e tr@s asser&es que passam sem problemas. )amos i>er que precisamos e mais um usurio. Para isso mo i"icamos o nosso arquivo e "i0turesA
*15
login: ronaldo pa""word: te"t admin: true email: ronaldo@refle tive"urfa e. om mar u": id: & name: Bar u" login: mar u" pa""word: te"t admin: fal"e email: mar u"@refle tive"urfa e. om ale""andra: id: 3 name: Lle""andra login: ale""andra pa""word: te"t admin: fal"e email: ale""andra@refle tive"urfa e. om
ronaldo@minerva:~/tmp/gtd$ ruby te"t/unit/u"erAte"t.rb Doaded "uite te"t/unit/u"erAte"t >tarted 9 9ini",ed in '.&85&83 "e ond". 1( 9ailure: te"tAba"i A"ele tion"%O"er<e"t( )te"t/unit/u"erAte"t.rb:6+: K&. expe ted but wa" K3.. 1 te"t"J 3 a""ertion"J 1 failure"J ' error"
os tr@s testes que estabelecemos6 um "alhou. ?sso J enota o no s< pela lista#em #eral
no "im o teste comotambJm pela linha que comea com uma letra 7 maiPscula S"ailureT. + teste nos in"orma tambJm o que "alhou6 para que possamos corri#i;lo. Bo caso aqui6 vamos corri#ir o nosso teste6 = que a "alha est no mesmo. /m testes elabora os corretamente6 porJm6 basicamente qualquer problema seria corri#i o no c< i#oHa menos que o teste realmente contenha um bu#. + nosso teste a#ora seriaA
re@uire 9ile.dirname%AA97D1AA( N H/../te"tA,elperH la"" O"er<e"t K <e"t::Onit::<e"t4a"e fixture" :u"er" def te"tAba"i A"ele tion" a""ertAe@ual Q2onaldoQJ u"er"%:ronaldo(.name a""ertAe@ual QBar u"QJ u"er"%:mar u"(.name a""ertAe@ual 3J O"er. ount end end
.ma coisa a se manter em mente J que a primeira assero a "alhar em um teste invali a to o o teste. ?sso se =usti"ica pelo "ato e que um teste J uma uni a e6 que eve representar um con=unto completo e asser&es sobre o que se quer testar e mo o que na aHatJ on e J humanamente possIvelH"ique e "ora. Di#amos a#ora que queremos esten er os nossos testes para testar opera&es bsicas em nosso mo elo a os. e
re@uire 9ile.dirname%AA97D1AA( N H/../te"tA,elperH la"" O"er<e"t K <e"t::Onit::<e"t4a"e fixture" :u"er" def te"tAba"i A"ele tion" a""ertAe@ual Q2onaldoQJ u"er"%:ronaldo(.name a""ertAe@ual QBar u"QJ u"er"%:mar u"(.name a""ertAe@ual 3J O"er. ount end def te"tA rud u"er ! O"er.new%:name !. Q2enatoQ( a""ert u"er."ave end end
Ro an o esse teste6 temos uma "alha6 obviamente6 = que s< in"ormarmos o nome al#umas vali a&es que impe em que o usurio se=a salvo sem a os.
o usurio e temos
ronaldo@minerva:~/tmp/gtd$ ruby te"t/unit/u"erAte"t.rb Doaded "uite te"t/unit/u"erAte"t >tarted .9 9ini",ed in '.&61*5$ "e ond". 1( 9ailure: te"tA rud%O"er<e"t( )te"t/unit/u"erAte"t.rb:1$+: Kfal"e. i" not true. & te"t"J $ a""ertion"J 1 failure"J ' error"
)e=a que a#ora temos ois testes6 com quatro asser&es6 as quais uma est "alhan o. A linha lo#o abai0o a "rase N-tarte O se#un o no. +bviamente6 como estamos crian o os nossos testes epois e = termos construI o a aplicao6 os mesmos no "a>em muito senti o. DaI a necessi a e e testarmos antes e construir a aplicao6 plane=an o em testes a "uncionali a e que ese=amos antes mesmo que ela este=a l. A i Jia por trs o 9P no J mo i"icar testes para que eles passem6 mas mo i"icar c< i#o para que ele satis"aa aos testes estabeleci os6 consi eran o que este=am corretos. -e montarmos os nossos testes antes e qualquer coisa6 to a a nossa uma representao NvisualO e nossos testes mostran o que o primeiro passou6 mas o
*1!
pro uo
estabelecemos. .m teste um pouco mais so"istica o6 que po erIamos ter elabora o no comeo ter si o assimA e nossa aplicao po eria
re@uire 9ile.dirname%AA97D1AA( N H/../te"tA,elperH la"" O"er<e"t K <e"t::Onit::<e"t4a"e fixture" :u"er" def te"tAba"i A"ele tion" a""ertAe@ual Q2onaldoQJ u"er"%:ronaldo(.name a""ertAe@ual QBar u"QJ u"er"%:mar u"(.name a""ertAe@ual 3J O"er. ount end def te"tA rud u"er ! O"er.new%:name !. Q2enatoQJ :login !. QrenatoQJ :pa""word !. QrenatoQJ :email !. Qrenato@refle tive"urfa e. omQ( a""ert u"er."ave a""ertAe@ual $J O"er. ount a""ert Wu"er.adminU u"er.pa""word ! Qte"tQ a""ert u"er."ave "ameAu"er ! O"er.find%u"er.id( a""ertAe@ual u"er.nameJ "ameAu"er.name differentAu"er ! O"er.find%1( a""ertAnotAe@ual u"er.idJ differentAu"er.id a""ertAnotAe@ual u"er.nameJ differentAu"er.name a""ert u"er.de"troy a""ertAe@ual 3J O"er. ount end end
)e=a que estamos testan o basicamente to as as opera&es bsicas para um ob=eto nosso teste a#ora6 temosA
e a os. /0ecutan o o
ronaldo@minerva:~/tmp/gtd$ ruby te"t/unit/u"erAte"t.rb Doaded "uite te"t/unit/u"erAte"t >tarted .. 9ini",ed in '.13*3$3 "e ond". & te"t"J 1& a""ertion"J ' failure"J ' error"
Para e0perimentar com a criao e "uncionali a e que no temos no momento6 vamos supor que queremos a icionar uma relacionamento entre usurios e a&es. Como no temos isso em nosso mo elo no momento6 po emos comear e maneira simplesA
*15
re@uire 9ile.dirname%AA97D1AA( N H/../te"tA,elperH la"" O"er<e"t K <e"t::Onit::<e"t4a"e fixture" :u"er" def te"tAba"i A"ele tion" a""ertAe@ual Q2onaldoQJ u"er"%:ronaldo(.name a""ertAe@ual QBar u"QJ u"er"%:mar u"(.name a""ertAe@ual 3J O"er. ount end def te"tA rud u"er ! O"er.new%:name !. Q2enatoQJ :login !. QrenatoQJ :pa""word !. QrenatoQJ :email !. Qrenato@refle tive"urfa e. omQ( a""ert u"er."ave a""ertAe@ual $J O"er. ount a""ert Wu"er.adminU u"er.pa""word ! Qte"tQ a""ert u"er."ave "ameAu"er ! O"er.find%u"er.id( a""ertAe@ual u"er.nameJ "ameAu"er.name differentAu"er ! O"er.find%1( a""ertAnotAe@ual u"er.idJ differentAu"er.id a""ertAnotAe@ual u"er.nameJ differentAu"er.name a""ert u"er.de"troy a""ertAe@ual 3J O"er. ount end def te"tAa tion"Arelation",ip u"er ! u"er"%:ronaldo( a""ertAre"pondAto u"erJ :a tion" a""ertAe@ual 'J u"er.a tion"."ize end end
/m nosso novo teste6 veri"icamos se a classe respon e a um mJto o actions6 que representaria o nosso relacionamento e6 em se#ui a6 se o tamanho que esperamos para o con=unto e a&es e um usurio J va>io. + resulta o o teste JA
ronaldo@minerva:~/tmp/gtd$ ruby te"t/unit/u"erAte"t.rb Doaded "uite te"t/unit/u"erAte"t >tarted 9.. 9ini",ed in '.315*& "e ond". 1( 9ailure: te"tAa tion"Arelation",ip%O"er<e"t( )te"t/unit/u"erAte"t.rb:$1+: K?KO"er:'xbRR5&e$' @attribute"! SQnameQ!.Q2onaldoQJ QadminQ!.Q1QJ QidQ!.Q1QJ
*12
Qpa""wordQ!.Qte"tQJ QloginQ!.QronaldoQJ QemailQ!.Qronaldo@refle tive"urfa e. omQT.. of type KO"er. expe ted to re"pondAtoUK:a tion".. 3 te"t"J 13 a""ertion"J 1 failure"J ' error"
Como espera o6 temos uma "alha. ?mplementamos a nossa "uncionali a e a#ora com o se#uinte c< i#oA
la"" O"er K L tive2e ord::/a"e attrAa e""or : urrentAu"erAid eAof eAof eAof eAof :name :login :pa""word :email
ronaldo@minerva:~/tmp/gtd$ ruby te"t/unit/u"erAte"t.rb Doaded "uite te"t/unit/u"erAte"t >tarted ... 9ini",ed in '.&R3$8 "e ond". 3 te"t"J 1$ a""ertion"J ' failure"J ' error"
e teste que
veri"icasse se o relacionamento "unciona como queremos. Po erIamos nos alon#ar mais6 mas a i Jia = est passa a. /stes so os testes a#ora como so os testes "uncionais. )amos trabalhar com o mesmo mo elo e a os6 usan o o controller responsvel pelo mesmo. + arquivo6 chama o apropria amente users0controller0test.rb6 est no iret<rio functional e contJm o se#uinte6 no momentoA e uni a e e po emos ver
re@uire 9ile.dirname%AA97D1AA( N H/../te"tA,elperH re@uire Hu"er"A ontrollerH ? 2e-rai"e error" aug,t by t,e ontroller. la"" O"er"4ontroller5 def re" ueAa tion%e( rai"e e end5 end la"" O"er"4ontroller<e"t K <e"t::Onit::<e"t4a"e
*!$
def "etup @ ontroller ! O"er"4ontroller.new @re@ue"t ! L tion4ontroller::<e"t2e@ue"t.new @re"pon"e ! L tion4ontroller::<e"t2e"pon"e.new end ? 2epla e t,i" wit, your real te"t". def te"tAtrut, a""ert true end end
)e=a que a i Jia J a mesma6 com a i"erena que temos a#ora um mJto o setup6 que J responsvel por criar as con i&es necessrias para nosso teste. .m mJto o oposto6 chama a teardown6 e0iste para principalmente quan o h recursos "Isicos associa os e os mesmos posteriores. )amos testar a#ora al#uns pontos em nosso controllerA escarre#ar o que criamos em setup6 o que #eralmente J necessrio somente para ob=etos com epen @ncias comple0as6 evem ser reiniciali>a os para testes
re@uire 9ile.dirname%AA97D1AA( N H/../te"tA,elperH re@uire Hu"er"A ontrollerH ? 2e-rai"e error" aug,t by t,e ontroller. la"" O"er"4ontroller5 def re" ueAa tion%e( rai"e e end5 end la"" O"er"4ontroller<e"t K <e"t::Onit::<e"t4a"e def "etup @ ontroller ! O"er"4ontroller.new @re@ue"t ! L tion4ontroller::<e"t2e@ue"t.new @re"pon"e ! L tion4ontroller::<e"t2e"pon"e.new end def te"tAindex get :index a""ertAre"pon"e :"u end end
e""
ronaldo@minerva:~/tmp/gtd$ ruby te"t/fun tional/u"er"A ontrollerAte"t.rb Doaded "uite te"t/fun tional/u"er"A ontrollerAte"t >tarted 9 9ini",ed in '.'5536* "e ond". 1( 9ailure: te"tAindex%O"er"4ontroller<e"t( )te"t/fun tional/u"er"A ontrollerAte"t.rb:1*+: 1xpe ted re"pon"e to be a K:"u e"".J but wa" K3'&. 1 te"t"J 1 a""ertion"J 1 failure"J ' error"
.ma "alha <bvia = que a nossa ao e0pera um lo#in. .m teste mais completo seriaA
*!1
re@uire 9ile.dirname%AA97D1AA( N H/../te"tA,elperH re@uire Hu"er"A ontrollerH ? 2e-rai"e error" aug,t by t,e ontroller. la"" O"er"4ontroller5 def re" ueAa tion%e( rai"e e end5 end la"" O"er"4ontroller<e"t K <e"t::Onit::<e"t4a"e fixture" :u"er" def "etup @ ontroller ! O"er"4ontroller.new @re@ue"t ! L tion4ontroller::<e"t2e@ue"t.new @re"pon"e ! L tion4ontroller::<e"t2e"pon"e.new end def te"tAindex get :index a""ertAredire tedAto : ontroller !. QloginQJ :a tion !. QloginQ end def te"tAindexAwit,Alogin @re@ue"t."e""ion):u"er+ ! u"er%:ronaldo(.id get :index a""ertAre"pon"e :"u e"" end end
-e quisermos "a>er al#o ain a mais completo6 po emos testar o que e0iste na respostaA
re@uire 9ile.dirname%AA97D1AA( N H/../te"tA,elperH re@uire Hu"er"A ontrollerH ? 2e-rai"e error" aug,t by t,e ontroller. la"" O"er"4ontroller5 def re" ueAa tion%e( rai"e e end5 end la"" O"er"4ontroller<e"t K <e"t::Onit::<e"t4a"e fixture" :u"er" def "etup @ ontroller ! O"er"4ontroller.new @re@ue"t ! L tion4ontroller::<e"t2e@ue"t.new @re"pon"e ! L tion4ontroller::<e"t2e"pon"e.new end def te"tAindex get :index a""ertAredire tedAto : ontroller !. QloginQJ :a tion !. QloginQ end def te"tAindexAwit,Alogin @re@ue"t."e""ion):u"er+ ! u"er"%:ronaldo(.id get :index a""ertAre"pon"e :"u e"" a""ertAtag :tag !. QtableQJ : ,ildren !. S : ount !. O"er. ount N 1J :only !. S :tag !. QtrQ T T end end
Aqui estamos testan o se e0iste uma tabela com um nPmero pa#inao6 mas isso tambJm po eria ser consi era o "acilmente.
usurios que temos mais um Sque seria o cabealhoT. Bo estamos levan o em conta uma possIvel
*!*
re@uire 9ile.dirname%AA97D1AA( N H/../te"tA,elperH re@uire Hu"er"A ontrollerH ? 2e-rai"e error" aug,t by t,e ontroller. la"" O"er"4ontroller5 def re" ueAa tion%e( rai"e e end5 end la"" O"er"4ontroller<e"t K <e"t::Onit::<e"t4a"e fixture" :u"er" def "etup @ ontroller ! O"er"4ontroller.new @re@ue"t ! L tion4ontroller::<e"t2e@ue"t.new @re"pon"e ! L tion4ontroller::<e"t2e"pon"e.new end def te"tAindex get :index a""ertAredire tedAto : ontroller !. QloginQJ :a tion !. QloginQ end def te"tAindexAwit,Alogin @re@ue"t."e""ion):u"er+ ! u"er"%:ronaldo(.id get :index a""ertAre"pon"e :"u e"" a""ertAtag :tag !. QtableQJ : ,ildren !. S : ount !. O"er. ount N 1J :only !. S :tag !. QtrQ T T a""ertAtag :tag !. QtdQJ : ontent !. u"er"%:ronaldo(.name end end
/stamos acima testan o o conteP o e uma Pnica cJlula a tabela e usurios. :estes "uncionais so Pteis para testar um controller em in ivi ual e pelos tipos e testes acima para ter uma i Jia e0ecuo e como "arIamos na maior parte epen e os casos. :estar A=a0 J um pouco mais complica o porque a e respostas te0tuais. .m plu#in especI"ico para isso e Qava-cript mas = e0istem plu#ins inte#ra os a "erramentas pr<prias para isso que o que veri"icao
po e ser encontra o no en ereoA httpAZZan thennothin#.netZarchivesZ*$$1Z$*Z$5Zselenium;on;rails. + Pltimo e0emplo o que temos em termos e testes no Rails so os testes e inte#rao menciona os e lo#in. A tItulo e
anteriormente que nos permitem testar "uncionali a e ao lon#o po erIamos ter #era o um teste e0emplo6 vamos "a>er =ustamente isso. Primeiro6 vamos #erar o teste em siA
*!'
re@uire Q?S9ile.dirname%AA97D1AA(T/../te"tA,elperQ la"" Dogin<e"t K L tion4ontroller::7ntegration<e"t fixture" :u"er" def te"tAlogin u"er ! u"er"%:ronaldo( get Q/,omeQ a""ertAredire tedAto Q/login/loginQ followAredire tW a""ertAre"pon"e :"u e""
po"t Q/login/loginQ a""ertAre"pon"e :"u e"" a""ertAe@ual Q7nvalid login and/or pa""word.QJ fla",):noti e+ po"t Q/login/loginQJ :login !. u"er.loginJ :pa""word !. u"er.pa""word a""ertAredire tedAto Q/,omeQ followAredire tW a""ertAtag :tag !. QdivQJ :attribute" !. S :id !. QeditAformQ T end end
po e ser6 inclusive6 aumenta o para manipular iversas sess&es simultLneas se esse "or o caso. Ro an o o teste temosA
ronaldo@minerva:~/tmp/gtd$ ra#e te"t:integration %in /,ome/ronaldo/tmp/gtd( /u"r/bin/ruby1.8 -7lib:te"t Q/u"r/lib/ruby/gem"/1.8/gem"/ra#e-'.R.1/lib/ra#e/ra#eAte"tAloader.rbQ Qte"t/integration/loginAte"t.rbQ Doaded "uite /u"r/lib/ruby/gem"/1.8/gem"/ra#e-'.R.1/lib/ra#e/ra#eAte"tAloader >tarted . 9ini",ed in '.1*&65& "e ond". 1 te"t"J 8 a""ertion"J ' failure"J ' error" Doaded "uite /u"r/bin/ra#e >tarted 9ini",ed in '.'''31$ "e ond". ' te"t"J ' a""ertion"J ' failure"J ' error"
?sso encerra o nossa sesso sobre testes. /mbora se=a uma intro uo bem rpi a6 esperamos que tenha si o o su"iciente para mostrar como essa parte J importante e po erosa em suas aplica&es. Lembre;se e comear a testar mesmo antes e esenvolver o que voc@ precisa6 e e e criar testes su"icientemente
completos e comple0os para veri"icar plenamente a sua aplicao testan o es e mJto os customi>a os em mo elos e a os atJ intera&es entre vrios controllers.
*!4
IMPLANTAO
/m al#um momento6 obviamente6 voc@ ter que implantar a sua aplicao em um servi or. Bo caso o Rails6 e0istem etalhes e>enas e op&es para implantao6 consi eran o vrios aspectos e necessi a es e C%? e cluster o processo. + Rails po e ro ar em conte0tos que vo e servi ores Eeb customi>a os. /ntrar em esse tutorial6 mas
po emos tecer al#umas consi era&es sobre o processo e enviar a aplicao para o servi or. Para reali>ar esse trabalho6 e0iste uma "erramenta especI"ica cria a para o Rails que automati>a o processo6 controlan o vers&es e reali>an o outras tare"as especI"icas e implantao como6 por e0emplo6 reinicar o servi or Eeb em caso e necessi a e. /ssa "erramenta J chama a e Capistrano. Para instal;la6 si#a a rota comumA
api"trano
Depos
corretamenteA
ronaldo@minerva:~/tmp/gtd$
ap -,
+ que precisamos a#ora J a icionar a "uncionali a e necessria M nossa aplicao com o coman o abai0oA
ronaldo@minerva:~/tmp/gtd$ ap --apply-to . X<G exi"t" onfig reate onfig/deploy.rb exi"t" lib/ta"#" reate lib/ta"#"/ api"trano.ra#e Doaded "uite /u"r/bin/ ap >tarted 9ini",ed in '.'''&6 "e ond". ' te"t"J ' a""ertion"J ' failure"J ' error"
e0ecutemos novos coman os necessrios para a implementao. )amos "a>er al#uns testes a#ora consi eran o somente uma aplicao simples6 sem balanceamento vers&es para um ou mais servi or6 controlan o;as corretamente. Precisamos6 primeiro6 con"i#urar nossas op&es cria o em nosso iret<rio configA e implantao6 e itan o o arquivo deploy.rb6 que "oi e
car#a ou qualquer coisa como isso6 embora o Capistrano se=a po eroso o su"iciente no s< para enviar
*!5
"et :appli ationJ QgtdQ "et :repo"itoryJ Q"vnN"",://yourdomain. om/repo"itory/gtd/trun#Q role :webJ Qyourdomain. omQ role :appJ Qyourdomain. omQ role :dbJ QyourdomainJ omQJ :primary !. true "et :deployAtoJ Q/,ome/ronaldo/web/gtd/Q "et :u"erJ QronaldoQ
+ arquivo acima "oi re u>i o ao mInimo6 mostran o um reposit<rio -ubversioK servi ores Eeb6 e aplicao e e banco e a os con"i#ura o Sno nosso caso o mesmoTK o iret<rio para on e as vers&es sero envia asK e o usurio remoto que e0ecutar esses coman os. A con"i#urao acima obviamente assume que voc@ possui um reposit<rio -ubversion o c< i#o que estamos ro an oHsem o qual o coman o no ro ar. 7eito isso6 precisamos a#ora ro ar um coman o para iniciali>ar nossas estruturasA
ronaldo@minerva:~/tmp/gtd$ ra#e remote:exe L4<7:8!"etup %in /,ome/ronaldo/tmp/gtd( loading onfiguration /u"r/lib/ruby/gem"/1.8/gem"/ api"trano1.&.'/lib/ api"trano/re ipe"/"tandard.rb loading onfiguration ./ onfig/deploy.rb P exe uting ta"# "etup P exe uting Qm#dir -p -m RR5 /,ome/ronaldo/web/gtd/relea"e" /,ome/ronaldo/web/gtd/",ared/"y"tem [[En m#dir -p -m RRR /,ome/ronaldo/web/gtd/",ared/log [[En m#dir -p -m RRR /,ome/ronaldo/web/gtd/",ared/pid"Q "erver": )Qyourdomain. omQ+ =a""word: PPPPPP )yourdomain. om+ exe uting ommand fini",ed ommand
+ coman o acima cria o que J necessrio para a implantao. Po emos a#ora enviar os nossos arquivos para o servi or.
ronaldo@minerva:~/tmp/gtd$ ra#e deploy %in /,ome/ronaldo/tmp/gtd( PP 7nvo#e deploy %fir"tAtime( PP 7nvo#e remote:deploy %fir"tAtime( PP 1xe ute remote:deploy loading onfiguration /u"r/lib/ruby/gem"/1.8/gem"/ api"trano1.&.'/lib/ api"trano/re ipe"/"tandard.rb loading onfiguration ./ onfig/deploy.rb P exe uting ta"# deploy P exe uting ta"# update PP tran"a tion: "tart P exe uting ta"# updateA ode P @uerying late"t revi"ion... =a""word: PPPPPP P exe uting Qif )) W -d /,ome/ronaldo/web/gtd/relea"e"/&''*1''&'*$'&8 ++5 t,enEn "vn o -@ -r1 "vnN"",://yourdomain. om/repo"itory/gtd/trun# /,ome/ronaldo/web/gtd/relea"e"/&''*1''&'*$'&8 [[En %te"t -e /,ome/ronaldo/web/gtd//revi"ion".log MM %tou , /,ome/ronaldo/web/gtd//revi"ion".log [[ ,mod *** /,ome/ronaldo/web/gtd//revi"ion".log(( [[ e ,o \date NEQYF-Ym-Yd Y;:YB:Y>EQ\ $O>12 1 &''*1''&'*$'&8 .. /,ome/ronaldo/web/gtd//revi"ion".log5En fiQ "erver": )Qyourdomain. omQ+ =a""word: PPPPPP )yourdomain. om+ exe uting ommand
*!1
)out :: yourdomain. om+ =a""word: )out :: yourdomain. om+ "ubver"ion i" a"#ing for a pa""word )out :: yourdomain. om+ =a""word: )out :: yourdomain. om+ "ubver"ion i" a"#ing for a pa""word ommand fini",ed P exe uting Qrm -rf /,ome/ronaldo/web/gtd/relea"e"/&''*1''&'*$'&8/log /,ome/ronaldo/web/gtd/relea"e"/&''*1''&'*$'&8/publi /"y"tem [[En ln -nf" /,ome/ronaldo/web/gtd/",ared/log /,ome/ronaldo/web/gtd/relea"e"/&''*1''&'*$'&8/log [[En ln -nf" /,ome/ronaldo/web/gtd/",ared/"y"tem /,ome/ronaldo/web/gtd/relea"e"/&''*1''&'*$'&8/publi /"y"temQ "erver": )Qyourdomain. omQ+ )yourdomain. om+ exe uting ommand ommand fini",ed P exe uting Qte"t -d /,ome/ronaldo/web/gtd/",ared/pid" [[ En rm -rf /,ome/ronaldo/web/gtd/relea"e"/&''*1''&'*$'&8/tmp/pid" [[ En ln -nf" /,ome/ronaldo/web/gtd/",ared/pid" /,ome/ronaldo/web/gtd/relea"e"/&''*1''&'*$'&8/tmp/pid"5 trueQ "erver": )Qyourdomain. omQ+ )yourdomain. om+ exe uting ommand ommand fini",ed P exe uting ta"# "ymlin# P exe uting Ql" -x1 /,ome/ronaldo/web/gtd/relea"e"Q "erver": )Qyourdomain. omQ+ )yourdomain. om+ exe uting ommand ommand fini",ed P exe uting Qln -nf" /,ome/ronaldo/web/gtd/relea"e"/&''*1''&'*$'&8 /,ome/ronaldo/web/gtd/ urrentQ "erver": )Qyourdomain. omQ+ )yourdomain. om+ exe uting ommand ommand fini",ed PP tran"a tion: ommit P exe uting ta"# re"tart P exe uting Q"udo /,ome/ronaldo/web/gtd/ urrent/" ript/pro e""/reaperQ "erver": )Qyourdomain. omQ+ )yourdomain. om+ exe uting ommand PP )out :: yourdomain. om+ 4ouldnHt find any pro e"" mat ,ing: /,ome/ronaldo/web/gtd/ urrent/publi /di"pat ,.f gi ommand fini",ed PP 1xe ute deploy
PP PP PP PP
-e houver c< i#o no reposit<rio6 o resulta o J que a verso mais recente J veri"ica a e lana a no iret<rio correto a aplicao. -e houver processos ativos ro an o com a verso anti#a eles sero reinicia os con"orme a necessi a e. Bo caso acima6 voc@ po e ver que essa reiniciali>ao "alhou porque no con"i#uramos um servi or Eeb. 7inalmente6 se voc@ olhar o iret<rio a aplicao6 ver o se#uinte a#oraA
root@bitbu #et:/,ome/ronaldo/web/gtd? l" -l total 1& lrwxrwxrwx 1 ronaldo ronaldo $5 &''*-1'-'& /,ome/ronaldo/web/gtd/relea"e"/&''*1''&'*$'&8 drwxrwxr-x R ronaldo ronaldo $'6* &''*-1'-'& -rw-rw-rw- 1 ronaldo ronaldo &&5 &''*-1'-'& drwxr-xr-x 5 ronaldo ronaldo $'6* &''*-1'-'&
'3:$R
urrent -.
:emos um iret<rio e vers&es e uma verso corrente. /mbora a ao acima no envie o banco e0istem e>enas e a os6 uma as tare"as o Capistrano "a> =ustamente isso. / o
*!!
ocumentao no
AMBIENTES DE DESENVOLVIMENTO
Desenvolver com "i>emos atJ o momento6 usan o apenas a linha sistemas operacionais em que o "rameGor8 est sen o usa o. .ma as "erramentas em especial6 o Ra Rails6 que J basea o no /clipse6 possui uma #ama e e coman o e um e itor o e te0tos po e parecer conveniente mas "erramentas esto sur#in o para a=u ar a vi a esenvolve or Rails6 nos vrios
caracterIsticas volta os para o Rails que poupa uma quanti a e substancial encontrar o Ra Rails em http://www.radrails.org/6 sen o que e0istem vers&es "uncionali a es
previamente para Ein oGs6 Linu0 e (acintosh. Caso voc@ queira6 voc@ po e inte#rar
o mesmo a uma instalao /clipse comum6 usan o up ate sites. /ssa Pltima maneira J
bem conveniente para aqueles que = esto usan o o /clipse para outros tipos e esenvolvimento.
*!5
A tela acima mostra viso toma a lo#o ap<s ro armos os testes Ra8e6 instalao e plu#ins inte#ra a6 ocumentao6 e e>enas
e uni a e
nenhuma "alha. Como po emos ver6 temos nave#ao6 e io com sinta0e pr<pria6 tare"as especI"icas tornam o Ra Rails um e0celente ambiente para pro=etos Rails. Com um pouco e es"oro6 J possIvel tambJm epurar aplica&es Rails
?nstru&es para isso po em ser encontra as no pr<prio site o Ra Rails. /u estou usan o o Ra Rails es e sua verso $.*6 lana a h quase um ano atrsHa verso atual J a $.!1H e estou bastante satis"eito com o ambiente. Apesar e al#uns problemas em al#unas vers&es interme irias6 a verso atual J uma "erramenta que vale a pena pelo menos testar. Duas outras possibili a es que eu ain a no testei so6 respectivamenteA RIDE-MEA um ?D/ Rails para Ein oGs6 "amiliar para usurios o )isual -tu io. http://www.pro%ectrideme.com/
*!2
KDevelopA + ?D/ pa ro para o FD/ possui suporte ao Rails em suas vers&es mais recentes. http://www.3de!elop.org/
ronaldo@minerva:~/tmp/gtd$ ra#e "tat" %in /,ome/ronaldo/tmp/gtd( N----------------------N-------N-------N---------N---------N-----N-------N M 8ame M Dine" M D:4 M 4la""e" M Bet,od" M B/4 M D:4/B M N----------------------N-------N-------N---------N---------N-----N-------N M ;elper" M &3 M &1 M ' M 1 M ' M 16 M M 4ontroller" M $'5 M 3$R M 6 M $5 M 5 M 5 M M 4omponent" M ' M ' M ' M ' M ' M ' M M 9un tional te"t" M 155 M 11$ M 1* M &5 M 1 M & M M Bodel" M 1'5 M R& M R M $ M ' M 1* M M Onit te"t" M 1&8 M 86 M R M 1& M 1 M 5 M M Dibrarie" M ' M ' M ' M ' M ' M ' M M 7ntegration te"t" M &6 M 18 M 1 M 1 M 1 M 1* M N----------------------N-------N-------N---------N---------N-----N-------N M <otal M 8$5 M **1 M $' M 88 M & M 5 M N----------------------N-------N-------N---------N---------N-----N-------N 4ode D:4: $$' <e"t D:4: &&1 4ode to <e"t 2atio: 1:'.5
Bo h na a
os testes
contrapro ucente ho=e6 mas comparar a quanti a e e c< i#o e0istente por rea a aplicao po e ser bem Ptil6 especialmente epois e nos acostumarmos com os nPmeros que evemos esperar. + coman o abai0o nos mostra tu o o que o Rails po e "a>erA
ronaldo@minerva:~/tmp/gtd$ ra#e --ta"#" %in /,ome/ronaldo/tmp/gtd( ra#e db:fixture":load ? Doad fixture" into t,e urrent environmentH" databa"e. Doad "pe ifi fixture" u"ing 97_<O21>!xJy ra#e db:migrate ? Bigrate t,e databa"e t,roug, " ript" in db/migrate. <arget "pe ifi ver"ion wit, Z12>7:8!x ra#e db:" ,ema:dump ? 4reate a db/" ,ema.rb file t,at an be portably u"ed again"t any G/ "upported by L2 ra#e db:" ,ema:load ? Doad a " ,ema.rb file into t,e databa"e ra#e db:"e""ion": lear ? 4lear t,e "e""ion" table ra#e db:"e""ion": reate ? 4reate" a "e""ion" table for u"e wit, 4X7::>e""ion::L tive2e ord>tore ra#e db:"tru ture:dump ? Gump t,e databa"e "tru ture to a >CD file ra#e db:te"t: lone ? 2e reate t,e te"t databa"e from t,e urrent environmentH" databa"e " ,ema ... ra#e te"t ? <e"t all unit" and fun tional" ra#e te"t:fun tional" ? 2un te"t" for fun tional"db:te"t:prepare ra#e te"t:integration ? 2un te"t" for integrationdb:te"t:prepare ra#e te"t:plugin" ? 2un te"t" for plugin"environment ra#e te"t:re ent ? 2un te"t" for re entdb:te"t:prepare ra#e te"t:un ommitted ? 2un te"t" for un ommitteddb:te"t:prepare ra#e te"t:unit" ? 2un te"t" for unit"db:te"t:prepare
*5$
...
Bo estamos e0ibin o tu o6 mas voc@ po e investi#ar al#umas tare"as que que voc@ usar com maior "reqR@ncia6 alJm os testes6 J claro. /m especial6 @ uma olha a nas se#uintes tare"asA db:structure:dump6 rails:update6 e doc:rails.
VIVENDO NO LIMITE
+ "uturo Al#umas o Rails parece estar #aranti o. + "rameGor8 vem #anhan o a eptos continuamente6 livros esto e>enas e i iomas e o pro#resso e novas vers&es J continuo. ois as novi a es previstas para a verso 1.*6 que est para sair a qualquer momento6 incluem sen o publica os sobre o mesmo em os #ran es t<picos em vo#a atualmenteA -uporte pa ro a R/-:6 menciona o e passa#em anteriormenteK
e bibliotecas
e novo nas
c< i#o mais recente6 h sempre a possibili a e e que ele contenha bu#s que impe iram que sua aplicao ro e. Portanto6 use esse c< i#o somente para testes e nunca para pro uo. Para reverter o coman o acima6 useA
ra#e rail":unfreeze
UE FALTA
Besse tutorial no "oram incluI os al#uns assuntos porque consi eramos que no conse#uirIamos trat;los e maneira a equa a6 #eralmente por prop<sito esse ocumento. /ntre esses assuntos6 o mais importante se=a talve> o uso pr<prio6 mas cu=o uso J relativamente especI"ico e cu=a so e0tens&es ao Rails.
*51
epen erem
epen @ncia
tratamento rpi o o mesmo. Da mesma "orma6 no tratamos e en#ines e esenvolvimentos similares6 que
e ser um material
sobre o Rails6 somente as "un&es estritamente necessrias para o c< i#o "oram menciona as. + Rails J um
"rameGor8
mJto o e varivel presentes no mesmo. Para isso6 e0istem livros = publica os sobre o assunto que voc@ po e encontrar "acilmente em livrarias tJcnicas. /spero que o resto o tutorial se=a o su"iciente para compensar essas "altas.
Concluso
:erminamos aqui o o tutorial. :o os os erros presentes no te0to e no c< i#o so6 obviamente6 meus. /u tomei um cui a o especial nas se&es que envolvem pro#ramao6 procuran o sempre mostrar entra a e saI a e0atas al#uma coisa tenha passa o. -e voc@ abai0o. De acor o com a licena escolhi a para este para copiar6 istribuir eZou mo i"icar esse ocumentoHque voc@ po e ler no ap@n ice AHvoc@ J livre a maneira que achar melhor. )oc@ po e6 inclusive6 plena liber a e para o que estava ei0a o "a>en o6 e voltan o para corri#ir minunciosamente o que eu mu ara6 mas sempre h a possibili a e e que escobrir al#um problema6 entre em contato no en ereo
ocumento
publicar um livro usan o o material completo6 com ou sem mo i"ica&es6 no seu pr<prio nome6 e ven @;lo pelo preo que achar =usto. ) em "rente e "aa o que bem enten erA a licena lhe isso. Caso queira me enviar um e;mail com su#est&es6 crIticas ou quaisquer outros comentrios6 no hesite. + en ereo para contato JA ronaldo?reflecti!esurface.com. Com e0ceo e pe i os e a=u a que no tem a ver com o tutorial em siHque eu teria o maior pra>er em aten er se tivesse tempo6 mas6 in"eli>mente6 esse no J casoHeu "arei o melhor para respon er to os e;mails. A minha empresa tambJm est trabalhos em Rub, on Rails caso ha=a interesse. Bo mais6 compartilhe e ivirta;se. isponIvel para
*5*
!" PREAMBLE
:he purpose o" this License is to ma8e a manual6 te0tboo86 or other "unctional an use"ul ocument c"reec in the sense o" "ree omA to assure ever,one the e""ective "ree om to cop, an re istribute it6 Gith or Githout mo i",in# it6 either commerciall, or noncommerciall,. -econ aril,6 this License preserves "or the author an publisher a Ga, to #et cre it "or their Gor86 Ghile not bein# consi ere responsible "or mo i"ications ma e b, others. :his License is a 8in o" ccop,le"tc6 Ghich means that erivative Gor8s o" the ocument must themselves be "ree in the same sense. ?t complements the %B. %eneral Public License6 Ghich is a cop,le"t license esi#ne "or "ree so"tGare. Ee have esi#ne this License in or er to use it "or manuals "or "ree so"tGare6 because "ree so"tGare nee s "ree ocumentationA a "ree pro#ram shoul oes. 3ut this License is not limite come Gith manuals provi in# the same "ree oms that the to so"tGare manualsK it can be use as a printe "or an, te0tual Gor86 this License boo8. Ee recommen so"tGare
cDocumentc6 beloG6 re"ers to an, such manual or Gor8. An, member o" the public is a licensee6 an as c,ouc. Cou accept the license i" ,ou cop,6 mo i", or permission un er cop,ri#ht laG. A c(o i"ie
)ersionc o" the Document means an, Gor8 containin# the Document or a portion o" it6 either
copie verbatim6 or Gith mo i"ications an Zor translate into another lan#ua#e. A c-econ ar, -ectionc is a name appen i0 or a "ront;matter section o" the Document that eals e0clusivel, Gith the relationship o" the publishers or authors o" the Document to the Documentas overall sub=ect Sor to relate mattersT an contains nothin# that coul "all irectl, Githin that overall sub=ect. S:hus6 i" the matters6 or o" le#al6 Document is in part a te0tboo8 o" mathematics6 a -econ ar, -ection ma, not e0plain an, mathematics.T :he relationship coul be a matter o" historical connection Gith the sub=ect or Gith relate commercial6 philosophical6 ethical or political position re#ar in# them. :he c?nvariant -ectionsc are certain -econ ar, -ections Ghose titles are esi#nate 6 as bein# those o"
?nvariant -ections6 in the notice that sa,s that the Document is release un er this License. ?" a section oes not "it the above e"inition o" -econ ar, then it is not alloGe to be esi#nate as ?nvariant. :he Document ma, contain >ero ?nvariant -ections. ?" the Document oes not i enti", an, ?nvariant -ections then there are none.
*5'
:he cCover :e0tsc are certain short passa#es o" te0t that are liste 6 as 7ront;Cover :e0ts or 3ac8;Cover :e0ts6 in the notice that sa,s that the Document is release un er this License. A 7ront;Cover :e0t ma, be at most 5 Gor s6 an a 3ac8;Cover :e0t ma, be at most *5 Gor s. A c:ransparentc cop, o" the Document means a machine;rea able cop,6 represente speci"ication is available to the #eneral public6 that is suitable "or revisin# the Gith #eneric te0t e itors or S"or ima#es compose Gi el, available raGin# e itor6 an in a "ormat Ghose
ocument strai#ht"orGar l,
that is suitable "or input to te0t "ormatters or "or automatic translation to thGart or iscoura#e subsequent "or an, substantial
to a variet, o" "ormats suitable "or input to te0t "ormatters. A cop, ma e in an otherGise :ransparent "ile "ormat Ghose mar8up6 or absence o" mar8up6 has been arran#e amount o" te0t. A cop, that is not c:ransparentc is calle c+paquec. /0amples o" suitable "ormats "or :ransparent copies inclu e plain A-C?? Githout mar8up6 :e0in"o input "ormat6 La:e9 input "ormat6 -%(L or 9(L usin# a publicl, available D:D6 an 4:(L6 Post-cript or PD7 PB%6 9C7 an proprietar, Gor available6 an stan ar ;con"ormin# simple an e ite onl, b, esi#ne "or human mo i"ication. /0amples o" transparent ima#e "ormats inclu e mo i"ication b, rea ers is not :ransparent. An ima#e "ormat is not :ransparent i" use
QP%. +paque "ormats inclu e proprietar, "ormats that can be rea the machine;#enerate 4:(L6 Post-cript or PD7 pro uce
processors6 -%(L or 9(L "or Ghich the D:D an Zor processin# tools are not #enerall, b, some Gor processors "or
output purposes onl,. :he c:itle Pa#ec means6 "or a printe boo86 the title pa#e itsel"6 plus such "olloGin# pa#es as are nee e to o
hol 6 le#ibl,6 the material this License requires to appear in the title pa#e. 7or Gor8s in "ormats Ghich Gor8as title6 prece in# the be#innin# o" the bo , o" the te0t. A section c/ntitle 9Cdc means a name
not have an, title pa#e as such6 c:itle Pa#ec means the te0t near the most prominent appearance o" the
contains 9Cd in parentheses "olloGin# te0t that translates 9Cd in another lan#ua#e. S4ere 9Cd stan s "or a speci"ic section name mentione beloG6 such as cAc8noGle #ementsc6 cDe icationsc6 c/n orsementsc6 or c4istor,c.T :o cPreserve the :itlec o" such a section Ghen ,ou mo i", the Document means that it remains a section c/ntitle 9Cdc accor in# to this e"inition. :he Document ma, inclu e Earrant, Disclaimers ne0t to the notice Ghich states that this License applies to the Document. :hese Earrant, Disclaimers are consi ere onl, as re#ar s voi an has no e""ect on the meanin# o" this License. to be inclu e b, re"erence in this License6 but isclaimin# GarrantiesA an, other implication that these Earrant, Disclaimers ma, have is
*54
in all copies6 an
that ,ou a
License. Cou ma, not use technical measures to obstruct or control the rea in# or "urther cop,in# o" the istribute. 4oGever6 ,ou ma, accept compensation in e0chan#e "or copies. ?" ,ou istribute a lar#e enou#h number o" copies ,ou must also "olloG the con itions in section '. Cou ma, also len copies6 un er the same con itions state above6 an ,ou ma, publicl, ispla, copies.
3ac8;Cover :e0ts on the bac8 cover. 3oth covers must also clearl, an visible. Cou ma, a other material on the covers in a
publisher o" these copies. :he "ront cover must present the "ull title Gith all Gor s o" the title equall, prominent an ition. Cop,in# Gith chan#es limite to the covers6 as lon# as the, preserve the title o" the Document an satis", these con itions6 can be treate as verbatim cop,in# in other respects. ?" the require te0ts "or either cover are too voluminous to "it le#ibl,6 ,ou shoul put the "irst ones liste Sas man, as "it reasonabl,T on the actual cover6 an continue the rest onto a =acent pa#es. ?" ,ou publish or istribute +paque copies o" the Document numberin# more than 1$$6 ,ou must either oGnloa e istribution
inclu e a machine;rea able :ransparent cop, alon# Gith each +paque cop,6 or state in or Gith each +paque cop, a computer;netGor8 location "rom Ghich the #eneral netGor8;usin# public has access to usin# public;stan ar material. ?" ,ou use the latter option6 ,ou must ta8e reasonabl, pru ent steps6 Ghen ,ou be#in location until at least one ,ear a"ter the last time ,ou a#ents or retailersT o" that e ition to the public. ?t is requeste 6 but not require 6 that ,ou contact the authors o" the Document Gell be"ore re istributin# an, lar#e number o" copies6 to #ive them a chance to provi e ,ou Gith an up ate version o" the Document. netGor8 protocols a complete :ransparent cop, o" the Document6 "ree o" a
o" +paque copies in quantit,6 to ensure that this :ransparent cop, Gill remain thus accessible at the state istribute an +paque cop, S irectl, or throu#h ,our
&" MODIFICATIONS
Cou ma, cop, an above6 provi e istribute a (o i"ie )ersion o" the Document un er the con itions o" sections * an )ersion un er precisel, this License6 Gith the (o i"ie ' that ,ou release the (o i"ie
)ersion "illin# the role o" the Document6 thus licensin# istribution an mo i"ication o" the (o i"ie )ersion to Ghoever possesses a cop, o" it. ?n a ition6 ,ou must o these thin#s in the (o i"ie )ersionA
A. .se in the :itle Pa#e San on the covers6 i" an,T a title istinct "rom that o" the Document6 an "rom
those o" previous versions SGhich shoul 6 i" there Gere an,6 be liste in the 4istor, section o" the
*55
DocumentT. Cou ma, use the same title as a previous version i" the ori#inal publisher o" that version #ives permission.
3. List on the :itle Pa#e6 as authors6 one or more persons or entities responsible "or authorship o" the
mo i"ications in the (o i"ie requirement. )ersion6 to#ether Gith at least "ive o" the principal authors o" the Document Sall o" its principal authors6 i" it has "eGer than "iveT6 unless the, release ,ou "rom this
C. -tate on the :itle pa#e the name o" the publisher o" the (o i"ie )ersion6 as the publisher. D. Preserve all the cop,ri#ht notices o" the Document. /. A 7.
an appropriate cop,ri#ht notice "or ,our mo i"ications a =acent to the other cop,ri#ht notices. en um beloG. ?nclu e6 imme iatel, a"ter the cop,ri#ht notices6 a license notice #ivin# the public permission to use the (o i"ie )ersion un er the terms o" this License6 in the "orm shoGn in the A the Documentas license notice.
%. Preserve in that license notice the "ull lists o" ?nvariant -ections an require Cover :e0ts #iven in 4. ?nclu e an unaltere cop, o" this License. ?.
Preserve the section /ntitle title6 ,ear6 neG authors6 an no section /ntitle c4istor,c6 Preserve its :itle6 an publisher o" the (o i"ie a to it an item statin# at least the )ersion as #iven on the :itle Pa#e. ?" there is an item escribin# the (o i"ie
c4istor,c in the Document6 create one statin# the title6 ,ear6 authors6 an
publisher o" the Document as #iven on its :itle Pa#e6 then a )ersion as state in the previous sentence.
Q.
Preserve the netGor8 location6 i" an,6 #iven in the Document "or public access to a :ransparent cop, o" the Document6 an Gas base li8eGise the netGor8 locations #iven in the Document "or previous versions it in the c4istor,c section. Cou ma, omit a netGor8 location "or a on. :hese ma, be place
Gor8 that Gas publishe at least "our ,ears be"ore the Document itsel"6 or i" the ori#inal publisher o" the version it re"ers to #ives permission.
F. 7or an, section /ntitle cAc8noGle #ementsc or cDe icationsc6 Preserve the :itle o" the section6 an
preserve in the section all the substance an an Zor e ications #iven therein. tone o" each o" the contributor ac8noGle #ements in their te0t an in their titles. in the (o i"ie
L. Preserve all the ?nvariant -ections o" the Document6 unaltere (. Delete an, section /ntitle
)ersion.
-ection numbers or the equivalent are not consi ere part o" the section titles. c/n orsementsc. -uch a section ma, not be inclu e
license notice. :hese titles must be istinct "rom an, other section titles. Cou ma, a a section /ntitle c/n orsementsc6 provi e it contains nothin# but en orsements o" ,our
*51
(o i"ie
)ersion b, various parties;;"or e0ample6 statements o" peer revieG or that the te0t has been
approve b, an or#ani>ation as the authoritative e"inition o" a stan ar . Cou ma, a :e0t an a passa#e o" up to "ive Gor s as a 7ront;Cover :e0t6 an a passa#e o" up to *5 Gor s as a 3ac8; o" the list o" Cover :e0ts in the (o i"ie )ersion. +nl, one passa#e o" 7ront;Cover e b, ,ou or b, arran#ement e b, Sor throu#h arran#ements ma e b,T an, one entit,. ?" the anotherK but ,ou ma, replace the ol
Document alrea , inclu es a cover te0t "or the same cover6 previousl, a ma e b, the same entit, ,ou are actin# on behal" o"6 ,ou ma, not a one6 on e0plicit permission "rom the previous publisher that a :he authorSsT an publisherSsT o" the Document
e the ol one.
"or publicit, "or or to assert or impl, en orsement o" an, (o i"ie )ersion.
combine Gor8 in its license notice6 an that ,ou preserve all their Earrant, Disclaimers. :he combine Gor8 nee onl, contain one cop, o" this License6 an multiple i entical ?nvariant -ections
ma, be replace
Gith a sin#le cop,. ?" there are multiple ?nvariant -ections Gith the same name but in# at the en o" it6 in parentheses6 the
i""erent contents6 ma8e the title o" each such section unique b, a
name o" the ori#inal author or publisher o" that section i" 8noGn6 or else a unique number. (a8e the same a =ustment to the section titles in the list o" ?nvariant -ections in the license notice o" the combine Gor8. ?n the combination6 ,ou must combine an, sections /ntitle "ormin# one section /ntitle c4istor,c in the various ori#inal ocuments6
cAc8noGle #ementsc6 an
an, sections /ntitle cDe icationsc. Cou must elete all sections /ntitle c/n orsements.c
*5!
in ivi ual Gor8s permit. Ehen the Document is inclu e in an a##re#ate6 this License oes not appl, to the other Gor8s in the a##re#ate Ghich are not themselves erivative Gor8s o" the Document. ?" the Cover :e0t requirement o" section ' is applicable to these copies o" the Document6 then i" the Document is less than one hal" o" the entire a##re#ate6 the Documentas Cover :e0ts ma, be place on covers that brac8et the Document Githin the a##re#ate6 or the electronic equivalent o" covers i" the Document is in electronic "orm. +therGise the, must appear on printe covers that brac8et the Ghole a##re#ate.
+" TRANSLATION
:ranslation is consi ere a 8in o" mo i"ication6 so ,ou ma, istribute translations o" the Document un er ition to the all the the terms o" section 4. Replacin# ?nvariant -ections Gith translations requires special permission "rom their cop,ri#ht hol ers6 but ,ou ma, inclu e translations o" some or all ?nvariant -ections in a license notices in the Document6 an /n#lish version o" this License an ori#inal version Gill prevail. ?" a section in the Document is /ntitle cAc8noGle #ementsc6 cDe icationsc6 or c4istor,c6 the requirement an, Earrant, Disclaimers6 provi e ori#inal versions o" these ?nvariant -ections. Cou ma, inclu e a translation o" this License6 an the ori#inal versions o" those notices an
that ,ou also inclu e the ori#inal isclaimers. ?n case o" a isclaimer6 the
Ssection 4T to Preserve its :itle Ssection 1T Gill t,picall, require chan#in# the actual title.
," TERMINATION
Cou ma, not cop,6 mo i",6 sublicense6 or istribute the Document e0cept as e0pressl, provi e "or un er this License. An, other attempt to cop,6 mo i",6 sublicense or istribute the Document is voi 6 an Gill automaticall, terminate ,our ri#hts un er this License. 4oGever6 parties Gho have receive "ull compliance. copies6 or
ri#hts6 "rom ,ou un er this License Gill not have their licenses terminate so lon# as such parties remain in
*55
version o" this License cor an, later versionc applies to it6 ,ou have the option o" con itions either o" that speci"ie version or o" an, later version that has been oes not speci", a version ra"tT b, the 7ree -o"tGare Snot as a ra"tT b, the 7ree -o"tGare 7oun ation. ?" the Document
number o" this License6 ,ou ma, choose an, version ever publishe 7oun ation.
*52