週刊Railsウォッチ(20190520-1/2前編)Evil Martians愛用の便利gemたち、render_asyncでRails表示を高速化、split gemでA/Bテストほか

こんにちは、hachi8833です。今回は何となくEvil Martians成分が多めになりました。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

お知らせ: 第11回公開つっつき会(無料)

第11回目を迎えた公開つっつき会は6月6日(木)19:30にBPS会議スペースにて開催されます。皆さまのお気軽なご参加をお待ちしております🙇。

Rails: 先週の改修(Rails公式ニュースより)

コミットリストから見繕いました。ドキュメントのタイポ修正が増えてきています。

uniquenessバリデータでミスマッチするコレーション比較をdeprecate

これと次のPRは先週より前のものですが、後述のkamipoさん記事で取り上げられていたものです。

# activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L456
+     def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
+       column = column_for_attribute(attribute)
+
+       if column.collation && !column.case_sensitive?
+         ActiveSupport::Deprecation.warn(<<~MSG.squish)
+           Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
+           To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
+           pass `case_sensitive: true` option explicitly to the uniqueness validator.
+         MSG
+       end
+
+       super
+     end

Active Recordのuniqueness validatorはデフォルトでcase sensitiveな比較をするんですが、これが、文字列のデフォルトのcollationがcase insensitiveなMySQLと相性が悪く、DB上のUNIQUE制約と一致しない振る舞いだったりINDEXが効率よく使えずDBが死ぬみたいな問題を引き起こしていました。
Rails 6.0でDeprecatedになるActive Recordの振る舞い3つ - かみぽわーるより


つっつきボイス:「あー、uniquenessのバリデーションをRDBのコレーション設定に合わせるように変えるという話か」「Rails 6.1から実施されるんですね」「今まではRDBのコレーション設定と無関係にバリデーションしてたのか」「Rubyの世界でやると常にcase sensitive(大文字小文字を区別する)になるけど、コレーションによってはcase insensitive(大文字小文字を区別しない)なものがあるので」「@kamipoさんの記事で明示的にcase_sensitive: trueしてくださいとあるのはそういうことでしたか」「case sensitiveでやりたい人はね☺️」

「コレーションは元々RDB側の設定ですね」「collationって日本語が一応あるんですけど何て言うんでしたっけ…照合順序!」「あーそんな用語ありましたね、使わないけど😆」

参考: MySQLのCollationを理解するためにまとめてみた。 - 6VOX

レシーバのスコープがリークしている場合のクラスレベルのクエリメソッドの利用をdeprecate

# activerecord/lib/active_record/relation.rb#L69
    def new(attributes = nil, &block)
-     block = klass.current_scope_restoring_block(&block)
+     block = _deprecated_scope_block("new", &block)
      scoping { klass.new(attributes, &block) }
    end

つっつきボイス:「これも@kamipoさんのブログで言及されていました」「そっちを読む方が早い☺️」「これ使ってたかも😅」

(中略)scopeのメソッドチェインは条件が累積したrelationを伝搬させるのにクラスグローバルな状態を汚染する実装方法を現状とっており、scope定義内はその汚染されたクラスグローバルな状態の影響を受けるためです。
Rails 6.0でDeprecatedになるActive Recordの振る舞い3つ - かみぽわーるより

「今はunscopeしないとちゃんと動かないのか」「unscopeは使うけど、こういうケースで使ったことがあったかどうか🤔」

Actionable Errorsを追加

エラーをactionableにするには、ActiveSupport::ActionableErrorモジュールをincludeしてactionクラスマクロを呼び、そのアクションを定義する。アクションには名前と実行したいプロシージャが必要。名前はエラーページのボタン名として表示され、クリックするとそのプロシージャが呼び出される。
同PRより

先週公開したEvil Martiansの翻訳記事↓でも言及されていました。

Rails 6のB面に隠れている地味にうれしい機能たち(翻訳)


つっつきボイス:「あ〜なるほど、Actionable ErrorsはActive Supportに定義されているので実はActive Modelに限らず使えるのか: これは意外と便利かも😋」「Webのエラー画面が出たら[サポートに知らせる]ボタンを出すなんて使い方もありそう」「おや、erbとか使ってるし」「とするとActive SupportだけどRailsに依存するのかな?🤔」

