Tech Racho エンジニアの「?」を「!」に。
  • 開発

週刊Railsウォッチ(20191007前編)Ruby 2.6.5でセキュリティ修正、Arel.sqlがstable APIに、Puma 4.2、RailsのDomain ObjectとService Objectほか

こんにちは、hachi8833です。そういえば今夜からノーベル賞発表が始まりますね。

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

今回は第15回公開つっつき会からお送りします。お集まりいただきありがとうございました!🙇。

臨時ニュース: Ruby 2.6.5などセキュリティ修正リリース

ウォッチ20190925で取り上げた#16136の文字化け問題も2.6.5で修正されています。

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

(master)stat(2)の呼び出しを削減

File.file?やその他の述語(メソッド)はパーミッションについて同じstat(2)の呼び出し結果を利用できる。
同PRより大意


つっつきボイス:「nobuさんのRailsコミットを初めて見たような気がします」「stat(2)って何を指しているんでしたっけ?🤔」「manコマンドで2 statとセクション番号を指定するということだと思います☺️」「そちらでしたか😅」「セクション2だからシステムコールですね↓」

参考: manコマンドについて詳しくまとめました 【Linuxコマンド集】

STAT(2)                     Linux Programmer's Manual                     STAT(2)

NAME
       stat, fstat, lstat, fstatat - get file status

SYNOPSIS
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <unistd.h>

       int stat(const char *pathname, struct stat *statbuf);
       int fstat(int fd, struct stat *statbuf);
       int lstat(const char *pathname, struct stat *statbuf);

       #include <fcntl.h>           /* Definition of AT_* constants */
       #include <sys/stat.h>

       int fstatat(int dirfd, const char *pathname, struct stat *statbuf,
                   int flags);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       lstat():
           /* glibc 2.19 and earlier */ _BSD_SOURCE
               || /* Since glibc 2.20 */ _DEFAULT_SOURCE
               || _XOPEN_SOURCE >= 500
               || /* Since glibc 2.10: */ _POSIX_C_SOURCE >= 200112L

       fstatat():
           Since glibc 2.10:
               _POSIX_C_SOURCE >= 200809L
           Before glibc 2.10:
               _ATFILE_SOURCE

「manのセクション番号、6が『ゲームやデモ』ってなってますし😆」「セクション番号っていつまでたっても覚えられなくて😅」「自分も2がシステムコール、ぐらいしか覚えてませんけど😆」

参考: singleton method File.stat (Ruby 2.6.0)

rescueelseって書けるんですね↓😳」「書いてもよかったはず」「必ず実行するときは何でしたっけ」「ensureで〜す😆」「File.statはシステムコールのstat()を呼んで返しているだけですね☺️」

# actionpack/lib/action_dispatch/middleware/static.rb#L84
      def file_readable?(path)
-       file_path = File.join(@root, path.b)
-       File.file?(file_path) && File.readable?(file_path)
+       file_stat = File.stat(File.join(@root, path.b))
      rescue SystemCallError
+       false
+     else
+       file_stat.file? && file_stat.readable?
      end
# railties/lib/rails/commands/dbconsole/dbconsole_command.rb#L122
      def find_cmd_and_exec(commands, *args) # :doc:
        commands = Array(commands)
        dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR)
        unless (ext = RbConfig::CONFIG["EXEEXT"]).empty?
          commands = commands.map { |cmd| "#{cmd}#{ext}" }
        end
        full_path_command = nil
        found = commands.detect do |cmd|
          dirs_on_path.detect do |path|
            full_path_command = File.join(path, cmd)
-           File.file?(full_path_command) && File.executable?(full_path_command)
+           begin
+             stat = File.stat(full_path_command)
+           rescue SystemCallError
+           else
+             stat.file? && stat.executable?
+           end
          end
        end

(master)serializeメソッドでシンボルもstringにシリアライズするよう修正

model.some_string = :foo
model.save! # "foo" が永続化される

