アヤオのレベリングキロク

日々のインプットをここでアウトプットします

【Rails】RSpecでjsonファイルを読み込んで保存するテストを書く

きっかけ

今回はjsonファイルを読み込んで、そのjsonデータを保存する実装をしていました。

RSpecも書くということでしたが(当たり前やん!)、jsonデータを読み込む時のやり方に少し躓いたので、まとめてみようと思います。

実装内容

  • 外部のファイルサービスに置いてあるjsonデータを取得する
  • その取得したjsonの中から特定のデータを今回作成した専用テーブルに保存する
  • この実装のRSpecも書く

という感じ。

RSpecを書くときは、外部ファイルサービスへのアクセスはしないでモックと/spec/fixtureに置いたファイルで対応する。

そもそもRSpec初心者の私には、 外部ファイルサービスにアクセスしないでテストする というのをどうしたらいいのかから悩む事になった。

モックを使うことを知る

こちらの記事を参考に直接外部ファイルサービスにアクセスしない方法が見えてきました。

qiita.com

ここで私が使ったのは、1番よく使われるらしい double メソッドと allow メソッド。

jsonを読みにいく処理が走るときは、/spec/fixtureに置いたjsonファイルの中身を返すというように書きました。

例えば、こんな前提

  • userに対するscoreが保存されているjsonデータが外部ファイルサービスのバケットにある
  • バケットにコールするモジュールがCallBucket
  • データを保存する処理はStoreJsonDataにかかれている
  • CallBucketStoreJsonDataの中から呼ばれる
  • テストはStoreJsonDataで保存されたsocreが正しいかを確認する
  • sample.jsonにはとあるuserのscore=3というデータがあるとする

これをテスト書いてみる

let(:json_file) { Rack::Test::UploadedFile.new("#{Rails.root}#{file_path}", "application/json") }

before(:each) do
    bucket = double('bucket')
    json_data = JSON.parse(File.read(json_file.path))
    allow(CallBucket).to receive(:new).and_return(json_data)
    StoreJsonData.new.import(bucket)
end

context "sample data" do
    let(:file_path) { "/spec/fixtures/sample.json" }
    it "save" do
      expect(user.score).to eq 3
    end
end

詰まったところ

JSON.parse(data) としていますが、最初は JSON.load(file) としていて、rubocopにセキュリティ的によくないよ!と怒られました。

ただ、単純にJSON.parse(file) ではだめで、一回オブジェクト化した状態で JSON.parse を使わなければならないことに少し時間がかかりました。

before/afterはこんな感じ

before

json_data = JSON.load(json_file.path)

after

json_data = JSON.parse(File.read(json_file.path))

JSON.parseにはファイルの中身をロードする処理はないので、事前にfile.readしてjson文字列として渡してあげないといけないということらしいです。

さいごに

まだまだRSpecは勉強中ですが、次からファイルの読み込みが必要なRSpecはすぐに書けそうです!

でも全体的にRSpecの知識が足りてないから、一回体系的にやりたいところ。。

時間を見つけてコツコツとキャッチアップできればと思います。

【AWS】EC2×dockerでRails開発環境を構築する②

前回の続きです。

ayao-tech.hatenablog.com

前回まではEC2の立ち上げでしたが、AWSのこのサービスを使うともっと簡単にできそうなことに気が付きました。。

aws.amazon.com

まだ試してないけど、やって見る価値あるかも。。

ちなみにAWS cloud9は1~2分ですべての設定をしてくれたのでとても感動的だったのですが、

静的なIPを設定できなかったので、sshが面倒で諦めました..

VScodesshする

この動画を参考にしました。拡張機能Remote - SSH にしましたが、設定内容や操作は同じようにできました。

www.youtube.com

このdocker講座シリーズ、とてもわかりやすかったので、dockerに自信ない人はおすすめ

EC2にdockerをインストール

このドキュメントのインストール部分だけやりました。

docs.aws.amazon.com

docker-composeもインストール

このあと起動するdocker-compoeのversionにあったものをインストールするようにする。

qiita.com

gitをインストールしてssh接続しておく

gitとgithubは最近httpsで接続することを推奨しているけど、EC2の場合は専用のIAM作ったりしないといけないので、sshで接続することに。

