- Ruby / Rails関連
週刊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の翻訳記事↓でも言及されていました。
つっつきボイス:「あ〜なるほど、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 ガイド
「これこれ、こういうパラメータ形式↓のときが問題だったと」「ははぁ、これか〜」「これいやらしいパラメータですよね😆: ハッシュのキーが0
、1
、2
ときて全部数値になるかと思ったら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さん記事
blogged / “Rails 6.0でDeprecatedになるActive Recordの振る舞い3つ - かみぽわーる” https://t.co/fwUx6vUD3I
— Ryuta Kamizono (@kamipo) May 15, 2019
つっつきボイス:「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")
これも以下の記事で紹介されていました。
つっつきボイス:「このanyway_configもRailsのcredentialに対応したそうです」「秘密キーみたいな情報をcredentialやyamlや環境変数から透過的にアクセスできるそうです」「なるほど、ただこの種のツールは扱いに気をつけないとセキュリティの穴になる可能性があったりしますね⚠」「あー」「そうかも☺️」「思わぬところから上書き差し込みされることがないとは言えないので、十分理解してから使わないとハマるかもしれません🧐」
「たとえばですが、環境変数の設定を最優先するようになっている場合、仮に環境変数に外からインジェクトできるような脆弱性がもし発生すれば、もう何されてもおかしくないし😇」「うんうん」「configファイルに外から触れなくても、そうやって環境変数から攻撃される可能性があることは知っておくべき」「設定の優先順位も理解しておかないといけないんですね」
「でもデフォルトで環境変数を最優先するツールって割と一般的な気がするんですよね😅」「たしかにそういうの多いかも」「環境変数って起動時に読み込まれるものだから、起動するときに環境変数を優先するというのは直感的には理解できるんですけど、実は環境変数は割と書き換えることができてしまうので😆、攻撃の足がかりにされたり、そこまでいかなくても変数汚染されたりする可能性はあります」「自分は環境変数キライ: 汚染される可能性あるし😆」
「環境変数がコンフリクトしないようにするためには、結局うんと長〜い名前を付けることになりますよね😆」「アンスコだらけの😆」「オレオレルールのプレフィックスとか😆」「名前空間という概念がないのでどうしてもしんどくなる😢」
⚓Evil MartiansのレガシーRailsアプリ「テラフォーミング」
Evil Martiansの中の人がRailsConf 2019(ミネアポリス)で発表したスライドです。
つっつきボイス:「テラフォーミングと言っているのは、レガシーRailsアプリの単なるアップグレードにとどまらずに、この際やっておくべきことを全部やっておくという趣旨のようです」「なおTerraforming Marsというボドゲがあるらしいです👽」「なるほどEvil Martiansらしい👽」
「目的は、さくっと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が見えた↓」
「factoryのlinterですか」「こう書く方がいいよ、みたいなサジェスチョンでしょうか?」「あ、これはFactoryBotのバージョンが上がって古い書き方が使えなくなったというチェックだ」「なるほど、そっちですか」「これを自動でやってくれるなら悪くなさそう😋」
「他にもbundler-auditを使ったりしてますね」「ぎょぎょ、.rubocop_strict.yml使ってるし😅」「聞くからにめちゃシバかれそう😣」「テストで使われているgemをトラックするGem Trackerですって」
- リポジトリ: rubysec/bundler-audit: Patch-level verification for Bundler
- リポジトリ: terraforming-rails/tools/gem_tracker at master · evilmartians/terraforming-rails
「使われていないテンプレートを見つけられるTemplates Tracker!」「この辺かなり参考になるな〜❤️」「あとはどこまで信用するかですが😆」「may be unusedですって😆」「まあ本当に使われていないのかどうかについては動かさないとわかりませんけどね☺️」「Javaならはっきりわかるけど、Rubyですから☺️」
「ほほー、tracerouteは使われていないルーティングを見つけてくれるヤツか」「これamatsudaさんのgemだ!」
- リポジトリ: amatsuda/traceroute: A Rake task gem that helps you find the unused routes and controller actions for your Rails 3+ app
- リポジトリ: cookpad/chanko: Rapidly and safely prototyping your rails application
「学びが凝縮されたいいスライド!👍」「これはいいですね😍」
後で気が付きましたが、発表で使われているツールなどは以下にすべてまとまっています。
⚓Postman: APIサーバーのデバッグ/自動テストツール
つっつきボイス:「APIを叩いたりテストを自動で回したり、ちょっと値を変えて試したり、みたいな、Chromeのツールとかにもあったりするヤツみたいですね」「そんな感じのようです」「こういうツールは、どれかひとつを使えていればいいのかなと思います☺️」
「こういうツールで重要なのは、作ったAPI定義ファイルを他の人と共有できること」「あ、たしかに」「それができていれば、最悪でもテスト用のconfigデータを共有すればまったく同じAPI POSTのテストができますし🧐」「このPostmanはオンラインでやるタイプのツールですね」
参考: APIの開発がむちゃくちゃ捗る「Postman」の使い方 - WPJ
⚓ポーリングをJSなしで行う
render_asyncでやれるそうです。
つっつきボイス:「まず、コントローラで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にがっつり乗せて高速化できます❤️」
⚓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との相性もいいと謳っています。
- リポジトリ: stimulusjs/stimulus: A modest JavaScript framework for the HTML you already have
- サイト: Stimulus: A modest JavaScript framework for the HTML you already have.
⚓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もコントローラに置けるし」「ミドルウェアで変数を参照したいとき、かな?🤔」
- リポジトリ: cookpad/chanko: Rapidly and safely prototyping your rails application
参考: Ruby 開発備忘録: vanityとchankoを使ってRailsで簡単安全にABテストをする
「お〜、split gemの方はレポート機能があるのか↓」「これはエライ!😋」「エライ!😍」「ちゃんと実行計画を立ててイベントフックを書いておけば、実データでこういうレポートを生成できるのはありがたい🙏」
# 同リポジトリより: イベントフック
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
- 元記事: Instrumenting Ruby on Rails with Prometheus | FireHydrant
- 元記事: Moving on from Rails and what’s next -- Sean GriffinさんがRailsからリタイア
つっつきボイス:「PrometheusでやるヤツはRubyKaigi 2019でもやってましたね」
こちらも↓Evil Martiansでした😳。
「2本目は、Active Recordを長らくやってたコアコミッターのSean Griffinさんが、ShopifyとRailsから同時に離れることになったという、ご本人からのお別れメッセージでした」「ありゃ〜」「ActiveRecord::Attributes
の人だ」「おおそういえば!」「あの大人気のライブラリですね」
「そしてSeanさん、今度はRustでORMやるのか!」「その名もDiesel」
- サイト: Diesel
「このスクショも、OKを押してリポジトリのアクセス権を手放す前に記念で撮っておいたんでしょうね」「気持ちわかる」「もらい泣きしそう😭」
同記事より
⚓Ruby
⚓「Rubyのunless
は使いたくない」
Controversial Ruby opinion, if I had a time machine and mandate I would remove the keyword `unless` from Ruby. pic.twitter.com/J9zWGN9CeG
— Sam Saffron (@samsaffron) May 14, 2019
つっつきボイス:「『unless
がif
より文字数が多いから』使わないって言ってる?」「7文字の条件文がunless
だと10文字になると」「design mistakeとは言ってますが😆」「でも自分はunless
が不要とまでは思わないな〜」
「そもそもこの書き方↓は前からよくないって言われてるんだし、今更すぎる感😆」「うん、これはだめでしょ😆」「if !
で書いてもわかりにくいし😆」
destroy(thing) unless open
「『タイムマシンがあったらunless
を消し去りたい』と🕙」「上の書き方がよくないということについては同意するけど、unless
そのものがよくないとまでは思わないし☺️」「unless
の設計上の弱点を問題にするというか」「そういう書き方ができてしまうという意味ではdesign mistakeなのかも🤔」「まあこれ をパーサーレベルで禁止するのは結構難しそうだし」「でしょうね〜」
「そういうのを叱ってくれるのがRubocopなんだし👮🏼♀️」「『条件が複数の場合はunless
を使わない』とかunless !
はダメとか🤣」「怒られる前に自分がわけわからなくなる〜🤣」「unless
にelsif
が付けられないなんてのもありましたね↓」「そこはちゃんとできないようになってるし☺️」「あったらコワい🥶」
⚓Rubyの同一性チェックとitself
(Hacklinesより)
# 同記事より
[3, 1, 2, 1, 5].group_by(&:itself)
# => {3=>[3], 1=>[1, 1], 2=>[2], 5=>[5]}
つっつきボイス:「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
になったんでしたっけ?」「エイリアスだからどちらも使えます☺️」
⚓Gem Check: gemを作るときのチェックリスト
- サイト: Gem Check — Writing Better Ruby Gems Checklist -- Gemを作るときのチェックリスト
- リポジトリ: palkan/gem-check: GemCheck: Writing Better Ruby Gems Checklist
つっつきボイス:「kazzさんが『gem作ろうかな』と言ってたので」「そうそう、アプリの外部APIへのアクセスをgemに切り出しとくといいよって話を聞きまして😆」「上のサイトにはチェックボックスも付いてます😋」「.gemspecって正しく書かれていて欲しいと常日頃思っていても、自分で正しく書こうとすると割と大変だし😅」
「st0012さんが以前『gemをGitHubに置くと.gemspecとGitHubの情報の一部が重複する』って言ってました」「そうそう、心配だから.gemspecに多めに書いておくとかぶっちゃったりとか😢」
「それもあって、自分で.gemspec書くときはできるだけgem dependencyがないようにしますね😆」「でないとバージョン違いでgem dependencyがかぶって、それはもう悲惨なことに😇」「あ〜」「それはコワい😱」「なのでdependencyはできるだけ避けて、Rails用のgemならRailsにdependするとかしないとね」
⚓その他Ruby
- 元記事: RubyKaigi 2019: A speaker’s report — Martian Chronicles, Evil Martians’ team blog
- 元記事: RubyKaigi and the Path to Ruby 3 | Square Corner Blog
- 元記事: Better Security Through Package Fingerprints | You’ve Been Haacked
つっつきボイス:「最初の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に危険なコードが含まれていた(影響を受けるサイトは少ない可能性)
Also, the schedule from #railsconf 2006. #rubyfriends pic.twitter.com/FNVFG5yVWS
— ewlarson (@ewlarson) May 6, 2019
つっつきボイス:「2006年のRailsConfのスケジュール表をたまたま拾って、ずいぶんぎっしりあるんだなと思ったので」「10年以上前か〜」「capistranoとか、全体にデプロイの話が多いっすね」「13年前だとDockerとか影も形もない頃だし」「それ以前にDockerが動くカーネルすらない時代🤣」「そっか🤣」「仮想化はあったんでしょうけど」「cgroupぐらいはあっただろうけど、コンテキストのスイッチまでできたかどうか」
参考: 第3回 Linuxカーネルのコンテナ機能[2] ─cgroupとは?(その1):LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術|gihyo.jp … 技術評論社
RubyKaigi 2019 Keynote レポート「nagachikaさん「安定したRubyを届けたい –All bugfixes are incompatibilities–」 〜RubyKaigi 2019 2日目 基調講演」公開 https://t.co/JkrjT3mD7r
— gihyo.jp (@gihyojp) May 15, 2019
つっつきボイス:「こういう人にスポットライトが当たるのが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のリクエスト/レスポンスをビジュアル表示ほか
- 20190508-2/2後編 サロゲートキーのコスト、Cloud RunとLambdaの違い、miniredis、CSS Subgridほか
- 20190507-1/2前編 Rails 6.0.0rc1が4/24にリリース、Rails 6の新メソッド群、RubyリポジトリがCgitに移行ほか
- 20190416-2/2後編 最近のRDBMS市場、Flutterがデスクトップにも向かう、書籍『失敗から学ぶRDBの正しい歩き方』ほか
- 20190415-1/2前編 Railsバージョンアップに便利なstill_life gem、Zeitwerkの改修進む、named_capture追加ほか
- 20190409-2/2後編 Ruby 2.3系サポート終了、Thoughtbotのコーディング指南書、PostgreSQLのgenerated column、Chromebrewほか
- 20190408-1/2前編 RubyKaigiの予習資料、Rails「今年ベストのプルリク」、numbered parametersの議論ほか
- 20190402-2/2後編 Apache Arrowとは何か、prop drillingはアンチパターン、Node-REDほか
- 20190401-1/2前編 Rails 5.2.3/5.1.7がリリース、Railsdmの「Railsの正体」、Ruby 2.7のnumbered parameter、新元号「令和」ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSなど)です。