- 開発
週刊Railsウォッチ(20190708-1/2前編)ActiveRecord::FixtureSetがめちゃ強くなってた、MacだとRubyが遅い理由、Puma 4登場ほか
こんにちは、hachi8833です。「👨🦲」という絵文字をSlackに貼ったらこんなふうにぶっ壊れたことで合字だということを知りました。
つっつきボイス:「Bald?」「人間の顔の絵文字にズラのコンポーネントをかぶせてたことが判明しました😆」「😆」
参考: 👨🦲 Man: Bald Emoji
参考: 🦲 Emoji Component Bald Emoji
「そうそう😆、Unicodeってこんなふうに複数の文字を組み合わせて合字が作れるんですよね☺️」「4人家族もパパとママと子ども2人を悪魔合体っぽく作ったりしてますね👨👩👧👦」「こういうのに長けたUnicode職人がいるんですよきっと😆」「こういう字は1文字でもバイト数めっちゃ多かったりするので、データベースにぶちこむと溢れたりすることがまれによくあるという😆」
参考: 合字 - Wikipedia
参考: 絵文字一覧(家族:family)👪 | Let's EMOJI
ほんと絵文字の話題見るたびに思うけど、よくこんなのをUnicodeの仕様として規定しようとか考えたなぁ…https://t.co/agqpX53b0r pic.twitter.com/NCNhuo1yIA
— ++C++; // 管理人: 岩永 (@ufcpp) October 26, 2017
「そういえば最近のRubyだとなんとかlengthみたいなので取れますね」
後でやってみたらscan(/\X/).length
で取れました。\X
がgraphemeの境界にマッチする正規表現だそうです。
# Ruby 2.6.3
» str = "👨👩👧👦"
» str.each_char do |i|
» p i
» end
"👨"
""
"👩"
""
"👧"
""
"👦"
#» "👨👩👧👦"
» str.length
#» 7
» str.bytesize
#» 25
» str.scan(/\X/).length
#» 1
参考: Ruby: 書記素クラスターを考慮して文字数を求める - Sarabande.jp
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
- 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください
今回は7/4(木)に開催された「公開つっつき会#12」を元にお送りいたします。おかげさまで1周年にして満員御礼となりました。お集まりいただいた皆さま、ありがとうございます!😂
⚓Rails: 先週の改修(Rails公式ニュースより)
今回は公式更新情報とコミットリストの両方からみつくろいました。公式情報が夏っぽいですね☀️🏖🏄♀️。
⚓Active Recordスキーマキャッシュのcolumns_hash
のシリアライズとパースを止めた
#35891の続き。
columns_hash
は、名前でインデックス化されたものを単純にcolumns
しているので、単にカラムのarrayからカラムを再インデックスした場合よりもシリアライズとデシリアライズがずっと遅い。
同PRより大意
つっつきボイス:「とりあえずコード見るのが早そう↓」
# activerecord/lib/active_record/connection_adapters/schema_cache.rb#L28
def encode_with(coder)
coder["columns"] = @columns
- coder["columns_hash"] = @columns_hash
coder["primary_keys"] = @primary_keys
coder["data_sources"] = @data_sources
coder["indexes"] = @indexes
coder["version"] = connection.migration_context.current_version
coder["database_version"] = database_version
end
def init_with(coder)
@columns = coder["columns"]
- @columns_hash = coder["columns_hash"]
+ @columns_hash = {}
@primary_keys = coder["primary_keys"]
@data_sources = coder["data_sources"]
@indexes = coder["indexes"] || {}
@version = coder["version"]
@database_version = coder["database_version"]
end
...
def columns_hash(table_name)
- @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
- [col.name, col]
- }]
+ @columns_hash[table_name] ||= columns(table_name).index_by(&:name)
end
「こういうHash[columns(table_name).map
みたいなのって遅くなりそうな印象あるし」「高速化系の改修ですね☺️」
⚓time.getutc
の呼び出しを削減
つっつきボイス:「まさにタイトル通り: タイムゾーンが既にUTCだったらtime.getutc
を呼ばないと」「わかりやすい〜🥰」「時刻変換ってどれぐらい重いんだろう🤔」
# activemodel/lib/active_model/type/helpers/time_value.rb#L6
module ActiveModel
module Type
module Helpers # :nodoc: all
module TimeValue
def serialize(value)
value = apply_seconds_precision(value)
if value.acts_like?(:time)
- zone_conversion_method = is_utc? ? :getutc : :getlocal
-
- if value.respond_to?(zone_conversion_method)
- value = value.send(zone_conversion_method)
+ if is_utc?
+ value = value.getutc if value.respond_to?(:getutc) && !value.utc?
+ else
+ value = value.getlocal if value.respond_to?(:getlocal)
end
end
value
end
「ベンチマークはついてないけど、実行命令数レベルでは減ってる😆」「7行から5行に🎉」「Railsはこういう地道な修正も多いですね☺️」
⚓TranslationHelper#translate
のハッシュデフォルト値の扱いを修正
translate
メソッドがHashを1つ返すことを期待する場合がある(たとえばnumber.format
キーの場合)。
そういう人は次のようにデフォルトのHashを指定する必要があるかもしれない。
translate(:'some.format', default: { separator: '.', delimiter: ',' })
このやり方はI18n.translate
メソッドで期待どおり動作するが、TranslationHelper#translate
ではデフォルト値にArray()
を適用しているのでデフォルト値が最終的に[:separator, '.', :delimiter, ',']
(というarray)になってしまう。
同PRより大意
# actionview/lib/action_view/helpers/translation_helper.rb#L60
def translate(key, options = {})
options = options.dup
if options.has_key?(:default)
- remaining_defaults = Array(options.delete(:default)).compact
+ remaining_defaults = Array.wrap(options.delete(:default)).compact
options[:default] = remaining_defaults unless remaining_defaults.first.kind_of?(Symbol)
end
つっつきボイス:「Array.wrap
ってActive Supportのヤツ?」「...arrayだったら何もしない的なメソッドだったはず↓」「やっぱAPIdock見やすい〜😋」
# apidock.comより
Array.wrap(nil) # => []
Array.wrap([1, 2, 3]) # => [1, 2, 3]
Array.wrap(0) # => [0]
「あ〜なるほど、arrayでなければ必ずarrayでラップしてくれるのね❤️」
「エッジなバグでもあったのかな?」「...このテストケースに対応してる↓: 前はハッシュが単なるarrayになってた」「おかしな変換が残ってたのね☺️」
# 同PRより
def test_hash_default
default = { separator: ".", delimiter: "," }
assert_equal default, translate(:'special.number.format', default: default)
end
「(内職中ふと顔を上げて)ははぁ、Array()
にハッシュを食べさせるとハッシュがarrayに分解されちゃうけど、Array.wrap
すればハッシュのままarrayに入ってくれるのね😋」「それだ」「arrayの中にハッシュが入ってるのが欲しかったと☺️」「translate
するならそうあって欲しいし」「よく踏んだなこれ」
後でArray.wrap
をrails c
で試してみました↓。
development» hash = { separator: ":", delimiter: "," }
#» {:separator=>":", :delimiter=>","}
development» Array(hash)
#» [[:separator, ":"], [:delimiter, ","]]
development» Array.wrap(hash)
#» [{:separator=>":", :delimiter=>","}]
⚓URL破損チェックを修正
# activerecord/lib/active_record/database_configurations.rb#L137
def build_db_config_from_hash(env_name, spec_name, config)
if config.has_key?("url")
url = config["url"]
config_without_url = config.dup
config_without_url.delete "url"
ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url, config_without_url)
- elsif config["database"] || (config.size == 1 && config.values.all? { |v| v.is_a? String })
+ elsif config["database"] || config["adapter"] || ENV["DATABASE_URL"]
ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
else
config.each_pair.map do |sub_spec_name, sub_config|
walk_configs(env_name, sub_spec_name, sub_config)
end
end
end
つっつきボイス:「ほほぅ、URL形式のデータベースコンフィグが反映されてないことがあったと☺️」「database.ymlって実はいろんな形式を食わせることができるんですけど、たしか前もウォッチで話した気がする(ウォッチ20181105)」「そうでした!」「ODBCなんかでよく使うmysql://なんちゃら
みたいなヤツもRailsでは食えるんですけど、あの手のオレオレURLみたいなのって何か名前あるんですかね?🤔」「URLスキームっぽいといえばそうだけど、どうだろね〜、なさそう😆」「たぶんRFCとかにはないんじゃないかなって😆」「雰囲気URLスキーム🤣」「このプルリクは、そういうのをパースするあたりを修正したんでしょうね☺️」
参考: URL schemeの一覧
「そういえばRails 6の例のマルチプルデータベースで、複数DBをURL形式で指定した場合ってどうなるんだ?みたいな話をどっかで聞いたんですけど」「定義ごとにURL持つみたいな形になるだろうからどうってことなさそうだけど?🤔」「自分はdatabase.ymlでURL形式で書くときに別途ユーザー名とかパスワードとか書かないんですよね😆」「あ、Rails 6からdatabase.ymlが1階層増えるから、ユーザー名とかパスワードをバラして書かずにURLにまとめて書いたときに個別のユーザー名とかパスワードとかがどう扱われるかってことか😳」「まあそんな感じで☺️」
「今の話を簡単に説明すると、Rails 6でマルチプルデータベースにしたときのdatabase.ymlでURL形式で書いたときにどうなるかという話です: 今Sublimeで記憶ベースで要点部分だけ書いてみますけど、普通はこんな感じ↓」
production:
primary:
host: 'hoge'
port: '3306'
user: 'user1'
password: 'pass1'
replica:
host: 'huga'
port: '3306'
user: 'user1'
password: 'pass1'
「それをたとえば以下↓みたいに書いたときにどう扱われるか、みたいな話です」
production:
primary: 'mysql://user1:pass1@hoge:3306/test'
replica: 'mysql://user2:pass2@huga:3306/test'
「まあ移行するときに頑張れば済むんですけどー」「そうそう、書き換えれば頑張れる😆」「以上雑談でした😆」
⚓オートロードガイドに追記
# guides/source/upgrading_ruby_on_rails.md#L273
+#### Having `app` in the autoload paths
+Some projects want something like `app/api/base.rb` to define `API::Base`, and add `app` to the autoload paths to accomplish that in `classic` mode. Since Rails adds all subdirectories of `app` to the autoload paths automatically, we have another situation in which there are nested root directories, so that setup no longer works. Similar principle we explained above with `concerns`.
+If you want to keep that structure, you'll need to delete the subdirectory from the autoload paths in an initializer:
+ActiveSupport::Dependencies.autoload_paths.delete("#{Rails.root}/app/api")
+In `classic` mode, if `app/models/foo.rb` defines `Bar`, you won't be able to autoload that file, but eager loading will work because it loads files recursively blindly. This can be a source of errors if you test things first eager loading, execution may fail later autoloading.
+In `zeitwerk` mode both loading modes are consistent, they fail and err in the same files.
つっつきボイス:「例のZeitwerkの記述がちょっと増えたようです」「そういやZeitwerkって、おれドイツ人じゃないから発音わかんないんでホットロードだったか何か別の名前になるんじゃないかってRubyKaigiあたりの発表で誰か言ってたような気が😆」「マジで😆」「まだ名前変わってないかな😆」「ツァイトヴェルクってそんなに言いにくいかな〜😆」
「ちなみにZeitwerkについて簡単に説明すると、Rails 6でオートローダー周りがZeitwerkというものに変わるんですね: 一応従来のローダーも使えるけど多少コンフィグが必要で、あとSTI使ってると場合によっては読み込み順を明示的に指定しないとエラーになることがある、みたいなことが移行ガイドに書いてますね」
参考: 「シングルテーブル継承 (STI)」Active Record の関連付け (アソシエーション) - Rails ガイド
そういえばKraftwerkもドイツ語ならクラフトヴェルク(=発電所)ですが、例のバンド名はみんなクラフトワークって英語風に呼んでるっぽいですね。
⚓Rails
⚓Puma 4が登場(Ruby Weeklyより)
つっつきボイス:「お〜Puma 4出ましたか!」「@schneemsさんの記事なんですが、ご本人のツイートのコラ動画が何だかかわいくて↓😍」「字幕が『ずっと一緒だよ』って😆」「そして3.42と4.0が泣き別れ😆」「3系はサヨナラと😆」「ズッ友って言ったのにって😆」「Reactorとかいう部分が変更されてAPIがちょい変わったみたい」
By the coders who brought you Llamas in Pajamas. A new cinematic Ruby server experience. Directed by @evanphx, cinematography by @nateberkopec, produced by @schneems.
Introducing - Puma: 4 Fast 4 Furioushttps://t.co/06PG0lzubk pic.twitter.com/O1dLfwnctJ
— Richard Schneeman 🤠 (@schneems) June 25, 2019
⚓RailsのWebサーバーあれこれ
「ついでにゲストの皆さんにお伺いしますけど、Railsで使ってるWebサーバーは何ですか?: じゃあなさそうなところから、Mongrelの人!」「え何それ?😆」「いないか😆、じゃWEBrickの人」「これもいないかな😆」「じゃPassengerの人!」「お〜1人いますね」「じゃUnicornの人!」「1人、いや2人😆」「じゃPumaの人!」「お〜多い」「...Thinは?」「そういえばThinってありましたね😆」「これもなつかしい😆」「Thin使ったことあった😆」
参考: WEBrick互換の軽量WebサーバMongrel - [Ruby on Rails/Ruby] ぺんたん info
参考: library webrick (Ruby 2.6.0)
参考: Passenger - Enterprise grade web app server for Ruby, Node.js, Python
参考: unicorn: Rack HTTP server for fast clients and Unix
参考: macournoyer/thin: A very fast & simple Ruby web server
「やっぱり今だとデフォルトのPumaですねよ: Pumaもマルチプロセス・マルチスレッドで設定すれば全然速いですし苦労しなくなったし😋」「みんながPumaを使うようになったことでスレッドセーフに書くことを心がけるようになったであろうというか😆」「みんなどこまでスレッドセーフって意識してるのかな、なんて😆」
「ついでに、Railsアプリでマルチスレッド周りのバグを踏んだことのある方は?Railsそのものじゃなくてアプリケーションのコードを書いたときに」「お、意外にいない😆」「オレはあるっ🤣: あれを一度でも踏むと、クラスインスタンス変数とかを見かけたときにコイツメ〜という気持ちになりますよもう🤣」「🤣」「🤣」
参考: 【まとめ】インスタンス変数、クラス変数、クラスインスタンス変数 - Qiita
「そういえばUnicornのgraceful restartがどうやってもグレースフルになってくれなかったことあるし😇」「間違った方向に苦労してたりとか?😆」「手動でシグナル送ったりもういろいろやりましたよ〜😭、Capistrano書き換えてstop/startしたり」「そういうときは念のため数秒待つ🤣」「そうそう🤣、そしてstopしたらps
でマスタープロセスがちゃんと死んだのを確認してからリスタートするコードとか書きましたし😭」「Unicornだとちゃんと死んでくれないマスターっていたな〜😆」「Pumaにしてからまったくそういう経験ないっすね😋」「Pumaが突然死したんでUnicornに替えたことならあるといえばあるけど😇、今ならインスタンス増やすとかすればいいんだろうし」「今はメモリだけ軽く監視しとけばいいかみたいな☺️」
⚓Action TextをJSなしでテストする
つっつきボイス:「Action TextはRails 6で入ることになっているWYSIWYGエディタですね」「BasecampのTrixエディタを使ってるヤツ」「そのAction TextをJSなしでテストすると」「has_rich_text :content
?😆」「そんな感じのDSLが入ってたかも」「Action Textまだ使ったことないし😆」
参考: Trix: A rich text editor for everyday writing
週刊Railsウォッチ(20181009)Rails 6の新機能:WYSIWYGエディタ「Action Text」、Rails 6の青写真スライド、Apache POIはスゴイほか
「ところでRailsでWYSIWIGエディタやるときって皆さん何をお使いですか?」「redcarpetかな〜」「redcarpetはよく使われますよね☺️」「く、このリポジトリにデモがない...😇」「デモがないあたりが昔のgemっぽい😆」
例のRailscasts↓に動画チュートリアルがありました。
参考: #272 Markdown with Redcarpet - RailsCasts
「他のWYSIWIGエディタを使ってる方は?」「んーと、フロアーラみたいな名前のWYSIWIGエディタをこないだ社内で使ってたかな」「お、これですね↓」「そうそう、Froala Editor」「うん、ひととおりのリッチテキスト使えるっぽい😋」
参考: イカすwysiwygエディタFroalaをRailsに5分で導入
「Floala、新し目でオシャレ感あるし😘」「エディタでUndo使えるし!」「有能👍」「ところでこの記事↑でもFloala使ってるけど、gem 'wysiwyg-rails'
でインストールって😆」「なんつう名前😆」「どーなんだろう、他のWYSIWIGをもラップする神gemな匂いありそうだけど😆」「この名前空間取るってどうよ😆」「これは攻めてる⚡️」「攻めてる🔫」
「Floalaのリポジトリ、2 years agoみたいなのがそこそこ目につく😆」「Floalaは一応商用がメインでライセンス料払うみたいな形式なんだけど、オープンの方の更新は微妙かも?🤔」「ということはRails専用とかではないわけですね😋」「まあnpmとか使わずに入れられるという感じなんだけど、今なら無理して使わなくてもいいんじゃね?って😆」
「...CKEditorってのもあります😆」「CKEditorなら使ってもいいかな〜: でもWYSIWIGって外部ライブラリを使うと結局サーバー側でアップロードとかの実装を自分でやらないといけないのが面倒なんですよね😭」「昔はCKEditorぐらいしかまともなのがなかった🧐」
「こういうライブラリはどこまでまともなHTMLを吐いてくれるかが気になりますし😆」「自分の職場のiOSエンジニアがですね、世の中にはIMEで変換中にうまく動かないWYSIWIGエディタが多いってボヤいてましたよ」「うお〜それはキツそう😅」「もうね、気の毒すぎてお疲れさま以上の言葉をかけられないっすよ😆」「尊い犠牲😆」「WYSIWIGって入れようとするとホント大変だし、家帰りたくなるし🏠」「何回車輪を再発明したら気が済むんだの世界😆」
「Floala、割と良さそうな印象❤️」「wysiwyg-railsって名前を除けば🤣」「そうそう🤣」
⚓hanmoto: 静的ページをhamlなどで書けるgem
弊社ソニックガーデンで愛用しているgemの紹介です。hamlを使ったり、application.cssを使い回したりして、動的に404ページや500ページが作れるようになります。便利!!
Railsでpublic以下の静的ページ(404.htmlといったエラーページ等)をデプロイ時に生成するgemを作った https://t.co/tCwsuRM3Rs— Junichi Ito (伊藤淳一) (@jnchito) July 2, 2019
// 同リポジトリより
// app/views/public_pages/404.html.haml:
- provide(:title, 'Not found')
%h1 Not found
%p This webpage is not found.
%p= link_to 'Home', root_path
つっつきボイス:「@junchitoさんの会社で使われてるhanmoto(版元)というgemだそうです」「お、こういうの以前にも見たような?🤔」「昔からあるといえばある☺️」「hanmotoのリポジトリで、gakubuchi↓にヒントを得たってありますね😋」「自分が覚えてたのはgakubuchiじゃなかったけど、こういうのはみんな一度はやりたくなるヤツ😆」
「Railsのエラーページを動的にやろうとすると『Railsが死んだときにエラーページを出せない』という問題がありますよね😆」「😆」「なのでエラーページは静的にする必要があるんですが、でもエラーページでCSSとかJSとかも読み込もうとするとアセットのファイル名についてるシグネチャとかをちゃんと扱えないと困る: hanmoto gemはそこらへんをいい感じにやってくれるんでしょうね☺️」「Rails自身はそういう仕組みってないのかな...😅」「Rackが死んだときに表示するエラーはRailsでは対応できませんね🧐」「Railsで404.erbみたいなのが欲しいってこと?」「ああ、エラーページのerbをプリコンパイルするのをRails自身が持ってればいいのにってことね: まああればあったでいいかも☺️」
後で探してみたら、hanmotoと少し似た感じのrambulanceというgemもありました。
参考: 【動的VS静的】Railsの404/500エラーページ 静的の勝利 - 珈琲駆動開発
「まあエラーページを何もカスタマイズしてないRailsアプリが本番でデフォルトのエラーページを表示すると、なかなかショッキングな色合いが目に飛び込みますし🤣」「赤い赤い真っ赤っ赤🤣」「そしてむっちゃ電話かかってくる📞」「赤は刺激が強いかも😅」「その意味で、必要以上にユーザーを驚かせないエラーページを作っておくというのは大事だと思います🧐」「それが身を助けますね☺️」「いつだったかAmazon Payでいつもの真っ赤な画面が出てましたし↓」「😆」
Railsじゃんw
— アルフォートおじさん (@joker1007) November 12, 2018
「ユーザーがあの赤い画面を見ると『オレ、壊しちゃった?😱』って気持ちになりがちですし😆」「『大丈夫だよ』がユーザーに伝わるエラー画面が重要😆」(以下延々)
⚓初心者向けRSpecの書き方
つっつきボイス:「メドピアさんの記事ですけど、例のRSpecえかきうたと合わせて読むとちょうどいいのかなと思って」
「『describe/context/itのフォーマットが統一されていない』のコード例は確かに悲しい😭」
# 同記事より
# contextが日本語だったり英語だったり式だったり
describe 'valid?' do
context '非公開のとき'
context 'publish'
context 'expired < Time.now'
end
# itに条件が書かれている
describe 'valid?' do
it '◯◯で△△のとき、回答が登録されること'
end
「『letで定義した変数名が、何を表しているかわからない』、自分はlet否定派だけど😆」「なぬう😆」「とはいえ変数名を考えるのってめっちゃ時間かかるし、件数が多くなるとどんどんつらくなってくるし😇」
「『beforeブロック内でテストを行う』、これはやっちゃダメでしょ😆」「書こうと思えば書けちゃうのか😱」
「『テスト対象が同じ、複数のテストケースで、subjectが使われていない』、subjectを使うと読みやすくなるのかというと個人的には疑問ありますが🤣」「subjectがきれいにキマるといいんだけどね〜😎」「キマるとかっこいいけど、今のsubjectってどれ?っていちいち上の方見に行かないといけないのってどうかな〜って😆」「subjectが遠くなるとつらくなる😭」「結局ネストするとつらくなってくるし😅」「ネストとともに地獄感高まる👹」
⚓みんなでうたおう『RSpecえかきうた』
「うちのメンバーが書いてくれた『RSpecえかきうた』記事↓、自分もイチオシなので😆読んだことのない方はぜひ一度どうぞ」
「前半は普通にit
でテストケースを並べて、expect
書いて、前提条件を書いて、ってやってくわけですね」「どきどき😋」「で後半は『やっぱ離れてると読みにくいよね』って途中からit
をまとめてって、context
もit
に合流させて、するとsubject
がないのにit
って何だかヘンだからit
やめてtest
って名前にしましょって😝」「するとRSpecがあっという間にかわいいアレのようになってしまうわけですよ🤣」
「こ、これは🤣」「mとiとnとiがつくテストに🤣」「えかきうたがいつの間にか替え歌に🤣」「そりゃもうアンチでしょ〜😛」「自分は好きっ🤣」「業務のテストコードってdescribe
とかきれいに書けないことの方が多いくらいだし、もうこれでいいんじゃね?って思うことは、 多々ある😆」
「このRSpecえかきうた記事、Twitterでもはてブでも『最高です』というコメントだけが付いてました😆」「もうアンチしかいない🤣」「みんなアンチ🤣」「RSpecで疲れた人にひとときの安らぎを☺️」「一服の清涼剤🍹」
とっても引用したい動画があったのですが、やめときます😅。察してください。
「ちなみにRSpecでテスト書いてる人は?」「やっぱRSpec多い」「minitestの人は?」「お、いますね〜😳」「他のテストフレームワーク使ってる人は?」「さすがにいない😆」「ZenTestとかは?」「今は亡きZenTest😆」「いないかやはり〜😆」
- リポジトリ: seattlerb/zentest
⚓ActiveRecord::FixtureSet
がスゴくなってた
↑もともと上の記事を取り上げようとエントリしてたのですが、FixtureSetの話があまりによかったので組み替えました。
つっつきボイス:「そうそう、今日のミーティングで出た話なんですけど、テストデータをFactoryで管理するのとFixtureで管理するのとでそれぞれメリットとデメリットがあるよねって」
- リポジトリ: thoughtbot/factory_bot: A library for setting up Ruby objects as test data.
- フィクスチャのしくみ: Rails テスティングガイド - Rails ガイド - Railsガイド
⚓fixtureとfactoryについて
「fixtureとfactoryは皆さんご存知ということでいいでしょうか?」「fixtureは知らない人いるかも?🤔」「fixtureはRailsが最初から持っている仕組みで、fixturesというフォルダの下にyamlファイルを置いておくとテスト時にそれがいい感じに1行ずつ展開されてデータベースに入ってくるヤツで、生データを置いているような感覚に近い」
「factoryは、基本的な定義はfactoryに書くんですけど、実際のデータはコードで作ります: factory create
とかtrait
とかを使ったりすることもあります」
「ついでに皆さんどっちをお使いでしょうか?、まずはfixtureの人」「ほいっ」「おお、fixtureなんですか?」「オレfixtureでしか書かないし😆」「へ〜、以前factory_bot(当時はfactory_girl)の記事とか書いてたのに😆」
「factory_botって、テストが増えてくるとすごく遅くなるのが問題なんですよ🐢」「あ〜」「一方fixtureを全部グローバルなfixtureでやれると、最初にガッと全部読み込んで後はトランザクションでやれるから速い🐰: その代り、そのガッと読み込みを実現するまでの作業はつらい😇」
「整合性のあるfixtureを書くのってつらくないですか?」「そこはまあそんなに大変でもないんですけど、大変なのはグローバルfixtureだと全部入ってくるんで本番データでテストしているみたいな感じになってくるところで、User.first
とかUser.last
みたいなのが気軽に使えなかったりとか」「なるほど〜」
「fixtureでやるときは、考え方をfactoryのときとは変えた方がいいというのはありますね🧐: たとえばファイル名を変えてそのときだけロードするみたいな設計をある程度最初から考えとかないといかないとか」「このテストコードではこっちのfixture、あちらでは別のfixtureみたいな感じですね」
「でもfixtureって自由度高く書けるから、昔ほどアンチにすることもないかな〜って: 初期段階からやれるんならfixtureでやってみるといいんじゃないでしょうか😋」
「続いてfactoryの方は?やっぱりいますね: 反論オーケーですよ😆」「そうですね、fixtureだとどんなふうにデータが入ってくるかとかって、特にチームに初めて参加する人はfixtureを全部見ないとわかりにくいかなと思うんですよね」「たしかにfixtureだとidも生idが入ったりしますし🤔」
⚓ActiveRecord::FixtureSet
がいつの間にかスゴくなってた
「それがですね、ここだけの話、実はfixtureにはActiveRecord::FixtureSet
といういいものがあるんですよ奥さん❤️」「ほほぅ?」「多くの人はfactory_girlにやられる前のfixtureしか知らないかもしれないんですけど、以下のサンプルコード↓を見るとだいたいわかります」
「え、ダイナミックfixtureとかある?」「ナウいfixutureにはあるんですよ〜😆」
<!-- api.rubyonrails.org より -->
<%Q1.upto(1000) do |i| %Q<
fix_<%= i %>:
id: <%= i %>
name: guy_<%= 1 %>
<% end %>
「つかもうちょっと下のアドバンストfixtureの方がむしろ普段づかいするところで、たとえばidなんか自動生成してくれるし↓」
george:
id: 1
name: George the Monkey
reginald:
id: 2
name: Reginald the Pirate
george: # generated id: 503576764
name: George the Monkey
reginald: # generated id: 324201669
name: Reginald the Pirate
「belongs_toなんかもこうやって扱えるし↓」
### in pirates.yml
reginald:
id: 1
name: Reginald the Pirate
monkey_id: 1
### in monkeys.yml
george:
id: 1
name: George the Monkey
pirate_id: 1
「おお、末尾のpirate: reginald
でreginald:
を参照してくれるのか😳」「だからfactory_botとかでリレーション作るよりは楽ですね〜😋」「たしかに〜😍」
### in pirates.yml
reginald:
name: Reginald the Pirate
monkey: george
### in monkeys.yml
george:
name: George the Monkey
pirate: reginald
「結局factory_botの何がつらいって、複雑なリレーションや入れ子になったデータを作るのがつらいんですよね😭」「相互参照も地獄感あるし😈」「それ、誰かが作ってくれてればいいんですけど😆、自分でつくるのはホントしんどいし、あったらあったで今度はそれが要らなくなったときに困るんですよ😭」「それを消すと?」「他のテストに影響出ますね〜😢」「createすると思わぬところで使われたりとか😅」
「fixtureの続きを見ると、ポリモーフィックbelongs_toもやれるし!」
### in fruit.rb
belongs_to :eater, :polymorphic => true
### in fruits.yml
apple:
id: 1
name: apple
eater_id: 1
eater_type: Monkey
「has_and_belongs_to_manyもやれますし😋」
### in monkeys.yml
george:
id: 1
name: George the Monkey
fruits: apple, orange, grape
### in fruits.yml
apple:
name: apple
orange:
name: orange
grape:
name: grape
「ラベルのinterpolation(式展開)もやれますし😋: まあこのラベル名が何やら一意の整数値になってるみたいなんで、スペルミスするといつまでたっても関連付けられなくて他のが関連付けられちゃう、みたいなつらいこともありますけど😆」「何だかマクロ的に展開される?」「まあそんな感じで」
geeksomnia:
name: Geeksomnia's Account
subdomain: $LABEL
george_reginald:
monkey_id: <%= ActiveRecord::Fixtures.identify(:reginald) %>
pirate_id: <%= ActiveRecord::Fixtures.identify(:george) %>
「YAMLのdefaultsも使えますよ〜😋」「おぉっ!」「ただこれは大文字のDEFAULTS
にしないと普通のラベルとして解釈されちゃうとか、arrayでしか使えないみたいなのがあって、fixture使っている人が少ないせいか、ドキュメントにそうやってポロポロ穴開いてますし😆」「なるほど、DEFAULTS
が予約語になってると」「これはもっと使ってフィードバックしないと!」
DEFAULTS: &DEFAULTS
created_on: <%= 3.weeks.ago.to_s(:db) %>
first:
name: Smurf
*DEFAULTS
second:
name: Fraggle
*DEFAULTS
「fixture model classもアンスコ付きの_fixture:
でやれるし: これを使ってたとえばusersとerror_usersみたいにファイルを用意しておいて、specごとにfixture
コマンドで呼ぶことでテストケースを分けたりできますし」「は〜なるほど!、fixtureはデフォルトで実在のモデル名を使うけど、モデル名じゃないのを_fixture:
で定義しておくと読み込めると」「読み込んで、さらにグローバルfixtureとは別にこっちを使ってねって指定できる」
_fixture:
model_class: User
david:
name: David
「いやだ何これスゴい」「やべ〜、こんな賢いfixtureが書けるなんてっ🥰」「fixtureでここまでできるとfactoryってあんまり要らなくて、むしろトランザクションだけでたいてのテストが書けるっつーか☺️」
「しかしいつの間にこんなにスゴくなったんですか?」「まあ少しずつ増えてたみたいで、Rails 5の頃にはだいたい今のような感じになってたかな〜😋」「何だか『眼鏡を取ったらスゴい美人だった』みたいな😆」
「そういうのを調べるにはやっぱりAPIdock↓に限る💪」「なるほど、4.0.2から入ったと」「APIdockだとその機能がいつから入ったかがわかるのがとってもいい😘」「ラベルのinterpolationとかERB直接扱えるようになったのは割と最近だった印象ですけど」
「fixtureは、たとえばステージングより前の段階でQA用のサンプルデータを作ったりするのにもよかったりしますね❤️: fixtureってステージングでもdevelopment環境でも使えるんで、fixtureでテストケースをばばっと書き並べておくとか、本番からfixtureにダンプしてそこから削っていくとかみたいな用途ならfactory_botよりもいいかなって🥰」
「あとfixtureのデータの整合性の問題ですけど、いったん全部ぶっこんでからvalidかどうかで流せばいいだけなんで大したことないと思うし」「たしかに!整合性はたまにチェックすればいいぐらいかと😋」「そうそう☺️」
「そういうわけで皆さん一度ActiveRecord::FixtureSet
はチェックしてみるとよいかと🕶」「少なくともDHHはfixture派だろうし、そこは譲らないだろうし😆」「今日からfixture派!😍」「きっとCIが早く終わって地球に優しいっすよホレホレ🌎」「このAPI翻訳しようかな✨」
⚓その他Rails
つっつきボイス:「Deviseの動きをトラックするgemみたいです」「どちらかというと証跡ログっぽいかな?」「Geocodingとかもあるから、こいつはどこからログインしたのかみたいなのをデータベースに入れてくみたいな」「上の方のHow It Worksあたりに書いてあるかな〜: ログインの成功/失敗とかIPアドレスとかを記録できるから、やっぱり証跡ログかな」「この手のログは提出を求められることがよくあるし、とりあえず入れとけ感あるかも☺️」「GeoIPとか入れとくと、やたら中国からアクセスあるなみたいなのも取れるし😆」
参考: GeoIP2 Databases | MaxMind
つっつきボイス:「これは来週7/10に渋谷で開催されるTokyo Rubyist Meetupという英語話者を前提としたミートアップなんですけど、誘われたのでちょっくら行ってきます↓」「そういえば前からやってますね〜☺️」
I want to join 7/8's #ossgate, 7/9's @asakusarb and 7/10's @trbmeetup. Any #rubyfriend will be there too? 😄
— Stan (@_st0012) June 29, 2019
「scenicか久しぶりに聞いたな〜」「この間の銀座Rails↓でもscenicのこと話しましたし😆」「そうそう😆」「ミートアップのお題もdatabase viewsだから怖いくらい既視感ありますね😆」「やっぱデータベースでやった方が速いことはデータベースでやろうよって🤣」
⚓Ruby
⚓impersonator: オブジェクトのやりとりを記録/再生(Ruby Weeklyより)
# 同リポジトリより
calculator = Impersonator.impersonate(:add, :divide) { Calculator.new }
つっつきボイス:「impersonateって『偽装する』でしたっけ」「なるほど、オブジェクトインタラクションを記録して再生すると」「なんかVCRって文字見えたし😆」「VCRってVideo Cassette Recorderだからビデオデッキでしたっけ」「VCRってgemがあるんですよ↓」「そうそう、HTTPリクエストを記録して再生するヤツ」「HTTPSpyなんてのもあったような」「impersonateはそういうののメソッド版というか」
「しかし何でまたこういうの作ったんだろ🤣」「やりたかったからとか😆」「VCRにインスパイアされたってあるけどどうしてこうなったというか😆」「使いみち何かあるかな〜🤔」
⚓MacだとRubyが遅い理由(Ruby Weeklyより)
つっつきボイス:「最近Mac使ってないからわかんないけど😆Ruby遅いの?」「Discourseが出してたベンチマークでもMacが遅いって出てたの見たな〜: WindowsでVM使うより遅いって」「えぇそんなに遅い?😅」「お、記事にもまさにそのツイートが↓」
We benchmarked how long it takes to run the Ruby test suite for Discourse across our various dev machines. I can not believe what a crazy tax I have paid over the years insisting on sticking with Windows, highlighted results mine. pic.twitter.com/mEKzh5ac18
— Sam Saffron (@samsaffron) March 29, 2019
「リストの上の方がMacで独占されてるし🤣」「だっはっは🤣(マカーだけど)」「これはひどい🤣」「Win10のVMwareで動いてるLinuxにすら負けてるし😇」「か、悲しい😭」「どうしてこうなった😇」
「下の速い方を見るとさすがに素のLinuxのRubyが速いですね」「しかしディストリで差が出ているのはなぜ?って思うし😆」「デフォルトのカーネルパラメータとかが関係してそう」「果たしてMacのSSDが原因なのか、それとも何なのか」「上と下でダブルスコアぐらい違ってますし😆」「@samsaffronさんの記事↓はタイトルではWindowsの話かと思ったらMacの方が遥かにやべーじゃねーかって😆」「WindowsのHypervisorはベンチ入ってないのかな?」
参考: Why I stuck with Windows for 6 years while developing Discourse
「MacのRubyが遅いのは、果たしてMach-Oバイナリだからなのか、それともmacOSのHypervisor層が遅いのか、いろいろご意見が出そうではありますね🤔」「どうなんだろね〜?」「もしMach-Oが原因なら、Hypervisor層でLinux動かしてそこで動かせばもう少しマシになったりして」「どっちにしろ遅い🤣」「とほほ🤣」「それにしても負けすぎにもほどがあるというか😆」
「というわけで皆さんWindowsマシンに乗り換えましょう」「🤣」「🤣」「おやこんなところにWindows機が😝」
追記(2019/07/08)
上の測定結果はあくまで雑談の参考どまりとお考えください。
⚓Rubyの「Direct Instruction」
Direct instruction marking in Ruby 2.6 https://t.co/dYKc2vs4kI
— Stan (@_st0012) June 20, 2019
つっつきボイス:「@tenderloveことAaron Pattersonさんの記事ですが、いつもの自分のサイトではなくGitHubのブログに書いてますね」「こういうアニメーション↑作ってるのはすげー」「@tenderloveさん、最近GIFアニメに入れ込んでますね: 自分もTechRacho記事でこういうのやりたいなと思いつつ😅」「どんなツール使ってるんだろ?」「聞いてみたら案外パワポかも😝」「🤣」「案外その方が楽だったりして😆」
「ASTツリーとかもみっちり書いてる↓」「Rubyは新しい技術をこうやって解説してくれる人がいるからいいですね〜😋」「Linuxだと最新技術を学べる書籍がそもそも最近なくなってるし😇」「Linuxの最新カーネルなんかだと、最近は大学の研究室なのか誰なのか結構みっちりドキュメント書いてたりするんですけど、どれを信用していいのかよくわからないという🤣」「🤣」
⚓その他Ruby
つっつきボイス:「最近流行りのRuby型チェッカー」「まだちゃんと読んでませんが、型チェックについて思うところを書き連ねてますね」
「Rubyの型チェッカーをJetBrainsのIDEとかが採用するようになるとまた世の中が変わってくるかなって」「JetBrainsのIDEなら、型チェッカーがない今でもめちゃうまく回ってるんじゃないかって😆」「ですよね、今でもIDEがYardのドキュメントとかを参照して引数とか型のヒントとか出してくれますし😋」「取りあえず押せば何か出てくる😆」「Yardでちゃんと書かれてさえいればヒント出してくれるとかホント神がかってるし⛩」「Rubyの型チェッカーはむしろVSCodeとかの方がうれしいんじゃ?」
論文読んだメモを残した | VM命令ディスパッチ手法: Context Threading https://t.co/0mfiN0l8MP #Qiita
— k0kubun (@k0kubun) July 1, 2019
つっつきボイス:「@k0kubunさんのメモ書きだそうです」「そういえばRubyのVMを書くためにJVMを書いてみることにしたとかスゴいことをやってるらしき」「マジで😆」
↓こちらのようです。
参考: セルフホストで学ぶJVM入門 - k0kubun's blog
⚓Quoraより
- 元記事: Rubyではimmutable string literalといった非互換な機能の導入はどのようなプロセスを経て決定されますか?
- 元記事: Rubyのブロックは本当に素晴らしい発明だと思いますが、他の言語はそれを追随していますか? - Quora
つっつきボイス:「最近QuoraでMatzの回答を読むのが楽しみで😋」「Rubyのグルがこうやって一次回答を示してくれるのはいいですね〜☺️」「しかも日本語で🇯🇵」「Quoraだと英語ロケールは自動翻訳されるのかな?🤔」
「Rubyのブロック、そういえば他でほとんど見かけないですね🤔」「Rubyのブロックのよさは、何といってもブロックを1個しか取らないと決めたことでしょうね〜」「それはある!👍」「おかげで記法が複雑にならずに済んだし😋」
前編は以上です。
おたより発掘
https://t.co/GViJnfy3kM
最近「Passengerって何ですか?」って質問された。— さく (@sakuro) July 9, 2019
バックナンバー(2019年度第3四半期)
週刊Railsウォッチ(20190701)RMagickのメモリ使用量が劇的に改善、インスタンス変数の定義順で速度が変わる?、GitLab CIランナーをローカルで回すほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSなど)です。