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

週刊Railsウォッチ(20180903)次世代アップローダーgem「Shrine」、RSpecをどこまでDRYに書くか、Rubyのmainオブジェクトの秘密、GitLabのCookie利用許諾機能はエライほか

こんにちは、hachi8833です。少し涼しくなったらなぜかGobyのコミット頻度が上がってきました。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを社内有志でつっついたときの会話です👄

第2回「週刊Railsウォッチ 公開つっつき会」いよいよ今週木曜開催

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

rake initializersタスクを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エラーが正しいっすね💪」

空のトランザクションからBEGINCOMMITを除外

トランザクションでクエリが実行されずに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

参考: マテリアライズドビュー - Wikipedia

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したらそもそも遅くなるし😎」

RailsのObject#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

ActiveRecordの便利機能previous_changes


追いかけボイス:「wasが名前変わった話知ることができてちょうど良かった〜」「タイムリーでしたね😋」

参考: Rails 5.1 で attribute_was, attribute_change, attribute_changed?, changed?, changed 等が DEPRECATION WARNING - Qiita

上のPRの下の方でこの記事↓がよくまとまってると紹介されてました。

参考: Cleaning Up: ActiveRecord::Dirty 5.2 API Changes - The Lean Software Boutique

Railsは2018年の今も現役か?

「何がむかつくって、グローバル変数を普段ほいほい改変しておきながら『AIが世界を支配する』とかぬかす開発者だよ」

同記事より


つっつきボイス:「めちゃ長い記事なんですが、既に翻訳許可を取って今半分ぐらい翻訳しているところです: 結論だけ言うと『私は今後もRubyとRailsを使う』でした☺️」

DHHのインタビューPodcast


つっつきボイス:「例のYouTubeチャンネルじゃなくて?」「音声だけのインタビューで文字起こしもありません😅」「じゃあ頑張って聞くしかない😆」「やっぱりダルいのでapp.voicebase.comというサービス↓に試しに流し込んでみたら思ったよりよくできてました: 機械起こしの文字をクリックするとその位置にジャンプ再生できるので少々の聞き取りミスがあっても気にならないし」「こういうのいいっすねー😋: ディクテーションの練習になりそうだし」「これ日本語でできたら最高😃」


app.voicebase.comより

torque-postgresql: RailsでPostgreSQLの高度な機能を使うgem(Ruby Weeklyより)

# 同リポジトリより
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本目の記事はすごく長い論文で、10年ぐらい前に翻訳されたものです。


元記事1より


つっつきボイス:「上述のIs Rails still relevant in 2018 ?の中で言及されていて知りました」「DCIって何の略?名前ぐらいは聞いたことあるけど」「えーと『データコンテキストインタラクション』: MVCアーキテクチャに対するものとして説明されている感じ」「ははぁ、こういう図ね↑コントローラとモデルをコンテキストとして扱う、でモデルの本体がオブジェクトなのか: でコンテキストはシナリオレベルで作ると↓」

  • データ:これはドメインオブジェクトの中にあり、ドメインクラスに由来している。
  • コンテキスト:要求に応じて、アクティブなオブジェクトをシナリオにおけるポジションに位置づける。
  • インタラクション:ロールの観点からエンドユーザのアルゴリズムを記述するもの。ロールもアルゴリズムもエンドユーザの頭の中に存在する。
    元記事1より

「腰を据えて読まないと評価しようがないけど、大規模で複雑なアプリをユースケースからブレイクダウンして実装する場合によさそうに見える🧐」


おまけ: 同じ記事OODDという初めて見る略語から以下のコワモテ動画にリンクされてました。英BBCの番組というのがまた不思議。

参考: Object Oriented Versus Functional

「なぜかググってもOODDの略語の意味がなかなか見つからなくて、こちら↑でやっと『Object-Oriented Decomposition and Design』だとわかりました: それに賛同しているっぽいElegant Objectsというサイト↓はnull禁止/ゲッターセッターコンストラクタ禁止とか主張が強めな感じでちょっとビビりました💦」「こういうのは同じようなものにいろんな人がいろんな名前を付けたりしますからね〜☺️」


