週刊Railsウォッチ(20170317)Railsパフォーマンスチューニング本、DBレコード存在チェックの最速メソッド、RubyのUnicode正規化ほか

こんにちは、hachi8833です。

「HackerNewsのランキングアルゴリズムをリバース・エンジニアリングした記事がHackerNewsから削除されたという記事」が昨日までHackerNewsでトップでした。ややこしいですね。

IPAが自ら脆弱性を報告した無料ゲーム「安全なウェブサイト運営入門」は書籍の名前かと思ってしまいました。

それでは今週もいってみましょう。

書籍「The Complete Guide to Rails Performance」(HackerNewsより)

(Amazon.co.jpにはエントリがないので同サイトの書影をリンク付きで引用しました)


The Complete Guide to Rails Performanceより

元記事は「私はいかにしてRails本で7万ドル稼いだか」的な記事ですが、その書籍の方をピックアップしました。読んでみないと何とも言えませんが、よさそうです。

ActionController::Parameters:にreverse_mergereverse_merge!を追加(Rails公式より)

改修箇所は以下です。

  # 現在のハッシュをother_hashにマージし、新しいActionController::Parametersインスタンスをすべてのキーとともに返す
  def reverse_merge(other_hash)
    new_instance_with_inherited_permitted_status(
      other_hash.to_h.merge(@parameters)
    )
  end

  # 現在のハッシュをother_hashにマージし、現在のActionController::Parametersインスタンスを返す
  def reverse_merge!(other_hash)
    @parameters.merge!(other_hash.to_h) { |key, left, right| left }
    self
  end

#merge#merge!のパラメータ順序をRubyのHash#mergeで確認すると以下のようになっていました。

  • merge(other) {|key, self_val, other_val| ... } -> Hash
  • merge!(other) {|key, self_val, other_val| ... } -> self

「これ欲しかった!」「strong parametersにデフォルト値を設定したいときにいいかも」という声がありました。

strong parametersの#permitは、paramsに入ってないkeyを#permitした場合にParameterオブジェクトの当該keyがnilになってしまいます。従来であればデフォルト値を与えたいときには自力でkeyに与える必要がありましたが、#reverse_merge!を使ってparamsのkeyにデフォルト値を与えられます。

たとえばモデル側で余計なことをしたくない(ビューの都合だけでデフォルト値を設定したいなど)場合に、モデルではなくコントローラ側でデフォルト値を与えられるのがよいかもという意見もありました。

個人的にはコントローラでparamsをそれ以上いじるのは避けたい気がしました。

Capybaraでドライバがスクショ非対応の場合にはスクショを撮らないようになった(Rails公式より)

Improvementに分類されてましたが、バグ修正ですね。修正もごくわずかでした。

Migrator.schema_migrations_table_nameを非推奨化(Rails公式より)

SchemaMigrationモデルが切り出されたことによる変更です。変更自体はアプリ開発者には直接関連しませんが、schema_migrationsという名前のテーブルが話題になりました。

今さらですが、Railsのデータベースにはschema_migrationsテーブルがあり、versionカラムにはそれまでマイグレーションに使ったファイル(db/migrate/以下)のバージョン番号がすべて保存されています。

マイグレーションが思うようにいかなくて、思い余ってマイグレーションファイルのバージョン番号を変更してしまう人がいるらしいのですが、「schema_migrationsテーブルの一貫性が損なわれるから絶対やめた方がいいよ」という話を聞きました。

マイグレーションには恥ずかしい履歴が残りがちなので、気持ちはわかります。

例外時にローカルキャッシュがクリアされない問題を修正(Rails公式より)

改修箇所はわずかでしたが、見終わってからRackとWardenの役割の違いについて話題になりました。

手短にまとめると、Wardenは認証用フレームワークであり、RackとRailsアプリの間の層で認証やセッション管理をつかさどります。Rackmorimorihogeさんいわく「薄いWebサーバー」であり、Rackの上でRailsやSinatraなどのアプリケーションが動作します。

Rackについては、Railsガイド: RailsとRackと技術評論社サイトの記事「Rackについて」、Wardenについては、「Devise を知るにはまず Warden を知るが良い」がそれぞれ参考になります。

サブディレクトリでのrake db:schema:loadの不具合を修正(Rails公式より)

実はRailsではサブディレクトリでもrails db:migrateを実行できるのですが、「何が起きるかわからないからコマンドはプロジェクトのルートディレクトリでしか実行しない」という意見がありました。私も同感です。