qiita.com

yarnをインストールしておく

なんだかんだ以下のコマンドで直接ダウンロードしたらエラーにならなくなった。

curl -o- -L https://yarnpkg.com/install.sh | bash    #パッケージをダウンロード&インストールされる

source ~/.bashrc  

yarn -v   #インストールできたか確認
 

docker-compose buildする

先方のアプリケーションを手順に従ってbuild

localhostを使わないでホストを変更する場合

開発環境だけどlocalhostではなく、特定のドメインホストを使うようにするところで詰まりました。

EC2内の/etc/hostsをこのようにする。

$ sudo vi /etc/hosts

127.0.0.1   xxxx.zz
127.0.0.1   yyyyyy.zz

その後、自分のPCの/etc/hostsにも以下のように設定する設定する。

この時、EC2の立ち上げの際に設定したElasticIPにして設定する。

ElasticIPが52.69.70.22だとしたらこんな感じ。

$ sudo vi /etc/hosts

52.69.70.22   xxxx.zz
52.69.70.22   yyyyyy.zz

注意点

従量課金なので業務終了時に止めないといけません。

また、swapもつけたほうが安心かもです。

【AWS】EC2×DockerのRails開発環境を立てる①

きっかけ

今回開発に携わることになったアプリケーションが今まで以上にスペックが必要ということでしたが、今持ってるMacBook Airはそこそこ新しく気に入ってるので買い替えたくない状況。。

でも実際に環境を立ててみると、CPUが足りなくてギリギリ動くけど、すごくヌルヌル。。

1クリックに1分ずつぐらいかかるので、生産的にやばすぎ…

ということで、前からやってみたかったクラウド環境での開発をやってみることにしました〜!

事前に用意するもの

  • AWSアカウントを作成
  • VScodeのアプリをインストール

手順

EC2を立ち上げる

  1. AWSコンソールにログインし、EC2を起動します。

f:id:ayao22191:20220202093007p:plain
EC2を起動

  1. OSの選択 AWS linuxにしました。ver4系をいつも使ってたのでなんとなく4系にしました。

f:id:ayao22191:20220202093735p:plain
AWS linuxを選択

  1. インスタンスの選択 私はメモリが最低16GBは必要だったので、とりあえずt2.xlargeを選択しました!

f:id:ayao22191:20220202093833p:plain
インスタンスタイプはt2.xlarge

  1. インスタンスの詳細設定 パブリックIPを有効にします。それ以外はデフォルトで。

f:id:ayao22191:20220202093957p:plain
パブリックIPを有効にする

  1. ボリュームの設定

dockerを使う時は10GBぐらいあった方がいいと書いてあったので、20GBにしておきました。

f:id:ayao22191:20220202094047p:plain
ボリュームは20GBにした

  1. タグを付ける

なんのインスタンスかわかりやすいようにNameタグだけつけておきます。

後で変更もできるし、なくても次に進めます。

f:id:ayao22191:20220202094303p:plain
Nameタグをつける

  1. セキュリティグループを設定する

私はとりあえずsshだけ開けて、必要な時に開けていったので、ここはsshだけです。

f:id:ayao22191:20220202094725p:plain
セキュリティグループの設定

結果的には以下のような感じになりました。

  • ssh / ポート:22 / [作業場所IP]/32 ←初回で設定したもの
  • カスタムTCP / ポート:3000 / [作業場所IP]/32 ←Railsアプリケーションを立ち上げるポート
  • カスタムTCP / ポート:1080 / [作業場所IP]/32 ←その他アクセスが必要なポート

  • 設定完了

このあと確認ページが出てくるので、内容を確認して、インスタンスを開始します。

数分で立ち上がるはずです。

ElasticIPをEC2に割り当てる

先程、自動的にパブリックIPを割り当てるようにしましたが、インスタンスを再起動するたびにIPアドレスが変わります。

IPアドレスが変わってしまうと後ほど出てくるVScodeからのアクセス時に毎回IPアドレスを変更しないといけないので、少し面倒です。

なので、Elastic IPを使ってIPを固定しておくと便利。

ただし、少しお金がかかりますので、節約したい人はがんばって毎回修正しましょう。

