Tech Racho エンジニアの「?」を「!」に。
  • 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開発のコツ


blog.kollegorna.seより

たくさん載ってます。

  • トップレベルのコントローラではrescue_fromを使う
  • コントローラではload_resourceを使う
  • ほか続々

つっつきボイス: 「よく使うテクニックもいろいろありそうなので、ざっと見ておいて損はなさそう」「これ翻訳しますね」

Rack DeflateをオンにしてRailsのページサイズを80%削減(Ruby Weeklyより)


schneems.comより

Railsの設定にRack::Deflateを追加することで100msほど高速化したそうです。

# 同記事より
config.middleware.insert_after ActionDispatch::Static, Rack::Deflater

つっつきボイス: 「要するにzipしてるってことね」

なぜService Objectが思ったほど普及しないのか(Ruby Weeklyより)


aaronlasseigne.comより

この記事は、先週のウォッチでご紹介した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より)


rossta.netより

手順が具体的でよさそうです。


つっつきボイス: 「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より)


karolgalanciak.comより

# 同記事より
class MyModel
  before_validate :strip_url

  private

  def strip_url
    self.url = url.to_s.strip
  end
end

つっつきボイス::before_validateは一見バッドプラクティスに見えたりするけど、データのフォーマットはバリデーション前にやらないと意味がないこともある」

この間翻訳させていただいた記事「RailsのObject#tryがダメな理由と効果的な代替手段」の著者でもあります。

RailsのObject#tryがダメな理由と効果的な代替手段(翻訳)

Ruby/Railsプログラミングでよくある5つの間違い


business2community.comより

  • 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が取り上げられています。

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

parallel_tests: テストを並列化するgem

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さんの作です。半月足らずで★200超えてます。


つっつきボイス: 「データベース間でJOINできます?」「使ってみないとわかりませんが、さすがに無理かなー」

data-migrate: スキーマの他にデータもマイグレーションするgem

これも半月足らずで★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

リクエストの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カンファレンス

今年の7月にポーランドで開催されたカンファレンスです。

蚊に食われないかなと心配になってしまいます。

grill rbさん(@grill.rb)がシェアした投稿 -

ReactやRailsで作った28のアプリリスト(ソース付き)

作者はさまざまです。アプリ開発のヒントにしたり、作りたいアプリが思いつかない学生さんとかにもよいかもしれません。

github-awesome-autocompleteはちょっと便利そう。


github.algolia.comより

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-rubysentry.ioというエラーレポート集約サイトがありますね」


sentry.ioより

Ruby

ベテランRubyistならPythonコードを5倍速くできることもある


schneems.comより

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

ガイジン向けRubyKaigiガイド(翻訳)

requireの仕組み(Ruby Weeklyより)


ryanbigg.comより

とても短い記事です。active_support/allというファイルがないのにrequireできる理由を解説しています。
作者のRyan Biggさんは2011年にRuby Heroを受賞しています。

Railsの`CurrentAttributes`は有害である(翻訳)

一味違う方法でRubyのパフォーマンスをプロファイリング(Ruby Weeklyより)


kollegorna.seより

ruby-prof-flamegraphというgemを援用して次のようなflame graphを生成する記事です。flame graphという呼び名を初めて知りました。関係ありませんが、最近medium.comでよい記事を見かけることが多い気がします。


kollegorna.seより


つっつきボイス: 「このグラフどうやって読むの?」「一番下がThreadだから、呼び出しが上に進んでいる感じですね」

wsdirector: WebSocketをCLIとyamlで操作するgem(Ruby Weeklyより)

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です。同社ブログの以下の記事を翻訳させていただきました。

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

ぼっち演算子&.の落とし穴


antulik.comより

これもとても短い記事です。

# 同記事より
# 落とし穴を踏んだコード
if start_date &.< start_of_month && end_date.nil?
  # …
end

つっつきボイス: 「演算子結合の優先順位の罠か」「(メソッドチェーンかと思ったら違った...)」

RailsのObject#tryがダメな理由と効果的な代替手段(翻訳)

PB MEMO: Rubyコミットを追いかけるブログ

Railsコミットをひたすら追うy-yagiさんのなるようになるブログのように、このブログではRubyのコミットをひたすら追いかけています。頭が下がります。

Rubyのインスタンス変数とアクセス制御

Rubyのインスタンス変数とアクセサの関係がちょっとモヤモヤしてたので貼ってみました。


つっつきボイス: 「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より)


postgresql.orgより

バージョン9以前に影響するものもあります。

  • CVE-2017-12172: 初期化スクリプトに権限昇格の脆弱性
  • CVE-2017-15098: JSON関数でサーバーのメモリの一部が露出
  • CVE-2017-15099: INSERT ... ON CONFLICT DO UPDATEすると権限無しでSELECTできる

pglogical拡張でPostgreSQL 9.6から10にダウンタイム最小限で移行する(Postgres Weeklyより)


rosenfeld.herokuapp.comより

PostgreSQL 10のテーブル継承と宣言的パーティショニングでスケールする(Postgres Weeklyより)


timescale.comより

PostgreSQLのパーティショニングされたテーブルを10のネイティブパーティショニングに移行する(Postgres Weeklyより)


openscg.comより

PostgreSQL 11の機能を先行紹介するブログ


depesz.comより


つっつきボイス: 「何と気が早い」

JavaScript

webpack-bundle-size-analyzer: Webpackのインストール内訳を分析

Webpackでインストールされたライブラリをこんな感じで表示できます。


github.com/robertknight/webpack-bundle-size-analyzerより

CSS/HTML/フロントエンド

Unicodeについて知っておくべき5つのこと


gojko.netより

  • 画面に表示されないUnicodeポイントはたくさんある
  • 見た目が互いにそっくりなUnicodeポイントはたくさんある
  • 正規化(normalization)はそんなに簡単な話じゃない
  • 表示の長さとメモリのサイズは同じとは限らない
  • Unicodeは単なる静的なデータではない

つっつきボイス: 「右の4つから左の合字を作れる話を思い出しました↓」


http://unicode.org/emoji/charts/emoji-zwj-sequences.htmlより

Source Mapとは何か(Ruby Weeklyより)

// 同記事より
{
  "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":[]
}

Firefox Quantum(57)から拡張機能はWebExtensionのみになる

Firefox 57リリース後にBPS社内でも小さな悲鳴がいくつか上がっていました。


つっつきボイス: 「私も拡張全滅しましたorz」

WebAssemblyが主要なブラウザでサポート(Frontend Weeklyより)


blog.mozilla.orgより

FirefoxとChromeに続き、SafariとEdgeでもWebAssemblyがサポートされたとのことです。


つっつきボイス: 「Aaron PattersonさんがWebAssemblyに興味持ってると言ってたのを思い出しました: Rubyで動くかしら」「機械語になるから難しそうですね」

dev.toの激速が話題

記事そのものもよさそうです。


つっつきボイス:CDNの効果が大きいのかな」

その他

スレッドとは何か

10年間見逃されていたmanコマンドの脆弱性


sudosatirical.comより

バージョンアップできないAndroid端末の台数をグラフ化

番外

無電力パワードスーツで工場の事故や怪我を大きく削減


今週は以上です。

バックナンバー(2017年度)

週刊Railsウォッチ(20171110)dry-rbでFormObjectを作る、RailsのSQLインジェクション手法サイト、年に1度だけ起きるバグほか

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

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

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Postgres Weekly

postgres_weekly_banner

Frontend Weekly

frontendweekly_banner_captured


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。