- Ruby / Rails関連
週刊Railsウォッチ(20191015)スライド「Rails Performance issues and Solutions」を見る、dirtyに*_previously_was が追加、Sidekiq 6.0.1ほか
こんにちは、hachi8833です。台風前のつっつきでしたので、エントリを減らし気味にしてみました🙇。
台風19号の最大瞬間風速は75mらしく、これを時速に直すと270km/h。
東海道新幹線の最高速度が285km/h、飛行機の離陸時の速度が240〜300km/hらしいので、新幹線や飛行機並みのスピードでいろんなものが飛んできます。ヤバい。https://t.co/iKTTEgf5Nzhttps://t.co/Ic8ky9GPsUhttps://t.co/Ic8ky9GPsU pic.twitter.com/Y5TpNX2Dvg— Junichi Ito (伊藤淳一) (@jnchito) October 9, 2019
つっつきボイス:「jnchitoさんは関西の方でしたね☺️」「map
で風速を秒速から時速に変換してる」
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
- 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください
⚓Rails: 先週の改修(Rails公式ニュースより)
今回は公式情報を中心に見繕いました。
なお、6.0.1マイルストーンも見てみましたが、「再現できない」「自分のコードが間違ってた」でcloseされているケースがいくつか見当たりました。
⚓新機能: dirtyトラッキング用 *_previously_was
を追加
なおマージされたのは8/2です。
# 同PRより
pirate.update(catchphrase: "Ahoy!")
pirate.previous_changes["catchphrase"] # => ["Thar She Blows!", "Ahoy!"]
pirate.catchphrase_previously_was # => "Thar She Blows!"
# activemodel/lib/active_model/dirty.rb#L125
included do
attribute_method_suffix "_changed?", "_change", "_will_change!", "_was"
- attribute_method_suffix "_previously_changed?", "_previous_change"
+ attribute_method_suffix "_previously_changed?", "_previous_change", "_previously_was"
attribute_method_affix prefix: "restore_", suffix: "!"
end
...
+ # Dispatch target for <tt>*_previously_was</tt> attribute methods.
+ def attribute_previously_was(attr_name) # :nodoc:
+ mutations_before_last_save.original_value(attr_name.to_s)
+ end
API: ActiveModel::Dirty
参考: 1.4 Dirtyモジュール -- Active Model の基礎 - Rails ガイド
つっつきボイス:「またdirtyに新しいメソッドが生えるのね😆」「DHHが自分でツイートしていたような気がします」
探しましたがDHHのツイートはありませんでした😅。代わりに以下を貼っておきます↓。
New attribute methods, regex matching and more!: Originally appeared on Riding Rails. Hello! Tim here with the latest scoop on Ruby on Rails! New *_previously_was attribute methods! Your models just got a sprinkling of a little extra sugar. For any given… https://t.co/l3l7qqDqAH pic.twitter.com/LrdOri2h6O
— Malibu IT Labs (@it_malibu) October 7, 2019
⚓新機能: MySQLでmatches_regex
とdoes_not_match_regexp
が使えるようになった
# 同PRより
users = User.arel_table;
# 全gmailユーザーを検索
users = User.arel_table; User.where(users[:email].matches_regexp('(.*)\@gmail.com'))
# gmail以外の全ユーザーを検索
users = User.arel_table; User.where(users[:email].does_not_match_regexp('(.*)\@gmai
# activerecord/lib/arel/visitors/mysql.rb#L51
+ def visit_Arel_Nodes_Regexp(o, collector)
+ infix_value o, collector, " REGEXP "
+ end
+
+ def visit_Arel_Nodes_NotRegexp(o, collector)
+ infix_value o, collector, " NOT REGEXP "
+ end
つっつきボイス:「ActiveRecordで#matches_regexp
を使った正規表現比較がMySQLでもできるようになった🎉」「今までもwhere
の中に書いたりしてたけど、エスケープしてくれるのがありがたい😋「PostgreSQL版の実装は前からあったみたいで、今回はMySQLでも使えるようにしたそうです」
調べてみるとPostgreSQL版は2年前に入っていました↓。
# https://github.com/rails/rails/blob/master/activerecord/lib/arel/visitors/postgresql.rb#L29
def visit_Arel_Nodes_Regexp(o, collector)
op = o.case_sensitive ? " ~ " : " ~* "
infix_value o, collector, op
end
def visit_Arel_Nodes_NotRegexp(o, collector)
op = o.case_sensitive ? " !~ " : " !~* "
infix_value o, collector, op
end
「SQL文の中で正規表現が使いたくなることってたまにありますよね?」「今どきのRDBではたいてい使えますけど😋」「あ、SQLite3で正規表現をちょっとだけ使おうとしたときが割と面倒でした😆」「SQLite3にはなさそう😆」「一応あるにはありました↓」
参考: SQLite で正規表現を使う - しょぼんメモリ (´・ω・`)
「まあRDBの正規表現を使いすぎるとちゃんとした速度が出なくなったり、インデックスが効いたり効かなかったりすることもあるので微妙ですけど😆」「そうですよね」「コレーション(照合順序)とかも影響する可能性ありそうですし😅」
参考: PostgreSQL 11.5ドキュメント 23.2. 照合順序サポート
⚓joins
のorderをできるだけ維持するよう修正
- PR: Preserve user supplied joins order as much as possible by kamipo · Pull Request #36805 · rails/rails
- Rails 6 RC2 joins / arel regression · Issue #36761 · rails/rails
- Order of JOIN statements not respected when building ActiveRecord query with `merge` and `joins` · Issue #34328 · rails/rails
- Order of "manual" joins messed up when using .merge · Issue #24281 · rails/rails
- Strange ActiveRecord behaviour when using merge and joins · Issue #12953 · rails/rails
修正量が多めだったのでソースは貼りませんでした。
つっつきボイス:「最近kamipoさんたちが苦しみ続けていたjoins
のorderの件が『できるだけ維持する』方向でひとまず収束したようです」「ここはたしかに厄介な部分...😅」「Active Recordのオブジェクトとしてどう解釈されるかまで考えたうえでSQL文も見ないといけないのはつらい🥺」
⚓RackのMigration::CheckPending
でFileUpdateChecker
を使うようにしてマイグレーションのパフォーマンスを大きく改善
ここからはマージされたプルリクから見繕いました。最近は大半がドキュメントの微修正です。
- PR: Use FileUpdateChecker for Migration::CheckPending by jhawthorn · Pull Request #37395 · rails/rails
# 改修前
$ be ruby benchmark.rb
Warming up --------------------------------------
CheckPending#call 11.000 i/100ms
Calculating -------------------------------------
CheckPending#call 135.510 (±12.5%) i/s - 671.000 in 5.083931s
# 改修後: file_watcher == FileUpdateCheckerの場合
$ be ruby benchmark.rb
Warming up --------------------------------------
CheckPending#call 34.000 i/100ms
Calculating -------------------------------------
CheckPending#call 348.063 (±18.1%) i/s - 1.666k in 5.024624s
# 改修後: file_watcher == EventedFileUpdateCheckerの場合
$ be ruby benchmark.rb
Warming up --------------------------------------
CheckPending#call 128.936k i/100ms
Calculating -------------------------------------
CheckPending#call 1.884M (± 2.5%) i/s - 9.412M in 5.000561s
従来は
last_migration.mtime
の値で変更を監視していたが、ActiveSupport::FileUpdateChecker
とEventedFileUpdateChecker
(新しいアプリではこちらがデフォルトになる)を用いてパフォーマンスを大幅に向上させた。
同PRより大意
つっつきボイス:「この間も話に出たファイルウォッチャー(ウォッチ20190930)を使ってマイグレーションのpendingチェックを速くしたそうです」「お〜なるほど、これでファイル更新チェックのために毎回全なめしなくてよくなった😋」「pendingチェックたしかに今まで遅かったですね」「productionも含めてRailsサーバーの起動が速くなりそう👍」
「この#37395と同じお題で以前に#29759↓でチャレンジしたときはうまくいかなかったのを再挑戦して今度はうまくいったそうです」「こういうのはどうやってテストするかという問題もありますし、Railsはマルチワーカーで動くからそこでもちゃんと動かないといけないでしょうし☺️」
- PR: Use FileUpdateChecker for Migration::CheckPending by jhawthorn · Pull Request #29759 · rails/rails
⚓(ドキュメント)autosaveの関連付けの振る舞いについて注意点を追加
レコード自身が変更された場合、
autosave
は永続化済み関連付けレコードでのみトリガーされる。これは循環した関連付けバリデーションによるSystemStackError
から保護するのが目的。例外が1つあって、カスタムのバリデーションコンテキストが使われていると、関連付けられたレコードのバリデーションが常に発火する。
同PRより大意
つっつきボイス:「APIドキュメントに注意事項が1つ追加されていました」「circular association validationって😆」「循環参照があるとautosave
の挙動がおかしくなるでしょうね☺️」「ただしカスタムのバリデーションコンテキストが使われているとバリデーションが常に発火すると」
参考: 4.3.2.2: has_many
のautosave
-- Active Record の関連付け - Rails ガイド
# 5a3e34eより
def test_validations_still_fire_on_unchanged_association_with_custom_validation_context
firm_with_low_credit = Firm.create!(name: "Something", account: Account.new(credit_limit: 50))
assert firm_with_low_credit.valid?
assert_not firm_with_low_credit.valid?(:bank_loan)
end
「5a3e34eのcreate!
でやってるこのあたり↑がそうかな」「おぉ?」「中でAccount.new
しているものがsaveされないとidが確定しないからcreate!
できなくなりますね」「あ〜なるほど!」「こういうのがカスタムバリデーションコンテキストでのautosave
ということだと思います☺️: 中のAccount
が外のFirm
にhas_manyしているときは、firm.account_id
をセットするために中のものから先に作らないといけないのでAccount.new
が自動でsaveされると」「こういう状態を何て言えばいいんでしたっけ?😅」「循環でもデッドロックでもない、普通の依存関係ですね☺️」
⚓Rails
⚓スライド: amatsudaさんの「Rails Performance issues and Solutions」
Just published my slides deck for my opening keynote "Rails Performance Issues and Solutions" at RubyConf Indonesia 2019! https://t.co/Gxa4btN9n7 #RubyconfID
— Akira Matsuda (@a_matsuda) September 29, 2019
つっつきボイス:「この間の公開つっつきでこちらのスライドを取り上げるのを忘れていました😅」「そうそう、この間の銀座Rails#13↓でamatsudaさんが発表したRailsパフォーマンス話もこれに含まれていると思います😋」「スライドはその後のRubyConf Indonesia 2019でのものだそうです」「つか銀座Railsのスライドはそっち用のドラフトだったのかも😆」「銀座Railsのときにも資料書いてる途中だって言ってましたね☺️」
「このスライドでもRails 6でのrails new
では--skip-spring
と--skip-bootsnap
してますが↓、銀座Railsのときにも--skip-action-text
でAction Textをスキップすることが重要という話がありましたね😆」「え?😅」「今のAction Textが半端なく重いのでとりあえず無効にしておきましょう、だそうです😆」
「銀座Railsのときにマジで面白かったのはこの辺の計測の話でした❤️」「どんなお話でした?」「まあ実際のアプリでベンチマーク取るときはcurlとかでやればいいんですけど、Railsフレームワークのどこにボトルネックがあるのかを調べるときには、こんなふうに↓イニシャライザで直接モンキーパッチを当ててやる方がいいということでした」「おぉ〜!」「こうすればRackの処理の重さとかに影響されなくなるので純粋にRailsコードの速度を測れるようになるからいいよって😋」「これいいノウハウですね😍」「ここではprepend
で直接差し込んでます」
「Apache Bench(ab
)とかで測定すると、どうしてもRails以外のボトルネックも出てきたりしてしまいますが、この方法ならRailsのコードだけにフォーカスできますね👍」「あとはアタッチする場所を少しずつ変えて試す↓: パッチのコードは他の場所でもほぼそのまま使い回せるので、うまい方法だと思います😋」「いいですね〜😋」
参考: Apache Benchでサクッと性能テスト - Qiita
「次がRailsのアロケーションを削減する話」「『先週の改修』でもamatsudaさんがやってるのを見てきましたね」「こうやってProf.mem
してプロファイラの結果を眺めて、これはちょっと多くね?と思ったところをひたすら地道に直していくというのをやってるそうです↓」「ふむぅ」
「そうそう、使ってないはずのAction Textエンジンがなぜかいるという話も↓😆」「おおっと😆」
「その修正方法はというと、require 'rails/all'
をやめてAction Textだけを除いたものを自力で書くと↓🤣」「🤣」「Action Textをrequireするだけで重くなるからRails 6ではこうするとこれだけアロケーションが減って速くなるよと」「何ということでしょう😆」
「rails new
する段階でAction Textを入れないようにする方法ってありますよね?🤔」「あります」「でもたぶんapplication.rbでrequire 'rails/all'
するとAction Textも入っちゃうんだと思います: というのもRailsのgemspecにはAction Textも入ってるはずなので、Gemfileでrequire "rails"
と書かれている状態でbundle install
した段階ではAction Textのgemも入ってくるはずで、その状態でrails new
するとapplication.rbでrequire 'rails/all'
となって入ってきちゃうと思います」「あ〜」
後で、ごく最近のRails 6で--skip-action-text
などを指定してrails new
したもので見てみるとapplication.rbでは自動的に除外されていますね↓😋。rails new
で何もスキップしない場合はrequire 'rails/all'
になります。rails new
した後から機能を外す場合は自分でrequire
周りを変える必要がありますね。
require_relative 'boot'
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "active_storage/engine"
require "action_controller/railtie"
# require "action_mailer/railtie"
# require "action_mailbox/engine"
# require "action_text/engine"
require "action_view/railtie"
# require "action_cable/engine"
# require "sprockets/railtie"
require "rails/test_unit/railtie"
参考: Rails 6.0.0 performance regression because of ActionText::Engine hook · Issue #36963 · rails/rails -- open
「Action Textはrequire
から外さないとアロケーションされてしまうらしい🤔」「amatsudaさんが#36963↑を投げてくれて『Rails 6.0.1で直ることを期待』と」「Action Text以外のオプション機能についてはrequire
から外してもなぜかアロケーションは変わらないと」
↓こちらのissueも関連してそうです。
参考: ActionText forces all apps to include an ActionController::Base subclass · Issue #37183 · rails/rails -- open
「後は細かいところだと=~
よりmatch?
の方が速いとか↓」「そういえば今のRubyはmatch?
の方が速いんでした」「match?
は余分なオブジェクトに触らない分速いとかそんな感じ」
「ハッシュもHash#merge
より添字アクセスのHash#[]=
の方が速い↓」
「同じくHash#fetch
よりHash#[] || default
の方が速い↓」「上もそうだけど、前者はメモリがアロケーションされるけど後者はされないのね」「Hash#[]
はCRubyのコアのところに直接つながっているはずだけど、Hash#merge
やHash#fetch
はメソッド呼び出しだから、たぶん仮引数代入とかが発生するんじゃないかしら🤔」「おぉ」
「まさにマイクロオプティマイゼーションを積み上げていく作業😳」「塵も積もれば5〜10%のアロケーション削減が見込まれると🎉」「このスライドはじっくり追いかけていくといろいろ発見がありますね❤️」
「この辺はテンプレートエンジンの話↓」「hamlは速いぜと😋」「k0kubunさんも記事にしてたヤツですね(ウォッチ20190925)」「stringの式展開にすると速いという話とか」
「amatsudaさんによると実はI18nが重いんですよ↓」「おぉ?」「I18nだけは式展開にできないのでどうしてもメソッド呼び出しが残っちゃう」「なるほど!」
「なのでロケールごとにI18nのキャッシュを作って高速化しようとしてるそうです↓: 銀座Railsで見たときはまだ作業中と言ってたかな」「これもすごく有用な話ですね😋」「こういう積み重ねでメモリアロケーションをじわじわ減らしていると」「すげ〜!」「Railsはメモリをめちゃ食うという印象があったりするので、こういうところが改善されていくのはいいですね〜☺️」
Ruby 2.6.4を入れるとRailsで使うと遅くなる、知見じゃん #ginzarails
— sue445 (@sue445) September 12, 2019
「上のツイート↑はこの間のウォッチでは裏を取れなくて見送った話でした」「そうそう、Ruby 2.6.4だとMonitor#synchronize
でRailsのベンチマークが遅くなるそうです↓」
参考: class Monitor (Ruby 2.6.0)
⚓Rails 6のAR associationとscoping
の変更
ruby-jp Slackで知りました。
つっつきボイス:「上の記事はもともと論理削除が使われていたコードのつらみから始まったとのことで、今日はつっつきに出られないkazzさんにこの記事を見せたら苦笑いしてました😅」
しかし、Rails 6では…
仕様が変更された為、先程のコードはそのままでは動きません。具体的には、
Blog.not_deleted.scoping do
BlogComment.where(blog_id: 1).blog
end
は
nil
を返しません。BlogComment.blog
のAssociation解決時に実行されるBlog
モデルへのSELECTクエリへのスコープがリセットされ、deleted_at IS NULL
の条件が消える為です。
同記事より
「Rails 6では上のように変わってたそうです」「へ〜、たしかにassociationのタイミングで違うものを返されるとビビるからマジ止めてほしい😭」「associationの読み込みはdefault_scope
では有効だけどunscope
以外の普通のスコープだと有効じゃなくなったとは😳」「そういえばRailsアップグレードガイド↓でこの変更を翻訳した覚えがありませんでした」
参考: Rails アップグレードガイド - Rails ガイド
「まあscoped chainはやりすぎるとつらくなるし😢」「やっぱこういうのはテストを書いておくべきだな〜: でないとこういうbreaking changesが起きたときに死ぬ😇」「踏んだときに原因がわかりにくそう😅」
「記事はやむを得ずdefault_scope
で対応することにしてますね」「default_scope
使わないぞキャンペーンもしつつ😆」「default_scope
は止めた方がいいかと😆」
以下はつっつきの後で見つけたツイートです↓。
与太話をすると、default_scopeとscopingは無限のissueを産む機能でメンテナに嫌われ見放されてたけど、k0kubunさんのテクニックでjoins/eager_loadのdefault_scopeを無効化できる変更を通すことに成功したのがきっかけでどんなscopingも受け付けると無限のコーナーケース(無限ループするとか)が産まれ
— Ryuta Kamizono (@kamipo) September 26, 2019
throughアソシエーションじゃない遅延ロードだけなぜかscopingの影響を受けるテストが存在してて当時のわいの徳ではそれを覆す気合いがなかったからそこだけ非互換変更を入れず一貫してない挙動を許容する道を選んだのや当時は。その後無限のやる気を持つメンテナ(わい)がscopingのバグ直しまくるため
— Ryuta Kamizono (@kamipo) September 26, 2019
手ぬいたが一応CHANGELOG書いたけど、めっちゃ偏執的にやるならこれですらもdeprecation warning出したらよかったですね。でもわいも人間やし無限にバグ直しまくってると疲れてちょっと手ぬいたってええかなってなるときもあるねん。それは広い心で受け止めてくれたらうれしい、ほんとすまんかったわ。
— Ryuta Kamizono (@kamipo) September 26, 2019
⚓Sidekiqが6.0.1で高速化(Ruby Weeklyより)
つっつきボイス:「10〜15%速くなったはず、とありますね」「タグを付けられるようになってる↓」「Pro版だとこれで絞り込めるそうです」
# changelogより
class MyWorker
include Sidekiq::Worker
sidekiq_options tags: ['bank-ops', 'alpha']
...
end
「find_job
もかなり速くなってる↓」
zscan 0.179366 0.047727 0.227093 ( 1.161376)
enum 8.522311 0.419826 8.942137 ( 9.785079)
「結局Sidekiqをナマで使うことになるのかな〜: Active Job越しにやると機能が足りなかったりしますし😅」「そんな雰囲気ですね」「Rails way的には本来Active Jobでジョブワーカーを抽象化するんですけど、ジョブワーカーの種類によって機能が違うから特定のジョブワーカーの機能を使おうと思うとActive Job経由できれいにやろうとするより生のSidekiqを使うことになりそうかな〜🤔」「ラッパー越しだと靴の上から足を掻くみたいになっちゃうんですね😳」「単純な作業ならラッパー越しでもいいんですけど、ジョブの制御までやり始めるとそうなりがち😢」「うーむ」
「ジョブで名前空間を使いたいとかプライオリティ付きのキューとかは、ジョブワーカーによってできるものとできないものがあったと思いますし」「ジョブワーカーというとこのsidekiqの他にdelayed_jobと、あと何だっけ?」「思い出せない😅」「最近sidekiq使うこと多いし😆」「あ、resqueか!」
- リポジトリ: delayed_job - Google 検索
- リポジトリ: resque/resque: Resque is a Redis-backed Ruby library for creating background jobs, placing them on multiple queues, and processing them later.
「sidekiqのリリースノートを見ると、なぜか『ダークモードに対応』ってありますけど😆」「sidekiqのWeb UIには割とお世話になりますし、マウントするだけでWeb UIを使えるのは便利😋」「『もっとイケてるデザイン募集』ともあります🤣」「たしかにあんまりイケてないけど🤣」「まああれで十分ではある☺️」
⚓ARモデル内でクエリロジックを共有する(Ruby Weeklyより)
# 同記事より
class User < ApplicationRecord
# our class method from above
def self.can_receive_alerts
where(receives_sms_alerts: true).
or(where(receives_email_alerts: true)).
joins(:alert_configurations).
distinct
end
# our new instance method which builds on the class method
def can_receive_alerts?
self.class.can_receive_alerts.where(id: id).exists?
end
end
つっつきボイス:「自分も先週見つけていた記事でしたがRuby Weeklyに先越されました😢」「コードの再利用ですね☺️: self.can_receive_alerts
で作った条件をインスタンスメソッドでちょっと変えてスコープ的に再利用すると」「kazzさんともこの記事で雑談してたんですが今思い出せない😅」「クラスメソッドで使った条件にwhere(id: id).exists?
を足してインスタンスメソッドでも使いたいという感じなので、そんなに難しい話ではないですね」「おぉ」「スコープにこういう感じで条件を付けることはよくあるので、インスタンスメソッドでその条件に合っているかを確認するのに同じことをもう一回書かずにやれるよということで☺️」
⚓sprockets 4.0.0がリリース
これもruby-jp Slackで知りました。キーワード引数周りの修正やアロケーション削減、脆弱性修正などが行われました。Ruby 2.5以降のみがサポート対象になりました。
つっつきボイス:「sprocketや〜😆」「Webpackに押されてるのかと思ったらいろいろやってるんですね」「お、JSのSource Mapが使えるようになってるし😋」「3.xにはなかったのか〜」
"version":3,
"file":"application.js",
"mappings": "AAAA;AACA;AACA;#...",
"sources": [
"jquery.source-56e843a66b2bf7188ac2f4c81df61608843ce144bd5aa66c2df4783fba85e8ef.js",
"jquery_ujs.source-e87806d0cf4489aeb1bb7288016024e8de67fd18db693fe026fe3907581e53cd.js",
"local-time.source-b04c907dd31a0e26964f63c82418cbee05740c63015392ea4eb7a071a86866ab.js"
],
"names":[]
}
「manifest.jsも対応してるし!欲しかったものが割と増えてる気がする❤️」「デフォルトでES6をサポートですって」
// app/assets/config/manifest.js
//
//= link application.css
//= link marketing.css
//
//= link application.js
「まあES6でやるならWebpackでいいんじゃね?とも思いますが😆」「Sprocketsで十分という人にはありがたそうですね」「RubyのコードでJavaScriptコードを生成したい人はSprocketsを使いたいでしょうね」「というと?」「JavaScriptのコードの中に、パーシャル的にRubyのコードが埋まっているようなコードを書きたいときとか: SprocketsならRubyのコンテキストで処理できるので😋」「おぉ〜」「まあ今となってはあまりやりませんし、manifest.jsがあるならそっちに入れるという手もありますし」
「Rubyエンジニアが多いプロジェクトなら、Rubyが気軽に使えるという意味でSprocketsがいいかも: WebpackにはRubyを処理させられないので(探せばあるかもしれませんが😆)」
「Reactとか使わないんだったらSprocketsでもよさそうですね🤔」「まあそれでもいいんですけど、要はフロントエンドのコードを誰がやるかがポイントかも😆: フロントエンジニアはSprocketsの面倒は見たくないでしょうから彼らが慣れているWebpackの方がいい、とかね」「ふ〜む」「フロントエンジニアにしてみれば、SprocketsのためだけにRailsを勉強するとなるとオーバーヘッドがヤバいですし😆」「たしかに😆」「逆にみんなRailsエンジニアならSprocketsでいいでしょうし、Webpackerがむしろわけわからんかもしれませんし」
⚓Ruby
⚓Ruby 2.7のキーワード引数変更
つっつきボイス:「例の#14183↓がウルトラ長くて私には要約できそうになくて😂」「またしかに😆」「まだ読めてませんが、この記事で端的にまとまってるといいなと願っています🙏: ちなみにこのblog.saeloun.comブログのRuby記事はいいですね😋」
⚓ruby/specに新しいexpectationが追加
これもruby-jp Slackで見かけました。
describe "String#start_with?" do
it "returns true only if beginning match" do
"hello".start_with?('hel').should == true
end
end
つっつきボイス:「ruby/specって何だろうと思ったら、Ruby自身のテストのためのものみたいです」「RSpecライクというか☺️」「それを動かすのがMSpecというツールだそうです」「RSpecをいっぱい書いている人がRubyのspecも書きたい、とかそういう感じ?😆」「自分なら書かないかな〜😆」「言語のチェックならアサーションとかでやる方がシンプルになりそうですし☺️」
参考: ruby/spec: The Ruby Spec Suite aka ruby/spec
参考: ruby/mspec: RSpec-like test runner for the Ruby Spec Suite
「なおRSpecも3.9がリリースされたそうです」「お、システムspecのジェネレータができた❤️」「今までなかったんですね?」「まあジェネレータがなかったというだけで、ディレクトリは前からありますし」「type: :system
で指定するのか〜」
RSpec 3.9がリリースされていますね。小規模なアップデートが中心ですが、個人的にはrspec-railsでシステムスペックのジェネレータが追加されたのが一番大きいニュースかも!
ただ、このbeforeブロックは別になくても良い気がするな・・・。https://t.co/fVyLfcv6CJ pic.twitter.com/CCDGFGfoHA— Junichi Ito (伊藤淳一) (@jnchito) October 9, 2019
⚓testrocket: インラインでテストを書きたい(Ruby Weeklyより)
# 同リポジトリより
require 'testrocket'
using TestRocket
# BASIC USAGE
# +-> { block that should succeed }
# --> { block that should fail }
+-> { Die.new(2) }
--> { raise }
+-> { 2 + 2 == 4 }
# These two tests will deliberately fail
+-> { raise }
--> { true }
# A 'pending' test
~-> { "this is a pending test" }
# A description
!-> { "use this for descriptive output and to separate your test parts" }
つっつきボイス:「テストを本編コードにインラインで書きたいそうです」「スーパーシンプルであると😆」
「むむ、こういう書き方をする言語って他にもあった気がする!」「見覚えありますね🤔」「オプションつけて実行するとテストが走って、付けないと普通に動くみたいなの、あった」「何だったかな〜?」
「こういうのをうまく設計すれば、APIドキュメントとしても成立しないかなって思ったり」「いや〜邪魔😆」「邪魔😆」「そういう方向よりも、JetBrainsのIDEみたいにコードからテストコードに即ジャンプできるとかマウスオーバーでテストコードが見えるみたいな方がうれしい気がしますけどっ😆」「エンジニアにとってエディタの行数という貴重な有限のスペースを他に使いたくないですし😆」
「こういうインラインなアサーションを書く言語...Javaにあった!」「あ〜そうだった!」
参考: - JUnit 実践講座 - シナリオベースのテストケースの書き方
// 同記事より
public class LoginFormTest extends TestCase
{
public void test() throws Exception
{
LoginForm form = new LoginForm();
form.setUserId("user1");
form.setPassword("password1");
form.execute();
assertEquals("こんにちは,ユーザ1さん!", form.getMessage());
}
}
⚓その他Ruby
去年参加させていただいたのですが、確か女性と男性の比率が6:4ぐらいだったと思います。それでさえいつもと違う居心地だったので、男性にとっても、普段勉強会で圧倒的少数の女性がどんな居心地なのかを少しでも知るいい機会だと思います。 https://t.co/34nSbfknDh
— Sho Nagata (@s_naga03) October 8, 2019
⚓言語・ツール
⚓Bashヒストリーの便利ワザ
Love this kind of posts, I feel my productivity has increased by 1% 😆 - 7 Bash history shortcuts you will actually use by @ianmiell https://t.co/RIXhD47AnG
— Stan Lo (@_st0012) October 8, 2019
つっつきボイス:「!$
は知らないな〜」「!:なんちゃら
は使うこともあるかな」「!:0
はコマンド自身で、!:1
以降が引数になる」「知りませんでした😅」
$ !:0 !:1 !:3 !:2
tar -cvf afolder.tar afolder
「!$:h
は知らないな〜」「前回のコマンドの引数の親ディレクトリを取れるらしい」「挙動がよくわからないうちにこの辺を使うのはコワいかも😅」
$ tar -cvf system.tar /etc/system
tar: /etc/system: Cannot stat: No such file or directory
tar: Error exit delayed from previous errors.
$ cd !$:h
cd /etc
「コマンド履歴を当てにしすぎてると、ある日履歴が吹っ飛んで悲しい思いをしたりしますよね😅」「rsyncとかであるある😅」
⚓その他
⚓awesome-for-beginners: 初心者に優しいオープンソースプロジェクトリスト
つっつきボイス:「Gobyの@st0012さんがここにGobyも登録したいと言ってて知りました: 初心者にとって敷居が低いプロジェクトの言語別リストだそうです」「Hanamiも入ってる🌸」「優しいというか比較的開かれたコミュニティという感じでしょうね☺️」
「Rubyのohai↓もリストにありますね」「ohaiって何でしょう?」「CPUとかメモリみたいな実行環境を詳しくプロファイリングするヤツですね」「へ〜!😳」「ChefとかAnsibleで、たとえばメモリの何割をconfigに使うかとか、Railsのワーカー数とかを動的に指定するときなんかに使えます😎」
{
"filesystem" => {
"/dev/disk0s2" => {
"size" => "10mb"
},
"map - autohome" => {
"size" => "10mb"
}
},
"network" => {
"interfaces" => {
"eth0" => {...},
"eth1" => {...},
}
}
}
「Ohaiにとっては、新しい環境が出てきたときにそれを足してくれるだけでありがたいので、コミットしやすいんでしょうね☺️」「なるほど!」「だからすご〜くマイナーなディストリとかの環境を追加すればそれだけで喜んでもらえますよきっと😋」
「あれ?Gobyもリストに入ってますけど😆」「あホントだ!、いつの間に😳」「早業😆」
⚓クンロクモデムが100万円の時代
9600bpsのモデムが100万円をきって、購入の稟議書が常務決裁で良くなったころから。 https://t.co/JtHatCYbrn
— 河野太郎 (@konotarogomame) October 7, 2019
遠い昔に雑誌で音響カプラの写真を見たとき、何に使うのかわからなかったのを思い出しました。
つっつきボイス:「この辺はおっさん話ですみません😅」「カプラはさすがに知らないわ〜😆」「電話の受話器に無理やりマイクとスピーカーをはめ込んでデータ通信するという原始的なヤツです😆」
「そういえばモデムの時代にも、モデムの音を録音されるとパスワード情報を抜き取られるなんて話がありましたね🤣」「あ〜たしかに可能だ🤣」「モデムのピ〜ガガ〜って音にはデータが全部乗ってるから、録音しちゃえばパスワードも含めて復号できちゃいます☺️」「デジタルデータを音にエンコード・デコードしてるだけだから、もともとそういうものですし😆」
⚓番外
⚓今度はWi-Fiで
つっつきボイス:「この方面の研究は割と前からありますね☺️」「ありますね〜☺️」「そうでしたか!😳」「データソースは物珍しいけど内容はよくある感☺️」「それを実際に実装して動かしたのはスゴい💪」「恣意的な部分も相当ありそうですが😆」「研究ってそんなもんです🤣」「一般性なくても『この人かどうかさえわかればいい』とかかもしれませんね😆」
今回は以上です。
バックナンバー(2019年度第4四半期)
週刊Railsウォッチ(20191008後編)Ruby 2.7のInteger#[]でバイナリチェック、rubyzip gemは強力、13KBのJavaScriptゲームほか
- 20191008前編 Ruby 2.7のInteger#[]でバイナリチェック、rubyzip gemは強力、13KBのJavaScriptゲームほか
- 20191001後編 RedisとRubyをつなぐredis-object gem、Fullstaq Rubyの新バージョン、COUNT(*)とCOUNT(1)の速度ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSなど)です。