FactoryGirlを追加した

いつもの、ごみシリーズです。

今週土日は、自分の所属しているバドミントンクラブのHPの作成で終わってしまいました。 デザイン難しい。

さて、今回は前回 RspecでHTTPのリクエスト・レスポンスのテストを行った - takapi86のブログ で追加したテストに、テストデータを追加していきます。

こちらを参考に導入を行いました。

File: GETTING_STARTED — Documentation for factory_girl (4.8.0)

導入

  • Gemfilefactory_girl_railsを追加して、bundle installします。
group :test do
  gem 'factory_girl_rails'
end
  • factory_girlのテストデータを作成するコードを保管する場所として、spec/factories/ を作成しておきます。

ファイルを作成する

区モデル(ward)、町モデル(town)のFactoryGirlファイルを作成します。 ※ward、townは1:nの関係です。

詳しくは、

  • spec/factories/wards.rbを作成
FactoryGirl.define do
  factory :ward do # モデル名を指定
    sequence(:id)
    ward_code 16
    name '港北区'
  end
end
  • spec/factories/towns.rbを作成
FactoryGirl.define do
  factory :town do # モデル名を指定
    sequence(:id)
    ward_code 16
    initial_code 3
    town_code 1073
    name '篠原町'
  end
end

試してみる

  • test環境にコンソールで入ります。
RAILS_ENV=test bundle exec rails c

wardのデータを作ってみる

[1] pry(main)> ward = FactoryGirl.create(:ward)
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "wards" ("id", "ward_code", "name", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)  [["id", 1], ["ward_code", 16], ["name", "港北区"], ["created_at", 2017-03-26 14:57:30 UTC], ["updated_at", 2017-03-26 14:57:30 UTC]]
   (145.8ms)  commit transaction
=> #<Ward:0x00559a99c3ccb8
 id: 1,
 ward_code: 16,
 name: "港北区",
 created_at: Sun, 26 Mar 2017 14:57:30 UTC +00:00,
 updated_at: Sun, 26 Mar 2017 14:57:30 UTC +00:00>

townのデータを作ってみる

[2] pry(main)> FactoryGirl.create(:town, ward: ward)
   (0.1ms)  begin transaction
  SQL (0.3ms)  INSERT INTO "towns" ("id", "ward_code", "initial_code", "town_code", "name", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?)  [["id", 1], ["ward_code", 2], ["initial_code", 3], ["town_code", 1073], ["name", "篠原町"], ["created_at", 2017-03-26 14:59:00 UTC], ["updated_at", 2017-03-26 14:59:00 UTC]]
   (60.9ms)  commit transaction
=> #<Town:0x00559a9daa47f8
 id: 1,
 ward_code: 2,
 initial_code: 3,
 town_code: 1073,
 name: "篠原町",
 created_at: Sun, 26 Mar 2017 14:59:00 UTC +00:00,
 updated_at: Sun, 26 Mar 2017 14:59:00 UTC +00:00>

うまくいったっぽいです。 念の為、testのDBはクリアしておきます。

RAILS_ENV=test bundle exec rake db:migrate:reset

前回のテストに追加

require 'rails_helper'

RSpec.describe TownsController, type: :request, json: true do
  let(:town) { FactoryGirl.create(:town, ward: create(:ward)) }

  describe 'GET /towns/:name.json' do
    before do
      get URI.escape("/towns/#{town.name}.json")
    end

    it '200 OK を返す' do
      expect(response.status).to eq(200)
    end
  end
end

こちらもうまくいったようです。

ちなみに、ここではFactoryGirl.createと記載していますが、クラス名は、 spec_helper.rbの Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } のコメントを外し、以下のようにファイルを追加するか、そのままconfig.include FactoryGirl::Syntax::Methods と追記することで、省略できます。

# spec/support/factory_girl.rb
RSpec.configure do |config|
  config.include FactoryGirl::Syntax::Methods
end

ただ、このままでは2回目実行すると、以下のようなエラーが発生してしまうようです。

ActiveRecord::RecordNotUnique:
       SQLite3::ConstraintException: UNIQUE constraint failed: wards.id: INSERT INTO "wards" ("id", "ward_code", "name", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)
     # ./spec/requests/towns_spec.rb:2:in `block (2 levels) in <top (required)>'
     # ./spec/requests/towns_spec.rb:7:in `block (3 levels) in <top (required)>'
     # ------------------
     # --- Caused by: ---
     # SQLite3::ConstraintException:
     #   UNIQUE constraint failed: wards.id
     #   ./spec/requests/towns_spec.rb:2:in `block (2 levels) in <top (required)>'

