週刊Railsウォッチ(20190128)Rails 6のオートローダーがZeitwerkに置き換わる?Rails 6はRuby 2.5が必須、最近のSQLiteほか

こんにちは、hachi8833です。LinuCLPI-JapanLinux標準教科書は無料のPDF版よりKindle版(200円)の方がKindleでは見やすいことに気づきました。ついでに英語版↓も制作されていたことを知りました。


つっつきボイス:「Linux標準教科書って何か英語版を元にしてるとかじゃなくって?🤔」「かなと思ったんですが、『JICAボランティアのご協力によりLinux標準教科書が英語化されました』とあるので日本語から訳したみたいです」「ま英語圏にはいい教科書がいっぱいあるし😆」「😆」「日本人が英語の書籍を読まなさすぎというか📚」

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

週刊Railsウォッチ「公開つっつき会#7」開催のお知らせ

次回の公開つっつき会は2/7(木)に開催いたします。皆さまのご参加をお待ちしております🙇。

Rails: 先週の改修(Rails公式ニュースより)

先週のウォッチまでに公式の更新情報をだいぶ先回りしたので、コミットログからも少し見繕いました。

Rails 6.0.0 beta1リリース

6.0.0の機能は、だいたいこれまでウォッチで追ってきたとおりですね。


つっつきボイス:「おー、タイムラインが発表されてるし📅」「とりあえず餅の絵は描き終わったようだ😆」「4月末のRailsConf 2019にリリースって、もうすぐじゃん!: そんなに期間残ってないし、果たしてどのぐらずれ込むか😆」「もろもろ織り込み済みでしょうね☺️」

「そしてRails 6ではRuby 2.5以上が必須か〜: Rubyのバージョンアップが必要になることが増えるから大変そう😅」「気のせいか、以前のRailsよりRubyバージョンの要件の幅が狭くなっているような?🤔」「以前のRubyが結構切り捨てられてますね: まああまり古いRubyで動かすのも考えものだし」

「Rails 6のマルチデータベース、果たして今後落ち着いてくれるんだろうかとも思ったり🤔」「あー」「ま、いきなり使うことはなさそうだけど😆」

ActionCable::Connection::TestCaseが完全にマージ

# actioncable/lib/action_cable/connection.rb#L3
module ActionCable
  module Connection
    extend ActiveSupport::Autoload
    eager_autoload do
      autoload :Authorization
      autoload :Base
      autoload :ClientSocket
      autoload :Identification
      autoload :InternalChannel
      autoload :MessageBuffer
      autoload :Stream
      autoload :StreamEventLoop
      autoload :Subscriptions
      autoload :TaggedLoggerProxy
+     autoload :TestCase
      autoload :WebSocket
    end
  end
end
# 同PRより: 利用例
class ConnectionTest < ActionCable::Connection::TestCase
  def test_connected_with_signed_cookies_and_headers
    cookies.signed["user_id"] = "456"

    connect headers: { "X-API-TOKEN" => "abc" }

    assert_equal "abc", connection.token
    assert_equal "456", connection.current_user_id
  end

  def test_connected_when_no_signed_cookies_set
    cookies["user_id"] = "456"

    assert_reject_connection { connect }
  end

  def test_connection_rejected
    assert_reject_connection { connect }
  end
end

つっつきボイス:「Action Cableのテストがひととおり入ったのね☺️」「入ったヘルパーは思ったほど長くないですね」「長々と書く意味ないですし😆」「😆」

親ディレクトリの監視を止めた

# activesupport/lib/active_support/evented_file_update_checker.rb#L125
      def directories_to_watch
-       dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) }
+       dtw = @files.map(&:dirname) + @dirs.keys
        dtw.compact!
        dtw.uniq!

        normalized_gem_paths = Gem.path.map { |path| File.join path, "" }
        dtw = dtw.reject do |path|
          normalized_gem_paths.any? { |gem_path| path.to_s.start_with?(gem_path) }
        end
        @ph.filter_out_descendants(dtw)
      end

つっつきボイス:「y-yagiさんの改修です」「指定のディレクトリが存在しないとEventedFileUpdateCheckerが親ディレクトリをチェックしてたのか: そういえば今日ちょうどチームでこのあたりが話題になってた🥺」「node_modulesまで対象に加わってたんですね」「確かに遅くなるやつ」「社内SlackのJavaScript板にこの間貼られてたこれ↓を思い出しちゃいました🦆」

render_templateの引数が変更された

# actionview/lib/action_view/renderer/streaming_template_renderer.rb#L46
-   def render_template(template, layout_name = nil, locals = {}) #:nodoc:
+   def render_template(view, template, layout_name = nil, locals = {}) #:nodoc:
      return [super] unless layout_name && template.supports_streaming?

      locals ||= {}
      layout   = layout_name && find_layout(layout_name, locals.keys, [formats.first])

      Body.new do |buffer|
-       delayed_render(buffer, template, layout, @view, locals)
+       delayed_render(buffer, template, layout, view, locals)
      end
    end

つっつきボイス:「tenderloveさんの改修です」「お?render_templateが変更されてるし…」「インスタンス変数で渡すのをやめたということでしょうか?」「逆ですね: インスタンス変数を渡すようになった」「あっと失礼しました💦」「従来はビューのコンテキストから@viewを取っていたのを、改修後は引数経由で取るようになったというということですね🧐」

# actionview/lib/action_view/renderer/template_renderer.rb#L5
module ActionView
  class TemplateRenderer < AbstractRenderer #:nodoc:
    def render(context, options)
