Mark Tasks as completed
Visit the application’s root, then select the Task
records to act on.
As an initial feature, marking a Task
as “complete” submits an “Event”
to the CompletionsController
so that it can mark the selected Task
records’ completed_at
timestamp to “now”.
Testing
Without the configuration changes, the error is unclear:
Error:
TasksTest#test_marks_multiple_Tasks_as_complete:
Capybara::ElementNotFound: Unable to find link or button "<span class=\"translation_missing\" title=\"translation missing: en.helpers.submit.completion.create\">Create</span>"
test/system/tasks_test.rb:12:in `block in <class:TasksTest>'
rails test test/system/tasks_test.rb:6
After configuring raise_on_missing_translations = true
, the error is
more clear:
Error:
TasksTest#test_marks_multiple_Tasks_as_complete:
I18n::MissingTranslationData: translation missing: en.helpers.submit.completion.create
test/system/tasks_test.rb:16:in `submit'
test/system/tasks_test.rb:12:in `block in <class:TasksTest>'
rails test test/system/tasks_test.rb:6
Collapse app/controllers/completions_controller.rb
Expand app/controllers/completions_controller.rb
app/controllers/completions_controller.rb
diff --git a/app/controllers/completions_controller.rb b/app/controllers/completions_controller.rb
new file mode 100644
index 0000000..d7078ab
--- /dev/null
+++ b/app/controllers/completions_controller.rb
@@ -0,0 +1,15 @@
+class CompletionsController < ApplicationController
+ def create
+ tasks = Task.where(id: event_params.fetch(:task_ids))
+
+ Task.transaction { tasks.each(&:complete!) }
+
+ redirect_back fallback_location: root_url
+ end
+
+ private
+
+ def event_params
+ params.require(:event).permit(task_ids: [])
+ end
+end
Collapse app/controllers/tasks_controller.rb
Expand app/controllers/tasks_controller.rb
app/controllers/tasks_controller.rb
diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb
new file mode 100644
index 0000000..7ca1389
--- /dev/null
+++ b/app/controllers/tasks_controller.rb
@@ -0,0 +1,7 @@
+class TasksController < ApplicationController
+ def index
+ tasks = Task.all
+
+ render locals: {tasks: tasks}
+ end
+end
Collapse app/models/task.rb
Expand app/models/task.rb
app/models/task.rb
diff --git a/app/models/task.rb b/app/models/task.rb
new file mode 100644
index 0000000..782976b
--- /dev/null
+++ b/app/models/task.rb
@@ -0,0 +1,8 @@
+class Task < ApplicationRecord
+ scope :completed, -> { where.not(completed_at: nil) }
+ scope :todo, -> { where(completed_at: nil) }
+
+ def complete!
+ update!(completed_at: Time.current)
+ end
+end
Collapse app/views/layouts/application.html.erb
Expand app/views/layouts/application.html.erb
app/views/layouts/application.html.erb
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 0f01585..c0f76fd 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
- <title>A11yTodos</title>
+ <title>A11y To-dos</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
Collapse app/views/tasks/index.html.erb
Expand app/views/tasks/index.html.erb
app/views/tasks/index.html.erb
diff --git a/app/views/tasks/index.html.erb b/app/views/tasks/index.html.erb
new file mode 100644
index 0000000..e46a4f7
--- /dev/null
+++ b/app/views/tasks/index.html.erb
@@ -0,0 +1,38 @@
+<section>
+ <%= form_with(scope: :event, url: completions_path) do |form| %>
+ <fieldset>
+ <legend>
+ <h2><%= translate(".todo") %></h2>
+ </legend>
+
+ <table>
+ <tbody>
+ <%= form.collection_check_boxes :task_ids, tasks.todo, :id, :name do |builder| %>
+ <tr>
+ <td><%= builder.check_box %></td>
+ <td><%= builder.label %></td>
+ </tr>
+ <% end %>
+ </tbody>
+ <tfoot>
+ <tr>
+ <td><%= form.button %></td>
+ </tr>
+ </tfoot>
+ </table>
+ </fieldset>
+ <% end %>
+</section>
+
+<section>
+ <h2><%= translate(".completed") %></h2>
+
+ <table>
+ <% tasks.completed.each do |task| %>
+ <tr>
+ <td>✓</td>
+ <td><%= task.name %></td>
+ </tr>
+ <% end %>
+ </table>
+</section>
Collapse bin/setup
Expand bin/setup
bin/setup
diff --git a/bin/setup b/bin/setup
index 5853b5e..853d880 100755
--- a/bin/setup
+++ b/bin/setup
@@ -26,7 +26,7 @@ FileUtils.chdir APP_ROOT do
# end
puts "\n== Preparing database =="
- system! 'bin/rails db:prepare'
+ system! 'bin/rails db:prepare db:fixtures:load'
puts "\n== Removing old logs and tempfiles =="
system! 'bin/rails log:clear tmp:clear'
Collapse config/environments/development.rb
Expand config/environments/development.rb
config/environments/development.rb
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 66df51f..cebe106 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -54,7 +54,7 @@ Rails.application.configure do
config.assets.quiet = true
# Raises error for missing translations.
- # config.action_view.raise_on_missing_translations = true
+ config.i18n.raise_on_missing_translations = true
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
Collapse config/environments/test.rb
Expand config/environments/test.rb
config/environments/test.rb
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 0cb2424..fa490b6 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -45,5 +45,5 @@ Rails.application.configure do
config.active_support.deprecation = :stderr
# Raises error for missing translations.
- # config.action_view.raise_on_missing_translations = true
+ config.i18n.raise_on_missing_translations = true
end
Collapse config/locales/en.yml
Expand config/locales/en.yml
config/locales/en.yml
diff --git a/config/locales/en.yml b/config/locales/en.yml
index cf9b342..9dbc994 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -30,4 +30,12 @@
# available at https://guides.rubyonrails.org/i18n.html.
en:
- hello: "Hello world"
+ helpers:
+ submit:
+ event:
+ submit: "Complete"
+
+ tasks:
+ index:
+ completed: "Completed"
+ todo: "Tasks"
Collapse config/routes.rb
Expand config/routes.rb
config/routes.rb
diff --git a/config/routes.rb b/config/routes.rb
index c06383a..1f3ca28 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,3 +1,6 @@
Rails.application.routes.draw do
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
+ resources :completions, only: :create
+
+ root to: "tasks#index"
end
Collapse db/migrate/20200910233326_create_tasks.rb
Expand db/migrate/20200910233326_create_tasks.rb
db/migrate/20200910233326_create_tasks.rb
diff --git a/db/migrate/20200910233326_create_tasks.rb b/db/migrate/20200910233326_create_tasks.rb
new file mode 100644
index 0000000..0720bbb
--- /dev/null
+++ b/db/migrate/20200910233326_create_tasks.rb
@@ -0,0 +1,10 @@
+class CreateTasks < ActiveRecord::Migration[6.0]
+ def change
+ create_table :tasks do |t|
+ t.string :name, null: false
+ t.timestamp :completed_at, index: true
+
+ t.timestamps
+ end
+ end
+end
Collapse db/schema.rb
Expand db/schema.rb
db/schema.rb
diff --git a/db/schema.rb b/db/schema.rb
index 4603022..659234f 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,9 +10,17 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 0) do
+ActiveRecord::Schema.define(version: 2020_09_10_233326) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
+ create_table "tasks", force: :cascade do |t|
+ t.string "name", null: false
+ t.datetime "completed_at"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["completed_at"], name: "index_tasks_on_completed_at"
+ end
+
end
Collapse test/controllers/completions_controller_test.rb
Expand test/controllers/completions_controller_test.rb
test/controllers/completions_controller_test.rb
diff --git a/test/controllers/completions_controller_test.rb b/test/controllers/completions_controller_test.rb
new file mode 100644
index 0000000..fc6d356
--- /dev/null
+++ b/test/controllers/completions_controller_test.rb
@@ -0,0 +1,18 @@
+require "test_helper"
+
+class CompletionsControllerTest < ActionDispatch::IntegrationTest
+ test "#create marks a group of Tasks' completed_at to now" do
+ freeze_time do
+ do_the_homework, pass_the_test = tasks(:do_the_homework, :pass_the_test)
+
+ post completions_path, params: {
+ event: {
+ task_ids: [do_the_homework, pass_the_test].map(&:to_param)
+ }
+ }
+
+ assert_equal Time.now, do_the_homework.reload.completed_at
+ assert_equal Time.now, pass_the_test.reload.completed_at
+ end
+ end
+end
Collapse test/fixtures/tasks.yml
Expand test/fixtures/tasks.yml
test/fixtures/tasks.yml
diff --git a/test/fixtures/tasks.yml b/test/fixtures/tasks.yml
new file mode 100644
index 0000000..d4f6841
--- /dev/null
+++ b/test/fixtures/tasks.yml
@@ -0,0 +1,9 @@
+read_the_book:
+ name: "Read the book"
+ completed_at: <%= 1.week.ago %>
+
+do_the_homework:
+ name: "Do the homework"
+
+pass_the_test:
+ name: "Pass the test"
Collapse test/models/task_test.rb
Expand test/models/task_test.rb
test/models/task_test.rb
diff --git a/test/models/task_test.rb b/test/models/task_test.rb
new file mode 100644
index 0000000..cac4296
--- /dev/null
+++ b/test/models/task_test.rb
@@ -0,0 +1,31 @@
+require "test_helper"
+
+class TaskTest < ActiveSupport::TestCase
+ test ".completed excludes Task records with a nil completed_at" do
+ read_the_book, do_the_homework = tasks(:read_the_book, :do_the_homework)
+
+ completed = Task.completed
+
+ assert_includes completed, read_the_book
+ assert_not_includes completed, do_the_homework
+ end
+
+ test ".todo excludes Task records with a completed_at" do
+ read_the_book, do_the_homework = tasks(:read_the_book, :do_the_homework)
+
+ todo = Task.todo
+
+ assert_not_includes todo, read_the_book
+ assert_includes todo, do_the_homework
+ end
+
+ test "#complete! marks a Task's completed_at to now" do
+ freeze_time do
+ pass_the_test = tasks(:pass_the_test)
+
+ assert_changes -> { pass_the_test.completed_at }, from: nil, to: Time.current do
+ pass_the_test.complete!
+ end
+ end
+ end
+end
Collapse test/system/tasks_test.rb
Expand test/system/tasks_test.rb
test/system/tasks_test.rb
diff --git a/test/system/tasks_test.rb b/test/system/tasks_test.rb
new file mode 100644
index 0000000..d5d6110
--- /dev/null
+++ b/test/system/tasks_test.rb
@@ -0,0 +1,33 @@
+require "application_system_test_case"
+
+class TasksTest < ApplicationSystemTestCase
+ include ActionView::Helpers::TranslationHelper
+
+ test "marks multiple Tasks as complete" do
+ do_the_homework, pass_the_test = tasks(:do_the_homework, :pass_the_test)
+
+ visit root_path
+ check do_the_homework.name
+ check pass_the_test.name
+ click_on submit(:event)
+
+ within_section :todo do
+ assert_no_text do_the_homework.name
+ assert_no_text pass_the_test.name
+ end
+ within_section :completed do
+ assert_text do_the_homework.name
+ assert_text pass_the_test.name
+ end
+ end
+
+ def within_section(i18n_key, &block)
+ title = translate(i18n_key, scope: [:tasks, :index])
+
+ within("section", text: title, &block)
+ end
+
+ def submit(i18n_key)
+ translate(:submit, scope: [:helpers, :submit, i18n_key])
+ end
+end