- Ruby / Rails関連
週刊Railsウォッチ(20180813)Rails 5.2.1リリース、sanitize_sql_arrayは5.2からpublicだった、Dev.toがRailsアプリのソースを公開ほか
こんにちは、hachi8833です。自宅のエアコンの室内機からジャージャー水漏れしたのでこいつ↓でドレーンパイプを吸ったらコガネムシの破片が転がり出てきました🐞。
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを社内有志でつっついたときの会話です👄
⚓Rails: 先週の改修(Rails公式ニュースより)
Rails 5.2.1が8/7に正式にリリースされました🎉🎉。
早速記念写真。
その後のコミットを見ると、Railsガイドの修正が目立ちます。
なお、References to changes in Rails 5.2という変更点まとめ記事をHacklinesで見つけました。網羅しているかどうかはわかりませんが、トリビアな変更を避けているっぽいです。
つっつきボイス: 「お、これで5.2系もマイナーバージョンが1つ上がって安定度が増すかな😋」「😆書籍も初版は誤植が付き物ですしね」「実際gem install rails
でインストールされるようになってから本格的にバグが見つかるし😎」
⚓ActiveRecordのsanitize_sql_*
系メソッドが5.2で既にpublicメソッドに
先週のではなく2017年のy-yagiさんによる改修でした。
それ、絶対どこの会社にもあるw
— アルフォートおじさん (@joker1007) August 8, 2018
5.2からpublicやで https://t.co/lmuaeXnblI
— Ryuta Kamizono (@kamipo) August 8, 2018
# activerecord/test/cases/sanitize_test.rb#L12
def test_sanitize_sql_array_handles_string_interpolation
quoted_bambi = ActiveRecord::Base.connection.quote_string("Bambi")
- assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi"])
- assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi".mb_chars])
+ assert_equal "name='#{quoted_bambi}'", Binary.sanitize_sql_array(["name='%s'", "Bambi"])
+ assert_equal "name='#{quoted_bambi}'", Binary.sanitize_sql_array(["name='%s'", "Bambi".mb_chars])
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote_string("Bambi\nand\nThumper")
- assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper"])
- assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper".mb_chars])
+ assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.sanitize_sql_array(["name='%s'", "Bambi\nand\nThumper"])
+ assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.sanitize_sql_array(["name='%s'", "Bambi\nand\nThumper".mb_chars])
end
もうsend
で叩かなくて済むということですね。
つっつきボイス: 「おおこれは前から何かと話しに上がってたヤツ: 自分もsend
で叩いてたし」「やっぱり使ってたんですね?」「sanitize_sql_array
とかは生SQLを書く人には絶対に必要です😤」「これはSQLインジェクション防止のために超大事なヤツっす」「なぜ今までこれがprivateだったのかホント不思議なくらいで、ツイートにもあるけど誰もが同じこと思ってた」「send
すれば使えますけどねっ🕶書き方としてよくないというだけで」
「生SQLで変数のプレースホルダーが複数出てきたりすると結局sanitize_sql_array
とかでやる以外に方法はないですね: 当時自力で追いかけましたもん🤓」「2017年に入ってたのにウォッチで見逃してたとは...😅これだけのために5.2入れる甲斐がありそう」「あ、今回の5.2.1からじゃなくて5.2で既に入ってたのか😲」「そうそう、もう大手を振って使えます😋ある程度以上大きなプロジェクトならたいてい生SQL書くことになるので」
確かに5.2で入ってました↓。5.1ではClassMethods
が丸ごとprivateになっていました。
# https://github.com/rails/rails/blob/5-2-0/activerecord/lib/active_record/sanitization.rb#L123
def sanitize_sql_array(ary)
statement, *values = ary
if values.first.is_a?(Hash) && /:\w+/.match?(statement)
replace_named_bind_variables(statement, values.first)
elsif statement.include?("?")
replace_bind_variables(statement, values)
elsif statement.blank?
statement
else
statement % values.collect { |value| connection.quote_string(value.to_s) }
end
end
private
参考: Ruby on Rails 5.2 / ActiveRecord::Sanitization::ClassMethods
— DevDocs
参考: SQLインジェクション - Wikipedia
「今までprivateだったのは、もしかすると『生SQL書くな』ってことなのかな?なんて思ったりもするけど」
「話逸れますけど、@__gfx__
さんのTwitter IDの前後にアンスコが2つずつついてるので、この人のツイートはTechRachoの記事に直接埋め込めないんです😭」「Markdownと誤認識されちゃうのね: ワカルワカル😆」「RailsdmでElasticsearchの話してた方でしたね↓」
「SQL向けのpublicなsanitizeメソッドって他にあった気がしないでもないけど気のせいだったかな🤔...?」
ActiveRecord::Base.sanitize
が以前あったのが削除されたそうです↓。placeholder展開もしないようです。
* ActiveRecord::Base.sanitize removed in 5.1 · Issue #28947 · rails/rails
追いかけボイス: 「(後日)sanitize_sql_array
あまり使わないと聞きましたが」「Arelは基本使わないし、生SQLも本当に必要になるまで使わないマンなので🤓」「Rails wayというかActiveRecord wayでやってるんですね☺️」「生SQLは最近少し使ったのでそのときはさすがにsanitize_sql_array
しましたが😎」
⚓「Relation#update
のスコープ追加回避」を取り消し
5.2.1rc1から5.2.1の唯一の変更点でした。
# activerecord/lib/active_record/persistence.rb#L100
- def update(id, attributes)
+ def update(id = :all, attributes)
if id.is_a?(Array)
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
object.update(attributes[idx])
}
+ elsif id == :all
+ all.each { |record| record.update(attributes) }
else
if ActiveRecord::Base === id
raise ArgumentError,
"You are passing an instance of ActiveRecord::Base to `update`. " \
"Please pass the id of the object by calling `.id`."
end
object = find(id)
object.update(attributes)
object
end
end
つっつきボイス: 「all.each { |record| record.update(attributes) }
のところeach
で回してるとは😲あ、でもeach
でやらないとフックが実行されないのか: でもパフォーマンスが問題になりそうな気もするけど🤔」「breaking changesになるのでいったん取り消したみたいですね」「確かにこれはbreaking changesになりますね: ARの#update
のフックをざっと見てみると、before_validation
とかbefore_save
なんかも呼ばれてるので、breaking changesだけどこの取り消し前の修正が正しいんだな💡」「😃」
参考: Railsのcallbackについて調べた - Qiita
「元の修正は0b29a42か: それにしてもIDにarrayを渡せるとは知らなかったし」「まあ自分はRelation#update
はそんなに頻繁には使ってませんけどね😋」
⚓DBのパラレルテストを高速化
# activerecord/lib/active_record/tasks/database_tasks.rb#L248
def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env, spec_name = "primary") # :nodoc:
file ||= dump_filename(spec_name, format)
+ verbose_was, Migration.verbose = Migration.verbose, verbose? && ENV["VERBOSE"]
check_schema_file(file)
ActiveRecord::Base.establish_connection(configuration)
case format
when :ruby
load(file)
when :sql
structure_load(configuration, file)
else
raise ArgumentError, "unknown format #{format.inspect}"
end
ActiveRecord::InternalMetadata.create_table
ActiveRecord::InternalMetadata[:environment] = environment
+ ensure
+ Migration.verbose = verbose_was
end
つっつきボイス: 「ピンポイントで修正してますね」「これは見てのとおりマイグレーションを呼ばずにスキーマを読み込むように変わってるし: これなら確かに速くなる🏎」
⚓ネストした複数のrespond_to
同士に互換性がない場合にのみ例外を発生するようになった
# actionpack/lib/action_controller/metal/exceptions.rb#L64
+ class RespondToMismatchError < ActionControllerError
+ DEFAULT_MESSAGE = "respond_to was called multiple times and matched with conflicting formats in this action. Please note that you may only call respond_to and match on a single format per action."
+ def initialize(message = nil)
+ super(message || DEFAULT_MESSAGE)
+ end
+ end
つっつきボイス: 「今までのだとouterとinnerが違っても通っちゃってたのか↓: jsなのに中にHTMLがあったらそりゃマズイ」
# 同PRより
respond_to do |outer_type|
outer_type.js do
respond_to do |inner_type|
inner_type.html { render body: "HTML" }
end
end
end
「でRespondToMismatchError
クラスを新しく定義してrespond_to
でraiseするようになったと↓: respond_to
ってネストできるのね💡」
# actionpack/lib/action_controller/metal/mime_responds.rb#L193
def respond_to(*mimes)
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
collector = Collector.new(mimes, request.variant)
yield collector if block_given?
if format = collector.negotiate_format(request)
+ if content_type && content_type != format
+ raise ActionController::RespondToMismatchError
+ end
_process_format(format)
_set_rendered_content_type format
response = collector.response
response.call if response
else
raise ActionController::UnknownFormat
end
end
⚓HEAD
のデフォルトのContent-Type
がtext/html
に
# actionpack/lib/action_controller/metal/head.rb#L38
if include_content?(response_code)
- self.content_type = content_type || (Mime[formats.first] if formats)
+ self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html]
response.charset = false
end
つっつきボイス: 「今までHEADが返してたのはMime::NullType
...😲」「HEADはHTTPのメソッドですね」「あーなるほど、HEAD 200↓のときにMime::NullType
を返すのは確かにどこかヘンだ: Webサーバーならtext/html
を返すのが普通だと思うし」
# actionpack/test/controller/render_test.rb#L230
+ def head_default_content_type
+ # simulating path like "/1.foobar"
+ request.formats = []
+ respond_to do |format|
+ format.any { head 200 }
+ end
+ end
「たぶんですけど、修正前だとヘルスチェックで引っかかるんじゃないかな」「HEADって本文がないレスポンスでしたっけ」「お、RFC2616の14.17によるとContent-Type
はGETリクエストと同じものを返すとある↓から、Mime::NullType
を返すのは仕様違反だったっぽい」「おー😲」「もちろん常にtext/html
を返していいものではないので適切なものを返さないといけないはずですが、少なくともデフォルト値としてはtext/html
の方が適切でしょうね🧐」「なるほどっ😃」
The Content-Type entity-header field indicates the media type of the entity-body sent to the recipient or, in the case of the HEAD method, the media type that would have been sent had the request been a GET.
HTTP/1.1: Header Field Definitions 14.17より
⚓ログ出力されるIPアドレスをプロキシのIPからリモートIPに修正
# railties/lib/rails/rack/logger.rb#L48
# Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
def started_request_message(request) # :doc:
'Started %s "%s" for %s at %s' % [
request.request_method,
request.filtered_path,
- request.ip,
+ request.remote_ip,
Time.now.to_default_s ]
end
つっつきボイス: 「おおー!この修正はかなりエライ!💪そしてbreaking changeなんだけど、これがbreaking changeになるような設定はよくないという意味でこの修正は正しい」「というと?」「修正前だとロードバランサーを間に挟んだときにロードバランサーのIPが取れてしまうから」「これってbreaking changeになるんでしょうか?ログの項目が変わっただけなのかなと思ったり」「いえいえ、修正前にこれでロードバランサーのIPが取れると思ってそれを当てにして実装していた人たちにとってはbreaking changeになるということです: 修正後が正しいのは確かですが🧐」
参考: remote_addrとかx-forwarded-forとかx-real-ipとか - Carpe Diem
⚓ソート順変更をいったん取り消し
例のlimit(1).first
問題の修正(#24131)の影響が大きいので、deprecationサイクルをはさんで行うことにしたそうです。
# activerecord/lib/active_record/relation/finder_methods.rb#L552
def ordered_relation
- if order_values.empty? && primary_key && limit_value.blank?
+ if order_values.empty? && primary_key
order(arel_attribute(primary_key).asc)
else
self
end
end
つっつきボイス: 「ああこの間のlimit(1).first
ですね: deprecationを挟むはもっともだし、こういうPRを見るとRailsのdeprecation warningにはちゃんと対応しないといけないなって改めて思いますね😎」
⚓Rails
⚓Rails 5.2のcredentialチートシート(Ruby Weeklyより)
# 同記事より
Rails.application.credentials.secret_key_base
Rails.application.credentials.fetch(:secret_key_base) { raise "it seems you didn't configure credentials" }
Rails.application.credentials[:secret_key_base] || "someDefaultValue"
ENV["SECRET_KEY_BASE"] || Rails.application.credentials[:secret_key_base]
Rails.application.credentials.dig :secret_key_base
つっつきボイス: 「例のRAILS_MASTER_KEY
を使うヤツっすね: 前も話しましたけど個人的にはあまり好きでない方式」「そうでしたね☺️」「リポジトリよりはKMSとかに保存したいし、この方式だとマスターキーを持っている人じゃないとviで開けないし、そもそもエディタで開いて編集するのもちょっとねー😅」「確かにー」「ま、でも個人がHerokuで動かす小規模なアプリならこの方法で十分だと思いますけどね」「小規模チームにもよさそう」
「ところで記事タイトルのCheat Cheatってスペルミスなのかわざとなのかちょっと謎でした🤔」「😆」
↓Urban Dictionaryに一応ありました。ダジャレのようです。
参考: Urban Dictionary: Cheat Cheat
⚓Hanami v1.3.0.beta1リリース
変更点:
- デフォルトのフレームワークがRSpecに
Hanami::Utils::String
やHanami::Utils::Hash
やHanami::Utils::Inflector
が非推奨に: 一部はdry-inflectorに移行body_parsers
がミドルウェアに移行- HTTPSがRackミドルウェアに移行
- アセットプリコンパイル時にディレクトリ構造を保持
- ネストしたモジュール/クラス定義でactions/views/mailersを生成
- CLIの機能追加
以下で試せます。
gem install hanami --pre
hanami new bookshelf
Hanamiは以前はLotusという名前だったことをついでに知りました。
つっつきボイス: 「RSpecに変わるのね☺️ユーザー数多いし」「HTTPSをRackミドルウェアに任せるんだそうです」「アプリ側ではSSLしないよなーやっぱり」
後で検索してみると、記事中で触れられていたrack-sslミドルウェアはアーカイブ化されてたので、今だとrack-ssl-enforcerミドルウェアを使うんでしょうね。
- リポジトリ: josh/rack-ssl -- 開発終了
- リポジトリ: tobmatth/rack-ssl-enforcer
⚓Dev.toがRailsアプリのソースコードを公開
フロントエンドはPreactjs、単体テストはJestだそうです。
つっつきボイス: 「社内Slackで教わりました」「Dev.toって本当にRailsだったのね😳」「爆速で有名になりましたね: CDNも使いまくってるんでしょうね」「CDNは当然使ったうえで他にもいろんな最適化やってるはず: CDNだけであんなに速くなるはずないので🧐」「コミット数500そこそこは意外に少ないかも」「お、コントローラでset_surrogate_key_header "now_page"
みたいにサロゲートキー使ってる↓👀」「何でしょう?」「ちゃんと見ないと何とも言えないけど、ハッシュ的なものに格納してるっぽいので、おそらくこの辺は固定ページなのかな」「最適化されまくったソースを参考にRailsアプリを書くのはちょっと大変そうだし、Railsで超高速を目指すのは果たしてどうなのかとは思いつつも、これは面白いですね😋今度中身見てみよっと🛠」
# 同リポジトリより
class PagesController < ApplicationController
# No authorization required for entirely public controller
before_action :set_cache_control_headers, only: %i[rlyweb now events membership survey]
def now
set_surrogate_key_header "now_page"
end
def survey
set_surrogate_key_header "survey_page"
end
def about
set_surrogate_key_header "about_page"
end
⚓Inspec: インフラのテスト/監査フレームワーク(Ruby Weeklyより)
つっつきボイス: 「RSpecのインフラ版みたいなやつが前からありましたけど...えっと何でしたっけ」「serverspec」「それそれ、それのライバル的なものみたいです」
- サイト: Serverspec - Home
「↓この辺はserverspecっぽい」
# inspec.ioより
describe file('/etc/myapp.conf') do
it { should exist }
its('mode') { should cmp 0644 }
end
describe apache_conf do
its('Listen') { should cmp 8080 }
end
describe port(8080) do
it { should be_listening }
end
「↓これはAWSのリソースチェックか: だいたいやってることは同じかな😎」
# inspec.ioより
describe aws_s3_bucket(bucket_name: 'my_secret_files') do
it { should exist }
it { should_not be_public }
end
describe aws_iam_user(username: 'test_user') do
it { should have_mfa_enabled }
it { should_not have_console_password }
end
参考: InSpecではじめるテスト駆動インフラ - tkak's tech blog
「そういえばInspecのロゴの下にちっちゃーくChefって書いてありますね↑」「うんChefっぽい」
「最近Chefってあまり聞かなくなった気がしますけどどうなんでしょう?」「Chefは何年か前に流行りましたが結局オーバースペックでしたね: Chefに精通したインフラエンジニアがいないと結局つらくなる」「😲」「そういえばBPS社内でもAnsibleに移り変わってますね」「chef-soloしか使わないならAnsibleでいいんじゃね?って思うし: Ansibleはシンプルだし実行順序が保証されるので」「ということは...」「Chefは実行順序が保証されないところが大変: イミュータブルなコンフィグレーションだと思って書かないとつらい」「あー」「実行順序を保証するときにはChefからシェルスクリプト呼んだりとか」「それって本末転倒感💦」
「Itamaeは?」「クックパッドさんの作ったヤツで、こちらは使いやすいです❤️」
「あとChefにいいrecipeがあまりなかったのが残念」「うーむ」「今はchef.ioだけど確か元々違う会社名でChefのホスティングサービスをやってたはず...何だっけ...(しばらく探す)↓Opscodeだっっっ!🎯」「しかも2013年に社名変わってたんですね」「というぐらい最近Chefを使ってなかった😅」
参考: Chef開発元のOpscode、社名をChefに変更。「検索が難しくなる」とあちこちで悲鳴が - Publickey
⚓番外: gist-it: GitHubリポジトリをGist的に埋め込めるサービス
つっつきボイス: 「Railsとは関係ないんですが、GistでなくてもブログにGitHub上のコードを埋め込めるというので後で試してみます」「😃」
できました↓。?slice=12:18
のようにパラメータを追加すれば行数を指定できます。
diffは無理みたい(´・ω・`)。
⚓Ruby trunkより
⚓提案: Hash#===
とArray#===
(Ruby Weeklyより)
- Feature #14916: Proposal to add Array#=== - Ruby trunk - Ruby Issue Tracking System
- Feature #14869: Proposal to add Hash#=== - Ruby trunk - Ruby Issue Tracking System
他にFeature #14973: Proposal of percent literal to expand Hash - Ruby trunk - Ruby Issue Tracking Systemもありました。
珍しくRuby Weeklyがtrunkのissueを紹介していたのですが、いずれも日本語で書かれています。
Hash#===
レシーバのキーの要素と引数のハッシュのキーの要素を #=== で比較して、全てが真なら true を返し、そうでないなら false を返す。
また、レシーバが空のハッシュの場合、引数が空のハッシュなら true を返し、そうでないなら false を返す。
user = { id: 1, name: "homu", age: 14 }
# name 要素が data にあるので true
p ({ name: "homu" } === user)
# => true
# 複数の要素があっても OK
p ({ id: 1, name: "homu", age: 14 } === user)
# => true
Array#===
配列の各要素をそれぞれ順に === で比較し、全要素が true の場合に true を返す。そうでない場合は false を返す。
# 配列の各要素を #=== を使用して比較する
[ String, /\w/ ] === [ "a", "c", 7 ] # => false
[ String, /\w/, (1..10) ] === [ "a", "c", 7 ] # => true
[ String, /\w/, (1..10) ] === [ "a", "!", 42 ] # => false
つっつきボイス: 「さっきbugs.ruby-lang.orgが落ちてたんですが今は動いてるのでやっと読めました💦」「issueは日本語だけど英語のレスもついているという😆」「トリプルイコールかー😲」「ディープな場合はどうなるんだろう?」「これは要素を順に比較するだけだからディープな動作ではないですね😎」「こういうのを見ると、メソッドの戻り値が複数の場合に===
でパターンマッチ的に比較したくなりますね」
「今は===
ってどこにあるんだっけ?(コードを掘り始める)」「owner
で取れますね確か」「あった: Kernel#===
なのね↓」「普段めったに使わない操作w」「methods
でやるとドバっとメソッドが表示されるから大変ですよね」
[].method(:'===').owner #=> Kernel
「Hash#===
とArray#===
、あったらうれしいという気持ちはわからなくもないけど」「===
だからcase
文で使えるってことなんですね」「これかー↓」「これは相当しっかり理解しとかないとハマりそう😅」「このcase
文はヤバイ😆」「Rubyのcase
文はもともとかなり自由だし」
#14916より
def plus *args
case args
# 数値の場合
when [Integer, Integer]
args[0] + args[1]
# 数字の場合
when [/^\d+$/, /^\d+$/]
args[0].to_i + args[1].to_i
# それ以外はエラー
else
raise "Error"
end
end
p plus 1, 2
# => 3
p plus "3", "4"
# => 7
p plus "homu", "mado"
# Error (RuntimeError)
⚓Solarisでコケるテストがある
つっつきボイス: 「Solarisという単語につい惹かれてしまいました」「Ruby、Solarisサポートしてるのか😲」「そういえばSolaris使ったことあります?」「大学にあった気がする」「前職でちょっぴり」「薄紫色のピザボックス型でした」「筐体はいろいろありますからね: Fireには冷蔵庫みたいなデカイのもあったし」「マシン自体は見たことなかった...」「自分Ultra SPARC IIIのマシン自宅に持ってました💪」「おほー!」「DECのAlpha 21264マシンも持ってたし: どっちも捨てちゃったかな...」「それもスゲー」「64ビットマシンだったし: 今は珍しくも何ともないという🤣」「🤣」「あっ、でもAlpha 21264はRISCだったし: RISCは珍しーだろー🤓」
参考: Solaris - Wikipedia
参考: Alpha 21264 - Wikipedia
参考: RISC - Wikipedia
⚓提案: Any
型
# issueより
class Any
class << self
def ===(b)
true
end
def ==(b)
true
end
def to_proc
proc { true }
end
end
end
# ------
case ['Foo', 25]
when [/^F/, Any] then true
else false
end
# => true
case {id: 1, name: 'foo', age: 42}
when {id: Any, name: /^f/, age: Any} then true
else false
end
# => true
case {id: 1, name: 'foo'}
when {id: Any, name: /^f/, age: Any} then true
else false
end
# => false
先のArray#===
やHash#===
と組み合わせるといい感じになるという提案でした。
つっつきボイス: 「お、Any
型かー: こういう番兵的なものが言語レベルであると実は便利なんですよね❤️」「確かにー」「Scala言語にAnyってのがあるのね」「番兵って...? 英語だとsentinelって言うみたいですが...」「よくC言語なんかで文字列終端を表すヌル文字\0
が番兵って呼ばれてますね: ただヌルって他でも使うから、たまたま値がヌルになるとそこで止まっちゃうなんてことがよくあるという」「なるほどー!😃」「だからこのAny
みたいに他では絶対使われない値があれば、終端が事前にわからないストリームなんかを扱う場合の番兵として使いやすいですよね☺️」
参考: Scala - Wikipedia
参考: 番兵 - Wikipedia
「提案しているのはRubyと関数型の記事でお馴染みのBrandon Weaverさんでした」
⚓Ruby
⚓Rubyの高速化は単純には見積もれない(Ruby Weeklyより)
Noah Gibbsさんの記事です。
つっつきボイス: 「パフォーマンスの見積もり方というか、個別の高速化を足しても100%の高速化にはならないよ的な話のようです」「ぼやきというか」「高速化は単純に足し算できませんからね🕶」「Noah Gibbsさんがチューニングしたものをさらに高速化するのはすげー大変そう」「自分ならマシンを速くして高速化するな🤣」「札束で殴る🤣」
⚓undercover: 賢いカバレッジ(Ruby Weeklyより)
- 元記事: Stop shipping untested Ruby code with undercover – [:] futureDev – Medium
- リポジトリ: grodowski/undercover
つっつきボイス: 「カバレッジに対するRuboCopみたいなものだそうです」「おー、lambdaとかの中までチェックしているみたい」「全部のクラスにはかけたくはないけど、決済系のコードみたいなすごく重要な部分に絞ってこういうツールをかけるのはありかも😋」「ありそうでなかったツールなんですね😃」「機会があったらピンポイントで使ってみようかな」
⚓STDOUT/STDERRデバッグメッセージの出処を突き止める
# 同記事より
class << STDERR
alias_method :orig_write, :write
def write(x)
orig_write(caller[0..3].join("\n"))
orig_write(x)
end
end
つっつきボイス: 「Sam Saffronさんの記事です」「ははぁこうやってSTDERRを拡張するのね↑」「おほー😃」「STDERRってクラスじゃないですよね?」「RubyではIOオブジェクトだったはず: Rubyはオブジェクトを直接拡張できるので、そこにalias_method
でwrite
をフックすると」「やるな~」「これは実にRubyらしい発想❤️」
参考: constant Object::STDOUT
(Ruby 2.5.0)
参考: class IO
(Ruby 2.5.0)
「他の言語だとどんな感じになるんでしょうか?」「他の言語だと、普通はSTDOUTやSTDERRのファイルディスクリプタが取れるので、ファイルディスクリプタをウォッチするOSのメソッド(macOSだとFSEvents、Linuxだとinotifyあたりだったかな)を使って、特定のディスクリプタに対して特定のAPI呼び出しをハンドルする感じですかね」「なるほどー😃」「例のguard gemみたいなファイル更新をチェックするソフトウェアは、たぶんそのあたりを使ってると思いますよ」「他の言語だとOS側でイベントを監視してもらわないといけないんですね」「ですです: 普通はOSにどこかでイベントをディスパッチしてもらわないといけない: でもRubyだと上のようにSTDOUTやSTDERRといったオブジェクトに直接フックをかけられるのが面白いですね😋」
そういえばGobyでたまたまSTDOUT.to_s
したらそのまんま<File: /dev/stdout>
が出てきたのを思い出しました😅。もろにファイルディスクリプタ。
⚓Rubyの整数の最適化
Ruby凄いよぉ (Rustでできる気がしない pic.twitter.com/igONjRDAYF
— uint256_t (@uint256_t) August 7, 2018
説明が足りなかった.
ポインタを, 何らかの構造体へのポインタとして使ったり, 整数そのものとして扱ったりすることを褒めたつもりだった— uint256_t (@uint256_t) August 7, 2018
参考: 第2章 オブジェクト -- Rubyソースコード完全解説
つっつきボイス: 「RubyのRVALUE
の最適化ってほんと半端ない」「RVALUE
は記事を翻訳して知りました↓」「ツイートで『ポインタを, 何らかの構造体へのポインタとして使ったり, 整数そのものとして扱ったりすることを褒めたつもりだった』ってありますね」「最適化では割と使われる手法だけど、自分はちょっとビビる😅」
/* http://i.loveruby.net/ja/rhg/book/object.html より*/
123 #define INT2FIX(i) ((VALUE)(((long)(i))<<1 | FIXNUM_FLAG))
122 #define FIXNUM_FLAG 0x01
(ruby.h)
「このFIXNUM_FLAG
↑っていう発想が面白い: 下位1ビットをフラグとして使うことでFixnumかBignumかを判定するという😆」「それ完全にハックやん...🛠」「4の倍数アドレス云々の部分は確かPOSIXで規定されていたはずだから、malloc
を使っていればポインタが4の倍数アドレスであることが保証されるはず」「仮に1ビット単位で最適化するソフトウェアがあったとしても、malloc
を使えば4の倍数にアラインされる」「その隙間をフラグに使うという😲」「爪に火を灯すような最適化🔥」
⚓Julia Evansさんがプロファイラを語る
つっつきボイス: 「このPodcastの凄い点は、スポンサーを募って文字を全部書き起こしているところかも」「おータイムスタンプまできっちり起こしてるし↑😲この起こしは大変そう」「PDFですけどね😆」「他のPodcastなんかで、あちこち<UNREAD>
みたいになってる文字起こしをちょくちょく見かけますけど、あれはきっと自動で起こしてるんだろうなーって🤣」「🤣」「🤣」「技術トークの起こしって音声認識泣かせですねホント」「実際に目の前で聞いてもよくわからないことあるし🤣」
⚓debug_helper: デバッグ情報をyaml風に出力するgem(Hacklinesより)
- 元記事: Custom Handlers for Ruby Gem debug_helper - DEV Community 👩💻👨💻
- リポジトリ: BurdetteLamar/debug_helper
# 同記事より
require 'debug_helper'
ary = [0, 'one', :two]
DebugHelper.show(ary, 'My mixed array')
Array (message='My mixed array'):
size: 3
Element 0: Fixnum 0
Element 1:
String:
to_s: one
size: 3
encoding: !ruby/encoding UTF-8
ascii_only?: true
bytesize: 3
Element 2:
Symbol:
to_s: two
size: 3
encoding: !ruby/encoding US-ASCII
つっつきボイス: 「ほほーyamlに出力とな」「出力に改行が混じったりするとyamlもそんなに読みやすくないけど😅」「😆」「↑このぐらいならキレイでも現実のデータだとなかなかこうならなかったりしそうだけど、とりあえずこうやって作ってみたのはいいですね😋」
⚓Rubyのオブジェクト生成方法を変えるには(Hacklinesより)
割と短い記事です。
つっつきボイス: 「モジュールでsuper
してるし↓」「なぬ?😅」「その後object.singleton_class.include(Logging)
してるから動くけど、なかなかキモい👻」
# 同記事より
module Logging
def make_noise
puts "Started making noise"
super
puts "Finished making noise"
end
end
class Bird
def make_noise
puts "Chirp, chirp!"
end
end
object = Bird.new
object.singleton_class.include(Logging)
object.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise
「最終的にself.new
してるし↓」「なぬなぬー?😅」「ははぁ、allocate
をオーバーライドできないからこういう遠回りな方法でやってるのか」「おー😲」「確かにコアクラスの方でオーバーライドされると手も足も出なくなっちゃうからこういう手段に出たんだろうけど、基本的にはこういうことはしない方がいい気はするなー🧐」「STIなんかで下の方に埋め込まれてコントロールできなくなっちゃうとか、たまにありそうですね」「new
を無理やりオーバーライドするか、さもなければリフレクションしまくるか、どっちもやりたくないなー😭」「この記事のやり方はモジュールをinclude
で入れているあたりがDIっぽいからまだいい方かも😋」
# 同記事より
class Bird
def self.new(*arguments, &block)
instance = allocate
instance.singleton_class.include(Logging)
instance.send(:initialize, *arguments, &block)
instance
end
end
⚓mini_i18n: 小規模なi18n化ライブラリ
- リポジトリ: markets/mini_i18n
# 同リポジトリより
>> MiniI18n.t(:hello)
=> "Hello"
>> MiniI18n.t(:hello, locale: :fr)
=> "Bonjour"
>> MiniI18n.locale = :fr
=> :fr
>> MiniI18n.t(:hello)
=> "Bonjour"
>> MiniI18n.t(:hellooo)
=> nil
つっつきボイス: 「文字どおりミニなi18n☺️」
「ところでRailsのi18nって基本的にRubyのI18nだから別に大きくはないですよね?」「お、でも#l
(#localize
のエイリアス)とか割と複雑なことやってた気がしたけど: DateTimeとかを渡すとその国の形式に変換してくれるヤツ」「あれってRubyのI18nに丸投げしてませんでしたっけ?」「あーrequire i18n
してるからやっぱりRubyのI18nですね」「でもエイリアスはRailsの方で定義されてるみたい...」「ややこしい😅」
# File actionview/lib/action_view/helpers/translation_helper.rb, line 117
def localize(*args)
I18n.localize(*args)
end
参考: Ruby on Rails 5.2 / ActionView::Helpers::TranslationHelper#localize
— DevDocs
- リポジトリ: svenfuchs/i18n
「そういえばRailsの#l
メソッドってnil
渡すとエラーになるんですよね😅」「そうそうw 誰もが一度はハマるヤツ😆」「でpresent?
するとその後if
文がだんだん増えていくという🤣」
「ちょっと話逸れるんですけど、10年くらい前に英語/日本語を同じぐらい使える大先輩に『ローカライズの仕事してます』って話したら大爆笑されたことがあって、どうやらlocalizeっていう単語は技術から離れると『土着化』とか『現地住民と同化する』的に響くところがあるみたいで」「😆」「もともとそういうニュアンスで使われてた時代があったからかも🤔」「そのせいかどうかわかりませんが、最近はlocalizeという言葉をglobalizeと言い換えてるところもあるようです☺️」
localizeの基本的な意味は「局所化」ですね。
⚓その他Ruby
Finally, Ruby's JIT compiler started to work on Microsoft Visual C++!! https://t.co/Gva7P4MUcH
— k0kubun (@k0kubun) August 7, 2018
Rubyは何か最適化をしようとすると先回りしてその最適化を阻む言語仕様が用意されているので、全ての言語実装における最適化を研究して仕様が作られた可能性がある
— Miura Hideki (@miura1729) August 7, 2018
↑シンガポールでの講演だそうです。
⚓クラウド/コンテナ/インフラ/Linux/Serverless
⚓Google Cloud MemorystoreのRedisサービス
- 元記事: AWS vs Google Cloud Platform. What is better for DevOps in the cloud?
- 元記事: Meet new Cloud Memorystore for Redis from Google Cloud
- サイト: Memorystore | Google Cloud
最初の比較記事を書いたときはCloud Memorystoreはまだなかったそうです。
⚓GLB: GitHubのロードバランサー
つっつきボイス: 「GLBはまだざっとしか見てなくて正体わかってないんですが、この図↓などを見た感じではNGINX Unitにちょっと似ている印象でした」「とりあえずロゴ↑は秀逸ですね😋」「カワイイ❤️」
⚓Statsd: 統計収集ツール
- リポジトリ: etsy/statsd
Node.jsベースで書かれているようです。★13,000超えです。
つっつきボイス: 「Sam Saffronさんの記事に登場してたので」「めっちゃ★付いてる」「これは使ったことなかった」
参考: netdata と statsd によるリアルタイムモニタリング - biaxident’s blog
⚓標準電波JJYの夏時間対応
ちなみにJJY(電波時計)は夏時間の対応をしていたりする. https://t.co/iI6Yji7zBk
— 泉㌠智紀 (@jsdfq43wtr) August 7, 2018
欧米のエンジニアの多くがマルチバイトの無い世界に甘やかされているように、我々日本のエンジニアも国内時差やサマータイムの無い世界に甘やかされているので、否応無しに対応が降ってくるのは面白いかもしれないとも思ったりもする
— songmu (@songmu) August 6, 2018
つっつきボイス: 「JJY、夏時間は対応してるけど2時間ずらしは対応してないという😆」
⚓サーバーレスアプリのアーキテクチャとパターン(Serverless Statusより)
つっつきボイス: 「Microsoftのドキュメント、例によって最近まとまりがいいですね😋」「日本語版もあります↓」
参考: サーバーレス アプリ: アーキテクチャ、パターン、および Azure の実装 | Microsoft Docs
⚓Google App Engineの次世代ランタイムとPython 3.7
- 元記事: Introducing App Engine Second Generation runtimes and Python 3.7 | Google Cloud Blog
- リポジトリ: GoogleCloudPlatform/google-cloud-python
Introducing App Engine Second Generation runtimes and Python 3.7 | Google Cloud Blog
よし、これでpython2を捨てれる
つっつきボイス: 「個人的におおっと思ったので: 以前のApp EngineライブラリはPython 2系で、機械学習系ライブラリは3系が多かったので、自分のMacbookでpyenvで切り替えないといけなくて何かと面倒でした」「今までPython 3系使えなかったとは😲」「GoogleってPythonの偉い人がいるはずなんだけどなー: でももしかするとその人が2系を守ってたとか、2系に寄せたチューニングをやりすぎてたとかだったりして🤣」「🤣」
⚓モバイル/Android/iOS
⚓Flutter対React Native(Mobile Dev Weeklyより)
- 元記事: Flutter vs. React Native: Choosing the Best Hybrid Framework for Mobile Development - DZone Mobile
つっつきボイス: 「Flutterの方が前からあったというのが意外でした」「まあ検索ボリュームって意外と当てになりませんけどねっ😎: みんなが当たり前に使うようになるとかえって検索数減ったりとか」「😆」
⚓SQL
⚓サイボウズさんのMySQLのパフォーマンスチューニング
つっつきボイス: 「お、はてブでも上がってましたねこの記事: JOIN禁止とか書いてないところがエライ😆」「😆」「JOIN禁止ってソシャゲ界だとありがちなんですよね」「そんなレベルでパフォーマンスを気にしてるんですかね?」「ソシャゲだとシャーディングしてたりするから」「あー😲」
「ちなみに昔のMySQLはJOINが遅かったし、今でもサブクエリは遅いという😆」「それでもMySQLのサブクエリは以前の異常な遅さよりはずいぶんマシになったというか順当な遅さ(😆)にとどまるようになったみたいですね」「以前があまりに遅すぎた🐢」「そんなに!?」「サブクエリをWHEREで書き直すだけで1000倍以上速くなったりしてましたし🏁: おかげでサブクエリをWHEREで書き直す方法を覚えたけど、今度はPostgreSQLでサブクエリ使うのを最初ためらっちゃいましたね🤣」「サブクエリの方が読みやすいのは確か」「ほんに」
参考: 漢(オトコ)のコンピュータ道: なぜMySQLのサブクエリは遅いのか。 -- 2009年の記事です
⚓AWS AuroraとAWS RDSの使い分け(DB Weeklyより)
- 元記事: When Should I Use Amazon Aurora and When Should I use RDS MySQL? - Percona Database Performance Blog
つっつきボイス: 「これはPerconaさんの記事ですね」「Perconaは元々MySQLのチューニングツールを出してた会社ですが、その後MariaDBのカスタマイズ版のPerconaDBを出したりしてます」
参考: MariaDB - Wikipedia
参考: What is Percona DB | InMotion Hosting
「AuroraがPostgreSQLでしたっけ?」「いや、Auroraは元々MySQL互換で、その後for PostgreSQLが出た: まだベータだったかな?と思ったらもうベータは取れてるのか」「でRDSが...」「MySQL、PostgreSQL、Microsoft SQL Server、MariaDB、Oracle、それとAuroraですね」「そういう構成だったんですね😃」
「ただAuroraはノード数がある程度必要なので結構お金かかります💰」「スケーラビリティとか凄そうですもんね」「あとAuroraが面白いのがストレージを使えば使うほど速くなるところ😆」「😆」「どういう仕組みなんでしょう?」「分散がデータブロック単位になっていて、データ量が増えるとデータブロックも分散して並列化が進むので速くなる」「おほー😲」「なのでデータ量が増えても指数関数的に遅くならないという」「すげー」
「Auroraのコアって実は分散ファイルシステムになっていて、他にも書き込みの非同期ネゴシエーションで『n個以上のノードからACKが返ってきたら書き込み成功と見なして進む』(Asynchronous group commits)ようになってるとかすごく頑張ってる、という解説を2、3年前のAWSサミットで聞いてしみじみ感心しました」「😃」「それをMySQLとかPostgreSQLとかと別のところでここまで追い込んでいるのが凄すぎ」「昔は研究レベルだったことが今こうやって使えますからねー☺️」
参考: Amazon Aurora(MySQL、PostgreSQL 互換のリレーショナルデータベース)|AWS
参考: 無料でデータベースを構築する
⚓TimescaleDBとInfluxDBを比較(Postgres Weeklyより)
つっつきボイス: 「InfluxDBはNoSQL系だそうです」「Fluxってどこかで聞いた...あ、時系列データベースか」「Timescale社のブログなので最後はSQLに軍配を上げつつ自社のTimescaleDBを推してます☺️」
参考: Flux | Application Architecture for Building User Interfaces
参考: Influx Query Language (InfluxQL) reference | InfluxData Documentation
⚓JavaScript
⚓Reduxの「Reselect」ライブラリでメモ化
⚓React/Preact/VueでサーバーサイドレンダリングのXSSを修正(JSer.infoより)
- 元記事: React v16.4.2: Server-side vulnerability fix - React Blog
- Preact修正版: Release 8.3.0 · developit/preact
- Vueの修正: fix: fix potential xss vulnerability in ssr when using v-bind · vuejs/vue@3d36a44
“VueをSSRに乗せると容易にXSSを生み出す場合がある - Qiita” https://t.co/rcD3yh1Xxy
— 徳丸 浩 (@ockeghem) September 5, 2017
つっつきボイス: 「Preactって名前だけどこかで見たんですけど何でしたっけ?」「リポジトリにFast 3kB React alternativeって書いてあるし」「速いReact」「もしかしたらFacebookのライセンスを気にしなくていいとか?」
後で「Facebook、ReactをMITライセンスに変更」という記事を見つけました。
参考: Using Preact as a React Alternative — SitePoint
⚓CSS/HTML/フロントエンド/テスト
⚓EV証明書
Firefoxを自分でビルドしてオレオレEV証明書を使えるようにしてみたそうです。「再定義できない世界になりつつあるネットの新秩序 - 雑種路線でいこう」という記事で知りました。
⚓JavaScriptの「Reactivity」を理解する(Frontend Weeklyより)
つっつきボイス: 「Reactivity: また新しいものが」「DOM構造的にreactできるかどうかという話かな?」「時間ないので次へー」
⚓言語よろずの間
⚓Inko: RubyでコンパイルしてRust VMが走る言語
例外なし,並行処理が簡単に書ける,漸進的型付け,OOPの新しい言語らしい.環境設定にRustの設定が必要でなんだろ?と思ってリポジトリみたら,コンパイラはRubyでVMがRustでできてるみたい./
Inko Programming Language https://t.co/RUqOO0Dz3q— yuki (@helloyuki_) August 7, 2018
Link: Inko Programming Language: https://t.co/TWUYYy1VPT
— Yukihiro Matsumoto (@yukihiro_matz) August 7, 2018
GobyのメンバーにもInkoに興味津々の人がいました。
⚓chart: 標準出力からいきなりグラフを生成(Golang Weeklyより)
- リポジトリ: marianogappa/chart
Go言語で書かれています。早速インストールしました
# 同リポジトリより
history | awk '{print $2}' | chart
つっつきボイス: 「みんな一度はこういうの作る☺️」「☺️」
⚓その他
⚓G.ワインバーグ氏死去
Gerald M Weinberg has died: https://t.co/2HX6shW1FK Comments: https://t.co/PiIuuZ6b62
— Hacker News (@HNTweets) August 8, 2018
↓だいぶ前にこの本で知りました。
参考: ジェラルド・ワインバーグ - Wikipedia -- ワインバーグの法則がどっちゃり載っていてちょっとびっくりしました。
⚓名著復活の兆し?
少し違いますがこんな記事も↓。
つっつきボイス: 「売れない理工学書はそもそも売れないから運営資金が続かないという問題がつきまといますね...」「😭」
参考: 科学論文の海賊版「Sci-Hub」を違法と訴える巨大出版社に対して根本的法改正の必要性を訴える科学者という構図 - GIGAZINE
⚓【書評】SOFT SKILLS
最近改めてこの本を読み直してるんだけど、やっぱりいいこと書いてあるなあと思った。エンジニア2〜3年目の人は新技術を学ぶだけじゃなくて、こういう本を読むのもいいと思います。(お盆休みに!)
【書評】SOFT SKILLSを読んでプログラマとしてのキャリア設計を見直そう https://t.co/pYT8rMBWzx— Junichi Ito (伊藤淳一) (@jnchito) August 9, 2018
つっつきボイス: 「SOFT SKILLSはどうやら評価が両極端に分かれてますね」「身体を鍛える話とか載ってるみたい」「このあたりは人に言われてやるのとセルフモチベーテッドでは相当違ってきそう🤔」
⚓技術書オンリーイベント第5回が10/08(月)開催決定
技術書オンリーイベント 第5回 10月08日https://t.co/2H0MSYJnLK
ふむふむ— Tryforth (@tryforth) June 20, 2018
つっつきボイス: 「今度こそ行きたいと思って😤」
⚓番外
⚓Dimensions: 数理を動画で学ぶ
フランスで制作された、割と前からあるCreative Commonsベースのコンテンツで、個人的に好きです。最初に見たのはニコ動でした。
つっつきボイス: 「上の日本語版動画は東大のサイトにあります」「おwフォーマットがRealVideoとかWMVとか😆」「かなり歴史を感じるフォーマット📜」
参考: RealVideo - Wikipedia
参考: Windows Media Video - Wikipedia
今回は以上です。
バックナンバー(2018年度後半)
週刊Railsウォッチ(20180806)Rails 5.2.1.rc1リリース、Railsガイド日本語版が5.1に対応、Regexp#match?ほか
- 20180723 Railsdm Day 3 Extremeを後追い、PSDにはZeplin.io、好みの分かれるJSX、負荷テストツール比較ほか
- 20180709 Rails Developers Meetup Day 3 Extreme今週末開催、RailsのSTI/キャッシュ/添付ファイル/Redis/PDF出力、ECMAScript 2018、プロフェッショナルIPv6ほか
- 20180702 Ruby 2.2メンテ正式終了、Ransackがつらくなるとき、書籍『Domain-Driven Rails』、GitHubの高可用MySQLほか
- 20180622 Railsの需要未だ巨大、Unicode 11.0リリース、WebDriverがW3Cで勧告、Flutter.io、2封筒問題ほか
- 20180615 TTY gemとHTTPClient gemは優秀、Rubyの謎フリップフロップ、ちょいゆるRubyスタイルガイドほか
- 20180608 特集「RubyKaigi 2018後の祭り」、
Enumerable#index_with
は優秀、コントローラから@
を消し去るほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやRSSなど)です。