+     @view    = context
      @details = extract_details(options)
      template = determine_template(options)

      prepend_formats(template.formats)

      @lookup_context.rendered_format ||= (template.formats.first || formats.first)

-     render_template(template, options[:layout], options[:locals])
+     render_template(context, template, options[:layout], options[:locals])
    end

「この部分のAPIインターフェイスが変わるということだから、render_templateを生で書いてた人たちは影響を受けるかも?🤓」「あー🤭」「render_templateはRailsの内部向けのものだから、テンプレートを直にいじることは普通そんなにありませんけどね☺️: その代り、ビュー周りを扱うgemたちが影響を受ける可能性はありそうTemplateRendererクラス↑も変わってるし🤔」「そっかー😳: そういえばこの記事↓ではAction Viewの基本設計はこれまでほとんど変わってなかったと解説されてました」「gemによってはデカい変更かも⚡️」

Railsのテンプレートレンダリングを分解調査する#2 ActionView編(翻訳)

なお、上の記事を書いた@st0012さん(Gobyの作者)にもこの変更のことを知らせたところ、「100%納得いく変更🥰」とのことでした。

「ついでですが、ivarって何だろうと思ったらインスタンス変数の略記でした↓」「果たして一般に通じるのかと😆」「Railsだけなのかな😆: instance variableってやっぱ長いですよね」

参考: ruby – What is ivar in Rails controller? – Stack Overflow

ActiveRecord::StatementCache::Substituteのキャッシュのバグを修正

# activerecord/lib/active_record/relation/query_attribute.rb#L
      def unboundable?
        if defined?(@_unboundable)
          @_unboundable
        else
-         value_for_database
+         value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
          @_unboundable = nil
        end
      rescue ::RangeError
        @_unboundable = type.cast(value_before_type_cast) <=> 0
      end

問題: ステートメントキャッシュ(findとかfind_byとか)で使うステートメントをビルドするときに、ActiveRecord::StatementCache::Substituteオブジェクトがその属性の型のcastメソッドに渡される。キャスト呼び出しの戻り値はさして重要ではないが、このときに例外が出ないことを当てにしている。たとえば、ActiveRecord::StatementCache::SubstituteオブジェクトでActiveModel::Type::Float#cast_valueto_fを呼ぼうとすると例外が発生する。これはカスタム属性の型で問題になる可能性もある(PRとは別に含めたリグレッションテストを参照)。
解決: Active Modelの属性の型ではActiveRecord::StatementCache::Substituteオブジェクトを扱うべきではないので、ActiveRecord::Relation::QueryAttribute#value_for_databaseが北ときはsuperを呼ばないようにした。nil?メソッドでそうしたオブジェクトの型チェックが既にあったので、このクラスではそうしたクラスで既に特殊なキャストも行われていたことになる。
同PRより大意


つっつきボイス:「とりあえず普通にバグ: 使う人はビビるだろうな😨」「5b6daffのデグレだったとkamipoさんがフォローしてますね」

# activerecord/lib/active_record/relation/query_attribute.rb#L25
-     def boundable?
-       return @_boundable if defined?(@_boundable)
-       nil?
-       @_boundable = true
+     def infinite?
+       infinity?(value_before_type_cast) || infinity?(value_for_database)
      rescue ::RangeError
-       @_boundable = false
      end

-     def infinite?
-       infinity?(value_before_type_cast) || boundable? && infinity?(value_for_database)
-     def unboundable?
+       if defined?(@_unboundable)
+         @_unboundable
+       else
+         value_for_database
+         @_unboundable = nil
+       end
+     rescue ::RangeError
+       @_unboundable = type.cast(value_before_type_cast) <=> 0
      end

「ついでに5b6daff↑を見ると、unless bind.boundable?if bind.unboundable?に改められてる」「unlessifに変えて読みやすくしたんでしょうね」「infinite?と同じロジックで扱えるようにunboundable?にしたのね: まあ言い回しレベルかな」

Rubyにおけるunlessとコードの読みやすさについて

Rails

新しい「Zeitwerkコードローダー」をRails 6で導入の動き(Rails公式ニュースより)

Zeitwerk: 時間(time)と仕事(work)に相当するドイツ語

Zeitwerkという高級時計メーカーがあるんだそうです。「時の工匠」的な?


つっつきボイス:「これが例の新しいオートローダー」「ツァイトヴェルクっていう響きがいかにもドイツ語🇩🇪」「Zで始まる英単語ってあんまりないし😆」「そういえばドイツ語だとZの利用頻度がめちゃめちゃ高くて、逆にYがほとんど使われないのでキーが入れ替わってますね↓」

参考: Mac OS と iOS の融合 |  Mein dritter Blog — ドイツ語のキーボードレイアウトが解説されてます

「Mediumの記事では、Zeitwerkは高効率かつスレッドセーフのRubyローダー、とある」「known gotchaとあるのは、Railsガイドに載ってる今のオートローダーのハマりポイント↓ですね」「たしかにRailsのオートローダーはハマるときはハマるからな〜😭」「いつぞやの名前空間地獄とかですね😱」

「で、Railsでは従来const_missingが使われていていろいろ限度があったけど、ZeitwerkではKernel#autoloadを使ってるのか」「Mediumの記事には古いローダーは2004年から使われてきたと書いてますね」

