Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

週刊Railsウォッチ(20180209)RubyにMJIT初マージ、高速JSON API gem、Railsにparallel-testingブランチほか

こんにちは、hachi8833です。MacPaintとQuickDrawのソースコードが公開されていることに気づいたのでミーハー的にダウンロードしてみました。コードがびっくりするほど少ない...

臨時ニュース

祝MJIT初マージ🎉🎂🍣


社内Slack: 「おおー、RailsでもJITが効きやすそうな処理(ARによらない単純なHashをこねくり回すループとか)が多いアプリだと速くなりそう」

RubyのMJITを早速動かした記事も出ています。

Rails: 今週の改修

CSP(Content-Security-Policy)ヘッダ追加をデフォルトでオフにした

# content_security_policy.rb.tt#L7
-Rails.application.config.content_security_policy do |policy|
-  policy.default_src :self, :https
-  policy.font_src    :self, :https, :data
-  policy.img_src     :self, :https, :data
-  policy.object_src  :none
-  policy.script_src  :self, :https, :unsafe_inline
-  policy.style_src   :self, :https, :unsafe_inline
+# Rails.application.config.content_security_policy do |policy|
+#   policy.default_src :self, :https
+#   policy.font_src    :self, :https, :data
+#   policy.img_src     :self, :https, :data
+#   policy.object_src  :none
+#   policy.script_src  :self, :https
+#   policy.style_src   :self, :https, :unsafe_inline

この間公開した以下の翻訳記事でも、CSPがオンになっているとWebpackのホットリロード機能が効かなくなることが報告されていました。

Rails 5.2新機能を先行チェック!Active Storage/ダイレクトアップロード/Early Hintsほか(翻訳)

不要なmaterializeをやめてマーシャリングのパフォーマンス低下を修正

# activemodel/lib/active_model/attribute_set/builder.rb#L79
     def marshal_dump
-      materialize
+      [@types, @values, @additional_types, @default_attributes, @delegate_hash]

kamipoさんによる修正です。


つっつきボイス:#30680でリグレッションが報告されていたそうです」「6倍速くなったというか修正されるまで6倍遅くなってたのか: よく使われていそうなところだし」「当然バックポートされるでしょうね」


#30680より: リグレッション時のRails 4パフォーマンス低下

X-Request-Idヘッダで@記号を使えるように修正

# actionpack/lib/action_dispatch/middleware/request_id.rb#L30
     private
       def make_request_id(request_id)
         if request_id.presence
-          request_id.gsub(/[^\w\-]/, "".freeze).first(255)
+          request_id.gsub(/[^\w\-@]/, "".freeze).first(255)
         else
           internal_request_id
         end

つっつきボイス: 「Apacheのmod_unique_idの要件に@入ってますね↓」「テストmod_unique_id = "abcxyz@ABCXYZ-0123456789"とメアド的な形式になってる」

UNIQUE_ID 環境変数は 112 ビット (32 ビット IP アドレス、32 ビット pid, 32 ビットタイムスタンプ、16 ビットカウンタの四つの組) をアルファベット [A-Za-z0-9@-] を用いて MIME の base64 符号化と同様の方法により符号化し、19 の文字を生成することにより作成されます。
Apache モジュール mod_unique_idより

Rails

Railsテストアンチパターン記事2本

# 同記事より
# Feature specs are used to test validation and authorization details, when those 
# should be handled in faster, lower level specs (i.e. model & controller specs)
RSpec.describe 'Properties management', type: :feature do
  scenario 'Admin creates a property'
  scenario 'Admin attempts to create a property with missing name'
  scenario 'Admin attempts to create a property with name which is too short'
  scenario 'Admin attempts to create a property with duplicate name'
  scenario 'Guest attempts to create a property'
  scenario 'Regular user attempts to create a property'
end

つっつきボイス: 「privateメソッドは普通テストしないですよね」「そこにテストが必要だとしたらたぶん書き方おかしいかも」「これ翻訳リクエスト出してみますね」

