はじめに
私は4月に新卒入社したバックエンドエンジニアで、現在railsでの開発を行っています。dryrunなrakeタスクを最近書く機会があったので、簡単な例を交えつつ紹介したいと思います。
Rakeタスクとは?
Rakeは設定したタスクをコマンドラインで実行できるもので、そのタスク自体のことを
Railsを触ったことがある人なら使用したことがあると思いますが、rails db:migrate
もrakeタスクです。ちなみrake db:migrate
でも同じ動作になりますが、rails
を使用するとよいかと思います。
準備
今回は単純なFruitsテーブルに果物のデータを追加・削除するタスクを作成してみようと思います。
そのための準備として以下のコマンドを実行します。
rails g model Fruit name:string price:integer
上記のコマンドを実行することでFruitsモデルとmigrationファイルが生成されるので、次にrails db:migrate
しましょう。
すると以下のようなFruitsテーブルができます。
Column | Data Type | Options |
---|---|---|
id | bigint | not null, primary key |
name | string(255) | |
price | integer | |
created_at | datetime | not null |
updated_at | datetime | not null |
これで準備は完了です
Rakeタスクの作成
ここからはRakeタスクの作成に入ります。学習のために、果物を追加するタスクはdry runではないRakeタスクに、削除するタスクをdry runで実行できるようにしましょう。まず、Rakeファイルを作成するために以下のコマンドを実行します。
rails g add_fruit
上記コマンドを実行するとlib/tasks/add_fruit.rake
が作成されます。
次に以下のようにadd_fruit.rake
を記述していきます
namespace :add_fruits do
desc '果物テーブルにりんごとみかんとぶどうを追加する'
task add: :environment do
logger = ActiveSupport::Logger.new($stdout)
fruits_to_add = [
{ name: 'りんご', price: 100 },
{ name: 'みかん', price: 50 },
{ name: 'ぶどう', price: 150 }
]
fruits_to_add.each do |fruit_data|
fruit = Fruit.new(fruit_data)
if fruit.save
logger.info "追加完了:#{fruit.name}"
else
logger.error "追加エラー:#{fruit.errors.full_messages.join(', ')}"
end
end
logger.info '追加が完了しました。'
end
end
ここまで書いて保存したらrails -T
してみましょう。タスク一覧の中にrails add_fruits:add_fruits
があればOKです。このタスクを実行すると、メッセージが表示され、果物がFruitsテーブルに追加されます。
dryrunな書き方
次にdry runな書き方で果物を削除するrakeタスクを作成していきましょう。rails g delete_fruit
を実行して削除用のrakeタスクを作成します。
次に、以下のようにdelete_fruitを記述します
namespace :delete_fruits do
desc '果物テーブルからりんごとみかんを削除する'
task :delete_fruits, [:status] => :environment do |_task, args|
logger = ActiveSupport::Logger.new($stdout)
logger.info '============================'
puts 'ドライランで実行' unless args[:status] == 'exec'
fruits_to_delete = ['りんご', 'みかん']
deleted_fruits = Fruit.where(name: fruits_to_delete)
print "#{fruits_to_delete.join('と')}を削除します。本当によろしいですか? (Y/n): "
confirmation = $stdin.gets.chomp.upcase
if confirmation == 'Y'
begin
ActiveRecord::Base.transaction do
deleted_fruits.each do |fruit|
logger.info "削除中:#{fruit.name}"
fruit.destroy if args[:status] == "exec"
end
end
logger.info '削除が完了しました。'
rescue StandardError => e
logger.error "エラーが発生しました: #{e.message}"
raise ActiveRecord::Rollback
end
else
logger.info '削除はキャンセルされました。'
end
logger.info '============================'
end
end
ここで、先程のコードと違う点は
- タスクの実行時に引数を入力する
- 引数がexecでないと削除の処理が行われない
という点です。rails delete_fruits:delete_fruit
を実行すると、
りんごとみかんを削除します。本当によろしいですか? (Y/n):
という文言が出てきますが、Yを入力してもテーブルに変化はありません。
rails delete_fruits:delete_fruit[exec]
を実行し、Yを入力すると削除の処理が行われ、テーブルは以下のようになります。
おわりに
Rakeタスクをdry runにすることで、本番実行前に操作をテストすることができ、Rakeタスクを安全に実行できます。