Railsの旧来のローダーはconst_missingを使っているため、ネストや、定数が見当たらない場合に使われるアルゴリズムをコールバックで取れないという根本的な限界がある。旧ローダー自身が持つ情報を使う分には間違いなく最適であり、2004年からずっとうまく動いていた。しかしRuby言語に新機能が加わったことで、さらに優れたアプローチが可能になった。
Zeitwerk gemはKernel#autoloadをベースにしており、ここ数年の私にとってはまさしく聖杯であった。
同記事の抜粋・大意

参考: const_missing — ActiveSupport::Deprecation::DeprecatedConstantAccessor

「Railsの既存のオートロードの仕組みってめっちゃ古いままだったのね👴」「Kernel#autoloadってRubyのメソッドでしょうか?新しいのかな?」「ざっとググってみると、Kernel.#autoloadはRubyの1.8.6とかには入ってるらしい」「そんなに前から!😳」「最初のRailsができた頃には既にあったのかもしれないけど、これまでKernel.#autoloadが活用されてなかったってことなのかな?🤔: 何にしろオートロードの仕組みが今後変わるとしたらデカい🗻」

参考: module function Kernel.#autoload (Ruby 2.6.0)
参考: requireとrequire_relativeとautoload – Qiita

「まーrequireとか何も気にしないで使えるようになればいいことだし☺️」「まだRailsに入れる作業は進行中なのか…」「Zeitworkのリポジトリも見てみるか: ネステッドrootディレクトリにも対応とか、loader.setupでセットアップできるとか↓」

# 同リポジトリより
loader.push_dir(...)
loader.push_dir(...)
loader.setup

「お、loader.reloadリロードもできるし、eager loadingもプリローディングもできるし↓: いいじゃないですか〜❤️」

loader.reload 

loader.eager_load

loader.preload("app/models/videogame.rb")
loader.preload("app/models/book.rb")

「inflection、つまりクラスとかの単数形複数形の活用も対応してますね↓」「どことなく想定外の動きをしそうな匂いがしそうではあるけど😅、カスタムinflectorもあるというのが何ともRailsらしい: つかRailsのオートローダーは活用形周りも扱ってるはずだから、確かにこういうこともできないと」

user             -> User
users_controller -> UsersController
html_parser      -> HtmlParser


# frozen_string_literal: true
class MyInflector < Zeitwerk::Inflector
  def camelize(basename, _abspath)
    case basename
    when "api"
      "API"
    when "mysql_adapter"
      "MySQLAdapter"
    else
      super
    end
  end
end

「名前空間になるクラスやモジュールはclassmoduleキーワードで定義しないといけない」「Trip::Geolocationは名前空間を生書きしてるんでしたね」

# trip.rb
class Trip
  include Geolocation
end

# trip/geolocation.rb
module Trip::Geolocation
  ...
end

「この書き方↓はできないと: この記法では普通でもオートロードはサポートしないし、納得」

# trip.rb
Trip = Class.new { ... }  # NOT SUPPORTED
Trip = Struct.new { ... } # NOT SUPPORTED

「Zeitwerk自体Ruby 2.4.1以上が必要ということだから、もしかするとそれでRailsが2.5以上必須になったのかも?🤔」

「これがZeitwerkを作った動機かー↓」「今さらですけど、requireがグローバルということは、特定のファイルでだけrequireしたりできないんですね?」「ですです: Rubyではできません😅」

requireの副作用がグローバルであるがゆえに、自分が依存するファイルのコードを読み込むrequireの呼び出しが完了したかどうかを安定して検証する方法が存在せず、実際一部が呼び出されてないなんてことが簡単に発生する。そしてこれのせいで、読み込み順に依存するバグが引き起こされる。Zeitwerkは、自分のコードでrequireのことなんか忘れて普通に書けばいいようにする方法を提供する。
その一方、Railsのオートロードはconst_missingベースであり、実際に使われた解決アルゴリズムやネストといった基本的な情報を欠いている。このせいで、RailsのオートロードはRubyのセマンティクスと一致することができず、さまざまなハマりポイントを生み出している。このプロジェクトは元々、改善されたオートロードをRails 6にもたらそうと思って始めたものである。
同リポジトリより大意

「Zeitwerkをざっと眺めてみて、必要そうなものはひととおり揃ってそうな感じ」「何だか期待できそうですね😍」「とにかくRailsの既存のオートローダーってたまにものすごいハマり方するから😭」「オートローダー地獄、何とかしないといかんですね…🥺」「あれは知ってても脱出が難しい、ホント😇」

開発者目線から見たタイムゾーン


つっつきボイス:「データベースとRailsとJavaScriptについてタイムゾーンを説明してるようです」「それぞれのタイムゾーンの設定が噛み合わないと大変なことになるみたいな話でしょうね🤕: 実によくある話」

「そういえば今のRailsは全部UTCに揃えるんだったけか?: configでタイムゾーンを変更しても保存はUTCになってた気がする」「取り出して使うときにローカルのタイムゾーンにするということなのかなと想像しました」「Railsで保存したタイムゾーンはRubyの世界では参照可能になるんですが、後で変更が効かない部分もあるんで厄介なんですよ」「えー😱」「システムタイムゾーンが違うとつらいことが起きたりします😇: JSも然り」

「LinuxのサーバーってUTCで運用するのが普通だったりするんでしょうか?」「んなことはない😆: システムタイムゾーンがJSTとか普通にあります」「そっかー」「だからややこしくなってしまうんですが、UTCのタイムスタンプが取れないシステムはさすがにないので、その点はマシ」