「関係ないんですけど、medium.comのブログって技術記事でも2日で拍手が一気に数万とか十数万になってたりする記事を見かけることが多くて、どこまで本当なんだろうと思ったりして」「拍手ボタンってはてブとかより気軽に押せるからかも」

Railsリポジトリにparallel-testingブランチ


同ブランチより

最初「How par8o Sped up Rails CI Builds 8x with Semaphore Boosters」にしようかと思ったのですが、つっつき中にこの話を教えてもらいましたのでこちらにしました。

ガイドのドラフトもありました。

パラレルテストは、テストを並列化してマルチプロセスにします。RubyのDRbシステムを用いてRailsが指定のワーカー数だけforkします。Active Recordは、使うワーカーごとのデータベース作成/マイグレーションを自動で行います。この機能を使うにはtest_helper.rbに以下を追加します。

class ActiveSupport::TestCase
  parallelize(workers: 2)
end

同ドラフトより大意

Code Climateの新機能: Velocity


codeclimate.comより


つっつきボイス: 「リスクの高いプルリクをあぶり出したり、コードレビューの待ち時間の傾向とかをチェックできるそうです」「待ち時間、切実っす」

WebpackerでStimulus.js使えるようにするプルリク投げた(RubyFlowより)

TurboLinksと相性がいいらしい例のStimulus.jsフレームワークです。Stimulusを試してみた別記事もありました↓。

# lib/tasks/webpacker.rake#L11
   "webpacker:install:vue"             => "Installs and setup example Vue component",
   "webpacker:install:angular"         => "Installs and setup example Angular component",
   "webpacker:install:elm"             => "Installs and setup example Elm component",
+  "webpacker:install:stimulus"        => "Installs and setup example Stimulus component",
   "webpacker:install:erb"             => "Installs Erb loader with an example",
   "webpacker:install:coffee"          => "Installs CoffeeScript loader with an example",
   "webpacker:install:typescript"      => "Installs Typescript loader with an example"

つっつきボイス: 「ややこしいことされるのかなと思ったらWebpackerの選択項目を追加しただけだった」「よかったー」

その後#1255に移動してマージされていました。

Netflixの高速JSONシリアライザ: fast_jsonapi gem(Ruby Weeklyより)

# 同リポジトリより
options[:meta] = { total: 2 }
hash = MovieSerializer.new([movie, movie], options).serializable_hash
json_string = MovieSerializer.new([movie, movie], options).serialized_json

公開後数日で★1700越えです。


つっつきボイス: 「えらく速いみたいですね」「あの有料動画サイトのNetflixが出してる」

「Netflixと言えば、最近何かスゴい名前のシステム運用やってますね: そうそうカオスエンジニアリング」「すげー! 一日一回わざとシステム障害を起こすことで障害に強くするのか」「カオスエンジニアリングは相当な技術力とリソースがないとつらいけど、インクリメンタル開発と高い耐障害性を本質的に望むなら実はとても良い方法だと思う」

参考: Chaos Engineeringの概要とPumba入門

  • リポジトリ: Netflix/chaosmonkey -- カオスエンジニアリングツール
  • リポジトリ: gaia-adm/pumba -- Docker向けカオスエンジニアリングツール

Rails+NginxでSSLプロキシを立てる(Ruby Weeklyより)

# 同記事より
require "down"

class App
  CHUNK_SIZE = 1024 * 128 # 128 KB

  def call(env)
    req = Rack::Request.new(env)
    @data_source = Down.open(req.params.fetch("target"))
    headers = @data_source.data.fetch(:headers)
    [
      200,
      {
        "Content-Type" => headers.fetch("Content-Type"),
        "Content-Encoding" => "Chunked"
      },
      self
    ]
  end

  def each
    while @data_source.eof? == false
      yield @data_source.read(CHUNK_SIZE)
    end
    @data_source.close
  end
end

use Rack::Chunked
run App.new

つっつきボイス: 「これdownってgem使ってるんですね: ダウンロードのdownみたい」「認証/ファイル分割/ストリーミングいろいろできるのか: やべ思ったより機能多い」

