Contentsaaa

Embed Size (px)

Citation preview

  • 8/11/2019 Contentsaaa

    1/85

    So an ORM layer maps tables to classes, rows to objects, and columns toattributes of those objects. Class methods are used to perform table-leveloperations, and instance methods perform operations on the individualrows.In a typical ORM library, you supply confiuration data to specify the map-

    pins between thins in the database and thins in the proram. !roram-mers usin these ORM tools often find themselves creatin and maintain-in a boatload of "M# confiuration files.$ctive Record$ctive Record is the ORM layer supplied with Rails. It closely follows thestandard ORM model% tables map to classes, rows to objects, and columnsto object attributes. It differs from most other ORM libraries in the way it isconfiured. &y relyin on convention and startin with sensible defaults,$ctive Record minimi'es the amount of confiuration that developers per-form. (o illustrate this, here)s a proram that uses $ctive Record to wrapour orders table.

    re*uire + activerecord +class Order $ctiveRecord%%&aseendorder Order.find/01order.discount 2.3order.save(his code uses the new Order class to fetch the order with an id of 0 andmodify the discount. /4e)ve omitted the code that creates a database con-nection for now.1 $ctive Record relieves us of the hassles of dealin withthe underlyin database, leavin us free to wor5 on business loic.&ut $ctive Record does more than that. $s you)ll see when we develop our

    shoppin cart application, startin on pae 67, $ctive Record interatesseamlessly with the rest of the Rails framewor5. If a web form containsdata related to a business object, $ctive Record can e8tract it into ourmodel. $ctive Record supports sophisticated validation of model data, andif the form data fails validations, the Rails views can e8tract and formaterrors with just a sinle line of code.$ctive Record is the solid model foundation of the Rails M9C architecture.(hat)s why we devote two chapters to it, startin on pae 0:2.Report erratum!repared e8clusively for Rida $l &ara'i$ C(IO; ! $C< % ( => 9 I>4 $;? C O;(RO##>R [email protected] $ction !ac5% (he 9iew and Controller4hen you thin5 about it, the view and controller parts of M9C are prettyintimate. (he controller supplies data to the view, and the controllerreceives bac5 events from the paes enerated by the views. &ecause ofthese interactions, support for views and controllers in Rails is bundledinto a sinle component, $ction !ac5.?on)t be fooled into thin5in that your application)s view code and con-troller code will be jumbled up just because $ction !ac5 is a sinle compo-

  • 8/11/2019 Contentsaaa

    2/85

    nent. Buite the contrary Rails ives you the separation you need to writeweb applications with clearly demarcated code for control and presenta-tion loic.9iew SupportIn Rails, the view is responsible for creatin either all or part of a pae to

    be displayed in a browser. 7 $t its simplest, a view is a chun5 of =(M#code that displays some fi8ed te8t. More typically you)ll want to includedynamic content created by the action method in the controller.In Rails, dynamic content is enerated by templates, which come in twoflavors. One embeds snippets of Ruby code within the view)s =(M# usina Ruby tool called >Rb /or >mbedded Ruby1. 6 (his approach is very fle8-ible, but purists sometimes complain that it violates the spirit of M9C.&y embeddin code in the view we ris5 addin loic that should be in themodel or the controller. (his complaint is larely roundless% views con-tained active code even in the oriinal M9C architectures. Maintainin aclean separation of concerns is part of the job of the developer. /4e loo5 at

    =(M# templates in Section [email protected], R=(M# (emplates, on pae 772.1Rails also supports builder-style views. (hese let you construct "M# doc-uments usin Ruby codeDthe structure of the enerated "M# will auto-matically follow the structure of the code. 4e discuss builder templatesstartin on pae 7A:.$nd the ControllerE(he Rails controller is the loical center of your application. It coordinatesthe interaction between the user, the views, and the model. =owever,7 Or an "M# response, or an e-mail, or.... (he 5ey point is that views enerate theresponsebac5 to the user.6 (his approach miht be familiar to web developers wor5in with !=! or Fava)s FS!technoloy.Report erratum!repared e8clusively for Rida $l &ara'i$ C(IO; ! $C< % ( => 9 I>4 $;? C O;(RO##>R 0GRails handles most of this interaction behind the scenes the code youwrite concentrates on application-level functionality. (his ma5es Railscontroller code remar5ably easy to develop and maintain.(he controller is also home to a number of important ancillary services.H It is responsible for routin e8ternal re*uests to internal actions. Ithandles people-friendly R#s e8tremely well.H It manaes cachin, which can ive applications orders-of-manitudeperformance boosts.H It manaes helper modules, which e8tend the capabilities of the viewtemplates without bul5in up their code.H It manaes sessions, ivin users the impression of an onoin inter-action with our applications.(here)s a lot to Rails. Rather than attac5 it component by component, let)sroll up our sleeves and write a couple of wor5in applications. In the ne8t

  • 8/11/2019 Contentsaaa

    3/85

    chapter we)ll install Rails. $fter that we)ll write somethin simple, just toma5e sure we have everythin installed correctly. In Chapter 3, (he ?epot$pplication, on pae 67, we)ll start writin somethin more substantialDasimple online store application.Report erratum

    !repared e8clusively for Rida $l &ara'iChapter 7Installin Rails&efore you can start writin a Rails application, you)ll need to downloadthe Rails framewor5 and install it on your computer. $ll you need to runRails is a Ruby interpreter /version 0.G.A or later1 and the Rails code. =ow-ever, thins o easier if you also have the RubyJems pac5ae manaementsystem available, so we)ll tal5 about ettin that installed too. Kinally, ifyou use a database other than MySB#, you may need to install the appro-priate Ruby libraries to interface with it.Kair warnin% this is a tedious chapter, full of Lclic5 that and Ltype this

    instructions. Kortunately, it)s short, and we)ll et on to the e8citin stuffshortly.#et)s loo5 at the installation instructions for 4indows, OS ", and #inu8.7.0 Installin on 4indows0. Kirst, let)s chec5 to see if you already have Ruby installed. &rinup a command prompt /usin Start N Run N cmd , or Start N !rorams N$ccessories N Command !rompt 1, and type ruby -v . If Ruby responds,and if it shows a version number at or above 0.G.A, we may well be inbusiness. One more chec5Dlet)s see if you have RubyJems installed.(ype em --version . If you don)t et an error, s5ip to step 7. Otherwise,we)ll install a fresh Ruby.A. If Ruby is not installed, there)s a convenient one-clic5 installer athttp%rubyinstaller.rubyfore.or . Kollow the download lin5, and run theresultin installer. Pou may as well install everythinDit)s a verysmall pac5ae, and you)ll et RubyJems as well.!repared e8clusively for Rida $l &ara'iI ;S($##I;J O; M $C OS " A27. ;ow we)ll use RubyJems to install Rails and a few thins that Railsneeds.C%QN em install rails --include-dependenciesConratulationsE Pou)re now on Rails.7.A Installin on Mac OS "0. OS " version 02.6 /(ier1 ships with Ruby 0.G.A. Pou can verify thisby startin the terminal application /use the Kinder to naviate to$pplications tilities and double-clic5 on (erminal 1 and enterin ruby -vat the prompt. /If you)re not runnin (ier, you)ll need to install Ruby0.G.A or later yourself. (he ni8 instructions that follow should help.1A. ;e8t you have to install RubyJems. Jo to http%rubyems.rubyfore.orand follow the download lin5. OS " will typically unpac5 the archivefile for you, so all you have to do is naviate to the downloaded direc-

  • 8/11/2019 Contentsaaa

    4/85

    tory and /in the (erminal application1 typedaveN tar 8'f rubyems-2.G.02.tar.'daveN cd rubyems-2.G.02rubyems-2.G.02N sudo ruby setup.rb!assword% enter your passwordN

    7. 4e)ll now use RubyJems to install Rails. Still in the (erminal appli-cation, issue the followin command.daveN sudo em install rails --include-dependenciesConratulationsE Pou)re now on Rails.7.7 Installin on ni8#inu8Pou)ll need to have Ruby 0.G.A /or later1 and RubyJems installed in orderto install Rails.0. Many modern distributions come with Ruby installed. &rin up yourfavorite shell and type ruby -v . If Ruby responds, and is at least version0.G.A, s5ip to step 7.A. Pou)ll probably be able to find a prepac5aed version of Ruby for your

    distribution. If not, Ruby is simple to install from source.a1 ?ownload ruby-8.y.'.tar.' from http%www.ruby-lan.oren .b1 ntar the distribution, and enter the top-level directory.c1 ?o the usual open-source build.Report erratum!repared e8clusively for Rida $l &ara'iR $I#S $;? ? $($&$S>S A0Ruby on Mac OS " (ierIt)s ood that $pple includes Ruby in OS ". nfortunately, Ruby isn)t confi-ured particularly well in OS " version 02.6 /(ier1. Support for the readlinelibrary isn)t confiured, ma5in interactive tools such as irb a lot harderto use. (he Ruby build environment is also incorrect, so you can)t builde8tension libraries until you fi8 it. $nd, just to ma5e matters worse, we havereports that the Ruby MySB# e8tension library doesn)t wor5 properly.One way of addressin both issues is to follow #ucas Carlson)s instructionsat http%tech.rufy.comentry6 /you)ll need the developer tools installed1.Once you)ve done this, you should be able to reinstall the Ruby MySB#em and thins should start wor5in.$n alternative for the adventurous is to reinstall Ruby usin fin5 or ?arwin!orts. (hat)s a pretty bi topic, and not one that we)ll cover here.daveN tar 8'f ruby-8.y.'.tar.'daveN cd ruby-8.y.'ruby-8.y.'N .confiureruby-8.y.'N ma5eruby-8.y.'N ma5e testruby-8.y.'N sudo ma5e install!assword% enter your passwordN7. Install RubyJems. Jo to http%rubyems.rubyfore.or , and follow thedownload lin5. Once you have the file locally, enter the followin inyour shell window.

  • 8/11/2019 Contentsaaa

    5/85

    daveN tar 8'f rubyems-2.G.02.tar.'daveN cd rubyems-2.G.02rubyems-2.G.02N sudo ruby setup.rb!assword% enter your passwordN6. 4e)ll now use RubyJems to install Rails. Still in the shell, issue the

    followin command.daveN sudo em install rails --include-dependencies$nd /one last time1, conratulationsE Pou)re now on Rails.7.6 Rails and ?atabasesIf your Rails application uses a database /and most do1, there)s one moreinstallation step you may have to perform before you can start develop-ment.Report erratum!repared e8clusively for Rida $l &ara'iR $I#S $;? ? $($&$S>S AARails wor5s with the ?&A, MySB#, Oracle, !ostres, SB# Server, and

    SB#ite databases. Kor all but MySB#, you)ll need to install a databasedriver, a library that Rails can use to connect to and use your databaseenine. (his section contains the lin5s and instructions to et that done.&efore we et into the uly details, let)s see if we can s5ip the pain alto-ether. If you don)t care what database you use because you just wantto e8periment with Rails, our recommendation is that you try MySB#. It)seasy to install, and Rails comes with a built-in driver /written in pureRuby1 for MySB# databases. Pou can use it to connect a Rails applicationto MySB# with no e8tra wor5. (hat)s one of the reasons that the e8amplesin this boo5 all use MySB#. 0 If you do end up usin MySB#, remember tochec5 the license if you)re distributin your application commercially.If you already have MySB# installed on your system, you)re all done. Oth-erwise, visit http%dev.mys*l.com , and follow their instructions on installina MySB# database on your machine. Once you have MySB# runnin, youcan safely s5ip ahead to Section 7., Rails and IS!s.If you)re still readin this, it means you)re wantin to connect to a databaseother than MySB#. (o do this, you)re oin to have to install a databasedriver. (he database libraries are all written in C and are primarily dis-tributed in source form. If you don)t want to o to the bother of buildina driver from source, have a careful loo5 on the driver)s web site. Manytimes you)ll find that the author also distributes binary versions.If you can)t find a binary version, or if you)d rather build from sourceanyway, you)ll need a development environment on your machine to buildthe library. nder 4indows, this means havin a copy of 9isual CTT.nder #inu8, you)ll need cc and friends /but these will li5ely already beinstalled1.nder OS ", you)ll need to install the developer tools /they come with theoperatin system, but aren)t installed by default1. Once you)ve done that,you)ll also need to fi8 a minor problem in the $pple version of Ruby /unlessyou already installed the fi8 from #ucas Carlson described in the sidebar

  • 8/11/2019 Contentsaaa

    6/85

    on the precedin pae1. Run the followin commands.daveN U Pou only need these commands under OS " V(ierVdaveN sudo em install fi8rbconfidaveN sudo fi8rbconfi0 =avin said that, if you want to put a hih-volume application into production, and

    you)re basin it on MySB#, you)ll probably want to install the low-level MySB#interfacelibrary anyway, as it offers better performance.Report erratum!repared e8clusively for Rida $l &ara'iR $I#S $;? ? $($&$S>S A7?atabases and (his &oo5$ll the e8amples in this boo5 were developed usin MySB# /version 6.0.Gor thereabouts1. If you want to follow alon with our code, it)s probablysimplest if you use MySB# too. If you decide to use somethin different, itwon)t be a major problem. Pou)ll just have to ma5e minor adjustments to

    the ??# we use to create tables, and you)ll need to use that database)ssynta8 for some of the SB# we use in *ueries. /Kor e8ample, later in theboo5 we)ll use the MySB# now /1 function to compare a database columnaainst the current date and time. ?ifferent databases will use a differentname for the now /1 function.1(he followin table lists the available database adapters and ives lin5s totheir respective home paes.?&A http%raa.ruby-lan.orprojectruby-dbAMySB# http%www.tmtm.orenmys*lrubyOracle http%rubyfore.orprojectsruby-ociG!ostres http%ruby.scriptin.capostresSB# Server /see note after table1SB#ite http%rubyfore.orprojectss*lite-ruby(here is a pure-Ruby version of the !ostres adapter available. ?ownloadpostres-pr from the Ruby-?&I pae at http%rubyfore.orprojectsruby-dbi .MySB# and SB#ite are also available for download as RubyJems / mys*land s*lite respectively1.Interfacin to SB# Server re*uires a little effort. (he followin is based ona note written by Foey Jibson, who wrote the Rails adapter.$ssumin you used the one-clic5 installer to load Ruby onto your system,you already have most of the libraries you need to connect to SB# Server.=owever, the $?O module is not installed. Kollow these steps.0. Kind the directory tree holdin your Ruby installation / C%QRuby bydefault1. &elow it is the folder QRubyQlibQrubyQsiterubyQ0.GQ?&? . Insidethis folder, create the directory $?O .A. 4ander over to http%ruby-dbi.sourcefore.net and et the latest sourcedistribution of Ruby-?&I.7. n'ip the ?&I distribution into a local folder. ;aviate into this folder,and then to the directory srcQlibQdbdado . Copy the file $?O.rb fromReport erratum

  • 8/11/2019 Contentsaaa

    7/85

    !repared e8clusively for Rida $l &ara'i< >>!I;J ! - (O -? $(> A6this directory into the $?O directory in the Ruby tree that you createdin step 0.(he SB# Server adapter will wor5 only on 4indows systems, as it relies on

    4in7AO#>.7.3

  • 8/11/2019 Contentsaaa

    8/85

    a directory called wor5 . In that directory, use the rails command to createan application called demo . &e slihtly careful hereDif you have an e8ist-in directory called demo , you will be as5ed if you want to overwrite anye8istin files.!repared e8clusively for Rida $l &ara'i

    C R>$(I;J $ ; >4 $ !!#IC$(IO; AdaveN cd wor5wor5N rails democreatecreate appapiscreate appcontrollerscreate apphelpers% % %create lodevelopment.locreate lotest.lowor5N

    (he command has created a directory named demo . !op down into thatdirectory, and list its contents /usin ls on a ni8 bo8 or dir under 4in-dows1. Pou should see a bunch of files and subdirectories.wor5N cd demodemoN ls -pC=$;J>#OJ app db lo testR>$?M> components doc public vendorRa5efile confi lib script$ll these directories /and the files they contain1 can be intimidatin to startwith, but we can inore most of them when we start out. Kor now, we needto use only one of them, the public directory.$s its name suests, the public directory contains the files that we e8poseto our end users, the people usin our application. (he 5ey files arethe dispatchers% dispatch.ci , dispatch.fci , and dispatch.rb . (he dispatch- dispatchersers are responsible for acceptin incomin re*uests from users sittin attheir browsers and directin those re*uests to the code in our application.(hey)re important files, but we won)t need to touch them for now.Pou)ll also notice that there)s a script directory underneath demo . It con-tains some utility scripts that we)ll be usin as we develop our applications.Kor now, we)ll use the script called server . It starts a stand-alone webserver that can run our newly created Rails application under 4>&ric5. 0So, without further ado, let)s start the application you just wrote.demoN ruby scriptserverN Rails application started on http%2.2.2.2%7222XA223-2A-A 2:%0%67Y I;KO 4>&ric5 0.7.0XA223-2A-A 2:%0%67Y I;KO ruby 0.G.A /A226-2G-A61 [email protected] 2:%0%67Y I;KO 4>&ric5%%=((!Server-start% pidAG7 port7222$s the last line of the start-up tracin indicates, we just started a webserver on port 7222. A 4e can access the application by pointin a browserat http%localhost%7222 . (he result is shown in Kiure 6.0.

  • 8/11/2019 Contentsaaa

    9/85

    0 4>&ric5 is a pure-Ruby web server that is distributed with Ruby 0.G.0 and later.A (he2.2.2.2 part of the address means that 4>&ric5 will accept connections on all inter-faces. On ?ave)s OS " system, that means both local interfaces /[email protected] and %%01 andhis

    #$; connection.Report erratum!repared e8clusively for Rida $l &ara'i= >##O , R $I#S E A@Kiure 6.0% ;ewly Created Rails $pplication4e)re oin to leave 4>&ric5 runnin in this console window. #ater on,as we write application code and run it via our browser, we)ll see thisconsole window tracin the incomin re*uests. 4hen you)re done usinthe application, you can press control-C to stop 4>&ric5.$t this point, we have a new application runnin, but it has none of ourcode in it. #et)s rectify this situation.

    6.A =ello, RailsE?ave spea5in% I can)t help itDI just have to write a =ello, 4orldE proramto try out a new system. (he e*uivalent in Rails would be an applicationthat sends our cheery reetin to a browser.$s we discussed in Chapter A, (he $rchitecture of Rails $pplications, onpae :, Rails is a Model-9iew-Controller framewor5. Rails accepts incom-in re*uests from a browser, decodes the re*uest to find a controller, andcalls an action method in that controller. (he controller then invo5es aparticular view to display the results bac5 to the user. (he ood news isthat Rails ta5es care of most of the internal plumbin that lin5s all thesethins toether. (o write our simple =ello, 4orldE application, we needcode for a controller and a view. 4e don)t need code for a model, as we)renot dealin with any data. #et)s start with the controller.Report erratum!repared e8clusively for Rida $l &ara'i= >##O , R $I#S E AGIn the same way that we used the rails command to create a new Railsapplication, we can also use a enerator script to create a new controllerfor our project. (his command is called enerate , and it lives in the scriptsubdirectory of the demo project we created. So, to create a controllercalled say , we ma5e sure we)re in the demo directory and run the script,passin in the name of the controller we want to create. 7demoN ruby scriptenerate controller Saye8ists appcontrollerse8ists apphelperscreate appviewssaye8ists testfunctionalcreate appcontrollerssaycontroller.rbcreate testfunctionalsaycontrollertest.rbcreate apphelperssayhelper.rb

  • 8/11/2019 Contentsaaa

    10/85

    (he script los the files and directories it e8amines, notin when it addsnew Ruby scripts or directories to your application. Kor now, we)re inter-ested in one of these scripts and /in a minute1 the new directory.(he source file we)ll be loo5in at is the controller. Pou)ll find it in the fileappcontrollerssaycontroller.rb . #et)s have a loo5 at it. definin classes

    W pae 6@0Kile A2: class SayController $pplicationControllerend!retty minimal, ehW SayController is an empty class that inherits from $ppli-cationController , so it automatically ets all the default controller behavior.#et)s spice it up. 4e need to add some code to have our controller handlethe incomin re*uest. 4hat does this code have to doW Kor now, it)ll donothinDwe simply need an empty action method. So the ne8t *uestionis, what should this method be calledW $nd to answer this *uestion, weneed to loo5 at the way Rails handles re*uests.Rails and Re*uest R#s

    #i5e any other web application, a Rails application appears to its users tobe associated with a R#. 4hen you point your browser at that R#, youare tal5in to the application code, which enerates a response bac5 toyou.=owever, the real situation is somewhat more complicated than that. #et)simaine that your application is available at the R# http%prapro.comonlinedemo . (he web server that is hostin your application is fairly smart7 (he concept of the Lname of the controller is actually more comple8 than you mihtthin5, and we)ll e8plain it in detail in Section 07.6, ;amin Conventions, on pae 0G2.Kornow, let)s just assume the controller is called Say.Report erratum!repared e8clusively for Rida $l &ara'i= >##O , R $I#S E A:http%prapro.comonlinedemosayhello0. Kirst part of R# identifies the applicationA. ;e8t part selects a controller /say17. #ast part identifies the action to invo5eKiure 6.A% R#s $re Mapped to Controllers and $ctionsabout paths. It 5nows that once it sees the onlinedemo part of the path,it must be tal5in to the application. $nythin past this in the incominR# will not chane thatDthe same application will still be invo5ed. $nyadditional path information is passed to the application, which can use itfor its own internal purposes.Rails uses the path to determine the name of the controller to use andthe name of the action to invo5e on that controller. 6 (his is illustrated inKiure 6.A . (he first part of the path followin the application is the nameof the controller, and the second part is the name of the action. (his isshown in Kiure 6.7, on the followin pae.Our Kirst $ction

  • 8/11/2019 Contentsaaa

    11/85

    #et)s add an action called hello to our say controller. Krom the discussionin the previous section, we 5now that addin a hello action means creatina method called hello in the class SayController . &ut what should it doW Kornow, it doesn)t have to do anythin. Remember that a controller)s job is toset up thins so that the view 5nows what to display. In our first appli-

    cation, there)s nothin to set up, so an empty action will wor5 fine. se methodsW pae 6:your favorite editor to chane the file saycontroller.rb in the appcontrollersdirectory, addin the hello /1 method as shown.Kile A02 class SayController $pplicationControllerdef helloendend6 Rails is fairly fle8ible when it comes to parsin incomin R#s.In this chapter, wedescribe the default mechanism. 4e)ll show how to override this in Section 0.7, Routin

    Re*uests, on pae AG2.Report erratum!repared e8clusively for Rida $l &ara'i= >##O , R $I#S E 72http%prapro.comonlinedemosayhelloclass CustomerController ...def helloU actions...endendclass OrderController ...def helloU actions...endendclass pdateController ...def helloU actions...endendclass SayController ...def helloU actions...endendclass #oinController ...def helloU actions...endend

  • 8/11/2019 Contentsaaa

    12/85

    class SayController ...def helloU code for hello action...endend

    (heninvo5e itshello/1methodCreate aninstance of classSayControllerKiure 6.7% Rails Routes to Controllers and $ctions;ow let)s try callin it. Kind a browser window, and naviate to the R#http%localhost%7222sayhello . /;ote that in this test environment we don)thave any application strin at the front of the pathDwe route directly to

    the controller.1 Pou)ll see somethin that loo5s li5e the followin.It miht be annoyin, but the error is perfectly reasonable /apart from theweird path1. 4e created the controller class and the action method, butwe haven)t told Rails what to display. $nd that)s where the views comein. Remember when we ran the script to create the new controllerW (hecommand added three files and a new directory to our application. (hatdirectory contains the template files for the controller)s views. In our case,we created a controller named say , so the views will be in the directoryappviewssay .(o complete our =ello, 4orldE application, let)s create a template. &ydefault, Rails loo5s for templates in a file with the same name as theReport erratum!repared e8clusively for Rida $l &ara'i= >##O , R $I#S E 70action it)s handlin. In our case, that means we need to create a file calledappviewssayhello.rhtml . /4hy . rhtml W 4e)ll e8plain in a minute.1 Kor now,let)s just put some basic =(M# in there.Kile A00 htmlNheadNtitleN=ello, RailsEtitleNheadNbodyNh0N=ello from RailsEh0NbodyNhtmlNSave the file hello.rhtml , and refresh your browser window. Pou should seeit display our friendly reetin. ;otice that we didn)t have to restart theapplication to see the update. ?urin development, Rails automaticallyinterates chanes into the runnin application as you save files.So far, we)ve added code to two files in our Rails application tree. 4e

  • 8/11/2019 Contentsaaa

    13/85

    added an action to the controller, and we created a template to display apae in the browser. (hese files live in standard locations in the Rails hier-archy% controllers o into appcontrollers , and views o into subdirectoriesof appviews . (his is shown in Kiure 6.6, on the ne8t pae.Ma5in It ?ynamic

    So far, our Rails application is pretty borinDit just displays a static pae.(o ma5e it more dynamic, let)s have it show the current time each time itdisplays the pae.(o do this, we need to ma5e a chane to the template file in the viewDitnow needs to include the time as a strin. (hat raises two *uestions. Kirst,how do we add dynamic content to a templateW Second, where do we etthe time fromW?ynamic Content(here are two ways of creatin dynamic templates in Rails. One uses atechnoloy called &uilder , which we discuss in Section [email protected], &uilder tem-plates, on pae 7A:. (he second way, which we)ll use here, is to embed

    Report erratum!repared e8clusively for Rida $l &ara'i= >##O , R $I#S E 7Ademoappcontrollersviewsmodelshello.rhtmlsaycontroller.rbsayclass SayController $pplicationControllerdef helloendendhtmlNheadNtitleN=ello, RailsEtitleNheadNbodyNh0N=ello from RailsEh0NbodyNhtmlNKiure 6.6% Standard #ocations for Controllers and 9iewsRuby code in the template itself. (hat)s why we named our template filehello.rhtml % the . rhtml suffi8 tells Rails to e8pand the content in the file usina system called >Rb /for >mbedded Ruby1.>Rb is a filter that ta5es an . rhtml file and outputs a transformed version.(he output file is often =(M# in Rails, but it can be anythin. ;ormal con-tent is passed throuh without bein chaned. =owever, content between

  • 8/11/2019 Contentsaaa

    14/85

    Z and ZN is interpreted as Ruby code and e8ecuted. (he result of thate8ecution is converted into a strin, and that value is substituted into thefile in place of the Z ... ZN se*uence. Kor e8ample, chane hello.rhtml tocontain the followin.Kile 07 ulN

    liN$ddition% Z 0TA ZN liNliNConcatenation% Z VcowV T VboyV ZN liN 0.hour.fromnowW pae 0G3liN(ime in one hour% Z 0.hour.fromnow ZN liNulN4hen you refresh your browser, the template will enerate the followin=(M#.ulNliN$ddition% 7 liNliNConcatenation% cowboy liNliN(ime in one hour% Sat Keb A 0G%77%03 CS( A223 liN

    ulNReport erratum!repared e8clusively for Rida $l &ara'i= >##O , R $I#S E 77Ma5in ?evelopment >asierPou miht have noticed somethin about the development we)ve beendoin so far. $s we)ve been addin code to our application, we haven)thad to touch the runnin application. It)s been happily chuin away inthe bac5round. $nd yet each chane we ma5e is available wheneverwe access the application throuh a browser. 4hat ivesWIt turns out that the 4>&ric5-based Rails dispatcher is pretty clever. Indevelopment mode /as opposed to testin or production1, it automati-cally reloads application source files when a new re*uest comes alon.(hat way, when we edit our application, the dispatcher ma5es sure it)srunnin the most recent chanes. (his is reat for development.=owever, this fle8ibility comes at a costDit causes a short pause after youenter a R# before the application responds. (hat)s caused by the dis-patcher reloadin stuff. Kor development it)s a price worth payin, butin production it would be unacceptable. &ecause of this, this feature isdisabled for production deployment /see Chapter AA, ?eployment andScalin, on pae 6621.In the browser window, you)ll see somethin li5e the followin.H $ddition% 7H Concatenation% cowboyH (ime in one hour% Sat Keb A 0G%77%03 CS( A223In addition, stuff in rhtml between Z and ZN /without an e*uals sin1 isinterpreted as Ruby code that is e8ecuted with no substitution bac5 intothe output. (he interestin thin about this 5ind of processin, thouh,is that it can be intermi8ed with non-Ruby code. Kor e8ample, we couldma5e a festive version of hello.rhtml .

  • 8/11/2019 Contentsaaa

    15/85

    7.timesW pae 6@@ Z 7.times do ZN=oEbr NZ end ZNMerry ChristmasE

    Refresh aain, and you)ll be listenin for sleih bells.=oE=oE=oEMerry ChristmasE;ote how the te8t in the file within the Ruby loop is sent to the outputstream once for each iteration of the loop.Report erratum!repared e8clusively for Rida $l &ara'i= >##O , R $I#S E 764e can mi8 the two forms. In this e8ample, the loop sets a variable that is

    interpolated into the te8t each time the loop e8ecutes.Kile 07G Z 7.downto/01 do [count[ ZNZ count ZN...br NZ end ZN#ift offE(hat will send the followin to the browser.7...br NA...br N0...br N#ift offE(here)s one last thin with >Rb. Buite often the values that you as5 itto substitute usin Z...ZN contain less-than and ampersand charactersthat are sinificant to =(M#. (o prevent these messin up your pae /and,as we)ll see in Chapter A0, Securin Pour Rails $pplication, on pae 6A@, toavoid potential security problems1, you)ll want to escape these characters.Rails has a helper method, h /1, that does this. Most of the time, you)reoin to want to use it when substitutin values into =(M# paes.Kile 07: >mail% Z h/V$nn \ &ill fra'ers]isp.emailNV1 ZNIn this e8ample, the h /1 method prevents the special characters in thee-mail address from arblin the browser displayDthey)ll be escaped as=(M# entities. (he browser sees >mail% $nn \ &ill fra'ers]isp.emailN Dthespecial characters are displayed appropriately.$ddin the (imeOur oriinal problem was to display the time to users of our application.4e now 5now how to ma5e our application display dynamic data. (hesecond issue we have to address is wor5in out where to et the timefrom.One approach would be to embed a call to Ruby)s (ime.now /1 method in oursay.rhtml template.htmlN

  • 8/11/2019 Contentsaaa

    16/85

    headNtitleN=ello, RailsEtitleNheadNbodyNh0N=ello from RailsEh0N

    pN(he time is Z (ime.now ZNpNbodyNhtmlNReport erratum!repared e8clusively for Rida $l &ara'i= >##O , R $I#S E 73(his wor5s. >ach time we access this pae, the user will see the currenttime substituted into the body of the response. $nd for our trivial applica-tion, that miht be ood enouh. In eneral, thouh, we probably want to

    do somethin slihtly different. 4e)ll move the determination of the timeto be displayed into the controller and leave the view the simple job of dis-playin it. 4e)ll chane our action method in the controller to set the timevalue into an instance variable called ]time . instance variableW pae 6@AKile A0A class SayController $pplicationControllerdef hello]time (ime.nowendendIn the . rhtml template we)ll use this instance variable to substitute the timeinto the output.Kile A07 htmlNheadNtitleN=ello, RailsEtitleNheadNbodyNh0N=ello from RailsEh0NpNIt is now Z ]time ZN.pNbodyNhtmlN4hen we refresh our browser window, we see the time displayed, as shownin Kiure 6.3. ;otice that if you hit Refresh in your browser, the timeupdates each time the pae is displayed. #oo5s as if we)re really eneratindynamic content.4hy did we o to the e8tra trouble of settin the time to be displayed inthe controller and then usin it in the viewW Jood *uestion. In this appli-Kiure 6.3% =ello, 4orldE with a (ime ?isplay

  • 8/11/2019 Contentsaaa

    17/85

    Report erratum!repared e8clusively for Rida $l &ara'i= >##O , R $I#S E 7Foe $s5s...=ow ?oes the 9iew Jet the (imeW

    In the description of views and controllers, we showed the controller set-tin the time to be displayed into an instance variable. (he . rhtml file usedthat instance variable to substitute in the current time. &ut the instancedata of the controller object is private to that object. =ow does >Rb ethold of this private data to use in the templateW(he answer is both simple and subtle. Rails does some Ruby maic sothat the instance variables of the controller object are injected into thetemplate object. $s a conse*uence, the view template can access anyinstance variables set in the controller as if they were its own.cation, you could just embed the call to (ime.now /1 in the template, but byputtin it in the controller instead, you buy yourself some benefits. Kor

    e8ample, we may want to e8tend our application in the future to supportusers in many countries. In that case we)d want to locali'e the displayof the time, choosin both the format appropriate to the user)s locale anda time appropriate to their time 'one. (hat would be a fair amount ofapplication-level code, and it would probably not be appropriate to embedit at the view level. &y settin the time to display in the controller, we ma5eour application more fle8ibleDwe can chane the display format and time'one in the controller without havin to update any view that uses thattime object.(he Story So Kar#et)s briefly review how our current application wor5s.0. (he user naviates to our application. In our case, we do that usina local R# such as http%localhost%7222sayhello .A. Rails analy'es the R#. (he say part is ta5en to be the name of a con-troller, so Rails creates a new instance of the Ruby class SayController/which it finds in appcontrollerssaycontroller.rb 1.7. (he ne8t part of the R# path, hello , identifies an action. Railsinvo5es a method of that name in the controller. (his action methodReport erratum!repared e8clusively for Rida $l &ara'i# I;S ( OJ>(=>R 7@creates a new (ime object holdin the current time and tuc5s it awayin the ]time instance variable.6. Rails loo5s for a template to display the result. It searchs the direct-ory appviews for a subdirectory with the same name as the con-troller / say 1, and in that subdirectory for a file named after the action/ hello.rhtml 1.3. Rails processes this template throuh >Rb, e8ecutin any embeddedRuby and substitutin in values set up by the controller.. (he result is sent bac5 to the browser, and Rails finishes processin

  • 8/11/2019 Contentsaaa

    18/85

    this re*uest.(his isn)t the whole storyDRails ives you lots of opportunities to over-ride this basic wor5flow /and we)ll be ta5in advantae of these shortly1.$s it stands, our story illustrates convention over confiuration, one ofthe fundamental parts of the philosophy of Rails. &y providin conve-

    nient defaults and by applyin certain conventions, Rails applications aretypically written usin little or no e8ternal confiurationDthins just 5nitthemselves toether in a natural way.6.7 #in5in !aes (oetherIt)s a rare web application that has just one pae. #et)s see how we can addanother stunnin e8ample of web desin to our =ello, 4orldE application.;ormally, each style of pae in your application will correspond to a sep-arate view. In our case, we)ll also use a new action method to handle thepae /althouh that isn)t always the case, as we)ll see later in the boo51.4e)ll use the same controller for both actions. $ain, this needn)t be thecase, but we have no compellin reason to use a new controller riht now.

    4e already 5now how to add a new view and action to a Rails application.(o add the action, we define a new method in the controller. #et)s call thisaction oodbye. Our controller now loo5s li5e the followin.Kile A06 class SayController $pplicationControllerdef hello]time (ime.nowenddef oodbyeendendReport erratum!repared e8clusively for Rida $l &ara'i# I;S ( OJ>(=>R 7GKiure 6.% $ &asic Joodbye Screen;e8t we have to create a new template in the directory appviewssay . (histime it)s called oodbye.rhtml , because by default templates are named afterthe associated actions.Kile A03 htmlNheadNtitleNSee Pou #aterEtitleNheadNbodyNh0NJoodbyeEh0NpNIt was nice havin you here.pNbodyNhtmlNKire up our trusty browser aain, but this time point to our new viewusin the R# http%localhost%7222sayoodbye . Pou should see somethin

  • 8/11/2019 Contentsaaa

    19/85

    li5e Kiure 6. .;ow we need to lin5 the two screens toether. 4e)ll put a lin5 on thehello screen that ta5es us to the oodbye screen, and vice versa. In a realapplication we miht want to ma5e these proper buttons, but for now we)lljust use hyperlin5s.

    4e already 5now that Rails uses a convention to parse the R# into ataret controller and an action within that controller. So a simple approachwould be to adopt this R# convention for our lin5s. (he file hello.rhtmlwould contain the followin.Report erratum!repared e8clusively for Rida $l &ara'i# I;S ( OJ>(=>R 7:htmlN....pNSay a hrefVsayoodbyeVNJood&yeaNE

    pNhtmlNand the file oodbye.rhtml would point the other way.htmlN....pNSay a hrefVsayhelloVN=elloaNEpNhtmlN(his approach would certainly wor5, but it)s a bit fraile. If we were tomove our application to a different place on the web server, the R#swould no loner be valid. It also encodes assumptions about the RailsR# format into our code it)s possible a future version of Rails mihtchane this.Kortunately, these aren)t ris5s we have to ta5e. Rails comes with a bunchof helper methods that can be used in view templates. =ere, we)ll use thehelper method lin5to /1, which creates a hyperlin5 to an action. 3 sinlin5to /1, hello.rhtml becomesKile A0@ htmlNheadNtitleN=ello, RailsEtitleNheadNbodyNh0N=ello from RailsEh0NpNIt is now Z ]time ZN.pNpN(ime to sayZ lin5to VJood&yeEV, %action N VoodbyeV ZN

  • 8/11/2019 Contentsaaa

    20/85

    pNbodyNhtmlN(here)s a lin5to /1 call within an >Rb Z...ZN se*uence. (his creates alin5 to a R# that will invo5e the oodbye /1 action. (he first parameter

    in the call to lin5to /1 is the te8t to be displayed in the hyperlin5, and thene8t parameter tells Rails to enerate the lin5 to the oodbye action. $swe don)t specify a controller, the current one will be used.#et)s stop for a minute to consider that last parameter to lin5to /1. 4e wrotelin5to VJood&yeEV, %action N VoodbyeV3 (helin5to /1 method can do a lot more than this, but let)s ta5e it ently for now....Report erratum!repared e8clusively for Rida $l &ara'i# I;S ( OJ>(=>R 62Kiure 6.@% =ello !ae #in5ed to the Joodbye !ae

    (he %action part is a Ruby symbol. Pou can thin5 of the colon as meaninthe thin named..., so %action means the thin named action. (he N Vood-byeV associates the strin oodbye with the name action . In effect, thisives us 5eyword parameters for methods. Rails ma5es e8tensive use ofthis facilityDwhenever a method ta5es a number of parameters and someof those parameters are optional, you can use this 5eyword parameterfacility to ive those parameters values.O

  • 8/11/2019 Contentsaaa

    21/85

    H how to create a new Rails application and how to create a new con-troller in that application,H how Rails maps incomin re*uests into calls on your code,H how to create dynamic content in the controller and display it via theview template, and

    H how to lin5 paes toether.(his is a reat foundation. ;ow let)s start buildin real applications.Report erratum!repared e8clusively for Rida $l &ara'i!art II&uildin an $pplication!repared e8clusively for Rida $l &ara'iChare itE4ilma Klintstone and &etty RubbleChapter 3(he ?epot $pplication

    4e could mess around all day hac5in toether simple test applications,but that won)t help us pay the bills. So let)s et our teeth into somethinmeatier. #et)s create a web-based shoppin cart application called ?epot.?oes the world need another shoppin cart applicationW ;ope, but thathasn)t stopped hundreds of developers from writin one. 4hy should webe differentWMore seriously, it turns out that our shoppin cart will illustrate many ofthe features of Rails development. 4e)ll see how to create simple main-tenance paes, lin5 database tables, handle sessions, and create forms.Over the ne8t eiht chapters, we)ll also touch on peripheral topics such asunit testin, security, and pae layout.3.0 Incremental ?evelopment4e)ll be developin this application incrementally. 4e won)t attempt tospecify everythin before we start codin. Instead, we)ll wor5 out enouh ofa specification to let us start and then immediately create some function-ality. 4e)ll try thins out, ather feedbac5, and continue on with anothercycle of mini-desin and development.(his style of codin isn)t always applicable. It re*uires close cooperationwith the application)s users, because we want to ather feedbac5 as we oalon. 4e miht ma5e mista5es, or the client miht discover they)d as5edfor one thin but really wanted somethin different. It doesn)t matter whatthe reasonDthe earlier we discover we)ve made a mista5e, the less e8pen-sive it will be to fi8 that mista5e. $ll in all, with this style of developmentthere)s a lot of chane as we o alon.!repared e8clusively for Rida $l &ara'i4 =$( ? >!O( ? O>S 66&ecause of this, we need to use a toolset that doesn)t penali'e us for chan-in our mind. If we decide we need to add a new column to a databasetable, or chane the naviation between paes, we need to be able to etin there and do it without a bunch of codin or confiuration hassle. $s

  • 8/11/2019 Contentsaaa

    22/85

    you)ll see, Ruby on Rails shines when it comes to dealin with chaneDit)san ideal aile prorammin environment.$nyway, on with the application.3.A 4hat ?epot ?oes#et)s start by jottin down an outline specification for the ?epot applica-

    tion. 4e)ll loo5 at the hih-level use cases and s5etch out the flow throuhthe web paes. 4e)ll also try wor5in out what data the application needs/ac5nowledin that our initial uesses will li5ely be wron1.se Cases$ use case is simply a statement about how some entity uses a system.Consultants invent these 5inds of phrases when they want to chare moremoneyDit)s a perversion of business life that fancy words always cost morethan plain ones, even thouh the plain ones are more valuable.?epot)s use cases are simple /some would say traically so1. 4e start offby identifyin two different roles or actors% the buyer and the seller.(he buyer uses ?epot to browse the products we have to sell, select some

    to purchase, and supply the information needed to create an order.(he seller uses ?epot to maintain a list of products to sell, to determinethe orders that are awaitin shippin, and to mar5 orders as shipped./(he seller also uses ?epot to ma5e scads of money and retire to a tropicalisland, but that)s the subject of another boo5.1Kor now, that)s all the detail we need. 4e could o into e8cruciatin detailabout Lwhat it means to maintain products and Lwhat constitutes anorder ready to ship, but why botherW If there are details that aren)t obvi-ous, we)ll discover them soon enouh as we reveal successive iterations ofour wor5 to the customer.(al5in of ettin feedbac5, let)s not foret to et some riht nowDlet)sma5e sure our initial /admittedly s5etchy1 use cases are on the mar5 byas5in our user. $ssumin the use cases pass muster, let)s wor5 out howthe application will wor5 from the perspectives of its various users.Report erratum!repared e8clusively for Rida $l &ara'i4 =$( ? >!O( ? O>S 63!ae Klow?ave spea5in% I always li5e to have an idea of the main paes in my appli-cations, and to understand rouhly how users naviate between them.(his early in the development, these pae flows are li5ely to be incomplete,but they still help me focus on what needs doin and 5now how thins arese*uenced.Some fol5s li5e to moc5 up web application pae flows usin !hotoshop,or 4ord, or /shudder1 =(M#. I li5e usin a pencil and paper. It)s *uic5er,and the customer ets to play too, rabbin the pencil and scribblin alter-ations riht on the paper.Kiure 3.0% Klow of &uyer !aesMy first s5etch of the buyer flow, shown Kiure 3.0 , is pretty traditional.(he buyer sees a catalo pae, from which she selects one product at

  • 8/11/2019 Contentsaaa

    23/85

    a time. >ach product selected ets added to the cart, and the cart isdisplayed after each selection. (he buyer can continue shoppin usinthe catalo paes, or she can chec5 out and buy the contents of the cart.Report erratum!repared e8clusively for Rida $l &ara'i

    4 =$( ? >!O( ? O>S 6?urin chec5out we capture contact and payment details and then displaya receipt pae. 4e don)t yet 5now how we)re oin to handle payment, sothose details are fairly vaue in the flow.(he seller flow, shown in Kiure 3.A , is also fairly simple. $fter loin in,the seller sees a menu lettin her create or view a product, or ship e8istinorders. Once viewin a product, the seller may optionally edit the productinformation or delete the product entirely.Kiure 3.A% Klow of Seller !aes(he shippin option is very simplistic. It displays each order that has notyet been shipped, one order per pae. (he seller may choose to s5ip to

    the ne8t, or may ship the order, usin the information from the pae asappropriate.(he shippin function is clearly not oin to survive lon in the real world,but shippin is also one of those areas where reality is often straner thanyou miht thin5. Overspecify it upfront, and we)re li5ely to et it wron.Kor now let)s leave it as it is, confident that we can chane it as the userains e8perience usin our application.Report erratum!repared e8clusively for Rida $l &ara'i4 =$( ? >!O( ? O>S 6@?ata(he last thin we need to thin5 about before plowin into the first roundof codin is the data we)re oin to be wor5in with.;otice that we)re not usin words such as schema or classes here. 4e)realso not tal5in about databases, tables, 5eys, and the li5e. 4e)re simplytal5in about data. $t this stae in the development, we don)t 5now if we)lleven be usin a databaseDsometimes a flat file beats a database tablehands down.&ased on the use cases and the flows, it seems li5ely that we)ll be wor5inwith the data shown in Kiure 3.7 . $ain, pencil and paper seems a wholelot easier than some fancy tool, but use whatever wor5s for you.Kiure 3.7% Initial Juess at $pplication ?ata4or5in on the data diaram raised a couple of *uestions. $s the userbuilds their shoppin cart, we)ll need somewhere to 5eep the list of prod-ucts she)s added to it, so I added a cart. &ut apart from its use as a tran-sient place to 5eep this list, the cart seems to be somethin of a hostDIcouldn)t find anythin meaninful to store in it. (o reflect this uncertainty,I put a *uestion mar5 inside the cart)s bo8 in the diaram. I)m assuminthis uncertainty will et resolved as we implement ?epot.Comin up with the hih-level data also raised the *uestion of what infor-

  • 8/11/2019 Contentsaaa

    24/85

    mation should o into an order. $ain, I chose to leave this fairly open forReport erratum!repared e8clusively for Rida $l &ara'i# >( ) S C O?> 6GnowDwe)ll refine this further as we start showin the customer our early

    iterations.Kinally, you miht have noticed that I)ve duplicated the product)s price inthe line item data. =ere I)m brea5in the Linitially, 5eep it simple ruleslihtly, but it)s a transression based on e8perience. If the price of aproduct chanes, that price chane should not be reflected in the line itemprice of currently open orders, so each line item needs to reflect the priceof the product at the time the order was made.$ain, at this point I)ll double chec5 with my customer that we)re still onthe riht trac5. /=opefully, my customer was sittin in the room with mewhile I drew these three diarams.13.7 #et)s Code

    So, after sittin down with the customer and doin some preliminary anal-ysis, we)re ready to start usin a computer for developmentE 4e)ll be wor5-in from our oriinal three diarams, but the chances are pretty ood thatwe)ll be throwin them away fairly *uic5lyDthey)ll become outdated as weather feedbac5. Interestinly, that)s why we didn)t spend too lon onthemDit)s easier to throw somethin away if you didn)t spend a lon timecreatin it.In the chapters that follow, we)ll start developin the application based onour current understandin. =owever, before we turn that pae, we have toanswer just one more *uestion. 4hat should we do firstWI li5e to wor5 with the customer so we can jointly aree on priorities. Inthis case, I)d point out to her that it)s hard to develop anythin else until wehave some basic products defined in the system, so I)d suest spendina couple of hours ettin the initial version of the product maintenancefunctionality up and runnin. $nd, of course, she)d aree.Report erratum!repared e8clusively for Rida $l &ara'iChapter (as5 $% !roduct MaintenanceOur first development tas5 is to create the web interface that lets us main-tain our product informationDcreate new products, edit e8istin products,delete unwanted ones, and so on. 4e)ll develop this application in smalliterations, where small means Lmeasured in minutes. #et)s et started.....0 Iteration $0% Jet Somethin Runnin!erhaps surprisinly, we should et the first iteration of this wor5in inalmost no time. 4e)ll start off by creatin a new Rails application. (his iswhere we)ll be doin all our wor5. ;e8t, we)ll create a database to hold ourinformation /in fact we)ll create three databases1. Once that roundwor5is in place, we)llH create the table to hold the product information,

  • 8/11/2019 Contentsaaa

    25/85

    H confiure our Rails application to point to our database/s1, andH have Rails enerate the initial version of our product maintenanceapplication for us.Create a Rails $pplication&ac5 on pae A3 we saw how to create a new Rails application. Jo to a

    command prompt, and type rails followed by the name of our project. Inthis case, our project is called depot , so typewor5N rails depot4e see a bunch of output scroll by. 4hen it has finished, we find that anew directory, depot , has been created. (hat)s where we)ll be doin ourwor5.!repared e8clusively for Rida $l &ara'iI (>R$(IO; $0% J >( S OM>(=I;J R ;;I;J 32wor5N cd depotwor5N lsC=$;J>#OJ app db lo test

    R>$?M> components doc public vendorRa5efile confi lib scriptCreate the ?atabasesKor this application, we)ll use the open-source MySB# database server/which you)ll need too if you)re followin alon with the code1. Kor reasonsthat will become clear later, we)re actually oin to create three databases.H depotdevelopment will be our development database. $ll of our pro-rammin wor5 will be done here.H depottest is a test database. It is considered to be transient, so it)sperfectly acceptable for us to empty it out to ive our tests a freshplace to start each time they run.H depotproduction is the production database. Our application will usethis when we put it online.4e)ll use the mys*l command-line client to create our databases, but ifyou)re more comfortable with tools such as phpmyadmin or CocoaMySB# , ofor it. /In the session that follows, we)ve stripped out MySB#)s somewhatuseless responses to each command.1depotN mys*l -u root -p>nter password% ^^^^^^^4elcome to the MySB# monitor. Commands end with or Q.mys*lN create database depotdevelopmentmys*lN create database depottestmys*lN create database depotproductionmys*lN rant all on depotdevelopment.^ to + dave + ] + localhost + mys*lN rant all on depottest.^ to + dave + ] + localhost + mys*lN rant all on depotproduction.^ to + prod + ] + localhost + identified by + wibble + mys*lN e8itCreate the !roducts (able&ac5 in Kiure 3.7, on pae 6@, we s5etched out the basic content of theproducts table. ;ow let)s turn that into reality. =ere)s the ?ata ?efinition

  • 8/11/2019 Contentsaaa

    26/85

    #anuae /??#1 for creatin the products table in MySB#.Kile AA drop table if e8ists productscreate table products /id int not null autoincrement,title varchar/0221 not null,

    description te8t not null,imaeurl varchar/A221 not null,price decimal/02,A1 not null,primary 5ey /id11Report erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; $0% J >( S OM>(=I;J R ;;I;J 30Our table includes the product title, description, imae, and price, justas we s5etched out. 4e)ve also added somethin new% a column calledid . (his is used to ive each row in the table a uni*ue 5ey, allowin

    other tables to reference products. &ut there)s more to this id column.&y default, Rails assumes that every table it handles has as its primary5ey an inteer column called id . 0 Internally, Rails uses the value in thiscolumn to 5eep trac5 of the data it has loaded from the database and tolin5 between data in different tables. Pou can override this namin sys-tem, but unless you)re usin Rails to wor5 with leacy schemas that youcan)t chane, we recommend you just stic5 with usin the name id .It)s all very well comin up with the ??# for the products table, but whereshould we store itW I)m a stron believer in 5eepin the ??# for my appli-cation databases under version control, so I always create it in a flat file.Kor a Rails application, I call the file create.s*l and put it in my applica-tion)s db subdirectory. (his lets me use the mys*l client to e8ecute the ??#and create the table in my development database. $ain, you)re free to dothis usin JI or web-based tools if you prefer.depotN mys*l depotdevelopment dbcreate.s*lConfiure the $pplicationIn many simple scriptin-lanuae web applications, the information onhow to connect to the database is embedded directly into the codeDyoumiht find a call to some connect /1 method, passin in host and databasenames, alon with a user name and password. (his is danerous, becausepassword information sits in a file in a web-accessible directory. $ smallserver confiuration error could e8pose your password to the world.(he approach of embeddin connection information into code is also infle8-ible. One minute you miht be usin the development database as youhac5 away. ;e8t you miht need to run the same code aainst the testdatabase. >ventually, you)ll want to deploy it into production. >very timeyou switch taret databases, you have to edit the connection call. (here)sa rule of prorammin that says you)ll mistype the password only whenswitchin the application into production.Smart developers 5eep the connection information out of the code. Some-

  • 8/11/2019 Contentsaaa

    27/85

    times you miht want to use some 5ind of repository to store it all /Favadevelopers often use F;?I to loo5 up connection parameters1. (hat)s a bit0 ;ote that the case is sinificant. If you use a nannyish JI tool that insists on chaninthe column name to Id , you miht have problems.Report erratum

    !repared e8clusively for Rida $l &ara'iI (>R$(IO; $0% J >( S OM>(=I;J R ;;I;J 3Adevelopment%adapter% mys*ldatabase% railsdevelopmenthost% localhostusername% rootpassword%test%adapter% mys*ldatabase% railstest

    host% localhostusername% rootpassword%production%adapter% mys*ldatabase% railsproductionhost% localhostusername% rootpassword%development%adapter% mys*ldatabase% depotdevelopmenthost% localhostusername% blan5Npassword%test%adapter% mys*ldatabase% depottesthost% localhostusername% blan5Npassword%production%adapter% mys*ldatabase% depotproductionhost% localhostusername% prodpassword% wibbleconfidatabase.yml>dit the fileOriinal Kile

  • 8/11/2019 Contentsaaa

    28/85

    ;ew KileKiure .0% Confiure the database.yml Kileheavy for the averae web application that we)ll write, so Rails simply usesa flat file. Pou)ll find it in confidatabase.yml . A$s Kiure .0 shows, database.yml contains three sections, one each for

    the development, test, and production databases. sin your favorite edi-tor, chane the fields in each to match the databases we created. ;otethat in the diaram we)ve left the username fields blan5 for the develop-ment and test environments in the new database.yml file. (his is con-venient, as it means that different developers will each use their ownusernames when connectin. =owever, we)ve had reports that with somecombinations of MySB#, database drivers, and operatin systems, leav-in these fields blan5 ma5es Rails attempt to connect to the databaseas the root user. Should you et an error such as $ccess denied for user)root)])localhost.localdomain), put an e8plicit username in these two fields.Create the Maintenance $pplication

    O

  • 8/11/2019 Contentsaaa

    29/85

    with Rails.depotN ruby scriptserverN Rails application started on http%2.2.2.2%7222XA223-2A-2G 0A%2G%62Y I;KO 4>&ric5 0.7.0XA223-2A-2G 0A%2G%62Y I;KO ruby 0.G.A /A226-0A-721 Xpowerpc-darwin@[email protected]

    XA223-2A-2G 0A%2G%62Y I;KO 4>&ric5%%=((!ServerUstart% pidA2A0 port7222Fust as it did with our demo application in Chapter 6, Instant Jratification,this command starts a web server on our local host, port 7222. 3 #et)sconnect to it. Remember, the R# we ive to our browser contains both theport number /72221 and the name of the controller in lowercase /admin1.7 nless, perhaps, you)re runnin OS " 02.6.It seems as if (ier has bro5en Ruby)sstandard MySB# library. If you see the error &efore updatin scaffoldin from new ?&schema,try creatin a table for your model /!roduct1, it may well be because Ruby /and henceRails1

    can)t et to the database. (o fi8 $pple)s bad install, you)re oin to need to reinstallRuby)sMySB# library, which means oin bac5 to on pae A0, runnin the script to repair theRubyinstallation, and then reinstallin the mys*l em.6 Some readers also report ettin the error Client does not support authenticationprotocolre*uested by server consider upradin MySB# client. (his incompatibility between thever-sion of MySB# installed and the libraries used to access it can be resolved by followintheinstructions at http%dev.mys*l.comdocmys*lenold-client.html and issuin a MySB#commandsuch as set password for )someuser)])somehost) O#?!$SS4OR?/)newpwd)1 .3 Pou miht et an error sayin $ddress already in use when you try to run 4>&ric5.(hat simply means that you already have a Rails 4>&ric5 server runnin on yourmachine.If you)ve been followin alon with the e8amples in the boo5, that miht well be the=ello4orldE application from Chapter 6. Kind its console, and 5ill the server usin control-C.Report erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; $0% J >( S OM>(=I;J R ;;I;J 36!ort% 7222 Controller% admin(hat)s pretty borin. It)s showin us a list of products, and there aren)tany products. #et)s remedy that. Clic5 the ;ew product lin5, and a formshould appear. Kiure .A, on the followin pae shows the form after itis filled in. Clic5 the Create button, and you should see the new productin the list /Kiure .7, on the ne8t pae1. !erhaps it isn)t the prettiestinterface, but it wor5s, and we can show it to our client for approval. (hey

  • 8/11/2019 Contentsaaa

    30/85

    can play with the other lin5s /showin details, editin e8istin products,as shown in Kiure .6, on pae 31. 4e e8plain to them that this isonly a first stepDwe 5now it)s rouh, but we wanted to et their feedbac5early. /$nd A3 minutes into the start of codin probably counts as early inanyone)s boo5.1

    Rails Scaffolds4e covered a lot of round in a very short initial implementation, so let)sta5e a minute to loo5 at that last step in a bit more detail.$ Rails scaffold is an autoenerated framewor5 for manipulatin a model.4hen we run the enerator, we tell it that we want a scaffold for a particu-lar model /which it creates1 and that we want to access it throuh a ivencontroller /which it also creates1.In Rails, a model is automatically mapped to a database table whose name name mappinW pae 0G2is the plural form of the model)s class. In our case, we as5ed for a modelcalled !roduct , so Rails associated it with the table called products . $nd how

    did it find that tableW 4e told it where to loo5 when we set up the devel-opment entry in confidatabase.yml . 4hen we started the application, themodel e8amined the table in the database, wor5ed out what columns ithad, and created mappins between the database data and Ruby objects.Report erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; $0% J >( S OM>(=I;J R ;;I;J 33Kiure .A% $ddin a ;ew !roductKiure .7% 4e Fust $dded Our Kirst !roductReport erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; $0% J >( S OM>(=I;J R ;;I;J 3Kiure .6% Showin ?etails and >ditin(hat)s why the ;ew products form came up already 5nowin about thetitle, description, imae, and price fieldsDbecause they are in the databasetable, they are added to the model. (he form enerator used by the scaf-fold can as5 the model for information on these fields and uses what itdiscovers to create an appropriate =(M# form.Controllers handle incomin re*uests from the browser. $ sinle applica-tion can have multiple controllers. Kor our ?epot application, it)s li5elythat we)ll end up with two of them, one handlin the seller)s administra-tion of the site and the other handlin the buyer)s e8perience. 4e createdthe product maintenance scaffoldin in the $dmin controller, which is whythe R# that accesses it has admin at the start of its path.(he utility that enerates a Rails scaffold populates your application)sdirectory tree with wor5in Ruby code. If you e8amine it, you)ll find thatwhat you have is the bare bones of a full applicationDthe Ruby code hasbeen placed inline it)s all in the source, rather than simply bein a sin-le call into some standard library. (his is ood news for us, because itReport erratum

  • 8/11/2019 Contentsaaa

    31/85

    !repared e8clusively for Rida $l &ara'iI (>R$(IO; $A% $ ?? $ M ISSI;J C O#M; 3@?avid Says...4on)t 4e >nd p Replacin $ll the ScaffoldsWMost of the time, yes. Scaffoldin is not intended to be the sha5e )n) ba5e

    of application development. It)s there as support while you build out theapplication. $s you)re desinin how the list of products should wor5,you rely on the scaffold-enerated create, update, and delete actions.(hen you replace the enerated creation functionality while relyin onthe remainin actions. $nd so on and so forth.Sometimes scaffoldin will be enouh, thouh. If you)re merely interestedin ettin a *uic5 interface to a model online as part of a bac5end inter-face, you may not care that the loo5s are bare. &ut this is the e8ception.?on)t e8pect scaffoldin to replace the need for you as a prorammerjust yet /or ever1.means that we can modify the code produced in the scaffold. (he scaffold

    is the startin point of an application, not a finished application in its ownriht. $nd we)re about to ma5e use of that fact as we move on to the ne8titeration in our project..A Iteration $A% $dd a Missin ColumnSo, we show our scaffold-based code to our customer, e8plainin that it)sstill pretty rouh-and-ready. She)s delihted to see somethin wor5in so*uic5ly. Once she plays with it for a while, she notices that somethinwas missed in our initial discussions. #oo5in at the product informationdisplayed in a browser window, it becomes apparent that we need to addan availability date columnDthe product will be offered to customers onlyonce that date has passed.(his means we)ll need to add a column to the database table, and we)llneed to ma5e sure that the various maintenance paes are updated to addsupport for this new column.Some developers /and ?&$s1 would add the column by firin up a utilityproram and issuin the e*uivalent of the commandalter table productsadd column dateavailable datetimeReport erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; $A% $ ?? $ M ISSI;J C O#M; 3GInstead, I tend to maintain the flat file containin the ??# I oriinally usedto create the schema. (hat way I have a version-controlled history of theschema and a sinle file containin all the commands needed to re-createit. So let)s alter the file dbcreate.s*l , addin the dateavailable column.Kile 6 drop table if e8ists productscreate table products /id int not null autoincrement,title varchar/0221 not null,description te8t not null,

  • 8/11/2019 Contentsaaa

    32/85

    imaeurl varchar/A221 not null,price decimal/02,A1 not null,dateavailable datetime not null,primary 5ey /id11

    4hen I first created this file, I added a drop table command at the top ofit. (his now allows us to create a new /empty1 schema instance with thecommandsdepotN mys*l depotdevelopment dbcreate.s*lObviously, this approach only wor5s if there isn)t important data alreadyin the database table /as droppin the table wipes out the data it contains1.(hat)s fine durin development, but in production we)d need to step morecarefully. Once an application is in production, I tend to produce version-controlled miration scripts to uprade my database schemas.>ven in development, this can be a pain, as we)d need to reload our testdata. I normally dump out the database contents /usin mys*ldump 1 when

    I have a set of data I can use for development, then reload this databaseeach time I blow away the schema.(he schema has chaned, so our scaffold code is now out-of-date. $swe)ve made no chanes to the code, it)s safe to reenerate it. ;otice thatthe enerate script prompts us when it)s about to overwrite a file. 4e typea to indicate that it can overwrite all files.depotN ruby scriptenerate scaffold !roduct $dmindependency modele8ists appmodelse8ists testunite8ists testfi8turess5ip appmodelsproduct.rbs5ip testunitproducttest.rbs5ip testfi8turesproducts.ymle8ists appcontrollerse8ists apphelperse8ists appviewsadmine8ists testfunctionaloverwrite appcontrollersadmincontroller.rbW XPna*Y aforcin scaffoldforce appcontrollersadmincontroller.rbReport erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; $A% $ ?? $ M ISSI;J C O#M; 3:Kiure .3% ;ew !roduct !ae $fter $ddin ?ate Columnforce testfunctionaladmincontrollertest.rbforce apphelpersadminhelper.rbforce appviewslayoutsadmin.rhtmlforce publicstylesheetsscaffold.cssforce appviewsadminlist.rhtml

  • 8/11/2019 Contentsaaa

    33/85

    force appviewsadminshow.rhtmlforce appviewsadminnew.rhtmlforce appviewsadminedit.rhtmlcreate appviewsadminform.rhtmlRefresh the browser, and create a new product, and you)ll see somethin

    li5e Kiure .3 . /If it doesn)t loo5 any different, perhaps the eneratoris still waitin for you to type a .1 4e now have our date field /and withno e8plicit codin1. Imaine doin this with the client sittin ne8t to you.(hat)s rapid feedbac5EReport erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; $7% 9 $#I?$(> E 2.7 Iteration $7% 9alidateE4hile playin with the results of iteration two, our client noticed some-thin. If she entered an invalid price, or forot to set up a product descrip-tion, the application happily accepted the form and added a line to the

    database. 4hile a missin description is embarrassin, a price of _2.22actually costs her money, so she as5ed that we add validation to the appli-cation. ;o product should be allowed in the database if it has an emptyte8t field, an invalid R# for the imae, or an invalid price.So, where do we put the validationW(he model layer is the ate5eeper between the world of code and thedatabase. ;othin to do with our application comes out of the database orets stored bac5 into the database that doesn)t first o throuh the model.(his ma5es it an ideal place to put all validation it doesn)t matter whetherthe data comes from a form or from some prorammatic manipulation inour application. If the model chec5s it before writin to the database, thenthe database will be protected from bad data.#et)s loo5 at the source code of the model class /in appmodelsproduct.rb 1.Kile 7 class !roduct $ctiveRecord%%&aseend;ot much to it, is thereW $ll of the heavy liftin /database mappin,creatin, updatin, searchin, and so on1 is done in the parent class/ $ctiveRecord%%&ase , a part of Rails1. &ecause of the joys of inheritance,our !roduct class ets all of that functionality automatically.$ddin our validation should be fairly clean. #et)s start by validatin thatthe te8t fields all contain somethin before a row is written to the database.4e do this by addin some code to the e8istin model.Kile 3 class !roduct $ctiveRecord%%&asevalidatespresenceof %title, %description, %imaeurlend(he validatespresenceof /1 method is a standard Rails validator. It chec5sthat a iven field, or set of fields, is present and its contents are not empty.Kiure ., on the followin pae, shows what happens if we try to submita new product with none of the fields filled in. It)s pretty impressive% thefields with errors are hihlihted, and the errors are summari'ed in a nice

  • 8/11/2019 Contentsaaa

    34/85

    list at the top of the form. ;ot bad for one line of code. Pou miht alsohave noticed that after editin the product.rb file you didn)t have to restartthe application to test your chanesDin development mode, Rails noticesReport erratum!repared e8clusively for Rida $l &ara'i

    I (>R$(IO; $7% 9 $#I?$(> E 0Kiure .% 9alidatin (hat Kields $re !resentthat the files have been chaned and reloads them into the application.(his is a tremendous productivity boost when developin.;ow we)d li5e to validate that the price is a valid, positive number. 4e)llattac5 this problem in two staes. Kirst, we)ll use the delihtfully namedvalidatesnumericalityof /1 method to verify that the price is a valid number.Kile 3 validatesnumericalityof %price;ow, if we add a product with an invalid price, the appropriate messaewill appear. MySB# ives Rails enouh metadata to 5now that

    price contains a number, so Railsconverts it to a floatin-point value. 4ith other databases, the value miht come bac5 as astrin, so you)d need to convert it usin Kloat/price1 before usin it in a comparisonReport erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; $7% 9 $#I?$(> E A;e8t we need to chec5 that it is reater than 'ero. 4e do that by writina method named validate /1 in our model class. Rails automatically callsthis method before savin away instances of our product, so we can use itto chec5 the validity of fields. 4e ma5e it a protected method, because it protectedW pae 6@7shouldn)t be called from outside the conte8t of the model.Kile 3 protecteddef validateerrors.add/%price, Vshould be positiveV1 unless price.nilW [[ price N 2.2endIf the price is less than or e*ual to 'ero, the validation method useserrors.add/...1 to record the error. ?oin this prevents Rails from writinthe row to the database. It also ives our forms a nice messae to displayto the user. (he first parameter to errors.add /1 is the name of the field, andthe second is the te8t of the messae. ;ote that we only do the chec5 ifthe price has been set. 4ithout that e8tra test we)ll compare nil aainst 2.2 ,and that will raise an e8ception.(wo more thins to validate. Kirst, we want to ma5e sure that each producthas a uni*ue title. One more line in the !roduct model will do this. (heuni*ueness validation will perform a simple chec5 to ensure that no otherrow in the products table has the same title as the row we)re about to save.Kile 3 validatesuni*uenessof %title#astly, we need to validate that the R# entered for the imae is valid.4e)ll do this usin the validatesformatof /1 method, which matches a field

  • 8/11/2019 Contentsaaa

    35/85

    aainst a reular e8pression. Kor now we)ll just chec5 that the R# starts reulare8pressionW pae 6@with http% and ends with one of . if , . jp , or . pn . @Kile 3 validatesformatof %imaeurl,

    %with N Zr`http%.TQ./if[jp[pn1_i,%messae N Vmust be a R# for a JIK, F!J, or !;J imaeV@ #ater on, we)d probably want to chane this form to let the user select from a list ofavailable imaes, but we)d still want to 5eep the validation to prevent malicious fol5sfromsubmittin bad data directly.Report erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; $6% ! R>((I>R # IS(I;JS 7So, in a couple of minutes we)ve added validations that chec5H (he field)s title, description, and imae R# are not empty.

    H (he price is a valid number reater than 'ero.H (he title is uni*ue amon all products.H (he imae R# loo5s reasonable.(his is the full listin of the updated !roduct model.Kile 3 class !roduct $ctiveRecord%%&asevalidatespresenceof %title, %description, %imaeurlvalidatesnumericalityof %pricevalidatesuni*uenessof %titlevalidatesformatof %imaeurl,%with N Zr`http%.TQ./if[jp[pn1_i,%messae N Vmust be a R# for a JIK, F!J, or !;J imaeVprotecteddef validateerrors.add/%price, Vshould be positiveV1 unless price.nilW [[ price N 2.2endend;earin the end of this cycle, we as5 our customer to play with the appli-cation, and she)s a lot happier. It too5 only a few minutes, but the simpleact of addin validation has made the product maintenance paes feel alot more solid..6 Iteration $6% !rettier #istinsOur customer has one last re*uest /customers always seem to have onelast re*uest1. (he listin of all the products is uly. Can we Lpretty it upa bitW $nd, while we)re in there, can we also display the product imaealon with the imae R#W4e)re faced with a dilemma here. $s developers, we)re trained to respondto these 5inds of re*uest with a sharp inta5e of breath, a 5nowin sha5eof the head, and a murmured Lyou want whatW $t the same time, we alsoli5e to show off a bit. In the end, the fact that it)s fun to ma5e these 5indsof chanes usin Rails wins out, and we fire up our trusty editor.

  • 8/11/2019 Contentsaaa

    36/85

    (he Rails view in the file appviewsadminlist.rhtml produces the currentlist of products. (he source code, which was produced by the scaffoldenerator, loo5s somethin li5e the followin.Kile h0N#istin productsh0NtableN

    trNZ for column in !roduct.contentcolumns ZNthNZ column.humanname ZNthNReport erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; $6% ! R>((I>R # IS(I;JS 6Z end ZNtrNZ for product in ]products ZNtrNZ for column in !roduct.contentcolumns ZN

    tdNZh product.send/column.name1 ZNtdNZ end ZNtdNZ lin5to + Show + , %action N + show + , %id N product ZNtdNtdNZ lin5to + >dit + , %action N + edit + , %id N product ZNtdNtdNZ lin5to + ?estroy + , `%action N + destroy + , %id N product,%confirm N V$re you sureWV ZNtdNtrNZ end ZNtableNZ if ]productpaes.current.previouslin5to V!revious paeV, ` %pae N ]productpaes.current.previous end ZNZ if ]productpaes.current.ne8tlin5to V;e8t paeV, ` %pae N ]productpaes.current.ne8t end ZNbr NZ lin5to + ;ew product + , %action N + new + ZN(he view uses >Rb to iterate over the columns in the !roduct model. It >RbW pae 70creates a table row for each product in the ]products array. /(his arrayis set up by the list action method in the controller.1 (he row contains anentry for each column in the result set.(he dynamic nature of this code is neat, as it means that the displaywill automatically update to accommodate new columns. =owever, it alsoma5es the display somewhat eneric. So, let)s ta5e this code and modifyit to produce nicer-loo5in output.Kile @ h0N!roduct #istinh0Ntable cellpaddinV3V cellspacinV2VNZoddoreven 2

  • 8/11/2019 Contentsaaa

    37/85

    for product in ]productsoddoreven 0 - oddorevenZNtr valinVtopV classV#ist#ineZ oddoreven ZNVNtdN

    im widthV2V heihtV@2V srcVZ product.imaeurl ZNVNtdNtd widthV2ZVNspan classV#ist(itleVNZ h/product.title1 ZNspanNbr NZ h/truncate/product.description, G211 ZNtdNtd alinVrihtVNZ product.dateavailable.strftime/VZy-Zm-ZdV1 ZNbrNstronN_Z sprintf/VZ2.AfV, product.price1 ZNstronNtdNtd classV#ist$ctionsVN

    Z lin5to + Show + , %action N + show + , %id N product ZNbrNZ lin5to + >dit + , %action N + edit + , %id N product ZNbrNReport erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; $6% ! R>((I>R # IS(I;JS 3Z lin5to + ?estroy + , ` %action N + destroy + , %id N product ,%confirm N V$re you sureWV ZNtdNtrNZ end ZNtableNZ if ]productpaes.current.previouslin5to/V!revious paeV, ` %pae N ]productpaes.current.previous 1endZNZ if ]productpaes.current.ne8tlin5to/V;e8t paeV, ` %pae N ]productpaes.current.ne8t 1endZNbr NZ lin5to + ;ew product + , %action N + new + ZN;otice how we used the oddoreven variable to tole the name of the CSSclass applied to alternatin rows of the table. (his will result in alternatinpastel-shaded lines for each product. /If you)re readin this on paper, you)llhave to ta5e our word for it about the pastels.1 4e also used Ruby)s sprintf /1method to convert the floatin-point price to a nicely formatted strin.$ll scaffold-enerated applications use the stylesheet scaffold.css in thedirectory publicstylesheets . 4e added our own styles to this file.Kile G .#ist(itle `color% UA66

  • 8/11/2019 Contentsaaa

    38/85

    font-weiht% boldfont-si'e% larer.#ist$ctions `font-si'e% 8-small

    te8t-alin% rihtpaddin-left% 0em.#ist#ine2 `bac5round% Ue2fGfG.#ist#ine0 `bac5round% UfGb2fG!ut some imaes in the publicimaes directory and enter some productdescriptions, and the resultin product listin miht loo5 somethin li5e

    Kiure .@, on the ne8t pae.$ Rails scaffold provides real source code, files that we can modify andimmediately see results. (his approach ives us the fle8ibility we need todevelop in an aile way. 4e can customi'e a particular source file andleave the rest aloneDchanes are both possible and locali'ed.Report erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; $6% ! R>((I>R # IS(I;JS Kiure .@% (idied-up !roduct #istinSo, we proudly show our customer her new product listin, and she)spleased. >nd of tas5. (ime for lunch.4hat 4e Fust ?idIn this chapter we laid the roundwor5 for our store application.H 4e created three databases /development, test, and production1 andconfiured our Rails application to access them.H 4e created the products table and used the scaffold enerator to writean application to maintain it.H 4e aumented that enerated code with validation.H 4e rewrote the eneric view code with somethin prettier.One thin that we didn)t do was discuss the paination of the productlistin. (he scaffold enerator automatically made use of Rails) built-inpaination helper. (his brea5s the lists of products into paes of 02 entrieseach and automatically handles naviation between paes. 4e discuss thisin more depth startin on pae 762.Report erratum!repared e8clusively for Rida $l &ara'iChapter @(as5 &% Catalo ?isplay$ll in all, it)s been a successful day so far. 4e athered the initial re*uire-ments from our customer, documented a basic flow, wor5ed out a first

  • 8/11/2019 Contentsaaa

    39/85

    pass at the data we)ll need, and put toether the maintenance pae for the?epot application)s products. 4e even manaed to cap off the morninwith a decent lunch.(hus fortified, it)s on to our second tas5. 4e chatted throuh prioritieswith our customer, and she said she)d li5e to start seein what thins loo5

    li5e from the buyer)s point of view. Our ne8t tas5 is to create a simplecatalo display.(his also ma5es a lot of sense from our point of view. Once we havethe products safely tuc5ed into the database, it should be fairly simple todisplay them. It also ives us a basis from which to develop the shoppincart portion of the code later.4e should also be able to draw on the wor5 we did in the product main-tenance tas5Dthe catalo display is really just a lorified product listin.So, let)s et [email protected] Iteration &0% Create the Catalo #istin&ac5 on pae 3, we said that we)d be usin two controller classes for this

    application. 4e)ve already created the $dmin controller, used by the sellerto administer the ?epot application. ;ow it)s time to create the secondcontroller, the one that interacts with the payin customers. #et)s call itStore .depotN ruby scriptenerate controller Store inde8!repared e8clusively for Rida $l &ara'iI (>R$(IO; &0% C R>$(> (=> C $($#OJ # IS(I;J GIn the previous chapter, we used the enerate utility to create a scaffoldfor the products table. (his time, we)ve as5ed it to create a new controller/called StoreController 1 containin a sinle action method, inde8 /1.So why did we choose to call our first method inde8 W &ecause, just li5emost web servers, if you invo5e a Rails controller and don)t specify ane8plicit action, Rails automatically invo5es the inde8 action. In fact, let)stry it. !oint a browser at http%localhost%7222store and up pops our webpae.It miht not ma5e us rich, but at least we 5now thins are all wiredtoether correctly. (he pae even tells us where to find the proram filethat draws this pae.#et)s start by displayin a simple list of all the salable products in ourdatabase. 4e 5now that eventually we)ll have to be more sophisticated,brea5in them into cateories, but this will et us oin. 4hat constitutesa salable productW Our customer told us that we only display ones withan available date on or before today.4e need to et the list of products out of the database and ma5e it availableto the code in the view that will display the table. (his means we have tochane the inde8 /1 method in storecontroller.rb . 4e want to proram at adecent level of abstraction, so let)s just assume we can as5 the model fora list of the products we can sell.Kile : def inde8]products !roduct.salableitems

  • 8/11/2019 Contentsaaa

    40/85

    endObviously, this code won)t run as it stands. 4e need to define the methodsalableitems /1 in the product.rb model. (he code that follows uses the Railsfind /1 method. (he %all parameter tells Rails that we want all rows thatmatch the iven condition. /(he condition chec5s that the item)s availabil-

    ity date is not in the future. It uses the MySB# now /1 function to et thecurrent date and time.1 4e as5ed our customer if she had a preferenceReport erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; &0% C R>$(> (=> C $($#OJ # IS(I;J :reardin the order thins should be listed, and we jointly decided to seewhat happened if we displayed the newest products first, so the code doesa descendin sort on dateavailable . def self.888W pae 6@0Kile @2 U Return a list of products we can sell /which means they have to beU available1. Show the most recently available first.

    def self.salableitemsfind/%all,%conditions N Vdateavailable now/1V,%order N Vdateavailable descV1end(he find /1 method returns an array containin a !roduct object for each rowreturned from the database. (he salableitems /1 method simply passes thisarray bac5 to the controller.;ow we need to write our view template. Kor now we)ll display the prod-ucts in a simple table. (o do this, edit the file appviewsstoreinde8.rhtml ./Remember that the path name to the view is built from the name of thecontroller / store 1 and the name of the action / inde8 1. (he . rhtml part sinifiesan >Rb template.1Kile @0 table cellpaddinV3V cellspacinV2VNZ for product in ]products ZNtr valinVtopVNtdNim srcVZ product.imaeurl ZNVNtdNtd widthV632VNh7NZh product.title ZNh7NsmallNZ product.description ZNsmallNbrNstronN_Z sprintf/VZ2.AfV, product.price1 ZNstronNZ lin5to + $dd to Cart + ,%action N + addtocart + ,%id N product ZNbrN

  • 8/11/2019 Contentsaaa

    41/85

    tdNtrNtrNtd colspanVAVNhrNtdNtrNZ end ZNtableN

    =ittin Refresh brins up the display in Kiure @.0, on the followin pae.4e call the customer over, and she)s pretty pleased. $fter all, we have thema5ins of a catalo and it)s ta5en only a few minutes. &ut before we ettoo full of ourselves, she points out that she)d really li5e a proper-loo5inweb pae here. She needs at least a title at the top and a sidebar withlin5s and news.Report erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; &A% $ ?? ! $J> ? >COR$(IO;S @2Kiure @.0% Our Kirst Catalo !ae$t this point in the real world we)d probably want to call in the desin

    fol5sDwe)ve all seen too many prorammer-desined web sites to feel com-fortable inflictin another on the world. &ut the !ramatic 4eb ?esineris off ettin inspiration somewhere and won)t be bac5 until later in theyear, so let)s put a placeholder in for now. It)s time for an [email protected] Iteration &A% $dd !ae ?ecorations(he paes in a particular web site typically share a similar layoutDthedesiner will have created a standard template that is used when placincontent. Our job is to add this pae decoration to each of the store paes.Kortunately, in Rails we can define layouts. $ layout is a template into layoutwhich we can flow additional content. In our case, we can define a sinlelayout for all the store paes and insert the catalo pae into that layout.#ater we can do the same with the shoppin cart and chec5out paes.&ecause there)s only one layout, we can chane the loo5 and feel of thisentire section of our site by editin just one thin. (his ma5es us feelbetter about puttin a placeholder in for now we can update it when thedesiner eventually returns from the mountaintop.Report erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; &A% $ ?? ! $J> ? >COR$(IO;S @0(here are many ways of specifyin and usin layouts in Rails. 4e)ll choosethe simplest for now. If you create a template file in the appviewslayoutsdirectory with the same name as a controller, all views rendered by thatcontroller will use that layout by default. So let)s create one now. Ourcontroller is called store , so we)ll name the layout store.rhtml .Kile @A#ine 0htmlN-headN-

  • 8/11/2019 Contentsaaa

    42/85

    titleN!rapro &oo5s Online StoretitleN-Z stylesheetlin5ta VdepotV, %media N VallV ZN3headN

    -bodyN-div idVbannerVN-im srcVimaesloo.pnVN [[W pae 6@:-Z ]paetitle [[ V!ramatic &oo5shelfV ZN02divN

    -div idVcolumnsVN-div idVsideVN-a hrefVhttp%www....VN=omeaNbr N-a hrefVhttp%www....fa*VNBuestionsaNbr N03a hrefVhttp%www....newsVN;ewsaNbr N-a hrefVhttp%www....contactVNContactaNbr N-divN-div idVmainVN-Z ]contentforlayout ZNA2divN-divN-bodyN-htmlN$part from the usual =(M# ubbins, this layout has three Rails-specificitems. #ine 6 uses a Rails helper method to enerate a lin5 N ta toour depot.css stylesheet. On line : we set the pae title to a value inthe variable ]paetitle . (he real maic, however, ta5es place on line 0:.

  • 8/11/2019 Contentsaaa

    43/85

    Rails automatically sets the variable ]contentforlayout to the pae-specificcontentDthe stuff enerated by the view invo5ed by this re*uest. In ourcase, this will be the catalo pae enerated by inde8.rhtml .4e)ll also ta5e this opportunity to tidy up the inde8.rhtml view in appviews .Kile @7 Z for product in ]products ZN

    div classVcataloentryVNim srcVZ product.imaeurl ZNVNh7NZ h/product.title1 ZNh7NZ product.description ZNspan classVcatalopriceVNZ sprintf/V_Z2.AfV, product.price1 ZNspanNZ lin5to + $dd to Cart + ,`%action N + addtocart + , %id N product ,%class N + addtocart + ZNbrNdivNdiv classVseparatorVN\nbspdivNZ end ZN

    Z lin5to VShow my cartV, %action N VdisplaycartV ZN;otice how we)ve switched to usin div N tas and added CSS class namesto tas to assist with layin out the pae. (o ive the $dd to Cart lin5 aReport erratum!repared e8clusively for Rida $l &ara'iI (>R$(IO; &A% $ ?? ! $J> ? >COR$(IO;S @AKiure @.A% Catalo with #ayout $ddedclass, we had to use the optional third parameter of the lin5to /1 method,which lets us specify =(M# attributes for the enerated ta.(o ma5e this all wor5, we need to hac5 toether a *uic5 stylesheet /or,more li5ely, rab an e8istin stylesheet and bend it to fit1. (he file depot.cssoes into the directory publicstylesheets . /#istins of the stylesheets starton pae 32G.1 =it Refresh,