Beruflich Dokumente
Kultur Dokumente
http://guides.rubyonrails.org/getting_started.html#...
Moreatrubyonrails.org: Overview|Download|Deploy|Code|Screencasts|Documentation|Ecosystem|Community|Blog
Chapters
1. GuideAssumptions 2. WhatisRails? TheMVCArchitecture TheComponentsofRails REST 3. CreatingaNewRailsProject InstallingRails CreatingtheBlogApplication InstallingtheRequiredGems ConfiguringaDatabase CreatingtheDatabase 4. Hello,Rails! StartinguptheWebServer SayHello,Rails SettingtheApplicationHomePage 5. GettingUpandRunningQuicklywithScaffolding 6. CreatingaResource RunningaMigration AddingaLink WorkingwithPostsintheBrowser TheModel AddingSomeValidation UsingtheConsole ListingAllPosts CustomizingtheLayout CreatingNewPosts ShowinganIndividualPost EditingPosts DestroyingaPost 7. AddingaSecondModel
1 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
ThisGuideisbasedonRails3.0.SomeofthecodeshownherewillnotworkinearlierversionsofRails.
1 Guide Assumptions
ThisguideisdesignedforbeginnerswhowanttogetstartedwithaRailsapplicationfromscratch.Itdoesnotassumethatyouhaveanyprior experiencewithRails.However,togetthemostoutofit,youneedtohavesomeprerequisitesinstalled: TheRubylanguageversion1.8.7orhigher NotethatRuby1.8.7p248andp249havemarshalingbugsthatcrashRails3.0.RubyEnterpriseEditionhavethesefixedsincerelease 1.8.7-2010.02though.Onthe1.9front,Ruby1.9.1isnotusablebecauseitoutrightsegfaultsonRails3.0,soifyouwanttouseRails3with 1.9.xjumpon1.9.2forsmoothsailing. TheRubyGemspackagingsystem AworkinginstallationoftheSQLite3Database RailsisawebapplicationframeworkrunningontheRubyprogramminglanguage.IfyouhavenopriorexperiencewithRuby,youwillfindavery steeplearningcurvedivingstraightintoRails.TherearesomegoodfreeresourcesontheinternetforlearningRuby,including: Mr.NeighborlysHumbleLittleRubyBook ProgrammingRuby Whys(Poignant)GuidetoRuby
2 What is Rails?
RailsisawebapplicationdevelopmentframeworkwrittenintheRubylanguage.Itisdesignedtomakeprogrammingwebapplicationseasierby makingassumptionsaboutwhateverydeveloperneedstogetstarted.Itallowsyoutowritelesscodewhileaccomplishingmorethanmany otherlanguagesandframeworks.ExperiencedRailsdevelopersalsoreportthatitmakeswebapplicationdevelopmentmorefun. Railsisopinionatedsoftware.Itmakestheassumptionthatthereisabestwaytodothings,anditsdesignedtoencouragethatwayandin somecasestodiscouragealternatives.IfyoulearnTheRailsWayyoullprobablydiscoveratremendousincreaseinproductivity.Ifyoupersist inbringingoldhabitsfromotherlanguagestoyourRailsdevelopment,andtryingtousepatternsyoulearnedelsewhere,youmayhavealess happyexperience. TheRailsphilosophyincludesseveralguidingprinciples: DRYDontRepeatYourselfsuggeststhatwritingthesamecodeoverandoveragainisabadthing. ConventionOverConfigurationmeansthatRailsmakesassumptionsaboutwhatyouwanttodoandhowyouregoingtodoit,rather thanrequiringyoutospecifyeverylittlethingthroughendlessconfigurationfiles.
2 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
RESTisthebestpatternforwebapplicationsorganizingyourapplicationaroundresourcesandstandardHTTPverbsisthefastest waytogo.
3 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
2.2.6 Active Model ActiveModelprovidesadefinedinterfacebetweentheActionPackgemservicesandObjectRelationshipMappinggemssuchasActiveRecord. ActiveModelallowsRailstoutilizeotherORMframeworksinplaceofActiveRecordifyourapplicationneedsthis. 2.2.7 Active Record ActiveRecordisthebaseforthemodelsinaRailsapplication.Itprovidesdatabaseindependence,basicCRUDfunctionality,advancedfinding capabilities,andtheabilitytorelatemodelstooneanother,amongotherservices. 2.2.8 Active Resource ActiveResourceprovidesaframeworkformanagingtheconnectionbetweenbusinessobjectsandRESTfulwebservices.Itimplementsaway tomapweb-basedresourcestolocalobjectswithCRUDsemantics. 2.2.9 Active Support ActiveSupportisanextensivecollectionofutilityclassesandstandardRubylibraryextensionsthatareusedintheRails,bothbythecorecode andbyyourapplications. 2.2.10 Railties RailtiesisthecoreRailscodethatbuildsnewRailsapplicationsandgluesthevariousframeworksandpluginstogetherinanyRailsapplication.
2.3 REST
ReststandsforRepresentationalStateTransferandisthefoundationoftheRESTfularchitecture.ThisisgenerallyconsideredtobeRoy Fieldingsdoctoralthesis,ArchitecturalStylesandtheDesignofNetwork-basedSoftwareArchitectures.Whileyoucanreadthroughthe thesis,RESTintermsofRailsboilsdowntotwomainprinciples: UsingresourceidentifierssuchasURLstorepresentresources. Transferringrepresentationsofthestateofthatresourcebetweensystemcomponents. Forexample,toaRailsapplicationarequestsuchasthis: DELETE /photos/17 wouldbeunderstoodtorefertoaphotoresourcewiththeIDof17,andtoindicateadesiredactiondeletingthatresource.RESTisanatural styleforthearchitectureofwebapplications,andRailshooksintothisshieldingyoufrommanyoftheRESTfulcomplexitiesandbrowserquirks. IfyoudlikemoredetailsonRESTasanarchitecturalstyle,theseresourcesaremoreapproachablethanFieldingsthesis: ABriefIntroductiontoRESTbyStefanTilkov AnIntroductiontoREST(videotutorial)byJoeGregorio RepresentationalStateTransferarticleinWikipedia HowtoGETaCupofCoffeebyJimWebber,SavasParastatidis&IanRobinson
4 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
$ cd blog
Inanycase,Railswillcreateafolderinyourworkingdirectorycalledblog.Openupthatfolderandexploreitscontents.Mostoftheworkinthis tutorialwillhappenintheapp/folder,butheresabasicrundownonthefunctionofeachfolderthatRailscreatesinanewapplicationby default: File/Folder Gemfile README.rdoc Rakefile app/ config/ config.ru db/ doc/ lib/ log/ public/ script/ test/ tmp/ vendor/ Purpose ThisfileallowsyoutospecifywhatgemdependenciesareneededforyourRailsapplication. Thisisabriefinstructionmanualforyourapplication.Useittotellotherswhatyourapplicationdoes,howtosetitup,and soon. Thisfilecontainsbatchjobsthatcanberunfromtheterminal. Containsthecontrollers,models,andviewsforyourapplication.Youllfocusonthisfolderfortheremainderofthisguide. Configureyourapplicationsruntimerules,routes,database,andmore. RackconfigurationforRackbasedserversusedtostarttheapplication. Showsyourcurrentdatabaseschema,aswellasthedatabasemigrations.Youlllearnaboutmigrationsshortly. In-depthdocumentationforyourapplication. Extendedmodulesforyourapplication(notcoveredinthisguide). Applicationlogfiles. Theonlyfolderseentotheworldas-is.Thisiswhereyourimages,javascript,stylesheets(CSS),andotherstaticfilesgo. Containstherailsscriptthatstartsyourappandcancontainotherscriptsyouusetodeployorrunyourapplication. Unittests,fixtures,andothertestapparatus.ThesearecoveredinTestingRailsApplications Temporaryfiles Aplaceforallthird-partycode.InatypicalRailsapplication,thisincludesRubyGems,theRailssourcecode(ifyouinstall itintoyourproject)andpluginscontainingadditionalprepackagedfunctionality.
bundle install
tohavethemready.
5 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
JustabouteveryRailsapplicationwillinteractwithadatabase.Thedatabasetouseisspecifiedinaconfigurationfile,config/database.yml. IfyouopenthisfileinanewRailsapplication,youllseeadefaultdatabaseconfigurationusingSQLite3.Thefilecontainssectionsforthree differentenvironmentsinwhichRailscanrunbydefault: Thedevelopmentenvironmentisusedonyourdevelopmentcomputerasyouinteractmanuallywiththeapplication Thetestenvironmentisusedtorunautomatedtests Theproductionenvironmentisusedwhenyoudeployyourapplicationfortheworldtouse. 3.4.1 Configuring an SQLite3 Database Railscomeswithbuilt-insupportforSQLite3,whichisalightweightserverlessdatabaseapplication.Whileabusyproductionenvironmentmay overloadSQLite,itworkswellfordevelopmentandtesting.RailsdefaultstousinganSQLitedatabasewhencreatinganewproject,butyoucan alwayschangeitlater. Heresthesectionofthedefaultconfigurationfile(config/database.yml)withconnectioninformationforthedevelopmentenvironment:
development: adapter: mysql2 encoding: utf8 database: blog_development pool: 5 username: root password: socket: /tmp/mysql.sock
6 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
development: adapter: postgresql encoding: unicode database: blog_development pool: 5 username: blog password:
Changetheusernameandpasswordinthedevelopmentsectionasappropriate.
$ rake db:create
4 Hello, Rails!
Oneofthetraditionalplacestostartwithanewlanguageisbygettingsometextuponscreenquickly,todothis,youneedtogetyourRails applicationserverrunning.
$ rails server
ThiswillfireupaninstanceoftheMongrelwebserverbydefault(Railscanalsouseseveralotherwebservers).Toseeyourapplicationin action,openabrowserwindowandnavigatetohttp://localhost:3000.YoushouldseeRailsdefaultinformationpage: Tostoptheweb server,hitCtrl+C intheterminal windowwhereits running.In development mode,Railsdoes notgenerally requireyouto stoptheserver; changesyou makeinfileswill beautomatically pickedupbythe server. TheWelcome Aboardpageis
7 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
thesmoketest foranewRails application:it makessurethat youhaveyour software configured correctlyenough toserveapage. Youcanalso clickonthe Aboutyour
applicationsenvironmentlinktoseeasummaryofyourApplicationsenvironment.
$ rm public/index.html
Blog::Application.routes.draw do #... # You can have the root of your site routed with "root" # just remember to delete public/index.html. root :to => "home#index"
8 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
6 Creating a Resource
Inthecaseoftheblogapplication,youcanstartbygeneratingascaffoldedPostresource:thiswillrepresentasingleblogposting.Todothis, enterthiscommandinyourterminal:
Whilescaffoldingwillgetyouupandrunningquickly,theonesizefitsallcodethatitgeneratesisunlikelytobeaperfectfitforyourapplication. Inmostcases,youllneedtocustomizethegeneratedcode.ManyexperiencedRailsdevelopersavoidscaffoldingentirely,preferringtowriteall ormostoftheirsourcecodefromscratch. Thescaffoldgeneratorwillbuild15filesinyourapplication,alongwithsomefolders,andeditonemore.Heresaquickoverviewofwhatit creates: File db/migrate /20100207214725_create_posts.rb.rb app/models/post.rb test/fixtures/posts.yml app/controllers/posts_controller.rb app/views/posts/index.html.erb app/views/posts/edit.html.erb app/views/posts/show.html.erb app/views/posts/new.html.erb app/views/posts/_form.html.erb app/helpers/posts_helper.rb test/unit/post_test.rb test/functional/posts_controller_test.rb test/unit/helpers/posts_helper_test.rb config/routes.rb public/stylesheets/scaffold.css Purpose Migrationtocreatethepoststableinyourdatabase(yournamewillincludeadifferent timestamp) ThePostmodel Dummypostsforuseintesting ThePostscontroller Aviewtodisplayanindexofallposts Aviewtoeditanexistingpost Aviewtodisplayasinglepost Aviewtocreateanewpost Apartialtocontroltheoveralllookandfeeloftheformusedineditandnewviews Helperfunctionstobeusedfromthepostviews Unittestingharnessforthepostsmodel Functionaltestingharnessforthepostscontroller Unittestingharnessforthepostshelper Editedtoincluderoutinginformationforposts Cascadingstylesheettomakethescaffoldedviewslookbetter
9 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
class CreatePosts < ActiveRecord::Migration def self .up create_table :posts do |t| t.string :name t.string :title t.text :content t.timestamps end end def self .down drop_table :posts end end
$ rake db:migrate
RailswillexecutethismigrationcommandandtellyouitcreatedthePoststable.
== CreatePosts: migrating ==================================================== -- create_table(:posts) -> 0.0019s == CreatePosts: migrated (0.0020s) ===========================================
Becauseyoureworkinginthedevelopmentenvironmentbydefault,thiscommandwillapplytothedatabasedefinedinthedevelopment sectionofyourconfig/database.ymlfile.
10 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
Thelink_tomethodisoneofRailsbuilt-inviewhelpers.Itcreatesahyperlinkbasedontexttodisplayandwheretogointhiscase,tothe pathforposts.
class Post < ActiveRecord::Base validates :name , :presence => true validates :title , :presence => true , :length => { :minimum => 5 } end
11 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
Thesechangeswillensurethatallpostshaveanameandatitle,andthatthetitleisatleastfivecharacterslong.Railscanvalidateavarietyof conditionsinamodel,includingthepresenceoruniquenessofcolumns,theirformat,andtheexistenceofassociatedobjects.
$ rails console
Aftertheconsoleloads,youcanuseittoworkwithyourapplicationsmodels:
>> p = Post.new(:content => "A new post") => #<Post id: nil, name: nil, title: nil, content: "A new post", created_at: nil, updated_at: nil> >> p.save => false >> p.errors => #<OrderedHash { :title=>["can't be blank", "is too short (minimum is 5 characters)"], :name=>["can't be blank"] }>
def index @posts = Post.all respond_to do |format| format.html # index.html.erb format.xml :xml => @posts } end end { render
12 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
13 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
< h1 >Listing posts</ h1 > < table > < tr > < th >Name</ th > < th >Title</ th > < th >Content</ th > < th ></ th > < th ></ th > < th ></ th > </ tr > <% @posts . each do |post| %> < tr > < td > <%= post.name %> </ td > < td > <%= post.title %> </ td > <
14 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
Thisviewiteratesoverthecontentsofthe@postsarraytodisplaycontentandlinks.Afewthingstonoteintheview: link_tobuildsahyperlinktoaparticulardestination edit_post_pathandnew_post_patharehelpersthatRailsprovidesaspartofRESTfulrouting.Youllseeavarietyofthesehelpers forthedifferentactionsthatthecontrollerincludes. InpreviousversionsofRails,youhadtouse<%=h post.name %>sothatanyHTMLwouldbeescapedbeforebeinginsertedintothepage.In Rails3.0,thisisnowthedefault.TogetunescapedHTML,younowuse<%= raw post.name %>. Formoredetailsontherenderingprocess,seeLayoutsandRenderinginRails.
<!DOCTYPE html> < html > < head > < title >Blog</ title > <%= stylesheet_link_tag :all %> <%= javascript_include_tag :defaults %> <%= csrf_meta_tag %> </ head > < body style = "background: #EEEEEE;" > <%= yield %> </ body > </ html >
15 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
Nowwhenyourefreshthe/postspage,youllseeagraybackgroundtothepage.Thissamegraybackgroundwillbeusedthroughoutallthe viewsforposts.
def new @post = Post. new respond_to do |format| format.html # new.html.erb format.xml :xml => @post } end end { render
Thenew.html.erbviewdisplaysthisemptyPosttotheuser:
< h1 >New post</ h1 > <%= render 'form' %> <%= link_to 'Back' , posts_path %>
16 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
<%= form_for( @post ) do |f| %> <% if @post .errors.any? %> < div id = "errorExplanation" > < h2 > <%= pluralize( @post .errors.count, "error" ) %> prohibited this post from being saved:</ h2 > < ul > <% @post .errors.full_messages. each do |msg| %> < li > <%= msg %> </ li > <% end %> </ ul > </ div > <% end
17 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
Thispartialreceivesalltheinstancevariablesdefinedinthecallingviewfile,sointhiscase,thecontrollerassignedthenewPostobjectto @postandso,thisisavailableinboththeviewandpartialas@post. Formoreinformationonpartials,refertotheLayoutsandRenderinginRailsguide. Theform_forblockisusedtocreateanHTMLform.Withinthisblock,youhaveaccesstomethodstobuildvariouscontrolsontheform.For example,f.text_field :nametellsRailstocreateatextinputontheform,andtohookituptothenameattributeoftheinstancebeing displayed.Youcanonlyusethesemethodswithattributesofthemodelthattheformisbasedon(inthiscasename,title,andcontent). Railsusesform_forinpreferencetohavingyouwriterawHTMLbecausethecodeismoresuccinct,andbecauseitexplicitlytiestheformtoa particularmodelinstance. Theform_forblockisalsosmartenoughtoworkoutifyouaredoingaNewPostoranEditPostaction,andwillsettheformactiontagsand submitbuttonnamesappropriatelyintheHTMLoutput. IfyouneedtocreateanHTMLformthatdisplaysarbitraryfields,nottiedtoamodel,youshouldusetheform_tagmethod,whichprovides shortcutsforbuildingformsthatarenotnecessarilytiedtoamodelinstance. WhentheuserclickstheCreate Postbuttononthisform,thebrowserwillsendinformationbacktothecreatemethodofthecontroller(Rails knowstocallthecreatemethodbecausetheformissentwithanHTTPPOSTrequest;thatsoneoftheconventionsthatImentionedearlier):
18 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
def create @post = Post. new (params[ :post ]) respond_to do |format| if @post .save format.html { redirect_to( @post , :notice => 'Post was successfully created.' ) } format.xml :xml => @post , :status => :created , :location => @post } else format.html { render :action => "new" } format.xml :xml => @post .errors, :status => :unprocessable_entity } end end end { render { render
19 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
ThecreateactioninstantiatesanewPostobjectfromthedatasuppliedbytheuserontheform,whichRailsmakesavailableintheparams hash.Aftersuccessfullysavingthenewpost,createreturnstheappropriateformatthattheuserhasrequested(HTMLinourcase).Itthen redirectstheusertotheresultingpostshowactionandsetsanoticetotheuserthatthePostwassuccessfullycreated. Ifthepostwasnotsuccessfullysaved,duetoavalidationerror,thenthecontrollerreturnstheuserbacktothenewactionwithanyerror messagessothattheuserhasthechancetofixtheerrorandtryagain. ThePostwassuccessfullycreatedmessageisstoredinsideoftheRailsflashhash,(usuallyjustcalledtheFlash)sothatmessagescanbe carriedovertoanotheraction,providingtheuserwithusefulinformationonthestatusoftheirrequest.Inthecaseofcreate,theusernever actuallyseesanypagerenderedduringthePostcreationprocess,becauseitimmediatelyredirectstothenewPostassoonRailssavesthe record.TheFlashcarriesoveramessagetothenextaction,sothatwhentheuserisredirectedbacktotheshowaction,theyarepresented withamessagesayingPostwassuccessfullycreated.
def show @post = Post.find(params[ :id ]) respond_to do |format| format.html # show.html.erb format.xml :xml => @post } end end { render
TheshowactionusesPost.findtosearchforasinglerecordinthedatabasebyitsidvalue.Afterfindingtherecord,Railsdisplaysitbyusing show.html.erb:
20 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
< p class = "notice" > <%= notice %> </ p > < p > < b >Name:</ b > <%= @post .name %> </ p > < p > < b >Title:</ b > <%= @post .title %> </ p > < p > < b >Content:</ b > <%= @post .content %> </ p > <%= link_to 'Edit' , edit_post_path( @post ) %> | <%= link_to 'Back' , posts_path %>
21 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
Likecreatinganewpost,editingapostisatwo-partprocess.Thefirststepisarequesttoedit_post_path(@post)withaparticularpost.This callstheeditactioninthecontroller:
Afterfindingtherequestedpost,Railsusestheedit.html.erbviewtodisplayit:
< h1 >Editing post</ h1 > <%= render 'form' %> <%= link_to 'Show' , @post %> | <%= link_to 'Back' , posts_path %>
22 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
def update @post = Post.find(params[ :id ]) respond_to do |format| if @post .update_attributes(params[ :post ]) format.html { redirect_to( @post , :notice => 'Post was successfully updated.' ) } format.xml :ok } else format.html { render :action => "edit" } format.xml :xml => @post .errors, :status => :unprocessable_entity } end end end { render { head
23 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
def destroy @post = Post.find(params[ :id ]) @post .destroy respond_to do |format| format.html { redirect_to(posts_url) } format.xml :ok } end end { head
ThedestroymethodofanActiveRecordmodelinstanceremovesthecorrespondingrecordfromthedatabase.Afterthatsdone,thereisnt anyrecordtodisplay,soRailsredirectstheusersbrowsertotheindexviewforthemodel.
24 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
class CreateComments < ActiveRecord::Migration def self .up create_table :comments do |t| t.string :commenter t.text :body t.references :post t.timestamps end end def self .down drop_table :comments end end
Thet.referenceslinesetsupaforeignkeycolumnfortheassociationbetweenthetwomodels.Goaheadandrunthemigration:
$ rake db:migrate
Railsissmartenoughtoonlyexecutethemigrationsthathavenotalreadybeenrunagainstthecurrentdatabase,sointhiscaseyouwilljust see:
== CreateComments: migrating ================================================= -- create_table(:comments) -> 0.0017s == CreateComments: migrated (0.0018s) ========================================
25 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
Youllneedtoeditthepost.rbfiletoaddtheothersideoftheassociation:
class Post < ActiveRecord::Base validates :name , :presence => true validates :title , :presence => true , :length => { :minimum => 5 } has_many :comments end
Thiscreatescommentsasanestedresourcewithinposts.Thisisanotherpartofcapturingthehierarchicalrelationshipthatexistsbetween postsandcomments.
26 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
Formoreinformationonrouting,seetheRailsRoutingfromtheOutsideInguide.
Thiscreatesfourfilesandoneemptydirectory: app/controllers/comments_controller.rbThecontroller app/helpers/comments_helper.rbAviewhelperfile test/functional/comments_controller_test.rbThefunctionaltestsforthecontroller test/unit/helpers/comments_helper_test.rbTheunittestsforthehelper app/views/comments/Viewsofthecontrollerarestoredhere Likewithanyblog,ourreaderswillcreatetheircommentsdirectlyafterreadingthepost,andoncetheyhaveaddedtheircomment,willbesent backtothepostshowpagetoseetheircommentnowlisted.Duetothis,ourCommentsControlleristheretoprovideamethodtocreate commentsanddeleteSPAMcommentswhentheyarrive. Sofirst,wellwireupthePostshowtemplate(/app/views/posts/show.html.erb)toletusmakeanewcomment:
27 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
< p class = "notice" > <%= notice %> </ p > < p > < b >Name:</ b > <%= @post .name %> </ p > < p > < b >Title:</ b > <%= @post .title %> </ p > < p > < b >Content:</ b > <%= @post .content %> </ p > < h2 >Add a comment:</ h2 > <%= form_for([ @post , @post .comments.build]) do
28 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
ThisaddsaformonthePostshowpagethatcreatesanewcomment,whichwillcalltheCommentsControllercreateaction,soletswirethat up:
class CommentsController < ApplicationController def create @post = Post.find(params[ :post_id ]) @comment = @post .comments.create(params[ :comment ]) redirect_to post_path( @post ) end end
29 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
< p class = "notice" > <%= notice %> </ p > < p > < b >Name:</ b > <%= @post .name %> </ p > < p > < b >Title:</ b > <%= @post .title %> </ p > < p > < b >Content:</ b > <%= @post .content %> </ p > < h2 >Comments</ h2 > <% @post .comments. each do |comment| %> < p >
30 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
Nowyoucanaddpostsandcommentstoyourblogandhavethemshowupintherightplaces.
8 Refactoring
NowthatwehavePostsandCommentsworking,ifwetakealookattheapp/views/posts/show.html.erbtemplate,itsgettinglongand awkward.Wecanusepartialstocleanthisup.
< p > < b >Commenter:</ b > <%= comment.commenter %> </ p > < p > < b >Comment:</ b > <%= comment.body %> </ p >
Thenintheapp/views/posts/show.html.erbyoucanchangeittolooklikethefollowing:
31 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
< p class = "notice" > <%= notice %> </ p > < p > < b >Name:</ b > <%= @post .name %> </ p > < p > < b >Title:</ b > <%= @post .title %> </ p > < p > < b >Content:</ b > <%= @post .content %> </ p > < h2 >Comments</ h2 > <%= render :partial => "comments/comment" ,
32 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
33 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
<%= form_for([ @post , @post .comments.build]) do |f| %> < div class = "field" > <%= f.label :commenter %> < br /> <%= f.text_field :commenter %> </ div > < div class = "field" > <%= f.label :body %> < br /> <%= f.text_area :body %> </ div > < div class = "actions" > <%= f.submit %> </ div > <% end
34 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
Thenyoumaketheapp/views/posts/show.html.erblooklikethefollowing:
35 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
< p class = "notice" > <%= notice %> </ p > < p > < b >Name:</ b > <%= @post .name %> </ p > < p > < b >Title:</ b > <%= @post .title %> </ p > < p > < b >Content:</ b > <%= @post .content %> </ p > < h2 >Comments</ h2 > <%= render :partial => "comments/comment" ,
36 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
9 Deleting Comments
AnotherimportantfeatureonablogisbeingabletodeleteSPAMcomments.Todothis,weneedtoimplementalinkofsomesortintheview andaDELETEactionintheCommentsController. Sofirst,letsaddthedeletelinkintheapp/views/comments/_comment.html.erbpartial:
< p > < b >Commenter:</ b > <%= comment.commenter %> </ p > < p > < b >Comment:</ b > <%= comment.body %> </ p > < p > <%= link_to 'Destroy Comment' , [comment.post, comment], :confirm => 'Are you sure?' , :method => :delete %> </ p >
37 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
class CommentsController < ApplicationController def create @post = Post.find(params[ :post_id ]) @comment = @post .comments.create(params[ :comment ]) redirect_to post_path( @post ) end def destroy @post = Post.find(params[ :post_id ]) @comment = @post .comments.find(params[ :id ]) @comment .destroy redirect_to post_path( @post ) end end
Thedestroyactionwillfindthepostwearelookingat,locatethecommentwithinthe@post.commentscollection,andthenremoveitfromthe databaseandsendusbacktotheshowactionforthepost.
38 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
class Post < ActiveRecord::Base validates :name , :presence => true validates :title , :presence => true , :length => { :minimum => 5 } has_many :comments , :dependent => :destroy end
10 Security
Ifyouweretopublishyourblogonline,anybodywouldbeabletoadd,editanddeletepostsordeletecomments. RailsprovidesaverysimpleHTTPauthenticationsystemthatwillworknicelyinthissituation.First,weenablesimpleHTTPbasedauthentication inourapp/controllers/application_controller.rb:
class ApplicationController < ActionController::Base protect_from_forgery private def authenticate authenticate_or_request_with_http_basic do |user_name, password| user_name == 'admin' && password == 'password' end end end
39 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
class PostsController < ApplicationController before_filter :authenticate , :except => [ :index , :show ] # GET /posts # GET /posts.xml def index @posts = Post.all respond_to do |format| # snipped for brevity
Wealsoonlywanttoallowauthenticateduserstodeletecomments,sointheCommentsControllerwewrite:
class CommentsController < ApplicationController before_filter :authenticate , :only => :destroy def create @post = Post.find(params[ :post_id ]) # snipped for brevity
Nowifyoutrytocreateanewpost,youwillbegreetedwithabasicHTTPAuthenticationchallenge
40 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
Again,runthemigrationtocreatethedatabase table:
$ rake db:migrate
Next,editthepost.rbfiletocreatetheotherside oftheassociation,andtotellRails(viatheaccepts_nested_attributes_formacro)thatyouintendtoedittagsviaposts:
41 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
class Post < ActiveRecord::Base validates :name , :presence => true validates :title , :presence => true , :length => { :minimum => 5 } has_many :comments , :dependent => :destroy has_many :tags accepts_nested_attributes_for :tags , :allow_destroy => :true , :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } } end
42 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
<% @post .tags.build %> <%= form_for( @post ) do |post_form| %> <% if @post .errors.any? %> < div id = "errorExplanation" > < h2 > <%= pluralize( @post .errors.count, "error" ) %> prohibited this post from being saved:</ h2 > < ul > <% @post .errors.full_messages. each do |msg| %> < li > <%= msg %> </ li > <% end %> </ ul > </ div >
43 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
44 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
<%= form.fields_for :tags do |tag_form| %> < div class = "field" > <%= tag_form.label :name , 'Tag:' %> <%= tag_form.text_field :name %> </ div > <% unless tag_form.object. nil ? || tag_form.object.new_record? %> < div class = "field" > <%= tag_form.label :_destroy, 'Remove:' %> <%= tag_form.check_box :_destroy %> </ div > <% end %> <% end %>
45 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
Finally,wewilledittheapp/views/posts/show.html.erbtemplatetoshowourtags.
46 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
< p class = "notice" > <%= notice %> </ p > < p > < b >Name:</ b > <%= @post .name %> </ p > < p > < b >Title:</ b > <%= @post .title %> </ p > < p > < b >Content:</ b > <%= @post .content %> </ p > < p > < b >Tags:</ b > <%= @post .tags.map { |t| t.name }.join( ", " ) %> </ p >
47 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
12 View Helpers
ViewHelpersliveinapp/helpersandprovidesmallsnippetsofreusablecodeforviews.Inourcase,wewantamethodthatstringsabunchof objectstogetherusingtheirnameattributeandjoiningthemwithacomma.AsthisisforthePostshowtemplate,weputitinthePostsHelper. Openupapp/helpers/posts_helper.rbandaddthefollowing:
module PostsHelper def join_tags(post) post.tags.map { |t| t.name }.join(", ") end end
Nowyoucanedittheviewinapp/views/posts/show.html.erbtolooklikethis:
48 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
< p class = "notice" > <%= notice %> </ p > < p > < b >Name:</ b > <%= @post .name %> </ p > < p > < b >Title:</ b > <%= @post .title %> </ p > < p > < b >Content:</ b > <%= @post .content %> </ p > < p > < b >Tags:</ b > <%= join_tags( @post ) %> </ p >
49 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
13 Whats Next?
NowthatyouveseenyourfirstRailsapplication,youshouldfeelfreetoupdateitandexperimentonyourown.Butyoudonthavetodo everythingwithouthelp.AsyouneedassistancegettingupandrunningwithRails,feelfreetoconsultthesesupportresources: TheRubyonRailsguides TheRubyonRailsTutorial TheRubyonRailsmailinglist The#rubyonrailschannelonirc.freenode.net TheRailsWiki Railsalsocomeswithbuilt-inhelpthatyoucangenerateusingtherakecommand-lineutility: Runningrake doc:guideswillputafullcopyoftheRailsGuidesinthedoc/guidesfolderofyourapplication.Opendoc/guides /index.htmlinyourwebbrowsertoexploretheGuides. Runningrake doc:railswillputafullcopyoftheAPIdocumentationforRailsinthedoc/apifolderofyourapplication.Open doc/api/index.htmlinyourwebbrowsertoexploretheAPIdocumentation.
14 Configuration Gotchas
TheeasiestwaytoworkwithRailsistostoreallexternaldataasUTF-8.Ifyoudont,RubylibrariesandRailswilloftenbeabletoconvertyour nativedataintoUTF-8,butthisdoesntalwaysworkreliably,soyourebetteroffensuringthatallexternaldataisUTF-8. Ifyouhavemadeamistakeinthisarea,themostcommonsymptomisablackdiamondwithaquestionmarkinsideappearinginthebrowser. Anothercommonsymptomischaracterslikeappearinginsteadof.Railstakesanumberofinternalstepstomitigatecommoncausesof theseproblemsthatcanbeautomaticallydetectedandcorrected.However,ifyouhaveexternaldatathatisnotstoredasUTF-8,itcan occasionallyresultinthesekindsofissuesthatcannotbeautomaticallydetectedbyRailsandcorrected. TwoverycommonsourcesofdatathatarenotUTF-8: Yourtexteditor:Mosttexteditors(suchasTextmate),defaulttosavingfilesasUTF-8.Ifyourtexteditordoesnot,thiscanresultin specialcharactersthatyouenterinyourtemplates(suchas)toappearasadiamondwithaquestionmarkinsideinthebrowser.This alsoappliestoyourI18Ntranslationfiles.MosteditorsthatdonotalreadydefaulttoUTF-8(suchassomeversionsofDreamweaver) offerawaytochangethedefaulttoUTF-8.Doso. Yourdatabase.RailsdefaultstoconvertingdatafromyourdatabaseintoUTF-8attheboundary.However,ifyourdatabaseisnot usingUTF-8internally,itmaynotbeabletostoreallcharactersthatyourusersenter.Forinstance,ifyourdatabaseisusingLatin-1 internally,andyouruserentersaRussian,Hebrew,orJapanesecharacter,thedatawillbelostforeveronceitentersthedatabase.If possible,useUTF-8astheinternalstorageofyourdatabase.
15 Changelog
August30,2010:MinoreditingafterRails3releasebyJoostBaaij July12,2010:Fixes,editingandupdatingofcodesamplesbyJaimeIniesta May16,2010:AddedasectiononconfigurationgotchastoaddresscommonencodingproblemsthatpeoplemighthavebyYehuda Katz April30,2010:Fixes,editingandupdatingofcodesamplesbyRohitArondekar April25,2010:CoupleofmoreminorfixupsMikelLindsaar April1,2010:FixeddocumenttovalidateXHTML1.0Strict.JaimeIniesta February8,2010:Fullre-writeforRails3.0-beta,addedhelpersandbefore_filters,refactoredcodebyMikelLindsaar January24,2010:Re-writeforRails3.0byMikelLindsaar July18,2009:MinorcleanupinanticipationofRails2.3.3byMikeGunderloy February1,2009:UpdatedforRails2.3byMikeGunderloy November3,2008:FormattingpatchfromDaveRothlisberger November1,2008:FirstapprovedversionbyMikeGunderloy October16,2008:RevisedbasedonfeedbackfromPratikNaikbyMikeGunderloy(notyetapprovedforpublication) October13,2008:FirstcompletedraftbyMikeGunderloy(notyetapprovedforpublication) October12,2008:Moredetail,rearrangement,editingbyMikeGunderloy(notyetapprovedforpublication) September8,2008:initialversionbyJamesMiller(notyetapprovedforpublication)
Feedback
You'reencouragedtohelpinkeepingthequalityofthisguide.
50 of 51
02/11/2011 11:39 AM
http://guides.rubyonrails.org/getting_started.html#...
51 of 51
02/11/2011 11:39 AM