# actionpack/lib/action_dispatch/middleware/actionable_exceptions.rb#L3
+require "erb"
+require "action_dispatch/http/request"
+require "active_support/actionable_error"
+
+module ActionDispatch
+  class ActionableExceptions # :nodoc:
+    cattr_accessor :endpoint, default: "/rails/actions"
+
+   def initialize(app)
+     @app = app
+   end

monotonic_subscribeを追加

# activesupport/lib/active_support/notifications.rb#L234
      def subscribe(*args, &block)
-       notifier.subscribe(*args, &block)
+       pattern, callback = *args
+       notifier.subscribe(pattern, callback, false, &block)
+     end
+
+     def monotonic_subscribe(*args, &block)
+       pattern, callback = *args
+       notifier.subscribe(pattern, callback, true, &block)
      end

-     def subscribed(callback, *args, &block)
-       subscriber = subscribe(*args, &callback)
+     def subscribed(callback, pattern, monotonic: false, &block)
+       subscriber = notifier.subscribe(pattern, callback, monotonic)
        yield
      ensure
        unsubscribe(subscriber)
      end

つっつきボイス:「ActiveSupport::Notificationsに追加されてる」「サブスクライブ系はタイミングをしっかりやりたいだろうから、そういう意図で追加したんでしょうね」「へー、こんなブロックになるのか↓」「何だかいっぱい付いてくる😆」「ぱっと見ブロック変数が多いかなと思ったけど、必要なものはこれで全部取れるのはいいですね☺️」

# activesupport/lib/active_support/notifications.rb#L44
  ActiveSupport::Notifications.monotonic_subscribe('render') do |name, start, finish, id, payload|
    name    # => 文字列(上の`above`のようなイベント名)
    start   # => monotonicな時刻(instrumentationされたブロックの実行開始時刻)
    finish  # => monotonicな時刻(instrumentationされたブロックの実行終了時刻)
    id      # => 文字列(発火したイベントのinstrumenterのユニークID)
    payload # => ハッシュ(ペイロード)
  end

ActiveSupport::Notificationsってそもそもどういうときに使うんでしょうか?」「ロガーとかinstrumentation(計測)みたいなpub/subで使われますね🧐」「あ、『Rails自身が持ってるpub/sub機能』だって以前教わったヤツでした😅」「それそれ🎯」

参考: Active Support の Instrumentation 機能 - Rails ガイド
参考: 出版-購読型モデル - Wikipedia

「pub/subで便利そうな機能😋」「終わったら自動で消えてくれるかな?たぶんやってくれるだろう」

非数値キーがある場合のstrong parametersを修正

Changelogにst0012さんの名前がありました。

# actionpack/lib/action_controller/metal/strong_parameters.rb#L226
+   class << self
+     def nested_attribute?(key, value) # :nodoc:
+       key =~ /\A-?\d+\z/ && (value.is_a?(Hash) || value.is_a?(Parameters))
+     end
+   end
...
-     def fields_for_style?
-       @parameters.all? { |k, v| k =~ /\A-?\d+\z/ && (v.is_a?(Hash) || v.is_a?(Parameters)) }
+     def nested_attributes?
+       @parameters.any? { |k, v| Parameters.nested_attribute?(k, v) }
+     end
+
+     def each_nested_attribute
+       hash = self.class.new
+       self.each { |k, v| hash[k] = yield v if Parameters.nested_attribute?(k, v) }
+       hash
      end
# actionpack/lib/action_controller/metal/strong_parameters.rb#L
-     def each_element(object)
+     def each_element(object, &block)
        case object
        when Array
          object.grep(Parameters).map { |el| yield el }.compact
        when Parameters
-         if object.fields_for_style?
-           hash = object.class.new
-           object.each { |k, v| hash[k] = yield v }
-           hash
+         if object.nested_attributes?
+           object.each_nested_attribute(&block)
          else
            yield object
          end
        end
      end

つっつきボイス:「普通にstrong parametersのバグ?」「あ〜、ネステッドでキーがnon-numericだった場合の挙動を修正したのか!」

参考: strong parameters — Action Controller の概要 - Rails ガイド

