週刊Railsウォッチ(20190729-1/2前編)Rails 6のリリースは近そう?、Evil MartiansのRails+Docker記事、Railsパフォーマンス測定ほか

こんにちは、hachi8833です。夏がやってまいりました。

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

週刊Railsウォッチ「公開つっつき会」第13回のお知らせ(無料)

1年目を越えた第13回目公開つっつき会は、8月1日(木)19:30〜にBPS会議スペースにて開催されます。引き続き皆さまのお気軽なご参加をお待ちしております🙇。

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

今回は公式情報からです。


つっつきボイス:「多くの人はrc2が取れてから使いはじめてバグを踏むから😆、rc2で動かして見つけてくれないと出ちゃうよ、と」「rc2と最終リリース、ほぼほぼ変わらないですし☺️」

「この後でも取り上げますが、Rails 6が近々リリースされるかもしれないそうです」「お、そういえば7月末だか8月第一週ぐらいにリリースされるかもって聞いたし」「いよいよ出るのか〜(感無量)」「6.0の後早々に6.0.1を出さずに済むためにも、やれる人は今のうちにrc2でやってみましょう〜☺️」「社内プロジェクトでも上げられそうなものがあったらやってみますか😆」

なお、以下はつっつき後の日曜のツイートです。どうぞお大事に。

「6.0の新機能といえば、Action TextにAction Mailbox、それとツァイトなんとか😆」「Zeitwerkですね」「Action Textあまり語られてない感が😆」

Rails 6 Beta2時点のZeitwerk情報(要訳)

Active Storageにファイルを追加するとhas_many_attachedの元からあったファイルを消すかどうかの振る舞いを選択可能に

期待する振る舞い:
元のファイルアップロードは保存されるべき。上の再現手順の最後のステップでは、モデルのインスタンスには元のファイルと追加アップロードしたファイルの両方が保存されるべき。
実際の振る舞い:
追加のファイルをアップロードすると元のファイルが吹っ飛び、新しいファイルしか残らない。
注意: 現在の振る舞いは機能するために必要な方法であるという議論もあるだろう。しかし現在の6.0.0rc1の振る舞いは、5.2.2.1で観察される振る舞いとは違うので、これが新たなバグを呼び込むかもしれないという気がする。
同issueより大意

5.2ではattachmentsのコレクションを代入するとコレクションに追加される。この振る舞いに依存している既存の5.2アプリを6.0にアップグレードしても壊れないようになった。
6.0以降に生成した新しいアプリでは、代入するとコレクション内の既存のattachmentsは置き換わる。既存のattachmentsを消さずにコレクションにattachmentsを新たに追加するのであれば#attachを使うべき。
自分は6.1では古い振る舞いがdeprecateされることを期待している。
同PRより大意

# activestorage/lib/active_storage/attached/model.rb#L95
          def #{name}=(attachables)
-           attachment_changes["#{name}"] =
-             if attachables.nil? || Array(attachables).none?
-               ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self)
-             else
-               ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, attachables)
+           if ActiveStorage.replace_on_assign_to_many
+             attachment_changes["#{name}"] =
+               if attachables.nil? || Array(attachables).none?
+                 ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self)
+               else
+                 ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, attachables)
+               end
+           else
+             if !attachables.nil? || Array(attachables).any?
+               attachment_changes["#{name}"] =
+                 ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, #{name}.blobs + attachables)
              end
+           end
          end
        CODE

つっつきボイス:「もしかしてマルチプルアップロードを誰も使ってなかった?😆」「んなこたーない😆」「RedMineとかでもめっちゃ使いますし☺️」「6.0でマルチプルアップデートの振る舞いが変わるけど、このままだと5.2とかで現在の振る舞いを当てにしているコードがコケるから、それを回避するということみたいです」

「変更後の仕様の方が本来ということか: 1つのhas_manyなcollectionが対象なんだから、再アップロードしたら洗い替えするものでしょうって」「洗い替えか〜」「複数ファイルの組み合わせの完全性を保証したいというときもあるから、洗い替えするかどうかを選べるオプションは一時的にあってもいいかな」「APIとしてはbreaking changesになるから、追加でやりたい人は#attachを使えと」「ぐぉっ、またPCがフリーズした😇」「モニタを私のに差し替えましょう」

