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

週刊Railsウォッチ(20190930前編)知られざる7つの便利gem、Duration.buildにstringを渡せなくなる、Webpackerのpacksをマスターほか

こんにちは、hachi8833です。Google Translator Toolkitが12月4日にディスコンになるそうなので忘れないうちにファイルをGTTからダウンロードしておきました。


つっつきボイス:「終わっちゃうんだ😳」「GTTは自分も最近めっきり使ってませんでしたが、突然の終了宣言で😇」「見た感じユーザー数は少なそうではありますけど😆」「ローカライズに関心のある人ぐらいしかいないと思いますので、きっとそうです😆」「マイグレーションパスとかもないのかしら?」「終了までの間にデータを逃がすことはできるみたいですけど、マイグレーションパス的なのはない感じですね: たぶん今後はGoogle内部でしか使わないのかも🤔」「無料とはいえ他に類似の無料ツールがあんまりないから、これに頼ってた人は泣くしかなさそう: 他の商用ツールを使ってくれという感じですかね」「Googleのハシゴ外し伝説がまた1つ増えました😭」

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

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

第15回目公開つっつき会は、今週10月5日(木)19:30〜にBPS会議スペースにて開催されます。週刊Railsウォッチの記事にいち早く触れられるチャンスです!皆さまのお気軽なご参加をお待ちしております🙇。

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

今回は公式の更新情報があったのでやりやすくて助かりました😋。

キャッシュストアから複数のキーを削除するActiveSupport::Cache::Store#delete_multiメソッドを追加

キャッシュからキーのarrayを削除するActiveSupport::Cache::Store#delete_multiを追加。
ほとんどの実装ではActiveSupport::Cache::Store#deleteを単にn回読んでいるが、Redisはキーのarray削除をサポートしている。従来のdeleteをn回呼び出していたのを1回の呼び出しにできるので、パフォーマンス向上が期待できる。
なおMemcachedはマルチdeleteをサポートしていない
同PRより大意

# activesupport/lib/active_support/cache.rb#L480 
+     def delete_multi(names, options = nil)
+       options = merged_options(options)
+       names.map! { |key| normalize_key(key, options) }
+
+       instrument :delete_multi, names do
+         delete_multi_entries(names, options)
+       end
+     end
...
+       def delete_multi_entries(entries, options)
+         entries.inject(0) do |sum, key|
+           if delete_entry(key, options)
+             sum + 1
+           else
+             sum
+           end
+         end
+       end

つっつきボイス:「キーのarrayを削除する機能が追加された❤️」「キャッシュエンジンがキーのarrayを一発削除する機能をサポートしているなら、それを使うほうが絶対速いですね👍」「イミュータブルだろうし」「Redisだと効くようになるとPRメッセージにありますね😋」「なるほど!」「大量の削除を大量のAPI呼び出し発行でやるのはしんどいですから😆」

Action Mailerにemail_address_with_nameを追加

