Professional Documents
Culture Documents
LearnWebDevelopmentWithRubyonRails
WhoamI?
IamHarryandjustlikeyouIhadneverdoneanycodingbefore.AcompletebeginnerIwanted
tolearnhowtocreatewebappssuchasapersonalblog,Signupforms,Authenticationapps
thatallowuserstologinandlogout.Iwantedtoknowhowtocodeusefulappsthatcouldbe
usedforpersonalwebsitesorevenabusinessstartup.SoIstartedlearninghtmlandcssand
rubyandeventuallyrubyonrails.AndnowIwanttosharewithyouwhatIlearned.Ihopeyou
learnsomethingfromthesetutorials.
Howtoworkthroughthisbook
IfyouarenewtocodingIrecommendyoureadthebookandtypeoutallthecodeshown.This
willtakelongerbutitwillstopyoufromglazingover.TrustmeIknow.Alsoifyoutypeoutthe
codeexamplesandfollowalongthenyouwilllearnandretaintheinformationfarbetter.
Ifyouhavecodedbeforethenfeelfreetojumptowhateveryoufindmostrelevant.
Settingupourworkingenvironment
TheworkingenvironmentIamgoingtorecommendisacloudbasedenvironment,butwhyyou
mightask?Whynotalocalenvironmentonyourowncomputer?Wellsimplyputeveryone's
computerwillvaryandsoacloudbasedenvironmentisaneasydefaultthatallcanuse.Ifyou
wanttosetupalocalenvironmentthenfeelfree,butthisisabookoncodetutorialsandsoI
wantyoutobecodingasquicklyaspossible.Alsocloud9providesafreeaswellaspaid
serviceandthefreeserviceisverygoodsoitiswhatwewillbeusing.
Goto
http://c9.io/
thewebsiteforcloud9whichisacloudbasedcodingenvironment.
Clickonthegettingstartedbuttonorsignupbutton.
Youwillseeasignupform,goaheadandsignup.
Afteryouhaveputinyourdetailsandverifiedyouremailyouwillseeascreenliketheone
below:
Thisscreenisyourhomescreenandholdsallofyourworkspaces.Workspacescanbethought
ofasprojectfolders.Youcancreatemanyrailsapplicationsinsidetheseworkspaces,sodont
thinkyouhavetocreateanewworkspaceforeveryrailsapplication.
ClicktheCreateanewworkspacebutton
Thiswillleadyoutoascreenthatlookslikethis:
Itprovidesyouwithvariousdefaultstohelpsetupyourworkspacehoweverwewillgoforthe
customtemplateoption.
Fillintheworkspacenameatthetop,callitwhateveryoulikeandaddadescription.Icalledmy
workspacerailstutorials.Leavethehostedworkspaceoptionaspublic.Againmakesureyou
haveselectedthecustomtemplateoption.Itisthedefaultoptionanyway.Finallyclickthe
createworkspacebutton.
Itmaytake30secondsbutdontworryallisgood...
OkayoncetheworkspaceiscreatedyouwillbegreetedwithyourenvironmentorIDE
integrateddevelopmentenvironment.Basicallyitisyourowncomputerinthecloudandhas
everythingpreinstalledthatweneedtogetstarted.
Youwillseeatoolbaratthetop,fileeditfindetc.Onthelefthandsideisasidebarofyour
projectfolders.SinceInamedmyworkspacerailstutorialsIaminthatworkspace.Thesidebar
showsallthefilesandfoldersinthatworkspace.Theonlyfileinthereatthemomentwillbea
README.mdfile.Asyoucanseeitisalsobeingdisplayedinthecenterofthepage.Inthe
centerofthepagenearthetoparesometabs.ClosetheREADMEfilebyclickingthelittlexon
thetab.
Youwillseethewelcometabisalsoopen.Youcanadjustthesettingsasyouwish,I
recommendkeepingitthesame.Theonlychangeneedediswhereitsayssofttabs4,
Change
thesofttabsto2.
Thatishowfaryourcodeisindentedwhenpressingthetabkeyonyour
computer.Nowclickcloseonthewelcometab.
Atthebottomofyourscreenyouwillseeyourconsole,itwilldisplayyourusernamefollowedby
thefolder/directoryyouarein,inthiscasetheworkspacedirectory.
Coolourdevelopmentenvironmentisnowsetup.
LetscheckthattheprogramminglanguagerubyisinstalledandtheRailsframeworkisalso
installed.
Atthebottominyourconsoletypethecommand:
ruby
Hereismyoutput.Noticethatthedollarsignistheprompt.Youdonttypeit,itshowstheendof
yourpromptorthebeginningofwhereyoutypeyourcommands.SeehowitshowsIaminthe
workspacedirectory.
harryoliver
:~/
workspace$ruby
v
ruby
2.3
.
0p0
(
2015
12
25
revision
53290
)
[
x86_64
linux]
Asyoucanseetheoutputsaysruby2.3.0whichisfine.Ifyouhaveaversionthatisnewerthen
thatwillalsobefine.
Nextintheconsoletypethecommand:
rails
YoucanseetheoutputisRails4.2.5againifyourversionislaterthanthisversionthenitshould
befine.
harryoliver
:~/
workspace$rails
v
Rails
4.2
.5
Excellentthetherubyprogramminglanguagewerequireisinstalledandsoistherails
framework.
Thecommandline
Ifyouhaveexperiencewiththecommandlinealreadythenfeelfreetoskipthischapter.Itisfor
thepeoplewhohaverarelyusedorneverusedthecommandlineatall.Iwillcoversomebasic
commandsandwhattheydo,thesecommandswillcropupthroughoutthetutorialssodont
worryaboutmemorizingthemasyouwillseethemenoughthroughoutthisbook.
TheTouchCommand
Atthebottomofyourscreeninthecommandline/console(Iwillusethenames
interchangeably)type:
$touchhello.txt
Rememberyoudonttypethedollarsignasthatistheprompt.
Ifyoulooktotheleftofyourscreenwhereallyourfilesandfoldersareyoushouldseeafile
calledhello.txt.Ifyoudontthenlookforasmallgeariconnearthetopofthefilesandfolders
section.Clickitandyouwillseeadropdownmenuwithanoptionto
refreshthefiletree
which
willshowanynewfilesorfoldersthathavebeencreated.
Howdoesthetouchcommandwork?
Thetouchcommandcreatesanewemptyfile.Itworksbytypingthecommandnamewhichis
touchfollowedbyanyoptionsandthenthefilename.
touch
[
option
]
file_name
Dontworrythisactuallymakesthecommandlookmoreconfusing.Wewontbeusingany
optionswiththetouchcommandandsowecanjusttypetouchfollowedbythefilename.
Typethistouchcommandintotheconsole:
$touchfile1file2file3
Nowlookatyourfilestotheleft,inonecommandyouhavetouched3filesintoexistence,pretty
coolright?
TheLSCommand
Nowtypethiscommandintoyourconsole:
$ls
README.mdfile1file2file3hello.txt
YoushouldgetalistofallthefilesyouhavecreatedincludingtheREADMEfile.Thels
commandisshortforlistandliststhefilesinthecurrentdirectory.Prettycool,wecanalsopass
optionsorargumentstothelscommandaswell.Wecouldpassthelscommandthealikethis:
$lsa
./../.c9/README.mdfile1file2file3hello.txt
Whenyouaddanoptionsuchastheawhichstandsforall,youaresayinglistmethefilesbutI
wantallofthem.Eventhehiddenfiles.Hiddenfileshaveasingledotinfrontofthem..c9/isa
hiddendirectory,Iknowitisadirectory/folderandnotafilebecauseoftheforwardslashatthe
end.Thataoptionthatyouusedisknownasaflag,thatisthecorrectterminologyforthe
optionsyoupasstothecommands.So
ls
isthecommandandtheflagis
a
,itisanoptionyou
passtothelscommand.
ThePWDCommand
Thiscommandprintsyourcurrentlocationtothescreen.Youareintheworkspace
directory/foldersothatiswhatwillbeprintedtothescreen,trythis:
$pwd
/home/ubuntu/workspace
Thecommandstandsfor
PrintWorkingDirectory
andasyoucanseeyourcurrentdirectoryis
theworkspacedirectorythatcloud9hasgivenyou.Youcanalsoseethattheworkspace
directoryisinadirectorycalledubuntuwhichisinanotherdirectorycalledhome.Bydefault
cloud9actuallyshowwhatdirectoryyouarein,youcanseethisbylookingatyourprompt.
~/workspace$
Thepromptshowstheworkspacedirectory.
TheMkdirCommand
Thiscommandcreatesanewdirectoryorfolder.Rememberhowyouusedthetouchcommand
tocreateanewfile?Thiscommandisusedtocreateanewfolder.
Trytypingthisintoyourconsole:
$mkdirmyfolder
Themkdircommandisshortfor
makedirectory
andisquiteselfexplanatory.Ifyoulooktothe
leftatyourfilesandfoldersyouwillseeanewfoldercalledmyfolder.Itisemptyatthemoment
sowewilladdsomefilestoit.Wealreadyknowhowtocreateanewfilewiththe
touch
commandbuthowdowegetinsidethatnewfolderwejustcreated?
TheCDcommand
Thecdcommandisusedtochangethedirectoryyouarein.Itstandsfor
changedirectory
.If
youlookatyourpromptyouwillseeyouareintheworkspacedirectory.Trytypingthisto
changetothemyfolderdirectory.
$cdmyfolder
~/workspace/myfolder$
Seehowthepromptupdatesandyouarenowinthemyfolderdirectory.SohowdoIgetout?
Thewaytogetbacktoyourworkspacedirectoryistotype:
$cd../
Thatisthecdcommandthenaspacethentwodotsandaforwardslash.Thisjumpsyououtof
onefolderandintotheoneabove.Sincewewereintheworkspacedirectorywhenwecreated
themyfolderdirectorytheworkspacedirectoryistheoneabove.
Ifyoutypecdonitsownthenyouwillgototherootdirectory.Inthiscaseitisrepresentedbya
tilde~oncloud9.Ifyoulistallthefilesandfolderswiththelscommandyouwillseethe
workspacedirectorywhereyouhavebeenworking.
~/workspace$cd
~$ls
lib/workspace/
~$cdworkspace
~/workspace$
Iwantedtoshownewusersofthecommandlinehowtousethecommandscdandlshereso
thatyoudontgetstuck.Remembercd../isusedtojumpoutofthecurrentfolderyouarein.
TheRMcommand
Thermcommandisshortforremoveanditdeletesfilesandfolders.Trytyping:
$rmfile1
Youwillseethatthefilenamedfile1hasnowbeendeleted.
Nowtrytodeletethefoldernamedmyfolder.
$rmmyfolder
Youwillprobablygetamessagelikethis:
$rmmyfolder
rm:cannotremovemyfolder:Isadirectory
Okayyoucannotdeletemyfolderasitisadirectory.
Thenextcommandwillremovethefolder,trythecommandagainbutaddtherflag.
$rmrmyfolder
Asyouwillseethefolderisnowdeleted.Therisshortforrecursively,Itbasicallymeansthat
anyfilesinsidethatdirectorywillalsobedeletedandaregoneforever.Thatiswhywehadthat
originalerrormessage.Wewerebeingprotectedfromaccidentallydoingsomethingstupid.We
didntactuallyhaveanyfilesinourmyfolderdirectorybutyougettheidea.
Okaytogetbacktosquareonetype:
$rmfile2file3hello.txt
Thisremovesallthefileswecreated,wehavejustnamedthemoneafteranotherinsteadof
typingthermcommandmultipletimes.
DuringtherestofthetutorialsIwillmentionwhatcommandsneedtobeusedsoyoudontneed
tomemorizethesecommands,Theyarejusttheretoshowyouthebasicsofnavigating,
creatinganddeletingfiles.
Ifyouarebrandnewtoanyofthistryexperimentingandcreatingyourownfilesandfolders.
Thebestwaytolearnistogetstuckandthengetunstuck.Hopefullynottakingtoolonginthe
process.
YourFirstRailsApp
AStaticWebpage
Okaytimeforyoutocreateyourfirstrailsapp.Itwillbeverysimpleandonlydisplayapage,
howeverhopefullyyouwilllearnabitaboutthestructureofarailsapp(thewayallthefilesand
foldersarelaidout)aswellasafewofthebasicrailscommands(Thatwewillusetogenerate
thingsforus,similartothecommandswehavebeenusingabove)andalittlebitaboutrails
conventions(railsdoesthingsinacertainwaytosaveyoutime).
Inyourworkspacedirectorytypetherailscommandtogenerateanewrailsapp:
Creatingyourfirstrailsapp
/workspace
$rails_4.2.5_newstatic_page
create
createREADME.rdoc
createRakefile
createconfig.ru
create.gitignore
createGemfile
createapp
createapp/assets/javascripts/application.js
createapp/assets/stylesheets/application.css
createapp/controllers/application_controller.rb
createapp/helpers/application_helper.rb
createapp/views/layouts/application.html.erb
createapp/assets/images/.keep
createapp/mailers/.keep
...
Youwillgetanicebiglistofthingsthathavebeencreatedandinstalled,muchlongerthanthe
listhere,Ihaveshortenedittosavespace.
Letslookatthatrailscommandusedtogenerateanewapplication:
$rails_4.2.5_newstatic_page
Westartwiththewordrailsasweareusingarailscommand,thenwehaveanargumentwe
passedspecifyingaversionnumber.Let'sgetridofthatforaminuteandtakeanotherlookat
thecommand.
$railsnewstatic_page
OkaycoolthisisstartingtolookalittlebitmorelikeenglishIcanunderstand!Seethewords
static_page,thatisthenameofthedirectoryorrailsapp.Itiswhatwehavecalledthisproject.
$railsnew
Sorailsnewistheactualcommand.Weusethiscommandtogenerateanewapplication.A
newwhatthough?Thatiswhywenamedtheapplicationstatic_page.Itcouldhavebeennamed
anythingbutitmadesensetocallitsomethingrelevant.Sobacktotheoriginalcommand,why
theunderscoresandnumbers?
$rails_4.2.5_newstatic_page
The_4.2.5_wepassedtotherailsnewcommandisaversionnumber.Itmeansusethis
versionofrails.ThereasonIhavedonethisissothatyoucanfollowalongwithasfewhiccups
aspossible.Ifyouweremakingyourownappyoumightjustdo
railsnewmyapp
withouta
versionnumber.
Okaycdintoyournewrailsapp,andonthelefthandsidewherethefilesandfoldersare
displayedclickyournewfolder(thelittletriangle)toopenupthenewapplication.
$cdstatic_page
~/workspace/static_page$
Doyouseetheapp/directory,config/directorythesetwofoldersaretheoneswewillusemost
forthistutorial.Okayinorderforustocheckthateverythinghasinstalledcorrectlylet'sstartup
theserverandruntherailsapplication.Typethecommand:
Creatingyourfirstrailsapp
/workspace/static_page
$railsserverb$IPp$PORT
Theactualcommandisrailsserverallonitsown,howeverwehaveabflagandaglobal
variable$IPfollowedbyapflaganda$PORTglobalvariable.Whenusingcloud9youadd
thoseflagsandglobalvariablestoruntheserver.Whenusingcloud9ifyoutypejustrailsserver
itwontwork.Theextraflagsandglobalvariablesarealittlebitofapaintotype,sofeelfreeto
copythiscommandintoyourconsole.
Creatingyourfirstrailsapp
/workspace/static_page
$railsserverb$IPp$PORT
=>BootingWEBrick
=>Rails4.2.5applicationstartingindevelopmentonhttp://0.0.0.0:8080
=>Run`railsserverh`formorestartupoptions
=>CtrlCtoshutdownserver
[2016022313:05:55]INFOWEBrick1.3.1
[2016022313:05:55]INFOruby2.3.0(20151225)[x86_64linux]
[2016022313:05:55]INFOWEBrick::HTTPServer#start:pid=1677port=8080
Lookattheoutputitsaysbootingwebrick,thisisthedefaultrailsserver.Thenabitfurtherdown
itsaysCTRLCtoshutdowntheserver.ThatistheCTRLkeyplustheletterCkeyonyour
computer.
Seethecloud9helppopupontherightthere,clickthelinkanditwillopenyourrailsappina
newtab,Orintheconsoleoutputafterthebootingwebrickline,clickthe
http://0.0.0.0:8080
line
whereitsaysapplicationstartingindevelopment.Youshouldseethis:
Thisisthedefaultrailsapplication,Feelfreetocheckoutthelinkontherightsuchasrails
guides,railsapietc.ClickCTRLCtoexittheserver(youneedtoclickintheconsolefirst)and
closetheextratab.Coolyouhavecreatedadefaultrailsapplicationandruntheserverand
viewedtheapplication.LETSGETCUSTOMIZING!
GeneratingtheController
Westartbygeneratingthecontroller,asitsnamesuggestsitcontrolsthings.Ifwetypeinaurl
itwillcontrolwhatwesee(Dontworryagreaterexplanationwillfollow).Typethecommandin
yellow,asyoucanseethenameoftherailscontrollerisPages.
Creatingyourfirstrailsapp
/workspace/static_page
$railsgeneratecontrollerPages
RunningviaSpringpreloaderinprocess1711
createapp/controllers/pages_controller.rb
invokeerb
createapp/views/pages
invoketest_unit
createtest/controllers/pages_controller_test.rb
invokehelper
createapp/helpers/pages_helper.rb
invoketest_unit
invokeassets
invokecoffee
createapp/assets/javascripts/pages.coffee
invokescss
createapp/assets/stylesheets/pages.scss
$railsgeneratecontroller
Theactualcommandistheoneabove,wetellrailsgenerateacontroller.Wepassthe
commandthenameofthecontroller.InthiscasePages.Lookatwhatgetscreated,a
pages_controller.rb
filethatislocatedinthedirectory
app/controllers/pages_controller.rb
.
Alsolookattheapp/views/pagesdirectory.Apagesdirectoryhasbeencreated.Inyour
navigatorontheleftwithallyourfilesandfolderstryfindingthesefilesanddirectories
mentioned.
WeusedthecommandrailsgeneratecontrollerPageswithacapitalp.Thefilethatgot
generatedthoughwaspages_controller.rb.Railshasautomaticallyconvertedthefilenameto
snakecase(Theuseofunderscores).
Startupyourserveragain:
Creatingyourfirstrailsapp
/workspace/static_page
$railsserverb$IPp$PORT
Intheurlatthetopofthepageadd
/pages
totheendoftheurl.Youwillseearoutingerror.We
willsolvethisshortly.Basicallyweneedtotellrailsalltheroutes/urlsthatwewilluse.
Openupthefilepages_controller.rbthatyouhavejustgenerated.Seehowthe
pages_controller.rbisnicelylocatedintheappfolderthenthecontrollersfolderandthenyou
seethefilewewantpages_controller.rb.
Creatingyourfirstrailsapp
/workspace/static_page/app/controllers/pages_controller.rb
classPagesController<ApplicationController
end
Letseditthisfilealittlebit.SeewehaveaclassofPagesControllerthatgotgenerated,theless
thansign<meansthisclassinheritsfromtheApplicationControllerwhichispartofrails.Also
seethattheclassisdefinedwiththeclasswordfollowedbythenameoftheclassandthenona
newlinetheendword.Betweenthisiswhereweaddourcode.
Creatingyourfirstrailsapp
/workspace/static_page/app/controllers/pages_controller.rb
classPagesController<ApplicationController
defhome
end
end
Addthehomeaction.ThethingIfounddifficultaboutlearningrailswasthatthingshaddifferent
names,forexamplethehome
action
isactuallyaruby(programminglanguage)method.Rails
isbuiltontopofrubyandthewayyoudefineamethodinrubyiswiththedefandend
keywords.Inotherprogramminglanguagesamorecommonnameformethodisafunction.So
technicallyyoucouldcallwhatyoujustwroteahomemethodorfunction.Howeverwewillstick
withthecorrectterminologyhereandcallitahomeaction.Alsoyouwillseethatthemethod
itselfisempty.Thisisokay,innormalrubyprogrammingthiswoulddonothing,howeverwith
railssincethisPagesControllerclassinheritsfromtheApplicationControllerfromrailsinsteadof
doingnothingbydefaultitrendersaview.Wewillcreatetheviewinaminute.
FirstSaveThe
FileByPressingCTRLSasmallcircleinthetabnearthetopwillbecomeanx.Always
saveeverychangeyoumake!
Lookatthepages_controller.rbfile,itisnamedwithsnakecaseorunderscoresbuttheclass
insidethefilePagesControllerhasbeennamedincamelcasewhereeveryfirstletterofaword
iscapitalized.Thisisanotherwayrailsdoesthingsanditwasautomaticallydonewhenwe
generatedthePagesController.Donttypethatbitbelow.
$railsgeneratecontrollerPages
Sincethecommandstateswewanttogeneratea
controller
wedontneedtotypetheword
controllerafterpages,itisconvertedforus.AlsoifyoulookatthewordPagesnoteIused
camelcaseforthenameofthecontroller.SoifIhadcalledthecontrollerwebsitepagesitwould
havebeennamed:WebsitePages.
AddingtheRoute
Nowthatwehaveourcontrollerwiththehomeaction,letsaddtheroute.Ifyourememberwhen
wetypedin/usersontotheendoftheurlwegotanerror,aroutingerror.Railsdidntknow
wheretolook.Soletsfixtheproblem.Gotoyourroutes.rbfilelocatedintheconfigfolder.
Creatingyourfirstrailsapp
/workspace/static_page/config/routes.rb
Rails.application.routes.drawdo
#Thepriorityisbaseduponorderofcreation:firstcreated>highestpriority.
#Seehowallyourrouteslayoutwith"rakeroutes".
#Youcanhavetherootofyoursiteroutedwith"root"
#root'welcome#index'
#Exampleofregularroute:
#get'products/:id'=>'catalog#view'
#Exampleofnamedroutethatcanbeinvokedwithpurchase_url(id:product.id)
#get'products/:id/purchase'=>'catalog#purchase',as::purchase
#Exampleresourceroute(mapsHTTPverbstocontrolleractionsautomatically):
#resources:products
#Exampleresourceroutewithoptions:
#resources:productsdo
#memberdo
#get'short'
#post'toggle'
#end
#
#collectiondo
#get'sold'
#end
#end
...
Okaythereisalotgoingonbuttheyareallcommentsapartfromthetopandbottomlines.Add
thiscodetothefile:
Creatingyourfirstrailsapp
/workspace/static_page/config/routes.rb
Rails.application.routes.drawdo
root'pages#home'
#Thepriorityisbaseduponorderofcreation:firstcreated>highestpriority.
#Seehowallyourrouteslayoutwith"rakeroutes".
#Youcanhavetherootofyoursiteroutedwith"root"
#root'welcome#index'
#Exampleofregularroute:
#get'products/:id'=>'catalog#view'
#Exampleofnamedroutethatcanbeinvokedwithpurchase_url(id:product.id)
#get'products/:id/purchase'=>'catalog#purchase',as::purchase
...
Ihavenotdeletedanything,onlyaddedsomecodetoline2.Therootwordtellsrailswhatpage
toloadfirstwhenyougototheapplication.Ahomepageiswhatyouwouldusuallygoto,this
canofcoursebeanypageyoucreatebutinthistutorialweonlyhaveonepagesodontworry.
Lookatthelinepages#home,rememberwegeneratedthepagescontrollerwearetellingrails
usethatsamepagescontroller.Openupthatpages_controller.rbfileandyouwillseethehome
actionwecreated.Afterthewordpagesweaddahashorpoundsign(railssyntax)andthenwe
sayusethishomemethodIwrote.
Sohopefullythatroutinglinemakesalittlebitmoresense.Rootmetothepages_controllerand
Iwanttoseethehomeaction.Soitisthecontrollerfirstthenapoundsignandthentheaction
name.
Startupyourserver:
Creatingyourfirstrailsapp
/workspace/static_page
$railsserverb$IPp$PORT
Okaywehavingamissingtemplateerror.Notaproblem.RememberIsaideventhoughour
homeactionisemptyrailsstilldoesstuffbydefault.Wellbydefaultitrendersaview.Timeto
createthatview.
PressCTRLCtoclosetheserver.
CreatingTheView
Lookintheappfolderthenviewsfolderandyouwillseeanemptypagesfolder.Thisfolderwas
generatedwhenweusedtherailsgeneratecontrollerPagescommand.Itautomatically
generatedafoldertoputallofourviewsrelatedtothepages_controller.
Youhavetwowaystocreatetheviewfilethatweneed.Youcanusethetouchcommand
coveredearlieroryoucanrightclickandhitcreatenewfile.Eitherwayweneedafilenamed
home.html.erb
Thefileisnamedhomebecausewecreatedahomeactionremember?The
HTMListoletusknowitisahtmlfileandthe.erbletsusknowwecanuseembeddedrubya
typeofsyntaxthatletsustyperubycodeinahtmlfileaswellasnormalhtmltagssuchas
<p></p>and<div></div>.
Method1
~/workspace/static_page$touchapp/views/pages/home.html.erb
Seehowwespecifythepathwherewewanttocreatethefile,Wewantitintheappfolderthen
intheviewsfolderthenouremptypagesfolderandthentheactualfilenameitself
home.html.erb
Rememberrailsisbuiltwithruby,thefilenamethereforehasthe.erbextension
sothatwecanuseembeddedrubyinthefileifwewantto.
Method2
Oryoucanrightclickandnamethenewfilehome.html.erbwhicheveriseasierforyou:
Okayopenupyournewlycreatedhome.html.erbfileandaddsomehtmltothepage.
Creatingyourfirstrailsapp
/workspace/static_page/app/views/pages/home.html.erb
<h2>WelcomeHome</h2>
<p>Afteryourlongjourneythroughrockymountainsanddrydustbowlsyouhavefinally
nearedtheend.</p>
Feelfreetocopyandpastethehtml.OnceagaindontforgettosavebypressingCTRLSYou
candofilethensavebutthattakestoolong.Ihavenotmentionedsavingverymuchbutmake
sureyousaveinordertoseethechangesyouhavemade.
Okaystartupyourserveragain:
Creatingyourfirstrailsapp
/workspace/static_page
$railsserverb$IPp$PORT
Youshouldhavesomethingsimilartothis:
Welldoneyouhavecreatedyourfirstrailsapplication.Sowhatexactlyhaveyoudonethen?
YoustartedbygeneratingaPagescontrollerandaddingahomeactiontothePagesController
class.Youthendefinedtherootroutewhichmeantyourapplicationwouldbydefaultloadthe
homeactionofthePagescontroller,Andfinallyyouaddedthehome.html.erbview.Youhave
createdastaticrailsapp.Iknowitdoesntseemveryimpressivebuttheseareallthingsyouwill
domultipletimeswhencreatingrailsappssoithelpstonailthebasics!
Ifyouarestillinyourrailsapplicationfolderyoucancdoutofitbackintoyourworkspace.Onto
applicationnumber2.
~/workspace/static_page$cd../
~/workspace$pwd
/home/ubuntu/workspace
WELLDONE!
AStyledApp
Thesecondappyoubuildwillbesimilartothefirstapp.Itwillhelpdrumhomevariousrails
commandsandthingsyouneedtoremember.Thestartofthisappisgoingtohavealittleless
explanationcomparedtothefirstapp.Thisissothatwecanspeedilygetsetupandcontinue
learning.ThenwhenweareatanewpointIwillstarttheexplanationsagain.Thisfirstbitwont
beanythingtoonew,itwillbeverysimilartothepreviouschapter.
Youmaybewonderingwhynotjustcontinuewiththefirstappandbuildontopofthat?Wellin
myopinionabetterwaytolearnistobuildmultipleappsbecauseyouarefarmorelikelytobe
typingthecommandshundredsoftimescomparedtojustafewtimes.Forexampleyoudont
actuallytypetherailsnewcommandthatoften,onlyatthestartofanapplication.Howeveritis
stillimportanttorememberthecommandotherwiseyouwontbecreatinganynewapps
hahahaha.
Makesureyouareintheworkspacedirectory:
~/workspace$pwd
/home/ubuntu/workspace
Generateanewrailsappcalledstyled_app
Astyledapp
/workspace
$rails_4.2.5_newstyled_app
create
createREADME.rdoc
createRakefile
createconfig.ru
create.gitignore
createGemfile
createapp
createapp/assets/javascripts/application.js
createapp/assets/stylesheets/application.css
createapp/controllers/application_controller.rb
createapp/helpers/application_helper.rb
createapp/views/layouts/application.html.erb
createapp/assets/images/.keep
createapp/mailers/.keep
...
Changedirectoryintothenewapp:
$cdstyled_app/
~/workspace/styled_app$
Runtheserver:
Checkallisfine.
Astyledapp
/workspace/styled_app
$railsserverb$IPp$PORT
Generateapagescontroller:
Astyledapp
/workspace/styled_app
$railsgeneratecontrollerPageshome
RunningviaSpringpreloaderinprocess3734
createapp/controllers/pages_controller.rb
routeget'pages/home'
invokeerb
createapp/views/pages
createapp/views/pages/home.html.erb
invoketest_unit
createtest/controllers/pages_controller_test.rb
invokehelper
createapp/helpers/pages_helper.rb
invoketest_unit
invokeassets
invokecoffee
createapp/assets/javascripts/pages.coffee
invokescss
createapp/assets/stylesheets/pages.scss
Okaytimeforacloserlookatthatgeneratecommand.Mostofthecommandshouldmake
sense,youaretellingrailstogenerateacontrollernamedPages,howeverifyoulookwehave
addedthewordhome.Wehaveusedthesamecommandbutaddedanargument.
railsgeneratecontrollerNAME[action]
Theargumentweaddedwasanaction.Rememberinsidethecontrollerwecanaddruby
methodshowevertheyarecalledactions.Rememberthehomeactionfromtheprevious
chapter.Thisisaquickerwayofgeneratingthesamething.
Lookatwhatgotcreated,aroutepages/homeaswellasahome.html.erbtemplateinits
appropriatepagesfolderwhichisagaininsidetheviewsfolder.Attheverytopthe
pages_controller.rbgotcreatedandifyoulookatthebottomyoucanseesome
app/assets/stylesheetsgotgeneratedwhichwecanusetostyletheapplicationwithcss.
Lookatyourpages_controller.rbfile:
Thehomeactionautomaticallygotgenerated.
Astyledapp
/workspace/styled_app/app/controllers/pages_controller.rb
classPagesController<ApplicationController
defhome
end
end
Lookatyourhome.html.erbfileitalsogotautomaticallygenerated:
Astyledapp
/workspace/styled_app/app/views/pages/home.html.erb
<h1>Pages#home</h1>
<p>Findmeinapp/views/pages/home.html.erb</p>
Nowtakealookatyourroutes.rbfile:
Astyledapp
/workspace/styled_app/config/routes.rb
Rails.application.routes.drawdo
get'pages/home'
#Thepriorityisbaseduponorderofcreation:firstcreated>highestpriority.
#Seehowallyourrouteslayoutwith"rakeroutes".
...
Agetroutealsogotaddedautomatically.Thisisabitdifferentthantherootrouteweused
earlier.Howeveritstillusesthepagescontrollerandhomeactioninthatcontroller.
Runtheserver:
Astyledapp
/workspace/styled_app
$railsserverb$IPp$PORT
Add
/pages/home
totheendofyoururltoviewtheaboutpage,Thiscomesfromtheroutes.rb
filewhereitcreatesagetrequestforthepagescontrollerandthehomeactionwhichdefaultsto
renderingthehome.html.erbviewfile.Youshouldseethehomepagefirst:
ClosetheserverCTRLC
Remembertobesavingyourwork.
Addanaboutactiontothepages_controller.rbfile:
Astyledapp
/workspace/styled_app/app/controllers/pages_controller.rb
classPagesController<ApplicationController
defhome
end
defabout
end
end
Addanabout.html.erbfiletoyourapp/view/pagesdirectory:
Iusethetouchcommandherebutrememberyoucanrightclickifyoulike.
Astyledapp
/workspace/styled_app
$touchapp/views/pages/about.html.erb
Updatetheroutes.rbfile:
Astyledapp
/workspace/styled_app/config/routes.rb
Rails.application.routes.drawdo
get'pages/about'
root'pages#home'
#Thepriorityisbaseduponorderofcreation:firstcreated>highestpriority.
#Seehowallyourrouteslayoutwith"rakeroutes".
...
Iremovethegetrouteandreplaceitwithtwolinesabove.Therootoftheapplicationbecomes
thepagescontrollerhomeactionwhichdefaultstorenderingthehome.html.erbfile.Ifyoutype
/pages/about
ontotheendofyoururlyouwillgettotheabout.html.erbfile,thisfilewillbe
blankaswehavenotaddedanythingtoityet.Thepages/aboutrouteusesthekeywordget.
Thisisatypeofhttprequestbetweenawebbrowserandserver.Agetrequestretrievesdata
fromaparticularsource.Inthiscasewewanttogettheabout.html.erbfile.Toreadmoreonget
requestsandpostrequestscheckout
w3schools
.
Okaywherehavewegottowiththisapp?Wehavecreatedthesamehomepagelikewedidin
thepreviousappandwehavealsoaddedanaboutpageaswell,Hopefullyyoudidthisina
muchshortertimethanthelastchapter.
AddingLinks
Addthefollowinglinestoyourhome.html.erbfile:
Astyledapp
/workspace/styled_app/app/views/pages/home.html.erb
<h1>Pages#home</h1>
<p>Findmeinapp/views/pages/home.html.erb</p>
<%=link_to'ABOUT'%>
Saveyourworkandruntheserver:
Astyledapp
/workspace/styled_app
$railsserverb$IPp$PORT
LookatyourhomepageandyouwillseewehaveaddedalinkwiththetextABOUT.Howeverif
youclickitthenitwontgoanywhere.Whynot?Okaylookatthelineyouadded,youused
embeddedruby.
Embeddedrubyuseseithertwoformats
1. <%%>
2. <%=%>
Versiononeisalessthansignandapercentagesign.itisusedforprocessinglogicsuchasif
statements.Versiontwoisalessthansignapercentagesignandanequalssign.Itisusedfor
OUTPUTTING
tothescreen.Ifyouwanttoseetheresultthenyouusetheembeddedrubywith
theequalssign.Asyoubuildmoreappsthiswillbecomealotclearer.
Insidetheembeddedrubyweusearailshelpermethod.Itiscalledthatbecauseitishelping
generatealink.Themethodislink_toittakesthenameofthelinkinthiscasewenamedit
ABOUTanditalsotakesthepathtogotoasanargument.Beforeweaddthatthoughlets
inspectourhomepage.
Rightclickonthepageandselectinspect:
Ifyouopenupthebodytagsandlookattheaboutanchorlinkyouwillseethelink_tojust
generatesanormallinkinhtml.Ifyoulookatthehref=/youwillseewehaventprovideda
destinationsothelinkhasdefaultedtoforwardslashwhichjustreturnsustothehomepage.
Okaytimetoupdatethelinktotakeustotheaboutpage.
Updatethehome.html.erbfile:
Astyledapp
/workspace/styled_app/app/views/pages/home.html.erb
<h1>Pages#home</h1>
<p>Findmeinapp/views/pages/home.html.erb</p>
<%=link_to'ABOUT',pages_about_path%>
Youaddacommafollowedbythetextpages_about_path,Thislinewilltakeyoutotheabout
page.Ifyourestarttheserverandinspecttheanchorelementagainyouwillseethehyperlink
referencenowpointstothepagescontrollerandaboutaction:href=/pages/about.
Wheredoesthepages_about_pathcomefrom?
Youdefinedsomeroutesinyourroutes.rbfilesothelinepages_aboutisaprefixthatweuseto
gettotheaboutpage.Insteadofhardcodingtheactuallinkweusethisprefixsothatifthe
actuallinkchangestheprefixwillstillwork.
Inyourconsoletypethefollowing:
Astyledapp
/workspace/styled_app
$rakeroutes
PrefixVerbURIPatternController#Action
pages_aboutGET/pages/about(.:format)pages#about
rootGET/pages#home
Youcanseealltheroutesthataredefinedintheroutes.rbfile.Theleftcolumnshowstheprefix,
thenthetypeofhttpverbwhichforthetworoutesdefinedisaGETrequest.Wethenhavethe
URIpatterncolumn,ThisishowthelinkwouldlookintheURL,itistheactualpathtothe
resource.Thenthefinalcolumnshouldlooksomewhatfamiliar,itisthecontrollerfortheroute
anditsaction.
Wecanusetheprefixalongwiththerailslink_tohelpermethodtocreatealink.Allwedowith
theprefixisadd_pathtotheendofitandthepathtothatresourcegetsgeneratedforthe
anchorlink.Sotogettotheaboutpagewelookattheprefixwhichispages_aboutandappend
_pathtothengetpages_about_path.Ifwewantedtogetourhomepageorrootroutewelookat
ourhomeactionandseetherootprefix,wecanthenadd_pathtothengetroot_pathwhichcan
againbeusedincombinationwiththerailshelpermethodlink_totosendustothehomepage.
Excellentnowwewilladdalinktotheaboutpage:
Astyledapp
/workspace/styled_app/app/views/pages/about.html.erb
<%=link_to'Home',root_path%>
Remembertheroot_pathhelpercomesfromappending_pathtotherootprefix.
Ifyousaveandstartupyourserveryoushouldbeabletonavigatebetweenthepages:
Astyledapp
/workspace/styled_app
$railsserverb$IPp$PORT
Shuttheserverdownwhenyouaredone.
TheApplicationLayoutFile
Intheviewsfolderaboveyourpagesfolderyouwillseealayoutsfolder.Openitupandyouwill
findtheapplication.html.erbfile.Youcanseethatitlookslikeanormalhtmlwebpageexcept
forsomestylesheetandjavascripthelpermethodsinthe<head>tag.
Alsointhebodytagyouwillseeapieceofembeddedrubycode,Itoutputssomethingbecause
ofthe=sign.
Astyledapp
/workspace/styled_app/app/views/layouts/application.html.erb
<!DOCTYPEhtml>
<html>
<head>
<title>StyledApp</title>
<%=stylesheet_link_tag'application',media:'all','dataturbolinkstrack'=>true%>
<%=javascript_include_tag'application','dataturbolinkstrack'=>true%>
<%=csrf_meta_tags%>
</head>
<body>
<%=yield%>
</body>
</html>
Theyieldwordmeansthatitwillshowwhateverview/templateyouarerequesting.Sowhenyou
visittheaboutpageitistheabout.html.erbviewfilethatgetsyielded.
Alsonoteyouarenotpassinganyargumentstoyield,Youarejustusingthewordyieldonits
own,Inthissituationyieldrendersthetemplateorviewofthecontrollerandactionthatyouare
requesting.WhenwevisittheaboutpagewerequestthePagesControllerandinthattheabout
action.
Whyisthisuseful?Itmeansyoudonthavetokeepontypingoutthedoctypedeclarationand
htmlandheadtagsetc.Itremainsinoneplace.Ifyoulookatyourabout.html.erbfileyouwill
seeithasnoneofthosetags,Thisisbecauseyouareessentiallyfillinginthecontentforthe
bodyofthewebpage.Lookbackatyourapplicationlayoutfileandyouwillseeyieldisinsideof
thebodytags.Sotheabouttemplategetsyieldedwhenrequestedandagainsodoesthehome
template.
Allofthismeansyoucaneasilyaddcontentthatremainsthesameoneverypage,suchas
navigation.Inyourapplication.html.erbfileadd:
Astyledapp
/workspace/styled_app/app/views/layouts/application.html.erb
<!DOCTYPEhtml>
<html>
<head>
<title>StyledApp</title>
<%=stylesheet_link_tag'application',media:'all','dataturbolinkstrack'=>true%>
<%=javascript_include_tag'application','dataturbolinkstrack'=>true%>
<%=csrf_meta_tags%>
</head>
<body>
<nav>
<%=link_to'HOME',root_path%>
<%=link_to'ABOUT',pages_about_path%>
</nav>
<%=yield%>
</body>
</html>
Addanavtagandinsideitaddthesamelinks.Removethelinksfromyourhome.html.erband
about.html.erbviewtemplates:
Astyledapp
/workspace/styled_app/app/views/pages/about.html.erb
Astyledapp
/workspace/styled_app/app/views/pages/home.html.erb
<h1>Pages#home</h1>
<p>Findmeinapp/views/pages/home.html.erb</p>
Remembertosaveandrunyourserver:
Astyledapp
/workspace/styled_app
$railsserverb$IPp$PORT
Nomatterwhatpageyouareonyouwillseethelinks.Thisisbecausetheyareinthe
applicationlayoutfile.Theyarenotinatemplatethatisbeingyieldedsotheyremaintherethe
wholetime.
Letsaddalittlebitmoretothelayoutfile.Aswellasanavtagweshalladdafooteraswellas
mostpageshaveafooterbeneaththecontent.
ThethreedotsrepresentcodeIamnotshowing,justtosavespace,Onlyaddthehighlighted
code.
Astyledapp
/workspace/styled_app/app/views/layouts/application.html.erb
<!DOCTYPEhtml>
...
<nav>
<%=link_to'HOME',root_path%>
<%=link_to'ABOUT',pages_about_path%>
</nav>
<%=yield%>
<footer>
<p>Copyright<%=Date.current.year%></p>
</footer>
</body>
</html>
Asyoucanseewehaveasemantic(itdescribesitself)htmlfootertagandinsideitisa
paragraphtag.Ifyoulookattheparagraphtagthereisthewordcopyrightandthensome
embeddedrubytags,rememberthesetagsallowustouserubycodeinourhtmlviewfiles.The
=signmeansthecodegetsoutputtedtothescreen.
WeusetheDatemethod,wethencalltherailscurrentmethodonthedatemethodwhichgives
usthecurrenttime,Wethenusetheyearmethodonthecurrenttimeandtheresultofthese
chainedtogethermethodsisthecurrentyear.Thesemethodsmayseemabitconfusing
howevertheyareallmethodsoftheDateclass.Youcanseeandtryoutothermethods
here
suchastheyesterdaymethod(Iwonderwhatthatwilloutput)ortheagomethodwhichcanbe
usedtoseehowmanysecondsagosomethingwassubmittedforexample.
NowthatwehaveanavigationandafooterIthinkthehomepageandaboutpageneed
sprucingupabit.Youcanaddwhatyouwantinsideofthefilesorjustcopyme,itsuptoyou.
Astyledapp
/workspace/styled_app/app/views/pages/home.html.erb
<h1>HOMELY</h1>
<p>Welcomehome</p>
<p>Sometimesitisnicetojustputyourfeetupandrelax</p>
<p>Ifyouhaven'tdoneitrecentlythenIwouldreallyrecommendit!</p>
Astyledapp
/workspace/styled_app/app/views/pages/about.html.erb
<h1>ABOUT</h1>
<h3>WhoamI?</h3>
<p>ToyouIamastrangerbuttoothersIamafriend</p>
<p>Whichoneareyou?OnlyIdecideintheend.</p>
Okaynowthatthehomeandaboutpageshavesomecontentitstimetoaddalittlebitofstyle
tothepage!
StylingThePages
Intheassetsfolderandinthestylesheetsfolderisapages.scssstylesheet.Thisiswherewe
willputourCSStostylethewebpage.
Astyledapp
/workspace/styled_app/app/assets/stylesheets/pages.scss
//PlaceallthestylesrelatedtothePagescontrollerhere.
//Theywillautomaticallybeincludedinapplication.css.
//YoucanuseSass(SCSS)here:http://sasslang.com/
*{
margin:0;//resetthepaddingandmargin
padding:0;
}
body{
fontfamily:sansserif;//changethefontofthewebpage
textalign:center;//centerthetext
}
nav{
background:#222;//colorthenavigationblack
marginbottom:1em;//addmarginatthebottomofnavforwhitespace
a{//stylethelinks
display:inlineblock;//alignthelinksnexttoeachother
padding:1em2em;//paddthelinksoutmakethembigger
color:#fff;//colorthetextwhite
textdecoration:none;//removetheunderlinefromthelinks
}
}
h1,h3{
margin:1em0;//addatopandbottommarginof1emtotheheadings,giveleftandright
0margin
}
p{
padding:.5em0;//givetheparagraphssomepadding
}
footer{//stylethefooter
bordertop:1pxsolid#eee;//addalightgreybordertothetopofthefooter
padding:1em0;//paddoutthefooter
fontstyle:italic;//changethefonttoitalic
position:absolute;//changethepositionsothefooterstickstothebottomofthepage
bottom:0;//makethefootersticktotheverybottom
left:0;//centerthetext
right:0;//centerthetext
}
Feelfreetocopyandpastethiscssoraddyourown.Inrealityyouwouldntusethiscssina
website,youwoulduseacssframeworklikebootstrapthataddssomenicedefaultstyleswe
canuse.Howeverthatwillcomelater.
Thetextafterthetwoforwardslashes//isacsscomment,youdontneedtotypethemout,they
areherejustforaquickexplanation.
Nicelydoneyouhavejustbuiltyoursecondrailsapp,Itwassimilartothefirstapphoweveryou
addedanextraaboutactionandviewtemplate.Youthenaddedanavandafootertothelayout
sothatyoucouldnavigatebetweenpageswiththelink_torailshelpermethod.Finallyyou
styledthewebpagewithcss.
WELLDONE!
Challenge:AStaticRailsApp
Okayitistimeforachallenge,Iwillgiveyousomeinstructionstofollowandyouwillbuildyour
ownrailsapp.What!IhearyousaybutI'vejustbuilttworailsappsandyourmakingmebuild
yetanother??!!?Yeahsorry,exceptthistimeyouwillbegettinglesshelp.Itisagoodwayto
learn,alsoyouwillquicklyrealizethethingsthatyoudontknow.Whendoingthisexercise
remembertodoublecheckeverythingyoutypeanddontworryifyoumuckupasyoucanjust
startover.WhenIfirststartedoutsomesimplethingsIgotstuckonwerenotaddingacomma
inthecorrectplaceonalink_tohelpermethodorforgettingtousean=signwhenusing
embeddedrubysonothinggotoutputtothescreen,Essentiallyjustsyntaxerrors,Okaywith
thatsaidfeelfreetolookbackatthepreviouschaptersforhelp.
Step1
CD
intoyour
workspace
directory
Step2
Generateanewrailsapp
calledmy_pages(Youdontneedtouseaspecificversionnumber)
Step3
CD
intothenewrailsapp
(my_pages)
Step4
Runtheserver
andcheckthatyougetthedefaultrailspage
Step5
Generateacontroller
named
Pages
andatthesametimegeneratea
home
and
about
action
(Youshouldgettheviewsgeneratedaswelletc.)
Step6
Openyour
routes.rb
fileandchangethegethomeroutetoa
rootroute
,(rememberthe#sign
syntax)
Step7
Runtheserver
andcheckyouaredirectedtothehomepage
Step8
Openthe
application.html.erb
fileandaddanavhtmltag.Insidethenavtagaddtworails
links:
OnelinkfortheHomepageandonelinkfortheAboutpage.
(Rememberyoucan:
rakeroutes
)
Step9
Updatethe
home.html.erb
fileand
about.html.erb
file,addyourowncontent.
Step10
Openthe
pages.scss
stylesheetandaddsomestylestoyourapp.
SuccessIn10stepsyouhavemadeyourownrailsappwithnohelp,prettygoodgoingifyou
askme.Whatdidyougetstuckon?Wasitrememberingcertainrailscommandsormaybe
somespecificsyntax?Makeanoteofitandstickittoyourscreen!
ASimpleFormApp
Thenextrailsappwillbeaverysimpleform,youwillgenerateamodel(aclassthatdefines
attributes)withanameattribute,andthensubmitthedatatothedatabase.Youwillthenshow
allthenamesonapage.Thereareafewnewthingstolearninthislesson,howeveritshould
soonclicktogether.
Makesureyouareintheworkspacedirectory:
~/workspace$pwd
/home/ubuntu/workspace
Generateanewrailsappcalledform_app
Asimpleformapp
/workspace
$rails_4.2.5_newform_app
create
createREADME.rdoc
createRakefile
createconfig.ru
create.gitignore
createGemfile
createapp
createapp/assets/javascripts/application.js
createapp/assets/stylesheets/application.css
createapp/controllers/application_controller.rb
createapp/helpers/application_helper.rb
createapp/views/layouts/application.html.erb
createapp/assets/images/.keep
createapp/mailers/.keep
...
Changedirectoryintothenewapp:
$cdform_app/
~/workspace/form_app$
Runtheserver:
Checkallisfine.
Asimpleformapp
/workspace/form_app
$railsserverb$IPp$PORT
Generateapeoplecontroller:
Asimpleformapp
/workspace/form_app
$railsgeneratecontrollerPeopleindexnew
RunningviaSpringpreloaderinprocess726
createapp/controllers/people_controller.rb
routeget'people/new'
routeget'people/index'
invokeerb
createapp/views/people
createapp/views/people/index.html.erb
createapp/views/people/new.html.erb
invoketest_unit
createtest/controllers/people_controller_test.rb
invokehelper
HerewearegeneratingacontrollercalledPeople,Thisisbecauseinthisappwewillbe
submittingpeople'snamessoacontrollerofpeopleseemsappropriate.Ifyourememberwe
alsogettheindex.html.erbfileandthenew.html.erbfilegeneratedforus,(aswearespecifying
thosetwoactionsinthecommand)andwealsogetsomeroutesautomaticallygeneratedinthe
routes.rbfile.
Updatetheroutes
Asimpleformapp
/workspace/form_app/config/routes.rb
Rails.application.routes.drawdo
resources:people
root'people#index'
#Thepriorityisbaseduponorderofcreation:firstcreated>highestpriority.
#Seehowallyourrouteslayoutwith"rakeroutes".
#Youcanhavetherootofyoursiteroutedwith"root"
#root'welcome#index'
Changetheindexactionsothatitistherootroute,whenyouloadtheapplicationthiswillbethe
firstpagethatshows.Theresourceslineautomaticallycreatesmanyroutesforus.Use
rake
routes
totakeaquicklook.(Wewillcoverthemmorelater).
Runtheserver:
Asimpleformapp
/workspace/form_app
$railsserverb$IPp$PORT
Youshouldseetheindex.html.erbpage,ifyouadd/people/newtotheendoftheurlyouwill
alsoseethenewpage.Asyoumighthaveguessedthiswillbethepagewherewecreateanew
person.
Generatethepersonmodel
Asimpleformapp
/workspace/form_app
$railsgeneratemodelPerson
RunningviaSpringpreloaderinprocess875
invokeactive_record
createdb/migrate/20160226194318_create_people.rb
createapp/models/person.rb
invoketest_unit
createtest/models/person_test.rb
createtest/fixtures/people.yml
Herewearegeneratinga
model
,wehavenamedthemodelPerson.Amodelisusedtodefine
anobject.WhatdoImeanbyobject?Anobjectcanbeanything,inthiscasewearedefininga
singleperson.Themodelfilecanbefoundunderapp/models/person.rbifyouopenitupyouwill
seethatitisempty.Soamodelfileofperson.rbgotgenerated.Ifyoulookyouwillseea
migrationfilegotgeneratedunderdb/migrate/20160226194318_create_people.rb,thatnumber
isatimestamp,yourswillbedifferent.
Themigrationfileisthefilethatupdatesthedatabase,Eventhoughyouhaveaperson.rb
modelfileyourapplicationdoesnotactuallyhaveanywheretosavethepersonintobecausewe
havenotupdatedthedatabase.Sothemigrationfileisusedtoupdatethedatabase.
Lookclosely,youusedtherailsgeneratemodelcommandtogenerateaPersonmodel,the
modelfilethatgotgeneratedwasindeedcalledperson.rb,butlookatthemigrationfile,
db/migrate/20160226194318_create_people.rbthefileiscalledcreate_
people
.rbsoitgot
pluralizedtopeople.Themigrationfilecreatesthetableinthedatabase.Itupdatesthe
databasewiththeinformationthatwewillputinit.Sinceitisatablethatholdsinformationon
notonepersonbutmanypeoplethispluralizationstartstomakealittlemoresense.Thisisone
ofrailsconventions,Amodelwillbesingular(person)butadatabasetablewillbeplural(people
asitholdsinformationonmanypeople).Dontworryaboutmemorizingthisinitially,Iwillremind
youthroughoutthebook.
Asimpleformapp
/workspace/form_app/app/models/person.rb
classPerson<ActiveRecord::Base
end
Eventhoughyourperson.rbfileisemptyitisstillusedbyrailstogenerateapersonobjectand
saveittothedatabase.Wewillseelaterhowwecanusethisfiletovalidateattributes.For
exampleifyouwantedpeopletoonlyenteranamelongerthan3characters.
Updatingthemigrationfile
Asimpleformapp
/workspace/form_app/db/migrate/20160226194318_create_people.rb
classCreatePeople<ActiveRecord::Migration
defchange
create_table:peopledo|t|
t.timestampsnull:false
end
end
end
ThemigrationfilehasaclassofCreatePeople,whichisjustanormalrubyclass.Thisclass
inheritsfromActiveRecord::Migrationwhichispartofrails.Thisallowsustothenusethisfileto
updateourdatabase.
InsidetheCreatePeopleclasswehaveamethod(definedwiththedefandendkeywords)
calledchange.Thechangemethodhasinsideitsomethingcalledaschemastatement.The
create_tablestatementisastatementthatisusedtocreateatablecalledpeople.
Thecreate_tablestatementtakesaparameterforthename.
:people
isthenameofthetable
andithasacoloninfrontofit.why?Itiscalledasymbol,Ifyouhavenevercomeaccrossa
symbolbeforethenknowthatitissimilartoastringexceptthatitusesfarlessmemory.Itis
usedtorepresentsomething,inthiscasethenameofthetable.
Afterthesymbolwehavea
do
keywordand
end
keywordafewlinesdown.Thisiswhatyou
callablock.Ablockiscodethatisusedbyothercode.Ifyoulookjustafterthedokeywordyou
see
|t|
Thisisatemporaryvariablethatisusedtospecifythecolumnswewilladdtothetable.
Youcanseethiswitht.timestampswhichcreatescreated_atandupdated_atcolumns,(dont
worrytoomuchaboutthatfornowthough).
Addthefollowinglinetoyourmigrationfile.
Thislinecreatesacolumninthepeopletable,thecolumniscallednameanditisoftypestring.
Thetypeofstringmeansthatwewillbeputtingshortcontentinthere,lessthan255characters.
Asimpleformapp
/workspace/form_app/db/migrate/20160226194318_create_people.rb
classCreatePeople<ActiveRecord::Migration
defchange
create_table:peopledo|t|
t.string:name
t.timestampsnull:false
end
end
end
UpdateTheDatabase
Asimpleformapp
/workspace/form_app/
$rakedb:migrate
==20160226194318CreatePeople:migrating=====================================
create_table(:people)
>0.0012s
==20160226194318CreatePeople:migrated(0.0013s)============================
Thiscommandrunsthemigrationfilethatyoujustupdated.Asyoucanseebytheoutputa
tablegetscreatedcalledpeople.
Ifyoulookinthedbfolderyouwillseeanewfilecalledschema.rb,Thedatabaseschemahas
beenupdatedandifyouopenupthefileyouwillseethetableyouhavecreated.
Asimpleformapp
/workspace/form_app/db/schema.rb
...
ActiveRecord::Schema.define(version:20160226194318)do
create_table"people",force::cascadedo|t|
t.string"name"
t.datetime"created_at",null:false
t.datetime"updated_at",null:false
end
end
Youcanseethepersontablehasacolumncallednamewhichisoftypestring.Youcanalso
seethatthetimestampslineinthemigrationfilecreatedtwocolumns,oneofcreated_atand
oneofupdated_at.Thesecanbeusedtoseewhenanattributewascreated,Agoodexampleis
ablogpostgetscreated_atacertaintime.
Okayyouhavecreatedapersonmodelfile,youhavecreatedandupdatedyourmigrationfile
andyouhavealsomigratedyourdatabaseschematoupdateitwithyourpeopletable.What
next?
UpdateThePeopleController
Asimpleformapp
/workspace/form_app/app/controllers/people_controller.rb
classPeopleController<ApplicationController
defindex
end
defnew
@person=Person.new
end
end
Thetext@personiscalledaninstancevariable.Itisavailabletoanymethodthatyouwritein
thepersonclass(person.rbfile).Irecommendyoureadupalittleoninstancevariablesifyou
areshakyonthem.Inrails,instancevariablesinthecontrolleraremadeavailabletotheview
files.
Herewecreateanewpersonobject/instancewiththelinePerson.new(capitalP).Thisisusing
ourperson.rbfiletocreateapersonobject,whichthengetssavedintheinstancevariable
@person.The=signistheassignmentoperator,thenewPersonobjectisstored/assignedto
the@personvariable.Nowwewillusethis@personvariableinourviewfile.
AddAFormToThenew.html.erbView
Asimpleformapp
/workspace/form_app/app/views/people/new.html.erb
<%=form_for@persondo|f|%>
<%=f.text_field:name,:placeholder=>"Name"%>
<%=f.submit"Submit"%>
<%end%>
Inthisviewfileweuseerborembeddedrubytocreatetheform.Weusean=signonthefirst
linetooutputtheform,thentheform_formethodwhichusesthe@personinstancevariablethat
wedefinedinourcontrollernewaction.SointheviewwearenowusingthatnewPersonobject
thatwehavecreated.Wethencreateablockwiththedokeywordandatemporaryvariable
called|f|whichisusedtocreatetheformfields.
Thetoplineoferbisthenclosed,ifyoulookatthebottomyouwillsee<%end%>whichcloses
outourblockofrubycode.Noticeitdoesnthaveanequalssignsincewedontwanttooutput
thatcode.
InBetweenourblockofcodewehavemoreembeddedrubythatoutputsstuff.weusethe
temporaryvariabletocreateatext_fieldwhichrelatestoournamefieldinthepeopletable.(Go
openuptheschema.rbfileagainandseethatthepeopletablehasanamecolumnsoweare
creatinganametext_fieldsothatwecansaveanametothedatabase)Alsoyouwillseea
placeholder,thisputstextinsidethetextfieldautomaticallysoastoprompttheuser.Theother
erblineisf.submitwhichcreatesasubmitbuttonsothatwecansubmitourdatatothe
database.ThelineSubmitiswhatwillshowonthebutton.
Runtheserver:
Asimpleformapp
/workspace/form_app
$railsserverb$IPp$PORT
Navigateto/people/newandyouwillseeanametext_fieldandasubmitbuttonthatlookslike
this:
Ifyouclickthesubmitbuttonhoweveryouwillgetanerror:
Theerrorsaysthecreateactioncouldnotbefound.Theformisusingthenewactioninthe
peoplecontrollerandthenlooksforanactioncalledcreatewhenthesubmitbuttonispressed.
AddingTheCreateAction
Asimpleformapp
/workspace/form_app/app/controllers/people_controller.rb
classPeopleController<ApplicationController
defindex
end
defnew
@person=Person.new
end
defcreate
@person=Person.create(person_params)
if@person.save
redirect_to/
else
render:new
end
end
private
defperson_params
params.require(:person).permit(:name)
end
end
Wehaveaddedacreateactionandaperson_paramsmethodwhichisprivateasitis
underneaththeprivatekeyword.Thismeansthatthemethodperson_paramscannotbecalled
withareceiver.
Theideaisthatprivatemethodsarehidden,andcanonlybecalledintheirownclass.Inthis
casethePersonclass.Irecommendyougoogleaboutrubyprivatemethodsasitisabit
confusing.
Inrailsanactionrendersatemplate,thenewactionrendersthenew.html.erbfile.Sowehide
theperson_paramsmethodtoensurerailsdoesntrouteanyHTTPcallstothataction.
Theperson_paramsmethodusesthelineparams.require(:person).permit(:name)towhitelist
whatwesubmittoourdatabase.Herewesayonlyallowthe:nametobesubmitted.
IfwelookbackatthecreateactionnowwearecreatinganewPersonwiththecreatemethod,
andwearepassingtheperson_paramsmethodtothecreatemethod,sothatwhenwecreatea
newpersonweonlyallowthename.Wethenstorethisinthe@personinstancevariable.
Wehaveanifstatementthatchecksisthe@personwecreatedgotsavedtothedatabase.If
thisistrueanditgotsavedtothedatabasethenweredirecttotheindextemplate.
Thecreatemethodautomaticallycreatesanobject(apersonobject)andtriestosaveittothe
database,Ifthecreatemethodcannotdothisthentheelsestatementthatcheckswhetherthe
@persongotsavedwillrunandwewillrenderthenewpageagain.
Runtheserver:
Asimpleformapp
/workspace/form_app
$railsserverb$IPp$PORT
Navigateto/people/newandaddsomenamesandsubmitthem,dothisacoupleoftimes.
UpdatingTheIndex.html.erbPageAndIndexAction
Ifyoulookatyourindexpageyouwillseethatitisntshowinganyofthenameswehave
submittedtothedatabase.Timetofixthat.
Asimpleformapp
/workspace/form_app/app/controllers/people_controller.rb
classPeopleController<ApplicationController
defindex
@people=Person.all
end
defnew
@person=Person.new
end
defcreate
@person=Person.create(person_params)
if@person.save
redirect_to/
else
render:new
end
end
private
defperson_params
params.require(:person).permit(:name)
end
end
Inthepeoplecontrollerindexactionwecreateaninstancevariablecalled@peoplebecausewe
wanttostoreeverypersoninourdatabaseinthatinstancevariablesowepluralizethename
(despitethefactitcouldbecalledanything).WethentargetourPersonclassandcallthe.all
methodtoqueryallthepeopleinthedatabase.Wecannowusetheinstancevariableinour
index.html.erbview.
Asimpleformapp
/workspace/form_app/app/views/people/index.html.erb
<%@people.eachdo|p|%>
<p>Name:<%=p.name%></p>
<%end%>
<%=link_to"AddName",new_person_path%>
Intheindexfileaddtheembeddedrubyabove,Wetaketheinstancevariable@peoplefromour
controllerindexactionandcalltherubymethodeachonit.Thisiteratesthrougheverypersonin
thedatabase.Wecreateaparagraphwiththetextname:andthenusingatemporaryvariable
|p|anderbweprintoutthenameofeachpersonobject.Thenamecomesfromthename
attributeinourdatabase.Ifyouopentheschema.rbfileandlookatthepeopletableyouwillsee
thenamecolumn,Thisiswhatwearetargetingforeachpersonobjectthatweloopthroughwith
theabovecode.Rememberweuseanequalssigninerbtooutputtothescreen,Sinceweonly
wanttoprintoutthenameweonlyuseerbwithanequalssignintheparagraphtag.
Youcanaddan=signtothefirstlineoferbifyouwish,tryitandseewhathappens.Then
removeitwhenyouaredone.(Inorderforanythingtoshowupmakesureyouhaveadded
somenamestoyourdatabase).
Runtheserver:
Asimpleformapp
/workspace/form_app
$railsserverb$IPp$PORT
Tryaddingsomenames,Eachtimeyoudoyouwillberedirectedtotheindexpage.Ifyouadda
blanknamethenthatwillshowupasblank.Wewillcovervalidationsinthenextprojectsothat
youcanmakesureanameisacertainlengthbeforeitissavedtothedatabase.
Theindexpageshowsnamesthathavebeensubmittedaswellasblanknames.
Thatwasalot,nicelydone!Youcreatedaverysimpleformwithoneformfieldforpeopletoput
theirnamesandthensubmittheirnamestothedatabase.Youthencollectedallthosenames
andloopedthroughthemanddisplayedthemontheindexpage.NiceJob!
WELLDONE!
AStyledFormApp
Inthisprojectyouwillbuildaformforausertosubmitanemailandname.Youwilllearnhowto
addvalidationstothemodelfilesothatanameforexample,hastobeaminimumlengthbefore
beingsubmitted.Youwillalsolearnhowtoaddgemstothegemfileandinstallthem,aswellas
configuringthem.Thegemyouwilllearnaboutisbootstrap,andisusedforstylingyourapp.
Makesureyouareintheworkspacedirectory:
~/workspace$pwd
/home/ubuntu/workspace
Generateanewrailsappcalledstyled_form
Astyledform
/workspace
$rails_4.2.5_newstyled_form
create
createREADME.rdoc
createRakefile
createconfig.ru
create.gitignore
createGemfile
createapp
createapp/assets/javascripts/application.js
createapp/assets/stylesheets/application.css
createapp/controllers/application_controller.rb
createapp/helpers/application_helper.rb
createapp/views/layouts/application.html.erb
createapp/assets/images/.keep
createapp/mailers/.keep
...
Changedirectoryintothenewapp:
$cdstyled_form/
~/workspace/styled_form$
Runtheserver:
Checkallisfine.
Astyledform
/workspace/styled_form
$railsserverb$IPp$PORT
.
GenerateaUsermodel:
Astyledform
/workspace/styled_form
$railsgeneratemodelUser
RunningviaSpringpreloaderinprocess718
invokeactive_record
createdb/migrate/20160301193656_create_users.rb
createapp/models/user.rb
invoketest_unit
createtest/models/user_test.rb
createtest/fixtures/users.yml
Updatethemigrationfile:
Astyledform
/workspace/styled_form/db/migrate/20160301193656_create_users.rb
classCreateUsers<ActiveRecord::Migration
defchange
create_table:usersdo|t|
t.string:name
t.string:email
t.timestampsnull:false
end
end
end
Addanamecolumnandanemailcolumnandmakethe,bothoftypestring.
Migratethedatabase:
Astyledform
/workspace/styled_form
$rakedb:migrate
==20160301193656CreateUsers:migrating======================================
create_table(:users)
>0.0024s
==20160301193656CreateUsers:migrated(0.0026s)=============================
MigrateyourdatabaseandcreatetheUserstable.Youcanseetheresultinthe
db/schema.rb
file.
GenerateaUserscontroller
Astyledform
/workspace/styled_form
$railsgeneratecontrollerUsersindexnew
RunningviaSpringpreloaderinprocess789
createapp/controllers/users_controller.rb
routeget'users/new'
routeget'users/index'
invokeerb
createapp/views/users
createapp/views/users/index.html.erb
createapp/views/users/new.html.erb
invoketest_unit
createtest/controllers/users_controller_test.rb
invokehelper
createapp/helpers/users_helper.rb
invoketest_unit
invokeassets
invokecoffee
createapp/assets/javascripts/users.coffee
invokescss
createapp/assets/stylesheets/users.scss
Thegeneratecontrollercommandcreatesausers_controller.rbfilewiththeactionsindexand
new.Openthefileuptotakealook.IfyouarewonderingwhydidntIaddthecreateactionto
thelistitisbecauseacreate.html.erbviewfilewouldalsogetgeneratedandwewouldendup
deletingit.(Arailsactionbydefaultrendersaview).
Updatetheroutes:
Astyledform
/workspace/styled_form/config/routes.rb
Rails.application.routes.drawdo
resources:users
root'users#index'
...
Updatetheroutesinyourroutes.rbfile.Setuptherootrouteandaddtheresourcesline.
Rememberyoucanseetheroutesresourcesgeneratesbyusingtherakeroutescommand.
Runtheserver:
Astyledform
/workspace/styled_form
$railsserverb$IPp$PORT
Checkthelinksworkbyloadinguptheserverandthennavigateto/users/newaswell.
Updatetheusers_controllerfile:
Astyledform
/workspace/styled_form/app/controllers/users_controller.rb
classUsersController<ApplicationController
defindex
end
defnew
@user=User.new
end
defcreate
@user=User.create(user_params)
if@user.save
flash[:success]="YouSignedUpwoo!"
redirect_to'/'
else
flash[:danger]="Therewasanerrortryagain!"
render:new
end
end
private
defuser_params
params.require(:user).permit(:name,:email)
end
end
InthenewactionyoucreateanewuserbycallingthenewmethodonyourUserclass.You
thenstorethenewUserobjectinthe@userinstancevariable.
InthecreateactionyoucreateaUserwiththepermittedparamswhichareintheuser_params
privatemethod.Inthemethodyouallowonlythenameandemailparameters.
Iftheuserissavedthenyouredirecttotherootpageandiftherewasanerroryourenderthe
newactionagain.
Iftheuserwassavedthenwesetaflashhash,thisisallowsustoshowamessageinthenext
actionthatgetsrendered.Withtheflashhashwecanshowamessagetotheuserandletthem
knowiftheyweresuccessfulornot.
Thekeyornameoftheflashis:successandthevalueormessageisthestringYouSignedUp
WooThismessageiswhatwedisplaytotheuser.Thekeyornameofthehashiswhatweuse
forstyling/identifying.
IftheUserisnotsavedtothedatabasethenwerenderthenewactionandtryagain.However
wealsodisplayadifferentflashhashwithanewmessage.
Thehashhasakeyornameof:dangerandavalueormessageofTherewasanerrortry
again!Themessagewillgetdisplayedtotheuseriftherewasaproblem.
Ifyouarewonderingcanthekeyornameofthehashbeanything?Theanswerisyes,justlike
thevalueormessageyoucangiveitanynameyouwant,ThereasonIchose:successand
:dangerisbecausethesearetwostylesusedinbootstrap.
ShowTheFlashMessages:
Astyledform
/workspace/styled_form/app/views/layouts/application.html.erb
<!DOCTYPEhtml>
<html>
<head>
<title>StyledForm</title>
<%=stylesheet_link_tag'application',media:'all','dataturbolinkstrack'=>true%>
<%=javascript_include_tag'application','dataturbolinkstrack'=>true%>
<%=csrf_meta_tags%>
</head>
<body>
<%flash.eachdo|name,message|%>
<%=content_tag:div,message,class:"alertalert#{name}"%>
<%end%>
<%=yield%>
</body>
</html>
Weusesomeembeddedrubyinthelayoutfiletoiteratethrougheachflashmessage(ifthereis
morethanone)andshowitscontent.Wetargettheflashhashandcalltheeachmethodonitto
gothrougheachflashhash.
Inrubyahashhasakeyandavalue.Wesetthekeyandvaluebackinthecontrollerfile.The
keywas:successandthevaluewasastring.
Herethekeyisnameandthevalueismessage.Wehaveusedvariablestorepresentthekey
andvalueofthehash.Theycouldbecalledanything.Wecouldhavecalledthevariables|k,v|
forkeyandvalue,butitisnicertobeabitmoredescriptive.
Wethenoutput(noticetheerbwithanequalssign,theothererbislogiccodethatwedont
wanttooutput)adivtag,Railshasahelpermethodcalledcontent_tagwhichallowsusto
specifyahtmltagthatwecanthenaddcontentto.
Themessageinthedivthatgetsdisplayedisthevariablecalledmessage,Thismessage
variableissettothestringfromthecontrollerfile.Afterthecommayouseetheclass:word,This
istherailswayofaddingaclasstoahtmlelement.Aswearegeneratingthedivtagwiththe
helpermethodwecantaddtheclassinthenormalwaylikewithhtmlclass=classnameWe
usetherailssyntaxofclass:withacolonafterit.
Thedivgetsgivenaclassofalertandaclassofalertname,thenameisactuallythevariable
justlikemessageisalsoavariable.Thenamevariablewilleitherhaveavalueofsuccessor
dangerbecauseofhowwenamedourflashhashesinthecontroller.
Justafterthedashisapoundsignfollowedbytwocurlybraces,Thisisinterpolationand
convertsthenamevariableintoitsvalue,soeithersuccessordanger.
Theclassthenbecomesalertalertsuccessnotalertalertname.Ifwedidntusestring
interpolationtoconvertthevariablethentheclasswouldremainasalertnamewhichisnotwhat
wewant.
Youdonthavetodoitthisway,youcouldjusthaveastaticclassofnoticeifyoulike,however
thisisabitnicerforthepersonviewingthesite.
Updatethenew.html.erbfilewithaform:
Astyledform
/workspace/styled_form/app/views/users/new.html.erb
<%=form_for@userdo|f|%>
<%=f.text_field:name,:placeholder=>"Name"%>
<%=f.email_field:email,:placeholder=>"Email"%>
<%=f.submit"Submit"%>
<%end%>
Runtheserver:
Astyledform
/workspace/styled_form
$railsserverb$IPp$PORT
Ifyounavigateto/users/newyoushouldseeaform,Entersomedetailsandsubmitthem.You
willberedirectedtotheindexpage,Youwillalsoseetheflashmessageofsuccess.
Prettycool!
Validations
Astyledform
/workspace/styled_form/app/models/user.rb
classUser<ActiveRecord::Base
validates:name,presence:true
validates:email,presence:true
validates:name,length:{minimum:3}
validates:email,length:{minimum:5}
end
Addthefollowinglinestoyouruser.rbmodelfile.ThesearethevalidationsthatItalkedabout
earlier.Thevalidateshelpermethodtakesasymboloftheattributeyouarelookingtovalidate.
Theuserstablehastheattributesnameandemailsothesearewhatwetarget.
Herewevalidatethepresenceandsetittotruetomakesurethatsomethinghasbeentyped
intotheinputboxes.Wealsovalidatethelengthoftheattributesandsetthemaminimum
constraint.
Thenamemustbeatminimum3charactersandwemakesuretheemailisalsoaminimumof5
characters.Thesearesomesimplevalidations,wewillcoversomeotherslateron.
Ifyouentersomeinformationintotheinputfieldsnowandtheyareblankortooshort.thenyou
willgetaflasherrormessagethatyousetupinthecontrollerandapplication.html.erbfilesand
youwillberedirectedtothenew.html.erbpagetotryagain.
Runtheserver:
Astyledform
/workspace/styled_form
$railsserverb$IPp$PORT
AddTheBootstrapGem:
Astyledform
/workspace/styled_form/app/gemfile
source'https://rubygems.org'
#BundleedgeRailsinstead:gem'rails',github:'rails/rails'
gem'rails','4.2.5'
#Usesqlite3asthedatabaseforActiveRecord
gem'sqlite3'
#UseSCSSforstylesheets
gem'sassrails','~>5.0'
#UseUglifierascompressorforJavaScriptassets
gem'uglifier','>=1.3.0'
#UseCoffeeScriptfor.coffeeassetsandviews
gem'coffeerails','~>4.1.0'
#Seehttps://github.com/rails/execjs#readmeformoresupportedruntimes
#gem'therubyracer',platforms::ruby
gem'bootstrapsass','~>3.3.6'
...
Thegemfileisthelocationofallyourpreinstalledgems(prepackagedcode)Theyallowyouto
quicklyaddfeaturestoyourappwithouthavingtoreinventthewheelanddoeverythingfrom
scratch.Forexampleifyouneedtouploadattachmentsorimagesthereisagemforthatcalled
paperclip,allyoudoisinstallitandsetitupandoffyougo.Configurationofgemswillvaryfrom
gemtogemhoweveryoucanfindsimpletutorialsontheirassociatedgithubpages.
Ihaveexcludedsomeofthegemfileasitisquitelarge.Addthegembootstrapsassline.
BundleInstall
Astyledform
/workspace/styled_form
$bundleinstall
Fetchinggemmetadatafromhttps://rubygems.org/...........
Fetchingversionmetadatafromhttps://rubygems.org/...
Fetchingdependencymetadatafromhttps://rubygems.org/..
Resolvingdependencies...
...
Thebundleinstallcommandinstallsallthegemsinthegemfile(Ihaveshortenedtheoutput).
Sincethebootstrapgemwasaddedtothefilethebundleinstallcommandhastoberun.This
fetchesthegemsandanydependenciesandinstallsthemforyoutouseinyourapplication.
Afterthatyoujustneedtodosomeconfiguration.
RenameTheapplication.cssFileToapplication.scss
Astyledform
/workspace/styled_form/app/assets/stylesheets/application.css
$mvapp/assets/stylesheets/application.cssapp/assets/stylesheets/application.scss
HereIusethemovecommandtorenamethefileandchangetheextensionfrom
css
to
scss
Thatisallwearechanging.Youcanjustrightclickonthefileandclickrenameifyouprefer.
ImportBootstrapStyles:
Astyledform
/workspace/styled_form/app/assets/stylesheets/application.scss
/*
*Thisisamanifestfilethat'llbecompiledintoapplication.css,whichwillincludeall
thefiles
*listedbelow.
*
*AnyCSSandSCSSfilewithinthisdirectory,lib/assets/stylesheets,
vendor/assets/stylesheets,
*oranyplugin'svendor/assets/stylesheetsdirectorycanbereferencedhereusinga
relativepath.
*
*You'refreetoaddapplicationwidestylestothisfileandthey'llappearatthebottom
ofthe
*compiledfilesothestylesyouaddheretakeprecedenceoverstylesdefinedinany
styles
*definedintheotherCSS/SCSSfilesinthisdirectory.Itisgenerallybettertocreatea
new
*fileperstylescope.
*
*=require_tree.
*=require_self
*/
Yourfilewilllookliketheoneabove,Makethechangesshownbelow:
Astyledform
/workspace/styled_form/app/assets/stylesheets/application.scss
/*
*Thisisamanifestfilethat'llbecompiledintoapplication.css,whichwillincludeall
thefiles
*listedbelow.
*
*AnyCSSandSCSSfilewithinthisdirectory,lib/assets/stylesheets,
vendor/assets/stylesheets,
*oranyplugin'svendor/assets/stylesheetsdirectorycanbereferencedhereusinga
relativepath.
*
*You'refreetoaddapplicationwidestylestothisfileandthey'llappearatthebottom
ofthe
*compiledfilesothestylesyouaddheretakeprecedenceoverstylesdefinedinany
styles
*definedintheotherCSS/SCSSfilesinthisdirectory.Itisgenerallybettertocreatea
new
*fileperstylescope.
*
*/
@import"bootstrapsprockets";
@import"bootstrap";
Removetherequire_selfandrequire_treelinesandaddthe@importlinesbeneath.
RequireBootstrapJavascripts:
Astyledform
/workspace/styled_form/app/assets/javascripts/application.js
//Thisisamanifestfilethat'llbecompiledintoapplication.js,whichwillincludeall
thefiles
//listedbelow.
//
//AnyJavaScript/Coffeefilewithinthisdirectory,lib/assets/javascripts,
vendor/assets/javascripts,
//oranyplugin'svendor/assets/javascriptsdirectorycanbereferencedhereusinga
relativepath.
//
//It'snotadvisabletoaddcodedirectlyhere,butifyoudo,it'llappearatthebottom
ofthe
//compiledfile.
//
//ReadSprocketsREADME(https://github.com/rails/sprockets#sprocketsdirectives)for
details
//aboutsupporteddirectives.
//
//=requirejquery
//=requirejquery_ujs
//=requirebootstrapsprockets
//=requireturbolinks
//=require_tree.
Youonlyneedtoaddonelinetotheapplication.jsfile.Makesureyouadditbelowtheothertwo
jquerylines.
Runtheserver:
Astyledform
/workspace/styled_form
$railsserverb$IPp$PORT
Loaduptheserverandyoushouldseesomeslightlydifferentstylingsuchasasansseriffont.
Youwillalsoseethattheflashmessageshavebeenstyledwithbootstrap.Thatisbecausewe
namedtheclassalertsuccessandalertdangerwhicharebootstrapstyles.
Byaddingagemandinstallingitandthendoingasmallamountofconfigurationfortheappwe
getsomeprettynicestyles.
StyleTheIndexPage:
Updatetheindex.html.erbviewfileandaddalinktothenew.html.erbpage.
Astyledform
/workspace/styled_form/app/views/users/index.html.erb
<divclass="container">
<divclass="row">
<divclass="colmd6colmdoffset3textcenter">
<%=link_to'SIGNUP',new_user_path,class:'btnbtnsuccess'%>
</div>
</div>
</div>
Herewehavestyledtheindexpagewithbootstrapclasses.Bootstrapclassesaredesignedso
thatyoudontneedtowriteyourownutilityclassesetc.Ifyouneedtocentersometextthen
thereisabootstrapclassforthat.Theframeworkhelpstospeedupthestylingofyour
application.Ofcourseifyouwanttooverrideastylethenallyouneedtodoisaddyourown
classandthenaddtherulestothatcssclassinyourstylesheet.
Youwillseethefirsthtmltagisadivandithasaclassofcontainer,Thisisabootstrapclass
anditisrequiredifweareaddingarowclasslateron,Allrowsmustgoinacontainersothat
theyareformattedproperly.
Thenexthtmltagisanotherdivandhasaclassofrow,Therowisthewidthofthescreenand
canbebrokendowninto12columns.Thisistheamountbootstraphasspecified.Thisisknow
asagridsystemandyoucanreadmoreaboutitonthebootstrapwebsite.
Thenexthtmldivhasafewmorebootstrapclasses,theclasstextcenterisquiteself
explanatory,Ifyouremoveitandreloadyourpageyouwillseethetextisnolongercentered.
Ontheleftisaclassofcolmd6whichstandsforacolumnthatis6wideonamediumdevice.
Themdmeansmedium.Dontworrytoomuchaboutthemdpart,Itallowsyoutostyleyoursite
dependingonwhatdevicesyouthinkyoursitewillbeviewedon.Soifyouweretargeting
mobileusersthenyoumightusecolsm6smstandsforsmall.Checkoutthesebootstrap
grid
examples
.
RememberIsaidthegridismadeupof12columns.Thecurrentdivis6columnswidebutitwill
beontheleftsideofthepageunlessweoffsetitwithaclass.WhenisayoffsetImeanpush
over.Weaddtheclasscolmdoffset6noticethatthereisanextraword(offset)andthatitis
onlyoffsetby3.Thisisbecauseitadds3totheleftandright.So6wideplus3ontheleftand3
ontherightmeansa12columngrid.Tryadjustingtheclassesandremovingtheoffsetclassto
seewhathappens.
Ifyoulookattherailslink_tomethodyoucanseeweaddaclasstherailswaybecauseweare
generatingthislinkwithrails,Itisnotplainhtmlsowechangethesyntax.(
class:classname
)
Thelinkgetsgivenabootstrapclassofbtnwhichstandsforbuttonandthenanotherbootstrap
classofbtnsuccesswhichstylesthebuttonagreencolour.Trychangingtheclassto
btnprimaryandseewhathappens.
Soinveryfewlinesyouhavestyledalink/anchorintoabuttonandcentereditonthepage.If
youweretowritethosecssrulesyourselfitwouldhavetakensignificantlymorecode.
StyleTheNewPage:
Updatethenew.html.erbviewfileandstylewithbootstrapclasses.
Astyledform
/workspace/styled_form/app/views/users/new.html.erb
<divclass="container">
<divclass="row">
<divclass="colmd6colmdoffset3">
<%=form_for@userdo|f|%>
<divclass="formgroup">
<%=f.text_field:name,:placeholder=>"Name"
,class:'formcontrol'
%>
</div>
<divclass="formgroup">
<%=f.email_field:email,:placeholder=>"Email"
,class:'formcontrol'
%>
</div>
<%=f.submit"Submit"
,class:'btnbtnprimary'
%>
<%end%>
</div>
</div>
</div>
First3divsareaddedwithacontainerclass,arowclasswhichhastobeinsideacontainer
class,andadivwithcolumnclassesandoffsetclassessoastocentertheformonthepage.
Thenintheformtwodivsareadded,theyeachwraparoundaninputfield.Theyhavea
bootstrapclassofformgroup,Thisclassaddssomespacingandformattingsothattheinput
boxesarenotsquashedupnexttoeachother.Theneachinputfieldgetsgivenaclassof
formcontrol.Thisincreasesthewidthoftheinputfield,addssomestylingsuchaspaddingand
borderradiusandmakestheinputsdynamicsothattheyadjusttothesizeofthescreen.
Thesubmitbuttonalsogetsgivenaclassofbtnandbtnprimarytostyleitblue,
Excellentyouhavecreatedaformwithmodelvalidationsthatcheckwhetheranythingispresent
intheinputboxesandalsomakesurethattheinputisaminimumlength,Ifnotanicelystyled
errormessageisdisplayed.Youalsoinstalledthebootstrapgemandconfigureditforusein
yourapp.Youthenstyledthepageswiththebootstrapclasses.
WELLDONE!
Challenge:ABootstrapRailsApp
Inthischallengeyouwillcreateanewrailsappandinstallthetwitterbootstrapgem.Youwill
thenconfigurethegemsothatthebootstrapstylescanbeusedinyourapp.
Step1
CD
intoyour
workspace
directory
Step2
Generateanewrailsapp
calledbootstrap_app(Youdontneedtouseaspecificversion
number)
Step3
CD
intothenewrailsapp
(bootstrap_app)
Step4
Runtheserver
andcheckthatyougetthedefaultrailspage
Step5
Generatea
Pagescontroller
,indexactionandindex.html.erbviewfile
Step6
Openyour
routes.rb
fileandchangethegetindexroutetoa
rootroute
,(rememberthe#sign
syntax)
Step7
Runtheserver
andcheckyouaredirectedtothehomepage
Step8
Addthebootstrapgemtothegemfile
gem
'bootstrapsass'
,
'~>3.3.6'
Step9
Installthegemwiththecommand:
bundleinstall
Step10
Renameyour
application.css
filetohavea
.scss
extension.
app
/
assets
/
stylesheets
/
application
.
css
app
/
assets
/
stylesheets
/
application
.
scss
Step11
Inyourapplication.scssfileaddthe@importlinesatthebottomofthefile
@import
"bootstrapsprockets"
;
@import
"bootstrap";
Step12
Removetherequiretreelinesfromtheapplication.scssfile
*=
require_tree.
*=
require_self
Step13
Addtherequirebootstrapsprocketslinetoyourapplication.jsfile(additbelowthejquerylines)
//=requirebootstrapsprockets
Step14
Addalinktoyourindexpagethatjustlinkstotheroot_path,Stylethelinkintoabuttonwith
bootstrapclasses.(btnbtnprimary)
Welldoneinafewstepsyouhaveconfiguredanewrailsapptousetwitterbootstrap,The
actualconfigurationdoesnttakelongatallandthereareonlyafewstepsrequiredbeforeyou
areupandrunningandreadytousebootstrapclassesforstyling.
AnEmailMailchimpApp
Forthisrailsappyouwillcreateanemailformtosubmitanemailhowevertheappwillconnect
tothemailchimpapiandsubmittheemailtoalist.Youwillusethegibbongemtosimplifythe
processofconnectingtothemailchimpapi.
Tobuildthisappyouwillneedtosignupforamailchimpaccount.Goto
mailchimp.com
and
signupforafreeaccount.Youwillneedtwothingstobuildthisapp,amailchimp
apikey
so
thatyoucanconnecttothemailchimpservice,anda
listid
Whenyoucreateamemberslistit
willhaveanid,thisisusedtosubscribepeopletothatparticularlist.
Anapikeyisalineofcodethatgetspassedbetweencomputerprogramsandisusedtoidentify
theperson(computer)thatwantstousethewebsite.Wewanttousethemailchimpwebsiteso
weneedanapikeysothatwecanbeidentified.
Onceyouhavesignedupclickonthe
dropdownmenu
inthetoolbaratthetop,itwillbe
namedafteryourname/username.Clickthe
accountbutton
.
Youwillnowbeonyouraccountpagewhichwillshowanoverviewforyou.Youwillseeanother
toolbar/menubelow.Clickthe
extras
dropdownandthenselect
apikeys
.
Ontheapikeypageyouwillseeabuttoncalled
createakey
Clickitandanewapikeywillbe
created.Copyitdownasyouwilluseitinyourapp.
Nowinthemenuatthetopclickthe
lists
link,Youwillseeabutton
createlist
,clickthatbutton
andfilloutthedetailsonthenextpage.Onceyouaredoneatthebottomclickthe
save
button.
Clickthe
list
linkatthetopofthepageagain,Nexttoyournewlycreatedlistisa
stats
dropdownmenu,Clickthedropdownarrowandclickon
settings
,Atthebottomofthesettings
pageyouwillseea
uniquelistid
,copyitdownasthisistheidforthelistyoujustcreated.
AlsoifyoufoundthemailchimpwebsitealittlebitconfusingdontworryasIdotoo,yousoon
getusedtoitthough.
Whydidyouneedtogetanapikeyandalistidagain?Itissothatwhenuserssubscribeto
yourapp(hypotheticallyasthisisatestapp)Theemailwillgetsubscribedtothelistyoujust
created,Youcanthenemailusersandkeepthemupdated.
Mailchimpsetupdone!
Makesureyouareintheworkspacedirectory:
~/workspace$pwd
/home/ubuntu/workspace
Generateanewrailsappcalledemail_mailchimp
Anemail_mailchimpapp
/workspace
$rails_4.2.5_newemail_mailchimp
create
createREADME.rdoc
createRakefile
createconfig.ru
create.gitignore
createGemfile
createapp
createapp/assets/javascripts/application.js
createapp/assets/stylesheets/application.css
createapp/controllers/application_controller.rb
createapp/helpers/application_helper.rb
createapp/views/layouts/application.html.erb
createapp/assets/images/.keep
createapp/mailers/.keep
...
Changedirectoryintothenewapp:
$cdemail_mailchimp/
~/workspace/email_mailchimp$
Runtheserver:
Checkallisfine.
Anemail_mailchimpapp
/workspace/email_mailchimp
$railsserverb$IPp$PORT
GenerateaUsermodel
Anemail_mailchimpapp
/workspace/email_mailchimp
$railsgeneratemodelUseremail:string
RunningviaSpringpreloaderinprocess788
invokeactive_record
createdb/migrate/20160305100909_create_users.rb
createapp/models/user.rb
invoketest_unit
createtest/models/user_test.rb
createtest/fixtures/users.yml
Hereinthegeneratecommandemail:stringisaddedsothatthemigrationfileautomaticallyhas
theemailcolumninit.Openthemigrationfiletosee.
Migratethedatabase
Anemail_mailchimpapp
/workspace/email_mailchimp
$rakedb:migrate
==20160305100909CreateUsers:migrating======================================
create_table(:users)
>0.0019s
==20160305100909CreateUsers:migrated(0.0020s)=============================
Youcanviewthe/db/schema.rbfiletoseetheuserstablethatgotgenerated.Ithasacolumn
namedemailoftypestring.
GeneratetheUserscontrollerandactions
Anemail_mailchimpapp
/workspace/email_mailchimp
$railsgeneratecontrollerUsersindexnew
RunningviaSpringpreloaderinprocess819
createapp/controllers/users_controller.rb
routeget'users/new'
routeget'users/index'
invokeerb
...
Edittheroutes.rbfile
Anemail_mailchimpapp
/workspace/email_mailchimp/config/routes.rb
Rails.application.routes.drawdo
resources:users,only:[:index,:new,:create]
root'users#index'
...
Addtherootrouteandsetittotheindexpage.Ifyouremembertheresourceslineit
automaticallycreateslotsofroutesforourcontroller/actions.Inthiscasetheuserscontroller.
Thesearecalledrestfulroutesandtheyarecommonroutesthatareusedinwebapp
development.
Sevendifferentroutesgetcreatedandaremappedtocontrolleractions.
Theroutesareindex,new,create,show,edit,update,destroy.See
railsguides
formore
informationandcheckoutsection3.2foratableshowingyouthecombinations.Justlookingat
thetableofroutes,actionsandhttpverbsdoesntdoitjustice,orhelpyoulearn,Sotheroutes
willbecoveredmorelateron.
Notethe
only:[:index,:new,:create]
line,Thistellsrailsthatyouwanttocreatetherestful
routesbutonlytheonesnamedthere.Forexamplewedontneedthedestroyaction/routeso
dontgenerateit.Usetherakeroutescommandtoseethereducedamountofroutes.
Runtheserver:
Anemail_mailchimpapp
/workspace/email_mailchimp
$railsserverb$IPp$PORT
UpdatetheUserscontroller:
Anemail_mailchimpapp
/workspace/email_mailchimp/app/controllers/users_controller.rb
classUsersController<ApplicationController
defindex
end
defnew
@user=User.new
end
defcreate
@user=User.create(user_params)
if@user.save
flash[:success]="Thankyouforsigninguptothemailinglist!"
redirect_toroot_path
else
flash[:error]="Therewasanerror,tryagain"
render:new
end
end
private
defuser_params
params.require(:user).permit(:email)
end
end
Addthecreatemethodandprivateuser_paramsmethod.
Updatetheindexandnewpages:
Anemail_mailchimpapp
/workspace/email_mailchimp/app/views/users/index.html.erb
<%=link_to"SUBSCRIBE",new_user_path%>
Anemail_mailchimpapp
/workspace/email_mailchimp/app/views/users/new.html.erb
<%=form_for@userdo|f|%>
<%=f.email_field:email,:placeholder=>"Email"%>
<%=f.submit"SUBMIT"%>
<%end%>
Addalinkintheindexpageandaforminthenewpage.
Updateapplicationlayoutfiletoshowflashmessages:
Anemail_mailchimpapp
/workspace/email_mailchimp/app/views/layouts/application.html.erb
<!DOCTYPEhtml>
<html>
<head>
<title>EmailMailchimp</title>
<%=stylesheet_link_tag'application',media:'all','dataturbolinkstrack'=>true%>
<%=javascript_include_tag'application','dataturbolinkstrack'=>true%>
<%=csrf_meta_tags%>
</head>
<body>
<%flash.eachdo|name,message|%>
<%=content_tag:div,message,class:name%>
<%end%>
<%=yield%>
</body>
</html>
AddbasicvalidationstotheUsermodelfile:
Anemail_mailchimpapp
/workspace/email_mailchimp/app/models/user.rb
classUser<ActiveRecord::Base
validates:email,presence:true
validates:email,length:{minimum:5}
end
Runtheserver:
Anemail_mailchimpapp
/workspace/email_mailchimp
$railsserverb$IPp$PORT
Testthatyougetasuccessflashmessagewhenyousubscribeyouremail,andalsoanerror
flashmessageifyouremailistooshort.
Addthegibbongemtothegemfile:
Anemail_mailchimpapp
/workspace/email_mailchimp/gemfile
source'https://rubygems.org'
#BundleedgeRailsinstead:gem'rails',github:'rails/rails'
gem'rails','4.2.5'
#Usesqlite3asthedatabaseforActiveRecord
gem'sqlite3'
#UseSCSSforstylesheets
gem'sassrails','~>5.0'
#UseUglifierascompressorforJavaScriptassets
gem'uglifier','>=1.3.0'
#UseCoffeeScriptfor.coffeeassetsandviews
gem'coffeerails','~>4.1.0'
#Seehttps://github.com/rails/execjs#readmeformoresupportedruntimes
#gem'therubyracer',platforms::ruby
#UsejqueryastheJavaScriptlibrary
gem'jqueryrails'
#Turbolinksmakesfollowinglinksinyourwebapplicationfaster.Readmore:
https://github.com/rails/turbolinks
gem'turbolinks'
#BuildJSONAPIswithease.Readmore:https://github.com/rails/jbuilder
gem'jbuilder','~>2.0'
#bundleexecrakedoc:railsgeneratestheAPIunderdoc/api.
gem'sdoc','~>0.4.0',group::doc
gem'gibbon','~>2.2','>=2.2.1'
...
Runbundleinstall
Anemail_mailchimpapp
/workspace/email_mailchimp
$bundleinstall
Addyourmailchimpapikeyandlistidtoyoursecrets.ymlfile
Anemail_mailchimpapp
/workspace/email_mailchimp/config/secrets.yml
...
development:
secret_key_base:
bc65b2dfe8afd3dc0cbdbe1bbbee3811d0fa94088936711c356eb3dc9fc8548227eb3bc70527e56d6ca8b9e1c762
2e80ac6f117d6bdb31e79f23c8fc6222a8d3
mailchimp_api_key:hfuksolfjd83j38d39ik9skj989dk
list_id:h3g5ll89sf
test:
secret_key_base:
67ef21b8a50815bfa531a4fdcd4beae45634589b53f2aee3976abd6ddfe526c1193583e7e612773f419d6dce3171
fdeb176cb154735d40537fa6eedcf2e007f6
#Donotkeepproductionsecretsintherepository,
#insteadreadvaluesfromtheenvironment.
production:
secret_key_base:<%=ENV["SECRET_KEY_BASE"]%>
Inyoursecrets.ymlfileaddyourapikeyandlistid,useaspacebetweenthecolonandthe
key/code.Thosekeysabovearefaketheywontwork,youwillneedyourownbysigningup.
Alsothisisnotnormallyhowyouwouldaddsensitiveinformationtoyouapp,youwoulduse
environmentvariableswhichIwillcoverlater.Howeversincethisisonlyatestapprunningin
developmentmodeyoucanaddyourkeystothesecretsfileunderdevelopment.
Addasubscribemethodtosubscribetheemailtomailchimp:
Anemail_mailchimpapp
/workspace/email_mailchimp/app/models/user.rb
classUser<ActiveRecord::Base
validates:email,presence:true
validates:email,length:{minimum:5}
before_save:downcase_email
after_save:subscribe
defdowncase_email
self.email=email.downcase
end
defsubscribe
gibbon=Gibbon::Request.new(api_key:Rails.application.secrets.mailchimp_api_key)
list_id=Rails.application.secrets.list_id
begin
gibbon.lists(list_id).members.create(
body:{
email_address:self.email,
status:"subscribed"
})
rescueGibbon::MailChimpError=>e
puts"TryAgain:#{e.message}#{e.raw_body}"
end
end
end
Ifyoulookattheusermodelyouwillseeamethodcalleddowncase_email,thismethodcalls
theemailattributeforthecurrentuser(self)andconvertsthecharacterstolowercase.
Lookatthelinebefore_save:downcase_email
Thisisacallbackmethodthatrunsbeforethedataissavedtothedatabase.SoBeforewesave
theemailtothedatabaserunthedowncase_emailmethod.
Ifyoulookfurtherdownyouwillseethesubscribemethod.
Themethodstartswithalocalvariablecalledgibbonwhichcreatesanewgiboninstance
request.Itusestheapikeyinthesecrets.ymlfile.Theapikeygetspassedviatheline:
api_key
:
Rails
.
application
.
secrets
.
mailchimp_api_key
Youcanseewetargetthesecretsfileandthemailchimp_api_keyvariable.
Theninthelist_idvariablewealsostorethelistidfromthesecrets.ymlfilewiththeline:
list_id
=
Rails
.
application
.
secrets
.
list_id
Withthesetwopiecesofinformationyoucannowtargetaspecificlist(list_id)andsubscribean
emailtoit.
Nextisabeginrescueblockthatisusedtocatchanyerrorsandprintthemtotheconsole.In
thebeginsectionwehavethelinegibbon.lists(list_id).members.createwhichwilltargetthelist
withtheidstoredinthelist_idvariableandcreateanewmember,(subscribeanewemailtothe
list).
Thenyouhavethebodyofthecreatemethod,Thisiswhatgetspassedtotheapiandisadded
toyourmailchimplist.Inthebodyweaddtheemailaddresswithself.emailandweaddastatus
ofsubscribed.Thestatusisneededinordertoaddanemailaddresstoamailchimplist.
Belowthebodywehavearescuestatement,Ifthereisanerroritgetsstoredinthevariablee.
Theerrormessagethengetsputs(printedtotheconsolescreen)Thee.messageprintsthe
errormessageande.raw_bodygivesyougreaterinformationabouttheerror.
Runtheserverandsubscribesomeemails.Thenlogintoyourmailchimpaccountandyouwill
seethesubscribedemailswhenyouclickonthelist.Ifyoutypeinsomethingthatisobviouslya
fakeemailthenitmaynotwork.Checktheconsoletoseeiftherewasanerror.
WELLDONE!
Youcreatedanappthatsubscribesanemailtoamailchimplist.Youlearnedhowtouseanapi
toconnecttoanothersystem/service.Youlearnedalittlebitaboutstoringkeys/codeinthe
secrets.ymlfileandyoualsolearnedaboutcallbackmethodsthatcanberunatcertaintimes
duringtheappsprocessing(beforesavingtothedatabase,aftersavingtothedatabaseetc.).
Usingfigarotostoresensitiveinformation
Thisappwillbesimilartothelasthoweveritwillcovertheuseofthefigarogem.Thisgemis
usedforstoringsensitiveinformationinenvironmentvariables.Itisimportanttoknowthisas
whilstitisokaytoputapi_keysandcodeinyoursecrets.ymlfileitisnotideal.
Makesureyouareintheworkspacedirectory:
~/workspace$pwd
/home/ubuntu/workspace
Generateanewrailsappcalledemail_mailchimp
Anemailappwithfigaro
/workspace
$rails_4.2.5_newemail_with_figaro
create
createREADME.rdoc
...
Changedirectoryintothenewapp:
$cdemail_with_figaro/
~/workspace/email_with_figaro$
Runtheserver:
Checkallisfine.
Anemailappwithfigaro
/workspace/email_with_figaro
$railsserverb$IPp$PORT
GenerateaUsermodel
Anemailappwithfigaro
/workspace/email_with_figaro
$railsgeneratemodelUseremail:string
RunningviaSpringpreloaderinprocess788
Migratethedatabase
Anemailappwithfigaro
/workspace/email_with_figaro
$rakedb:migrate
==20160305100909CreateUsers:migrating======================================
create_table(:users)
>0.0019s
==20160305100909CreateUsers:migrated(0.0020s)=============================
GeneratetheUserscontrollerandactions
Anemailappwithfigaro
/workspace/email_with_figaro
$railsgeneratecontrollerUsersindexnew
RunningviaSpringpreloaderinprocess819
...
Edittheroutes.rbfile
Anemailappwithfigaro
/workspace/email_with_figaro/config/routes.rb
Rails.application.routes.drawdo
resources:users,only:[:index,:new,:create]
root'users#index'
...
Runtheserver:
Anemailappwithfigaro
/workspace/email_with_figaro
$railsserverb$IPp$PORT
UpdatetheUserscontroller:
Anemailappwithfigaro
/workspace/email_with_figaro/app/controllers/users_controller.rb
classUsersController<ApplicationController
defindex
end
defnew
@user=User.new
end
defcreate
@user=User.create(user_params)
if@user.save
flash[:success]="Thankyou!"
redirect_toroot_path
else
flash[:error]="Therewasanerror!"
render:new
end
end
private
defuser_params
params.require(:user).permit(:email)
end
end
Updatetheindexandnewpages:
Anemailappwithfigaro
/workspace/email_with_figaro/app/views/users/index.html.erb
<%=link_to"SUBSCRIBE",new_user_path%>
Anemailappwithfigaro
/workspace/email_with_figaro/app/views/users/new.html.erb
<%=form_for@userdo|f|%>
<%=f.email_field:email,:placeholder=>"Email"%>
<%=f.submit"SUBMIT"%>
<%end%>
Updateapplicationlayoutfiletoshowflashmessages:
Anemailappwithfigaro
/workspace/email_with_figaro/app/views/layouts/application.html.erb
<!DOCTYPEhtml>
<html>
<head>
<title>EmailWithFigaro</title>
<%=stylesheet_link_tag'application',media:'all','dataturbolinkstrack'=>true%>
<%=javascript_include_tag'application','dataturbolinkstrack'=>true%>
<%=csrf_meta_tags%>
</head>
<body>
<%flash.eachdo|name,message|%>
<%=content_tag:div,message,class:name%>
<%end%>
<%=yield%>
</body>
</html>
AddbasicvalidationstotheUsermodelfile:
Anemailappwithfigaro
/workspace/email_with_figaro/app/models/user.rb
classUser<ActiveRecord::Base
validates:email,presence:true
validates:email,length:{minimum:5}
end
Runtheserver:
Anemailappwithfigaro
/workspace/email_with_figaro
$railsserverb$IPp$PORT
Testthatyougetasuccessflashmessagewhenyousubscribeyouremail,andalsoanerror
flashmessageifyouremailistooshort.
Addthegibbongemtothegemfileandthefigarogem:
Anemailappwithfigaro
/workspace/email_with_figaro/gemfile
source'https://rubygems.org'
...
#Turbolinksmakesfollowinglinksinyourwebapplicationfaster.Readmore:
https://github.com/rails/turbolinks
gem'turbolinks'
#BuildJSONAPIswithease.Readmore:https://github.com/rails/jbuilder
gem'jbuilder','~>2.0'
#bundleexecrakedoc:railsgeneratestheAPIunderdoc/api.
gem'sdoc','~>0.4.0',group::doc
gem'gibbon','~>2.2','>=2.2.1'
gem
"figaro",~>1.1.1
...
Runbundleinstall
Anemailappwithfigaro
/workspace/email_with_figaro
$bundleinstall
Runbundleexecfigaroinstall
Anemailappwithfigaro
/workspace/email_with_figaro
$bundleexecfigaroinstall
createconfig/application.yml
append.gitignore
Asyoucanseethisfigarocommandcreatesafilecalledapplication.ymlintheconfigfolder.
Theappend.gitignoremeansthatifyouareusingversioncontrolsuchasgit,thenifyou
push/submityourworkpubliclythatfilewillbeignored.Thismeansyoucansafelyadd
informationsuchasyourapikeysasitwontbesubmittedanywhere(Ifyouareusingversion
control).
Addyourmailchimpapikeyandlistidtotheapplicationymlfile
(notabsjustspaces)
Anemailappwithfigaro
/workspace/email_with_figaro/config/application.yml
#Addconfigurationvalueshere,asshownbelow.
#
#pusher_app_id:"2954"
#pusher_key:7381a978f7dd7f9a1117
#pusher_secret:abdc3b896a0ffb85d373
#stripe_api_key:sk_test_2J0l093xOyW72XUYJHE4Dv2r
#stripe_publishable_key:pk_test_ro9jV5SNwGb1yYlQfzG17LHK
#
#production:
#stripe_api_key:sk_live_EeHnL644i6zo4Iyq4v1KdV9H
#stripe_publishable_key:pk_live_9lcthxpSIHbGwmdO941O1XVU
MAILCHIMP_API_KEY:hfuksolfjd83j38d39ik9skj989dk
LIST_ID:h3g5ll89sf
Wewilltargettheseenvironmentvariablesinoursecrets.ymlfile.
Targettheenvironmentvariablesinthesecrets.ymlfile:
Anemailappwithfigaro
/workspace/email_with_figaro/config/secrets.yml
...
#Makesurethesecretsinthisfilearekeptprivate
#ifyou'resharingyourcodepublicly.
development:
secret_key_base:
0e8108dc2f3b58b38c75734cadf6842b7472fc7ebd6191f5c37a1a30cefee176687d7d45a68666f9a3b7ec9c5093
86e9fca1bc58deca2faf35743f890791f2e1
mailchimp_api_key:<%=ENV["MAILCHIMP_API_KEY"]%>
list_id:<%=ENV["LIST_ID"]%>
test:
secret_key_base:
bc8b9479f36a56748cc7c8902e5f18b37cce19abbabceb4e019c288f6e60cea6847a847b1cc9d9d183131bbf34da
74a9bae0397ac8c010ae5f926b33ef2d5c31
#Donotkeepproductionsecretsintherepository,
#insteadreadvaluesfromtheenvironment.
production:
secret_key_base:<%=ENV["SECRET_KEY_BASE"]%>
Wearenowtargetingthethevariablesinourapplication.ymlfileusingtheENVenvironment
variableandthenameofthevariableintheapplication.ymlfile.Asyoucanseewedonthave
theapi_keyinthisfilesotheapplicationisnowsaferespeciallyifsubmittingthecodepublicly.
Addasubscribemethodtosubscribetheemailtomailchimp:
Anemailappwithfigaro
/workspace/email_with_figaro/app/models/user.rb
classUser<ActiveRecord::Base
validates:email,presence:true
validates:email,length:{minimum:5}
after_save:subscribe
defsubscribe
gibbon=Gibbon::Request.new(api_key:Rails.application.secrets.mailchimp_api_key)
list_id=Rails.application.secrets.list_id
begin
gibbon.lists(list_id).members.create(
body:{
email_address:self.email,
status:"subscribed"
})
rescueGibbon::MailChimpError=>e
puts"TryAgain:#{e.message}#{e.raw_body}"
end
end
end
Asyoucanseewiththegibbonlocalvariablewearestilltargetingthemailchimp_api_keyinthe
secrets.ymlfile,
gibbon
=
Gibbon
::
Request
.
new
(
api_key
:
Rails
.
application
.
secrets
.
mailchimp_api_key)
Butifyoulookinthatfileyouwillseeenvironmentvariablesarenowbeingusedtostorethe
information.
WELLDONE!
Coolyourecreatedtheappusingthefigarogemandenvironmentvariables.Youmaythinkthis
wasabitofapointlessappasnothingmuchwasnewhoweveritisimportanttoknowhowto
useandstoreinformationthatismoresensitive.Youdontwantotherpeopletoaccessyourapi
keysoranyotherinformation.Youcanknowbuildanappknowingyourinformationissafe.
Anexplanationonenvironmentvariables:
Environmentvariablesareusedtokeepsensitiveinformationsecure.
Let'ssayyouwereconnectingtotheGmailapi.Youcoulduseenvironmentvariablesforthe
usernameandpassword.
user_name
:
ENV
[
"GMAIL_USERNAME"
]
password
:
ENV
[
"GMAIL_PASS"]
ENVtellsrailstolookforanenvironmentvariablewiththenameGMAIL_USERNAMEor
GMAIL_PASSontheserverthattheappisrunningon.
Soinourearlierexamplewewererunningonthelocalserver,andinthesecrets.ymlfileyou
canseeweplacedthevariablesunderthedevelopmentlineofcode.(Wearebuildingtheapp).
IfweweregoingtousetheappinreallifethenwewouldaddENVvariablesbeneaththe
productionlineofcodeandpushourcodetoaliveserver.TheENVvariablesaredividedup
dependingontheenvironmentinwhichtheappisrunning.
TherearevariouswaystosetENVvariablesforuse,aymlfileisinmyopiniononeoftheeasier
approaches,especiallywhenagemlikefigarocanbeusedtohelpgeneratethefilesforyou
andaddthefiletothe.gitignorefileautomatically.Thegemdoestheheavyliftingandyoudont
needtoreinventthewheel.
Makesureyouareinyourpreviousappcdintoemail_with_figaro:
Anemailappwithfigaro
/workspace/email_with_figaro
$railsconsole
RunningviaSpringpreloaderinprocess1038
Loadingdevelopmentenvironment(Rails4.2.5)
2.3.0:001>
ENV['MAILCHIMP_API_KEY']
=>"hfuksolfjd83j38d39ik9skj989dk"
2.3.0:002>
2.3.0:002>
exit
~/workspace/email_with_figaro$
Therailsconsolecommandletsyouinteractwithyourappfromthecommandline.
Youcanuseittomakequickcheckswhendevelopingyourappetc.
Typetherailsconsolecommandtoopenuptheconsole.
ThentypetheENV[MAILCHIMP_API_KEY]ENVvariableintotheconsole.Thevariablewillbe
printedout.
Lookaboveandyouwillseethelinesaysloadingdevelopmentenvironment,Ifyouhaddifferent
ENVvariablesforproductionandaliveserverthentheywouldgetloadednotthedevelopment
ENVvariables.Howeversincetheconsoleisrunningindevelopmentmodethesearethe
variablesthatgetloaded.
APostsapp
Inthisappyouwillutilizealloftherestfulroutes/actionsindex,new,create,show,edit,update,
destroy,andbuildanappthatallowsyoutocreateanewpostwithatitleandbody.Youwill
thenbeabletoeditthepostandupdateitscontentsaswellasdeleteit.
Makesureyouareintheworkspacedirectory:
~/workspace$pwd
/home/ubuntu/workspace
Generateanewrailsappcalledposts
APostsapp
/workspace
$rails_4.2.5_newposts
create
createREADME.rdoc
...
Changedirectoryintothenewapp:
$cdposts/
~/workspace/posts$
Runtheserver:
Checkallisfine.
APostsapp
/workspace/posts
$railsserverb$IPp$PORT
GenerateaPostmodel
APostsapp
/workspace/posts
$railsgeneratemodelPosttitle:stringbody:text
RunningviaSpringpreloaderinprocess1147
Migratethedatabase
APostsapp
/workspace/posts
$rakedb:migrate
==20160306094443CreatePosts:migrating======================================
create_table(:posts)
>0.0012s
==20160306094443CreatePosts:migrated(0.0013s)=============================
checkoutyourschema.rbfiletoseethepoststablethatgotgenerated.
GeneratethePostscontrollerandactions
APostsapp
/workspace/posts
$railsgeneratecontrollerPostsindexneweditshow
RunningviaSpringpreloaderinprocess819
...
Thereisnothingnewhere,theoutputtotheconsoleislargeraswearecreatingmoreactions
andviewswiththegeneratecommand.Ifyoulookatwhatgotcreatedyouwillseeallthe
associatedviewfiles.
Edittheroutes.rbfile
APostsapp
/workspace/posts/config/routes.rb
Rails.application.routes.drawdo
resources:posts
root'posts#index'
...
Thistimewewillbewantingalltheresourcesforthepostscontroller.
Runtheserver:
Trygoingtothevariouspages/posts/new
APostsapp
/workspace/posts
$railsserverb$IPp$PORT
Updatethepostscontroller:
APostsapp
/workspace/posts/app/controllers/posts_controller.rb
classPostsController<ApplicationController
defindex
@posts=Post.all
end
defnew
@post=Post.new
end
defcreate
@post=Post.create(post_params)
if@post.save
flash[:success]="Youcreatedanewpost"
redirect_to@post
else
flash[:danger]="Therewasanerror."
render:new
end
end
defedit
@post=Post.find(params[:id])
end
defupdate
@post=Post.find(params[:id])
if@post.update(post_params)
flash[:success]="Postupdated."
redirect_to@post
else
flash[:danger]="Therewasanerrorwhenupdating."
render:edit
end
end
defshow
@post=Post.find(params[:id])
end
defdestroy
@post=Post.find(params[:id])
@post.destroy
redirect_toroot_path
end
private
defpost_params
params.require(:post).permit(:title,:body)
end
end
Therearequiteafewnewactionsgoingontherebutitisquitesimilartowhatyouhavebeen
doing.Takealookattheshowaction.Thecodeinthatactionusestheparamshashandlooks
foranid,itthendisplaysthepostwiththatid.
@post=Post.find(params[:id])
Theidispassedfromtheurl,eachnewpostthatgetscreatedinthetablewillhaveanid
attribute,youwontseethiscolumnonthetableintheschema.rbfilebutitisgenerated
automaticallyforeverytableanditisthere.Thefirstpostyoucreatewillhaveanidof1andthe
secondanidof2andsoon.Thatiswhythelineabovelooksfortheidparameterintheparams
hash(whichcomesfromtheurl)whichitthenusestodisplayaparticularpost.
Ifyoudothe
rakeroutes
commandyouwillseethisline
postGET
/
posts
/:
id
(.:
format
)
posts
#show
Youcanseethe/posts/:idURIpattern,Thisshowsusthatweneedauniqueidinorderforthis
routetowork.Remembertheidisanautomaticallygeneratedcolumninyourtable.
Thepostthatgetsfoundwiththefindmethodandidpassedtoitfromtheurl,iswhatgets
storedinthe@postinstancevariable.Thisinstancevariablewillbeusedinthepost.html.erb
viewlateron.
Asyoucanseebylookingatthevariousactionsweusefindtofindapostwithaparticularid
quiteoften.
Theupdateactionisverysimilartothecreateaction,ifyoulookweareupdatingwhich
automaticallysavestothedatabaseagain,andweareonlyupdatingwiththepost_params
privatemethod.Iftheupdatewasasuccessweredirecttothat@postthatjustgotcreated.
(Theshowaction)otherwisewerendertheeditactionagain.
WiththedestroyactionweagainlocateaPostbyid,wethencalldestroyonthatposttodelete
itandfinallyredirecttotheroot_path.
Updatetheindexpage:
APostsapp
/workspace/posts/app/views/posts/index.html.erb
<%@posts.eachdo|p|%>
<pclass="title"><%=p.title%></p>
<pclass="body"><%=p.body%></p>
<%=link_to"EditPost",edit_post_path(p)%>
<%=link_to"ShowPost",post_path(p)%>
<%end%>
Youcyclethrougheachpostthatisstoredinthe@postsinstancevariablebyusingtheeach
method.Youthendisplaytheinformationthatgetsoutputfromtheerbtags.
Lookatthelinksthatarebeinggenerated,runtherakeroutescommand,seetheeditandshow
routesrequireanid(URIpatterncolumn)Wepassthatidinthelink_tomethodbypassingthe
prefix(post_path)thelocalvariablewhichcontainstheidforthatparticularpost.Wecan'tuse
the@postsinstancevariablebecausethatcontainsalltheposts.
Showaparticularpostsinformationontheshowpage:
APostsapp
/workspace/posts/app/views/posts/show.html.erb
<pclass="title"><%=@post.title%></p>
<pclass="body"><%=@post.body%></p>
Usingthepostthatisstoredinthe@postinstancevariableyououtputitstitleandbody
attributesontheshowpage.
Updatethenew.html.erbfilewithaform
APostsapp
/workspace/posts/app/views/posts/new.html.erb
<%=form_for@postdo|f|%>
<%=f.text_field:title,:placeholder=>"Title"%>
<%=f.text_field:body,:placeholder=>"Body"%>
<%=f.submit"CreatePost"%>
<%end%>
Updatetheedit.html.erbfilewithaform
APostsapp
/workspace/posts/app/views/posts/new.html.erb
<%=form_for@postdo|f|%>
<%=f.text_field:title,:placeholder=>"Title"%>
<%=f.text_field:body,:placeholder=>"Body"%>
<%=f.submit"Update"%>
<%end%>
Thisformisexactlythesameastheoneonthenewpage,Ijustchangedthebuttonname.Also
ifyouwanttolearnhowtoreducetherepetitionofthecodewiththeseformsthenyoucan
renderapartial.Iwontcoveritherebutitallowsyoutorendercommoncodeandstoreitina
singlefile.NotacrossmultiplefileslikeIhavedonehere.
Updateapplicationlayoutfiletoshowflashmessagesandaddlinks:
APostsapp
/workspace/posts/app/views/layouts/application.html.erb
<!DOCTYPEhtml>
<html>
<head>
<title>Posts</title>
<%=stylesheet_link_tag'application',media:'all','dataturbolinkstrack'=>true%>
<%=javascript_include_tag'application','dataturbolinkstrack'=>true%>
<%=csrf_meta_tags%>
</head>
<body>
<%=link_to"Home",root_path%>
<%=link_to"CreatePosts",new_post_path%>
<%flash.eachdo|name,message|%>
<%=content_tag:div,message,class:"alertalert#{name}"%>
<%end%>
<%=yield%>
</body>
</html>
AddbasicvalidationstothePostmodelfile:
APostsapp
/workspace/posts/app/models/post.rb
classPost<ActiveRecord::Base
validates:title,presence:true,length:{minimum:3}
validates:body,presence:true,length:{minimum:5}
end
Runtheserver:
Andcheckflashmessagesdisplaycorrectly,addapostandthenupdateit,try
updatinginformationthatisbelowtheminimumlength.
APostsapp
/workspace/posts
$railsserverb$IPp$PORT
Youshouldnowbeabletoaddapostandedititandupdateit.Youshouldalsobeabletoview
anindividualpost.
Addthedestroylinktotheshowpage:
Addalinkontheshowpagesothatwhenyouclicktoviewapostyoucanthendeletethatpost
ifyouwish.
APostsapp
/workspace/posts/app/views/posts/show.html.erb
<pclass="title"><%=@post.title%></p>
<pclass="body"><%=@post.body%></p>
<p><%=link_to'Delete',post_path(@post),method::delete,data:{confirm:"Doyoureally
wanttodeletethispost?"}%></p>
Hereweaddalink_tomethodwiththetextDelete.Ifyourakeroutesyouwillseetheprefixfor
thedestroyactionispost,_pathgetsappendedtothisandwepasstheIDofthepostwewant
todeleteinparenthesis.The@postinstancevariablecontainsthepostasitwasfoundusing
theparamshash.
The:deletemethodusedisthenexplicitlystatedasanoptionthatweaddtothelink_tomethod.
Thedataoptionisalsoadded,thisprovidesapopupconfirmationboxbeforetheactionis
carriedthrough.Ifyoucancelthenthepostisnotdeleted.
Inyourjavascriptfileyouhavethelinejquery_ujsautomaticallyincludedwhentheappwas
made.Thisisthejavascriptthatisusedtoshowthedialogbox.
Addbootstrapstylestotheapp:
APostsapp
/workspace/posts/app/gemfile
gem
'bootstrapsass'
,
'~>3.3.6'
...
Addthebootstrapgemtoyourgemfile.
Bundleinstall:
APostsapp
/workspace/posts/
$bundleinstall
Renamethestylesheetwitha.scssextension:
APostsapp
/workspace/posts/
$mvapp/assets/stylesheets/application.cssapp/assets/stylesheets/application.scss
Updatethestylesheet:
APostsapp
/workspace/posts/assets/stylesheets/application.scss
...
*
fileperstylescope.
*
*/
@import
"bootstrapsprockets";
@import
"bootstrap";
@import"posts";//importtheposts.scssstylesheet
Removetherequiretreelinesandaddtheimportlines.
Updatethejavascriptfile:
APostsapp
/workspace/posts/assets/javascripts/application.js
//
//=requirejquery
//=requirejquery_ujs
//=requirebootstrapsprockets
//=requireturbolinks
//=require_tree.
Addthebootstrapsprocketslinetotheapplication.jsfile.
Starttheserverupandyourpageshouldbestyledwithsomebootstrapstyles:
Updateapplicationlayoutfilewithbootstrapstyles:
APostsapp
/workspace/posts/app/views/layouts/application.html.erb
<body>
<divclass="container">
<%=link_to"Home",root_path
,class:"btnbtnprimary"
%>
<%=link_to"CreatePost",new_post_path
,class:"btnbtnsuccess"
%>
<%flash.eachdo|name,message|%>
<%=content_tag:div,message,class:"alertalert#{name}"%>
<%end%>
<%=yield%>
</div>
</body>
Updatetheposts.scssfilewithcssrules:
APostsapp
/workspace/posts/app/assets/stylesheets/posts.scss
.title{
fontweight:bold;
margin:.75em0;
fontsize:1.8em;
}
.body{
background:#eee;
padding:.5em;
}
Addsomestylestothetitleandbodyrules.
Updatetheeditandnewpageswithbootstrapstyles:
APostsapp
/workspace/posts/app/views/posts/new.html.erb
<divclass="row">
<divclass="colmd6colmdoffset3">
<divclass="well">
<pclass="textcenterlead">NewPost</p>
<%=form_for@postdo|f|%>
<divclass="formgroup">
<%=f.text_field:title,:placeholder=>"Title"
,class:"formcontrol"
%>
</div>
<divclass="formgroup">
<%=f.text_field:body,:placeholder=>"Body"
,class:"formcontrol"
%>
</div>
<%=f.submit"CreatePost"
,class:"btnbtnprimary"
%>
<%end%>
</div>
</div>
</div>
APostsapp
/workspace/posts/app/views/posts/edit.html.erb
<divclass="row">
<divclass="colmd6colmdoffset3">
<divclass="well">
<pclass="textcenterlead">NewPost</p>
<%=form_for@postdo|f|%>
<divclass="formgroup">
<%=f.text_field:title,:placeholder=>"Title"
,class:"formcontrol"
%>
</div>
<divclass="formgroup">
<%=f.text_field:body,:placeholder=>"Body"
,class:"formcontrol"
%>
</div>
<%=f.submit"UpdatePost"
,class:"btnbtnprimary"
%>
<%end%>
</div>
</div>
</div>
Ifyourunyourserverandgototheneworeditpageyouwillseeanicelystyledform:
Thefinalappshouldlooksomethinglikethis:
Youshouldbeabletoaddapost,show/viewit,updateitanddeleteitifneeded.
WELLDONE!
Youcreatedanappthatyoucanaddaposttoandviewit.Youcaneditandupdatethepost
andevendeleteit.Therewasquitealottothisappincludinginstallingandconfiguring
bootstrap.Hopefullyyoucanseehowthisappcouldbeturnedintoablogwithalittlebitmore
elbowgrease.
FINALTHOUGHTS
Youhavecomealongwayfromnowknowinganyrubyonrails,youhavebuiltmultipleapps
andlearnedthebasicsofcreatingyourownapps.Youcangenerateanewapp,addcontrollers
modelsandviewstoyourapplicationandeveninstallandconfiguregems.Youalsolearneda
littleaboutENVvariablesandkeepingsensitivedatasecureandalsoaboutinteractingwiththe
mailchimpapi.NicelyDone.
Ifyouenjoyedthebookthencouldyoupleaseleaveareviewonamazonitwouldbegreatly
appreciated.
IfyoufindanyerrorsinthetutorialsjustdropmeanemailandIwillcorrectthem:
allforcoding@gmail.com
ThereisplentythatIhaventcoveredinthisbooksuchasrelationshipsbetweenmodels,
renderingpartials,basicauthenticationandmuchmore.AtthebareminimumIhopeyou
enjoyedthebookandfoundituseful,
KindRegards
Harry