シンボルは永続化するときに上のようにstringにシリアライズされる。
このPRでは、シンボルをstringにシリアライズするImmutableStringクラスを更新して、ActiveModel::Attributeがこのシリアライズメソッドを呼び出してchanged_in_place?の戻り値を確定させる振る舞いと同じになるようにする。
この変更が行われる前は、以下のように"something"が変更されたと表示されていたが、変更後はcomment.something_changeがnilを返すようになる。
同PRより大意

comment = Comment.create! # (文字列の"something"フィールドがある)
# commentsテーブルかcomment.attributesの"something"フィールドで"anything"を永続化する
comment.update_column :something, :anything
comment.something  # or comment.attributes
comment.something_change
# 上は["anything", "anything"]になる
# `to`と`from`の値なのに同じになってしまう

つっつきボイス:「シンボルをシリアライズするときにstringにするということらしい」「前はシンボルの場合にto_sされていなかったと」

# activemodel/lib/active_model/type/immutable_string.rb#L10
      def serialize(value)
        case value
-       when ::Numeric, ActiveSupport::Duration then value.to_s
+       when ::Numeric, ::Symbol, ActiveSupport::Duration then value.to_s
        when true then "t"
        when false then "f"
        else super
        end
      end

(master)ActionController::Base.log_atを追加

ここからはcloseした6.0.1マイルストーンから見繕いました。

# 同PRより
# リクエストごとにログレベルを変える
class ApplicationController < ActionController::Base
  log_at :debug, if: -> { cookies[:debug] }
end

つっつきボイス:「Base.log_atでリクエストごとにログレベルを変えられるそうです」「ほほぉ〜😋」「うーん、こういうのって気持ちはわかるんだけど、消し忘れて惨事を招きそうな気がしないでもない😇」「同時実行制御あたりとの相性も気になるし」

# actionpack/lib/action_controller/metal/logging.rb
module ActionController
  module Logging
    extend ActiveSupport::Concern

    module ClassMethods
      def log_at(level, **options)
        around_action ->(_, action) { logger.log_at(level, &action) }, **options
      end
    end
  end

「使いみちとしては、うるさいログを黙らせるとかでしょうね☺️」「Authenticationコントローラのログはもう出さなくていいよ、とか😆」「惨事というのは、クライアントからcookieを仕込めるからですか?」「その辺ですね、中身を知っていればクライアントが『このリクエストだけログを出さないでくれ』みたいなのをやれる可能性があるといえばある😆」

(master、6.0.1)RedisCacheStoreが最大クライアントコネクション数到達時に安全にfaliするよう修正

# activesupport/lib/active_support/cache/redis_cache_store.rb#L477
        def failsafe(method, returning: nil)
          yield
-       rescue ::Redis::BaseConnectionError => e
+       rescue ::Redis::BaseError => e
          handle_exception exception: e, method: method, returning: returning
          returning
        end

