- Ruby / Rails関連
週刊Railsウォッチ(20180903)次世代アップローダーgem「Shrine」、RSpecをどこまでDRYに書くか、Rubyのmainオブジェクトの秘密、GitLabのCookie利用許諾機能はエライほか
こんにちは、hachi8833です。少し涼しくなったらなぜかGobyのコミット頻度が上がってきました。
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを社内有志でつっついたときの会話です👄
⚓第2回「週刊Railsウォッチ 公開つっつき会」いよいよ今週木曜開催
⚓Rails: 先週の改修(Rails公式ニュースより)
⚓rake initializers
タスクをrailsコマンドに移動
- PR: Move the initializers rake task to Rails::Command by anniecodes · Pull Request #33631 · rails/rails
# railties/lib/rails/commands/initializers/initializers_command.rb#3
+module Rails
+ module Command
+ class InitializersCommand < Base # :nodoc:
+ desc "Print out all defined initializers in the order they are invoked by Rails."
+ def perform
+ require_application_and_environment!
+ Rails.application.initializers.tsort_each do |initializer|
+ puts "#{initializer.context_class}.#{initializer.name}"
+ end
+ end
+ end
+ end
+end
つっつきボイス:「またrake
からrails
コマンドに引っ越しですね」「rails initializers
というコマンドがそもそもあったのかー😲: 使ったことないな」「私もです」「Railsディレクトリでbundle exec rake -vT
してみるとたしかに↓こう出てくる」
rake initializers # Print out all defined initializers in the order they are invoked by Rails
「でbundle exec rails initializers
してみると...」「お~出た出た🌙」
「なるほど、イニシャライザをパスとともに起動順に表示してくれるのね😋: rake middleware
というミドルウェアを表示してくれるコマンド↓があるけど、それのinitializer版みたいなものかな」「起動順を追いたいときによさそう😃」
⚓コントローラ名が複合語のときにrails routes -c
できない問題を修正
rails routes -c UserPermissionsController
ができるようになりました。
# actionpack/lib/action_dispatch/routing/inspector.rb#L83
private
def normalize_filter(filter)
if filter[:controller]
- { controller: /#{filter[:controller].downcase.sub(/_?controller\z/, '').sub('::', '/')}/ }
+ { controller: /#{filter[:controller].underscore.sub(/_?controller\z/, "")}/ }
elsif filter[:grep]
{ controller: /#{filter[:grep]}/, action: /#{filter[:grep]}/,
verb: /#{filter[:grep]}/, name: /#{filter[:grep]}/, path: /#{filter[:grep]}/ }
end
end
つっつきボイス:「rails routes -c
がそもそもかなり新しそう」「Rails 6.0向けかなと思ったら5.2にもバックポートされてる」
⚓ActionCableのテストに便利なアダプタを追加
#33635もこれと関連するPRのようです。
+# actioncable/lib/action_cable/subscription_adapter/test.rb
+# frozen_string_literal: true
+require_relative "async"
+module ActionCable
+ module SubscriptionAdapter
+ # == Test adapter for Action Cable
+ #
+ # このテストアダプタはテスト以外では使わないこと
+ # ActionCable::TestHelperと併用することでRailsアプリのテストに有用
+ #
+ # このテストアダプタを使うにはcable.ymlのアダプタ値に「test」を設定する
+ #
+ # メモ: テストアダプタはActionCable::SubscriptionsAdapter::Asyncをextendするので
+ # システムテストでも使える
+ class Test < Async
+ def broadcast(channel, payload)
+ broadcasts(channel) << payload
+ super
+ end
+ def broadcasts(channel)
+ channels_data[channel] ||= []
+ end
+ def clear_messages(channel)
+ channels_data[channel] = []
+ end
+ def clear
+ @channels_data = nil
+ end
+ private
+ def channels_data
+ @channels_data ||= {}
+ end
+ end
+ end
+end
つっつきボイス:「broadcast
とかclear_messages
とか、こういうテストアダプタが欲しいのわかる」「😀」「ActionCableのテストは確かに難しいよな〜: pub/subのテストってマルチスレッドにしないとちゃんとできないし」
⚓ActiveStorageでファイルが見つからない場合に500ではなく404エラーを出すように修正
これによって#33647がcloseされました。
# activestorage/app/controllers/active_storage/disk_controller.rb#L7
class ActiveStorage::DiskController < ActiveStorage::BaseController
skip_forgery_protection
def show
if key = decode_verified_key
serve_file disk_service.path_for(key), content_type: params[:content_type], disposition: params[:disposition]
else
head :not_found
end
+ rescue Errno::ENOENT
+ head :not_found
end
つっつきボイス:「うん、これは404エラーが正しいっすね💪」
⚓空のトランザクションからBEGIN
とCOMMIT
を除外
- PR: Omit BEGIN/COMMIT statements for empty transactions by eugeneius · Pull Request #32647 · rails/rails
トランザクションでクエリが実行されずにopen/closeされる場合はBEGINやCOMMITを安全に除外できる。このPRは変更なしのレコードをsaveするときのオーバーヘッドを取り除くことで
save if changed?
を不要にできる。
同PRより大意
つっつきボイス:「言われてみればRailsって、トランザクションで何もしていないのにBEGINやCOMMITを吐くことがあったし: そのあたりが修正されるということかな」「広範囲にがっつり更新されてますね: save if changed?
しなくてよくなるみたい」「このあたり↓が改修の中心のようだけど複雑なことしてる感: マテリアライズというとPostgreSQLなんかのマテリアライズドビューみたいで中々紛らわしい...」「ですね💦」「やりたいことはだいたいわかる: シングルのトランザクションならそもそもBEGIN TRANSACTIONで囲まなくていいとか、そういうところもやってるんだろうし」「そのうちRailsガイドあたりで解説が付きそうな予感🙏」
# activerecord/lib/active_record/connection_adapters/abstract/transaction.rb#L110
+ def materialize_transactions
+ return if @materializing_transactions
+ return unless @has_unmaterialized_transactions
+ @connection.lock.synchronize do
+ begin
+ @materializing_transactions = true
+ @stack.each { |t| t.materialize! unless t.materialized? }
+ ensure
+ @materializing_transactions = false
+ end
+ @has_unmaterialized_transactions = false
+ end
+ end
⚓try
を20%高速化
Twitterで拾ったsgrifさんのPRです。
Warming up --------------------------------------
old 179.987k i/100ms
new 199.201k i/100ms
Calculating -------------------------------------
old 3.029M (± 1.6%) i/s - 15.299M in 5.052417s
new 3.657M (± 1.2%) i/s - 18.326M in 5.012648s
Comparison:
new: 3656620.7 i/s
old: 3028848.3 i/s - 1.21x slower
# activesupport/lib/active_support/core_ext/object/try.rb#L5
module ActiveSupport
module Tryable #:nodoc:
- def try(*a, &b)
- return unless a.empty? || respond_to?(a.first)
- if a.empty? && block_given?
+ def try(method_name = nil, *args, &b)
+ if method_name.nil? && block_given?
if b.arity == 0
instance_eval(&b)
else
yield self
end
- else
- public_send(*a, &b)
+ elsif respond_to?(method_name)
+ public_send(method_name, *args, &b)
end
end
- def try!(*a, &b)
- if a.empty? && block_given?
+ def try!(method_name = nil, *args, &b)
+ if method_name.nil? && block_given?
if b.arity == 0
instance_eval(&b)
else
yield self
end
else
- public_send(*a, &b)
+ public_send(method_name, *args, &b)
end
end
end
end
つっつきボイス:「respond_to?
を後回しにしたりして最初の条件チェックを軽くしてるっぽいですね」「empty?
よりnil?
の方が速いとかそういう感じかな?: 微細な最適化だけどこういうのが効いたりしますね😋」「引数名もa
とかじゃなくてmethod_name
に変えてるし」「try
は結構使われるし、ループで使ってたら効果ありそう: いい修正」「try
というとこの記事思い出しちゃいます↓」「何でもtry
したらそもそも遅くなるし😎」
⚓ActiveModelで時間の乗算を20%高速化
これもTweetで見つけました。
# activemodel/lib/active_model/type/helpers/time_value.rb#L70
# Doesn't handle time zones.
def fast_string_to_time(string)
if string =~ ISO_DATETIME
- microsec = ($7.to_r * 1_000_000).to_i
+ microsec_part = $7
+ if microsec_part && microsec_part.start_with?(".") && microsec_part.length == 7
+ microsec_part[0] = ""
+ microsec = microsec_part.to_i
+ else
+ microsec = (microsec_part.to_r * 1_000_000).to_i
+ end
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
end
end
end
つっつきボイス:「これも高速化」「microsec_part.start_with?(".")
...?ははぁ、小数の場合は文字列として分解することで計算しないようにしてるのか!そりゃ速くなるな」「大胆だな〜🤯」
⚓Rails
⚓5.0->5.1で地味に名前が変わっていたActiveModel::Dirty
系メソッド
2017年の記事です。場所もActiveRecord::AttributeMethods::Dirty
に変わったものがあるようです。
Rails 5.0 | Rails 5.1〜 |
---|---|
<attribute>_changed? |
saved_change_to_<attribute>? |
<attribute>_was |
<attribute>_before_last_save |
changed? |
saved_changes? |
changed_attributes |
saved_changes.transform_values(&:first) |
つっつきボイス:「今日kazzさんが<attribute>_was
という以前からあるメソッドを使ってみようかなと言ってたので、私もちょっと調べてみたら早くも名前が変わってました」「wasが消えた話、そういえばどっかで聞いたな〜: とにかく名前がわかりにくいし、そもそもDirty
があまりに複雑すぎるから、メソッド名を長くしてでもわかりやすくしたいというのはわからんでもない😎」「wasはあんまりですね〜😢」「ちょろいトランザクションならともかく、それ以上はどうも不安が残るんで自分もあんまり使わないし😆」
参考: Ruby on Rails 5.2 / ActiveRecord::AttributeMethods::Dirty
— DevDocs
参考: Ruby on Rails 5.2 / ActiveModel::Dirty
— DevDocs
追いかけボイス:「wasが名前変わった話知ることができてちょうど良かった〜」「タイムリーでしたね😋」
上のPRの下の方でこの記事↓がよくまとまってると紹介されてました。
参考: Cleaning Up: ActiveRecord::Dirty 5.2 API Changes - The Lean Software Boutique
⚓Railsは2018年の今も現役か?
Link: Is Rails still relevant in 2018 ?: https://t.co/vKMaQLgcUJ
— Yukihiro Matzmotto (@yukihiro_matz) August 28, 2018
同記事より
つっつきボイス:「めちゃ長い記事なんですが、既に翻訳許可を取って今半分ぐらい翻訳しているところです: 結論だけ言うと『私は今後もRubyとRailsを使う』でした☺️」
読もうと思って、一旦サーッとスクロールしたら、コメント欄に見慣れたアイコンあるなと思ってコメント読んだら、techrachoさんだった。
もう記事翻訳の許可とってた、さすが!https://t.co/g3vgSOQzwG— fakiyer (@fakiyer1) August 30, 2018
⚓DHHのインタビューPodcast
- Podcast(40分): Test and Code: 45: David Heinemeier Hansson - Software Development and Testing, TDD, and exploratory QA
つっつきボイス:「例のYouTubeチャンネルじゃなくて?」「音声だけのインタビューで文字起こしもありません😅」「じゃあ頑張って聞くしかない😆」「やっぱりダルいのでapp.voicebase.comというサービス↓に試しに流し込んでみたら思ったよりよくできてました: 機械起こしの文字をクリックするとその位置にジャンプ再生できるので少々の聞き取りミスがあっても気にならないし」「こういうのいいっすねー😋: ディクテーションの練習になりそうだし」「これ日本語でできたら最高😃」
⚓torque-postgresql: RailsでPostgreSQLの高度な機能を使うgem(Ruby Weeklyより)
- リポジトリ: crashtech/torque-postgresql
# 同リポジトリより
create_table :users do |t|
t.string :name
t.role :role # Uses the type of the column, since enum is a type
t.enum :status # Figures the type name from the column name
t.enum :last_status, subtype: :status # Explicit tells the type to be used
end
つっつきボイス:「ぽすぐれのEnumとArrayは確かRails本家でも最近サポートされてたような気がするけどRails 6からだったかな...?」
↓EdgeguidesにはArrayが載っていました。Enumなどの複合型はサポートしていないようです。
参考: Active Record and PostgreSQL — Ruby on Rails Guides
「どのバージョンのRailsで動くのかをチェックするか(torque_postgresql.gemspec)↓」「5.0〜5.1ですね」「せま〜い😆このあたりの機能はRailsが今後サポートに含める可能性があるので、今gemで対応すると後で少々面倒になるかもね😉」「それもそうか〜」「どうしても必要なら別ですけどね☺️」
s.add_dependency 'rails', '>= 5.0', '< 5.2'
⚓DCIアーキテクチャとは
- 元記事1: DCIアーキテクチャ - Trygve Reenskaug and James O. Coplien - Digital Romanticism
- 元記事2: DCIアーキテクチャについて語ってみるよ - uehaj's blog
1本目の記事はすごく長い論文で、10年ぐらい前に翻訳されたものです。
つっつきボイス:「上述のIs Rails still relevant in 2018 ?の中で言及されていて知りました」「DCIって何の略?名前ぐらいは聞いたことあるけど」「えーと『データ、コンテキスト、インタラクション』: MVCアーキテクチャに対するものとして説明されている感じ」「ははぁ、こういう図ね↑コントローラとモデルをコンテキストとして扱う、でモデルの本体がオブジェクトなのか: でコンテキストはシナリオレベルで作ると↓」
- データ:これはドメインオブジェクトの中にあり、ドメインクラスに由来している。
- コンテキスト:要求に応じて、アクティブなオブジェクトをシナリオにおけるポジションに位置づける。
- インタラクション:ロールの観点からエンドユーザのアルゴリズムを記述するもの。ロールもアルゴリズムもエンドユーザの頭の中に存在する。
元記事1より
「腰を据えて読まないと評価しようがないけど、大規模で複雑なアプリをユースケースからブレイクダウンして実装する場合によさそうに見える🧐」
おまけ: 同じ記事でOODDという初めて見る略語から以下のコワモテ動画にリンクされてました。英BBCの番組というのがまた不思議。
参考: Object Oriented Versus Functional
「なぜかググってもOODDの略語の意味がなかなか見つからなくて、こちら↑でやっと『Object-Oriented Decomposition and Design』だとわかりました: それに賛同しているっぽいElegant Objectsというサイト↓はnull禁止/ゲッターセッターコンストラクタ禁止とか主張が強めな感じでちょっとビビりました💦」「こういうのは同じようなものにいろんな人がいろんな名前を付けたりしますからね〜☺️」
- サイト: Elegant Objects
⚓Railsコンソールの便利ワザ(Hacklinesより)
つっつきボイス:「1.の--sandbox
、へぇぇこんなのあったんだ!😳」「使ったことあったかも」「ロールバックが複雑になったときにどこまでできるかな?😆」
「2.の_
で前の値が取れるのってRailsコンソールの機能じゃなくてIRBとかPryでもできたと思うし」「そっか😲」「うん、irbでもできた💪」「3もまあ普通かな」
「4.や5.の.method(:inquiry).source_location
や.method(:inquiry).source.display
↓はこれもRubyの機能かな」「6.のhelper
は普通にRailsコンソールでコントローラから取れます」
「7のapp
オブジェクト↓もありますね: app.get
とかはテスト書くときに使いますし」「そうだった💦」
# 同記事より
>> app.get('/')
Started GET "/" for 127.0.0.1 at 2018-08-25 22:46:52 +0000
(0.5ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
Processing by HomeController#show as HTML
Rendering home/show.html.erb within layouts/application
Rendered home/show.html.erb within layouts/application (11417.2ms)
Rendered shared/_menu.html.erb (3.6ms)
Rendered shared/header/_autocomplete.html.erb (292.2ms)
Rendered shared/_header.html.erb (312.9ms)
Rendered shared/_footer.html.erb (3.7ms)
Completed 200 OK in 11957ms (Views: 11945.5ms | ActiveRecord: 0.0ms)
=> 200
「これはルーティングのパスを取れる↓」「6.と7.はRails的、他はRuby寄りの機能かな」「😃」
# 同記事より
>> app.methods.grep(/_path/).grep(/game/)
=> [:search_games_path, :game_ownlist_placements_path, :game_ownlist_placement_path, :game_wishlist_placements_path, :game_wishlist_placement_path, :game_path]
⚓対決!Node.js vs Rails(Hacklinesより)
つっつきボイス:「Railsの強みというとやっぱり生産性ですかね」「Nodeの『growing fast』ってメリットなのかと😆」「『変化が激しすぎる』🤣」
⚓RSpecをどこまでDRYに書くのが正しいかを探る(Hacklinesより)
# 同記事より: bad
describe 'token verification' do
shared_examples 'invalid token' do |expected_error|
it 'responds with the expected error' do
response_json = JSON.parse(response.body)
expect(response).to be_forbidden
expect(response_json.fetch("error").to eq expected_error
end
end
subject { get :test }
context 'missing header' do
include_examples 'invalid token', 'Authorization header not found'
end
context 'bad header' do
before { request.headers.add('Authorization', 'badvalue') }
include_examples 'invalid token', 'Incorrect authorization token'
end
end
つっつきボイス:「どこまでDRYに...😅😅😅」「テストのduplication、これ判断が難しいよね〜: 自分ならテストコードの重複をそこまで気にするよりも、少々重複してもいいから書けばよくね?と思うし」「上の図↑みたいにコントローラのテストとサービスのテストが同じところをテストすると重くなるのは確かだし、重複を気にするのももっともなんだけど、それよりテストのカバレッジを上げる方が有用なのではと思うし😎」「この記事ももちろん有用❤️」
⚓⭐Rails向け画像アップローダShrine⭐
つっつきボイス:「Revisited!」「前にもそういう記事書いたんでしょうね」「結論はActiveStorageかShrineだそうです」「ActiveStorageは言わずもがなだけど、Shrineって?」「初めて見た」「★1800超えてるから使っている人はけっこういそう」
- サイト: Shrine
- リポジトリ: shrinerb/shrine
「↓こういう感じか: プラグインを使えたりフォームを拡張したりできる」
# 同リポジトリより
require "shrine"
require "shrine/storage/file_system"
Shrine.storages = {
cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"), # temporary
store: Shrine::Storage::FileSystem.new("public", prefix: "uploads"), # permanent
}
Shrine.plugin :sequel # :activerecordでもよい
Shrine.plugin :cached_attachment_data # 複数のフォーム再表示用にキャッシュファイルを保持
Shrine.plugin :restore_cached_data # キャッシュされたファイルのアタッチ時にメタデータを再抽出
Shrine.plugin :rack_file # Rails以外のアプリ向け
「Uploaderのクラスを作るところはCareerWaveっぽいかな」
# 同リポジトリより
class ImageUploader < Shrine
# 画像添付のロジックをここに書く
end
uploader = ImageUploader.new(:store)
uploader #=> `:store`で登録されたストレージ向けのアップローダー
「お、ストリームを直接扱えるのか😋: これは結構よさそう」
# 同リポジトリより
uploaded_file.url #=> "https://my-bucket.s3.amazonaws.com/949sdjg834.jpg"
uploaded_file.open # アップロードされたファイルを開く
uploaded_file.download #=> #<File:/var/folders/.../20180302-33119-1h1vjbq.jpg>
uploaded_file.stream(destination) # アップロードされたコンテンツを書込み可能な相手にストリーミングする
uploaded_file.exists? #=> true
uploaded_file.delete # ストレージのファイルを削除
# アップロードされたファイルをブロックの中でオープン/ダウンロード
uploaded_file.open { |io| io.read }
uploaded_file.download { |tempfile| tempfile.read }
「AWS S3もこうやって扱えるみたい」
# 同リポジトリより
# Gemfile
gem "aws-sdk-s3", "~> 1.2" # for AWS S3 storage
require "shrine/storage/s3"
s3_options = {
bucket: "my-bucket", # required
access_key_id: "abc",
secret_access_key: "xyz",
region: "my-region",
}
Shrine.storages = {
cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
store: Shrine::Storage::S3.new(**s3_options),
}
「しかもrewind
を明示的に行える↓し: いいじゃないの~❤️」「rewindできるアップローダーってあんまりないかも?」「ファイルのIOを取るには本来ファイルが手元にないとできないはずなんだけど、Shrineは手元になくてもできそうな雰囲気」「そういえばHTTP 1.1のchunkedRangeリクエストを使うと指定のバイトから取り出せたりしますけど、Shrineがそれとうまく連携できていればレジュームもできるのかな?なんて期待しちゃいますね😋」「動画を途中から再生するとか」
参考: HTTP/1.1 の Transfer-Encoding: chunked をビジュアライズするツール書いてみた - blog.nomadscafe.jp
# 同リポジトリより
uploaded_file.read # アップロードされたファイルのコンテンツを返す
uploaded_file.eof? # IOをすべて読み終わったらtrueを返す
uploaded_file.rewind # IOを巻き戻す
uploaded_file.close # IOをクローズする
「Rackでダイレクトアップロードもできるのかっ😃」「Railsのワーカーにファイルを流さずに済むと」
# 同リポジトリより
# config.ru (Rack)
map "/images/upload" do
run ImageUploader.upload_endpoint(:cache)
end
# OR
# config/routes.rb (Rails)
Rails.application.routes.draw do
mount ImageUploader.upload_endpoint(:cache) => "/images/upload"
end
「Shrine、CarrierWaveよりも痒いところに手が届いてる感じ😋: ものすごく大きなファイルにアクセスするときはストリームで引き回せるといいなと思ったりするし」「気が利いてますね👍」「このとおりにちゃんと動くならいいヤツ!」「インターフェイスもほぼCarrierWaveっぽいし」「実績もシェアもあるCarrierWaveで足りるならわざわざShrineを使うこともないかもしれないけど、CarrierWaveだと機能不足ならShrine悪くなさそう😍」
「Shrine(神社仏閣)って名前も面白いっすね」「ところで元記事を見ると『CarrierWaveとRefileとDragonflyは使ったことないのでわかりましぇん』とあるし🤣」「そこだけ投げやり🤣」「まあShrineから先に覚えたらCarrierWave欲しくないかもね☺️」「Dragonflyってまだあったとは: 一応更新されてるけどそんなにメンテされてないかな〜」
Shrineに今週の⭐を進呈したいと思います。おめでとうございます。
⚓GitLabのCookie利用許諾機能はエライ
https://t.co/Vmy4osMKwzのcookie利用許可を得るやつ、利用用途をクライアントから選べるのすごいし、個別のcookieの提供元と目的が全部説明されてるし、むちゃくちゃ誠実だ
— ちゃーしゅーねこ (@charsiuCat) August 28, 2018
つっつきボイス:「あーGitLabのこれね↓: 例のGDPR対応のときからやってる」「Cookieをこれとこれになら使っていいよ、とユーザーが選択できる: これはほんとエライ」「さすが丁寧なご挨拶」「GitLabとしてはライバルのGitHubにこういうところで差をつけたいだろうし🤣」「🤣」「『うちはあそこより正しい』を強調しすぎたら逆効果でしょうけど🤣」「これが当然という感じで黙々とやっていくでしょうね」
「Cookieをこう使っていいですか宣言って今後普及しそうですね」「いやマジで、こういうのがgemとかJSのライブラリとして提供されてもいいぐらい: Cookieでやりたいことなんてどこのサイトも基本同じなんだし」「フォーマットも同じでいいんだし」「これは共通化すべき」
⚓Ruby trunkより
何だかbugs.ruby-lang.orgにスパム書き込みが繰り返されてたっぽいです。
⚓ENVのエンコーディングがUTF-8にならないことがある?
$ irb
2.5.1 :001 > 'secret'.encoding
=> #<Encoding:UTF-8>
2.5.1 :002 > ENV['PASS'] = 'secret'; ENV['PASS'].encoding
=> #<Encoding:US-ASCII>
2.5.1 :009 > ENV['PASS'] = 'Ł'
=> "\u0141"
2.5.1 :010 > ENV['PASS'].encoding
=> #<Encoding:ASCII-8BIT>
つっつきボイス:「私のmacOS環境では普通にUTF-8になりました」「もしかするとターミナルの言語設定に影響されてるのかな?」
⚓Ruby
⚓Rubyの変数の「シャドウイング」(Ruby Weeklyより)
割と長い記事です。
# 同記事より
x = 42
3.times { |x| puts "x is #{x}" }
$ ruby variable_shadowing.rb
x is 0
x is 1
x is 2
つっつきボイス:「こういうの↑をシャドウイングって言うというのを聞いたことはある: ブロックの外のx
とブロックのx
が別物になるヤツ」「正直、Rubyのシャドウイングは別につらくない: つらいのはやっぱりJavaScript😆」「やっぱり〜😆」
参考: シャドーイング – rilakkuma3xjapan's blog
「JavaScriptのつらさといえばもうthis
に集約されますよね😭」「あれもワカラン😭」
参考: 【JavaScript基礎】thisとは何か・シーン別参照先のまとめ - KDE BLOG
「Rubyで気をつけたいものといえば、mixinしたときの変数の重複: これ踏むとけっこうビビる」「includeするモジュール同士でかぶることもあるし」「解決するにはモジュールのancestorsを調べたりとか面倒になりがち」「だからconcernsを書くときなんかはできるだけ変数名がかぶらないように結構気遣いますよ」「モジュールでインスタンス変数を使う場合も相当気をつけないとヤバい」「そうそう、かなりヤバみある😅」
⚓Rubyのモジュールをクリーンにinjectする(Ruby Weeklyより)
# 同記事より
2.5.1 :001 > module PrintIncludedClass
2.5.1 :002?> def self.included(base)
2.5.1 :003?> puts base.class
2.5.1 :004?> end
2.5.1 :005?> end
=> :included
2.5.1 :006 > Object.include(PrintIncludedClass)
Class
=> Object
つっつきボイス:「もじゅーるくりーんいんじぇくしょん!: Railsならincluded
でフックかければできるっていう話かな」「included
ってRailsの機能?」「Rubyの機能ですね」「RailsではActiveSupport::Concern#included
か: Railsではこっちを使うのが推奨されてる」「これがないとconcernsの意味がなくなるヤツ」「このあたりのメソッドって、クラスを拡張するのかインスタンスを拡張するのかとか毎回調べてる💦」
参考: Ruby 2.5 / Module#included
— DevDocs
参考: Ruby on Rails 5.2 / ActiveSupport::Concern#included
— DevDocs
⚓「Roseメモ化」でモジュールの一部だけをincludeする(Hacklinesより)
- 元記事1: A trick for partial includes in Ruby - The Pug Automatic
- 元記事2: Rose memoization - The Pug Automatic
# 元記事1より
module MyModule
def the_method_i_want
"Hello!"
end
def method_i_do_not_want
"I don't think so."
end
end
class MyClass
def run_demo
# このヘルパーオブジェクトを明示的に呼べる
helpers.the_method_i_want
# 委譲もできる:
the_method_i_want
# これはできない
method_i_do_not_want
end
private
def helpers
@helpers ||= Object.new.extend(MyModule)
end
extend Forwardable
def_delegator :helpers, :the_method_i_want
# Railsなら以下で委譲することもできる
delegate :the_method_i_want, to: :helpers
end
MyClass.new.run_demo
この方の造語だと思いますが、元記事2で@ ||=
を「ASCII Rose」と呼んでいます。
つっつきボイス:「モジュールのパーシャルインクルードとかやめて〜🤣普通にモジュール分けようよ」「🤣」「MyModule.extend(MyModule)
とかトリック感ヤバい」「まあでもライブラリなんかだと、こうでもしないと実現できないことってありそう」
「この記事の人、@インスタンス名 ||=
をRose memoizationって名付けてますね」「@ ||=
って、バラの花を横に倒したものに見立ててるのか〜」「||=
は大好き😋」「まともにメモ化しようと思ったらインスタンス変数かクラス変数使うだろうから必ずローズになりそうなもんですけどね😆」
「ところで||=
は『たてたてイコール』っていうかわいい呼び方が好きなんですが、これも他では言わないだろうな...」「英語圏も含めて普通『orイコール』じゃないかな〜」「今更だけど、||=
って単独の演算子なんだな: 間にスペース入ると意味変わるし」「公式だと『自己代入演算子』↓」「自己代入だと何だかちょっと意味が違うような...?🤔」「内部的に自己代入してるとか?😆」「+=
とか-=
と同じくくりなのかも」「まあまあ謎の名前は家庭内新聞ぐらいにしときましょう☺️」
⚓Rubyの"main"オブジェクトの謎(Hacklinesより)
つっつきボイス:「お馴染みNoah Gibbsさんの記事です」「IRBのトップレベルのmainオブジェクトをどうやって取るか」「mainってselfで取れるのか↓😲」「あー、mainって欲しくなるときあるな: プロセスコンテキストを取りたいときとか」
# 同記事より
2.5.0 :001 > self
=> main
2.5.0 :002 > self.class
=> Object
2.5.0 :003 > self.singleton_class
=> #<Class:#<Object:0x00007f9ea78ba2e8>>
「mainってObject
クラスだけど自力で拡張しているみたい: こういうのって楽しい〜😍」
# 同記事より
2.5.0 :005 > self.methods.sort - Object.methods
=> [:bindings, :cb, :chws, :conf, :context, :cws, :cwws, :exit, :fg,
:help, :install_alias_method, :irb, :irb_bindings, :irb_cb,
:irb_change_binding, :irb_change_workspace, :irb_chws,
:irb_context, :irb_current_working_binding,
:irb_current_working_workspace, :irb_cwb, :irb_cws, :irb_cwws,
:irb_exit, :irb_fg, :irb_help, :irb_jobs, :irb_kill, :irb_load,
:irb_pop_binding, :irb_pop_workspace, :irb_popb, :irb_popws,
:irb_print_working_binding, :irb_print_working_workspace,
:irb_push_binding, :irb_push_workspace, :irb_pushb, :irb_pushws,
:irb_pwb, :irb_pwws, :irb_quit, :irb_require, :irb_source,
:irb_workspaces, :jobs, :kill, :popb, :popws, :pushb, :pushws,
:pwws, :quit, :source, :workspaces]
「こういうのはRubyの中でC言語で書くしかないだろうなー」
/* top self */
static VALUE
main_to_s(VALUE obj)
{
return rb_str_new2("main");
}
VALUE
rb_vm_top_self(void)
{
return GET_VM()->top_self;
}
「これは相当楽しい: 仕事の役には立たないけど🤣」「🤣」「RubyでRubyじゃないものを動かすみたいなやんちゃしたいときとかに使えそう❤️」
⚓transducers-ruby: Rubyでトランスデューサー
こちらの記事↓を見て見つけました。JavaScript版のトランスデューサー解説記事をRubyで書き直したいというリクエストです。
つっつきボイス:「上の記事のそのまたリンク先の記事↓にあったトランスデューサーの解説図↓がちょっと面白いかも」「わかったようなわからないような🤣」「たぶん上はシーケンシャルな処理で、下は並列化可能という感じなんでしょうね」
「トランスデューサーって、どこかApacheのHadoopを思わせるものがある: map/filter/reduceってまさにHadoopっぽいし、reducerって言ったりするし」「おー」「Hadoopもそのぐらいしか知りませんけど😆」
「そういえばRubyだとinject
とreduce
って同じですよね」「たぶん他の言語屋さんならreduce
を使いたいんじゃないかな〜?: Matzがinject
という言葉を使ったのは何かの言語の影響だった気がする」「そういえばRubyスタイルガイドではSmalltalkの影響ってあった」
「話ちょっと逸れますけど、Brandon Weaverさんのこの翻訳記事↓のタイトル、元は『Reducing Enumerable』なんですが日本語タイトルすっごく悩みました😅: 本文でもreduceが掛けことば的に使われまくってるし」「reduceで繰り返しを減らそう、みたいな😆」「英語でうまいこと言ってるタイトルを訳すのは大変そう😆」
「あとBrandon Weaverさんは関数型言語ラブの人なので、絶対にメソッドと言わずに関数と言う」「メソッドはあくまでクラス型オブジェクト指向の用語ですね🧐」「あ〜なるほど」
⚓問題「Rubyのクラス名とファイル名は合わせるのか」(Hacklinesより)
つっつきボイス:「答えは『Railsでは合わせるけどRubyは別にそうではない』という」「まあRailsもオートローダーを使わなければクラス名とファイル名はいくら違っててもいいんですけどね😎」「そうそう: オートローダーが認識するためだけなので☺️」「Javaはそこが厳しいんですよね?」「Javaはクラス名とファイル名の一致が必須: 一致してないと読み込んでくれない」「そのぐらいは強制でもいいんじゃね?とは思うけどねっ☺️」
⚓その他Ruby
- 元記事: Explain Ruby's define_method like I'm five - DEV Community 👩💻👨💻 -- 5歳児にもわかる
define_method
(Hacklinesより)
⚓クラウド/コンテナ/インフラ/Linux/Serverless
⚓ヒッチハイカーのためのAWS Step Functionsガイド(Serverless Statusより)
つっつきボイス:「AWSのStep Functionって何だろうと思って」「これは自分も知らなかったー: AWSって前からワークフロー系のサービスをやってたけどそれなのかな🤔」
公式: AWS Step Functions (分散アプリケーションとマイクロサービスの構築) | AWS
「あれ?公式サイトの料金表クリックしたら404 not foundだし?🤔」「れれ、ほんとだ」「URLのpricing/pricing
が間違ってるっぽい」「1つ取ったら見えたー🤣」「公式がリンクエラーとは🤣」
参考: AWS Step Functions で作る Serverless バッチシステム - Qiita -- 状態遷移1000回ごとに課金だそうです
⚓SQL
⚓Railsで複数PostgreSQLデータベースの分散やった(Ruby Weeklyより)
つっつきボイス:「こういうのはどのレベルで分散させるかでアレゲ感が違ってくる」「個人的にはアプリケーションレベルで分散頑張るよりはデータベースのドライバで頑張りたい気がするけど🕶」
現実のSSDはこうやって死ぬ(Postgres Weeklyより)
つっつきボイス:「サーバーの用途によってSSDがどう劣化していくかという記事です」「SSDって実際にデータが消えるから👻」「消える消える」「そういえばSSDの耐久テスト記事ってひと頃よくあった」
「SSDは素子ごとに読み書きできる上限(10万回とか)があるんですけど、そのブロック管理をSSDのコントローラが行っているので、SMARTのインターフェイスとかを経由しないと知りようがない」「隠蔽されてるんですね😮」「実際コントローラがやるべきだと思いますし: そして正常な空きブロックがなくなるとSSDは死ぬ」
参考: 【レビュー】SSD/HDDの“S.M.A.R.T.”情報を取得して表示する「DiskSmartView」 - 窓の杜
「私のMacbook Pro、もう5年も使ってるしSSDそろそろヤバいかな...?😅」「いやーそのぐらいなら全然問題ないっすよ💪: ましてやサーバー用SSDならスペックにもっと余裕あるものが使われてるし」「仮に自分のマシンでハイバネーションを毎日やったりしてたらあっという間に底をついちゃいますけどね」「よかったー、ハイバネーションはしてない😂」「後は毎日ディスクを動画でぎっしり埋めるとかそういうことしてなければ😎」
⚓JavaScript
⚓lazyestload.js: viewportまで読み込みを遅延するライブラリ(Frontend Focusより)
- リポジトリ: Paul-Browne/lazyestload.js
// 同リポジトリより
img {
transition: filter 0.3s;
}
img.lazyestload {
width: 100%;
filter: blur(8px);
}
つっつきボイス:「最上級😆」「あんまりlazyにすると今度は遅くなりますけどね🕶」
⚓CSS/HTML/フロントエンド/テスト
⚓Let's Encryptの快挙
日本の Web ユーザのページアクセスのうち 69% が HTTPS です。わずか 2 年前には 50% 未満でした。
これは 100% 暗号化されたウェブへのすばらしい進歩であり、時雨堂の @voluntas による Let's Encrypt! への継続的なスポンサーシップとサポートに感謝します— Let's Encrypt (@letsencrypt) August 29, 2018
つっつきボイス:「もうHTTPSが70%近い😍」
⚓「結合テストと呼ぶのをやめた話」
フロントエンドのパフォーマンスチェックリスト(Frontend Focusより)
その他CSS/HTML/フロントエンド/テスト
すごい。ロゴの無償・自動生成サービスは、今まで結局満足いくものなかったんだけど、これはプロダクトで使える。
Font、Iconの微調整や差し替えまで機能ついてて無料!https://t.co/GDbPEoRUoC pic.twitter.com/opguExNxMa— Masahiko Sakakibara (@rdlabo) August 26, 2018
⚓言語よろずの間
⚓Go 2でGenerics導入の検討始まる
🎊 We just announced an update for Go 2!
This includes preliminary draft designs for generics, error handling, and error values.
Read our blog and watch the video by @_rsc for more information. https://t.co/WB4EJuyGCR
— Go (@golang) August 28, 2018
つっつきボイス:「Go言語のバージョン2はやっぱりGo 2と呼ばれるみたい: このダジャレが言いたかったに違いないと睨んでます☺️」
go-to
{形-1} : 頼りになる
{形-2} : 〔チーム・組織などの〕主力の、大黒柱の
⚓その他言語よろず
書いた。 / “Big Sky :: golang で tensorflow のススメ” https://t.co/7SaKenlUUo
— mattn (@mattn_jp) August 24, 2018
⚓その他
⚓コードには癖が残る
つっつきボイス:「元記事はマルウェア作成の犯人を捜すためみたいですけど、言われてみればコードでも普通の文章でも人物特定は十分可能だなって」「やーこれは全然あるある: 自分のコードも特徴ありありだし☺️」「ボクもー😜」「git blame
する前にだいたいわかっちゃうとか」「さすがに1行だけ見せられてもわからないけどねー😆」
参考: プログラムのコードには、個人を識別できる“指紋”が残されている:研究結果|WIRED.jp
⚓RISC-Vを一晩で実装
- リポジトリ: darklife/darkriscv
まだまだやる気満々のロードマップもあります。
⚓番外
⚓眼球を3Dプリンタで
参考: 3Dプリンターで完璧に機能する目「バイオニックアイ」を作り出す研究 - GIGAZINE
つっつきボイス:「今や航空機のパーツでも3Dプリンターで作れますね✈️」
⚓いいこと聞いた
オリンピックの金属供出調べてみた。
1. 無料宅配で自宅まで取りに来てもらえる
2. 対象は小型家電リサイクル対象28品目
3. 28品目にはデスクトップ型(タワー型及び一体型を含む)PCも含まれる
4. 事業者も可\要らないデスクトップPCを無料で廃棄するチャンス!/
— mizti (@mizti) August 28, 2018
つっつきボイス:「最後は珍しく実用的な情報で」「そうそう、これチャ~ンス!😋」「ざわっ😋」「きら〜ん✨」「『供出』って言葉が戦時中に釣り鐘とか徴用しまくったときみたいで🤣」
参考: 金属類回収令 - Wikipedia
参考PDF: 小型家電リサイクル対象品目(28品目)
今回は以上です。今週の公開つっつきでお会いできるのを楽しみにしております。
おたより発掘
TechRacho、恐ろしく勉強になるな‥
— けん (@ken3ypa) August 30, 2018
バックナンバー(2018年度後半)
- 20180820 Railsで構築されたサイト40選、Deviseはつらいよ、ARのスコープとクラスメソッドの使い分けほか
- 20180813 Rails 5.2.1リリース、sanitize_sql_arrayは5.2からpublicだった、Dev.toが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など)です。