rails 独学して軽くにちゃんまとめつくる

読者です 読者をやめる 読者になる 読者になる

rails独学して軽く2ちゃんまとめビルダーをつくる。

rails tutorial読破→2chまとめビルダー作成→アメリカでインターンシップ→恋活SNSを作成・・・・・→音で感触をつくり感触をあつめたライブラリをつくる

delayed_job+ clockwork を使って定期的に自動的にデータを保存・更新する。

RailsでAcitiveJobとDelayedJobを使ってバックグランド処理を行う - Rails Webook

このサイトがよかったのでこれの抜けているところを補ったり、これを応用してclockwork と連動させて定期的に自動的にモデルにデータを保存できるように改造したのでメモ。

delayed_job をつかえば任意のタイミングである一連の動作をこなしてくれる。上のサイトでは、deliver というボタンを押したタイミングでdelivered_at というカラムの時間を更新してくれるようにしていた。このブログに習いまずはそれをするために。

次にそれを ActiveJob と clockwork とに連携させる。これによって定期的に一連の動作を実行する機能を付ける。今回は

rails new Letter で Letter (定期) というプロジェクトを作成する。そんで移動する。 cd Teiki

$ rails g scaffold Koushin name:text description:text delivered_at:timestamp でスキャフォールディングする。

Running via Spring preloader in process 12234
    create  
      create  README.md
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
      create  app
      create  app/assets/config/manifest.js
      create  app/assets/javascripts/application.js
      create  app/assets/javascripts/cable.js
      create  app/assets/stylesheets/application.css
      create  app/channels/application_cable/channel.rb
      create  app/channels/application_cable/connection.rb
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/jobs/application_job.rb
      create  app/mailers/application_mailer.rb
      create  app/models/application_record.rb
      create  app/views/layouts/application.html.erb
      create  app/views/layouts/mailer.html.erb
      create  app/views/layouts/mailer.text.erb
      create  app/assets/images/.keep
      create  app/assets/javascripts/channels
      create  app/assets/javascripts/channels/.keep
      create  app/controllers/concerns/.keep
      create  app/models/concerns/.keep
      create  bin
      create  bin/bundle
      create  bin/rails
      create  bin/rake
      create  bin/setup
      create  bin/update
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/secrets.yml
      create  config/cable.yml
      create  config/puma.rb
      create  config/spring.rb
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/application_controller_renderer.rb
      create  config/initializers/assets.rb
      create  config/initializers/backtrace_silencers.rb
      create  config/initializers/cookies_serializer.rb
      create  config/initializers/cors.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/mime_types.rb
      create  config/initializers/new_framework_defaults.rb
      create  config/initializers/session_store.rb
      create  config/initializers/wrap_parameters.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  lib
      create  lib/tasks
      create  lib/tasks/.keep
      create  lib/assets
      create  lib/assets/.keep
      create  log
      create  log/.keep
      create  public
      create  public/404.html
      create  public/422.html
      create  public/500.html
      create  public/apple-touch-icon-precomposed.png
      create  public/apple-touch-icon.png
      create  public/favicon.ico
      create  public/robots.txt
      create  test/fixtures
      create  test/fixtures/.keep
      create  test/fixtures/files
      create  test/fixtures/files/.keep
      create  test/controllers
      create  test/controllers/.keep
      create  test/mailers
      create  test/mailers/.keep
      create  test/models
      create  test/models/.keep
      create  test/helpers
      create  test/helpers/.keep
      create  test/integration
      create  test/integration/.keep
      create  test/test_helper.rb
      create  tmp
      create  tmp/.keep
      create  tmp/cache
      create  tmp/cache/assets
      create  vendor/assets/javascripts
      create  vendor/assets/javascripts/.keep
      create  vendor/assets/stylesheets
      create  vendor/assets/stylesheets/.keep
      remove  config/initializers/cors.rb

delayed_job という gem をインストールするために Gemfilegem 'delayed_job_active_record'  をつけたして bundle install としてインストールする。

rails g delayed_job:active_record
rake db:migrate

これもしておく

ここから本番です。次にコントローラーに job を追加しましょう。ここではさっき作った :koushin というカラムに update した時間を書き換えることにしましょう。

# app/controllers/tegamis_controller.rb
class TegamisController < ApplicationController

  # POST/tegami/1/deliver
  def deliver
    @tegami = Tegami.find(params[:id])
    @tegami.deliver
    redirect_to tegamis_url, notice: "手紙を送りました。"
  end
  ...
end

# app/models/letter.rb
class Tegami < ActiveRecord::Base
  def deliver
    sleep 10 # 処理に時間がかかることを擬似的に実施
    update(delivered_at: Time.zone.now)
  end
end

あの記事はここまでで終わっているが、あとルーティングの設定と、deliver ボタンの設置が必要なので追記しておく。

# config/routes.rb

Rails.application.routes.draw do
  resources :tegamis do
    member do
      get 'deliver'
    end
  end
end