# 同リポジトリより
remote_file = Down.open("http://example.com/image.jpg")
remote_file.each_chunk { |chunk| ... }
remote_file.close

「ところで記事の最初のコメントに『これは危険な手法だからさらにプロキシ重ねないとおすすめできない』ってありますね...」「ほんとだ: 著者も後づけで免責事項追加してる」

gutentag: ActiveRecordのタギングを拡張

# 同リポジトリより
article.tag_names #=> ['pancakes', 'melbourne', 'ruby']
article.tag_names << 'portland'
article.tag_names #=> ['pancakes', 'melbourne', 'ruby', 'portland']
article.tag_names -= ['ruby']
article.tag_names #=> ['pancakes', 'melbourne', 'portland']

つっつきボイス: 「gutentagってドイツ語だった気が」「『こんにちは』」

後で気づきましたが、同じコンセプトのacts-as-taggable-onというgemも前からあります。以前のウォッチつっつき↓で「このレベルならgem足すより自分で書いた方が早いかも」という話になったのを思い出しました。

週刊Railsウォッチ(20170804)Rails 5.1.3と5.0.5が正式リリース、GitHubでローカライズ基盤サービス、正規表現で迷路を解くほか

light-service: Railsコントローラをシンプルに書けるgem(Awesome Rubyより)

以前はorchestratorという名前のgemだったのが名前を変えたそうです。


同リポジトリより

# 同リポジトリより
class TaxController < ApplicationContoller
  def update
    @order = Order.find(params[:id])

    service_result = CalculatesTax.for_order(@order)

    if service_result.failure?
      render :action => :edit, :error => service_result.message
    else
      redirect_to checkout_shipping_path(@order), :notice => "Tax was calculated successfully"
    end

  end
end

つっつきボイス: 「名前からしてService Objectですね」「READMEで割りと詳しく説明してくれてる」

Railsで重要なパターンpart 1: Service Object(翻訳)

composite-primary-keys: 複合主キーを使えるようにするgem

# 同リポジトリより
MembershipStatus.primary_key # => "id"    # normal single key
Membership.primary_key  # => [:user_id, :group_id] # composite keys
Membership.primary_key.to_s # => "user_id,group_id"

BPSの社内勉強会で言及されたgemです。


勉強会より: 「普通のRailsアプリをスクラッチで設計するときには単体キーで問題ありませんが、既存のRDBMSに接続するときは複合主キーが使われていることが割りとあるので、こういうgemで対応することがあります」

RailsのAPIバージョニング手法とバージョニングgem


同記事より


つっつきボイス: 「バージョニングgemって結構いろいろありますね」「↑バージョニングの解説もあるし、このチャート↑も便利」「rocket_pantsって名前w」

(動画+リポジトリ)Railsのコツ・ヒント集

15分の動画です。

# 同サイトより
class Product < ApplicationRecord
  belongs_to :brand #, optional: true

  scope :cheap_products, -> { where(price: 0..50.00).order(price: :asc) }
  # scope :expensive_products, -> { where(price: 50.01..Float::INFINITY).order(price: :asc) }
  scope :expensive_products, -> { where(price: 50.01..1.0/0.0).order(price: :asc) }

  # def self.cheap_products
  #   where(price: 0..50.00).order(price: :asc)
  # end
end

つっつきボイス: 「この..1.0/0.0って書き方、元の..Float::INFINITYの方がいいと思うんだけどなー」

RackミドルウェアのキャッシュでRailsのパフォーマンスを改善(RubyFlowより)


同記事より

# 同記事より
class CacheMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    req = Rack::Request.new(env)
    cache_path = req.path == "/api/promotions.json"
    no_param = req.params["discounted_by"] == nil

    if cache_path && no_param
      [
        200,
        {"Content-Type" => "application/json"},
        [$redis.get(Product::PROMOTIONS_CACHE_KEY) || ""]
      ]
    else
      @app.call(env)
    end
  end
end

つっつきボイス: 「5倍ぐらい速くなってますね」「関係ないけどcronの書き方ってホント覚えられない...」