Ransackか生SQLか


つっつきボイス:「jnchitoさんのスライドで、どこまでRansackでやってどこから生SQLにするかみたいな話をしてました」「これは自分もよく同じ話をしてますね: スライドでも『9割はRansackでいけるけど』とあるようにRansackで頑張りすぎるぐらいなら生SQLにする方がいいし」「いつぞやのRansackでOR使うと死ねるという話もありましたね」「複雑になるなら生SQLの方が全然いいし☺️: スライドでやってるみたいにQuery ObjectとかFormクラスとか使ったり、conditions自分で書いたりとかして」

「お、『SQLはERBに書く』方針か、うーんなるほどー😎」「え、それってあり?と思ったら『Railsのビューとは関係ない素のERB』でしたか😅」「言われてみれば、最終的に完全なSQLができるならERBのような何らかのテンプレート言語を使って書くというのは悪くないですね: いろいろ融通が効くし、自分もちょうど昨日muninのコンフィグをERBで書いたりしてたし」「へー!」


munin-monitoring.orgより

「いいスライド👍」「🥰」

2019年の今、RailsをWindows 10で動かす(Hacklinesより)


つっつきボイス:「Windows 10でRails、割と動きますけどね」「昔は大変だったみたいですね」「むしろDockerとかの方が言うこと聞いてくれない😆: で、この記事はというと…なーんだ、WSLでやってるのか」「ほんとだ」「WindowsにネイティブにインストールするんじゃなくてWSL使うんだったら、たいてい動くんじゃないかなー普通に: 遅いとかあるかもしれないけど😆」

Web開発環境をMacBook ProからWindows機に移行してみた話

ActiveModelとActiveRecordの関係と、ActiveModel::Attributes

熱烈なファンのいるActiveModel::Attributesはもっと前からあってよさそうなのに、なぜこんなに最近になってできたのかが気になったので、Webチームの名メンターであるkazzさんに教わったことを中心にメモしてみました。

参考: ActiveModel::Attributes が最高すぎるんだよな。 – Qiita


つっつきボイス:「ちょっとメモするつもりが雑然としてしまったのでこの項スキップでもいいです💦」「あーそうそう、ActiveRecordとActiveModelって、インターフェイスはかなり似通っているのに直接の関係がないというのがややこしいんですよ🧐」「やっぱり〜」「そしてActiveModelは永続化のインターフェイスも少し持っているんですが、永続化についての責務は持たないというか永続化の実装がない🧐」「なるほど!」「スタブというか’口’だけを持ってるみたいな感じですね👄」

「そうそう、ActiveModelはRails 3からだった: そしてActiveModel::Attributesが登場する前にもactive_attrとか、Dry-rbの前身だったvirtusみたいな似た感じのgemがあって、自分も使ってましたね😋」

「そして今はActiveModel::Attributesがあるからこれでいいんじゃねって思うし😘: ActiveRecordは重いけど、ActiveModelは使いたいなんていうケースはあるかも」「ActiveModel::AttributesみたいなのってRailsでも何度かプルリクされてたんですね」「active_attrもそうですけど、みんな同じようなものを作ってたし😆」

「実はActiveModel::Attributesって、アトリビュートを生やすだけなら実装はそんなに難しくなくて、自分で一度作ってみるととってもいい勉強になります🍖」「おー😍」「誰もが同じことをやってるぐらいだし、RubyでDSLをどう実装するかという練習にはぴったりだし、かつ実用的: ActiveModel::Dirtyみたいなことをするんでなければ大丈夫😉」「いいですね!😋」

参考: ActiveModel::Dirty

追いかけボイス「ActiveModel::Attributesの登場は自分は遅いとは思わなかったなー: むしろ早い方だと思うし」

Railsで巨大データを扱ってみた


つっつきボイス:「ぱっと見activerecord_importあたりを使うとか、ActiveRecordを通さないでやるとかそういう話かなと想像」

「ほほー、IOforeachのLazy Enumでこういう書き方↓できるんだ: だしかにlazyを通さないとサイズによってはメモリが溢れて死ぬだろうし😇」「CSVのエクスポートでやる方法も紹介されてますね」

# 同記事より
imported_products_old_ids = Work.pluck(:old_version_id).uniq

IO
  .foreach('tmp/export_products.txt')
  .lazy
  .map { |raw_line| JSON.parse(raw_line) }
  .select { |product_hash| imported_products_old_ids.include?(product_hash.fetch('id')) }
  .each do |product_hash|
    # ...
  end

YAML.storePStoreか↓」「どっちもRubyのライブラリなんですね: PStoreの方が速いっぽい」

# 同記事より
# export
require 'yaml/store'

def product_hash(product)
  hash = { id: product.id, id: product.title }
  hash.merge! product.media.map do |medium|
    { ... } #media data
  end
  hash.merge! product.comments.map do |comment|
    { ... } #comment data
  end
  hash
end

store = YAML::Store.new(Rails.root.join('tmp/products.yaml'))
store.transaction do
  store[:products] = []
end

Product.find_each do |product|
  store.transaction do
    store[:products] << product_hash(product)
  end
end

# import
store = YAML::Store.new(Rails.root.join('tmp/products.yaml'))
store.transaction(true) do
  store[:products].each do |product_hash|
      # ...
  end
end

