- Ruby / Rails関連
週刊Railsウォッチ(20171117)Rails開発3年分のコツ集大成、PostgreSQL 10.1でセキュリティ問題修正ほか
こんにちは、hachi8833です。Firefox Quantumで泣きましたか?私は拡張使ってませんでした。
11月中旬のウォッチ、いってみましょう。
Rails: 今週の改修
今週の公式更新情報はありません。
タイムゾーンがあいまいになることがある問題を修正
# 修正前
"2014-10-26 01:00:00".in_time_zone("Moscow")
#=> TZInfo::AmbiguousTime: 26/10/2014 01:00 is an ambiguous local time.
# 修正後
"2014-10-26 01:00:00".in_time_zone("Moscow")
#=> Sun, 26 Oct 2014 01:00:00 MSK +03:00
# activesupport/lib/active_support/values/time_zone.rb
def period_for_local(time, dst = true)
- tzinfo.period_for_local(time, dst)
+ tzinfo.period_for_local(time, dst) { |periods| periods.last }
end
ほとんど何も説明がありません。普段はしないのですが、y-yagiさんブログをチェックすると以下のように書かれています。Rubyの挙動に合わせてActiveSupportが修正されたとのことです。
Europe/Moscowのようにタイムゾーンが複数ある値を指定した場合に、TZInfo::AmbiguousTimeが発生していたのを、発生しないよう修正しています。
「同じ地域に複数のタイムゾーンがある」というのがよくわからなかったので、#31128で修正されたという#17395を見ると、「最近(2014年)ロシアのタイムゾーン変更がtzinfoに反映された」とあります。さらに調べると、Time-j.netで以下の記述を見つけました。
ロシアのタイムゾーンは2014年10月26日(日)から以下の図のようにUTC+2 ~ UTC+12 の11個のタイムゾーンになりました。世界一広い国だけあって国内の最大の時差が10時間あります。
サマータイムについては、実施していません。2010年までは実施していましたが、2011年にサマータイムを標準時間としてサマータイムを廃止し、2014年10月26日(日)に本来の標準時間に戻りました。
2016年には、以下の地域で1時間時計の針を進めるタイムゾーンの変更がありました。
www.time-j.netより
そしてtimeanddate.comで、上のソースと同じ日付の2014/10/26 2:00に変更が行われていることがわかりました。このエッジケースに対応したということのようです。
ロシアのタイムゾーンが近年こんなにガッツンガッツン変更されているとは知りませんでした。ロシア国民とライブラリメンテナの苦労が伺えます。
rails new
でできる.gitignoreにmaster keyが含まれていなかったのを修正
# railties/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb
+ def ignore_key_file_silently(key_path, ignore: key_ignore(key_path))
+ append_to_file ".gitignore", ignore if File.exist?(".gitignore")
+ end
つっつきボイス: 「おっとっと、気をつけないとmaster keyをリポジトリに突っ込んじゃうやつだ」
Rails
3年かけて培ったRails開発のコツ
たくさん載ってます。
- トップレベルのコントローラでは
rescue_from
を使う - コントローラでは
load_resource
を使う - ほか続々
つっつきボイス: 「よく使うテクニックもいろいろありそうなので、ざっと見ておいて損はなさそう」「これ翻訳しますね」
Rack DeflateをオンにしてRailsのページサイズを80%削減(Ruby Weeklyより)
Railsの設定にRack::Deflate
を追加することで100msほど高速化したそうです。
# 同記事より
config.middleware.insert_after ActionDispatch::Static, Rack::Deflater
つっつきボイス: 「要するにzipしてるってことね」
なぜService Objectが思ったほど普及しないのか(Ruby Weeklyより)
この記事は、先週のウォッチでご紹介したEnough With the Service Objects Already(Service Objectにちょっとうんざり)に答える内容です。
つっつきボイス: 「確かにこのコード先週も見たなー↓」「『彼の主張には一理あるかもしれない』だそうです」「Service Objectは使いみちが広い分、増えたときに設計がぐらつかないようにしないといけないし、YAGNIになる可能性もあるかも」
# 同記事より
class IpnProcessor
def process_ipn(...)
# controller code here
end
end
SprocketからWebpackに移行する方法(Ruby Weeklyより)
手順が具体的でよさそうです。
つっつきボイス: 「Webpack使う機会がまだなかった...」「Webpackいいですよー」「そうそう、Webpack使うとjavascript_pack_tag
が挿入される↓」
<!-- application.html.erb -->
<html>
<body>
<!-- ... -->
<%= javascript_pack_tag 'vendor' %>
<%= javascript_include_tag 'vendor' %>
<%= javascript_pack_tag 'application' %>
<%= javascript_include_tag 'application' %>
</body>
</html>
:before_validate
コールバックの変わった使い方(Ruby Weeklyより)
# 同記事より
class MyModel
before_validate :strip_url
private
def strip_url
self.url = url.to_s.strip
end
end
つっつきボイス: 「:before_validate
は一見バッドプラクティスに見えたりするけど、データのフォーマットはバリデーション前にやらないと意味がないこともある」
この間翻訳させていただいた記事「RailsのObject#try
がダメな理由と効果的な代替手段」の著者でもあります。
Ruby/Railsプログラミングでよくある5つの間違い
method_missing
の使いすぎ- gemに頼りすぎ
- アプリのロジックがビューに漏出する
- 「コントローラを薄くする」ことにこだわりすぎる
- SQLインジェクションを放置する
つっつきボイス: 「method_missing
って、Railsアプリではまず書かないかな: gem作るとかフレームワーク作るならともかく」「アプリのロジックがビューに漏れるというのも、このぐらいだったら許容範囲なんじゃないかなー↓: 程度問題だけど」
# 同記事より
<h2>
Congratulations
<% if winning_player %>
<%= winning_player.name %>
<% else %>
Contestant
<% end %>
</h2>
「DHHが薄いコントローラを推してるんですよね」「ファットコントローラは確かによくないけど」
あなたが知らないかもしれないRailsの5つのコツ
# 同記事より
params[:sort].presence_in(sort_options) || :by_date
つっつきボイス: 「Object#presence_in
は知らなかったナー」「後はおなじみといえばおなじみかな」
テストは簡潔に書くべきか言葉をつくすべきか
# 元記事より
# アプローチ1
describe '#[]' do
subject { [1, 2, 3].method(:[]) }
its_call(0) { is_expected.to ret 1 }
its_call(1..-1) { is_expected.to ret [2, 3] }
its_call(:b) { is_expected.to raise_error TypeError }
end
# アプローチ2
describe '#[]' do
subject { [1, 2, 3] }
it 'returns the value at a given index' do
expect(subject[0]).to eq 1
end
it 'returns a list of values between a range on indexes' do
expect(subject[1..-1]).to eq [2, 3]
end
it 'raises TypeError when passed a symbol' do
expect { subject[:b] }.to raise_error TypeError
end
end
つっつきボイス: 「う、実はアプローチ1って個人的には好き」「テストでエラーになったときに、テストを修正すべきなのかコードを修正すべきなのかわからないと困っちゃう」「普通にアプローチ2かな」
「これと直接関係ないけど、過去の経緯でプロジェクトによってテストの書き方が大きく違ってたりすると大変」「its_call
なんてのがあるのか」「自分はlet
好きなのでlet
使う派」「私はlet
キライ」「お、派閥が分かれてるんですね」
Rubyで「Interactor」パターン
# 同記事より
# DeleteAccount interactor
class DeleteAccount
include Interactor
def call
end
end
# Controller
def destroy
account = Account.find(params[:id])
DeleteAccount.call(account: account) # pass whatever you want as a hash
end
つっつきボイス: 「InteractorパターンってFacadeみたいなものなのかな?」「#call
使いまくるあたりがそんな感じですね」
以下の記事でもInteractor gemが取り上げられています。
parallel_tests: テストを並列化するgem
- リポジトリ: grosser/parallel_tests
- screencast: #104 Speeding Up Tests -- DriftingRuby
RSpec、Test::Unit、Cucumberのテストを並列化できるそうです。
# grosser/parallel_testsより
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
...
つっつきボイス: 「これ使った方います?」「一応データベースもスレッド化してくれるようです: ただ、案件で追加してみたときはテスト動かなくなったんで外しました」「もしかしてテストの方に問題があったのかもw」「プロジェクトの初期段階から使うのがよさそう」
multiverse: Railsで複数のデータベースを扱うgem(Ruby Weeklyより)
- リポジトリ: ankane/multiverse
ankaneさんの作です。半月足らずで★200超えてます。
つっつきボイス: 「データベース間でJOINできます?」「使ってみないとわかりませんが、さすがに無理かなー」
data-migrate: スキーマの他にデータもマイグレーションするgem
- リポジトリ: ilyakatz/data-migrate
これも半月足らずで★300近くあります。
$> rake -T data
rake data:forward # Pushes the schema to the next version (specify steps w/ STEP=n)
rake data:migrate # Migrate data migrations (options: VERSION=x, VERBOSE=false)
rake data:migrate:down # Runs the "down" for a given migration VERSION
rake data:migrate:redo # Rollbacks the database one migration and re migrate up (options: STEP=x, VERSIO...
rake data:migrate:status # Display status of data migrations
rake data:migrate:up # Runs the "up" for a given migration VERSION
rake data:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rake data:version # Retrieves the current schema version number for data migrations
rake db:forward:with_data # Pushes the schema to the next version (specify steps w/ STEP=n)
rake db:migrate:down:with_data # Runs the "down" for a given migration VERSION
rake db:migrate:redo:with_data # Rollbacks the database one migration and re migrate up (options: STEP=x, VERSIO...
rake db:migrate:status:with_data # Display status of data and schema migrations
rake db:migrate:up:with_data # Runs the "up" for a given migration VERSION
rake db:migrate:with_data # Migrate the database data and schema (options: VERSION=x, VERBOSE=false)
rake db:rollback:with_data # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rake db:version:with_data # Retrieves the current schema version numbers for data and schema migrations
つっつきボイス: 「rake db:migrate:down:with_data
のような感じで使うのね」
webmock: HTTPリクエストのモック/expectation gem
- リポジトリ: bblimke/webmock
リクエストのheaderやbodyを細かに指定できます。★2600超えです。
require 'webmock/rspec'
expect(WebMock).to have_requested(:get, "www.example.com").
with(body: "abc", headers: {'Content-Length' => 3}).twice
expect(WebMock).not_to have_requested(:get, "www.something.com")
expect(WebMock).to have_requested(:post, "www.example.com").
with { |req| req.body == "abc" }
# Note that the block with `do ... end` instead of curly brackets won't work!
# Why? See this comment https://github.com/bblimke/webmock/issues/174#issuecomment-34908908
expect(WebMock).to have_requested(:get, "www.example.com").
with(query: {"a" => ["b", "c"]})
expect(WebMock).to have_requested(:get, "www.example.com").
with(query: hash_including({"a" => ["b", "c"]}))
expect(WebMock).to have_requested(:get, "www.example.com").
with(body: {"a" => ["b", "c"]},
headers: {'Content-Type' => 'application/json'})
Grill.rb: バーベキューしながらRubyカンファレンス
- サイト: http://grillrb.com/
今年の7月にポーランドで開催されたカンファレンスです。
蚊に食われないかなと心配になってしまいます。
ReactやRailsで作った28のアプリリスト(ソース付き)
作者はさまざまです。アプリ開発のヒントにしたり、作りたいアプリが思いつかない学生さんとかにもよいかもしれません。
github-awesome-autocompleteはちょっと便利そう。
Ruby trunkより
提案: ArgumentErrorにメソッドのプロトタイプを表示
[4] pry(main)> Kerk.new.foo1
ArgumentError: wrong number of arguments (0 for 1)
Method prototype:
def foo1(a)
from /home/esjee/src/printprototype/spec/kerk_class.rb:2:in `foo1'
つっつきボイス: 「ノンジャパニーズの方が日本語で書いているところにほだされてしまいました」「エラーにプロトタイプって必要かしらん」
「sentry-ravenって何だろ?」「raven-rubyとsentry.ioというエラーレポート集約サイトがありますね」
Ruby
ベテランRubyistならPythonコードを5倍速くできることもある
Rubyに精通していればPythonでも同じ考えがいろいろ通用するという主旨です。Richard Schneemanさんは怒涛のように濃い記事を書いてますね。
Total time: 1.17439 s
File: perf.py
Function: get_legal_moves_fast at line 53
Total time: 5.80368 s
File: perf.py
Function: get_legal_moves_slow at line 69
require
の仕組み(Ruby Weeklyより)
とても短い記事です。active_support/all
というファイルがないのにrequire
できる理由を解説しています。
作者のRyan Biggさんは2011年にRuby Heroを受賞しています。
一味違う方法でRubyのパフォーマンスをプロファイリング(Ruby Weeklyより)
ruby-prof-flamegraphというgemを援用して次のようなflame graphを生成する記事です。flame graphという呼び名を初めて知りました。関係ありませんが、最近medium.comでよい記事を見かけることが多い気がします。
つっつきボイス: 「このグラフどうやって読むの?」「一番下がThread
だから、呼び出しが上に進んでいる感じですね」
wsdirector: WebSocketをCLIとyamlで操作するgem(Ruby Weeklyより)
- リポジトリ: palkan/wsdirector
yamlでWebSocketのやり取りを書いて実行できます。
# script.yml
- client: # first clients group
name: "publisher" # optional group name
multiplier: ":scale" # :scale take number from -s param, and run :scale number of clients in this group
actions: #
- receive:
data: "Welcome"
- wait_all # makes all clients in all groups wait untill every client get this point (global barrier)
- send:
data: "test message"
- client:
name: "listeners"
multiplier: ":scale * 2"
actions:
- receive:
data: "Welcome"
- wait_all
- receive:
multiplier: ":scale" # you can use multiplier with any action
data: "test message"
wsdirector script.yml ws://websocket.server:9876 -s 10
#=> Group publisher: 10 clients, 0 failures
#=> Group listeners: 20 clients, 0 failures
つっつきボイス: 「テスト用なのかな」「そういえばWebSocketまだ使ったことなかったナ」
なおスポンサーはEvil Martiansです。同社ブログの以下の記事を翻訳させていただきました。
ぼっち演算子&.
の落とし穴
これもとても短い記事です。
# 同記事より
# 落とし穴を踏んだコード
if start_date &.< start_of_month && end_date.nil?
# …
end
つっつきボイス: 「演算子結合の優先順位の罠か」「(メソッドチェーンかと思ったら違った...)」
PB MEMO: Rubyコミットを追いかけるブログ
- 元記事: PB MEMO
Railsコミットをひたすら追うy-yagiさんのなるようになるブログのように、このブログではRubyのコミットをひたすら追いかけています。頭が下がります。
Rubyのインスタンス変数とアクセス制御
Rubyのインスタンス変数とアクセサの関係がちょっとモヤモヤしてたので貼ってみました。
インスタンス変数は外に見せるものではないので結構略されますね。まあ、わたしも disp で排気量なのはちょっとびっくりしますが。
— Yukihiro Matz (@yukihiro_matz) November 15, 2017
「Rubyのpublic/private/protectedはaccess controlとしては壊れている」と言われたが、その答えは「確かに。access controlとして作ってないからな」というものであった。
— Yukihiro Matz (@yukihiro_matz) November 15, 2017
もっともな主張だ。とはいえprivateなのに見えるのがテスト書くときめっちゃべんりなのになぁ
— 成瀬 (@nalsh) November 15, 2017
つっつきボイス: 「dispだとdisplayしか思いつかないw」「attr_accessor
でアクセサを作ると、代入時に同じ名前のインスタンス変数が作成されるという理解」
「protected
はJavaと同じに考えるとハマるやつですな」「access controlとして作ってないんだとすると、どんな意図で作られたんだろう...」
(動画)MatzとRuby 3.0について語る
30分の動画です。駆け足で聞いてみたところ、最初と最後はよもやま話で、12:02あたりからがRuby 3.0の話でした。「パフォーマンス」「concurrency」「型」の3つのコンセプトについて語っています。
つっつきボイス: 「ここ駐車場かな?」「いや、運転してるし」
SQL
PostgreSQL 10の記事が続々出ています。
PostgreSQL 10.1などリリース: セキュリティ問題修正(Postgres Weeklyより)
バージョン9以前に影響するものもあります。
- CVE-2017-12172: 初期化スクリプトに権限昇格の脆弱性
- CVE-2017-15098: JSON関数でサーバーのメモリの一部が露出
- CVE-2017-15099:
INSERT ... ON CONFLICT DO UPDATE
すると権限無しでSELECT
できる
pglogical拡張でPostgreSQL 9.6から10にダウンタイム最小限で移行する(Postgres Weeklyより)
PostgreSQL 10のテーブル継承と宣言的パーティショニングでスケールする(Postgres Weeklyより)
PostgreSQLのパーティショニングされたテーブルを10のネイティブパーティショニングに移行する(Postgres Weeklyより)
PostgreSQL 11の機能を先行紹介するブログ
つっつきボイス: 「何と気が早い」
JavaScript
webpack-bundle-size-analyzer: Webpackのインストール内訳を分析
- リポジトリ: robertknight/webpack-bundle-size-analyzer
- 関連記事: Analyzing bundle size with the Angular CLI and Webpack
Webpackでインストールされたライブラリをこんな感じで表示できます。
CSS/HTML/フロントエンド
Unicodeについて知っておくべき5つのこと
- 画面に表示されないUnicodeポイントはたくさんある
- 見た目が互いにそっくりなUnicodeポイントはたくさんある
- 正規化(normalization)はそんなに簡単な話じゃない
- 表示の長さとメモリのサイズは同じとは限らない
- Unicodeは単なる静的なデータではない
つっつきボイス: 「右の4つから左の合字を作れる話を思い出しました↓」
Source Mapとは何か(Ruby Weeklyより)
- 元記事: WTF is a Source Map
// 同記事より
{
"version":3,
"file":"application.js",
"mappings": "AAAA;AACA;AACA;#...",
"sources": [
"jquery.source-56e843a66b2bf7188ac2f4c81df61608843ce144bd5aa66c2df4783fba85e8ef.js",
"jquery_ujs.source-e87806d0cf4489aeb1bb7288016024e8de67fd18db693fe026fe3907581e53cd.js",
"local-time.source-b04c907dd31a0e26964f63c82418cbee05740c63015392ea4eb7a071a86866ab.js"
],
"names":[]
}
- 参考: MDN ソースマップを使用する
Firefox Quantum(57)から拡張機能はWebExtensionのみになる
- 元記事: Add-ons in 2017
Firefox 57リリース後にBPS社内でも小さな悲鳴がいくつか上がっていました。
つっつきボイス: 「私も拡張全滅しましたorz」
あの可愛かったフォクすけの面影がまったく無くなってしまった…… pic.twitter.com/lJLoKABTiX
— y-yagi (@y_yagi) November 16, 2017
WebAssemblyが主要なブラウザでサポート(Frontend Weeklyより)
FirefoxとChromeに続き、SafariとEdgeでもWebAssemblyがサポートされたとのことです。
つっつきボイス: 「Aaron PattersonさんがWebAssemblyに興味持ってると言ってたのを思い出しました: Rubyで動くかしら」「機械語になるから難しそうですね」
- 【2017年4月版】WebAssemblyとは?〜実際にC言語をブラウザで動かす〜
- WebAssemblyチャレンジ -- RubyでWebAssemblyを動かしてみたそうです
dev.toの激速が話題
- サイト: https://dev.to/
dev.to は、やるべきことを全部やったらweb browsingがどんだけ快適になるか、という事実を見せるという1点ですばらしい価値があるなという気がする
— tagomoris (@tagomoris) November 15, 2017
記事そのものもよさそうです。
つっつきボイス: 「CDNの効果が大きいのかな」
その他
スレッドとは何か
- 元記事: WTF is a thread
10年間見逃されていたman
コマンドの脆弱性
バージョンアップできないAndroid端末の台数をグラフ化
番外
無電力パワードスーツで工場の事故や怪我を大きく削減
今週は以上です。
バックナンバー(2017年度)
週刊Railsウォッチ(20171110)dry-rbでFormObjectを作る、RailsのSQLインジェクション手法サイト、年に1度だけ起きるバグほか
- 20171026 factory_girlが突然factory_botに改名、Ruby Prize最終候補者決定、PhantomJS廃止、FireFoxのFireBug終了ほか
- 20171020 Rubyが来年で25周年、form objectでサニタイズ、コアなString解説本ほか
- 20171013 Ruby 2.5.0-preview1リリース、RubyGems 2.6.14でセキュリティバグ修正、Bootstrap 4.0がついにBetaほか
- 20171006 PostgreSQL 10ついにリリース、Capybaraコードを実画面から生成するnezumiほか
- 20170929 特集: RubyKaigi 2017セッションを振り返る(2)Ruby 2.3.5リリースほか
- 20170922 特集: RubyKaigi 2017セッションを振り返る(1)、Rails 4.2.10.rc1リリースほか
- 20170915 Ruby 2.4.2リリースで脆弱性修正、strong_migrations gemでマイグレーションチェック、書籍『Mastering PostgreSQL』ほか
- 20170908 Rails 5.1.4と5.0.6リリース、コード書換え支援gem「synvert」、遅いテストを分析するTestProfほか
- 20170818 RailsとYarnでTypeScript、Rails新コミッタにkamipoさんも、CSVにSQLクエリをかけられるツールほか
- 20170804 Rails 5.1.3と5.0.5が正式リリース、GitHubでローカライズ基盤サービス、正規表現で迷路を解くほか
- 20170623 gemを見極める7つのコツ、mixinがよくない理由、重いページをrender_asyncで軽減ほか
- 20170616 railsdiff.orgはアップグレードに便利、RubyのDSLとかっこの省略、TerraformをRubyで制御ほか
- 20170609 ついにtherubyracerからmini_racerへ、注意しないとハマるgem、5.1でのVue.jsとTurbolinksの共存ほか
- 20170602 チームが喜ぶ19のgem、Bundler 1.15が高速化&機能追加、Deviseに挑戦する新認証gem「Rodauth」ほか
- 20170512 Rubyの不思議な挙動「シャドウイング」、コードレビュー作法を定めるDanger gemほか
- 20170428 Rails 6.xでの’#form_for’と
#form_tag
廃止決定のその後、deviseの5.1対応はこれから、ほか - 20170421 RailsConfが来週アリゾナで開催、コントローラを宣言的に書けるdecent_exposure gemほか
- 20170414 サーバーを危うくする1行のコード、PostgreSQL 10の新機能ほか
- 20170407 N+1問題解決のトレードオフ、Capybaraのテスト効率を上げる5つのコツほか
- 20170331 PostgreSQLの制約機能を使えるRein gemはビューも使えるほか
- 20170324 Ruby 2.4.1リリース、GAEがついにRubyに対応、このgemがないと生きていけない27選ほか
- 20170317 Railsパフォーマンスチューニング本、DBレコード存在チェックの最速メソッド、RubyのUnicode正規化ほか
- 20170310 クールなDocker監視ツールCtop、RailsがGoogle Summer of Code 2017に正式参加、Unicode 10.0.0ドラフト発表ほか
- 20170303 5.0.2正式リリース、メタプログラミングに懲りた話、bundler 1.12のバグ、すぐ試せるWebアノテーションほか
- 20170227 Rails 4.2.8リリース、SHA-1コリジョンアタック、便利なハッシュ変換ツールほか
- 20170217 Rails 4.2.8.rc2リリース、Ruby 2.4正規表現とActiveSupportのnormalizeほか
- 20170210 JRubyやRubiniusの配列への追加はスレッドセーフではないほか
- 20170203 AnyLogin gemで開発中に楽々再ログイン、イベント数ベース課金の監視サービスRollbarほか
- 20170127 わかりやすいAWSサービス名、Rails DBは便利、TruffleRubyの驚異的速度ほか
- 20170120 Ruby 2.5.0 devリリース、古いMySQLのサポート終了、uniqメソッドが削除ほか
- 20170116 Ruby 2.4の詳細、範囲指定したsumメソッドは速い、rescueの挙動を動的に変更ほか
- 20170110 ReactをRailsに置き換える、Ruby 2.4の新機能ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。