「これこれ、こういうパラメータ形式↓のときが問題だったと」「ははぁ、これか〜」「これいやらしいパラメータですよね😆: ハッシュのキーが012ときて全部数値になるかと思ったらnew_recordという数値でないヤツが入ってくる😇」「これはこういうパラメータを投げるフォームを書いた人がそもそも悪いのでは😅」

# actionpack/test/controller/parameters/nested_parameters_permit_test.rb#L154
    params = ActionController::Parameters.new(
      book: {
        authors_attributes: {
          '0': { name: "William Shakespeare", age_of_death: "52" },
          '1': { name: "Unattributed Assistant" },
          '2': "Not a hash",
          'new_record': { name: "Some name" }
        }
      })

「こういう予想の斜め上を行くパラメータを投げられるとバグるというのはわかるな〜😭」「各行はハッシュの形式だけどキーに0とか来たときにRailsがarrayとして解釈していたのに、最後はどんでん返し😆」「結局パラメータを全部チェックしないとarrayにしていいかどうかというのは正確にはわからないというヤツなのでは😅」「なんといういやらしいパラメータ😤」

「パラメータといえば、GW中にちょっと調べてBPS社内Slackにも書いたんですけど、こういうパラメータのフォーマットをCGIで一般的にどうやって解釈して展開するか、みたいなHTTP POST周りの仕様って、少なくとも自分が調べた限りではどうもなさそうなんですよね😇」「あ〜、それありそう!」「hoge[0]hoge[1]はたとえばarrayとして解釈する、みたいな仕様がありそうで見当たらない😢」

「HTTPの仕様を追いかけた範囲では、キー(文字列)とバリュー(文字列)がアンパサンド&でつながる、ということしか規定されてなくて、ネストした配列やハッシュがプログラム側でどう展開されるべきか、という仕様は少なくとも見つからなかったんですよ…」