「おー、PStore.newしてPStoreの形式でダンプしてロードできるっぽい: マーシャリング的なことを高速にやれるこういう仕組みがあったとは知らなかったなー☺️」「おー」「たしかにYAMLとかに比べれば変換が発生しない分速そう❤️」

参考: class PStore (Ruby 2.6.0)

require 'pstore'
db = PStore.new("/tmp/foo")
db.transaction do
  p db.roots       # => []
  ary = db["root"] = [1,2,3,4]
  ary[0] = [1,1.5]
end

db.transaction do
  p db["root"]     # => [[1, 1.5], 2, 3, 4]
end

「元記事はSQLで頑張る方の話かと思ったら、どちらかというとRubyの世界で頑張る話か: こういうの社内に好きな人がいそう🥰」

ReactとRailsを統合する方法(Hacklinesより)


つっつきボイス:「APIサーバーで頑張るとかかな?」「やり方が3つ紹介されてました」

「1つはWebpackerかやっぱりー☺️」

「2つ目はreact_on_railsと」「これは前からあるヤツですよね?」「ですね: まあこういうのは今後どんなふうに変わるかわかりませんけどっ😆」「😆」「生で書く方がいいような気はするけどな〜」

「3つ目はやはりAPIサーバーとReactフロントですね」

Ruby

Pythonで欲しかったRubyの機能(RubyFlowより)

# 同記事より: Python
engineers = [e for e in employees if e.department == 'engineering'] # filter
engineer_salaries = [e.salary for e in engineers] # map
sum(engineer_salaries) # reduce
# 同記事より: Ruby
employees
  .select { |e| e.department == 'engineering' } # filter
  .collect { |e| e.salary } # map
  .sum # reduce

つっつきボイス:「いろいろわかりみはある: Pythonほとんど使ってないけど😆」「Pythonってprivateメソッドがないんですね: __付けて紳士協定的にこれはprivateだよみたいな感じでやってるみたい」「へー、privateがないとは😳」

「Pythonは多重継承でやることが多い一方、Rubyはmixinでやれるともありますね」「Rubyだと式展開を引用符の中に置ける↓とか」「まあそういう違いはもうしょうがないっすね☺️」「記事でも違いは微妙だけどと言ってました😌」

# 同記事より: Python
"Hello, {}! Welcome to {}.".format(employee_name, company_name)
# 同記事より: Ruby
"Hello, #{employee_name}! Welcome to #{company_name}."

Pythonの「モジュール」は個人的にちょっと好きです😘。名前空間が他と別になっているあたりとか。

参考: 6. モジュール (module) — Python 3.6.5 ドキュメント

自分が作ったgemをバズらせる方法(RubyFlowより)


つっつきボイス:「技術畑のマーケティング話的な」「READMEとかのドキュメントをちゃんと書いて、ドメイン取ってWebサイトも立てて、コミュニティを盛り上げる方向に頑張ってと、いたって王道な感じ☺️」「もうこういうのが普通なんでしょうね」

「今のオープンソースはだいたいこういう感じでやってますね: 最初に立ち上げるものをキレイに作っておかないとものが良くても見向きもされなかったりするし、逆に見た目優先で立ち上げてもそこに人が集まってきて後から実体が伴ってくるなんてこともあるし☺️」「☺️」

Rubyのメソッドブロックを取り出してみた(Hacklinesより)

短い記事なので結果を載せます。

# 同記事より
require_relative 'block_source'

def foo
  pp block_source
end

foo { 'hello' }   #=> " { 'hello' }"
foo { |i| i * 3 } #=> " { |i| i * 3 }"
foo               #=> nil

つっつきボイス:「お〜〜ブロックのソースを抜き出すなんて、そんなことができるとは!」「Ruby標準の機能にはなさそうですね」「ないんじゃないかな🤓: 今動いているコードの行を引っ張ってくる機能は最近のRubyにあったと思いますが」「あーそうでしたっけ」「今動いているコードがソースのファイルの何行目なのかを知るみたいな機能をRubyKaigiで見た気がする」「デバッグで便利そうですね😋」

探してみたのですがうまく見つけられませんでした🙇。

参考: instance method IO#lineno (Ruby 2.6.0)
参考: Ruby で今読み込んでいるファイルの行数を取得する – Qiita
参考: 変数と定数 (Ruby 2.6.0) — 疑似変数__LINE__で取れるようです。

「お、caller_locationslineno↓を使ってソースを取り出してるっぽい: Railsコンソールとかでこういうの使いたくなることがあるかも😋」

# 元記事より
module Kernel
  # RUBY_VERSION >= '2.6.0'
  def block_source
    @file_specs ||= {}
    bl = caller_locations.last
    source = @file_specs.dig(bl.path, :source) || File.readlines(bl.path)
    @file_specs[bl.path] ||= { source: source, ast: RubyVM::AbstractSyntaxTree.parse(source.join) }
    node = find_node(
      ast: @file_specs.dig(bl.path, :ast),
      type: :ITER,
      lineno: bl.lineno
    )
    extract_source(node: node.children[1], source: source) if node
  end
end

参考: module function Kernel.#caller_locations (Ruby 2.6.0)

「あれ?この記事書いた人のアバター見たことあると思ったら安川さんのところの人だ」「沖縄在住ですね☺️」

Rubyist Magazine 0059号がリリース

るびま応援いたします❤️。

その後リリースされました🎊🎉。Ovto面白そう!

その他Ruby


つっつきボイス:「伽藍とバザール、懐かし!」「90年代でしたよね」「今や古典📚」「MINIXのタネンバウムがLinuxを巡ってリーヌスとやりあった話とか思い出しちゃいました」