ActionController::Rendererでのasset_urlの不具合を修正(Rails公式より)

asset_urlが不完全だった(’https://’が含まれないことがあった)問題の修正です。

#28250ではやや緊張感のあるやりとりが繰り広げられていました。こんな写真が貼られてたり。もちろんお茶目でやっていると思います。


https://github.com/rails/rails/pull/28250より

最終的にPRのrebaseに手違いが見つかり、それを修正して無事commitされました。

has_many関連付けでブロックにselectしたときに新レコードが作成されない問題を修正(Rails公式より)

kamipoさんによる1行修正で完了しました。

#titlizeがアポストロフィに対応(Rails公式より)

改修箇所は、正規表現への\w(語の区切りを表す)の追加で終わりました。

humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { |match| match.capitalize }   # 修正前
humanize(underscore(word)).gsub(/\b(?<!\w['’`])[a-z]/) { |match| match.capitalize } # 修正後

#titlelizeをどこかで見たと思ったらActiveSupport探訪シリーズ「[Rails5] Active Support::Inflectorの便利な活用形メソッド群」でお見かけしていました。その後お変わりありましたね。

ちなみに私はtitlelizeという造語のスペルを一発で入力できたことがありません。いつも間違えます。

#titlelizeのサンプルに使われている映画のタイトルが趣味全開ですね。さりげに新旧取り混ぜているのはコミッターの層の厚さゆえかシニアコミッターに気を遣ったのかはわかりませんが。

    #   titleize('man from the boondocks')   # => "Man From The Boondocks"
    #   titleize('x-men: the last stand')    # => "X Men: The Last Stand"
    #   titleize('TheManWithoutAPast')       # => "The Man Without A Past"
    #   titleize('raiders_of_the_lost_ark')  # => "Raiders Of The Lost Ark"

PostgreSQLのJSON型データのdeserializeが文字列を返す問題を修正(Rails公式より)

これもkamipoさんによる修正で、PostgreSQLのみが対象です。kamipoさんのコミットが膨大なので、昨年既に七福神入りしていたんですね。いつもありがとうございます?

簡潔・高効率かつRubyらしいコードを書く(RubyWeeklyより)

#attr_readerで初期化を簡潔に書く、ブロックをprocオブジェクトに置き換えるなどの方法などが紹介されています。追記に、Ruby 2.3ではprivateでの#attr_reader警告が表示されなくなったということについても触れられています(#10967 Is “warning: private attribute?” wrong?)。それほど短い記事ではありませんが、込み入ってはいないので読んでおくとよさそうです。

参考: 初期化を便利にするgem 2つ

ある程度複雑な初期化を楽に書くgemとして、morimorihogeさんが以下の2つを教えてくれました。

RubyのUnicode正規化とは

週刊Railsウォッチ(20170217)でマサカリが大量に飛んできた「Testing Ruby’s Unicode Support」記事のリベンジです。

Frankly, I was a bit fuzzy on Unicode normalization.

一転してUnicode正規化を熱心に調べています。短くもよくまとまった良記事です。後でじっくり読んでみます。

  • NF: “normalization form”の略
  • D: “decomposition”の略
  • C: “composition”の略
  • K: “kompatibility”の略 ┐(´∀`)┌

「RubyやRailsのコミュニティがUnicode正規化に思ったほど関心を寄せていないのに少しだけ驚いた」としめくくっています。ASCIIだけで事足りる英語圏ではある程度仕方がないかなとも思いますが、私としては、絵文字のおかげで近年こうした問題に以前よりも関心が高まっているような気はしています。

微細な最適化が効く(RubyWeeklyより)

長いです。最適化の主題となっているsigprocmaskって何だろうと思って一同で検索したところ、プロセスが応答するシグナルを変更するシステムコールでした。

ただ、新しい記事にもかかわらず、使っているのがRuby 1.8なので現在のRuby 2.x VMには符合しなさそうです。

なぜRuby 1.8なのか

以下は、同記事のRubyバージョンがえらく古い点についての私の推測です。

全文がHTMLで公開されている「Rubyソースコード完全解説」は2004年に公開されましたが、英語化されたのはかなり最近です。そして同書がRuby 1.7.xをベースにしている(前書きには1.8でもほとんど同じだろうと書かれている)ので、2.xではこうしたローレベルの最適化を追求しづらかったのではないかと思いました。

リポジトリ: Ruby Hacking Guide Translation

データベースレコードの存在チェックはexists?が最速

present?     #=>  2892.7 ms
any?         #=>   400.9 ms
empty?       #=>   403.9 ms
exists?      #=>     1.1 ms

だそうです。morimorihogeさんが「それぞれのメソッドがどんなSQLを吐いているかを知っておくことが重要」と指摘していました。記事中のサンプルコードでは#existだけにLIMIT 1が含まれているのが効いているようです。

著者は「いつもexists?でおk」と結論づけていますが、こうした速度はさまざまな条件によって影響される可能性があるので、参考として頭の隅に置いておこうと思います。

私はといえば、select 1 as oneはクエリの結果がいらない場合の定番のSQLであることを今さら知りました。

google-drive-ruby gem: RubyでGoogleスプレッドシートにアクセスできる(RubyFlowより)

表計算を何とかするgemです。

OAuth認証と組み合わせることで、RubyからGoogleスプレッドシートにアクセスして読み取りや更新を行えます。記事やリポジトリには記載されていませんが、Railsでも使えると思います。スプレッドシートを正しく運用できるのであれば、案件によっては合うかもしれません。

この種のgemを導入するときは、頻繁に更新されている(=放置されていない)ものを選ぶのが重要ですね。機会があれば使ってみたいですが、データをシートから取ってくるだけにしておきたいと思いました。

Rubyのモンキーパッチで怪我をしないためには(RubyFlowより)

Rubyはオープンクラスなのでモンキーパッチを簡単に当てられますが、よく切れるナイフと同様、自分の身を傷つけないように注意が必要と説いています。

  • ひとつのメソッドにモンキーパッチが2つ当たると、最初のモンキーパッチは上書きされてまったく効かなくなる
  • モンキーパッチで発生したエラーはクラス内で発生したように見えてしまう
  • モンキーパッチをオフにする方が面倒
  • モンキーパッチ実行前にクラスをrequireするのを忘れると、パッチではなくクラスの再定義になってしまう
  • モンキーパッチをモジュール化することもできるが、問題はさして変わらない: パッチはグローバルに効くので、別のライブラリでいつの間にか上書きされてしまう可能性がある

morimorihogeさんが、趣旨の近い記事としてクックパッド開発者ブログ「Ruby on Rails アプリケーションにおけるモンキーパッチの当て方」を挙げました。

Webアプリ・モバイルアプリ開発にRuby on Railsを選ぶ理由(RubyFlowより)

RubyFlowのリンクが壊れていたので、直接元記事にリンクしました。

  • オープンソースであり、コミュニティによるサポートが絶大
  • 改修が容易
  • 短期間でのリリースが可能
  • 開発ツールの再利用が利く
  • 開発者に優しい
  • 英語に近い感覚

タイトルと本文の量がほぼ同じぐらいですね。リンクはしませんが、About Usを見るとインドのアフマダーバードの会社でした。

EuRuKo 2017に参加すべき理由(RubyFlowより)

「〜する理由」記事が続きます。EuRuKoはヨーロッパで開催される大きなRubyセッションです。2016年度はブルガリアのソフィアでの開催、そして今年は9月にハンガリーの首都ブダペストでの開催となります。


https://euruko2017.org/より

  • コミュニティが主役
  • 交通の便がよい
    • Geekすぎないのもありがたい
  • 内容がよい
  • 会社にもメリットがある
  • 出張の名目として絶好

Matzももちろんキーノートスピーチを担当しますし、昨年はLTやCoding Golfも大いに盛り上がったそうです。

morimorihogeさんによると「会場が空港や駅から遠い」「宿がスムーズに取れない」「会場の周辺に居酒屋などがまったくない」カンファレンスはやっぱりつらいそうです。

日本であれば多少不便でも旅情を味わえたりしますが、海外(特にヨーロッパ)だと都市部であってもコンビニが少なかったりレストランも土日閉まってたりすることが多いので、参加にあたってはそのあたりをチェックしたいですね。

今週は以上です。

関連記事

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。

Rails公式ニュース

Ruby Weekly

OpenRuby

RubyFlow

160928_1638_XvIP4h

Hacker News

160928_1654_q6srdR

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833 コボラー、ITコンサル、ローカライズ業界を経てなぜかWeb開発者志願。 これまでにRuby on Rails チュートリアルの大半、Railsガイドのほぼすべてを翻訳。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

週刊Railsウォッチ

インフラ

Rubyスタイルガイドを読む

BigBinary記事より

ActiveSupport探訪シリーズ