1
0

【Rails】dry runなrakeタスクの作成方法

Last updated at Posted at 2023-09-29

はじめに

私は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テーブルに追加されます。

スクリーンショット 2023-09-29 19.03.14.png

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を入力すると削除の処理が行われ、テーブルは以下のようになります。

スクリーンショット 2023-09-29 19.34.55.png

おわりに

Rakeタスクをdry runにすることで、本番実行前に操作をテストすることができ、Rakeタスクを安全に実行できます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0