Skip to main content

Explain To do code midway.

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 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


<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

Popular posts from this blog

Problem Solving - Refactored

I am going to outline how I approach problem solving. The relative importance and the amount of effort/time required for each is stated as a percentage beside each topic. I borrowed some idea from George Polya's How to Solve It Thoroughly Understand the Problem (30%) When encountering hard problem , you need to deeply understand the problem at hand. Take a paper and list down all known facts and data and what the question is trying to find. Sketch out the problem if applicable. Visualize the problem in your head. A lot of times, we only have to understand the problem well, then the solution will obvious. Have a Plan (20%) You need to have an outline of how you are going to tackle the problem. You need to have a logical pathway that will ultimate produce outcome (nothing to do with coding syntax yet). Without a plan, you are just randomly poking around and got lucky. No hard problem ever gets solved without a plan. Plan using pseudo-code, pen & paper or flowchart. Use wh

My Burnout Experience

I want to share with you my experience of burning out. After registering with Launch School, I am extremely excited about my programming journey. I studied for 10 to 12 hours a day, memorizing fact, trying out practice problems, understanding programming concepts. It was fun and exciting and I love seeing myself growing from nothing in programming to something more. After about 3 months, thing starts to change. I started noticing myself paying less attention to details. I find myself skimming through the course material. I skip "Further Exploration" in the practice problem. I am more interested to study just to pass the assessment rather than truly mastering the concept. It was a gradual burning out process but I continue to study for 10 to 12 hours a day through sheer grit. It felt like doing house chore or working a day job that you don't like. One particular morning I woke up, and I remember this deep feeling of dread because I can anticipate that the next 10 to 1

Sharing my Weakness

It makes sense to know about your weakness and do something about it. Here are my known weaknesses uncovered during my time in Launch School. 1. I don't like to refactor my code   - Your first draft will not be perfect. It works but it may not be efficient/readable/best practices. You final code will almost always be better than your first draft. - It is easier to separate the task between writing code that works and refactor later to make it efficient/readable/best practices. - If you refactor your code often, over time you will discover your bad habits and change it. 2. I don't like to read other people's code - There are more good programming practices in other people than in you (especially for beginners like me). - To be good , you need to know more than one pathways to solve a programming problem (and there are always more than one way). Then you can judge their merit. - Reason for dislikes    1. It is considerably harder to read code than to write one (be