Todos

Explanation

What revolution in software development would be complete without a tiny, highly-performant implementation of TodoMVC?

Code (156 LOC)

JavaScript (43 LOC)
app/javascript/controllers/todos_controller.js (43 LOC)
import Velocity from 'velocity-animate'
import ApplicationController from './application_controller'
export default class extends ApplicationController {
static targets = ['input', 'list']
cancelEdit (event) {
if (event.type === 'keyup' && !['Escape', 'Esc'].includes(event.key)) return
this.stimulate('TodosReflex#cancel_edit')
}
afterCreate () {
this.highlight(this.lastListItem)
this.inputTarget.value = ''
}
afterToggle (checkbox) {
this.highlight(checkbox.closest('li'))
}
afterEdit (element) {
const input = element.querySelector('input[type="text"]')
const value = input.value
input.focus()
input.value = ''
input.value = value
}
afterUpdate (element) {
this.highlight(this.listItem(element.dataset.id))
}
afterToggleAll () {
this.listItems.forEach(li => this.highlight(li))
}
highlight (element) {
Velocity(element, { scale: 1.01, backgroundColor: '#ff9' }, 50).then(
Velocity(element, { scale: 1, backgroundColor: '#fff' }, 100)
)
}
listItem (id) {
return this.listTarget.querySelector(`li[data-id="${id}"]`)
}
get listItems () {
return this.listTarget.querySelectorAll('li')
}
get lastListItem () {
return this.listItems[this.listItems.length - 1]
}
}
ERB (52 LOC)
app/views/todos/_demo.html.erb (20 LOC)
<div class="col-md-10 col-lg-8 col-xl-7">
<section id="todoapp" class="todoapp" data-controller="todos" data-reflex-root>
<header class="header">
<input <%= "autofocus " unless @edit_id %> class="new-todo" placeholder="What needs to be done?"
data-target="todos.input" data-reflex="change->TodosReflex#create">
</header>
<% if @all_todos.any? %>
<section class="main">
<input class="toggle-all" type="checkbox">
<label data-reflex="click->TodosReflex#toggle_all">
Mark all as complete
</label>
<ul class="todo-list" data-target="todos.list">
<%= render partial: "todo", collection: @filtered_todos %>
</ul>
</section>
<% end %>
<%= render "/todos/filters" if @all_todos.any? %>
</section>
</div>
app/views/todos/_filters.html.erb (15 LOC)
<section class="footer">
<span class="todo-count">
<strong><%= @all_todos.active.count %></strong> <%= "item".pluralize @all_todos.active.count %> left
</span>
<ul class="filters">
<%= tag.li link_to("All", "#", id: "todos-filter-all", class: filter_css(:all), data: { reflex: "click->TodosReflex#filter", filter: "all" }) %>
<%= tag.li link_to("Active", "#", id: "todos-filter-active", class: filter_css(:active), data: { reflex: "click->TodosReflex#filter", filter: "active" }) %>
<%= tag.li link_to("Completed", "#", id: "todos-filter-completed", class: filter_css(:completed), data: { reflex: "click->TodosReflex#filter", filter: "completed" }) %>
</ul>
<% if @all_todos.completed.any? %>
<button class="clear-completed" data-reflex="click->TodosReflex#destroy_completed">
Clear completed
</button>
<% end %>
</section>
app/views/todos/_todo.html.erb (17 LOC)
<% if todo.id == @edit_id %>
<li class="editing">
<input type="text" class="edit" value="<%= todo.title %>"
data-action="keyup->todos#cancelEdit blur->todos#cancelEdit"
data-reflex="change->TodosReflex#update" data-id="<%= todo.id %>">
</li>
<% else %>
<li class="<%= "completed" if todo.completed? %>" data-reflex="click->TodosReflex#edit" data-id="<%= todo.id %>">
<div class="view">
<input <%= "checked" if todo.completed? %> type="checkbox" class="toggle" data-reflex="change->TodosReflex#toggle" data-id="<%= todo.id %>">
<label>
<%= todo.title %>
</label>
<button name="button" type="button" class="destroy" data-reflex="click->TodosReflex#destroy" data-id="<%= todo.id %>" />
</div>
</li>
<% end %>
Ruby (61 LOC)
app/controllers/todos_controller.rb (16 LOC)
class TodosController < ApplicationController
FILTERS = %w[all active completed].freeze
after_action :cleanup
def show
session[:todo_filter] = "all" unless filter_permitted?(session[:todo_filter])
@all_todos = Todo.where(session_id: session.id.to_s)
@filtered_todos = @all_todos.public_send(session[:todo_filter]).order(:created_at)
end
private
def filter_permitted?(filter)
FILTERS.include? filter
end
def cleanup
Todo.old.delete_all
end
end
app/helpers/todos_helper.rb (5 LOC)
module TodosHelper
def filter_css(filter)
"selected" if session[:todo_filter] == filter.to_s
end
end
app/models/todo.rb (10 LOC)
class Todo < ApplicationRecord
validates :session_id, presence: true
validates :title, presence: true
scope :completed, -> { where completed: true }
scope :active, -> { where completed: false }
scope :old, -> { where arel_table[:created_at].lt(1.month.ago) }
def active?
!completed?
end
end
app/reflexes/todos_reflex.rb (30 LOC)
class TodosReflex < ApplicationReflex
def create
Todo.create session_id: session.id, title: element[:value]
end
def edit
@edit_id = element.dataset[:id].to_i
end
def cancel_edit
@edit_id = nil
end
def update
Todo.find_by(session_id: session.id.to_s, id: element.dataset[:id])&.update title: element[:value]
end
def toggle
Todo.find_by(session_id: session.id.to_s, id: element.dataset[:id])&.toggle! :completed
end
def destroy
Todo.find_by(session_id: session.id.to_s, id: element.dataset[:id])&.destroy
end
def toggle_all
todos = Todo.where(session_id: session.id.to_s)
todos.update_all completed: todos.active.exists?
end
def destroy_completed
Todo.where(session_id: session.id.to_s, completed: true).destroy_all
end
def filter
session[:todo_filter] = element.dataset[:filter]
end
end