参考: 伽藍とバザール – Wikipedia
参考: アンドリュー・タネンバウム – Wikipedia
参考: MINIX – Wikipedia

Ruby trunkより

卜部さんが手がけたRuby高速化

trunkではありませんが。


つっつきボイス:「今朝電車の中で読んでて高速化の追い込みがスゲーと思ったので: エン・ジャパンのエンジニアHubはいい記事が多いなと改めて感じました😍」「あそこはかなり頑張っていい記事書いてますね👍」「シンタックスハイライトがスマホで読みにくかったぐらいでした😅」「まあそのぐらいは😆: コードをスマホで読むのもどうかと😆」「ですね😆」

クラウド/コンテナ/インフラ/Linux/Serverless

Podman: Docker互換の新コンテナエンジン(Publickeyより)


同サイトより

とりあえずかわいい❤️。


つっつきボイス:「おーこういうの出てましたね: Red Hatは元々Dockerに相当つぎ込んでるし、わかる気はする」「おー」「PodmanってDockerエンジンと差し替えられるのかな?」「デーモンがないのと、KubernetesのPodもサポートしてるのが特徴みたいですね」「だからPodmanなのかな?知らんけど😆」

「OCI↓に対応してるというのも筋がよさそう: 後はみんながどれだけ使ってくれるかですが☺️」「Dockerに追いつけ追い越せの気合いがPodmanのマスコットの可愛らしさに表れてる気がしました🐹」「Dockerと完全互換ならワンチャンあるかもしれないけど😆」

参考: Home – Open Containers Initiative

「PodmanのRootlessコンテナもかなりありがたい🙏」「ということはDockerはそうじゃないんですね?」「です: DockerはDockerデーモンが基本的にはroot権限で動くので」「権限強すぎな感じですね…」「Dockerの中のファイルのUIDはDockerホスト側のUIDと同じもので作ることを強いられるので、root権限で動かすか権限昇格するかしないとファイルを作れなかったりするし😅」「なるほど!」「Dockerはそこが不便なんですよね😢: 特に-vとか付けるとDockerホストを汚染する可能性があって危険⚠」「ほあっ😨」「だからKubernetesとかの方が分離しやすかったりしますね」

SQL

AWS DocumentDBはほんとはPostgreSQLじゃね?(DB Weeklyより)


つっつきボイス:「AWS DocumentDBが実はPostgreSQLでできてるんじゃないかと睨んでるそうです」「内部的に同じものを使ってる可能性はまああるかもですが、ずばりそのものを使ってるんじゃないかという勢いで書かれてるっぽい☺️」「っぽいですね」

「でもDocumentDBが自動でAvailability Zoneにコピーを6つ作成するなんてのは、データベースというよりはAuroraファイルシステムの特徴なので、その上にDocumentDBやPostgreSQLが乗ってるならそこが同じだとしても不思議じゃない」「おー」「ま、こういうものがフルスクラッチで書かれているかどうかとか気にしないけどっ😆」

最近のSQLiteは何だかスゴイ(DB Weeklyより)


つっつきボイス:「お、SQLiteにいつの間にかwindow関数が入ってるし」「マジっすか!?😳」「FILTER構文に、それからON CONFLICTって何だろう?」「衝突したらUPDATEできるみたいなヤツかな?UPSERTみたいな」「複数プロセスが開いているときの挙動とか?知らんけど」

-- 同記事より
INSERT INTO target
SELECT *
  FROM source
    ON CONFLICT (id)
    DO UPDATE SET val = excluded.val

「実はSQLiteってかなり頑張ってますね: 新しいSQLiteは相当ちゃんとしていて、結構いい❤️」「おぉ〜」「実際使っててホント速いと思いました(参照のみだけど)」 「規模が小さければもうSQLiteでいいんじゃねというぐらいの勢い」「そうそう🥰」

「SQLiteは特にクライアントで動くアプリで使うのに最適: 何てったって速いし🚅」「ですね〜」「サーバーだとどうしても同時アクセスが多数発生するから、それをちゃんと捌けるRDBMSが必要になるけど、クライアントアプリなら自分しか使ってないからそんな心配もいらないし🤓」「Webアプリでも、リードオンリーのマスターデータ専用に使う手もあるかも(やったことないけど😆)」


sqlite.orgより

その他SQL


同リポジトリより


つっつきボイス:「ほーNodeで動くSQLデータベース」「SQLiteを使いたくても使えないような環境で使うとか?もしかするとGAS(Google Apps Script)でも動いたりして」「それありそう」「頑張るところが何か違う気もするけどっ😆」「😆」

参考: Apps Script  |  Google Developers

JavaScript

私はいかにしてJSモジュールのデフォルトexportをやめたか(JavaScript Weeklyより)


つっつきボイス:「export default class LinkedList {}なんて書き方できるんだー: JSそこまで詳しくない〜😅」「exportをデフォルトでやって何かつらい目にあったのかしら?」「exports.LinkedList = class LinkedList {};みたいな名前付きexportもできるのか: 昔見た気もするけど」

「あーimportでこうやってas名前変えられると↓」「ないと困りそう」「importする側で何とかしてくれみたいな☺️」「こういう文化の言語ってありますよね: Pythonとか」「Go言語もそうですね」「みんな同じような名前で作っちゃうという想定の世界」

// 同記事より
import { LinkedList as MyList } from "./list.js";

