本記事の内容
本記事は、こちらの記事の続編です。
その記事で実装した内容をもとに、実装していきますので、まだご覧になっていない方は、ぜひご覧ください。
以下の内容の実装方法だけ知りたいという方は、このままご覧ください。
本記事での実装内容
① 実行時間の情報を持ったタスクの作成機能
② 当日のタスクのみ表示する機能
③ 全ての実行日の情報を持ったカレンダー作成機能
④ どのペットに対するタスクか表示する機能
⑤ タスクを実行する人間を表示する機能
⑥ 実行時間に自動メール送信する機能
⑦ タスク完了時にメール送信機能
では実装していきましょう。
実行時間の情報を持ったタスクの作成機能
タスクの実行時間がindexページに表示されるタスクアプリケーションを作成します。
手順は2つです。!
- CRUD機能を持ったアプリケーションの作成
- 時間を日本時間に変更
順に実装していきましょう。
CRUD機能を持ったアプリケーションの作成
Scaffoldというジェネレータを利用します。
Scaffold機能を使うことで、CRUD機能を持ったアプリケーションを自動で生成できます。
タスクアプリケーションに関して、今回作成するカラムは下記の写真の3つです。
外部キーの設定に関しては、後述します。
以下のコマンドを実行してください。
$ rails g scaffold Task title:string content:text start_time:datetime
生成が完了したらマイグレーションを実行します。
$ rails db:migrate
これでタスクアプリケーションの作成は完了です。
下記の状態になっていれば成功です。
時間を日本時間に変更
今のままでは時間がUTC時間になっていますので、日本時間に変更します。
また、今回必要な情報はタスクを実行する時間だけですので、時間のみを表示するようにします。
日本時間への変更
module アプリ名
class Application < Rails::Application
config.time_zone = 'Asia/Tokyo' #追加部分
end
end
rails sでサーバーを立ち上げ、確認します。
これで表示される時間は日本時間になりました。
表記を時間情報のみにする
start_timeの部分のコードはデフォルトでは以下のようになっています。
<td><%= task.start_time %></td>
この状態だと全ての情報を取り出してしまいます。
表示するものを選択したい場合は、.strftimeメソッドを使用します。
<td><%= task.start_time.strftime("%Y-%m-%d %H:%M:%S") %></td>
%Yは年
%mは月
%dは日
%Hは時
%Mは分
%Sは秒
をそれぞれ表しています。
例えば、何時何分かの情報だけ取り出したい場合は
<td><%= task.start_time.strftime("%H:%M") %></td>
時間と分数だけが表示されていますね。
これで完了です。
当日のタスクのみ表示する機能
作成したタスクアプリケーションのindexファイルには、当日のみではなく、全ての日付のタスクが表示されています。
当日のタスクのみ表示するよう実装します。
手順は2つです。
- モデルにメソッドを定義
- 定義したメソッドを使用
モデルにメソッドを定義
モデルに、実行日が当日のもののみ、配列から出力するメソッドを定義します。
#メソッド名を定義する
def self.today_tasks
#配列を用意
today_tasks = []
#全てのタスクの情報が入っているタスクの配列から、1つずつ取り出す
Task.all.each do |task|
#設定したタスクの日付と、当日の日付が等しい場合
if task.start_time.strftime("%Y-%m-%d") == Date.today.strftime("%Y-%m-%d")
#タスクを先ほど用意した配列に入れていく
today_tasks << task
end
end
#出力する値を示す。(=戻り値)
today_tasks
end
定義したメソッドを使用
task.controller.rbへ移動し、indexメソッドに、定義したメソッドを使用します。
def index
@tasks = Task.all
end
def index
@tasks = Task.today_tasks
end
これで、実行日が当日のもののみ、インデックスページに表示されるようになりました。
③実行日の情報を持ったカレンダー作成機能
現在タスクのインデックスファイルでは、当日のタスクのみ見ることができる状態になっています。
しかし、未来に実行するタスクも見ることができるようにしたいです。
そこでカレンダーを作成し、そこに今後のタスクを表示できるようにします。
手順は2つです。
- カレンダーの作成
- カレンダーにタスク情報を反映
順に実装していきましょう。
カレンダーの作成
カレンダーの作成方法にも手順は2つあります。
- simple calendarのインストール
- インデックスページへの反映
simple calendarのインストール
Gemで、カレンダーを簡単に導入してくれます。
以下のコマンドをGemfileに記載してください。
gem "simple_calendar"
ターミナルでbundle installを入力します。
bundle install
インストールを終えたら、application.cssファイルにデフォルトのスタイルシートを読み込みます。
*= require simple_calendar
これでGemのインストールは完了です。
インデックスページへの反映
インデックスページに導入したカレンダーを反映させます。
今回は1ヶ月分のカレンダーを表示させたいので、month_calendarメソッドを使用しますが、1ヶ月以外にも週単位で表示させる事もできたりします。
以下のコードをインデックスファイルに記載してください。
<%= month_calendar do |date| %>
<%= date %>
<% end %>
これで完了です。
めちゃめちゃ簡単ですよね?笑
では確認してみましょう。
カレンダーが表示されていますね。
これでカレンダーの作成は完了です。
カレンダーにタスク情報を反映
現時点では、1ヶ月単位のカレンダーが表示されているだけです。
ここに、現在と未来のタスクが反映されるように設定します。
- インデックスファイルの変更
- 新しいインスタンス係数の作成
順に実装していきます。
インデックスファイルの変更
インデックスファイルに以下を追記してください。
#タスクのインスタンス係数から、dateとタスクを取り出す
<%= month_calendar events: @tasks do |date, tasks| %>
#日付を表示
<%= date %>
#改行
<div>
#タスクを一つ一つ取り出す
<% tasks.each do |task| %>
#タスクのタイトルのみ表示するようにする。リンクからshowに飛べるようにしておく。
<%= link_to task.title, task %>
<% end %>
</div>
これで、カレンダーにタスクの情報が反映されるようになりました。
新しいインスタンス係数の作成
現時点では、当日のタスクしか表示されません。
当日のみでなく、未来のタスクも表示されるようにしましょう。
全てのタスクの情報を持ったインスタンス係数をtask_controller.rbに作成します。
def index
@tasks = Task.today_tasks
@calendars = Task.all
end
インデックスファイルのインスタンス係数を、新しく作成したものに変更。
#タスクのインスタンス係数から、dateとタスクを取り出す
<%= month_calendar events: @calendars do |date, tasks| %>
#日付を表示
<%= date %>
#改行
<div>
#タスクを一つ一つ取り出す
<% tasks.each do |task| %>
#タスクのタイトルのみ表示するようにする。リンクからshowに飛べるようにしておく。
<%= link_to task.title, task %>
<% end %>
</div>
これで、登録した全ての日付のタスクがカレンダーに表示されるようになりました。
④ どのペットに対するタスクか表示する機能
ペットとタスクの紐付けを実装していきましょう。
手順は以下の3つです。
- インデックスファイルの変更
- 外部キーの設定
- アソシエーションの定義
インデックスファイルの変更
インデックスファイルに以下の情報を追記してください。
<tbody>
<% @tasks.each do |task| %>
<tr>
<td><%= link_to task.title , task %></td>
<td><%= task.pet.name%></td>
</tr>
<% end %>
</tbody>
今までのままでは、エラーが起きます。
petのメソッドが定義されていないためです。
正常に機能するためには、外部キーの設定、アソシエーションの定義が必要です。
外部キーの設定
外部キーの設定は、たった一つのコマンドを実行すれば完了します。
rails g migration AddPetRefToTasks pet:references
アソシエーションの定義
それぞれのモデルに以下を追記してください。
class Pet < ApplicationRecord
belongs_to :family
has_many :tasks
end
class Task < ApplicationRecord
belongs_to :pet
end
以上にアソシエーションの定義は完了です。
⑤ タスクを実行する人間を表示する機能
タスク作成時に、タスクを実行できる人間を指定できるようにします。
さらに、インデックスファイルでその情報を一覧できるようにします。
- フォームファイルの変更
- コントローラーの変更
- インデックスファイルの変更
フォームファイルの変更
以下のコードを追記します。
<div class="field">
<%= form.label :実行人 %>
<%= form.collection_select(:user_id,@users,:id,:name)%>
</div>
データの保存先としてuser_idのカラムと、usersのインスタンス係数を作成してあげます。
コントローラーの変更
やることは2つ!
- usersのインスタンス係数の作成
- ストロングパラメーターの作成
usersのインスタンス係数の作成
createメソッドに以下を追記します。
def create
@task = Task.new(task_params)
#自分が所属している団体のユーザーすべてを@usersに入れる。
@users = current_user.families.first.users #追記
respond_to do |format|
if @task.save
format.html { redirect_to task_url(@task), notice: "Task was successfully created." }
format.json { render :show, status: :created, location: @task }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @task.errors, status: :unprocessable_entity }
end
end
end
ストロングパラメーターの作成
ストロングパラメーターを許可し、DBで保存できるようにしてあげます。
def create
def task_params
params.require(:task).permit(:title, :content, :start_time, :pet_id, :user_id)
end
インデックスファイルの変更
以下を追記します。
<tbody>
<% @tasks.each do |task| %>
<tr>
#追記
<td><%= task.user.name%></td>
</tr>
<% end %>
</tbody>
アソシエーションの定義を行ったことで、.userでユーザーテーブルの情報をとってくることができるようになりました。
これで、タスクを実行する人間を表示する機能の実装は完了です。
⑥ 実行時間に自動メール送信する機能
タスクの実行時間になったら自動的にタスクを送信する機能を実装したいです。
しかしその前にまず、メールの送信機能を実装します。
メール送信機能の実装
手順は3つです。
- メールの送信設定
- メーラーの作成
- 全体メーラーの更新
メールの送信設定
gmailからの送信機能を実装します。
実装手順は2つ
- Gmail側の設定
- Rails側の設定
順に実装していきましょう。
Gmail側の設定
詳しく解説してある記事を見つけましたので、こちらをご覧ください。
Rails側の設定
development.rbに以下のコードを記載してください。
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: 'smtp.gmail.com',
port: 587,
domain: 'gmail.com',
user_name: '設定したgmailアドレス',
password: '2段階認証したアカウントで発行したアプリパスワード',
authentication: 'plain',
enable_starttls_auto: true
}
end
これで設定は完了です。
メーラーの作成
メーラーとは、メール送信におけるコントローラー的役割を果たすものです。
以下のコマンドで作成します。
$ rails g mailer TodoMailer
Mailer前は好きな名前で設定してください。
このコマンドを実行すると、app/mailer以下に次の2つのメーラーが作成されます。
- application.rb
- Todo_mailer.rb
前者が全体のメーラー、後者が個別のメーラーです。
次の項目では、全体のメーラーの設定を行います。
全体メーラーの更新
全体メーラーとは、送信元の設定をするファイルです。
開くと以下のような記述になっているはずです。
class ApplicationMailer < ActionMailer::Base
default from: "from@example.com"
layout 'mailer'
end
以下のように変更してください。
class ApplicationMailer < ActionMailer::Base
default from: '送信元に設定したいメールアドレスを記載'
layout 'mailer'
end
これで、メールの送信機能の実装は完了です。
実行時間に自動メール送信する機能
実装手順は以下の3つです。
- wheneverというgemのインストール
- 読み込みメソッドの設定
- メソッドの実行処理
Wheneverというgemのインストール
インストールすることで、自動的に一分おきに読み込み、設定したメソッドを実行してくれます。
gem 'whenever', require: false
追記したら、bundle install を実行します。
読み込みメソッドの設定
- schedule.rbの生成
- 読み込みタイミングの設定
schedule.rbの生成
以下のコマンドを実行することで生成されます。
$ bundle exec wheneverize .
読み込みのタイミングの設定
生成したschedule.rbに以下の記述をし、読み込みのタイミングの設定をしてあげます。
#railsアプリフォルダ内の./log/cron.logにログが出力されるようにする記述
set :output, "#{Rails.root}/log/cron.log"
#railsアプリ内の実行環境を変数に代入している
require File.expand_path(File.dirname(__FILE__) + '/environment')
set :path_env, ENV['PATH']
rails_env = ENV['RAILS_ENV'] || :development
set :environment, rails_env
#railsアプリ上の実行環境と同条件になるよう規定している
#:outputで事前にsetしたパスにログの出力先を設定
job_type :runner, "cd :path && PATH=':path_env' bin/rails runner -e :environment ':task' :output"
#読み込むタイミングを1分に設定
every 1.minute do
runner "Task.remind"
end
これで、設定したタイミングにメソッドを実行してくれます。
メソッドの実行処理
1分おきに読み込み、設定した時間に処理を実行してくれるメソッドは作成しましたが、タイミングの指定と、送信のメソッドがまだできていません。
そこで、以下の機能を実装します。
- タスクの実行時間になったらメールを送信してくれるメソッドの定義
- 送信先の定義
- メール本文の定義
順に実装していきましょう。
タスクの実行時間になったらメールを送信してくれるメソッドの定義
task.rbに以下のコードを記載します。
def self.remind
#mailer(boolean)がfalseのものを取得
tasks = Task.where(mailer:false)
tasks.each do |task|
#タスクの実行時間を超えたらメールを送る処理
if task.start_time < Time.now
TodoMailer.send_at_the_time(task).deliver
#送信したらbooleanをtrueに変更
task.mailer = true
task.save
end
end
end
boolean(mailer)のカラムの追加が未実装なため、下記のコマンドを実装し、追加します。
$ rails generate migration AddColumnTasks
作成されたマイグレーションファイルに以下を記載してください。
class AddColumnTasks < ActiveRecord::Migration[6.1]
def change
add_column :tasks, :mailer, :boolean, default: false, null: false
end
end
rails db:migrateを実行しましょう。
これで、タスクの実行時間になったらメールが送信されるメソッドの定義は完了です。
送信先の定義
送信先の定義はtodo_mailer.rbで行います。
以下のコードを記載してください。
class TodoMailer < ApplicationMailer
# task.rbで定義した引数を受け取る
def send_at_the_time(task)
@task = task
@user = task.user
mail to: @user.email, subject: 'タスクの時間です。'
end
end
タスクを実行させたいユーザー宛にメールが送信されます。
メール本文の定義
以下のコードをsend_at_the_time.html.erbに記載してください。
<!doctype html>
<html lang="ja">
<head>
<meta content="text/html; charset=UTF-8" />
</head>
<body>
<h2><%= @user.name %> 様</h2>
<hr />
<p>
こんにちは! <%= @user.name %>さん!
<%= @task.title %>を実行してください。<br>
<%= @task.content %>
</p>
</body>
</html>
これで、タスク実行時間に、実行させたいユーザーにメールが自動的に送信されます。
⑦ タスク完了時にメール送信機能
手順は以下の4つです。
- メーラーの作成
- メソッドの定義
- 送り先の定義
- メール本文の定義
順に実装していきましょう!
メーラーの作成
別のメーラーメソッドを実行したい場合、別のメーラーを生成します。
以下のコマンドを実行してください。
$ rails g mailer DoneMailer
create app/mailers/done_mailer.rb
invoke erb
create app/views/done_mailer
invoke test_unit
create test/mailers/done_mailer_test.rb
create test/mailers/previews/done_mailer_preview.rb
メソッドの定義
今回はタスクの完了ボタンを押したら、メールが送信される機能を実装します。
手順は4つです。
- ルーティングの定義
- コントローラーの追記
- ボタンの作成
- 完了タスク一覧機能の実装
順に実装していきましょう。
ルーティングの定義
# 追記
post '/tasks/:id/done' => 'tasks#done', as: 'done'
コントローラーの追記
Doneボタンを押したら、メールが送信されるメソッドを追記します。
def done
# タスクがアップデートされたらbooleanがtrueになる。
if @task.update(is_done: true )
#メール送信メソッド
DoneMailer.send_when_done(@task).deliver
redirect_to tasks_path
else
render 'index'
end
end
これで、ボタンを押したら完了メールが送信されるようになりました。
is_doneのカラムの追加は未実装なので、先ほどの要領で、実装してください。
ボタンの作成
まだ肝心のボタンが実装できていないので、実装します。
インデックスファイルに記載します。
def done
<tr>
<td><%= link_to task.title , task %></td>
<td><%= task.start_time.strftime("%H:%M") %></td>
<td><%= task.user.name%></td>
<td><%= task.pet.name%></td>
#追記
<td><%= link_to 'Done', done_path(task.id), method: :post %></td>
</tr>
完了タスク一覧機能の実装(変更前)
完了したタスクと、未完了のタスクを一覧で見れるようにします。
インデックスファイルを以下のように変更してください。
<h2>タスク一覧</h2>
<%= link_to 'New Task', new_task_path %>
<hr />
<br>
<table>
<thead>
<tr>
<th>Title</th>
<th>Time</th>
<th>User</th>
<th>Pet</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
#変更ポイント
<% @tasks.each do |task| %>
<tr>
<td><%= link_to task.title , task %></td>
<td><%= task.start_time.strftime("%H:%M") %></td>
<td><%= task.user.name%></td>
<td><%= task.pet.name%></td>
<td><%= link_to 'Done', done_path(task.id), method: :post %></td>
</tr>
<% end %>
#変更ポイント
</tbody>
</table>
<h2>完了</h2>
<br>
<hr />
<table>
<thead>
<br>
<tr>
<th>Title</th>
<th>User</th>
<th>Pet</th>
</tr>
</thead>
<tbody>
#変更ポイント
<% @tasks.each do |task| %>
<tr>
<td><%= link_to task.title , task %></td>
<td><%= task.user.name%></td>
<td><%= task.pet.name %></td>
</tr>
<% end %>
#変更ポイント
</tbody>
</table>
<h2>タスク一覧</h2>
<%= link_to 'New Task', new_task_path %>
<hr />
<br>
<table>
<thead>
<tr>
<th>Title</th>
<th>Time</th>
<th>User</th>
<th>Pet</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
#ここから追記
<% @tasks.filter{|task| !task.is_done? }.each do |task| %>
<tr>
<td><%= link_to task.title , task %></td>
<td><%= task.start_time.strftime("%H:%M") %></td>
<td><%= task.user.name%></td>
<td><%= task.pet.name%></td>
<td><%= link_to 'Done', done_path(task.id), method: :post %></td>
</tr>
<% end %>
#ここまで追記
</tbody>
</table>
<h2>完了</h2>
<br>
<hr />
<table>
<thead>
<br>
<tr>
<th>Title</th>
<th>User</th>
<th>Pet</th>
</tr>
</thead>
<tbody>
#ここから追記
<% @tasks.filter{|task| task.is_done? }.each do |task| %>
<tr>
<td><%= link_to task.title , task %></td>
<td><%= task.user.name%></td>
<td><%= task.pet.name %></td>
</tr>
<% end %>
#ここまで追記
</tbody>
</table>
ちなみに、コントローラーでインスタンス係数を作成するやり方でも大丈夫です!
これで、タスクが完了したらメールを送信するメソッドの定義はできました。
送り先の定義
done_mailer.rbに以下を追記してください。
class DoneMailer < ApplicationMailer
#コントローラーから引数を受け取る。
def send_when_done(task)
@task = task
mail to: task.user.email, subject: 'タスクの時間です。'
end
end
メール本文の定義
最後にメール本文の定義をしましょう!
<!doctype html>
<html lang="ja">
<head>
<meta content="text/html; charset=UTF-8" />
</head>
<body>
<h2><%= @task.user.name %> 様</h2>
<hr />
<p>
こんにちは! <%= @task.user.name %>さん!<br>
<%= @task.title %>が無事実行されました!
</p>
</body>
</html>
これで、タスクの完了ボタンを押すと、メールが送信される処理の実装完了です。
終わりに
これでタスク機能に関する実装は完了です!
お疲れ様でした。
part2ではログイン機能実装編(devise)について実装します。
興味がある方はご覧ください。