以前、delayed_job と rails runner について 2 つ記事を書きました。
これらの記事で、それぞれを個別に扱ったのですが、組み合わせられないか確かめたのでメモです。
目次
参考
今回実装するもの 今回は、コントローラーから rails runner を実行して、指定したオブジェクト一覧のログファイルを出力してみます。
実装 呼び出されるバッチスクリプトを作る まずは、コントローラーから呼び出すスクリプトを作ります。lib/script/getlog.rb
を用意します。
lib/script/getlog.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 module Script class GetLog def self .get_log classs = ["User" ,"Food" ] lists = Array .new classs.each do |c | obj = c.safe_constantize if obj.present? lists += obj.all.to_a end end @lists_sorted = lists.sort{|a,b | a.created_at <=> b.created_at} File .open("log/log.txt" , "w" ){|f | @lists_sorted .each do |l | f.write("#{l.class } ,#{l.name} ,#{l.updated_at} \n" ) end } end end end Script : :GetLog .get_log
複数のモデルをまたいで created_at
を元に並べ変えています。 詳細な処理は、文字列からクラスにアクセスする で解説しています。
lib/script/getlog.rb
が用意できたら、bundle exec rails runner lib/script/getlog.rb
で実行します。log/log.txt
が作られていることを確認します。 以下のようになっています。
log/log.txt 1 2 3 4 5 6 7 8 9 10 User,UserA,2020-03-14 16:24:16 UTC Food,FoodC,2020-03-14 16:24:02 UTC Food,FoodA,2020-03-14 16:24:47 UTC Food,FoodD,2020-02-28 15:00:51 UTC User,UserB,2020-02-28 16:26:11 UTC Food,FoodE,2020-02-28 16:26:34 UTC User,UserC,2020-02-29 01:52:55 UTC User,UserD,2020-03-07 13:30:49 UTC User,UserE,2020-03-07 14:09:11 UTC User,UserF,2020-03-07 14:09:22 UTC
呼び出すコントローラを用意する app/controllers/mix_lists_controller.rb
に、ログ書き出しするアクションを追加します。
app/controllers/mix_lists_controller.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class MixListsController < ApplicationController def index classs = ["User" ,"Food" ] lists = Array .new classs.each do |c | obj = c.safe_constantize if obj.present? lists += obj.all.to_a end end @lists_sorted = lists.sort{|a,b | a.created_at <=> b.created_at} end def get_log Delayed : :Job .enqueue LogJob .new redirect_to mixlists_path, notice: 'Start_Log' end private class LogJob def perform system("bundle exec rails runner lib/script/getlog.rb" ) end end end
get_log が実行されると、呼び出し元の get_log を押したmixlists
にリダイレクトします。 ポイントは、Delayed::Job.enqueue LogJob.new
の部分なのですが、Delayed_job に.delay
を使わずキューにタスクを積む方法がこちらです。perform
メソッドを持つオブジェクトを渡すことでperform
メソッドに書かれた処理を遅延実行できます。
ルーティングの実装 config\routes.rb
を編集して、作成したget_log
を呼び出せるようにします。config\routes.rb
を以下のようにします。
config\routes.rb(抜粋) 1 2 3 4 5 6 Rails .application.routes.draw do get '/mixlists' , to: 'mix_lists#index' get '/getlog' , to: 'mix_lists#get_log' end
ビューの設定 ルーティングができたので、ビューを用意します。app/views/mix_lists/index.html.erb
を編集します。
app/views/mix_lists/index.html.erb(抜粋) 1 2 3 <%= link_to 'ログを出力する。', getlog_path %>
確認 コンソールを 2 つ用意し、一方は、bundle exec rails jobs:work
もう一方はbundle exec rails s
を実行します。localhost:3000/mixlists
にアクセスします。 すると以下の画面になります。
ここで、「ログを出力する」をクリックすると、’localhost:3000/mixlists’にリダイレクトして、表面では何も起きません。log/log.txt
が作られます。
画面から入力して、バッチ処理を delayed_job で呼び出すことができました。
少し改修 log/log.txt
は、作られましたが遅延実行するとはいえブラウザから取得するようにしてみたいところです。 少し改修します。
呼び出されるバッチスクリプトを改修 まずは、コントローラーから呼び出すスクリプトをlib/script/getlog.rb
を改修します。
lib/script/getlog.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 module Script class GetLog def self .get_log classs = ["User" ,"Food" ] lists = Array .new classs.each do |c | obj = c.safe_constantize if obj.present? lists += obj.all.to_a end end @lists_sorted = lists.sort{|a,b | a.created_at <=> b.created_at} File .open("public/log/log#{Time .now.strftime("%Y-%m%d-%H%M%S" ) } .txt" , "w" ){|f | @lists_sorted .each do |l | f.write("#{l.class } ,#{l.name} ,#{l.updated_at} \n" ) end } end end end Script : :GetLog .get_log
ディレクトリの追加 public の下に log ディレクトリを作ります。
呼びだすコントローラを改修 app/controllers/mix_lists_controller.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class MixListsController < ApplicationController def index classs = ["User" ,"Food" ] lists = Array .new classs.each do |c | obj = c.safe_constantize if obj.present? lists += obj.all.to_a end end @lists_sorted = lists.sort{|a,b | a.created_at <=> b.created_at} @files = Array .new Dir .foreach('public/log' ) do |item | if item != "." && item != ".." hash={:name => "#{item} " , :path => "log/#{item} " } @files .push( Struct .new(*(hash.keys)).new(*(hash.values)) ) end end end def get_log Delayed : :Job .enqueue LogJob .new redirect_to mixlists_path, notice: 'Start_Log' end private class LogJob def perform system("bundle exec rails runner lib/script/getlog.rb" ) end end end
public/log
にある log ファイルを出力するので、こちらからファイル一覧を取得して、パスとのペアを作ります。
ビューの改修 取得したファイルの一覧を表示するために link_to でファイルへのリンクを作ります。
app/views/mix_lists/index.html.erb(抜粋) 1 2 3 4 5 6 7 8 <%= link_to 'ログを出力する', getlog_path %> <ul > <% @files.each do |file| %> <li > <%= link_to file.name, file.path,{download:file.name} %></li > <% end %> </ul >
改修後確認 コンソールを 2 つ用意し、一方は、bundle exec rails jobs:work
もう一方はbundle exec rails s
を実行します。localhost:3000/mixlists
にアクセスします。 すると以下の画面になります。
ここで、「ログを出力する」をクリックすると、’localhost:3000/mixlists’にリダイレクトして、表面では何も起きません。
ここまでは同じですが、ブラウザのリロードをしているとそのうちリンクが現れます。 画像では、3 回「ログを出力する」をクリックしています。
表示されたリンクにアクセスすると、作成されたログファイルをダウンロードできます。
Delayed_job で遅延実行したログ作成バッチで、作成したログファイルをブラウザで入手できるようになりました。
今回は、delayed_job と rails runner を組み合わせました。 最近、これまでやってきたことが噛み合うようになってきて、複合的に扱って作りたいものが実装できるようになってきました。 学習の効果で出てきていると感じます。
ではでは。