Upload
gteodorescu
View
217
Download
0
Embed Size (px)
Citation preview
8/13/2019 Building Single Page Web Apps With Sinatra
1/20
Building Single Page Web Apps with
Sinatra: Part 1
Jonathan Cutrellon Nov 8th 2012 with 14 Comments and 157 Reactions
Have you ever wanted to learn how to build a sinle !ae a!! with "inatra and #noc$out%&s'
(ell) today is the day you learn* +n this ,irst section o, a two-!art series) we.ll review the
!rocess ,o buildin a sinle !ae to-do a!!lication where users can view their tas$s) sort
them) mar$ them as com!lete) delete them) search throuh them) and add new tas$s%
What is Sinatra?
/ccordin to their website
"inatra is a " ,or 3uic$ly creatin web a!!lications in Ruby with minimal e,,ort%
"inatra allows you to do thins) li$e
get "/task/new"do erb :formend
his is a route that handles 6 re3uests ,or tas$new9 and renders an erb,orm named
form.erb% (e won.t be usin "inatra to render Ruby tem!lates: instead) we.ll use it only to
send J";N res!onses to our #noc$out%&s manaed ,ront end % (e will be usin erb only to render the main H? ,ile%
What is Knockout?
#noc$out is a ?odel-@iew-@iew?odel Java"cri!t ,ramewor$ that allows you to
$ee! your models in s!ecial observable9 ob&ects% +t also $ee!s your A+ u! to date) based onthose observed ob&ects%
1
http://net.tutsplus.com/author/jonathan-cutrell/http://net.tutsplus.com/tutorials/javascript-ajax/building-single-page-web-apps-with-sinatra-part-1/#disqus_threadhttp://net.tutsplus.com/tutorials/javascript-ajax/building-single-page-web-apps-with-sinatra-part-1/http://net.tutsplus.com/author/jonathan-cutrell/http://net.tutsplus.com/tutorials/javascript-ajax/building-single-page-web-apps-with-sinatra-part-1/#disqus_thread8/13/2019 Building Single Page Web Apps With Sinatra
2/20
Here is what you.ll be buildin
-ToDo/-app.rb-models.rb--views/ -index.erb-- public /--- scripts/ - knockout.js - juer!.js - app.js
--- st!les/ - st!les.css
(e.ll et started by de,inin our model and then our CRA actions in "inatra% (e.ll rely on
ata?a!!erand "=ite,or !ersistent storae) but you can use any ;R? that you !re,er%
et.s add a tas$ model to the models.rb,ile
Dataapper.setup#:default %slite:///pat&/to/project.db%'classTask include Dataapper::(esource propert! :id )erial
propert! :complete *oolean propert! :description Text
2
http://datamapper.org/http://www.sqlite.org/http://sqlite//path/to/project.dbhttp://datamapper.org/http://www.sqlite.org/http://sqlite//path/to/project.db8/13/2019 Building Single Page Web Apps With Sinatra
3/20
propert! :created+at DateTime propert! :updated+at DateTimeendDataapper.auto+upgrade,
his tas$ model essentially consists o, a ,ew di,,erent !ro!erties that we want to mani!ulatein our to-do a!!lication%
NeBt) let.s write our "inatra J";N server% +n the app.rb,ile) we.ll start by re3uirin a ,ew
di,,erent modules
reuire %rub!gems%reuire %sinatra%reuire %data+mapper%reuire ile.dirname#++0++' 1 %/models.rb%reuire %json%reuire %Date%
he neBt ste! is to de,ine some lobal de,aults: in !articular) we need a ?+?6 ty!e sent with
each o, our res!onse headers to s!eci,y that every res!onse is J";N%
before do content+t!pe %application/json%end
he beforehel!er ,unction runs be,ore every route match% ou can also s!eci,y matchin
routes a,ter before: i, you) ,or instance) wanted to only run J";N res!onses i, the AR
ended in %&son9) you would use this
before 2r3.14.json$5 do content+t!pe %application/json%end
NeBt) we de,ine our CRA routes) as well as one route to serve our index.erb,ile
get "/"do content+t!pe %&tml% erb :indexendget "/tasks"do
6tasks7 Task.all 6tasks.to+jsonendpost "/tasks/new"do 6task7 Task.new 6task.complete 7 false 6task.description 7 params8:description9 6task.created+at 7 DateTime.now 6task.updated+at 7 nullendput "/tasks/:id"do 6task7 Task.find#params8:id9' 6task.complete 7 params8:complete9
6task.description 7 params8:description9 6task.updated+at 7 DateTime.now
D
8/13/2019 Building Single Page Web Apps With Sinatra
4/20
if6task.save 3:task7 6task :status7 "success"5.to+json else 3:task7 6task :status7 "failure"5.to+json endend
delete "/tasks/:id"do 6task7 Task.find#params8:id9' if6task.destro! 3:task7 6task :status7 "success"5.to+json else 3:task7 6task :status7 "failure"5.to+json endend
"o the app.rb,ile now loo$s li$e this
reuire %rub!gems%reuire %sinatra%reuire %data+mapper%reuire ile.dirname#++0++' 1 %/models.rb%reuire %json%reuire %Date%before do content+t!pe %application/json%endget "/"do content+t!pe %&tml% erb :indexendget "/tasks"do 6tasks7 Task.all 6tasks.to+jsonendpost "/tasks/new"do 6task7 Task.new 6task.complete 7 false 6task.description 7 params8:description9 6task.created+at 7 DateTime.now 6task.updated+at 7 null if6task.save 3:task7 6task :status7 "success"5.to+json else 3:task7 6task :status7 "failure"5.to+json end
endput "/tasks/:id"do 6task7 Task.find#params8:id9' 6task.complete 7 params8:complete9 6task.description 7 params8:description9 6task.updated+at 7 DateTime.now if6task.save 3:task7 6task :status7 "success"5.to+json else 3:task7 6task :status7 "failure"5.to+json endenddelete "/tasks/:id"do
6task7 Task.find#params8:id9' if6task.destro!
4
8/13/2019 Building Single Page Web Apps With Sinatra
5/20
3:task7 6task :status7 "success"5.to+json else 3:task7 6task :status7 "failure"5.to+json endend
6ach o, these routes ma!s to an action% here is only one view thathouses every action% Remember in Ruby) the ,inal value returns im!licitly% ou can
eB!licitly return early) but whatever content these routes return will be the res!onse sent ,rom
the server%
Knockout: Models
NeBt) we start by de,inin our models in #noc$out% +n app.js) !lace the ,ollowin code
functionTask#data' 3
t&is.description 7 ko.observable#data.description'; t&is.complete 7 ko.observable#data.complete'; t&is.created+at 7 ko.observable#data.created+at'; t&is.updated+at 7 ko.observable#data.updated+at'; t&is.id 7 ko.observable#data.id';5
/s you can see) these !ro!erties are directly ma!!ed to our model in models.rb% /
ko.observable$ee!s the value u!dated across the A+ when it chanes without havin to
rely on the server or on the ;? to $ee! trac$ o, its state%
NeBt) we will add a Task
8/13/2019 Building Single Page Web Apps With Sinatra
6/20
varnewtask 7 newTask#3 description: t&is.newTaskDesc#' 5'; $.get>)?@#"/getdate" function#data'3 newtask.created+at#data.date'; newtask.updated+at#data.date'; t.tasks.pusnewtask'; t.saveTask#newtask';
t.newTaskDesc#""'; 5'5;t.saveTask 7 function#task' 3 vart 7 ko.to>)#task'; $.ajax#3 url: "&ttp://local&ost:ABAB/tasks" t!pe: "C?)T" data: t 5'.done#function#data'3 task.id#data.task.id'; 5';5
Eirst) we set newTaskDescas an observable% his allows us to use an in!ut ,ield easily to ty!e
a tas$ descri!tion% NeBt) we de,ine our addTask#',unction) which adds a tas$ to the
observable=rra!: it calls the saveTask#',unction) !assin in the new tas$ ob&ect%
he saveTask#',unction is anostic o, what $ind o, save it !er,orms%
8/13/2019 Building Single Page Web Apps With Sinatra
7/20
,-- Clace favicon.ico and apple-touc&-icon.png in t&e rootdirector! -- linkrel7"st!les&eet" &ref7"st!les/st!les.css" scriptsrc7"scripts/moderniLr-M.N.M.min.js"/script ,--8if lt 0 G9 p class7"c&romeframe"Fou are using an outdated browser. a
&ref7"&ttp://browse&app!.com/"Jpgrade !our browser toda!/a or a&ref7"&ttp://www.google.com/c&romeframe/Oredirect7true "install PoogleE&rome rame/a to better experience t&is site./p ,8endif9-- ,-- =dd !our site or application content &ere -- divid7"container" sectionid7"taskforms"class7"clearfix" divid7"newtaskform"class7"floatleft fift!" &MEreate a @ew Task/&M formid7"addtask" input inputt!pe7"submit" /form
/div divid7"tasksearc&form"class7"floatrig&t fift!" &M)earc& Tasks/&M formid7"searc&task" input /form /div /section sectionid7"tasktable" &Mncomplete Tasks remaining: span/span/&M aDelete =ll Eomplete Tasks/a table tbod!tr
t&D* D/t& t&Description/t& t&Date =dded/t& t&Date odified/t& t&EompleteO/t& t&Delete/t& /tr tr td/td td/td td/td td/td tdinputt!pe7"c&eckbox" /td tdclass7"destro!task"aI/a/td /tr /tbod!/table /section /div scriptsrc7"&ttp://ajax.googleapis.com/ajax/libs/juer!/K.H.K/juer!.min.js "/script scriptwindow.jQuer! RR document.write#%scriptsrc7"scripts/juer!.js"4/script%'/script scriptsrc7"scripts/knockout.js"/script scriptsrc7"scripts/app.js"/script ,-- Poogle =nal!tics: c&ange J=-IIIII-I to be !our site%s D. -- script var +ga788%+set=ccount%%J=-IIIII-I%98%+trackCageview%99;
7
http://browsehappy.com/http://www.google.com/chromeframe/?redirect=truehttp://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.jshttp://browsehappy.com/http://www.google.com/chromeframe/?redirect=truehttp://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js8/13/2019 Building Single Page Web Apps With Sinatra
8/20
#function#dt'3varg7d.create0lement#t's7d.get0lements*!Tag@ame#t'8S9; g.src7#%&ttps:%77location.protocolO%//ssl%:%//www%'1%.google-anal!tics.com/ga.js%; [email protected]*efore#gs'5#document%script%''; /script
/bod!/&tml
et.s ta$e this tem!late and ,ill in the bindins that #noc$out uses to $ee! the A+ in sync% Eor
this !art) we cover the creation o, o-o items% +n the !art two) we will cover more advanced
,unctionality %
Ge,ore we move on) let.s ive our !ae a little bit o, style% "ince this tutorial isn.t about C"")
we.ll &ust dro! this in and move riht alon% he ,ollowin code is inside the H?5
Goiler!late C"" ,ile) which includes a reset and a ,ew other thins%
section 3 widt&: HSSpx; margin: MSpxauto;5table 3 widt&: KSS2;5t& 3 cursor: pointer;5tr 3 border-bottom: Kpxsolidddd;
5tr.complete tr.complete:nt&-c&ild#odd' 3 background: efffdG; color: ddd;5tr:nt&-c&ild#odd' 3 background-color: dedede;5td 3 padding: KSpxMSpx;5td.destro!task 3 background: ffeaea;
color: AUBcBc; font-weig&t: bold; opacit!: S.U;5td.destro!task:&over 3 cursor: pointer; background: ffacac; color: GAMGMG; opacit!: K;5.fift! 3 widt&: VS2; 5input 3 background: fefefe;
box-s&adow: insetSSNpxaaa; padding: Npx; border: none;
8
8/13/2019 Building Single Page Web Apps With Sinatra
9/20
widt&: AS2; margin: Upx;5input:focus 3 outline: none; box-s&adow: insetSSNpxrgb#KG KUH MKK';
-webkit-transition: S.Ms all; background: rgba#KG KUH MKK S.SV';5input8t!pe7submit9 3 background-color: KKAUdB; background-image: -webkit-gradient#linear lefttop leftbottomfrom#rgb#KG KUH MKK'' to#rgb#VA AV KUM'''; background-image: -webkit-linear-gradient#top rgb#KG KUH MKK'rgb#VA AV KUM''; background-image: -moL-linear-gradient#top rgb#KG KUH MKK' rgb#VAAV KUM''; background-image: -o-linear-gradient#top rgb#KG KUH MKK' rgb#VAAV KUM'';
background-image: -ms-linear-gradient#top rgb#KG KUH MKK' rgb#VAAV KUM''; background-image: linear-gradient#top rgb#KG KUH MKK' rgb#VA AVKUM''; filter:progid:DImageTransform.icrosoft.gradient#PradientT!pe7S)tartEolor)tr7%KKAUdB% 0ndEolor)tr7%BbVfHe%'; padding: NpxApx; border-radius: Bpx; color: fff; text-s&adow: KpxKpxKpxSaBdVM; border: none; widt&: BS2;
5input8t!pe7submit9:&over 3 background: SaBdVM;5.floatleft 3 float: left; 5.floatrig&t 3 float: rig&t; 5
/dd this code to your st!les.css,ile%
Now) let.s cover the new tas$9 ,orm% (e will add data-bindattributes to the ,orm to ma$e
the #noc$out bindins wor$% he data-bindattribute is how #noc$out $ee!s the A+ in sync)
and allows ,or event bindin and other im!ortant ,unctionality% Re!lace the new tas$9 ,ormwith the ,ollowin code%
divid7"newtaskform"class7"floatleft fift!" &MEreate a @ew Task/&M formid7"addtask"data-bind7"submit: addTask" inputdata-bind7"value: newTaskDesc" inputt!pe7"submit" /form/div
(e.ll ste! throuh these one by one% Eirst) the ,orm element has a bindin ,or the submit
event% (hen the ,orm is submitted) the addTask#',unction de,ined on the Task
8/13/2019 Building Single Page Web Apps With Sinatra
10/20
the ko.observable newTaskDescthat we de,ined earlier% (hatever is in this ,ield when
submittin the ,orm becomes the as$.s description!ro!erty%
"o we have a way to add tas$s) but we need to dis!lay those tas$s% (e also need to add each
o, the tas$.s !ro!erties% et.s iterate over the tas$s and add them into the table% #noc$out
!rovides a convenient iteration ability to ,acilitate this: de,ine a comment bloc$ with the,ollowin syntaB
,-- ko foreac&: tasks -- tddata-bind7"text: id"/td tddata-bind7"text: description"/td tddata-bind7"text: created+at"/td tddata-bind7"text: updated+at"/td td inputt!pe7"c&eckbox"/td td aI/a/td,-- /ko --
his uses #noc$out.s iteration ca!ability% 6ach tas$ is s!eci,ically de,ined on theTask
8/13/2019 Building Single Page Web Apps With Sinatra
11/20
Building Single Page Web Apps With
Sinatra: Part 2
+n the ,irst !arto, this mini-series) we created the basic structure o, a to-do a!!lication usin
a "inatra J";N inter,ace to a "=ite database) and a #noc$out-!owered ,ront-end that
allows us to add tas$s to our database% +n this ,inal !art) we.ll cover some slihtly more
advanced ,unctionality in #noc$out) includin sortin) searchin) u!datin) and deletin%
et.s start where we le,t o,,: here is the relevant !ortion o, our index.erb,ile%
divid7"container" sectionid7"taskforms"class7"clearfix" divid7"newtaskform"class7"floatleft fift!"
&MEreate a @ew Task/&M formid7"addtask"data-bind7"submit: addTask" inputdata-bind7"value: newTaskDesc" inputt!pe7"submit" /form /div divid7"tasksearc&form"class7"floatrig&t fift!" &M)earc& Tasks/&M formid7"searc&task" input /form /div /section sectionid7"tasktable" &Mncomplete Tasks remaining: span/span/&M aDelete =ll Eomplete Tasks/a table tbod!tr t&D* D/t& t&Description/t& t&Date =dded/t& t&Date odified/t& t&EompleteO/t& t&Delete/t& /tr ,-- ko foreac&: tasks -- tr tddata-bind7"text: id"/td tddata-bind7"text: description"/td tddata-bind7"text: created+at"/td tddata-bind7"text: updated+at"/td tdinputt!pe7"c&eckbox"data-bind7"c&ecked:complete click: $parent.mark=sEomplete" /td tddata-bind7"click: $parent.destro!Task"class7"destro!task"aI/a/td /tr ,-- /ko -- /tbod!/table /section /div
11
http://net.tutsplus.com/tutorials/javascript-ajax/building-single-page-web-apps-with-sinatra-part-1/http://net.tutsplus.com/tutorials/javascript-ajax/building-single-page-web-apps-with-sinatra-part-1/8/13/2019 Building Single Page Web Apps With Sinatra
12/20
Sort
"ortin is a common tas$ used in many a!!lications% +n our case) we want to sort the tas$ list
by any header ,ield in our tas$-list table% (e will start by addin the ,ollowin code to the
Task
8/13/2019 Building Single Page Web Apps With Sinatra
13/20
hese bindins allow each o, the headers to trier a sort based on the !assed strin value:
each o, these directly ma!s to the Taskmodel%
Mark As Complete
NeBt) we want to be able to mar$ a tas$ as com!lete) and we.ll accom!lish this by sim!ly
clic$in the chec$boB associated with a !articular tas$% et.s start by de,inin a method in the
Task
8/13/2019 Building Single Page Web Apps With Sinatra
14/20
his ,unction adds a !ro!erty similar to the !ut9 method ,or com!letin a tas$% he built-in
destro!#'method removes the !assed-in tas$ ,rom the observable array% Einally) callin
saveTask#'destroys the tas$: that is) as lon as the .+met&odis set to delete9%
Now we need to modi,y our view: add the ,ollowin
tddata-bind7"click: $parent.destro!Task"class7"destro!task"aI/a/td
his is very similar in ,unctionality to the com!lete chec$boB% Note that the
class7"destro!task"is !urely ,or stylin !ur!oses%
Delete All Completed
NeBt) we want to add the delete all com!lete tas$s9 ,unctionality% Eirst) add the ,ollowincode to the Task
8/13/2019 Building Single Page Web Apps With Sinatra
15/20
ncomplete Tasks !emaining
;ur inter,ace should also dis!lay the amount o, incom!lete tas$s% "imilar to our
completeTasks#',unction above) we de,ine an incompleteTasks#',unction in
Task
8/13/2019 Building Single Page Web Apps With Sinatra
16/20
tddata-bind7"text: $root.dateormat#created+at#''"/tdtddata-bind7"text: $root.dateormat#updated+at#''"/td
his !asses the created+atand updated+at!ro!erties to the dateormat#',unction% ;nce
aain) it.s im!ortant to remember that !ro!erties o, each tas$ are not normal !ro!erties: they
are ,unctions% +n order to retrieve their value) you must call the ,unction % Note $rootis a $eyword) de,ined by #noc$out) that re,ers to the
@iew?odel% he dateormat#'method) ,or instance) is de,ined as a method o, the root
@iew?odel
8/13/2019 Building Single Page Web Apps With Sinatra
17/20
#inal Code
index.erb
,D?ETFC0 &tml
&tml,--8if lt 0 G9 &tml class7"no-js lt-ieA lt-ieH lt-ieG" ,8endif9--,--8if 0 G9 &tml class7"no-js lt-ieA lt-ieH" ,8endif9--,--8if 0 H9 &tml class7"no-js lt-ieA" ,8endif9--
,--8if gt 0 H9,-- ,--,8endif9-- bod! metac&arset7"utf-H" meta&ttp-euiv7"I-J=-Eompatible" content7"07edgec&rome7K" titleToDo/title metaname7"description" content7"" metaname7"viewport"content7"widt&7device-widt&" ,-- Clace favicon.ico and apple-touc&-icon.png in t&e root
director! -- linkrel7"st!les&eet" &ref7"st!les/st!les.css" scriptsrc7"scripts/moderniLr-M.N.M.min.js"/script ,--8if lt 0 G9 p class7"c&romeframe"Fou are using an outdated browser. a&ref7"&ttp://browse&app!.com/"Jpgrade !our browser toda!/a or a&ref7"&ttp://www.google.com/c&romeframe/Oredirect7true "install PoogleE&rome rame/a to better experience t&is site./p ,8endif9-- ,-- =dd !our site or application content &ere -- divid7"container" sectionid7"taskforms"class7"clearfix" divid7"newtaskform"class7"floatleft fift!" &MEreate a @ew Task/&M formid7"addtask"data-bind7"submit: addTask" inputdata-bind7"value: newTaskDesc" inputt!pe7"submit" /form /div divid7"tasksearc&form"class7"floatrig&t fift!" &M)earc& Tasks/&M formid7"searc&task" inputdata-bind7"value: uer! valueJpdate:%ke!up% event : 3 ke!up : searc&5" /form /div
/section sectionid7"tasktable" &Mncomplete Tasks remaining: spandata-bind7"text:incompleteTasks#'.lengt&"/span/&M adata-bind7"click: remove=llEomplete visible:completeTasks#'.lengt& S "Delete =ll Eomplete Tasks/a table tbod!tr t&data-bind7"click: function#'3 sort#%id%' 5"D*D/t& t&data-bind7"click: function#'3 sort#%description%' 5"Description/t& t&data-bind7"click: function#'
3 sort#%created+at%' 5"Date =dded/t&
17
http://browsehappy.com/http://www.google.com/chromeframe/?redirect=truehttp://browsehappy.com/http://www.google.com/chromeframe/?redirect=true8/13/2019 Building Single Page Web Apps With Sinatra
18/20
t&data-bind7"click: function#'3 sort#%updated+at%' 5"Date odified/t& t&data-bind7"click: function#'3 sort#%complete%' 5"EompleteO/t& t&Delete/t& /tr
,-- ko foreac&: tasks -- trdata-bind7"css: 3 %complete%: complete 5 visible:isvisible" tddata-bind7"text: id"/td tddata-bind7"text: description"/td tddata-bind7"text:$root.dateormat#created+at#''"/td tddata-bind7"text:$root.dateormat#updated+at#''"/td tdinputt!pe7"c&eckbox"data-bind7"c&ecked:complete click: $parent.mark=sEomplete" /td tddata-bind7"click: $parent.destro!Task"class7"destro!task"aI/a/td
/tr ,-- /ko -- /tbod!/table /section /div scriptsrc7"&ttp://ajax.googleapis.com/ajax/libs/juer!/K.H.K/juer!.min.js "/script scriptwindow.jQuer! RR document.write#%scriptsrc7"scripts/juer!.js"4/script%'/script scriptsrc7"scripts/knockout.js"/script scriptsrc7"scripts/app.js"/script ,-- Poogle =nal!tics: c&ange J=-IIIII-I to be !our site%s D. --
script var +ga788%+set=ccount%%J=-IIIII-I%98%+trackCageview%99; #function#dt'3varg7d.create0lement#t's7d.get0lements*!Tag@ame#t'8S9; g.src7#%&ttps:%77location.protocolO%//ssl%:%//www%'1%.google-anal!tics.com/ga.js%; [email protected]*efore#gs'5#document%script%''; /script /bod!/&tml
app.js
functionTask#data' 3 t&is.description 7 ko.observable#data.description'; t&is.complete 7 ko.observable#data.complete'; t&is.created+at 7 ko.observable#data.created+at'; t&is.updated+at 7 ko.observable#data.updated+at'; t&is.id 7 ko.observable#data.id'; t&is.isvisible 7 ko.observable#true';5functionTask
8/13/2019 Building Single Page Web Apps With Sinatra
19/20
t.?@TX) 7 8">an" "eb" "ar" "=pr" "a!" ">un" ">ul" "=ug"")ep" "?ct" "@ov" "Dec"9; $.get>)?@#"&ttp://local&ost:ABAB/tasks" function#raw' 3 vartasks 7 $.map#raw function#item' 3 returnnewTask#item' 5'; t.tasks#tasks'; 5';
t.incompleteTasks 7 ko.computed#function#' 3 returnko.utils.arra!ilter#t.tasks#' function#task' 3 return#,task.complete#' WW task.+met&od ,7 "delete"' 5'; 5'; t.completeTasks 7 ko.computed#function#' 3 returnko.utils.arra!ilter#t.tasks#' function#task' 3 return#task.complete#' WW task.+met&od ,7 "delete"' 5'; 5'; // ?perations t.dateormat 7 function#date'3 if#,date' 3 return"refres& to see server date"; 5 vard 7 newDate#date'; returnd.getXours#' 1 ":"1 d.getinutes#' 1 " "1 d.getDate#' 1 "
"1 t.?@TX)8d.getont'9 1 " "1 d.getullFear#'; 5 t.addTask 7 function#' 3 varnewtask 7 newTask#3 description: t&is.newTaskDesc#' 5'; $.get>)?@#"/getdate" function#data'3 newtask.created+at#data.date'; newtask.updated+at#data.date'; t.tasks.pusnewtask'; t.saveTask#newtask'; t.newTaskDesc#""'; 5' 5; t.searc& 7 function#task'3
ko.utils.arra!or0act.tasks#' function#task'3 if#task.description#' WW t.uer!#' ,7 ""'3 task.isvisible#task.description#'.toowerEase#'.index?f#t.uer!#'.toowerEase#'' 7 S'; 5 elseif#t.uer!#' 77 ""' 3 task.isvisible#true'; 5 else3 task.isvisible#false'; 5 5' returntrue; 5 t.sort 7 function#field'3 if#t.sorted*!.lengt& WW t.sorted*!8S9 77 field WWt.sorted*!8K977K'3 t.sorted*!8K97S; t.tasks.sort#function#firstnext'3 if#,next8field9.call#''3 returnK; 5 return#next8field9.call#' first8field9.call#'' O K :#next8field9.call#' 77 first8field9.call#'' O S : -K; 5'; 5 else3 t.sorted*!8S9 7 field; t.sorted*!8K9 7 K; t.tasks.sort#function#firstnext'3 if#,first8field9.call#''3 returnK; 5 return#first8field9.call#' next8field9.call#'' O K :#first8field9.call#' 77 next8field9.call#'' O S : -K; 5';
1I
http://localhost:9393/taskshttp://localhost:9393/tasks8/13/2019 Building Single Page Web Apps With Sinatra
20/20
5 5 t.mark=sEomplete 7 function#task' 3 if#task.complete#' 77 true'3 task.complete#true'; 5 else3
task.complete#false'; 5 task.+met&od 7 "put"; t.saveTask#task'; returntrue; 5 t.destro!Task 7 function#task' 3 task.+met&od 7 "delete"; t.tasks.destro!#task'; t.saveTask#task'; 5; t.remove=llEomplete 7 function#' 3 ko.utils.arra!or0act.tasks#' function#task'3
if#task.complete#''3 t.destro!Task#task'; 5 5'; 5 t.saveTask 7 function#task' 3 vart 7 ko.to>)#task'; $.ajax#3 url: "&ttp://local&ost:ABAB/tasks" t!pe: "C?)T" data: t 5'.done#function#data'3 task.id#data.task.id';
5'; 55ko.appl!*indings#new Task