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

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

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