update_promotions_cache:
   cron: "*/30 * * * *"
   class: "CacheUpdaterJob"

その他のRailsミニ記事


つっつきボイス: 「著者のPaweł Dąbrowskiさんに一括で翻訳許可いただいたので、今後シリーズで翻訳しようと思います」

【妄想】Rails 6の空想キーノートスピーチ【注意】

フィクションかつ2016年の記事です。一瞬本気にしそうになりました。

  • ビューごとのCSSやJSはなくなるであろう
  • jQueryはデフォルトでなくなるであろう
  • app/servicesディレクトリがデフォルトで生成されるであろう
  • カラムはデフォルトで非nullになるであろう
  • ルーティングはアクションから自動生成されるであろう↓
  • etc.
# 同記事より
class UsersController < ApplicationController
  def index # automatically sets up GET /users
  end

  def create # automatically sets up POST /users
             # also sets up GET /users/new
  end

  opt_out :new # remove GET /users/new

  def destroy # configure DELETE /users/:I'd
  end

  def deactivate # does not introduce any route
  end
end

つっつきボイス: 「Rails 6の青写真的なものを探してて見つけました: 青写真の方はまだなさげ」「jQueryは予言当たってるかな」「ルーティング自動生成とかしたくないー」「Appleのお偉いさんっぽい口調で書いてみたそうです」「ひどいけど面白い」

Ruby trunkより

MJITのバグがぼちぼち上がり始めているようです。

gsubとかにハッシュを渡すときのpattern引数を省略したい(継続)

"blahblah".sub(/[abc]/, {"a" => "A", "b" => "B", "c" => "C"})
# pattern略したい
"blahblah".sub("a" => "A", "b" => "B", "c" => "C")

卜部さんも以前#13016で同様の提案をした後取り下げたようです。


つっつきボイス: 「patternよりも、gsubとかにハッシュを与えられるって知らなかった!」「この方が書きやすいときもあるかも」

参考: [Ruby]gsubにhashを渡す

MatchData#[]のエイリアスが欲しい

&.[](1)をたとえば&.capture(1)とか&.at(1)のようにしたいそうです。


つっつきボイス: 「他の言語だとどんなメソッド名なんでしょうね: そっちに合わせたネーミングでもいいのかも」

参考: Ruby 2.5.0 リファレンスマニュアル MatchData#[]

Ruby

Rubrowser: 依存関係グラフをインタラクティブにビジュアル表示


つっつきボイス: 「これよさそう」「なぜかサイトにデモがないですね」

というわけで手元でrack-attack gemにrubrowser -o result.htmlしてhtmlを生成して雑にCodePenに貼ってみました。

See the Pen Rubrowser_demo by hachi8833 (@hachi8833) on CodePen.

CRubyのメモリスロット

Noah Gibbsさんのコアな記事です。

Ruby 2.5のThread#fetchメソッド(Ruby Weeklyより)

>> th = Thread.new { Thread.current[:cat] = 'meow' }
=> #<Thread:0x0000556e11ebd0c8@(irb):1 run>
>> th.join
=> #<Thread:0x0000556e11ebd0c8@(irb):1 dead>
>> th.fetch(:cat, true)
=> "meow"
>> th.fetch(:dog, true)
=> true

つっつきボイス: 「fiber-localな変数を取れるのか」「スレッドだけどfiber?」

なお以下には「スレッドに固有のデータ」とありました。

参考: Rubyリファレンスマニュアル Thread#fetch

rubysec.comより: NokogiriとlibxmlでDoS脆弱性(Hacklinesより)

前からあったんだと思いますが、https://rubysec.com/を今頃知りました。


rubysec.comより


つっつきボイス: 「rubysec.comってbundler-auditで使ってるみたいですね」「ここが情報源だったのか」

py2rb.py: PythonコードをASTベースでRubyに変換

# 同記事より
s1 = "hello"
s2 = "world"
s3 = "abcd"

s4 = list(zip(s1,s2,s3))

for item in s4:
    print("----")
    for val in item:
        print(val)
