Delay multiple Tasks
As a second step toward executing status updates in-bulk, mark a group
of selected Task
records as “delayed” by marking their new
delayed_at
DATETIME column to “now”.
When a Task
is either “complete” or “delayed”, exclude it from the
“Todo” section of the list.
To submit the shared <form>
element to a different URL, declare the
“Delay” <button>
element with the formaction
attribute set to the delays_path
.
Collapse app/controllers/delays_controller.rb
Expand app/controllers/delays_controller.rb
app/controllers/delays_controller.rb
diff --git a/app/controllers/delays_controller.rb b/app/controllers/delays_controller.rb
new file mode 100644
index 0000000..9a1e183
--- /dev/null
+++ b/app/controllers/delays_controller.rb
@@ -0,0 +1,15 @@
+class DelaysController < ApplicationController
+ def create
+ tasks = Task.where(id: event_params.fetch(:task_ids))
+
+ Task.transaction { tasks.each(&:delay!) }
+
+ redirect_back fallback_location: root_url
+ end
+
+ private
+
+ def event_params
+ params.require(:event).permit(task_ids: [])
+ 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
index 782976b..1a4c8cc 100644
--- a/app/models/task.rb
+++ b/app/models/task.rb
@@ -1,8 +1,13 @@
class Task < ApplicationRecord
scope :completed, -> { where.not(completed_at: nil) }
- scope :todo, -> { where(completed_at: nil) }
+ scope :delayed, -> { where.not(delayed_at: nil) }
+ scope :todo, -> { where(completed_at: nil, delayed_at: nil) }
def complete!
update!(completed_at: Time.current)
end
+
+ def delay!
+ update!(delayed_at: Time.current)
+ end
end
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
index e46a4f7..03191a3 100644
--- a/app/views/tasks/index.html.erb
+++ b/app/views/tasks/index.html.erb
@@ -1,3 +1,4 @@
+<% if tasks.todo.any? %>
<section>
<%= form_with(scope: :event, url: completions_path) do |form| %>
<fieldset>
@@ -16,14 +17,38 @@
</tbody>
<tfoot>
<tr>
- <td><%= form.button %></td>
+ <% with_options scope: [:helpers, :submit, :event] do |action| %>
+ <td>
+ <%= form.button action.translate(:complete) %>
+ </td>
+
+ <td>
+ <%= form.button action.translate(:delay), formaction: delays_path %>
+ </td>
+ <% end %>
</tr>
</tfoot>
</table>
</fieldset>
<% end %>
</section>
+<% end %>
+
+<% if tasks.delayed.any? %>
+<section>
+ <h2><%= translate(".delayed") %></h2>
+
+ <table>
+ <% tasks.delayed.each do |task| %>
+ <tr>
+ <td><%= task.name %></td>
+ </tr>
+ <% end %>
+ </table>
+</section>
+<% end %>
+<% if tasks.completed.any? %>
<section>
<h2><%= translate(".completed") %></h2>
@@ -36,3 +61,4 @@
<% end %>
</table>
</section>
+<% 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 9dbc994..c42c105 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -33,9 +33,11 @@ en:
helpers:
submit:
event:
- submit: "Complete"
+ complete: "Complete"
+ delay: "Delay"
tasks:
index:
completed: "Completed"
+ delayed: "Delayed"
todo: "Tasks"
Collapse config/routes.rb
Expand config/routes.rb
config/routes.rb
diff --git a/config/routes.rb b/config/routes.rb
index 1f3ca28..ddb5c00 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,6 +1,7 @@
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
+ resources :delays, only: :create
root to: "tasks#index"
end
Collapse db/migrate/20200911021405_add_delayed_at_to_tasks.rb
Expand db/migrate/20200911021405_add_delayed_at_to_tasks.rb
db/migrate/20200911021405_add_delayed_at_to_tasks.rb
diff --git a/db/migrate/20200911021405_add_delayed_at_to_tasks.rb b/db/migrate/20200911021405_add_delayed_at_to_tasks.rb
new file mode 100644
index 0000000..b17d826
--- /dev/null
+++ b/db/migrate/20200911021405_add_delayed_at_to_tasks.rb
@@ -0,0 +1,7 @@
+class AddDelayedAtToTasks < ActiveRecord::Migration[6.1]
+ def change
+ change_table :tasks do |t|
+ t.timestamp :delayed_at, index: true
+ 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 659234f..cfacc26 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2020_09_10_233326) do
+ActiveRecord::Schema.define(version: 2020_09_11_021405) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -20,7 +20,9 @@ ActiveRecord::Schema.define(version: 2020_09_10_233326) do
t.datetime "completed_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
+ t.datetime "delayed_at"
t.index ["completed_at"], name: "index_tasks_on_completed_at"
+ t.index ["delayed_at"], name: "index_tasks_on_delayed_at"
end
end
Collapse test/controllers/delays_controller_test.rb
Expand test/controllers/delays_controller_test.rb
test/controllers/delays_controller_test.rb
diff --git a/test/controllers/delays_controller_test.rb b/test/controllers/delays_controller_test.rb
new file mode 100644
index 0000000..6015f81
--- /dev/null
+++ b/test/controllers/delays_controller_test.rb
@@ -0,0 +1,18 @@
+require "test_helper"
+
+class DelaysControllerTest < ActionDispatch::IntegrationTest
+ test "#create marks a group of Tasks' delayed_at to now" do
+ freeze_time do
+ do_the_homework, pass_the_test = tasks(:do_the_homework, :pass_the_test)
+
+ post delays_path, params: {
+ event: {
+ task_ids: [do_the_homework, pass_the_test].map(&:to_param)
+ }
+ }
+
+ assert_equal Time.now, do_the_homework.reload.delayed_at
+ assert_equal Time.now, pass_the_test.reload.delayed_at
+ end
+ end
+end
Collapse test/controllers/tasks_controller_test.rb
Expand test/controllers/tasks_controller_test.rb
test/controllers/tasks_controller_test.rb
diff --git a/test/controllers/tasks_controller_test.rb b/test/controllers/tasks_controller_test.rb
new file mode 100644
index 0000000..158392b
--- /dev/null
+++ b/test/controllers/tasks_controller_test.rb
@@ -0,0 +1,33 @@
+require "test_helper"
+
+class TasksControllerTest < ActionDispatch::IntegrationTest
+ include ActionView::Helpers::TranslationHelper
+
+ test "#index omits the Todo list when there are no ready Task records" do
+ Task.todo.update_all(completed_at: nil)
+
+ get root_path
+
+ assert_no_section :todo
+ end
+
+ test "#index omits the Completed list when there are no delayed Task records" do
+ Task.completed.update_all(completed_at: nil)
+
+ get root_path
+
+ assert_no_section :completed
+ end
+
+ test "#index omits the Delayed list when there are no delayed Task records" do
+ get root_path
+
+ assert_no_section :delayed
+ end
+
+ def assert_no_section(i18n_key)
+ title = translate(i18n_key, scope: [:tasks, :index])
+
+ assert_select "section", count: 0, text: title
+ end
+end
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
index cac4296..602ecc4 100644
--- a/test/models/task_test.rb
+++ b/test/models/task_test.rb
@@ -10,6 +10,16 @@ class TaskTest < ActiveSupport::TestCase
assert_not_includes completed, do_the_homework
end
+ test ".delayed excludes Task records with a nil delayed_at" do
+ read_the_book, do_the_homework = tasks(:read_the_book, :do_the_homework)
+ do_the_homework.delay!
+
+ delayed = Task.delayed
+
+ assert_not_includes delayed, read_the_book
+ assert_includes delayed, 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)
@@ -19,6 +29,16 @@ class TaskTest < ActiveSupport::TestCase
assert_includes todo, do_the_homework
end
+ test ".todo excludes Task records with a delayed_at" do
+ do_the_homework, pass_the_test = tasks(:do_the_homework, :pass_the_test)
+ do_the_homework.delay!
+
+ todo = Task.todo
+
+ assert_not_includes todo, do_the_homework
+ assert_includes todo, pass_the_test
+ end
+
test "#complete! marks a Task's completed_at to now" do
freeze_time do
pass_the_test = tasks(:pass_the_test)
@@ -28,4 +48,14 @@ class TaskTest < ActiveSupport::TestCase
end
end
end
+
+ test "#delay! marks a Task's delayed_at to now" do
+ freeze_time do
+ pass_the_test = tasks(:pass_the_test)
+
+ assert_changes -> { pass_the_test.delayed_at }, from: nil, to: Time.current do
+ pass_the_test.delay!
+ 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
index d5d6110..0f3eaa1 100644
--- a/test/system/tasks_test.rb
+++ b/test/system/tasks_test.rb
@@ -9,25 +9,41 @@ class TasksTest < ApplicationSystemTestCase
visit root_path
check do_the_homework.name
check pass_the_test.name
- click_on submit(:event)
+ click_on submit(:event, :complete)
- within_section :todo do
- assert_no_text do_the_homework.name
- assert_no_text pass_the_test.name
- end
+ assert_no_section :todo
within_section :completed do
assert_text do_the_homework.name
assert_text pass_the_test.name
end
end
+ test "marks multiple Tasks as delayed" 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, :delay)
+
+ assert_no_section :todo
+ within_section :delayed do
+ assert_text do_the_homework.name
+ assert_text pass_the_test.name
+ end
+ end
+
+ def assert_no_section(i18n_key)
+ assert_no_text translate(i18n_key, scope: [:tasks, :index])
+ 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])
+ def submit(i18n_key, action)
+ translate(action, scope: [:helpers, :submit, i18n_key])
end
end