キャッシュに使っていたHerokuのRedisに関連して最近サービス中断の憂き目に遭った。Redisサーバーが最大クライアント数に達していた。Redis Cache Storeにはコネクションエラー時にもデータを返せるようにするフェイルセーフがあるが、最大クライアント数に達したときのエラーはRedis::ConnectionErrorではなくRedis::CommandErrorである。データベースから新鮮なデータが返らず、ユーザーに500エラーが表示された。
この修正は単にrescueを変更してRedisのあらゆるエラーでリクエストをsaveする。もちろんエラーハンドラーにはエラーが報告されるので、エラーはユーザーが選択したメソッド経由で引き続き送信される。これは他のキャッシュストアと同様なので↓、実質的にすべてのエラーでsaveできる。

  • MemCacheStoreはすべてのDalliエラーをrescueする(Dalli::DalliError
  • ReadThis(別のRedisキャッシュ)はRedis::BaseErrorをrescueする
  • FileStoreは特定のrescueを宣言しないのですべてrescueする
    同PRより

つっつきボイス:「Redisはコネクション数が最大になったときのエラーだけ種類が違ってたのか😳」「ReadThisっていうRedis互換のキャッシュストアがあるんですね」「dalliとどう違うのやら😆」「そういえばdalliってありましたね(ウォッチ20180413)」

# sorentwo/readthisより
config.cache_store = :readthis_store, {
  expires_in: 2.weeks.to_i,
  namespace: 'cache',
  redis: { url: ENV.fetch('REDIS_URL'), driver: :hiredis }
}

「ReadThisは見た感じ普通のキャッシュgemですね☺️」「もしかしてReadThisってRedisのダジャレか😆」「Redisみたいにライセンスであーだこーだ言わないよみたいな😆」「😆」「そういえばちょっと前から議論になってましたね」「Redisが使えなくなると他もいろいろ死にますし😭」

参考: Redis Labsの2度のライセンス変更はフリーライドを防げるか - ITmedia エンタープライズ

(master、6.0.1)IE 9互換のためElement.closest()を回避

# actionview/app/assets/javascripts/rails-ujs/utils/form.coffee#L12
  inputs.forEach (input) ->
    return if !input.name || input.disabled
-   return if input.closest('fieldset[disabled]')
+   return if matches(input, 'fieldset[disabled] *')
    if matches(input, 'select')
      toArray(input.options).forEach (option) ->
        params.push(name: input.name, value: option.value) if option.selected
    else if input.checked or ['radio', 'checkbox', 'submit'].indexOf(input.type) == -1
      params.push(name: input.name, value: input.value)

つっつきボイス:「そういえばElement.closest()の互換問題ってありますね」「closest()便利ですし😋」「一番近い要素を取れるんでしょうか?」「ツリーを上に遡っていって一番近いところにあるものを取ってくるヤツですね☺️」「なるほど!」「いくつ上に上がればいいかわからないときにホント便利😍」

参考: Element.closest() - Web API | MDN

「この修正のファイル名見ると.coffeeってなってますけど、CoffeeScriptってまだ残ってるんですね😳」「入っているといえば入ってるみたいですね〜: 使われているかどうか知りませんが🤣」「🤣」

後で調べると、Action Viewのrails-ujs以下は現在もCoffeeScriptでした↓。

参考: rails/actionview/app/assets/javascripts/rails-ujs at master · rails/rails

なお#34177ではAction CableのCoffeeScriptがES2015に書き換えられました↓。

参考: Convert ActionCable javascript to ES2015 modules with a modern build environment by rmacklin · Pull Request #34177 · rails/rails

(番外、master、6.0.1)Arel.sqlをstable APIとしてドキュメントに記載

Arel.sql他のAPIドキュメントdeprecationメッセージから参照されているのに、それ自身にドキュメントがない。
同PRより大意

# activerecord/lib/arel.rb#L30
+ # 既知の安全なSQL文字列をラップしてクエリメソッドに渡せるようにする。例:
+ #
+ #   Post.order(Arel.sql("length(title)")).last
+ #
+ # SQLインジェクションの脆弱性回避には万全の注意を払うこと。<a href="https://techracho.bpsinc.jp/wp-content/uploads/2019/07/ransack-h_captured.png"><img src="https://techracho.bpsinc.jp/wp-content/uploads/2019/07/ransack-h_captured.png" alt="" width="400" class="aligncenter size-full wp-image-78429" /></a>
+ # このメソッドでrequestパラメータやモデル属性といった「安全でない」値を使うべきではない。
+ # 
  def self.sql(raw_sql)
    Arel::Nodes::SqlLiteral.new raw_sql
  end

つっつきボイス:「Arel.sqlに今までAPIドキュメントがなかったので足したそうです」「Arel.sql、こないだRansackあたりでちょっと使ったな〜😆」


activerecord-hackery/ransackより

その後社内Slackで、以下の記事↓とともに、回避手段としてのArel.sqlの存在感が再び増してきているのではないかという指摘がありました。ありがとうございます!🙇

参考: Rails 6 以降は order/pluck の引数に SQL 文字列を渡すことはできない (容易に対策可能) - Qiita

Rails

Pumaの新バージョンがリリース(Ruby Weeklyより)


puma.ioより


つっつきボイス:「Pumaの新しいのが出ましたね〜😋」「Pumaといえば以前公開つっつき会で見たあの動画ですね😆(ウォッチ20190708)」「ああ😆、バージョン3とさよならするあの動画↓」

「リリースノートには、URLでセミコロンが使えるようになったとかありました」

0番ポートはany

Puma now reports the correct port when binding to port 0, also reports other listeners when binding to localhost (#1786)

「リリースノートにある0番ポートとのバインディング↑ってどういうものでしたっけ...?🤔」「0番ポートはless /etc/servicesにはなかったけど、RFC1700に載ってるらしい↓」

参考: https://www.ietf.org/rfc/rfc1700.txt

Decimal Keyword Protocol References
------- ------- -------- ----------
0 Reserved [JBP]
1 ICMP Internet Control Message [RFC792,JBP]
2 IGMP Internet Group Management [RFC1112,JBP]
3 GGP Gateway-to-Gateway [RFC823,MB]
4 IP IP in IP (encasulation) [JBP]
5 ST Stream [RFC1190,IEN119,JWF]
6 TCP Transmission Control [RFC793,JBP]
7 UCL UCL [PK]
8 EGP Exterior Gateway Protocol [RFC888,DLM1]
9 IGP any private interior gateway [JBP]
(略)

「0番はreservedか」「well-knownポートか、思い出した: 任意のポートでソケットを開くときに0番ポートを使うんだった」「おぉ?」「自分でソケットを開くときに、空いているポートを使いたかったら0番ポートを指定すると開いてるephemeralポートから適当に割り当ててもらえる」「なるほど!」

参考: TCPやUDPにおけるポート番号の一覧 - Wikipedia

0番のポートはエニーポート(any port)と呼ばれ、アプリケーションに対して、動的に別番号の空きポートを割り当てるために用意された特殊なポート番号である。別番号のポートの再割り当てを行わずに0番のポートとして使用することは禁止されているため、利用上では注意が必要である。
Wikipediaより

「たしかephemeralポートの範囲はOSによって違ったはず」「そうなんですか!😳」

参考: エフェメラルポート - Wikipedia

  • IANA: 49152〜65535を提言
  • BSD: バージョン5.0以降はIANAの提言に従う
  • Linux: 32768〜61000が多い
  • Windows: XPやServer 2003までは1025〜5000、VistaとServer 2008以降はIANAに従う

「手元のAmazon Linuxの/etc/servicesを見てみると↓、well-knownポートは0〜1023、registeredポートが1024〜49151、ダイナミックなプライベートポートは49152〜65535となってますね」「へぇ〜!」「IANAのサイトにも膨大なリストがあるな↓」「142ページもあるとは😳」「すごい量🐳」

参考: Service Name and Transport Protocol Port Number Registry

The latest IANA port assignments can be gotten from
http://www.iana.org/assignments/port-numbers
The Well Known Ports are those from 0 through 1023.
The Registered Ports are those from 1024 through 49151
The Dynamic and/or Private Ports are those from 49152 through 65535
Amazon Linuxの/etc/servicesより

「でPumaの話に戻ると、0番で任意ポートで立ち上げられるようになったということですね」「なるほど〜😋」「空いているポートを自分で調べて使うのってだるいですし😆」「たしかに😆」「空きポートを調べる処理って案外重たいんですよね: netstatとかでやれますけど、仕組みがあるならそっちに任せてしまう方がいいかと」「そういえば最近はssコマンドでやるらしいと昨日知りました😆」「最近のコマンドはわかんないな〜😆」「ssってなんか覚えにくいし😆」

参考: netstat - ホストのネットワーク統計や状態を確認する
参考: 【 ss 】コマンド――ネットワークのソケットの情報を出力する:Linux基本コマンドTips(150) - @IT

スケールしたときのAPIエラーを正しく扱う(Ruby Weeklyより)

# 同記事より
def track_exception(exception)
  redis.hincrby("tracked_exceptions", exception.class.to_s, 1)
  raise Monolist::TrackedException.new(exception)
end

def poll_gitplace(user, client)
  time = user.gitplace_last_sync

  client.get_pull_requests({ created_after: time }).each do |pull_request|
    comments = client.get_pull_request_comments(pull_request)

    unless user.action_items.find { |s| s.github_id == pull_request.id }
      create_action_item(user, pull_request, comments)
    end

    time = pull_request.created_at
  end
rescue Gitplace::ConnectionTimeout => e
  track_exception(e)
ensure
  user.update!({ gitplace_last_sync: time })
end

見出し:

  • リトライは早期に、ただし正しく
  • 常にジョブの進捗を確認せよ
  • 失敗をトラッキングせよ

つっつきボイス:「常にmake progressせよはそのとおり: 進捗取れてないとログ見ても何が起こってるかマジでわからないし😅」「なるほど!」「この記事は、APIをいっぱい叩くようなアプリを書くときはこの辺をちゃんとやっときましょうねという定番の注意ですね☺️」「ふむふむ」「最近自分もAPI周りでハマって面倒くさいことになってて😆」

「APIで何かするときに、やりとりが有限回で済むかどうかが事前にわからないときがあるんですよ」「あ〜😳」「データセットのサイズがめちゃくちゃでかいと、データをぶん投げるまでにものすごく時間がかかったりしますし😭」「たしかに!」「stagingでは一瞬で終わるのに本番だと永遠に返ってこなかったりして、そういうときにプログレス出してないとつらいです😇」

RailsのDomain Objectsは善、Service Objectは悪(Ruby Weeklyより)

見出し:

  • Active Recordモデルの長所と短所
  • 「Active Record寄せ集め」アンチパターンの代替に使われるService Object
  • Service Objectがよくない理由
  • Service ObjectよりもDomain Objectがよい
  • もっと知りたい方へ

つっつきボイス:「久しぶりにオピニオン記事を見かけたので」「たしかにService ObjectよりはDomain Objectの方が設計としてはキレイになりますし、おっしゃるとおりという感じ😆」「😆」「Serivce Objectは自分にとって『設計を諦めて追いやる』的な位置づけというか😆」「😆」

「記事の見出しに『Active Record grab bagアンチパターン』というのがありますけど、grab bagはいわゆる『福袋』『寄せ集め』のことみたいなので、まあ闇鍋かなと😆」

「個人的にはService Objectってそれほどいいパターンには思えないんですけど、ただコードの見通しが悪くなったときに、昔なつかしいSOAP↓の内部API版みたいな感じで使うならワンチャンありかなという気はちょっとしますね🤔」「ふむふむ」「印象としてはそういうのがグローバルな名前空間にふらっと入ってくる感じ: もちろん使うならネームスペースちゃんと切りますけど😆」

参考: SOAP (プロトコル) - Wikipedia


Wikipediaより

「Service Objectにしたからといってキレイになったりはしませんし😆」「ただいろいろ条件を整えないといけないもので、かつ繰り返し実行するものならService Objectにしておいてさっと呼べるようにしておくはありかも🤔」「そうかも☺️」「Service Objectはそんなにうれしいパターンではないけど、価値がないわけではない💰」「コピペコードになりやすいのが難点かな😅」「Service Objectをキレイにキメるのは割と難しいです☺️」

Ruby/Railsのプロ開発者としての5年間を振り返る(翻訳)

「このDomain Objectは、この間のForm Object話(ウォッチ20190930)に出てきたApplication ObjectとかApplication Modelとかとはまた違うんでしょうか?」「Domain Objectは、たぶんここではビジネスオブジェクト的な、ビジネスロジックを置く場所という意味でしょうね☺️」「なるほど!」

「記事の下の方を見るとMartin Fowlerさんの薄いドメインモデル(Anemic Domain Model)↓のことが書かれてますね」「まさにエンタープライズアプリケーションアーキテクチャパターンで言われているような話かな☺️」

参考: AnemicDomainModel -- Martin Folwer

エンタープライズアプリケーションアーキテクチャパターンを読む: 1.概要

「なになに、『Devise gemはなかなかいい仕事をしてる』ですって🤣」「」「Devise嫌われてるけど🤣」「まあDeviseはでかいのと、どこを触ったら壊れるかがわからないのが怖いんですけどっ👻」

[Rails] Devise Wiki日本語もくじ1「ワークフローのカスタマイズ」(概要・用途付き)

Rails 6のZeitwerkを理解する(RubyFlowより)

見出し:

  • Zeitberkの方がよい理由
    • classicモードでのオートロード
    • Zeitwerkのオートロード
  • Rails 6のZeitwerk

つっつきボイス:「Zeitwerkの紹介というかおさらい的な」「内容はRailsガイドでも辿れそうな雰囲気でした」

参考: 定数の自動読み込みと再読み込み (Zeitwerk) - Rails ガイド
参考: 定数の自動読み込みと再読み込み (Classic) - Rails ガイド

「ところでZeitwerkはRubyのautoloadを使っているんですけど、autoloadはずうっと昔にRubyから消そうという話↓があったのをこの間教わって、今のRubyではどうなってるのかが気になりました🤔」「autoloadを消したいのは何となくわかるな〜: うまく動いているときはいいけど、ひとたび暴れ始めると大変そう😅」「下のissueの冒頭でも、autoloadはマルチスレッドで根本的な問題があるみたいなことが書かれてました」「そうでしょうね☺️」

「autoloadって結局消されないのかな?」「一度入れちゃったら無理でしょうね😆」「マルチスレッドのautoloadはカオスになるかもしれないけど、シングルスレッドだったらそういうことにはなりにくいんじゃないかな?🤔」「あとFiberとかなら😆」

「あ、今issueの一番下を見てみたらMatzが8か月前に取り下げてますね↓」「少なくともRuby 3.0では残すと」「8年越しの決定🎉」「autoloadは死なず」


同issueより

chef-sugar gemの作者が米国移民法に抗議のためリポジトリを閉鎖して議論に(Ruby Weeklyより)

https://twitter.com/shanley/status/1173692656192385024


つっつきボイス:「ああchef-sugarの問題ね☺️」「技術系じゃない英語記事ってあんまり読み慣れてなくて😅」「この件はちょっと前にもはてブで話題になってましたけど😆↓」「う、そうでしたか😅: じゃそっちで見ましょう」

参考: 政治的問題のためRuby GemsとGitHubからChef関連の諸々が消えた件について - tpdn blog

「これは元々、Chefの会社で働いてたメンテナーが、そこを辞めた後に元の会社がICEという政府機関と契約をしたのが気に入らなくて自分が関わったソフトウェアをリポジトリから消しちゃったという話😇」「すごいことするな〜😳」「1つだけかと思ったら他にも消してたとは😳」「あちこちでいっぱいアラートが鳴ってたでしょうね」「何だか壮絶😨」「ある日突然Railsがリポジトリから消えたらと考えたら...💀」

参考: アメリカ合衆国移民・関税執行局(ICE) - Wikipedia

「この件に関連して、オープンソースを反社会的なことに利用することについてのライセンス条項を見直すべきなのかどうかみたいな記事をはてブあたりで見た気がするんですが、今日ちょっと体力なくて見つけきれない😅」「そういう議論ってたしかに起きそうですね」「発端はさっきも話に出たRedisとかMongoDBなどのライセンス変更↓ですけど☺️」

参考: 「Redis」「MongoDB」「Kafka」が相次いで商用サービスを制限するライセンス変更 AWSなどによる「オープンソースのいいとこ取り」に反発 (1/3) - ITmedia エンタープライズ

後で探しましたが、やはりそれらしい記事は見つけられませんでした😇。

「Redisとかの件もそうですけど、それ以外にもたとえば人を害するプログラムを使ってはいけないという議論とかもあったりして、でもそれをどうやって実現するの?とか、とにかく最近のオープンソース界隈は議論が紛糾してますね☺️」「いろいろ考えさせられます😓」「う〜ん、やっぱり記事見つからないか😢」

「こういうのを見ると、gemをrubygems.orgとかから直接取ってくるんじゃなくて社内にリポジトリのミラーサーバーを立てて使うことで、サービス提供が中断しないように手を打つことも考えちゃいますね」「そういえばクックパッドさんとかもミラーやってますね」「そうそう、その方がより確実ですし☺️」「gemが汚染されたときにタイミングが悪いとミラーにしばらく残っちゃったりもしそう😅」「それもありますね」

html5_validators: RailsでHTML5バリデーションを自動でやれる

この間amatsudaさんのリポジトリ↓(forkなどは除外)を物色してて知りました。

参考: amatsuda (Akira Matsuda) / Repositories


つっつきボイス:「リポジトリのタイトルにRails 3, Rails 4, Rails 5、Rails 6ってあるのがスゴい💪」「クライアントサイドバリデーション?」「ああなるほど!モデル側にバリデーションを付けるとビュー側でHTML5のこういうrequiredみたいなタグ↓を自動で使ってくれるのね😋」「おぉ〜😍」

# 同リポジトリより
class User
  include ActiveModel::Validations
  validates_presence_of :name
end
<input id="user_name" name="user[name]" required="required" type="text" />


同リポジトリより

「そういえばsimple_form↓もそういうタグを付けてくれた覚えが」「やってくれるみたいです」「simple_form、自分は割と好きなんだけど他の人がだいたいキライなので最近はあまり押さなくなってきてます😅」「simple_formはカスタマイズしないのがコツでしたっけ」「そうそう😆」


plataformatec/simple_formより

「html5_validators、割とよさそうですね😘」「まあ油断するとブラウザごとのバリデーション実装の違いにやられることもあるかもしれませんけど😆」「フォームで数字を指定したときのちっちゃ〜な上下矢印なんかはブラウザごとに違ってるし、しかもたいてい使いにくい😆」「ブラウザのカレンダーもたいがい使いにくいですし😆」「ブラウザ側の実装の違いはつらいよ〜😭」「ブラウザ組み込みのデフォルトのバリデーションメッセージも、たしか英語レベルですらブラウザごとに文言が違ってて泣いたことあります😭」

その他Rails

つっつきボイス:「SQLite3用だそうです」「SQLite3は使ってないな〜😆」「自分はSQLite3割と好きです❤️」「よほどじゃないと使いませんし、たまに使おうとするとSQLite3がそもそも入ってなかったりしますし😆」


つっつきボイス:「これは?」「以前のRailsウォッチ(ウォッチ20181210)で、Railsチュートリアルの演習問題にupdate_columns(バリデーションやコールバックが発火しない)が使われているという話があったのをこの間やっと思い出して、作者のMichael HeartlさんにDMで知らせたところ一言注意を加えておくよという返信をもらったところです」「update_columnsの方が行数は少なくなりますけど、実際に使うとハマるやつですね☺️」


前編は以上です。

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

週刊Railsウォッチ(20191001後編)RedisとRubyをつなぐredis-object gem、Fullstaq Rubyの新バージョン、COUNT(*)とCOUNT(1)の速度ほか

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

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

Ruby 公式ニュース

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h


CONTACT

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