「古い振る舞いは6.1でdeprecateしようとPRにあります」「PRもマージされてissueも閉じたからそういう流れか」「config.active_storage.replace_on_assign_to_manyという設定も追加されてますね」「falseにすれば従来の振る舞いになるという、いつもの延命措置😆」「知らずにアップグレードするとコケると😆」


その後私がRailsガイドを6.0向けに更新翻訳しているときに、アップグレードガイドで該当の記述をたまたま見つけました。Active Recordの挙動に合わせたんですね。プルリクにも目的を書いて欲しいです😢。

Rails 6.0のデフォルト設定では、添付ファイルのコレクションへの代入は、追加ではなく既存ファイルの置き換え操作になります。これにより、Active Recordでコレクションの関連付けに代入するときの振る舞いと一貫するようになります
アップグレードガイドのドラフトより(強調は編集部)


「ところで上のコードの末尾にあるCODEって何かしら?🤔」「たぶんヒアドキュメントの終わり部分では?😆」「あ、そのちょっと上に開始部分があった↓😅」「そもそもメソッド名に#{name}みたいなのが書かれてる時点でヒアドキュメントっぽいですし😆」

      def has_many_attached(name, dependent: :purge_later)
        generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
          def #{name}
...
       CODE

新機能: ハッシュの条件で開始値なしのrangeをサポート

Ruby 2.7で導入予定の開始値なしrangeをいち早く取り入れたようです。

# 同PRより
Order.where(created_at: ..1.year.ago)

class Users < ApplicationRecord
  scope :unruly, -> { where(karma: ...0) }
# activerecord/lib/arel/predications.rb#L37
    def between(other)
      if unboundable?(other.begin) == 1 || unboundable?(other.end) == -1
        self.in([])
-     elsif open_ended?(other.begin)
+     elsif other.begin.nil? || open_ended?(other.begin)
        if other.end.nil? || open_ended?(other.end)
          not_in([])
        elsif other.exclude_end?
          lt(other.end)
        else
          lteq(other.end)
        end
      elsif other.end.nil? || open_ended?(other.end)
        gteq(other.begin)
      elsif other.exclude_end?
        gteq(other.begin).and(lt(other.end))
      else
        left = quoted_node(other.begin)
        right = quoted_node(other.end)
        Nodes::Between.new(self, left.and(right))
      end
    end

つっつきボイス:「PC再起動完了」「開始値なしのrangeって不思議感ありありだし😆」「Ruby 2.7で入るんですね」「これどういうクエリになるんだろうか?🤔」「大なり小なりとかでやるのかも?」「なんか微妙に読みづらくなりそう😅」

参考: カルマ値とは - はてなキーワード

新機能: SMSリンクを作成するsms_toヘルパーメソッド

<%= sms_to "15155555785", "Text me", body: "I have a question.." %>
# actionview/lib/action_view/helpers/url_helper.rb#L606
+     def sms_to(phone_number, name = nil, html_options = {}, &block)
+       html_options, name = name, nil if block_given?
+       html_options = (html_options || {}).stringify_keys
+
+       extras = %w{ body }.map! { |item|
+         option = html_options.delete(item).presence || next
+         "#{item.dasherize}=#{ERB::Util.url_encode(option)}"
+       }.compact
+       extras = extras.empty? ? "" : "?&" + extras.join("&")
+
+       encoded_phone_number = ERB::Util.url_encode(phone_number)
+       html_options["href"] = "sms:#{encoded_phone_number};#{extras}"
+
+       content_tag("a", name || phone_number, html_options, &block)
+     end

つっつきボイス:「リンクを叩くとSMSメッセージ画面↓がPC/Android/iOSのどれでも表示されるそうです」「sms:なんちゃらのショートハンドなのね」「hrefにsms:って書けるって知らなかったし😆」


同PRより

<a href="sms:5155555785;?body=Hello%20Jim%20I%20have%20a%20question%20about%20your%20product">Text me</a>

新機能: ActiveRecord::QueryAbortedスーパークラスを追加