JSでタイムゾーンを扱う(JavaScript Weeklyより)


つっつきボイス:「GMTって最近この業界ではめっきり見かけないな〜」「そういや見ませんね」「グリニッジ標準時でしたっけ: イギリスが覇権を失った感🇬🇧」「そしてサマータイムの話題も😆」

参考: グリニッジ標準時 – Wikipedia

「そして恐怖のDateオブジェクト↓」「Local Storageにマーシャリングして保存した後で元が変わったりしたときにつらくなった覚えが: 変わってもいいように書いておけばいいんだけど、それを忘れて死んだりとか😇」「新しいタイムゾーンを読み込むとローカルで持っているデータがぶっ壊れたり🧨」

// 同記事より
const d1 = new Date(1489199400000);
d1.toString(); // Sat Mar 11 2017 11:30:00 GMT+0900 (KST)

「Momentsって何だろうと思ったらライブラリでしたか」「Moments.jsといえば日時関連処理で結構メジャーですね🧐」「JSの日付時刻処理が非力すぎるからこういうMoments.jsとか昔流行ってた」


momentjs.comより

↑サイトでは時計が実際に動いています。

TypeScriptなしで型厳密にやる方法(JavaScript Weeklyより)

割と短い記事です。


つっつきボイス:「TypeScriptなんかなくたって型厳密にやれるんだぜみたいな😆」「ランタイムでannotationを解決しながら実行するとかそんな雰囲気」「まあでもTypeScriptは中で実際にこういうことをしてるわけだし、手作りできるっちゃできるし」


typescriptlang.orgより

wpk: Webpackの非公式CLI(JavaScript Weeklyより)


同リポジトリよりより


つっつきボイス:「アンオフィシャルなWebpack CLIとな」「オフィシャルのCLIってつらいんでしょうかね: いっそBundlerと一緒のコマンド体系だったらいいのに」「きっと逆も真なりで、Webpackを使ってるJSエンジニアもRubyに対してそう思ってるかも😆」「😆」「何にしろ公式に入ってくれないとどう変わるかわかんないし」

sockette: 薄いWebSocketラッパー(JavaScript Weeklyより)


同リポジトリより

// 同リポジトリより
const Sockette = require('sockette');

const ws = new Sockette('ws://localhost:3000', {
  timeout: 5e3,
  maxAttempts: 10,
  onopen: e => console.log('Connected!', e),
  onmessage: e => console.log('Received:', e),
  onreconnect: e => console.log('Reconnecting...', e),
  onmaximum: e => console.log('Stop Attempting!', e),
  onclose: e => console.log('Closed!', e),
  onerror: e => console.log('Error:', e)
});

ws.send('Hello, world!');
ws.json({type: 'ping'});
ws.close(); // graceful shutdown

// Reconnect 10s later
setTimeout(ws.reconnect, 10e3);

つっつきボイス:「ロゴと名前はsocketとsocksの掛詞っぽいですね🧦」「なるほどカワイイ🐼」「これはWebSocketに限らずできるのかな?WebSocketが対象っぽい」「サイズが348バイト!」「えれえ小さい🐁!」「ソースも50行ちょい!」

CSS/HTML/フロントエンド/テスト

手動テストで必ずやってる4つのこと


つっつきボイス:「1つ目はブラウザでアプリをキーボード操作してみること」「アコーディオンがちゃんと開くかとか」「こんなふうに↓マウスオーバーで選択するタブがキーボードでも選択できるかどうかとか: 確かにこういうのは実際にやらないとわからないし: スクリーンリーダー使ってる人はそれできないと困るし」「ちょうど2番目がスクリーンリーダーの話ですね」「スクリーンリーダーのチェックも必要☺️」


同記事より

「4番目のWave Toolって知りませんでした」「おー、これはいわゆるWeb評価用ツールですね: こんな感じ↓で評価してくれるのか」


同サイトより

「Web評価ツールといえば、日本の官公庁が出してるアクセシビリティチェッカーがあるんですよ: 何て名前だったかな…miChekcer!↓」「へーこんなのがあったとは」「このmiChekcerというツール、恐ろしく古いんですが今もすごく使われているという」「官公庁向けだと使わないわけにいかないんでしょうね😢」「でないと納品できなかったり」「音声読み上げとか、そういうチェックも入ってるのね」「32ビットとか書いてますけど😆」「Javaのサポートが変わったお知らせも書いてある: ああ例のアレ」「ActiveX…だと…?🥶」「そもそもIEが対象だし」「ひえぇ〜」「こういうツールを公のところが出す意義は間違いなくあるけど、その後も更新してくれないとね😅」

参考: ActiveX – Wikipedia
参考: 行政サイトを作る時に気をつけておいた方がいい事 – Qiita

言語

ミューテーション解析とは


つっつきボイス:「ひところ卜部さんがRuby TrunkすごくエッジなバグをRubyで次々にアップしてたことがあったんですが、もしかしてこういうミューテーション解析みたいに、ソースをランダムに機械的にちょっと書き換えてバグをあぶり出すとかしていたのかなと思って」「ああそういえばRubyKaigiでそういうのを自動で回してるみたいなことを言ってたかも🤔」「ミューテーション解析っていう言葉があるというのを今頃知りました😅」「これはまさにソフトウェアテストのコアな手法のひとつ」「パーサーの仕様を把握したら後は総当たりで回して、落ちたところを掘っていくみたいな」

PPL 2019: 第21回プログラミングおよびプログラミング言語ワークショップ