「Railsではこうやってる、ぐらいしか言えないとか?」「いえ、やってることはほぼすべてのCGIプログラムで基本同じなんですが、その仕様がないという🤪」「フレームワークで縛るしかないんでしょうか?」「それも不可能ですね: 誰がどんな形でPOSTしてくるかは縛りようがないので」「たしかに、上みたいなパラメータでもPOSTしていけないということはありませんし😆」「パラメータの解釈はプログラム側に委ねられていると😅: 極端に言えば開きかっこ(が閉じてなくたっていいわけですし🤣」「たしかに🤣」「『文字列だ文句あるか』と🤣」「後でパーサーがエラー吐くかもしれませんが☺️」

Rails

kamipoさん記事


つっつきボイス:「kamipoさんがこういう記事を書くのは珍しいかも」「3つのうち2つは上で見たので、3つ目はwhere.notがNORからNANDになる件」「ちょうど昨日BPSのSlackで、where.notのこれとはまた別の側面の挙動が話題になってましたね」

「ああ、where.notの項で言うネステッドスコープはTopic.toplevel.where(id: Topic.children.select(:parent_id))みたいなヤツで、そしてリレーションにクラスグローバルなステートがあるのか!」「つまり、スコープで評価されたその瞬間のステートがクラスグローバルになるから、unscopeしないと正しく取れない…」

# 同記事より
class Topic < ActiveRecord::Base
  scope :toplevel, -> { where(parent_id: nil) }
  scope :children, -> { where.not(parent_id: nil) }
  scope :has_children, -> { where(id: Topic.children.select(:parent_id)) }
end

# Works as expected.
Topic.toplevel.where(id: Topic.children.select(:parent_id))

# Doesn't work due to leaking `toplevel` to `Topic.children`.
Topic.toplevel.has_children

anyway_config: さまざまな設定を透過的に扱うgem

# 同リポジトリより
# load data from config/my_app.yml, secrets.my_app (if using Rails), ENV["MY_APP_*"]
# MY_APP_VALUE=42
config = Anyway::Config.for(:my_app)
config["value"] #=> 42

# you can specify the config file path or env prefix
config = Anyway::Config.for(:my_app, config_path: "my_config.yml", env_prefix: "MYAPP")

これも以下の記事で紹介されていました。

Rails 6のB面に隠れている地味にうれしい機能たち(翻訳)


つっつきボイス:「このanyway_configもRailsのcredentialに対応したそうです」「秘密キーみたいな情報をcredentialやyamlや環境変数から透過的にアクセスできるそうです」「なるほど、ただこの種のツールは扱いに気をつけないとセキュリティの穴になる可能性があったりしますね⚠」「あー」「そうかも☺️」「思わぬところから上書き差し込みされることがないとは言えないので、十分理解してから使わないとハマるかもしれません🧐」

「たとえばですが、環境変数の設定を最優先するようになっている場合、仮に環境変数に外からインジェクトできるような脆弱性がもし発生すれば、もう何されてもおかしくないし😇」「うんうん」「configファイルに外から触れなくても、そうやって環境変数から攻撃される可能性があることは知っておくべき」「設定の優先順位も理解しておかないといけないんですね」

「でもデフォルトで環境変数を最優先するツールって割と一般的な気がするんですよね😅」「たしかにそういうの多いかも」「環境変数って起動時に読み込まれるものだから、起動するときに環境変数を優先するというのは直感的には理解できるんですけど、実は環境変数は割と書き換えることができてしまうので😆、攻撃の足がかりにされたり、そこまでいかなくても変数汚染されたりする可能性はあります」「自分は環境変数キライ: 汚染される可能性あるし😆」

「環境変数がコンフリクトしないようにするためには、結局うんと長〜い名前を付けることになりますよね😆」「アンスコだらけの😆」「オレオレルールのプレフィックスとか😆」「名前空間という概念がないのでどうしてもしんどくなる😢」

Evil MartiansのレガシーRailsアプリ「テラフォーミング」

Evil Martiansの中の人がRailsConf 2019(ミネアポリス)で発表したスライドです。


つっつきボイス:「テラフォーミングと言っているのは、レガシーRailsアプリの単なるアップグレードにとどまらずに、この際やっておくべきことを全部やっておくという趣旨のようです」「なおTerraforming Marsというボドゲがあるらしいです👽」「なるほどEvil Martiansらしい👽」

参考: テラフォーミング - Wikipedia

「目的は、さくっとgit cloneしてさくっとrails consoleできるようにしたいと☺️」「わかるわかる☺️」「『手順書でひたすら頑張る』オールドスクールなやり方から、docker-compose --buildするニュースクールなやり方に移行する」「今のBPS Webチームも新しいプロジェクトはdocker-composeに移行していますし😆」「理想は理想だけど、やれるかどうかは案件の複雑さにもよるんですよね😅」

「そしてログイン情報の生書きをやめると↓😆」「この辺なかなか参考になりますね👍」「ログイン情報は環境変数から引っ張ってdocker-compose.yamlに設定するようにすれば、本番とまったく同じconfigが使えるようになる、と」「うんうん😋」


「Railsのconfigもこんなふうに、本番かローカルかで挙動を変えるようにしておくと↓」「こんなふうに、本番環境と開発環境でconfigファイルを書き換えずに済むようにできるのは理想ですね🥰」

「それから、環境変数やHerokuのconfigが設定がこんなふうに長くなる↓問題😇」「Envヘル!👹 」「🤣」「🤣」「そこでさっき登場したanyway_configを使うと、既にconfigファイルになってしまった設定であっても、特に頑張らずにdocker-compose.yamlのENVに渡せるようになるよ、という流れ」「ははぁ〜なるほど!」「anyway_configを作ったモチベーションはそこにあったのね: 気持ちはとてもよくワカル☺️」

「スライドはそこから先もSidekiqやらFactoryBotやらを改善したりテストを高速化したりしてますね」「TestProf gemが見えた↓」

TestProf: Ruby/Railsの遅いテストを診断するgem(翻訳)

「factoryのlinterですか」「こう書く方がいいよ、みたいなサジェスチョンでしょうか?」「あ、これはFactoryBotのバージョンが上がって古い書き方が使えなくなったというチェックだ」「なるほど、そっちですか」「これを自動でやってくれるなら悪くなさそう😋」

「他にもbundler-auditを使ったりしてますね」「ぎょぎょ、.rubocop_strict.yml使ってるし😅」「聞くからにめちゃシバかれそう😣」「テストで使われているgemをトラックするGem Trackerですって」

「使われていないテンプレートを見つけられるTemplates Tracker!」「この辺かなり参考になるな〜❤️」「あとはどこまで信用するかですが😆」「may be unusedですって😆」「まあ本当に使われていないのかどうかについては動かさないとわかりませんけどね☺️」「Javaならはっきりわかるけど、Rubyですから☺️」

「ほほー、tracerouteは使われていないルーティングを見つけてくれるヤツか」「これamatsudaさんのgemだ!」

「学びが凝縮されたいいスライド!👍」「これはいいですね😍」


後で気が付きましたが、発表で使われているツールなどは以下にすべてまとまっています。

Postman: APIサーバーのデバッグ/自動テストツール


同サイトより


つっつきボイス:「APIを叩いたりテストを自動で回したり、ちょっと値を変えて試したり、みたいな、Chromeのツールとかにもあったりするヤツみたいですね」「そんな感じのようです」「こういうツールは、どれかひとつを使えていればいいのかなと思います☺️」

「こういうツールで重要なのは、作ったAPI定義ファイルを他の人と共有できること」「あ、たしかに」「それができていれば、最悪でもテスト用のconfigデータを共有すればまったく同じAPI POSTのテストができますし🧐」「このPostmanはオンラインでやるタイプのツールですね」

参考: APIの開発がむちゃくちゃ捗る「Postman」の使い方 - WPJ

ポーリングをJSなしで行う

render_asyncでやれるそうです。

Rails: render_async gemでレンダリングを高速化(翻訳)


つっつきボイス:「まず、コントローラでrender partialして、そのパーシャルの中にpromiseでフェッチするコードを書くというのが普通のやり方であると」「ふむふむ」

# 同記事より
# app/controllers/movies_controller.rb
class MoviesController < ApplicationController
  # somewhere inside Movies controller
  def rating
    @rating = @movie.rating

    render partial: 'movie_rating'
  end
end
<!-- app/views/movies/show.html.erb -->

<div id="rating">Loading rating...</div>

<script>
  const checkRating = () => {
    fetch("<%= movie_rating_path(@movie) %>")
      .then(response => response.text())
      .then(response => {
        document.getElementById("rating").innerHTML = response;
      });
  };
  setInterval(checkRating, 2000);
</script>

「それがrender_asyncを使うとこう書けると↓: ああ、たしかに」

<!-- app/views/movies/show.html.erb -->

<%= render_async movie_rating_path(@movie), interval: 2000 %>

render_asyncの使いどころ

「ちなみにrender_asyncはRubyのスクリプトでありながら、内部的にはJavaScriptで取ってきてそこに挿入するというようなことをやります」「上のpromiseとAjaxで取ってきてレンダリングするのと内容は同じ」「誰もがやりたくなるヤツですね☺️」

「render_asyncは一見ちょろいんですが、ものすごく重いサイトを手軽に軽くするのにとても便利😋」「render_asyncはもっと評価されるべき」

「もっと言うと、ページのDOM構造なんかをきちんと設計したうえでrender_asyncを使えば、HTMLを全部CDNに乗せることができます」「おぉ〜」「たとえば、ページの中でキャッシュ可能でない特定の部分をすべてrender_asyncで表示すれば、それ以外のキャッシュ可能なコンテンツは全部CDNに乗っていてもよくなります」「なるほど!」「つまり表示がすごく速くなるという圧倒的なメリットを得られる✈」

参考: コンテンツデリバリネットワーク - Wikipedia

「ただ最初からそういうふうに設計されていないと、CDNのキャッシュが効くかどうかドキドキしながらCloudFrontなんかを設定することになりますが😆」「😆」「render_asyncを後から入れるのは大変なんでしょうか?」「たとえCDNを使わないとしても、1ページあたりのレイアウトコンポーネント数が増えすぎたときにrender_asyncで表示を速くできるので、一定のメリットはあります🧐」

「よくあるランキングページで考えると、たとえばページにある特定のコンポーネントの表示が非常に遅くて、レンダリング時間の半分がそのコンポーネントに取られているといった場合に、そのコンポーネントだけrender_asyncで表示すれば、そこだけ遅延読み込みになるので結果として速くなる」「ふむふむ」「なので後から入れる手もあります😋」

「でもさっきも言ったように、render_asyncのパワーを最大限に発揮するにはページの設計時点から意識することですね: ユーザーごとに表示内容が違う動的な部分は全部render_asyncにするとか、フォームもrender_asyncにするならCSRFが必ずユーザーごとに分かれるようにするとか、やるならそこまでやらないと😎」「いいですね!😋」「そこまでやれれば、RailsアプリでありながらCDNにがっつり乗せて高速化できます❤️」

RailsのCSRF保護を詳しく調べてみた(翻訳)

StimulusJSでネステッドフォームを作る(Ruby Weeklyより)

<!--同screencastより-->
  <h1>Tasks</h1>
  <div data-controller="nested-form">
    <template data-target='nested-form.template'>
      <%= form.fields_for :tasks, Task.new, child_index: 'TEMPLATE_RECORD' do |task| %>
        <%= render 'task_fields', form: task %>
      <% end %>
    </template>

    <%= form.fields_for :tasks do |task| %>
      <%= render 'task_fields', form: task %>
    <% end %>

    <div data-target="nested-form.add_item">
      <%= link_to "Add Task", "#", data: { action: "nested-form#add_association" } %>
    </div>
  </div>

つっつきボイス:「一部で流行っているという噂のStimulusJS😆」「DHHのいるBasecampが推しているのがこれですね」「へ〜」

Turbolinksとの相性もいいと謳っています。


同サイトより

split: レポート機能付き、RackベースのA/Bテスト


つっつきボイス:「まあA/Bテストは何で動かしてもいいですけど😆」「ははぁ、テストをRackに挟んでおけて、かつRack変数をよしなに参照して切り替えられるという感じか」

<!--同リポジトリより: ビューの場合-->
<% ab_test(:login_button, "/images/button1.jpg", "/images/button2.jpg") do |button_file| %>
  <%= image_tag(button_file, alt: "Login!") %>
<% end %>
# 同リポジトリより: コントローラの場合
def register_new_user
  # 無料ポイントをいくらにすれば購入するユーザーが最大化されるかをチェック
  @starter_points = ab_test(:new_user_free_points, '100', '200', '300')
end

「ところでRackに入れるメリットって…?」「Rackの手前にA/Bテストを置けばどこでも使える、とか?」「ビューでA/Bテストするならクックパッドのchanko gemとか使えばできるし、と思ったけどchankoもコントローラに置けるし」「ミドルウェアで変数を参照したいとき、かな?🤔」

「お〜、split gemの方はレポート機能があるのか↓」「これはエライ!😋」「エライ!😍」「ちゃんと実行計画を立ててイベントフックを書いておけば、実データでこういうレポートを生成できるのはありがたい🙏」


splitrb/splitより

# 同リポジトリより: イベントフック
Split.configure do |config|
  config.on_trial  = :log_trial # すべてのトライアルで実行
  config.on_trial_choose   = :log_trial_choose # 新規ユーザーのみで実行
  config.on_trial_complete = :log_trial_complete
end

def log_trial(trial)
  logger.info "experiment=%s alternative=%s user=%s" %
    [ trial.experiment.name, trial.alternative, current_user.id ]
end

def log_trial_choose(trial)
  logger.info "[new user] experiment=%s alternative=%s user=%s" %
    [ trial.experiment.name, trial.alternative, current_user.id ]
end

def log_trial_complete(trial)
  logger.info "experiment=%s alternative=%s user=%s complete=true" %
    [ trial.experiment.name, trial.alternative, current_user.id ]
end

「A/Bテストをサーバーサイドでやりたいならsplit gemはよさそう👍」「まあサーバーサイドでやるのか、それともGoogleタグマネージャ的なツールを使うのかは悩ましいですけど😅」「サーバーサイドがからむA/Bテストはこういう形でないとできなさそうではあるし」

「ちなみに同じURLにGoogleのボットがアクセスしたときだけ別の画面を出す(クローキング)みたいなことをするとペナルティでランク下げられます😎」「でしょうね〜😆」「ボット相手に見た目を取り繕うのはアカンと😆」

参考: ABテストでペナルティ?/Googleが警鐘
参考: クローキング - Search Console ヘルプ

その他Rails


つっつきボイス:「PrometheusでやるヤツはRubyKaigi 2019でもやってましたね」

こちらも↓Evil Martiansでした😳。

「2本目は、Active Recordを長らくやってたコアコミッターのSean Griffinさんが、ShopifyとRailsから同時に離れることになったという、ご本人からのお別れメッセージでした」「ありゃ〜」「ActiveRecord::Attributesの人だ」「おおそういえば!」「あの大人気のライブラリですね」

Rails5: ActiveRecord標準のattributes API(翻訳)

「そしてSeanさん、今度はRustでORMやるのか!」「その名もDiesel」

「このスクショも、OKを押してリポジトリのアクセス権を手放す前に記念で撮っておいたんでしょうね」「気持ちわかる」「もらい泣きしそう😭」


同記事より

Ruby

「Rubyのunlessは使いたくない」


つっつきボイス:「『unlessifより文字数が多いから』使わないって言ってる?」「7文字の条件文がunlessだと10文字になると」「design mistakeとは言ってますが😆」「でも自分はunlessが不要とまでは思わないな〜」

「そもそもこの書き方↓は前からよくないって言われてるんだし、今更すぎる感😆」「うん、これはだめでしょ😆」「if !で書いてもわかりにくいし😆」

destroy(thing) unless open

Ruby: 紛らわしい条件文を書かないこと(翻訳)

「『タイムマシンがあったらunlessを消し去りたい』と🕙」「上の書き方がよくないということについては同意するけど、unlessそのものがよくないとまでは思わないし☺️」「unlessの設計上の弱点を問題にするというか」「そういう書き方ができてしまうという意味ではdesign mistakeなのかも🤔」「まあこれ をパーサーレベルで禁止するのは結構難しそうだし」「でしょうね〜」

「そういうのを叱ってくれるのがRubocopなんだし👮🏼‍♀️」「『条件が複数の場合はunlessを使わない』とかunless !はダメとか🤣」「怒られる前に自分がわけわからなくなる〜🤣」「unlesselsifが付けられないなんてのもありましたね↓」「そこはちゃんとできないようになってるし☺️」「あったらコワい🥶」

Ruby: unless式にはelsifを書けない

Rubyの同一性チェックとitselfHacklinesより)

