記事について
前回、モデルの作成まで行ったので、Reactで作ったUIとやり取りするためのAPIを用意してみます。
ここで作成するAPIは以下の二つにします。(作成とか削除とかはまた今度。。)
・ユーザ毎のタスク一覧の取得
・タスクの押し付け・・・もとい、タスク所有ユーザの更新
関連する記事
書いているうちに分量がすごくなって記事を分割したので、リンク先をまとめておきます。
その1(環境構築〜モデル作成編)
その2(API作成編)
その3(UI作成編1)
その4(UI作成編2)
その5(react-contenteditable導入編)
おまけ(モデルのテスト編)
ユーザ毎タスク一覧の取得
RailsでAPIを作成する際に作るのはControllerとViewです。
ユーザの一覧を取得したいので、"api/users/user_task_list"のようにアクセスできればなと。
コントローラの生成
ということで、コントローラを生成してもらいます。
# 以下のコマンドで、viewの追加やroute.rbの編集も行ってくれます。
# APIにスタイルはいらないので、--no-stylesheetsをつけてみました。
bundle exec rails g controller API::Users user_task_list --no-stylesheets
コントローラの編集
生成されたコントローラに実際に処理を追加してみます。
class Api::UsersController < ApplicationController
def user_task_list
# ユーザ情報を取得してインスタンス変数に格納しておく。
@users = User.all(); # <= 追加する処理
end
end
jbuilderファイルの作成
React用のAPIなのでデータのやりとりは、JSONで行うのが楽です。
ということで、ユーザデータをJSONで出力するためのテンプレートファイルを追加してあげます。
名前は、app/views/api/users/list.json.jbuilderとなります。
# jbuilderでは好きな形でデータが出力できます。
json.users @users do | user |
json.id(user.id);
json.name(user.name);
json.tasks do
user.tasks.each do | task |
json.set! task.id do
json.id(task.id);
json.title(task.title);
json.description(task.description);
json.due_date(task.due_date.strftime("%Y-%m-%d"));
end
end
end
end
# こんなイメージのjsonデータになります。
# あとで、IDをキーにタスクデータを取得したいので、ちょっと変な形になってます。
# {
# users: [ {
# id: 1
# name: "Not Assigned"
# tasks: {
# "1" : { id: 1, title: "test001", description: ...},
# "3" : { id: 3, title: "test003", description: ...}
# }
# }, {
# id: 2
# name: "Alice"
# tasks: {
# "2" : { id: 2, title: "test002", description: ...}
# }
# }
# ]
# }
テストしてみます。
これで、ユーザ一覧をJSONデータとして出力できるようになったはず。
と言うことで、テストしてみます。
ここでは、以下のようなテストコードをテストコードに追加してみました。
test "should get user_task_list.json" do
# jsonフォーマット指定でGETリクエストを投げます。
get api_users_user_task_list_url(:json)
# 200OKが返ってくるはず。
assert_response :success
# Boby部をJSONデータに変換
json_data = ActiveSupport::JSON.decode(@response.body);
# usersの配列の長さは3のはず(fixtureで3人作っていれば)
assert_equal(3, json_data['users'].length);
end
テスト実行
# うまくいったら喜ぼう。
bundle exec rails test test/controllers/api/users_controller_test.rb
タスクの押し付け
それでは、タスク所有ユーザ切り替え用のAPIを準備していきます。
コントローラの生成
ということで、コントローラの作成です。
内部的にはタスクのユーザIDを切り替えるので、'switch_user'の名前でアクションを追加します。
bundle exec rails g controller API::Tasks switch_user --no-stylesheets
# おや、なんか変だ。。
# route.rbに"get 'tasks/switch_user'"を追加したと言っている。
create app/controllers/api/tasks_controller.rb
route namespace :api do
get 'tasks/switch_user'
end
route.rbの修正
先ほどの結果で、switch_userアクションの呼び出しメソッドが"GET"になってしまったので、route.rbを修正します。(更新ならPUTがいいかな?と。)
# 変更箇所の前後のみ記載しております。
namespace :api do
put 'tasks/switch_user' # <= getからputに変更します。
end
コントローラの編集
それでは、追加されたtasks_controller.rbを編集して、switch_userアクションを実装します。
なお、ここで大事になるのはリクエストデータのもらい方。
今回は、以下のような形でもらうことを想定した実装としています。
{
"switch_info": {
"task_id": 1,
"user_id": 2
}
}
switch_userアクションの内容は、以下のとおり、変更対象のタスクのユーザデータを入れ替えて、更新する処理となります。
後々、エラー処理がしやすいように、エラーメッセージを返すための仕組みも入れてあります。
# タスク押し付け処理
def switch_user
# エラーメッセージを入れるための配列を用意しておきます。
@errors = [];
# 許可済みのパラメータを生成します。(switch_uesr_params()というprivateメソッドを追加しておきました。)
suparam = switch_user_params();
begin
# タスクデータを取得
@task = Task.find(suparam[:task_id]);
# ユーザデータを取得
@user = User.find(suparam[:user_id]);
# タスク所有者を変更
@task.user = @user;
# タスクデータを更新
if ! @task.save then
# 失敗したら、show_errorというテンプレートを使ってエラー情報を返すようにしました。
@errors.push("failed to switch user: task.save() faield");
render :show_error
end
rescue => ex
# 例外発生時もshow_errorというテンプレートを使って例外メッセージを返すようにしました。
@errors.push("failed to switch user: an exception occurred.");
@errors.push(ex.to_s);
render :show_error
end
end
private
# task_idとuser_idだけを許可済みパラメータとして返します。
def switch_user_params()
return params.require(:switch_info).permit(:task_id, :user_id);
end
jbuilderファイルの準備
ここでは、処理結果をJSON形式で返すため、以下2つパターンでjbuilderファイルを用意します。
エラーがない場合は、更新後のタスクデータを。 => switch_user.json.jbuilder
エラーがあった場合は、エラーメッセージを。 => show_error.json.jbuilder
まず、更新成功時に返すswitch_user.json.jbuilder
# task { id: 1, title: "hoge" ...}というような形で返します。
json.task do
json.id(@task.id);
json.title(@task.title);
json.description(@task.description);
json.due_date(@task.due_date.strftime("%Y-%m-%d %H:%M:%S"));
json.user_id(@task.user.id);
end
次にエラー情報を返すshow_error.json.jbuilder
# @errorsと言う名前の配列にエラー情報を入れてあるので、その中身を出力してあげます。
json.errors @errors do | msg |
json.message(msg);
end
テストしてみます。
これで、適切なパラメータでリクエストを投げれば、タスクの担当者が切り替わるはずです。
ので、やっぱりテストしてみます。
テスト内容はこちら!
・ユーザの切り替えがうまく行くこと。
・失敗したらエラー情報がもらえること。
# 正常パターン
test "should success to switch user" do
notassigned = users(:notassigned);
alice = users(:alice);
task1 = notassigned.tasks[0];
# aliceのタスクが増えるはず。
assert_difference 'alice.tasks.count' do
# リクエスト投げてみる。
put(api_tasks_switch_user_url(:json), params: { switch_info: { task_id: task1.id, user_id: alice.id } });
# 200OKのはず
assert_response :success
# 念のためリロード
alice.tasks.reload();
notassigned.tasks.reload();
end
# レスポンスから、JSONデータを取得
json_data = ActiveSupport::JSON.decode(@response.body);
# タスクデータのユーザIDがaliceのIDになるはず。
assert_equal(alice.id, json_data['task']['user_id']);
# notassignedさんのタスクが0個になるはず。
assert_equal(0, notassigned.tasks.count);
end
# 存在しないユーザに切り替えるとエラーになるはず。
test "should fail to swith user if user is not exist" do
# 新しいユーザを作る。が、保存しない。
new_user = User.new(name: "Bab");
# タスクデータのインスタンスを取得
task2 = tasks(:task2);
# 保存してないユーザに割り当ててみよう。
put(api_tasks_switch_user_url(:json), params: { switch_info: { taks_id: task2.id, user_id: new_user.id } });
# 一応200OK。
assert_response :success
json_data = ActiveSupport::JSON.decode(@response.body);
# エラー情報が返されるはず。
assert(json_data['errors'].count > 0);
end
テストOKであれば、一応APIは出来上がりと言うことにしよう!!