This is the to do project code in course 170.
sinatra
get "/" do
redirect "/lists"
end
if the user go to homepage app.com/ , it will be redirected to app.com/lists
sinatra
get "/lists" do
@lists = session[:lists]
erb :lists, layout: :layout
end
in app.com/lists , the session hash key of :lists will be stored in @lists. Then the layout lists.erb will be rendered.
lists.erb
<ul id="lists">
<% @lists.each_with_index do |list, index| %>
<li>
<a href="/lists/<%= index %>">
<h2><%= list[:name] %></h2>
<p><%= list[:todos].size %></p>
</a>
</li>
<% end %>
</ul>
<% content_for :header_links do %>
<a href="/lists/new">New List</a>
<% end %>
Actually @list will contain a data structure like this @lists = [{name: "todo" , todo: [] ]
So now we are displaying each list's name and todos in a h2 and p tag contained within a list. There will be an index associated with the link , that correspond to the position in the @lists array.
There is also a link that points to /lists/new get HTTP request.
get "/lists/new" do
erb :new_list, layout: :layout
end
This will render new_list.erb template.
<form action="/lists" method="post">
<dl>
<dt>
<label for="list_name">Enter the name for your new list:</label>
</dt>
<dd>
<input name="list_name" placeholder="List Name" type="text" value="">
</dd>
</dl>
<fieldset>
<input type="submit" value="Save">
<a href="/lists">Cancel</a>
</fieldset>
</form>
This is a form that will create a HTTP post request to app.com/lists. The variable name is list_name for the name of the todo list. Clicking the Cancel button will bring you back to app.com/lists.
# Create a new list.
post "/lists" do
list_name = params[:list_name].strip
error = error_for_list_name(list_name)
if error
session[:error] = error
erb :new_list, layout: :layout
else
session[:lists] << { name: list_name, todos: [] }
session[:success] = "The list have been created."
redirect "/lists"
end
end
get "/lists/:id" do
id = params[:id].to_i
@list = session[:lists][id]
erb :list , layout: :layout
end
If you created a new list, then params[:list_name] will contain the name of your new to do list. Calling strip will cut off white space in front and back of the string. error_for_list_name(list_name) checks for invalid input. If there is error, then store the error message in session[:error] Then render back new_list.erb which is the form that allow you to create the name for the to do list.
if there is no error, then insert {name: list_name, todos: [] } into the session[:lists] hash and pass into session hash success with a success message. Then redirect back to app.com/lists
def error_for_list_name(name)
if !(1..100).cover? name.size
"List name must be between 1 and 100 characters."
elsif session[:lists].any? { |list| list[:name] == name }
"List name must be unique."
end
end
This checks for error if the name's length is not within 1 to 100 characters. , if it pass, then check if any lists contains the same name as the current name. If any of the them failed , a failure message is return and saved in session[:error]
layout.erb
<body>
<header>
<h1>Todo Tracker</h1>
<div class="actions">
<%= yield_content :header_links %>
</div>
</header>
<% if session[:error] %>
<div class="flash error">
<p><%= session.delete(:error) %></p>
</div>
<% end %>
<% if session[:success] %>
<div class="flash success">
<p><%= session.delete(:success) %></p>
</div>
<% end %>
<main>
<%= yield %>
</main>
</body>
In the main layout.erb, the session[:success] will be deleted via session.delete(:success) , which return the value of that key. It is display with a css decoration of "flash success". Session is a good global variables that persist across routes.
lists.erb
<a href="/lists/<%= index %>">
In the lists.erb , there is a new link that look like app.com/lists/0 , this create a link to here
get "/lists/:id" do
id = params[:id].to_i
@list = session[:lists][id]
erb :list , layout: :layout
end
Here a form that create a POST request to app.com/lists/0 will be created. The input name will be stored in the variable called list_name that is stored in params[:list_name].
value="<%= params[:list_name] || @list[:name] %>"
The value is the attribute will be params[:list_name] first if it is not nil. list_name is not created in /lists/:id/edit but will only created in edit_list.erb , so if users has inputted something before and doesn't pass, then value will be the last inputted value. If params[:list_name] doesn't exist, then it will return nil and the || operator will return @list[:name] which is saved in get "/lists/:id/edit"
When you are done, then a post request to "/lists/:id is created.
post "/lists/:id" do
list_name = params[:list_name].strip
id = params[:id].to_i
@list = session[:lists][id]
error = error_for_list_name(list_name)
if error
session[:error] = error
erb :edit_list, layout: :layout
else
@list[:name] = list_name
session[:success] = "The list have been updated."
redirect "/lists/#{id}"
end
end
params[:list_name] is strip and stored in list_name . The only different than creating a new to do list is @list[:name] = list_name. This will mutate the caller. Because @list will store session[:lists][id], so @list will point to that particular lists. So altering the @list[:name] will alter the session[:lists] as well.
You nee to remember the data structure very well
lists = [list, list, list]
list = {name: list_name, todos: [todo, todo, todo] }
todo = {name: todo_name, completed: true/false }
sinatra
get "/" do
redirect "/lists"
end
if the user go to homepage app.com/ , it will be redirected to app.com/lists
sinatra
get "/lists" do
@lists = session[:lists]
erb :lists, layout: :layout
end
in app.com/lists , the session hash key of :lists will be stored in @lists. Then the layout lists.erb will be rendered.
lists.erb
<ul id="lists">
<% @lists.each_with_index do |list, index| %>
<li>
<a href="/lists/<%= index %>">
<h2><%= list[:name] %></h2>
<p><%= list[:todos].size %></p>
</a>
</li>
<% end %>
</ul>
<% content_for :header_links do %>
<a href="/lists/new">New List</a>
<% end %>
Actually @list will contain a data structure like this @lists = [{name: "todo" , todo: [] ]
So now we are displaying each list's name and todos in a h2 and p tag contained within a list. There will be an index associated with the link , that correspond to the position in the @lists array.
There is also a link that points to /lists/new get HTTP request.
get "/lists/new" do
erb :new_list, layout: :layout
end
This will render new_list.erb template.
<form action="/lists" method="post">
<dl>
<dt>
<label for="list_name">Enter the name for your new list:</label>
</dt>
<dd>
<input name="list_name" placeholder="List Name" type="text" value="">
</dd>
</dl>
<fieldset>
<input type="submit" value="Save">
<a href="/lists">Cancel</a>
</fieldset>
</form>
This is a form that will create a HTTP post request to app.com/lists. The variable name is list_name for the name of the todo list. Clicking the Cancel button will bring you back to app.com/lists.
# Create a new list.
post "/lists" do
list_name = params[:list_name].strip
error = error_for_list_name(list_name)
if error
session[:error] = error
erb :new_list, layout: :layout
else
session[:lists] << { name: list_name, todos: [] }
session[:success] = "The list have been created."
redirect "/lists"
end
end
get "/lists/:id" do
id = params[:id].to_i
@list = session[:lists][id]
erb :list , layout: :layout
end
If you created a new list, then params[:list_name] will contain the name of your new to do list. Calling strip will cut off white space in front and back of the string. error_for_list_name(list_name) checks for invalid input. If there is error, then store the error message in session[:error] Then render back new_list.erb which is the form that allow you to create the name for the to do list.
if there is no error, then insert {name: list_name, todos: [] } into the session[:lists] hash and pass into session hash success with a success message. Then redirect back to app.com/lists
def error_for_list_name(name)
if !(1..100).cover? name.size
"List name must be between 1 and 100 characters."
elsif session[:lists].any? { |list| list[:name] == name }
"List name must be unique."
end
end
This checks for error if the name's length is not within 1 to 100 characters. , if it pass, then check if any lists contains the same name as the current name. If any of the them failed , a failure message is return and saved in session[:error]
layout.erb
<body>
<header>
<h1>Todo Tracker</h1>
<div class="actions">
<%= yield_content :header_links %>
</div>
</header>
<% if session[:error] %>
<div class="flash error">
<p><%= session.delete(:error) %></p>
</div>
<% end %>
<% if session[:success] %>
<div class="flash success">
<p><%= session.delete(:success) %></p>
</div>
<% end %>
<main>
<%= yield %>
</main>
</body>
In the main layout.erb, the session[:success] will be deleted via session.delete(:success) , which return the value of that key. It is display with a css decoration of "flash success". Session is a good global variables that persist across routes.
lists.erb
<a href="/lists/<%= index %>">
In the lists.erb , there is a new link that look like app.com/lists/0 , this create a link to here
get "/lists/:id" do
id = params[:id].to_i
@list = session[:lists][id]
erb :list , layout: :layout
end
Here the id is pass into params[:id] that is stored in id . The to_i method will convert it to an integer.
In the session[:lists] , there is an array like so session[:lists] = [{name: "Work", todo = [] } , {name: "Play", todo = [] } ] , so if we pass in @list = session[:lists][id] with id of 0 , then this will be stored in @list = {name: "Work", todo = [] } Then after that , we render list.erb
Note : we could create a sinatra route that look like below. It will work but for the first item in the list only . And params[:id] will not have value, remember that params will not cross over to other route.
get "/lists/0" do
end
get "/lists/0" do
end
<section id="todos">
<header>
<h2><%= @list[:name] %></h2>
<ul>
<li><a class="edit" href="/lists/<%= params[:id] %>/edit">Edit List</a></li>
</ul>
</header>
</section>
<% content_for :header_links do %>
<a href="/lists">All List</a>
<% end %>
Here , @list[:name] will display "Work" in the h2 tag. In the edit_list link , a link is created to app.com/lists/0/edit via GET request. The code will flow to below
get "/lists/:id/edit" do
id = params[:id].to_i
@list = session[:lists][id]
erb :edit_list, layout: :layout
end
That particular to do list will stored in @list. that look like @list = {name: "Work", todo = [] }. Then edit_list.erb will be rendered
<form action="/lists/<%= params[:id] %>" method="post">
<dl>
<dt>
<label for="list_name">Enter the new name for the list:</label>
</dt>
<dd>
<input name="list_name" placeholder="List Name" type="text" value="<%= params[:list_name] || @list[:name] %>">
</dd>
</dl>
<fieldset>
<input type="submit" value="Save">
<a href="/lists/<%= params[:id] %>">Cancel</a>
</fieldset>
</form>
Here a form that create a POST request to app.com/lists/0 will be created. The input name will be stored in the variable called list_name that is stored in params[:list_name].
value="<%= params[:list_name] || @list[:name] %>"
The value is the attribute will be params[:list_name] first if it is not nil. list_name is not created in /lists/:id/edit but will only created in edit_list.erb , so if users has inputted something before and doesn't pass, then value will be the last inputted value. If params[:list_name] doesn't exist, then it will return nil and the || operator will return @list[:name] which is saved in get "/lists/:id/edit"
When you are done, then a post request to "/lists/:id is created.
post "/lists/:id" do
list_name = params[:list_name].strip
id = params[:id].to_i
@list = session[:lists][id]
error = error_for_list_name(list_name)
if error
session[:error] = error
erb :edit_list, layout: :layout
else
@list[:name] = list_name
session[:success] = "The list have been updated."
redirect "/lists/#{id}"
end
end
params[:list_name] is strip and stored in list_name . The only different than creating a new to do list is @list[:name] = list_name. This will mutate the caller. Because @list will store session[:lists][id], so @list will point to that particular lists. So altering the @list[:name] will alter the session[:lists] as well.
You nee to remember the data structure very well
lists = [list, list, list]
list = {name: list_name, todos: [todo, todo, todo] }
todo = {name: todo_name, completed: true/false }
Comments
Post a Comment