つっつきボイス:「東北地方(岩手県花巻市)で開催されるそうです」「JSSSTということは日本ソフトウェア科学会↓なので普通に学会ですね🧐」


jssst.or.jpより

「ちなみに、日本には日本ソフトウェア科学会の他に情報処理学会と電子情報通信学会という3つの大きな学会があります」「おー知らんかった😳」


ipsj.or.jpより


ieice.orgより

「下の2つは歴史が長くて、情報処理学会は数学寄りでコンピュータサイエンスをガチでやってる」「なるほど!」「電子情報通信学会は名前からも想像できるように通信とか工学系も扱っていて、ネットワークのような下のレイヤにも強い」「へぇ〜」

「日本ソフトウェア科学会は比較的新しくて、いわゆる学術論文になりづらいようなテーマ(何かソフトウェアを構築したとか)でも提出できるところが他の2つと違う特徴ですね」「あーそういう違いですか!」「論文にはなりにくいけど意義のある研究というのはやはりあって、日本ソフトウェア科学会はそういうのも扱う」

「一方他の2つはソフトを書いたというだけじゃ論文や研究として認められないんですね」「下の2つはガチ勢という感じですか」「書きましただけじゃダメと」「学術の世界は、極端に言えば理論があればコードは実際には書かなくてもよかったりするので」「わかるわかる☺️」「せいぜいシミュレーターとか」

「たとえばRubyを作りましただけでは、それは研究ではなくて開発だろとみなされる: 新しいアルゴリズムの研究とか、論文として新規性などを満たさないといけなかったりしますね🧐」「そうすると学生がたとえばRubyで何か凄いものを作ってもそのままではドクターが取れないことになってしまう😆」

「日本ソフトウェア科学会はそういうのも論文として評価することで門戸を開いているので、オープンソース方面と親和性が高いですね☺️」「学術の世界、知らないことだらけでした😅」

COBOLが去りゆく

代わりにPythonが加わるそうです。


つっつきボイス:「CASLを試験に出すぐらいならRuby入れればいいのに🤣」「🤣」「CASL、日本でしか通用しなくて実物もないという🤣」「ベンダに依存しないアセンブラの問題を出すための苦肉の策だったのかしら🤔」「知人はES2015を入れればいいのにと言ってました😆」

参考: CASL – Wikipedia

「Rubyは言語仕様の動きが激しいから試験にしづらいとか?」「でもJIS規格でしたよね?」「ISOも取ってた気が」「お〜どっちもやってる↓」「更新もされてるんだし、Rubyにしとけばいいのに」「まさか試験に出るPythonってPython 2じゃないよね?🤣」「🤣」

参考: JIS X 3017 「プログラム言語Ruby」が改正されました:IPA 独立行政法人 情報処理推進機構
参考: ISO/IEC 30170:2012 – Information technology — Programming languages — Ruby

後日

その他言語

その他

「HRTの原則」とは

「このメソッドの制御フローは完全に間違ってますよ。みんなが使ってる標準的なxyzzyコードパターンを使うべきですよ」

Team Geek p.21

上のメッセージはもっとえげつない感じで訳す方が伝わったのかなと思いました。

なおHRTの原則を見かけたのは永和さんの記事でした。

参考: 知識差−スキル差を埋めるためのペアプロ+αのコツ(2) – 時を超えたプログラミングの道

whatthecommit.com: コミットメッセージをひたすらランダムに表示するサイト


whatthecommit.comより: 「ボクを信じてくれればこんなことにならなかったのに」

以下で見かけました。


つっつきボイス:「リロードするたびに変わりますね」「いわゆる面白コミットメッセージ😆」「どこからクロールしてきたんだろうか😆」「そんなにたくさんはないっぽいですが☺️」

ビデオ会議のノイズキャンセルアプリ


つっつきボイス:「Yasslabの安川さんの記事を見つけたので」「ノイズをキャンセルするアプリってどういう仕組なんだろ?🤔」「なるほど、このパネルからしてスピーカーやマイクのドライバとして選択すると機能するのか↓🎤」「ビデオ会議を頻繁に使うなら便利かも😋」


同記事より

番外

宅配ロボット

参考: Amazonがついに宅配ロボットを開発! 名前は「Amazon Scout」 | ロボスタ


つっつきボイス:「名前はスカウト!」「階段はクリアできないことが確定😆」「道路事情からして日本は蚊帳の外🇯🇵」「弓矢やボウガンを持った一団にヒャッハー🏹される事案がちょっと心配😆」

「でも米国だとこういう無人配達はある意味切実に求められてたりしますよね: 地域によっては他人の敷地にうっかり入ると撃たれることがたまにあったりするし🔫」「そういえば随分昔にそんな事件あった!😢」「宅配便や郵便配達も油断ならなかったりしそう😢」

参考: 日本人留学生射殺事件 – Wikipedia


今回は以上です。

おたより発掘

こちらこそありがとうございます🙇。

バックナンバー(2019年度第1四半期)

週刊Railsウォッチ(20190121)Rails 6.0.0 beta1リリース、Railsは2019年も「あり」か、Jetsでサーバーレス、ES2018の新機能、RSpecの心ほか

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines

Publickey

publickey_banner_captured

DB Weekly

db_weekly_banner

JavaScript Weekly

javascriptweekly_logo_captured

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の監修および半分程度を翻訳、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れて更新翻訳中。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好き。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ

BPSアドベントカレンダー