設定方法は以下の記事を参考にさせてもらいました。

qiita.com

②につづく...

【Rails】ActiveStrageを使ってみた話

きっかけ

Rails5.2からの機能だから使ったことがありませんでしたが、新規実装時にファイル添付機能をこれで実装することになりました。

その時にキャッチアップした内容を書きたいと思います。

ActiveStrageの設定

1. 専用テーブルの作成

すでにされているいる場合はスキップします。

まだ無い場合は rails g XXX を使って先に受け皿を作っておきます。

2. 環境への設定

開発はローカルに、productionはS3にするというような切り分けができる

私はproductionの設定がどうなるかわからなかったので、とりあえずベースだけ作ることにした

※とりあえず、regionは東京(ap-northeast-1)にしておく

本番保存先が決まったら、以下のことをするらしい

Active Storageのコア機能では、s3:ListBucket、s3:PutObject、s3:GetObject、s3:DeleteObjectという4つのパーミッションが必要です。ACLの設定といったアップロードオプションを追加で設定した場合は、この他にもパーミッションが必要になることがあります。

他に検討したことはこれ。

  • ミラーサービスは一旦つけない
  • gemはs3に連携する環境だけで良い気がするが、一旦localの場合も入れておく

3. レコードとS3データの紐付けをして保存できるようにする

  • レコードの紐付けはhas_many_attached :evidence_files という設定を作ったモデルに書く

4. viewに添付ボタンを作る

私は、file_fieldを使用しました。

multiple: true をつけると複数選択可能になります。

最後に

主にRailsガイドを見てやったやりましたが、わかりやすく書いてあってよかったです。

railsguides.jp

全体の流れはこのブログを参考にさせてもらいました

bagelee.com

【Rails】migrationしたりし直したりするときのコマンドまとめ

きっかけ

新しいサービスの開発をお手伝いしている時、仕様がどんどん変わり、DBの構成の変化も大きかったです。

その時テーブルのカラムの構成を変えたりすると、今あるデータをまるっと入れ替えたいことがありました。

Railsにはサクッと直してくれるコマンドがたくさんあるので、整理しておこうという感じです。

コマンド集

  • テーブルを作る
rails db:migrate
  • テーブルは消さずにデータだけ消す
rails db:reset
  • テーブルも削除して、全てのテーブルをmigrateし直す
rails db:migrate:reset

seed.rbにある初期設定のデータセットを一括で入れる

rails db:seed
  • ひとつ前のmigrationをなかったことにする
    • ※migrateする時と同じmigrationファイルの状態じゃないとできない
rails db:rollback

→これを使う時は、

  1. rails db:rollback
  2. migrationファイルを修正
  3. rails db:migrate する

の順にやらないとエラーになる

  • rollbackが使えない時

  • よく使うデータや重要な検証ケースのデータをseedに書く

  • rails db:migrate:reset をする
  • rails db:migrate をする
  • seedを流す

rails db:migrate:resetはMySQLでいうdrop tableなので、以前のmigrationファイルがどうなっているかは関係なく実行できる

※seedに書かれたデータ以外はすべてなくなるので注意

最後に

今日は簡単によく使うDB関連のコマンドをメモ的にまとめました。

自分のアプリケーションもいつか作ってみたいので、その時に活用できそうかな。

【Rails】Mailhogを使ってみる@MacOSのローカル

Mailhogを使ってみたい!

会社で使ったことはありますが、他の方が設定してくれたのをDockerでまるっと使っていました。

今回、自分で初めて設定してみて、若干つまったのでキロクしておこうと思います。

Mailhogって何?

goで作られたオープンソースのメールシステム。

githubのスターも9000件以上と最近ちょっと人気らしい。

github.com

使うメリットは、個人的にはこの辺に感じてる

  • テストメールを間違って送付してしまう心配がない
  • 簡単にインストールできる
  • 様々な環境に対応している (MacOS, Debian / Ubuntu, Dockerなど)

今回やりたいこと

今開発しているシステムはまだ駆け出しで、docker化とかもされてません。

そのため、簡単にローカルでテストしたいと思いました。

で、調べてみると、phpやdockerで使う方法はよく出てくるのですが、Railsでやる方法があまりなくちょっと困りました。