あらゆるタイムアウトをrescueするには(ただしそれ以外のクエリ例外はrescueしないとする)、rescue ActiveRecord::QueryCancelledActiveRecord::StatementTimeoutActiveRecord::AdapterTimeoutを行わなければならない。これでは、どれかひとつ足し忘れて欲しいものをrescueできなくなりがち。
そこで、すべてのタイムアウトエラーを簡単にrescue ActiveRecord::QueryAbortedできるよう、スーパークラスを1つ導入してみたいと思う。
同PRより大意

# activerecord/lib/active_record/errors.rb#L357
+ class QueryAborted < StatementInvalid
+ end
+
  # LockWaitTimeout will be raised when lock wait timeout exceeded.
  class LockWaitTimeout < StatementInvalid
  end

  # StatementTimeout will be raised when statement timeout exceeded.
- class StatementTimeout < StatementInvalid
+ class StatementTimeout < QueryAborted
  end

  # QueryCanceled will be raised when canceling statement due to user request.
- class QueryCanceled < StatementInvalid
+ class QueryCanceled < QueryAborted
  end

  # AdapterTimeout will be raised when database clients times out while waiting from the server.
- class AdapterTimeout < StatementInvalid
+ class AdapterTimeout < QueryAborted
  end

つっつきボイス:「クエリのタイムアウトをrescueするときにQueryAborted一発で囲んで集約したいということか」「クエリをパースするタイミングで発生するエラーを同一視したいというか」「クエリを投げたときに失敗する可能性のあるエラーをすべて1個のスーパークラスにしたというか」

StatementTimeoutって見た目だけで推測すると、SELECTを打ってから返ってくるまでの時間が長すぎた場合にRDBMSの何かのパラメータに基づいてタイムアウトする、とかかな〜😆」「それっぽいかも☺️」

QueryCanceledはどういうタイミングで出るのかな?🤔」「たぶんですが、トランザクション分離レベルで待つとか?」「と思ったらコードの上に説明書いてあった🤣」「QueryCanceledはユーザー側でキャンセルした場合か」「BEGIN TRANSACTIONとかの中でキャンセルできた気がするからそういう場合なのか、それともぽすぐれがマルチスレッドクエリを使えた覚えがあるからそっちかな?とも思うけど、とりあえず推測ですし😆」

「とにかくこういうタイムアウトたちを一回で取れるようにしたいと😆」「こういうの今後も増えてきそうですし😆」「その基盤はできたと☺️」

「こういう処理って、こうやってスーパークラスを作ってやる場合もあれば、query aborted typesみたいな感じで配列に入れてincludes?で評価するやり方もあったりしますよね」「rescueするときはスーパークラスの方がやりやすそうかな〜☺️」

新機能: ジェネレーターのskip-collision-checkオプション

y-yagiさんのPRです。

Rails 5.2までは同じ名前のジェネレータをdestroyせずに複数回実行できたが、Rails 6.0とZeitwerkではできない。クラス名のコリジョンチェックでエラーになる。
このチェックではconst_defined?↓を使っているが、これはオートロードオブジェクトも定義されていることが前提になっている。
この部分はRails 5.2まで機能していなかったが、Zeitwerkならアプリケーションのコードを正しくチェックできそうに見える。
ただし、たとえば間違えた属性名を修正するためにジェネレータを再実行する(その場合事前にdestroyしておく必要がある)ときに少々不便。
PRはこの点を解消するためにコリジョンチェックをスキップするオプションを追加する。このオプションを使えばRails 5.2までと同様ファイルを上書きできるようになる。
同PRより大意

# railties/lib/rails/generators/base.rb#L252
        def class_collisions(*class_names)
          return unless behavior == :invoke
+         return if options.skip_collision_check?

          class_names.flatten.each do |class_name|
            class_name = class_name.to_s
            next if class_name.strip.empty?
            # Split the class from its module nesting
            nesting = class_name.split("::")
            last_name = nesting.pop
            last = extract_last_module(nesting)

            if last && last.const_defined?(last_name.camelize, false)
              raise Error, "The name '#{class_name}' is either already used in your application " \
