Skip to content

Instantly share code, notes, and snippets.

@frankolson
Last active March 8, 2019 00:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save frankolson/f03ff55a8ae53e4b045a2175bfe79db7 to your computer and use it in GitHub Desktop.
Save frankolson/f03ff55a8ae53e4b045a2175bfe79db7 to your computer and use it in GitHub Desktop.
class ApplicationController < ActionController::Base
# While in such a small app, this extraction into a concern seems unnecessary.
# I included this abstraction as an example of DRY and modular code..
include SetRequest
end
module SetRequest
extend ActiveSupport::Concern
included do
before_action :set_user
end
private
def set_user
# ActiveSupport::CurrentAttributes is thread isolated and resets before
# and after each request. Since it the user is only being set once during
# each request, memoization is not needed.
Current.user = User.find(session[:id])
end
end
class TodoItemsController < ApplicationController
# This was moved to its own controller to match RESTful route principles. It
# was also renamed from "complete_item" to "complete" to make a prettier URL.
#
# ex. /todo_items/:id/complete
#
# Routes configuration not included as instructed by the original Gist
# description
def complete
@item = TodoList.find(params[:id])
@item.complete!
# Rails 5 introduces the improved "redirect_back" to replace the old
# "redirect_to :back", which was deprecated. This counters the
# ActionController::RedirectBackError exception when HTTP_REFERER is not
# present
redirect_back fallback_location: @item.todo_list
end
end
class TodoListsController < ApplicationController
before_action :set_list, only: [:show, :complete_item]
def index
# "TodoList.where(user_id: User.current_user.id)" was not as readable as the
# following code
@lists = Current.user.todo_lists.order("#{sort_by} #{sort_order}")
end
def show
@list = TodoList.find(params[:id])
end
def new
@list = TodoList.new
end
def create
@list = TodoList.new(todo_list_params)
if @list.save!
redirect_to todo_lists_url
else
# Provide the user with helpful error messages
flash[:notice] = "Error: #{@list.errors.full_messages.to_sentence}"
redirect_to new_todo_list_url
end
end
private
# Never trust parameters from the scary internet. I figured we add some
# strong parameters
def todo_list_params
# I made some guesses on the structure of TodoList based on the model and
# views
params.require(:todo_list).permit(:title)
end
# "sort_by" and "sort_order" were refactored using the Extract Method
def sort_by
params[:sort] || 'created_at'
end
def sort_order
(params[:asc] == 'false') ? 'ASC' : 'DESC'
end
end
class Current < ActiveSupport::CurrentAttributes
attribute :user
end
class TodoItem < ApplicationRecord
belongs_to :todo_list
validates :title, presence: true
scope :incomplete, -> { where(done: false) }
# Completes to do item, saving to database.
def complete!
update!(done: true)
end
end
class TodoList < ApplicationRecord
has_many :todo_items
belongs_to :user
before_validation :set_user, on: :create
def formatted_title
title + (todo_items.empty? ' (Complete)' : '')
end
# Assign this list to the currently signed in user when it's created
# (Personal preference) I find that keeping the method and variable names
# complete, rather than abbreviated makes for more readable code
def set_user
self.user ||= Current.user
end
end
class User < ApplicationRecord
has_many :todo_lists
end
<li>
<%= item.title %> - <%= link_to 'Done!', complete_todo_item_path(item), method: :put %>
</li>
<h2><%= list.formatted_title %></h2>
<ul>
<%# extract todo_item list out into a partial %>
<%= render list.todo_items.incomplete %>
</ul>
<%# Display the current user's lists along with any uncompleted items %>
<%# extract todo_lists out into a partial %>
<%= render @lists %>
<%# extract todo_list out into a partial %>
<%= render @list %>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment