16526


Railsのプロジェクトで定期処理を実行するrakeのタスクを

[code]

    lib/tasks/foo.rake
    lib/tasks/bar.rake
    lib/tasks/hoge.rake
    
[/code]

のように配置し、多重起動を禁止するために

[code]

    namespace: foo do
      task :update_all, [:column] => :environment do |task, args|
        process_count = `ps -ef | grep #{task.name} | grep -v grep | wc -l`.to_i
        # whenever は `bash -c` を使って呼び出すため2個のプロセスが必ず出現する。だから3を使う
        next if process_count >= 3 
        # do task
      end
    end
    
[/code]

と書いているのですが、同様の処理はメソッド化したく、そのメソッドの置き場所をどこにするか迷っています。このような場合にはどこに置けば良いかという慣習はありますか？

### 追記

私の説明が不明瞭だったので具体的に書かせていただきますと

[code]

    def task_already_running?(task)
    process_count = `ps -ef | grep #{task.name} | grep -v grep | wc -l`.to_i
            # whenever は `bash -c` を使って呼び出すため2個のプロセスが必ず出現する。だから3を使う
            if process_count >= 3 
    end
    
[/code]

というメソッドを定義して

[code]

    namespace: foo do
      task :update_all, [:column] => :environment do |task, args|
        next if task_already_running?(task)
        # do task
      end
    end
    
[/code]

と書きたかったのですが、この `task_already_running?`
というメソッドを定義するファイルの置き場所・方法になんらかの慣習的に置き場があるか知りたいです。


CC BY-SA 3.0
2015-08-30T02:24:22.530
15024
2015-09-12T08:54:20.300
2015-09-12T07:23:05.787
3271
3271
question
ruby-on-rails rake
複数のRakeタスクから呼び出されるモデルに依存しないメソッドの置き場所


特にRailsとして規約は無いと思うので基本的に自由だと思います。  
僕だったらこうするかな、という例を載せておきます。

[code]

    # lib/tasks/cron.rake
    
    namespace :cron do
      task :foo, [:column] => :environment do |task, args|
        Cron::FooRunner.new(task).run
      end
      task :bar, [:column] => :environment do |task, args|
        Cron::BarRunner.new(task).run
      end
      task :hoge, [:column] => :environment do |task, args|
        Cron::HogeRunner.new(task).run
      end
    end
    
    # app/models/cron/task_runner.rb
    module Cron
      class TaskRunner
        def initialize(task)
          @task = task
        end
    
        def run
          process_count = `ps -ef | grep #{@task.name} | grep -v grep | wc -l`.to_i
          return if process_count >= 3
          do_task
        end
    
        def do_task
          # Override in sub classes
        end
      end
    end
    
    # app/models/cron/foo_runner.rb
    module Cron
      class FooRunner < Cron::TaskRunner
        def do_task
          puts "update all with Foo"
        end
      end
    end
    
    # app/models/cron/bar_runner.rb
    module Cron
      class BarRunner < Cron::TaskRunner
        def do_task
          puts "update all with Bar"
        end
      end
    end
    
    # app/models/cron/hoge_runner.rb
    module Cron
      class HogeRunner < Cron::TaskRunner
        def do_task
          puts "update all with Hoge"
        end
      end
    end
    
[/code]

実行例はこんな感じです。

[code]

    $ bundle exec rake cron:foo
    update all with Foo
    
    $ bundle exec rake cron:bar
    update all with Bar
    
    $ bundle exec rake cron:hoge
    update all with Hoge
    
[/code]

Githubにコードもアップしてあるので、手元で動かすこともできます。

<https://github.com/JunichiIto/multiple-rake-sandbox>

cron/xxx_runnerはmodelsの下ではなくlibの下に置く、というアプローチもあると思いますが、autoload_pathsの追加等が面倒なのでmodelsの下に置きました。  
人によっては好みが分かれるところかもしれません。

以上、ご参考までに。

## EDIT

クラスの継承ではなく、ブロックを使うパターンも作ってみました。  
こっちの方がシンプルかもしれません。

[code]

    # lib/tasks/cron.rake
    namespace :cron do
      task :foo, [:column] => :environment do |task, args|
        Foo.update_all(task)
      end
      task :bar, [:column] => :environment do |task, args|
        Bar.update_all(task)
      end
      task :hoge, [:column] => :environment do |task, args|
        Hoge.update_all(task)
      end
    end
    
    # app/models/cron/task_runner.rb
    module Cron
      class TaskRunner
        def self.run(task)
          process_count = `ps -ef | grep #{task.name} | grep -v grep | wc -l`.to_i
          return if process_count >= 3
          yield
        end
      end
    end
    
    # app/models/foo.rb
    class Foo
      def self.update_all(task)
        Cron::TaskRunner.run(task) do
          puts "update all with Foo"
        end
      end
    end
    
    # app/models/bar.rb
    class Bar
      def self.update_all(task)
        Cron::TaskRunner.run(task) do
          puts "update all with Bar"
        end
      end
    end
    
    # app/models/hoge.rb
    class Hoge
      def self.update_all(task)
        Cron::TaskRunner.run(task) do
          puts "update all with Hoge"
        end
      end
    end
    
[/code]

コードはこちらです。

<https://github.com/JunichiIto/multiple-rake-sandbox/tree/with-block>

なお、「同様の処理はメソッド化したく」の具体的な内容がハッキリわかっておらず、「たぶんこういうことかな？」と推測しながらこのコードを作っています。  
もし、観点がずれていたら質問に追記してください。

## EDIT2

追記ありがとうございました。  
この手の処理で慣習があるかどうかというと、無いと思います。（少なくとも僕が知る限りは）

なので、基本的にどんなやり方でもいいと思いますが、とりあえず別パターンの回答を追記しておきます。

[code]

    # lib/tasks/rake_helpers.rb
    def task_already_running?(task)
      process_count = `ps -ef | grep #{task.name} | grep -v grep | wc -l`.to_i
      process_count >= 3
    end
    
    # lib/tasks/foo.rake
    require_relative 'rake_helpers'
    
    namespace :foo do
      task :update_all, [:column] => :environment do |task, args|
        next if task_already_running?(task)
        puts "update all with Foo"
      end
    end
    
    # lib/tasks/bar.rake
    require_relative 'rake_helpers'
    
    namespace :bar do
      task :update_all, [:column] => :environment do |task, args|
        next if task_already_running?(task)
        puts "update all with Bar"
      end
    end
    
    # lib/tasks/hoge.rake
    require_relative 'rake_helpers'
    
    namespace :hoge do
      task :update_all, [:column] => :environment do |task, args|
        next if task_already_running?(task)
        puts "update all with Hoge"
      end
    end
    
[/code]

コードはこちらです。

<https://github.com/JunichiIto/multiple-rake-sandbox/tree/multiple-rakes>


CC BY-SA 3.0
2015-09-11T12:50:46.423
16526
2015-09-12T08:54:20.300
2015-09-12T08:54:20.300
85
85
15024
answer