# frozen_string_literal: true

require 'module'

using EnumerableEx
using PythonZipEx
using PythonPrintEx
using PythonIsBoolEx
using PythonIndexEx
using PythonFindEx
using PythonSplitEx
using PythonStripEx
using PythonStringCountEx
using PythonRemoveEx
using PythonMethodEx

s1 = "hello"
s2 = "world"
s3 = "abcd"
s4 = zip_p(s1, s2, s3).to_a
for item in s4
  print("----")
  for val in item
    print(val)
  end
end

つっつきボイス: 「これ凄いかも: 表面的な置き換えとは違う感じ」「usingやたら出てきますね」「usingってrefinementで使うアレか」「forはそのまま、と」

RubyのRefinement(翻訳: 公式ドキュメントより)

RubyアプリにGoのバイナリを埋め込んでみた(Hacklinesより)

ffiで呼んでいます。

# 同記事より
require 'ffi'
module Foo
  extend FFI::Library
  ffi_lib './my_lib.so'
  attach_function :my_add, [:int, :int], :int
end
puts Foo.my_add(2, 2)
# => 4

Goのソースをgemに組み込んで配布しようとしても$GOPATHを勝手に変えにくいなどで自動コンパイルが難しいのが残念です。バイナリサイズもC言語より大きくなってしまいます。

Matzの言語談義から

Rubyを知る上でいろいろ参考になりそうです。全部貼ると長すぎるので、埋め込みツイートの日付のところをクリックして開いてください。


SQL

PostgreSQLの陽の当たらない貢献者(Postgres Weeklyより)


つっつきボイス: 「ん?gemってRubygemじゃなさそうですね」「ほんとだ: タイトルだけ見てPostgreSQL用のgemの紹介かと思ったら人名録だった」「gemは宝石/お宝の意味だったか(´・ω・`)」

標準SQLのうちでPostgreSQLが他のRDBMSに勝っている機能(Postgres Weeklyより)

PostgresConf US 2018が4月にニュージャージー州で開催(Postgres Weeklyより)

とても追いきれないぐらい多数のセッションです。

JavaScript

ECMAScript 2018の機能が決まる(JSer.infoより)


つっつきボイス: 「個人的には正規表現の後読みがJSでできるのがうれしいです(∩´∀`)∩ワーイ」

正規表現の先読み・後読み(look ahead、look behind)を活用しよう

30 seconds of code: JSコード精選スニペット集


30secondsofcode.orgより


つっつきボイス: 「これも便利そう」「とりあえずvar使ってるスニペットは1件もありませんでした」

参考: Webサイトやスマホアプリでよく使う、JavaScriptの有用なコードのスニペットのまとめ

Async Generatorsでビジネスロジックを分離(JavaScript Liveより)


medium.com/dailyjsより

割りと長い記事です。

V8エンジン6.5がリリース(JavaScript Weeklyより)


  • Untrusted codeモードの追加
  • WebAssemblyのストリームコンパイル
  • パフォーマンス向上


同記事より

JSの日時処理にMoment.jsのLuxonを使うべき理由(JavaScript Weeklyより)

// 同記事より
var m1 = moment();
var m2 = m1.add(1, 'hours');
m1.valueOf() === m2.valueOf(); //=> true

つっつきボイス:JSネイティブのDateライブラリがあまりに機能不足だからだそうです」「DataじゃなくてDate wranglingだから以下とは別っぽい」

参考: Webなエンジニアのための「Pythonデータラングリング」入門

JavaScript、こうだったらいいのに

// テンプレートリテラル(Rubyで言う式展開)は${startX}より簡単になあれ
return `M[pathArr2.join("")] [startX] [(inc * (rate*2) + rate)]`
// 三項演算子はパイプでつなげるようになあれ
const func = function( .. ) {
  return condition1 ? value1 | condition2 ? value2 | condition3 ? value3 | value4
}
// アロー関数はこのぐらいシンプルになあれ
(y) { y += 1 }

