- 開発
週刊Railsウォッチ(20190716-1/2前編)Railsアプリの最適化テクニック、あなたの知らなそうなRuby 2.7の変更点、Stripe向けRailsエンジンほか
こんにちは、hachi8833です。高気圧の到来を割と本気で待ち望んでます。
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
- 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください
⚓Rails: 先週の改修(Rails公式ニュースより)
公式更新情報とコミットリストから見繕いました
⚓MySQLのenum
とset
カラムのダンプを修正
# activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb#L40
def schema_type(column)
case column.sql_type
when /\Atimestamp\b/
:timestamp
+ when /\A(?:enum|set)\b/
+ column.sql_type
else
super
end
end
def schema_limit(column)
- super unless /\A(?:tiny|medium|long)?(?:text|blob)/.match?(column.sql_type)
+ super unless /\A(?:enum|set|(?:tiny|medium|long)?(?:text|blob))\b/.match?(column.sql_type)
end
enum
とset
は:string
として型キャストされるが、現時点の:string
型がスキーマダンプで誤って再利用されている。
カラムのキャスト型はsql_type
と同じとは限らない。この修正では、enum
やset
カラムのスキーマダンプでtype
ではなくsql_type
を正しく使うようになる。
同PRより大意
つっつきボイス:「お、これ@kamipoさんがつぶやいてたヤツか: MySQLのenum
がdb:schema:dump
に対応したとか何とか」「後で探してみます」
おそらくこれかなと↓。
RailsでMySQLつかってる人に朗報なんですけど6.0ではdb:schema:dumpでenumがちゃんとdumpされるようになるのこれってうれしくないですか?
— Ryuta Kamizono (@kamipo) July 9, 2019
「最近MySQLとスキーマ周りの修正が目につきますね」「この修正はいいと思う🥰」「データベースの機能を使うべき」「今までstringになっちゃってた?」「ぽすぐれだと完全にenum型になるんだけど、MySQLのこの辺よくわからん😆」「バグとまではいかないけど適切ではなかったっぽい🤔」「今まではstringにしてたけど、本来はenum値がstringでないこともあったとかなんでしょうね: このテストのdiffに出てる'time'
とか↓」
# activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb#L42
def test_enum_type_with_value_matching_other_type
- assert_lookup_type :string, "ENUM('unicode', '8bit', 'none')"
+ assert_lookup_type :string, "ENUM('unicode', '8bit', 'none', 'time')"
end
⚓DB作成時にyamlを読み取れなかった場合にwarningを出す
# activerecord/lib/active_record/railties/databases.rake#L23
- ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
desc "Create #{spec_name} database for current environment"
task spec_name => :load_config do
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
ActiveRecord::Tasks::DatabaseTasks.create(db_config.config)
end
end
end
つっつきボイス:「for_each(databases)
に変わったところからしてマルチプルDB絡みっぽい」「マルチプルDB関連の修正がこれまで相当ありましたけど、やっぱり大きな改造なんですね...😅」「かなり変わってきてますからね〜: 普段ほぼ誰も使ってないような機能まで影響受けたりするだろうから、見落としもいろいろ出てきそう🥺」
⚓MySQLのエラーチェックをエラー番号ベースに
# activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L9
module ActiveRecord
module ConnectionHandling # :nodoc:
+ ER_BAD_DB_ERROR = 1049
+
# Establishes a connection to the database that's used by all Active Record objects.
def mysql2_connection(config)
config = config.symbolize_keys
config[:flags] ||= 0
if config[:flags].kind_of? Array
config[:flags].push "FOUND_ROWS"
else
config[:flags] |= Mysql2::Client::FOUND_ROWS
end
client = Mysql2::Client.new(config)
ConnectionAdapters::Mysql2Adapter.new(client, logger, nil, config)
rescue Mysql2::Error => error
- if error.message.include?("Unknown database")
+ if error.error_number == ER_BAD_DB_ERROR
raise ActiveRecord::NoDatabaseError
else
raise
end
end
end
つっつきボイス:「これは見たまんまの修正😆」「今までエラーメッセージを文字列リテラルで比較してたんですね」「文字列で比較しちゃうと関係ないところでマッチしちゃう可能性もありますし」「こういうのはエラーメッセージテーブルとかから一括で引っ張ってまとめられるといいな〜☺️」
参考: MySQL :: MySQL 8.0 Reference Manual :: B.3.1 Server Error Message Reference
以下は直接関係ありませんが、つっつき後にたまたま見かけたツイートです。
わい情弱やからアプリケーションで使ってるすべてのHTTPステータスコード熟知してるわけじゃないから別に422を定数にしてくれててもいいと思うねんけど "`BAD_REQUEST = 400` みたいな定数を定義して使っているコードは、間違いなく残念なコードです。" みたいな強い言葉を遣われるとびっくりしてまう
— Ryuta Kamizono (@kamipo) July 15, 2019
⚓HTTP Feature-Policy
の設定をサポート
- PR: Adds support for configuring HTTP Feature Policy by jacobbednarz · Pull Request #33439 · rails/rails
Feature-Policy: geolocation 'none'; autoplay https://example.com
# config/initializers/feature_policy.rb
Rails.application.config.feature_policy do |f|
f.geolocation :none
f.camera :none
f.payment "https://secure.example.com"
f.fullscreen :self
end
つっつきボイス:「またHTTPヘッダーが増える?😅」「見た感じ、ブラウザがサーバーに『この機能ならあるから、使うのはやぶさかではない』ことを伝えるヤツなんでしょうね」「おー」「そうすれば、たとえばブラウザでGeolocationが使えるならそれに応じたレスポンスを返せるし、ブラウザのカメラを使うことは相成らぬということであればカメラのUI自体をオミットすることもできる、という感じかな😆」「あと、これを使ってRails側でライブラリを選択的にロードすることで軽くできるなんてこともできそうですし」「あ〜」
参考: Feature Policy -- W3C
追記(2019/07/17)
feature-policyは、基本的にサーバー側からブラウザ機能やAPIを選択的に有効にしたり無効にしたりする機能であるとのことです🙇。
開発者は、セキュリティやパフォーマンス上の予防措置として、特定のブラウザ機能やAPIへのアクセスを選択的に無効にして、開発者のアプリケーションを「ロックダウン」することで、アプリケーション内の自社や第三者のコンテンツが望ましくない動作や予期しない動作を引き起こすのを防げる。
w3c.github.io/webappsec-feature-policyより大意
⚓番外: システムテストでhttpsセッションがやりづらい
- システムテストでSSLを使うようPumaを設定
Capybara.server = :puma, { Host: "ssl://#{Capybara.server_host}?key=<key_file>&cert=<cert_file>"
2. アプリのホストでHTTPSを使うよう設定
Capybara.app_host = "https://#{Capybara.server_host}"
3. 簡単なシステムテストを回す-> Chromeブラウザは起動するがテストが詰まってタイムアウトで落ちる
it 'shows correct version of home page' do
visit root_path
expect(page).to have_content('anything')
end
つっつきボイス:「プルリクではなくissueを拾ってみました」「HTTPSであることをそうやってテストしようとすればそうなるし😆: HTTPSでないとできないテストは確かにめんどくさい😭」「とりあえず設定でapp_host
を上書きして回避したそうです↓」
config.before :each, type: :system do
Capybara.app_host = "https://#{Capybara.server_host}"
end
「HTTPSのテストって、証明書をどうする問題とか、そもそもignore_ssl=true
していいのかどうか問題とか、考えないといけない部分多いし😆」「そうそう、issueにもあるけど、DEFAULT_HOST
にhttp://127.0.0.1
がハードコードされるとできないとか😇」「ありゃ〜😆」「まあ証明書を無視しちゃえばやれますけど🤣」「🤣」
「それ以前に、HTTPSをRails自身が解釈すべきなのかどうかというのはありますね: だいたいどの人も間にNginxとかELBとかを挟んでそこでHTTPS化したりするので🧐」「今ここは想像だけど、Railsは『HTTPSの部分はSSLアクセラレータ的なものに任せる』という考え方だったりするんじゃないかな〜って😆」「たしかにその方がもろもろラクになりますよね😋」「ただ、HTTPSのときにこういうヘッダーが付くかどうかみたいなテストとか、クライアント証明書の中にあるパラメータを取ってきて動かすテストみたいなのは、どうしてもこういう形でやらざるを得ないんですよ😢」「これはもうしょうがないですね😅」
⚓Rails
⚓Stripe::Rails
: stripeを統合したRailsエンジン(Ruby Weeklyより)
# 同リポジトリより
Stripe.plan :silver do |plan|
plan.name = 'ACME Silver'
plan.amount = 699 # $6.99
plan.interval = 'month'
end
Stripe.plan :gold do |plan|
plan.name = 'ACME Gold'
plan.amount = 999 # $9.99
plan.interval = 'month'
end
Stripe.plan :bronze do |plan|
# Use an existing product id to prevent a new plan from
# getting created
plan.product_id = 'prod_XXXXXXXXXXXXXX'
plan.amount = 999 # $9.99
plan.interval = 'month'
end
つっつきボイス:「Stripeを扱うための単なるgemとかではなくてRailsエンジンの形なんだそうです」「StripeのAPIをRailsにマウントできるようにしたとかそういう感じかな?🤔」「Stripeというサービスには元々管理画面がありますけど、そういうStripeの管理画面相当のことをRailsでもやれるとかなんでしょうね」「READMEにいくつか書いてますね↓」
- Stripeの設定を一箇所にまとめて管理
- stripe.jsをアセットパイプラインで使える
- プランやクーポンをアプリ内で管理
- StripeからのWebhookを受け取ったりバリデーションしたりが簡単にやれる
「なるほど、Stripeサービスの管理画面をポチポチする部分を、このエンジンをずごっと入れることで、Stripeの管理画面を使わなくてもRailsでやれるようになると😋」
「Stripeなら自分で管理画面作らなくてもStripeサービスの管理画面でやれるのがうれしい部分なのかなと思ってましたけど」「そこはアプリの運用によっては一部をRailsでやりたい場合もあるでしょうね」「そっか〜」「特にプランの変更なんかは、Railsのモデルと連動させたいこともあるでしょうね🧐」「それありそう!」「そういうのをやりたいときにこのエンジンがあるといいのでわっ😆」
⚓Railsアプリの最適化テクニック(Ruby Weeklyより)
つっつきボイス:「測定して、データベースを最適化して、キャッシュを最適化して、HTTPを最適化して、バックグラウンドに移して、DRYにして...と、まあ普通に行えるテクニック集😆」「つまり定番の最適化ですね😋」「DRYはやりすぎると逆に遅くなることありますけどっ🤣」
同記事見出しより:
- まずは測定
- データベースの最適化
- N+1をなくす
- インデックスをちゃんと付ける
- ORMクエリを生SQLに書き換えてみる
- データベースの正規化度合いを下げる
- INSERTをトランザクションで一括でやる
- キャッシュの最適化
- ビューのキャッシュ
- DBクエリのキャッシュ
- 「あえてキャッシュしない」テクニック
- HTTPの最適化
- アセットのキャッシュ
- 画像の最適化
- JavaScriptコードの分割
- ロジックをバックグラウンドワーカーに移す
- コードをDRYに
- 他にもいろいろあるからね
⚓スベってるRSpecテストの実例(Ruby Weeklyより)
つっつきボイス:「割と短い記事かな」「『アソシエーションがあるかどうかのテスト』のような意味のないテストの例が」「タイトルのpointlessってそういうことね☺️」
# 同記事より
it { expect(profile).to belong_to(:user) }
it { expect(user).to have_one(:profile }
「has_many
と書いてあるかどうかのテストとか、モデルで自動生成されるメソッドのテストとかは普通意味ないですし😆」「そういうテストを書いちゃう人がいるんでしょうね😆」「まあ書けばテストの件数は増えますけどっ😆」「あ〜カバレッジ増やすためだけのテストみたいな😆」
同記事の続きはこちら↓だそうです。
⚓12-Factorに沿ったRailアプリ(RubyFlowより)
つっつきボイス:「12-Factorといえばこれですね↓」「12-Factorに沿ってRailsアプリを構築する方法を考える記事らしき🤔」
「Railsの場合、12-Factorのためにやることはそんなになさそう?」「まあこういうENV切り出し↓とかは基本ですね」
# 同記事より
default: &default
secret_key_base: <%= ENV.fetch('SECRET_KEY_BASE', 'some-default') %>
host: <%= ENV.fetch('HOST', 'localhost:3000') %>
s3:
assets_bucket: <%= ENV.fetch('S3_BUCKET') %>
access_key_id: <%= ENV.fetch('S3_ACCESS_KEY') %>
secret_access_key: <%= ENV.fetch('S3_SECRET_ACCESS_KEY') %>
region: <%= ENV.fetch('S3_REGION', 'us-west-1') %>
development:
<<: *default
test:
<<: *default
production:
<<: *default
「記事のその先では『dotenv使うのってどうよ?』みたいな話が😆」「dotenvじゃなくても単に環境変数読み込めばよくね?みたいな😆」「dotenvをどうやって連携するかだけ最初にきちっと考えておけばいいかなという気もするけど🤔」「記事には『dotenvはアプリの設定を管理するgemではなく、.envから環境変数を読み出すgemでしかない』ってありますね」「たしかにアプリの設定管理用じゃない😆」
記事の「dotenvを使わないやり方のメリット」↓です。
- 素のRailsでやれるのでgem追加不要
- アプリ起動時に設定がバリデーションされる
- 必要な変数が不足していればアプリの起動が失敗してくれる
- 開発者がproductionの設定にアクセスできないようにできる
- 必要な設定のマニフェストを得られる
同記事より大意
「そういう意味では、環境変数とかで『アプリの振る舞いの設定』と秘密鍵のような『(振る舞いではない)単にアプリで必要な情報』をごっちゃに扱う人ってたしかにいる!🤣」「いるいる!🤣」「そういったものってきちんと分けておくべき😤」「そういう設定が2000行とか超えたり😭」「何とかモードとDBパスワードがおんなじファイルに入っているとう〜ん😇って気持ちになるし」「混ぜるな危険🚫」「一度ぐちょぐちょに混ざっちゃうと誰も解体できなくなるし😭」
⚓Rails 6のbefore?
やafter?
の日本人にとっての微妙感(Ruby Weeklyより)
↑先ほどこのサイトを開くと「問題があるため開けません」と表示されましたが、その後正常に復帰したようです。
つっつきボイス:「BigBinaryのRails 6記事なんですけど、記事にはないRuby Weeklyの見出しで『no more confusing <
comparison』とありました」「今までなかったのね☺️」「英語よくわかんないけど😆、これちょっとわかりにくいというか、before?
やafter?
のレシーバーと引数って英語的に合ってる?🤔」「英語圏の人にとってどうなんだろ?」「んん〜、どうやら英語的には合ってるみたいですが日本人にはややこしい😅」「before?
が<
の、after?
が>
のエイリアスみたいです」「どっちが主体か割とわかりにくい...😅」
# 同記事より
Date.new(2019, 3, 31).before?(Date.new(2019, 4, 1))
# => true
- EdgeAPI:
before?
--DateAndTime::Calculations
- EdgeAPI:
after?
--DateAndTime::Calculations
「beforeって動詞じゃないですよね?」「そのはずです」「Weblio辞書見ても動詞はないですね〜」「接続詞、前置詞、副詞はある」「動詞じゃないメソッドが出てくると割と考え込んじゃう😆」「副詞的用法なら引数いらなさそうだし😆」「そこらへんが何か違和感残るし😅」「スペルアウトしてless_than?
とかgreater_than?
とかの方がよかったりして😆」「それはそれでありそうだけど😆」
参考: Rails6 のちょい足しな新機能を試す4 (Date#before? Date#after? 編) - Qiita
# Qiita.comより
# 今日の後は昨日? => この解釈が間違い。
irb(main):001:0> Date.today.after?(Date.today.yesterday)
=> true
Qiitaの記事によると、当初は#32185でalias_method
で定義されていたのが、#32398でリファクタリングされたようです。
# Qiita.comよりcalculation.rb
# Returns true if the date/time falls before <tt>date_or_time</tt>.
def before?(date_or_time)
self < date_or_time
end
# Returns true if the date/time falls after <tt>date_or_time</tt>.
def after?(date_or_time)
self > date_or_time
end
⚓その他Rails
なんかバンドやるみたいにプロダクト作れないかな、と最近考えている。引っ越したら自宅のリビングで作業もできるし始めてみようかな。
— 🇸🇬 (@yoshi_hirano) July 10, 2019
よくあるやつだ!w
— 🇸🇬 (@yoshi_hirano) July 10, 2019
つっつきボイス:「バンド関連だと『当方ボーカル。全パート募集』やんなるほど見かけます😆」「学生の起業なんかでも超よくありますし🤣」「🤣」「🤣」
参考: 「当方ボーカル、他全パート募集!」実際どれくらいある? 憧れのボーカリスト1位はオアシス&ワンオク - エキサイトニュース
⚓Ruby
⚓あなたの知らなそうなRuby 2.7の変更点6つ(Ruby Weeklyより)
クックパッドの中の人の記事です。ラテン系の方で、RubyConf Colombia↓の主催者でもあるそうです。
¿Quieres tener una idea de cómo es RubyConf Colombia?
En nuestro #ThrowbackThursday queremos compartir un video para que tengas una breve idea. ¡Únete a nosotros! #rubyconf #rubyconfcolombia #rubyconfco #ruby #rorhttps://t.co/ufwTRPCLhb— RubyConf Colombia (@RubyConfCo) July 4, 2019
つっつきボイス:「最初は『IRBで複数行再表示できる』、これってk0kubunさんが入れたヤツかなと思ったらあれはインクリメンタルシンタックスハイライトでした😅」
「次は『Module#constant_source_location
が入った』」「source_locationは2.6にも入ってるけど、2.7でさらに拡張!」
参考: Module#constant_source_location
[Feature #10771] · ruby/ruby@9384383
参考: source_location
-- Class: Method (Ruby 2.6.3)
「次は『FrozenError#receiver
が入った』」「エラー起こしたヤツのレシーバーのを取れるんですね😋」
参考: Add FrozenError#receiver
· ruby/ruby@39eadca
「次は『キーの非シンボルとシンボルの混在がまた許されるようになった』」「againだからいっとき許されなくなってたのね😆」
# 同記事より
method_with_keyword_args(a: 1, "b" => 2, "c" => 3)
#=> {:a=>1}
# 2.6.0
method_with_keyword_args(a: 1, "b" => 2, "c" => 3)
#=> ArgumentError (non-symbol key in keyword arguments: "b")
「次は『ブロックなしのProcやlambdaの扱い』」「Procがwarningになって、lambdaがエラーに😳」「ブロックなしがやれると嬉しい場合があったようなそうでもないような😅」「Procやlambda周りを整理しようとしてるのかも🤔」
# 同記事より
def proc_without_block
proc
end
# 2.7以降
proc_without_block { "in here!" }.call
# => warning: Capturing the given block using Proc.new is deprecated; use `&block` instead
# => "in here!"
# 同記事より
def lambda_without_block
lambda
end
# 2.7以降
lambda_without_block { :in_here }
ArgumentError (tried to create Proc object without a block)
「そして『$;
や$,
がdeprecation warningに』😆」「Perl由来の特殊変数にも手を付け始めた😆」「あの読みづらい$
系を減らしていく方向なのかな😆」「よりリーダブルになるのは賛成😋」「そうやって記号が使えるようになったらまた新しい記法に使ったりして🤣」
# 同記事より
$; = " "
#=> warning: non-nil $; will be deprecate
#=> " "
"hello world!".split
#=> warning: $; is set to non-nil value
#=> ["hello", "world!"]
$, = " "
#=> warning: non-nil $, will be deprecated
#=> " "
["hello", "world!"].join
#=> warning: non-nil $, will be deprecated
#=> "hello world!"
Ruby 2.7の変更点について詳しくは以下で。
参考: ruby/NEWS at master · ruby/ruby
⚓ソルベってみた話(Ruby Weeklyより)
Evil Martiansの中の人です。
つっつきボイス:「Sorbetのオープンソース化直前の記事だそうです」「前からSorbetはライブラリ向きかもみたいな話は聞きますね☺️」「小さいライブラリなんかはSorbetでやれるといいかも😋」「記事の結論は『まだ始まったばかり』だそうです」
⚓slop: 軽量なコマンドオプションパーサー(Ruby Weeklyより)
つっつきボイス:「オプションの処理といえばRubyには前から優秀なoptparseがありますけどね(ウォッチ20180518)☺️」「slopだと何か嬉しい点があるのかしら?」「こんなふうに↓型も指定できるあたりがそうかも」「あ〜」「またしかにoptparseでやれないようなリッチなことがやれそう😋」
# 同リポジトリより
o.string #=> Slop::StringOption, expects an argument
o.bool #=> Slop::BoolOption, no argument, aliased to BooleanOption
o.integer #=> Slop::IntegerOption, expects an argument, aliased to IntOption
o.float #=> Slop::FloatOption, expects an argument
o.array #=> Slop::ArrayOption, expects an argument
o.regexp #=> Slop::RegexpOption, expects an argument
o.null #=> Slop::NullOption, no argument and ignored from `to_hash`
o.on #=> alias for o.null
⚓その他Ruby
#RubyWorld Conference 2019の基調講演者が、まつもとゆきひろ、Nadia Odunayo氏の2名に決定いたしました。 https://t.co/whItkU1jpW
— RubyWorld Conference (@RubyWorldConf) July 8, 2019
つっつきボイス:「RubyWorld Conference、日にちもう決まってましたっけ?」
とうに決まってました↓😅。
「 #RubyWorld Conference 2019」の開催日が2019年11月7日(木)、8日(金)に決定しました!会場は島根県松江市の「くにびきメッセ」にて開催します。どうぞご期待ください。 https://t.co/8UZ1Xnz4uA
— RubyWorld Conference (@RubyWorldConf) April 8, 2019
「技術トピックがそれほど多くないこともあって最近はRubyWorld Conferenceそれほど行ってなかったな〜😅」「私も最近ご無沙汰してます😅」「松江のうまい飯を食いに行くというのはありますけど🍽🍶」「😋」
当選し申した。mrubyよりも、ほぼ独学の電子工作知識を整理する話メインになる予定です。 #技術書典 pic.twitter.com/kXCBN9ufZ9
— kishima (@kishima) July 10, 2019
参考: サークル詳細 | Kishima Craft Works | 技術書典
⚓Ruby Trunkより
⚓新機能はデフォルトでオフにして欲しい -- 却下(Ruby Weeklyより)
もっと長期間議論してから入れてもいいのでは、というニュアンス。--enable-experimental=...
フラグとかでやって欲しいと。
つっつきボイス:「このissueがなぜかRubyWeeklyに載ってました」「experimentalな機能についてこう思う気持ちはわからなくもないけど🤔」「issueの最後でMatzが『experimentalフラグは入れたくない』とジャッジを下していました↓」
- 「フラグがあればリリースが安定する」について: ユーザーが新機能をオプトインすることは可能だが、先延ばしの期間がさらに延びるだけだし、リリース前に決定できないならいずれにしろマージすべきではない。
- 「フラグがあれば多くの人に使ってもらえる」について: trunkとpreviewで十分だと思う。experimentalな機能をリリース後に無効にする人は、経験上ほぼいない。
同issueより大意
「これはホントそのとおりで、そのためのRCやらpreviewだと思いますし😆」「experimentalは最終的に使って欲しいものでしょうし😆」「リリースでstableにするときにはexperimentalな機能を整理すべきだと思うし、『入れるけどデフォルトでオフ』とかはしない方がいいでしょうし」「デフォルトでオフにしないといけないような環境でtrunkを使ってくれるなと😆」「😆」
前編は以上です。
バックナンバー(2019年度第3四半期)
週刊Railsウォッチ(20190709-2/2後編)strong_password v0.0.7がハイジャックされていた、TerraformとCloudFormation、CSSの設計ミスリストほか
- 20190708-1/2前編 ActiveRecord::FixtureSetがめちゃ強くなってた、MacだとRubyが遅い理由、Puma 4登場ほか
- 20190701 RMagickのメモリ使用量が劇的に改善、インスタンス変数の定義順で速度が変わる?、GitLab CIランナーをローカルで回すほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSなど)です。