-                          "or reserved by Ruby on Rails. Please choose an alternative and run "  \
-                          "this generator again."
+                          "or reserved by Ruby on Rails. Please choose an alternative or use --skip-collision-check "  \
+                          "to skip this check and run this generator again."
            end
          end
        end

参考: Class: Module (Ruby 2.6.3)


つっつきボイス:「これはどのジェネレータ?」「rails gのジェネレータか」「multiple timesはジェネレータを複数回実行するってことなのね」「ぱっと見同時実行かと思った😆」「ジェネレータでフィールド名間違えたりしたときにbashの履歴から修正して再実行するときとかなんでしょうね」「destroyしなくてもやれるようにしたと」

rails gって自分はマイグレーションのときぐらいしか使わないけど😆、マイグレーションを再実行したらどうなるんだろう?」「マイグレーションファイルにはタイムスタンプが付くからマイグレーションは再実行してもかぶらなさそうだけど🤔」「でも同じクラスがあったらそれこそコリジョンしそうですし😆」「上のコードのメッセージにこう書いてある↓ぐらいだから、今はコリジョンするようになってるんですね☺️」

The name ‘#{class_name}’ either already used in your application or reserved by Ruby on Rails. Please choose an alternative and run this generator again.

「ということはこのskip-collision-checkはマイグレーションでも使えるということかな?」「ということでしょうね」「Railsチュートリアルとかだとワンライナーでマイグレーションを作れるのは便利そうですけど☺️」

issue: Rails 6 rc2のマルチプルDBでテストが通らない

rails db:create
rails db:migrate
rails test

つっつきボイス:「こちらはPRではなくissueなんですが、rc2が出たときは空になっていたRC2のマイルストーンを見に行ったら、これが加わっていました(その後3つに増えています)」「rc1とrc2って相当差が出ましたからね〜」「マルチDBの設定が必要なのか」「これはまさにrc2でテストやってくれてありがとうというissueですね☺️」「再現用のリポジトリとかも作ってくれてるとか有能」「kamipoさんが望んでいるのはまさにこれ」「私も試しに取り急ぎローカルで再現しようとしたら、なぜかポスグレのgemが自分の環境でつっかえたのでそこどまりです😅」

なお、つっつきの後で伸びたスレで、#36439でのSchemaMigrationの移動が影響しているらしいという書き込みがありました。

参考: Rails 6 multiple DB test Issue - PG::ConnectionBad: connection is closed error · Issue #36743 · rails/rails

Rails

Railsのどこで時間がかかっているか(Hacklinesより)

#同記事より抜粋
==================================
  Mode: cpu(1000)
  Samples: 4293 (0.00% miss rate)
  GC: 254 (5.92%)
==================================
SAMPLES    (pct)     FRAME
    206   (4.8%)     ActiveRecord::Attribute#initialize
    189   (4.4%)     ActiveRecord::LazyAttributeHash#[]
    122   (2.8%)     block (4 levels) in class_attribute
     98   (2.3%)     ActiveModel::Serializer::Associations::Config#option
     91   (2.1%)     block (2 levels) in class_attribute
     90   (2.1%)     ActiveSupport::PerThreadRegistry#instance
     85   (2.0%)     ThreadSafe::NonConcurrentCacheBackend#[]
     79   (1.8%)     String#to_json_with_active_support_encoder

つっつきボイス:「パフォーマンス系でおなじみNoah Gibbsさんの記事です」「まずはプロファイリングをinstrumentationでやるか、サンプリングでやるか」

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

「シングルスレッドとマルチスレッドでそれぞれ測定してますね」「Noah Gibbsさんのこの記事は、どういう戦略でいくかを示してから実際に測定して切り分けていくという順序で進んでいくのがいいですね〜❤️」「おぉ〜」「『推測するな。計測せよ。』を実践してる人☺️」

参考: UNIX哲学 - Wikipedia — 「推測するな。計測せよ。」の出典

「結論↓は普通っぽい😆」「でも測定で裏付けたという」「数値でちゃんと出したのが重要☺️」

  • Railsをきちんと設定したとしても、Rails CRUDアプリの時間はDBクエリやActive Recordやシリアライズにかなり食われる
  • GCはRuby 1.9よりかなりよくなっているが、それでも少なからぬ時間を食われてる → ごみオブジェクトを減らせ
  • DBのオーバーヘッドに加えて、Active Recordもかなりオーバーヘッドがある → Sequelなどのオルタナティブを検討
  • StackProfは使いやすくていいヤツなのでRubyアプリでやってみる価値あり

