Ruby,Windows,環境構築

こんにちは、しきゆらです。
ちょっと前まで気が狂うかと思うくらい雨続きでしたが、今度は溶けるほどの暑さにやられています。
良しなに均していただけないものかと毎年思っています。

昔、【Ruby/Win】Windows上のAtomからWSL2上のrubocopを利用するという記事にて、Windows上のAtomからWSL上のRubocopを使って文法チェックを行う方法をメモしました。
しかし、現在ではうまく動かないようです。
そこで、執筆時現在動作する方法をメモしておきます。

linter-rubocopにて以下のように指定してあげるだけ。

これにて、設定完了。
では動作確認してみます。

きちんと動いてくれているようです。
これにて、Windows上にRuby環境を構築せずともWSL上のRubocopを使ってチェックすることができるようになりました。

今回は、短いですがWindowsのAtomからWSL上のRubocopを使ってコードチェックを行う方法を改めてメモしました。
同じように設定すれば、ほかのツール等も利用できるのではないかと思います。

それでは、今回はここまで。
おわり

Ruby,読書

こんにちは、しきゆらです。
今回は、人生初の洋書である「Effective Testing with RSpec 3」を購入したので、洋書ってどうなの?というところをメモしておきます。

購入した本

購入したのは、先述の通り「Effective Testing with RSpec 3: Build Ruby Apps with Confidence」です。

数年前に発売されたRSpecの本です。
RSpecの書籍といえば、長年参照されていたのは「The RSpec Book」ではないでしょうか。

これは、日本語で読める数少ないRSpecの書籍ですが、内容としてはRSpecよりもCucumberなどの別ツールの話が多いことや、バージョンが古いなどの理由で、純粋にRSpecについてきちんと学ぼうと思って買うにはちょっとなぁ、という印象でした。

そんな中で発売された「Effective Testing with RSpec 3: Build Ruby Apps with Confidenc」。
こちらは英語ではありますが、RSpec 3で書かれており、純粋なRSpecについての説明がメインなのでRSpecを学ぶ上ではよさそうな本です。

しかし、洋書ということでハードルが高そう。
私もそう思っていました。
そんな中で、なぜ買ったのかをいかにまとめておきます。

洋書を買ってみて

そもそも、洋書とはいえ今回購入したのはRuby/RSpecの本です。
小説などと違い、スクリプトが読めればたいていの内容は理解できるはずです。
そんな軽いノリで買ってみました。

あとは、以下のブログを読んだから。
【洋書Q&A】どうやって洋書を読んでるの?どれくらい時間がかかるの?という質問に答えてみた – give IT a try | https://blog.jnito.com/entry/017/09/12/072903

正直に言うと、翻訳されたら買おうと思っていたのですが一向にそのような話が出てこないので早く読みたい、というモチベが勝ってしまった形です。

実際に読んでみると、英語が苦手な私でもある程度雰囲気で理解できている印象です。
要所でサンプルコードを提示してくれているので、単語がわからないから理解できない、ということはあまりありません。
単語的にも比較的簡単なほうなのではないでしょうか。

まだすべてを読み切ったわけではないので、内容的な話は控えますが
洋書を読むというハードルは思っているより低いようです。
洋書を買うという心理的なハードルを越えることができれば、意外とすんなり進んでいけそうな印象でした。

まとめ

今回は、人生で初めて洋書を買ったので、買うまで思っていたことや読んだ印象をさっくりメモしておきました。
洋書に手を出そうかと考えている方の参考になれば幸いです。
(そもそも、情報がほぼないですが・・・)

今回は、ここまで。
おわり

Ruby

こんにちは、しきゆらです。
今回は、タイトル通り非Railsアプリのデプロイ作業をCapistranoで自動化するための手順をメモしておきます。

Capistranoといえば、Ruby製のデプロイツールです。
A remote server automation and deployment tool written in Ruby. | https://capistranorb.com/

Capistranoさん自体はRubyやRails専用ではなく、様々なものをデプロイすることができるように設計されています。
とはいえ、調べて出てくる情報の大半はRailsのデプロイについてばかり。
Sinatraのデプロイすら情報が少ないような状態です。
そんな中で、非Railsどころかただ単にCronなどで定期実行するだけの簡単なスクリプトを自動でデプロイするのを自動化する情報なんて見つけるのが難しい。

ということで、今回はフレームワークを使っていないただのRubyスクリプトをCapistranoを使ってサーバへデプロイする方法をメモしておきます。

Ruby

こんにちは、しきゆらです。
気が付けば、7月が終わっていました。

今回は、ちょいちょい書くRSpecさんの記述で見たことないものが出てきたのでメモしておきます。
タイトル通り「described_class」というものです。

こいつは何かというと、ドキュメントによると以下の通り。

If the first argument to an example group is a class, the class is exposed to
each example in that example group via thedescribed_class()method.

described class – Metadata – RSpec Core – RSpec – Relish | https://relishapp.com/rspec/rspec-core/v/3-9/docs/metadata/described-class