つっつきボイス: 「さすがに無理あるなー: (y)とかパーサーが困りそう」「たぶんアメリカンジョーク」

CSS/HTML/フロントエンド

Bootstrap 4の新機能をおさらい


  • Normalize.css廃止
  • サポートするブラウザの変更
  • Flexboxにがっつり移行
  • Gridシステムの改善
  • メディアクエリの改善
  • フォームのサポートの改善
  • Sassがデフォルト、Lessは廃止に
  • カードコンポーネントのサポート
  • スペーシング方法の整理
  • ツールチップの配置方法の改善
  • 単位をremからpxpxからremに移行
  • Glyphiconsを廃止
  • レスポンシブのみをサポート
  • etc

つっつきボイス: 「FlexboxになるならIE9以下はサポート外っすね」

WebSockets vs Server Sent Events(SSE)(JavaScript Liveより)

// 同記事より
const express = require('express');
const app = express();

app.get('/eventstream', (req, res, next) => {
    res.set({
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });
    app.on('message', data => {
        res.write(`event: message\n`);
        res.write(`data: ${JSON.stringify(data)}\n\n`);
    });
});

参考: MDN Server-Sent Events の利用

HTML/CSSレイアウトでやってはいけないこと

* 元記事: Pixels are Tech Debt


medium.com/podible-engineeringより

WebAssemblyで何ができるか

その他

ConcurrencyとParallelismの違い(JavaScript Liveより)

図がよいです。

caire: 画像の構成を壊さずにリサイズできるライブラリ(GitHub Trendingより)


同リポジトリより

Go言語製です。


つっつきボイス: 「どうやってるんだろう」「ますます写真を信じられなくなりそうですね」

タイムゾーンはここに注意


つっつきボイス: 「圧倒的な情報量」「相当血を流したんでしょうね...」「Javaだけど他でも通用する話」

Intel CPUの内部最適化記事

osquery: SQLベースのOS測定/監視/分析ツール

Facebook製です。morimorihogeさんが社内Slackに投下しました。★17000越え。


osquery.ioより

SELECT DISTINCT processes.name, listening_ports.port, processes.pid
  FROM listening_ports JOIN processes USING (pid)
  WHERE listening_ports.address = '0.0.0.0';

社内Slackより: 「これはなかなか面白い: (chefの)Ohai的な感じ」「今後のメンテ次第だけど、こういうツールがあると便利なのはわかる」

参考: ohaiを使ってサーバの情報をプログラムで扱おう

Pythonのdict(Rubyで言うとハッシュ)の順序が保証される方向に


つっつきボイス: 「Rubyでハッシュの順序が保証されたのっていつでしたっけ(->1.9)」「本来的にはハッシュの順序は保証されないはずだけど、やっぱりあるとありがたい」

参考: RubyとPythonのHash(Dictionary)操作対応まとめ

番外

丸め

https://twitter.com/nullset_mn/status/959207951398354944

Googleドライブの鬼進化


つっつきボイス: 「マジですか!」「自炊したままのデータが一気に宝になる」「マンガはまだ無理そうですねー」

顔認証を無効化する技術

人間の目ではわからないように画像の顔認証を無効にするそうです。こういう技術はイスラエルが強いですね。

参考: 顔認証を無効化する新テクノロジー、イスラエル企業が開発

fMRIの成果


つっつきボイス: 「何もしてないとバレちゃうようになるのかな(´・ω・`)」「フラグが特定されたというか」「脳波を隣の部屋から測定するとかもうできてるみたいです」

確定申告の季節


今週は以上です。

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

週刊Railsウォッチ(20180202)Rails 5.2.0 RC1と5.1.5.rc1リリース、Rails 6開発開始、メソッド絵文字化gemほか

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

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

Rails公式ニュース

Ruby Weekly

Awesome Ruby

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines

Postgres Weekly

postgres_weekly_banner

JavaScript Weekly

javascriptweekly_logo_captured

JavaScript Live

jslive_logo_captured

JSer.info

jser.info_logo_captured

Github Trending

160928_1701_Q9dJIU


CONTACT

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