elegantobjects.orgより

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超えてるから使っている人はけっこういそう」


shrinerb.comより

「↓こういう感じか: プラグインを使えたりフォームを拡張したりできる」

# 同リポジトリより
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(神社仏閣)って名前も面白いっすね」「ところで元記事を見ると『CarrierWaveRefileDragonflyは使ったことないのでわかりましぇん』とあるし🤣」「そこだけ投げやり🤣」「まあShrineから先に覚えたらCarrierWave欲しくないかもね☺️」「Dragonflyってまだあったとは: 一応更新されてるけどそんなにメンテされてないかな〜」

Shrineに今週の⭐を進呈したいと思います。おめでとうございます。

GitLabのCookie利用許諾機能はエライ


つっつきボイス:「あーGitLabのこれね↓: 例のGDPR対応のときからやってる」「Cookieをこれとこれになら使っていいよ、とユーザーが選択できる: これはほんとエライ」「さすが丁寧なご挨拶」「GitLabとしてはライバルのGitHubにこういうところで差をつけたいだろうし🤣」「🤣」「『うちはあそこより正しい』を強調しすぎたら逆効果でしょうけど🤣」「これが当然という感じで黙々とやっていくでしょうね」


gitlab.comより

「Cookieをこう使っていいですか宣言って今後普及しそうですね」「いやマジで、こういうのがgemとかJSのライブラリとして提供されてもいいぐらい: Cookieでやりたいことなんてどこのサイトも基本同じなんだし」「フォーマットも同じでいいんだし」「これは共通化すべき」

Railsアプリと技術ブログにGDPRコンプライアンスを追加する(翻訳)

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

Ruby: メタプログラミングに役立つフック系メソッド(翻訳)

「Roseメモ化」でモジュールの一部だけをincludeする(Hacklinesより)

# 元記事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 2.5.0)

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じゃないものを動かすみたいなやんちゃしたいときとかに使えそう❤️」

Ruby 2.5.0はどれだけ高速化したか(翻訳)

transducers-ruby: Rubyでトランスデューサー

こちらの記事↓を見て見つけました。JavaScript版のトランスデューサー解説記事をRubyで書き直したいというリクエストです。


つっつきボイス:「上の記事のそのまたリンク先の記事↓にあったトランスデューサーの解説図↓がちょっと面白いかも」「わかったようなわからないような🤣」「たぶん上はシーケンシャルな処理で、下は並列化可能という感じなんでしょうね」



同記事より

「トランスデューサーって、どこかApacheのHadoopを思わせるものがある: map/filter/reduceってまさにHadoopっぽいし、reducerって言ったりするし」「おー」「Hadoopもそのぐらいしか知りませんけど😆」

参考: Apache Hadoop - Wikipedia


Wikipediaより

「そういえばRubyだとinjectreduceって同じですよね」「たぶん他の言語屋さんならreduceを使いたいんじゃないかな〜?: Matzがinjectという言葉を使ったのは何かの言語の影響だった気がする」「そういえばRubyスタイルガイドではSmalltalkの影響ってあった」

Rubyスタイルガイドを読む: 文法(8)配列や論理値など

「話ちょっと逸れますけど、Brandon Weaverさんのこの翻訳記事↓のタイトル、元は『Reducing Enumerable』なんですが日本語タイトルすっごく悩みました😅: 本文でもreduceが掛けことば的に使われまくってるし」「reduceで繰り返しを減らそう、みたいな😆」「英語でうまいこと言ってるタイトルを訳すのは大変そう😆」

Ruby: Enumerableを`reduce`で徹底理解する#1 基本編(翻訳)

「あとBrandon Weaverさんは関数型言語ラブの人なので、絶対にメソッドと言わずに関数と言う」「メソッドはあくまでクラス型オブジェクト指向の用語ですね🧐」「あ〜なるほど」

