週刊Railsウォッチ(20181015)Rails初心者と一発でバレる書き方、次のVue.js構想、RubyのOpenStruct、Twilioほか

こんにちは、hachi8833です。Pixel 3がちょい気になっています。

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

つっつきボイス:「お、Pixel 3か、値段の割にストレージが少ないんですよね📱: 今の自分のiPhoneも、いつか見るつもりのNetFlix動画やら音楽やらであっというまにストレージ埋まっちゃうし」「私はWikipedia(日と英)がまるっと入ればいいかな…」「Pixelを見てると、ちゃんとしたスマホを作れば結局このぐらいの値段になるよなって思いますね😎」

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

名前付きスコープに委譲するメソッドを定義のタイミングで生成

名前付きスコープへ委譲するメソッドは、リレーションでmethod_missingが呼び出されるときに定義される。
名前付きスコープ内のレシーバは#29301以降、一貫性のためdefault_scopeなどと同様にリレーションに変更されている。
大半の名前付きスコープはmethod_missingでリレーションから委譲される。というのも、Relationのインスタンスメソッドと競合しないよう#31179でスコープの定義を許さなくなったため。しかし、名前付きスコープと同じ名前のメソッドがスーパークラス(Kernel.openとか)のいずれかのメソッドで定義されていると、リレーションのmethod_missingが呼び出されていなかった。
問題修正のため、名前付きスコープに委譲するメソッドは定義時に生成されるようにする。
同PRより大意