# 同記事より
[3, 1, 2, 1, 5].group_by(&:itself)
# => {3=>[3], 1=>[1, 1], 2=>[2], 5=>[5]}

Ruby: Kernel#itselfの美学(翻訳)


つっつきボイス:「itselfっていうキーワードがあることを忘れてました😅」「itself好きな人いますよね☺️」「自分itself好き〜❤️: 上みたいなgroup_by(&:itself)とかよく使うし」

参考: instance method Object#itself (Ruby 2.6.0)

itselfってどういう使いどころがあるんでしたっけ?」「ん〜何かのメソッドに似てるんだけど…そうそう、tapと似てる!」「自分を返すメソッドをgroup_byと組み合わせるとそういう感じになって、上みたいに『この要素はいくつあるか』というカウントも取れる」

参考: instance method Object#tap (Ruby 2.6.0)

self を引数としてブロックを評価し、self を返します。
docs.ruby-lang.orgより

itselfってそれだけ見ると何に使うの?という感じなんだけど、組み合わせて使うと上みたいなのが一文で書けるんですよね😋」「そういえば英語記事の人も同じこと言ってますね」「自分も以前のウォッチでitselfを見たときにピンとこなかったけど、その後group_byと組み合わせるのを見てなるほど〜って思えたし😍」「itselfはメソッドチェーンを短く書けるというメリットはありますね: 自分は使いませんが😆」「😆」「まあ言われてみればitselfを使う方が美しいといえば美しいかも✨」