どうやら、続けてテストする場合は、テスト後にテストしたデータを削除する処理を入れる必要があるようです。 こちらは、次回対応していきたいと思います。

RspecでHTTPのリクエスト・レスポンスのテストを行った

rspecでHTTPのリクエスト・レスポンスのテストを行った。

今回も、ごみシリーズです。

ごみ収集日の検索をLineからできるようにした - takapi86のブログで、APIを作っていく中で、rspecでのテストでHTTPのリクエスト・レスポンスのテストができるということを知ったので、メモして起きます。

事前準備

  • 以下のgemはインストール済みである前提で行います。

対象

  • ごみ収集日の検索をLineからできるようにした。にも書いてありますが、ゴミ収集APIには以下のエンドポイントがあります。
    • 収集場所コードから収集場所のスケジュールを返すAPI
    • 収集場所名を前方一致で検索し、一覧で返すAPI
    • 上記2つのAPIを使い、Lineでメッセージを受け取り、スケジュールや収集場所一覧を返すAPI

今回は上記のうち、収集場所名を前方一致で検索し、一覧で返すAPIのテストを書きたいと思います。

導入

rspecのテストファイルは以下の場所 * spec/requests/towns_spec.rb

  • 今回はTownsControllerのテストで、リクエストを投げて確認するため、以下のように記述します。
describe TownsController, type: :request do
end
  • エンドポイントはGETメソッドで、/towns/:nameなので以下のように記述します。
describe TownsController, type: :request do
  describe 'GET /towns/:name' do
  end
end
  • ステータスコードは200で返ってくるのを期待するテストを追加します。
  • ここでは省略していますが、事前にFactoryGirl等でテストデータを生成するようにすると良いでしょう。
describe TownsController, type: :request do
  describe 'GET /towns/:name' do
    it '200 OK を返す' do
      get URI.escape("/towns/#{"日吉"}")
      expect(response.status).to eq(200)
    end
  end
end
  • 事前にFactoryGirl等でテストデータを生成するようにすると良いでしょう。
  • テストを実行してみましょう。
bundle exec rspec spec/requests/towns_spec.rb
Finished in 0.50264 seconds (files took 3.08 seconds to load)
1 example, 0 failures

正常にテストが終了したようです。

今回は、test環境にデータが入った状態でテストを行いましたが、 先ほど言ったように事前にFactoryGirl等でテストデータを生成するようにすると良いでしょう。

ごみ収集日の検索をLineからできるようにした

前回のLINE Notifyを使ってごみ収集日を自分にお知らせしてみた。に続き、ごみシリーズです。

会社で携わっているサービスをゴリゴリAPI化していく予定(やっていき)なので、その練習も兼ねて作成しました。

今回は、Messaging API(LineBot)を使って、メッセージから横浜市のゴミ収集日を検索できるようにしました。

現在は、ざっくりイメージとして実装してみた感じなので、機能強化・リファクタリングは今後行っていく予定です。

こんな感じです。

収集場所名を前方検索し、もし、町名がドンピシャで一致していたら、収集曜日を出力。複数ある場合は「もしかして?」と収集場所名・コードを一覧出力します。

収集場所コードでも検索ができるので、収集場所名がわからない場合は、ざっくり町名などを入れて調べ→コードで検索といった流れで調べます。

環境は、

で実装しました。今回作成したAPIは以下3つ

  • 収集場所コードから収集場所のスケジュールを返すAPI
  • 収集場所名を前方一致で検索し、一覧で返すAPI
  • 上記2つのAPIを使い、Lineでメッセージを受け取り、スケジュールや収集場所一覧を返すAPI

です。Restfulな感じで作りました。

今回は、データについては横浜市のページを見て手動で作っていますが、 今後は、横浜市のHPからスクレイピングしてデータを取り込み、それを返すようにしていきたいと思います。

実は、今回APIを実装するのが初めてだったのですが、API設計や命名規則な部分で悩みました。

  • Rest APIのURLの付け方
  • APIでどんなリクエストを受けるか、どんなレスポンス・JSONを返すか
  • Lineメッセージのルール作り

実はまだまだ納得がいっておりませんので、今後、バージョンアップする際には、特にこの辺を意識して取り組んでいきたいと思います。

では、また。

LINE Notifyを使ってごみ収集日を自分にお知らせしてみた

Rubyの練習も兼ねて、LINE Notifyを使ってごみ収集日を自分にお知らせしてみました。

やったこと

  • LINE Notifyのマイページへログイン(スマホ版のLINEで登録したものです。)し、トークンを発行
  • メッセージの送信テスト
  • Rubyからメッセージを送信
  • ごみ収集日のお知らせテキストを作成し、メッセージを飛ばす処理を追加
  • 上記をcrontabへ登録