「Sequelか〜😅」「StackProfってNoah Gibbsさんが作ったような気がしてたけど違った😅」「@tmm1さんか」「tackProfのcontributor見ると@tenderloveさんとかいろんな人がやってるし😳」「そういえばCSVが遅遅だったときに StackProf使ったことあったわ: 今ググったら検索結果で自分がこのリポジトリをクリックした跡残ってるし😆」

参考: tmm1/stackprof: a sampling call-stack profiler for ruby 2.1+

LefthookとCrystalballとGitのマジックで楽に開発しよう(Ruby Weeklyより)

おなじみEvil Martiansの記事です。


lefthandリポジトリより


つっつきボイス:「crystalballはRubyKaigiにも登場してましたね→」

「それとLefthook」「お〜Gitフックマネージャか」「ボクシングの左フックにかけてるんでしょうね😆」「Overcommit↓と似たようなものをEvil Martiansが作ったと」「しかもGo言語で」「LefthookでフックをかけてCrystalballを動かすみたいな」

「Crystalballって何でしたっけ?」「影響しそうな部分だけテストするみたいなgem」「いわゆる占いの水晶玉🔮」

「こういう組み合わせで速くなるといいですね〜: RSpecがGitのフックで動くともうストレスでしかないし😭」「みんなローカルでテストしてからにしてねと言いたいけど、ローカルで回しても時間かかりますし🕗」「回してる間に余裕でメシ食えるし😆」「昔のLinuxカーネルとかX11とか、帰る前にコンパイルを走らせたりしましたね〜😇」「そして翌日も終わってなかったり😆」「この記事翻訳したいです😋」

Evil MartiansのDocker記事


つっつきボイス:「Evil MartiansがRails+Docker記事も出してますね」「Evil Martiansはこうやったと」「たしかRailsConfでRailsアップグレードをテラフォーミングになぞらえた話をしたときのサイドストーリーということね」

「先週のDocker記事よりはイケてるかな?」「Aptfileって?」「これをcatして出てきたリストをapt-getするという😆」「あ〜なるほどっ」「大文字で始まってるから何かのパッケージかと思ったら、それをやってるだけ😆」「わかる〜😍、こういうのってちょいちょい更新したくなるし」