# activerecord/lib/active_record/relation/delegation.rb#L34
+     protected
+       def include_relation_methods(delegate)
+         superclass.include_relation_methods(delegate) unless base_class?
+         delegate.include generated_relation_methods
+       end
+     private
+       def generated_relation_methods
+         @generated_relation_methods ||= Module.new.tap do |mod|
+           mod_name = "GeneratedRelationMethods"
+           const_set mod_name, mod
+           private_constant mod_name
+         end
+       end
+       def generate_relation_method(method)
+         if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
+           generated_relation_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
+             def #{method}(*args, &block)
+               scoping { klass.#{method}(*args, &block) }
+             end
+           RUBY
+         else
+           generated_relation_methods.send(:define_method, method) do |*args, &block|
+             scoping { klass.public_send(method, *args, &block) }
+           end
+         end
+       end
+     end
+   end

つっつきボイス:「generated_relation_methods@generated_relation_methods ||= Module.new.tapでメモ化してから定数をprivateにして渡している」「generated_relation_methods.send(:define_method, method)define_methodを使って定義してるんですね」「難しい…」

参考: instance method Module#define_method (Ruby 2.5.0)
参考: instance method Module#private_constant (Ruby 2.5.0)

関連付けの再読み込み時にQueryCacheをクリアすることで振る舞いを一貫させた

# 「Post --has_many--> comments」「Post --has_one/belongs_to--> author」が前提

# この場合QueryCacheがクリアされ、レコードが読み込まれる
post.reload

# この場合QueryCacheの外でクエリが実行されることでレコードが読み込まれるが
# QueryCacheはクリアされない
post.reload_author

# この場合レコードは読み込まれず、QueryCacheもクリアされない
post.comments.reload

つっつきボイス:「現状が上↑のようになってると」「関連付けがある場合とそうでない場合でのキャッシュのクリア方法を揃えたってことね☺️」「バグではなさそう?」「バグではないと思うけど元の挙動がわかりにくいしバグの温床になりやすいからでしょうね😎: で、以下のように↓キャッシュのクリアを一貫させるようになったと」

    # 以下はいずれもキャッシュをクリアするようになった
    post.reload_category
    post.reload_author
    post.comments.reload

「キャッシュはだいたい難しいですね: 細かく制御しようとするとよくバグるので🕷」

skip-webpack-installオプションを追加

# railties/lib/rails/generators/testing/behaviour.rb#L67
        def run_generator(args = default_arguments, config = {})
          capture(:stdout) do
            args += ["--skip-bundle"] unless args.include? "--dev"
            args |= ["--skip-bootsnap"] unless args.include? "--no-skip-bootsnap"
-           args |= ["--skip-javascript"] unless args.include? "--no-skip-javascript"
+           args |= ["--skip-webpack-install"] unless args.include? "--no-skip-webpack-install"
             generator_class.start(args, config.reverse_merge(destination_root: destination_root))
          end
         end

つっつきボイス:「お、今まではスキップできなかった?」「今までは--skip-javascriptだったのが削除されて--skip-webpack-installに変わったみたいです」「なるほど: skip-javascriptだとJavaScriptを全部無効にするみたいに見えてよくないという考え方もあるし🤔」「Webpackerのインストールが重いから外せるようにしたとか?」「むしろNode.jsなどのJSエンジンが入ってないとrails newに失敗するから、それを回避するためかも: --skip-webpack-installすればJSエンジンがなくても動くんじゃないかな?と予想してみる」

db:migrate:statusDatabaseTasksに引っ越し

# activerecord/lib/active_record/railties/databases.rake#L150
    desc "Display status of migrations"
    task status: :load_config do
-     unless ActiveRecord::SchemaMigration.table_exists?
-       abort "Schema migrations table does not exist yet."
-     end
-     # output
-     puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
-     puts "#{'Status'.center(8)}  #{'Migration ID'.ljust(14)}  Migration Name"
-     puts "-" * 50
-     ActiveRecord::Base.connection.migration_context.migrations_status.each do |status, version, name|
-       puts "#{status.center(8)}  #{version.ljust(14)}  #{name}"
-     end
-     puts
+     ActiveRecord::Tasks::DatabaseTasks.migrate_status
    end
  end

つっつきボイス:「単なる移動にしては、コメントを見るとやけに喜ばれてますね」「『Nice refactor! 👏』とか😊」「気になってたけど後回しにしてた箇所をリファクタリングしてくれてありがとう!みたいな感じかな: お、今までなかったテストも追加されているし: コードの移動だけでこのテストが増えるはずはないから」「あー、移動前はrakeタスクだったからテストがやりにくかったんだ!」「あー、それで喜ばれたのか😃」「移動のついでにカバレッジも増やしてくれたから」

rakeタスクのテストってかなりやりづらいし、タスクをrunするのも遅いし、rakeのテスト書きたくない😆」「ほんにそうですね😣」

『Nice refactor! 👏』でこれを思い出してしまいました↓。

ActiveRecordにconnects_toconnected_toを実装

モデルで複数のデータベースに接続する機能と、それらをブロックで切り替える機能を実装したそうです。

class AnimalsBase < ApplicationRecord
  connects_to database: { writing: :animals, reading: :animals_replica }
end
ModelInPrimary.connected_to(database: { slow_readonly: :primary_replica_slow }) do
  ModelInPrimary.do_something_thats_slow
end

つっつきボイス:「レプリカを参照できるようになった!」「あーなるほど、上のconnected_toは、たとえばこのブロックの中でだけデータベースを切り替えたいときに使うのか😍」「どんな場合でしょう?」「確かにこういうのやりたいことある: たとえばものすごく重いデータベースクエリ(集計クエリとか)を管理画面から出力したいとすると、通常のDBにそういう重いクエリを投げてしまうとフロントまで重くなってしまうんだけど、そういうクエリだけ別の集計用レプリカDBに投げる、なんてことができる」「お〜」「その処理自体は大したコードじゃなくて、その場でちょろっとできればいいのに、これまではそのためにdatabase.ymlを書き換えないといけなかった: でもこれなら不要になるし」「お〜😃」

「DHHの#33877の続きみたい」「その#33877の話を見ると、まさに先週のウォッチで話したマルチDBがらみそのものですね: やっぱりこのあたりはまだまだ変わる可能性ありそう😆」「そういえばプルリクのコメントもめちゃ伸びてますね」

テンプレートやパーシャルのアロケーション情報を表示

変更前:

  Rendering posts/new.html.erb within layouts/application
  Rendered posts/_form.html.erb (9.7ms)
  Rendered posts/new.html.erb within layouts/application (10.9ms)
Completed 200 OK in 902ms (Views: 890.8ms | ActiveRecord: 0.8ms)

変更後:

  Rendering posts/new.html.erb within layouts/application
  Rendered posts/_form.html.erb (Duration: 7.1ms | Allocations: 6004)
  Rendered posts/new.html.erb within layouts/application (Duration: 8.3ms | Allocations: 6654)
Completed 200 OK in 858ms (Views: 848.4ms | ActiveRecord: 0.4ms | Allocations: 1539564)

つっつきボイス:「コンソールの項目が1個増えてAllocations: 6004みたいに表示されるそうです」「ロガーで出てくるヤツ」「このアロケーションってメモリということでいいんでしょうか?」「単位がわからんけどpayloadと書いてあるし、たぶん普通にメモリのバイト数だと思うけどな〜🤔」「ともあれこういう情報が出てくれるとアプリが遅くなったときの原因特定がやりやすくなりますね❤️: アプリのビューがでかすぎてパフォーマンスが落ちたときとか」

Rails

無名コントローラでconcernsをテストする(RubyFlowより)


つっつきボイス:「とてもあっさりした記事ですが」「モジュール名にあるRescuerって英語的にありなんだろか?😆」「造語っぽい響き…と思ったら辞書にあった💦」「モジュールだから-erで終わらせたかったんだろうなきっと🤓」

「この記事みたいな書き方、確かあったはず🤔」「うん、ポイントはRSpecの中でこうやって一時的にコントローラを作れること↓: その中でモジュールをインクルードしておいてそのコントローラをテストする」

# 同記事より
describe BillingErrorRescuer, type: :controller do
  controller do
    include BillingErrorRescuer

    def action
      @account = FactoryBot.create :account
      raise Billing::LimitExceeded
    end
  end

  # 略
end

「元々は記事にあるBillingErrorRescuerモジュールをテストしたいんだけど、モジュールは直接テストできないのでインクルード用に何かダミーのコントローラが欲しい、でもそのコントローラはproductionでは使わないので本編のコードには置きたくない、ていうときにこれを使うんじゃないかな」「なるほど!」「このcontrollerってメソッド、ちゃんとRSpecに生えてるみたいですね: 元々RSpecにあるのかな?🤔」「おー、あったあった↓🎯」「確かにこういうのはRSpecにないとできないヤツ: controller(ApplicationControllerSubclass)みたいに引数も渡せると」

参考: anonymous controller - Controller specs - RSpec Rails - RSpec - Relish

# relishapp.comより
require "rails_helper"

class ApplicationController < ActionController::Base
  class AccessDenied < StandardError; end
end

class ApplicationControllerSubclass < ApplicationController

  rescue_from ApplicationController::AccessDenied, :with => :access_denied

private

  def access_denied
    redirect_to "/401.html"
  end
end

RSpec.describe ApplicationControllerSubclass do
  controller(ApplicationControllerSubclass) do
    def index
      raise ApplicationController::AccessDenied
    end
  end

  describe "handling AccessDenied exceptions" do
    it "redirects to the /401.html page" do
      get :index
      expect(response).to redirect_to("/401.html")
    end
  end
end

「これならモジュールの単体テストがキレイに書けるので、当然これ使う方がいいですね😋: なおこのモジュールは本来はconcernとしてコントローラで使います」

render_async 2.0がリリース(RubyFlowより)

関連記事を昨年翻訳したrender_asyncの2.0では、jQueryが素のJavaScriptに変更され、IEでのエラーハンドリングやイベント発火などに対応したそうです。

Rails: render_async gemでレンダリングを高速化(翻訳)


つっつきボイス:「お、2.0になってjQueryじゃなくなったし👍」「ちなみにrender_asyncがやってることは全然大したことなくて😆、以下のようにerbでrender_async書くとAjaxをロードしてdocument.writeとかができるようになる」「へぇ〜」「古いIEの対応も向上したようです」「render_asyncは、ものすごくアクセスの多いサイトなんかでは効果的だと思います😊: 某漫画サイトとかなら使いたいやつでしょうね😆」「😆」

<-- 同リポジトリより -->
<%= render_async comment_stats_path %>

Screencast: Twilio APIのService Object

Service Objectの話題にと思って取り上げましたが、つっつきは予想を超えてTwilioの話になりました😊。


つっつきボイス:「Twilioは、SMS(ショートメッセージサービス)とかと電話を連携させたりするAPIサービスで、かなり前から有名ですね☎: 確かテキストをXMLで放り込むと音声を再生できるんじゃなかったかな(Programmable Voice)」「知らなかった〜」


twilio.comより

「これも確かXMLで設定を投げると、電話の音声案内によくある『〜の場合は1、〜の場合は2を押してください』みたいなのをやってくれたと思う」 「ふむふむ」「サポセン構築を支えるサービスでもあるんですね」「Twilioは日本法人もあるはずで、(ググると)そうそう、KDDI↓」「確かに電話会社ならやりたいAPIサービスでしょうね」「よくあるSMS認証なんかもこれでさっとやれる: サイトで電話番号を入力するとそこに自動でSMSが送信されるヤツですね」「お〜」「SMS認証サービスは他にもいろんな会社もやってますが、Twilioは随分前からやってる」


twilio.kddi-web.comより

参考: 企業が「SMS認証」を導入するワケ!? | SMS送信サービス 「空電プッシュ」

「お、Faxサービスもある😆」「やっぱ日本に進出するにはFaxもサポートしないとあかんのでしょうね🤣」「未だにFax使ってるなんてもう日本ぐらい🤣」

「そういえばAWSのアカウント作成にもSMS認証使われてますね: アカウントを作ると電話がかかってきて、キーを音声で知らせてくる」「そういえばそう!」「そういうのも含めた文字の音声化や音声の文字化もやれる💪」

「Twilioは老舗なんで、その分APIも割と古い感じだった覚えがあります(今はわかりませんが)」「XML使うあたりがそんな感じですね: 今ならJSONとかYAMLでやりたいでしょうし」「TwilioはWebRTCと連携したり、あと確かSIPサーバーと直結させることもできたりとか、実は割と高機能」「確かに〜」

参考: WebRTC - Wikipedia
参考: SIPサーバ(SIP server)とは - IT用語辞典

「Twilioは日本のバックエンドがKDDIなんで、たぶんめったなことでは落ちないだろうという期待もできるし😎」「ちなみにもしこういう上位のTierで障害が起きたらたぶん日本のインターネットのかなりの部分に影響する💀」「ひょえ~」「Tier 1かどうかはどの地域から見るかで違ってくるところもあるので一概には言いにくいですが、日本のTier 1というとIIJWikipediaを見るとNTTコミュニケーションズがアジア地域でTier 1: お、Tier 1のSprintもソフバン傘下になっているからTier 1か」「それにしてもさっきから障害関連の情報ってなぜかググってもうまく出てこないし…」「もしかして検索結果に出にくくなってるとか😆」

参考: ソフトバンクが最上位インターネットプロバイダ「Tier 1」に:Geekなぺーじ
参考: ティア1 - Wikipedia


Wikipediaより(CC BY-SA 3.0

そういえば子どもの頃読んだ科学読み物で、モナリザの体格や身長から声を推測してモナリザの声をコンピュータで再現するという記事を見たことがあります。当時はまだ大型コンピュータやミニコンの時代だったので大変だったと思います。以下はもっと新しいもののようです。

Active Storageの添付ファイルを削除する(RubyFlowより)


つっつきボイス:「このCodemy.netっていうサイト、意外にscreencastが盛りだくさんですね」「動画の下に解説部分があるからすぐ見えるのがいいな: リスト部分はもっと狭くていいんだけど😆」


codemy.netより

# 同screencastより
class Post < ApplicationRecord
  # ...

  # add :remove_cover_picture to attr_accessor
  attr_accessor :state_event, :remove_cover_picture

  # add purge_cover_picture callback
  after_save :purge_cover_picture, if: :remove_cover_picture
  private def purge_cover_picture
    cover_picture.purge_later
  end

  # ...
end

「へー、Active Storageにpurgeってメソッドあるんだ↑」「そこは自分で実装するんですね😆: carrierwaveならremove_なんちゃらって付ければそう判定してくれるところだけど、そういうのじゃないのか😆」「Active Storageはそこまではやらなさそう」「まあやんなくてもいい気はします🤣」

参考: ActiveStorage::Attached::Many — purge

「removeといえば、carrierwaveだと確かサイズを引数で指定して、そのサイズ以上だったら消すってのができますね」「バージョンとかも引数に渡せたと思います」「Active Storageでもそのうちできるようになるんじゃないかな?なんて」

「ちなみにバージョンを指定して添付ファイルを消したいというのは割と需要がありますね: carrierwaveでバージョン指定することでオリジナルは消さずにサムネイルだけ一括で消すとかしといて、後は新規アクセスでサムネイルが再生成されるようになってればおkみたな」「ソース見てみっか」

「…く、最近Macbookが不調でなかなかIntelliJ IDEAが起動しない〜😅」「そういえば私の古いMacbookをこの間Mojaveにアップグレードしたら↓、Chromeのレインボーカーソルくるくるがピタッと止みました」「自分のMacbook、まだHigh SierraですらないSierraだからな〜、不調の原因それかもしれない…」

Rails: macOSをMojaveにアップグレード後`bundle install`がエラーになった場合の対応方法

「でActive Storageのpurgeメソッドは、と…お、Attached::Oneなんてクラスが😆」「ワン?!」「そういえばActive StorageのAttached::Oneってhas_manyっぽく書けますよね?」「てことはManyもあるってことか: あった🎯」「こいつらはActiveSupport::Autoloadで動いてないから、各自好きにロードして使ってくれってことなんでしょうね」

参考: ActiveStorage::Attached::One < ActiveStorage::Attached
参考: ActiveSupport::Autoload

「で、これか↓」「purgeには引数がないのでActive Storageでは特定のバージョンだけ削除とかはできないということがこれでわかった🧐」「detachってのあるんですね」「attachdetachとあるけどpurgeは実際にファイルを消すところまでやるっぽい」「detachはpurgeまではしないとあるからデータベースから消すだけで、ファイルが残ると」「purgeはデータベースもファイルも消すと」「これは知らないと間違えるヤツ😆」「detachしたけど残ってましたみたいな😆」「両方並んでればdetachの方が弱そうというのは想像つきますけど」

#5-2-stable/activestorage/lib/active_storage/attached/one.rb#L47
    # Deletes the attachment without purging it, leaving its blob in place.
    def detach
      if attached?
        attachment.destroy
        write_attachment nil
      end
    end

    # Directly purges the attachment (i.e. destroys the blob and
    # attachment and deletes the file on the service).
    def purge
      if attached?
        attachment.purge
        write_attachment nil
      end
    end

    # Purges the attachment through the queuing system.
    def purge_later
      if attached?
        attachment.purge_later
      end
    end

「バージョン指定云々はたぶんBasecampで使われてないだけな気がするから、そのうちサポートされるかもね😊」

Railsアプリで実際に起きたセキュリティ問題5つ(RubyFlowより)


つっつきボイス:「実は既に翻訳許可取って、翻訳だけは終わってる状態なので近々公開します😃」

「1.のセッション期限って、Railsがサポートしてなかったっけ?」「期限がものすごく未来の時間に設定されるやつですよね」「Devise使ってるとtimeoutableって指定が使えると」

# 同記事より
class User < ActiveRecord::Base
  devise :timeoutable
end

「お、Rails 5.2ではセッションcookieに期限が含まれるようになってるって記事に追記がある(翻訳したときはなかった)」「そういえば以前のTechRachoで5.2の署名済みcookieが非互換になった云々の話があった気がするけどそれじゃないですか?」「あったかもそういえば」「ともあれ5.2ならひとまず対応済みってことね😊」

↓5.2アップグレードガイドの更新情報に含まれているのを見つけました。

参考: Rails アップグレードガイド | Rails ガイド

セキュリティ向上のため、Railsでは暗号化または署名付きcookieに有効期限情報を埋め込むようになりました。
有効期限情報が付与されたcookieは、Rails 5.1 以前のバージョンとの互換性はありません。
Rails 5.1 以前で新しいcookieを読み込みたい場合、もしくは Rails 5.2 でうまくデプロイできるか確認したい場合は (必要に応じてロールバックできるようにしたい場合は) Rails.application.config の action_dispatch.use_authenticated_cookie_encryption を false に設定してください。
同ガイドより


「2.は何回間違えたらアカウントをロックするか」


「3.は、入力したユーザー名やパスワードが間違ってたときに『そのユーザーは登録されてません』とか『パスワードが間違ってます』みたいな親切すぎるメッセージを出すと、むしろ攻撃の手がかりになっちゃうというヤツですね」「バリデーションで余計な情報は出すべきでないという話: ただね、サービスの種類によっては親切に出してあげないと問い合わせが大量に押し寄せるので、ポリシーをどうするかは頭の痛い問題」「具体的にはどんな感じでしょう?」

「『パスワードの再設定』フォームなんかがそうなんですが、再設定のために入力したメールアドレスが登録済みなので送信しましたというメッセージを出してしまうと、登録してない第三者でもそのメールアドレスが登録されているかどうかがわかっちゃう: ネットには流出したメールアドレスとパスワードのリストを売ってるダークなサイトがいろいろあるんで、それを元に攻撃者が『こいつはいない、こいつはいる』と試していけば存在するユーザー名のリストを作れる: なのでメールアドレスが登録済みかどうかをうかつに表示すると防衛力が落ちるという話」「お〜」

「その意味ではそういう情報を表示しないほうがセキュアなんですが、表示しないと今度は『このメールアドレスは登録されてないって言われちゃったんですけど』みたいな問い合わせが山のようにやってくるという😅: リテラシーの高い人にはわかってもらえるんですけど、世の中そういう人ばかりではないので」「こういうのってメッセージの文面の書き方ひとつで問い合わせが目に見えて増えたり減ったりしますよね☺️」

Rails初心者とバレる書き方

「4.の権限昇格のコード例にあるこの書き方↓、ログインしないとできないはずの操作の中でこれ書いたらコードレビューで100%ツッコまれる」「そういう状況ではidで直接findしてはいけない」「リレーションがあるならそれを使えと: 反論の余地なし」「職務質問もの👮🏼‍♀️」

# 同記事より
Project.find(params[:id])               # あかん書き方

current_user.projects.find(params[:id]) # たとえばこんなふうに書くべき

「もっと言うと、たとえstrong parametersとかも正しく書かれていて、設計も十分吟味されているとしても、current_user.projects.find(params[:id])のように書くべき」「そう!ホントそう!」

「コード例の上と下ではコードが放つ臭いも違ってくるし: 下はどこから取ってきているのかが明らかだけど、上はもしかするとそのユーザー以外のところから取ってきているかもしれないから」

「テストを書くときにも、下は現在のユーザーについてテストを書けばいいけど、上はそれ以外のところから取ってきた場合についてもすべてテストを書かないと網羅できないし」

「上の書き方は基本的にデメリットしかないと思う😡」「同意!」「上はいかにも教科書に書いてありそうですよね」「アクセス制限とかないシンプルな場合なら確かに上のようになっちゃうし、IDEでデータベースを何も考えずに取りに行くと上のになっちゃうんですけどね」

「あと、このあたりはURLの設計にもよるんですよね: たとえばTwitterはログインしていなくてもある程度タイムラインとかを表示できるんですが、そんなふうにログインしてもしなくても同じURLで同じように表示したいときなんかはこういう書き方になることもないわけではない」

current_userみたいなスコープをいちいち介さないといけないのは面倒に思えるかもしれませんし実際面倒ですが、これはやらないわけにはいかないことなので」「たった2行のコードでご飯が三杯食べられそうな話になった😋」

「上はRails初心者にとても多い書き方」「初心者を見つけるのに使えそう」「ずっと前に指摘されたことあった気もする💦」「上の書き方してたら『あ、書き慣れてないな』って即断定する😎: モデルのクソコードは読み込まないと見分けが難しいんですが、コントローラのこういう書き方とか、他にはfreezeのし忘れとかをやってるのはコードレビューで見つけやすい」「こういう指摘は反論しようがないからレビュー戦争になる心配もないし」


「ちなみにCanCanだとaccessible_byっていうスコープが入るから、どのユーザーでも同じように書けるという謎のメリットはありますけどね: 自分はあまり好きじゃないけど」「なるほどね☺️」「あれ、Punditの方が好きかと思ってたけど?」「ん〜どっちもあんまり好きじゃない🤣: CanCanはやってることがいろいろ多すぎてちょっとウザい」「CanCanはビューにも生えてくるぐらい幅広いし: まあ必要ですけどね」「でもビューに寄りすぎるのはちょっとな〜とは思うし」

「確かにCanCanは責務がコントローラからビューまで広がっているのでちょっとつらい: Punditの方は、自分のやりたいことをやろうと思うとん〜?という気持ちになりがちではある」「自由度という意味では、Punditの方がどこまで生やすかを自分で決められるから高いですよね」「Punditは『概念モデルとしては正しい!』みたいな感じで書けるけど、頑張って設計していると結局モデルクラスを増やすしかない気がするんですよね〜: 1個のモデルクラスにいろんな可視性を持ち込むより、別のモデルを作ってそっちで生やす方がいいんじゃないかって」「ちょうど今の案件でPundit使っててつらみ理解できる😢」「Punditの方が原理主義っぽいから性に合うかと思った😆」「権限とかなくていいし🤣」


「最後の5.はweak password問題」「keypass、どっかで一度使おうとした覚えがあったけど結局1passwordにした」「1password、サブスクリプションに変わって買い切りできなくなったのが悔しいです😭」「有料だけど、もう自分も1passwordにロックインされちゃったしな〜😆」


1password.comより

「パスワードチェッカーって実装する側にとっては割と厄介ですよね: さっと導入できればいいけど、バリデーションで通らなかったパスワードをサーバーに投げないかどうかって意見が分かれるところだし」

「ところで、サイトによってはチェックが恐ろしく厳しいところがあるじゃないですか: 英大文字小文字と記号と数字を全部含めろぐらいならまだしも、ユーザー名と同じ並びの文字列が何文字以上あったら他がセキュアでも却下するところとか🔐」「あるある〜😅」「しかも記号や数字が固まってたらダメで、散らさないといけないとか」「あれはホントつらいし、それならパスワードを30文字にしろと言われる方がマシ😤」「あ、残り時間が🕑」「巻きで行きましょ〜」

Passengerでプロセスをオーケストレーションする


同ブログより


つっつきボイス:「Kubernetesを使わずにKubernetesみたいなことをする?」「英語タイトルからはわかりにくいんですが、PhusionなのでPassengerの記事でした: Hongliって何だろうと思ったら中国の企業で、Phusionがそこのお手伝いをしたようです」「最初の2つの図はPassengerで、3つ目のこれ↓がオーケストレーションの概略か: お?図だけ見ると、フロントのサーバーが複数あってもそれをPassengerに集約できるってことなのかな?もしかしたらPassengerにもクラスタ的なプロダクトがあるのかも🤔」


同記事より

Itamae


つっつきボイス:「Itamae、最近あまり見かけてない気もするけどコミットは着実に行われてる」「chefのオルタナティブですね」「クックパッドさんのgemだから当然そこでは使ってるだろうけど」「むしろ最近serverspecを見かけない気がする: たぶんDockerが普及したから🐳」「そういえばそうかも」「serverspecを一生懸命書く時間があるなら、Dockerでアイソレーションする方が考えることが減るだろうし」


itamae.kitchenより


serverspec.orgより

その他Rails



つっつきボイス:「速さが自慢というfast_jsonapiは以前のウォッチでも取り上げましたが一応」「Netflixはオープンソースでもいろいろ公開していますね」

↓こちらはつっつきの後で見つけました。

Ruby trunkより

String内部表現にUTF-8 BOMを含めるべきでない

BOM入りUTF-8が許容されているために、BOM入りCSVファイルでトラブったそうです。以下が回避方法だそうです。

# 同issueより
IO.read(mode:'r:BOM|UTF-8')

つっつきボイス:「あーBOM☺️」「BOMキライ😭: むかーしにUTF-16の納品ファイルにBOMありのとBOMなしのとあってスクリプトで外したりしないといけなかったことがありました」「そういうのこそCIでチェックすべき🧐」「CSVでBOM踏んだことあったナ😤」「エンジニアなら一度はBOMを踏むし😆」

参考: バイトオーダーマーク - Wikipedia

「Wikipedia見ると、UTF-8の場合BOMは本来不要だし必須ですらないのに、あっても許容されているそうです😢」「そうそう、たまにわざわざ付けちゃう人いるんですよね」「みんな、UTF-8にBOM要らないからね〜!」

リクエスト: Net::HTTPでHTTPSのSNIにserver_nameが欲しい


つっつきボイス:「あー、HTTPS接続のときはSNIで使うserver_nameが欲しいってことか: そういえばRubyコアライブラリのNet::HTTPっていつの間にかHTTPS対応してますね」

参考: Server Name Indication - Wikipedia

Ruby

HTTParty gemでログを出力する方法

# 同記事より
def GoogleGateway 
  include HTTParty 
  logger Rails.logger, :info, :apache 
  ... 
end

つっつきボイス:「HTTPartyは割と有名なRuby製HTTPクライアントソフトウェア: faradayなんかと並ぶ」「もっと好きなHTTPクライアントがあるって以前おっしゃってましたが何でしたっけ?」「自分が好きなのはhttpclient❤️」「そうでした: 以前のウォッチで話が出てた」

httpclientは重要な情報がREADMEではなくドキュメントにあるんだそうです。

Rubyのメソッドでdestructureする

# 同記事より
destructure def adds(a: 1, b: 2)
  a + b
end
adds(a: 1, b: 2)
# => 3
adds(OpenStruct.new(a: 1, b: 2))
# => 3
Foo = Struct.new(:a, :b)
adds(Foo.new(1,2))
# => 3

つっつきボイス:「Rubyって無茶して遊ぶ人がいろいろいるのが微笑ましいというか☺️」「遊ばれる言語🤓」「で、上のOpenStructって初めて見るけどコレナニ??」「いる、いるぞ、オレオレクラスかと思いきや標準ライブラリにいるぞ↓」「オープンクラスになってるStructってことか」「えー!?🤯」

要素を動的に追加・削除できる手軽な構造体を提供するクラスです。
docs.ruby-lang.orgより

参考: class OpenStruct (Ruby 2.5.0)
参考: class Struct (Ruby 2.5.0)
参考: Ruby のオープンクラスとは (メタプログラミングRuby)

「て、手軽ってw」「ハッシュよりは手堅くていいかも?」「データ構造が違いますけどね😎」「destructureメソッド↓はどうやらStructでdestructするってことか」

# 同記事より
def destructure(method_name)
  meta_klass  = class << self; self end
  method_proc = method(method_name)
  unless method_proc.parameters.all? { |t, _| t == :key }
    raise "Only works with keyword arguments"
  end
  arguments = method_proc.parameters.map(&:last)
  destructure_proc = -> object {
    values = if object.is_a?(Hash)
      object
    else
      arguments.map { |a| [a, object.public_send(a)] }.to_h
    end
    method_proc.call(values)
  }
  meta_klass.send(:define_method, method_name, destructure_proc)
  method_name
end

「定義したdestructure DSLを使うとこんな書き方できるのかっ↓😆」「😆」「このdefはキーワードじゃないんでしょうか?」「たぶんdef adds()が先に評価されてそれがdestructureに渡されるんでしょうね」「あーそっか!😳」

# 同記事より
destructure def adds(a: 1, b: 2)
  a + b
end

「そういえば確かjoker1007さんがこれと形が似た感じのgemをRubyKaigiで発表してましたよね: アブストラクターとかなんとかいう名前」「何て名前だっけ?」(一同ググる)「abstrikerだっ!!」「そうそう、abstract def fooみたいなDSLを追加するヤツ: defの定義がメソッド名のシンボルを返すのを利用して、defの手前にabstractっていう、Javaで使われているような構文が使えるようになって抽象クラスを定義できるという」「そこまでして抽象クラスにしたいかな〜?😆」「ま、ま、こういう仕組みを知ってるとより一層楽しめますよね😋」

# 同リポジトリより
class A1
  extend Abstriker

  abstract def foo
  end
end

class A3 < A1
  def foo
  end
end # => OK

class A2 < A1
end # => raise

Class.new(A1) do
end # => raise

hatefreeweb-gem: ヘイトスピーチ検出API gem(RubyFlowより)

# 同リポジトリより
require 'hatefreeweb'

client = Hatefreeweb::Client.new("Your API KEY HERE")

detection = client.detect("This content is fortunately clean!!","en")
-> 0
detection = client.detect("Here the tone has suffered because of some krauts!!","en")
-> 1

つっつきボイス:「まだ★はゼロですが、変わり種ということで」「このgemでヘイトスピーチを検出するのかと思ったら、hatefreeweb.org↓っていうサービスのAPIを使うのね」「あ、ほんとだ」「このサイトがどのぐらい使われているかはわかりませんが😆」「EmotiScoreなんてのも」「どのぐらいキラワれてるかもわかるんでしょうね☺️」


hatefreeweb.orgより

「過去形で言っちゃいますけど、以前こういうサービスってすごく流行ったんですよね: Twitterでポジティブなコメントやネガティブなコメントを検出してクラスタリングして、ユーザーの気分を探ろう!みたいなのがひと頃の学生の研究発表で山ほどありました😆」「😆」「だいたいみんなおんなじこと考えてる😆」「ひととおり終わった分野🏁」

csvreader: ゼロコンフィグを目指すCSVリーダー(RubyFlowより)

# csv11/csvreaderより
txt <<=TXT
1,2,3
4,5,6
TXT

records = Csv.parse( txt )     ## or CsvReader.parse
pp records
# => [["1","2","3"],
#     ["4","5","6"]]

# -or-

records = Csv.read( "values.csv" )   ## or CsvReader.read
pp records
# => [["1","2","3"],
#     ["4","5","6"]]

# -or-

Csv.foreach( "values.csv" ) do |rec|    ## or CsvReader.foreach
  pp rec
end
# => ["1","2","3"]
# => ["4","5","6"]

「csvいれぶん!っていうユーザー名が気になる😆」「integerとかdateみたいに型推論するようです」

「リポジトリにcsv11.github.ioというリンクがあるんで開いてみた」

「ん?これって公式…じゃないよね?」「この縦を揃える書式、どっかで見たことがあるナ」「『CSV Evolved (for Humans) – Easy-to-Write, Easy-to-Read』ってあるから独自なのかな?🤔」「どうもそうみたい」

# 同サイトより
ID,Name,Code,Area,Pop
ca,Canada,CAN,9984670,34278406
us,United States,USA,9629091,314167157
mx,México [Mexico],MEX,1972550,112322757
...

vs

#####################
# North America

# area (in sq km), pop(ulation)

ca, Canada,          CAN,   9 984 670 km²,  34 278 406
us, United States,   USA,   9 629 091 km², 314 167 157
mx, México [Mexico], MEX,   1 972 550 km², 112 322 757
...

個人的にはシンパシー感じます。

>その他Ruby


つっつきボイス:「そうそう、Subversionだと慣例的にtrunkが使われていたけどGitだとmasterなんですよね」「名前変えるのってそんなに大変なんすかね?」「賛同しない人もいそうだし、Ruby trunkって名前も既にあちこちにあるから、そういうのを全部変えて回るのは大変かも」「なるほどなるほど☺️」

「もしかしたら昨今のポリコレの影響でGitが将来改名を迫られたりとか😆」「昔はマスター/スレーブだったのがクライアント/サーバーになったみたいな😆」「主人と奴隷」「まあGitでいうmasterはそっちの意味のマスターではないと思いますが」「master意味多すぎ😢」

「素朴な疑問だけど、マスターテープみたいな『オリジナル/原本/原盤』的な意味のmasterって、マスター/スレーブのmasterと元は同じなのかな?」「反対語のスレーブを仮定しないmasterってことですよね?そういえばどうなんだろう…音楽だとマスターテープ(原盤)とかゴールデンマスターという言葉がありますけど」「元は別なんじゃないかなという気もしますね」「それで言うとGitのorigin/masterって見ようによっては意味がかぶってるのかな🤣オリジナル/オリジナルみたいな」「🤣」(しばらく一同で辞書を引きまくる)

「trunkも辞書引くといろいろ意味があるけど、Subversionの場合は『(木の)幹』なんでしょうね」「そういう意味だとtrunkは語義的にはGitにはふさわしくないのかも?: Gitに幹がありまっか?という話で」「確かに『Gitは本来すべてのブランチが平等であるべき』という考え方ですしね」「その意味ではブランチ名はGitの慣例に従ってmasterにするけど、意味的にはtrunkという決定は何となくワカル」「運用上は変わらないでしょうし」「origin/trunkなら意味的にも筋は通っていそうだし」

「(初参加の社員に)つっつきではだいたいこんな雑談をよくやってます😆」

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

サーバーレスの秘訣(Serverless Statusより)

# 同記事より
$ echo "s3cr3t" | gcloud kms encrypt \
    --location=global \
    --keyring=serverless-secrets \
    --key=app1 \
    --ciphertext-file=- \
    --plaintext-file=- \
    | base64

CiQAePa3VEpDBjS2acf...

つっつきボイス:「割と普通の記事かなと思いつつ」「AWSのIAMや環境変数の話の他にGCPも扱ってると」

「そうそう、IAMとKMSってAWSとGCPのどちらにもあるのが意外に厄介なんですよね〜😅」「言われてみれば😳」「固有のサービス名じゃないんだ?!」「なのでIAMとかKMSの話をしているときって、たいていはAWSの方なんだけど、たまにGCPの話をしている可能性もあるので油断ならない😣」「用語とか普通名詞に近い感じですね」「まあ役割も一緒だからいいんですけど」

参考: AWS Identity and Access Management (IAM - ユーザのアクセスを安全に制御)| AWS
参考: Cloud IAM - Identity & Access Management  |  Cloud Identity and Access Management  |  Google Cloud
参考: AWS Key Management Service (暗号化キーを簡単に作成・管理 - KMS)| AWS
参考: Cloud Key Management Service  |  Cloud KMS  |  Google Cloud

「ただ、AWSは『IAMでIAMユーザーのアカウントを作る』のに対し、GCPの場合は基本的に『IAMでアカウントに権限を付与する』だったと思う」

後で調べると、GCPの場合は個々のエンドユーザーではなくアプリケーションに属する「サービスアカウント」を作成できるのだそうです。

参考: サービス アカウントの作成と管理  |  Cloud Identity and Access Management のドキュメント  |  Google Cloud

IAMからは離れますが、GCPは最近「Cloud Identity」というソリューションを発表し、これを使うとG-Suiteを使ってない個人用Gmailアカウントのユーザーも一元管理できることもついでに知りました。

参考: Cloud Identity への移行  |  Cloud Identity and Access Management のドキュメント  |  Google Cloud

AWS AmplifyとサーバーレスVueアプリ(Serverless Statusより)


aws-amplify.github.ioより


つっつきボイス:「またAWSの新しいサービスかと思ったらJavaScriptのライブラリだそうです」「AWS公式?」「だと思うんですが、上のサイトが微妙にパチもんっぽいデザインで考え込んじゃいました😅」「色とかね☺️」

AWSのブログでもAMplify CLI Toolchainのアナウンスがあり、内容やリポジトリからして公式の位置づけのようです。

「Amplifyって名前わかりづらすぎるでしょ😆」「AMPよりはマシだと思います😆」「なるほどわからん😆が、ググって出た記事↓を大急ぎで読むと、AWSのサービスとの連携の他にJSの拡張とかも含めてAWSと強結合した形で提供するライブラリ、ってことなのかな🤔: AWSと強結合するなら公式だろうし、自分は使わないだろうけど、まあいいんじゃないかと☺️」「もしかするとFirebaseと勝負しようとしてる?🚒」

参考: AWSの次世代JavaScriptライブラリ「AWS Amplify」の概要とReactアプリに導入する手順 #serverless #adventcalendar | DevelopersIO


firebase.google.comより

SQL

PostgreSQLのソートのパフォーマンス改善(Postgres Weeklyより)

-- 同記事より
test=# explain analyze SELECT * FROM t_test ORDER BY x;
                                QUERY PLAN
--------------------------------------------------------------------------
Sort (cost=804270.42..816770.42 rows=5000000 width=11)
     (actual time=4503.484..6475.222 rows=5000000 loops=1)
     Sort Key: x
     Sort Method: external merge Disk: 102896kB
     -> Seq Scan on t_test (cost=0.00..77028.00 rows=5000000 width=11)
        (actual time=0.035..282.427 rows=5000000 loops=1)
Planning time: 0.139 ms
Execution time: 6606.637 ms
(6 rows)

つっつきボイス:「PostgreSQLは例によって正攻法でやってけばいいんじゃないかと☺️: クエリプランも読みやすいし」「ぽすぐれのクエリプランは、MySQLほど勘に頼らなくていいし😎」「そういえばうちらのチームにOracleを10年やってる人がいることがこのたび判明しましたね💪」「Oracleチョットワカル😆」「PostgreSQLだとEXPLAIN ANALYZEで実際に動かした結果を取れるのがいいですよね: あれってMySQLにはなかった気がしますが」「MySQLだとコストは取れなかったかも」「PostgreSQLのEXPLAINEXPLAIN ANALIZEって実際に動かすかどうかという違いがありますよね」「クエリオプティマイザを通すだけか、クエリオプティマイザを通した上でさらにコストを比較するという違い」

参考: 50.5. プランナ/オプティマイザ

「ぽすぐれはそこを比較できるのがすごく重要: だからこそ結果を見てクエリオプティマイザがの最適化がイマイチだなと気づいたらたとえば手動でFORCE INDEXしたりするなどの手が打てる😍」「MySQLだとそこが勘になりがち😆: 本当は勘じゃないにしても複雑になると勘を働かせないといけなくなる」

参考: 14.1. EXPLAINの利用

JavaScript

Vue.jsの次の構想(JavaScript Weeklyより)


vuejs.orgより


つっつきボイス:「Vue.jsは今後こうなる的なロードマップのようです」「フロント勢はjQueryに死んで欲しいという流れにほぼなりつつあるけど、その先の軽いJSライブラリの当てがあるかというと、ね☺️」「明日はどっちだ」

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

CSS Gridプロパティの覚え方(Frontend Focusより)

Understanding the difference between grid-template and grid-autoという記事もありました。


つっつきボイス:「あーこういうヤツね↓」「CSS Gridプロパティは、覚える価値はあると思う🧐」「たぶん書きながら覚えるのが一番早いっすね」

See the Pen Explicit Grid properties by Zell Liew (@zellwk) on CodePen.

「そうそう、Gridはこういう書き方ができるのがいい↓」「おぉ〜😳」

.grid {
  grid-template-areas: "header header header header"
                      ".      main   main   .     "
                      "footer footer footer footer";
}

言語よろずの間

書籍『The Art of Prolog』が無料ダウンロード

https://twitter.com/tyru/status/1050172005645922304


つっつきボイス:「ツイートのリンクがそのまま出てますけど😆」「そうなんですよ😭: ユーザー名にアンダースコアが入っているとWordPressが埋め込みに変換できない」

「それにしてもProlog」「ひっさびさに名前を見かけました」「学生のときに一瞬やった気がする」「何というか、関数型言語が台頭するより前のコワモテな言語というイメージ😨」「Prologはやったことなし」

参考: Prolog - Wikipedia

「そういえば最近授業のために作ったJavaScript講習スライドで、『手続き型でない言語』の例をいろいろ調べてたらちょうどProlog(論理型言語)が出てきた」「そういえばそうだった!」「ついでにSQLも非手続き型言語(宣言型言語)だというのも久しぶりに思い出したし」「あー確かに〜!」「あまりに普段からSQL使っているんで気にしてなかった😅」

参考: 非手続き型言語 - Wikipedia

「『The Art of Prolog』は無料でダウンロードできるそうです: 初版が1980年代みたいですが」「表紙の浮世絵が渋い」「何となく品川の宿に見える」「左上に『品川』って書いてあるし」「ほんとだ😅」

参考: 品川宿 - Wikipedia

後で探したら、さすがMatzがPrologを押さえてました↓。Erlangに影響を与えたんだそうです。

参考: Rubyist のための他言語探訪 【第 13 回】 Prolog

その他

awesome-vscode


同リポジトリより

GitHubにあるawesome何とかを載せるのは何となく悔しいのですが、えらくパッケージが増えてるので。


つっつきボイス:「最近VSCodeって快進撃ですね」「VSCodeはフロントエンジニアにかなり人気が高いらしい」「それにしてもすごい量のパッケージ」

短時間の観測で将来を高精度に予測?

1mm×2mmのスパイハードウェア

このサイズで成立するのか不思議です。

参考: 中国軍がSupermicro製マザーボードにスパイ・チップを製造段階で仕込んだという仰天報道がもたらしたものとは? - GIGAZINE


つっつきボイス:「そうそう、これどこまで本当に可能なのか謎: Supermicro自身も否定してるし」「スパイチップ、めちゃ小さいけど端子が6つある」「そもそもこのチップにどうやってアタッチするのかがわからないし、ちょっとやそっとじゃこういうことはできないと思うんだけどな〜」

「ちなみにSupermicroといえば自分も昔あこがれてたサーバー向けのハイエンドマザーボードメーカー」「これは知らんかった😳」「何しろマザボだけで10万円、しかしとても安定しているという評判」「そんなに凄かったんだ…」

参考: Supermicro - Wikipedia

番外

世界で最も正確な原子時計

参考: 「世界で最も正確な原子時計」を作る研究者に話を聞いたムービーが公開中 - GIGAZINE

そういえば東大でもこのストロンチウム光格子時計を研究してるそうです。従来のセシウム原子時計よりも破格に精度が高いらしいので、身の回りの物体の運動でも相対性理論を検証できるレベルみたいです。早く自転車に積んでみたい。

参考: 原子時計 - Wikipedia


今回は以上です。

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

週刊Railsウォッチ(20181001)Railsアップグレード記事と各種支援ツール、CLI向けRubyワンライナー集、Rubyアプリをワンバイナリ化ほか

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

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

Rails公式ニュース

RubyFlow

160928_1638_XvIP4h

Postgres Weekly

postgres_weekly_banner

Serverless Status

serverless_status_banner

Frontend Focus

frontendfocus_banner_captured

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探訪シリーズ