簡単に言えば、RSpecの最初に「RSpec.describe ClassNamedo」のように書くと思いますが、
このClassNameを参照するためのメソッドのようです。

確かに、このクラスのSpecですよ、と宣言しているので何度も参照することになるとは思いますが、まさか専用のメソッドが用意されているとは知りませんでした。

 

今回は、これだけ。
おわり

Ruby

こんにちは、しきゆらです。
なんかあれこれしているうちに今年もほぼ半分ほど過ぎていました。
早いものです。

今回は、SeleniumでAlertが表示されるまでの間待機したいことがあったので、調べて解決したのでメモしておきます。

Ruby

こんにちは、しきゆらです。
気が付くと、年が明けて2月です。

今回は、先輩に教えてもらった謎のコードを解読していきます。
謎のコードとは下記のもの。

[ruby]
ObjectSpace.each_object(ActiveRecord::Relation).each(&:reset)
GC.start
[/ruby]

いきさつ

AWS上にあるインスタンスでRailsを動かしていました。
その中で、DBのデータにミスがあったので、1.5万件ほどのデータに紐づく諸々の差し替え作業を行っていました。
このときにおこった問題としては「メモリ使い過ぎで怒られる」という状況でした。
コードを書き換えたりループを工夫したりしても解決せず困っていたとこで先輩が下記のようなコードを教えてくれました。

[ruby]
ModelClass.find_in_batches do |objects|
# 差し替える処理

ObjectSpace.each_object(ActiveRecord::Relation).each(&:reset) # ???
GC.start # ???
end
[/ruby]

このコードが入ると、途中で処理を止められることなく走らせることができるようになりました。

その当時は、先輩すげぇ~と思いながら後で調べておこうと思ってメモしておきました。
そのまま放置されていたものを思い出したので、時間のあるうちに調べてみよう、ということで調べてみました。

ObjectSpaceとは

そもそも、見慣れないクラス名です。
調べてみると、Rubyさんの組み込みモジュールでした。
リファレンスでは

全てのオブジェクトを操作するためのモジュール
module ObjectSpace (Ruby 2.7.0 リファレンスマニュアル)

とのこと。

わかるようなわからないような・・・という感じですが、定義されているメソッド類を見てみると
Rubyで定義したオブジェクトたちに対してあれこれしたり、プロファイルを取るときなどに使われるもののようでした。

ObjectSpace#each_object

その中に、ありました「each_object」メソッド。
「ObjectSpace」が何かわかれば、メソッド名で何をするものなのか大方予想が付きますね。

調べてみると、

指定されたクラスとObject#kind_of?の関係にあるすべてのオブジェクトに対して繰り返す
ObjectSpace.#each_object (Ruby 2.7.0 リファレンスマニュアル)

とのこと。
上記のコードでは「ActiveRecord::Relation」クラスのオブジェクトすべてに対する処理を行うということですね。

こいつは、ブロックを渡すとそのブロックを実行し、繰り返し回数を返すようです。
そして、ブロックが渡らない場合はEnumeratorオブジェクトを返すとのこと。
上記のコードでは、eachメソッドをつなげているので後者ですね。

なお、引数を与えなければすべてのオブジェクトに対して繰り返すようです。
また、Fixnumなどは対象外のようです。

つまり、上記コードの1行目はActiveRecord::Relationクラスのすべてのオブジェクト一つ一つに対して「reset」メソッドを呼び出していることになりますね。

ActiveRecord::Retation#reset

では、「ActiveRecord::Retation#reset」は何者でしょうか。

定義を確認してみると、内部で保持しているデータをすべて破棄しているようです。

つまり、上記コードの1行目が実行されたら、「ActiveRecord::Retationのオブジェクト」はすべて空のデータとなるようです。
1行目の内容は把握できました。
2行目を追っていきます。

GC

見たままGCでしょうね。
一応調べてみると、RubyのGCを制御するためのモジュールでした。
正確な情報は持っていませんが、イメージとしては使用していないデータを解放する仕組みという認識です。
メモリ上にあるいらないものを削除してきれいにしてくれる裏方さん。

GC#start

もう、見たままでしょう。
GC.start (Ruby 2.7.0 リファレンスマニュアル)
GCの処理を始めるためのメソッドです。

別メソッドでGCを禁止するようなこともできるようですが、このメソッドで実行した場合はGCを始めるようです。

すべてを見たうえで

[ruby]
ObjectSpace.each_object(ActiveRecord::Relation).each(&:reset)
GC.start
[/ruby]

たった2行のコードですが、知らないことが満載でした。
処理を簡単にまとめると、この処理の前までに作成されたすべてのActiveRecord::Relationオブジェクトを空にしてGCに削除してもらう感じでしょうか。

1.5万件のデータを処理していると、たくさんの不要なオブジェクトがたまっていき途中で利用できるメモリサイズをオーバーしてしまうということで、それを防ぐために一定の処理を終えると不要になったオブジェクトを削除して次に進む、ということを行っているようですね。

find_eachやfind_in_batchesなどである程度の粒度で処理することなどはわかっていましたが、それですら怒られるのでどうすればいいのかわからないところで、まさか自分でGCを動かして削除させることができるとは・・・。