# 同記事より
# We use an external Aptfile for that, stay tuned
COPY .dockerdev/Aptfile /tmp/Aptfile
RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
  DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
    build-essential \
    postgresql-client-$PG_MAJOR \
    nodejs \
    yarn=$YARN_VERSION-1 \
    $(cat /tmp/Aptfile | xargs) && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
    truncate -s 0 /var/log/*log

「こちらのdocker-composeはちゃんとcachedやってますね😋、と思ったら記事の中でもcached説明してるし」

volumes:
  - .:/app:cached
  - bundle:/bundle
  - rails_cache:/app/tmp/cache
  - node_modules:/app/node_modules
  - packs:/app/public/packs
  - .dockerdev/.psqlrc:/root/.psqlrc:ro

「ぽすぐれのメジャーバージョンをアップグレードするなんてのはめったに起きないから最初にやっとくと」「NodeやYarnもそんなにしょっちゅう変わらないだろうし」「その次のbuild-essential、新しいのを使いたいから入れてるんじゃないかなと」「お、node_modulesをボリュームにするのか」

ARG RUBY_VERSION
# See explanation below
FROM ruby:$RUBY_VERSION

ARG PG_MAJOR
ARG NODE_MAJOR
ARG BUNDLER_VERSION
ARG YARN_VERSION

# Add PostgreSQL to sources list
RUN curl -sSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \
  && echo 'deb http://apt.postgresql.org/pub/repos/apt/ stretch-pgdg main' $PG_MAJOR > /etc/apt/sources.list.d/pgdg.list

# Add NodeJS to sources list
RUN curl -sL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash -

# Add Yarn to the sources list
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
  && echo 'deb http://dl.yarnpkg.com/debian/ stable main' > /etc/apt/sources.list.d/yarn.list

# Install dependencies
# We use an external Aptfile for that, stay tuned
COPY .dockerdev/Aptfile /tmp/Aptfile
RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
  DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
    build-essential \
    postgresql-client-$PG_MAJOR \
    nodejs \
    yarn=$YARN_VERSION-1 \
    $(cat /tmp/Aptfile | xargs) && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
    truncate -s 0 /var/log/*log

「記事の後半でコンフィグをステップバイステップでめっちゃ丁寧に説明してくれてるし!」「エライ!🎉」「超エライ👍」「これも翻訳したいです😋」

docker-composeで便利そうなdip

「記事の最後にdipというツールが紹介されてますね: docker-composeで設定したアプリをCLIでインタラクティブに操作するみたいです」

asciicast

「どういうつくりなのかな?」「docker-composeを利用したときに、docker-compose runで使いたいものをgulp buildみたいにまとめられるんじゃないかしら: 以下のdip.ymlを見た感じではそれっぽいし↓」「ははぁ〜」

version: '2'

environment:
  RAILS_ENV: development

compose:
  files:
    - docker-compose.yml

interaction:
  bash:
    service: backend
    command: /bin/bash
    compose_run_options: [no-deps]

  bundle:
    service: backend
    command: bundle
    compose_run_options: [no-deps]

  rake:
    service: backend
    command: bundle exec rake

  rails:
    service: backend
    command: bundle exec rails
    subcommands:
      s:
        service: rails
        compose_run_options: [service-ports]

  yarn:
    service: backend
    command: yarn
    compose_run_options: [no-deps]

  rspec:
    service: backend
    environment:
      RAILS_ENV: test
    command: bundle exec rspec

  rubocop:
    service: backend
    command: bundle exec rubocop
    compose_run_options: [no-deps]

  psql:
    service: postgres
    command: psql -h postgres -U postgres -d example_app_dev

  'redis-cli':
    service: redis
    command: redis-cli -h redis

provision:
  - dip compose down --volumes
  - dip compose up -d postgres redis
  - dip yarn install
  - dip bash -c ./bin/setup

「interactionの下にbashとかbundleとかrailsとかyarnとかrspecとか書いてあるから、コマンド実行した後にいちいちRUNで作ったりしなくてもdip run なんちゃらでやれるとかそういう感じかなと🤔」「ちゃんと読まないとわからないけど何だかよさそう😋」

「docker-composeコマンドで延々入力したくない人向けかも」「たしかにdocker-composeってコマンド名自体が長いですよね😆」「docあたりでTab補完してもdockerで止まっちゃうし😆」

# 同リポジトリより
dip run rails c
dip run rake db:migrate

dip ssh upとかdip nginx upなんてのもできるみたいですし」「dip ssh upってまさにコンテナにsshするときのショートハンドか」「docker-compose.ymlにこんなふうに↓書けばこのショートハンドが使えるのね😳」

services:
  web:
    environment:
      - SSH_AUTH_SOCK=/ssh/auth/sock
    volumes:
      - ssh-data:/ssh:ro

volumes:
  ssh-data:
    external:
      name: ssh_data

dip nginx upの設定もnetworksの下にexternal:を指定してるから、別のdocker-composeからアクセスできるんだろうな」「nginxプロキシを差し込めると」「frontendの方でこれに合わせておいてくれればnginxをそっちに向けるよ、と」「dip dns upなんてのもできるし😍」「これ欲しかった人いるんじゃ?😆」

version: '2'

services:
  foo-web:
    image: company/foo_image
    environment:
      - VIRTUAL_HOST=*.bar-app.docker
      - VIRTUAL_PATH=/
    networks:
      - default
      - frontend
    dns: $DIP_DNS

networks:
  frontend:
    external:
      name: frontend

「たしかにdocker-composeで一度作っちゃった後でsshで入ろうとするのも閉じるのも面倒くさかったりするけど、こういう感じで一種の『付け外しできるバックドア的なツール』として使えるのかなと」「全部の機能を残らず使うかというと疑問ですけど😆」「知ってれば便利に使えそう❤️」「sshとDNSとnginxプロキシ、どれもやれたらうれしいヤツだし😋」「後でちゃんと見てみようかな☺️」

pg_search: PostgreSQLの全文検索をActive Recordで(Ruby Weeklyより)

# 同リポジトリより
class AntipatternExample
  include PgSearch::Model
  multisearchable against: [:contents],
                  if: :published?

  def published?
    published_at < Time.now
  end
end

problematic_record = AntipatternExample.create!(
  contents: "Using :if with a timestamp",
  published_at: 10.minutes.from_now
)

problematic_record.published?     # => false
PgSearch.multisearch("timestamp") # => No results

sleep 20.minutes

problematic_record.published?     # => true
PgSearch.multisearch("timestamp") # => No results

problematic_record.save!

problematic_record.published?     # => true
PgSearch.multisearch("timestamp") # => Includes problematic_record

casebookというニューヨークの会社が作っているgemです。


casebook.netより


つっつきボイス:「pg_searchは前からあるgemで、ずっと前にウォッチでちょっとだけ取り上げたことがありました」「ぽすぐれのフルテキストサーチですね☺️」「名前は聞いたことありますし☺️」「multisearchableとな😆」「こういう感じの名前って昔のgemによくありましたね」「最近流行りませんけど😆」

# 同リポジトリより
class EpicPoem < ActiveRecord::Base
  include PgSearch::Model
  multisearchable against: [:title, :author]
end

class Flower < ActiveRecord::Base
  include PgSearch::Model
  multisearchable against: :color
end

「pg_searchはPostgreSQLに何か拡張とか入れる必要あるのかな?」「READMEにはPostgreSQLの全文検索機能を利用すると書いてあるから、なしでもできそうですね」

「Ransackとかとは違うヤツでしょうか?」「全然違いますね😆」「ransackは全文検索とはアルゴリズムがまるで違いますし」「そうでしたか😅」「言ってみればRansackは、Arelを組み立てやすくするためのgemですから」「検索条件を組み立てやすくするものなんですね」「もしかするとpg_searchもRansackと併用できるかもしれないけど」「わかんないけどできそう😆」


より

better_errorsを高速化

とても短い記事です。


つっつきボイス:「better_errorsが遅いとかあまり思ったことないけど😆」「でっかいオブジェクトを巻き込んじゃうと遅くなったりするみたいですね」「そんなこともあるのかと😆」

「あ〜なるほど、inspectがすごくでっかくなっちゃうときか」「そういうときは早々と諦めると😆」「if inspected.size > 20_000のときに諦めるのね😆」

その他Rails


つっつきボイス:「エントリ増やしすぎないようにと思いつつ貼りました😅」「Active RecordをやめてROM(Ruby Object Mapper)にしてみたと」「まあたしかにActive Recordはヤバいくらい多機能ですけどね☺️」

  • サイト: ROM


rom-rb.orgより

勉強会・書籍


つっつきボイス:「『Rails 6リリースするかも?Party at Speee?』の特別ゲストのリストを見ると、先日の銀座Rails#11↓のときにいた人が全員いるというあたりで既視感が蘇ってきました🤣」「そうそう、全員いましたし🤣」「へぇ〜!」


roppongirb.connpass.comより

銀座Rails#11で「出張Railsウォッチ」発表させていただきました

「Meguro.rbはSlackを開設してるので取りあえず入ってみました😋」

「3つ目の書籍は、これも銀座Rails#11で発表していたken1flanさんが出してる本だそうで、新人教育でセキュリティホールの空いたアプリを触ってもらって実地に学ぶというのをやってるそうです」「『ちゃんと作らないとこうなるよ』と😆」「セキュリティはこうやって実際に見せるとわかりやすいですよね☺️」


前編は以上です。

おたより発掘

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

週刊Railsウォッチ(20190722-1/2前編)Rails 6エラー画面の改良点、Dateを四捨五入できるtime_calc、Rackミドルウェアのデザインパターンほか

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

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

Rails公式ニュース

Ruby Weekly

Hacklines

Hacklines

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

この記事の著者

hachi8833

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

hachi8833の書いた記事

BPSアドベントカレンダー

週刊Railsウォッチ