itselfがツボにハマるところってそんなに多くはなさそう?🤔」「tapもそういうところあるかも😆」「自分はRailsコンソールで値をさっと見たいときにtap使ったりしてます😋」「tap、自分は使わないし😆: 別にtapじゃなくても書けるんじゃね?って」「覚えるとつい使いたくなる魔力があるのかも🧙🏼‍♂️」「yield_self↓もうれしくてつい使っちゃったり😆」「今はthenになったんでしたっけ?」「エイリアスだからどちらも使えます☺️」

Ruby 2.5の`yield_self`が想像以上に何だかスゴい件について(翻訳)

Gem Check: gemを作るときのチェックリスト


同サイトより


つっつきボイス:「kazzさんが『gem作ろうかな』と言ってたので」「そうそう、アプリの外部APIへのアクセスをgemに切り出しとくといいよって話を聞きまして😆」「上のサイトにはチェックボックスも付いてます😋」「.gemspecって正しく書かれていて欲しいと常日頃思っていても、自分で正しく書こうとすると割と大変だし😅」

st0012さんが以前『gemをGitHubに置くと.gemspecとGitHubの情報の一部が重複する』って言ってました」「そうそう、心配だから.gemspecに多めに書いておくとかぶっちゃったりとか😢」

「それもあって、自分で.gemspec書くときはできるだけgem dependencyがないようにしますね😆」「でないとバージョン違いでgem dependencyがかぶって、それはもう悲惨なことに😇」「あ〜」「それはコワい😱」「なのでdependencyはできるだけ避けて、Rails用のgemならRailsにdependするとかしないとね」