まだまだ知らないことがいっぱいあるということですね。
まぁよく使うことではないとは思いますが、知っていると困ったときに役に立ちそうです。

まとめ

今回は、先輩が教えてくれた謎のコードを調べながら内容を理解してみました。
ActiveRecordなど、Githubで公開されているコードについては中身を読むことができるので、きちんと中身を知っておくことは大事だなと思いました。

 

今回は、ここまで。
おわり。

Ruby

こんにちは、しきゆらです。
今回は、文字列の先頭・末尾にある特定のものを正規表現で頑張らずとも判定する方法を知ったのでメモしておきます。

文字列の中に、特定のものが入っているかを判定するためには正規表現というものを使う場合があります。
そして、含まれているかどうかを判定するためのメソッドとして「String#match?」があります。
instance method String#match? (Ruby 2.6.0 リファレンスマニュアル)

しかし、文字列の先頭・末尾が特定のものかどうかを判定するためにmatch?を使うのはもったいないことでした。
というのも、この時にはより状況にマッチしたメソッドがあるからです。
それが「String#start_with?」「String#end_with?」です。
instance method String#start_with? (Ruby 2.6.0 リファレンスマニュアル)
instance method String#end_with? (Ruby 2.6.0 リファレンスマニュアル)

これらのメソッドは、機能はシンプルだが高速に処理できるようです。
Ruby: 文字列マッチは正規表現より先に専用メソッドを使おう
Ruby の文字列マッチ判定のパフォーマンス · Yuichi Takada

ということで、知らないメソッドシリーズでした。
ちなみに、これらはrubocop-performanceで怒ってもらえるようです。
Class: RuboCop::Cop::Performance::StartWith — Documentation for bbatsov/RuboCop (master)

今回、はここまで。
おわり

JavaScript,Ruby,Windows,環境構築

こんにちは、しきゆらです。
今回は、SimpackerというGemを使ってサーバ・フロント両方が良しなに動いてくれる環境を整えていきます。

今回はWSL2環境にUbuntuを入れ、そこで動かすことを想定しています。

Ruby,Windows

こんにちは、しきゆらです。
今回は、RSpecで同じ処理を書くことを避ける方法を知ったのでメモしておきます。

 

Railsにてテストを書いているときに、同じようなことを書いてるなぁと思うことがあります。
例えば「このAPIはユーザがいることが前提だ」となると、以下のようなことをいろんなテストで書くことになります。

[ruby]
before do
user = FactoryBot.create(:user)
end
[/ruby]

同じことを書くのは無駄ですよね。
何より繰り返し!修正面倒!!

ということで、このように特定の時に共通する処理を実行してほしい時にどうすればいいかわからなかったので、調べてみた結果をまとめます。

 

Ruby,Windows

こんにちは、しきゆらです。
今回は、RailsとRSpec、DatabaseCleanerを使った環境で起こったちょっとした問題と、(その場しのぎな)回避方法をメモしておきます。

Rails6環境で、DatabaseCleanerというGemを使ってテスト環境を毎回きれいにするようにしています。
そんな中、テストを複数実行すると2回目以降で以下のように怒られる現象に悩まされていました。

[bash]
ActiveRecord::NoEnvironmentInSchemaError:
Environment data not found in the schema. To resolve this issue, r
rails db:environment:set RAILS_ENV=test
[/bash]

 

調べてみると、これは「DBに保存されている環境情報がないから、それを設定してね」というメッセージとこのと。
https://blog.freedom-man.com/no-environment-in-schema-error

実際、上記のコマンドを実行した後にDBを確認するとしっかり情報が入っていましたが、テストを実行した後にはきれいさっぱりになっていました。

[sql]
# rails db:environment:set RAILS_ENV=test を実行した後
mysql> select * from db_name.ar_internal_metadata;
+————-+——-+—————————-+————–
| key | value | created_at | updated_at
+————-+——-+—————————-+————–
| environment | test | 2019-09-23 13:21:49.081104 | 2019-09-23 13
+————-+——-+—————————-+————–
1 row in set (0.00 sec)

# specを実行した後
mysql> select * from rts_test.ar_internal_metadata;
Empty set (0.00 sec)
[/sql]

これでは、確かに毎回設定しないといけません。

しかし、これはおかしな話です。
テストを回すたびに同じ環境情報を入れるなんて面倒でしかありません。

DBの情報がまっさらになっているので、DatabaseCleanerさんが悪さをしている可能性があります。
さらに調べてみると、(だいぶ古いですが)どうやらすでにissueとして報告されているようでした。
https://github.com/DatabaseCleaner/database_cleaner/issues/445

そして、最近これに関連したPRが動いているようです。
https://github.com/DatabaseCleaner/database_cleaner/pull/588
コードを見てみると、上記のar_internal_metadataを削除しないようにしてくれているようです。

しかし、執筆時点ではまだマージされていないので次のバージョンアップまで待ちましょう。
どうしても待てない!という場合は、diffを参考にコードを編集すればとりあえず回避することはできます。

今回はここまで。

おわり