問題「Rubyのクラス名とファイル名は合わせるのか」(Hacklinesより)


つっつきボイス:「答えは『Railsでは合わせるけどRubyは別にそうではない』という」「まあRailsもオートローダーを使わなければクラス名とファイル名はいくら違っててもいいんですけどね😎」「そうそう: オートローダーが認識するためだけなので☺️」「Javaはそこが厳しいんですよね?」「Javaはクラス名とファイル名の一致が必須: 一致してないと読み込んでくれない」「そのぐらいは強制でもいいんじゃね?とは思うけどねっ☺️」

その他Ruby

クラウド/コンテナ/インフラ/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ならスペックにもっと余裕あるものが使われてるし」「仮に自分のマシンでハイバネーションを毎日やったりしてたらあっという間に底をついちゃいますけどね」「よかったー、ハイバネーションはしてない😂」「後は毎日ディスクを動画でぎっしり埋めるとかそういうことしてなければ😎」

参考: ハイバネーション - Wikipedia

JavaScript

lazyestload.js: viewportまで読み込みを遅延するライブラリ(Frontend Focusより)


同リポジトリより

// 同リポジトリより
img {
    transition: filter 0.3s;
}

img.lazyestload {
    width: 100%;
    filter: blur(8px);
}

つっつきボイス:「最上級😆」「あんまりlazyにすると今度は遅くなりますけどね🕶」

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

Let's Encryptの快挙


つっつきボイス:「もうHTTPSが70%近い😍」

続編: Let’s Encrypt・激安SSL・AWS Certificate Managerの比較と注意点

「結合テストと呼ぶのをやめた話」

フロントエンドのパフォーマンスチェックリスト(Frontend Focusより)


同リポジトリより

その他CSS/HTML/フロントエンド/テスト

言語よろずの間

Go 2でGenerics導入の検討始まる


つっつきボイス:「Go言語のバージョン2はやっぱりGo 2と呼ばれるみたい: このダジャレが言いたかったに違いないと睨んでます☺️」

go-to
{形-1} : 頼りになる
{形-2} : 〔チーム・組織などの〕主力の、大黒柱の

その他言語よろず

その他

コードには癖が残る


つっつきボイス:「元記事はマルウェア作成の犯人を捜すためみたいですけど、言われてみればコードでも普通の文章でも人物特定は十分可能だなって」「やーこれは全然あるある: 自分のコードも特徴ありありだし☺️」「ボクもー😜」「git blameする前にだいたいわかっちゃうとか」「さすがに1行だけ見せられてもわからないけどねー😆」

参考: プログラムのコードには、個人を識別できる“指紋”が残されている:研究結果|WIRED.jp

RISC-Vを一晩で実装

まだまだやる気満々のロードマップもあります。


同リポジトリより

参考: RISC-V - Wikipedia

番外

眼球を3Dプリンタで

参考: 3Dプリンターで完璧に機能する目「バイオニックアイ」を作り出す研究 - GIGAZINE


つっつきボイス:「今や航空機のパーツでも3Dプリンターで作れますね✈️」

いいこと聞いた


つっつきボイス:「最後は珍しく実用的な情報で」「そうそう、これチャ~ンス!😋」「ざわっ😋」「きら〜ん✨」「『供出』って言葉が戦時中に釣り鐘とか徴用しまくったときみたいで🤣」

参考: 金属類回収令 - Wikipedia
参考PDF: 小型家電リサイクル対象品目(28品目)


今回は以上です。今週の公開つっつきでお会いできるのを楽しみにしております。

おたより発掘

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

週刊Railsウォッチ(20180827)Ruby Prize 2018募集開始、Interactor gemとReader Object、書籍『Real World HTTP』、Basecampのヒルチャート機能ほか

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

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

Rails公式ニュース

Ruby Weekly

Hacklines

Hacklines

Postgres Weekly

postgres_weekly_banner

Serverless Status

serverless_status_banner

Frontend Focus

frontendfocus_banner_captured

JSer.info

jser.info_logo_captured


CONTACT

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