こうすると deliver_tegami GET /tegamis/:id/deliver(.:format) tegamis#deliver

というルーティングができる。そうすることでさっき作った deliver アクションを利用することができる。

次にビューを追加する。 RailsでAcitiveJobとDelayedJobを使ってバックグランド処理を行う - Rails Webook では “deliver” というボタンを設置していた。あれを押すと deliver アクションが起動するようになっているので、上で追加したルーティングをパスにして link_to を作成する。

#index.html.erb

 <td><%= link_to 'deliver', deliver_tegami_path(tegami) %></td>

を追加しよう。すると"deliver"ボタンをおしたらコントローラーのアクションに中継され、あそこで定義したコードが実行され、 “delivered_at” が更新される。

さあ、実際に実行してみよう。

rails s を入力し、/tegamis にいってみる。

f:id:mooooooooooriiiiii:20170319134748p:plain

こんな画面になってるはず。

New Tegami って書いてあるボタンをおして、適当に内容を入力して、create して、index にもどるf:id:mooooooooooriiiiii:20170319135344p:plain

新しいデータができた。

f:id:mooooooooooriiiiii:20170319135824p:plain そして deliver ボタンを押す。10秒くらい待つ。かわらんかったら更新する。そしたらこんな風に時間が更新されている。

f:id:mooooooooooriiiiii:20170319135702p:plain

これで原型ができた。

まあ、これではあほらしい。いちいちボタン押してたら自動化には程遠い。しかも定期的じゃないし。ぼくらがしたいことは「定期的に自動で」だからそれを実現する Active Jobというのを使う。ActiveRecord というのはさっき使った delayed_job などの総称だ。

# config/application.rb
module DelayedJobTestApp
  class Application < Rails::Application
    ...

    # Gemfileにアダプターのgemを記載されており、gemがインストール済みであり、
    config.active_job.queue_adapter = :delayed_job# <- これを付け足す
  end
end

つぎにジョブを作成する。rails g job news_deliverと入力する。ジョブってなんやと

これをするのは一連の動作をまとめたものという認識。正直よくわからん。でもまあ job というのにメソッドを書き、それを使えば定期的に実行できるということだけ抑えておく。 この Job はうれしいことに モデルの操作も行える。 clockworkだけではできないことなんだここが Job の素晴らしいことである。

# app/jobs/news_deliver_job.rb
class NewsDeliverJob < ActiveJob::Base
  queue_as :default

  def perform(*args)
    # Do something later
  end
end

というファイルがある。これを改造してさっきつくった Tegami モデルのインスタンスをつくってみよう。

#app/jobs/news_deliver_job.rb
class NewsDeliverJob < ApplicationJob
  
  def perform 
    Tegami.create(title: "クソワロタwwwwww", delivered_at: Time.zone.now)
  end
end

clockwork という gem は

gem 'clockwork' と書き足して、 bundle install しておく。 そして config/clock.rb に以下のコードをかく。

#config/clock.rb

require 'clockwork'
require File.expand_path('../boot', __FILE__)
require File.expand_path('../environment', __FILE__)

module Clockwork
  handler do |job|
    puts "Running #{job}"
  end
  
  every(1.seconds, 'Tegami') { Delayed::Job.enqueue NewsDeliverJob.new.perform }
end

一番大事なのは every(1.seconds, 'Tegami') { Delayed::Job.enqueue NewsDeliverJob.new.perform } という部分である。

every(1.seconds, 'Tegami') の部分について。第一引数は繰り返すスパンである。この場合1秒ごとにジョブが実行され、 10.minutes とすれば 10分ごとにジョブが実行される。 第二引数の 'Tegami' というのだが、これは handlerのブロックの job という引数に渡される。まあいまはきにしなくていい。

Delayed::Job.enqueue というのは気にしなくていい。次の NewsDeliverJob.new.performというのが大事。 NewsDeliverrails g job news_deliverで作ったジョブの名前だ。ジョブはこんな感じで大文字+"Job" 命名される。 new はジョブのインスタンスを作成していると考える。User.newとか Article.new などとしたのと同じ具合だ。最後に .perform はジョブのメソッドだ。こうしてさっきのevery(1.seconds, 'Tegami') { Delayed::Job.enqueue NewsDeliverJob.new.perform }  を翻訳すると

1秒ごとに、 NewsDeliverJob で定義した perform メソッドを実行する となる。

で、bundle exec clockwork config/clock.rb をやってみたらできた。

f:id:mooooooooooriiiiii:20170319151107p:plain

ちゃんと新しいデータが製造されている。

コントローラーの deliverメソッド はつかわないので消しておいてもいい。

おつかれさまだ。これで「定期的に自動的にデータを保存する」という機能を付ける基礎が完了した。あとはこれの応用でしかないだろう。自動的にメールを送ったり、定期的に2ちゃんに書き込みをしたり、定期的に株式データを集めたり、毎日つぶやくツイッターのボットをつくったり・・・なんでもできる。がんばりたまえ。