参考

developers.linecorp.com

LINE Notifyのマイページへログイン(スマホ版のLINEで登録したものです。)し、トークンを発行

特に問題なく発行出来たので割愛

メッセージの送信テスト

参考ページ コマンドラインから LINE にメッセージを送れる LINE Notifyにもある通り、curlから簡単に試すことができた

curl -X POST -H 'Authorization: Bearer [access_token]' -F 'message=foobar' https://  
notify-api.line.me/api/notify

※ [access_token]の部分を発行されたトークンに置き換えます。([]は必要なし)

Rubyからリクエストを送信

以下のように実装してみた。

line_notify.rb

# coding: utf-8
require 'net/http'
require 'uri'
require 'openssl'

class LineNotify
  def self.send_msg(msg, token)

    if token.nil? || token.empty?
      puts 'Tokenをセットしてください。'
      return
    end

    uri = URI('https://notify-api.line.me/api/notify')
    req = Net::HTTP::Post.new(uri.path)
    req.set_form_data('message' => msg)
    req['Authorization'] = 'Bearer ' + token

    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true if uri.scheme == 'https'
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE

    res = http.start do |h|
      h.request(req)
    end
  end
end

irbでテスト

irb(main):001:0> require './line_notify'
=> true
irb(main):002:0> LineNotify::send_msg("テストです。", "ここはトークン")
=> #<Net::HTTPOK 200 OK readbody=true>

送れたみたい。

f:id:takapi86:20170102223433p:plain

ごみ収集日のお知らせテキストを作成し、メッセージを飛ばす処理を追加

schedule.rb

# coding: utf-8
require "date"
require 'json'
require 'yaml'
require './line_notify'

config = YAML.load_file("config.yml")

youbi = [:日,:月,:火,:水,:木,:金,:土]
wday = youbi[Date.today.wday]

gomi_schedule = {
  月: '燃やすごみ,燃えないごみ、スプレー缶、乾電池',
  火: '缶・びん・ペットボトル',
  水: 'プラスチック製容器包装',
  木: '燃えないごみ',
}

if !gomi_schedule[wday].nil?
  msg = "\n今日は"+ wday.to_s + "曜日【" + gomi_schedule[wday] + "】の収集日です。"
  LineNotify::send_msg(msg, config['token'])
end

config.yml

token: [ここにトークンを追加]

上記をcrontabへ登録

crontab -e

7時に通知が来るよう設定

0 7 * * * cd ~/ && ruby schedule.rb

きたぞっ!

f:id:takapi86:20170102224548p:plain

明日からゴミの出し忘れは無くなるに違いない。

ThinkPad X200に Ubuntu 16.04 LTSをインストールした(OSインストール編)

自宅のThinkPad X200Ubuntu 16.04 LTSをインストールしたので、手順を残しておく

手順の範囲

Ubuntuのイメージが入っているメディアが手元にある状態。PCにメディアをセットしインストール開始〜Ubuntuにログインするところまで

OSのインストー

Ubuntuのイメージが入っているメディアをセットし、PCを起動します。

以下の画面が表示されます。画面が切り替わるまでしばらく待ちます。

f:id:takapi86:20161217162615p:plain

f:id:takapi86:20161217162619p:plain

言語は日本語を選択し、Ubuntuをインストールを選択します。 f:id:takapi86:20161217162624p:plain

続けるを選択します。 (アップデートはインストール後に行います。) f:id:takapi86:20161217162732p:plain

ディスクを削除してUbuntuをインストールをチェックし、インストールを選択します。 f:id:takapi86:20161217162742p:plain

続けるを選択します。 f:id:takapi86:20161217162746p:plain

住んでいる場所がTokyoになっていることを確認し、 続けるを選択します。 f:id:takapi86:20161217162751p:plain

キーボードレイアウトを日本語になっていることを確認し、 続けるを選択します。 f:id:takapi86:20161217162757p:plain

あなたの名前、コンピュータの名称、ユーザ名、パスワードを入力し続けるを選択します。 f:id:takapi86:20161217162807p:plain

しばらく待ちます。 f:id:takapi86:20161217162813p:plain

今すぐ再起動するを選択します。 ※PCが再起動します。 f:id:takapi86:20161217165836p:plain

再起動後、先ほど設定したユーザ名、パスワードでログインします。 f:id:takapi86:20161217181405p:plain

ログインできたことを確認します。 f:id:takapi86:20161217181411p:plain

ターミナルを開き、以下のコマンドでOSをアップデートします。

sudo apt-get update
sudo apt-get upgrade

以上で、Ubuntuのインストールは完了です。