こんにちは、hachi8833です。Rails 5.1でrails new
すると忘れた頃にspringに邪魔されます。
GWをはさんだ2週間ぶりのRailsウォッチ、いってみましょう。
Rails: aggregated_results
で任意の種類の引数を取れるよう修正
y-yagiさんという方の「なるようになるブログ」は、Railsのコミットログをひたすら読む記事を毎日のようにアップするというすごいブログなのですが、そのy-yagiさんがRailsにちょくちょくプルリクを送っていることに昨日初めて気づきました。
コミットログでお名前を少しだけ追いかけてみたところ、少なくとも今年3月ぐらいから続々淡々とプルリクしていて、kamipoさんに迫りそうな勢いです。上は現時点で最新のPRです。
いつもありがとうございます。
Rails: secretsに関する記述を修正
-building on top of the [sekrets](https://github.com/ahoward/sekrets) gem.
+inspired by the [sekrets](https://github.com/ahoward/sekrets) gem.
これ、私も記事を書いていて引っかかってしまいました。Sekrets gemがRailsに組み込まれたのではなく、Secretsにインスパイアを受けて独自に実装したというのが正解でした。
BPS WebチームのakioさんがRails 5.1でrails new
してみて「Sekrets gemが入ってませんねー」と知らせてくれたことでわかりました。ありがとうございます。
Rails: #map
を#flat_map
に変更
- chain.map(&:scopes).flatten
+ chain.flat_map(&:scopes)
Rubyスタイルガイドでも推奨されている#flat_map
への置き換えです。
参考: Rubyスタイルガイドを読む: #mapと#flattenではなく#flat_mapを使うこと
Rails: ActionMailer::Baseからのrequire "active_support"
の脱落を修正
# actionmailer/lib/action_mailer.rb
+require "active_support"
require "active_support/rails"
どこで抜けたのかと思って履歴を追ってみたところ、3f8409でaction_mailer/base.rbからrequire
行が引越したのを見つけました。
+require 'active_support/core_ext/class'
+require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/array/uniq_by'
+require 'active_support/core_ext/module/attr_internal'
+require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/string/inflections'
そしてその前からrequire "active_support"
はなかったようです。
-require 'active_support/core_ext/class'
-require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/array/uniq_by'
-require 'active_support/core_ext/module/delegation'
-require 'active_support/core_ext/string/inflections'
Rails: RuntimeReflection#alias_name
に型変換を追加
以下が改修点です。
- Arel::Table.new(table_name)
+ Arel::Table.new(table_name, type_caster: klass.type_caster)
そのままArel::Table
も見てみました。
def initialize(name, as: nil, type_caster: nil)
@name = name.to_s
@type_caster = type_caster
...
end
type_caster
は以下にありました。
...
class Connection # :nodoc:
def initialize(klass, table_name)
@klass = klass
@table_name = table_name
end
...
Rails: bind_param
とbind_attribute
をActiveRecord::TestCase
に切り出す
kamipoさんによるテストコードの修正です。
- result = @connection.select_all("SELECT * FROM posts WHERE id = #{Arel::Nodes::BindParam.new.to_sql}", nil, [[nil, post.id]])
+ result = @connection.select_all("SELECT * FROM posts WHERE id = #{bind_param.to_sql}", nil, [[nil, post.id]])
- binds = [Relation::QueryAttribute.new("id", "10", Type::Integer.new)]
+ binds = [bind_attribute("id", "10", Type::Integer.new)]
テストコードで上のように重複している多数のnew
をリファクタリングして、以下のようにtest_case.rbに集約しています。
# activerecord/test/cases/test_case.rb
+ def bind_param
+ Arel::Nodes::BindParam.new
+ end
+
+ def bind_attribute(name, value, type = ActiveRecord::Type.default_value)
+ ActiveRecord::Relation::QueryAttribute.new(name, value, type)
+ end
一同でチェックしながら「たぶんテストコードのファイルの複製を繰り返しているうちにnew
がかぶってきたんだろうねー」という声がありました。
kamipoさんの地道な修正に頭が下がります。
mini_racer: 軽快なJS V8エンジン gem
mini_racerは名前からうかがえるように、GoogleのJavaScriptエンジンであるV8をRailsで使うときの定番gemであるtherubyracerの縮小版です。
試しにmini_racerをインストールしてみると、libV8のバージョンがtherubyracerより随分進んでます。
READMEのベンチマークを見る限りはかなり軽快そうです。
# https://github.com/discourse/mini_racer より
$ bundle exec ruby bench.rb mini_racer
Benching with mini_racer
mini_racer minify discourse_app.js 9292.72063ms
mini_racer minify discourse_app_minified.js 11799.850171ms
mini_racer minify discourse_app.js twice (2 threads) 10269.570797ms
sam@ubuntu exec_js_uglify % bundle exec ruby bench.rb node
Benching with node
node minify discourse_app.js 13302.715484ms
node minify discourse_app_minified.js 18100.761243ms
node minify discourse_app.js twice (2 threads) 14383.600207000001ms
sam@ubuntu exec_js_uglify % bundle exec ruby bench.rb therubyracer
Benching with therubyracer
therubyracer minify discourse_app.js 171683.01867700001ms
therubyracer minify discourse_app_minified.js 143138.88492ms
therubyracer minify discourse_app.js twice (2 threads) NEVER FINISH
Killed: 9
ところで、therubyracerといえば以下の記事で知られているように昔のRailsでひどい目にあった方が結構いました。RailsのV8周りについては、mini_racerに置き換えるにしても一応注意する方がよさそうです。
私もGemfileのtherubyracerのコメントアウトをむやみに解除しないようにします。
sassc-ruby: 高速Sassコンパイラ gem
sassc-rubyはSassのコンパイルをC言語ライブラリで行えるgemです。
# https://github.com/sass/sassc-rub より
[1] pry(main)> Benchmark.bm { |bm| bm.report { Rails.application.assets["application.css"] } }
user system total real
1.720000 0.170000 1.890000 ( 1.936867)
# Using sass-rails
[1] pry(main)> Benchmark.bm { |bm| bm.report { Rails.application.assets["application.css"] } }
user system total real
7.820000 0.250000 8.070000 ( 8.106347)
sassc-rubyのリポジトリでは★50個程度で「ありゃ」と思ったのですが、READMEに書いてあったsassc-railsの方は★400個達成しています。こちらはまだ2年ほどの比較的若いgemです。
急いでインストールする必要はないと思いますが、Sassの性能改善が不可避になったときに検討してみてもよいかもしれません。
Oj(Optimzed JSON) gem 3.0.0リリース(RubyWeeklyより)
- サイト: http://www.ohler.com/oj/index.html
- リポジトリ: ohler55/oj
高速を謳っているJSONパーサーgemです。akioさんがその場で早速動かしてみました。
require 'json'
v = {:a => {:b => :c}}
s = JSON.dump(v) # => "{\"a\":{\"b\":\"c\"}}"
JSON.parse(s, :symbolize_names => true) # => {:a=>{:b=>"c"}}
require 'oj'
v = {:a => {:b => :c}}
# s = Oj.dump(v, :mode => :compat) # => "{\"a\":{\"b\":\"c\"}}"
s = Oj.dump(v) # => "{\":a\":{\":b\":\":c\"}}"
Oj.load(s) # => {:a=>{:b=>:c}}
# oj はいったん文字列にしてしまったキーはシンボルにはできないと思いきや :symbol_keys オプションでシンボルに戻せる
# ただ値は文字列のまま
s = Oj.dump(v, :mode => :compat) # => "{\"a\":{\"b\":\"c\"}}"
Oj.load(s, :symbol_keys => true) # => {:a=>{:b=>"c"}}
それにしても、JSONパーサーってどうして言語ごとにあんなにたくさんあるんでしょうか。せっかくJSONの仕様が定まっているのに実装がこんなに多いのは「とにかく今この問題を切り抜けたい」「既存のJSONパーサーの使い勝手が気に入らない」といった理由なのでしょうか。
なお私はbashスクリプトでjqをときどき使っています。癖は強いですが速いです。
bootsnap gem: Railsに新たなキャッシュを追加して高速化(RubyWeeklyより)
- リポジトリ: Shopify/bootsnap
大規模Railsアプリをキャッシュで高速化するgemだそうです。
READMEで「Beta-quality」と書かれていることもあり、まだ発展途上のようですが、READMEはかなりみっちり書かれています。
やっていることは「$LOAD_PATH
などのスキャンを止める」「RubyのバイトコードコンパイルやYAMLをキャッシュする」など、きわどい感じです。一同から「えぇー、#require
にパッチ?!」と声が上がりました。
# https://github.com/Shopify/bootsnap/blob/master/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb#L48 より
class << Kernel
alias_method :require_without_cache, :require
def require(path)
if resolved = Bootsnap::LoadPathCache.load_path_cache.find(path)
require_without_cache(resolved)
else
raise Bootsnap::LoadPathCache::CoreExt.make_load_error(path)
end
rescue Bootsnap::LoadPathCache::ReturnFalse
return false
rescue Bootsnap::LoadPathCache::FallbackScan
require_without_cache(path)
end
...
READMEで参考として挙げられていた中にRubyコミッターであるko1さんのライブラリがありました。
コマンドがkakidasu
だったりと「こういう名前って英語圏ではどうなんでしょ?」と聞かれてしまいました。私は英語村出身ではありませんが、類推が効かなくてつらそうな感じですね。
ところで、READMEに書いてあるyomidasu
はgemspeck以外のどこにも見当たりませんでした。もしかして修正漏れでしょうか?
Rubyのシャドウイングで生じたバグ(RubyWeeklyより)
この挙動には一同かなり驚きました。Rubyがこんなふうに振る舞うなんて。akioさんも早速ぶん回してみました。
# https://thomasleecopeland.com/2017/04/20/shadowing-bug-in-the-wild.html
class Foo
def buz
42
end
def bar
unless buz
buz = 21
end
buz
end
end
p Foo.new.bar # => さて何が返るでしょうか?
Ruby 2.4.1での実行結果は以下でした。
はい、ご覧のとおりnil
が返ります。
元記事で「シャドウイング」と呼んでいるこの動作は、Rubyの公式ドキュメントにもしっかり書かれています。知らずに踏みそうで怖いですね。ドキュメントをざっと翻訳してみました。
Ruby 公式ドキュメントより
ローカル変数は、代入の発生時ではなくパーサーが代入を検出したときに作成される。
a = 0 if false # ここではaに何も代入されないことに注意
p local_variables # => [:a]
p a # => nil
メソッド名とローカル変数が類似していると、次の例のようにコードで混乱が生じることがある。
def big_calculation
42 # なにしろ「42」なので計算にものすごく時間がかかる
end
big_calculation = big_calculation()
ここでは
big_calculation
への参照はすべてローカル変数と解釈され、キャッシュされる。メソッドを呼ぶにはself.big_calculation
を使う。上のように空の丸かっこ
()
をつけるかself.
などのレシーバを明示的に使うことで、メソッドを呼び出せる。ただしレシーバを明示すると、メソッドがpublicでない場合にNameErrorが生じることがある。もうひとつの紛らわしい例は、以下のように後置の
if
を使う場合である。
p a if a = 0.zero?
上を実行すると
true
ではなく「undefined local variable or method 'a'」が出力される。Rubyはif
の左にある単独のa
をまず解析する。解析の時点では代入が行われていないため、Rubyはa
をメソッドであると仮定する。その後a
への代入が行われると、Rubyはこれをローカルメソッドへの参照と仮定する。この紛らわしい動作は、式が見た目とは違う順序で実行されていることによる。最初にローカル変数への代入が行われ、続いて存在しないメソッドを呼び出そうとしている。
Bundlerの新しいオプション
bundle update --conservative
というオプションを指定すると、gemのバージョンの進み方を極力少なくできるそうです。いいことを知りました。
見つけにくいスペルミスが原因でエラーになったお(RubyWeeklyより)
gauges
という名前を誤ってguages
にしてしまい、長年そのまま動いていたが、コード改修後のデプロイでビルドできなくなったことで発覚したというストーリーです。
レビュアーは何人もいたのに誰一人このスペルミスに気づかなかったという事実に著者も呆然としたそうです。
StringIO
: ファイルのように振る舞うオブジェクト
StringIOを使って、ファイルのように振る舞うオブジェクトを使う方法を解説しています。StringIOはRubyの公式ライブラリです。
テストコードで「ファイル入出力をテストしたいが実際にファイルは作って欲しくない」場合などに便利そうです。
途中で「StringScannerは名前は似てるけど違いますよー」と書いてあります。
Friendly_id: URLの無味乾燥な数字をActiveRecordの値に置き換えるgem(RubyFlowより)
- 元記事: How Rubycoders use FriendlyId gem
- リポジトリ: norman/friendly_id
以下のように、URLのパラメータをActiveRecordの値に置き換えてくれるそうです。日本人にはあまり関係なさそうかなと思いました。
※morimorihogeさんから「github.com/:username
のようないわゆるslugでの使いみちがありますよー」と補足いただきました。
http://example.com/states/4323454
↓
http://example.com/states/washington
一同で見ながら「ブログタイトルなんかをこれで処理したら、タイトルが変わったときにURLがユニークじゃなくなっちゃうんじゃ?」というツッコミがありました。
RailsをCapybaraでリダイレクトさせる方法(RubyFlowより)
拍子抜けするほど短い記事です。英語の記事を読み慣れていない方はぜひチャレンジしてみてください。
Rails製CMSアプリのベストはどれだ(RubyFlowより)
Rails製CMSアプリっていったいいくつあるのでしょうか。こんなに嫌になるほど種類があって分散してしまうと「だったらWordPressでいいよ」となってしまいそうです。
Rubyで学ぶSOLIDオブジェクト指向(RubyFlowより)
みっちり書かれていてよさそうな感じの記事です。SOLIDについては以下をどうぞ。
我、明示的でないプログラミングを愛する(RubyWeeklyより)
DHHのエッセイです。
ざっとしか読んでませんが、「コードで常に何もかも明示的に書かなきゃいけないんだとしたらそんなのゴメンだ」「ActiveRecordのモデルみたいに簡潔な方がいいに決まってる」「コードを明示的に書くのが好きというヤツは、似たような定型コードを何度も何度も書かされるのが好きってことなんだろ、そのことを認識しないでそう言ってるんだとしたら不実じゃないのか」といったDHH節炸裂です。
原文のvalueは技術用語の「値」ではなく、本来の「値打ち」「価値」の方の意味ですね。
Danger: プルリクの書式や手順を揃えるgem(GitHub Trendingより)
- サイト: http://danger.systems/
- リポジトリ: danger/danger
CIでのコードレビュー作法を定めるgemだそうです。以下のような感じでコメントを付けます。
GitHub/GitLab/BitBucketなどのメジャーなリポジトリに対応しています。設定ファイルはそのまんまDangerfileです。以下のようなオプションがあります。
- tableにコメント
message("アプリにgemを3つ以上追加したな(゚Д゚)ゴルァ!!")
- CIワーニングの宣言
warn("CHANGELOGに何も書いてないぞ(゚Д゚)ゴルァ!!")
- CIブロッキングエラーの宣言
fail("linterは大層お怒りのご様子です")
- tableの下にmarkdownを出力
markdown("## ")
- diff行にmarkdownを出力
warn("自分の名前書けや", file: "CHANGELOG.md", line: 4)
どなたか試してみたい勇者はいらっしゃいませんか。
Rooby->Goby: 100% Go言語で書かれたRuby実装
- リポジトリ: rooby-lang/goby
この間ウォッチで取り上げたgorubyはイマイチでしたが、このGobyはわたしもつい「もしや」と思ってしまいました。3か月ほどで早くも★が1100越えで、毎日のようにコミットが増えています。MacならHomebrewのcaskでもインストールできます。
ご多分に漏れず多くの機能が未実装で(まだirbもない)、最適化もこれからという状態ですが、ちょっと触ってみた限りでは動作が非常に安定しており、しかも「Ruby Under Microscope」やVMコードへのトランスパイルなどを下敷きにしたまっとうなつくりのようです。動作の猿真似ではなくちゃんと「すべてがクラス」になっています。
たとえば以下はRubyとGobyのどちらでも動きます。goby -c
でバイトコードを出力できます(バイナリではなくテキスト)。
module Foo
def ten
10
end
end
class Baz
def ten
1
end
def five
5
end
end
class Bar < Baz
include(Foo)
end
b = Bar.new
puts(b.ten * b.five)
Gobyの立ち上がりはRubyに比べてかなり速いですが、現時点の機能の少なさと、外部ライブラリを読み込んでないオールインワンバイナリという点を差し引いておく方がよさそうです。個別のメソッドにはまだまだ遅いものもあるようです。
元々Roobyという名前でしたが、おととい 「Roobyという名前は米国人にとってRubyと発音がまったく同じなので紛らわしいんだよね: もちっと違う名前にしない?」というIssueが上がり、その翌日本当に名前をGobyに変えてしまいました。
今週は以上です。
関連記事
- 週刊Railsウォッチ(20170428)Rails 6.xでの’#form_for’と
#form_tag
廃止決定のその後、deviseの5.1対応はこれから、ほか - 週刊Railsウォッチ(20170421)RailsConfが来週アリゾナで開催、コントローラを宣言的に書けるdecent_exposure gemほか
- 週刊Railsウォッチ(20170414)サーバーを危うくする1行のコード、PostgreSQL 10の新機能ほか
- 週刊Railsウォッチ(20170407)N+1問題解決のトレードオフ、Capybaraのテスト効率を上げる5つのコツほか
- 週刊Railsウォッチ(20170331)PostgreSQLの制約機能を使えるRein gemはビューも使えるほか
- 週刊Railsウォッチ(20170324)Ruby 2.4.1リリース、GAEがついにRubyに対応、このgemがないと生きていけない27選ほか
- 週刊Railsウォッチ(20170317)Railsパフォーマンスチューニング本、DBレコード存在チェックの最速メソッド、RubyのUnicode正規化ほか
- 週刊Railsウォッチ(20170310)クールなDocker監視ツールCtop、RailsがGoogle Summer of Code 2017に正式参加、Unicode 10.0.0ドラフト発表ほか
- 週刊Railsウォッチ(20170303)5.0.2正式リリース、メタプログラミングに懲りた話、bundler 1.12のバグ、すぐ試せるWebアノテーションほか
- 週刊Railsウォッチ(20170227)Rails 4.2.8リリース、SHA-1コリジョンアタック、便利なハッシュ変換ツールほか
- 週刊Railsウォッチ(20170217)Rails 4.2.8.rc2リリース、Ruby 2.4正規表現とActiveSupportのnormalizeほか
- 週刊Railsウォッチ(20170210)JRubyやRubiniusの配列への追加はスレッドセーフではないほか
- 週刊Railsウォッチ(20170203)AnyLogin gemで開発中に楽々再ログイン、イベント数ベース課金の監視サービスRollbarほか
- 週刊Railsウォッチ(20170127)わかりやすいAWSサービス名、Rails DBは便利、TruffleRubyの驚異的速度ほか
- 週刊Railsウォッチ(20170120)Ruby 2.5.0 devリリース、古いMySQLのサポート終了、uniqメソッドが削除ほか
- 週刊Railsウォッチ(20170116)Ruby 2.4の詳細、範囲指定したsumメソッドは速い、rescueの挙動を動的に変更ほか
- 週刊Railsウォッチ(20170110)ReactをRailsに置き換える、Ruby 2.4の新機能ほか
- 週刊Railsウォッチ(20161224)Float#roundの動作変更を取り消し、Rails 5.0.1/Ruby 2.4.0-rc1リリースほか
- 週刊Railsウォッチ(20161218)Ruby 2.4ではFloat#roundの動作が変わる、デフォルトのプライマリキーをBIGINTに変更ほか
- 週刊Railsウォッチ(20161209)Active Supportの非推奨メソッド廃止、RailsのjQueryへの依存を廃止ほか
- 週刊Railsウォッチ(20161125)Railsのデータベース・ベストプラクティス、SQLインジェクション解説ほか
- 週刊Railsウォッチ(20161117)DockerホスティングのHyper.sh、accepts_nested_attributes_for殺すほか
- 週刊Railsウォッチ(20161109)bundler audit gemは超おすすめ、CIAのFactbook gemほか
- 週刊Railsウォッチ(20161102)HTML 5.1正式勧告、CSS中央揃えに便利なサイトほか
- 週刊Railsウォッチ(20161027)LinuxカーネルのDirty COW脆弱性、DeviseはWikiを読めほか
- 週刊Railsウォッチ(20161019)ObjectSpaceモジュール活用法、Capybara統合、コミッターを撮影するソフト
- 週刊Railsウォッチ(20161012)RubyのHashの詳細、RethinkDBの会社が終業ほか
- 週刊Railsウォッチ(20161005)Mac OS SierraとRubyの問題、Learning Gitほか
- 週刊Railsウォッチ(20160928)constantizeの注意点、GoのGUI “gallium”登場ほか
- 週刊Railsウォッチ(20160921)クールなHTMLエディタ「Mozilla Thimble」他
- 週刊Railsウォッチ(20160913)MySQLの脆弱性ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。