Quantcast
Channel: Ruby on Railsの記事一覧|TechRacho by BPS株式会社
Viewing all 1412 articles
Browse latest View live

Rails開発者が採用面接で聞かれる想定Q&A 53問(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

Rails開発者が採用面接で聞かれる想定Q&A 53問(翻訳)

私はこれまで100人を超えるRuby on Rails開発者と面接を重ね、私自身も職階に関する面談をいくつも受けました。本記事は、これまで私が受けたり尋ねたりした質疑応答をまとめたものです。

2020年現在、どれほど多くの大企業がRailsを利用していることを知ったら皆さんは驚くかも知れません。Shopify、Airbnb、GitHub、Dribble、Etsy、Kickstarter、Zendesk、Twitch、500px、InstacartもRailsを採用している大企業です。

本記事が、これから面接を受ける開発者や、開発者候補の面接を担当する面接官のお役に立てば幸いです。

それでは質疑応答集を順不同でご紹介しましょう。

Q1: ブログアプリで記事のリストを取得するときのリクエスト/レスポンスサイクルをひととおり説明してください

ユーザーがボタンをクリックすると、/articlesというURLへのGETリクエストが発生します。Webサーバーがこのリクエストを受け取ります。続いてRailsは、routes.rbで割り当てられているURL/コントローラに基づいて、リクエストに対応するコントローラアクション#indexを実行します。

コントローラはArticle.allを呼び出して、Articleモデル経由でデータベースから記事のコレクションを読み込みます。このコレクションは、あるインスタンス変数に代入されます。

ビューは、記事のリストを表示するためにそのインスタンス変数の値を展開し、リクエストを送信したユーザーに向けてレンダリングします。

Q2:「Rubyでは(ほぼ)あらゆるものがオブジェクトである」について説明してください

オブジェクト指向言語におけるオブジェクトとは、クラスのインスタンスのことです。Rubyの場合、たとえば以下のように、あらゆるクラスがClassクラスのインスタンスになっています。

7.class #=> Fixnum
7.class.class #=> Class

ただし、中にはオブジェクトではないものもあります。ブロック、メソッド、(ifelseなどの)条件文などはオブジェクトではありません。

これは、Rubyにおいてほとんどのものが同じように振る舞い、それによって他の言語よりも扱いやすくなっていることをあなたが理解しているかどうかを尋ねる質問です。

Q3: Rubyの型は静的ですか?動的ですか?

Rubyは動的型付けです。したがって、変数の型をいつでも変更できます。

Rubyでは、以下のようなコードを繰り返し実行してもエラーにはなりません。

x = 1
x = "foo"

Q4: Rubyのゲッターとセッターについて説明してください

Rubyではゲッター(getter)を用いてインスタンス変数にアクセスでき、セッター(setter)を用いてインスタンス変数に値を設定できます。

ゲッターメソッドやセッターメソッドは、以下のように手動で定義することもできます。

Rubyでは、よりクリーンな方法でゲッターやセッターを定義できる以下の3種類のアクセサメソッドが提供されています。

  • attr_reader(ゲッター)
  • attr_writer(セッター)
  • attr_accessor(ゲッターとセッター)

Q5: Rubyであるメソッドを呼び出したときの動作を説明してください

メソッド名を含む1件のメッセージがそのオブジェクトに送信されます。オブジェクトにそのメソッドが存在する場合は、オブジェクトがそのメソッドを呼び出します。

以下のようにRubyのsendメソッドの動作を考えると、この点がよりよく見えてきます。

obj.hello  #=> 'hello'
obj.send(:hello) #=> 'hello'

Q6: あるRailsアプリ内のルーティングをすべて表示してください

$ rake routes

上の後に| grep <keyword>を追加して、表示されたルーティングをフィルタすることもできます。

Q7: Gemfileについて説明してください

Gemfileは、ひとつのRubyアプリケーションで利用される依存関係を指定します。これはプロジェクトのルートディレクトリに置かれます。

Q8: Gemfile.lockについて説明してください

Gemfile.lockには、インストールされたgemの正確なバージョンが記録されています。これにより、プロジェクトを別のPCにクローンしたときに同じバージョンのgemがインストールされます。

対照的に、Gemfileで特定のバージョンを指定していないgemについては、最新バージョンのgemがインストールされます。

Q9: Railsでどんなデザインパターンを使ったことがありますか?

Railsには、Service Objectパターン、Value Objectパターン、Form Objectパターン、Query Objectパターン、View Objectパターン、Policy Objectパターン、Decoratorパターンといった多くのデザインパターンがあります。

各デザインパターンとコード例については、以下のチュートリアル記事に詳しく載っています。

参考: 7 Design Patterns to Refactor MVC Components in Rails — SitePoint

Q10: Railsではデータベースのステートをどのように管理するか説明してください

開発者は手動でマイグレーションファイルを生成し、そこに指示を追加します。

これらのマイグレーションファイルは、Active Recordに対して既存のデータベースのステートの「変更方法」を指示します。そのために、過去のマイグレーションファイルを削除または変更するとデータベースのステートに悪影響を及ぼす可能性があり、おすすめできません。

対照的に、Djangoなどの他のフレームワークではマイグレーションファイルを作成する場合は、データベースの「最終的なステート」を指示し、それを元に必要な変更を行うためのマイグレーションファイルが自動生成されます。

Q11: countlengthsizeの違いを説明してください

count
レコードの件数をカウントするSQLクエリを実行します。これはDBとメモリでレコード数が違う可能性がある場合に有用です。
length
メモリ上のコレクションに含まれるアイテムの件数を返します。データベーストランザクションを実行しない分、countより高速です。lengthは文字列の文字数をカウントするときにも使われます。
size
これはlengthのエイリアスなので動作はlengthと同じです。

Q12: 認可(authorization)をどのように実装しましたか?

(認証: authenticationとお間違いなきよう)

認可は、ユーザーの種類に応じて、アプリで許可するアクセスレベルを変更することに関連します。これは、アクセスレベルの異なるユーザータイプがとても多い場合に便利です。

認可実装用にはPunditCanCanCanといったgemがあります。

コールバックとは何かを説明してください

コールバック(callback)は誤解を招きがちな用語です(訳注: 英語圏では電話の「折り返し」を想像させるためと思われます)。コールバックは、オブジェクトのライフサイクルの中でメソッドを実行するフックを指します。

before_validationafter_saveafter_destroyなど、オブジェクトの作成、更新、削除などに多くのコールバックが存在します。

コールバックは、たとえばUserレコードが作成されたときに、それに関連付けられているContactレコードを作成するといった条件付けのロジックを記述するのに有用です。

Q14: before_saveコールバックとafter_saveコールバックの使い分けについて説明してください

あるオブジェクトがsaveされた後に更新をかける場合、更新を永続化させるために追加のデータベーストランザクションが必要になります。つまり、あるオブジェクトの「属性」を更新する場合はbefore_saveコールバックの方が効率的です。

しかし、オブジェクトを保存するまでは存在しない情報もあります(idなど)。つまり、関連付けられたレコードの作成にidが必要な場合は、after_saveコールバックを実行しなければならないでしょう。

Q15: Railsの「イニシャライザ」について説明してください

イニシャライザには、アプリの起動時にのみ実行する設定ロジックを置きます。つまり、イニシャライザの内容を変更した場合はRailsサーバーの再起動が必要です。イニシャライザは/config/initializers/ディレクトリの下に置かれます。

Q16: deletedestroyの違いを説明してください

delete
レコードを1件削除する
destroy
レコードを1件削除し、コールバックを実行する

Railsアプリのモデルファイル関連付けで最もよく使われるのはdestroyコールバックです。たとえば、以下のコードはarticleがdestroyされると関連するcommentsもdestroyされます。

class Article < < BaseController
  has_many :comments, dependent: :destroy
end

Q17:「ファットモデル、薄いコントローラ」の意味を説明してください

ビジネスロジックはコントローラではなくモデルに配置すべきです。そうすることでロジックの単体テストが行いやすくなり、再利用性も向上します。

コントローラは、ビューとモデルの間で情報を受け渡しするための場でしかありません。

これはあくまで新人Rails開発者向けの一般的なアドバイスであり、特に巨大なアプリでは実際には推奨されていません。

訳注: 巨大なアプリでは、モデルやコントローラ以外のロジックの置き場所を別途定めるのが普通です。

Q18:「薄いコントローラ、薄いモデル」の意味を説明してください

コードベースが成長するに連れて、ファットモデルが手に負えなくなり、モデルの責務が過剰になって管理不能に陥ってしまいます。モデルは永続化に専念し、モデル内のロジックを肥大化させないようにすべきです。

「単一責任の原則」を常に意識し、ロジックをモデルから他のデザインパターン(Service Objectなど)に追い出すことで、モデルをもっと薄くできます。

Q19: クラスメソッドとインスタンスメソッドの違いを説明してください

クラスメソッドはクラス上で利用でき、インスタンスメソッドはインスタンス上で利用できます(当たり前ですが)。両者の利用目的は異なるのが普通です。

Articleというクラスで考えましょう。インスタンスメソッドは、特定の記事1件の本文に含まれるワード数をカウントするのに利用できます。クラスメソッドは、すべての記事のうち特定の著者が書いた記事の件数をカウントするのに利用できます(スコープが違うことにお気づきでしょうか?)。

クラスメソッドはdef self.メソッド名のように定義します。

Q20: POROについて説明してください

POROは「Plain Old Ruby Object」の略です。

Rubyではほぼあらゆるものがオブジェクトですが、Active Recordでは複雑なオブジェクトが多数使われがちです。一般にPOROという用語は、ビジネスロジックをサポートするシンプルな小さいオブジェクトを強調するのに使われます。

Q21: Rubyで多重継承は使えますか?

Rubyでは複数の親クラスからの多重継承は許されていません。その代わり、includeextendによるモジュールのミックスインが利用できます。

Q22: Rubyは「強い型付け」「弱い型付け」のどちらですか?

Rubyは「強い型付け」の言語です。"hello" + 3を計算するとエラーになります。

対照的にJavaScriptは「弱い型付け」言語であり、"hello" + 3の結果は"hello3"になります。

Q23: バックグラウンドジョブにどんなフレームワークを使ったことがありますか?

Delayed::Job
使いやすく、セットアップも簡単です。キューは1つのデータベーステーブルに保存されます。Delayed::Jobとproductionで同じデータベースが使われている場合、ジョブが増えすぎるとデータベースがボトルネックになる可能性があります。
Sidekiq
Redisを用いてジョブをキューイングします。Radisはインメモリデータストアなので高速です。Sidekiqを使うにはRedisの追加が必要なので、インフラがその分複雑になります。
Sucker Punch
Rubyの1つのプロセスとして実行され、すべてのジョブをメモリ上に配置します。プロセスがクラッシュするとジョブが失われるので、クリティカルなタスクには不向きです。

Q24: Rubyのクラスでコンストラクタを宣言する方法を説明してください

コンストラクタはinitializeメソッドで定義します。このメソッドはクラスの新しいインスタンスが初期化されたときに呼び出されます。initializeメソッドの定義は必須ではなく、新しいインスタンスに属性値を提供するときによく利用されます。

Q25: ヘルパーにはどのようなロジックを置きますか?

ヘルパーのロジックは、ビューだけをサポートすべきです。

ヘルパーの候補としては、複数の異なるビューで必要になる日付フォーマットロジックがよい例です。

Q26: Active Recordについて説明してください

Active Recordは、モデルとデータベースを対応付けるORM(Object-Relational Mapping)です。Active Recordを用いることで、オブジェクトの読み込みや保存や削除を直接SQLで記述する必要がなくなり、アプリのセットアップがシンプルになります。

Active Recordは、ある程度のSQLインジェクションの保護機能も提供しています。

Q27: Rubyのselfはどんなときに使うかを説明してください

  • クラスメソッドの定義や呼び出しではselfを使う
  • クラス内ではselfを用いて現在のクラスを参照する、つまり、あるクラスメソッドから(訳注: 同じクラス内の)別のクラスメソッドを呼び出すときに必須となる
  • インスタンスからクラスメソッドを呼び出す場合はself.class.methodという呼び出し方法が必須となる

Q28: Rackについて説明してください

Rackは、WebサーバーとRailsの間に位置するAPIです。Rackではプラグインを利用できるほか、フレームワークを差し替えたり(RailsをSinatraに差し替えるなど)、Webサーバーを差し替えたり(UnicornPumaに置き換えるなど)できます。

Q29: MVCについて説明してください

MVC(Model-View-Controller)はRailsを構築するソフトウェアデザインパターンです。MVCでは情報の扱いを以下の3つに分割しています。

モデルはデータとロジックを管理します。ビューは情報を表示します。コントローラは入力を受け取り、モデルやビューに渡すデータを準備します。

Q30: Rubyのブロックについて説明してください

Rubyのブロックは、コードを中かっこ{ }または「doend」で囲んだものです。eachを呼び出すときにはブロックをひとつ渡します。

ブロックには独自のスコープがあり、ブロックの外からアクセスできない独自の変数を1つまたは複数定義できます。ただしブロックの外で定義された変数はブロック内でも変更可能です。

{|x| puts x} # ブロックの例

Q31: procとlambdaの違いを説明してください

procもlambdaも、ブロックを保存したものであるという点では同じですが、構文や振る舞いがわずかに異なります。

lambdaの中に書いたreturnはlambda自身から抜けますが、procの中に書いたreturnはそのprocを含むメソッドから抜けます。

上のmethod_proc1が返る点にご注目ください。これは、procを呼び出すとその時点でmethod_procメソッド内での実行が終了するためです。

訳注: procから値を返すにはnextを使います。
参考: 手続きオブジェクトの挙動の詳細 (Ruby 2.7.0 リファレンスマニュアル)

Q32: Rubyのyieldについて説明してください

yieldは、メソッドに渡されたブロックにアクセスします。Railsアプリケーションのレイアウトファイルで使われるyieldが典型的です。

上のコードではyieldを呼び出したときに「its me」が出力されます。

Q33: content_forの使いみちを説明してください

content_forは、ビュー内でコンテンツを定義したりレンダリングしたりできます。これは、コンテンツを1箇所で定義してさまざまな場所でレンダリングするのに便利です。

Q34: HashJSONの違いを説明してください

HashはRubyのクラスであり、キーを指定して値にアクセスできるキーバリューペアのコレクションです。

JSONは、データ送信に用いる特定のフォーマットを持つ文字列です。

Q35: Active Jobについて説明してください

Active Jobは、バックグラウンドジョブを作成して、Delayed::JobやSidekiqといったさまざまなバックエンドにキューイングします。

Active Jobの典型的な利用法は、メインのWebスレッドで実行する必要のないコードの実行です。ユーザーにメール通知を送信するときによく使われます。

Q36: Railsのどういうところが好きですか?

私の場合は、さまざまなWebフレームワークを使ってきましたが、RailsほどMVPを短期間で構築できるものは他にありませんでした。他にも以下の点が特に気に入っています。

  • 開発が楽しい。Time.now + 5.daysobj.nil?といった書き方ができるのは幸せ。
  • コミュニティの存在がとても助けになり、サンプルやドキュメントが簡単に見つかる。
  • 「設定より規約」とは、新しい巨大なコードベースに触れたときにどこを見ればいいかがわかるということ。Djangoのような設定を好む他のフレームワークと比較したときに、特にそれを感じる。

Q37: Railsのどういうところがキライですか?

私の場合は、機械学習系のライブラリ開発が乏しかったり存在しなかったりする点。

Q38: ご贔屓のRuby gemを教えて下さい

Rails開発者なら誰もがご存知のDeviseが好きです。認証のような複雑なものを2分でセットアップできるからです。

Q39: springについて説明してください

springはアプリケーションプリローダーであり、アプリケーションをバックグラウンドで実行し続けることでマイグレーションやrakeタスクを実行するときにアプリケーションの起動が不要になります。

Q40: アセットパイプラインについて説明してください

ブラウザで使われるJavaScriptやCSSを用意するフレームワークです。

Q41: Railsで認証を管理するときには何を使っていましたか

Deviseです。

Q42: splat演算子について説明してください

splat演算子は、メソッドに渡される引数の数を事前に決めておきたくない場合に使われます。Rubyにはsplat演算子*とdouble splat演算子**の2種類があります。

splat演算子*は想像どおり以下のように動作します。

double splat演算子**は通常のsplat演算子*と似ていますが、キーバリューを引数に取る点が異なります。

Q43: includeextendの違いを説明してください

includeextendはどちらもミックスインであり、別のモジュールのコードを注入するのに使われます。

ただしincludeではそのコードにインスタンスメソッドとしてアクセスできますが、extendではそのコードにクラスメソッドとしてアクセスできる点が異なります。

Q44: loadrequireの違いを説明してください

load
別のファイルを読み込む(既にメモリ上に読み込まれている場合でも実行する)
require
別のファイルを1度だけ実行する(何度requireしても同じ)

Q45: クラスとモジュールの違いを説明してください

  • クラスには属性とメソッドが1つ以上ある。クラスはインスタンスを作成できる。
  • モジュールは単なるメソッドと定数のコレクションであり、他のモジュールやクラスにミックスインできる。

Q46: Active Recordのスコープについて説明してください

Active Recordのスコープは、Active Recordモデル内で定義され、他のどこからでも呼び出せるクエリロジックです。

アプリ内のさまざまな場所で同じロジックを複製するよりも、スコープを定義する方が便利なことがあります。

# スコープの例
class Post
  scope :active_posts, -> { where(active:true) }
end

Q47: クラス変数とインスタンス変数の違いを説明してください

インスタンス変数は@で表記され、クラスのインスタンスに関連付けられます。あるインスタンスで属性の値を変更しても、他のインスタンスにある同じインスタンス変数には影響しません。

クラス変数は@@で表記されますが、インスタンス変数よりも直感に反します。クラス変数は、クラスのあらゆるインスタンスで共有されるので、あるインスタンスでクラス変数を変更すると、すべてのインスタンスのクラス変数に影響します。

上のコード例で、クラスのどのインスタンスからでもクラス変数を変更できることがわかります。

Q48: Active Recordのfindfind_bywhereの違いを説明してください

find
引数を1つ取り、その引数とマッチする主キーを持つレコードを探索します。
find_by
キーと値を引数に取り、マッチする最初のレコードを返します。
where
キーと値を引数に取り、マッチするレコードのコレクションを返します。マッチしない場合は空のコレクションを返します。

Q49: selectmapcollectの違いを説明してください

3つともブロックをひとつ引数として受け取ります。

select
コレクションのサブセットを取得するのに使います。!付きのselect!を呼ぶと、元のコレクションが改変されます。
i = [1,2,3,4,5]
i.select {|x| x % 2 == 0}
# => [2, 4]
map
コレクションの各要素に対して操作を実行し、更新されたコレクションを出力します。!付きのmap!を呼ぶと、本のコレクションが改変されます。
i = [1,2,3,4,5]
i.map {|x| x+1}
# => [2,3,4,5,6]
collect
mapのエイリアスなので動作は同じです。

Q50: RailsのCRUD verbと、それに対応するアクションを述べてください

verb アクション
GET index
GET new
POST create
GET show
GET edit
PATCH/PUT update
DELETE destroy

Q51: createアクションへのルーティングを、resourcesを使わないで定義してください

resourcesありの場合
resources :photos
resourcesなしの場合
post '/photos', to: 'photos#create', as: :create_photo

Q52: Rubyの3段階のアクセス制御を述べてください

public
このメソッドは任意のオブジェクトから呼び出せる
protected
このメソッドは、そのメソッドが定義されているクラスと、そのクラスのサブクラスからしか呼び出せない
private
このメソッドは、そのオブジェクト自身しか呼び出せない

Q53: Rubyのシングルトンの使いみちを説明してください

シングルトンは、クラスにインスタンスを1つしか持たせないデザインパターンです。シングルトンはRuby界隈では嫌われがちですが、Rubyにはシングルトン用のモジュールも付属しています。

参考: module Singleton (Ruby 2.7.0 リファレンスマニュアル)

まとめ

面接を受ける方は、本記事をRuby on Railsに関する一般的な自分の知識を評価するのにお使いください。本記事がすべてを網羅しているわけではありませんが、ジュニア開発者や中堅開発者が受ける質問の大半をカバーしているはずです。

面接官の方は、本記事から質問を見繕うことから始めるとよいでしょう。

Zack Shapiroに感謝いたします。


Ruby 3.0のキーワード引数変更のスケジュールが変更に

$
0
0

こんにちは、hachi8833です。

昨日BPS社内で「週刊Railsウォッチ」のつっつき会をZoom開催する2時間ほど前に、@_ko1さんの以下のツイートを目にしました。

その後Matz自身もツイートしているのを見つけました。

今回取り急ぎ記事にしましたが、もちろん具体的な方法やスケジュールはこれからですし、今後状況が変わる可能性もあります。キーワード引数の仕様変更はいずれは必要なものだと自分も思います。

誤りや見落としなどありましたら@hachi8833までお知らせください。

参考: Separation of positional and keyword arguments in Ruby 3.0 — 昨年12月時点のキーワード引数変更に関する一次ドキュメントです
参考: キーワード引数の現状と将来構想 - HackMD — キーワード引数の仕様変更前のアジェンダです

discuss.rubyonrails.orgへのMatzの投稿

投稿の大意

Matzです。
Railsコア開発者数人(DHH自身も含む)から、最近のキーワード引数の仕様変更がつらいという連絡をいただきました。キーワード引数の移行に伴う痛みの見積もりが不足していたと自分でも思います。熟考の末、移行スケジュールを変更/延期することを決心しました。Ruby 3.0に「本物のキーワード引数」が入らなくなることについて大変申し訳無く思います。
今後取り得る選択肢があまりに多すぎるのが問題です。私たちが正しい決定を下すために、キーワード引数移行のどこがどのぐらいつらいかについて(Rails)コミュニティの皆さまからご意見をいただく必要があります。

Ruby 2.7

  • 現在のRuby 2.7ではキーワード移行に関するwarningを表示しているが、これがノイズになっていることを認識している。
  • おそらくだが、近々リリース予定の2.7.2ではキーワード引数がらみのwarningを消すか削減する予定。
  • 2.7.2におけるruby2_keywordメソッドの扱いについても決定が必要。

Ruby 3.0

  • 既にmasterブランチでは、キーワード引数を通常の(位置)引数から分離している。おそらくこれはユーザーにとってつらくなる可能性がある。
  • 移行を延期(または取りやめ)にすることについて私たちの方はOK。
  • しかしRuby 3.0でどこまで進めるかについては私たちの方で決定する必要がある。
  • 2.7の(一種生焼けの)ソリューションをRuby 3でも維持する方法も選択肢のひとつとしてありうる。
  • 他にも(もう少しうまい)妥協案を出せるかもしれない。

そういったわけで、キーワード引数に関連する懸念やご意見、あるいはRuby 2.7に移行するうえでつらかった部分についてこちらにコメントいただければと思います。
Matz.
同投稿より大意

苦渋の決断を下した心中、お察しいたします。

なお、議論については以下のbugs.ruby-lang.orgでお願いしますとのことです(#5)。今チェックしたら、以下のissueが立てられていましたので後で読んでみます。

反応

同投稿への現時点のコメントから目についたものをいくつか抜き出してみました。

  • @mame#27のコメントに特定のケースでとても有用な方法を書いてくれている。これは他のライブラリ作者にとっても有用そう。大感謝!(#2)。
  • frozen string literalと同じようにファイルごとに# keyword_arguments: trueのようなフラグを指定できるようにしてはどうか?(#3)– 賛同多し
  • RSpecメンテナーの主なつらみは自分らのライブラリでユーザーからの引数を透過的にユーザーのコードに引き渡している点(#4)。
  • キーワード引数の移行の段階をもっと細かくすべきだと思う(#6)。
  • 委譲がブラインド状態になるのがつらい。新しい...が有用な場合もあるがpublic_sendsendで委譲した場合は使えないなどのいろんな制約がある。この制約がなくなればかなり助かると思うが、それもアプリケーションコードの場合に限る。gemでは古いRubyをサポートすることも期待されるので使えない(#7)。
  • 今回の決定について心から賛同する。今使えるツールや移行ガイドのような情報が欲しい(#10)。
  • キーワード引数でシンボル以外のキーを許すのはやって欲しくなかった(#13)。
  • 以下のツイートスレッドでいくつかのケースでやれることが見つかった。自分たちはこれでだいぶ痛みを軽減できた(#8)。

  • 手順
    • 1. Double splat with an empty hash ( **{} ) passes no arguments絡みのエラーをまず修正する。
    • 2. このGistでwarningを抑制し、production環境やdevelopment環境でログがあふれないようにする。
    • 3. Shopifyのdeprecation_toolkitで、既存のwaringを表示したり、新たなwarningの追加を回避できる。このGistでgemのwarningではなく自分たちのコードベースのwarningのみを扱える。
    • 4. すべて修正する

キーワード引数の変更そのものについては全般に同意されているようです。


以下はツイートで目にした反応です。

関連記事

Ruby 2.7: ハッシュからキーワード引数への自動変換が非推奨に(翻訳)

週刊Railsウォッチ(20200518前編)スライド『令和時代のRails運用』、Ruby 3.0のキーワード引数変更リスケ、Action CableのCLIほか

$
0
0

こんにちは、hachi8833です。Rails 4.2.11.3セキュリティリリースが出ました(Rails公式ニュースより)。

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

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

以下の更新情報とコミットリストから見繕いました。

なお「A May of WTFs」というカテゴリがDiscourseのRails SNSに追加されたそうです。


つっつきボイス:「A May of WTFsって新しいポエムの書き場所か何か?😆」「Railsのissueに書きにくいようなことを書ける場を提供するということだそうです: 何でWTF(What the Fxxk)なのかわかりませんけど😆」「これとか今週の更新情報がよく出るようになったあたり、情報発信とかフィードバックをもっと頑張ろうというRails公式の意思を感じますね💪」「そうかも」「前は今週の更新情報も出たり出なかったりでしたし😆」「そのあたりに手が回るメンバーが増えたかやる気が出たかのかなと☺」「公式情報発信の頻度が上がるのはいいですね👍

そして@kamipoさんがついに1000プルリク突破です🎉

⚓minimummaximumでタイムゾーンが取れない問題を修正

# activerecord/lib/active_record/relation/calculations.rb#L309
        type_cast_calculated_value(result.cast_values.first, operation) do |value|
-         if value.is_a?(String) &&
-             column = klass.columns_hash[column_name.to_s]
-           type = connection.lookup_cast_type_from_column(column)
+         if type = klass.attribute_types[column_name.to_s]
            type.deserialize(value)
          else
            value
          end
        end

つっつきボイス:「groupした日時とかをminimumで取ってもTimeWithZoneクラスになってなかったとかそういう感じかな↓」「今までは…Timeクラスだった😆」「そうそう😆

# activerecord/test/cases/calculations_test.rb#L1075
+ def assert_minimum_and_maximum_on_time_attributes(time_class)
+   actual = Topic.minimum(:written_on)
+   assert_equal Time.utc(2003, 7, 16, 14, 28, 11, 223300), actual
+   assert_instance_of time_class, actual
+
+   actual = Topic.maximum(:written_on)
+   assert_equal Time.utc(2013, 7, 13, 11, 11, 0, 9900), actual
+   assert_instance_of time_class, actual
+
+   expected = {
+     false => Time.utc(2003, 7, 16, 14, 28, 11, 223300),
+     true => Time.utc(2004, 7, 15, 14, 28, 0, 9900),
+   }
+   actual = Topic.group(:approved).minimum(:written_on)
+   assert_equal expected, actual
+   assert_instance_of time_class, actual[true]
+   assert_instance_of time_class, actual[true]
+
+   expected = {
+     false => Time.utc(2003, 7, 16, 14, 28, 11, 223300),
+     true => Time.utc(2013, 7, 13, 11, 11, 0, 9900),
+   }
+   actual = Topic.group(:approved).maximum(:written_on)
+   assert_equal expected, actual
+   assert_instance_of time_class, actual[true]
+   assert_instance_of time_class, actual[true]
+ end

「このissueは実はBPSのbabaさんが上げてて↑、@kamipoさんが半日経たずに修正したものでした」「babaさんこれ踏んだのか😆」「昨日社内Slackに『変なバグ踏んだ』ってこれが上がってました😆」「自分もあんまり使わないメソッドだから踏んだ人少ないかも☺」「Active Recordにminimumとかmaximumみたいなメソッドがあるとは😆


これは#39039の逆方向になる。
#39111ではdateカラムのminimummaximumをデータベースのカラム型で型キャストすることで修正したが、カラム型にはタイムゾーンを認識している属性がないので、属性の型はカラム型より常に優先されるべき。これに関連する#39248でそのことに気づいた。
タイムゾーンコンシャスな属性が動くようにするため、#39039で期待される部分をrevertした。
同PRより大意

先行していた以下の#39039もつっつきで取り上げたのですが、上述のとおり取り消されています。

「Active Recordってやっぱ難しいよ🤣」「🤣」「だってRDBに型があってRubyの世界にも型があって、それを実行時に解決しようとしてるんだから、そりゃ難しい😆

⚓新機能: テストファイルのパターンを環境変数で設定可能に

テストファイルのパターンを環境変数で設定可能にする
次の2つの環境変数でテストファイルのパターンを設定できる: DEFAULT_TESTはテストするファイルの設定、DEFAULT_TEST_EXCLUDEはテストから除外するファイルの設定。
従来これらの値はハードコードされていたので、デフォルトで除外してはいけないテストカテゴリ(スモークテストなど)を追加できなかった。
同PRより大意


つっつきボイス:「テストのデフォルト対象ファイルを環境変数で指定できるようになってる」「通常は実行したくないテストを除外したりとか」「今までだとRSpecでタグを使って解決していた話なのかも?🤔」「それっぽいですね」「実行がめちゃめちゃ遅いテストってありますよね😆

⚓type_castにカラムを渡すことが非推奨化

# activerecord/lib/active_record/connection_adapters/abstract/quoting.rb#L20
      def type_cast(value, column = nil)
        value = id_value_for_database(value) if value.is_a?(Base)

        if column
-         value = type_cast_from_column(column, value)
+         ActiveSupport::Deprecation.warn(<<~MSG.squish)
+           Passing a column to `type_cast` is deprecated and will be removed in Rails 6.2.
+         MSG
+         type = lookup_cast_type_from_column(column)
+         value = type.serialize(value)
        end

        _type_cast(value)
      rescue TypeError
        to_type = column ? " to #{column.type}" : ""
        raise TypeError, "can't cast #{value.class}#{to_type}"
      end


つっつきボイス:「今までそんなことができてたとは😳」「ああなるほど、SQLiteのデータは内部的に全部stringになってるので、type_castはそういうキャストをサポートするためのメソッドなのか」「あ〜そういえばそんな話ありましたね😳

値をデータベースが理解可能な型にキャストする。たとえばSQLiteはdateを理解できないので、このメソッドでDateをStringに変換する。
type_cast APIドキュメント

「そういえば今週はSQLite絡みの改修がちらほらあった気がします」「プルリクメッセージからリンクされてるAPIを読むと、type_castを呼ぼうとしていたら既に何かがおかしいと書いてある😆」「まったくそのとおり😆」「そういう間違いが起きないように非推奨化すると」「データベースには型があるんだからそれを使いましょうと」

型キャストに使う型情報は型オブジェクトとは完全に別物なので、Rails 6でカラムをtype_castに渡すとおかしなことになりやすい。詳しくは以下のコメント参照:
rails/quoting.rb at 28d815b89487ce4001a3f6f0ab684e6f9c017ed0 · rails/rails
これはレガシーなバインド(4.2形式の[column, value]という配列)をコネクションのクエリメソッドに渡すことも非推奨化する。このようなレガシーフォーマットは後方互換性のために維持されていたが、自分はキャスト済みバインド形式(キャスト済みの値の配列)をサポートしているので、既存のバインド形式2種類を構築するよりはこのバインド形式を構築する方がやりやすい。
同PRより大意

⚓UrlGenerationエラーで”did you mean”をサポート


つっつきボイス:「UrlGenerationって何だっけ、あルーティングか↓」「これをdid you meanしてくれるようになったの嬉しい😂」「これはたしかにありがたい🙏」「いい👍


同PRより

「ほら、こういうルーティングって『もしかしたらこの書き方でイケるかも?』ってエイヤで書くことあるじゃないですか😆」「やるやるやります😆」「やってみて『この書き方やっぱダメか〜ゴメンナサ〜イ』って😆」「URLヘルパーに『こんなハッシュを渡したらうまくやってくれるかな?』って渡してみると案外拾ってくれたりすることありますし😆

「Railsのキャリアが浅い人にはこういう機能は特にありがたいでしょうね: 逆にRailsでさんざんつらい目に遭った人だと『やっぱダメだよね😇』って諦めがち🤣」「『きっとできないと思ってたし😎』とか🤣」「こうやってRailsの機能がいろいろ至れり尽くせりになるのってスゴいことだなって思います😋

「これはさっきのA May Of WTFsの以下のエントリをきっかけに改修したそうです↓」「Railsのリポジトリもissueが大量に増えまくってますし、ふわっとしたissueって立てづらくなってるので、そういうのを投げられるところができるというのはいいですね👍

参考: Provide a hint when unable to pluralize a route? - A May Of WTFs - Ruby on Rails Discussions


今見ると、その後で「初心者向けにインスタンス変数のタイポもdid you meanして欲しい」というリクエストとプルリクも上がってますね。

⚓Rails

⚓Ruby 2.7〜3.0のキーワード引数変更がリスケに

こちらについては一足先に別記事を出しました。

Ruby 3.0のキーワード引数変更のスケジュールが変更に


つっつきボイス:「つい2時間ぐらい前に出たスクープです」「あ、これマジで?!😳」「しかもMatzのエントリ自体もさっきのA May of WTFsに投稿されていました」「へぇ〜、MatzがRailsのところに書き込むのは珍しいかも😳」「自分も初めて見たかも😳

「DHHを含むRailsコミッターから陳情が出るとは😳」「それだけキーワード引数変更の対応がつらかったんでしょうね」「それもさることながら、Rubyをアップグレードしない人たちが出てくることの方を懸念したのかもしれませんね🧐」「あ〜たしかに」「自分らも何かあったらコワイのでまだRuby 2.7にアップグレードしてませんし😆

「ところで今新しくRailsアプリ作るときはどうしてます?」「Rails側の対応もだいぶ進んできてはいるので、新しくやるならRuby 2.7でもいいんじゃない?って最近は思ってます😋」「なるほど!」

「来たる2.7.2ではキーワード引数のwarningをなくすか減らすかもしれないそうです」「キーワード引数の変更によってgemで何かが壊れること自体はそうそうないんじゃないかなと思うんですけど、自分たちにとってはどちらかというとキーワード引数のdeprecation warningが大量に出ることの方が問題なんですよね😆」「それです😆」「あのwarningが出ないのであれば2.7に上げてもいいんじゃないかって思いますし☺

「自分らとしてはあのキーワード引数のdeprecation warningだけを黙らせたいんですよ: 他のdeprecation warningはむしろ黙らせたくない」「それはたしかに」「今出回ってる解決方法のひとつにdeprecation warning自体を止めるというのがありますけど、やりたいのはそれじゃないんだよぉぉ😆」「必要なwarningは鳴ってくれないと困る〜😆」「というような話が巡り巡って今回の結論に至ったのかなと思いました☺」「上の投稿では、どの辺がつらいかをコメントで教えて欲しいとのことでした」

「コメント見るととりあえずRSpecの人たちが困ってるようです」「それは間違いない😆」「😆」「RuboCopとかもそうですけど、ASTを追いかけている人たちはいろいろつらいでしょうね😭


なお今後のキーワード引数変更の進め方の議論は以下のissueで進められるそうです。

⚓スライド『令和時代のRails運用』


つっつきボイス:「社内Slackにあげていただいたスライドです」「そうそう@joker1007さんのスライド、これどこで発表したんでしょうね?🤔」「明日(5/15)の銀座Rails…じゃないか😆

「古代、中世、近代、現代という区分😆」「自分とこって中世なの〜?😆」「反論出そう😆」「まあ中世であることがいけないわけではなくて、どれであっても運用をヘルシーに保つのは大変だよねと言いたいんじゃないかと思いますし🧐」「だと思いますけど😆」「インフラエンジニアを潤沢に投入できる案件ならいいんですけど潤沢じゃない方が普通なので、ヘルシーな運用管理はいろいろ大変😅」「ですよね😅」「この運用でしかやってはいけないというレベルまで運用ワークフローをがっちり固められれば古代や中世でも別にいいと思いますし、想像ですけどNetFlixとかAWSなんかは内部でそういう感じでやってるんじゃないかなって思いますし☺」「なるほど!」

「スライドはいろいろ納得ですね😋: 結局一番厄介なのはスライドにもあるように『秘匿情報をどう管理するか』になりますし」「ですね」「やっぱりKMSが必要ということでしょうか?」「いえ、KMSに拘る必要はありませんね: 大事なのはKMSには暗号化設定そのものは入れてないというところで、KMSには鍵だけを入れて、暗号化した設定情報はファイルの方に存在します🧐」「なるほど😅」「ともあれだいたいこういう形になるでしょうね: secretsは設定ファイルなり環境変数なりに入れてプロセスからは自由に読める形にするしかないよね、ということをスライドで納得できたのが自分としてはよかったと思います😋」「ふぅむ」

「スライドのロギングドライバの話も数年前から割とこんな感じで行われてますね↓」

「デプロイの話ではCapistranoのプラグインを自作してる↓: インターフェースはCapistranoだけどやってることはCapistoranoのビルトイン機能とだいぶ違うものになるわけですけど、それも含めてだいたいこういう形になりますよね😆

「コンテナに全部を乗っけるとコンソールを取るにもコンテナ立ち上げが必要になる、という話もありますね」

「知見のある人が年に1度ぐらいこういう話をしてくれるのはありがたい🙏: 変わっていない知見があっても、それが変わっていないことがわかるのが助かります😋」「そうですね😋」「そして変わった部分を知ってショックを受ける人たちもいたりするという😆


あとでわかりましたが、@joker1007さんが発表したスライドは以下の「シューマイ」というイベントでした(また見逃してた…😭)。他にもいろいろ発表されていました。

⚓rodauth 2.0がリリース(Ruby Weeklyより)


つっつきボイス:「rodauthはy-yagiさんの好きなRodaというフレームワークを使った、Deviseのオルタナ的な認証gemです」「速いという噂のヤツですね」「設計もキレイみたいです❤

Ruby: 認証gem ‘Rodauth’ README(翻訳)

「以前のroda-railsがRails 4.2までしか使えなかったんですけど、rodauth 2.0はRails 6でも動くようになっているみたいです」「へぇ、rodauthはJWTやWebAuthとかもサポートしてるのか、なかなか頑張ってる感: 登録画面みたいなものがあまり要らなくて純粋に認証+JWT基盤があればいいような案件でうまくはまるかも😍」「おぉ」「Deviseだと登録画面とかにもいろいろ絡んでくるから複雑になりやすいんですよね😢」「たしかに」

「Deviseのメリットは、登録画面やログイン画面やパスワード再設定画面みたいな認証関連の画面を一から作るのが面倒なときにdevise.ja.ymlを引っ張ってくれば圧倒的な短期間で作れるということでしょうね😆」「そうそう😆」「だからこそそういう部分が足を引っ張ったりもするんですけど😆

「この間Deviseでちょっとテスト書こうと思ったら書きづらいのなんの😅」「Deviseのテストはもう地獄👿」「ほんと地獄でした😭、ログイン認証のテストとかどう書けばいいの?って」「その辺はDeviseのHow-To Wiki↓にバッドノウハウの塊みたいなのがひととおり載ってますけど🤣」「ははは😅」「ともあれ、本来の認証gemとしてこのrodauthみたいなものが欲しいというのはありますね☺

[Rails] Devise Wiki日本語もくじ1「ワークフローのカスタマイズ」(概要・用途付き)


changelogより抜粋:

  • Ruby 1.8のサポート終了
  • 2要素認証の追加
  • WebAuthnの追加
  • フィールドのオートコンプリート属性サポートを追加
  • ルーティングを呼ぶ前にデフォルトでCSRFトークンを自動チェック
  • jwt_refreshactive_sessionsでリフレッシュ後に直前のJWTアクセストークンの利用を防止
  • その他多数

⚓render_async 2.1.6がリリース(RubyFlowより)


つっつきボイス:「render_asyncもバージョンアップしたそうです」「render_asyncにバージョンアップの必要なところってそんなにあるのかしら?😆」「Turbolinks周りとかネステッドパーシャルの対応が改善されたりしてるのね」「Turbolinksなつかしい😆」「なつかしいですよね😆」「今もナチュラルに入ってるんでしょうか?」「入ってると思いますよ☺」「なくなったという話はなさそう」「最近のTurbolinksはそんなに悪くないという話も聞きますけど、Turbolinksが入ってることを意識したくないというのはありますね😆」「それはそう😆

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

「ちなみにrender_asyncでパーシャルの読み込みコードを書くと、従来のようにRails側でパーシャルをレンダリングしてビューに埋め込む代わりに、JavaScriptのAjaxコードを使って返してくれるようになります」「おぉ」「まあrender_asyncは非同期読み込みを頑張らずにやるという目的に非常に合致してると思いますし😆」「いいヤツなんですね😋」「いえ、どちらかというと付け焼き刃的に使えるツール🤣」「そっちですか🤣」「やりすぎると後々収拾がつかなくなったりしますし😇」「まさに付け焼き刃なツール😆


同記事より:

  • Turbolinksを用いたページナビゲーション時にポーリングを停止するようになった
  • X-Requested-Withヘッダーがデフォルトで設定されるようになった
  • render_asyncによる読み込みのトグルでイベントの委譲ができるようになった
  • ネストしたパーシャルをTurbolinksで読み込めるようになった
  • 最後に

⚓acli: Action CableのCLI

# 同リポジトリより
# Subscribe to channel (without parameters)
\s channel_name

# Subscribe to channel with params

\s+ channel_name id:1

# or interactively

\s+
Enter channel ID:
...
# Generate params object by providing keys and values one by one
Enter key (or press ENTER to finish):
...
Enter value:
# After successful subscription you receive a message
Subscribed to channel_name


# Performing actions
\p speak message:Hello!

# or interactively (the same way as \s+)
\p+

つっつきボイス:「Evil Martiansの人が作ったgemだそうです」「あ〜、対話型でAction Cableやれるのね」「俺たちが欲しかったのはもしかするとこれかもしれないというのはある😆」「おぉ😍

「たしかにAction Cableで作っているときにRailsコンソールに相当するものがクライアント側に欲しいときってありますし😋」「これmrubyで書かれてるみたいですね😳」「これは嬉しいヤツでしょうか?」「まあAction Cableをちょっと試すのにいちいちWebSocketのJSコードを書くのがとにかく面倒😭」「みんなJavaScriptがキライ説😆」「JavaScript脳に切り替えるのが面倒だったりして😆」「あ、そっちか😆」「Action CableのコードをRubyで書いているのにデバッグコードをJSで書くのは面倒でしょう😆

⚓Rails 6にWebpackerとSprocketsが両方入ってる理由

「WebpackerとSprocketsが両方入ってる理由😆」「『DHHがそう言ったから』だそうです」「移行パスを考えたらいきなりSprockets消せないでしょう普通😆」「実際Sprocketsでないとどうしてもできない部分はWebpackerでは実装し直しが必要になりますよね🧐」「あ、そういうことか😆」「アセットプリコンパイルのタイミングでRubyのコンテキストを参照するようなコードはそのままだとWebpackerで処理できませんし、マニフェストファイルを書き出してそれを読み込ませたりするのは結構大掛かりな修正が必要になると思うんですよ☺」「う〜む😅

「そもそもRailsにWebpackerが入ることになったのってなぜなんでしょう?」「流行りだから😆」「流行りだからって入れていいんでしょうか😆」「まあWebpackそのものじゃなくてWebpackerという形で入ったのはどうかと思いますけど、エンジニアが全員Railsの人ならWebpackerはいい選択肢だと思いますね🧐」「そうかも」「だってWebpackerはRailsでしか使わないから😆

「Webpackerが流行ってるのはわかるんですけど個人的にはいろいろ疑問が😅」「Railsのビューでjavascript_pack_tagみたいなものを書けるようにしたかったんでしょうね: いずれにしろWebpackとRailsをつなぐレイヤは必要だったでしょうし☺」「でWebpackerがどこまでWebpackのマウントを取るかという部分で不評を買っているというか😆」「😆」「WebpackerがもっとピュアなWebpackを触るだけみたいなつくりになってたら違ったかもしれませんけど、WebpackerでWebpackのコンフィグを制御する方向に行ったのがちょっと残念ではありますね😢


同記事より:

  • 両方入っているのを不思議に思うのはあなただけではない
  • 両方入っている理由
  • 両方あるのはどうかと思いませんか?
  • WebpackerまたはSprockets(または両方)を使う理由があるとすれば
  • 自分はSprocketsをやめるけど、皆さんはどうですか?

⚓その他Rails


同サイトより


つっつきボイス:「Community Survey 2020というアンケートを募集していて、とりあえず自分は入れてみました😋」「もう10年も継続してるんですって」「2年おきにやってる?」「でも3年空いてるところもあるし😆」「ノリでやってる感😆」「そこそこ項目多いな〜」「後で気が向いたら入れてみよっと😆」「自分も気が向いたら😆


「こちらは新人向けの記事です」「jnchitoさんはこういう記事をマメに書いてくれるのがいいですね❤」「なるべく早く反応しようとか期限を切ろうとか」「こういう指示をいちいち出さないといけない人と仕事をしているんだろうなという気持ちになります😆」「つらいことがあったのかな😆」「つらいと思いますよ実際😭」「個別に指示していられなくなって記事書いてここ読んでくれという感じでできたのかなと☺」「まあ新入社員にはこういうことをひととおり伝えないといけませんよね☺」「たしかに」「Railsエンジニアに限らず広く一般に通用する話ですし👍」「プログラマーにも限りませんし😋」「社会人として働くうえで必要👍


前編は以上です。

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

週刊Railsウォッチ(20200512後編)RubyのPStoreライブラリ、Lambda StoreのサーバーレスRedisは有能、Amazon Linux 2のライブパッチほか

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

週刊Railsウォッチ(20200519後編)Rails 5と6のセキュリティ修正、Ruby 3.0のGuildがRactorに名前変更、Node作者によるDeno登場ほか

$
0
0

こんにちは、hachi8833です。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

⚓臨時ニュース

⚓Railsのセキュリティ修正5.2.4.3と6.0.3.1がリリース(Rails公式ニュースより)

セキュリティ修正5.2.4.3と6.0.3.1がリリースされました。どちらも5件の脆弱性が修正されています。

念のため、昨日のウォッチに貼った4.2.11.3セキュリティ修正をもう一度貼っておきます。こちらで修正された脆弱性は1件です。

⚓Ruby

⚓Ruby 3.0に入るGuildが「Ractor」に名前変更

# 同リポジトリより
RN = 10000
CR = Ractor.current

last_r = r = Ractor.new do
  p Ractor.recv
  CR << :fin
end

RN.times{
  r = Ractor.new r do |next_r|
    next_r << Ractor.recv
  end
}

p :setup_ok
r << 1
p Ractor.recv

つっつきボイス:「こちらも準スクープという感じですが、Rubyに入る予定のGuildが名前変わったそうです」「お〜今度はRactorか😳」「RubyのアクターだからRactorらしいです😆」「ラクター😆」「記事書いてて何度もスペルミスしそうになりました😅

参考: アクターモデル - Wikipedia

「一般名詞っぽい名称をやめたのかな?」「UnityにもGUILDという用語があるから紛らわしいみたいな話がありましたね↓」「たしかに既存の概念と同じ名前だと誤解を招くこともあるので、ユニークな名前の方がいいでしょうね👍

参考: Unityゲーム開発者ギルド - Unityゲーム開発者ギルド(外部向け)

「Ractorごとにロックを完全に分離して通信ベースでやりとりするような形になるみたいですね」「元々Guildでもそれをやりたかったわけですし😆」「中身がGuildから大きく変わったわけではなさそう👀」「実装はこれからかな?」「並列化はまだだそうです」「Ractorのコードサンプル、数年前から見覚えある感じする」「アーキテクチャは随分洗練されてきた感ありますね😋

# 同記事より
                  Ractor r
                 +-------------------------------------------+
                 | incoming                         outgoing |
                 | port                                 port |
   r.send(obj) ->*->[incoming queue]     Ractor.yield(obj) ->*-> r.take
                 |                |                          |
                 |                v                          |
                 |           Ractor.recv                     |
                 +-------------------------------------------+


接続することができる(r2.send obj on r1、Ractor.recv on r2)
  +----+     +----+
  * r1 |-----* r2 *
  +----+     +----+


接続することができる(Ractor.yield(obj) on r1, r1.take on r2)
  +----+     +----+
  * r1 *------ r2 *
  +----+     +----+

同時に待つことができる(Ractor.select(r1, r2))
  +----+
  * r1 *------+
  +----+      |
              +----- Ractor.select(r1, r2)
  +----+      |
  * r2 *------|
  +----+

「なるほど、Ractorに対してsend/receiveできるのか〜」「このモデルって最近どのぐらい使われてるんだろう?🤔」「lambdaとかとあんまり相性がよくないかも?🤔」「今Railsサーバーでやっているような部分はRactorなら確実に安全に回せるようになるでしょうね❤」「期待しちゃいます😋

「GVLをうまく制御できなかったのが今まで問題だったので、今後そこがやれるようになるのはよさそう👍」「希望が見えてきそう✨」「ちなみに今翻訳を進めている以下の記事(近日公開予定)↓はGVLの説明としてよく書けてると思うんですけど、後半でReactorについて説明されていて今回の名称変更を知りました」

参考: The Practical Effects of the GVL on Scaling in Ruby

⚓sha256-animation: 暗号生成の様子をアニメーション表示(HackerNewsより)


つっつきボイス:「久々にHackerNewsから拾いました: 暗号生成の様子をシェルで逐一表示してくれるそうです」「おほ、なるほど😆」「見ていてついニヤニヤしちゃいますね😆」「ちょっと楽しい😋

「Rubyで書いて3日前にアップしたみたい」「これgemじゃないのか😳」「ガチRubyで書いてる😆」「おぉ😳」「ncursesっぽいところはライブラリでやってるのかなと思って見たらgemspecファイルがなかったので😆

「どうやってるのかなと思ったら力技で上書きしてるっぽい↓😆」「マジですか😆

# sha256-animation/add.rbより
# -----
# Input
# -----
# defaults
numbers =
[0b01000000000000001111111111111111,
0b01000000000000000000000000000000,
0b01000000000000000000000000000000,
0b01000000000000000000000000000000,
0b01000000000000000000000000000000,
]
# arguments passed
if ARGV.size >= 2
    numbers = ARGV.map {|x| x.to_i(2)} # convert binary strings to integers
end

# ---------
# Animation
# ---------
total = numbers.inject(:+)
width = total.to_s(2).size

(numbers.size + 1).times do |i|
    system "clear"
    numbers.each.with_index do |x, j|
        print ("%032b" % x).rjust(width, " ")
        if j > 0
            if j <= i
                print " +"
            end
        end
        print "\n"
    end
    puts "--------------------------------".rjust(width, " ")

    if i == 0
        puts "" # numbers[0].to_s(2).rjust(32, "0").rjust(width, " ")
    elsif i < numbers.size # for each addition
        puts numbers[0..i].inject(:+).to_s(2).rjust(32, "0").rjust(width, " ")
        sleep 0.4 if i == numbers.size-1 # slight delay before the modulus function
    else
        puts (numbers[0..i].inject(:+) % 2**32).to_s(2).rjust(32, "0").rjust(width, " ") + " mod 2**32"
    end
    sleep 0.4
end
sleep 1.0

「ところでRubyのコードなのにtabとスペース混じりなのって久々に見た🤣」「違う世界線から来た感🤣」「仕事でやったら怒られが発生する😆」「こういうプルリク投げたらみっちり注意されそう😆」「まあ一発ネタだしいいんじゃないでしょうか😆」「もちろん😆」「エディタにスタイルお任せすればいいのに😆」「普段Ruby書いてない人なのかも☺

⚓lockbox: Rails用のモダンな暗号化(Ruby Weeklyより)

# 同リポジトリより
class User < ApplicationRecord
  encrypts :born_on, type: :date
  encrypts :signed_at, type: :datetime
  encrypts :opens_at, type: :time
  encrypts :active, type: :boolean
  encrypts :salary, type: :integer
  encrypts :latitude, type: :float
  encrypts :video, type: :binary
  encrypts :properties, type: :json
  encrypts :settings, type: :hash
  encrypts :messages, type: :array
end

つっつきボイス:「これはencryptor gem↓とかattr_encrypted gemとかのオルタナっぽい」「あ、こういうのが既にあるんですね😅」「世の中的にはattr_encrypted gemがRailsでよく使われてます🧐

「attr_encryptedは以下のような感じでよく使うんですけど、lockboxもそっくりですね: なのでlockboxをこれと比較してみればどんな機能があるかわかりやすいと思います☺」「なるほど〜」

# attr-encrypted/attr_encryptedより
  class User
    attr_encrypted :ssn, key: 'This is a key that is 256 bits!!'
  end

「お、lockboxはkms_encryptedというgemを使ってKMSを指定できる↓ところがなかなかよさそう😋」「どっちもankaneさんが作ってるんですね😳」「きっとankaneさんのことだから、どちらもproductionで使ってそう❤」「ankaneさんならやりそうですね😋

# ankane/lockboxより
class User < ApplicationRecord
  encrypts :email, key: :kms_key
end

⚓その他Ruby


つっつきボイス:「sportdbはサッカーの試合の結果をフェッチするgemだそうです⚽」「プログラミングの練習でこういうのを作ったりしますよね☺」「ところで今サッカーの試合できるんだろうか😆」「ドイツはやってるそうですよ😆」「つおい😆

⚓DB

⚓PostgreSQLのDISTINCT ONでN+1問題を回避する(Ruby Weeklyより)


つっつきボイス:「これはDISTINCTONまで含めるヤツですね」「あ、ONが必要なのか😅

-- 同記事より
  SELECT DISTINCT on (graphics.template_id) graphics.*
    FROM graphics
    WHERE team_id = :team_id AND template_id IN (:template_ids)
    ORDER BY template_id, created_at DESC

DISTINCT ONって使ったことないけどぽすぐれ独自みたい↓」

参考: » distinct onで重複なしのレコードを取得技術ブログ

DISTINCT自体はSQLにありますけど、ONを付けると式を評価した結果から特定のカラムだけを引っ張ってこられるということみたい」「へぇ〜!😳」「普通のDISTINCTは全部のカラムに対してかかりますけど、ONを付けると指定したカラムについてだけ1件目を取れるようだ」「そういうことでしたか😳」「でないとN+1にならない説明がつかなさそうですし☺

「そしてソリューションはRubyの中で生SQLを書くと」「そりゃそうだ😆

# 同記事より
    statement = <<-SQL
        SELECT DISTINCT on (graphics.template_id) graphics.*
          FROM graphics
         WHERE team_id = :team_id AND template_id IN (:template_ids)
      ORDER BY template_id, created_at DESC
    SQL

「データベースのチューニング的な知識ですけど、知っておくと便利そう😋」「知らないとびっくりしそう😅」「こうやってRubyに生SQLを書く方法もあるでしょうし、生SQLを嫌がる人がいるならデータベースビュー↓でラップしておけばActive Recordレベルで普通に扱えるでしょうし🧐

RDBMSのVIEWを使ってRailsのデータアクセスをいい感じにする【銀座Rails#10】

⚓生SQLはお好き?

「ちなみに自分は生SQL苦手で〜す😅」「😆」「ほら、生SQLなら圧倒的に速いじゃないですか😆」「速いんですけど😅」「宣言的に書けるからシンタックスエラーも一発でわかりますし実行時エラーにならないからめちゃくちゃ信頼できますヨ😆」「そうなんですけど😅

「自分もこの間80行ぐらいの生SQL書いてINSERT INTO SELECTでもりっとアップデートかけましたし😋」「それはアプリケーションの中で書いたんですか?」「そのときはバッチ処理ですね: 全ユーザーの特定期間内の特定データを日次で集計するみたいな」「なるほど😋」「INSERT INTO SELECTなら一発で、しかもトランザクショナルに書けますから: 同じことをRailsでループ回してやろうとするとクエリの数がめちゃ増えますし、途中で止まったときの再開方法も考えないといけませんし」「ですよね😆」「せっかくデータベース使ってるんだからACID特性を活用しないと😆

参考: ACID (コンピュータ科学) - Wikipedia

「てな具合に自分はSQLで明らかにうまくやれるならそっちを使っています: Railsだと無駄にループが増えることもありますし、データがおかしくてNULLで落ちるみたいなことがSQLなら起こらないので😋」「😆」「もちろんRailsの世界でnowみたいに型変換が必要なものを注入するのと同等のことをSQLでしようとすると面倒ですけど😆」「せっかくRails使ってるのに何だかもったいなさそうで😅」「まあそういうものが多いときはSQLだと面倒だなって思います😆

⚓クラウド/コンテナ/インフラ/Linux/Serverless

⚓GitHub Code Scanning(Publickeyより)


つっつきボイス:「ここで使ってるCodeQLって脆弱性解析エンジンなのか↓」「オープンソースなんでしょうか?」「買収したとあるぐらいなのでたぶん違うでしょうね☺

参考: CodeQL - GitHub Security Lab

「CodeClimateとかがこれまでやっていたことをGitHubが公式で始めたということなんでしょうね」「OSSプロジェクトが無料ということは、そうでないプロジェクトは使いたければお金払ってねと💰」「やってることはCodeClimateと同じなので特に新しくはないですけど☺


codeclimate.comより

「GitHubもそろそろ独禁法周りに注意が必要になってきたりして😆」「それちょっと思いました😆」「GitHubの勢い的にもかつてのマイクロソフトを思わせるところがありますし」「サービスがGitHubに統合される方がたしかに便利ですけど😅

参考: 独占禁止法 - Wikipedia


「あと、GitHubのシェアがあまりに大きいからかもしれませんけど、サードパーティのこの種のサービスってGitHubには対応していてもGitLabのような他のリポジトリには対応してないところもときどきあるんですよ😢」「ありますね😆」「以前某静的解析サービスにGitLab対応できるかどうかを問い合わせたらできなくて、サポートにも聞いたら『貴重なご意見ありがとうございます』で終わっちゃいました😇」「ありゃ🤣

⚓GitLabの「Tech Stack Application」(Serverless Statusより)


同記事より


つっつきボイス:「これは何でしょう?」「記事の図↑をぱっと見したところGitLabの内部構造の話かと思ったら、GitLabの法人が使っているサービスについての解説みたい」「GmailとかLinkedInも入ってるしそれっぽいですね」「やっぱりSalesforceでつなぎ込んでるのか〜」「使ってるサービス全部書いてあるんですね😳」「記事の真ん中あたりにあるGoogleスプレッドシート↓でリストが公開されてます」

「こんなにたくさんサービスが😳」「さすが公開を重んじるGitLab😳」「それぞれのサービスにいくらかかってるのか気になります😆」「ユーザー名とか出てますけど大丈夫なのかしら?🤔」「たぶんそういうオープンな文化ということでやってるでしょうし、それなりの契約も結んでるでしょうし🧐」「そういえばシートの上の方にちゃんとapproveされてるって書いてますね」「へぇ〜😳

「ここまでオープンにしてあると、これから組織を立ち上げる人に参考になりそうですね😋」「GitLab的にはかなり自信を持ったアプローチでしょうけど、それにしても大胆ですね」「たしかに」「もちろんGitLabはすべて承知のうえでやってるでしょうし、認証デバイス使ったりしてプライバシー周りは十分注意しているでしょうし🧐」「個人的には公開して大丈夫?って思いますけど😆

⚓その他クラウド(Serverless Statusより)


つっつきボイス:「ツイートとは関係ありませんけど、最近サーバーレスアーキテクチャって言葉をあんまり見かけなくなったのは、もう普通になってきたのかなって😆」「流行りを通り越して定着したのかもですね」「マイクロサービスというバズワードは完全に過渡期を過ぎた感😆」「もちろんマイクロサービスは取り組んでるところは取り組んでますけど☺

「まあビジネス上のアーキテクチャを考えればたいていどこかでRDBが絡んでくるので、すべてがサーバーレスになることはほとんどないと思いますし🧐」「ですよね」「あとサーバーレスという言葉が『マネージドの機能を使っている』という意味なのか、文字どおり『ステートフルなものがない』という意味なのかという点で誤解を招きやすいところがあるかなという気はします😆」「たしかに誤解が入りやすい😆」「マネージドのRDBを使っているのはサーバーレスと呼んでいいの?みたいな話😆」「ほんにそれ😆

「もちろんサーバーレスそのものは普通にいいと思います: 自分も提案に入れますし🧐」「ふむふむ」「何をサーバーレスと呼ぶかですけど、自分の場合はだいたい『メンテナンスしたくないもの』😆」「まあたしかに😆

⚓JavaScript

⚓Deno 1.0.0がリリース


つっつきボイス:「今話題のDeno」「社内Slackでも盛り上がってましたね」「えっこれ知らない😅」「デノではなくディーノだそうです」「つい男爵ディーノを思い出しちゃうんですけど😆」「それなし〜!🤣」「若い人知らないですよきっと😆

参考: 男爵ディーノとは (ダンシャクディーノとは) [単語記事] - ニコニコ大百科

「Node.jsの作者が、Nodeでもう引き返せなくなったつらい部分を全部作り直したいってやり直したんだそうです↓😆」「上の記事見るとNodeの作者がめちゃめちゃ反省してますね😆

「Denoは今のNodeと機能上の互換性がないみたいです😆」「ありゃ😆」「JavaScriptのレイヤは基本的に互換みたいなんですけど、まだライブラリやパッケージマネージャとかが整備されてないとかいろいろあるらしくて、とりあえずnodenvではインストールできなかった😇」「う〜む😅」「まあNodeじゃありませんし😆

「なのでこれを使って何をするかはもう少し先の話っぽいんですけど、TypeScriptがビルトインされているあたりなんかは、それこそマイクロサービスでマイクロなものだけを作ったりするのによさそうですね👍」「ふむふむ」「まあ今のJavaScriptはnpm周りがいろいろ地獄味を帯びてますから😆、こうやってクリーンにやり直すのはいいかもしれませんね☺」「新しもの好きの人はどうぞという感じで😆


「それにしてもJavaScriptという言語の出自を考えたら、よくぞここまで成長したと思います😂」「たしかに😆」「PHPもいろいろ乗り越えてきてますけどそんな比じゃないですよね😆」「『セキュリティのためにJavaScriptはオフにしましょう』なんて言われてた太古の時代を知ってる者としては特に😆」「ECMAScriptの標準化もスゴいし💪」「JavaScriptがこんなに発展したのは、ひょっとしたら世の中を支配してたかもしれないMacromediaが消えたのも大きいかも😇」「言語は面白いな〜😆

参考: マクロメディア - Wikipedia


後で公式サイトを見つけました↓。アイコンは恐竜のようです。


deno.landより

⚓CSS/HTML/フロントエンド/テスト/デザイン

⚓servo: Rust製のパラレルブラウザエンジン

つっつきボイス:「servoってブラウザエンジン?」「レンダリングエンジンじゃなくて?」「レイアウトエンジンですって」「しかもサムスンがやってる😳」「埋め込み系を想定してたりして」「どの辺がパラレルなんだろか😆」「犬のアイコンが謎🐶

「コマンドがmachというあたりがちょっとそそる↓😋」「マークって何でしょう?😅」「今のmacOSの元になっているのがMach(マーク)カーネルというヤツです🧐

# 同サイトより
./mach build --release
./mach run --release tests/html/about-mozilla.html

参考: Mach - Wikipedia

そういえばMachはドイツ語読みだと「マッハ」でしたね。

Servo(サーボ)とは、Mozillaの研究によって開発されているウェブブラウザ用レイアウトエンジンである。サムスンによってAndroidおよびARMプロセッサへ移植されている[3]。多くのコンポーネントは、きめの細かい、孤立したタスクによって処理される。Servoは高度な並列処理を行い、多くのコンポーネント(レンダリング、レイアウト、HTML解析、画像復号など)が独立したタスクによって処理される。プログラミング言語Rustによって開発されている。
Wikipediaより

「パラレルって要するにコンポーネントの処理がそうなってるということね😳」「Chromeもやれるところはパラレル化してるみたいですけど、Wikipediaの日本語を信じるなら、そのあたりをRustでもっと徹底的にやろうという企画なのかも、たぶん😆」「サムスンがやってるいうことは、最近のマルチコア化したスマホでパラレルに動くブラウザを目指しているのかもとちょっと思いました☺」「『FirefoxのGeckoのコンポーネントをこのservoに置き換え始めてる』んですね😳」「Mozilla Reseachがやってるからそういうことなんでしょう☺


以下はつっつき後に見つけたツイートです。


github.com/servo/servoより

⚓iOSのブラウザ

「そういえば最近はどうなのか知りませんが、iPhone用のブラウザってガワはFirefoxやChromeとかでも中身のレンダリングエンジンはSafariのエンジン(WebKit)だったりしますね」「え、そうなんですか😅」「Appleのライセンス規約ではSafari以外のレンダリングエンジンを丸ごと入れてはいけないことになってたと思います🧐」「よく聞く話ですね〜😆

参考: iPhoneではあらゆるWEBブラウザのエンジンがSafariと同じ? - いまさら聞けないiPhoneのなぜ | マイナビニュース
参考: WebKit - Wikipedia

「たしかAppleの規約だと、外部から受け取ったコードやバイナリによって動作を完全に変えてしまえるもの、つまりプログラム実行環境はアプリに搭載することを禁止してますよね」「そうそう😆」「ブラウザってまさにそれですし😆」「昔ScratchだかSqueakだかもiOS版を出そうとしたらそこをツッコまれて頓挫したという話も聞きました😇」「要はプログラムを書いて実行できるアプリはダメという😆」「プログラム書いちゃダメなんだ、へ〜、って当時思いました😆」「まあ気持ちはワカル😆

「あ、だからiPadを開発環境にするみたいな話があんまり出てこないのか😳」「今ならオンラインIDEにすればやれますけど☺」「2010年頃にはそんないいものありませんでしたよ😆」「当時はニーズもなかったでしょうし、利用者の環境も追いついていなかったから商売にはならなかったでしょう😆

⚓その他

⚓置くだけ?


つっつきボイス:「Brewletって日本人ならもう青いアレしか連想しない😆」「小林製薬😆」「置くだけ😆」「作者日本人じゃないからその意図はなさそう😆」「Macに入れようか迷ってます😅」「appletのletみたくBrewletという接尾語をつけたんでしょうけど😆」「きっとそう😆」「はいそれだけでした〜」

⚓MacとWindowsでのパッケージ管理

「でもHomebrewのパッケージってそんなに頻繁にアップデートします?」「私は所在ないときに割とアップデートしちゃいます😅」「まあアップグレードされたらまずいものはpinすれば別にいいのか☺」「PostgreSQLとかはpinしてます」

参考: Homebrewのupgradeで無視したい - 銀の弾丸、はじめました

「ちなみにWindows環境だとHomebrewの代わりに何使ってます?」「WindowsにはHomebrewなんかありませんけど😆、最近はArch Linuxの勢いのよさに媚を売ってManjaro Linux使ってるのでpacmanコマンドとかyayコマンドとか使ってます😆」「媚って😆」「未だにpacmanコマンドのオプション覚えられない😆

参考: pacman - ArchWiki
参考: Arch Linux に yay をインストール - Qiita

⚓メモリ64GBはもう当たり前?

「Windowsでここまで環境構築したんで、もうMacに戻そうと思わないかな〜😆」「😆」「Macで同じスペック実現するならチーズおろし器買わないと😆」「先週もチーズおろし器の話出てましたけどそれ何でしたっけ?」「最近のタワー型Mac↓😆」「あれチーズおろし器って言うんですか😆」「本物はAmazonで1,500円ぐらいで買えますし😆

「そして本当にチーズおろしてる人↓🤣

参考: iFixit、新Mac Proで「チーズおろし」を敢行。みごと細切れに - Engadget 日本版

「中にMac mini入れて動かしたい😆」「タワーMacは細長いからMac miniみたいに正方形だと入るかな〜?😅」「Amazonで買ったゴミ箱でガチでPC組み立てた人いましたね😆」「あれは頑張れば入りそう😆

「まあ今のWindows環境は快調ですよ、とりあえずメモリ64GBに16コアあれば困ることがないってわかりましたし😆」「Chrome起動したら結構メモリ食いそうですけど😆」「いえいえ、Chrome立ち上げてもまだ20GBぐらい空いてますし、Hyper-Vに割り当てるメモリをケチらなくてもいいし、もう心の余裕が全然違う🥰」「それは幸せですね〜😆」「富豪は富豪でもビリオネア💰」「今はもうメモリ32GBだと全然足りないですよ😆

参考: 富豪的プログラミング - 増井俊之

⚓番外

⚓これは欲しい


つっつきボイス:「あ〜穴開けられそう🤣」「そう思いますよね🤣」「開けてみたい😆」「持ち運びできるのは大きな価値だとは思うんですけどね〜」「これは電車に乗るときに嬉しいヤツでしょう😋

「イメージ的には京都に持ってって使う感じですかね〜、京都って移動大変だから」「あ〜なるほど」「こういう電動カートって今公道を走れるようになってたと思います☺」「やっぱり免許ないとダメでしょうね😅」「電動車椅子と同じくくりなら免許なくてもイケそうですけど😆」「スピードが遅いから大丈夫そう」「時速10kmなら早歩きレベルでしょう😆

参考: 公道を走れる”電動三輪車”が登場!?|クラウドファンディングで販売開始|【業界先取り】自動車ニュース2020国産車から輸入車まで【MOTA】


後半は以上です。

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

週刊Railsウォッチ(20200518前編)スライド『令和時代のRails運用』、Ruby 3.0のキーワード引数変更リスケ、Action CableのCLIほか

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

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

Ruby Weekly

Awesome Ruby

RubyFlow

160928_1638_XvIP4h

Publickey

publickey_banner_captured

Serverless Status

serverless_status_banner

Hacker News

160928_1654_q6srdR

週刊Railsウォッチ(20200525前編)2020年のRailsマストgem 19個、スライド『Fat Modelの倒し方』、AR mergeのrewhereオプションを変更ほか

$
0
0

こんにちは、hachi8833です。JavaScriptが25歳の誕生日を迎えたそうです🎉。10日そこそこで最初のプロトタイプを作ったとは😳。Rubyはちょっとだけ年上なんですね。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

今回のつっつき会は日中の社内勉強会枠で行いました。

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

今週は以下のコミットリストから見繕いました。Changelogのdiffも見るのが効率よいとやっと気づきました😅

⚓Active Recordで署名付きidをサポート

署名付きidベースでのレコード検索サポートを追加した。署名付きidは不正防止の照合済みidのことで、expires_inで期限を設定したりpurposeでスコープを指定したりできる。これが特に有用なのは、パスワードリセットやメール認証などでレコードとやりとりできる署名付きidの所有者が欲しいが、特定の期限を設けたい場合。
同PRより大意

# 同PRより
signed_id = User.first.signed_id expires_in: 15.minutes, purpose: :password_reset

User.find_signed signed_id # => nil, since the purpose does not match

travel 16.minutes
User.find_signed signed_id # => nil, since the signed id has expired

travel_back
User.find_signed signed_id, purpose: :password_reset # => User.first

User.find_signed! "bad data" # => ActiveSupport::MessageVerifier::InvalidSignature

つっつきボイス:「DHH自らのプルリクです」「サインドid?」「purpose: :password_resetみたいに書けるのね」「いろんなidにマッピングされる、有効期限付きのidを発行できる機能がサポートされたように見える: インターフェイスが便利そう👍

「この署名付きidって何でしょう?」「生のidを外部にさらさないために一時的に使えるidですね🧐」「有効期限を付けられるし、そんな感じかな」「そうそう😋

「このfind_signed↓なんか、署名付きidを渡すと生のidが取れるし😋」「単に取り直してるだけという😆

# activerecord/lib/active_record/signed_id.rb#42
      def find_signed(signed_id, purpose: nil)
        if id = signed_id_verifier.verified(signed_id, purpose: combine_signed_id_purposes(purpose))
          find_by id: id
        end
      end

「SHA256使ってる〜」「他の部分はこの署名付きidの管理周りでしょうね」「idや期限はどこに保存してるんだろう?🤔」「復号したら時刻が入っててアプリケーションでそれをチェックしたりして、って何も見ないで言ってますけど😆」「まあやろうと思えばできますね☺

参考: SHA-256(Secure Hash Algorithm 256-bit)とは - IT用語辞典 e-Words

「テストも1分で期限切れにしてtravelで2分進めたらnilが返るようになってる↓」

# activerecord/test/cases/signed_id_test.rb#32
  test "fail to find signed record within expiration date" do
    signed_id = @account.signed_id(expires_in: 1.minute)
    travel 2.minutes
    assert_nil Account.find_signed(signed_id)
  end

「このプルリク、いいね🎉がいっぱい付いてますね」「こういう一時的なid発行はユースケースとしてよくあるヤツ🧐」「今までだとgem使ったり自力で実装したりという感じだったんでしょうね」「こういう機能があることを知ってれば使うかも😋」「自力でやらずに済む😂

「Deviseを使うとこういうのが入ってきますね: インターフェイスはちょっと違いますけど😆」「なるほど」「Deviseだと何とかtokenというカラムがあった覚えがあります」

⚓each_paireach_valueにブロックを渡さない場合にEnumeratorを返すようになった

# actionpack/lib/action_controller/metal/strong_parameters.rb#L363
    def each_pair(&block)
+     return to_enum(__callee__) unless block_given?
      @parameters.each_pair do |key, value|
        yield [key, convert_hashes_to_parameters(key, value)]
      end
    end
  ...
    def each_value(&block)
+     return to_enum(:each_value) unless block_given?
      @parameters.each_pair do |key, value|
        yield convert_hashes_to_parameters(key, value)
      end
    end

つっつきボイス:「改修前はブロックを渡さないとエラーになってたんですね😳」「ハッシュの場合とインターフェイスを合わせたということですね👍」「なるほど!」「普通のハッシュならこうやって呼べますので☺」「こういうのが直っていくのはいいですね〜😋

irb(main):001:0> ActionController::Parameters.new(foo: "bar").each_pair
=> #<Enumerator: <ActionController::Parameters {"foo"=>"bar"} permitted: false>:each_pair>
irb(main):002:0> ActionController::Parameters.new(foo: "bar").each_value
=> #<Enumerator: <ActionController::Parameters {"foo"=>"bar"} permitted: false>:each_value>

「この間社内でもちょっと話題になったんですけど、RailsのActive Recordとかって、インターフェイスはたしかにEnumerableと同じなのに、実際にはEnumerableモジュールをインクルードしてないものがちょくちょくあったりするんですよね😅」「あ〜わかります😆」「それと似た感じで、Enumerableモジュールはインクルードしてないけど、あたかもしているかのような挙動に近づけたんでしょうね☺

⚓raise_on_missing_translations設定をビューとコントローラで統一

# actionview/lib/action_view/railtie.rb#L43
    config.after_initialize do |app|
      ActiveSupport.on_load(:action_view) do
        app.config.action_view.each do |k, v|
+         if k == :raise_on_missing_translations
+           ActiveSupport::Deprecation.warn \
+             "action_view.raise_on_missing_translations is deprecated and will be removed in Rails 6.2. " \
+             "Set i18n.raise_on_missing_translations instead. " \
+             "Note that this new setting also affects how missing translations are handled in controllers."
+         end
          send "#{k}=", v
        end
      end
    end

つっつきボイス:「今までconfig.action_viewにあったi18n(国際化)設定をconfig.i18nにまとめたのね↓」「あ、設定の話なのか😳

# actiontext/test/dummy/config/environments/development.rb#L57
  # Raises error for missing translations
- # config.action_view.raise_on_missing_translations = true
+ # config.i18n.raise_on_missing_translations = true

「訳文をフェッチして取れなかった場合の振る舞いなんでしょうね☺」「まあi18nの設定をビューとコントローラで別々にすることはまずないから、まとめちゃってもいいと思いま〜す😋」「普通に考えて、コントローラではi18nエラーを出さないけどビューでは出すなんてしませんし😆」「一緒にしたいですよね😆

⚓:rewhereオプションとmergeを組み合わせたときの挙動を変更

mergeのオプション:rewhereで、mergeされる側の条件をそのまま置換するようサポートされた。
Changelogより


つっつきボイス:「rewhereって何これ?🤣」「スゴい名前🤣」「へ〜、rewhere自体は前からあったみたい」「あ、そうなんですね😅」「名前からしてreorderと同じ頃にrewhereが実装されたのかも🤔」「この改修は、Active Recordのmergerewhereを使ったときの挙動をあるべき姿に戻したということでしょうね☺

「まあunscopeよりはrewhereの方が直感的ではありますし😋」「たしかに😋」「条件が既に設定されてしまっているwhereとかorderとかをやり直したいときって割とよくありますけど、unscope.whereって書くよりはrewhereって書く方が早いし😆」「whereを付け直したいときあるといえばある😆

「ま、自分はActive Recordのインスタンスを取り直す方がいいんじゃないかと思いますけど、どうしても付け直したい人がいるんでしょうし🤣」「😆

関連PR: #39236
relation.mergeメソッドは、mergeされる側の条件を置き換えることもあるが、relation.rewhereを使わない場合はmergeする側とされる側の条件が両方残ることもある。
このままでは、mergeされる側の条件が置き換えられるのかどうかをmergeの結果で予測するのがきわめて難しい。
既にある方法のひとつは、mergeする側のリレーションでrelation.rewhereを使うことだが、これもmergeでリレーションが使われるかどうかを事前に知るのが難しい(mergeのワンタイムリレーションを除く)。
この問題を修正するために、merge:rewhereオプションで、mergeされる側の条件をそのまま置換することをサポートするよう提案したい。
このオプションは、rewhereでないリレーションがrewhereリレーションとして振る舞うようになる。
同PRより大意

david_and_mary = Author.where(id: david.id..mary.id)

# マージする側とされる側の両方に競合条件が存在する
david_and_mary.merge(Author.where(id: bob)) # => []

# マージされる側の条件がrewhereによって置き換えられる
david_and_mary.merge(Author.rewhere(id: bob)) # => [bob]

# マージされる側の条件がrewhereオプションによって置き換えられる
david_and_mary.merge(Author.where(id: bob), rewhere: true) # => [bob]

where(id: david.id..mary.id)した状態でwhere(id: bob)をマージすると、bobがない結果セットからさらに絞り込む形になるので結果は空になると」「それが本来望ましい動作だから、出るはずのないbobがwhere(id: bob)で取れたら確かにびっくりしますし😳」「でrewhere(id: bob)だとやり直せるからbobが取れると」

「一番下にrewhere: trueっていうオプションがある」「このオプションが必要になるときって一体😆」「この辺の挙動を理解してないとrewhere: trueって付けるのを思いつかなさそう😆

「このscope.unscope!しているあたり↓が改修ポイントかな」「breaking changesにならない範囲で修正したんでしょうね😋

# activerecord/lib/active_record/relation/query_methods.rb#L704
    def rewhere(conditions)
-     attrs = []
      scope = spawn
-
      where_clause = scope.build_where_clause(conditions)
-     where_clause.each_attribute do |attr|
-       attrs << attr
-     end

-     scope.unscope!(where: attrs)
+     scope.unscope!(where: where_clause.extract_attributes)
      scope.where_clause += where_clause
      scope
    end

rewhere、自分はあんまり使わないかな〜: そもそもrewhereを使わないといけないようなコードの書き方しませんし🤣」「それやったらスコープのライフサイクル長過ぎ〜🤣

⚓ENVのBACKTRACEオプションでバックトレースの削除をオフにできる


つっつきボイス:「Railsフレームワークのバックトレースを消せるようになったのかと思ったら、バックトレースのクリーンアップを環境変数で止められるようになったのね」「二重否定っぽくてややこしい😆」「これはなかなかいい機能😋

「最初ドキュメントにハウツーを追加したのかなと思ったら、よく見ると下にコードも追加されてました↓😅」「BACKTRACE=1という書式がちょい微妙😆」「backtrace_cleanerにもともとremove_silencersというのがあって、それをオンにできるようになったと」「デフォルトではバックトレースを削除すると😆」「ほとんど隠し機能😆

# railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb.tt#L6
-# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
-# Rails.backtrace_cleaner.remove_silencers!
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code
+# by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'".
+Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"]

フレームワークのコードや埋もれたissueをデバッグするときは、一時的にバックトレースの削除をオフにしておきたくなることが多い。これを環境変数で設定できれば楽だ。
同PRより大意

⚓Rails

⚓2020年度のマストgem 19


つっつきボイス:「今年のマストgemどんなんかな?」「RuboCopは入ってませんが、たぶんあって当然なのかなと😆

「bootstrap-emailがある」「これおいしいヤツでしょうか?😋」「HTMLメールはBootstrapのスタイルを付けられないので、それを効かせられるようにするやつなのかなと😆」「HTMLメールのスタイルは普通styleに入れないといけないですし☺

「先週話題にしたlockboxも入ってますね↓(ウォッチ20200519)」

「rolifyって初めて見たかも↓」「いわゆるユーザーの認可(authorization)とか権限周りを管理するgemですね」「cancancanが隣にあるし😆」「has_role?するヤツ」

# 同リポジトリより
user.has_role?(:moderator, @forum)
#=> false # ユーザーが別のフォーラムのモデレータである場合

「Faradayもいらっしゃる↓」「Faraday今も現役なのね〜」「更新もされてますね」

「まあこれまでとそんなに大きく違うわけではなさそうかな」「bootstrap-emailがマストかどうかはさておき😆」「メール使わなければ要らないですし😆」「突飛なgemはなさそう😋」「Railsもそれだけ枯れてきたというか成熟したんでしょうね☺

⚓interactor-rails: RailsでInteractorパターン

# 同リポジトリより
class AuthenticateUser
  include Interactor

  def call
    # 何かする
  end
end

つっつきボイス:「interactor-railsは取り上げたことがありそうでなかったので」「interactor gemをRailsですぐ使えるようにした感じみたい」「interactorはさっきのマスト19 gemにも載ってましたし😆」「どうせRailsで使うことが多いでしょうから統合したんでしょうし😆」「使いたいならどうぞ〜😋

interactor gemは以下の翻訳記事にも載っています。

Railsコードを改善する7つの素敵なGem(翻訳)

⚓銀座Railsスライド『Fat Modelの倒し方』


つっつきボイス:「この間の銀座Railsのスライドですね」「ファットモデルはRailsの『滑らない話』の定番😆」「後で読もっと😋

「そういえば神速さんはなるべくgemを使わない主義って聞いた覚えありますね」「その辺も含めて、やっぱり設計周りは人によってさまざまだな〜って思いながら聞いてましたし、こういう設計周りの話はいろんな人のを聞いておくのがいいと思います👍」「設計で『これしかない』って思い込んだらコワいですね😅

⚓Awesome Code: リポジトリのコードをオートフォーマットするサービス


同サイトより


つっつきボイス:「GitHubだけじゃなくてBitBucketやGitLabにも対応してるようです」「リポジトリCIのフックに挟めるとかそういう感じですね😋」「おいくらかな?」「オープンソースは無料で、デフォルトブランチのみのStartupプランだと月10ドルか〜💰」「サービスでやる方が楽ならやってもいいかも」

「その下の紹介記事によると、Awesome CodeはRuboCopやrufoなどをいいとこ取りして、面倒な部分を考えずにやれるようです」「こういうlintの設定ってプロジェクトによって違いますけどね😅」「そういや最近rufo使ってない😆」「自分も外しました😆」「その辺の設定をIDEのlintとなかなか同じにできないのが面倒なんですよね😢、全員が同じIDE使っていればそれでやれるんですけど」「たしかに」

⚓書籍『The Rails 6 Way』


つっつきボイス:「銀座Railsでこの本の話題が出ていたので」「Rails 6のRails Wayね」「と思ったら執筆の進捗20%でした😆」「はよ出さないとRailsが6.1になっちゃう😆」「頑張って欲しいです😂」「でもRails 6はマルチDB周りとかが固まってなかったりしますし」「Active StorageはRails 6より前からですけど、そこそこ定着した感ありますね☺


目次より:

  • 強力・スケーラブルでRESTに沿ったバックエンドサービスを構築する
  • Action Controllerを用いる複雑なフローのプログラミング
  • Active Recordによるモデル/リレーションシップ/操作の表現、およびActive Recordの高度なテクニック
  • マイグレーションでデータベーススキーマをスムーズに進化させる
  • Action Viewでフロントエンドを構築
  • アセットパイプラインやWebpackerによるアセットの構築
  • キャッシュやTurbolinksによるパフォーマンスやスケーラビリティの最適化
  • hamlテンプレートで生産性を向上させる
  • SQLインジェクション/XSS/XSRFなどの攻撃からシステムを守る
  • Action MailerやAction Mailboxによるメール統合
  • Action CableでWebSocketsベースのリアルタイムなブラウザ動作を有効にする
  • バックグラウンド処理でレスポンスを改善する
  • JSONをやりとりするAPI専用バックエンドプロジェクトを構築する
  • Active Storageでファイルをクラウドに保存する
  • Action Textで洗練されたWYSIWYGを追加
  • マルチプルデータベースの活用

⚓その他Rails


つっつきボイス:「1つ目はTruffleRubyのCIテストが全部通ったそうです」「あ、Rubygemsのテストね😆」「TruffleRubyでRailsが通ったのかと思った😆

つっつき後にTruffleRuby 20.1.0がリリースされました。

「2つ目はRailsでRSpecやる人にはお馴染みの『Everyday Rails』が10周年だそうです🎉」「🎉」「そういえば昨日大学の講義で自己紹介するときに自分もRailsやって10年経ってるって気づきましたし😆」「10年!」「もうPHPフレームワークより長かった😆


「以下はついさっき見つけたjnchitoさんのチュートリアル動画です↓(30分)」


前編は以上です。

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

週刊Railsウォッチ(20200518前編)スライド『令和時代のRails運用』、Ruby 3.0のキーワード引数変更リスケ、Action CableのCLIほか

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

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

Rails公式ニュース

週刊Railsウォッチ(20200526後編)Rubyでよくやるスレッドバグ、Kubernetesでよくあるミス10、CSS/SVG/Canvasの使い分けほか

$
0
0

こんにちは、hachi8833です。先ほど以下のツイートを見つけました。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

今回のつっつき会は日中の社内勉強会枠で行いました。

Ruby

Rubyでやりがちなスレッドバグ


つっつきボイス:「thoughtbotの記事です」「お、久しぶり😋」「TOCTOUバグ(time-of-check to time-of-use)っていう言い方初めて知りました😳」「その呼び方は自分も初めて見ましたけど、競合状態周りでは超一般的によくあるヤツ😆」「ソケットが取れないときとか😆」「これはどんなリソースでも発生する可能性のある競合状態ですね🧐」

「あぁ、こういうの↓は絵に描いたような競合の書き方😆」

# 同記事より
def eval_gemfile
  if File.exist?("Gemfile.local")
    eval(File.read("Gemfile.local"))
  end
end


同記事より

「こういうのもそう↓: ファイルディスクリプタを渡して処理しないと当然ヤバい😇」「『ファイルがあるかどうか聞いたらあるって答えてくれたのに、その後ファイルを読もうとしたら消えてる〜』ってヤツ🤣」「🤣」「まさに典型的な競合状態🤣」「競合とかあんまり考えたくないけど😆」「まあ例外で落ちてもいいように書けばいいことですし🧐」「そうなんですよね☺️」

def print_secrets
  if has_access?("secrets")
    puts File.read("secrets")
  end
end

「authorization(認可)を更新した場合の競合↓、これもよくあるヤツ😆」「よくあるよ〜😆」「こういう権限を本当にキャッシュしていいかどうかは十分考えておかないとね☺️」「記事にもありますけど、これにトランザクションも絡むとさらに厄介👺」

# 同記事より
class PromotionsController < ApplicationController
  def new
    @user = User.new
  end

  def create
    @user = User.find(params[:user_id])

    if @user.promote
      redirect_to new_promotions_path
    else
      render :new
    end
  end
end

class User < ActiveRecord::Base
  def promote
    update(can_promote: true)
  end
end

権限チェックの厳密さとパフォーマンスのトレードオフ

「一般に権限とかパーミッションを動的に更新する場合はキャッシュしちゃいけないんですよ😇」「そうそうっ😤」「ちゃんとやるならキャッシュを通さずに元の情報を直にチェックしないといけません: だから権限の絡む処理は遅くなりがちなんですけど😆」「😆」

「ちょうど今日もその辺の話になったんですけど、権限処理があるとデータベースにある権限テーブルを毎回チェックしに行かないといけなくなるわけですよ」「わかります〜」「権限周りをすごく厳密にやろうとすると権限チェックのたびにトランザクションでテーブルをロックしないといけなくなるんですけど🔐、果たしてそこまでやるのかと🤣」「それつらくなるヤツ😭」

「権限が1秒間に5回も変わりまくるようならやらないといけないでしょうけど😆」「あとレスポンスに時間のかかるサービスが絡んでたりすると現実にそこまでやるのは無理でしょ😆」「そうなんですよ〜😅」

「権限処理をどこまで厳密にやるかはホント悩ましい問題😭」「よほどクリティカルなものなら別ですけど、リクエストの最初の時点で権限チェックがパスすればまあいいんじゃないのって思うこともありますし😆」「非公開の情報が思わぬタイミングで公開されちゃうような類でなければ☺️」


同記事見出しより:

  • ネットワークの事例
  • ファイルアクセスの事例
  • ファイルアクセスでのセキュリティ問題
  • 認可(authorization)の事例
  • まとめ: 「ask for forgiveness, not for permission」が基本

AWS Lambdaのランタイムサポートポリシー


つっつきボイス:「Ruby 2.5が今後AWS Lambdaでサポート終了したときの扱いについて社内Slackで見かけたので」「そうそう、Rubyに限らない話ですけど☺️」

廃止は、2 つのフェーズで行われます。最初のフェーズでは、廃止予定のランタイムを使用する関数を作成することはできません。少なくとも 30 日間は、廃止予定のランタイムを使用する既存の関数を引き続き更新することができます。この期間が過ぎると、関数の作成と更新のいずれも、完全に無効になります。ただし、この関数は呼び出しイベントの処理に引き続き使用することができます。
同ドキュメントより

「この記述↑で面白いのは、最初のフェーズではunsupportedなランタイムを使う関数が作れなくなるんですけど、更新はできるというところ」「ほぉ〜😳」「だからその期間なら同じLambdaリソースのバージョンアップはできます🧐: それを過ぎたら更新もできなくなるので、そうなったらマジで困りますけど😅」

ライフゲームをRuby 1行で書いた(RubyFlowより)

# 同記事より
generations = Enumerator.produce(grid) { |cur| life(cur) }.take(10).map(&:wrap)
show AA[*generations]


同記事より


つっつきボイス:「zverokさんがRuby 1行でライフゲームを書いたそうです🧩」「ほほぅ😋」「今日は若い人もつっつきに参加してるのでライフゲームの説明必要かしら?」「ググってください😆」「😆」「英語だとConway’sが付きますね😆」「ライフゲームは計算機科学方面だとアルゴリズムの授業とかで名前ぐらいは出るんじゃないかな😆」「自分のところはメディア方面だったから授業でやらなかったけど😇」

参考: ライフゲーム - Wikipedia


ライフゲームの動画は山ほどありますが、ライブゲームで作ったデジタル時計を見つけました。

キーワード引数その後


つっつきボイス:「例のキーワード引数リスケの後に見つけたツイートです」「上で話したAWS LambdaのRuby 2.5サポートが終わったら、Ruby 2.7のコードにruby2_keywordsを入れて対応するのがいいかなとこれを見て思いました😋」

「公式移行ガイドに『Module#ruby2_keywords使え』と書かれているのにあまり認識されてないのかな😳」「warningを見ると、ついキーワード引数周りを直すことで対応しないといけないような気持ちになるからかもしれませんね☺️」「warningに『Module#ruby2_keywordsでも対応できますヨ』と出したらもっと使われるようになるかなとちょっと思いました😋」

「コワイのは特定条件の場合にしか通らないようなメソッドでしょうね: テストもないprivateメソッドなんかだと、warningで気づいたものはつぶせますけど気づけなければつぶせないので😇」「ですね🤔」「アプリレベルでは、privateメソッドも含めてひととおりテストがあれば対応できるかな…☺️」

Ruby 3.0のキーワード引数変更のスケジュールが変更に


以下はつっつき後に見つけたツイートです。

Koreanumbers: 韓国語の数詞<->数値変換


つっつきボイス:「コリアンナンバーって特殊なのかな?」「韓国語わかりませんけど😆、何万何千何百みたいな数詞を数値にしたりその逆をやってるみたいです」「触ってみた感じ、何となく日本の漢数字と体系似てる気がする😆」「ハングルは比較的新しい言語体系だから、数詞で英語みたいな理不尽なところが少なそう😆」「たしかに英語はいろいろ理不尽😆」「読みにくいよなって思いますし😆」


# 同サイトのサンプルより
Base numbers:
일: 1, 이: 2, 삼: 3, 사: 4, 오: 5, 육: 6, 칠: 7, 팔: 8, 구: 9

Small Units:
십: 10, 백: 100, 천: 1000

Big Units:
만: 10_000, 억: 100_000_000, 조: 1_000_000_000_000

参考: 韓国語の数字の読み方と数え方 | 韓国語(ハングル)|韓国旅行「コネスト」

以前自分が日本語の数詞を追加したhumanize gemをちょっと思い出しました↓。

Ruby: 数値をスペルアウトするhumanize gemに日本語ロケールを追加しました

書籍『実用的Rubyスクリプティング』が無料公開

つっつきボイス:「こちらはMatzがリツートしてたのを見つけたんですけど、ダウンロードしてみるとRuby 1.9〜2.0にかけての頃のようでした」「なかなかレガシー☺️」「オープンにしたということは次の版を出すのかなとちょっと思いました😋」「第10講に『GUIにおける代表的なライブラリはtk』とある」「時代を感じますね👴」「もちろん今でもtk使えますし🧐」

参考: Tcl/Tk - Wikipedia

同PDFの奥付を見ると2014年1月に初版第1刷発行となっていました。ライセンスはCC BY NCです。

参考: Creative Commons — 表示 - 非営利 4.0 国際 — CC BY-NC 4.0

その他Ruby

「こちらはRubyのえかきうたということで」「Rubyの絵でしたか😆」

DB

Amazon KendraがGAに(DB Weeklyより)


つっつきボイス:「けんどら?」「AWSに去年からプレビュー版があったみたいです」「『機械学習を原動力とする高精度のエンタープライズ検索サービス』なのね」

参考: Kendra 試してみたよ。 - Qiita
参考: Amazon Kendra – アマゾン ウェブ サービス

「なるほど、マネージドで取りあえず突っ込めばいい感じに検索できるようになるみたい❤️」「もしファイルシステム検索もできるなら、BPSの寂しいNASでも突っ込んでみようかな😆」「お高いのかしら?💵」「30日間無料なのか」「まだ日本のリージョンは含まれてないし😇」「ありゃ😆」「AWSのストレージ限定かと思ったら、コネクタを追加すれば外部ストレージも検索できるらしい😋」

「Googleドライブを接続するぐらいならやってみてもいいかも、って思ったけどGoogleドライブ自体検索機能がありますし😆」「Googleドライブ何でも結果に出すぎる傾向ありますけど😆」「まあそこはKendraも似たような感じでしょうし☺️」

「この辺のパラメータで機械学習をチューニングできるみたい↓😋」「AWSではこういうフルマネージドのサービスはちょいちょい追加されますね🧐、使うケースはそんなになさそうですけど😆」


aws.amazon.com/jp/kendraより

クラウド/コンテナ/インフラ/Linux/Serverless

WSL 2正式リリース、wingetプレビューリリース(Publickeyより)


つっつきボイス:「WSL 2が一応正式にリリースされましたね🎉」「社内SlackのWindows板でも盛り上がってましたね😋」

「そうそう、2つめのwinget↓、もうちょっといい名前にならなかったのかなって😆」「微妙ですね😆」「まあ公式のCLIでやれるようになったのはいいことですけど😋」

「Microsoft Storeってかなり使いにくくありません?😆」「最新のアプリはStoreじゃなくて自分でダウンロードしてくれというソフトウェアもよくありますし😆」「開発者はMicrosoft Store使わなそうな印象😆」

参考: Microsoftストア - Wikipedia


以下はつっつき後のツイートです。

Kubernetesでよくあるミス10(StatusCode Weeklyより)


つっつきボイス:「IAMとかはマネージドなものを使う、はもう基本ですね🧐」「Affinityはあるとやりやすいけど、やりすぎない方がいいんでしょうし🤔」「requiredDuringSchedulingIgnoredDuringExecutionってすんごく長い😆」

参考: Node上へのPodのスケジューリング - Kubernetes — Affinityについて

externalTrafficPolicy: Clusterのあたりの図↓はKubernetesのアーキテクチャによく出てきますね😋」


同記事より

「くばねてのリソース設定って簡単に決められないのが面倒なので、デフォルトでやっちゃうというのは割とよくある😆」「記事にもBestEffortは使うなとか書いてますね👀」「といって絞りすぎても困っちゃいますし😅」「Kubernetesに『とりあえずこの設定にしておけばOK』というのがもうちょっとあればな〜😢」

「記事最後の『latestタグを使うな』しかわからなかった😢」「latestタグはDockerでもあるある😆」「Kubernetesを実際に運用している人向けの記事👍」


同記事見出しをまとめようと思ったのですが、Kubernetesがわかってないのでやめておきました🙇。

JavaScript

Promise、async/awaitのGIFアニメ


つっつきボイス:「はてブでバズってました」「たしかに非同期の話はアニメーションで説明する方がわかりやすいでしょうね😋」「マルチスレッドの話なんかも人類には想像することすら難しかったりしますし😆」「そうそう😆」「マルチスレッドはホワイトボードに書いても再現しきれませんし😆」「動画じゃないと捉えきれない😆」

CSS/HTML/フロントエンド/テスト/デザイン

スライド『ソフトウェアテストの歴史と近年の動向』


つっつきボイス:「社内Slackで知りました」「最近はテストにAIを使い始めてるみたい」「へぇ〜😳」「よくまとまった感じの資料👍」「偉い人を説得するときに便利そう😆」

「ところでこれ何のカンファレンスなんだろう?🤔」「HiSSTでググって出てくるものが何だか違う…😅」「スペルの同じ別の学会があったりして😆」「これか↓」「JaSSTソフトウェアテストシンポジウムだったとは😆」

参考: JaSSTソフトウェアテストシンポジウム-JaSST’18 Kansai

「そういえばスライドにある『ニューノーマル』っていう言葉最近ちらちら見かけますね😆」「そういえば😆」

参考: ニュー・ノーマル - Wikipedia

CSS/SVG/Canvasの使い分け


つっつきボイス:「グラフィックわからん😆」「この辺はBPS社内のアプリチームに詳しい人がいそう👀」「使い分けしようとすると割と考えちゃいますね😅」

「少なくともCSS/SVG/Canvasを組み合わせたくはない🤣」「それそれっ🤣」「たしかに🤣」「ピクセル操作したい場合はCanvasでやるしかないし」「アニメーションもパスに沿って動かすならSVGがいいし😋」

「この記事は視点と項目の順序がいいですね👍: どれについてもCanvasを最後の手段にしているのがエラい😆」「😆」「Canvasに不用意に手を出したりすると後で苦しんだりしますし😇」

参考: Scalable Vector Graphics - Wikipedia
参考: canvas要素 - Wikipedia

言語・ツール

最近のVS Code拡張


つっつきボイス:「そういえば最近VS Codeをdraw.ioエディタとしてインストールしましたヨ↓😋」「え、そんなのあるんですか!😳」

参考: VSCodeでDraw.ioが使えるようになったらしい! - Qiita

後で気づきましたが、draw.ioのドメイン名はdiagrams.netに引っ越したんですね😳。


diagrams.netより

参考: Blog - Open source diagramming is moving to diagrams.net, slowly — 引っ越し進行中とのこと

その他

一度も使わなかった


つっつきボイス:「親指シフトまだあったのか😆」「ついに一度も使わないままでした😅」「タイピングコンテストだと親指しふた〜が上位に多かった気がします💪」「へぇ〜😳」「今のうちに親指シフトキーボード買い込んでおけば後で高く売れたりして😆」

参考: 親指シフト - Wikipedia

番外

昔にもあった気が


つっつきボイス:「フィルムカメラをデジカメにするのか〜」「たしかに原理的には可能ですね」「フィルムが通る部分をCCDに置き換えられればやれそう😋」「光学系は昔のカメラでもしっかりしてるはずですし」「あとはシャッターとファインダーをうまく同期させられれば📸」

参考: I’m Back® 35 - An 50’s camera that takes digital photos? by Samuel Mello Medeiros — Kickstarter


後編は以上です。

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

週刊Railsウォッチ(20200518前編)スライド『令和時代のRails運用』、Ruby 3.0のキーワード引数変更リスケ、Action CableのCLIほか

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

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

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

StatusCode Weekly

statuscode_weekly_banner

Publickey

publickey_banner_captured

DB Weekly

db_weekly_banner

Rails: kaminari gemのセキュリティ修正1.2.1がリリース

$
0
0

いつもより前倒しの時間で公開します。

kaminari gemはRailsのページネーション機能で広く使われています。ページネーションリンク経由で外部からコードをページに注入できる可能性のある脆弱性が同gemで見つかり、修正されました。緊急度は「Medium(6.4)」ですが、kaminari 1.2.1より前のバージョンはすべてこの脆弱性の影響を受けます。

参考: クロスサイトスクリプティング - Wikipedia

kaminari gemをお使いの方は、上のadvisoryに沿ってkaminariを1.2.1にアップデートするか、記載の方法で回避しましょう。

この情報はruby-jp Slackの#securityで知りました。
* サイト: ruby-jp.slack.com | ruby-jp

関連記事

Rails 6/5とRubyのJSON gem向けセキュリティ修正がリリース

週刊Railsウォッチ(20200601前編)Active Recordに新機能「delegated typing」追加、RuboCopのデフォルト設定アンケートほか

$
0
0

こんにちは、hachi8833です。この記事↓にどよめきました👍🎉😂

⚓オープニングつっつき: 経産省のnpmモジュールが素晴らしい


つっつきボイス:「そうそう、経産省がnpmモジュール出してましたね」「個人的にめちゃ感動しました😂😂」「やだこれ素晴らしいじゃないですか!😍」「ようやく登場した感」「住所の正規化が公式の方法でできるのはいいですよね😋

「これまでは住所の正規化が民間でまちまちだったから、これが正しい住所だという一致チェックがなかなかできませんでしたし」「そうなんですよ😢」「これ使えば今滞りまくっている特別給付金の支払い作業もはかどるでしょう、きっと😆」「ちゃんと使われればですけど😆」「こういう公式のものがちゃんとあれば使う人は割といるはずですし☺

「個人的に1つ注文あります: 住所のふりがな表記とローマ字表記のフィールドもぜひ欲しいです〜😭」「あ〜たしかに」「ふりがなやローマ字表記がないと読みを正確に翻訳できなくて😢」「住所のふりがなって、まったく同じ地域で同じ漢字なのにふりがなが違うことすらありますよね」「それ!それなんです😭」「役所の書類上ではこう読むけど地元の人はこう読んでる、みたいに😆

「この手の情報で公式にかろうじて近いものといえば日本郵便のKEN_ALL.csvぐらいしかありませんでしたよね↓」「そうそう、頑張ってKEN_ALL.csv使ってました😆」「自分も😆」「郵政省の頃からですからKEN_ALL.csvの時代どんだけ長かったのかと🤣

参考: 郵便番号データダウンロード - 日本郵便

「そういえば経産省のnpmには郵便番号のフィールドがありませんね👀」「ほんとだ😳」「郵便番号データベースってすべての住所番地を網羅してるわけではなくて、『その他』とかで丸められてるところが多数あるんですよ😆」「そうそう、私書箱とかもありますし、管理が別なんでしょう」「まあ住所を正規化できれば郵便番号は推測できるかな」「住所からの郵便番号逆引きは可能ですね😋

「住所のような情報は国が公式に正規化するのが重要👍、民間でばらばらにやるのってマジで意味ありませんし」「まったくです」


豆知識: 原文に含まれる「人名」「地名・住所(ビル名や店名)」「固有名詞(企業名や商品名など)」「動植物の名前」の正確な翻訳は、汎用の機械翻訳が極めて苦手とする作業です😭。例: honestyは「ギンセン草」を表すことがあります(「正直」とは限りません)。

住所といえば、以下の文庫『地名の謎』『番地の謎』は住所や番地についての研究として自分の知る限り最強です。住所改定や平成の大合併など歴史や由来も網羅していて、熱量の高さが素晴らしいです❤。住所を扱うときに一読をおすすめします。Kindleで読みたい…😢

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

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

以下のコミットリストのChangelog差分を中心に見繕いました。

⚓新機能: Active Recordにdelegated_typeが追加

DHH自らのプルリクです。説明がかなり長いですが、いずれRailsガイドに載ることを期待します。delegated typingのいい訳語が思いつかないのでそのままにしてあります。

リレーショナルデータベースのテーブルにクラス階層を対応付ける方法はいろいろある。たとえばActive Recordは純粋な抽象クラスを提供してスーパークラスでは属性を永続化しないようにしているし、STI(Single-Table Inheritance)では階層の全レベルの全属性が単一テーブルで表現される。どちらにもそれぞれ使い所はあるが、どちらにも欠点がないわけではない。
純粋な抽象クラスの問題点は、あらゆる具象サブクラスが各テーブルですべての共有属性そのものを永続化しなければならない点(「クラス-テーブル継承」とも呼ばれる)。この場合、階層をまたがるクエリを書くのが難しい。たとえば以下のクラス階層があるとしよう。

Entry < ApplicationRecord
Message < Entry
Comment < Entry

MessageのレコードとCommentのレコードの両方を持つフィードを表示して、楽にページネーションするにはどうすればよいだろうか?これは無理。メッセージの背後にはmessagesテーブルがあり、コメントの背後にはcommentsテーブルがあるが、一貫したOFFSET/LIMITスキームを使って両方のテーブルから一発でデータを取り出すことはできない。

STIはページネーションの問題を回避できるが、今度は全サブクラスにあるすべての属性を単一の巨大なテーブルに押し込めざるを得なくなる。クラスがどれほど違っていようとそうなる。MessageにsubjectがあるがCommentにはない場合、Commentでもsubjectが使えてしまう!つまりSTIが最適なのは、サブクラス同士やその属性同士の違いが極力少ない場合ということになる。

しかしここで第3の方法がある: それが「delegated type」だ。このアプローチでは、「スーパークラス」は独自のテーブルを持つ具象クラスであり、すべての「サブクラス」で共有されるすべてのスーパークラスの属性がそこに保存される。そして各サブクラスも、サブクラスの実装に必要な追加属性用に独自のテーブルを持つ。これはDjangoの「multi-table inheritance」と呼ばれるものに似ているが、実際にはこのアプローチで使うのは継承ではなく、階層を形成して責務を共有するのに「委譲(delegation)」を用いる。

「delegated type」でentry/message/commentを実装した例を見てみよう↓。

# Schema: entries[ id, account_id, creator_id, created_at, updated_at, entryable_type, entryable_id ]
class Entry < ApplicationRecord
  belongs_to :account
  belongs_to :creator
  delegated_type :entryable, types: %w[ Message Comment ]
end

module Entryable
  extend ActiveSupport::Concern

  included do
    has_one :entry, as: :entryable, touch: true
  end
end

# Schema: messages[ id, subject ]
class Message < ApplicationRecord
  include Entryable
  has_rich_text :content
end

# Schema: comments[ id, content ]
class Comment < ApplicationRecord
  include Entryable
end

参考: Module#included (Ruby 2.7.0 リファレンスマニュアル)

ご覧のとおり、MessageCommentも孤立していない。2つのクラスの重要なメタデータは「Entryスーパークラス」にあるが、Entryは特にクエリ送信能力という点では絶対的に独立可能になっている。これでAccount.entries.order(created_at: :desc).limit(50)みたいな書き方が楽にやれる。

commentsとmessagesをまとめて表示するときに欲しいのは、まさしくこれだ。このentry自身は、その「delegated type」として以下のように簡単にレンダリングできるようになる。

# entries/_entry.html.erb
<%= render "entries/entryables/#{entry.entryable_name}", entry: entry %>
# entries/entryables/_message.html.erb
<div class="message">
  Posted on <%= entry.created_at %> by <%= entry.creator.name %>: <%= entry.message.content %>
</div>
# entries/entryables/_comment.html.erb
<div class="comment">
  <%= entry.creator.name %> said: <%= entry.comment.content %>
</div>

concernsとコントローラで振る舞いを共有する

このEntryスーパークラスは、messagesやcommentsの両方に適用する共有ロジックの完璧な置き場も提供してくれるし、主に共有属性として振る舞う。以下で考えてみよう。

class Entry < ApplicationRecord
  include Eventable, Forwardable, Redeliverable
end

上のように書くことで、entriesでForwardsControllerForwardsControllerのように振る舞うコントローラが手に入る。しかもmessagesとcommentsの両方で共有する機能も提供できる。

新規レコードの作成

「delegated typing」を用いてレコードを1件作成するには、次のようにdelegatorとdelegeteeを同時に作成する。

Entry.create! message: Comment.new(content: "Hello!"), creator: Current.user

もっと複雑なコンポジションが必要な場合や、依存バリデーションの実行が必要な場合は、factoryメソッドかクラスを構築して、その複雑なニーズの面倒を見てやるべきだ。これは以下のようにシンプルにやれる。

class Entry < ApplicationRecord
  def self.create_with_comment(content, creator: Current.user)
    create! message: Comment.new(content: content), creator: creator
  end
end

delegationをさらに追加する

「delegated type」は、背後のクラスで何が呼ばれているかという問いに単に答えるだけのものであってはならない。実は、それはほとんどの場合アンチパターンになる。この階層を構築するのは、ポリモーフィズムのメリットを享受するのが目的である。シンプルなコード例を以下に示す。

class Entry < ApplicationRecord
  delegated_type :entryable, types: %w[ Message Comment ]
  delegates :title, to: :entryable
end

class Message < ApplicationRecord
  def title
    subject
  end
end

class Comment < ApplicationRecord
  def title
    content.truncate(20)
  end
end

これでentriesをいくつでもリストしてEntry#titleを呼び出せるようになり、ポリモーフィズムが答えを提供してくれるようになる。
同コミットより大意


つっつきボイス:「時間が取れたので、この長いプルリクメッセージをガガガッと大急ぎで訳してみました(訳さないと理解が遅くて😅)」「delegated typing?」「説明長い〜」

「STI(Single-Table Inheritance)のつらみを避けるためにDHHが実装したということみたい」「普通のモジュールインクルードとどう違うのかな?👀

参考: STI — Active Record の関連付け - Railsガイド

「どれどれ、以下みたいな設計でMessageとCommentの両方にあるものをリストとして引っ張ってくるときは、通常ならEntryに対してクエリを投げるけど、当然ながらMessageやCommentのメソッドは呼び出しづらくなるし、STIだと説明にもあるように全サブクラスの属性が1個のテーブルに入ってしまってテーブルがでかくなると」「DHHも『どちらにも使い所はあるけどそれぞれ欠点がある』って書いてる😆

# 同PRより
Entry < ApplicationRecord
Message < Entry
Comment < Entry

「それがdelegated typingだとそれぞれが独自テーブルを持つからSTIの形にはならなくなるのか」「DHHは『第3の方法』と呼んでますね」「第3😆」「Djangoのmulti-table inheritanceにも似てるみたい」

参考: Multi-table inheritance — Models | Django documentation | Django

「うん、サンプルコード↓を見るとEntryもMessageもCommentも単にApplicationRecordを継承してる: なるほどだんだん見えてきた😋」「delegate typingでは、共通のカラムはEntryにあるけど差分のテーブルはMessageやCommentにあって、それらがデータベース上Entryへの参照を持っていると」「EntryはEntryableモジュールを取ってくることでワンクッション置いてるのね☺

# 同PRより
# Schema: entries[ id, account_id, creator_id, created_at, updated_at, entryable_type, entryable_id ]
class Entry < ApplicationRecord
  belongs_to :account
  belongs_to :creator
  delegated_type :entryable, types: %w[ Message Comment ]
end

module Entryable
  extend ActiveSupport::Concern

  included do
    has_one :entry, as: :entryable, touch: true
  end
end

# Schema: messages[ id, subject ]
class Message < ApplicationRecord
  include Entryable
  has_rich_text :content
end

# Schema: comments[ id, content ]
class Comment < ApplicationRecord
  include Entryable
end

「これもうマージされてます?」「されました😋」「おぉ〜、じゃ次のRailsをバージョンアップしたら入ってくるのかな?😋」「masterブランチにマージされただけだとどのバージョンに入るかは決まりませんけど、でかい機能追加なのでRails 6.1あたりになりそうですね☺」「これはいい機能👍

「DHHの説明長いので、ウォッチ公開した後で切り取って別記事にしようと思います: Railsガイドに追加されるまでのつなぎということで」「お〜いいんじゃないでしょうか☺

⚓STIよもやま話

「delegated typing、STIキライな人によさそう😆」「この書き方もっと前からできててもよかったかも」「公式にこれが入ったことが大きいですね😂」「このプルリクに『いいね👍』が大量に付いてました」「STIキライな人多いので😆

「もちろんSTIでも問題なくやれる場合はあります: STIで一番問題になりやすいのは、まさにこのサンプルコードでやっているみたいに、リッチテキストのようなむちゃくちゃ大きいコンテンツが入ってくるカラムをSTIで増やすと行サイズがバカでかくなること😇」「そうですね😅」「ほとんどのカラムが共通カラムで、ごく一部のカラムだけが特定のテーブル用に追加される形(integerカラムをちょっと足す程度)なら、STIでも大したことにはなりませんし🧐」「ふむふむ」「STIで特定のテーブルにしかないカラムが長大になると(text型とか)、データベース上の無駄が多すぎるんですよ😢

「そういう長大なカラムを使う場合は明らかにこのdelegated typingの方がいいでしょうね❤: データモデル的にもキレイですし、オブジェクト指向プログラミングでよく言われる『なるべく委譲しよう』的な考え方ですし🧐」「おぉ😍」「サブクラスを増やして継承するより先にデリゲートを検討しましょう、コンポジションしましょうというヤツ☺」「なるほど!」

参考: Rubyによるデザインパターン5原則 - Qiita

「もちろんどんな場合でもdelegated typingがいいわけではないと思います: STIの方が速くなることも普通にありますので🚀」「設計の選択肢が増えるのはいいこと👍

⚓スキーマにENGINE=InnoDBをデフォルトで含めないようになった

スキーマをよりagnostic(=決め打ちしない)にするため、デフォルトのエンジンENGINE=InnoDBをダンプしないようにした。
Changelogより

# Changelogより
# before
create_table "accounts", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
end

# after
create_table "accounts", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
end

つっつきボイス:「@kamipoさんの改修です」「そうそう、昔はMySQLでENGINE=InnoDB ROW_FORMAT=DYNAMICしないとutf8mb4でインデックス作れなかった、なつかし〜😆」「今のMySQLではENGINE=InnoDBは不要だから出さなくなるのもごもっとも」「時代が変わったということで☺

MySQLのencodingをutf8からutfmb4に変更して寿司ビール問題に対応する

「MyISAMを使い続ける人、今もたまに見かけますよ😆」「いるんですか?😆」「ソシャゲ界隈あたりだったかな?、たぶん枯れているものしか使わないポリシーなのかなも🤔」「MyISAMはログDBに向いてるって昔教わったことあります」「MyISAMで参照が発生しないカラムサイズにうまく押し込められればいいでしょうね: MyISAMは長年チューニングされてますし」

参考: 漢(オトコ)のコンピュータ道: MyISAMからInnoDBへ切り替えるときの注意点

⚓キャッシュフラグメントキーに関連テンプレートをすべて含めるようになった

まとめ

Railsビューは、アクションに対応するテンプレート(show.html.erbなど)のレンダリング時に(レイアウトやパーシャルなどの)複数のテンプレートにも依存することがある。現在レンダリング中のビューファイルをトラッキングしてキャッシュフラグメントキーで使うテンプレートツリーのダイジェストを生成するために、Action Viewはスタックを使っているが、スタックのトップには@current_template変数経由でアクセスされる(1581cabで導入)。
以下のテンプレートで考える:

<!-- home.html.erb -->
<%= render layout: "wrapper" do %>
  <%= cache "foo" %>
    HOME
  <%= end %>
<%= end %>

上のrenderヘルパーに渡されるブロック内の@current_templateは、(home.html.erbではなく)wrapper.html.erbテンプレートに対応する。そしてキャッシュフラグメントキーで使われるテンプレートツリーダイジェストの生成にはwrapper.html.erbが使われるので、home.html.erbが変更されてもキャッシュフラグメントの有効期限が期限切れしなくなってしまう。さらに、2番目のテンプレートでwrapper.html.erbレイアウトが使われ、キーが同じキャッシュフラグメントを含んでいると、2つのテンプレートのキャッシュフラグメントキーが同一になってしまい、あるビューのキャッシュの中身が別のビューにお漏らししてしまう(#38984)。
このプルリクは、renderヘルパーにブロックを与えてテンプレートをレイアウトとしてレンダリングした場合にスタックにテンプレートを追加する。これにより、@current_templateが現在レンダリング中のビューファイルに正しく対応するようになり、キャッシュフラグメントダイジェストが一意かつ有効期限も正しく期限切れになる。さらに、CacheHelperで見つけたvirtual_pathキーワード引数を削除し、そこに関数が置かれることのないようにした。(@current_templateの導入後、virtual pathは単独の変数としてではなく、@current_template.virtual_path経由でアクセスされるようになっていた)。最後に、@virtual_pathインスタンス変数をコードベースから完全に削除し、そこへの参照をすべて@current_template.virtual_pathに置き換えた。

その他の変更点

actionview/test/template/render_test.rbで使われていたコントローラーキャッシュストアがnilだったのを:memory_storeが設定されるようにした(このプルリクで修正された「失敗するテスト」を提供するために必要)。

その他の情報

このプルリクは2つのコミットに分かれている。最初のコミットは、テストがすべて通ってしまう#38984のバグを最小限の形で修正するのに必要。次のコミットは、余分な@virtual_path変数をコードベースから完全に取り去って@current_template.virtual_pathに置き換えるリファクタリング。2つに分けたのはレビューしやすくするためで、#38984の修正で変更された点や無関係なリファクタリングを追いやすくする目的だが、マージの前に1つのコミットにrebaseしてもらえるとうれしい。
同コミットより大意


つっつきボイス:「これも説明長かったので同じくガガッと翻訳してみました」「『キーが同じキャッシュフラグメントを含んでいると2つのテンプレートのキャッシュフラグメントキーが同一になってしまう』ようになってたとは😳」「自分はこんなにキャッシュフラグメントキーを細かく指定したことがほぼないのでこれ踏んだことなかった😆」「キャッシュフラグメントキー、昔使ってみたけどややこしいことになりました😅

<!-- 同PRより -->
<!-- home.html.erb -->
<%= render layout: "wrapper" do %>
  <%= cache "foo" %>
    HOME
  <%= end %>
<%= end %>

<%= cache "foo" %>の部分って普通はActive Recordとかのオブジェクトを指定することが多いので、そういうものなら同一のものを指していても問題ないんですけど、サンプルコードみたいに固定値を指定するとこういうことが起きちゃってたんですね☺」「キャッシュフラグメントキーなつかしいから後でちょっと試してみようかな〜😋

参考: 1.3 フラグメントキャッシュ — Rails のキャッシュ機構 - Railsガイド

⚓groupを重複フィールドで集約することが非推奨化

クエリのannotateで付けたメッセージの重複も非推奨化されています。


つっつきボイス:「重複例ではキーが[1, 1]みたいに配列になってますね↓」

accounts = Account.group(:firm_id)

# グループ化のフィールドが重複する(非推奨化)
accounts.merge(accounts.where.not(credit_limit: nil)).sum(:credit_limit)
# => {
#   [1, 1] => 50,
#   [2, 2] => 60
# }

# グループ化するフィールドの重複解消には今後`uniq!(:group)`を使うこと
accounts.merge(accounts.where.not(credit_limit: nil)).uniq!(:group).sum(:credit_limit)
# => {
#   1 => 50,
#   2 => 60
# }

「新しく追加されたuniq!(:group)↓で重複解消して欲しいと」「この方がごもっとも😋」「これを追加したから重複を非推奨化したという流れなんでしょうね☺

# 同PRより
    # Deduplicate multiple values.
    def uniq!(name)
      if values = @values[name]
        values.uniq! if values.is_a?(Array) && !values.empty?
      end
      self
    end

uniq!(:group)って書き方、ぱっと思いつけるだろうか?😆」「ぱっと思いつけなさそうな気がします😅」「ともあれこういう正しい書き方が提供されたところに意味がありますね👍

⚓キーワード引数関連の改修多数


つっつきボイス:「@kamipoさんのキーワード引数関連改修です」「みんな大好きキーワード引数😋

# activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb#L376
-     def column(name, type, **options)
+     def column(name, type, index: nil, **options)
        name = name.to_s
        type = type.to_sym if type

        if @columns_hash[name]
          if @columns_hash[name].primary_key?
            raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
          else
            raise ArgumentError, "you can't define an already defined column '#{name}'."
          end
        end

-       index_options = options.delete(:index)
-       index(name, index_options.is_a?(Hash) ? index_options : {}) if index_options
        @columns_hash[name] = new_column_definition(name, type, **options)
+
+       if index
+         index_options = index.is_a?(Hash) ? index : {}
+         index(name, **index_options)
+       end
+
        self
      end

「お、column()index:キーワード引数が追加されたのかと思ったら、index:オプションが**optionsから取り出されてキーワード引数になったということか」「なるほど!」「今まではindex:オプションをoptions = {}で受けていた」「index:の指定は必須かなと思ったら、デフォルト値がnilだからindex:キーワードは省略できるのね😋」「options = {}というオールドスタイルな書き方も**optionsに置き換えられた☺」「options = {}が置き換わっていくのうれしい😂

Railsフレームワークで多用される「options = {} 」引数は軽々しく真似しない方がいいという話

⚓Rails

⚓「RuboCopのデフォルト設定」に関するアンケート結果

ruby-jp Slackで知りました。最新のRuby Weeklyでもトップに上がっていました。


つっつきボイス:「ruby-jp Slackでも話題になってました🎉」「そうそう、ちょうどさっきkoicさんがLayoutLineLengthのデフォルトを120文字に変えた記事↓を見てマジ感動しましたし😂

⚓1行の最大文字数はいくつがいいか

「line lengthは120文字欲しい!」「これはオレたちの待ち望んでいた改修😂」「koicさん記事の『いつかこの変更をするのが目標のひとつだった』、わかる😆」「80文字のつらさは異常」「80はないわ〜😆」「それどんな言語?って思いますし😆」「コンソールが80文字x25行だったのって相当昔ですよね😆」「まあ1行があまりにもでかいのも考えものなんですけど、ビジネスロジックを真面目に書いているとせめて120文字は欲しい😭」「メソッドチェインのたびに改行するのも何だかな〜ですし😆

⚓文字列リテラルは""で囲むか''で囲むか

「クォートのスタイルはどんな結果になったかな?」「シングルクォート派は58%」「Rubyのシングルクォート' 'とダブルクォート" "は意味が違うので、自分は両方使い分けるでいいと思いますし😋」「同感で〜す😋」「悩むぐらいならRuboCopに直させればいいことですし👮🏼‍♀️

参考: 文字列リテラル — リテラル (Ruby 2.7.0 リファレンスマニュアル)

Rubyの式展開(string interpolation)についてまとめ: `#{}`、`%`、Railsの`?`

⚓コレクションリテラルのケツカンマはどのスタイルがいいか

# 同記事より
# ケツカンマなし
{ one: 1,
  two: 2 }

# 複数行リテラルの場合のみケツカンマ
{ one: 1,
  two: 2, }

{ one: 1, two: 2 }

# 常にケツカンマ
{ one: 1,
  two: 2, }

{ one: 1, two: 2, }

「trailing comma付けない人多いんだ😳」「付けたくない気持ちわかります😅」「まあこれもRuboCopにお任せで😆

参考: 末尾のカンマ - JavaScript | MDN

⚓* andorは使うか使わないか

「優先順位の低いand演算子やor演算子は以前から使わない方がいいって言われてますし」「記事にもありますけど、例外はRailsコントローラのリダイレクト and returnみたいなフローコントロールのとき」「Railsではイディオムになってますね」「そうそう、Railsでこの書き方は認められてますし、自分もあっていいと思いますけど、なくすのはそれはそれでありかもって思います😆

参考: 演算子式 (Ruby 2.7.0 リファレンスマニュアル)
参考: 2.2.14 二重レンダリングエラーを避ける — レイアウトとレンダリング - Railsガイド

⚓!!は使うか使わないか

!!は好きでもキライでもないかな〜☺」「ダブルビックリ😆

Rubyの否定演算子2つ重ね「!!」(double-bang)でtrue/falseを返す

⚓ハッシュリテラル{}のすぐ内側にスペースを置くか置かないか

「ハッシュリテラルのすぐ内側にはスペース置きたい派({ key: :value })」「私もスペースないと窮屈な感じします」「ある方が見やすいけどたまに書き忘れます😆

⚓Kernelメソッドの引数を()で囲むか囲まないか

Kernelメソッドの引数に()付けるか問題」「自分は付けません😆」「exitなら付けるかもだけどputsには付けないかな🤔」「自分はあんまり意識しませんし、そもそもKernelメソッドかどうかも気にしてない😆

参考: module Kernel (Ruby 2.7.0 リファレンスマニュアル)

⚓Metrics copは便利か

「Metrics copキライな人が多いから聞いてみたとありますね」「まあうるさいのはたしか」「でもないよりはある方がいいですし😋」「うるさかったらRuboCopの設定変えれば済みますし」「記事の下に書いてあるこのyaml設定↓でMetricsをまとめて殺せるとありますね」「Metricsを全部殺したいと思うことってあんまりなさそうですけど😆」「たしかに😆

Metrics:
  Enabled: false

参考: Metrics Cops - RuboCop: The Ruby Linter that Serves and Protects

⚓RuboCopを素の設定で使ってどのぐらい幸せになれるか

「素のRuboCop設定でどのぐらい幸せか問題」「これはもう人それぞれでしょう」「単なるhappyが一番多いけど、very happyとまではいかないということ?😆」「デフォルトでいいと思ってる人が大多数なんでしょうね😊」「デフォルトだとちょっとな〜😅」「やっぱりデフォルトのスタイルがあることが大事だと思います: スタイルにデフォルトがないと迷ったときに不便😢


「そういえばRailsがジェネレートするRubyコードって素のRuboCopに怒られますよね😆」「そうそうめっちゃ怒られる、オレが書いたんじゃないのに😆」「RailsジェネレーターのRubyコードはもっと素のRuboCopに寄せてもいいと思います☺

⚓delete_in_batches: Active Recordのバルク削除(Ruby Weeklyより)

# 同リポジトリより
# Slow
Tweet.where(user_id: 1).delete_all
# DELETE FROM tweets WHERE user_id = 1

# Faster
Tweet.where(user_id: 1).in_batches(of: 10000).delete_all
# SELECT tweets.id FROM tweets WHERE user_id = 1 ORDER BY id LIMIT 1000
# DELETE FROM tweets WHERE user_id = 1 AND id IN (1, 2, 3, ...)
# ...

# Fastest
Tweet.where(user_id: 1).delete_in_batches
# DELETE FROM tweets WHERE id IN (SELECT id FROM tweets WHERE user_id = 1 LIMIT 10000)
# DELETE FROM tweets WHERE id IN (SELECT id FROM tweets WHERE user_id = 1 LIMIT 10000)
# ...

つっつきボイス:「ankaneさんのARバルク削除gemだそうです」「こんなのあるんですね😳

「fastestつおい😆」「これはロックを小さくしてDELETEする感じかな」「おぉ?」「slowのTweet.where(user_id: 1).delete_allだとロックがかかって遅くなるけど、fasterだとDELETE FROM tweets WHERE user_id = 1 AND id IN (1, 2, 3, ...)の中にトランザクションが挟まれるようになって速くなるんだろうと想像してます🚀: DELETEする対象が大量にある場合に速くするということなんでしょう☺」「よさそう😍」「Railsのfind_in_batches↓と対をなす感じの名前ですね」

⚓TRUNCATEにご注意

「ところでREADMEに『全部消すならTRUNCATEが最速だぞ』って書いてあるんですけど🤣」「それは危険な考え方🤣」「そりゃ爆速でしょうけど🤣」「これをギャグだとわかって読める人が使うべきでしょう🤣」「READMEに書くと本気にする人出そうなんですけど🤣」「いそうでコワイ🤣」「喜び勇んでTRUNCATE書いてコードレビューではじかれたりして🤣

# 同リポジトリより
ActiveRecord::Base.connection.execute("TRUNCATE tweets")

参考: TRUNCATE と DELETE の違い - オラクル・Oracleをマスターするための基本と仕組み
参考: PostgreSQLでTRUNCATEをROLLBACKした際の内部の挙動を追いかけてみた | Developers.IO

⚓命名の基準


つっつきボイス:「jnchitoさんのQiita記事です」「どこかでちらっと見かけたかも」「そもそも基準ってあるのかと思ったら、Stack Overflowレベルで一応それっぽい回答はあるらしい😆」「実は前に自分のブログでこのネタ書きました😋

「ActionだっけActiveだっけって割と迷いますよね」「迷います😆」「モデル層に属するコンポーネントはActiveという考え方は割と腑に落ちます😋」「Action Viewはビューに近いからActionになったとは😳

「ところでActive StorageとActive Supportを省略すると、どちらもASになっちゃう😆」「Action MailerとActive ModelもAMですし😆」「惜しい😆」「この辺に一意な省略形があるといいんですけどね〜」

⚓Rails製ゲームサイトの応答速度を5倍改善(Ruby Weeklyより)

Evil Martians記事です。


つっつきボイス:「Evil Martiansが手掛けているらしいMayhemとかいうゲームコミュニティプラットフォームのレスポンスを改善したということらしいです」「Mayhem見たことない😆」「自分もゲーム音痴なので詳しくはわかりません😅」「全然ちゃんと見てないけど、ゲームのロビーマッチングをやってるサービスなのかしら?🤔」「サイトにもlobby nameってありますね」

「記事ではGraphQLや、以前ウォッチでちょっとだけ取り上げた同社のAction Policyを使ってるようです(ウォッチ20180420)」「GraphQLになるのわかる😆」「Action Policyはauthorizationのgemか」「それをGraphQLと使ってるっぽい🤔

「そしてレスポンスが5倍改善したと↓」「開発方法がちょっと特殊になったので開発者を集めて特訓したそうです🏋🏻‍♀️」「Evil Martians、ゲーム系もやってるのね☺


同記事より


「Mayhemって『暴動』みたいな意味らしくて、同名のヘビメタバンドもあるのでややこしい😅」「Mayhemってギリシャ語とかラテン語あたりの神様っぽい雰囲気のスペルかな?⛩」「そういえばどことなくヘブライ語っぽい響きかも?🇮🇱

後で調べるとmayhemは古フランス語由来のようです。

参考: mayhem | Origin and meaning of mayhem by Online Etymology Dictionary


前編は以上です。

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

週刊Railsウォッチ(20200526後編)Rubyでよくやるスレッドバグ、Kubernetesでよくあるミス10、CSS/SVG/Canvasの使い分けほか

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

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

Rails公式ニュース

Ruby Weekly


週刊Railsウォッチ(20200602後編)JSONストリームパーサーyajl-ruby、ruby-buildとopenssl、GoogleのCloud SQL、Rubyと機械学習ほか

$
0
0

こんにちは、hachi8833です。例のRuby 3.0キーワード引数変更のつらみをmameさんがまとめてくださいました🙇

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

⚓Ruby

⚓mrubyでシェルを作った話


つっつきボイス:「ruby-jp Slackでも反響を呼んでました」「お〜mrubyでシェル作ってる😋

「シェルづくりって、昔から気合の入ったCS学部なんかでLinuxを理解する第一歩でやったりしますね💪」「そういうのやってたんだ😆」「エディタを自力で作るのもそんなノリで😋」「オレのための最強ツール😆」「コンパイラを作るためにコンパイラのコンパイラを作るとか😆」「それやってる人いました😆

⚓ruby-buildはopensslを毎回取得してRubyをコンパイルするようになっていた

ついでながら、今年4月に報じられたOpenSSLのセキュリティアップデート 1.1.1gは既に公開されています。


つっつきボイス:「これは別の記事を書いているときに気づいたんですけど、ruby-buildがopensslを毎回取得してRubyをコンパイルするようになってたのを今になって知りました」「え、そうなんだ😳

参考: OpenSSL - Wikipedia

「ruby-buildで新しいopensslを取得するのは最近のRubyだけですよね?」「たぶんそうだと思います😅」「惜しい、昔のバージョンのRubyでもこうして欲しいんですけど」「おぉ?」「たまに古いRubyをビルドしないといけなくなることがあって、この間もRuby 2.1をビルドしないといけなかったんですよ😆」「2.1何に使うんですか?😆」「今でも2.1で動いているところがありましてですね😅

「でopensslのバージョンが古くてビルドできなくてマジ困りました😭」「あ〜!」「それはつらそう😢

「すっごく頑張って1個だけインスタンスつくりましたよもう😤」「お疲れさまです😅」「gemも古いのしか動かなかったりするから絶妙なバランスの上に成り立っている⚖: Gemfile.lockを頼りにバイナリサーチして、どこまでバージョンを下げれば動くかとか1つずつ調べましたし😭」「人間git bisectみたい😅」「マジつらかった〜」

参考: Git - git-bisect Documentation

「まあでもopensslの取得をビルドに含めるのは正しいと思います👍: もうシステムのopensslに依存するのはよくない💀」「たしかに」「昔のRubyでもやってくれればな〜😢」「古いRuby使うなと言われればそれまでですけど😆


後でrbenvで2.4.9と2.7.0のビルドの出だしだけチェックしたところ、Ruby 2.4.9では取得こそしているものの最新のopensslではありませんでした。古いRubyで最新のopensslを使うには手動で指定が必要ということですね。

rbenv install 2.4.9
Downloading openssl-1.1.1d.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/1e3a91bc1f9dfce01af26026f856e064eab4c8ee0a8f457b5ae30b40b8b711f2
Installing openssl-1.1.1d...
rbenv install 2.7.0
Downloading openssl-1.1.1g.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46
Installing openssl-1.1.1g...

⚓yajl-ruby: JSONストリームパーサーYAJLへのバインディング


つっつきボイス:「やじる?」「Yet Another JSON Libraryですって😳」「SAXっぽくアクセスできるJSONパーサーライブラリらしい」「yajlのパーサーはイベントドリブンなのね」「これはへ〜という感じ」

参考: Simple API for XML(SAX) - Wikipedia

# 同リポジトリより
require 'yajl'

# ファイルIOの場合
json = File.new('test.json', 'r')
parser = Yajl::Parser.new
hash = parser.parse(json)

# またはたぶんStringIOの場合
json = StringIO.new("...some JSON...")
parser = Yajl::Parser.new
hash = parser.parse(json)
# またはたぶんSTDINの場合
cat someJsonFile.json | ruby -ryajl -e "puts Yajl::Parser.parse(STDIN).inspect"

「自分はXMLの時代しか知りませんけど、こういうパーサーがイベントドリブンかどうかで速度は結構変わりますね🧐」「おぉ」「READMEにも圧縮済みストリームをパースできるとある↓」

  • IOストリーム(ファイルやソケットなど)やStringを入出力としてJSONのパースやエンコーディングを直接行う。Bzip2やGzipやDeflateについて圧縮済みストリームのパースやエンコーディングをサポートする。
  • 複数のJSONオブジェクトから文字列への(およびその逆の)パースやエンコーディングを連続実行でする。
  • JSON gemのAPIと互換: JSON gemをyajl-rubyで置き換えられる
  • 基本的なHTTPクライアント(現時点ではGETのみサポート)で、受け取ったものを直接JSONにパースしてレスポンスのbodyにする
  • JSON.generateの3.5倍高速
  • JSON.parseの1.9倍高速
  • YAML.loadの4.5倍高速
  • YAML.dumpの377.5倍高速
  • Marshal.loadの1.5倍高速
  • Marshal.dumpの2倍高速
    同READMEより

「おそらくですけど、ローカルに保存できないぐらいバカでかいJSONでもパースできると言いたいのかも💪」「ユースケースによってはこういうイベントドリブンなパーサーでないとメモリが足りなくなることもあるでしょうし☺」「なるほど、すごくでかいJSONで使う感じですか」「あるいは以下のHTTPストリーム処理↓のように、個別の処理時間は短いけどデータの転送に時間がかかるものをパースしたいときとか」「なるほど!」

# 同リポジトリより
require 'uri'
require 'yajl/http_stream'

url = URI.parse("http://search.twitter.com/search.json?q=engineyard")
results = Yajl::HttpStream.get(url)

「どこまでやれるかですけど、イベントドリブンならJSONを最終行まで取れない可能性があっても原理的には一応やれるでしょうね🧐」「JSONがちぎれる可能性のある場合、と」「yajl-rubyが速度のほかにメモリ使用量の少なさを謳ってるのもイベントドリブンならではでしょう☺

「JSONをフィルタ的にパースしたいなら、yajl-rubyのようなのが速いでしょうね👍: 条件を変えながら同じJSONを何度もトラバースするようなユースケースでは違ってくるでしょうけど」

「ちなみに、以前ウォッチでも取り上げたVCR gemの6.0リリースノートでこのyajl-rubyに切り替えたという記述を見てこのgemを知りました(ウォッチ20200225)」「なるほど、たしかにVCR gemのようにJSONを上から高速になめたいユースケースならSAX的なパーサーの方が速いでしょうね😋

参考: vcr/vcr: Record your test suite’s HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.

⚓最近のRuby機械学習

pycallでやっているそうです。ツイートにあるGymというのもよさそうですね。


つっつきボイス:「倒れそうで倒れない棒」「何回ぐらい学習したのかな?📘」「pycallでやってるということは最終的にPythonですし😆」「そこはRubyじゃないと😆

⚓その他Ruby


つっつきボイス:「Rustコードをコンパイルして使うgemはたしかにやれますね🦀」「これはやる人出るだろうなと思いました😋

参考: Rust (プログラミング言語) - Wikipedia

⚓DB

⚓PostgreSQLのEXPLAIN ANALIZEはたまに正しくないかも(Postgres Weeklyより)


つっつきボイス:「へぇ〜、EXPLAIN ANALYZEの方がやたらスコアがでかくなるときがあると↓」「8倍遅いなんてことあるんだ😳


同記事より

「単なるEXPLAINはクエリプランナーを走らせてクエリプランを取るだけで、EXPLAIN ANALYZEは実際にSELECT文を実行するはず🤔」「ぽすぐれはそうなんですね(MySQL派なので😆)」

「この辺の記事↓を見てもそうなってる: EXPLAIN ANALIZEは実際にクエリを実行して実行時間も取ると」「なるほど理解しました」「だからEXPLAIN ANALYZEの方がむしろ本番に近いはずですけど🤔

参考: PostgreSQLの実行計画について調べてみた | キャスレーコンサルティング株式会社

「記事の真ん中辺で引用されてるドキュメントによると、EXPLAIN ANALYZEにはネットワーク転送とI/O変換のコストが含まれてないとありますね😳」「あ〜そういうことね!」「そこが含まれてなかったら違い出そう」「たしかに」「これ知らなかったら気づかないな〜」

「それにしてもそんなことがあるのかしら😳」「AWS EC2インスタンスのr4.largeでも起きるらしいと記事にある」「I/Oバウンドな操作を行うとこういうことが起こるのかも🤔

参考: インスタンスタイプ - Amazon EC2 | AWS

「手当たりしだいにEXPLAIN ANALYZEしてみて『こんなはずじゃないのに』みたいな結果が出たらこういうことが起こっている可能性があると」「これを知っておくのは有用ですね👍」「8倍遅くなるなんて、なかなか予測付きませんけど😅


見出しより:

EXPLAIN ANALYZEにより測定される実行時間が同じ問い合わせを普通に実行する場合と大きくそれる可能性がある、2つの重大な点があります。 1つ目は、出力行がクライアントに配信されませんので、ネットワーク転送コストとI/O変換に関するコストが含まれないことです。 2つ目は、EXPLAIN ANALYZEによって加わる測定オーバーヘッドが大きくなることが、特にgettimeofday()オペレーティングシステムコールが低速なマシンであり得ることです。 pg_test_timingを用いて、使用中のシステムの時間測定にかかるオーバーヘッドを測ることができます。
postgresql.jp 14.1. EXPLAINの利用より

  • クエリ実行の「Volcanoモデル
  • あまりうれしくないニュース: クエリと実行ノード次第ではクエリ最適化でEXPLAIN ANALYZEを信用できなくなる
  • もう少し信頼できるようになるには
  • 追加の考察

⚓GoogleのCloud SQLを使ってみて


つっつきボイス:「最近つっつきに参加いただいている、すろっくさんことsrockstyleさんの記事をかたじけなくも貼らせていただきました🙇」「ここに貼られる日が来るとは😆

参考: Cloud SQL: リレーショナル データベース サービス  |  Google Cloud

「お〜Cloud SQL使ってるんですね」「最近めっちゃ使ってるんですけど、そのときに詰まった知見を書いてみました😋」「やっぱりAWS RDSみたいには使えない感じですか?😆」「まさにおっしゃるとおり😆、RDSのつもりで使うとめちゃくちゃハマります」「なるほど〜😆

参考: Amazon RDS(マネージドリレーショナルデータベース)| AWS

「見方を変えれば、RDSがいかに泥臭い部分をまとめて引き受けてくれてるかってことでしょうね😆」「Cloud SQLはRDSより妙に速いところがあって、たとえば再起動なんか10秒で終わるんですけどね」

「RDSは結局中で普通のEC2インスタンスが動いてるから、普通にLinuxでMySQLを立ち上げるのとほぼ変わらないのがいいところでしょうね」「それあると思います」「むしろそれがRDSの利点かなと思いますし👍

「Cloud SQLの方はGoogleのサービスにいろいろラップされててよくわかりませんし😆」「たしかによくわかんない😅: Cloud SQLが個別のネットワークに立てられるとか、そんなの知りませんよって😆」「そうそう😆」「どうしてVPCからピアリングしないとあかんの?、自分のVPC作れないの?って😆」「Googleにはそういう感じの謎あるある😆

参考: VPC ピアリング接続 - Amazon Virtual Private Cloud
参考: VPC ネットワーク ピアリングの使用  |  Google Cloud

「結論は『Cloud SQLはRDSじゃない🤣」「それです🤣」「たぶんRDSを触ったことない人がCloud SQL使えば違和感ないんでしょうけど」「それ思いました😅」「逆にAWS使い慣れてる人は、リージョンがあってAZがあってみたいなものを意識するのが当たり前になってるから、そういう人がCloud SQL使うとわけわからんということになりそう😆」「なりました😆

「GCPは、AWSで積み上げた知見のままだと通用しないところがありますよね😅」「通用してよかったなと思ったのはパラメータグループぐらいかも😂」「オンプレで立ち上げるときにも使うパラメータですから、さすがに通用するでしょうし☺

⚓その他DB


つっつきボイス:「次は13か」「毎年メジャーアップデートするポリシー目まぐるしい😅」「ぽすぐれ全然追ってません😆」「まあぽすぐれは最新stable版使っとけばぐらいでいいんじゃないでしょうか: MySQLに比べれば新しいものを使ったときに問題になりにくい気はしますし☺

「MySQLは新しいのを使うとおおっとぉ〜みたいなこと起きたりしますし😆」「MySQLはコンフィグパラメータがたまに廃止されたりしますし😆」「この機能使おうと思ったらそのバージョンではdeprecatedになってたり😇」「my.confを移植するとはじかれたり😆」「新しいMySQLを最初から使う分にはいいんですけど、既存の古いMySQLから移行するときはパラメータの機能や意味を調べ直してコンフィグ作り直すのがしんどい😭

参考: my.confの設定 - Qiita

「ところでPostgreSQLにメジャーアップデートかけることってあんまりしない印象あるんですけどどうでしょう?」「ぽすぐれアップグレードやったことない😅」「PostgreSQLのEOL(end-of-life)って結構長かった気がする」「へぇ、5年なんだ↓」(以下延々)

参考: PostgreSQL: Versioning Policy

⚓クラウド/コンテナ/インフラ/Serverless

⚓AWSで組んだ典型的な100%サーバーレスアーキテクチャ(Serverless Statusより)


同記事より


つっつきボイス:「Nodeベースですけど、これが典型だそうです」「100%サーバーレスとは果たして何ぞやと😆」「サーバーレス、サーバーレス…ん〜😅」「100%フルマネージドなアーキテクチャならわかるかな〜」

⚓「サーバーレス」という言葉

「だってサーバーレスって言ってますけど、サーバーあるじゃないですか🤣」「そこツッコみますか🤣」「LambdaやNodeはどこで動いてると思ってるんですかって🤣」「サーバーあるのにサーバーレスとはこれいかに🤣」「それを言うならフルマネージドじゃないの?って思いますし😆

参考: マネージドサービスとは - IT用語辞典 e-Words

「自分もサーバーレスって言葉あんまり好きじゃないかも」「サーバーレスは人によってイメージするものが違ってそうなのがどうもね…😅

「そうだ、今度からマネージレスって呼びましょう😆」「マネージメントだったら意味するものの境界線が比較的はっきりしてそうですよね😆」「まあそう思うのは自分たちがインフラ寄りの知識で考えるからかもしれませんけど☺」「それもそうかも☺

「サーバーレスって、実はサーバーエンジニアレスの略だったりして🤣」「🤣」「インフラエンジニアが不要なんじゃなくて、サーバーサイドアプリケーションのエンジニアが不要っていう意味でサーバーレスと呼んでる人もいるのかなってちょっと思ったりしました😉」「フロントエンドの人とかそう思ってたりするかも🤔」「人によって意味が違ってきそうな用語ってやりづらい😅

⚓その他クラウド

つっつきボイス:「マストドンついに終わりましたね」「5年ぐらいで終わりそうな予感してました😆」「ついに使うチャンスないまま😅」「使っている人がどのぐらいいるのかわかりませんけど、Twitterを使いたくない人が使ってたのかも🤔


⚓JavaScript

⚓門外漢がJSを触って詰んだ話(StatusCode Weeklyより)


つっつきボイス:「どんなアウトサイダーなんだろう?」「コンピューターサイエンスやってるけどJSは初めてという人のようです」「warningに怒られて心が折れたみたいな😆」「大学でCやC++やってる人が仕事でCやC++をやると同じようなことがあったりしますね☺

⚓CSS/HTML/フロントエンド/テスト/デザイン

⚓Googleフォントをもっと速くする(StatusCode Weeklyより)


つっつきボイス:「Googleフォントは速いけどもっと速くしてやったという記事のようです」「CSS Wizardryというタイトルがつおい🧙‍♀️」「CSS魔術🧹

font-display: swap;って初めて見た😳」「Async CSSってあったかも」

参考: font-display - CSS: カスケーディングスタイルシート | MDN
参考: CSSを非同期ロードする最も簡単な方法 - Qiita

preconnectって何それ?😆」「ボーナスにfont-display: optional;なんてのも😆

参考: リソースの優先度付け - ブラウザの有用性を高める  |  Web Fundamentals  |  Google Developers<link rel="preconnect">

「知らないものいっぱいある」「仕様上はこういうのがありますよという感じ☺」「これ知ってるかってドヤ顔できそう😋

⚓言語/ツール/OS/CPU

⚓プログラマーなら知っておくべき各種レイテンシの年代推移


同記事より


つっつきボイス:「前にも似たようなのを紹介したかなと思いつつ😅」「画面上のハンドルを横にスライドすると年代を切り替えられるのね」「いろんなレイテンシが年々小さくなってる😂

「こういうレイテンシ可視化は楽しいですね🥰」「特に面白いのは、SSDが最近のものほど劇的に速くなっているのがわかるところ🏎」「おぉ😍」「メモリは最近だとそれほど速さがましていませんけど、SSDは10倍とかのオーダーで速くなってますし😋」「これはマジ速い😂」「最近のSSDインターフェースはシリアルATA(SATA)からM2に変わってきているのも大きいですね」「もう昔には戻れない😆

参考: ソリッドステートドライブ - Wikipedia
参考: M.2 SSDとは何かを分かりやすく徹底解説!|ドスパラ通販【公式】

「SSDと一口に言っても昔のSSDと今のSSDでは速度がこれだけ違うということなので、こういうサイトではそうした時代の差も読み取るのがいいと思います👍」「そうですね❤」「もちろんSSDはハードディスクに比べれば全然速いんですけど、SSDも時代によってこういう違いがあるという感覚は養っておきたいですね☺」「M2を知らなかった…😅

⚓CPUよもやま話

「そういえばLinus Torvaldsも最近Ryzenマシンに乗り換えてドヤ顔してますね↓😆」「自分も4月にLinusと同じRyzenにしてよかった😋」「いいな〜」「Intel CPUじゃないと何か不具合あるかなと思ったら何の不具合もありませんし、今のデスクトップPCはRyzenでキマリかなと💪」「自分も何だかWindowsに行きたくなってきた😆

「MacにもRyzen来て欲しいです😭」「ノートPCでRyzenはちょい厳しいかも😅」「そういえばARMの入ったMacが出る予定という話があったような」「あ、そうらしいですね」「昔のiPhoneとかiPadで使われてたARMですか?」「たぶんですけどiPhoneとかも含めてARMになるのかも🤔

参考: Apple、Arm Mac「Kalamata」を2021年に発売? - ITmedia NEWS
参考: ARMアーキテクチャ - Wikipedia

「以前はそうでもなかったんですけど、最近のiOSはだんだんmacOSに近いアーキテクチャになってきてるんですよ」「へぇ〜」「iOSやmacOSのUIも最近だんだん似てきてますし😆


「Ryzenと全然関係ありませんけど、Appleは未だにmacBookにタッチパネル入れないのはどういうこだわりなのかなって」「きっと画面を指紋だらけにされたくないんだと思います😆」「それかなりあると思う😆」「光沢ディスプレイの指紋ってバリ目立ちますし😆」「指紋ばっちい😆

⚓その他

⚓Yahoo! Mapの混雑レーダー


つっつきボイス:「たまに欲しくなるときもあるかなと思って」「人間の混雑具合ってそんなにしょっちゅう見るかな〜?😆」「たぶん建物の階の重なりまでは判別できないでしょうし😆」「ああっ😆

⚓番外

⚓これは欲しい


つっつきボイス:「首がスースーする機械🎐」「首を冷却するのはいろいろ理にかなってますね😋」「効率よさそう😍」「夏コミで熱中症になりそうになったときに首冷やしてます😆」「これ付けたままうたた寝したら危ないのかな…😅

「ところでコモドギアの本体重量が840gって結構重くありません?」「あ、1kg近いのか😅」「首に1kgですか?😆」「いえ、首は170gで、それとは別に本体があるから、それをワイヤでつなぐんでしょうね」「つまり腰あたりに本体をぶら下げると」「おそらくそこでファンを回すのかなと」「結局ファン必要なんですか?」「ペルチェ素子は熱を移動するだけだから、どこかで冷やさないと成立しませんし😆」「あ、そうですね😅

参考: ペルティエ素子 - Wikipedia

「本体ってどんな形か見たいゾ😆」「この動画↓、絶妙に本体を映していないんですけど😆」「見せたくない事情でもあるんだろうか🤣」「お値段結構しそうですけど、それなら首から扇風機ぶら下げる方が早そうですし😆」「6月に予約開始だからもうすぐですね😋


後編は以上です。

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

週刊Railsウォッチ(20200526後編)Rubyでよくやるスレッドバグ、Kubernetesでよくあるミス10、CSS/SVG/Canvasの使い分けほか

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

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

Ruby Weekly

StatusCode Weekly

statuscode_weekly_banner

Postgres Weekly

postgres_weekly_banner

Serverless Status

serverless_status_banner

Rails: anyway_config gemでRubyの設定を正しく整理しよう(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

Rails: anyway_config gemでRubyの設定を正しく整理しよう(翻訳)

よく育ったRubyプロジェクト、特にRailsプロジェクトにおける「コンフィグ」「各種設定」「秘密情報」「credential」「環境変数」というものをそろそろ真面目に考えるときがやってまいりました。本記事では、お節介ついでにAnyway Configというgemもご紹介します。Evil Martiansで使われているこのgemは私が設計したもので、プロジェクトの各種設定を正常に保ちます。皆さまを「ENV地獄」から救済するお役に立つことを願っています。

コンフィグは、コードベースの健康状態を最も如実に表すマーカーのひとつです。アプリケーションが成長して成熟すればするほど、APIキーだのenvファイルだのといった設定を扱うのが面倒になってきます。私がRailsConf 2019でお話しした「Terraforming legacy Rails applications」というセッションで、いわゆる「ENV地獄」についても触れました。大規模Rubyアプリを扱ってきた読者の皆さまのほとんどが、何らかの形でENV地獄の因縁にまとわりつかれていると確信しています。

ENV Hell example

RailsConf 2019でお目にかけた「ENV地獄」

皆さんもENV地獄行きですか?

Railsアプリで最も広く使われている設定パターンは、すべての値を.envに保存して、アプリケーションの起動時に(dotenv gemやdotenv-rails gemなどを用いて)プロセス環境に読み込み、ENV["KEY"]でコードからアクセスできるようにするという手法です。

このパターン自身の由来は真っ当です。かの有名なTwelve Factorの原則にも『設定は環境に保存すべし』と述べられています。

しかしここでひとつシンプルな実験をしてみましょう。シェルを開いて.envのあるプロジェクトディレクトリのルートディレクトリに移動し、以下のコマンドを打ってください。

cat .env | grep '[^\s]' | wc -l
  52 # これは私たちが目にした成熟プロジェクトの平均の数値です

この数値はいったい何でしょう?あなたの数値が数十個のオーダーに達していたら、残念ながらあなたは今「ENV地獄」の真っ只中です。私たちもENV地獄にいたので、それはまあいいとしましょう。

ENV地獄にハマるとだいたい以下のようなことになります。

  • .envファイルが肥大化し、ほぼ理解不能になる
  • .env.sampleファイルの同期が取れなくなって、デバッグしづらい失敗がローカル開発中に発生するようになる
  • ENV外の世界を表すグローバルステートなので、デバッグ、特にテストがつらくなってくる
RailsアプリケーションにおけるENV利用の治安を保つために、私たちはLint/EnvというRuboCopのcopを書きました。グローバルステートを設定ファイルの外に漏らさないためにお使いいただけます。

こうした問題は、たいてい開発環境のセットアップ中に忍び寄ってきます。たとえばHerokuアプリのコンフィグは、数百個もの環境変数であふれがちです。以下を実行してクイックチェックできます。

$ heroku config -a legacy-project | wc -l

  131

今度は、皆さんのRailsプロジェクトの現状を取り急ぎ調査しましょう。

  1. プロジェクトを(bin/setupなどで)セットアップした直後にrails sで起動できますか?
  2. 続いてテストを実行するとパスしますか?
  3. アプリケーションのcredentialがない場合に、credentialの置き場所がすぐわかるようになっていますか?
  4. 設定に新しい値を追加するときのワークフローは明確になっていますか?
  5. サードパーティAPIトークンなどの固有の秘密情報は、アプリケーションのコードを変更せずに利用できるようになっていますか?

「いいえ」の数が多いほど、設定のアプローチを見直す作業をその分急ぐべきです


調査の方法については次をご覧ください。

設定パラメータは「秘密情報」と「設定」の2種類ある

大量の卵(設定パラメータ)を1つの籠(.envファイル)に押し込める方法には、ひとつ大きな欠点があります。つまり、その値がそもそも何であるかという「本質」の情報が失われてしまうのです。機密情報と機密でない情報や、ビジネスロジック情報とフレームワーク関連の情報がまぜこぜになってしまいます。

設定値のトラッキングを改善するために、「設定」と「秘密情報」を心の中で分けます。両者の具体的な違いについて見ていきましょう。

設定は「内部に属する」

WEB_CONCURRENCYRAILS_MAX_THREADSRAILS_SERVE_STATIC_FILESといった設定は、技術上の特性やフレームワーク設定を改変します。これらを「フレームワーク設定」と呼ぶことにします。ここで言うフレームワークはRailsに限らず、PumaやSidekiqなどスタック上のあらゆるものを指します。

もうひとつは「アプリケーション設定」です。これは、グローバルな機能をオンオフするCHAT_ENABLED=1や、開発ツールを有効にするGRAPHIQL_ENABLED=1フラグなどが該当します。

理想的な設定とは、以下の要素を満たすものです。

  • development環境やtest環境の常識的なデフォルト設定は必ず完了していなければならない
  • production環境では必要に応じてそれらの設定を環境変数で上書きできる(HerokuではRAILS_SERVE_STATIC_FILESが自動的に設定されます)
  • 設定をプレーンテキストでリポジトリに保存できる
  • 必要なら環境固有の設定ファイル(config/environments/development.rb)にハードコードすることも可能

秘密情報は「外部に属する」

秘密情報は、他のシステムやサービスとのやりとりで必要な情報を指します。秘密情報はさらに以下のの2種類に分けられます。

  • システム秘密情報
  • サービス秘密情報
システム秘密情報
インフラの必要不可欠な部分にアクセスするためのcredential
(データベースDATABASE_URL、キャッシュサーバーREDIS_URL、その他)

たとえばHerokuのアドオンは、productionで使うそれらの値を設定するので、気にする必要があるのはdevelopment環境だけです(私たちの場合は、そうした設定をdocker-compose.ymlに保存しています)。

サービス秘密情報
サードパーティサービスのcredential
(APIキー、トークン、その他)

秘密情報に該当する情報であっても、必ずしも隠さなければならないとは限りません。APIの「ホスト名」や「上限値」のことを考えればおわかりでしょう。

システム秘密情報」と「サービス秘密情報」には、技術的に重大な違いがひとつあります。


システム秘密情報がない場合や無効な場合、アプリケーションは必ず起動に失敗しなければなりません


これは、サービス秘密情報の場合には必ずしも真ではありません。サービス秘密情報は、アプリケーションが起動するために必要不可欠な設定とは限らないからです。

Active Storageの例

たとえば、ファイルアップロード機能を実現するのにActive StorageとAWS S3バックエンドを用いることに決めたとしましょう。以下はmasterブランチにマージされたstorage.ymlと設定ファイルです(なおコードは実際のコミットからの引用です)。

# config/application.rb
config.active_storage.service = :s3

# config/environments/test.rb
config.active_storage.service = :local
# config/storage.yml
local:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

s3:
  service: S3
  access_key_id:
  secret_access_key:
  region: us-east-1
  bucket: ENV["AWS_BUCKET"]

こうすることで、AWS関連の環境変数が設定されてないチームメンバーは、たとえdevelopment環境であってもアプリの実行は不可能になります。言ってみれば、私たちは玄関に人工的なバリアーを構築しています(たとえAWS SDKをローカルで直接利用することが必須でないとしてもです)。Active Storageでは、:local設定をtest環境のみならず、development環境でも利用できるようになっています。もし誰かが「自分のローカル環境をproductionになるべく近づけたいんです!」と言ったとしましょう。お気持ちはありがたいのですが、そんな厳しい要件が必要でしょうか?

ここで仮に、development環境で以下のように本物のAWSバケットを利用できる機能をオプトインとして導入したと想像してみましょう。

# config/application.rb
config.active_storage.service =
  if AWSConfig.storage_configured?
    $stdout.puts "Using :s3 service for Active Storage"
    :s3
  else
    :local
  end

おやおや、こんな見慣れない書き方をRailsで手軽に使えるものでしょうか。そもそもAWSConfigが何なのか、それがどんなふうに設定されているのかをどうやって知ればよいのでしょう。そこで、本記事の冒頭からチラ見せしていたAnyway Config gemの出番です。

Anyway Configの概要

今どきのRailsでは、環境変数に保存した設定データ以外にもさまざまな選択肢が使えます。config/initializersのパラメータを直接編集する方法、昔ながらのyamlファイル編集による方法、そしてRails 5.2からは暗号化済みcredentialを用いてソースコード管理に安全にチェックインもできます。

Rails 6.0では、環境ごとにcredentialを使い分ける機能も追加されました。なお、このgistを使えばそれらをRails 5.2にバックポートできます。

私たちEvil Martiansが今試している、設定における鉄の規律は以下のとおりです。

  • 重要な情報は、Railsのcredentialに保存する(環境ごとに*.encファイルを作る)
  • 重要でない情報は、名前を付けてyaml設定に保存する
  • あらゆる値を環境変数で上書きできるようにする
  • ローカル(development環境)の秘密情報や設定は、*.local.ymlcredentials/local.yml.encに保存する
  • 取り扱いに特別な注意を要するcredentialをチームメンバー全員で共有する必要がある場合は、暗号化されたストレージで集中管理する。Keybaseはこれを一手に引き受けます。

「選択肢が多すぎる問題」を避けるのは困難です。各開発者が思い思いの設定方法で、設定を片っ端からアプリにぶちこむと、開発者の認知能力にずしりと負担がのしかかります。そこで私たちは、ピュアなRubyインターフェイスを標準としてすべての設定に提供するツールを使うという手法にたどり着いたのです。


Anyway Config gemは、さまざまな場所に散在するデータを透過的に管理できます。さらに、コンフィグ専用のクラスを導入することで、設定をコードとは独立に保存します。
もうRails.credentialsだのRails.application.config_forだのENVを呼び出す必要はありません。必要なのはRubyのクラスを扱うことだけです。

このgemができるまでに長い話があります。当初は、私が最初に作ったInfluxerというgemから抽出しました。なおInfluxerはほとんどの場合ライブラリ(AnyCableなど)で使われてきました。最近リリースしたAnyway Config 2.0は、ここ数年Evil Martians社内のアプリケーション開発のユースケースから大いなるヒントを得ています。

それではActive Storageの例に戻って、Anyway Configでどんなふうに設定できるかを見ていきましょう。

RailsのGemfileにanyway_configを追加すると、新しいコンフィグクラスを作成する手軽なジェネレータにアクセスできるようになります。

$ rails generate anyway:config aws access_key_id secret_access_key region storage_bucket
    generate  anyway:install
       rails  generate anyway:install
      create  config/configs/application_config.rb
      append  .gitignore
      insert  config/application.rb
      create  config/configs/aws_config.rb
Would you like to generate a aws.yml file? (Y/n) n

ジェネレータを実行すると、以下の2つのファイルがプロジェクトに追加されます。

  • config/configs/application_config.rb: すべてのコンフィグクラスのベースクラス(存在しない場合にのみ作成される)
app/configsではなくconfig/configsを使っている理由は、Railsのオートロードやオートリロードに関連します。詳しくはREADMEの”Organizing configs”をご覧ください。
# ファイルを設定するための抽象ベースクラス
# 設定のデフォルトインスタンスを返す`instance`メソッドを提供する
#
# また、missingメソッドはすべてインスタンスに委譲されるので
# このクラス自身をシングルトンのコンフィグインターフェースとして使える
class ApplicationConfig < Anyway::Config
  class << self
    delegate_missing_to :instance

    def instance
      @instance ||= new
    end
  end
end
  • config/configs/aws_config.rb: AWS設定用のクラス
class AWSConfig < ApplicationConfig
  # attr_configはパラメータ設定用の
  # リーダーとライターを定義する
  attr_config :access_key_id, :secret_access_key,
              :region, :storage_bucket
end

なお、以下のようにconfig/initializers/inflections.rbに略語を追加することで、「AWS」という語をRailsのinflectorで認識できるよう設定する必要があります。

ActiveSupport::Inflector.inflections do |inflect|
  # ...
  inflect.acronym "AWS"
end

ジェネレータのプロンプトでyesを入力すると、config/aws.ymlファイルも追加されます。今は情報をプレーンテキストに保存したくないので、credentialを使うことにします。

作成したコンフィグクラスを編集してregion:にデフォルト値を追加し、#storage_configured?メソッドも追加します。

class AWSConfig < ApplicationConfig
  # ハッシュをひとつ渡すことでデフォルト値を指定できる
  attr_config :access_key_id, :secret_access_key,
              :storage_bucket, region: "us-east-1"

  def storage_configured?
    access_key_id.present? &&
      secret_access_key.present? &&
      storage_bucket.present?
  end
end

続いて、production用の値も渡せるようにする必要があります。credentialファイルを開いて以下の値を定義しましょう。

$ RAILS_MASTER_KEY=<production key> rails credentials:edit -e production

aws:
  access_key_id: "secret"
  secret_access_key: "very-very-secret"
  storage_bucket: "also-could-be-a-secret"

storage_bucketは、ユースケースによっては重要な情報の一部とみなす必要はないかもしれません。その場合は値をconfig/aws.ymlに設定できます。

# config/aws.yml
production:
  aws:
    storage_bucket: my-public-bucket

対応する環境変数に値を設定すれば、いつでも値をオーバーライドできます。

AWS_STORAGE_BUCKET=another-bucket rails s

ところで、私たちのコードでは設定値がどこにあるかを気にする必要がないことにお気づきでしょうか?コードが認識しているのはAWSConfigクラスだけです。このアプローチの主なメリットが、まさにこれです。

今後AWSをローカルで使うことが決まったら、config/aws.local.ymlに個人用のcredentialを書き込めば済みます。秘密情報を自分のコンピュータ上にプレーンテキストで保存するのがイヤな場合は、Railsのローカルcredentialを使えばよいのです。

rails credentials:edit -e local

Anyway Configの設定は、ローカルデータよりも優先して代入されます。

さらにうれしいオマケとして、あらゆる値の出どころをトラッキングできます。これは特にproduction環境で便利です。

AWSConfig.to_source_trace
# =>
# {
#  "access_key_id" => {value: "XYZ", source: {type: :credentials, store: "config/credentials/production.yml.enc"}},
#  "secret_access_key" => {value: "123KLM", source: {type: :credentials, store: "config/credentials/production.yml.enc"}},
#  "region" => {value: "us-east-1", source: {type: :defaults}},
#  "storage_bucket" => {value: "example-bucket", source: {type: :yml, path: "config/aws.yml"}}
#  }

ppで人間にとって読みやすい出力を得ることもできます。

pp AWSConfig.new
# =>
# #<AWSConfig
#  config_name="aws"
#  env_prefix="AWS"
#  values:
#    access_key_id => "XYZ" (type=credentials store=config/credentials/production.yml.enc)
#    secret_access_key => "123KLM" (type=credentials store=config/credentials/production.yml.enc)
#    region => "us-east-1" (type=defaults)
#    storage_bucket => "my-public-bucket" (type=yml path=config/aws.yml)

まとめると、Anyway Configでは以下を行えます。

  • さまざまなデータソースラッパーの代わりにコンフィグ用クラスを使える
  • ローカルの秘密情報や設定もサポートされている
  • 設定ファイルが分かれているので、単一の.envapplication.ymlに設定をパンパンに押し込める必要がない

Rubyアプリケーションが成長するに連れて、設定の管理はたちまち悪夢と化します。設定ファイルの編成方法や、さまざまな種類の値の扱い方に注意することで、設定の治安を維持できるようになります。

あらゆる個別の設定データに関する知識をコードベースから切り離すことで、プロジェクトの健全性も保てます。Anyway Config gemは、あらゆるユースケースに合う共通の抽象化を提供しているので、開発者の認知能力に負担をかけることなく、さまざまなところからやって来るさまざまな設定値を自由自在にミックス、比較、オーバーライドできるようになります。

ENVは責任を持って使いましょう!

ところで、皆さんのよく育ったRailsアプリケーションを「テラフォーミング」して、以下のような最新のベストプラクティスを導入することにご興味はおありですか?

私どもEvil Martiansが皆さまにお力添えいたします。

本記事の翻訳や転載についてのご相談は、まずメールにてお願いします。

Evil Martiansでは、外宇宙流の製品開発およびご相談を承ります。

関連記事

クジラに乗ったRuby: Evil Martians流Docker+Ruby/Rails開発環境構築(翻訳)

週刊Railsウォッチ(20200608前編)RubyKaigi 2020が開催中止に、ネステッドSTIを避けるべき理由、rails newがインタラクティブになるかもほか

$
0
0

こんにちは、hachi8833です。9月のRubyKaigi 2020の開催が中止となりました😢

⚓臨時ニュース: RubyKaigi 2020の開催が中止に

別途オンラインイベントを構想中とのことです。


つっつきボイス:「この時期はもうしょうがないですね」「開催3か月前だとそろそろチケットやら宿やらを取らないといけなくなる時期ですし、正しい判断だと思います」「決定するのつらかっただろうな…」「Kaigiで会いたい海外勢いたのに〜😢」「来日する人たちもまずスムーズに入出国できないでしょうし」「そうか!😭」「空港を通過するたびに2週間ずつ足止めくらうでしょうから、行って帰るまで1か月はかかっちゃうでしょうし」「会社もそこまで許してくれなさそう😢」「チケットの他にスポンサー代も返金になるのか、とりあえず連絡待ちかな」


「ところで日本は東京アラート出ている割には解除ムードのせいかあまり様子が変わってないような気が😆」「それサーバー運用だったらダメなヤツ😆」「アラート鳴いてるのに『鳴いてるね、うんうん』で終わるみたいな😆


以下はつっつき後に見つけたツイートです。


  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

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

以下のコミットリストやRails Discussionを元に見繕いました。

⚓コントローラレベルのforce_sslを削除


つっつきボイス:「え、force_sslが消えた?」「マジで?😆」「コントローラレベルでは消されましたけど、たぶんコンフィグのforce_sslはあるのかな🤔」「これ↓見たらautoloadされなくなっただけなのかなと思ったら、actionpack/lib/action_controller/metal/force_ssl.rbがバツっと削除されてるし😳」「metalからファイルが抹殺された😇」「前からdeprecatedだったのね」

# actionpack/lib/action_controller.rb#L32
    autoload :EtagWithFlash
    autoload :FeaturePolicy
    autoload :Flash
-   autoload :ForceSSL
    autoload :Head
    autoload :Helpers
    autoload :HttpAuthentication

あとで探すと、2018年に以下のプルリクでforce_sslがdeprecatedにされていました。せっかくconfig.force_sslがあるのにApplicationControlerforce_sslを書いてしまう例があとを絶たなかったのでdeprecateしたそうです。

「まあ考えてみれば、今後Railsで作るアプリのコントローラでforce_sslをわざわざ書くことってまずないと思いますし」「ないないっ😆

「でも昔はコントローラにforce_sslを書かざるを得ないことってありましたし: ログインしてないユーザーにはhttpsじゃなくてhttpでアクセスさせよ、とか😢」「httpsのアクセスが遅かった昔なんかはそうでしたね」「自分は『お、force_sslあるじゃんやった〜😋』みたいな感じで書いたことあったかも😅」「force_ssl、割と地雷でしたね🧨

⚓require_dependencyhelperから削除

require_dependency削除作業の一貫のようです。


つっつきボイス:「require_dependencyまだ残ってたのか」「Zeitwerkがやってきて要らなくなったヤツですね」「どんどん削られてる〜」

Rails 6 Beta2時点のZeitwerk情報(要訳)

helper UtilsHelper?」「Changelogに以下が追記されてました↓」「ああなるほど、前はhelperにモジュール名を文字列やシンボルで渡してたけど、UtilsHelperみたいにモジュール名を直接書けるようになったのね」「たしかにこの方が直感的ですし😋

コントローラ向けのhelperクラスメソッドで文字列やシンボルとして読み込むヘルパーモジュールは、require_dependencyではなくString#constantizeで読み込まれるようになる。
文字列やシンボルのサポートは単なるお便利APIに過ぎないことをお忘れなく。以下のようにモジュールオブジェクトをいつでも渡せる。

helper UtilsHelper

そしてこれはシンプルかつストレートなのでおすすめ。helperは文字列やシンボルを受け取ると、同じモジュールオブジェクトを得るためにその引数を操作してinflectionで活用しているに過ぎない。
Xavier Noria、Jean Boussier
Changelogより

⚓fixture_file_uploadで使うファイルパスがfixture_pathからfile_fixture_pathに変更

fixture_file_uploadではfile_fixture_pathを使うこと。
file_fixture_pathがなかった頃はfixture_pathを使っていたが、今後はfile_fixture_pathを使うべき。
fixture_pathを使うのはActive Recordだけなのだが、ActionPackに何かの間違いで紛れ込んだ様子。
file_fixture_pathのもうひとつのメリットは、fixture_file_upload('../files/blabla.png')みたいにいちいちディレクトリを1階層上げなくてもよいこと。
同PRより大意


つっつきボイス:「file_fixture_upload見たことなかったな〜」「fixtureだからテスト用ですね」

# actionpack/lib/action_dispatch/testing/test_process.rb#L17
      def fixture_file_upload(path, mime_type = nil, binary = false)
        if self.class.respond_to?(:fixture_path) && self.class.fixture_path &&
            !File.exist?(path)
-         path = File.join(self.class.fixture_path, path)
+         original_path = path
+         path = Pathname.new(self.class.fixture_path).join(path)
+
+         if !self.class.file_fixture_path
+           ActiveSupport::Deprecation.warn(<<~EOM)
+             Passing a path to `fixture_file_upload` relative to `fixture_path` is deprecated.
+             In Rails 6.2, the path needs to be relative to `file_fixture_path` which you
+             haven't set yet. Set `file_fixture_path` to discard this warning.
+           EOM
+         elsif path.exist?
+           non_deprecated_path = path.relative_path_from(Pathname(self.class.file_fixture_path))
+           ActiveSupport::Deprecation.warn(<<~EOM)
+             Passing a path to `fixture_file_upload` relative to `fixture_path` is deprecated.
+             In Rails 6.2, the path needs to be relative to `file_fixture_path`.
+             Please modify the call from
+             `fixture_file_upload(#{original_path})` to `fixture_file_upload(#{non_deprecated_path})`.
+           EOM
+         end
+       elsif self.class.file_fixture_path && !File.exist?(path)
+         path = file_fixture(path)
        end
+
        Rack::Test::UploadedFile.new(path, mime_type, binary)
      end
    end

⚓config.generators.after_generateが追加

# 同PRより
config.generators.after_generate do |files|
  system("bundle exec rubocop --auto-correct " + files.join(" "), exception: true)
end

つっつきボイス:「@y-yagiさんのプルリクですね」「フックポイントがまたひとつ増えた」「after_generateでフックかけたいことってありますね😋」「その中でまたジェネレーターを呼び出してループしたりして😆」「😆

⚓番外: ActiveRecord::Core.[]を追加してArel属性オブジェクトにアクセスできるようにする(closed)

このコミットはActiveRecord::Core[]メソッドを実装する。この機能のポイントは、開発者がArel属性オブジェクトにアクセスできるようにして、もっと複雑なクエリを以下のように簡単に構築できるようにすること。

Developer.where(Developer[:salary].gt(9000))

単純な==や!=に収まらない複雑な条件でクエリを構築できる。このメソッドは他のテーブル内のカラムも正しく参照できるので、joinsでのクエリ制約も「そのまま適用できる」。
同コミットより


[]じゃない方法を考える方に賛成。こちらのコードベースではfinderお便利メソッドとしてself.[]を既存のモデルに実装しているのだけれど、他のプロジェクトでそうしてなかったとするといささか驚きかもしれない。

たしかにとてもよい指摘。
自分もModel[:xxx]よりModel::Table.xxxの方がいいかもと思えてきた。この方がコードがやっていることもクリアだし、[]の他の実装との衝突を気にしなくてよくなるし。
このプルリクはcloseしようと思う。@flanger001へ、もし時間があればTableを実装するプルリクを作ってこのプルリクを引用してもらえるだろうか?聞いてみただけなので無理ならいいです😆
#39198のコメントより


つっつきボイス:「最近公式のRuby on Rails Discussionsがめちゃ面白くておすすめなんですけど、その中で以下のやりとりを元に@tenderloveさんが上のプルリクを投げて、その後説得を受けてcloseしてました」「Arelをpublicにして欲しい人っているのかな〜?😅」「Arel使って欲しくないんですけど😅」「Arelが欲しいのはSQL絶対書きたくないマンでしょう😆」「😆

⚓Arelよもやま話

「Arelってなんか黒魔術っぽいから🧙‍♀️、SQL書くかActive Record書くかどっちかにして欲しいです〜😢」「これまでも話しましたけど、どういうSQLを吐くかを想像しながら一生懸命Arelで組み立てるのって、なんか人生の無駄遣いだなって思いますし😆」「😆」「Arelは他とパラダイムの違うラッパーなのに、変換先のSQLをのことを考えないといけないのってどんな縛りプレイなのって😆」「日本人同士なのに英語でしゃべってるような気持ちになりそう😆」「それそれ、しなくていい苦労をしてる感😆

「ただ、どのタイミングで生SQLに切り替えるかというさじ加減が人によって違うんですよ😅」「それありますよね」「機能が確定していてそれ以外の使い方をしないなら生SQLでいいんですけど、取ってきたオブジェクトをこねくり回すことが多いとArelでやりたくなる気持ちもわからなくもないですし」「気持ちはわかるんですけど😅」「気持ちどまり😆

「個人的にはチームでArel使おうとする人がいたらたぶん血相を変えて止めるだろうなって😆」「Arelで書くようになると、そのうちどんなSQLが生成されるかをコメントに貼っておかないとやっていけなくなったりしますよね😆

koicさんの最近の記事も貼っておきます。

⚓Rails

⚓rails newをインタラクティブにやりたい -> ではお願い(DHH)(Ruby on Rails Discussionsより)


つっつきボイス:「これもRails Discussionsの投稿で、rails newをインタラクティブにしたいという投稿にDHHが『じゃ任せるから』的にOKを出して、現在別の人から以下のプルリクが上がっています」「お、たしかにrails newはインタラクティブになってもいいかも😋」「Action Mailer要らないとか、Action Mailbox要らないとかを対話的に指定できるということでしょうね😋

rails new --interactive使えるようになったらとてもよさそう❤」「とてもいいと思います👍」「実際rails newってたまにしかやりませんし、オプションを調べる方が面倒ですし😆」「今までだとオプション間違えては消してやり直しを繰り返しがち😆」「たいていgit initした後だからさらに面倒になりますし😆」「もういくつ寝るとできるのかな〜😋

⚓Rails Discussionsが好調


discuss.rubyonrails.orgより

「というような話題も見つかったりするので、Railsやってる人はRuby on Rails DiscussionsA May of WTFカテゴリあたりをときどきチェックするととてもいいと思います😋」「Railsコミッターがこれを通じてコミュニケーションしようという動きが急に加速してきましたね」「しかもいい感じに盛り上がってますし❤」「前は過疎ってましたけど😆

「Railsコミッターがこの板に積極的に関わるようになってきたのが大きいですね」「DHHが書き込むと『Resolve✅』チェックマークが付くことが多い感じです😆」「このサイト、RSSあるかな?👀」「ありますヨ、私もSlackでRSS読んでますし😋」「じゃやろうっと😋」「お、ページのソースにRSSフィードのURLが隠れてた↓😆」「最近RSSボタンがないサイト、多いですよね😆」「カテゴリごとにもフィードできたと思います」

    <link rel="alternate" type="application/rss+xml" title="Latest posts" href="https://discuss.rubyonrails.org/posts.rss" />
    <link rel="alternate" type="application/rss+xml" title="Latest topics" href="https://discuss.rubyonrails.org/latest.rss" />
    <link rel="alternate" type="application/rss+xml" title="RSS feed of topics in the 'A May Of WTFs' category" href="https://discuss.rubyonrails.org/c/a-may-of-wtfs/8.rss" />

「こうやってRails Discussionsが回るようになったことで、GitHubにあるRailsリポジトリのissuesが荒れなくて済むという効用もあるでしょうね👍」「たしかに!」「Railsのissue、読みきれないほど増えてますし😆」「issueじゃないものまでジャカスカ書き込まれてましたし😆」「それissueじゃないからみたいなヤツ😆」「discussしたいのにissueに書き込んだり😆

「Railsぐらい大規模なフレームワークになってくるとissueがパンクしがちなので、こういうふうに雑談的なトピックをディスカッションできるRails Discussionsという場が回るようになったのはいいですね☺


「上には貼らなかったんですけど、『Railsのconcernsの正しい使い方をドキュメント化して欲しい』みたいなトピックもRails Discussionsに上がってました」「それ欲しいかも😆」「concernsは単なるモジュールインクルードだから正しい使い方と言われてもね😅…みたいなレスが付いてました😆」「実際concernsはconcernsという機能でしかないので、正しく使うかどうかは設計の問題なんですよね☺」「DHHもさすがに一刀両断には回答してませんでした」「たぶんですけど、昔のRubyだと気を付けないとハマりがちな部分をサポートするために、Active Supportにconcernsという形のラッパーが入ったんだろうと思いますし🧐」「なるほど」「Active Supportの典型的なサポート機能だと思います☺

これでした↓。

参考: ActiveSupport::Concern

⚓ネステッドSTIを避けるべき理由


つっつきボイス:「STI(Single Table Inheritance)のネストは避けろだそうです」「ネステッドSTIはキツい🤣」「ネステッドは確実にやめた方がいいです、マジで🤣

参考: 5 シングルテーブル継承 (STI) — Active Record の関連付け - Railsガイド

「もう記事を読むまでもないと😆」「STIをネストすると絶対おかしくなりますって😆」「そもそもちゃんと動くんだろうか」「やれるのかもしれないけど、やろうと思ったことがない😅」「誰がやりたいと思うんでしょう?」「たぶんRDBを意識したことのないオブジェクト指向プログラマーが、STIは単なるリポジトリレイヤの話でしかないよねとばかりにオブジェクト指向で突っ走るとこうなっちゃったりして😆」「そんなバカな😆」「この間みたいにdelegated typingしましょうよ(ウォッチ20200601😆


同記事より

「そういえばこういう図↑って、15年ぐらい前のオブジェクト指向の教科書によくありましたよね😆」「動物シリーズなつかしい🦁」「独習Javaあたりに書いてありそう😆

オブジェクト指向の説明で動物を例えに使うと誤解を招くのでよくないという話はとてもよく耳にしますね。

参考: オブジェクト指向の教え方で「動物を例えにする」説明は誰が始めた... - Yahoo!知恵袋

「STIキライな人って多いですけど、参照するクエリやインデックスをちゃんと考慮したうえでSTIを導入するのであれば十分リーズナブルになることはかなりあると思いますし🧐」「毛嫌いしたらあかんと」「自分はその意味でSTIはそんなにキライじゃないです😋」「STIを使うべきじゃないところで使う人があとを絶たなくて、そのせいでSTIがキラワれるんだろうと思いますし😆


記事見出し:

  • 説明のためのコンテキスト
  • STIはlazyなコード読み込みでうまく回らない
  • STI + lazy loading + nested models = 予想外の振る舞い
  • 普通のSTIのベースクラスならうまくいくのに中間クラスだとうまくいかない理由
  • 回避方法はあるのか
  • 私はこう解決するのが好き

⚓RailsアプリでStripeのWebhookイベントを安全に扱う方法(Awesome Rubyより)

stripe_eventというgemが紹介されています。


つっつきボイス:「Webhookをいかに安全に扱うかについてはいろいろ気にすべき点がありますね」「おぉ?」「Webhookは基本的に公開URLに対して投げるものなんですけど、サーバーがそのWebhookを本当にさばいても大丈夫なのかどうかを本来ちゃんと考えておかないといけないんですよ🧐」「ふむふむ」「特に不可逆な操作を行うWebhookは要注意☢

参考: Webhookって何?を子どもでもわかるように描いてみた | kintone hive online

「まあWebhookについてはベストプラクティス的なものはだいたいあります☺」「Stripeのようにお金を扱うサービスだともちろん安全にやらないとヤバいんですけど、Stripeにはその辺をverifyするしくみは入ってますので😋」「ありがたいです🙏

Stripe決済を自社サービスに導入してわかった5つの利点と2つの惜しい点

⚓Railsアプリのルーティングを多面的に捉える(Hacklinesより)


つっつきボイス:「ルーティングは…やっぱり難しいよね😅」「難しいです😆」「ジェダイとかライトセーバーが例えに使われてる⚔

⚓REST vs GraphQL

「最近GraphQLが気に入っているんですけど、GraphQLだとこの記事にあるようなルーティングを避けられるのがメリットですね😋」「おぉ?」「正確に言うと避けてはいないんですけど😆、少なくとも無理やりRESTfulにしようとした結果ルーティングがネステッドすることは避けられます👍

参考: GraphQL - Wikipedia

「ルーティングをこねくり回すぐらいならGraphQLにする方がリーズナブルかなって思いますし、RESTだと合わないものもあるよなって思いますし☺」「ルーティングこじらせたくないです😅」「RESTがネストしてくると同じリソースにアクセスするのに複数の方法を作るようになってきて面倒くさい😆」「ふむふむ」「以下で言うと2行目でも4行目でもlightsabers:showにアクセスするとか: この場合どっちを叩いても同じアクションが呼ばれることになりますし」

# 同記事より
resources :jedis, only: [:index] do
  resources :lightsabers, only: [:show]
end
resources :lightsabers, only: [:index, :show]

「でも以下のようにmodule:を使う方法↓にも引っかかりどころはあって、たしかにこうすれば違うアクションを参照できるんですけど、果たしてそれでいいんだろうかと思ったり😅」「あ〜😳」「ルーティングは悩ましいです😭

# 同記事より
resources :jedis, only: [:index] do
  resources :lightsabers, only: [:show], module: :jedis
end
resources :lightsabers, only: [:index, :show]

「GraphQLか〜🤔」「まあGraphQLにしたらしたで、今度は別の悩みが発生するんですけどね😅: クライアント側のビューにも影響してくるので」「たしかに」「でもGraphQLならスキーマで提供できるものしか渡さないので、こっちのルーティングでもあっちのルーティングでも取りたいものがあるというようなときには比較的柔軟にやれると思います❤

「GraphQLのいいところは1つのリクエストにクエリを複数乗せられること: システム的には1つしか提供していないリソースでも、複数の場所で取りたければクライアントがそれぞれクエリすれば済みますし😋」「ほほぅ😋」「RESTだとN+1的なクエリがHTTPレベルで発生しがちなんですけど、それを回避できる点でGraphQLはエラいなと思います💪

「GraphQLでそういうクエリになってくると今度はサーバー側の負荷がつらくなってくるんですけどね🤣」「どうやらGraphQLはちゃんと勉強しないとダメそうかな…🤓」「その辺はちゃんと調べないといけませんね🧐」「でもGraphQLにはその手のサーバーサイド寄りの資料があんまりなくて😢」「ネットでその辺を試行錯誤している人たちの記事を探して自分でもやってみたりしているんですけど、結局バッチジョブでキャッシュに乗せるみたいな普通のことしか書いてない😆」「要は普通のことをやれと😆

RailsでGraphQL APIをつくる: Part 1 – GraphQLとは何か(翻訳)

⚓サーバーの数を減らしてパフォーマンスを上げた(Hacklinesより)


つっつきボイス:「Babbel.comは語学学習サイトのようなんですけど、サーバーの数を減らしつつパフォーマンス改善したそうです」「オートスケーリング周りって、AWSとかでやっていて一番考えずに済ませたい部分😆」「😆」「真面目にやってたらすんごく大変😅」「きりがないですね😅

⚓AWSのおまかせオートスケーリング

「そういえば最近だとAWSでオートスケーリング周りをおまかせでやってくれる機能が増えましたよね❤」「お!」「最近はスケーリングポリシーで済ませることが多い😆

参考: Amazon EC2 Auto Scaling(需要に合わせてコンピューティング性能を拡張)| AWS
参考: Amazon EC2 Auto Scaling のステップスケーリングポリシーおよび簡易スケーリングポリシー - Amazon EC2 Auto Scaling (日本語)

「このターゲット追跡スケーリングポリシー↓が一番新しくて、AWS推奨」「おぉ😍」「これにパラメータをちょこっと渡すとよしなにやってくれることになってます☺

参考: Amazon EC2 Auto Scaling のターゲット追跡スケーリングポリシー - Amazon EC2 Auto Scaling (日本語)

「昔からオートスケールって信用ならないという印象があって自分で作ったりしたことあるんですけど😅、最近のオートスケールってどうなんでしょう?」「オートスケーリング自体は普通に動くけど、反応は遅いかも🤔

⚓負荷試験はつらいよ

「まあ結局どれを使おうと、パフォーマンス試験は必ずやらないといけませんし🚜」「たしかに」「でも計測する環境を整えるのが特に面倒で工数がかかっちゃう😢」「あ〜結局そうなりますよね😅」「テストシナリオを作るのにも時間かかりますし、サーバーが十分あふれるだけのリクエストをちゃんと投げられる能力のあるクライアントを構築するのも面倒😭」「う〜む」「厳密なことを言えば、自分の構築した計測環境が正しいかどうかの確認も本当は必要ですし、ホント大変😅」「自分も、これでいいのかな?本当にいいのかな?って思いながらやったりしますし😅

「その意味では、パフォーマンス試験をやれる人が2人は欲しい」「わかります、ダブルチェックしてもらわないとコワいです😅」「自分はこういうポリシーでこういう部分を測定するように設計しているから十分な負荷はかけられるはず、と思って作るんですけど」「でも後で『あれ、こっちだったかな?』『これ抜けてたかな?』なんてなったり🤣」「アミバ様みたいに🤣」「とにかく負荷試験は自分ひとりだけだとコワい」「自分の書いた負荷試験シナリオの妥当性を誰もレビューしてない状態でやるのはドキドキものですよ」「プルリクを見もしないでマージする的な😆

「自分ひとりでやった負荷試験を頼りに上を説得したことありましたし😅」「負荷試験を本気でやるといくらお金があっても足りないので、もし負荷試験で問題があったらこのパラメータを変えればよくなるはず、というところまで落とし込んでから報告するのが現実的でしょうね☺」「でないといつまでたってもリリースできませんし」「安全弁は用意しておいて、問題が起きたらこの安全弁を外せというところまで固めてからリリースすると」「安全弁が万一安全でなかったら外した途端にサービス止まりますけど🤣」「そのときはゴメンナサイするしか🤣


前編は以上です。

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

週刊Railsウォッチ(20200602後編)JSONストリームパーサーyajl-ruby、ruby-buildとopenssl、GoogleのCloud SQL、Rubyと機械学習ほか

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

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

Rails公式ニュース

Ruby on Rails Discussions

Awesome Ruby

Hacklines

Hacklines

Rails: AppSignalが採用する「シタデルアーキテクチャ」(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

Rails: AppSignalが採用する「シタデルアーキテクチャ」(翻訳)

DHHが「シタデル(Citadel: 要塞、とりで)」という用語を命名しました。これは私たちAppSignalでアプローチしている技術を表すうってつけの用語となっています。「うちでやってるのはまさにこれだよ!ついに名前が付いた」と話題になったものです。

DHH: やはりマイクロサービス採用による痛みはとてつもなく大きい。
DHH: マジェスティックモノリス(Majestic Monolith)パターンに続いて、誰かCitadelパターンについてもまとめ記事を書くべき。マジェスティックモノリスがアプリの主な重量を支え、特殊かつ多様なニーズについては周辺に配置した数個のアウトポストを用いるというものだ。

AppSignalで用いているシタデルパターンについて説明するために、私たちのシステムがどうなっているかを少しばかりご紹介します。AppSignalとは、顧客が利用しているアプリケーションを監視する製品であり、監視エージェントからのデータ送付先となるAPIです。このデータが処理されてグラフやインサイトとして表示されます。

モノリス

私たちの顧客がやりとりするアプリケーションは、フロントエンドの一部がReactで書かれたモノリシックなRailsアプリ、すなわちモノリス(Monolith)です。バックエンドはすべてRubyで書かれており、いくつかのデータベースと通信します(スケール上の理由でデータを顧客ごとにクラスタに分離しています)。このRailsアプリでは、外部サービスへのアラート送信といった多くのタスクも扱っています。

このRailsアプリで監視エージェントから受け取ったデータも処理するようになった頃、データインジェストが今後ボトルネックになるであろうと予見されました。そこで私たちはSinatraアプリをサブドメインで動かし、Railsアプリで処理されたデータをそこでインジェスト(ingest: 摂取)してSidekiqジョブを作成するようにしました。

つらみが増してきた

このアーキテクチャは長年うまく動いていました。しかしビジネスが成長するに連れて、エージェントから受け取ったデータを処理する特定のタスクの扱いについて特別な注意が必要になってきたのです。数十億単位のリクエストを絶え間なく監視していれば厳しい上限に突き当たるものです。Rubyの遅さは上限の主要なファクターではありませんでした(Rubyのせいでないことはとっくに承知です😉)が、私たちが構築していたアーキテクチャではデータベースロックがあまりに多すぎたのです。

アウトポスト

私たちはさまざまな可能性を検討した末、自分たちの状況にはApache Kafkaが最適であると判断を下しました。私たちの場合はRustの経験も多少あったので、このシステムではスピードや信頼性の点でRustが非常によい結果を生むと考えたのです。そこでデータのインジェストシステムや処理システムをRustで書き換え、キューとストレージを組み合わせたシステムとしてKafkaを用いました。

Railsアプリからこのアウトポスト(outpost: 出城、前哨基地)サービスに移行したのは、受け取ったデータを処理する部分だけでした。システムのその他の部分は、モノリシックアプリの形のまま問題なく動作しています。私たちはこのシステムを深く理解し、システムをシンプルに保つことに没頭しました。モノリスは従来同様にロジックのほとんどを扱い、Kafkaと密にやり取りしています。そして私たちのモノリスを崩さないためにKafka用のgemを書いたことで、メインアプリはアウトポストと簡単にコミュニケーションできます。

AppSignalにおけるKafkaの利用法について詳しくは、以下の動画でRailsConfでの私のセッションをご覧ください。

要塞暮らしはいいぞ

というわけで、今や私たちの籠城生活は実にハッピーです。DHHが以下に述べたように。

マジェスティックモノリスはアプリの主な重量を支え、特殊かつ多様なニーズについては周辺に配置した数個のアウトポストを用いるというものだ。

私たちの場合は、極めて特殊なニーズを1個のアウトポストサービスで賄っています。もし仮に今年RailsConfが開催されるのであれば、素敵な名前を付けてくれたDHHに私たちからストロープワッフルを余分に進呈することでしょう🍪

おたより発掘

関連記事

開発チームを苦しめるマイクロサービス(翻訳)

Rails: config.force_ssl = true によるHSTSの動作をローカル環境とChromeで試してみた

$
0
0

こんにちは、hachi8833です。RailsのHSTS周りでハマったので、自分のためにメモします。

Rails 5.0以降ではforce_sslでHSTSも有効になる

Rails 5.0以降では、アプリケーション設定でconfig.force_ssl = trueを指定すると、HSTS設定「も」同時に有効になります。

参考: Rails 5 adds more control to fine tuning SSL usage | BigBinary Blog

参考: HTTP Strict Transport Security - Wikipedia

HTTP Strict Transport Security (エイチティーティーピー・ストリクト・トランスポート・セキュリティ、略称 HSTS)とは、WebサーバーがWebブラウザに対して、現在接続しているドメイン(サブドメインを含む場合もある)に対するアクセスにおいて、次回以降HTTPの代わりにHTTPSを使うように伝達するセキュリティ機構である。RFC 6797 で規定されている。
Wikipediaより

Chromeでローカル環境のHSTSを試してみる

整理のため、ローカル環境でRailsとChromeのHSTSの挙動を試してみました。

使ったもの

  • 業務で使わない「捨て」のRailsアプリ環境(ここではRails 6.0.3.1を使いました)
  • Chrome(現時点では83.0.4103.97)
  • ngrok

自分はMac環境でやりました。

手順

作業内容はgitに登録する必要はありません。

1. 準備

  • Railsアプリのconfig/environments/development.rbのRails.application.configure doブロックに以下を追加しておきます。
  config.hosts << 'usousouso.lvh.me'
  config.hosts << ".ngrok.io"

念のため、config.hostsのローカルホスト名にはusousouso.lvh.meのような絶対使いそうにないものを使いました。

  • Chromeを起動し、念のためシークレットモードのウィンドウを開いておきます(コマンド+Shift+Nキー)。以後は常にここでブラウザ画面を表示します。

2. lvh.meでの通常起動を確認

  • ローカルでRailsアプリをbin/rails sで起動します。
  • ブラウザでhttp://usousouso.lvh.me:3000を開いてアプリ画面が表示されることを確認し、Railsをいったん終了します。

3. HSTSを有効にし、lvh.meにHTTP接続する

  • Railsアプリのconfig/environments/development.rbのRails.application.configure doブロックに以下を一時的に追加します。
config.force_ssl = true
  • ブラウザで再度http://usousouso.lvh.me:3000を開きます。以下の画面が表示されます。

このサイトは安全に接続できません
localhostから無効な応答が送信されました。
ERR_SSL_PROTOCOL_ERROR

Railsには以下のようなログが表示されます。

2020-04-30 12:19:55 +0000: HTTP parse error, malformed request (): #<Puma::HttpParserError: Invalid HTTP format, parsing fails.>
  • ここでRailsをいったん止め、config.force_ssl = trueをコメントアウトして再び起動しても、同じエラー画面が出ます。

ハマった点

話が横にそれますが、実はこのときの挙動がなかなかわからなくてebiさんとbabaさんに助けていただきました。このときのERR_SSL_PROTOCOL_ERRORは、HSTSそのもので設定されるのではなく、それより前の段階でHTTPSへの恒久的な301リダイレクトが発生して↓それがブラウザに記憶され、以後のHTTPアクセスでも同じリダイレクトが繰り返されます。

babaさんのご指摘のように、ブラウザ内のHSTSリストへの登録は、HTTPS接続を確立できてからでないと行えません。そしてここまでのセットアップではHTTPS通信を用意していません。

このとき表示されたERR_SSL_PROTOCOL_ERRORそのものは、単に「ブラウザから送ったプロトコルとサーバーが期待するプロトコルの不一致」を表すエラーであり、これ自体はHSTSともリダイレクトとも無関係であるとbabaさんから教わりました。

ebiさんに以下のリンクも教えていただきました。

参考: Strict-Transport-Security - HTTP | MDN

メモ: サイトに HTTP を使用してアクセスしたとき、ブラウザーは Strict-Transport-Security ヘッダーを無視します。これは攻撃者が HTTP 接続に介入して、ヘッダーを挿入したり削除したりするかもしれないからです。ウェブサイトに HTTPS でアクセスして、証明書のエラーがない場合、ブラウザーはサイトが HTTPS でアクセスできることを知り、 Strict-Transport-Security ヘッダーを信用します。
developer.mozilla.orgより

この記事ではすべてChromeのシークレットモードウィンドウで作業しているので、ウィンドウを閉じればいつでも元通りになりますが、これを通常ウィンドウでやってしまうと(以前localhostでこれをやってしまいました😅)、記事末尾に書いたようにブラウザのキャッシュを削除しないとそのURLではHTTPSへの301リダイレクトを取り消せません。

自分がこれを知らずに、RailsのHSTSをオンにして通常ウィンドウでlocalhost:3000にアクセスしてしまったとき、記事末尾のリンクを参考にChromeのchrome://net-internals/#hstsでHSTS設定からこのドメイン名を削除しようとしても、ドメイン名がそもそも登録されていなくて首を傾げたのですが、これが理由だったんですね…

4. HSTSを有効にし、ngrokでhttp接続する

ここからが通常のHSTSらしい動作になります。なおngrokでは同じURLでHTTP接続とHTTPS接続を両方使えます。

  • Railsをいったん終了し、再びconfig.force_ssl = trueを有効にして再度起動します。
  • ターミナルで別ウィンドウを開き、ngrok http 3000でngrokを起動します。

  • まずHTTPでアクセスしてみます。ngrok画面に表示されているhttp://で始まるURLをコピーして、シークレットモードウィンドウで開きます。

少々待たされますが、今度は以下のように307一時リダイレクトが発生し、正常にHTTPS接続できるようになります。

なおbabaさんによると、この307リダイレクトはChromeの内部実装によるもので、通信を伴わないそうです。まだ試していませんが、Firefoxでは起きないとも教わりました。

  • HTTPとHTTPSでそれぞれアクセスを繰り返します。

以後はHTTPでアクセスすると常に307でリダイレクトし、HTTPSでアクセスすれば普通にHTTP 200で接続されます。

以上でお試しはおしまいです。Chromeのシークレットモードウィンドウを閉じ、ngrokとRailsも終了します。development.rbの変更も元に戻しておきましょう。
あ〜すっきりした。

おまけ: Chromeを復元する方法

実は元々この部分を記事にしていました😅

たとえばHTTPS接続のないRailsのdevelopment環境でforce_ssl = trueをオンにしたまま、うっかりhttp://localhost:3000をブラウザで開いてしまうと、ブラウザにlocalhostドメインが記憶されてしまい、先ほどと同じくHSTSが発動してしまいます(してしまいました)。こうなるとそのブラウザではローカルの開発サーバーをlocalhostで開けなくなってしまいます。

急いでいるときなら、ブラウザのシークレットモードウィンドウでhttp://localhost:3000にアクセスするなりlvh.meを使うなりすれば取りあえず回避はできますが。

解決方法その1: ChromeのキャッシュとCookieを削除する

301リダイレクトが原因の場合は、この方法で復元します。

Chromeで⌘ + ,キーを押して設定を表示し、「セキュリティとプライバシー」の「閲覧データの削除」を開きます。

「Cookieと他のサイトデータを削除」「キャッシュされた画像とファイル」チェックボックスをオンにします。発生してすぐなら期間を「1時間以内」にしておくとよいでしょう。
「データを削除」をクリックし、Chromeを再起動します。

解決方法その2: ChromeのHSTS設定を解除する

以下の記事では、Chromeのchrome://net-internals/#hstsでHSTS設定からドメイン名を削除する方法が紹介されています。HTTPS接続が確立してブラウザのHSTSリストに登録された場合は、この方法で復元します。

参考: Google Chrome SSL Error Guide for “ERR_SSL_PROTOCOL_ERROR”

週刊Railsウォッチ(20200615前編)`rails new`に`minimal`がマージ、ARの共通集合を取る`and`、RubyMine+Dockerチュートリアル動画ほか

$
0
0

こんにちは、hachi8833です。オードリー・タンさんのPodcast聞いちゃいました😋


つっつきボイス:「Rebuild昨日やってたんですね」「どちらも英語のレベルも話のレベルも高くて脱帽でした🎩」「やはりリモートで対談」「見出しの『モナドは自分の仕事で必要というわけでもない』というあたりしか覚えてませんが😆」「こういう人のジョブがどんなのか興味あるな〜😋

参考: モナド (プログラミング) - Wikipedia

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

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

⚓ホストのcookieドメイン選択のマッチを厳密にした

# actionpack/lib/action_dispatch/middleware/cookies.rb#L442
        def handle_options(options)
          if options[:expires].respond_to?(:from_now)
            options[:expires] = options[:expires].from_now
          end
          options[:path]      ||= "/"
          options[:same_site] ||= request.cookies_same_site_protection
          if options[:domain] == :all || options[:domain] == "all"
            # If there is a provided tld length then we use it otherwise default domain regexp.
            domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
            # If host is not ip and matches domain regexp.
            # (ip confirms to domain regexp so we explicitly check for ip)
            options[:domain] = if !request.host.match?(/^[\d.]+$/) && (request.host =~ domain_regexp)
              ".#{$&}"
            end
          elsif options[:domain].is_a? Array
-           # ホストが、ドットが前に付いていないドメイン名のいずれかにマッチする場合
-           options[:domain] = options[:domain].find { |domain| request.host.include? domain.sub(/^\./, "") }
+           # hostが、渡されたドメインのいずれかにマッチする場合
+           options[:domain] = options[:domain].find do |domain|
+             domain = domain.delete_prefix(".")
+             request.host == domain || request.host.end_with?(".#{domain}")
+           end
          end
        end

つっつきボイス:「どれどれ、以前はexample.comを指定してた場合にexample.com.aumyexample.comにまでマッチしてたのか!」「それはイカン😅」「『cookieでは元々おかしなドメイン名は無視するから互換性はそんなに心配いらないだろうけど』とコメントにありますね」「ブラウザ側の実装上は一応大丈夫ということなのかな?🤔」「いずれにしろ正しい挙動にするのがいいですね👍」「行儀が悪いのを直したと」

⚓新機能: Active Recordでリレーション名.andをサポート

集合論で言う「共通集合」を取れるそうです。

参考: 共通部分 (数学) - Wikipedia


つっつきボイス:「@kamipoさんのプルリクでした」「おぉ、今度はandが入った🎉」「なるほど、サンプルコードのようにdavid_and_mary.and↓と書けるようになった😋」「今までだとwhere文のところにSQLを書かないとできなかったヤツですね」「なるほど!」「こういうクエリを書きたいときはあるので、Active Recordの機能でやれるようになったのはいいことですね👍

# 同PRより
david_and_mary = Author.where(id: [david, mary])
mary_and_bob   = Author.where(id: [mary, bob]) # => [bob]

david_and_mary.merge(mary_and_bob) # => [mary, bob]

david_and_mary.and(mary_and_bob) # => [mary]
david_and_mary.or(mary_and_bob)  # => [david, mary, bob]

「これいいな〜、いつ使えるようになるんですか?😋」「マージされたから次のリリースで入るのかな〜😋」「早く欲しいよ〜😂」「😆」「😆

⚓委譲をやめて属性アクセスを15%高速化

# activemodel/lib/active_model/attributes.rb#L135
      def read_attribute(attr_name)
        name = attr_name.to_s
        name = self.class.attribute_aliases[name] || name

-       _read_attribute(name)
+       @attributes.fetch_value(name)
      end

つっつきボイス:「こちらも@kamipoさんでした」「read_attribute周りの修正か」「『たった1行のメソッドに委譲する意味はないので避けた』と書かれてますね」「こういう修正の見当が付くのがスゴい💪」「メソッド自動生成がらみはわけわからなくなりがちですし😭」「@kamipoさん止まらないですね」

わずか1行のメソッドに委譲するほどの価値はない。委譲を避けることでread_attributeを15%高速化できた。
同コミットより大意

Warming up --------------------------------------
read_attribute('id')   165.744k i/100ms
read_attribute('name')
                       162.229k i/100ms
fast_read_attribute('id')
                       192.543k i/100ms
fast_read_attribute('name')
                       191.209k i/100ms
Calculating -------------------------------------
read_attribute('id')      1.648M (± 1.7%) i/s -      8.287M in   5.030170s
read_attribute('name')
                          1.636M (± 3.9%) i/s -      8.274M in   5.065356s
fast_read_attribute('id')
                          1.918M (± 1.8%) i/s -      9.627M in   5.021271s
fast_read_attribute('name')
                          1.928M (± 0.9%) i/s -      9.752M in   5.058820s

⚓同じカラムをmergeしたときの条件の扱いをRails 6.2で統一

従来の振る舞いは非推奨になるとのことです。

同じカラムでのmergeで両方の条件を維持しないようになり、Rails 6.2では常に後者の条件で置き換えられるようになる。
Rails 6.2の振る舞いに移行するにはrelation.merge(other, rewhere: true)を使うこと。
Changelogより

# Changelogより
# Rails 6.1 (IN句はマージする側の等価条件で置き換えられる)
Author.where(id: [david.id, mary.id]).merge(Author.where(id: bob)) # => [bob]

# Rails 6.1 (両者の条件に矛盾が存在する: 非推奨化済み)
Author.where(id: david.id..mary.id).merge(Author.where(id: bob)) # => []

# Rails 6.1 で`rewhere`を用いることでRails 6.2の振る舞いに移行する
Author.where(id: david.id..mary.id).merge(Author.where(id: bob), rewhere: true) # => [bob]

# Rails 6.2 (IN句と同じ振る舞い、マージされる側の条件は常に置き換えられる)
Author.where(id: [david.id, mary.id]).merge(Author.where(id: bob)) # => [bob]
Author.where(id: david.id..mary.id).merge(Author.where(id: bob)) # => [bob]

つっつきボイス:「この間から@kamipoさんがmerge周りに手を加えてましたね」「この間のrewhere: trueあたりとか(ウォッチ20200525)」「今後振る舞いが少し変わるのか🤔」「今回はdeprecatino warningを出すようにしたんですね😋

関連PR: #39250と#39236
今回の変更の目的は、マージの振る舞いがこれまで一貫していなかったのを統一すること。
現在は、マージされる側(mergee)の条件がマージする側(merger)によって置き換えられるのは、双方のarelノードが等価条件またはIN句の場合のみだった。
言い換えると、マージされる側の条件が等価条件でもIN句でもない場合(betweengtltなど)、双方の条件は同じカラムであっても維持されていた。
これではマージの振る舞いに熟知していないと予測が難しい。
元々自分は、この振る舞いは意図したものではなく単なる実装上の問題だと推測していた。理由は、mergeより後に導入されたunscoperewhereの挙動はmergeよりも一貫性が高くなっているため。
等価条件やIN句は条件のほとんどを占めるのが普通なので、この問題を踏んだことのある人はほとんどいないだろうと推測しているが、一貫性に欠ける現在の振る舞いを非推奨化し、将来のUXを改善するために今後完全に統一したいと思う。
同PRより大意


「ところでRails 6.2っていつ頃出るんでしょう?🤔」「どうでしょう、新し目の機能もだいぶたまってきた感ありますし、そんなに先の話じゃないのかな?」「さっきのmerge周りとかは挙動が変わるので、今のうちにテスト書いておきたいですね☺」「今作ってる機能が月末リリースなので早いとこRailsのバージョン上げたいです〜😅」「今月だと絶妙に微妙そう😆

後でRailsのマイルストーンを見てみました。

  • マイルストーン: 6.1.0 Milestone — 現時点で92件中残り21件
  • マイルストーン: 6.2 Milestone — 現時点で3件中残り2件

「そういえばmergeの変更はコンフィグで現状維持できるんでしたっけ?🤔」「コンフィグあったかな…😅」「もしかすると今後コンフィグを追加するのかも?」「リリースまではまだ多少時間もありますし、そうするかもしれませんね☺」「breaking changeならコンフィグ欲しいかも」

⚓続報: rails new--minimalオプション機能がマージ(Ruby Weeklyより)


つっつきボイス:「この間の#39444--interactiveが入るかと思ったらこちらが先でした」「minimumじゃなくてminimalとは😆」「ほぼスッピンの形でrails newやれるようになるそうです」

# railties/lib/rails/generators/rails/app/app_generator.rb#300
+       if options[:minimal]
+         self.options = options.merge(
+           skip_action_cable: true,
+           skip_action_mailer: true,
+           skip_action_mailbox: true,
+           skip_action_text: true,
+           skip_active_job: true,
+           skip_active_storage: true,
+           skip_bootsnap: true,
+           skip_dev_gems: true,
+           skip_javascript: true,
+           skip_jbuilder: true,
+           skip_spring: true,
+           skip_system_test: true,
+           skip_webpack_install: true,
+           skip_turbolinks: true).tap do |option|
+             if option[:webpack]
+               option[:skip_webpack_install] = false
+               option[:skip_javascript] = false
+             end
+           end.freeze
+       end

「ミニマルにするとAction Mailerもスキップされるのか😆」「Active Strage、最初はなくてもいいかな😋」「JBuilderは今となってはなくてもいいでしょう😆」「Webpackも飛ばせるとは、まさしくミニマル😳」「すっぽんぽん🤣」「普通のAPIモードよりさらに禁欲的というか🤣」「システムテストも飛ばしてる🤣」「スースーしそう🤣

参考: Rails による API 専用アプリケーション - Railsガイド

「『いいね👍』付けてる人がめちゃ多い」「嬉しい人は嬉しいでしょうね😋」「Railsに慣れた人がうんとささやかなAPIサーバーを作りたいときなんかはミニマルだとありがたいかも☺」「JavaScriptも切るあたりがAPIを想定してるっぽいかも😋

「インタラクティブなrails newはそれはそれでやるのかな?」「Railsの場合、機能同士の依存関係なんかもあるので、何を外すべきかを考えるのって割と難しいところはありますね😅」「あ、そうですね😳」「ミニマルで作っておいて、機能が必要になったら後から足す方が理にかなってそうですし🧐

「そもそもどの機能を外したらいいのかが自分に見当が付かないという😆」「デフォルトだと機能が山盛りだからなおさら😆」「これとこれは競合するとか、これはこれに依存するとか」「機能を外したつもりなのに外れてなかったこともあった気がします😅」「Action TextがActive Storageに依存してるんでしたっけ、だとするとActive Storageを外したつもりでもAction Textを使うとまた入ってきたり😆

⚓Rails

⚓RailsアプリをJS抜きで同じに作り直してみた


つっつきボイス:「この記事こないだ読んだんですけど意図がよくわからなかったかも😅」「前に作ったRailsアプリを、機能を変えずにJS抜きで作り直したのかな?🤔」「サーバーサイドだけのアプリケーションにしてみたという感じかも」

# 同記事より
# app/views/todos/_todo.html.erb

<div id="<%= dom_id(todo) %>" class="ToDoItem">
  <p class="ToDoItem-Text"><%= todo.name %></p>
  <%= button_to "-", todo_path(todo.id),
      method: :delete,
      remote: true,
      class: "ToDoItem-Delete"
     %>
</div>

「こんなふうに↑前はJSでDOM制御していた画面を、あえてサーバーサイドスクリプトを通して実行するようにAction Cableで書く、みたいな😆」「JavaScriptがキライなのが伝わってきそう😆」「断捨離というか😆

「以下のhtml: render_to_stringのあたりなんかはHTMLでレンダリングしてますね: しかも書き換えは[:html]を指定してHTML置換してますし☺」「男らしい💪」「カッコイイ✨」「フロントエンドの人たちから何か言われそうですけど😆」「想像できます😆

# 同記事より
# app/controllers/todos_controller.rb

def create
  todo = Todo.new(todo_params)

  if todo.save
    cable_ready[TODOS_CHANNEL].insert_adjacent_html(
      selector: "#todo-list",
      position: "afterbegin",
      html: render_to_string(partial: "todos/todo", locals: {todo: todo}, formats: [:html])
    )
    cable_ready[TODOS_CHANNEL].set_value(
      selector: "#todo_name",
      value: ""
    )
    cable_ready[TODOS_CHANNEL].remove(
      selector: ".error"
    )
    cable_ready.broadcast

    return render(plain: "", status: :created)
  end

  cable_ready[TODOS_CHANNEL].insert_adjacent_html(
    selector: "#todo_name",
    position: "afterend",
    html: "<p class='error'>#{todo.errors[:name].first}</p>"
  )
  cable_ready.broadcast

  render json: {errors: todo.errors.to_h}, status: :unprocessable_entity
end

「まあ昔はこういう書き方が結構使われてましたね: Railsでも今はなきRJSとかで、JSを含むパーシャルとHTMLを含むパーシャルをサーバーサイドでいい感じに返して、それを使ってDOMを書き換えるようなコードは自分も書いてましたし🧐」「自分も当時そういうコードいっぱい書いてたら後の時代にフロントエンドの人に怒られました😅」「まあ治安の悪さを考えれば、この書き方がなくなったのもワカル😆

参考: RubyOnRails を使ってみる 【第 7 回】 RJS を使ってみる

「でもあの当時はjQueryが一般的な時代でしたし」「そうですよね」「あの時代はJSフレームワークと言うと他にBackbone.jsぐらいしか見当たりませんでしたし、当時はいろいろしょうがないと思います😆」「時代が違うのでご勘弁を😆


記事見出しより:

  • Action Cableをセットアップする
  • JavaScriptコードを消し去る
  • JavaScript抜きで機能を再実装する
  • 締めくくり

⚓ActiveModel::AttributeAssignmentとは


つっつきボイス:「今日のWebチーム内発表で話題に出た機能です」「そうそう☺

「Railsのフォームに標準で入ってくるDate/Timeセレクタって、そのままだと『年』『月』『日』『時』『分』『秒』という6つのセレクトボックスができるんですけど、それをそのままPOSTすると当然ながら6つのパラメータに分解されてから送信されるので、それを組み立てる仕事をやってるのがこれだそうです」「へぇ〜😳」「hashアトリビュートになっている値を渡すとよしなにやってくれるらしいです😆

# 同APIより
class Cat
  include ActiveModel::AttributeAssignment
  attr_accessor :name, :status
end

cat = Cat.new
cat.assign_attributes(name: "Gorby", status: "yawning")
cat.name # => 'Gorby'
cat.status # => 'yawning'
cat.assign_attributes(status: "sleeping")
cat.name # => 'Gorby'
cat.status # => 'sleeping'

「勉強会のお題ではActive Recordを使わないでActive Modelだけでやってたので、この機能を明示的に使う必要があったんだそうです」「へ〜、assign_attributes()ってここに実装されてるのね😳」「好きな人が多い機能でしたっけ」「assign_attributes()は普通によく使うヤツですね☺

後で調べると、assign_attributes()はRails 3.1のときにActive Recordに入ってたんですね。APIdockを見た感じでは、5.0のときにActive Modelに引っ越したようです。

参考: Rails 3.1: assign_attributesメソッド - Rails 雑感 - Ruby on Rails with OIAX
参考: assign_attributes (ActiveModel::AttributeAssignment) - APIdock

「そうそう、Qiitaの記事↓でいうとPOSTされたときはこんな感じデータになってたのを、このメソッドでいい感じにRailsのTimeWithZoneに変換してやってくれたということで」「Rails標準だとこういう形になるよねという話」

参考: 【Tips】Rails の assign_attributes は分割されたパラメータを飲み込む - Qiita

# Qiita記事より
params
=> {
    "name"=>"ダミー名前",
    "reserved_at(1i)"=>"2020",
    "reserved_at(2i)"=>"3",
    "reserved_at(3i)"=>"2",
    "reserved_at(4i)"=>"00",
    "reserved_at(5i)"=>"00"
  }

「記事はこの動作に興味を持って追ったんですね」「実際、POSTでやってくるこの謎パラメーターに一度は首を傾げますし😆」「1iとか2iとか😆

⚓geared_pagenation: 速度可変のページネーション(Ruby Weeklyより)


つっつきボイス:「Basecampが直々に出してきたgemのようです」「gearedというと回るギアの?⚙」「自動車の変速装置というかトランスミッションみたいな動作をイメージしてるのかな🤔

# 同リポジトリより
class MessagesController < ApplicationController
  def index
    set_page_and_extract_portion_from Message.order(created_at: :desc)
  end
end
# app/views/messages/index.html.erb

Showing page <%= @page.number %> of <%= @page.recordset.page_count %> (<%= @page.recordset.records_count %> total messages):

<%= render @page.records %>

<% if @page.last? %>
  No more pages!
<% else %>
  <%= link_to "Next page", messages_path(page: @page.next_param) %>
<% end %>

「なるほど、人間の性格として、ページネーションのあるページをオートスクロールするときなんかだと、2ページ目ではそんなにたくさん表示しなくてもいいけど、2ページ目まで開いた人なら3ページ目はどうせ開くだろうし、もっとたくさん表示して欲しいと思うでしょうから」「ふむふむ」「READMEにも書いてますけど、たとえばページのelementsは1ページ目なら15個でいいけど、2ページ目なら30個、3ページ目なら50個、4ページ目なら100個…みたいにだんだん増やしていく、というのをgeared pagenationと呼んでるんでしょうね😋」「な〜るほど!」

「実際ページのスレッドを追いかけて次々にページをめくっていると、どうせなら先に進むに連れて多めに読み込んで欲しいって思うことありますし☺」「ときにはページネーションなしで一気に全ページ出して欲しいと思うときもありますけど😆」「それもわかる😆」「マウスホイールをゆっくり回したときと勢いよく回したときでスクロールの距離が違うみたいな🐭」「そんな感じ」

「なるほど〜という感じのgemですけど、これをサーバーサイドでやるのかという気持ちはちょっとありますね😆」「😆

⚓Railsでメモ化しない方がいい場合(RubyFlowより)

# 同記事より
def slow_method
  @result ||= perform_slow_method
end

つっつきボイス:「メモ化を使わないとき」「記事にもありますけど、いつも言ってる『それはメモ化してもセーフなのか?』というヤツ😆」「あ、なるほど」「クラス変数やクラスインスタンス変数をメモ化すると競合が発生する可能性がありますし、スレッドで使ったときもそうですし🧐

「メモ化ってそんなに好きというほどじゃないかも😆」「もちろん何でもメモ化するのはよくありませんけど、Active Recordで毎回pluckで取ってきたりすると遅くなるデータもあるので、自分は状況に応じてメモ化を使いますね☺

Rails: pluckでメモリを大幅に節約する(翻訳)

⚓Railsマイグレーションのup_only


つっつきボイス:「めちゃめちゃ短い記事なんですけど、こんなのあるって知らなかったので😳」「up_onlyですって😳」「で探してみたらkoicさんの少し前の記事が見つかりました↓」「up_onlyが当初RuboCopでアラート出たからCopに書き足してくれたのね😋

up_onlyというとdownはやらないということでしょうか?」「でしょうね、データを更新するマイグレーションなんかだとdownしたくないこともありますし☺

⚓その他Rails


つっつきボイス:「JetBrainsの動画記事です」「RubyMineは前からDocker Composeをサポートしてるけど新しい機能でも増えたのかなと思ったらチュートリアル動画ね☺」「コメント欄でjnchitoさんが『これは素晴らしい!』と激賞していますね」「RubyMineとDocker Composeか〜😋

⚓JetBrains IDEのDocker Composeインテグレーション

「ところでRubyMineというかJetBrains IDEのDocker Composeインテグレーションは、右クリックでexecできたりしますし、なかなかよくできてますよ❤」「おぉ😍

「記事からリンクされてるこれ↑もいい機能ですし🥰」「リモートインタプリタ?」「つまりローカル環境にRubyがなくても、Docker Composeの中で動かすDockerコンテナの上にあるRubyをリモートインタプリタとして指定できます👍」「そしたらローカルにわざわざいろんなバージョンのRuby入れんでもええよねと😋」「そうそう、OpenSSH 1.0.いくつを使わないとコンパイルできないような古〜いRubyでもDockerコンテナに乗ってれば作業できますし😆」「この間の古いRubyコンパイル話っすね😆


前編は以上です。

おたより発掘

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

週刊Railsウォッチ(20200609後編)Rubyにカスタマイズ可能な軽量fiberスケジューラを実験導入、RailsとGraphQL、DBについて知って欲しいことほか

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

週刊Railsウォッチ(20200616後編)本番環境をFullstaq Rubyに換えた理由、CSRF発生フローチャート、DBのトランザクション分離レベル比較ほか

$
0
0

こんにちは、hachi8833です。風向きが変わりつつある今日このごろですね。


morimorihoge注:本トピックに関するコメントとして、初出時人種差による不平等を容認・揶揄するようにも取れる内容がありましたので当該部分を削除させていただきました。
Twitter上でのご指摘を受けて社内ヒアリングを行ったところ、私を含む参加メンバーで主題の認識違いがあり、主題のすれ違いの中話していた内容をテキスト化した際に公開記事として掲載するにあたって不適切な内容となってしまっていたことが判明いたしました。不快に感じられた方にはこの場を借りてお詫びいたします。
※BPS株式会社は会社・組織として人種その他先天的・後天的な要因による差別を容認・支持する立場にはありません(弊社代表にも確認済み)
  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

Ruby

Rubyが自動化に向いている理由(Hacklinesより)


つっつきボイス:「比較の対象がJSやJavaやPythonになってます」「まJavaでやるのはね😆」「自動化という感じじゃないですね😆」

「向いている理由1がリフレクションとブレークポイント?」「Cucumberをbreakしてデバッグ、今ならJSにもこういうのは普通にありますけど😆」

「記事書いている人はRubyが好きだからRubyでやりたい感😆」「まあこういうふうに書ける↓のはたしかにRuby以外ではあんまり見かけませんけど☺️」

# 同記事より
class LoginPage < SitePrism::Page
  set_url '/login.html'

  element :un, 'input#username'
  element :pw, 'input#password'
  element :submit, 'input#submit'
end

「Cucumberでこういうふうに書ける↓なんてのもそうですし」「そうですね〜」

# 同記事より
Given("I login with valid credentials") do
  $LoginPage.load
  $LoginPage.un.set 'jeff@amzn.corp'
  $LoginPage.pw.set ENV['TESTPW']
  $LoginPage.submit.click
  $LoginPage.should_not have_error
  $Dashboard.should be_displayed
end

「Rubyだと人間による操作をキレイにコード化しやすいという気持ちは何となくわかりますね😋」

site-prism

「ところでSitePrismとかいうCapybara支援DLSがあるみたい↓」「おや👀」

参考: RubyでPageObjectsパターンを実装できる SitePrism のご紹介 - Qiita
参考: CapybaraとSitePrismを使ってみる - Qiita

piperator: Elixir風パイプライン演算子(Ruby Weeklyより)


つっつきボイス:「何て読むんだろか?」「パイペレーター?」「パイプライン演算子(pipe operator)のもじり?😆」

参考: パイプライン演算子 - Wikipedia

# 同リポジトリより
Piperator.
  pipe(->(values) { values.lazy.map { |i| i * 3 } }).
  pipe(->(values) { values.sum }).
  call([1, 2, 3])
# => 18

「何をするんでしょうね?😆」「きっと普通にパイプラインというかストリーム処理をやるんでしょう😆」「内部でスレッドになってたりしそう」「クローラーなんかだとこういうパイプライン的な処理やりますね」「パイプライン処理をサポートするツールがあれば、メモリも節約できるでしょうし😋」

「そういえばRubyにもパイプライン演算子|>が入りかかった後でrevertされてましたね↑」「その辺は、本当の意味でのパイプラインになっているかどうかという問題もありますし😆」「それもそうですね😅」「パイプラインっぽく見えるのとパイプライン処理をできるというのはまったく別の話なので☺️」「ですよね☺️」「詳しくは見てませんが、このpiperatorはlazyとか使ってたりしますし、たぶん処理としてパイプラインになっているんじゃないかな〜😋」「超巨大な円周率データファイルだって処理できますよ、なんて😆」

Rubyを含むパイプライン演算子の流れについては以下の記事が詳しいです。

参考: パイプライン演算子の歴史 - まめめも

Fullstaq Rubyに乗り換えた理由(Hacklinesより)


つっつきボイス:「Evil Martiansの記事に続いてFullstaq Rubyに乗り替えたところが出ましたね」「お〜、最初から使える案件ならFullstaq Rubyはもう使ってみてもいいよね❤️」「使ってはいけない理由もありませんし😋」「上の記事でもまさに同じことを言ってました」

Fullstaq Rubyの第一印象とDocker/Kubenetes Rubyアプリとの統合(翻訳)

「途中からFullstaq Rubyに乗り換えるのはしんどそうだけど、最初から使えるならもう別に使ってもいいでしょう😆」「Dockerで使えば環境も関係なくなるし😋」「結局違いはjemallocを使うところですし」「mallocよりいいと言われているjemallocですね」

参考: jemalloc について調べたのでまとめた - zonomasaの日記

Rubyとjemallocの関係については以下の記事もどうぞ。

Ruby: mallocでマルチスレッドプログラムのメモリが倍増する理由(翻訳)

test-bench: 語彙を絞り込んだテストフレームワーク

# 同リポジトリより
context "Some Context" do
  context "Some Inner Context" do
    test "Some test" do
      # ...
    end
  end
end

つっつきボイス:「まだ★は少ないんですが、ボキャブラリーを増やしすぎないところがちょっといいかなと思って」「ボキャブラリー少ないのは重要😆」「普通にアサーションするタイプのテストフレームワークという感じ☺️」「こうやって車輪が再発明されていくのかなと🚗」

# 同リポジトリより
assert(true)               # Passes
assert(false)              # Fails
assert(1 == 1)             # Passes
assert(some_object.nil?)   # Passes if some_object is nil
assert(1 > 1)              # Fails

refute(true)               # Fails
refute(false)              # Passes
refute(1 != 1)             # Passes
refute(!some_object)       # Passes if some_object is *not* nil

「そういえばrefuteって何だっけ?」「あ〜ど忘れ😅」「見るからにassertの逆なんでしょうけど😆」「今辞書を引いたら『論破する』でした」「そうそう、『そうでないこと』を主張するヤツ😆」「言われてみると普通のアサーションだと冗長になりそうなときとかにrefuteがたまに欲しくなるかも」「アサーションに!付けたくないときとか」「語彙を増やさないなら!でもよかったりして?😆」「equalityチェックみたいに単純なものならいいんですけど、!付けるだけだと表しにくいものもありますし🧐」「refuteの方がシンプルに書けますよね、わかります😋」


RubyのMiniTestにもrefuteがあるのですが↓、そういえばRSpecでは聞いたことありませんね。

参考: module MiniTest::Assertions - Documentation for Ruby 2.1.0

「ところで公式サイトのドメイン名がtest-bench.softwareですし↓」「ドットsoftwareドメイン?😳」「そんなドメイン名使えるんだ😆」

DB

コンカレントシステムにおけるデータベース一貫性モデル


つっつきボイス:「何だか見慣れない図😅」「図のピンクはネットワーク障害によっては利用できなくなる可能性があるけど青はできるみたいな感じだそうです」「モデルというのはデータベースを含むシステムのモデリングの話なのね☺️」


同記事より

「それぞれの要素はだいたいわかるけど、全体としては何を表してるんだろう?🤔」「よくわかんな〜い😆」「この記事はどこから?」「この間話題にした『データベースについて知っておきたかったこと17(英語)』記事からここにリンクされているのを見つけました(ウォッチ20200609)」「なるほど、そういう文脈ね☺️」

「ざっと見た感じ、データベース屋さんとか分散システム屋さん向けのsurvey paperをまとめたものっぽい: リンク先は最終的に論文のリファレンスになってますし🧐」「たしかに図のピンクやオレンジや青のブロックをクリックするとその用語の説明や関連論文を説明するページが開きますね😳」「ほんとだ」

「情報は新しいのかな?」「このサイトにはコピーライト以外に日付情報が見当たらなくてよくわかりませんけど😅」「新しめの論文も参照してるみたい😋」「たぶんデータベース一貫性モデルのsurvey情報としてはいいサイトなんだろうなと思います👍」

RDBMSごとのトランザクション分離レベルの違い


つっつきボイス:「これも同じく『データベースについて知っておきたかったこと17(英語)』記事からのリンクで知りました」「PostgreSQLやMySQLとかが載ってる↓👀」


同リポジトリより

参考: トランザクション分離レベル - Wikipedia

「見た感じ、SerializableやRead Uncommitedのようないわゆる一般的なトランザクションレベル↑の用語が、実際にはRDBMSごとにどういう用語で表されてどう実装されているのかというのをまとめたんでしょうね🧐」「ふむふむ」「たとえば同じRepeatable Readでも、ぽすぐれの場合とMySQLの場合ではそれぞれこうなってるよ、みたいに」「あ、そういうことか😋」「ちょっと見えてきた😋」「そういえばリンク元記事でも『トランザクション分離レベルはRDBMSごとに違う』という話の中で引用してました」

「G0とかG1aとか何だろうと思ったらページの下に略語の意味が書いてあった😅」「厳密に追っていくと、本当はRDBMSごとにこれだけ細かく違ってるということなんでしょうね」「ここまで理解しているWeb系エンジニアっているんでしょうか?😭」「あんまりいないでしょう🤣」

「Oracleやってる人ならそれなりに知ってるのかな?🤔」「Oracleはほんのちょっと触ったけどMSSQLわかんない😆」「MSSQLの分離レベルがなぜか他より細かく分かれてる😳」「最後のFDB SQL Layerって何ですかこれ?😆」「全然知りませんけど😆、ググったらこんなの出てきましたね↓」「FoundationDBって知らな〜い😆」「分散データベースに特化してるっぽいことが書いてある👀」

「お、コード例も載ってる↓👍」「検証用のテストコードもあるんですね😋」「これも研究屋さんまたはデータベースの専門家が作った資料っぽいですね🧐」

-- 同リポジトリより(PostgreSQLの場合)
begin; set transaction isolation level read committed; -- T1
begin; set transaction isolation level read committed; -- T2
update test set value = 11 where id = 1; -- T1
update test set value = 12 where id = 1; -- T2, BLOCKS
update test set value = 21 where id = 2; -- T1
commit; -- T1. This unblocks T2
select * from test; -- T1. Shows 1 => 11, 2 => 21
update test set value = 22 where id = 2; -- T2
commit; -- T2
select * from test; -- either. Shows 1 => 12, 2 => 22

「資料末尾のリファレンスも、VLDBとかSIGMODみたいなトップ会議がずらっと並んでいるし、データベースの専門家ならこの辺のリファレンスは常識的に知ってるということなんでしょう、たぶん🧐」「本来ならここまで勉強せいと😅」「リポジトリが6年前だからあんまり新しくないのかと思ってました😅」「新しい古いというよりは研究系のサーベイですし☺️」

「ところで、こういうサーベイって大学院の情報系学科なんかで課題に出そう🎓」「こんな課題出るんですか〜?😅」「CS系だとあってもおかしくないですヨ☺️」「ちょっと新しい世界を垣間見たかも😆」「こんな世界もあるんだな〜😅」

クラウド/コンテナ/インフラ/Serverless

IPA-DN-EasyBgpStarterKit


つっつきボイス:「はてブでもバズってました」「そうそう、さんの作ったこの資料がとてもよくできてるって知り合いとも話題になりましたし😆」「何だかパワーワードっぽい言葉がいっぱいありますね😆」「読んでいくうちにだんだんエクストリームな言い回しに気がつくという😆」「おぉ〜おもしれ〜😆」「真ん中辺の図の『上流ISPにうまいこと言ってタダで貰ってきた1GbpsのBGP接続ポート』なんていうパワーワードも🤣」「『あっ、あのけしからんソフトイーサ社だな』とか文章のレベル高い🤣」

「オモシロイけど途中からだんだん難しくてわかんなくなる😅」「お、この辺のインフラ方面はあんまりやらない感じですか?」「まあ😅」「要はAS番号取ってBGPで完全二重冗長のネットワークを組んでみたぜという感じなので、やりたいことはよくわかります: IPv4のフルルートを取るところとか楽しそう😆」

参考: BGP(Border Gateway Protocol)とは

「よく見たらすっごく長い😅」「チュートリというには長いですけど手順がっつり明らかにしてますし☺️」「『友達のプロバイダー仲間に馬鹿にされないように、自宅の BGP ルータとして Cisco CRS-3 や ASR9000 などを利用する場合の設定サンプル』なんてのもある🤣」「BPSルーター持ってなくても馬鹿にされませんし普通😆」「いろいろ常人を超えてる🤣」「あなたの自宅にハイエンドBGPバックボーンルータを設置したくなるでしょうって、なりませんよ🤣」「まあいつもの登さん節ですね😆」「やっぱり有名な方なんですね?」「この道では普通に有名です☺️」「本気出した天才プログラマー肌ですね」(以下延々)

物理レイヤの障害


つっつきボイス:「これは?」「サメが海底の通信ファイバーケーブルをかじって通信障害が発生した瞬間を捉えた貴重な記録映像だそうです📽」「ぶほっ😇」「え?え?」「かじりやがった…こいつめ😭」「本気でかじってますよこの子😳」「まさにシャークバイト」「これでデータ不整合とか起きたら報告書に何て書くんでしょうね😆」「サメのせい🦈」

「一説にはケーブルの電磁波に誘われたのかもって」「それあるらしいですね😆」「ケーブルに歯型が残ってたとかならありそうですけど、よくその瞬間を撮影できたな〜😳」「どうやって撮ったんでしょ?」「障害調査中か、はたまた通常メンテ中にたまたま見つけたのかな?🤔」

「今さらですけど、ぼくらの快適なネット生活をこういう海底ケーブルが支えてくれてるんですね: 感謝しときます🙏」「ちなみにNetFlixなんから海底ケーブルに相当投資してますね💵」「へぇ〜、ねとふりって海底ケーブルにも手を出してるんですか😳」「彼らのビジネスへの影響が大きいインフラですし、凄い額の投資が必要ですし: こういった企業が海底ケーブルの主導権を巡って熾烈な争いを繰り広げているという記事も何かで見たことありますヨ☺️」

参考: 海底ケーブル - Wikipedia

その他インフラ

CSS/HTML/フロントエンド/テスト/デザイン

CSRFフローチャート


同記事より


つっつきボイス:「CSRFはこうやって起きるの図だそうです」「いいな〜、こういうの知りたかった😍」

参考: クロスサイトリクエストフォージェリ - Wikipedia

「CSRFって防がなきゃと思いつつどうなってるのかなって思ってました😅」「これだけだとあんまりわかりやすいとは思えないけど😆、チェックシート的に使えそうなのと、ちゃんと解決方法のパスも示されてるのがいいですね😋」「お、図の下にはちゃんとPOC(proof-of-concept)のサンプルコードまである↓のがエライ!❤️」「これなら実際に動かしてCSRFを確かめられる😋」「有能👍」

// 同記事より
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://www.example.com/api/setrole");
xhr.withCredentials = true;
//application/json is not allowed in a simple request. text/plain is the default
xhr.setRequestHeader("Content-Type", "text/plain");
//You will probably want to also try one or both of these
//xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
//xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.send('{"role":admin}');

その他HTML


つっつきボイス:「NginxがHTTP/3をクイックサポートするという話、出てましたね😋」「HTTP/3、ついにやるのか〜」

参考: Webサイトの表示速度をさらに高速化!「HTTP/3」とは? | さくらのSSL

# 同記事より
    $ hg clone -b quic https://hg.nginx.org/nginx-quic
    $ cd nginx-quic
    $ ./auto/configure --with-debug --with-http_v3_module       \
                       --with-cc-opt="-I../boringssl/include"   \
                       --with-ld-opt="-L../boringssl/build/ssl  \
                                      -L../boringssl/build/crypto"
   $ make
   $ #sudo make install ##面倒くなければ install してしまっても良い

--with-http_v3_moduleを付けてコンパイルするのね↑」「ChromeのDevToolsでh2-27とドラフトの番号表示されてる👀」「ChromeもCanaryを--enable-quic --quic-version=h3-27で起動しないといけないと」「あ、クライアントもか😳」「クライアントも対応しないとできませんし😆」

NginxのHTTP/3対応版が公開されました。実際に動かしていきます。(なお、現在サポートしているのはHTTP/3 draft 27版です)
同記事より

「HTTP/2のときにもnginx-buildとか使って頑張って対応したの思い出しました😭」

参考: nginx-build〜nginxのビルドプロセスを自動化〜 - Mercari Engineering Blog

言語/ツール/OS/CPU

迷うとき


つっつきボイス:「方向性の似ているツイートをまとめて貼りました」「1つ目のツイート、関数型言語にも戻り値の配置が普通と逆になるものがあったような?🤔」「C言語だと戻り値の型を最初に書きますもんね😋」「そうそう😆」「mattnさんのツイートだから『関数名の後に戻り値の型を書いて』はGo言語ですね😆」

「2つ目は久々にCoffeeScriptやったらハマった話」「こ〜ひ〜すくりぷとか〜😆」「if文なのにendいらないよ〜って😆」「インデントで考えるところで頭おかしくなりそう😆」「CoffeeScriptで書かれている以上やるしかない😅」

参考: CoffeeScript - Wikipedia

「実はCoffeeScript好きだったんだけどな〜😅」「あの頃は数行しか書かないみたいなところがありましたよね: ちょっと複雑になってくるとインデントレベルを追えなくなる😇」「それはあります😆」

「3つ目は、1つの記号を2つ以上の意味で使わないということなのかなと」「プログラミングを初心者に教える立場としてはその方がありがたい面はあるでしょうね☺️」「C言語がいいっていう人たちは『言語仕様が小さいからいい』って言ってたりしますけど🤣」「まあそうなんですが😅」「難しさって人によってもレベルによっても違ってくるのが悩ましいですよね: 言語仕様は小さいけどポインタみたいな難しい概念がある言語がいいのか、予約語はいっぱいあるけど、1つのやり方には1つの書き方しかない言語がいいのか、みたいに☺️」「人生いろいろ、言語もいろいろ」

その他

用語をどうするか問題


つっつきボイス:「GitHub CLIのブランチ名をmaster以外の名前に変えようというissueが出てて、trunkとかどうだ?みたいな話が出てました😆」「trunkにしようというのはウケる😆」「以前のRubyはSubversionでリビジョン管理してたからメインブランチがtrunkでしたね😆」

参考: Apache Subversion - Wikipedia

「issueに『generally problematic term』ってあるから、やっぱり今米国で騒ぎになっているあの問題がらみなんでしょうね」「え〜、こんなところにまで…😅」「masterだとslaveを連想させるときがあるから云々で」「と言われても…😅」「大昔のハードディスクなんかによくあったマスター/スレーブもプライマリ/セカンダリに変わったりしてましたね」(以下延々)

つっつき中は気づきませんでしたが、後でこのissueを見ると本当にこのツールのmasterがtrunkに変更されていました😳。

番外

聞こえてますか…?


つっつきボイス:「プログラミングのときに活性化する部分が、どうやら会話のときに音声を理解する場所らしいと」「誰かの声って、自分の中のもうひとりの自分なのかもって思いました」「バグを発見すると右脳前頭部の活動が瞬間的に跳ね上がるって😆」

「皆さんもやっぱり聞こえてるんでしょうか?」「自分の実感としては、プログラミングの最中にかける音楽に歌が入ってると作業効率ががっくり落ちますね📉」「それすごくわかる!」「歌入りはアカン😆」「なのでだいたいゲーム音楽のサントラみたいなインスト音楽になっちゃいます🎶」「歌ものが作業中に合わないって言う人、たしかに割と見かけます」

「無意識のうちに脳が言葉を処理しようとして動き出しちゃうからでしょうね☺️」「それがコーディング作業とバッティングするというのは、まあわからなくもないかも🤔」「あと脳内デバッグというか脳内で処理を流しているときなんかも」「よくわかんないけどわかる感じ😆」


後編は以上です。

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

週刊Railsウォッチ(20200615前編)`rails new`に`minimal`がマージ、ARの共通集合を取る`and`、RubyMine+Dockerチュートリアル動画ほか

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

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

Ruby Weekly

Hacklines

Hacklines


セキュリティアップデート: Rails 6.0.3.2および一部のgem

$
0
0

いつもより早めの公開です。

Rails 6.0.3.2セキュリティアップデート

Rails 6.0.xが対象です。

例外の詳細を表示しなければならないようカスタマイズしたいときはこのメソッドをオーバーライドする。このメソッドはconsider_all_requests_localがfalseの場合にのみ呼び出される。デフォルトではfalseを返すが、これをrequest.local?に設定すればproductionでのローカルリクエストに詳しい例外ページを表示することが可能。
APIドキュメントより大意


なお、現時点ではCVE-2020-8185は”RESERVED”になっており、具体的な記述はこれからのようです。

puma、rack、websocket-extensions gemにも別途セキュリティアップデート

こちらは上のRailsセキュリティアップデートとは別の情報です。私がオレオレRailsアプリをホスティングしているHerokuで無料で使えるSqreen.comというセキュリティチェックサービス↓から昨日アラートメールを受け取りました。

それによると、オレオレアプリ(Rails 5.2.4.3)で使われているpuma、rack、websocket-extensions gemが脆弱とのことでした↓。これらのgemをアップデートして対応しました。pumaは5月に修正が出ていたんですね。

puma

rack

websocket-extensions


以上は私の環境での観測範囲なので、まだの方はbundler-auditなどで各自の環境のセキュリティ情報を確認しましょう。

週刊Railsウォッチ(20200622前編)AR attributes周りの高速化進む、Active RecordでUNIONクエリを書く、Cable Ready gemほか

$
0
0

こんにちは、hachi8833です。GitHub Marketplaceの無料カテゴリを覗いてみたらいろいろありすぎてまごつきました。


つっつきボイス:「GitHub Marketplaceをちゃんと見たのは初めてでした」「GitHubリポジトリと連携できるアプリとかアクションとかですね👀」「IDEまであるのね」「エコシステムがあるのはありがたい🙏」「人気も表示して欲しいかなとちょっと思いました」「人気の指標次第では殴り合いが始まりそうですけど😆」「Chrome拡張みたいに😆」「誰かマーケットプレースをキュレーションしてくれるといいな」「やりたい人ならいくらでもいそうですけど」

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

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

以下のコミットリストのChangelogを中心に見繕いました。

⚓新機能: immutable_strings_by_default設定がActiveRecord::Baseに追加

全stringカラムをデフォルトでイミュータブルにできるそうです。Rails 4のときにマージされずにcloseした#29938↓に、MySQL向けの修正を加えたそうです。


つっつきボイス:「@kamipoさんによる修正です」「Active Recordのattribute:immutable_stringを指定できるようになったのか👀」「コンフィグにもimmutable_strings_by_defaultが入るのね😋」「この改修でちょっと速くなるみたい🚅

Warming up --------------------------------------
           user.name    34.811k i/100ms
      user.fast_name    39.505k i/100ms
Calculating -------------------------------------
           user.name    343.864k (± 3.6%) i/s -      1.741M in   5.068576s
      user.fast_name    384.033k (± 2.7%) i/s -      1.936M in   5.044425s

29938にMySQLのbooleanシリアライズの修正を加えたもの。
attributeのパフォーマンス改善を長年手掛けてきたが、(ミュータブルな)string型キャストはattributeで効率のよくない部分のひとつだ。
インプレースの改変を検出するため、string型は値の読み込みや代入をすべてdupすることで元の値を改変から保護するが、(ミュータブルな)string型の効率が低い理由がこれ。
イミュータブルなstring型は、読み込まれたり代入されたりする値をすべてfreezeすることでdupを回避でき、それによってfrozenな値はインプレース改変できないようになる。
string型attributeひとつと、そのイミュータブルなstring型のブランチによるシンプルなベンチマークは以下のとおり。
同PRより大意

# 同PRより
ActiveRecord::Schema.define do
  create_table :users, force: true do |t|
    t.string :name
    t.string :fast_name
  end
end

class User < ActiveRecord::Base
  attribute :fast_name, :immutable_string
end

user = User.new

Benchmark.ips do |x|
  x.report("user.name") do
    user.name = "foo"
    user.name_changed?
  end
  x.report("user.fast_name") do
    user.fast_name = "foo"
    user.fast_name_changed?
  end
end

「これが既存のコードに影響を与えることはまずなさそう: Active Recordで取ってきたstringのインスタンス自体を破壊的に変更することは普通ないだろうなと思うのでこれでいいでしょうし」「速い方がいいですよね😋

⚓Springをboot.rbに移動


つっつきボイス:「bundle exec spring binstub--allが要らなくなった」「#39622でspringがコケてた問題を修正したそうです」

1. `gem 'spring', group: :development`を`Gemfile`に追加
2. `bundle install`でspringをインストール
-3. `bundle exec spring binstub --all`でbinstubをspring化する
+3. `bundle exec spring binstub`でspring binstubを生成する

「DHHの元のプルリク↓も少し変えてるみたい」

「spring gemはいつの間にか動いててよくわからない😅」「マイグレーションがspringでつっかかったことはときどきあります😢」「割と謎だけど、Dockerコンテナで動かしている分には関係なくなることも多いのであまり気にならないかな」「springがあれば起動速くなりますし😋

⚓Rails 5より前の古いYAMLの読み込みを非推奨化

古いYAMLを使いたい人はLegacyYamlAdapterでやって欲しいそうです。


つっつきボイス:「古いyamlでdeprecation warningが出ると」「そろそろやっていい時期でしょうね」

# activerecord/lib/active_record/legacy_yaml_adapter.rb#L4
- module LegacyYamlAdapter
+ module LegacyYamlAdapter # :nodoc:
    def self.convert(klass, coder)
      return coder unless coder.is_a?(Psych::Coder)

      case coder["active_record_yaml_version"]
      when 1, 2 then coder
      else
+       ActiveSupport::Deprecation.warn(<<-MSG.squish)
+         YAML loading from legacy format older than Rails 5.0 is deprecated
+         and will be removed in Rails 6.2.
+       MSG
        if coder["attributes"].is_a?(ActiveModel::AttributeSet)
          Rails420.convert(klass, coder)
        else
          Rails41.convert(klass, coder)
        end
      end
    end

「その名もlegacy_yaml_adapter.rbなんてのがある↑」「Rails420とかRails41みたいに、書式が変わるたびにここに追加してたのね」「yamlのコンフィグファイルを使うテストで後方互換性をこうやって維持しているのか」「こういう実装は参考になる👍」「なるほど〜」「古い書式が必要ならLegacyYamlAdapterを使えばやれますし😋

⚓Arelで使われていないEquality#operatorを削除

# activerecord/lib/arel/nodes/equality.rb
module Arel # :nodoc: all
  module Nodes
    class Equality < Arel::Nodes::Binary
-     def operator; :== end
-
      def equality?; true; end

operatorメソッドは87b6856で追加され、Active Recordの12b3ecaでこのメソッドを参照している。
既にHomogeneousInが追加されているが、これにはInノードのようなoperatorはないので、operatorは以前のような互換性目的では動作しなくなっている。
これをやりたいときは代わりにequality?メソッドを使うべき。
同PRより大意


つっつきボイス:「operatorっていうメソッドがArelにあったんですね」「不要になったら消されるのが常」「homogeneousって何でしたっけ?」「『同じ』的な意味っぽいけど🤔」「辞書見ると『同種の』という堅苦しい意味でした」

⚓各種高速化


つっつきボイス:「#39612によるとModel.find(1)はいいけど、Model.find(1).attr_nameLazyAttributeHashがあっても遅かったのね」「findはlazyになってたけどattributeがlazyになってなかったので修正したとかそういう感じでしょうね☺」「なるほど!」「今ちょうどこういう感じのベンチマーク書いてるので参考になる↓🥰

「ところでプルリクに貼られてるこのgist↑、折り畳めるようになってるんですけどmarkdownでどう書くんだろう?」「GitHubのgistだからGitHubだと書くだけで折りたたみ表示になるとか?🤔」「それっぽいですね」

生のデータベース値を元にattributes hashをインスタンス化するのはattribute周りで遅い部分のひとつだった。
これはattributeの改変検出で必要だったためで、言い換えれば改変が発生するまでは不要ということになる。
0f29c21で導入されたLazyAttributeHashはattributeに最初にアクセスするまでインスタンス化をlazyにする(つまりModel.find(1)は遅くならなくなったがModel.find(1).attribute名が遅かった)
このPRはLazyAttributeSetによるattributeのインスタンス化をよりlazyにし、attributeに最初に代入またはdirtyチェックするときまでattributeをインスタンス化しないようになる(つまりModel.find(1).attribute名は遅くなくなる)。
これによってreaonly(改変なし)のattributeアクセスがおよそ35%改善される。
同PR冒頭より


「#39645はdefine_methodを置き換えて速くしたそうです」「実はdefine_methodでメソッドを動的生成する必要がなかった感😆」「呼び出すメソッドが決まってれば動的にやらなくてもよかったと」「なるほど」

# activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb#L4
  module Type
    module Helpers # :nodoc: all
      class AcceptsMultiparameterTime < Module
-       def initialize(defaults: {})
+         define_method(:serialize) do |value|
        module InstanceMethods
          def serialize(value)
            super(cast(value))
          end

-         define_method(:cast) do |value|
+         def cast(value)
            if value.is_a?(Hash)
              value_from_multiparameter_assignment(value)
            else
              super(value)
            end
          end

-         define_method(:assert_valid_value) do |value|
+         def assert_valid_value(value)
            if value.is_a?(Hash)
              value_from_multiparameter_assignment(value)
            else
              super(value)
            end
          end

-         define_method(:value_constructed_by_mass_assignment?) do |value|
+         def value_constructed_by_mass_assignment?(value)
            value.is_a?(Hash)
          end
...
Before:
Warming up --------------------------------------
type.serialize(time)    12.899k i/100ms
Calculating -------------------------------------
type.serialize(time)    131.293k (± 1.6%) i/s -    657.849k in   5.011870s

After:
Warming up --------------------------------------
type.serialize(time)    14.603k i/100ms
Calculating -------------------------------------
type.serialize(time)    145.941k (± 1.1%) i/s -    730.150k in   5.003639s

「Active Record、着々と速くなってますね💪」「Rubyのdefine_methodって便利なのでときどき使いたくなりますけどね😋

⚓セキュリティ修正: show_detailed_exceptionsをオンにしないとActionableErrorsを表示しないようにした


つっつきボイス:「こちらは今日(6/18)出した記事↓で扱ったセキュリティ修正です」「ああ、pendingしたマイグレーションに絡むヤツでしたっけ」「う、そうでしたか😅

セキュリティアップデート: Rails 6.0.3.2および一部のgem

「このリンク先↓ちょっとだけ眺めたんですけど」「お、リンク切れ直ってますね」「pendingしたマイグレーションを実行できてしまうらしいんですけど、なぜexceptionを見ることで実行できたのかが今のところ謎なんですよね🤔」「あら〜」「productionで本当に実行できるとしたらエグいといえばエグいけど、実行できるのはpending中の定義済みマイグレーションだけみたいなので、ただちに大事になったりはしないんじゃないかな」「問題につながるのは、pendingマイグレーションが残った形でデプロイしたときなんでしょうし🤔」「pendingマイグレーションを実行できちゃうのはたしかにイケてませんし」「後で読んでみます👀

信頼されていないユーザーがproduction環境でpendingマイグレーションを実行できる問題
これはRails 6.0.3.2より前のバージョンにおける脆弱性で、信頼されていないユーザーが、production環境で任意のpendingマイグレーションを実行できてしまう。
(中略)
影響
攻撃者はこのissueを用いて、productionモードで動くRailsアプリで任意のpending中マイグレーションを実行できるようになる可能性がある。ただし攻撃者が実行できるマイグレーションは、アプリケーションで定義済みであり、かつ未実行のものに限られる点が重要である。
groups.google.comより抜粋・大意

追記(2020/06/23)

y-yagiさんブログでこのissueがActionableErrorに関連していることを解説しているそうです。ありがとうございます!

⚓Rails

⚓Active RecordでUNIONクエリを書く(Hacklinesより)


つっつきボイス:「Active RecordでUNIONクエリですか」「生SQL使ったりいろいろ苦心してますね」「まあBase.connection.execute()でやれますけど」「ActiveRecordExtendedというgemも使ってみてる👀」「同じモデルをUNIONするのは比較的わかりやすいかな☺

# 同記事より
result = ActiveRecord::Base.connection.execute("SELECT users.* FROM users WHERE users.active = 't' UNION SELECT users.* FROM users WHERE users.manager = 't'")
result.to_a #=> Collection of users from the union query
# 同記事より
User.where.not(updated_at: nil).merge(
  User.union(
    User.where(name: "Jon")
  ).union(
    User.where(active: true)
  )
).order(start_date: :desc)

「結局unionヘルパーを自力で書いてる↓」

# 同記事より
module ActiveRecordUnion
  extend ActiveSupport::Concern

  class_methods do
    def union(*relations)
      mapped_sql = relations
        .map(&:to_sql)
        .join(") UNION (")

      unionized_sql = "((#{mapped_sql})) #{table_name}"

      from(unionized_sql)
    end
  end
end

ActiveRecord::Base.send(:include, ActiveRecordUnion)

.join(") UNION (")という無理やりな書き方↓がちょっと微笑ましい」「あるある😆

# 同記事より
mapped_sql = relations
  .map(&:to_sql)
  .join(") UNION (")

参考: PostgreSQL12 7.4. 問い合わせの結合

「普段からActive Recordを使っていると、SQLのINTERSECTとかUNIONってそれほど使う機会なさそうな気はしますけどね」「pluckでフェッチして差分取って検索し直すぐらいだったらSQLのINTERSECTで一発でキメたいときはあるといえばありますし」「まあこんなに苦労してやるぐらいだったら、記事冒頭みたいにBase.connection.execute()使うか(データベース)ビューを実行したくなりますね☺」「わかります」

「ただBase.connection.execute()は返ってくる値が使いにくいので、できればビューを使いたいかな: でもビューだと今度は動的なことがやれなくなるのが痛し痒し…」「そうなんですよね😅

RDBMSのVIEWを使ってRailsのデータアクセスをいい感じにする【銀座Rails#10】

⚓parallel_tests: RSpecやTest::Unitをパラレル化(Ruby Weeklyより)

# 同記事より
rake parallel:test          # Test::Unit
rake parallel:spec          # RSpec
rake parallel:features      # Cucumber
rake parallel:features-spinach       # Spinach

rake parallel:test[1] --> force 1 CPU --> 86 seconds
rake parallel:test    --> got 2 CPUs? --> 47 seconds
rake parallel:test    --> got 4 CPUs? --> 26 seconds
...

つっつきボイス:「parallel_tests、見たことある気がする👁」「ウォッチでまだ扱ったことがなかったので入れてみました」「使ったことないけど歴史長そう🏯」「RSpec用に似たようなgemって他にもあったと思いますし」

後で調べるとinitial commitは2009年でした。

「今は更新されなくなって無料になったRailscasts↓へのリンクがREADMEに貼ってあるぐらいなので相当昔からあるのはたしか」「ほんとだ」「これも今は使われなくなったzeus gemとかがあった時代ですし」「なつかしい」「間違いなく老舗ですね☺

「このparallel_tests gemとか、クックパッドさんが出してるrrrspecというRSpec並列化gem↓あたりが老舗かな」「なるほど」

⚓RaiilsのActiveModel::Errorで各バリデーションエラーをカプセル化


つっつきボイス:「この記事ではRails自体の機能でやってるんですが、これも知らなかったので😅」「どれどれ、errorsの構造がこういう配列になったのね↓」

user.errors.where(:contact_number)
=> [<#ActiveModel::Error attribute=contact_number, type=not_a_number, options={:value=>"abcdefghijk}>]

「どっかで見たな〜これ」「記事によるとこの#32313は昨年入ってたそうです↓」「ですよね、最近こうなった覚えがありますし😋」「ActiveModel::ErrorsオブジェクトからActiveModel::Errorオブジェクトの配列に変わった」

# 同記事より
user.errors.add(:contact_number, :too_short, count: 10)
=> <#ActiveModel::Error attribute=contact_number, type=too_short, options={:count=>10}>

user.errors.where(:contact_number)
=> [<#ActiveModel::Error attribute=contact_number, type=not_a_number, options={:value=>nil}>, <#ActiveModel::Error 
attribute=contact_number, type=too_short, options={:count=>10}>]

user.errors.added?(:contact_number, :too_short, count: 10)
=> true

user.errors.added?(:contact_number, :too_short)
=> false

user.errors.delete(:contact_number, :too_short, count: 10)

user.errors.where(:contact_number)
=> [<#ActiveModel::Error attribute=contact_number, type=not_a_number, options={:value=>nil}>]

user.errors.match?(:contact_number, :not_a_number)
=> true

user.errors.match?(:contact_number, :too_long)
=> false

「データモデルとしてはこの方が本来望ましい形ですね👍」「おぉ😍

⚓delete_allの挙動にびっくりした話(Hacklinesより)


つっつきボイス:「delete_allしてみたらUPDATEクエリが走ってびっくりしたそうです↓」「ああ、has_manyのときは参照のidがNULLでUPDATEされるみたいなヤツですね」「記事のAPIドキュメントにもそう書かれてますね😳


同記事より

:dependentオプションで指定した戦略に応じてコレクションのレコードをすべて削除する。:dependentが指定されていない場合はデフォルトの戦略に従う。
has_many関連付けの場合、デフォルトの削除戦略は:nullifyになる。これは外部キーをNULLに設定する。

同記事で引用したdelete_allのAPIドキュメントより

「そういえばdelete_alldestroy_allだと、前者はコールバックしないんだったっけ?」「そうそう、destroy_allはコールバックを実行するんだった↓」

destroy_allはコールバックがある分、削除するレコード数が増えると当然めちゃ遅くなる可能性がある」「コールバック動かすにはeachすることになりますし」「ですよね」「delete_allは1つのSQLクエリでやるからコールバックはできない分、当然速い🚅」「delete_alldestroy_allの2つのメソッドがあるのには理由があるということで☺

メモ: レコードごとのインスタンス化、コールバック実行、削除は、多数のレコードを一気に削除しようとすると時間がかかることがある。1レコードにつき少なくともSQLのDELETEクエリがひとつ発行される(コールバックでさらに発行される可能性もある)。多数の行を短時間で削除したい場合、関連付けやコールバック関連の懸念がないのであればdelete_allを使う。

同記事で引用したdestroy_allのAPIドキュメントより

⚓Cable Readyでブラウザをリアルタイム更新(Ruby Weeklyより)


つっつきボイス:「Screencastsによる手順解説ですけど、最近Cable Readyを割と見かけるなと思って」「Action Cableといい感じに連携するヤツでしたっけ」


同リポジトリより

cable_readyっていうメソッド名なのね」「こういう書き方するのか〜」「include CableReady::Broadcasterしてるし」「チラ見した限りでは、Action CableがJSで出してきたブロードキャストを受けていい感じにリアルタイム更新してくれそう😋

# サンプルコードより
class Card < ApplicationRecord
  include CableReady::Broadcaster

  after_update do
    cable_ready["cards"].morph(
      selector: "#" + ActionView::RecordIdentifier.dom_id(self),
      html: ApplicationController.render(self)
    )
    cable_ready.broadcast
  end
end

「JSのmorph処理をモデルに書くとか、フロントエンジニアに睨まれそうな書き方😆」「morph?」「具体的には知りませんけど、名前からしていわゆるモーフィング的な動きをやれるヤツでしょうね」「あ、モーフィングですか」「引数がselectorhtmlですし、おそらく当該セレクタの内容をこの内容でモーフィング的に書き換えるんだろうな〜と推測しました」「なるほど!」

「まあApplicationRecordを継承したモデルにこういう処理を書くのはちょっと考えちゃいますけど😅


前編は以上です。

おたより発掘

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

週刊Railsウォッチ(20200616後編)本番環境をFullstaq Rubyに換えた理由、CSRF発生フローチャート、DBのトランザクション分離レベル比較ほか

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

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

Rails公式ニュース

Ruby Weekly

Hacklines

Hacklines

週刊Railsウォッチ(20200623後編)Bootstrap 5 alphaリリース、Lambda FunctionsとEFS、DB設計で気をつけていることほか

$
0
0

こんにちは、hachi8833です。つい先ほどこのニュース↓を見て一人でどよめいてます。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

⚓Ruby

⚓mrubyとC言語とRust


つっつきボイス:「『C言語はなんか好き』いいですね〜」「『Rustはヤバい』も」「Cって長いこと書いてないな〜」「Cを最後に真面目に読んだのはLinuxカーネル周りでしたし」「書いたのもLinuxのドライバぐらいでしたし」

参考: C言語 - Wikipedia
参考: The Linux Kernel: Linux カーネルソース

「Rustならちょっとやってみたいかも」「私も!」「今からCを一生懸命やるよりRustの方が楽しそうですし😍」「BPS社内のRust勢に教わりたい」「OSみたいなものを書くにはRustがいいでしょうね〜」「記事の『マルチスレッドやメモリ管理の問題が、Rustの世界では言語仕様の段階で解決されている』ってまさに夢の世界」「俺たちの欲しかったもの」「Go言語もそこまでやってませんし」

参考: Rust (プログラミング言語) - Wikipedia

Rustのドキュメント、どれを見るべきなのかという話

⚓Rubyで素数アルゴリズムを書く(Hacklinesより)

# 同記事より: 改良版素数アルゴリズム
def is_prime_number(item)
  return false if item == 1
  (2..(item - 1)).each do |number|
    if item % number == 0
      return false
    end
  end
  return true
end

つっつきボイス:「美しくない素数アルゴリズムと改良した素数アルゴリズムの二本立てです」「こういう基本的なアルゴリズムをステップバイステップで丁寧に説明してくれるのはいいですね👍


「ところで、こういう初心者向けの丁寧な教材って、不思議に日本語圏にあまりありませんよね」「たしかに!」「英語圏だと結構よく見かけるんですけど」「コードを書くときの意図をみっちり解説してくれる教材って何気に貴重✨

「強い人が初心者目線で書く時間ってなかなか取れなさそうですけど」「こういう丁寧な教材を書くのは、必ずしも強い人でなくてもいいんですよ」「あ、そうか!」「コードを書くのが得意な人と、教えるのが上手い人というのは別なこともよくありますので」「もちろん強いものを作るには強い人がいないと解説すらできませんが、世の中には初心者にレクチャーするのが得意な人も確実にいますし」「そういう教材は常に求められてるんですね…」

「ところでこのDZone.comっていうサイト、前も見た気がする」「トップページ開くといろんな言語がどっさり表示されてますね」「英語アレルギーがなければ、初心者の人はこういうサイトでみっちり学ぶのもよいと思います👍

調べてみるとDZoneは過去のウォッチにもときどき登場してますね。

⚓deep_dupdupの違い(Hacklinesより)


つっつきボイス:「非常に短い記事です」「cloneの話が載ってませんけど😆」「タイトルにUpdatedとあるので別記事の続きかな?」「どうやらこっち↓が本編ですね」「そのようでした😅

「そうそう、Rubyのcloneはshallow copy(浅いコピー)、dupもshallow copyだけどfrozenとかは無視する」「そしてRailsのdeep_dupはArrayとHashについてはdeep copy(深いコピー)するけど、それ以外はdupを呼ぶと」「frozenされているとselfを返すと」

「『deep_dupは銀の弾丸ではない』とありますね」「deep copyは中に何が入っているかわからないので難しいでしょう」「データ構造に応じて処理を変えないといけないので、一般的なdeep copyは作りようがないと思います😆」「よしなにやってくれるdeep_dupが仮にあったとしても、処理の末端でdeep_dupを呼ぶはずがありませんよね😆」「初めて使う人は、この記事を読んで『一般的なdeep copyは無理なんだな』と理解してから使いましょうということで」

⚓oxidized: Ruby製ネットワークデバイス設定バックアップツール(GitHub Trendingより)


つっつきボイス:「ネットワーク機器の、コンフィグバックアップツール?」「RANCIDのオルタナってあるけど、RANCIDって何だろうか?」「初めて見ました😳」「rancidでググると関係ないものがいっぱい出てくる😅」「これか↓、CISCOのルーターみたいなネットワーク機器のコンフィグ管理らしい」

「でoxidizedは何をするツールなのかな?」「★はいっぱい付いてますね」「RANCIDの説明を見た感じでは、ルーターとかにログインしてコマンド実行してコンフィグを取ってきて差分を表示したりするようだ」「こんなのあるんですね」「oxidizedはRESTful APIも提供しているところが新しそう」

# 同リポジトリより
source:
  default: csv
  csv:
    file: ~/.config/oxidized/router.db
    delimiter: !ruby/regexp /:/
    map:
      name: 0
      model: 1

「もしかするとCISCO以外のLinuxサーバーのコンフィグも扱えるのかな?」「サポートOSのリストを見た感じだとLinux系はないみたいなので、このツールはLinuxとかでデーモン的にも動かせるけど対象はあくまでネットワーク機器ということかなと🤔」「なるほど、oxidizedをRESTful APIで制御できるあたりとか、どうもそんな感じみたいですね」「欲しい人は欲しそうなツールということで☺


もしかするとrancidの意味をもじってoxidized(酸化した)と付けたのかもしれませんね。

rancid: {形} : (腐ったような)悪臭のする、嫌な匂いのする、鼻を突く

⚓その他Ruby


つっつきボイス:「こちらのSpringin’はサイトのページをスクロールするとMatzが推薦文を書いてます」「iOSアプリなのね」「名前にアポストロフィがあるあたりが日本っぽくなさそう」「ビジュアルプログラミングだけど、コードを書くというより、絵を描いて動かしたり音を出したりするのが中心みたい」「こういうのは楽しいですね😋」「楽しい😋


「ところでSpringin’見てて思い出したものがあるんだけど、あれ何て名前だったかな…昔すぎて思い出せない😅」「ビジュアルプログラミングですか?」「タブレットなんですけどビジュアルプログラミングもできるというヤツ」「うーん何でしょう?」「…EnchantMOONだ!」「あぁ!」

「EnchantMOON、すごく興味あったんですけど知ったときには販売終了してたのを思い出しました😢 」「当時としてはスペックが足りなかったんでしょうね」「iPadはスペックが統一されているところありがたいですね、高いけど💰」「廉価版でも高いです😭」「Chromebookの方が安いくらいですし」「Chromebookぐらいの値段でiPadが出てくれるといいんですけど」(以下延々)


enchantMOONのshi3zさんが今も更新しているブログがありました↓。

参考: enchantMOONの遺産|shi3z|note

⚓DB

⚓データベース設計の際に気をつけていること


つっつきボイス:「はてブで上がっていた記事です」「自分もざっと読みましたけど、現場の目線で書かれた感じで、取りあえずおかしな部分は見当たりませんでしたね👍」「身体で学んだんでしょうね」

「『activeというカラムではなく別途payment_historyなどのテーブルに移す』あたりはベストプラクティスのひとつ」

「『三角関係のリレーションを持つテーブルは(できるだけ)作らない』もそうで、テーブルとテーブルの間に中間テーブルがある状態では、中間テーブルを飛ばしてテーブル同士をリレーションするのは、できるだけやりたくないですね」

「『一時的なレコードと永続化が必要なレコードを同じテーブルに入れない』も同意: 記事にも書いてますけどSpreeはカートに入れた注文と実際の受注データが同じテーブルに入るんですよ」「ありゃ😅

⚓「適切な」が難しい

「単にベストプラクティスをかき集めたというより、自分で罠を踏んで自力で乗り越えてきたノウハウ集に近いと思います👍」「おぉ」「その分、データベースの初心者が初めて読んでもすぐにはピンとこないかもしれませんけど」「それはありますね」

「たとえば『適切なデータ型を使う』とか『インデックスを適切に貼る』と言ったとき、その『適切』の部分こそ難しいし経験が必要で、だからこそその部分が本当のノウハウだよなという気持ちはありますね」「経験者の価値はそういうところにあるんだろうなと思います☺

「『正規化が必要なところを見極める』も、その『見極める』が難しいわけですし😆」「ほんとにそう😆

⚓MySQLのJOIN

「あと記事ではMySQLのJOINの遅さについて書かれているけど、最近はそこまで遅いことはだいぶなくなったと思います🧐」「ふむふむ」「MySQLの場合テンポラリテーブルができると重くなりがちで、JOINしているデータがバッファプールからあふれるとファイルに書き出す機能がMySQLに標準であるんですけど、それが始まると遅くなりますね」「なるほど」

参考: MySQL - 一時テーブルの作成 | mysql Tutorial

「MySQLでそれが起きていなければ、最近はよほど変なクエリを投げない限りそこまで遅くはならないと思うんですけど」「@kamipoさんのツイートも同じ意見ですね↓」「そうそう、たしかに集合関数を変に混ぜると遅くなることがあります」

「今の時代にMySQLのJOINが遅くなるとしたらテンポラリテーブルのファイル書き出しぐらいなんじゃないかな〜🤔」「ふむふむ」「クエリがまともに設計されていればそうそう遅くならないはずですし、仮に遅くなってもインデックス周りを調整すれば基本的にはイケると思うんですけど」「まずはDB設計をちゃんとやりましょうということですね」

「RailsだとActive Recordの使い方がよくなくて遅くなる方が多いと思いますし、そういうときの方が極端に遅くなりやすい気はします」「ですね」


「@kamipoさんの一連のツイートで一番イイのはやっぱりこの『Rails新しいのが出たら絶対バージョンアップしてくれよな』でしょう↓」「まさに😋

⚓クラウド/コンテナ/インフラ/Serverless

⚓AWS Lambda FunctionsでEFSが使えるようになった


つっつきボイス:「BPSの社内Slackに貼っていただいた情報です」「Lambda FunctionsがEFSをマウントできるようになったのはアツい👍」「関連記事みたいに早くも喜びの声があちこちで上がってるみたいですね」

「EFSをマウントできるようになったことで、何かするのにS3からいちいち持ってこなくてよくなるのが嬉しいですね: S3だと遅いし、ファイル全部が必要じゃない場合でもS3だと端から取ってくるしかないので」「そうでしたか!」「S3はパーシャルで一部だけを持ってくることも一応できますけど、それでもファイルシステムAPIで取れるEFSの方が圧倒的に使いやすいですし」「なるほど〜」

⚓AWS EFS

「Lambda Functionsの場合、たとえばログを単純にEFSに出せるようになるのが便利ですね😋: まあEFSにそういうログの出し方をするのは本当はあんまりよくないんですけど、CloudWatchに出すと取り出すのが面倒なものなんかはこうやってサクッとEFSに出せるのはありがたい」「おぉ」

「ただEFSはIOPS(Input Output Per Second)があふれると死ぬことがあるので、そこは気をつけないといけませんけど」「なるほど」

参考: Amazon EFS のパフォーマンス - Amazon Elastic File System

「あとEFSにはバーストクレジットというものがあって、IOの量が制限値を超えると途端に遅くなるんですよ」「ありゃ😅」「これが起きるとホントつらいので、バーストクレジットはちゃんと監視しておかないとヤバい」

参考: Amazon EFS バーストクレジットを理解する

「Lambda FunctionsからEFSを使う場合、バーストさせたいとかスケールさせたいことが割とあると思うんですけど、でかいファイルとか細かいファイルが大量にあるところにアクセスする要件ではたぶん使わない方がいいんじゃないかなと思います」「ふ〜む」「それよりは、さっきのように単純にログに吐き出すみたいな使い方の方がおそらくキレイにやれるだろうと思いますね☺」「なるほど!」

⚓その他インフラ


つっつきボイス:「これこれ、Chrome OSでWindowsアプリが動くようになるらしい🎉」「みんなが欲しかったのってこれなのでは?という気持ちになりました」「教育現場でCeleron並の半端なPCを使わせるぐらいなら、もうChrome OSでいいのでは?って思いますし」

「ちなみにうちの中2男子も今学校でChromebookで一日置きにリモート授業やってます」「ほほぅ」「最高なんですけど、リモート授業やってるところを子どもが見せてくれなくて😅」「まあそのお年頃ではしょうがないでしょう🤣

「まあChromebookで動くWindowsアプリは相当遅いかもしれませんけど」「Windowsアプリが動くのが便利なのはわかるんですけど、何に使うんでしょうね?ブラウザでできちゃう時代なのに🤔」「職業訓練的にExcelを体験させたいときとか?」(以下延々)

⚓JavaScript

⚓Bootstrap 5 alphaがリリース: jQuery依存がなくなる

CSS変数(カスタムプロパティ)も使い始めているそうです。


つっつきボイス:「ついにBootstrap 5のalphaが出た」「ちなみに今授業ではBootstrap 4を教えてますけど、昔に比べるとBootstrapを積極的に使う理由は減った感じはありますね」「この前もお話ししてましたね」「今だとデザイナーなしで管理画面を作るときとか、そのぐらいかな〜」「今はFlexboxが使えるようになってきましたし、Bootstrapみたいにユーティリティまで包含するようなフレームワークだと余計なものが入ってきますし」

参考: A Complete Guide to Flexbox | CSS-Tricks

「Bootstrapを使うなら、完全にBootstrapに身を任せる方がいいでしょうね: 今のBootstrapにはたとえばほんのちょっとだけmarginやpaddingするみたいな機能↓があったりするんですけど、そういうのも含めてBootstrapを使うかどうか決心しないと」

参考: Spacing · Bootstrap v4.5

// getbootstrap.comより
.mt-0 {
  margin-top: 0 !important;
}

.ml-1 {
  margin-left: ($spacer * .25) !important;
}

.px-2 {
  padding-left: ($spacer * .5) !important;
  padding-right: ($spacer * .5) !important;
}

.p-3 {
  padding: $spacer !important;
}

「こんな細かい機能があったとは😳」「mt-0(margin top 0)とかml-1(margin left 1)みたいなのがいろいろあるんですよ」「!importantまで付いてる😆」「Bootstrapは元々CSSを一行も書かずに画面を作りたい人のためのものですし、それに合う要件なら使う方がいいと思います」


追いかけボイス「IEサポートなしだと使える場面が限られるけどjquery依存消えるのはありがたいし、custom propertiesでコンパイル無しで切り替え増やせるのも良い感じ👍」「脱jQueryは素直にありがたいですよね」

⚓CSS/HTML/フロントエンド/テスト/デザイン

⚓CSSの:is():where()

:is()は元々:matches()という名前だったんですね。


つっつきボイス:「なるほど、:is()を使うとこういうSCSSっぽい書き方↓ができるのね」

// 同記事より
/* BEFORE */
.embed .save-button:hover,
.attachment .save-button:hover {
  opacity: 1;
}

/* AFTER */
:is(.embed, .attachment) .save-button:hover {
  opacity: 1;
}

参考: とほほのSass入門 - とほほのWWW入門

:where()もこういうことができると↓」

// 同記事より
/* sanitize.css */
svg:where(:not([fill])) {
  fill: currentColor;
}

/* author stylesheet */
.share-icon {
  fill: blue; /* 詳細度がより高いため、上書きされる */
}

「CSSってこういう方向に進んでるのか〜、ブラウザ自体がSASSとかSCSSをサポートする方向には行かないのかな?🤔」「必要以上に高機能にしたくないのかもしれませんね」「まあSASSやSCSSの外部ファイルインクルード機能まであるとバンドルしにくいのかもしれませんし」「ブラウザも重くなりそうですけど、SASSやSCSSをブラウザがサポートする未来があってもいいかなとちょっとだけ思いました☺

⚓言語/ツール/OS/CPU

⚓wrk: マルチコアCPUで高負荷を出せるHTTPベンチマークツール


つっつきボイス:「Rubyで書かれてるかと思ったらC言語でした😅」「Apache Benchに近そうな雰囲気👀」「こういうツール昔からありそうですね」「いっぱいありますよ〜」

参考: ab - Apache HTTP server benchmarking tool - Apache HTTP Server Version 2.4

「wrkはマルチスレッドになってて、epollやkqueueみたいなLinuxの新しめのシステムコールで並列性とか遅延実行とかを効果的に回せるというのが特徴のようですね」「おぉ」「要するに高速でぶん回せる💪

「ベンチマークソフトは測定対象の負荷をあふれさせることが重要なので、ベンチマークソフトの能力が足りなくて十分な負荷をかけられないのが一番よくないんですよ」「なるほど」

「ちなみに以下のLinuxコマンドツールトップテン記事↓の中で知らなかったのがこのwrkでした」

参考: Top Command Line Tools for Development and Fun – Pavel Timofeev – On software development, startups, and other thingsHacklinesより)

⚓書籍『プロフェッショナルIPv6』ダウンロード可能


つっつきボイス:「IPv6か〜、今の時期だとIPv4の方が圧倒的にお金になるというのもありますし、IPv6の知識がアプリケーションエンジニアにとってどのぐらいメリットがあるかは悩ましいところなんですけど、IPv6の設計周りはとても参考になりますね👍」「お〜」「私v6はさっぱりです😅

「IPv6使ってても、閉じたネットワークの中ではIPv4が使われることも多いですよね」「本来IPv6はありとあらゆるデバイスに割り当てたいという夢から出発しているのに、プロバイダをIPv6で契約してもIPv6のグローバルアドレスがアサインされないことが多いのもちょっと悲しい😢」「そうですよね…」

「エイリアスIPアドレスなんかはIPv6で当たり前に使えるので、それによってIPv4でも使えるようになってきたところもあるのかなと思います」「おぉ?」「IPv6は、何もしなくてもアドレスを3つ持ったりするんですよ: リンクローカルアドレスと、ユニークローカルアドレスと、グローバルアドレス」「へ〜」

参考: IPv6アドレス - ユニキャストアドレス(グローバル、リンクローカル、サイトローカル)

「昔のIPv4は1つのネットワークインターフェイスに1つのアドレスしか持たなかったんですけど、IPv6でリンクローカルやグローバルやらのアドレスが付くようになって、その影響でIPv4でも普通にエイリアスIPアドレスが使えるようになってきたような印象がありますね、実際はどちらが先なのかはわかりませんけど」


「家の中をIPv6にしてみようかな〜😋」「私の家はルーターはIPv6の有効フラグは立ててますけど、そこ止まりです😆」「今だと、IPv6に対応したプロバイダーと契約してIP over Ethernet(IPoE)でつなぐのが現時点で最速のインターネット接続ですよね」「お〜そうでしたか!」「そういえば最近その話をよく目にしますね」「理論上はですけど😆」「逆にIPv6をオンにし忘れて遅くなった話も聞きますね」

参考: 【初心者でも分かる】PPPoE方式とIPoE方式の違いとメリット|ICT Digital Column|【公式】NTTPC

⚓その他

⚓全盛期のJeff Deanの都市伝説


つっつきボイス:「何のことかなと思ったら、USB 2.0云々はJeff Deanの都市伝説↓のコピペですね」「あ、そうでしたか😅」「1個だけ取り出すとよくわからないけど、まとめて読むと楽しいですヨ😆

参考: 全盛期のJeff Dean伝説 - Qiita

「まあどこの業界にもあるジョーク集です😆」「チャックノリス伝説みたいなものだったとは…失礼しました😅」(しばしJeff Dean伝説で盛り上がる)

参考: チャック・ノリス・ファクト - Wikipedia

⚓番外

⚓HASHWallet


つっつきボイス:「スマホとこのカードを2つ並べて使うなら、全部スマホに入ってる方が楽なのでは?😆」「全部スマホだと紛失したときに立ち直れなさそう😅」「両方ないとお金を使えないようにするというのならワカル」「スマホに入れるのってアプリの立ち上げが意外に面倒な気がしません?それならカードだけでやる方がいいかなと」「それもありますよね」

「そういえば最近VISAの非接触式カードが使えるところが増えてきてかなり楽になりました😋」「タッチするだけでクレカ決済できるヤツですね」「ほほ〜そんなものまで」「カード会社はこれまで頑なに磁気カードでしたけど、新しいKyashなんかも非接触になってますし、もう楽々❤

参考: Visaのタッチ決済(非接触決済) | Visa
参考: Kyash Card|Kyash(キャッシュ)


後編は以上です。

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

週刊Railsウォッチ(20200622前編)AR attributes周りの高速化進む、Active RecordでUNIONクエリを書く、Cable Ready gemほか

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

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

Hacklines

Hacklines

GitHub Trending

160928_1701_Q9dJIU

Rails Architects Conference 2020が7/1より順次オンライン開催

$
0
0

週刊Railsウォッチで今度取り上げるArkencyの『Don’t blindly apply software patterns | Arkency Blog』という記事の末尾でRails Architects Conference 2020 Onlineという無料のカンファレンスを知りました。

ちょっとそそるタイトルが並んでいたので、日本時間を確認したついでに別記事にいたしました。


railsarchitects.comより

スケジュールなど

1日1セッションで計5日間です。日本時間だとほとんどが夜半すぎまたは早朝というハードな時間帯ですが、『Simplify and speed up your Rails views』だけでも見たいのでサブスクライブしてみました。

セッション(発表者) 現地開催時間 日本時間
Typical problems in big Rails apps and how to solve themAndrzej Krzywda Tuesday, 30 June 17:00 UTC 2020年07月01日(水) 02:00
Multitenancy in Rails apps: PostgreSQL schemasTomasz Wróbel Wednesday, 1 July 20:00 UTC 2020年07月02日(木) 05:00
Painless Rails upgradesSzymon Fiedler Thursday, 2 July 20:00 UTC 2020年07月03日(金) 05:00
Simplify and speed up your Rails viewsMirosław Pragłowski Friday, 3 July 11:00 UTC 2020年07月03日(金) 20:00
Why and when The Rails Way can mislead youAndrzej Krzywda Monday, 6 July 18:00 UTC 2020年07月07日(火) 03:00

開催場所: 皆さんのカウチにて。主催: Arkencyチーム。開催理由: 第4回Rails Architest Masterclassスタートを祝して。これまで120人の生徒が同コースを終了しました。皆さまとRailsの有用な話を共有し、皆さまとつながり、そして納得してご入学いただければと思います。
Rails Architects Conference 2020 Onlineは参加費無料です。各ウェビナーの持ち時間は50分で、その後にお知らせタイムとQ&Aセッションが続きます。メールアドレスをフォームにて送信いただければウェビナーへのリンクをお送りします。それでは当日お会いしましょう!
同サイトより大意

以下はRails Architest Masterclassの紹介動画です。

おまけ

当初はタイムゾーンをWebサービスで日本時間に変換していましたが、貼り間違えそうだったのでRubyで雑に変換しました。お粗末さまです。

require 'date'

ary = ["Tuesday, 30 June 17:00 UTC",
"Wednesday, 1 July 20:00 UTC",
"Thursday, 2 July 20:00 UTC",
"Friday, 3 July 11:00 UTC",
"Monday, 6 July 18:00 UTC"]

ary.each do |d|
  puts DateTime.strptime(d, format='%A, %d %B %R %z').new_offset('+
09:00').strftime("%Y年%m月%d日 %R %z")
end
2020年07月01日 02:00 +0900
2020年07月02日 05:00 +0900
2020年07月03日 05:00 +0900
2020年07月03日 20:00 +0900
2020年07月07日 03:00 +0900

関連記事

[Ruby/Rails] strftimeのよく使うテンプレート

[Ruby/Rails] strptimeのよく使うテンプレート

週刊Railsウォッチ(20200629前編)RSpecをメンテしやすくする9つのコツ、application.jsのrequireをimportに置き換え、HTTP 308 Permanent Redirectとはほか

$
0
0

こんにちは、hachi8833です。ニュースウォッチ9で富嶽のニュース見ました。


つっつきボイス:「富嶽、特別価格で買えるらしいっすよ↓」「マジで?」「おひとつ包んでくださいな、みたいに?」

参考: 【やじうまPC Watch】世界一の「富岳」と同じA64FX環境をお手元に! 4,155,300円で - PC Watch

「ご自宅に2ノード単位で置けるそうです」「いちじゅうひゃくせん…400万とか書いてますけど😆」「いやいや、スパコンのモジュールが税別とはいえ400万って、むしろお安いでしょう!」「自動車一台分で買えるということか」「400万なら手の届かない値段じゃないでしょうし」

「構成はそんなにリッチじゃないでしょうけど」「2Uラックマウントで48コアCPUか」「でもこれも最近流行りのARMですし💪」「富嶽ってARMなんですか!」「今週はWWDCもあったし、ARMの話題がやたら多い」(以下延々)

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

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

以下のコミットリストより見繕いました。ドキュメントの小さな更新が増えているようです。

follow_redirect!テストヘルパーを修正

follow_redirect!が308リダイレクトのときに同じHTTP verbでリダイレクトをフォローするよう修正。
同PRより


つっつきボイス:「HTTP 308ってありましたっけ?」「そんな上の方の番号使ったことない😆」「Mozillaのドキュメントにはparmanent redirectとありますね↓」

The HyperText Transfer Protocol (HTTP) 308 Permanent Redirect リダイレクトステータスコードは、リクエストされたリソースが Location ヘッダーで示された URL へ完全に移動したことを示します。ブラウザーはこのページにリダイレクトし、検索エンジンはリソースへのリンクを更新します (「SEO 用語」では、「リンクジュース」が新しい URL に送られたと言われます)。
308 Permanent Redirect: developer.mozilla.orgより

「お〜、308は2014年頃できたみたいですね↓」「308にすると検索エンジンがリソースへのリンクを更新しちゃうのか〜」「301のMoved ParmanentlyじゃなくてRedirectということか」「どういう文脈で使うんでしょうね?」「何だろう?検索エンジン向けとかならわからなくもないですけど🤔」

参考: webdbg.com/test/308/ — ブラウザが308をサポートしているかどうかをテストできるサイト
参考: 新たなHTTPステータスコード「308」とは? - GIGAZINE

「でも301 Moved Parmanentlyもリソースへのリンクを更新する点では同じなんですよね…」「どう違うんでしょう?」「お、308を使うとPOSTメソッドをリダイレクトできるのか!↓」「お〜なるほど理解できた!」

参考: 301 Moved Permanently - HTTP | MDN

リダイレクトが行われるとき、仕様書ではメソッド (と本文) を変更しないよう要求していますが、すべてのユーザーエージェントが従っている訳ではありません。 – まだこの種のバグが発生するソフトウェアが見つかるでしょう。従って、 301 のコードは GET または HEAD メソッドのみに使用し、このステータスでは明確にメソッドの変更が禁止されているので、 POST メソッドでは代わりに 308 Permanent Redirect を使用することが推奨されています
301 Moved Parmanently: developer.mozilla.orgより(強調は編集部)

「そしてこれか↓、301はたしかにGETに変更される可能性がある: POSTに対して301を返すとGETでリクエストし直されちゃうのでPOSTされたものを引き継げないんですけど、なるほど308なら引き継げるのね、へぇ〜」「POSTしても308なら引き継いでもらえるんですか?」「ブラウザがそういうふうに動いてくれるというか、そう動かなければならないということでしょうね」「ははぁ」

301 の場合は不正に GET メソッドに変更される可能性があるのに対し、このコードの場合はリクエストメソッドと本文が変更されません。
308 Permanent Redirect: developer.mozilla.orgより

308がなかった頃はPOSTされる可能性のあるURLを変更するとどうしようもなかったんですけど、308が使えるとPOSTされるURLもリダイレクトできるようになるのね」「これは知らなかった〜」「でも、使います?😆」「😆」「まあAPIなんかで使う可能性はあるでしょうけど」「それ思いました!APIだと欲しいヤツです」

「昔301GETで再リクエストされる問題で悩んだことがあったんですけど、当時はもうどうしようもないという結論に達して、ブラウザ側でリロードして元のページに戻すみたいな処理にしたことがありましたよ」「HTTP、深いですね…」


「でfollow_redirect!は、今まで同じHTTP verbを使ってなかったのを修正したと↓」「そうしないといけない仕様ですもんね」「307や308の場合は今のリクエストメソッドをそのまま踏襲しないといけなくて、301の場合はGETで取り直している」「なるほど〜」

# actionpack/lib/action_dispatch/testing/integration.rb#L61
      def follow_redirect!(**args)
        raise "not a redirect! #{status} #{status_message}" unless redirect?

-       method = response.status == 307 ? request.method.downcase : :get
-       public_send(method, response.location, **args)
+       method =
+         if [307, 308].include?(response.status)
+           request.method.downcase
+         else
+           :get
+         end

+       public_send(method, response.location, **args)
        status
      end

follow_redirect!はよくみるとテストヘルパーみたいなのでテストだけ修正したということかな?」「あ、ほんとだ」「Action Dispatchのtesting/にあるから、実際にはリクエストを投げずにルーティングだけ回すみたいな、よくあるコントローラのテストヘルパーなんでしょうね」「ということは、システムテストは今までもちゃんと動いていたけどfollow_redirect!ヘルパーはちゃんと動いてなかったという流れなんでしょうね」「こんな感じでテストに書けると↓」「なるほど!」

# actionpack/test/controller/integration_test.rb#L375
+ def test_308_redirect_uses_the_same_http_verb
+   with_test_route_set do
+     post "/redirect_308"
+     assert_equal 308, status
+     follow_redirect!
+     assert_equal "POST", request.method
+   end
+ end

application.jsテンプレートのrequireをESモジュールのimportに置き換えた


つっつきボイス:「これはいいね👍が割と付いてますね」「requireimportに書き換わってる」「たしかにJSらしい書き方になった🎉」

# railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt#L6
-require("@rails/ujs").start()
+import Rails from "@rails/ujs"
<%- unless options[:skip_turbolinks] -%>
-require("turbolinks").start()
+import Turbolinks from "turbolinks"
<%- end -%>
<%- unless skip_active_storage? -%>
-require("@rails/activestorage").start()
+import * as ActiveStorage from "@rails/activestorage"
<%- end -%>
<%- unless options[:skip_action_cable] -%>
-require("channels")
+import "channels"
<%- end -%>

+Rails.start()
+<%- unless options[:skip_turbolinks] -%>
+Turbolinks.start()
+<%- end -%>
+<%- unless skip_active_storage? -%>
+ActiveStorage.start()
+<%- end -%>

「修正はrails/ujs周りのコードか」「ActiveStorageやTurbolinksも書き換わってるから、そういうのはJSのimportでやろうということになったんでしょうね」

概要

今回の変更は、Webpackerのapplication.js packテンプレートファイルやドキュメントのサンプルコードにあるCommonJSのrequire()構文をESモジュールのimport構文に差し替える。

その他

今回の変更の主な目的は、新しいRailsアプリを即使えるようにインクリメンタルに改良を進めること。以下のようなメリットが得られる。

  • 連続性を今よりも広くフロントエンドコミュニティに提供できる: Webpackerを採用する魅力のひとつは、BabelインテグレーションによってESモジュール構文をサポートできることだろう。Rails開発者がWebpackやWebpackerに抱く第一印象は、Webpacker入りの新しいRailsをインストールしてapplication.jsファイルの中を見たときに決まる。昨今、Rails開発者がネットで見つけるドキュメントやコード例はほぼESモジュール構文を元にしている。
  • 混乱を軽減できる: 開発者は自分のapplication.js packにESのimportを追加するのが普通だ(ネットのコード例に沿って作業する場合が典型的)が、今のままではrequire()importが1つのファイルで混在することになる。これでは混乱の元になるし、require()importで無用な軋轢も生じる。

  • ブラウザサポートに優しい: ESモジュール構文は将来を見据えてブラウザでサポートされる前提だが、require()構文は設計が同期的で、サーバーサイドJSとしてのNode.jsで元々採用されていたCommonJSと違ってブラウザでサポートされない。Webpackがrequire()をサポートしているのは利便性のためでしかない。

  • 最適化周りのベストプラクティスが促進される: WebpackはESモジュールやを統計的に分析して”tree-shake”する(特定の条件が満たされると最終ビルドから不要なexportを除去するが、package.jsonの指名がおかしくなる副作用もある)。

新機能: RubyネイティブのHash#exceptも使えるようになった

# activesupport/lib/active_support/core_ext/hash/except.rb#L12
  def except(*keys)
    slice(*self.keys - keys)
- end
+ end unless {}.respond_to?(:except)

つっつきボイス:「Hash#exceptはまだRubyのmasterブランチに入ったところみたいです↓」「今度Rubyに入る新しい機能に対応したということですね」

参考: Feature #15822: Add Hash#except - Ruby master - Ruby Issue Tracking System

「RailsのActive Supportの機能がRubyにバックポートされることがちょくちょくありますけど、これも同じような流れなのでしょう」「Rubyネイティブの機能があるときはそれを使うようにすると」

ArelのIsDistinctFromクラスをEqualityクラスからBinaryクラスに移動


つっつきボイス:「Arelの中のクラスの置き場所が変だと思ったので移動したようです」「リファクタリングに近い感じ」

34451で追加されたIsDistinctFromNotEqualとほぼ同じ。
歴史的にはEqualityのサブクラスは特殊な機能を持つようになっていたがequality?メソッドに統合されたので、今後の新しいクラスでequalityが欲しい場合はEqualityを継承しない。少なくともIsDistinctFromはequalityノードに置くべきではない(実際のIsNotDistinctFromはほぼEqualityと同じだが、めったに使われないこのノードに特殊な機能を与えることについて興味が湧かない)。
同PRより大意

番外: スペースやハイフンを含むenumからスコープ名を生成できるようになった(取り消し)


つっつきボイス:「今までだとenumの値にハイフンを含んでいるとエラーになったけど、アンスコに置き換えるようにしたと」「たしかに.underscoreやってる↓」

# activerecord/lib/active_record/enum.rb#184
        _enum_methods_module.module_eval do
          pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
          pairs.each do |label, value|
            if enum_prefix == true
              prefix = "#{name}_"
            elsif enum_prefix
              prefix = "#{enum_prefix}_"
            end
            if enum_suffix == true
              suffix = "_#{name}"
            elsif enum_suffix
              suffix = "_#{enum_suffix}"
            end

-           value_method_name = "#{prefix}#{label}#{suffix}"
+           value_method_name = "#{prefix}#{label.to_s.parameterize.underscore}#{suffix}"
            enum_values[label] = value
            label = label.to_s
# activerecord/test/cases/enum_test.rb#L595
+ test "scopes are named like methods" do
+   klass = Class.new(ActiveRecord::Base) do
+     self.table_name = "cats"
+     enum breed: { "American Bobtail" => 0, "Balinese-Javanese" => 1 }
+   end
+
+   assert_respond_to klass, :american_bobtail
+   assert_respond_to klass, :balinese_javanese
+ end

「enumにハイフンやスペース入りの文字列を使っていいのかという点はさておき、対応するならこう変換するしかないでしょうね」「プルリク例みたいにpublic_sendする↓よりはいいかも😆」


Railsモデルでシンボルではなくstringキーを用いたのだが、その中にハイフンが入っているものがあると、欲しいスコープ名を得られないことに気づいた。

# MyModelが`enumパターンを含むとする: { "dot-fill" => 0 }`
>> puts MyModel.dot_fill.to_sql
Traceback (most recent call last):
        3: from /my_app/script/console:82:in `<main>'
        2: from (irb):1
        1: from /my_app/vendor/gems/2.6.6/ruby/2.6.0/gems/activerecord-6.0.3.2.5438067/lib/active_record/dynamic_matchers.rb:22:in `method_missing'
NoMethodError (undefined method `dot_fill' for #<Class:0x0000000125f6ede0>)
>> puts MyModel.public_send(:"dot-fill").to_sql
SELECT `my_table`.* FROM `my_table` WHERE `my_table`.`pattern` = 0
=> nil

このブランチは、生成されたスコープ名をenum値用に変換し、スペースやハイフンをアンダースコアに置き換える。これで期待どおりMyModel.dot_fillと書けるようになる。
同PRより大意


「そして以下の続きのプルリクは@kamipoさんによるものなんですけど、上の変更はbreaking changesだと指摘していますね」「こちらは大文字を含むActiveSupportみたいな文字列を今までどおり使えるようにするために上をrevertするということか」「こちらもマージされました」「こっちのユースケースの方が多いと思うのでいいと思います👍」「そもそもこの制約を意識したことがなかった😆」

# activerecord/test/cases/enum_test.rb#L595
- test "scopes are named like methods" do
+ test "capital characters for enum names" do
    klass = Class.new(ActiveRecord::Base) do
-     self.table_name = "cats"
+     enum breed: { "American Bobtail" => 0, "Balinese-Javanese" => 1 }
+     self.table_name = "computers"
+     enum extendedWarranty: [:extendedSilver, :extendedGold]
    end

-   assert_respond_to klass, :american_bobtail
-   assert_respond_to klass, :balinese_javanese
+   computer = klass.extendedSilver.build
+   assert_predicate computer, :extendedSilver?
+   assert_not_predicate computer, :extendedGold?
  end

39673ではenum名の制約を変更して大文字で始まる単語を含むメソッド名の生成を禁止しているが、これはbreaking changeの懸念がある。
とりあえずブランチを2つ用意した。既存のアプリが動かなくなってもいいという意図がなければ今はrevertするべき(このブランチはrevert用)。
逆にこのbreaking changeが意図的(つまり既存のアプリは新しい制約に移行すべき)であれば、少なくとも既存の動作を削除する前に非推奨にしておくべき。
同PRより大意


ナイスフォロー感謝です!スペースやハイフンをアンダースコアに変換したかっただけなので、capitalをなくすつもりはなかった。スペースやハイフンを変換するときにcapitalを変えないためにシンプルなgsub正規表現でやる方がいいのかもしれない。
同PRコメントより大意

Rails

ソフトウェアパターンを闇雲に適用しないこと

短い記事です。


つっつきボイス:「よく言われることですけどピックアップしてみました」「英語でもblindly applyって言うのか」「銀の弾丸は英語でもsilver bulletなのか」「それはきっと英語の方が元でしょう😆」「あ、そうでしたか😅」「『狼男は銀の弾丸でないと倒せない』という伝説が元でしたっけ🐺」

参考: 銀の弾丸 - Wikipedia


記事目次より:

  • ソフトウェアパターンは「銀の弾丸」ではない
  • ソフトウェアパターンを闇雲に適用しないこと
  • ここではこう解決する方がいいのでは?

ルーティングヘルパーとRailsアーキテクチャのtips


つっつきボイス:「書かれていることはまあ普通ですね」「Railsを学び始めたばかりだと覚えることが大量にあるから、こういうtipsが必要なのもわかります😭」


記事要点

# 同記事より
resources :users do
  # 既存の外部ルーティング
  get :avatar
  get :new_avatar
  post :create_avatar
end
  • このルーティング↑だけのうちはいいが、コントローラのコードを書き始めるとUserモデル以外のリソースに対するコントローラの責務が増えがち
    • user_save_avatarみたいなやりすぎルーティングに誘惑される)
  • コントローラは小さくしよう(コントローラが小さいほどメンテやテストがやりやすくなる)
  • Railsの7つのCRUDアクションになるべく収めよう
# ネステッドルーティングで改善後
resources :users do
  resource :avatar, only: [:show, :new, :create]
end

Railsのルーティングを極める(前編)

Rails Architects Conference 2020がオンライン開催

こちらは一足先に記事を出しました↓。

Rails Architects Conference 2020が7/1より順次オンライン開催


つっつきボイス:「6月末〜7月頭に開催されるんですが、トピックがそそる感じだったので」「オンラインカンファレンスだけど1日1トピックというのがなかなか珍しそう」「このぐらいのボリューム感の方が何かしながら流し聞けていいかも😋」「時間帯UTCなんですけど😳」「そこなんですよ、日本時間だと殆どが夜半過ぎとか早朝で」「朝2時5時3時😆」「1つだけ夜20:00ありますけど」「極東住民にはなかなかハードな時間割ですね〜🕐」

「スピーチは英語でやってくれるのかしら?」「発表者の名前はいかにもポーランド系なんですけど、集客のためにもたぶん英語でやってくれるだろうと想像してます」「よかった〜☺️」「ネイティブじゃない分英語聞き取りやすいと思います」「ポーランド語とか無理😆」「名前見てポーランド系ってわかるんですか?」「はい、昔の仕事で目が慣れました☺️」

RSpecのメンテナンス性を改善する9つのコツ(RubyFlowより)


つっつきボイス:「RSpecのコツか〜」「6.のこれ↓は誰が見ても下の方がいいですし」

# 同記事より
# BAD
it 'has correct attributes' do
  expect(user.name).to eq 'john'
  expect(user.age).to eq 20
  expect(user.email).to eq 'john@ruby.com'
  expect(user.gender).to eq 'male'
  expect(user.country).to eq 'us'
end

# GOOD
it 'has correct attributes' do
  expect(user).to have_attributes(
    name: 'john',
    age: 20,
    email: 'john@ruby.com',
    gender: 'male',
    country: 'us',
  )
end

「7.の『RSpecのトランザクション』のあたりっていろいろ面倒」

「8.の『モックでexpect使うな』」「モックをうまく使うのも難しいですよね…」

「9.の、BADでやってる個別のテストは同じ処理をやってるからGOODみたいにDRYに書こう↓というのはワカル」「こんな書き方できるんですか?」「え、普通こう書きませんか?」「やったことなかった😅」「こう書かないとcaseを増やすのが面倒だと思いますけど」「不思議にレビューでも指摘されたことなかった…」

# 同記事より
# BAD
#
describe '.extract_extension' do
  subject { described_class.extract_extension(filename) }

  context 'when the filename is empty' do
    let(:filename) { '' }
    it { is_expected.to eq '' }
  end

  context 'when the filename is video123.mp4' do
    let(:filename) { 'video123.mp4' }
    it { is_expected.to eq 'mp4' }
  end

  context 'when the filename is video.edited.mp4' do
    let(:filename) { 'video.edited.mp4' }
    it { is_expected.to eq 'mp4' }
  end

  context 'when the filename is video-edited' do
    let(:filename) { 'video-edited' }
    it { is_expected.to eq '' }
  end

  context 'when the filename is .mp4' do
    let(:filename) { '.mp4' }
    it { is_expected.to eq '' }
  end
end


# GOOD
#
describe '.extract_extension' do
  subject { described_class.extract_extension(filename) }

  test_cases = [
    '' => '',
    'video123.mp4' => 'mp4'
    'video.edited.mp4' => 'mp4'
    'video-edited' => ''
    '.mp4' => ''
  ]

  test_cases.each do |test_filename, extension|
    context "when filename = #{test_filename}" do
      let(:filename) { test_filename }
      it { is_expected.to eq extension }
    end
  end
end

「ただこのDRYな書き方にすると、テストがfailしたときにどのパターンでコケたのかがちょっと探しにくくなるところが難点なんですよ」「たしかに!」

「DRYじゃない方の書き方なら、failしたときにそこの行番号を出してくれるから探しやすいんですけど、DRYな方はeachで回しているからテストデータが似通っていたりすると落ちた場所がすぐにわからなくて😢」「でもメンテしやすいのは明らかにDRYな方なんですよね…」「そこが悩ましい」「そういえばGo言語のテストにもまったく同じ問題あります」

「テストがコケたときにitの中身も出力してくれたらもう少し探しやすくなるかな🤔」「好みが分かれそうな部分かも」

RSpec vs minitest

「ところで最近Railsプロジェクトのテストはminitestでやってますヨ」「やや、ついにminitestですか?」「RSpec、好きというほどでもないので😆」「初めて聞きました😆」

「RSpecって、キレイに書かないとという思いに囚われて結局大変な気がするんですよ」「それわかります😆」「😆」「その代わりRSpecだとモデルのSpecはとてもキレイに書けると思います: subject形式とか」「それはありますね!」「個人の感想ですけど、RSpecはそれ以外のテストにはあんまり合ってない気がします😭」

「モデルのSpecだと、テストする主体がそのままsubjectでとても読みやすい👍」「たしかに」「でもコントローラのテストとかだとね…」「コントローラ、合う場合と合わない場合はあるかも🤔」

RSpecえかきうた

その他Rails


つっつきボイス:「AciveModel::Errorsは前回も取り上げたヤツかな?(ウォッチ20200622)」「New thingsのあたりのerrors.where()でフィルタするのは前回も出ましたね」「失礼しました😅」「errorsのハッシュ改変でdeprecation warningを出すというあたりは仕様変更っぽい👀」

book.errors.where(:name) # => name属性関連の全エラー
book.errors.where(:name, :too_short) # => name属性の全"too short"エラー
book.errors.where(:name, :too_short, minimum: 2) # => name属性の全"too short"エラー、2件以上

「以前はエラーを取り出すと全部ぐちゃらっと出てきてしまったので、errors.where()でフィルタする機能は欲しかったんですよ」「6.1早く出ないかな〜😋」「新機能も速度も楽しみですよね😋」


前編は以上です。

おたより発掘

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

週刊Railsウォッチ(20200623後編)Bootstrap 5 alphaリリース、Lambda FunctionsとEFS、DB設計で気をつけていることほか

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

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

Rails公式ニュース

RubyFlow

160928_1638_XvIP4h

Viewing all 1412 articles
Browse latest View live