その他Ruby


つっつきボイス:「最初の2つはRubyKaigi 2019の報告で、1つはまたしてもEvil Martiansです😆」

「3つ目は例のbootstrap-sassの件に関連して、フィンガープリントをチェックしようよという記事です」「GPGがツールでチェックされているときとか、sshのknown_hostsとかに初めて追加されるときなんかは見ますね: あれは使われたことのないキーが使われるときにチェックされるので」「ですね☺️」「いつも使ってるサーバーなのにknown_hosts追加のメッセージが出たらさすがに緊張感走るし、見落としたらいけないヤツですし🧐」

参考: GNU Privacy Guard - Wikipedia
参考: OpenSSH known_hosts ファイルの使用 | SSH Tectia® Client 6.0

セキュリティ: 3月26日のbootstrap-sass gem 3.2.0.3に危険なコードが含まれていた(影響を受けるサイトは少ない可能性)


つっつきボイス:「2006年のRailsConfのスケジュール表をたまたま拾って、ずいぶんぎっしりあるんだなと思ったので」「10年以上前か〜」「capistranoとか、全体にデプロイの話が多いっすね」「13年前だとDockerとか影も形もない頃だし」「それ以前にDockerが動くカーネルすらない時代🤣」「そっか🤣」「仮想化はあったんでしょうけど」「cgroupぐらいはあっただろうけど、コンテキストのスイッチまでできたかどうか」