実際どうやった?

以下のような手順で進めました

1. Mailerを実装する

こちらの公式ガイドを参考にまずはMailerを用意し、メールの本文まで準備しました

railsguides.jp

2. Mailhogをインストール

GitHubのReadmeにかかれている通りにインストールします。

github.com

私はMacOSだったので、以下を実行しました。

brew update && brew install mailhog

3. sendmailの代わりのmhsendmailをインストール

readmeを見るとmhsendmailを使うというのでインストールします。

brew install go
go get github.com/mailhog/mhsendmail

4. RailsからMailhogに送付するようにする

最後にconfig/environments/development.rbに以下のような記述を追記します。

 config.action_mailer.delivery_method = :smtp
 config.action_mailer.smtp_settings = { location: '/usr/local/bin/mhsendmail', port: 1025 }

これでRailsのサーバーを立て直して、送付してみたらできました!

実際にはこんな感じ!

f:id:ayao22191:20220127204808p:plainf:id:ayao22191:20220127204759p:plain

まとめ

今回はMailerActionのsendmail_settingsのオプションを学びました!

こういった設定系はやらないと覚えないので、いい経験でした。

【Rails】トランザクションについて見直してみる

きっかけ

  • 働いていた時、特にコード規約がなく、いろんな人が触ったコードだったためか、いろんな書き方がされていた
  • 複数のレコードを同時にsaveする時に、使うのは知っている
  • ただベストプラクティスはどうなのかをちゃんと考えたことなかったので見てみる

学んだこと

そもそもエラーハンドリングの心得から勉強してみる

  • こちらの記事がとても勉強になった
  • Ruby on Railsを使って、大事なところを簡潔に書いてくれていてありがたい
  • アンチパターンと、その場合どうすべきかが書かれている

qiita.com

  • 運用のことを考えると、以下の2つの内容はすごく重要だったな。。

特別な理由がない限り、エラー情報を破棄してはいけない。 最悪の場合、エラーの原因を特定できなくなる。

 

エラー処理も必ずテストすること。 「たぶん動くはず」でそのままコードをリリースしてしまうと、最悪の場合「エラー処理のバグによるエラー」が発生してしまい、元のエラー内容が失われてしまう場合がある。(二重障害) こうなるとエラーの原因を調査するのが非常に難しくなる。

Railsトランザクションはどう使うのか?

  • 上記の記事の最後の項目が今回の内容に近い

qiita.com

  • 基本的な書き方はRailsガイドを確認

railsguides.jp

  • これらをみるとわかるように、.transactionをつけた場合は、.saveを通った瞬間DBにcommitされるわけではなく、ブロックを抜けた瞬間にDBにコミットされる
    • つまりコミットタイミングをコントロールしている
  • . lock.with_lockを使うことで、同じレコードを書き込まないようにすることもできる
    • この時はdeadlockが発生しないような処理にする

ふと思い出した他の書き方

  • 昔やってた時に、モデルに対して.transactionをかけただけでなく、以下のような書き方もしてたな〜と
ActiveRecord::Base.transaction do
  # データを作るような処理とsave
end
  • 要は同じ処理で得られる結果は同じ
    • ActiveRecord::Baseは各モデルの親クラスであるため
    • ModelでActiveRecordを継承しているものが対象

techracho.bpsinc.jp

応用編

  • その他、いろんなパターンのトランザクションの使い方は、この記事がわかりやすかった

techracho.bpsinc.jp

  • 中でもなるほど〜って思ったのが、「複数のデータベースコネクションに分散されない」ということ
    • 複数DBを取り扱ったことがなかったので知らなかった
    • 複数DBを跨いでトランザクションをかける時には、.transactionをネストにしないといけないそう

おまけ

  • ついでにコールバックも読んでみた
  • コールバックも密接に関わってくるので、また今度まとめたい

railsguides.jp

まとめ

  • Railsでのトランザクションをどうかけるべきかを改めて見てみた
  • ベストプラクティスが何か?までは答えが出てないが、こうするのが良さそうという基準は自分の中にはできた
    • 基本的にModel.transaction do ... endにすると何のモデルに対してトランザクションがかかるかわかりやすい
    • エラーは握りつぶさず、正常に通知させる