現状のガイドではmail(to: %("#{@user.name}" <#{@user.email}>))のように自力でフォーマットするようアドバイスしている。
残念ながらnameがAaron "Tenderlove" Pattersonとか-_-" <3の場合は破綻する。さらに困るのは、メールは送信完了したと暗黙のうちに思わせてしまうこと。実際にはこれを理解できないメールサーバーに飲み込まれてしまう。
今回の変更で、email_address_with_nameメソッドでmail(to: email_address_with_name(@user.email, @user.name))のようにフォーマットを扱えるようになる。これは内容を正しくエスケープできるようにする手法のひとつとなる。
同PRより大意

# actionmailer/lib/action_mailer/base.rb#L593
+     def email_address_with_name(address, name)
+       Mail::Address.new.tap do |builder|
+         builder.address = address
+         builder.display_name = name
+       end.to_s
+     end
...
+   def email_address_with_name(address, name)
+     self.class.email_address_with_name(address, name)
+   end

つっつきボイス:「ここで行っているnameのエスケープというのが何が目的なのかわからなくて😅」「あ〜なるほど!: これはですね、メールのto:ヘッダーとかでは、以下みたいにdisplay nameの後ろに< >で囲んでメールアドレスを示すなどの仕様がメール関連のRFCに記載されているんですよ😎」「おぉ〜!」「MTAはこういう仕様を頼りにヘッダーから送信先メールアドレスを取り出します☺️」

mail(to: %("#{@user.name}" <#{@user.email}>))

「そして今のRailsではそれをメソッドとして実装できないので、RFCの仕様を知っている人が自力で実装することになっているんですけど、やっぱりこういうふうにメアドと表示名をエスケープしてくれるヘルパーメソッド↓があった方がいいよねということで今回足したんでしょうね☺️」「はぁ〜なるほど!」「表示名に<>"があったらエスケープするとかはどのみちやらないといけないので」

mail(to: email_address_with_name(@user.email, @user.name))

参考: 3.4 RFC2822(Internet Message Format)
参考: 3.4 RFC 2822 - Internet Message Format -- 原文

「RFC 2822のこの辺の仕様ですね↓」「そういうことでしたか😳」「いにしえの昔からこの仕様です😎」


tools.ietf.orgよりRFC2822の3.4

「上のname-addrは[display-name] angle-addrだからdisplay-nameはオプション、そしてdisplay-name = phrase、phraseはこのRFCを探すと3.2.6phrase = 1*word / obs-phrase、そしてobs-phraseは4.1obs-phrase = word *(word / "." / CFWS)だからダブルクォートで囲む...という仕様」「こういう感じで仕様を辿るんですね😋」

「というような仕様を満たすためのemail_address_with_nameメソッドがRailsに足されたという話」「このemail_address_with_nameという名前がちょい長い気はしますけど😆、いいんじゃないでしょうか」「😆」

ActiveSupport::Duration.build()がNumericのみを受け取るように変更

今回の変更は、型が::Numericでない場合にActiveSupport::Duration.build(value)によってActiveSupport::Durationインスタンスが作成されることを防ぐ(でないとTypeErrorになる)。
この修正によって、#37012のさまざまな振る舞い(ActiveSupport::Durationが原因不明で失敗する、文字列からビルドしたdurationの比較結果がおかしくなる)が修正される。
同PRより大意


つっつきボイス:「Durationbuildがあるとは😳」「しかもstring入れられる!ひゃ〜😅」「数字を入れると秒として認識されると」「従来はsmall_duration_from_int < large_duration_from_stringという比較↓でエラーになる😇」「durationをintから作ったかstringで作ったかで違ってくると」「よく見つけたな〜😳」「知らずに踏みそうなバグ」「どうやってビルドしたかとか普通関知しないですし😆」「to_iしたらむしろそのせいで壊れたりしそう😆」

# Changelogより
# 修正前:
small_duration_from_string = ActiveSupport::Duration.build('9')
large_duration_from_string = ActiveSupport::Duration.build('100000000000000')
small_duration_from_int = ActiveSupport::Duration.build(9)

large_duration_from_string > small_duration_from_string
#=> false

small_duration_from_string == small_duration_from_int
#=> false

small_duration_from_int < large_duration_from_string
#=> ArgumentError (comparison of ActiveSupport::Duration::Scalar
#                    with ActiveSupport::Duration failed)

large_duration_from_string > small_duration_from_int
#=> ArgumentError (comparison of String with ActiveSupport::Duration failed)

# 修正後

small_duration_from_string = ActiveSupport::Duration.build('9')
#=> TypeError (can't build an ActiveSupport::Duration from a String)

「今回の修正を見ると、Numericしかビルドしないことにしたのか↓」「まあ文字列使わなければいいんでしょうけど」「でもこれはDurationを文字列でビルドしてた人にとってはbreaking changesになるから対応が必要になるでしょうね⛔️」「そうか!」「API側で文字列をto_iしてもいいような気もするけど、文字列のto_iって割と事故りやすいんですよね😅」「たしかに〜😆」「この#37013はもうマージされたんだなあ」

# activesupport/lib/active_support/duration.rb#L183
      def build(value)
+       unless value.is_a?(::Numeric)
+         raise TypeError, "can't build an #{self.name} from a #{value.class.name}"
+       end
+
        parts = {}
        remainder = value.to_f

        PARTS.each do |part|
          unless part == :seconds
            part_in_seconds = PARTS_IN_SECONDS[part]
            parts[part] = remainder.div(part_in_seconds)
            remainder = (remainder % part_in_seconds).round(9)
          end
        end
        parts[:seconds] = remainder
        new(value, parts)
      end

[Rails5] ActiveSupport::Durationの期間処理メソッド(1)演算、比較など

buildにはそもそもstring渡すなという話なんだろうな🤔」「buildがドキュメントでどう扱われているかを参照すべきでしょうね↓」

「以下のサンプルコード↓は載っているけど、yardのドキュメントに書かれてるわけではなかった😅」「つまりstringを渡せるとは書かれていないけど、渡しても動いてた😆」「内部でto_iしてくれてるよねきっと、みたいな気持ちになるのは、わかる気はする☺️」

# APIより
ActiveSupport::Duration.build(31556952).parts # => {:years=>1}
ActiveSupport::Duration.build(2716146).parts  # => {:months=>1, :days=>1}

watcherに渡すファイルやディレクトリの扱いを修正

現在のwatcherに渡されるautoloadパスがディレクトリになっている。evented watcherを使うと、これがListenにそのまま渡される可能性がある。しかしautoloadパスにはファイルが含まれているので、ファイルが渡されるとListenでエラーが発生する。このため、ファイルとディレクトリを正しく仕分ける必要がある。
#37011が修正される。
同PRより大意

# railties/lib/rails/application.rb#L350
    def watchable_args #:nodoc:
      files, dirs = config.watchable_files.dup, config.watchable_dirs.dup

      ActiveSupport::Dependencies.autoload_paths.each do |path|
-       dirs[path.to_s] = [:rb]
+       File.file?(path) ? files << path.to_s : dirs[path.to_s] = [:rb]
      end

      [files, dirs]
    end

つっつきボイス:「修正はわずかですけど、watcherっていうのがRailsにあるとは知りませんでした😅」「これだけ見ると、いわゆるファイル変更を監視するwatcherなんじゃないかな?🤔」「Railsのdevelopmentモードでよくやる、ファイルが更新されたらリロードするみたいな?」「gemでそういうのありましたけど、名前が出てこない😅」

guard gemでした↓。

「この修正ではevented watcherと言ってるので変更イベントの通知用でしょうね」「以下の記事に『ファイルの変更を検知しているのはapp/config/environments/development.rbfile_watcherの部分』とあるから、これのことか」「FileUpdateCheckerもあればEventedFileUpdateCheckerもあると😆」「ポーリングもやれるということでしょうね☺️」

参考: dockerでrails5環境構築 - Qiita
参考: Rails アプリケーションを設定する - Rails ガイド

config.file_watcher: config.reload_classes_only_on_changeがtrueの場合にファイルシステム上のファイル更新検出に使われるクラスを指定します。デフォルトのRailsではActiveSupport::FileUpdateChecker、およびActiveSupport::EventedFileUpdateChecker(これはlistenに依存します)が指定されます。カスタムクラスはこのActiveSupport::FileUpdateChecker APIに従わなければなりません。
Railsガイドより

ファイル更新通知について

「ファイル更新のトリガー通知って、内部的にはD-Bus使ったり、LinuxだとinotifyとかMacだとFile System Eventsみたいにいろいろあるんですけど、POSIXに仕様がないんですよ」「あ〜」「だからこういう機能は基本的にOS依存になります」「知らなかった〜😅」「OSによってやり方を変えるしかないんですね😅」

参考: D-Bus: DBusWatch
参考: Man page of INOTIFY
参考: macos - Is there a command like "watch" or "inotifywait" on the Mac? - Stack Overflow

「お、fswatch↓はクロスプラットフォームらしい?😳」「リポジトリの冒頭を見ると、macOSではFile System Events、BSD系だとkqueue、Linuxだとinotify、Windowsだとstat()ベースのバックエンドってあるから、内部で使い分けてますね😆」「へぇ〜!」「だからバックエンドのファイル更新イベントはやっぱりOS依存のAPIを使ってる😆」

レスポンスのVary: Acceptを再設定しないよう修正

#36213で、レスポンスにAcceptヘッダーを使うVary: Acceptヘッダーが追加された。このため、アプリでVaryヘッダーにAccept以外の値を設定した場合に問題が起きる。コントローラのアクションでこのヘッダーにどんな値を設定しても、_set_vary_headerメソッドが値を"Accept"に設定する。
このコミットによって、ヘッダーに"Accept"を設定する前に、ヘッダーに既に値が設定済みかどうかをチェックするようになる。このヘッダーが空欄の場合はヘッダーを設定する。このヘッダーに既に値がある場合、アプリケーションがこのヘッダーを扱っているという前提に立って_set_vary_headerは何もしない。
同PRより大意

# actionpack/lib/action_controller/metal/rendering.rb#L78
      def _set_vary_header
-       self.headers["Vary"] = "Accept" if request.should_apply_vary_header?
+       if self.headers["Vary"].blank? && request.should_apply_vary_header?
+         self.headers["Vary"] = "Accept"
+       end
      end

参考: Vary - HTTP | MDN


つっつきボイス:「Vary: Acceptヘッダーを二重設定してしまうケースがあったのを修正したと」

「ちょっと前にVaryヘッダーが追加されたときも話題にしましたね(ウォッチ20190805)」「う、それの続きのようです😅」「たしかキャッシュするかどうかの基準をどのヘッダーにするかをブラウザに通知する的なのがVaryヘッダーでしたね😆」「HTTPのヘッダーっていろいろありすぎて覚えきれない😅」「見慣れないヘッダーだったので何となく覚えてました☺️」

Varyヘッダーは、クライアント側というかリバースプロキシが使うヤツですね: リバースプロキシがどのヘッダーを見てキャッシュするかどうかを決めるとか」「おぉ〜」「たとえばVaryにUser Agentを指定すればUser Agentごとにキャッシュできるし、それに加えて別のヘッダーも指定すれば、その組み合わせによってキャッシュするしないを指定できると」「な〜るほど!😋」「でもCloudFrontではVaryヘッダーが効かないらしい、みたいな話を前回してた覚えがあります😎」

Rails

GitHub ActionsでRailsのCIを回す(RubyFlowより)


同記事より


つっつきボイス:「先週はmizchさんのGitHub Actions記事でしたが(ウォッチ20190925)、今週はこちらのやってみた記事を拾ってみました」「今なら普通に動きますしね☺️」「CircleCIから割とさくっと移行できるみたいですし☺️」

「Boring Railsってなかなかスゴい名前😆」「このサイトはどうやら最新記事以外は単行本に移しているようなので、次の記事が出たら見えなくなるかも😅」「なるほど😆」


boringrails.comより

Webpackerのpacksをマスターする

# 同記事より
app/javascript
├── admin
├── channels
├── login
└── packs
    ├── admin.js
    ├── application.js
    └── login.js

つっつきボイス:「1本目は少し前に取り上げた気がしますね😆」「あ、かぶってました(ウォッチ20190902)😅: 2本目がその続き的な記事です」「packファイルを太らせるな、とか書いてますね」

「うん、この記事はmanifest.jsonとかがどういうタイミングで参照されるとか、このファイルがダイジェスト付きになるとかが丁寧に説明されているっぽいのがいいですね👍」「お〜」「大事なのはjs/admin-67dd60bc5c69e9e06cc3.jsみたいなダイジェストを引き回してくれるところ」「webpack-dev-serverを動かせばこの辺の情報が出てくれるのね↓」「後でちょっと読んでおこう😋」「翻訳したいです😋」

# 同記事より
▶ ./bin/webpack-dev-server
ℹ 「wds」: Project is running at http://localhost:3035/
ℹ 「wds」: webpack output is served from /packs/
ℹ 「wds」: Content not from webpack is served from /Users/prathamesh/Projects/scratch/better_hn/public/packs
ℹ 「wds」: 404s will fallback to /index.html
ℹ 「wdm」: Hash: 5387bbdba96d7150c792
Version: webpack 4.39.2
Time: 2753ms
Built at: 09/24/2019 12:23:20 AM
                                     Asset       Size       Chunks             Chunk Names
          js/admin-67dd60bc5c69e9e06cc3.js    385 KiB        admin  [emitted]  admin
      js/admin-67dd60bc5c69e9e06cc3.js.map    434 KiB        admin  [emitted]  admin
    js/application-d351b587b51ad82444e4.js    505 KiB  application  [emitted]  application
js/application-d351b587b51ad82444e4.js.map    569 KiB  application  [emitted]  application
          js/login-1c7b2341998332589ec0.js    385 KiB        login  [emitted]  login
      js/login-1c7b2341998332589ec0.js.map    434 KiB        login  [emitted]  login
                             manifest.json  958 bytes               [emitted]

記事末尾のまとめ:

  • packファイルは最小限にし、必要なコードは他のファイルからインポートすること
  • app/javascript/packsにはpackファイル以外は置かないこと
  • それ以外についてはapp/javascriptの下は自由にやってよい
  • Webpackの出力でbundleのサイズに目を光らせておこう
  • packファイルは必要に応じて編成し、それらが提供する機能に依存するpackを管理する

Railsのerrors.added?errors.of_kind?


つっつきボイス:「ロケールに依存しない形でテストできるそうです」「お?added?っていうメソッドがあるのか😳」「こういうふうに、実際の生メッセージじゃなくてシンボルでメッセージを指定できるのね↓」「当然この方がいいですよね❤️」

# 同記事より
expect(john.errors.added?(:first_name, :too_short, count: 2)).to be_truthy

# または(predicateマッチャを使う場合)
expect(john.errors).to be_added(:first_name, :too_short, count: 2)

「マルチリンガルのメッセージを変えただけなのにバリデーションロジックのテストが落ちたら、それはテストがI18nから分離できていないということだからおかしいですしね」「そうそう😆」「本来はこういうふうにテストすべき」

「このメソッドを覚えてたら使うだろうけど、そのときに思い出せるかどうか😅」「ついしれっと日本語でテスト書いちゃいそうな気も😆」「自分で何度か使ってみたり、既に誰かが使っててくれたらいいんだけど」「あったら使いますし😋」

jnchitoさんの記事に、Rails 6のerrors.of_kind?ならcount: 2を書かなくていいと追記されていました。

Quoraより: Railsとworker killer


つっつきボイス:「サーバーの利用メモリがひたすら増加していくような挙動を観測したら?そりゃもうworker killerでしょう🤣」「🤣」「このQuoraで、Pumaにもworker killer↓があるって初めて知りました」「ありま〜す😆どれにもworker killerありま〜す」「Unicornでworker killer使うという話しか知りませんでした😅」「Passengerにも何リクエスト受けたら自滅するヤツとかありますよ」

「Quoraのリンク先の記事↓にもあるように、memory bloatだったりリークだったりいろいろ原因は考えられますけど、この辺はもうしょうがないんですよね🤣」「🤣」「Rubyに限りませんけど、動的にオブジェクトを生成するスクリプト言語だと、メモリにきれいに乗らないからエッジをまたぐみたいなことが起きたりしますし、GCしてもうまく寄ってくれないとかありますし」「ふむふむ」「それ以前にGCって重いからそうそうGCしませんし😆」

参考: What causes Ruby memory bloat? – Joyful Bikeshedding -- (ウォッチ20190401でも取り上げました)


同記事より

「私のオレオレRailsアプリがworker killerなしでやっていけてるのは、単に作りがあまりに素朴だからなのかな?🤔」「そういうちっちゃなアプリならほぼ問題になりませんね☺️」「よかった😂」「問題になるのはメモリフラグメンテーションが大量に発生するようなケースですし、内部のメモリ効率が落ちるだけなので」「そういえば私のRailsアプリはモデル1個しかないし、一般ユーザーはモデルの読み出ししかやってませんし😅」「Active RecordやActive Modelがオブジェクトをぼこぼこ作ったり、スコープ外れて消えたり、ということが起きまくっていると、フラグメンテーションが起きやすくなります☺️」

「というわけでworker killerはみなさん使ってますので😆」「Rubyを使ってRailsのようにひたすら動的にメモリを確保するようなビッグアプリを動かしていれば、この辺は割とどうしようもありませんし☺️」

あまり知られてないけど便利なgem 7つ(Ruby Weeklyより)


つっつきボイス:「gemのリポジトリのリンクが記事にあまりなくて辿りにくいので、下にリストアップしてみます」

  1. traceroutes
  2. strong_migrations
  3. isolator
  4. test-profとruby-prof
  5. database_consistency
  6. attractor
  7. coverband

1. traceroutes

「amatsudaさんのgemだ!」「rake tracerouteで使われてないルーティングやアクションを見つけてくれる❤️」「お?記事はこれがRails 6で動かないからプルリクを送ったと書いてる↓」「でもtracerouteのREADMEにはRails 6対応って書いてあるけど?😆」「入れ違いかな?🤔」

2. strong_migrations

「strong_migrations、これは前にもウォッチに出てきたような↓🤔」「だいぶ前だったかな」

週刊Railsウォッチ(20170915)Ruby 2.4.2リリースで脆弱性修正、strong_migrations gemでマイグレーションチェック、書籍『Mastering PostgreSQL』ほか

「gemのREADMEに、マイグレーションでこういう潜在的に危険な操作↓を行ったときに警告してくれるとある」「これがデンジャラスオペレーションですよ😆」

  • カラムの削除
  • カラムにデフォルト値を追加する
  • 後からデータを入れる
  • インデックスをnon-concurrentに追加する
  • referenceを追加する
  • 外部キーを追加する
  • カラムの型を変更する
  • カラムをリネームする
  • テーブルをリネームする
  • forceオプションを付けてテーブルを作成する
  • change_column_nullでデフォルト値を使う
  • jsonカラムを追加する

「正しいマイグレーションのやり方もREADMEに書かれているし」「へ〜、ignored_columns↓って知らなかった!」「先にignored_columnで入れておけば、今動いているRailsワーカーが誤って参照するのを防げるということか〜!」「なかったことにしてくれる😂」「すげ〜こんなのあるんだ😳」「でデプロイしてから最後にsafety_assuredで確定すると」

# 同リポジトリより
# Good
class User < ApplicationRecord
  self.ignored_columns = ["some_column"]
end
# 同リポジトリより
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[6.0]
  def change
    safety_assured { remove_column :users, :some_column }
  end
end

参考: ignored_columns -- ActiveRecord::ModelSchema::ClassMethods

「こうやってデフォルト値を足すときも間違えがち↓」

# 同リポジトリより
# Bad
class AddSomeColumnToUsers < ActiveRecord::Migration[6.0]
  def change
    add_column :users, :some_column, :text, default: "default_value"
  end
end

「strong_migrations gemでこの手の危険をはらむマイグレーション操作を警告してくれるのはありがたいですね😋」「こういうのは気を付けててもやっちゃいますし😅」「わかってても踏む😆」「いいgem!」「おや、既に自分もこのgemに★付けてた😆」

3. isolator

「isolatorか」「non-atomicなDBトランザクションを検出してくれると」「この例は確かにnon-atomicだわ↓」

# 同リポジトリより
# HTTP calls within transaction
User.transaction do
  user = User.new(user_params)
  user.save!
  # HTTP API call
  PaymentsService.charge!(user)
end

#=> raises Isolator::HTTPError

# background job
User.transaction do
  user.update!(confirmed_at: Time.now)
  UserMailer.successful_confirmation(user).deliver_later
end

#=> raises Isolator::BackgroundJobError

「こういうのを自動検出してくれるならいいですね❤️」「代わりに怒ってくれると😆」「当てにしすぎるわけにもいかないと思うけど、初歩的なミスを拾ってくれるならいいと思います😋」

4. test-profとruby-prof

「どのテストが遅いかをこの2つで検出すると」「test-profは記事ありました↓」

TestProf: Ruby/Railsの遅いテストを診断するgem(翻訳)

5. database_consistency

bundle exec database_consistencyでデータベースの一貫性をチェックしてくれるそうです」「ああ、こういうgemを作りたくなることってよくありますね: このgemは、Railsのバリデーターをすり抜けたデータが入ってないかどうかをチェックしてくれる」「おぉ〜😍」

「こういう感じのコードってよく自作しますよ、不安だから😆」「やりますね〜😆」「eachで回してvalid?メソッドをひたすら呼ぶバッチとか😆」「まさにそういうのをやってくれると」

「生SQLでデータをいじる人が出始めると、だいたい整合性がおかしくなるんですよ😭」「バリデーションをスキップする悪しきupdate系メソッドが使われちゃってたとかね😇」

「このgemは多分単純なことしかしてないし、消費税対応とかで生SQLでデータをいじった後に使いたい感じですね」「ただバリデーションに誰かがcallbackを書いていたりすると副作用でこいつがデータを破壊して回りそうではある🤣」「🤣」

6. attractor


同リポジトリより

「attractor gemはコードからこういうメトリックスレポート↑を出してくれるようですね」「complexityとかあるし、静的に解析してくれるツールのひとつらしき」「グラフの斜めの線より上にあるコードはリファクタリング候補、とか☺️」「こういうツールはたくさんありますね☺️」

7. coverband

「coverbandは知ってる!」「production環境で使われてないコードを検出できる」「使ってないビューとかも検出できると↓」「ただし実行されてないというだけで判断してるので、年1でしか動かさないバッチも使われてないとみなされちゃうかも😆」「でも機械的にそうしたコードを検出できるのはいいですね😋」「coverbandはいい👍」


同記事より

「こういうので、Ruby 2.5以降あたりを使ってもっといい感じにやれるのが確かありましたね」「名前思い出せないけどそういえばありました😅」「たしかRubyKaigiでもこれ↓とか、後もうひとつつぐらいこのテーマで発表されてたと思います☺️」


「この記事のgemのチョイスはいいと思います!❤️」「忘れがちだけど、入れておくとコードの健康が保てる系のgemということで☺️」

Form Objectでモデルを複数使う(Ruby Weeklyより)


つっつきボイス:「Form Objectを使うときって、こうやってモデルが複数出てくるときが多いですよね😆」「自分はForm Objectはキライじゃない派」「自分もForm Objectは好き😍」「つまり2人とも好きなんですね😆」

Form Objectよもやま話

「Form Objectをどう位置づけるかですよね: 世の中にはService Objectでやりたい人たちもいて、もちろんそれもわかるんですけど、Form Objectなら1リクエストを受けるフォームとみなせるじゃないですか」「そうそうっ😋」

「もしかするとForm Objectという言葉が好きになれないとかあったりするのかも🤔」「その気持もわかりますね」

「Form Objectといえば、kazzさんも好きな、Active Modelだけ継承した素のクラスでやる方法を、最近誰かのスライドで『Application Object』だったか『Application Model』って呼んでたのを思い出しました: 一般的な呼び方かどうかよくわからないので、そのスライド限定の呼び方かもしれませんけど」「正しい呼び方は知らないけど😆、そういえばそんな話もしましたね」

「それで言うと、Form Objectと呼んだときは、あくまでユーザーのリクエストとかAPIへのPOSTリクエストを扱うもの、というニュアンスが出ますよね: 逆にApplication Objectと呼ぶと、内部APIからもアクセスされるもの、というニュアンスになる」「そうそうっ😋」

「その意味で、外部のインターフェイスに強依存するならForm Objectと呼ぶ方がしっくりくる」「うんうん」「逆にたとえばバッチの中で所定のハッシュを作って、内部からもそのAPIを呼ぶんだったら、Application Objectの方がしっくりきますね〜」「それはフォームでもあるんですけどね🤣」「そうそう🤣: でもForm Objectを内部APIとして叩くのはちょっと違和感があるので、そういうものだったらApplication Objectと呼ぶ方がより汎用的で使い回しが効く感じはありますね☺️」

「どちらに重きを置くかで変わってくるんですね」「Form Objectの方が限定的です☺️」「ビュー寄りですし☺️」


その後社内でRailsdm 2018 Day4のこのスライド↓を教えてもらいました。ありがとうございます🙇

その他Rails

つっつきボイス:「今からActive Recordを全部追おうとすると大変だろうな〜😅」

後で買っちゃいました😋。


つっつきボイス:「GitLabでTerraformも動かせるんですね」「まあ何でも動かせますし😆」「『ベースになるジョブをincludeで別リポジトリから読み込む』とか、GitLab CIハック感ありますね〜😋」「BPS社内でも参考になりそう!」「もうやってる人いるかも😆」



つっつきボイス:「igaigaさんの『Railsの教科書』、これはいい本ですよぉ〜❤️」「おぉ〜」「書いてあるとおりにやればできる本😋」「carrierwave使ってますね」

「そういえば、この間Railsチュートリアル↓をRails 6版に更新翻訳したんですけど、旧チュートリアルで使われてたcarrierwaveがRails 6のActive Storageに切り替わってました」「そりゃそうでしょうね😆」「Rails 6でrails newするのに今どきcarrierwave使うとしたら理由が必要☺️」


railstutorial.jpより


前編は以上です。

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

週刊Railsウォッチ(20190925-2/2後編)AWS Lambdaの秘密鍵保存法、Rubyコミット歴史の動画、Rubyコードの最適化と式展開ほか

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h


CONTACT

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