Rakeタスクとwhenever ※ごちゃごちゃメモ
RubyのMakeコマンド = Rake
■ Makeコマンドとは
あるソースファイルから目的のファイルを生成するためのコマンド。
使い方はmakefileというファイルに、ソースファイル名と目的のファイルの生成方法を記述しておき、$ make
とターミナルに入力するだけで、ファイル内のコードが実行され、目的のファイルが作成できるというもの。それのRuby版がRake。
コマンド「make」初心者向けメモ(Hishidama's make-command Memo)
■ Rakeコマンド
Rakeにおいても、まずRailsで定期的に実行したい処理をRakeタスク
として定義しておき、必要な時にコマンド実行することでファイル内に記載した処理が実行される。
■ 自動化
whenever
というgemを使うことで、わざわざ手動でコマンド実行しなくても指定した時間間隔で自動的にRakeコマンドを実行させることができる。
使用例
- あるブログ制作サイトの投稿ステータス(下書き、公開予定、公開中)があったとする
- この投稿ステータスはアップロード時間によって決定される
- アップロード時間が設定されていなければ下書き状態、アップロード時間が現在または過去であれば公開中、未来であれば公開予定といった形
- アップロード時刻が現在になったタイミングで投稿ステータスを公開中に切り替えるというRakeタスクを作成しておく
whenever
を用いて自動化させる
→これにより、製作者が手動で操作しなくても投稿ステータスを適宜変更してくれる機能が作れる
作成方法
$ rails g task article # lib/tasks/article.rake namespace :article do # 目的の処理を記述 desc '記事のステータスを変更する' task :change_status # 処理内容 end
- lib/tasksディレクトリ配下にarticle.rakeが生成される
- desc(description)はどういうタスクかの説明文
- taskにはタスク名と具体的な処理を記載
- Rakeタスクのファイル名(namespaceで指定される名前空間)に命名規則はないものの、ブロック内のタスクを包括する名詞が望ましい(メールならmail、記事ならarticleみたいに)
→ その方がコマンド実行する際、直感的で分かりやすいから
今回の例で言えば、namespaceがarticleというタスクのinsert_recordというタスクを実行するので
$ rails article:change_status
という形になる。
※ rails db:migrate
も同じような形をしているが実はこれもRakeタスク。
↑の内容と全く関係ないけれどRakeタスクの例
例)youtubeの埋め込み設定で、IDをフォームに入力して貰う仕様だったが、URLをそのまま入力できるように変更。その際、リニューアル前にすでに保存されているidentifierカラム内のデータを変更しなければデータの整合性が保てない。データを一括修正するタスクを作成する。
namespace :fix_embed_youtube_identifier do desc 'IDを入力していたidentifierカラムの過去データを一括で修正' task update_old_identifier_for_youtube_embed: :environment do Embed.youtube.each do |embed| embed.update(identifier: "https://youtube/#{embed.identifier}") end end end
- taskの末尾に記載されている
:environment
はDBに接続し変更を加える場合に記載が必要 - 今回はindentifierカラムの内容を書き換えるタスクのため、記載が必要
- タスクの概略として、元々末尾の相対パス部分だけ保存していたがURL完全体として保存し直すために変数展開で補完して更新している
wheneverとは
crontab管理ライブラリ。wheneverを使うことでcronを動かすことができる。
cronとは(詳細は省く)
【入門】cron(クロン)設定・書き方の基本 - カゴヤのサーバー研究室
- プログラムを定期的に実行したい時に使う
- UNIX系のOSに標準で備わっている
- cronは機能を示す名称で、crontabとはコマンド名またはファイル名
wheneverはcronの設定をrubyの簡単な文法で扱えるようにしたライブラリ。
wheneverの使い方
① インストール
gem 'whenever', require: false
whenever
はバックグラウンドで処理されるものであり、Railsとは関係のないプロセスrequire: false
はRailsの実行時に読み込まないようにするための記載- まとめるとwheneverはRailsと関係のないプロセスのため、Railsの実行時に読み込まないようにrequire: falseを記載しているよということ
$ bundle exec wheneverise
- このコマンドにより、config配下に
schedule.rb
が作成される
wheneverの設定
細かい点で不明なことが多いけれど、ひとまずこういうものと思っておく。
# config/schedule.rb # Rails.root(Railsメソッド)を使用するために必要 require File.expand_path(File.dirname(__FILE__) + "/environment") # cronを実行する環境変数 # 環境変数ENV['RAILS_ENV']にセットされている変数または:developmentを指定 # 自分の環境でENV['RAILS_ENV']にすでにdevelopmentがセットされていた rails_env = ENV['RAILS_ENV'] || :development # cronを実行する環境変数をセット set :environment, rails_env # cron.logの出力先を指定している # Rails.rootはこのアプリのルート階層が返される set :output, "#{Rails.root}/log/cron.log" # 以下、wheneverが上手く機能しなかったので追加 # シェルコマンド設定 # デフォルトはbash -l -c 'command...'で実行される # 自分の環境は.zshなので設定しておく set job_template, "/bin/zsh -l -c ':job'" # .zshrcとrbenvのパスを指定してrakeを定義 # cronは.zshとrbenvの環境で動いてくれないためPATHを通す job_type :rake, "source /Users/[ユーザー名]/.zshrc; export PATH=\"$HOME/.rbenv/bin:$PATH\"; eval \"$(rbenv init -)\"; cd :path && RAILS_ENV=:environment bundle exec rake :task :output"
一番上のrequireは何を読み込んでいる?
# config/schedule.rb # Rails.root(Railsメソッド)を使用するために必要な記載 require File.expand_path(File.dirname(__FILE__) + "/environment")
結論: config/environmentをrequireしている
config/environmentを読み込む理由
インストールの部分と重複するが、wheneverはバックグラウンドで処理するものであり、普段rails sで起動しているプロセスとは別プロセスで実行されるもの。これはRailsとは切り離されたもので、単なるRubyファイルに過ぎない。Railsとは関係のないRubyファイルの中でRailsのメソッドを使いたいので、一番上の行でconfig/environmentを読み込んでいる。
__FILE__
- File.dirname
- File.expand_path
上記三つの意味をそれぞれ深堀してみることで、File.expand_path(File.dirname(__FILE__) + "/environment")
が指し示す意味が分かる◎
■ File.expand_path
pathを絶対パスに展開した文字列を返します。pathが相対パスであればdefault_dirを基準にします。File.expand_path (Ruby 3.1 リファレンスマニュアル)
ちなみにdefault_dirはデフォルトのディレクトリのこと。File.expand_path(path, default_dir = '.')
渡されたpathが相対パスだった場合は第二引数のdefault_dirを基準にする(よく分かってない)
- 絶対パス(フルパス)とは、最上位に位置するディレクトリ(ルートディレクトリ)から、対象のファイルまでの道順全てを記述する方法
- 相対パスとは、自分の現在位置を基準として説明される対象ファイルの場所。(B町に住む佐藤さんから見て、田中くんはA町にある田中家の2階の部屋にいる。佐藤さんを基準とした田中くんの相対パス)
絶対パス、相対パスとは?使用例からメリット/デメリットまでをまるっと解説
相対パス | 「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
■ __FILE__
現在実行しているファイル。現在のソースファイル名が格納された擬似変数 [Ruby] __FILE__や__dir__って何? - Just do IT
■ File.dirname("dir/file.ext")
filenameの一番後ろのスラッシュより前を文字列として返します。スラッシュを含まないファイル名に対しては"."(カレントディレクトリ)を返します。
クラス:File > クエリ:dirname | るりまサーチ
filenameの一番後ろのスラッシュより前を文字列として返す。小見出しの例で言えば一番後ろのスラッシュより前にある"dir"
を返す
まとめると
# config/schedule.rb require File.expand_path(File.dirname(__FILE__) + "/environment")
__FILE__
は現在実行しているファイルであるconfig/schedule.rb
を指すFile.dirname(__FILE__)
によってconfig/schedule.rb
の一番後ろのスラッシュより前の文字列を取得するため、この場合はconfigが返される- 最後、
File.expand_path
に"config/environment"というパスが渡されるため、config/environmentの絶対パスが返されている - それをrequireしている
スケジュールの記述はgithubを参考に
every 3.hours do # 1.minute 1.day 1.week 1.month 1.year is also supported # the following tasks are run in parallel (not in sequence) runner "MyModel.some_process" rake "my:rake:task" command "/usr/bin/my_great_command" end every 1.day, at: '4:30 am' do runner "MyModel.task_to_run_at_four_thirty_in_the_morning" end every 1.day, at: ['4:30 am', '6:00 pm'] do runner "Mymodel.task_to_run_in_two_times_every_day" end every :hour do # Many shortcuts available: :hour, :day, :month, :year, :reboot runner "SomeModel.ladeeda" end every :sunday, at: '12pm' do # Use any day of the week or :weekend, :weekday runner "Task.do_something_great" end every '0 0 27-31 * *' do command "echo 'you can use raw cron syntax too'" end # run this task only on servers with the :app role in Capistrano # see Capistrano roles section below every :day, at: '12:20am', roles: [:app] do rake "app_server:task" end
https://github.com/javan/whenever#example-schedulerb-file
wheneverが上手く機能しなかった
wheneverが機能しているか確かめるために、スケジュールを1分おきに設定
every 1.minutes do rake "{自作したRakeコマンド実行}" end
上手く動かなかったので、下記の設定を追加
# シェルコマンド設定 # デフォルトはbash -l -c 'command...'で実行される # 自分の環境は.zshなので設定しておく set job_template, "/bin/zsh -l -c ':job'" # .zshrcとrbenvのパスを指定してrakeを定義 # cronは.zshとrbenvの環境で動いてくれないためPATHを通す job_type :rake, "source /Users/[ユーザー名]/.zshrc; export PATH=\"$HOME/.rbenv/bin:$PATH\"; eval \"$(rbenv init -)\"; cd :path && RAILS_ENV=:environment bundle exec rake :task :output"
https://github.com/javan/whenever#define-your-own-job-types
メモ:環境変数やPATHについて不理解のため調べる
参考にしたサイト
Wheneverは導入が超簡単なcrontab管理ライブラリGemです![Rails 4.2 x Ruby 2.3] | 酒と涙とRubyとRailsと
[Rails]Rails5 wheneverでRakeタスクを定期的に実行
【Rails】Rakeタスクの基本情報と作成・実行方法 - AUTOVICE