参考: 第3回 Linuxカーネルのコンテナ機能[2] ─cgroupとは?(その1):LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術|gihyo.jp … 技術評論社


つっつきボイス:「こういう人にスポットライトが当たるのがRuby界隈のいいところですね😍」「それはありますね☺️」「こうやって裏で支えている人がいなかったら安定して使えなくなるわけですし」

Ruby trunkより

提案: OptionParser::ACは外してもよいのでは

参考: Class: OptionParser::AC (Ruby 2.6.1)


つっつきボイス:「optparseかと思ったら、optparseに含まれているACというライブラリは誰も使ってないしドキュメントもないから外そうということでした」「issueに書かれている--enable-fiber-coroutine=amd64というオプションがマニアック😆」

「たまに素のRubyスクリプトを書くときにoptparseはやっぱり便利ですね: 一度使ってみるとよさがワカル👍」「以前のウォッチでもoptparseがいいという話ありましたね↓」「optparseはいいですよホント❤️」

週刊Railsウォッチ(20180518)Paperclip開発終了、RailsアプリをGDPR準拠に、optparseはやっぱりいい、Rubyの`super`ほか


前編は以上です。

バックナンバー(2019年度第2四半期)

週刊Railsウォッチ(20190513-1/2前編)6.0の地味に嬉しい機能、ActiveModelエラーの扱いが変更、Railsのリクエスト/レスポンスをビジュアル表示ほか

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の監修および半分程度を翻訳、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れて更新翻訳中。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好きで、Goで書かれたRubyライクなGoby言語のメンテナーでもある。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

BPSアドベントカレンダー

週刊Railsウォッチ