Quantcast
Channel: babaの記事一覧|TechRacho by BPS株式会社
Viewing all 101 articles
Browse latest View live

週刊Railsウォッチ(20190708-1/2前編)ActiveRecord::FixtureSetがめちゃ強くなってた、MacだとRubyが遅い理由、Puma 4登場ほか

$
0
0

こんにちは、hachi8833です。「👨‍🦲」という絵文字をSlackに貼ったらこんなふうにぶっ壊れたことで合字だということを知りました。


つっつきボイス:「Bald?」「人間の顔の絵文字にズラのコンポーネントをかぶせてたことが判明しました😆」「😆

参考: 👨‍🦲 Man: Bald Emoji
参考: 🦲 Emoji Component Bald Emoji

「そうそう😆、Unicodeってこんなふうに複数の文字を組み合わせて合字が作れるんですよね☺」「4人家族もパパとママと子ども2人を悪魔合体っぽく作ったりしてますね👨‍👩‍👧‍👦」「こういうのに長けたUnicode職人がいるんですよきっと😆」「こういう字は1文字でもバイト数めっちゃ多かったりするので、データベースにぶちこむと溢れたりすることがまれによくあるという😆

参考: 合字 - Wikipedia
参考: 絵文字一覧(家族:family)👪 | Let’s EMOJI

「そういえば最近のRubyだとなんとかlengthみたいなので取れますね」

後でやってみたらscan(/\X/).lengthで取れました。\Xがgraphemeの境界にマッチする正規表現だそうです。

# Ruby 2.6.3
» str = "👨‍👩‍👧‍👦"
» str.each_char do |i|
»   p i
» end
"👨"
"‍"
"👩"
"‍"
"👧"
"‍"
"👦"
#» "👨‍👩‍👧‍👦"
» str.length
#» 7
» str.bytesize
#» 25
» str.scan(/\X/).length
#» 1

参考: Ruby: 書記素クラスターを考慮して文字数を求める - Sarabande.jp

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

今回は7/4(木)に開催された「公開つっつき会#12」を元にお送りいたします。おかげさまで1周年にして満員御礼となりました。お集まりいただいた皆さま、ありがとうございます!😂

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

今回は公式更新情報コミットリストの両方からみつくろいました。公式情報が夏っぽいですね☀🏖🏄‍♀️

⚓Active Recordスキーマキャッシュのcolumns_hashのシリアライズとパースを止めた

#35891の続き。
columns_hashは、名前でインデックス化されたものを単純にcolumnsしているので、単にカラムのarrayからカラムを再インデックスした場合よりもシリアライズとデシリアライズがずっと遅い。
同PRより大意


つっつきボイス:「とりあえずコード見るのが早そう↓」

# activerecord/lib/active_record/connection_adapters/schema_cache.rb#L28
      def encode_with(coder)
        coder["columns"]          = @columns
-       coder["columns_hash"]     = @columns_hash
        coder["primary_keys"]     = @primary_keys
        coder["data_sources"]     = @data_sources
        coder["indexes"]          = @indexes
        coder["version"]          = connection.migration_context.current_version
        coder["database_version"] = database_version
      end

      def init_with(coder)
        @columns          = coder["columns"]
-       @columns_hash     = coder["columns_hash"]
+       @columns_hash     = {}
        @primary_keys     = coder["primary_keys"]
        @data_sources     = coder["data_sources"]
        @indexes          = coder["indexes"] || {}
        @version          = coder["version"]
        @database_version = coder["database_version"]
      end
...
      def columns_hash(table_name)
-       @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
-         [col.name, col]
-       }]
+       @columns_hash[table_name] ||= columns(table_name).index_by(&:name)
      end

「こういうHash[columns(table_name).mapみたいなのって遅くなりそうな印象あるし」「高速化系の改修ですね☺

⚓time.getutcの呼び出しを削減


つっつきボイス:「まさにタイトル通り: タイムゾーンが既にUTCだったらtime.getutcを呼ばないと」「わかりやすい〜🥰」「時刻変換ってどれぐらい重いんだろう🤔

# activemodel/lib/active_model/type/helpers/time_value.rb#L6
module ActiveModel
  module Type
    module Helpers # :nodoc: all
      module TimeValue
        def serialize(value)
          value = apply_seconds_precision(value)

          if value.acts_like?(:time)
-           zone_conversion_method = is_utc? ? :getutc : :getlocal
-
-           if value.respond_to?(zone_conversion_method)
-             value = value.send(zone_conversion_method)
+           if is_utc?
+             value = value.getutc if value.respond_to?(:getutc) && !value.utc?
+           else
+             value = value.getlocal if value.respond_to?(:getlocal)
            end
          end

          value
        end

「ベンチマークはついてないけど、実行命令数レベルでは減ってる😆」「7行から5行に🎉」「Railsはこういう地道な修正も多いですね☺


同PRより

⚓TranslationHelper#translateのハッシュデフォルト値の扱いを修正

translateメソッドがHashを1つ返すことを期待する場合がある(たとえばnumber.formatキーの場合)。
そういう人は次のようにデフォルトのHashを指定する必要があるかもしれない。
translate(:'some.format', default: { separator: '.', delimiter: ',' })
このやり方はI18n.translateメソッドで期待どおり動作するが、TranslationHelper#translateではデフォルト値にArray()を適用しているのでデフォルト値が最終的に[:separator, '.', :delimiter, ','](というarray)になってしまう。
同PRより大意

# actionview/lib/action_view/helpers/translation_helper.rb#L60
      def translate(key, options = {})
        options = options.dup
        if options.has_key?(:default)
-         remaining_defaults = Array(options.delete(:default)).compact
+         remaining_defaults = Array.wrap(options.delete(:default)).compact
          options[:default] = remaining_defaults unless remaining_defaults.first.kind_of?(Symbol)
        end

つっつきボイス:「Array.wrapってActive Supportのヤツ?」「…arrayだったら何もしない的なメソッドだったはず↓」「やっぱAPIdock見やすい〜😋


apidock.comより

# apidock.comより
Array.wrap(nil)       # => []
Array.wrap([1, 2, 3]) # => [1, 2, 3]
Array.wrap(0)         # => [0]

「あ〜なるほど、arrayでなければ必ずarrayでラップしてくれるのね❤

「エッジなバグでもあったのかな?」「…このテストケースに対応してる↓: 前はハッシュが単なるarrayになってた」「おかしな変換が残ってたのね☺

# 同PRより
  def test_hash_default
    default = { separator: ".", delimiter: "," }
    assert_equal default, translate(:'special.number.format', default: default)
  end

「(内職中ふと顔を上げて)ははぁ、Array()にハッシュを食べさせるとハッシュがarrayに分解されちゃうけど、Array.wrapすればハッシュのままarrayに入ってくれるのね😋」「それだ」「arrayの中にハッシュが入ってるのが欲しかったと☺」「translateするならそうあって欲しいし」「よく踏んだなこれ」

後でArray.wraprails cで試してみました↓。

development» hash = { separator: ":", delimiter: "," }
#» {:separator=>":", :delimiter=>","}
development» Array(hash)
#» [[:separator, ":"], [:delimiter, ","]]
development» Array.wrap(hash)
#» [{:separator=>":", :delimiter=>","}]

⚓URL破損チェックを修正

# activerecord/lib/active_record/database_configurations.rb#L137
      def build_db_config_from_hash(env_name, spec_name, config)
        if config.has_key?("url")
          url = config["url"]
          config_without_url = config.dup
          config_without_url.delete "url"

          ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url, config_without_url)
-       elsif config["database"] || (config.size == 1 && config.values.all? { |v| v.is_a? String })
+       elsif config["database"] || config["adapter"] || ENV["DATABASE_URL"]
          ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
        else
          config.each_pair.map do |sub_spec_name, sub_config|
            walk_configs(env_name, sub_spec_name, sub_config)
          end
        end
      end

つっつきボイス:「ほほぅ、URL形式のデータベースコンフィグが反映されてないことがあったと☺」「database.ymlって実はいろんな形式を食わせることができるんですけど、たしか前もウォッチで話した気がする(ウォッチ20181105)」「そうでした!」「ODBCなんかでよく使うmysql://なんちゃらみたいなヤツもRailsでは食えるんですけど、あの手のオレオレURLみたいなのって何か名前あるんですかね?🤔」「URLスキームっぽいといえばそうだけど、どうだろね〜、なさそう😆」「たぶんRFCとかにはないんじゃないかなって😆」「雰囲気URLスキーム🤣」「このプルリクは、そういうのをパースするあたりを修正したんでしょうね☺

参考: URL schemeの一覧


wiki.suikawiki.orgより

「そういえばRails 6の例のマルチプルデータベースで、複数DBをURL形式で指定した場合ってどうなるんだ?みたいな話をどっかで聞いたんですけど」「定義ごとにURL持つみたいな形になるだろうからどうってことなさそうだけど?🤔」「自分はdatabase.ymlでURL形式で書くときに別途ユーザー名とかパスワードとか書かないんですよね😆」「あ、Rails 6からdatabase.ymlが1階層増えるから、ユーザー名とかパスワードをバラして書かずにURLにまとめて書いたときに個別のユーザー名とかパスワードとかがどう扱われるかってことか😳」「まあそんな感じで☺

「今の話を簡単に説明すると、Rails 6でマルチプルデータベースにしたときのdatabase.ymlでURL形式で書いたときにどうなるかという話です: 今Sublimeで記憶ベースで要点部分だけ書いてみますけど、普通はこんな感じ↓」

production:
  primary: 
    host: 'hoge'
    port: '3306'
    user: 'user1'
    password: 'pass1'
  replica:
    host: 'huga'
    port: '3306'
    user: 'user1'
    password: 'pass1'

「それをたとえば以下↓みたいに書いたときにどう扱われるか、みたいな話です」

production:
  primary: 'mysql://user1:pass1@hoge:3306/test'
  replica: 'mysql://user2:pass2@huga:3306/test'

「まあ移行するときに頑張れば済むんですけどー」「そうそう、書き換えれば頑張れる😆」「以上雑談でした😆

⚓オートロードガイドに追記

# guides/source/upgrading_ruby_on_rails.md#L273
+#### Having `app` in the autoload paths

+Some projects want something like `app/api/base.rb` to define `API::Base`, and add `app` to the autoload paths to accomplish that in `classic` mode. Since Rails adds all subdirectories of `app` to the autoload paths automatically, we have another situation in which there are nested root directories, so that setup no longer works. Similar principle we explained above with `concerns`.

+If you want to keep that structure, you'll need to delete the subdirectory from the autoload paths in an initializer:

+ActiveSupport::Dependencies.autoload_paths.delete("#{Rails.root}/app/api")

+In `classic` mode, if `app/models/foo.rb` defines `Bar`, you won't be able to autoload that file, but eager loading will work because it loads files recursively blindly. This can be a source of errors if you test things first eager loading, execution may fail later autoloading.

+In `zeitwerk` mode both loading modes are consistent, they fail and err in the same files.

つっつきボイス:「例のZeitwerkの記述がちょっと増えたようです」「そういやZeitwerkって、おれドイツ人じゃないから発音わかんないんでホットロードだったか何か別の名前になるんじゃないかってRubyKaigiあたりの発表で誰か言ってたような気が😆」「マジで😆」「まだ名前変わってないかな😆」「ツァイトヴェルクってそんなに言いにくいかな〜😆

「ちなみにZeitwerkについて簡単に説明すると、Rails 6でオートローダー周りがZeitwerkというものに変わるんですね: 一応従来のローダーも使えるけど多少コンフィグが必要で、あとSTI使ってると場合によっては読み込み順を明示的に指定しないとエラーになることがある、みたいなことが移行ガイドに書いてますね」

参考: 「シングルテーブル継承 (STI)」Active Record の関連付け (アソシエーション) - Rails ガイド

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

そういえばKraftwerkもドイツ語ならクラフトヴェルク(=発電所)ですが、例のバンド名はみんなクラフトワークって英語風に呼んでるっぽいですね。

⚓Rails

⚓Puma 4が登場(Ruby Weeklyより)


つっつきボイス:「お〜Puma 4出ましたか!」「@schneemsさんの記事なんですが、ご本人のツイートのコラ動画が何だかかわいくて↓😍」「字幕が『ずっと一緒だよ』って😆」「そして3.42と4.0が泣き別れ😆」「3系はサヨナラと😆」「ズッ友って言ったのにって😆」「Reactorとかいう部分が変更されてAPIがちょい変わったみたい」

⚓RailsのWebサーバーあれこれ

「ついでにゲストの皆さんにお伺いしますけど、Railsで使ってるWebサーバーは何ですか?: じゃあなさそうなところから、Mongrelの人!」「え何それ?😆」「いないか😆、じゃWEBrickの人」「これもいないかな😆」「じゃPassengerの人!」「お〜1人いますね」「じゃUnicornの人!」「1人、いや2人😆」「じゃPumaの人!」「お〜多い」「…Thinは?」「そういえばThinってありましたね😆」「これもなつかしい😆」「Thin使ったことあった😆

参考: WEBrick互換の軽量WebサーバMongrel - [Ruby on Rails/Ruby] ぺんたん info
参考: library webrick (Ruby 2.6.0)
参考: Passenger - Enterprise grade web app server for Ruby, Node.js, Python
参考: unicorn: Rack HTTP server for fast clients and Unix
参考: macournoyer/thin: A very fast & simple Ruby web server

「やっぱり今だとデフォルトのPumaですねよ: Pumaもマルチプロセス・マルチスレッドで設定すれば全然速いですし苦労しなくなったし😋」「みんながPumaを使うようになったことでスレッドセーフに書くことを心がけるようになったであろうというか😆」「みんなどこまでスレッドセーフって意識してるのかな、なんて😆

「ついでに、Railsアプリでマルチスレッド周りのバグを踏んだことのある方は?Railsそのものじゃなくてアプリケーションのコードを書いたときに」「お、意外にいない😆」「オレはあるっ🤣: あれを一度でも踏むと、クラスインスタンス変数とかを見かけたときにコイツメ〜という気持ちになりますよもう🤣」「🤣」「🤣

参考: 【まとめ】インスタンス変数、クラス変数、クラスインスタンス変数 - Qiita

「そういえばUnicornのgraceful restartがどうやってもグレースフルになってくれなかったことあるし😇」「間違った方向に苦労してたりとか?😆」「手動でシグナル送ったりもういろいろやりましたよ〜😭、Capistrano書き換えてstop/startしたり」「そういうときは念のため数秒待つ🤣」「そうそう🤣、そしてstopしたらpsでマスタープロセスがちゃんと死んだのを確認してからリスタートするコードとか書きましたし😭」「Unicornだとちゃんと死んでくれないマスターっていたな〜😆」「Pumaにしてからまったくそういう経験ないっすね😋」「Pumaが突然死したんでUnicornに替えたことならあるといえばあるけど😇、今ならインスタンス増やすとかすればいいんだろうし」「今はメモリだけ軽く監視しとけばいいかみたいな☺

⚓Action TextをJSなしでテストする


つっつきボイス:「Action TextはRails 6で入ることになっているWYSIWYGエディタですね」「BasecampのTrixエディタを使ってるヤツ」「そのAction TextをJSなしでテストすると」「has_rich_text :content😆」「そんな感じのDSLが入ってたかも」「Action Textまだ使ったことないし😆

参考: Trix: A rich text editor for everyday writing


trix-editor.orgより

週刊Railsウォッチ(20181009)Rails 6の新機能:WYSIWYGエディタ「Action Text」、Rails 6の青写真スライド、Apache POIはスゴイほか

「ところでRailsでWYSIWIGエディタやるときって皆さん何をお使いですか?」「redcarpetかな〜」「redcarpetはよく使われますよね☺」「く、このリポジトリにデモがない…😇」「デモがないあたりが昔のgemっぽい😆

例のRailscasts↓に動画チュートリアルがありました。

参考: #272 Markdown with Redcarpet - RailsCasts

「他のWYSIWIGエディタを使ってる方は?」「んーと、フロアーラみたいな名前のWYSIWIGエディタをこないだ社内で使ってたかな」「お、これですね↓」「そうそう、Froala Editor」「うん、ひととおりのリッチテキスト使えるっぽい😋



froala.comより

参考: イカすwysiwygエディタFroalaをRailsに5分で導入

「Floala、新し目でオシャレ感あるし😘」「エディタでUndo使えるし!」「有能👍」「ところでこの記事↑でもFloala使ってるけど、gem 'wysiwyg-rails'でインストールって😆」「なんつう名前😆」「どーなんだろう、他のWYSIWIGをもラップする神gemな匂いありそうだけど😆」「この名前空間取るってどうよ😆」「これは攻めてる⚡」「攻めてる🔫

「Floalaのリポジトリ、2 years agoみたいなのがそこそこ目につく😆」「Floalaは一応商用がメインでライセンス料払うみたいな形式なんだけど、オープンの方の更新は微妙かも?🤔」「ということはRails専用とかではないわけですね😋」「まあnpmとか使わずに入れられるという感じなんだけど、今なら無理して使わなくてもいいんじゃね?って😆

「…CKEditorってのもあります😆」「CKEditorなら使ってもいいかな〜: でもWYSIWIGって外部ライブラリを使うと結局サーバー側でアップロードとかの実装を自分でやらないといけないのが面倒なんですよね😭」「昔はCKEditorぐらいしかまともなのがなかった🧐


ckeditor.comより

「こういうライブラリはどこまでまともなHTMLを吐いてくれるかが気になりますし😆」「自分の職場のiOSエンジニアがですね、世の中にはIMEで変換中にうまく動かないWYSIWIGエディタが多いってボヤいてましたよ」「うお〜それはキツそう😅」「もうね、気の毒すぎてお疲れさま以上の言葉をかけられないっすよ😆」「尊い犠牲😆」「WYSIWIGって入れようとするとホント大変だし、家帰りたくなるし🏠」「何回車輪を再発明したら気が済むんだの世界😆

「Floala、割と良さそうな印象❤」「wysiwyg-railsって名前を除けば🤣」「そうそう🤣

⚓hanmoto: 静的ページをhamlなどで書けるgem

// 同リポジトリより
// app/views/public_pages/404.html.haml:
- provide(:title, 'Not found')
%h1 Not found
%p This webpage is not found.
%p= link_to 'Home', root_path

つっつきボイス:「@junchitoさんの会社で使われてるhanmoto(版元)というgemだそうです」「お、こういうの以前にも見たような?🤔」「昔からあるといえばある☺」「hanmotoのリポジトリで、gakubuchi↓にヒントを得たってありますね😋」「自分が覚えてたのはgakubuchiじゃなかったけど、こういうのはみんな一度はやりたくなるヤツ😆

「Railsのエラーページを動的にやろうとすると『Railsが死んだときにエラーページを出せない』という問題がありますよね😆」「😆」「なのでエラーページは静的にする必要があるんですが、でもエラーページでCSSとかJSとかも読み込もうとするとアセットのファイル名についてるシグネチャとかをちゃんと扱えないと困る: hanmoto gemはそこらへんをいい感じにやってくれるんでしょうね☺」「Rails自身はそういう仕組みってないのかな…😅」「Rackが死んだときに表示するエラーはRailsでは対応できませんね🧐」「Railsで404.erbみたいなのが欲しいってこと?」「ああ、エラーページのerbをプリコンパイルするのをRails自身が持ってればいいのにってことね: まああればあったでいいかも☺

後で探してみたら、hanmotoと少し似た感じのrambulanceというgemもありました。

参考: 【動的VS静的】Railsの404/500エラーページ 静的の勝利 - 珈琲駆動開発

「まあエラーページを何もカスタマイズしてないRailsアプリが本番でデフォルトのエラーページを表示すると、なかなかショッキングな色合いが目に飛び込みますし🤣」「赤い赤い真っ赤っ赤🤣」「そしてむっちゃ電話かかってくる📞」「赤は刺激が強いかも😅」「その意味で、必要以上にユーザーを驚かせないエラーページを作っておくというのは大事だと思います🧐」「それが身を助けますね☺」「いつだったかAmazon Payでいつもの真っ赤な画面が出てましたし↓」「😆

「ユーザーがあの赤い画面を見ると『オレ、壊しちゃった?😱』って気持ちになりがちですし😆」「『大丈夫だよ』がユーザーに伝わるエラー画面が重要😆」(以下延々)

⚓初心者向けRSpecの書き方


つっつきボイス:「メドピアさんの記事ですけど、例のRSpecえかきうたと合わせて読むとちょうどいいのかなと思って」

「『describe/context/itのフォーマットが統一されていない』のコード例は確かに悲しい😭

# 同記事より
# contextが日本語だったり英語だったり式だったり
describe 'valid?' do
  context '非公開のとき'
  context 'publish'
  context 'expired < Time.now'  
end

# itに条件が書かれている
describe 'valid?' do
  it '◯◯で△△のとき、回答が登録されること'
end

「『letで定義した変数名が、何を表しているかわからない』、自分はlet否定派だけど😆」「なぬう😆」「とはいえ変数名を考えるのってめっちゃ時間かかるし、件数が多くなるとどんどんつらくなってくるし😇

「『beforeブロック内でテストを行う』、これはやっちゃダメでしょ😆」「書こうと思えば書けちゃうのか😱

「『テスト対象が同じ、複数のテストケースで、subjectが使われていない』、subjectを使うと読みやすくなるのかというと個人的には疑問ありますが🤣」「subjectがきれいにキマるといいんだけどね〜😎」「キマるとかっこいいけど、今のsubjectってどれ?っていちいち上の方見に行かないといけないのってどうかな〜って😆」「subjectが遠くなるとつらくなる😭」「結局ネストするとつらくなってくるし😅」「ネストとともに地獄感高まる👹

⚓みんなでうたおう『RSpecえかきうた』

「うちのメンバーが書いてくれた『RSpecえかきうた』記事↓、自分もイチオシなので😆読んだことのない方はぜひ一度どうぞ」

RSpecえかきうた

「前半は普通にitでテストケースを並べて、expect書いて、前提条件を書いて、ってやってくわけですね」「どきどき😋」「で後半は『やっぱ離れてると読みにくいよね』って途中からitをまとめてって、contextitに合流させて、するとsubjectがないのにitって何だかヘンだからitやめてtestって名前にしましょって😝」「するとRSpecがあっという間にかわいいアレのようになってしまうわけですよ🤣

「こ、これは🤣」「mとiとnとiがつくテストに🤣」「えかきうたがいつの間にか替え歌🤣」「そりゃもうアンチでしょ〜😛」「自分は好きっ🤣」「業務のテストコードってdescribeとかきれいに書けないことの方が多いくらいだし、もうこれでいいんじゃね?って思うことは、 多々ある😆

「このRSpecえかきうた記事、Twitterでもはてブでも『最高です』というコメントだけが付いてました😆」「もうアンチしかいない🤣」「みんなアンチ🤣」「RSpecで疲れた人にひとときの安らぎを☺」「一服の清涼剤🍹

とっても引用したい動画があったのですが、やめときます😅。察してください。

「ちなみにRSpecでテスト書いてる人は?」「やっぱRSpec多い」「minitestの人は?」「お、いますね〜😳」「他のテストフレームワーク使ってる人は?」「さすがにいない😆」「ZenTestとかは?」「今は亡きZenTest😆」「いないかやはり〜😆

⚓ActiveRecord::FixtureSetがスゴくなってた

↑もともと上の記事を取り上げようとエントリしてたのですが、FixtureSetの話があまりによかったので組み替えました。


つっつきボイス:「そうそう、今日のミーティングで出た話なんですけど、テストデータをFactoryで管理するのとFixtureで管理するのとでそれぞれメリットとデメリットがあるよねって」

⚓fixtureとfactoryについて

「fixtureとfactoryは皆さんご存知ということでいいでしょうか?」「fixtureは知らない人いるかも?🤔」「fixtureはRailsが最初から持っている仕組みで、fixturesというフォルダの下にyamlファイルを置いておくとテスト時にそれがいい感じに1行ずつ展開されてデータベースに入ってくるヤツで、生データを置いているような感覚に近い」

factoryは、基本的な定義はfactoryに書くんですけど、実際のデータはコードで作ります: factory createとかtraitとかを使ったりすることもあります」

FactoryGirlでtraitを使うとintegration test書くのが捗るという話

「ついでに皆さんどっちをお使いでしょうか?、まずはfixtureの人」「ほいっ」「おお、fixtureなんですか?」「オレfixtureでしか書かない😆」「へ〜、以前factory_bot(当時はfactory_girl)の記事とか書いてたのに😆

「factory_botって、テストが増えてくるとすごく遅くなるのが問題なんですよ🐢」「あ〜」「一方fixtureを全部グローバルなfixtureでやれると、最初にガッと全部読み込んで後はトランザクションでやれるから速い🐰: その代り、そのガッと読み込みを実現するまでの作業はつらい😇

「整合性のあるfixtureを書くのってつらくないですか?」「そこはまあそんなに大変でもないんですけど、大変なのはグローバルfixtureだと全部入ってくるんで本番データでテストしているみたいな感じになってくるところで、User.firstとかUser.lastみたいなのが気軽に使えなかったりとか」「なるほど〜」

「fixtureでやるときは、考え方をfactoryのときとは変えた方がいいというのはありますね🧐: たとえばファイル名を変えてそのときだけロードするみたいな設計をある程度最初から考えとかないといかないとか」「このテストコードではこっちのfixture、あちらでは別のfixtureみたいな感じですね」

「でもfixtureって自由度高く書けるから、昔ほどアンチにすることもないかな〜って: 初期段階からやれるんならfixtureでやってみるといいんじゃないでしょうか😋

「続いてfactoryの方は?やっぱりいますね: 反論オーケーですよ😆」「そうですね、fixtureだとどんなふうにデータが入ってくるかとかって、特にチームに初めて参加する人はfixtureを全部見ないとわかりにくいかなと思うんですよね」「たしかにfixtureだとidも生idが入ったりしますし🤔

⚓ActiveRecord::FixtureSetがいつの間にかスゴくなってた

「それがですね、ここだけの話、実はfixtureにはActiveRecord::FixtureSetといういいものがあるんですよ奥さん❤」「ほほぅ?」「多くの人はfactory_girlにやられる前のfixtureしか知らないかもしれないんですけど、以下のサンプルコード↓を見るとだいたいわかります」

参考: ActiveRecord::FixtureSet

「え、ダイナミックfixtureとかある?」「ナウいfixutureにはあるんですよ〜😆

<!-- api.rubyonrails.org より -->
<%Q1.upto(1000) do |i| %Q<
fix_<%= i %>:
  id: <%= i %>
  name: guy_<%= 1 %>
<% end %>

「つかもうちょっと下のアドバンストfixtureの方がむしろ普段づかいするところで、たとえばidなんか自動生成してくれるし↓」

george:
  id: 1
  name: George the Monkey

reginald:
  id: 2
  name: Reginald the Pirate
george: # generated id: 503576764
  name: George the Monkey

reginald: # generated id: 324201669
  name: Reginald the Pirate

belongs_toなんかもこうやって扱えるし↓」

### in pirates.yml

reginald:
  id: 1
  name: Reginald the Pirate
  monkey_id: 1

### in monkeys.yml

george:
  id: 1
  name: George the Monkey
  pirate_id: 1

「おお、末尾のpirate: reginaldreginald:を参照してくれるのか😳」「だからfactory_botとかでリレーション作るよりは楽ですね〜😋」「たしかに〜😍

### in pirates.yml

reginald:
  name: Reginald the Pirate
  monkey: george

### in monkeys.yml

george:
  name: George the Monkey
  pirate: reginald

「結局factory_botの何がつらいって、複雑なリレーション入れ子になったデータを作るのがつらいんですよね😭」「相互参照も地獄感あるし😈」「それ、誰かが作ってくれてればいいんですけど😆、自分でつくるのはホントしんどいし、あったらあったで今度はそれが要らなくなったときに困るんですよ😭」「それを消すと?」「他のテストに影響出ますね〜😢」「createすると思わぬところで使われたりとか😅

「fixtureの続きを見ると、ポリモーフィックbelongs_toもやれるし!」

### in fruit.rb

belongs_to :eater, :polymorphic => true
### in fruits.yml

apple:
  id: 1
  name: apple
  eater_id: 1
  eater_type: Monkey

「has_and_belongs_to_manyもやれますし😋

### in monkeys.yml

george:
  id: 1
  name: George the Monkey
  fruits: apple, orange, grape

### in fruits.yml

apple:
  name: apple

orange:
  name: orange

grape:
  name: grape

ラベルのinterpolation(式展開)もやれますし😋: まあこのラベル名が何やら一意の整数値になってるみたいなんで、スペルミスするといつまでたっても関連付けられなくて他のが関連付けられちゃう、みたいなつらいこともありますけど😆」「何だかマクロ的に展開される?」「まあそんな感じで」

geeksomnia:
  name: Geeksomnia's Account
  subdomain: $LABEL
george_reginald:
  monkey_id: <%= ActiveRecord::Fixtures.identify(:reginald) %>
  pirate_id: <%= ActiveRecord::Fixtures.identify(:george) %>

「YAMLのdefaultsも使えますよ〜😋」「おぉっ!」「ただこれは大文字のDEFAULTSにしないと普通のラベルとして解釈されちゃうとか、arrayでしか使えないみたいなのがあって、fixture使っている人が少ないせいか、ドキュメントにそうやってポロポロ穴開いてますし😆」「なるほど、DEFAULTSが予約語になってると」「これはもっと使ってフィードバックしないと!」

DEFAULTS: &DEFAULTS
  created_on: <%= 3.weeks.ago.to_s(:db) %>

first:
  name: Smurf
  *DEFAULTS

second:
  name: Fraggle
  *DEFAULTS

fixture model classもアンスコ付きの_fixture:でやれるし: これを使ってたとえばusersとerror_usersみたいにファイルを用意しておいて、specごとにfixtureコマンドで呼ぶことでテストケースを分けたりできますし」「は〜なるほど!、fixtureはデフォルトで実在のモデル名を使うけど、モデル名じゃないのを_fixture:で定義しておくと読み込めると」「読み込んで、さらにグローバルfixtureとは別にこっちを使ってねって指定できる」

_fixture:
  model_class: User
david:
  name: David

「いやだ何これスゴい」「やべ〜、こんな賢いfixtureが書けるなんてっ🥰」「fixtureでここまでできるとfactoryってあんまり要らなくて、むしろトランザクションだけでたいてのテストが書けるっつーか☺

「しかしいつの間にこんなにスゴくなったんですか?」「まあ少しずつ増えてたみたいで、Rails 5の頃にはだいたい今のような感じになってたかな〜😋」「何だか『眼鏡を取ったらスゴい美人だった』みたいな😆

「そういうのを調べるにはやっぱりAPIdock↓に限る💪」「なるほど、4.0.2から入ったと」「APIdockだとその機能がいつから入ったかがわかるのがとってもいい😘」「ラベルのinterpolationとかERB直接扱えるようになったのは割と最近だった印象ですけど」


apidock.comより

「fixtureは、たとえばステージングより前の段階でQA用のサンプルデータを作ったりするのにもよかったりしますね❤: fixtureってステージングでもdevelopment環境でも使えるんで、fixtureでテストケースをばばっと書き並べておくとか、本番からfixtureにダンプしてそこから削っていくとかみたいな用途ならfactory_botよりもいいかなって🥰

「あとfixtureのデータの整合性の問題ですけど、いったん全部ぶっこんでからvalidかどうかで流せばいいだけなんで大したことないと思うし」「たしかに!整合性はたまにチェックすればいいぐらいかと😋」「そうそう☺

「そういうわけで皆さん一度ActiveRecord::FixtureSetはチェックしてみるとよいかと🕶」「少なくともDHHはfixture派だろうし、そこは譲らないだろうし😆」「今日からfixture派!😍」「きっとCIが早く終わって地球に優しいっすよホレホレ🌎」「このAPI翻訳しようかな✨

⚓その他Rails

つっつきボイス:「Deviseの動きをトラックするgemみたいです」「どちらかというと証跡ログっぽいかな?」「Geocodingとかもあるから、こいつはどこからログインしたのかみたいなのをデータベースに入れてくみたいな」「上の方のHow It Worksあたりに書いてあるかな〜: ログインの成功/失敗とかIPアドレスとかを記録できるから、やっぱり証跡ログかな」「この手のログは提出を求められることがよくあるし、とりあえず入れとけ感あるかも☺」「GeoIPとか入れとくと、やたら中国からアクセスあるなみたいなのも取れるし😆

参考: GeoIP2 Databases | MaxMind


つっつきボイス:「これは来週渋谷で開催されるTokyo Rubyist Meetupという英語話者を前提としたミートアップなんですけど、誘われたのでちょっくら行ってきます↓」「そういえば前からやってますね〜☺

scenicか久しぶりに聞いたな〜」「この間の銀座Rails↓でもscenicのこと話しましたし😆」「そうそう😆」「ミートアップのお題もdatabase viewsだから怖いくらい既視感ありますね😆」「やっぱデータベースでやった方が速いことはデータベースでやろうよって🤣

RDBMSのVIEWを使ってRailsのデータアクセスをいい感じにする【銀座Rails#10】


⚓Ruby

⚓impersonator: オブジェクトのやりとりを記録/再生(Ruby Weeklyより)

# 同リポジトリより
calculator = Impersonator.impersonate(:add, :divide) { Calculator.new }

つっつきボイス:「impersonateって『偽装する』でしたっけ」「なるほど、オブジェクトインタラクションを記録して再生すると」「なんかVCRって文字見えたし😆」「VCRってVideo Cassette Recorderだからビデオデッキでしたっけ」「VCRってgemがあるんですよ↓」「そうそう、HTTPリクエストを記録して再生するヤツ」「HTTPSpyなんてのもあったような」「impersonateはそういうののメソッド版というか」

「しかし何でまたこういうの作ったんだろ🤣」「やりたかったからとか😆」「VCRにインスパイアされたってあるけどどうしてこうなったというか😆」「使いみち何かあるかな〜🤔

⚓MacだとRubyが遅い理由(Ruby Weeklyより)


つっつきボイス:「最近Mac使ってないからわかんないけど😆Ruby遅いの?」「Discourseが出してたベンチマークでもMacが遅いって出てたの見たな〜: WindowsでVM使うより遅いって」「えぇそんなに遅い?😅」「お、記事にもまさにそのツイートが↓」

「リストの上の方がMacで独占されてるし🤣」「だっはっは🤣(マカーだけど)」「これはひどい🤣」「Win10のVMwareで動いてるLinuxにすら負けてるし😇」「か、悲しい😭」「どうしてこうなった😇

「下の速い方を見るとさすがに素のLinuxのRubyが速いですね」「しかしディストリで差が出ているのはなぜ?って思うし😆」「デフォルトのカーネルパラメータとかが関係してそう」「果たしてMacのSSDが原因なのか、それとも何なのか」「上と下でダブルスコアぐらい違ってますし😆」「@samsaffronさんの記事↓はタイトルではWindowsの話かと思ったらMacの方が遥かにやべーじゃねーかって😆」「WindowsのHypervisorはベンチ入ってないのかな?」

参考: Why I stuck with Windows for 6 years while developing Discourse

「MacのRubyが遅いのは、果たしてMach-Oバイナリだからなのか、それともmacOSのHypervisor層が遅いのか、いろいろご意見が出そうではありますね🤔」「どうなんだろね〜?」「もしMach-Oが原因なら、Hypervisor層でLinux動かしてそこで動かせばもう少しマシになったりして」「どっちにしろ遅い🤣」「とほほ🤣」「それにしても負けすぎにもほどがあるというか😆

参考: Mach-O - Wikipedia

「というわけで皆さんWindowsマシンに乗り換えましょう」「🤣」「🤣」「おやこんなところにWindows機が😝

追記(2019/07/08)

上の測定結果はあくまで雑談の参考どまりとお考えください。

⚓Rubyの「Direct Instruction」


同記事より

つっつきボイス:「@tenderloveことAaron Pattersonさんの記事ですが、いつもの自分のサイトではなくGitHubのブログに書いてますね」「こういうアニメーション↑作ってるのはすげー」「@tenderloveさん、最近GIFアニメに入れ込んでますね: 自分もTechRacho記事でこういうのやりたいなと思いつつ😅」「どんなツール使ってるんだろ?」「聞いてみたら案外パワポかも😝」「🤣」「案外その方が楽だったりして😆

「ASTツリーとかもみっちり書いてる↓」「Rubyは新しい技術をこうやって解説してくれる人がいるからいいですね〜😋」「Linuxだと最新技術を学べる書籍がそもそも最近なくなってるし😇」「Linuxの最新カーネルなんかだと、最近は大学の研究室なのか誰なのか結構みっちりドキュメント書いてたりするんですけど、どれを信用していいのかよくわからないという🤣」「🤣


同記事より

⚓その他Ruby


つっつきボイス:「最近流行りのRuby型チェッカー」「まだちゃんと読んでませんが、型チェックについて思うところを書き連ねてますね」

「Rubyの型チェッカーをJetBrainsのIDEとかが採用するようになるとまた世の中が変わってくるかなって」「JetBrainsのIDEなら、型チェッカーがない今でもめちゃうまく回ってるんじゃないかって😆」「ですよね、今でもIDEがYardのドキュメントとかを参照して引数とか型のヒントとか出してくれますし😋」「取りあえず押せば何か出てくる😆」「Yardでちゃんと書かれてさえいればヒント出してくれるとかホント神がかってるし⛩」「Rubyの型チェッカーはむしろVSCodeとかの方がうれしいんじゃ?」



つっつきボイス:「@k0kubunさんのメモ書きだそうです」「そういえばRubyのVMを書くためにJVMを書いてみることにしたとかスゴいことをやってるらしき」「マジで😆

↓こちらのようです。

参考: セルフホストで学ぶJVM入門 - k0kubun’s blog

⚓Quoraより


つっつきボイス:「最近QuoraでMatzの回答を読むのが楽しみで😋」「Rubyのグルがこうやって一次回答を示してくれるのはいいですね〜☺」「しかも日本語で🇯🇵」「Quoraだと英語ロケールは自動翻訳されるのかな?🤔

「Rubyのブロック、そういえば他でほとんど見かけないですね🤔」「Rubyのブロックのよさは、何といってもブロックを1個しか取らないと決めたことでしょうね〜」「それはある!👍」「おかげで記法が複雑にならずに済んだし😋


前編は以上です。

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

週刊Railsウォッチ(20190701)RMagickのメモリ使用量が劇的に改善、インスタンス変数の定義順で速度が変わる?、GitLab CIランナーをローカルで回すほか

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

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

Rails公式ニュース

Ruby Weekly


週刊Railsウォッチ(20190709-2/2後編)strong_password v0.0.7がハイジャックされていた、TerraformとCloudFormation、CSSの設計ミスリストほか

$
0
0

こんにちは、hachi8833です。自宅のプロバイダを某Tier 1配下のところに変えたら、それまで深夜で300Kbpsしか出なかったのが60Mbps台まで一気に改善されました😂

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

⚓臨時ニュース: strong_password gemのv0.0.7がハイジャックされていた(RubyFlowより)

詳しくは元記事をどうぞ。CVEも発行され、現時点では分析待ち状態です。

現在のstrong_passwordは0.0.8にアップデートされています。

Railsアプリで実際にあった5つのセキュリティ問題と修正方法(翻訳)

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

⚓Zabbix: オープンソースの大規模総合監視ソフトウェア


zabbix.comより

この間のWEB+DB PRESS↓の書評で知りました。

今月の『WEB+DB PRESS』Vol.111「詳解Rails 6」は読んでおきたい!


つっつきボイス:「監視ソフトというとNagiosとかはウォッチでちょくちょく登場してたんですけど、Zabbixってやっぱり有名なんでしょうか?」「そりゃもう〜😆、そもそもいつからあったんだろうというぐらい歴史ありますし🗽」「90年代ぐらいからあった気が👴

「ちなみに今回お集まりの皆さんの中でインフラ系をやってる方は?」「割と少ない😅」「ちなみに今ボクの隣りに座ってる方は全部やってます😆」「ども〜☺

「Nagiosとかは比較的シンプルだけど、Zabbixはでかいところで使われてるイメージのある監視系ツール: あくまでイメージね😆」「NTTコム ソリューションズみたいなでかい会社がパートナーにずらっと並んでるし、たしかにでかそう🗻」「たしかマスターZabbixの下にサブのZabbixがたくさんぶら下がるみたいな大規模構成が取れて、設定が難しいという印象ありますね😆

参考: Nagios - The Industry Standard In IT Infrastructure Monitoring


nagios.orgより

「そういやCactiなんてツールもありますね〜: スペルはCactus(サボテン)の複数形で 」「カクタイ!」


cacti.netより

サボテンで何となく思い出したのが映画『Three Amigos』の「歌う灌木」と「透明剣士」のシーンでした🌵。10年に一度ぐらい見返したくなる映画です🎥

参考: サボテン・ブラザース - Wikipedia — 邦題


「監視って割とやることが多いんですよ😆: 監視サーバーが落ちてないかどうかをどうやって監視するかとか、落ちたらどうするかとか、あと監視の頻度を上げすぎるとそのヘルスチェック自体がシステムにとって負荷になってしまうとか😆

「今ほどCPUとかの性能が高くなかった昔によくあったのが、ヘルスチェックが毎回TLSコネクションを開いて閉じていたせいでパフォーマンスががっつり落ちる、みたいな事案😆」「まあ今はELBとか便利なものがありますし😋

「…最近だと、監視サーバーを2つ立てて相互監視させるのと、マネージドな監視に寄せるのとどっちが主流なんでしょうか?」「2つ立てて相互監視、前はよくやってましたね〜」「相互監視は今もやってるところはやってると思いますけど、今だとマネージドに寄せて金で解決💸の方が多いんじゃないかな〜🤔」「一部SI業界ではマネージドを入れたがらない傾向あるみたいですが🤣」「そういうところは、それこそこういう大規模監視ツールで相互監視してたりするんでしょうね: 自分やりたくないけどっ🤣

「そういえば、外に出す開発案件なんかだと、Zabbixクライアントをインストールすることが開発要件に入ってることあったし」「Zabbixぐらいになると管理画面を分散することなんかもできそうですね😋

⚓Terraform vs CloudFormation


つっつきボイス:「今日のWebチームミーティングでTerraformが話題になったので」「そうそう😋: ついでにお伺いしますけど、TerraformCloudFormationをお使いの方は?」「Terraform使ってます🌏」「他のを使ってる方は?」「CloudFormationをちょっと使ってます☁」「素のCloudFormationですか?」「Cloud9がCloudFormationを使ってるので、そこらへんをカスタマイズするのに使う感じで☺」「なるほど、既存のCloudFormation環境でやってると」


terraform.ioより


aws.amazon.comより

「この種のインフラのコード化っていろいろあって、最近だとGCPのTerraformerが話題になりましたね」「既存のインフラからTerraformファイルを生成するリバースTerraformって見出しにある😳」「既に定義されているAWSのリソースとかにこれをかけると定義ファイルを作ってくれるそうです😋」「知らなかった〜😅」「つまり、それまで手動でダッシュボードをポチポチして作ったリソースをTerraformerでズバッと定義ファイルに出してくれる」「ふぅむ」


同リポジトリより(ただしTerraformer作者のアイコン)

「AWSで細かいものを大量に作ってきた後でTerraform定義ファイルをスクラッチで書くのってつらいじゃないですか😢: 普通に考えても、EC2インスタンスを立ち上げるだけでも、そのためにはまずVPCが必要で、VPCにはVPCサブネットが必要で、VPCサブネットが外に出るためにはInternet Gatewayが必要で…とめちゃめちゃ依存関係が増えてくるんですよ😆」「😆」「なんでTerraformerでどかっと出せるとありがたい🙏: 本当にすべてのリソースをどかっと出してくるんで量がスゴいですが😆、それでもないよりは全然いい😋

参考: Terraform によるインフラ構築 | もばらぶエンジニアブログ

⚓追いかけボイス

AWSを対象にリバースするTerraformingというツール↓もあると先ほど教わりました。Terraformerと名前が接近しすぎてる気が😅

⚓その他インフラ

つっつきボイス:「Slackの共有チャンネルか〜」「皆さんの中でSlackをお使いの方は?」「お〜、全員使ってる🏆」「じゃSlackの共有チャンネル機能は?」「合併した会社同士のSlackをつなぐのに使ってます」「なるほど、そういう使い方もあると」「でもいちいち招待しないといけないとかボットが動かないとかがあってちょっと悲しい😢」「そうなるとSlackをマージする方がよさそうですね😆

「BPSは受託開発をやっている関係で外部とのやりとりに共有チャンネルを使ってみたんですけど、やってみるとちょっと挙動が怪しい部分があるんですよ😅」「あ〜」「共有チャンネルのこっち側と向こう側に同じユーザー名のユーザーがいる場合にどう表示されるかとか、共有チャンネルを切断した後のログがどうなるのか、とか」「ふ〜む」

「これまで共有チャンネルを少し使ってみた限りはですが、双方のチャンネルに同じストリームが流れるようになっているらしくて、どうやらチャンネルをぶった切っても双方に残っているっぽい: でもこのあたりはちゃんと実験してみないとわかりません🤣」「🤣


⚓DB

⚓SQLのランダマイズテスト(DB Weeklyより)


cockroachlabs.comより


つっつきボイス:「CockroachDBってあったな〜」「名前だけは聞いたことありました」「記事によるとSQLSmithというのをやってるところがあるようだ↓」「SQLSmithはSQLクエリをランダムに生成するんですね」


同リポジトリより

参考: Google Spannerクローンを目指した「CockroachDB」正式版リリース。クラウド上にグローバル展開することで大規模障害からも生き残れる分散型SQLデータベース - Publickey

「記事ちゃんと読まないとわからないけど、業務のテストパターンを作るとかではなくて、RDBMSそのものの開発のためにパーサーに総当たりテストをかけるためのツールのように見えますね」「あ〜そっちでしたか😅」「valid SQLをチェックすると書かれてるのはその辺を指してるのかなと☺」「記事の後半の図にもLexical Analyzerとか書いてある↓からそれっぽいですね」


同記事より

「前にもウォッチで話したと思うんですけど(ウォッチ20190128)、Ruby本家でも、どうやって見つけたのか見当もつかないようなニッチなバグを次々に発見するコミッターたちがいますよね💎: ああいうのもランダムに生成したり改変したコードを使って見つけたりするんじゃないかって」「そうでした!」「Rubyのパーサーをどうやって普通じゃない方向からイジメるかみたいな😆

「SQLSmithはこれ↓にインスパイアされたってリポジトリにありますね」「このCsmithはC言語で同じことをやってるんでしょうね☺」「smithは人の名前にもなってますけど、本来の『鍛冶屋』の意味でしょうね」


embed.cs.utah.edu/csmithより

⚓その他DB


pgibz.ioより


つっつきボイス:「Ibizaというぽすぐれのカンファレンスだそうですが、『一味違うカンファレンス』って何だろうと思って」「たしかにぽすぐれのカンファレンスだけど世界規模というよりはローカルな催しかな?🤔」「On the beachとかロケーションがいいっすよね🏖」「あ〜たしかに🏝」「会場出たらいきなりビーチとかいいな〜⛵」「Ibizaってどこでしたっけ?」「スペインというか地中海?」「むっちゃ風光明媚🌅

参考: イビサ島 - Wikipedia

案内を見ると「英語ベースの国際カンファレンス!」「ご家族連れで長期滞在を!」とかありますね。

スケジュールが「WED 19TH〜FRI 21TH」という日本で馴染みのない表記しか見当たらず、いつだろうと思って調べたら「6/19(木)〜6/21(金)」でした。終わってた😇

⚓JavaScript

⚓ECMAScriptの現状と将来


つっつきボイス:「はてブで見つけたんですが、TechRachoの記事が引用されてたので貼りました(正規表現記事ですが😆)」

「ところでこの中でJSを自分で書く方はいます?」「あ、意外に少ない😳」「JSのバージョンは?それともTypeScriptだったりします?」「いえ、TypeScriptではないんですが、jQueryとVueが混じってる過渡的な状況で、かつCoffeeScriptで書いてます」「お〜なるほど!」「なので早くES5の世界に行きたいなって思ってるんですけどなかなかそうもいかず😅

2019年前半の「JavaScriptをちゃんとやるための地図」

「ついでに弊社CTOはどんなJSを?」「ボクTypeScriptしか使ってません🧐」「TSからの最終出力はどんなものに?」「Electronで動かすときは適当にES2015とかES2017あたりで出して、ブラウザで動かすときはBabel通してます☺」「どのレベルまで落とします?」「IE11が動くところまで」「IE11だとECMA以前になるんでしたっけ?」「いえ、ES2015はダメですがES5ならいけますね」「お〜」

参考: Babel · The compiler for next generation JavaScript


babeljs.ioより

「つーことでES5以上はもう当たり前と😆」「それより前は触りたくないですし😆」「自分も大学で教えるときはES6でやってますし🎓: でないとつらすぎて」「今日はJS勢が少なめなので流していきましょう😆

⚓GoogleのAMP記事


つっつきボイス:「これもはてブからです」

AMP(Accelerated Mobile Pages)について簡単に説明すると、Webページのスタティック版みたいなものをAMP HTMLの仕様に沿って作ると、Googleのクラウドにキャッシュされて爆速で表示されるというものです」「今モバイルのブラウザはだいたいAMPに対応していて、モバイルでGoogleのページで検索するとAMPのページが表示されるはずですし、SmartNewsやはてなブックマークなどのモバイルアプリでも、AMPページがある場合はそちらを表示するようになってますね」


amp.devより

「Googleが『モバイルではAMPに対応したページのスコアをアップするよ』的なアナウンスをしたことでAMPが大流行して、今でも配信に力を入れているサイトはAMPやってると思います」「AMPに乗っかればモバイルブラウザからのリクエストがサーバーに来なくなるのでサーバーの負荷も下がりますし🧐

「AMP自体はよくできているんですけど仕様の縛りが結構多くて、JavaScriptが動かせないとか、画像にはwidthheightを必ず付けるとか😅」「静的なページにせよと😅」「そういえばAMPだとCSSファイルの長さにも制限ありますよね😅

「AMPのJavaScriptについてこないだ調べた時点では、一応ドラフトというかexperimentalな仕様は出ている状態、でもまだブラウザでは動きませんでした😇(Chromeで開発者モードにすると動きますが)」

参考: 独自のJavaScriptをAMPで動かせる<amp-script>が公開。オリジントライアル参加募集中 #AMPConf | 海外SEO情報ブログ

「そういえばAMPにしとくとモバイルでググったときにそのページがカルーセル↓に乗りやすくなりますね」「カルーセルの特等席に乗れるということでSEO関係者がAMPにうわっと飛びついた感ある😆」「『AMP SEO』あたりでググるといろいろ出てくるかも😆」「AMPページの検索結果の横にはちっちゃな雷マーク↓が表示されます⚡」「わかりにくい〜😅

参考: カルーセルとは - IT用語辞典 e-Words

「そういえばどこかの商用サイトが『商品ページをニュースとしてAMPフィードした』とかでだいぶ問題になったことがありましたね😎」「あ〜それはヤバそう😅」「商品は検索トップにずらっと並んだんでしょうけど😆」「Google様に排除されかねない😅

「AMP HTML自体は仕様が公開されているんですけど、考えてみればAMPがそもそもGoogleのクラウドを前提にしているので、仕様どおりにAMPをやったとしてもその結果がどうなるかはGoogle様次第なんですよね😆」「😆

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

⚓CSSの設計ミスの不完全リスト

BPS社内Slackの「box-sizing:content-boxにするかborder-boxにするか」という話題で知りました。


つっつきボイス:「この間BPSの社内Slackで盛り上がったヤツです」「これ公式のWiki?」「CSS Working GroupがやってるWikiですね」

「ああこれ😆: 本来CSSはこういう仕様にすべきであったという後悔リスト」「『誰かタイムマシン持ってたら直して』って😆」「反実仮想だらけ😆」「お悔やみリスト😆」「今更直せないヤツ😇

「『vertical-align:middletext-middlex-middleにすべき』とか😆」「middleが本当のmiddleじゃない問題😆」「後方互換性とかあるから厳しい😅

「このWikiを翻訳してみたい欲がふつふつと湧いてくるんですけど😆、訳していいのかな?」「いいんじゃない?😆」「一番下に『ideas/mistakes.txt』とか書かれてるし😆」「😆」「ここ、最近更新激しいですよ😆」「定期的に恨み節をアップデート😆」「RSSフィード欲しいです😆

参考: ウェブ関連仕様 日本語訳

⚓「Address-bound Token for QUIC」仕様提案


同記事より

これもWEB+DB PRESSでkazuhoさんとmizchiさんのどちらかが言及していました。

同誌で以下を初めて知りました。Kindleなのでページ番号がわかりません😇

TCPとTLS(TransportLayerSecurity)を置き換える部分をQUIC、HTTP特有の部分をHTTP/3と呼ぶことになりました。
WEB+DB PRESS Vol.111より(強調はTechRacho)


つっつきボイス:「例のH2Oサーバー↓を開発したkazuhoさんが上のブログを推してました」


h2o.example1.netより

「QUICのコネクション周りはよくわからないけど、congestion controlをしたいから同一エンドポイントかどうかの識別をしたいという提案ということかな🤔

congestion n. 密集, 過密, 渋滞; 充血

「ところでこういうQUICとかHTTP/2みたいなややこしいところをやってる方はいます?」「いや〜😅」「じゃTCPとかのネットワーク周りをやってたりする方は?UDPとか輻輳制御とか」「😅」「じゃ今日この辺はスルーで😆

⚓その他フロント


つっつきボイス:「この中ではrobots.txtのパーサーが公開された話が割と話題になってましたね」「今までなかったとは😳」「パーサーの仕様は公開されていましたが実装は公開されてたわけではなかったんですね☺」「つまり今まではみんなお気持ちでrobots.txtを書いてたということに😆」「書いてみてGoogle Search Consoleで様子見つつやってくしかなかったという😆」「今回ソースが公開されたことで根拠を調べる手がかりができたというか」「C++で書かれてますけど😆

「その下の記事もほぼ同時に見つけたんですけど、Googleがnoindexのサポートを終了するそうです」「えっマジで😅」「今まで機能はしてたけどサポートしてたわけではなかったと😇」「ホントに隠し機能だったとは😆」「でもまあmetaタグに乗るヤツは引き続きサポートされるのか」「crawl-delayとか知らないし😆」「実はrobots.txtは知らない機能が山ほどあったりしますし😆

「Googleのクローラーはrobots.txtをちゃんと見ますけど、他の検索エンジンのクローラーがどのぐらいちゃんとrobots.txtを見てくれるのかというのはありますよね」「う〜む😅」「大企業のWebサイトあたりになると、いろんなクローラーが鬼のように大量にやってくるんですけど、たいていのクローラーは相当お行儀が悪いんですよね🤣」「やはり🤣」「数秒おきにアクセスしてくるクローラーとかゴロゴロしてますし、明らかにGoogleじゃないのにGoogleを詐称してるのとかも😇」「ヤバすぎ😆

⚓言語・ツール

⚓VSCodeの気になる機能


つっつきボイス:「VSCode使ってる方は?」「1、2、…5人はいますね」「その方はRailsを書くときは別のを使ってたりします?」「RubyMine使ってます☺」「Cloud9使ってます☺

参考: AWS Cloud9(Cloud IDE でコードを記述、実行、デバッグ)| AWS

「弊社CTOは?」「TypeScriptはVSCodeですけどRailsはVimでやってます」「😆」「😆」「C++書くときもVimですし」「でしょうね〜」

「フロントやってるとVSCodeが多い感じなのかな🤔」「そういえばStackOverflowの調査でもJetBrainsの調査でも、ほぼすべての言語でVSCodeが1位みたいですし」「はれぇ〜」

「自分はJetBrains派ですけど😆」「私はSublimeをVSCodeに乗り換えました😅」「Sublime Textはメモ用にめっちゃ使ってます😆」「WebStorm重いからVSCodeに移ってもいいかなって思うんですけど、JetBrains IDEのショートカットに慣れきったからな〜😆

「でもVSCode重いですよ、メモリ持ってかれますよ🤣」「いや〜JetBrainsの方がメモリ食いそうですけど😆」「RubyMineはメモリ食うしな〜」「めちゃ食います🦈

⚓その他

⚓プリウス、やっぱり静か過ぎ

参考: EUの電気自動車に人工的な騒音「フェイク・ノイズ」が加えられることに | ギズモード・ジャパン


つっつきボイス:「こっからは雑談系です😆」「ははぁ、EUでもついにフェイクノイズ追加か」「プリウスがあまりに静かすぎて危ないから音を出すようにって」「この辺は電気自動車の初期から問題になってましたね☺」「特に聴覚障害のある人にとっては致命的なんですよね」「たしかに!」

「だからというわけじゃないけど、日本でそういう話が出たときに雑コラみたいなのがいろいろ出て、京急の電車の発車音が出るヤツとかありましたね🚃」「🤣」「🤣」「例のシドレミファソラシ〜って🎶

参考: ドレミファインバーターとは (ドレミファインバーターとは) [単語記事] - ニコニコ大百科

「プリウスに風鈴ぶら下げとけばいいんじゃね?😆」「まあ音の高さとか調整しないといけなそうですが☺」「フェラーリのエンジン音出すとか🚗」「🤣」「プリウスに風鈴、知人に『それ夜の田舎道でマジで怖いんですけど』って言われました😆

「ノイズ出さないと車検通らないとかになるんだろうか?🤔」「安全の一環だしありそう」「ゼロから電気自動車作ったらあるかも😆

⚓その他のその他


つっつきボイス:「Twitterでネタが大喜利状態だったので」「なつかしのネタ😆」「あやっぱ元ネタが?😅」「その昔シスター・プリンセスという企画ものがあってですね😆」「今の若い人は知らないかも😆」「知ってる人?」「いるいる、よかった〜年が近くて😆」「いろんな言い回しされてたけど、1つめのツイートは割と本家に近かったかも」

参考: シスター・プリンセス - Wikipedia

「2つ目のツイートいいっ🤣」「これ🤣」「12台全部OS違うのって逆に難しくね?😆」「バージョン違いありならいけるかも😆」「Win95、98とか😆」「CPU違いも、x86を全部同じとみなされたら突破難しそう😆」「我が家のDEC Alpha CPUマシンを繰り出せば😆: 重量めちゃスゴいけど」「まあこんだけいろいろ違ってたらそりゃサポート期間も終了するわな〜」「🤣」「🤣

参考: DEC Alpha - Wikipedia

⚓番外

⚓量子重力に対称性はない


ipmu.jpより


つっつきボイス:「先々週貼り忘れてました😅」「『素粒子には必ず対が存在する』みたいな仮定が崩れたとかそんな話でしたっけ」「電子と陽電子とか、N極とS極とか、右回りと左回りとかそんな感じで、どうやら理論の深いところに行っちゃうとシンメトリーを当てにできなくなっちゃうみたいです😇」「へぇ〜」「高校生の頃よく読んでた当時のブルーバックスとかだとクォークが一番細かい単位だったんだけど、今はさらに細かくなっちゃってそう😆」「ちょうど時間となりましたので、親睦会にうつりま〜す🍺🍕」「は〜い😋

参考: 標準模型 - Wikipedia


後半は以上です。公開つっつき会にご参加いただいた皆さまありがとうございます!

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

週刊Railsウォッチ(20190701)RMagickのメモリ使用量が劇的に改善、インスタンス変数の定義順で速度が変わる?、GitLab CIランナーをローカルで回すほか

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

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

RubyFlow

160928_1638_XvIP4h

Postgres Weekly

postgres_weekly_banner

DB Weekly

db_weekly_banner

Microsoft Silverコンピテンシーを取得しました

$
0
0

このたび、晴れてBPSは Microsoft Silver Application Development コンピテンシーを取得しました。

取得の背景や経緯を記録しておきます。

Microsoft Partner Network (MPN) 制度

コンピテンシーは Microsoft Partner Network (MPN) の制度で、簡単に言えば「条件を満たして年会費を払えば、Officeライセンスなどたくさんの特典がもらえる」ものです。

Action Pack, Silverコンピテンシー, Goldコンピテンシーと特典

MPNに登録するだけなら無料ですが、特典を得られるプログラムには下からMicrosoft Action Pack, Silverコンピテンシー, Goldコンピテンシーの3つがあります。

得られる特典は色々書かれていますが、多くの場合メインになるであろうOfficeやVisual Studio Subscriptionのライセンスだけでも、年会費を大きく上回る量が入手できます。身も蓋もない言い方をすればOfficeやVisual Studioがたくさん入ったバルクパッケージですね。

一例として、

Action Pack
・年間4.4万円
・Office 365 E3が5個
・Office Professional Plusが10個
・Visual Studio Professional Subscriptionが3個
Silverコンピテンシー
・年間15.5万円
・Office 365 E3が25個
・Visual Studio Enterprise Subscriptionが5個
Goldコンピテンシー
・年間44.4万円
・Office 365 E3が100個
・Visual Studio Enterprise Subscriptionが10個
共通
・毎月100ドルのAzureクレジット

これらをベースにしつつ、コンピテンシーの種類による追加ライセンスや、パフォーマンス(顧客販売実績などを積む)次第ではさらに数倍のライセンスをもらえるようです。

※Visual Studio Subscriptionは旧MSDNです。

取得の条件

Action Packは条件がほぼなく、金さえ払えば取得できます。Silver, Goldは一定の条件を満たすと取得できます。基本的には企業向けで、事業所単位で取得可能なようです(Action Packを1事業所で2つも3つも買うことはできない)。

コンピテンシーの条件としては、種類によって異なりますが「Microsoft製品やAzureを代理店として一定以上売り上げる」「Microsoft製品のトレーニングを行う」「Microsoft認定資格を取る」などがあるようです(あまり詳しく調べていない)。

以前は顧客事例(販売実績?)の登録が必須だったような気がするのですが、最近条件が変わったのかそれとも見落としていたのか、改めて確認したら種類によっては試験合格だけで取れるようになっていました。

今回取得したコンピテンシー

BPSでは3年ほど前からAction Packを契約していました。OfficeとVisual Studioがもっと欲しかったのでSilver以上も検討していたのですが、取得要件の顧客事例を満たせる気がしなかった(BPSではMicrosoft製品は消費するばかりで、開発ではオープン系技術、クラウドはAWS等を中心にしていたため、販売事例がなかった)ので諦めていました。

最近、Application Developmentコンピテンシーなら試験のみで取得できることに気づいたので、早速取得に向けて動くことにしました。

Silver Application Developmentの取得条件

https://partner.microsoft.com/ja-jp/membership/application-development-competency によると、試験に2名が合格するだけで良いようです。

Web開発とスマートフォンアプリ開発を専門にしており、Microsoft固有のOfficeやSharePoint, Azure関連の専門性には乏しいBPSにとっては、間違いなくこれが簡単そうです。

Web and Mobile Client App Dev focus area

2 individuals must pass one of the following exams:

Exam 70-480: Programming in HTML5 with JavaScript and CSS3
Exam 70-483: Programming in C#
Exam 70-486: Developing ASP.NET MVC Web Applications

ということで70-480に決定ですね。

Silver Application Developmentの特典

標準のものに加えて、Visual Studio Enterpriseが5個追加になり、合計で

  • Office 365 E3が25個
  • Visual Studio Enterprise Subscriptionが10個
  • Windows 10 Enterpriseが25個

などがもらえます。

BPSで利用できる価値に換算してみます。

Office 365
・月1,000円相当(E3は本来月2,000円相当ですが、Officeだけあれば良くてSharePointとかOutlookはいらないので、Office 365 Businessの価値で計算)
Visual Studio Enterprise
・年15万円相当(Enterpriseは本来年30万くらいだと思いますが、そんな機能いらなくてVisual Studio ProfessionalとOffice利用権程度の価値に換算)
・Visual Studio Expressが2017止まりで2019が出なくて困っていたので割と切実
Windows 10 Enterprise
1万円相当(本当はもっとずっと高いですが、Docker for Windowsを使いたいのにOSがHomeだったからProにしたいが主用途なので)

合計するまでもなく、Visual Studio 1本で既にお得感があります。

ほかのコンピテンシーとの比較

Application Development以外のコンピテンシーももちろん検討しました。

  • Communicationsは、Officeがたくさんついていて大変魅力的です。しかし、どうも試験合格だけではだめで色々な条件が必要そうなので断念しました。
  • Cloud ProductivityもOffice 365が合計50個ついていて魅力的ですが、取得条件の「MCSA: Office 365」がすでに廃止されていてどうしろ状態だったので断念しました。
    • なお、サポートには全然繋がりませんでしたが、後日確認したら条件が更新されていました。
  • Dev Opsは試験合格だけで取れそうですが、3つも受けないといけなくて、受験料が高いのでやめておきました。

条件は複雑で、ORがどこにつながるのかもわかりづらい

※コンピテンシーの取得要件(に限らずMPN制度全般)は頻繁にリニューアルされており、数年後にはこの方法で取得/更新できなくなっている可能性はあります。

「70-480試験」の受験

ということで、あとは試験70-480 JavaScript および CSS3 を使った HTML5 でのプログラミング
の合格者を2名用意するだけです。

ここで問題になるのが「2名」ということです。1名なら僕が取れば良いので簡単なのですが、もう1人どうにか確保しなくてはいけません。ということで期間限定キャンペーン開催です。

もう1人いれば良かったのですがここはボーナスなので2名にしておきました。

受験申し込み

そして自分でもさっさと受けておきます。内容はよくわかりませんが、Webの試験ですし、まあ普通に考えれば合格できるでしょう。

文字化けは気にせず進めます

なお、普通に申し込んでも良いですが、バウチャーを買うとキャンペーンで安いことがあるのと、1回落ちても再受験できるバウチャーが売っていることがあります。

合格率2倍にツッコんではいけない

試験対策

  • まずは公式からリンクされている模擬試験を受けてみようと思いましたが、リンク先が404エラーだったので諦めました。
  • 同じくリンクされていた試験対策ページを開いてみましたが、リンク先が500エラーだったので諦めました。
  • 公式の対策本が売っていましたが、英語しかなくて面倒だったのでやめました。
  • 怪しい試験対策サイトがありましたが、怪しいアプリを入れるように言われたのでやめました。

ということで受験料が高いのが怖いですが、ぶっつけです。

当日朝に直前対策として10分ほどXHRの文法とreadyStateの値だけ復習しておきました(役立たなかった)。

試験当日

おなじみのプロメトリック試験会場で、身分証2枚を出して同意書類にサインして試験開始です。予約時間より30分早く到着しましたが、速攻開始させてくれました。

  • CSSについてはセレクタの理解を問う問題が多かった。
  • jQuery推しで、特に素のJSとjQueryの差は気にせず常にjQueryは使えるもの前提の問題が大半だった。
    • jQuery以外のライブラリに関する知識は不要。
  • JavaScriptの基本的なprototypeチェーン等に関する理解を問う問題も多かった。
  • 日本語訳は悪くなく、日本語がわからず英語原文を見るボタンを使ったのは1~2回程度。
  • マウスのホイールが壊れていて問題の下の方にスクロールするのが難しかった。

試験時間は申し込みページでは150分と書いてありましたが、実際には120分でした。60分もあれば終わると思います。途中退室は可能で、即座に結果がもらえます。

結果は1000点満点890点(合格ライン700点)でした。

後日、 @morimorihoge さんが合格してくれたので、無事2名集まりました。

登録

試験に合格しても、紙の合格証書どころか、メールの1通すら届きません。翌日以降にマイページに表示されるだけです。2万円以上払っているので、もう少し何かくれてもいいのに。

ダッシュボードにはMCSAへの道のりが表示される

合格するとこんなバッジはダウンロードできました。

MPNとの関連付け

合格の前でも後でも良いのですが、MPNページでの関連付けが必要です。

まず該当ユーザがMPNに登録されていなければユーザを作成しておきます。

そして、合格したユーザが自分のアカウントで認定アカウントを紐付けます。

購入

条件を満たすとこんなメールが届きます。

You have qualified for a Microsoft Partner Network competency


Dear Takao Baba,

Congratulations on qualifying for Silver Application Development competency!

When you qualify for a Silver competency, you also receive a set of internal-use rights benefits, including licenses and subscriptions to Microsoft software and services.


To start using these benefits, go to your Offers page, select Silver membership offer, and pay the fee using a valid credit card by 04-Sep-2019.

Learn more: https://go.microsoft.com/fwlink/?linkid=864604


Thanks,
Your Partner Center team

そうしたらMPNページから購入を押して

クレジットカード情報を入力して決済すると、即座に特典が使えるようになりました。

エラーが出たら

ちなみに、そのうち改善されると思いますがMPNや認定ページは少し放置するとすぐ真っ白で何もできなくなりますが、この場合は partner.microsoft.com のCookieを消してやるとたいてい良い感じです。全部消すとログアウトされすぎてめんどくさいです。

こういうパターンのときは諦めて翌日にやり直す。

取得後

ということで、無事にOfficeやVisual Studioの不足に悩まされない快適な開発ライフを送ることができるようになりました。気軽にOfficeとVisual Studioがたくさん手に入るMPN制度は、中小企業にとてもおすすめです。パートナーに優しいMicrosoftさんには感謝しつつ、今後も制度の維持・改善にも期待しています。

せっかくなのでエントランスに飾る盾やプレートをノリで注文してみようと思ったのですが、注文ページが見つかりませんでした。あれおいくらくらいするんですかね?

Gitで巨大プロジェクトを扱うときに少し便利なupdate-ref

$
0
0

ビルドに時間がかかる(数十分~数時間以上)プロジェクトを扱うときに役立つかもしれない、Gitの小ネタです。

Gitには git help しても出てこない( git help -a すれば出る)便利なコマンドがたくさんあり(※)、そのうちの1つ update-ref のご紹介です。

※他には例えば update-index --assume-unchanged なども有名ですね。

どんなときに欲しくなるか

こんな感じの、あるヘッダファイルに多数のソースファイルが依存するプロジェクトがあったとします。

repos
 |- common.hpp
 |- source1.cpp
 |- source2.cpp
 |- source3.cpp
 |- source4.cpp
 |- source5.cpp
 |- ...

まずは master ブランチにいます。

$ git status
On branch master
nothing to commit, working tree clean

新機能を開発するので、featureブランチを切ります。

$ git checkout -b feature/hoge
Switched to a new branch 'feature/hoge'

featureブランチでは新機能開発のため、共通ヘッダを更新しました。

$ vi config.hpp

ローカルでビルド・テストしてみて、動作は良好です。

$ cmake .
$ ninja

出来上がったのでpushしてMerge Request / Pull Requestを出します。

$ git commit -am "new feature"
[feature/hoge 45817d2] new feature
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git push origin feature/hoge
Total 0 (delta 0), reused 0 (delta 0)
To git@example.com:practice.git
 * [new branch]      feature/hoge -> feature/hoge

無事レビューを通過し、Web UI上でマージボタンが押されました。

o new feature [feature/hoge]
o initial commit [master]

↓

o Merge branch 'feature/hoge' into 'master' [origin/master]
|-
| | new feature
|-
o master [master]

ここで、自分もブランチから戻るのに、

$ git checkout master
$ git pull origin master

のようにしてしまうと、1行目のコマンドの時点でconfig.hppが一旦古いバージョンに戻ってしまいます
その後2行目のコマンドで新しいバージョンになりますが、ファイルのタイムスタンプが更新されるので、リビルドが必要になります。
リビルドに数時間かかるようなプロジェクトでは、これは致命的です。

update-refの登場

update-ref は、対象のブランチが指すコミットを書き換えるのに使えます。ワークツリーは feature/hoge にいるままで、 master の指す先を origin/master と同じにしてしまいましょう。

$ git fetch
$ git update-ref refs/heads/master origin/master
$ git checkout master

この順番で操作すると、3行目のコマンドでmasterブランチに切り替えても、config.hppの中身に変更がないためタイムスタンプも変わりません。したがって、無駄なリビルドは回避されます。

※なお、gitコマンドを使わず .git/refs/heads/master を書き換えても同じ結果が得られますが、gitコマンドのほうが若干事故りにくいでしょう。

それ以外の対応案と補足

update-refを使わず、この方法でも良いと思います。

$ git branch -D master
$ git fetch
$ git checkout -b master origin/master

社内で聞いてみたら、「masterに直接pushしない運用にしている以上、手元にmasterブランチがある意味がないし、事故の元だから手元にmasterブランチは作らない」という声もありました。

常にこのような形でブランチ運用するわけですね。

$ git fetch
$ git checkout -b feature/new origin/master

言われてみると、とても理にかなった方法です。gitは人によって使いこなし方が違うので、聞いてみると発見があって面白いですね。

僕の場合は癖のレベルですが「とりあえず最新版をビルドしたい」「これから何をやるかまだ決まってないからブランチ名が思いつかない」などによってmasterが欲しい人なので、 update-ref を使い続けています。

その後追記

これで良いのではという指摘をいただきました。こっちのほうが簡単ですね 💦

$ git fetch
$ git branch -f master origin/master

関連記事

【社内勉強会】バージョン管理の重要性とGitの運用について

週刊Railsウォッチ(20191029後編)Ruby 2.7.0-preview2、tapping_device gemとhumanize gem、平成Ruby会議ほか

$
0
0

こんにちは、hachi8833です。久しぶりに腰を痛めてしまいました😇

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

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

第16回目公開つっつき会は、11月14日(木)19:30〜にBPS会議スペースにて開催されます。今回は月初ではありませんのでご注意ください。

週刊Railsウォッチの記事にいち早く触れられるチャンス!発言も自由です。皆さまのお気軽なご参加をお待ちしております🙇

⚓Ruby

⚓Ruby 2.7.0-preview2がリリース(Ruby公式ニュースより)

Ruby 2.7.0-preview2がリリース


つっつきボイス:「先に記事出しちゃいましたが、Ruby 2.7.0-preview2がリリースされて詳細がだいぶ明らかになりました😋」「めでたい🎉


「今回はExperimentalと銘打った新機能が目立ってましたが、気になった機能はあります?」「rangeの開始を省略できるようになった↓とありましたけど、個人的には冗長でもフルで書きたい方ですね〜😆」「実は私も😆」「一抹の気持ち悪さがあるといえばあるかも😆

ary = %w(1, 2, 3, 4)
ary[..3]    # ary[0..3]と同等
#=> ["1,", "2,", "3,", "4"]

「従来のRubyだと、ブロックなしのprocProc.newがメソッドのブロックをキャッチしていた↓というのが一番驚きでした😳」「そうそう☺」「ある意味バグに近い感じ☺」「こんな書き方できてたとは😅」「面白がって使う人とかいそう😆」「知らずに書いてハマる人の方が多いかも😇」「普通はyieldで受けるかブロック変数で受けますよね」

def foo
  proc.call # またはProc.new.call
end
foo { puts "Hello" }
#=> warning: Capturing the given block using Proc.new is deprecated; use `&block` instead
#=> Hello

「上は2.7でwarningになって、lambdaにもあった同様の挙動は一足先に2.7でエラー扱いになります↓」

def foo
  lambda.call
end
foo { puts "Hello" }
#=> ArgumentError (tried to create Proc object without a block)

「今まで他の言語やってたから思うのかもしれませんけど、こうやってブロックを食わせて処理するって慣れないと見えにくいところがあるかもですね☺」「でもRubyのブロックはやっぱり便利〜↓」

参考: Rubyのブロックは本当に素晴らしい発明だと思いますが、他の言語はそれを追随していますか?に対するYukihiro Matsumotoさんの回答 - Quora

なお、以下はつっつきの後で見つけたNoah Gibbsさんのベンチマーク記事です。

参考: Ruby 2.7preview2, a Quick Speed Update — Appfolio Engineering

⚓MJITのC生成を追う(Ruby Weeklyより)


つっつきボイス:「上はRubyのベンチマークやコアな部分を追っているNoah Gibbsさん↓の記事で、MJITがどんなCのコードを吐いているかを調べたそうです」「これはハードそう😅」「今追いきれないので後でサマリー追加しておきますね😋

Ruby 3 JITの最新情報: 現状と今後(翻訳)


MJITはほとんどの場合、コールスタックやCレベル関数呼び出しなどのシンプルな記録を最適化しているにすぎない。関数が再定義されたかどうかのチェックなどによるオーバーヘッドを含む、Rubyのほとんどの操作については基本的に従来と変わらない。
何らかのbreaking changesなしに言語レベルで最適化するのはきわめて困難なので、これは理解できる。MJITは言語のセマンティクスを一切変更しないようにしているので、MJITはほとんどの場合インタプリタがこれまで行っていたようにシンプルかつ機械的に変換している。
同記事末尾より大意

ご存知のとおりRuby 2.6から--jitオプションが使えます。自分でも2.7-preview2でごく簡単なベンチを回してみたところ、速度的には--jitありなしでほぼ違いはない雰囲気でした。

⚓humanize: 数値をさまざまな言語でスペルアウト(Ruby Weeklyより)

# 同リポジトリより
2.humanize  # => "two"
4.humanize  # => "four"
8.humanize  # => "eight"
15.humanize # => "fifteen"
16.humanize # => "sixteen"
23.humanize # => "twenty three"
42.humanize # => "forty two"

つっつきボイス:「個人的に面白そうなgemを見つけました😋」「RailsのActive Supportに似たようなものがありましたっけ?」「Active Supportのこれ↓とか少し近いかな」「あとモデル名や属性名を変換するヤツとか」

number_to_human(123)                         # => "123"
number_to_human(1234)                        # => "1.23 Thousand"
number_to_human(12345)                       # => "12.3 Thousand"
number_to_human(1234567)                     # => "1.23 Million"
number_to_human(1234567890)                  # => "1.23 Billion"
number_to_human(1234567890123)               # => "1.23 Trillion"
number_to_human(1234567890123456)            # => "1.23 Quadrillion"
number_to_human(1234567890123456789)         # => "1230 Quadrillion"
class BlogPost
  extend ActiveModel::Naming
end

BlogPost.model_name.human # => "Blog post"

「このhumanize gemで("9" * 156).to_i.humanizeを実行するとこんなの出ます↓」「うは🤣」「これは🤣」「quinquagintillionなんて数詞見たことありませんし😆」「阿僧祇恒河沙無量大数的な😆

=> “nine hundred and ninety nine quinquagintillion, nine hundred and ninety nine novenquadragintillion, nine hundred and ninety nine octoquadragintillion, nine hundred and ninety nine septenquadragintillion, nine hundred and ninety nine sesquadragintillion, nine hundred and ninety nine quinquadragintillion, nine hundred and ninety nine quattuorquadragintillion, nine hundred and ninety nine trequadragintillion, nine hundred and ninety nine duoquadragintillion, nine hundred and ninety nine unquadragintillion, nine hundred and ninety nine quadragintillion, nine hundred and ninety nine novemtrigintillion, nine hundred and ninety nine octotrigintillion, nine hundred and ninety nine septentrigintillion, nine hundred and ninety nine sextrigintillion, nine hundred and ninety nine quintrigintillion, nine hundred and ninety nine quattuortrigintillion, nine hundred and ninety nine trestrigintillion, nine hundred and ninety nine duotrigintillion, nine hundred and ninety nine untrigintillion, nine hundred and ninety nine trigintillion, nine hundred and ninety nine novemvigintillion, nine hundred and ninety nine octovigintillion, nine hundred and ninety nine septenvigintillion, nine hundred and ninety nine sexvigintillion, nine hundred and ninety nine quinvigintillion, nine hundred and ninety nine quattuortillion, nine hundred and ninety nine trevigintillion, nine hundred and ninety nine duovigintillion, nine hundred and ninety nine unvigintillion, nine hundred and ninety nine vigintillion, nine hundred and ninety nine novemdecillion, nine hundred and ninety nine octodecillion, nine hundred and ninety nine septendecillion, nine hundred and ninety nine sexdecillion, nine hundred and ninety nine quindecillion, nine hundred and ninety nine quattuordecillion, nine hundred and ninety nine tredecillion, nine hundred and ninety nine duodecillion, nine hundred and ninety nine undecillion, nine hundred and ninety nine decillion, nine hundred and ninety nine nonillion, nine hundred and ninety nine octillion, nine hundred and ninety nine septillion, nine hundred and ninety nine sextillion, nine hundred and ninety nine quintrillion, nine hundred and ninety nine quadrillion, nine hundred and ninety nine trillion, nine hundred and ninety nine billion, nine hundred and ninety nine million, nine hundred and ninety nine thousand nine hundred and ninety nine”
同リポジトリより

参考: 無量大数 - Wikipedia

「humanizeというよりto_english的つーか😆」「お、ロケール指定できるみたい↓」「じゃいいか😆」「小数点もやれますね」

# 同リポジトリより
Humanize.configure do |config|
  config.default_locale = :en  # [:en, :es, :fr, :tr, :de, :id], default: :en
  config.decimals_as = :digits # [:digits, :number], default: :digits
end

0.42.humanize(decimals_as: :digits) # => "zero point four two"
0.42.humanize(decimals_as: :number) # => "zero point fourty-two"

「これ日本語もやろうと思えばやれるんでしょうね😆」「誰かがやろうと思えば😆」「やってみちゃおうかな😋」「ひらがな読みも出せたらいいのに」「『ひとつふたつみっつ』とか😆」「『にひゃく』『はっぴゃく』とか活用形が地獄化しそう😇

「漢数字gemといえばこちらも話題になった気がしますね↓」「おぉ既にあるんですね😳

⚓tapping_device: RubyのTracePointで手軽にデバッグ

Goby言語の@st0012さんがRubyのTracePointを用いて作ったデバッグ用gemです。オレオレRailsアプリのdevelopmentに入れて動かしてみました。言わずもがなですが、tapping deviceは「盗聴器」になぞらえてますね😆

参考: class TracePoint (Ruby 2.6.0)

⚓その他Ruby

平成Ruby会議の登壇募集は本日締め切りだそうです。お早めにどうぞ。

⚓DB

⚓SQLで日時をフォーマットする(Postgres Weeklyより)

見出しより:

  • Dateのフォーマットは多種多様
  • Dateの表現はデータ型によって異なる
  • DateをStringに変換する
    • TO_CHAR()
    • EXTRACT()
    • DATE_TRUNC()
  • StringをDateに変換する
    • CAST()
    • TO_DATE()
  • PostgreSQLのDate関数リストとサンプル


同記事より

つっつきボイス:「初心者向けSQL日付フォーマットガイドということで」「EXTRACT()にCENTURYとかMILLENNIUMとかいろいろありますね↑」「SQL日時変換はRDBMSに相当依存するので、当該RDBのドキュメント読むのがいいでしょうね☺: 汎用性が高いのはEXTRACT()かな」「おぉ」

「SQLの日付フォーマットってストアドプロシージャとかでたまに必要になったりするのかも☺」「ストアドプロシージャはRDBMSごとに違いがありすぎると教わりました😆」「いつだったか、ストアドプロシージャを全部シェルスクリプトに書き換えたことありますけど🤣」「え?😅」「今さらっとすごいこと言った🤣」(以下延々)

参考: ストアドプロシージャ - Wikipedia

「このDOW(day of week)という略語は日本語で言う『曜日』なんですけど、どういうわけか英語圏ではさっぱり定着しないんですよ」「そういえば英語に1単語で曜日の概念を表す用語ってありませんね😳」「表示スペースがどうしても足りないときにしぶしぶDOWと表記するみたいです」

WikipediaにすらDOWという略語は載っていません↓。

参考: Names of the days of the week - Wikipedia

⚓その他DB


blog.aquameta.comより


つっつきボイス:「aquametaはPostgreSQLだけでWeb開発するIDEなんだそうで、★も800超えてますね」「シェルスクリプトでWebシステムを組んたことならありますけど、それに匹敵するキワモノ感ですね〜🤣」「え?😅」「さらっとまたスゴいこと言った🤣」「そこまでひとつの技術にこだわらなくても、今は便利なものがいろいろありますからそっちを使いましょうよって思いますし☺」「aquametaはかれこれ20年近く続いているプロジェクトだそうです☺」「趣味としてならとても理解できる☺」「変態やなーとも思いつつも、内部的にストアドプロシージャ使ってるならまあ確かに何でもできるかも😆: Web IDEまで作り込むその愛がすごい」

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

⚓Rails向けAWS S3 IAMポリシーセキュリティ設定(Ruby Weeklyより)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::another-test-bucket"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::another-test-bucket/*"
            ]
        }
    ]
}

見出しより:

  • デフォルトのS3設定の問題点
  • IAMポリシーの概要
  • S3のsudoアクセスを使う
  • セキュアなS3 IAMポリシーを設定するには

つっつきボイス:「Rails向けのS3 IAMポリシーについての記事です」「RailsアプリでIAM設定に関する話となると、Instance Roleで設定するというのも一つの手段ですが、EC2インスタンスが絡む実行でしか使えません」「おぉ」「記事のようにBucket Policyで設定するのはいいんですが、『特定のBucketアクセスだけできる』ちゃんとしたIAMポリシを書くのは結構難しいです」「そうでしたか!」「ListAllMyBucketsがないとaws s3 ls できないとか地味に色々問題が出ますし😇

AWSアカウントを新規作成した直後はrootユーザーのアクセスキーだけが利用できる状態になるが、これをそのままRailsアプリで使うと深刻なセキュリティ問題につながるので避けなければならない。
同記事より抜粋

「さすがにこんな初歩的な間違いをする人は今どきいないのでは😆: 確かAWS公式でもrootアカウントは初期利用だけにして、AdministratorAccessのIAMアカウントを作ったら後はIAMアカウントを使おうね、ということになってたはずですし、ちゃんと堅い運用しているところならAWS rootアカウントはMFA有効にした上で、MFAキーを金庫に入れて運用します」


参考: The Technical Side of the Capitol One AWS Security Breach

同記事の末尾からリンクされている上の記事も興味深い内容です。

参考: 米金融大手Capital One情報漏えいの容疑者、さらに30社超からデータ盗難の疑い - ZDNet Japan

⚓サーバーレスのキャッシュについて知っておきたいこと(Serverless Statusより)

見出し:

  • キャッシュは現在も「極めて」重要
  • キャッシュをどこに実装すべきか
    • クライアントアプリ
    • CloudFront
    • API Gateway
    • Lambda function
    • DAX(DynamoDB Accelerator)
  • まとめ

つっつきボイス:「サーバーレスでもキャッシュは重要」「そこはきっとそうでしょうね☺」「ほとんどのリージョンではコンカレント実行が1000までというソフトリミットがあるけど、その先に1分あたりのコンカレント実行が500までというハードリミットがあって、以下みたいにイベント開催中や飯どきにハードリミットに突き当たることがある↓、と」「ふむふむ」


同記事より

「こういう図が出てくるとちょっとほっとしますね」「それでも5つありますけど😆」「キャッシュ多いの〜」「それぞれ特性の異なるキャッシュが可能でうまく使えば効果的だけど、気を付けないと費用もかさむと」「実際には使用メモリの増加でしょうね💸」「CloudFrontはいいけど制約が多いとか、API Gatewayは強力だけどユースケースが少なかったともあります」「DAXはDynamoDB Acceleratorか」

参考: Amazon DynamoDB Accelerator (DAX) | AWS

参考: Amazon ElastiCache(インメモリキャッシングシステム)| AWS

DynamoDBを使わない場合や、複数の異なるデータソースからのデータをキャッシュしたい場合はElasticacheの導入も検討しよう。
元記事より抜粋

「サーバーレスでElasticache使うのかー: まあ安定・高パフォーマンス運用したいならわからなくもないけど、Elasticacheはインスタンス運用になるので時間課金の要素ができちゃうのが難点ですね」「そこが違うんですね😳」「その他のものは全部(ほぼ)完全従量課金ができます☺

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

⚓2019年のWeb動向


つっつきボイス:「少し長めの記事ですが、普段あまり目にしてないWebコーディングやデザイン方面の動向を大雑把に見るのにいいかなと思って😋

⚓SVG

「SVG使ってる人が7割」「この辺は元記事の母集団の構成にもよりそうですけど😆」「最近ファビコンとかアイコンでSVG使ってるサイト増えてる印象」

参考: Scalable Vector Graphics - Wikipedia

⚓WebP

「WebPって?」「割と最近にGoogleが作った画像フォーマットだそうです」「今の時代にまた新しい画像フォーマットが出るとは」「このアンケートではまだ8%しか使ってないようです」

「WebPにまだ対応してないブラウザもあるんですって😆」「IEですねわかります😆」「Safariも😆」「日本での普及は大変そうかな😅


developers.google.comより

参考: WebP - Wikipedia

「…WebPは10年近く経っててめっちゃ使われてるのではないかと🧐」「やや、そうでしたか😅」「最近むしろHEIFがすごい勢いで普及してて怖いんですけど」「こちらですか↓」「iPhoneのデフォルトフォーマットです🧐

参考: High Efficiency Image File Format - Wikipedia

⚓CSSプリプロセッサ

「CSSプリプロセッサはこのアンケートではSassが強いですね〜」「何も使ってない人も3割😆」「SassはもともとRubyで実装されてましたけど今年ruby-sassはディスコンになりましたね」


sass-lang.comより

⚓仕様のチェック

「HTMLやCSSの仕様をどこで確認するか」「W3Cが35%」「WHATWG HTML Standardは15%というのは意外に少ないかも」「仕様書確認しない派😆」「今年半ばにW3CとWHATWGがついに合意したというニュース↓があったから、今ならWHATWGで仕様を確認するのが正式ということになりますね」「MozillaのMDNが見やすいというのもわかる☺

参考: HTML標準仕様の策定についてW3CとWHATWGが合意 今後はWHATWGのリビングスタンダードが唯一のHTML標準仕様に - ITmedia NEWS

⚓CSSフレームワーク

「BootstrapやBulmaなどのCSSフレームワーク」「Bulma知らない〜😆」「フレームワーク使っていないが64%というのが意外ですね😳」「HTML・CSSコーダーが対象だからかも?🤔」「Bootstrapの型にはめられたくないとか?」「型にはまっちゃえば楽なんですけどね☺

参考: Bootstrap · The most popular HTML, CSS, and JS library in the world.


getbootstrap.comより

⚓Slack

「SlackはWebデザイナー・エンジニアともに6割使ってますね」「この世にはSlackの存在を知らない職場もまだまだありますから😆

⚓最近のメッセージサービス

「携帯の+メッセージとRCSも初めて見た😳」「いわゆるSMSみたいなヤツなんでしょうね: +メッセージは自分のスマホにインストールだけはしてますけど☺

参考: +メッセージ(プラスメッセージ) | サービス・機能 | NTTドコモ
参考: Rich Communication Services - Wikipedia

⚓番外: All your base are belong to us

「『All your base are belong to us』を知らない人が8割: もう20年ぐらい前だからレトロネタになっちゃったか〜👴」「…これ何ですか?」「その昔英語圏でめちゃめちゃコピペされた、ゲームのセリフの有名な誤訳ですね」「当時からネット使ってたけど気が付かなかった〜😅」「ニコ動のネットの歴史で見た気がする😆」「ゲーマー界隈では超有名ですね😆

参考: All your base are belong to us - Wikipedia

英語圏でも説明が必要になってるっぽいです↓。

⚓その他

⚓Googleの量子超越性


つっつきボイス:「仮想通貨系が騒がしくなってますね」「ひと月ぐらい前に査読中の論文がリークしたとかで英語圏で先に騒がれてたそうです」「暴落したのがちょっと痛快😆」「実際にどこまで通用するのか謎ですけど🤣」「結果が微妙なまま大々的に発表するとかよくありますし☺」「現実に降りてくるにはまだかかるでしょうね」

以下はつっつきの後で見つけました。

参考: グーグルが主張する「量子超越性の実証」に、IBMが公然と反論した理由|WIRED.jp
参考: 量子超越性実証で世界騒然のGoogle量子ラボに行ってきた | ギズモード・ジャパン

⚓番外

⚓無電源センサネットワーク


同記事より


つっつきボイス:「電波を電力に変換する高効率なダイオードで無電源でセンサーネットワークを作れるかもしれないそうです」「お〜、たぶん一瞬だけ電力を発生させてそれでセンシングして送信して、みたいに動作するのかな?😍」「こういう電源なしで外部マイクロ波や光駆動のセンサは普通に世の中的にありますね」「おぉ、そうでしたか😅」「無線駆動式センサは自分が学生の頃にも既にあったと思います: ちなみに送信onlyのノードなら割と昔から無線給電できるんですが、難しいのは受信の方ですね☺

「Appleウォッチがバッテリーなしで動くようになって欲しいです⌚」「まだ無理😆」「私のスマートウォッチも1日1回充電してますね😆」「電卓ぐらいならソーラーバッテリーで動くのに😅


後編は以上です。

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

週刊Railsウォッチ(20191028前編)RailsにSTI用メソッドsti_class_forとpolymorphic_class_forが追加、RuboCopを変更箇所だけにかけるgem、strftime書式生成サイトほか

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

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

Ruby 公式ニュース

Ruby Weekly

Postgres Weekly

postgres_weekly_banner

Serverless Status

serverless_status_banner

React Status

react_status_banner

週刊Railsウォッチ(20191105前編)Rails 6のデフォルト設定解説、DHHも消したいaccepts_nested_attributes_for、スライド『実践Railsアプリケーション設計』ほか

$
0
0

こんにちは、hachi8833です。今年の3連休は昨日のでおしまいだそうです。

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

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

第16回目公開つっつき会は、来週11月14日(木)19:30〜にBPS会議スペースにて開催されます。今回は月初ではありませんのでご注意ください。

週刊Railsウォッチの記事にいち早く触れられるチャンス!発言・質問も自由です。引き続き皆さまのお気軽なご参加をお待ちしております🙇

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

今週は平和に公式情報からです。

⚓Active Storage blobのパーマネントURLを公開可能に

blob向けのパーマネントURL。
configurations.ymlでサービスのキーにpublic: true | falseを設定することで、サービスのblobをpublicまたはprivateにできる。publicなサービスは常にパーマネントURLを返すようになる。
Blob#service_urlは非推奨になり、Blob#urlが推奨される。
changelogより

# activestorage/test/service/s3_public_service_test.rb
+# frozen_string_literal: true
+
+require "service/shared_service_tests"
+require "net/http"
+require "database/setup"

+if SERVICE_CONFIGURATIONS[:s3]
+  class ActiveStorage::Service::S3PublicServiceTest < ActiveSupport::TestCase
+    SERVICE = ActiveStorage::Service.configure(:s3_public, +SERVICE_CONFIGURATIONS)
+
+    include ActiveStorage::Service::SharedServiceTests
+
+    test "public URL generation" do
+      url = @service.url(@key, filename: +ActiveStorage::Filename.new("avatar.png"))
+
+      assert_match(/.*\.s3\.amazonaws\.com\/.*\/#{@key}/, url)
+
+      response = Net::HTTP.get_response(URI(url))
+      assert_equal "200", response.code
+    end
+  end
+else
+  puts "Skipping S3 Public Service tests because no S3 configuration was supplied"
+end

つっつきボイス:「issue #31419の、Active StorageのService APIからファイルへのアクセスも許可したいという流れで入ったPRだそうです」「ほほう😋」「パーマネントURLって何を指してるのかな?🤔」「#31419にいいね👍が48個もついてるのでみんな欲しがってるっぽい😆

ActiveStorage::Serviceの現時点のAPIではurlメソッドでしかリンクを取得できず、返されるpublic URLはほとんどのサービスでは同じタイムフレーム内で期限切れになる。サービスからファイルオブジェクトにもアクセスできるfileメソッド的なものがあれば、ファイルの公開方法をより柔軟にできて便利になると思われる。
#31419より大意

「#36729を見ると(ストレージ)プロバイダのpublic URLでは一般にファイル名をカスタマイズできないので、現状はpublic bucketとprivate bucketのディレクトリ構造が違ってしまっている: この修正ではconfigで設定すればS3やAzureやGCSで/キー/ファイル名の固定URLで統一的にアップロードして後でそのURLでダウンロードできるようにする、という感じのようです🤔」「まだActive Storageちゃんと使ってないけど、あるとうれしい機能らしいということはわかった😆

⚓has_manyのinverseが設定可能になった

この間取り上げた#34533の続きだそうです(ウォッチ20191021)が、#37413は取り上げてませんでした。

# activerecord/lib/active_record/railtie.rb#L29
    config.active_record.use_schema_cache_dump = true
    config.active_record.maintain_test_schema = true
+   config.active_record.has_many_inversing = false

つっつきボイス:「先週はhas_many関連が豊作でしたけど😆#34533で入ったhas_manyのinverseを利用できるようにするかどうかを設定で選べるようになったそうです(デフォルトはfalse)」「has_manyでinverseできるのが本来だけどbreaking changeになるから設定を増やしたのね☺

⚓inflectorが:zeitwerkモードでオーバーライド可能になった

# activesupport/lib/active_support/dependencies/zeitwerk_integration.rb#L56
      module Inflector
+       # Concurrent::Map is not needed. This is a private class, and overrides
+       # must be defined while the application boots.
+       @overrides = {}

-       def self.camelize(basename, _abspath)
+         basename.camelize
+         @overrides[basename] || basename.camelize
+       end
+
+       def self.inflect(overrides)
+         @overrides.merge!(overrides)
+       end
      end

つっつきボイス:「Active SupportのinflectorはRailsで名前の単数形複数形みたいな活用形(inflection)を制御するヤツですね☺」「@overridesというハッシュがあればオーバーライドできると」「特殊な活用形をここに入れられる感じですね😋」「basenameがあればよし、なければcamelizeすると」

「Railsガイドの更新↓を見るとautoloaderでこうやって活用形を定義できるとありますね」「html_parserHTMLParserに変換するヤツわかる〜😆」「HtmlParserだと違う感ありますね😆」「SslErrorも違う感😆」「Htmlは現実に使われちゃってるところもあったりするのでワンチャンありな気もしなくもないけど😆」「HTMLは大文字にしたいです〜😭

以下のようにすることでActive Supportの活用形がグローバルに効く。アプリケーションによってはこれでもよいが、Active Supportでデフォルトのinflectorにオーバーライドのコレクションを渡してbasenameを個別にcamerizeすることもできる。
ガイド更新分より大意

# guides/source/autoloading_and_reloading_constants.md#L289
# config/initializers/zeitwerk.rb
-inflector = Object.new
-def inflector.camelize(basename, _abspath)
-  basename == "html_parser" ? "HTMLParser" : basename.camelize
-end
-
Rails.autoloaders.each do |autoloader|
-  autoloader.inflector = inflector
+  autoloader.inflector.inflect(
+   "html_parser" => "HTMLParser",
+   "ssl_error"   => "SSLError"
+ )
end

「inflectionってときどきinfection(感染)と間違えそうになります😆

⚓ルーティングのマッパーでHTTPのOPTIONSをサポート

# actionpack/lib/action_dispatch/routing/mapper.rb#L752
+       # Define a route that only recognizes HTTP OPTIONS.
+       # For supported arguments, see match[rdoc-ref:Base#match]
+       #
+       #   options 'carrots', to: 'food#carrots'
+       def options(*args, &block)
+         map_method(:options, args, &block)
+       end

つっつきボイス:「何とHTTPのOPTIONS verbがルーティングマッパーで初めてサポートされたそうです」「あれ〜今までなかった?」「OPTIONSってそういえばあったわ😆

参考: OPTIONS - HTTP | MDN

curl -X OPTIONS http://example.org -i

HTTP/1.1 200 OK
Allow: OPTIONS, GET, HEAD, POST
Cache-Control: max-age=604800
Date: Thu, 13 Oct 2016 11:45:00 GMT
Expires: Thu, 20 Oct 2016 11:45:00 GMT
Server: EOS (lax004/2813)
x-ec-custom-error: 1
Content-Length: 0
developer.mozilla.orgより

「新機能にしてはテスト無しでズコっと入ってますね😆」「今更ですけどmapper.rbのコード↓めちゃ長い〜😇」「2300行😇

「#37370のプルリク↓見ると、今まではmatchを使わないと書けなかったのか」「シンタックスシュガーというか😳」「OPTIONS何に使うんだろう😆」「要るのかどうかは知らないけど😆」「クローラーとかで使いそうですけど、業務だとあまり使わないかな?」「とも言い切れなさそう😆

# 同PRより
# before
match 'bar', to: 'foo#bar', via: :options

# after
options 'bar', to: 'foo#bar'

「ルーティングといえば、以前つっつきでmorimorihogeさんが『Railsのルーティングは組み合わさったときにどう動くのかがわからなくてつらい』って言ってましたね(ウォッチ20180406)」「ほんにそれ: ひとつひとつのAPIにはドキュメントがあるけど組み合わせたときがマジむずいし、しょうがないからルーティングを切ってはアクセスしてみて動いた〜とか動かない〜とかやってますし😭

Railsのルーティングを極める(前編)

⚓番外: RailsはまだSameSite=Noneパッチがマージされていない


つっつきボイス:「前回のウォッチのレビュー中に教わった情報で、cookieにSameSite=Noneを設定していないサイトは来年2月からChromeでSameSite=Laxとみなすぞということだそうです」「Laxって『ゆるい』ってことか😆」「Railsではそれに対応する2017年の#28297がまだマージされてないそうです😳

参考: Chrome で SameSite=None に関する Cookieについての警告が表示される | ラボラジアン
参考: CookieのSameSite属性 NoneとLaxの違い - Qiita

#28297の最新のコメントを見ると『現在のメンテナンスポリシーによるとこれが入るのは早くてRails 6.1で、バックポートはされないかも』とあります。

⚓Rails

なお、今週のRuby Weeklyの末尾のICYMIがなかなかよさそうなエントリでした。


つっつきボイス:「ICYMIを調べたら『In case you missed it』の略で『もしご存知なければ』という感じですね☺

「その中でこの記事↓はリードオンリーのRailsコンソールを使う方法の解説です」「productionのデータをぶっ壊さずにコンソール使いたいときはあるかも☺」「dry run的な」「saveやめろぉぉみたいなことがなくなるのはよさそう😆」「ローカルでもリードオンリーコンソールをやりたいことはあったりしますね: 頑張って作ったテストデータをうっかり壊したくないときとか😋

参考: How to Setup a Readonly Rails Console - DEV Community 👩‍💻👨‍💻

⚓Rails 6の新しいデフォルト設定の意味と、安全にコメント解除する方法(Ruby Weeklyより)

同記事より(長いのでRails.application.config.は略しました):

  • action_view.default_enforce_utf8 = false
  • action_dispatch.use_cookies_with_metadata = true
  • action_dispatch.return_only_media_type_on_content_type = false
  • active_job.return_false_on_aborted_enqueue = true
  • active_storage.queues.analysis = :active_storage_analysis
  • active_storage.queues.purge = :active_storage_purge
  • active_storage.replace_on_assign_to_many = true
  • action_mailer.delivery_job = "ActionMailer::MailDeliveryJob"
  • active_record.collection_cache_versioning = true
  • config.autoloader = :zeitwerk

つっつきボイス:「この記事はRails 6で追加された新しい設定のいくつかを詳しく解説して、移行時に設定のコメントを安全に外す方法も書かれています」「おほ😍

「バージョンアップのたびにconfigの項目って増えますよね😆」「それはしゃーない😆」「RailsのconfigについてはRailsガイド↓にもありますけど最小限しか書かれていないことが多いので😅

「たとえばaction_dispatch.use_cookies_with_metadata = trueを有効にするとpurposeフィールドをcookieに追加してから署名・暗号化する、その代わり一度有効にしたらRails 5.xにダウングレードできなくなる、という具合」「へぇ〜😳」「引き返せない設定😇

action_dispatch.return_only_media_type_on_content_type = falseも長いですけど😆、有効にするとcontent_typeでmedia type以外の値(charset=utf-8など)も含まれるようになる」「最近のcontent_type周りの修正に関連してそう🤔ウォッチ20190902)」

active_job.return_false_on_aborted_enqueue = trueはActive Jobですね」「Active Jobでthrow(:abort)できる↓って知らなかった〜😳」「そこの挙動を変えられるんですね」

# 同記事より
class MyJob < ApplicationJob
  before_enqueue { |job| throw(:abort) if job.arguments.first }
  def perform; end
end

job1 = MyJob.perform_later(false)
job2 = MyJob.perform_later(true) 

active_storage.queues.analysis = :active_storage_analysis」「Active Storageで何か分析してくれるのかな?😆」「あ、画像のheightwidthを取ってmini_magickで使ったりできるのか」

[Rails] MiniMagickでPDFのページ数を取得するときはフォントエラーに注意!

「きりがないのでこの辺で止めますけど、Railsガイドだけだとわからない情報があってよさそうですね😍」「configできれば触りたくないけど😢」「必要になったらこの記事を泣きながら読むことになるんでしょうね😆」「ありそう😆」「この記事翻訳したいです😋

⚓Rails 6でビューヘルパーのimage_altが削除

たしかに最新のapi.rubyonrails.orgから消えています。

# api.rubyonrails.orgより
image_alt('rails.png')
# => Rails

image_alt('hyphenated-file-name.png')
# => Hyphenated file name

image_alt('underscored_file_name.png')
# => Underscored file name

つっつきボイス:「image_altヘルパーがRails 6から消えたそうです」「そういえば最近は自分でaltを設定しないといけないことになってた気がするけどそれかな😎」「image_altがファイル名から適当に推測して生成するaltテキストがスクリーンリーダーなどでいろいろ具合がよくなかったそうです」「やはりaltぐらい自分で書けと」「書きたくないけど😆

後で調べると、Rails 5.2でimage_altが非推奨になっていました↓。image_tagからの呼び出しも削除されたそうです。

⚓DHHも消したがっているaccepts_nested_attributes_for


#26976より

上はDiscourseのclean-railsで知りました↓。


つっつきボイス:「以前から評判のよろしくないaccepts_nested_attributes_forですけど(ウォッチ20180820)、少なくとも2016年の時点ではDHHも殺したいと思っていることを上で知りました😆」「これは殺していいと思う🙋‍♀️」「使ったことあるけど心底つらかった〜😇」「Discourseでjoker1007さんも滅びるべきと書いてますね」「自分もjoker1007さんに全面同意🙋‍♀️

「DHHもコメントで『新しいAPIとして推奨すべきでない』『むしろコントローラで手動でやる方法を示すべき』と書いてますね」「わかる〜😂

「joker1007さんのコメントでも言及されているけど、こういうのはむしろJSON構造から攻略するのがいいんじゃないかって自分も思いますし☺」「なるほど!」「動的にフォームの項目を増やすんなら結局JavaScriptのお世話になりますし、そうやってJavaScript使ったのに結局素のフォームだったら意味ないので、素直にAPI叩けばええやんって思いますし😆

⚓accepts_nested_attributes_forはデモ用なのか?

「ここは自分の推測でしかないんですけど、accepts_nested_attributes_forってもしかすると『Railsなら15分でアプリを書けまっせドヤァ😎』みたいなデモ用なんじゃないかって今思いました」「あ〜何だかわかる気がします😳」「ほとんど何も書かなくてもよしなにやれるあたりとか、そういう用途だと有用なんですよ」「ところがそれを真に受けて業務で使うと途端に破綻するという🤣」「🤣」「モデルもビューも結構癒着しますし😇、これって単純に追加して保存できるだけなんじゃね?って」

「こんな例えが合ってるかどうかわかりませんけど、楽器のキーボードについているデモ演奏ボタン↓にちょっと似ているかもですね😆」「それそれっ🤣」「そのデモ演奏ボタンを本番のライブで無理やり使おうとしているみたいな😆」「デモ演奏だとテンポも変えられませんし😆

「まあそういう感じのデモ機能って15年ぐらい前に流行りましたよね☺」「ボタン一発でブログサイトを作れますとか😆」「そういうデモでhas_many周りを一気にやれるのを見せるのはとってもインパクトあるんですけど、実際には使えないという🤣」「deleteってどうすんの?みたいなレベルで既に悩む」「で建て増しを繰り返すうちに結局詰んだり😇

「idがついてないとcreateだし、idがついていればupdateだし、deleteフラグが立ってればdestroyするし、というのを一見同時にやれそうな気がするんですよ: でも誰もコントロールできない😆」「スレッドセーフとかも大丈夫かどうかよくわからないし😅」「歴史調べてないのであくまで推測&印象😆」「自分一人しか使わない管理画面で、かつ重要じゃないデータを手軽に出したいみたいなユースケースならaccepts_nested_attributes_forはまだワンチャンあるかなって思います☺

「なおaccepts_nested_attributes_forはRails 2.3からあるそうです↓」「割と古くからあった気はします」「さすがに最初期からではなかった😆

⚓Meetup for Rails engineersのスライド3つ


connpass.comより


つっつきボイス:「こちらのイベントを見逃してて、終わってから気づきました😅」「3つは追いきれないので、とりあえず『実践Railsアプリケーション設計』のスライドを見てみましょうか😋

⚓『実践Railsアプリケーション設計』

つっつきではかなり盛り上がりましたが、記事にすると多すぎるので間引いています🙇

「『実装とテストは資料が多いけど、設計の書籍は抽象的な内容が多い』と」「これホントにそう!外部設計と内部設計の本ってたいてい抽象論になっちゃう😭」「なるべく具体的な設計の過程を知りたいですよね」「ところが設計を具体的に書くと、今度は分量が増える割には価値が薄くなっちゃうという😇」「読んだ人にとっては自分の業務に合わないとかが続出しちゃうんですよ😢」「言語が違うだけでも大きく変わりますし」「設計論を具体的に書くと一般性が損なわれちゃうんですね😅

「しかも本当に具体的な設計ってビジネス上の機密に直結しちゃうから、そういうものほど本にできない😆」「そうそう😆

「このスライドでは以下に絞って話を進めていますね」

「要件から重要な名詞と動詞を抽出して、概念を固めたうえで関係をまとめる↓」「つまりエンティティに適切な名前を与える」「そこが超重要👍」「設計って実はものすごく日本語力を要求されるんですよ」「誰が見ても誤解しない名前をつけるのが大事」

「最近自分が設計するときは、いきなり英語名を付けないように注意してますね☺

「これが実際の名前か↓」「ReassociatedRequestだと受動態か完了形か迷っちゃうので、個人的にはReassociationRequestとしたい気がしますけど😆」「実際の業務を見ないとどっちが適切か判断難しいですね😅」「やっぱり名前むずい😭

「このスライドいいですね👍」「ここに書ききれないぐらいいい話がいっぱい出ました😂」「こういうスライドを元に強い人が解説するのがよい気がしました😋

⚓でかい画像を正しく扱う


つっつきボイス:「お馴染みEvil Martiansの記事で、Railsに限定せずにでかい画像を適切に扱う方法を解説しています」「こんな図も↓」「かなり長い記事…😅


同記事より

⚓TimeWithZoneクラス


つっつきボイス:「ベストマサフミさんの短い記事です」「:dbto_timeで変換しないとUTCになっちゃうのか😳

⚓その他Rails

つっつきボイス:「RAILS_ENV=stagingはたしかに悪い文化!」「productionとstagingで環境を分ける意味ってあんまりなくて、stagingはデータが本物ならproductionになれるようにするのが正確ですね☺」「でないとif stagingみたいなのができてだんだんつらくなるし」「挙句の果てにstagingはよくてもproductionでコケたりしますし😇」「そしてproductionはデプロイしないとどうなるかわかりません、になって本末転倒になると😆


つっつきボイス:「まだbetaだそうですが気になりますね😋」「Railsとフロントエンドってそんなに相性悪くないと自分は思うんですけどね☺」「仲良くなれないという思い込みもあるのかも?」「たぶんね」「でもたぶん仲良くはなれない🤣」「🤣


前編は以上です。

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

週刊Railsウォッチ(20191029後編)Ruby 2.7.0-preview2、tapping_device gemとhumanize gem、平成Ruby会議ほか

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

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

Rails公式ニュース

Ruby Weekly

週刊Railsウォッチ(20191107前編)Active Recordモデルをprivateで封じ込める、心折れないRailsスキーマ管理、Railsセッションをクロスドメイン共有ほか

$
0
0

こんにちは、hachi8833です。Rails 6.0.1が先週リリースされましたね🎉

Rails 6.0.1がリリース!修正を追ってみました

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

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

第16回目公開つっつき会は、来週11月14日(木)19:30〜にBPS会議スペースにて開催されます。今回は月初ではありませんのでご注意ください。

週刊Railsウォッチの記事にいち早く触れられるチャンス!発言・質問も自由です。引き続き皆さまのお気軽なご参加をお待ちしております🙇

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

公式情報を中心に見繕いました。

⚓マルチDBのマイグレーション後に同じデータベースに再接続するよう修正

標準的なマルチDBセットアップで、2番目のデータベースがレプリカでなく、独立したテーブルセットを持っている状態で以下の非常にシンプルなタスクがあり、rails db:migrate fooを実行したとする。

task foo: :environment do
  puts User.last # or some model with a table that only exists in the primary db
end

実際の振る舞い: establish_connectionがマイグレーションタスクごとに実行されるため、プライマリではなく直前のマイグレーション対象データベースに対してクエリが実行される。
期待される振る舞い: マイグレーションタスクがクリーンアップされ、実行後プライマリ・データベースに再接続される。
#37578より大意


つっつきボイス:「Rails 6のマルチDBがらみの修正ですね」「お、rakeタスクですか」「コネクションをoriginal_configに保存しておいて、終わったらそれを復元しているんですね↓」「コネクションが1つだったら起きなかった問題っぽい」「本番で知らずに切り替わってたらびっくりして目を疑っちゃいそう😇」「実際に使わないと見つけにくそうなバグですね☺

# activerecord/lib/active_record/railties/databases.rake#L81
  desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
  task migrate: :load_config do
+   original_config = ActiveRecord::Base.connection_config
    ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
      ActiveRecord::Base.establish_connection(db_config)
      ActiveRecord::Tasks::DatabaseTasks.migrate
    end
    db_namespace["_dump"].invoke
+ ensure
+   ActiveRecord::Base.establish_connection(original_config)
  end

⚓ローカルキャッシュを改変するときのバグを修正

このテストケースがバグをある意味正しく示してくれると思う。手短に言うと、戻り値の改変からは既に保護されていたにもかかわらず、元の値の改変から保護されていなかった。
同PRより大意

# 同PRより
my_string = "foo"
cache.write('key', my_string)
my_string << "bar"
cache.read('key') # => "foobar"

つっつきボイス:「上のコード例を見る方が早いと思うんですけど、キャッシュの値に入れた元の値を改変するとキャッシュの値まで変わっちゃってるという😳」「これはアカンやつ〜😆」「dupしないでそのまんまキャッシュに入っちゃってたか😇」「それを防ぐためにdup_value!を追加したんですね」

# activesupport/lib/active_support/cache/strategy/local_cache.rb#L60
-         def write_entry(key, value, **options)
-           @data[key] = value
+         def write_entry(key, entry, **options)
+           entry.dup_value!
+           @data[key] = entry
            true
          end

「これに限らずhashやarrayで割とよくあるバグですね☺」「これはあるある」「単純に@data[key] = valueしたら、valueが変わるとキャッシュの値まで変わっちゃう😇」「誰も書き換えなければ問題ないんですけど、キャッシュだから書き換えあるでしょうし😆」「よけてたはずなのにどして?ってなったり😆」「ぱっと見正しそうなだけに見落としそう😅

「まあ誰しもやりそうなバグですから☺」「でもやったらアカン😆」「キャッシュに入れたmy_stringを後生大事に使い回すのがそもそもよくなかったという考え方もあるかも😆」「修正でdup入ったから微妙に遅くなるでしょうね😆

「#37587の場合は同じKeyに対してread / write処理を書いて、かつ元のオブジェクトが参照できるときにしか発生しないので、『同一リクエスト内で同じオブジェクトをwrite / readした』『クラス変数などのリクエストをまたいでメモリにデータを保持する変数でwrite / readした』とかのケースぐらいで、割とレアな気はしますね☺」「おぉ」


「こういうバグが起きにくい言語仕様ってあるのかなって、ついそっちを考えちゃいます😅」「全部値渡しにしたらデカいオブジェクトのコピーが半端ないコストになりますよ😆」「参照で渡したいときとコピーしたいときとありますからね〜☺」「C言語知ってる人にならRubyは基本的にポインタ渡しだよって説明できますけど」「若い人だとポインタ知らなさそう😆

参考: ポインタ (プログラミング) - Wikipedia

なおdup_value!はActiveSupport::Cache::Entryにありますが、なぜかapi.rubyonrails.orgで出てこなかったのでAPIdockを貼ります。

参考: dup_value! (ActiveSupport::Cache::Entry) - APIdock

⚓インラインジョブを別スレッドで実行できるようになった

# activejob/lib/active_job/queue_adapters/inline_adapter.rb#L13
    class InlineAdapter
      def enqueue(job) #:nodoc:
-       Base.execute(job.serialize)
+       Thread.new { Base.execute(job.serialize) }.join
      end

      def enqueue_at(*) #:nodoc:
        raise NotImplementedError, "Use a queueing backend to enqueue jobs in the future. Read more at https://guides.rubyonrails.org/active_job_basics.html"
      end
    end

つっつきボイス:「インラインジョブを別スレッドで実行できるようにしたそうです」「今までは別スレッドにできなかったと😳」「Thread.newでジョブを実行してからjoinしてますね」「enqueueだし、もしかするとjob.serializeが重いのかも🤔」「joinするということはジョブの完了を待つってことなのかな?」「issueの方を見るとよさそう↓」

「これはThread.newした中でexecuteさせることで直ちにenqueueした処理を解放させて、次のenqueue 待ちをなるべくゼロにしたい、ということだと思います」「おぉ」「これはOSの割り込みハンドラによるコンテキストスイッチ実装とかでもよくある実装方針で、割り込みハンドラはなるべく限界まで小さくする、というやつですね☺(割り込みハンドラの処理中は他の割り込みハンドラを受けられなくなってしまうので、その時間は最小限にするために、割り込みハンドラでは即queueに処理イベントを積むだけ積んでハンドラを脱出する)」「なるほど!」「多分、micro jobが大量に実行されるようなユースケースになってくると、ジョブのスループットに影響が出てくるんだと思われます」

「あ、issueに例のCurrentAttributes↓が登場してます😆」「憎っくきCurrentAttributes😆」「これってグローバルステートだからスレッドからこちょこちょするときに気を付けないといけないんでしょうね☺」「スレッド周り難しくてよくわかんないけど、上の修正は何となくworkaroundっぽい雰囲気🤔」「この修正で切り抜けられるならまあいいのかなと☺」「スレッド生成してもコストはそんなに変わらなさそうではある🤔

Railsの`CurrentAttributes`は有害である(翻訳)

「上はDHHが入れたCurrentAttributesに反対してた人の記事を翻訳したもので、CurrentAttributesはやりすぎだという主張ですね」「そもそもグローバルステートですし😆」「記事の人はどちらかというと設計として好きになれないみたいです」「わかる😆」「一応CurrentAttributesにはスレッドローカルな変数もあるみたいですけど、それが回り回って今回のissueにつながったんだとしたら何となくわかる気がする☺」「グローバルステートならスレッドセーフであって欲しいですよね」

「こういうCurrentAttributes的な機能って、わかってて使う人にはとっても有用なんですよ😆」「あ〜それはある意味難しい問題😅」「そしてよくわかってない人が飛びつくと詰む、みたいな害の方が大きくなったりしがち😆」「共有情報をスレッドに乗せるみたいなコードをJavaで見たことはあるので、CurrentAttributesがまったくナンセンスということはないんじゃないかとは思いますね☺」「使う人を選ぶ機能😆

⚓GitHub Actionsに対応

# .github/workflows/rubocop.yml
name: RuboCop
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Set up Ruby 2.6
      uses: actions/setup-ruby@v1
      with:
        ruby-version: 2.6.x
    - name: Install required package
      run: |
        sudo apt-get install libmysqlclient-dev libpq-dev libsqlite3-dev libncurses5-dev
+   - name: Cache gems
+     uses: actions/cache@preview
+     with:
+       path: vendor/bundle
+       key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
+       restore-keys: |
+         ${{ runner.os }}-gem-
    - name: Build and run RuboCop
      run: |
+       bundle config path vendor/bundle
        bundle install --jobs 4 --retry 3
        bundle exec rubocop --parallel

つっつきボイス:「これはGitHub向けの改修ですね」「gemのキャッシュと最新のRuboCopに対応」「GitHubがRailsでできてるのは有名ですよね😋」「GitHub Actionsはウォッチでも何度か取り上げましたけど(ウォッチ20190925)結構期待できそう😍

rubocop --parallelって知らなかった😳」「お、これは欲しいかも😋」「Rubocopちゃんで大量修正が必要なプロジェクトなんかでも、1ファイルごとにやれればいいからparallelにすれば速くなりますね😍」「どうせRSpecの方が断然遅いからまあ別にって感じですけど🤣」「🤣

参考: rubocop/basic_usage.md at master · rubocop-hq/rubocop
参考: Rubocop を使っているアナタがするべき2つのこと - Qiita

⚓ActiveStorage blobからrequire_dependencyを排除

# activestorage/app/models/active_storage/blob.rb#L17
class ActiveStorage::Blob < ActiveRecord::Base
- require_dependency "active_storage/blob/analyzable"
- require_dependency "active_storage/blob/identifiable"
- require_dependency "active_storage/blob/representable"
+ unless Rails.autoloaders.zeitwerk_enabled?
+   require_dependency "active_storage/blob/analyzable"
+   require_dependency "active_storage/blob/identifiable"
+   require_dependency "active_storage/blob/representable"
  end

つっつきボイス:「ZeitwerkがRails 6で殺したrequire_dependencyがActiveStorage::Blobにちょっぴり残ってたので排除したという小さい修正です」「サーチアンドデストロイ💀」「一応Zeitwerkなしでもやれるようにしないといけませんし☺」「これで殺戮完了したのかな?」「どうでしょう😆

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

require_dependencyはRailsで独自に作ったメソッドでしたか〜」「以前のウォッチでも名前空間地獄の話題でrequire_dependencyの話になりましたね(ウォッチ20181022)」「Zeitwerkになってrequire_dependencyが不要になったというかむしろ完全排除される方向に」

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

⚓ActiveRecord::Baseから不要なrequireを削除

# activerecord/lib/active_record/base.rb#L
-require "yaml"
require "active_support/benchmarkable"
require "active_support/dependencies"
require "active_support/descendants_tracker"
require "active_support/time"
-require "active_support/core_ext/module/attribute_accessors"
-require "active_support/core_ext/array/extract_options"
-require "active_support/core_ext/hash/deep_merge"
-require "active_support/core_ext/hash/slice"
-require "active_support/core_ext/string/behavior"
-require "active_support/core_ext/kernel/singleton_class"
-require "active_support/core_ext/module/introspection"
require "active_support/core_ext/class/subclasses"
require "active_record/attribute_decorators"
require "active_record/define_callbacks"
require "active_record/log_subscriber"
require "active_record/explain_subscriber"
require "active_record/relation/delegation"
require "active_record/attributes"
require "active_record/type_caster"
require "active_record/database_configurations"

つっつきボイス:「Active Record::Baseに要らないrequireが結構残ってたのが削除されてました」「Baseから消えるってなかなかスゴい😆」「core extensionあたりのrequireはいつの間にか冗長になってたんだろうな〜」「あ、requireしなくてもautoloadでモジュールが自動読み込みされるようになってたのか☺」「なるほどね!」

# b2c9ce3より
# activerecord/lib/active_record.rb#58
    autoload :Base
    autoload :Callbacks
+   autoload :Core
    autoload :CounterCache
    autoload :DynamicMatchers
    autoload :DynamicFinderMatch

参考: module function Kernel.#autoload (Ruby 2.6.0)

requireって雑に増えていきがちだからコワくてなかなか消せないのが大変😆」「いつかは消さないといけないんでしょうけど」「かぶっててもfalseが返るだけで害はないのでなかなか消されなさそう😆」「このたびfalseになることが確定したんでしょうね☺

⚓Rails

⚓Active Recordモデルをprivateにして大人しくさせてやった(Ruby Weeklyより)


つっつきボイス:「tameは『飼いならす』ですね😆」「モチベの説明が長いので後で追ってみます」

Account.public_methods.sizeでメソッドが685個出てきた😆」「むちゃくちゃや😆」「これは死にたくなる😇

「これがコンセプトみたいです↓」「むむむ…?Accountクラスを複数形のAccountsモジュールにして、Accounts::Modelnewしてからテーブル名だけ指定し、そしてprivate_constant :Modelでモデルをprivateにした…だと..?うはぁ〜!」「ウケた😆、喜びですか驚きですかあきれてますか?」「いや〜、これは面白い!!🎉

# 同記事より
module Accounts
  # Some important stuff up here, which will get to in a bit

  class Model < ApplicationRecord
    self.table_name = 'accounts'
  end
  private_constant :Model

  # Some important stuff down here, which will get to in a bit
end

「これは最近ActiveModel絡みでよく話している、永続化層切り離しですね〜❤」「おぉ」「上のように書くことでActiveRecordのメソッドのほとんどを殺すことができる: そして以下みたいに欲しいメソッドだけself.fetchとかself.createみたいに書いて単にモデルにdelegateして、かつそのモデルを返している」「いわゆる委譲ですね」

ModelモデルはprivateなのでAccountsモジュールの中なら見えるけど外部からはシャットアウトされる」「Accounts::Modelで外からいじるんじゃねーぞ、と😆」「これ確かに面白〜い!😋」「Rails wayじゃありませんけどね😆

# 同記事より
# app/models/accounts.rb
module Accounts
  def self.fetch(id:)
    Model.find(id)
  end

  def self.create(name:)
    Model.create!(name: name)
  end

  class Model < ApplicationRecord
    self.table_name = 'accounts'
  end
  private_constant :Model
end

「オレが委譲で許した以外の方法でモデルにアクセスするなよと😆」「返すものがモデルじゃなくてリレーションになるとまたちょっと微妙な話になるんですけど☺

「その発展型がこれか↓」「お、最後のAccountは普通のPORO(Pure Old Ruby Object)で、fetchcreateが今度はModelじゃなくてカスタムのAccountクラスを返すようにしたと、ほほぉ〜これはたぶんwhereみたいなリレーションを相手にしたくないと言ってそう😋

# 同記事より
# app/models/accounts.rb
module Accounts

  # --- Public APIs
  def self.fetch(id:)
    db_object = Model.find(id)
    Account.new(
      id: db_object.id,
      name: db_object.name,
    )
  end

  def self.create(name:)
    db_object = Model.create!(name: name)
    Account.new(
      id: db_object.id,
      name: db_object.name,
    )
  end

  # --- Private ActiveRecord model
  class Model < ApplicationRecord
    self.table_name = 'accounts'
  end
  private_constant :Model

  # --- Entity for the outside world
  class Account
    attr_reader :id, :name

    def initialize(id:, name:)
      @id = id
      @name = name
    end
  end
end

「さらにトランザクションもこの形↓でやってるし😳」「業務コードらしくなってきた😋

module Accounts
  def add_seat(id:)
    Model.transaction do
      db_object = Model.find(id)
      db_object.number_of_licenses += 1
      db.object.save!

      if db_object.number_of_licenses == 5
        SalesNotification.create!(account_id: id)
      end
    end
  end

  class SalesNotification
    belongs_to :account
  end
  private_constant :SalesNotification

  # Rest of implementation...
end

「まあこのパターンを既存のRailsアプリでいきなりやるのは無理あるのでそれはおいとくとして、今後はこういうふうに作ってみいやという感じかな〜😆」「自分には、ある意味カプセル化の基本に立ち返ったように見えますね☺」「そう!ちゃんとカプセル化してる」「これを実際にやるかどうかは別としても、ちょっと新鮮ですね😍

「一応記事の末尾にもいろいろ書いてますね: これはRailsのデフォルトのパターンじゃないし、どのActive Recordモデルに適用できるとも限らないと」「そりゃそうだ😆」「機が熟すまでこの設計には飛びつかない方がいいということみたいですね☺」「最初からこう書いていれば無駄なメソッドが600個も生えてこなくて済むでしょうけど😆

「元々Active Recordが継承でやるように作られちゃってるからなんでしょうけど😢」「まあそれはあるかも☺」「この記事を書いた人は、たぶんデータベースに直接触らせたくないマン😆

「このパターンでやれそうな例として『AccountUserをいつも同時に変更しているなら、同じモジュールに入れるべきじゃね?』と思えたときが挙げられてますね」「あ〜わかる!離れているモデルをいつも同時に扱ってわけわからなくなるぐらいだったらモジュールに閉じ込めてprivateにしちまえと😆

「いわゆるPoEAA↓のActiveRecordパターンからきちんとビジネスオブジェクトを切り離す前段階としては悪くなさそうですね: テストコードがあればとりあえず不用意にActiveRecordの呼ばれたくないメソッドを隠蔽できるのはまあ悪くないのかも?(つらそうだけど)」

「それにしても面白いパターンだわ〜😋」「カプセル化としてはとてもキレイではある☺」「これが実際にうまく当てはまる場合って何だろう?ん〜とん〜と🤔」(以下延々)


以下は記事冒頭の「モチベーション」より:

システムが大きくなったらカプセル化を強化すべきである。私たちはマイクロサービスや何ちゃらRailsエンジンでやりたいのではなく、Rubyの基本機能を少々用いることで実現する。
(中略)
そういうわけでモデリングにおいて防衛的なアプローチを始めた。つまりサポートできるものだけをpublicにしようということだ。これによってモデルの表面積が小さくなってサポートしやすくなるし、用途が絞られることで内部変更もしやすくなる。
(中略)
最後に、ROMSequelのようにData Accessパターンでこれに近いことをやれるライブラリはいろいろあるものの、それらに完全に乗り換えるのは簡単ではない。おそらく皆さんのアプリは最初からActive Recordを使っているだろう。本記事では、そうした技術への乗り換えが困難なまでに育ったRailsアプリを前提としている。
「データベースはインターフェイスじゃないんだけど!」とつぶやいてる人にはもしかするとこのパターンが向いているかもしれない。
同記事より大意

⚓Railsのセッションをクロスドメインで共有(RubyFlowより)


つっつきボイス:「1本目はcookieとセッションの基本的な解説で、2本目が本題のようです」「クロスドメインでセッションを共有ぅ〜?」「無茶な😆

「どうやらこの人たちはapp.kittens.iodev.kittens.ioという2つのドメインで認証を共有したいらしいです」「なるほどそっちですか😆」「cookieの仕様でこういうのってやれるんだったかな?🤔」「この記事ではセッションストアをRedisにしてるので、それならやれそう☺

「お、このconfig↓でdomain: :allにするのがポイントらしい😳」「これでドメインが変わってもcookieをよしなに扱えるってこと?」「へぇ〜😳

# 同記事2より
Rails.application.config.session_store :redis_session_store,
  key: '_kittens_session',
  serializer: :json,
  domain: :all,
  redis: {
    expire_after: 1.week,
    key_prefix: 'kittens:session:',
    url: ENV['REDIS_SESSIONS_URL']
}

「この記事みたいにサブドメインの違う複数サーバーでセッションを共有したいことって結構あるんでしょうか?」「サブドメインがwwwとかliveとかloginみたいに分かれてて、loginで認証したら他のサブドメインも見られるようにする、なんてのは普通にやりますね☺」「SSO↓でやると大げさになっちゃうんでしょうか?」「まあそのためだけにSSOは使わないでしょう😆」「これでやれるならサブドメインを気軽に作れますし☺」「昔セッション共有やるべきかどうかについて議論になった気がするけど思い出せない😆

参考: シングルサインオン - Wikipedia

追いかけボイス: 「web書くならにcookieのdomain指定は把握しておいてほしいなあ…大昔にこんなのを書いていました↓」「おぉありがとうございます😂」「まあ今はさらに色々ありますが😆

社内勉強会でSOP (Same Origin Policy) の話をしました

⚓Active StorageはRails 6でどう変わったか


つっつきボイス:「記事はActive StorageがRails 6でどう変わったかというまとめで、このSaeloun Blogは最近グロスで翻訳の許可ももらえました😋


「お、mini_magickが置き換わった?」「そういえばウォッチでもimage_processingというgem↓に置き換わったのを扱ってた覚えが(ウォッチ20180511)」「画像の向きも自動で修正してくれるとか、image_processingよさげ😍


「次は画像のvariantのサポート」「variantはサイズ違いの画像ですね」「carrierwaveとかでやってたのがActive Storageでもできるようになった☺

carrerwave↓のgemspecを見るとimage_processingが入ってますね😋。mini_magickもまだありますが。


「最後がhas_many_attached」「これだけで書けるのはアツい❤」「そういえばRails 6で挙動が変更されたんでした(ウォッチ20190729)」「前はattachしてupdateすると追加されてたのが、Rails 6で更新されるようになって他と挙動を合わせたと」「countの結果↓違う〜😨」「breaking changeなのでオプションで選べるようになったんでした」

# 同記事より
# Rails 5まで
blog = ActiveStorage::Blob.create_after_upload!(filename: "updated_pic.jpg")
user.update(images: [blog])

user.images.count
=> 2

# Rails 6
blog = ActiveStorage::Blob.create_after_upload!(filename: "updated_pic.jpg")
user.update(images: [blog])

user.images.count
=> 1

⚓心が折れないRailsスキーマ管理


つっつきボイス:「Railsスキーマ管理で心が折れないための方法😆」「冒頭で早速例の『マイグレーションでActive Recordモデルを参照するな』が出てきてますね」「morimorihogeさんも口を酸っぱくして言ってるヤツ(ウォッチ20190415)」「babaさんも昔記事書いてました↓」

[Rails 3] 失敗しないmigrationを書こう

「次は使い捨てのスクリプトでデータをインポート」「one-offは『使い捨ての』という意味ですね」「捨てスクリプトを全環境で実行したらもうgitにも登録するなと」「それわかる〜😋」「基本的には残す意味ないヤツ😆」「one-off migrationスクリプトを歴史としてコミット履歴に残すというのは別に悪くはない気もしますね: Wikiとかに書いてもいいんだけど『いつのコードで動かすことを想定していたか』を明らかにするという点ではコミットに挟まっててrevertした履歴があるというのも歴史管理としては一つの戦略だとは思う(これがベストだとは思いませんが)」「おぉ」

「次はどの環境のスキーマを『正』にするかみたいな話」「productionが正に決まっとる😆」「病欠でいない人がスクリプトをローカルで走らせてなくて、しかもスクリプトがもう消されたという状況になったら、production->staging->developmentの順で最新にすると」「考えたくない状況😅」「むか〜しスキーマのインデックス周りが環境ごとにちょっぴりずれてて修復したの思い出した😭」「マイグレーションの定番を押さえるのによさそうな記事ですね😋

見出しより:

  • マイグレーションはスキーマだけを変更する
  • seedやデータインポートを使い捨てスクリプトでやる
  • pruneを徹底して環境を同期する
  • おまけ: いらないマイグレーションファイルを消す
  • 上のやり方から離れるべき場合

⚓その他Rails


前編は以上です。

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

週刊Railsウォッチ(20191106後編)holiday_japan gemで日本の祝日判定、小さい関数が有害になるとき、Gitブランチのファジー検索ほか

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

週刊Railsウォッチ(20200427前編)Railsで避けたい8つのミス、ridgepole導入の注意点、RDS ProxyのPostgreSQL対応ほか

$
0
0

こんにちは、hachi8833です。

つっつきボイス:「近所のビアパブに注文しておいたビール取りに行ってた🍺」「お疲れさまです!」「最近酒類の販売免許が飲食店向けに割と簡単な手続きで申請できるようになったじゃないですか」「あ、酒の持ち帰りは居酒屋の免許とは別なのか😳」「持ち帰りだと販売として扱われるので☺️」「なるほど〜」「本来だと酒販免許を取るのはかなり面倒なんですけど、その店は5日ぐらいで取れたって😋」「そういえば都内で店やってる知り合いも2日で取れたって言ってました😋」「都内だと特に早いらしい」「ではつっつき始めましょう〜」

参考: 酒類のテイクアウト販売が可能になる「期限付酒類小売業免許」とは?─ 免許の概要から申請のポイントまで | 日本酒専門WEBメディア「SAKETIMES」

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

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

Active Recordのremove_indexif_exists:オプションが追加

# activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb#L451
-       def remove_index(table_name, column_name = nil, options = {}) #:nodoc:
+       def remove_index(table_name, column_name = nil, options = {}) # :nodoc:
          table = Utils.extract_schema_qualified_name(table_name.to_s)

          if column_name.is_a?(Hash)
            options = column_name.dup
            column_name = options.delete(:column)
          end
          if options.key?(:name)
            provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
            options[:name] = provided_index.identifier
            table = PostgreSQL::Name.new(provided_index.schema, table.identifier) unless table.schema.present?
            if provided_index.schema.present? && table.schema != provided_index.schema
              raise ArgumentError.new("Index schema '#{provided_index.schema}' does not match table schema '#{table.schema}'")
            end
          end

+         return if options[:if_exists] && !index_exists?(table_name, column_name, options)
+
          index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, column_name, options))
+
          algorithm =
            if options.key?(:algorithm)
              index_algorithms.fetch(options[:algorithm]) do
                raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
              end
            end
+
          execute "DROP INDEX #{algorithm} #{quote_table_name(index_to_remove)}"
        end

つっつきボイス:「remove_indexif_existsオプションを渡す必要のある巨大プロジェクトは大変そう😆」「😆」「本来ならRails標準のActiveRecord::Migrationを使うべきかなと😆」「ridgepoleみたいなものを使う方がいいのかなという気もするけど🤔」

if_existsを使うシチュエーションだとインデックス名が同じでオプション名が同じでないものについては管理できてないことになるだろうから、if_existsしたインデックスがMigrationで作られたインデックスと同一であることまでは保証しきれないんじゃないかという気がしますね😆」「付け焼き刃というか😆」「RailsのMigrationにSQL書いて追加したインデックスとかまでは検出できなさそうな予感😆」「むしろif_existsに頼る状況にならないようにしたい☺️」

「想像ですけどif_existsが欲しい理由ってもしかすると、db:migrateを含むコードが途中でコケるとインデックスだけ中途半端に作られてしまうからif_existsしたいということだったりして?」「あ〜😳」「それってRails開発中のあるあるなんですけど😆、本当ならMigrationがコケたときに安全にロールバックするようにすべきかなと思いますし☺️」「productionリリース後の方がif_existsつけたいかも🤔: remove_indexを2回叩いてMigrationが落ちたら手動で修正するしかありませんし」

このプルリクは、remove_indexif_existsオプションを渡すことで、インデックスが削除済みの場合に無視されるようにする。この動作は、カラムのif/if_not_exists(#38352)や、add_index:if_not_exists(#38555)の動作に合わせてある。
この改修はGitHubでのMigrationでインデックスが削除済みならraiseしたくない場合に有用であることに気づいた。このおかげでremove_indexのモンキーパッチを削除できる。
index_name_for_removeメソッド呼び出しの後でraiseすることも検討したが、こちらのメソッドだとインデックスが存在しない場合はメソッドが実行される前にraiseする。これをリファクタリングするコミットもやってみたが、熟慮の結果、他の実装よりもこちらの方がより明快で素直だと考えた。
今回の変更ではadd_indexのテストにバリデーションも少し追加してある。
編集したメソッドのnodocの修正をよろしく。
同PRより

ridgepoleの注意点

「…ridgepoleといえば、最近ridgepoleが入ってるプロジェクトをやったんですけど、それはそれはカオスでしたよ😇: 何も考えずにridgepoleが導入されてて、もう苦労しかないので何とかして消し去りたいけどめんどくさいのでそのままです🤢」

「ridgepoleのようなマイグレーターではup/down以外のデータのMigrationを扱えないのが面倒😅」「普通にMigrationしようとしたらridgepoleがいてできませんでした😇」「ridgepoleには特定の操作にフックを仕込むとかないですよね?」「はい、ありません😆: なのでわざわざデプロイしてrakeタスク叩いてもう1回デプロイして、というバカバカしい手順を踏むしかなくて😭」「ああ、たしかにそれしかできなくなりますね😅」「Rails標準のMigrationなら全部やれたのに…💢」「Migrationには時系列がありますけど、ridgepoleにはありませんし😆」「なので個人的にはridgepoleキライです」「自分も好きというほどではありませんが☺️」

「クックパッドさんはridgepole入れるにあたって『Migrationは行わない』と決めてますね↓」「困ったらデータを消しても良いようなプロトタイピングや小規模開発とか、Migrationに代わるデータの管理運用がちゃんとした確立されているプロジェクトで用途に合わせてridgepoleを使うのは別に良いと思うんですが、代わりの運用方法がないまま何も考えずにridgepoleが導入されてたのがつらかった😢」「Migrationが必要な案件にridgepoleが入ってたとは😳」「まあRailsのMigrationもひどいのを書かれたら同じくつらいので、チームメンバーのモラルが必要なのは変わりませんし、結局プロジェクトの方針次第ですね☺️」「db/schema.rbは大規模開発に完全に向いてないと思います😤」

「ところでMigrationだとGitの履歴とMigrationファイルの履歴が二重になるよねみたいな話をどこかで聞いたような気がします」「うぅんそれはちょっと違うかな: スキーマの履歴を管理するだけならridgepoleとGitでもいいんでしょうけど、Migrationはスキーマ履歴の管理だけじゃなくてスキーマ更新以外のデータ移行のような作業も含められますし、それがRailsのMigrationのメリットだと思うんですけどね🧐」「Migrationは履歴を知るためのものじゃなくてデータベースを運用するための機能ですから🧐」「う、失礼しました😅」

touch_attributes_with_timeがキーワード引数を取るよう修正

# activerecord/lib/active_record/counter_cache.rb#L52
          if touch
            names = touch if touch != true
-           updates.merge!(touch_attributes_with_time(*names))
+           names = Array.wrap(names)
+           options = names.extract_options!
+           touch_updates = touch_attributes_with_time(*names, **options)
+           updates.merge!(touch_updates)
          end

つっつきボイス:「@kamipoさんによる修正です」「例のキーワード引数関連の修正ですね☺️」「このテストだけtime: Time.now.utcが書いてある↓あたりが試行錯誤感」「キーワード引数対応はまだ先がありそう…」

# activerecord/test/cases/counter_cache_test.rb#L264
  test "reset multiple counters with touch: true" do
    assert_touching @topic, :updated_at do
      Topic.update_counters(@topic.id, replies_count: 1, unique_replies_count: 1)
-     Topic.reset_counters(@topic.id, :replies, :unique_replies, touch: true)
+     Topic.reset_counters(@topic.id, :replies, :unique_replies, touch: { time: Time.now.utc })
    end
  end

rename_columnするとMySQLのカラムコメントが消えるのを修正

# activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L663
        def rename_column_for_alter(table_name, column_name, new_column_name)
          column  = column_for(table_name, column_name)
          options = {
            default: column.default,
            null: column.null,
-           auto_increment: column.auto_increment?
+           auto_increment: column.auto_increment?,
+           comment: column.comment
          }


つっつきボイス:「コメントが消えてたとは😆」「rename_columnの実装レベルの話か: abstractのMySQLアダプタを修正しているし」「所定のオプションだけ通すようにしてたところに漏れがあったっぽい」「バグですね」

6.1でretry_jitter = 0.15がデフォルトで入る

# railties/lib/rails/application/configuration.rb#L159
        when "6.1"
          load_defaults "6.0"

-         if respond_to?(:active_job)
-           active_job.retry_jitter = 0.15
-         end
-
          if respond_to?(:active_record)
            active_record.has_many_inversing = true
          end
@@ -172,6 +168,7 @@ def load_defaults(target_version)
          end

          if respond_to?(:active_job)
+           active_job.retry_jitter = 0.15
            active_job.skip_after_callbacks_if_terminated = true
          end

          if respond_to?(:action_dispatch)
            action_dispatch.cookies_same_site_protection = :lax
          end

つっつきボイス:「前に話題にしたjitterがデフォルトで入るようになりました(ウォッチ20191216)」「こういうのはデフォルトがありがたい😋」「リトライのジッターだし大丈夫でしょう☺️」「これで困るようならたぶんアプリの設計の方がおかしい😆」

Webpackerのintegrityチェックが削除された

# lib/webpacker/configuration.rb#L66
  def check_yarn_integrity=(value)
-   data[:check_yarn_integrity] = value
- end
-
- def check_yarn_integrity?
-   fetch(:check_yarn_integrity)
+   warn "Webpacker::Configuration#check_yarn_integrity=(value) has been deprecated. The integrity check has been removed from Webpacker so changing this setting will have no effect."
  end

つっつきボイス:「Webpackerの修正ですが、昨日bundle updateした後でDockerのRails起動が急に速くなって気づきました」「yarnのintegrityチェックをやめたということ?」「プルリクコメントに、yarnの作者自身がyarn checkは使うなと言ってるのが引用されてました😆」「しかもyarn 2.0でyarn check削除されてるからそれもあって削ったのね😆」「お前のせいで起動遅かったのかと😭」

番外: DHHが#38588をrevert

# actionview/lib/action_view/template/resolver.rb#L229
      def find_template_paths_from_details(path, details)
-       if path.name.include?(".")
-         ActiveSupport::Deprecation.warn("Rendering actions with '.' in the name is deprecated: #{path}")
-       end
-
        query = build_query(path, details)
        find_template_paths(query)
      end

つっつきボイス:「先週のウォッチ20200413で取り上げた#38858のdeprecationをDHHがrevertしてました」「ああ、影響範囲割とでかいんじゃないのって話したヤツ?」「ですです」「deprecation自体を取りやめるということは、確実に検出できるまで差し止めるということでしょうね☺️」

文字列の式展開で誤ってdeprecation warningがトリガされるのが修正されるまでは#38858をrevertしておく。
同コミットより

Rails

Railsで避けたいありがちな8つのミス


つっつきボイス:「既視感ありまくりですけど、まとめてくれているのがいいかなと思って」「N+1問題にcredentialにファットコントローラもファットモデルもやめろ話とかね☺️」「別サービスを呼び出すときはなるべく非同期にと」「?付きの述語メソッドはtrueかfalseを返せというのは命名の話ね😆」「そうしない人がいるのか…😅」「メモ化を使うな、って書いてるのかと思ったらメモ化を使わないミステイクという話か😳」「この見出しの英文、統一感ないですね😆」「ミステイクにnotを付けるのはわかりにくい〜😆」「命名ルールに従う、にnotが付いてないとミステイクにならないんじゃ?」「こっちも整合してない😆」「ともあれ基本的な内容ですね☺️」「たぶん反論は出ないでしょう😆」


見出しを統一してみました。

  • ミス1: N+1クエリをチェックしない
  • ミス2: コンフィグが安全でない
  • ミス3: コントローラのロジックが多すぎる
  • ミス4: モデルのロジックが多すぎる
  • ミス5: 外部サービス呼び出しが非同期化されてなくてブロックされる
  • ミス6: 述語メソッド(?で終わるメソッド)がtrue/falseを返さない
  • ミス7: メモ化を使ってない
  • ミス8: Railsの命名ルールに沿ってない

Heya: Rails用キャンペーンメールgem(Ruby Weeklyより)


heya.emailより


つっつきボイス:「heyaはhey youのオーストラリア方言らしいという説があるようです🇦🇺」「.emailドメインなんてのを使ってる😳」

「どの辺が嬉しい感じでしょう?」「timed email sequenceとあるのでメールの順序が大事ということかな🤔」

# 同リポジトリより
Heya.configure do |config|
  # Heyaを使いたいモデル名を指定する
  config.user_type = "User"

  # キャンペーンのステップを処理するときのデフォルトオプション
  config.campaigns.default_options = {from: "user@example.com"}

  # キャンペーンの優先順位。ユーザーが複数のキャンペーンに加えられると、この順序で送信する。
  # 優先順位の設定がない場合は、ユーザーが追加された順でキャンペーンメールが送信される
  config.campaigns.priority = [
    "FirstCampaign",
    "SecondCampaign",
    "ThirdCampaign"
  ]
end

「なるほど、たとえばユーザー登録して1日後にこのメールを送信するみたいなことがやれるのか」「あ、それをユーザーごとにやれるんですね」「『このサイトの便利な使い方』みたいなメールを送り付けるヤツ😆」「たぶんこれに『登録後7日間ログインしていない場合』みたいな条件も付けられるんじゃないかしら、知らんけど😆」

# 同リポジトリより
class OnboardingCampaign < ApplicationCampaign
  default wait: 1.day,
    queue: "onboarding",
    from: "support@example.com"

  # support@example.comから1日後にメールを送信する
  step :welcome,
    subject: "Welcome to my app!"
end

「たしかにこういう処理はAction Mailer単体では定義不可能🧐」「そうか!」「Action Mailerはメール送信しかできませんし😆」「こういうのをキャンペーンメールと呼ぶのであれば、キャンペーンメールをとても積極的かつ複雑に使いたいときにこういうDSLを書くのかな〜☺️」「自分ならこういうのを実装するよりCRMツールに金払う方がいい気がしますけど😆」「😆」

参考: CRMツールとは?おすすめ14製品を紹介!選び方も併せて解説!|ITトレンド

「HeyaもそういうCRM路線だろうと思いますけど、たぶん世にあるCRMツールの方が遥かに複雑なことができますし😋」「おぉ」「たとえばメール開封率が一定以下の場合はこうする、みたいなことも普通はできますし🧐」「なるほど!」

「どうしても自分で実装したいとかRailsからメール送信したい人なんかはHeyaでいいと思いますが、自分ならCRMツールを統合するかな〜: wait: 1.dayみたいなコードをRailsに入れるのもちょっと心配ですし」「おぉ?」「この値を運用中に変えたときに同じメールがもう一回発射されるかもしれないじゃないですか😅」「なるほど」

tomo: capistranoのオルタナ(Ruby Weeklyより)


つっつきボイス:「capistrano的なデプロイツールみたいです」「tomoはそこそこ前からありますね: 以前検討した覚えあります☺️」「や、そうでしたか」「最近capistranoがあんまり更新されてなくて、capistrano的なデプロイツールないかな〜と探してて見つけた中のひとつにあった気がします👁」

# 同リポジトリより
# .tomo/config.rb

plugin "git"
plugin "bundler"
plugin "rails"
# ...

host "user@hostname.or.ip.address"

set application: "my-rails-app"
set deploy_to: "/var/www/%{application}"
set git_url: "git@github.com:my-username/my-rails-app.git"
set git_branch: "master"
# ...

setup do
  run "git:clone"
  run "git:create_release"
  run "bundler:install"
  run "rails:db_schema_load"
  # ...
end

deploy do
  run "git:create_release"
  run "core:symlink_shared"
  run "core:write_release_json"
  run "bundler:install"
  run "rails:assets_precompile"
  run "rails:db_migrate"
  run "core:symlink_current"
  # ...
end

参考: Capistrano - Wikipedia

「tomoってCapistranoよりは新しい気がするけどどうだったかな(リポジトリの履歴を遡る)」「ところでGitの履歴ってolderをクリックして1ページずつめくらないといけないのが面倒ですね」「まあ履歴の最終ページを算出しようとすると件数カウントが重たいので、それでこういうUIにしてるんじゃないでしょうか☺️」「あ、たしかに😅」「どうやらtomoの履歴の最初は2018年だから割と新しいかな: まあどのぐらい使われてるかも知りませんしまだ使ったこともありませんが😆」「😆」「コンテナ全盛の今、新たにDSL覚えるのもどうかと思いますけど😆」

Railsのコネクションとプールとハンドラ(Hacklinesより)


つっつきボイス:「コネクションとプールとハンドラの違いについての割と短い記事です」「そうそう、RailsにはRailsのコネクションプールがありますので🧐」「データベース側とは別にRailsにあるんですね😳」「たとえばPostgreSQLを使う大規模なプロジェクトだと、PostgreSQLに直接つなぐのではなくて手前にコネクションプールサーバーを配置するというモデルがあります🧐」「なるほど!」「まあアプリケーション側にも似たようなコネクションプールがありますよという話☺️」


記事見出しより:

  • コネクションとは
  • コネクションプールとは
  • コネクションハンドラとは

AWS RDS ProxyのPostgreSQL対応

「…コネクションプールといえば最近AWSのRDS Proxyで何かの機能がアップデートされてましたね: LambdaからRDSにつなぐためのプロキシがPostgreSQLに対応したとか何とか🤔」「へ〜、Lambdaから?」「LambdaからたくさんRDSにつなぐと溢れるからRDS Proxy通しますけど、それがぽすぐれに対応したらしいという話」「おぉ〜これか↓😋」「ごく最近ですね」「今までLambdaからコネクション張るのが地味に面倒だったんですよね😢」「たしかにLambdaからのコネクションはなかなか作れませんでしたね😅」

参考: PostgreSQL 互換の Amazon RDS Proxy (プレビュー)

「とまあこれはマネージドのコネクションプールなわけですが🧐: MySQL用は半年ぐらい前からあるんだったかな」「たしかにLambdaから接続するとコネクションがボトルネックになるので、これは欲しい機能😂」

参考: AWS LambdaでAmazon RDS Proxyを使用する | Amazon Web Services ブログ

「ほら、今までだとせっかくLambdaとAuroraがマネージドなのに、コネクションプールのためにEC2を立てるみたいなバカバカしいことをしないといけませんでしたし😭」「それはマジでバカバカしい😆」「😆」「そこがようやくまともになったという☺️」

「そもそもRDBMSってマネージドと相性があんまりよくありませんし😆」「とはいえ欲しいじゃないですかやっぱり😎」「欲しい😆」「絶対欲しい😆」「DynamoDBと戦うのイヤ😇」

参考: Amazon DynamoDB(マネージド NoSQL データベース)| AWS


「話を戻すと、アプリケーションサーバーは超々一般的にコネクションプールを持ちます、そしてRailsにはアプリケーションサーバーがあります、以上😆」「はい😆」

その他Rails


つっつきボイス:「Webpackerの挙動を追うためにWebpackの挙動を追ってみた感じですね☺️」


前編は以上です。

バックナンバー(2020年度第2四半期)

週刊Railsウォッチ(20200421後編)Ruby 2.4サポート終了、Ruby 3の右代入演算子、GitHubコア機能無料化ほか

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines


週刊Railsウォッチ(20200428後編)Rubyのバックトレース順序が戻る、KubernetesでRailsをスケール、セキュリティソフト入れますか?ほか

$
0
0

こんにちは、hachi8833です。ついさっき見つけた記事がよかったので貼りました。

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

⚓Ruby

⚓Rubyのバックトレース順序が戻る


つっつきボイス:「@kamipoさんが超喜んでますね」「そう、これこれ!以前バックトレースの表示順序が変更されてからマジでやりづらいと思ってましたし😭」「今回の改定を喜んでいる人は@kamipoさん以外にもきっとたくさんいる😂

以下は改定直前のツイートです。

「バックトレースの順序が変わることがあったんでしょうか?」「正確にはRubyのバックトレースが変わったけどRailsのバックトレースの順序は変わらなかった」「あ、そういうことでしたか😳」「Ruby 2.5の変更より前はバックトレースの順序は常に同じだったんですが、変わってからわかりにくくて😭

「『バックトレースの順序が反転するという画期的なギミック』って超ワナですよ😇」「まあちょっと読めば順番が変わっているのはわかるんですけど」「RubyとRailsで順序が違うのが痛い😇」「みんな困ってないんだろうかって不思議だったし😆

⚓提案: Struct#initializeでキーワード引数もデフォルトで取れるようにしよう

# 同issueより
Post = Struct.new(:id, :name)

# 以下に加えて
Post.new(1, "hello") #=> #<struct Post id=1, name="hello">

# 以下の初期化もできるようにしよう
Post.new(id: 1, name: "hello") #=> #<struct Post id=1, name="hello">

つっつきボイス:「あ、Struct.newした後のnewでということか😳」「まあ名前は付いてますし、使いやすさ的には全然いいかなと思うけど🤔

後で現行のRuby 2.7で実行すると以下のように引数が全部id:に入りました。

Post = Struct.new(:id, :name)
Post.new(id: 1, name: "hello")
=> #<struct Post id={:id=>1, :name=>"hello"}, name=nil>

「ちなみに自分はStructもOpenStructもあんまり使わない方で、Value Object作るならちゃんとクラス書きたい派😆」「😆」「今日はいないけどkazzさんが前にStructをちょくちょく使ってるのを見て『大変そうだな〜』って思いましたし😆

参考: class Struct (Ruby 2.7.0 リファレンスマニュアル)
参考: class OpenStruct (Ruby 2.7.0 リファレンスマニュアル)

⚓super_methodメソッドでRubyのDecoratorをテストする(Ruby Weeklyより)


つっつきボイス:「super_methodっていうメソッドがあるって知りませんでした😅」「途中の見出しが『The super super_method method』😆」「superで呼ばれるはずのメソッドをMethodオブジェクトにするみたいです」「Methodオブジェクトにsuper_methodメソッドがあるのは当然でしょうね☺」「リフレクションするのに必要でしょうし☺

self 内で super を実行した際に実行されるメソッドを Method オブジェクトにして返します。
ruby-lang.orgより

「そのsuper_methodでDecoratorをテストするという記事か」「えぇ?、described_class.new.method(:info).super_method.callみたいなテストって書くかなぁ?😅」「わざわざsupoer_methodしてからcallするって、どういう観点のテストなのかわからん😆」「継承とは何ぞやと😆

# 同記事より
RSpec.describe Address do
  describe '#info' do
    subject { described_class.new.info }

    it 'adds the phone number to the default address' do
      expect(subject).to include(phone_number: '123456789')
      expect(subject).to include(
        described_class.new.method(:info).super_method.call
      )
    end
  end
end

「呼べるか呼べないかで言えばたしかにsuper_methodは呼べますけど、こういうテストが欲しくなる状況がぱっと思いつかない😅」「記事はspecがシンプルになるということみたいだけど、ちゃんとテストできてるんだろうか🤔」「デバッグツールを作るのでトリッキーなことをしたいとかならともかく、普通のコードでsuper_methodを使うことはないんじゃないかな〜、普通は☺

⚓クロージャでRubyコードを読みやすくする(Ruby Weeklyより)

参考: クロージャ - Wikipedia


つっつきボイス:「f.call()のショートハンドがf.()というのも今頃知りました」「f.()はカリー化の文脈とかで割とよく見るヤツですね☺」「そうでしたか😅」「自分も見るたびに?って思いますし、あんまり好きな書式でもありませんけど、call()よりは短いですし😆

def bar(f)
  f.()
end
bar(method(:foo)) # => hello from foo

「記事にも『f.()はビジネスロジックではめったに見かけないがオープンソースではよく見かける』ってありますね」「カリー化がビジネスロジックでめったに使われないからでしょう、きっと😆」「まあ😆」「もちろんビジネスロジックでカリー化がふさわしいこともありますし、それなら別にいいんですけど、呼び出しが増えると遅くなりがちなので☺

参考: カリー化 - Wikipedia

「ともかく.()は随分昔からちゃんとしたRuby入門には書いてあると思いますヨ😋」「探します👁

ありました↓。

.()は、メソッド名のないメソッド呼び出しのように見える。これは定義できる演算子ではなく、callメソッドを呼び出すための構文糖である。この記法は、Proc オブジェクトに限らず、callメソッドを定義するすべてのオブジェクトで使える。
『プログラミング言語Ruby』(オライリー)p202より

⚓Rubyのlambda呼び出し

「…Rubyだとlambdaでインラインメソッド的なものを書くときに何だかキモチヨクナイ気がします😆」「はいわかります😆」「C++でもJavaScriptでもかっこ()だけつけて関数と同じように呼べるのに、Rubyはこの.()という書き方のせいで普通の関数とlambdaが区別されるので、一時的な関数を作る気力が削がれそう😇」「それはある😆」「すっごくわかる😆」「普通に()で呼びたいのに😢」「いろんな互換性に配慮した結果そうなっているのはわかるんですが😅」「言語仕様上しょうがないんですけど😅

後で自分でもやってみました。Rubyだと.()ですね。

// JavaScript
let foo = num => { return num * 2 };
foo(7);  // 14
// C++
auto foo = [](auto num) { return num * 2; };
printf("%d", foo(7)); // 14
# Ruby
foo = ->(num) { num * 2 }
foo.(7)  # 14

⚓RailsのHashWithIndifferentAccessがRubyにないのはなぜ?

参考: ActiveSupport::HashWithIndifferentAccess`


つっつきボイス:「Redditにあったスレです」「よく持ち上がる話😆」「昔Matzがその辺の理由についてコメントしてたような覚えがあります」「『RubyがPHPみたいになるから』だったりして😆」「文字列とシンボルは全然違うものなのに、そこがRails方面にうまく伝わらなかったとかそんな感じ🧐」「へ〜」

「プログラミング言語全般ではたしかに文字列とシンボルは違いますし、自分もそう思うんですけど、Webの文脈だとその辺が厄介なんですよね😅」「うんうん」「Webだとどんなパラメータもいったん文字列になってしまうので、そういうのが間に挟まってHashWithIndifferentAccessができたのかなと思ってます☺」「元PHPerとしてはHashWithIndifferentAccessって結構好きなんですけど😆、言語全般として考えたら一貫してないよねというのはワカル」

参考: Smalltalk と Ruby と LISP のシンボル - Smalltalkのtは小文字です

「でも最近のRubyだと文字列をfrozenにする動きがありますけど、それがデフォルトになったらHashWithIndifferentAccessと同じにしてもいいんじゃね?ってちょっと思いますね」「気持ちわかります」「もちろん文字列とシンボルは違うという主張はわかるんですけど、frozenがデフォルトの世界なら多少同一視してもいいのかなと😋」「探索速度とかを考えたら多少の同一視はありかも😋


「…さっきのMatzのコメントは、どうやら以下の記事↓の記憶が自分の中で改ざんされてたらしき🤣」「自分で訳してて忘れてた😆

Rubyのシンボルをなくせるか考えてみた(翻訳)

(Rubyの)シンボルはLispのシンボルを取り入れたもので、Lispのシンボルは文字列とは根本的に異なっていました。(Lispの)シンボルは文字列表現としてはイケてません(し速くもありません)が、Rubyはシンボルに関して独自路線を取ったため、シンボルと文字列の違いは(Rubyの)ユーザーからはそれほど認識されてこなかったのです。
同記事の引用より

「frozenなら事実上一意の値が与えられると考えてもいいんでしょうね」「frozenならそうだと思います☺」「unfreezeしなければ😆」「シンボルって要は整数の定数だから、frozenなstringのアドレスだと思えばいいっちゃいいかも😆」「シンボルはto_sできますし」「個人的にはfrozen string literalをオフにできないことを前提にできるのなら文字列とシンボルを同一視してもいいかなと思います: でも今のRubyだとファイルごとにfrozen string literalをオンオフできるから同一視するとマズいのかもしれないけど☺」「うーむ」

⚓Rubyで線形回帰(Ruby Weeklyより)


同記事より


つっつきボイス:「ruby-linear-regressionというgemで線形回帰やってみた記事だそうです」「自分ならExcelでやれるところはExcelでやって、ExcelでできないところはPythonとかRでやっちゃうかな😆」「たしかにExcelでもできますね😆」「回数が多すぎなければ☺

# 同記事より
require 'csv'
require 'ruby_linear_regression'

x_data = []
y_data = []
csv = CSV.read("top50.csv")
csv.shift

# Load data from CSV file into two arrays -- one for independent variables X (x_data) and one for the dependent variable y (y_data)
# Row[0] = title
# Row[1] = BPM
# Row[2] = Popularity
csv.each do |row|
  x_data.push( [row[1].to_i] )
  y_data.push( row[2].to_i )
end

# 回帰モデルの作成
linear_regression = RubyLinearRegression.new

# 訓練データの読み込み
linear_regression.load_training_data(x_data, y_data)

# 正規方程式でモデルを訓練
linear_regression.train_normal_equation

# 計算コストを出力
puts "Trained model with the following cost fit #{linear_regression.compute_cost}"

# ある曲の人気を250 BPMで予測
prediction_data = [250]
predicted_popularity = linear_regression.predict(prediction_data)
puts "Predicted popularity: #{predicted_popularity.round}"

参考: 線形回帰 - Wikipedia

⚓その他Ruby


つっつきボイス:「MatzのGitHub Sponsorsが今23人ぐらいになったそうです」「お、月10ドル払えばここのバッジに載せてもらえるのか🎫」「こういうのはいいですね〜😋

その後見てみたらスポンサーが65人に爆増してました🎉

なおRailsチュートリアルとRailsガイドでお馴染みのYasulabもGitHub Sponsorsの審査にパスしました🎉

⚓DB

⚓PostgreSQL 13のinsert-onlyテーブルautovacuumを改善(Postgres Weeklyより)


つっつきボイス:「insert-onlyテーブルなんてのがあるのか」「ぽすぐれ何でもある😆」「UPDATEしないテーブルという意味でしょうか?」「いわゆるappend-onlyテーブルでしょうね🧐」「昔からあるんですね」

参考: Querying Append-Only Tables | Stitch Documentation

「autovacuum自体はPostgreSQLに昔からあるけど、それをinsert-onlyテーブル向けに賢くチューニングしたということみたい」「おぉ」「そもそも最近のPostgreSQLはvacuumを手動で実行しなくてもそれほど遅くならなくなったはずなので☺

autovacuumはテーブルの特定の値が上回ると常に実行される。この値は“autovacuum_vacuum_insert_threshold(デフォルトは1000)とautovacuum_vacuum_insert_scale_factor(デフォルトは0.2)から以下のように算出される。
insert_threshold + insert_scale_factor * reltuples
reltuplesは、pg_classカタログから取得したテーブルの行数の見積もり。
同記事より


見出しより:

  • insert-triggeredでautovacuumが動くしくみ
  • ユースケース1: insert-onlyテーブルでの「anti-wraparound」vacuum
    • insert-onlyテーブルでVACUUMが必要な理由
    • insert-onlyテーブルのanti-wraparound vacuumが問題になる可能性がある理由
    • 破壊的なanti-wraparound vacuumから身を守る方法
  • ユースケース2: insert-onlyテーブルでのindex-onlyスキャン
    • PostgreSQLのindex-onlyスキャンのしくみ
    • insert-onlyテーブルでのindex-onlyスキャンの問題点
    • insert-onlyテーブルでindex-onlyスキャンを取得する方法
  • ユースケース3: insert-onlyテーブルでの「hint bit」
  • 今後の改良
  • まとめ

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

⚓KubernetesでRailsをスケールする(Hacklinesより)


つっつきボイス:「要はRailsをPodに押し込めろと😆」「ポッドというと…」「PodはKubernetesのコンテナの単位です🧐」「そういえばKubernetesを勉強中って話してましたね」「合間を見て、くばねて本をちまちまと😆

参考: Podとノードについて - Kubernetes

「最近BPSのWebチームもDockerやdocker-composeの運用が定着してきたので、そろそろ普通のコンテナオーケストレーションツールで包んじゃおうかなと構想中📈」「お、いよいよですね😋」「AWSのECSやFargateを見てくれているメンバーがいるので、じゃ自分はくばねて方面をやろうかなと思ってます☺」「なるほど〜」「BPS社内にあるDocker用のホスティングマシンが6個ぐらいできてきてそろそろややこしくなってきましたし😆」「前は4つだった気がしますけどさらに増えてるとは😳」「メモリが足りなくなると足す感じでやってます☺


見出しより:

  • スケールアップする
  • Autopilotでスケールする
  • タスクをJobsで実行する
  • productionのログファイルをinspectする
  • Podにログインする
  • その他の便利タスクたち

⚓GitHubとGitLab


つっつきボイス:「GitHubとGitLabの機能リリースがいくつか出てたので」「GitHubもGitLabも、リポジトリ管理ツールというよりは開発プロジェクトの管理ツール&オーサリングツールに変化してきた感じはありますね🧐」「ところで以下の記事の見出しにGitLabのカンバン表示が書いてありますけど、カンバン表示は前からオープンソース版のGitLabにもあるんですよね😆」「そうですね😆

参考: GitLab、有償版の機能をオープンソースへ移植すると発表。カンバン表示やカナリーデプロイ、複数のKubernetesクラスタの使い分けなど18機能 - Publickey

「まあGitLabは有料版とオープンソース版で元々そんなに違いはありませんでしたし、逆にオープンソース版にあるけど有償版の下の方にはなかった機能もあったりしましたし、一般に思われているほど差はないかなと思いますね☺」「へぇ〜😳


GitLab記事を見ると、以下のそれぞれのissueを足して18個になるようです。

  • 「Plan」「Create」「Verify」「Package」「Release」「Configure」「Defend」がオープンソースに移行した
    • Plan: 共同作業を同期する
    • Create: よりよいコードやブランチをビルドする
    • Verify: コードの品質をさらなる高みに
    • Package: パッケージのビルドや共有
    • Release: 継続的デリバリー(CD)をシンプルに
    • Configure: 複数のKubernetesクラスタをサポート
    • Defend: アプリケーションセキュリティ


about.gitlab.comより

GitLab自社運用のための注意点とノウハウ(2018/06版)

⚓CSS/HTML/フロントエンド/テスト/デザイン

⚓Periodic Background Syncとは


つっつきボイス:「例のjxckさんの記事です」「PWA(Progressive Web Apps)に関連するブラウザとかWebの仕様の話のようで、Service WorkerのBackground Syncとは違うことをやりたいという感じ」

参考: PWAをもっと簡単に初めてみる - Qiita
参考: サービスワーカー API - Web API | MDN
参考: ServiceWorkerのBackground Syncでオンライン復帰時にデータ送信 - Qiita

「お〜、『Webをインストールする』という言い方をしてますね」「Webをインストール?」「おそらくですがローカルで動くものとWebで動くものを区別しないで実行できるというような意味合いだと思います: 以下にあるように、Webアプリは本来インストールなしで実行できるけど、特定の機能が必要なときはインストールしてパーミッションを与えないといけないという感じ」「おぉ〜」

本来 Web は、インストールのような操作なく、 URL にアクセスするだけでページが読み込まれ、スクリプトが実行される。
ここで強力な機能(Powerful Features)がユーザの同意なく実行されると、意図しない問題が発生する可能性がある。
そのため、ここまでの機能は、その API の重要性に応じて User Gesture, Feature Policy, Permission Prompt などを使い分けてきた。
一方、 Native App は、強力な OS 機能の利用を、ユーザによる明示的なインストールという操作(とそこで発生する認証や追加許諾)によって許可してきた。
この考え方に合わせれば、 「Web もユーザが明示的に Install すれば、 Native と同等の機能を許可できるのではないか」という発想に至る。
同記事より

「Webアプリもインストールという明示的な操作を経れば、ブラウザで冒頭のPeriodic Background Syncみたいな動作を許可してもいいのでは、ということですね」「おぉ〜」「Windowsにもアプリのインストールでroot権限取るときにダイアログが出てくる機能があったけど、あれ何て名前だっけ?🤔」「…Windowsでぽーんと出るのはUACですね」「それそれ😋、Webのインストールはそういうのに近い考え方なのかなと思って」「Webをインストールするという言い方は何となくいい感じな気がします👍」「ですね😋

参考: ユーザーアカウント制御(UAC) - Wikipedia

「ところで『Webをインストールする』って元記事の人独自の言葉なのかな?🤔」「引用されてるissue↓とかにもinstalled web appとかinstallableみたいな言い方があるから、そちらが元みたい」

「つい昔なつかしのActiveXみたいなものを想像してしまったんですが、たぶん違いますよね?😅」「Webをインストールするというのは、ブラウザの権限付与の文脈の中でインストールらしい動作を明示することで特定の操作の許可を得ることだと今のところ理解しているので、ActiveXでOSライブラリの実行を許可するのとは違うでしょうね🧐」「なるほど😅

参考: ActiveX - Wikipedia

「まあPeriodic Background Syncがどのぐらい大事なのかはわかりませんが😆、Webをインストールするという考え方はキャッチーだし、なかなかいいと思いました😋」「はい😋」「今のService Worker周りは正しく使われてない度が高い気はしますけど🤣」「通知許可したことないです🤣

⚓Webプッシュ通知よもやま話

「最近通知の許可を求めてくる新聞系のサイトが多くて嫌になるじゃないですか😆」「たしかに😆」「あれを自動でオフにするChrome拡張とかもあるぐらいですし、ユーザーが速攻不許可にする感じになってますよね😆」「😆」「なのでインストールという考え方に移行して、もっとtrustedなしくみを導入するのはありなのかなと思いました☺」「それはそれで悪用されたりして?😆」「まあ先のことはわかりませんが😆

「…Webブラウザは昔からブラウザを閉じればすべてが終わるという大前提でやっていますけど、Service Workerとプッシュ通知が入ってきたあたりから変わりつつありますね: でもそこで昔からやっている人たちの機嫌を損ねないように、ものすごく慎重にバランスを取りながらやってるという印象あります🧐」「それはある」「それに何がセキュリティホールなのかは最早感触でしかないという側面もあるので、仕様が不評を買わないための落とし所を慎重に探している感じですね☺

「あの手の通知許可ダイアログ、あちこちで出くわす😆」「しょっちゅう出てくるあのダイアログは単なる通知の許可だと思います」「あれはService Workerの許可とは別でしたっけ?」「ブラウザのプッシュ通知は専用のAPIがありますね🧐」「つまりService Workerは通知そのものとは別なのか」「オフライン対応で広く裏で使われているとは思います: オフライン対応するならService Workerは必須です☺」「なるほど」「たしかにそれがないと動かしようがないですし」

「実はあの通知がうざすぎるということでMozillaとGoogleがそれぞれ通知をおとなしくさせる予定になっていて、もうすぐアップデートが来るはずです」「それは大歓迎🎉」「プッシュ通知はフォーカスをダイアログに持っていかれるのが問題」「ほんとそれ!😤」「アンケート取ったらほとんどの人が読まずに通知のNoを押してることがわかったそうです🧐」「でしょうね🤣」「🤣

「今リンク貼りました↓🩹」「非常に不人気🤣」「やはり🤣」「実装した人たちは乱用されると思わなかったんだろうか🤣」「許可が1%程度で48%は明示的に拒否ということは、残り50%は単に離脱しているということでは🤣」「もう即閉じ🤣

参考: 非常に不人気な「Web Push通知」、Firefox 72以降ではポップアップを表示せずURLバーのアイコンで通知するのみに | スラド IT

「おとなしめの通知ではURLバーに通知が出るのか: それならまあいいかも☺」「Webをインストール、覚えとこう😋

⚓言語・設計・ツール

⚓セキュリティソフト入れますか?


つっつきボイス:「仕事のパソコンはセキュリティポリシー上の理由でウィルスチェッカー入れてるけど私物のパソコンはWindows Defenderで十分って徳丸先生が言ってますけど、そういうものなんでしょうか?」「あ、私もそうですよ: 私物PCはWindows Defenderだけです☺」「おぉ」

参考: Windows セキュリティによる保護 - Windows Help

「…セキュリティソフトなんてセキュリティホールを増やすためのソフトじゃないですか🧐」「なんと🤣」「🤣」「セキュリティソフトがウィルスソフトって言われるのは、あれ言い間違いじゃないと思ってますから🧐」「そこまで🤣」「🤣

「だいたいセキュリティソフトってセキュリティ上よくないことしかしないじゃないですか: 特権で勝手に動いてフルスキャンかけるし、SSLを通そうとして証明書のチェックをおざなりにしたりとか、そんなんばっかしです🧐」「お、おう😅」「まあそうですけど😆

「まあ自分たちが業務用PCにセキュリティソフト入れてるのも徳丸先生と同じで、取引上の理由しかありませんし😆」「はい☺」「セキュリティソフトに意味があるとすれば、うっかりミスを防ぐ効果でしょうね🧐」「あまり詳しくない人ほど効果が高い☺」「Webカメラ止めてくれたりするのは実際ありがたいですし😋

「でもセキュリティにこだわればこだわるほどセキュリティホールの比率は高まると思います😆」「セキュリティソフトがルート証明書入れてくる時点でどうかと思いますし🤣」「Windows Defenderで十分😋」「Macの場合どうしよう…😅


以下はつっつき後のツイートです。

⚓ソースコードブランチ管理のパターン


つっつきボイス:「Martin Fowlerさんの記事です」「この辺の図で何となくわかりそう😋


同記事より

「ポリシーとしては上図の左のようにブランチを管理したいけど、だいたい右みたいになるという😆」「右が現実😆」「左はGit-flow↓で言えば下の線がmaster、その上がdevelop、その上がfeatureブランチに相当するんですけど、縦の下矢印は必ずmasterに一方向に向かっているのがポイント🧐」「ふむふむ」「本来こうあるべきなんですが、現実には右の下みたいに別のブランチが生えてきて一方向にならなくなってしまう、ということが言いたい図だと思います☺」「なるほど!」

参考: git-flow cheatsheet

「どんなプロジェクトでもそうですけど、ブランチ管理はルールを制定することと、それを守らない人をリジェクトする強い心がないと徹底しないでしょうね🧐」「たしかに」

「以下の記事でもMartin Fowlerのfeature toggles(feature flag)を使ってやってるそうです」「feature flagも昔からあるやり方です😆」「ありゃ😅

参考: フィーチャートグル - Wikipedia

⚓Gitブランチ運用よもやま話

「ところで自分たちWebチームはGit-flow使ってますけど、アプリチームでは何を使ってます?」「…完全なGit-flowではないけどそれに近い使い方はよくやってます」「現行バージョンにでかい機能を入れるときは普通にfeatureブランチ切ります?」「メンテナンスブランチを伸ばしたくないので、master/develop/feature/hotfixで極力やりたいです☺

「クライアントごとにリポジトリを分けるとかはしませんか?」「極力やらないように努力してます😆」「納得😆」「やっちゃダメなヤツ😆」「そこが分岐してしまうと二度と元に戻れなくなると思うので、ソースは同じままでクライアントごとのソースを生成するビルドオプション作ってます」「なるほど」(以下延々)

⚓その他ツール


つっつきボイス:「TweetDeck風にGitHubリポジトリをブラウズできるツールだそうです」「リポジトリのコミットを表示してるみたい」「そこまで頻繁にコミットされるんでしょうか😅」「オープンソースのコミッターなら使うかな?」

「22ドルだそうです😆」「高っ😆」「オープンソースでこれがないと困る人はむしろ金もらえる立場だったりして😆

⚓その他

⚓古書通販


つっつきボイス:「古書店の人が本棚を動画撮影して、欲しい本を知らせてもらうという企画が評判よさそうなので」「たしかに書籍データを打ち込む手間を考えたら動画の方がラクですね😋」「カメラ移動をゆっくりで願いますという注文もありました😆

「古書の背表紙だと第何版なのかまではわからないか🤔」「あ、たしかに」「初版じゃないとイヤとか😆」「背取らーとかなら版数も気にするでしょうし😆」「うちのK&R本は第200うん十刷までいってたかな😆」「そんなに😆」「今なら300ぐらいまでいってたりして😆

参考: プログラミング言語C - Wikipedia

著者名の頭文字からしばしばK&Rと略される。
ja.wikipedia.orgより

⚓番外

⚓元素周期律表がリニューアル

以前からいろんな周期律表が考案されているようです。


後編は以上です。皆さまよい祝日を。

バックナンバー(2020年度第2四半期)

週刊Railsウォッチ(20200421後編)Ruby 2.4サポート終了、Ruby 3の右代入演算子、GitHubコア機能無料化ほか

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

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

Ruby Weekly

Hacklines

Hacklines

Publickey

publickey_banner_captured

Postgres Weekly

postgres_weekly_banner

リモートワークをするためにやったこと

$
0
0

緊急事態宣言が出てしばらく経ちますが、皆様いかがお過ごしでしょうか。

僕は会社で仕事するのが好きで自宅作業はあまり得意ではないのですが、そうも言っていられないのでここしばらくはフルリモートです。少しでも仕事を進めるために、ここ最近で行った対策を記録していこうと思います。

ノートPCを買った

まずやったことは、サブとして使っているノートPCの新調です。当初部分リモートしながらビデオ会議も増えていたため、ノートPCは重要です。1年ほどSpectre x360 13を使っていたのですが、これをXPS 13 (9300)に買い替えました。

CPUが8565Uから1065G7になってメモリもDDR4 2400MHzからLPDDR4X 3733MHzに上がったものの、4Core CPU / 16GB RAM / 1TB SSD / UHDディスプレイという構成は同じため、あまり代わり映えしません。ただ、以下の理由で買い替えが必要と判断しました。

  • 色が微妙だった(アッシュブラックは好きじゃない)。フロストのXPSが格好良かった。
  • キーボード配列とタッチパッドが辛かった。
  • ビデオ会議で8565Uは性能不足で辛く、GPU性能が上がった1065G7なら少しはマシかと思った。

発売とほぼ同時に注文したXPS 13 (9300)で、今となっては32GBモデルも発売していますが、まだ黒しか無いようなので後悔はありません。

効果

  • 4K外部ディスプレイ出力しながらビデオ会議しても、プチフリがなくなり、だいぶ快適になりました。
  • フロストのカバーと白のパームレストが最高に綺麗で精神衛生上好ましいです。ただ、キーボードの白はなんというか普通でした。

デスクトップPCを買った

これまで自宅ではあまり仕事をせずPCパーツの価格を眺めるくらいしかしていなかったため、Sandy BridgeにL997で我慢していました。しかし、リモートワーク用のメインマシンとなると、そんな骨董品で仕事をするのは職務怠慢です。

会社ではCore i9-9900K、32GB RAM、1TB SSDのLinuxマシンをメインに使っていましたが、なるべく近い性能は欲しいものです。

自宅であまりうるさいマシンは嫌だったため、TDP65WということでRyzen 7 3700Xにしました。メモリは足りない気もしましたがとりあえず最低限で32GB、SSDは1TBの安いものを入れておきました。グラボは映ればいいので適当にAMDつながりでRX550です。

効果

  • 現実的なスペックのPCとして使えるようになりました。
  • シングルコア性能は若干物足りない気がしなくもないですが、コンパイル速度を除いて日常用途には快適です。
  • FluidMotionを試してみたかったのですが良い映像ソースを持っていないことに後で気づきました。

ディスプレイを買った

国産最後の最高峰ディスプレイL997は目に優しくて気に入っていましたが、流石に古くて狭すぎます。

会社ではEIZO EV3237 + L887を使っていますが、実家の四畳半の部屋に同じだけ置くのは少々窮屈です。それでもせめて4K1枚くらいは欲しいところ。本当はDELL U3219Qが欲しかったのですが、お金がないので価格重視でLG 32UL500 (32inch、4K、VA) を購入しました。たまたま余っていたエルゴトロンのアームがあったのでスタンドがしょぼいぶん安いこのモデルはお買い得です。

効果

  • VAは視野角が狭くてだいぶ気になりますが、端に単色を置かないなどウィンドウの配置を調整して強引に慣れました。
  • 多少目は疲れますが、VSCode等の黒画面ベースなら1日耐えられないほどではないです。

CPUファンとケースファンとCPUグリスを買った

自宅作業では空調の音が鳴らないため、騒音が集中力を妨げます。当初の構成ではCPU付属ファンを使っており、負荷をかけるとうるさくなって気が散っていました。

そこで、コスパと評判が良いScythe 虎徹 Mark IIと、性能が良さそうなNoctua NF-A12x25を2つ購入しました。また、当初どうも冷え方が物足りなかったので、後日追加でナノダイヤモンドグリスを買い足しました。

更に、普段止まっているグラボのファンが回ると非常にうるさいため、補助用に9cmファンを追加しました。

効果

  • 負荷をかけてもファンが静かなままになりました。
  • グラボの冷却効果は期待できませんでした。

オーディオインターフェースとマイクを買った

ビデオ会議では音質が命です。PC内蔵マイクでは、満足な会議は実現できません。

ヘッドホンの側圧が苦手で買っては誰かにあげている人なので、できればヘッドセットではなくスピーカーとマイクで会議をしたいところ。とはいえ、聞く方のオーディオについては多少わかるものの、マイクについては完全なる素人(※)で何もわかりませんでした。幸い社内や身近なところに音響設備やDTMのガチ勢が数名いたため、助言をいただきながら初心者向けの機材を購入しました。

※どのくらい素人かというと、XLR端子を差したことがなく、ミキサーといえば調理家電を思い浮かべ、オーディオIFの裏にある端子がRCAと勘違いしていてプリメインアンプにつながらないと慌てて変換ケーブルを買い足すくらいです。

専門家の助言をもらいながらミキサーやエフェクタを調べていき、素人が最高を目指して色々なエフェクタをかけまくったのですが、

結果事態が悪化したので、最低限のイコライザとコンプレッサーだけかけて運用するようにしました。

効果

  • Webカメラ内蔵マイクよりは良い音になった気がします。
  • 自分の声を録音して聞く苦行で心が削れました。
  • Voicemeeter Bananaの使い方を理解しました。
  • 手元にボリュームがあるので、BGMの音量調整がしやすくなりました(プリメインアンプのリモコンはすぐ電池が切れて使いづらい)。
  • DACとしては、96kHzで固定すると44.1kHzが再生できないためfoobar2000側でリサンプリングが必要なあたり、再生専用DACに劣る面もありました。

その後プロの声優の方の記事を見て、やはりプロは違うなと思いました。この記事が楽しく読める程度の知識がついたのもひとえにu-ichiさんのおかげです。この場を借りてお礼いたします。

スマホ固定用ブラケットを買った

ビデオ会議では画質も大切です。社会人として、髭を剃る・パジャマで参加しない・画質を確保するというのは最低限のマナーですよね。

知り合い経由で見ていると、皆さん一眼レフからHDMI入力してまともな環境を構築しているようです。それに倣おうとしたのですが、残念ながら手元にある一眼レフのAC電源アダプタがなく、バッテリだと連続使用時間が心もとないので諦めました(あとデスクの位置的に狭かった)。ということで、スマホのカメラもそこそこ高画質だろうとPC Watchさんの記事も見ながら、IVCamを入れました。

良い感じの位置に配置する方法を考えていたところ、車載アダプタがぴったりなことに気づいたので購入しました。

効果

  • ディスプレイの裏面に固定する方法では角度が取れませんでしたが、壁に固定すると完璧な位置に調整できました。
  • 設置が面倒な割に母親からもらったWebカメラと画質が大差ない気がしてきたので、お蔵入りしました。

スマホを買った

これまで使っていたZenFone 5は、一度画面を割って自分で交換したのですが、それ以来少しタッチパネルの動作が不安定です。これではいざというときに重要な連絡に支障を来す可能性があるため、買い替えました。

DSDV対応、イヤホンジャック搭載、安いもの、変なカスタムOSはパス、ということで中華を除外した結果、moto g8 plusを購入しました。

効果

  • 安定した操作環境が得られました。
  • ほぼ素のAndroidなので、ZenFoneから違和感なく移行できました。

SSDを買い足した

会社ではLinuxマシンでしたが、家ではWindowsマシンで、Linux VMを使っています。VMを使うとSSDがすぐに消費されていきます。SSDの残りが少ないと寿命を縮めている気がして落ち着きません。ということで2枚目のSSDを追加購入しました。

効果

  • 空きが5割を超えて、心が落ち着きました。
  • VMデータを移動するときにHyper-Vの操作を間違えて、OSインストールからやり直す羽目になりました。
  • 2つ目のM.2スロットとSATAが排他なことに気づいて、SATAのSSDが1つ認識しなくなりました。

椅子を買った

10年前に中古でもらって長らく使っていたよくわからない椅子はつかれるので、コンテッサセコンダを買いました。

会社エルゴヒューマンと同じではつまらないですし、アーロンチェアは家族が持っている(のとマンガ読むのにリクライニング固定は欲しかった)のでやめて、あとはブランドとデザインの憧れから選定。残念ながら既に緊急事態宣言で家具屋に見に行けなかったので、試せないのが怖いですがエイヤでポチりました。

効果

  • 多分腰に優しくなった気がします。
  • 既に滅びた肉体を復活させる効果があるわけではないので、運動不足の対策にはなりませんでした。
  • 同時に買ったチェアマットは少し小さいですがずれずに快適です。

スイッチングHUBを再起動した

リモートワークで回線速度は重要です。実家なのであまり勝手なことはできませんが、ひとまずフレッツ光ネクストのまま(auやNURO、クロスにするなどはなしで)追加コストのかからないところでIPoE接続にしています。なのにいつも95Mbpsしか出ないなと思っていたのですが、なぜかルーター/スイッチ間が100BASE-TXでリンクしていたため再起動したら無事1000BASE-Tになりました。

効果

  • 時間帯によりますが頻繁に300Mbps以上、最低でも60Mbps以上は出るようになりました。
  • 数十GB単位のリポジトリのcloneが早くなりました。

これらの対策をした成果

仕事と関係ない作業をする時間が増えました。ちゃんと仕事しようと思います。

今後検討している対策

  • キーボード(RealForce 91UBK)がうるさくてWeb会議相手に迷惑がかかるので、静音モデルが欲しい。
    • R2はレイアウトがだめなので91UG-Sが欲しいのですが売っていないorz
  • キーボードノイズ対策でRTX Voideを試すためGeForce RTXが欲しい。
    • Krispは試したのですが、たしかにノイズは完璧に消えるものの加工感が強すぎてだめでした。
  • ディスプレイを増強したい。
    • 他の人の作業環境を見ていると、やはりシングルディスプレイはケチりすぎたかもしれません。スピーカーを少しずらせば縦に1枚くらい増やせそう…
    • 特にビデオ会議中はもう1枚欲しくなります。
  • 10GbE環境を構築したい。
    • やはりマナーとしてやっておきたい。ただMicorATXのマザーなのでPCIeが空いていなくて実現は難しそうです。

Excelで改ページを挿入できない場合の対応方法(更新版)

$
0
0

Excelで改ページプレビューはとても便利なので、よく使います。

Excel 改ページ 例

改ページの例

こんなやつですね。

しかし、このプレビュー画面で「改ページの挿入」を行っても、プレビュー画面に上のような改ページの線が表示されないことがあります。
こんな時は、Excelの[ページ設定]が[次のページに合わせて印刷]になっていないかどうかを確認してみましょう。

確認と変更の手順

確認と変更の手順は次の通りです。

  • 1. [ファイル]、[印刷プレビュー] の順番にクリックし、[ページ設定] を開きます。
  • 2. [ページ設定] の [ページタブ] 内の「拡大縮小印刷」の設定で「次のページに合わせて印刷」が選択されていないかどうかを確認します。この設定では改ページを挿入できません。

Excel 改ページ 例

ページ設定画面
次のページに合わせて印刷」を選択してはダメ

  • 3. 改ページプレビューを使いたい時は、以下のように「拡大縮小印刷」の「拡大/縮小」を選択します。

Excel 改ページ 例

ページ設定画面で「拡大/縮小」を選択すること

「次のページに合わせて印刷」を選択していると、改ページ挿入はできないようです。
わかりづらいですね・・・

参考: 本記事の更新版について

2010/05/14に公開された元の記事では当時最新だったExcel 2010を確認に用いましたが、2020/10/16の更新版記事では改めて以下のバージョンで動作が現在も同じであることを確認しました。

  • 確認に使ったExcelのバージョン: Office 365(64ビット版)
  • 確認に使ったWindowsのバージョン: Windows 10

少なくとも2010年以降のExcelであれば本記事の手順は有効です(実際はほとんどのバージョンで有効と思われます)。

Google Wokspace: 社内ファイルサーバから共有ドライブ移行時の注意点と対策

$
0
0

⚓ 更新情報

⚓ 追記(2020/10/29)

参考までに、本記事を公開後の2020年8月に以下の「ビジター共有」機能が使えるようになりました。Googleアカウントを持たない社外のユーザーとも共有できるようになります。

⚓ はじめに

これまで独自で運用していたメールサーバ、ファイルサーバや個人Googleアカウントで利用していたGoogleサービスを、G Suiteに移行しました。色々と想定外の苦労もあったので、これまでに得られた知見を何回かに分けて記録していこうと思います。

今回は、SambaファイルサーバをGoogleドライブ(チームドライブ) へ移行する話です。40人程度の小規模な会社なので、まさかパフォーマンスや容量の問題は無いだろうとたかをくくっていたら意外と苦労しました。

⚓ 結論

  • チームドライブや「ドライブ ファイル ストリーム」アプリの登場により、以前よりもGoogle Driveを共有フォルダとして使いやすくなっている。
  • 実際に使うと、機能制限共有設定速度の面でそれぞれ大きな落とし穴がある。
  • Samba共有の感覚で運用を変えずそのまま移行するのはほぼ無理だが、わかった上で運用を工夫すれば代替は可能。

⚓ G SuiteとGoogleドライブの紹介

最初に、G SuiteやGoogleドライブの機能概要のおさらいです。

⚓ 個人GoogleアカウントとG Suiteアカウント

通常Googleアカウントを新規登録(無料)すると作成されるものを便宜上「個人Googleアカウント」と呼ぶことにします(正式な呼び方は知りません)。一般的にはGmailアドレスとともに発行しますが、すでに所有しているメールアドレスで発行することもできます。

一方、組織のG Suiteで発行したアカウントは、組織のドメインのメールアドレスと関連付けられます。このアカウントは組織によって管理され、アカウントの停止やパスワード再発行は組織管理者が行うことになります。

⚓ マイドライブとチームドライブの違い

Googleドライブには、「マイドライブ」と「チームドライブ」の大きく分けて2つがあります。


⚓ 1. マイドライブ

個人のGmailアカウントでも使える、一般的なGoogleドライブとして認識されているものです。以下の特徴があります。

  • ファイル作成者がオーナーになる。
  • オーナーのドライブ容量を消費する。共有された他ユーザのドライブ容量は消費しない。
  • フォルダ単位での共有もできる。
  • オーナーは、共有中の別ユーザにオーナーを移転できる。
  • オーナーのGoogleアカウントを削除すると、ファイルは消滅する。

元々はこのマイドライブしかなかったのですが、組織で使う際に退職者アカウントを削除するとファイルが消える、またオーナー自身しかオーナーを移転できないため、連絡が取れないユーザのオーナー権を移転できないと言った問題がありました。

これに対応するため追加されたのがチームドライブです。


⚓ 2. チームドライブ

G SuiteのBusinessプラン以上(Basic不可)でのみ使える機能です。以下の特徴があります。

  • チームドライブそのものがオーナーになる(作成者を問わない)。
  • 組織全体のドライブ容量を消費し、各ユーザのドライブ容量は消費しない。
    • ただしチームドライブを使えるのはBusinessプラン以上(5名以上で容量無制限)なので、コスト面への影響は少ない。
  • チームドライブ単位やファイル単位の共有はできるが、フォルダ単位の共有はできない
  • 作成はG Suite Businessプラン以上が必須だが、個人Googleアカウントに共有することは可能。

チームドライブ内に作ったファイルはすべてチームドライブがオーナーとなるため、ファイル作成者のGoogleアカウントを削除してもファイルが消えないのが特徴です。

⚓ Web UIとクライアントアプリ

GoogleドライブというとWeb(やスマートフォンアプリ)でアクセスするイメージが定着していると思いますが、PC用アプリも用意されています↓。


⚓ 1.「バックアップと同期」


google.comより

昔からある「Googleドライブ」アプリの後継です。

  • PCにファイルを同期する、昔ながらのDropbox的なアプリ。
  • 個人GoogleアカウントでもG Suiteアカウントでも使える。
  • マイドライブにのみアクセス可能(チームドライブへはアクセス不可能)。
  • マイドライブ全体、または選択したフォルダのみを同期できる。
    • マイドライブに大量のデータを置いておりPCに入りきらない場合、よく使うフォルダだけをノートPCに同期するなど。
  • PC内のどこに同期するかを指定できる。たとえば D:\Google\MyDrive のように任意のパスを指定できる。
  • PCのデスクトップ等、指定したフォルダをバックアップできる。

⚓ 2.「ドライブ ファイル ストリーム」


google.comより

G Suiteアカウントでのみ使えるアプリです。

  • ファイルを開くときにオンデマンドでダウンロードするので、SSDの小さいPCでも全データを同期できる。
    • Dropbox Businessのスマートシンクなどと同様。
  • 指定フォルダのみオフライン用に同期することも可能。
  • マイドライブおよびチームドライブにアクセス可能。
  • 常に専用のドライブレター(デフォルトは G: )が割り当てられる。任意のフォルダへのマウントはできない。
  • 個人Googleアカウントでは一切利用できない

⚓ 注意点 機能編

ようやく本題です。実際に運用してみると、いくつかはまりどころがありました。

⚓ 1. チームドライブのフォルダ共有ができない

チームドライブは、全体を共有するか内部のファイルを1個ずつ共有することしかできません。例えば「第1開発部」というチームドライブを作り、その中の「プロジェクトA」フォルダのみをアルバイトに共有する、といったことは不可能です。

公式のガイドラインにもある通り、ドライブ内の全ファイルが同じメンバーからアクセスできるべき、という単位でチームドライブを作る必要があります。この場合でいえば、「第1開発部」というチームドライブは不適切で「プロジェクトA」というチームドライブを作るべきでした。

⚓ 2. 個人GoogleアカウントでPCアプリを使いたい場合、チームドライブは利用不可

アルバイトや外部の協力会社に対して、G Suiteアカウントは手間やコストの関係で発行せず、個人Googleアカウント(gmail.com)で共有することもあると思います。この場合、そのアカウントではドライブ ファイル ストリームが使えず、バックアップと同期のみが利用可能です。つまり、デスクトップアプリからチームドライブにアクセスすることはできません。もしPCに同期しないと使いづらい運用をする場合(作業フォルダなど)、チームドライブを使わないという選択肢を検討する必要があるかもしれません。

⚓ 3. チームドライブの共有権限がへんてこ

チームドライブでは「すべて」「編集可能」「コメント可能」「閲覧のみ」の権限設定がありますが、なぜか「編集可能」ではファイルの削除や移動ができません

参考: Get started with Team Drives – Google Learning Center

さらに、ドライブ ファイル ストリーム経由でアクセスしている場合、「編集可能」権限では閲覧しかできません。つまり、共有フォルダ代わりに使うには、全員に「すべて」権限を与えるしかないということになります。

これに関しては不満も多いようで、最近「すべて」権限でもユーザを追加できないようにする管理者設定が追加されました。どうしてこうなった…

これをONにしてしまうと本当に全メンバーの管理を管理者が行わなくてはならなくて非効率です。通常のマイドライブと同じように編集可能権限が編集できればそれで良かったのです。機能リクエストは上がっているのでそのうちに実装されるかもしれません。

⚓ 4. チームドライブへの保存ファイル数に制限がある

「容量無制限」を売りにしているG Suite Businessプラン以上ですが、実際にはチームドライブへの保存ファイル数に大きな制約があります。

G Suite管理者ヘルプの「チームドライブの制限」に記載がありますが、1ドライブにつき40万アイテムフォルダ20階層までしか作れません。

先月まで25万アイテムだったのですが、しれっと増えていました。なおこのページは簡単にはたどり着けないうえに、増えたというアナウンスも公式ブログですら行われない秘密主義のようです。

想像以上に少ないですよね。手元でSamba共有フォルダを数えたら2000万ファイルほどあったので、分割は必須でした。「チームドライブ」という名称ですが、事実上はより細かい「プロジェクト」などの単位で分割するのが現実的でしょう。1つだけ、1プロジェクトで200万ファイルを超えるフォルダがあったので、諦めてprojectA-2018のような名付けをしました。

ちなみに、チームドライブ内に今何個のファイルがあるかを数える簡単な手段は存在しません。ひどい。ドライブ ファイル ストリームで右クリック→プロパティを出して、表示が落ち着くまでコーヒーを淹れてくるしかないようです。

※チームドライブ数には上限がないそうです。ただし、現時点ではチームドライブ一覧はフラットに表示されるのみで、分類やスター付けもできないので、数千数万といった単位で作ると使いづらいでしょう。数百~1000位にしておいた方が良いと思います。ということは、チームドライブで数億以上のファイルを扱うのは辛みがある、ということになります。

なお、試したことはありませんがサポートによるとマイドライブにはこの制限がないとのことです。ただし、だからといってマイドライブをファイル数制限のないチームドライブ代わりに使うことは絶対に避けたほうが良いです。本記事の後半に記載の通り、致命的なパフォーマンス上の問題が発生します。

⚓ 5. ドメインが違うユーザへオーナー権の移行はできない

Googleドライブファイルのオーナー権移行は、同一ドメイン内のユーザにのみ可能です。@gmail.com から @gmail.com への移行は可能ですが、 @gmail.com から @bpsinc.jp やその逆はできません。

つまり、これまで @gmail.com で運用していたGoogleドライブを組織のG Suiteに移行するのは困難が伴います。基本的な対応は2つあります。

⚓ 1. コピーする

URLやIDが変わって良ければ、コピーするのが一番お手軽です。これは、移行先(G Suite)アカウントを旧ドライブに招待し、移行先アカウントで「コピー」するだけです。これで移行先アカウントのマイドライブにコピーされるので、あとはドメイン内で好きに移動すればOKです。

⚓ 2. チームドライブを経由して移動する

一手間かけることで、IDを維持したままファイルを個人アカウントDriveからG Suiteアカウントに「移動」できます。

  1. まず、移行元(個人Googleアカウント)であるオーナーをチームドライブに招待します。
  2. オーナー側でファイルをチームドライブに「移動」します。これでオーナー権限がチームドライブに移行されます。

この操作のみ、ドメインをまたいでオーナーを移動できないというルールの例外のようです。

一度移動したら、すでにそのファイルはG Suiteドメインの所有なので、G Suiteドメイン内で自由にオーナー移行(チームドライブからG Suite内のマイドライブに移動するなど)が可能です。

なお、この操作は不可逆です。一度移動したら、二度とそのファイルを元のオーナーに戻すことはできません(組織ドメイン→gmail.comへの移動ができないため)。

⚓ 6. マイドライブの一括オーナー移行はできない

マイドライブのフォルダを、すべて別オーナーに移行することはできません。フォルダのオーナーを変更はできますが、この操作はあくまでそのフォルダのオーナーを変更するのみで、内部のフォルダやファイルのオーナーは変更されません。

※G Suite管理下のアカウントでのみ、ユーザAの所有する全ファイル・フォルダをユーザBにオーナー移行するといったことは管理コンソールから可能(ユーザAが退職する前にやるべき処理)ですが、後述の通りこの操作はものすごく遅いです。

なおG Suiteドメイン内では、以下の操作は可能です。ただし、同じくとても遅いです。

  • G Suiteアカウントのマイドライブから、チームドライブにフォルダごと移動することはできる。
  • チームドライブから、G Suiteアカウントのマイドライブにフォルダごと移動することはできる。

それ以外では、フォルダをまるごとチームドライブに移動したり、別ユーザにオーナー移行する機能はありません。つまり、これまで個人Googleアカウントで使っていたフォルダをまとめて移行するには、APIやサードパーティー製のツールを使うなど工夫する必要があります。サポートに聞くと「一度ダウンロードして再アップしろ」と案内されますが、当然URLは変わりますし、DocsやSpreadsheetなどがいったんOffice形式に変換されるため、データが一部変わってしまいます。

かといってG Suite Marketplaceに置いてあるアプリは人気のものでも驚くほど出来が悪い/セキュリティを考慮していないものも多いので、あまり頼りたくない…

⚓ 注意点 パフォーマンス編

⚓ 1. たくさんのファイルを扱うとドライブ ファイル ストリームが重くなる

多数のファイルをアップロード/ダウンロードすると、ドライブ ファイル ストリームがものすごくCPUを食うようになります。このような場合は、 %LOCALAPPDATA%\Google\DriveFS 配下にある metadata_sqlite_db が肥大化していないか確認しましょう。手元の環境では、2GBを超えたあたりからだんだん重くなりました(6GBを超えても重いだけで動作はしました)。

この場合、一旦ログアウトしてログインし直すとファイルが消えて元の速度に戻ります。たまにやると良いかもしれません。

⚓ 2. ファイル数が多いとアップロードがとても遅い

よく見かける「クラウドストレージの速度比較」のようなサイトで、Googleドライブは上位に位置することが多いようです。実際アップロード・ダウンロードの速度は高速で、100Mbps以上出ることも珍しくなく、時間帯を問わず50Mbps以上で安定している印象です(正確に測定はしていません)。

ただしこれは、1つの大きなファイルの場合の話で、ファイル数が多いと事情が異なります。Googleドライブは一度に10個程度しかアップロードできず、また1ファイルアップロードするごとに5~10秒ほどのインターバルがあるため、1KBのファイルを1000個アップロードするのは1MBのファイルを1個アップロードするのに比べて圧倒的に長い時間がかかります。(手元で大小入り乱れた100万ファイル程度のフォルダをアップロードするのに数週間かかりました)。

Sambaファイルサーバには、例えばリポジトリを展開して .svn.git が残っているケースもあるでしょう。また、 .DS_StoreThumbs.db が混じっていたり、そこまでいかなくても多数のテキストファイルやアイコン画像があることは珍しくありません。Googleドライブにこれらのファイルをアップロードするのは大変なので、古いプロジェクトについては適当にzipやtar.gzに固めるのが良いかもしれません。

※zipだとWeb UI上でダウンロードせずにプレビューできるので便利です。

これだけで、GoogleドライブはSambaファイルサーバの直接の代替にはならないことがよくわかります。

⚓ 3. ドライブ ファイル ストリームのアップロードが遅い

特に帯域制限をかけていないのに、1つの大きなファイルでもアップロード速度が30Mbps程度で頭打ちになることがあります。この場合、以下を参考に BandwidthTxKBPS を明示的に大きな数字にすると、速くなることがありました。

参考: G Suite管理者ヘルプ ドライブ ファイル ストリームを構成する

⚓ 4. マイドライブの共有設定は反映がとても遅い

マイドライブのフォルダにたくさんのファイルがある場合、そのフォルダを別ユーザに共有すると、反映にとても長い時間がかかります。手元で200万ファイル程度が入ったフォルダを5ユーザに共有したところ、反映完了まで1ヶ月半ほどかかりました

具体的には、マイドライブでフォルダを共有した場合、内部の各ファイルについて「共有権限設定: ユーザAを編集可能に」といった単位で設定がされていくので、200万ファイルのフォルダを5ユーザに共有すると内部的には1000万回の操作が発生するイメージになります。この操作はどうやらアイドル時に少しずつ行われるようで、ドメイン内でGoogleドライブをあまり使っていない時は1分間に200オペレーションほど進む(想像の30倍は遅い)のですが、ドメイン内でアクティブにDriveが使われている時間帯は一切進まないといった感じになります。

さらに悪いことに、この操作は中断することはできず、しかも進捗率を知ることもできません(管理コンソールの監査ログで最近どのような操作がされたかはわかるものの、全体件数や順番がわからないので、忙しいのか暇なのか位しかわからない)。つまり、うっかり大量ファイルの入ったマイドライブフォルダを共有かけてしまうと、ドメイン全体でドライブが遅くて不安定になったままいつ直るかもわからず我慢し続けるしかないことになります。

↓ドメイン全体の共有進捗状況です。速度の傾向が見えてきます。

幸い上記の200万ファイル共有は本格移行前に始めたので、まだ影響は小さかった方ですが、それでも完了までは以下のような不具合が頻発しました。

  • 新規でチームドライブを作ってしばらくの間(10日間程度)、チームドライブの名前が反映されず「チームドライブ」という名前になる(下の添付画像)。
  • チームドライブをメンバーに共有してからそのメンバーに見えるまで、3日ほどかかる。
  • マイドライブにアップロードしたファイルが消えたように見え、3日ほどたつと突然出てくる。その間に消えたと思って再度アップしたファイルも別途残る(バージョン管理されない)ため、大量のファイルが2個ずつ残る。
  • チームドライブを空にしたのに消せない状況が1ヶ月以上続く。

ものすごく不具合としか思えないですが仕様とのことです。大量のファイルが入ったフォルダを共有してはいけません!ゼッタイ!

⚓ 対策

上記の通り、マイドライブでたくさんのファイルを共有すると、致命的な問題が多発します。緩和策として、以下のような運用上の工夫が可能です。

  • チームドライブを使う: チームドライブでの共有設定はトップレベルにのみ行われるので、ファイルが多数あっても反映が早い。
  • グループを使う: ユーザ5人に共有するのではなく、ユーザ5人が入ったGoogleグループを作り、そのグループに共有する。

なお、AODocsさんのサイトにより詳しいナレッジが載っていました↓。hachi8833に翻訳してもらったので、こちらも併せてご覧いただくと役立つかと思います。

Googleドライブのパフォーマンスを低下させない推奨設定 — AODocs KB(翻訳)

⚓ まとめ

容量無制限に惹かれて導入したG SuiteのGoogleドライブですが、想像以上にパフォーマンス上の問題が大きかったです。試行錯誤の末、当面は以下の運用ルールにしてみました。

  • マイドライブは少数のファイルや少数の共有メンバーの場合、また一時的な用途を中心に利用する。正式なプロジェクトでは原則チームドライブを利用する。
  • プロジェクト単位でチームドライブを作成する。この際 project-XXX というようにprefixをつけることでソートしやすくする。
  • 雑多なものを入れる用途に project-その他team-XXX のチームドライブも作る。
  • プロジェクト以外の共有用に BPS-勉強会 などのチームドライブも作る。
  • チームドライブの内のファイル数が100~10万程度になるくらいの粒度を心がける。チームドライブ数は数年で数百程度になるのを想定する。
  • ZIPを展開したファイル群やリポジトリなどをドライブに置くことはなるべく避ける。

関連記事

Google Driveのファイル共有状況を一括出力するgoogle-drive-permission-searchを作った

Googleスプレッドシートのセル内に画像をぴったり埋め込み表示する方法

Railsのルーティングを極める(前編)

$
0
0

更新情報

  • 2014/03/03: 最初の版を公開
  • 2020/11/18: Rails 5と6の情報を反映

Railsのルーティングを極める(後編)もご覧ください。


こんにちは、hachi8833です。今回も弊社CTOの馬場さんによる勉強会のスライドを元に記事を書きました。発表当時はRails3だったので、Rails4情報も追加しました。

⚓ Railsのルーティング(routes)を極めよう

2012/03: baba

Railsのルーティングはきわめて自由度が高い分、気を付けないとすぐカオスになってしまいます。Railsのルーティングのコツについて勉強していきましょう。Railsのルーティングはconfig/routes.rbで設定します。

⚓ まずはrails routes

追記(2020/11/18):以前はrake routesコマンドでしたが、Rails 5や6ではrails routesも使えるようになりましたので原則こちらで表記します(rake routesも引き続き使えます)。

ルーティングを書く際にはrails routesなどでルーティングを確認しながら書く癖をつけましょう。ルーティングを作成するだけでなく、Railsのデバッグ時にも有用で、迷ったらとにかくrails routesを実行してもいいくらいです。

rails routesは実行のたびにRails環境を読み込むので、そのままでは遅いので有名です。以下のような方法で高速化しましょう。

  • dev環境でRailsを起動中ならhttp://localhost:3000/rails/infoを参照する (Rails 4以降)
  • pryとpry-rails gemがインストールされているなら、rails consoleを起動しておいてshow-routesを実行
  • spring gemがインストールされているならspring rails routesとすれば2度目以降から高速になる

参考までに、週刊Railsウォッチ20201012ではRailsのルーティング情報を動的にビジュアル表示する方法が紹介されています(graphvizをインストールする必要があります)。

⚓ 改めて、RESTとは

RESTとは、Railsに敷かれているレールの一つである概念で、REpresentational State Transferの頭字語です。RESTの特徴は以下のとおりです。

  • ステートレスであること
  • すべてを「リソース」で表す
  • リソースは名前を持つ

RESTに従っていることをRESTfulと呼んだりします。

⚓ RESTfulなURLの例

  • http://example.com/prefectures
  • http://example.com/users/1

⚓ RESTの反対はRPC

RESTのちょうど反対の概念がRPC(Remote Procedure Call)です。これはクエリ形式などと呼ばれることもあることからわかるように、?の後ろに問い合わせを&でつないで表現します。

⚓ RPC URLの例

  • http://example.com/index.php?action=prefecture_list&amp;id=1
  • http://example.com/PrefectureList.aspx?id=1

Railsではresourcesでルーティングを記述することで自動的にRESTfulなルーティングが生成されます。
以下はusersコントローラとproductsコントローラへのルーティングです。

resources :users
resources :products

その場合、対応するコントローラにもRESTfulなアクションが揃っている必要があります。rails generate scaffoldで生成した場合は自動的にRESTfulになります。

Railsでは、REST形式もRPC形式も両方扱うことができますが、RailsはRESTを「レール」として定めていますので、基本的には統一のためにRESTfulなルーティングの作成を心がけるようにしましょう。

ただし、RESTはあくまでポリシーであり万能ではないので、時にはRPC形式を一部に導入する方が素直に作れることもあります。

⚓ RESTメソッド

RESTとHTTPメソッドにはそれぞれ以下のような関係があります。なお、Rails 4 からはPUT非推奨となり、PATCHが推奨されています。

⚓ RESTメソッド一覧

メソッド 安全 冪等
GET
POST × ×
PATCH/PUT ×
DELETE ×
  • 安全が×になっているのは、危険という意味ではなく、実行すると元のデータが更新されるという意味です。
  • 同様に、安全が◯になっているのは、実行によって更新される心配がないという意味です。
  • 冪等(べきとう: idempotent)は近年よく使われる用語で、「1回実行しても2回以上実行しても結果が変わらない」ことを指します(例: 1人殺しても3人殺しても死刑、は冪等です。1人殺せば犯罪者、1000人殺せば英雄、1億人殺せば神、だと冪等ではありません)。chefやvagrantなどのサーバーデプロイ用DSLではその目的のため冪等性が重視されます。

⚓ RESTfulなメソッドとURLの例

以下の表では、BPSという会社の所在地をGETメソッドとRESTfulなURLで表現した場合の例を示しています。

概念 RESTfulなメソッドとURL
都道府県 GET /prefectures
東京都 GET /prefectures/tokyo
東京都市区町村一覧 GET /prefectures/tokyo/cities
東京都新宿区 GET /prefectures/tokyo/cities/shinjuku
東京都新宿区会社一覧 GET /prefectures/tokyo/cities/shinjuku/companies
東京都新宿区にあるBPSという会社 GET /prefectures/tokyo/cities/shinjuku/companies/bps
BPSという会社 GET /companies/bps

以下の表は、記事・ユーザ・コメントを表現した場合の例です。特に、IDが複数ある場合の表現方法にご注目ください。

概念 RESTfulなメソッドとURL
記事一覧 GET /articles
記事(ID=1)、コメント一覧 GET /articles/1/comments
記事(ID=1)、コメント(ID=1) GET /articles/1/comments/1
ユーザ(ID=1) GET /users/1
ユーザ(ID=1)、パスワード GET /users/1/password

以下の表は、記事・ユーザ・コメントに対して操作を行なう場合の例です。Rails 4以降ではPUT非推奨になり、PATCHが推奨されます。

概念 RESTfulなメソッドとURL
記事を投稿する POST /articles
記事(ID=1)にコメントを投稿する POST /articles/1/comments
記事(ID=1)を更新する PATCH /articles/1
記事(ID=1)のコメント(ID=1)を更新する PATCH /articles/1/comments/1
記事(ID=1)を削除する DELETE /articles/1
ユーザ(ID=1)のパスワードを更新する PATCH /users/1/password
(エラー) POST /users/1/password

⚓ ルーティングを綺麗に書くコツ

⚓ 1. 原則は「RESTに従う」

原則として、RESTに従うようにしましょう。頑張ればresourceですべて書くことができます。ただし原理主義的に何が何でもresourceで書こうとすると、かえって見通しが悪くなることもありますので、ほどほどにしましょう。

  resources :admin_menus, only:[ :index, :update ]
  resources :menus, only:[ :index ]
  resources :deadlines, except:[ :create, :destroy ]

⚓ 2.以下の用語を理解する

リソース
HTTPメソッド(GET/POST/PATCH/PUT/DELETE)で操作する対象となるURLです。
名前付きルート
「パス_path」(ドメイン名より下のパスのみ)または「パス_url」(httpなどから始まるフルパス)という形式でURL形式を指定できます。たとえばContactページが/contactというパスにある場合、contact_pathまたはcontact_urlという名前付きルートで指定できます。名前付きルートは、パスヘルパーやURLヘルパーとも呼ばれます。これにより、コード上でURL構造を意識せずにパスを指定できます。
なお似ているけど違うのは「名前付きスコープ」と「名前付きパラメータ」です。
ネストしたリソース
ネストしたルーティングを記述することで、あるリソースを他のリソースの子にすることができます。

ネストしたリソースなどについて詳しくは、後編で解説します。

⚓ 3. アルファベット順にソートする

これはメンテナンスのしやすさに通じます。

なお、ルーティングはファイルの上から順に有効になりますので、同じものを下に書いても効きません。

⚓ 4. ルーティングファイルの分割を検討する

Railsアプリケーションが成長してルーティングが膨大になったら、config/routes.rbの分割を検討しましょう。

たとえば以下のようにconfig/application.rbに記載することで、config/routes.rbを分割してconfig/routes/以下のroutes_1.rbとroutes_2.rbに置くことができます。

config.paths["config/routes"] << "config/routes/routes_1.rb" 
config.paths["config/routes"] << "config/routes/routes_2.rb" 

関連記事

Railsのルーティングを極める (後編)

Railsのルーティングを極める (後編)

$
0
0

更新情報

  • 2014/03/03: 初版公開
  • 2020/11/20: Rails 6で確認および更新

こんにちは、hachi8833です。「Railsのルーティングを極める」の後編です。今回はRails 4.0.3 + Ruby 2.1.1の環境で動作確認しています。

⚓ Railsのルーティング(routes)を極める

2012/03(baba

⚓ resourcesとネスト

Railsのルーティング記法の基本は、複数形のresourcesメソッドと単数形のresourceメソッドです。また、Railsのルーティングにはネストを含む多くのオプションがあり、自由度が飛躍的に高まっています。

以下の2つのルーティングは、ネストしていないシンプルなresourcesルーティングです。prefecturesとarticlesは、いずれもコントローラに合わせて複数形で書く点にご注意ください。

resources :prefectures
resources :articles

rails routesしてみると、prefecturesとarticlesそれぞれについてRESTfulかつ標準的なアクション(indexcreateneweditshowupdatedestroy)を網羅したなルーティングが一気に生成されています。

nonnested

ところで、せっかくなのでRails 4.0以降で使えるルーティング表示機能でも見てみましょう。development環境でRailsを起動して、ブラウザでhttp://localhost:3000/rails/info/routesを開くと以下のように表示されます。

名前付きルートもHelper列にわかりやすく表示され、[Path]と[Url]をクリックすれば名前付きルートの*_path*_urlを切り替えて表示するという細かい芸もやってくれます。

nonnested_4.0

参考までに、最近のRailsでは以下のように表示が洗練されています(6.0.3.4で確認)。

なお名前付きルートのうち、*_pathはドメイン名から下のパス、*_urlはhttp://などから始まるフルパスであることは前編でも説明いたしました。生成された名前付きルートをrails consoleで確認するには、app.に続けて名前付きルートを入力してみます。

namedroutes

「複数形にはidはなく、単数形にはidがある」と覚えておくとよいでしょう。

このようにコードで名前付きルートを使うことで、生のURLをコードに書かずに済みます。

名前付きルートはカスタムで指定することもできますが、なるべくこのように標準的なものをRailsに生成させる方が楽ですし混乱せずに済みます。

では、このうちprefecturesの下でcitiesとcompaniesをネストさせてみましょう。

resources :prefectures do
  resources :cities do
    resources :companies
  end
end

このときのルーティングテーブルは以下のようになります。

nested

見てのとおり、prefecturesのルーティングに加え、prefectures/cities、そしてprefectures/city/companiesという階層が追加されました。いずれも複数形の「resources」を指定しているので、prefectures, city, companyにはidがあります。

このときの名前付きルートは次のようになります。idの部分には適当な数字を入れてあります。

nestedroutes2

⚓ 単数リソース

上では複数形のresourcesを使用してid付きのルーティングを生成しましたが、単純なページへのルーティングのようにidが不要な場合は単数形のresourceを指定することができます。

仮にprefectureでidが不要だとすると、以下のように単数形のresourceを指定し、prefectureも単数で書きます。

resource :prefecture

このときのルーティングは以下のようになります。

resource

見てのとおり、Pathにid:が含まれなくなり、indexアクションもなくなりました。

なお、この記述「resource」も「prefecture」も単数ですが、これによって指定されるコントローラは「prefectures」と複数形になっていることにご注意ください。御存知のとおり、Railsではコントローラ名を複数形で書くことになっています。

名前付きルートを確認します。なお、無効なはずのidをわざと付けてみると、妙なパスが生成されました。

singleresource

⚓ 複数リソースと単数リソースのネスト

今度は複数リソースと単数リソースの組み合わせの例を示します。ユーザーは複数いるのが普通なのでidを指定しますが、ユーザーごとのパスワードは1つしかないのが普通なので、パスワードではidを指定しない、という状況です。

この場合以下のようなルーティングが考えられます。外側のusersは複数リソース、内側のpasswordは単数リソースです

resources :users do
  resource :password
end

この場合は以下のルーティングが生成されます。

combinednest

期待どおり、userにはidがあり、passwordにはidがありません。

なお、user_pathのようにそのリソースがパスの最後尾にある場合のidは「/:id」と表記されていますが、user_password_pathのようにuserがパスの途中にある場合では「/:users_id/」と表記されています。どちらもidです。

nestedsingle

⚓ resourceベースでないルーティングの書き方

resourceベースでない、HTTPメソッドを指定したルーティングも可能です。その方がresourcesで書くよりもルーティングテーブルがシンプルになるのであれば、使う方がよいと思います。

get 'hello1', to: 'pages#hello'
get 'hello2', :controller => 'pages', :action => 'hello2' 
get 'hello3/:id', to: 'pages#hello3'
post 'hello4', to: 'pages#hello4'

⚓ よくない書き方

以前は、GETPOSTPUTUPDATEDELETEのHTTPメソッドすべてにマッチさせたいときはmatchを使ったのだそうですが、ワイルドカードはセキュリティ上の隙になる可能性があるので、Rails 4からvia:オプションなしでのmatch指定は禁止されています。

match :hello5, to: 'pages#hello5' #禁止 (エラーが表示される)
match :hello5, to: 'pages#hello5', via: [:get, :post] #許される

さらに、かつては以下のように書くことができたのだそうです。

match ':controller(/:action(/:id))(.:format)'

こうすると「コントローラ」「アクション」「id」が実在してさえいればupdateやdestory`など何にでもマッチしてしまうという、楽ちんかつ風通しの良すぎるルーティングになります。このヒューヒューの全通しルーティングは当時から危険視されていたらしく、チュートリアルや実験用以外で使うべきでないとされていたようですが、現在は完全に禁止されています。

⚓ namespace

Railsのルーティングでは以下のようにnamespaceを指定してパスをグループ化することができます。これを使用して、たとえば管理用ページ(admin)のパスや置き場所を仕切ることができます。

namespace :page do
  get :privacy_policy
  get :company_information
  get :term_of_use
  get :businessdeal
end

namespaces1

上のように、「名前付きルート」「パス」「コントローラ#アクション」にpageが追加されました。

上は素朴なgetメソッドルーティングでしたが、resourcesルーティングに名前空間を与えることもできます。

namespace :admin do
  resources :users
end

namespaces2

同じく、「名前付きルート」「パス」「コントローラ#アクション」にadminが追加されました。

⚓ :moduleによる名前空間

:moduleオプションを使用してresourcesに名前空間を与えることもできますが、これは上と少し動作が異なります。

resources :users, module: :admin

#以下も同等
scope module: :admin do
  resources :users
end

namespace3

こちらは「コントローラ#アクション」にしかadmin名前空間が追加されていません。ここからわかるように、パスには表したくないが別のディレクトリにまとめたいコントローラがある場合に利用できます。

⚓ collectionとmember

既に見たように、resourcesを使用すれば主要な7つのルーティングが自動的に追加されますが、 それ以外のルーティングをそのリソースに追加したい場合はmemberまたはcollectionを使います。

さっきの「複数形はidなし、単数形はidあり」と同じ考え方で、「collection(集合)はidなし、member(個別)はidあり」と覚えましょう。以下のルーティングを例に取ります。

resources :books do
  collection do
    post :search
    post :remove_multi
  end
  member do
    get :thumbnail
    get :sample_file
  end
end

これをルーティングテーブルにすると以下のようになります。

collectionmember

見てのとおり、いつもの7つのルーティングに加えて4つのルーティングが追加されています。そしてcollectionで指定した2つにはidはなく、memberで指定した2つにはidがあります。

なお、この場合の名前付きルートはbooks_search_pathとかではなくsearch_books_pathのように上位のリソースが後ろに置かれていることにご注意ください。一応名前付きルートも確認してみましょう。

collectmember

以下のような簡略版表記も使用できます。

resources :books do
  post :search, on: :collection
  get :thumbnail, on: :member
end

ここで1つ注意があります。以下のようにcollectionmemberも指定せずに書いた場合はデフォルトで「member扱い」となります。

resources :books do
  post :search
  post :remove_multi
end

上の2つのリソースのルーティングテーブルを見てみると、確かにidが含まれており、member扱いされていることがわかります。

nocollectmember

⚓ root

今更ですが、rootへのルーティングの書き方は以下のとおりです。これだけ他の書き方と比べて少し浮いている感じですね。

root to: 'page#top'

⚓ ルーティングのオプション

最後に、ルーティングでよく使われるオプションを紹介します。

⚓ only:except:

resourcesonly:またはexcept:オプションを使用することで、主要な7つのアクション(index, show, new, create, edit, update, destroy)を限定することができます。

# indexとshowアクションだけ使う場合
resources :prefectures, only: [:index, :show]
# destory アクション以外を使う場合
resources :prefectures, except: :destroy 

updatedestroyのような破壊的なアクションは事前にルーティングレベルで塞いでおきましょう。

⚓ リソース名の変更

asオプションを使用して、リソース名を変更することができます。

get 'home', controller: :users, as: 'user_root'

asoption

⚓ httpsの指定

以下のようにprotocol: httpsを指定することができます。

scope protocol: 'https://', constrains: {protocol: 'https'} do
  root to: 'page#top'
end

なお、アプリの一部だけをHTTPS化するのは手間がかかる上にセキュリティ上の懸念も残るので、アプリ全体をHTTPS化することをおすすめします。

⚓ idを拡張

たとえば、以下のようにidの制約を変更してアルファベットのidを使用することができます。

resources :prefectures, id: '/^[a-z]+$/'

⚓ 最後に

Railsには他にも強力なルーティングのオプションがたくさんありますが、アドホックなカスタムルーティングを避け、なるべくresoucesresourceonlyexceptで素直かつ統一のとれたルーティングを生成するようにします。

コントローラが数百にのぼる巨大なルーティングをすべてresourcesresourceで書いた例もあります。

ただし、そこでRESTfulにしようと頑張り過ぎないのも大事です。

⚓ 追伸

Rails 6.1では、ルーティングの記述を間違えたときにdid you mean?で推測する機能が加わります。

⚓ 参考

関連記事

Railsのルーティングを極める(前編)

Rubyの内部文字コードはUTF-8ではない…だと…?!

$
0
0

更新情報
2016/10/13: 初版公開
2020/11/27: 追記

こんにちは、hachi8833です。

少し前に、babaさんから「Rubyの内部文字コードはUTF-8じゃないよ」とツッコミがありました。

(追記: 上は会話の途中から切り取りましたのでご了承ください)
いきなりの展開にくらくらきましたが、babaさんはさらにたたみかけます。

こうしたことはとっくにご存じの方も多いと思いますが、「Rubyといえば2.0以来UTF-8完全対応なんじゃないの」と勝手に思い込んでた私は脳に掌底を食らったような思いです。ああ、でもこういうことがあるから面白い。

⚓ プログラミング言語と内部文字コードの関係

まず最初に押さえておきたい点です。プログラミング言語で文字コードに関連する部分は、「文字列」「正規表現」「入出力」「コード中の文字リテラル(””の中など)」「コード中の文字リテラル以外の要素(変数名など)」「ファイル名」などが中心になります。そして文字列に関連して「ソート順」などについても考慮が必要です。

とbabaさんが指摘しているとおり、Rubyで文字コードに関連するのはほとんどの場合標準ライブラリです。

⚓ UCS正規化方式

Java、C#、Python、Perl、Goなど、多くの言語では内部でUnicodeを用いています。言い方を変えれば、文字コードを固定してそれ以外のコードについては変換のみで対応するということです。このように内部表現をUnicodeの文字コードに統一する方式をUCS正規化(UCS Normalization)と呼びます【注: リンク修正いたしました】。UCSは「UnicodeUniversal Character Set」の略です。

UCS正規化を採用している言語では、たとえばStringクラスなどで原則としてその文字コードしか保存できません。たとえばJavaのStringクラス、Characterクラス、char型であれば文字をUTF-16で保存します。そのため、UTF-16Unicodeに含まれない文字(実はそれなりにあるのです)は原則として言語標準のStringクラスなどでは扱わない/扱えないことになります。

こうした言語でも、標準のStringクラスなどを使わずにchar[]型などに保存して自力でハンドリングしたり別ライブラリで扱ったりする分には構いませんし、その必要が生じることはいくらでもありえます。

⚓ Rubyは1.9から「CSI方式」

これに対し、Rubyは1.9でCSI(Code Set Independent)という独自の多言語化方式を導入しました。

UCS正規化と異なり、CSI方式では特定の内部コードを仮定しません。たとえばStringクラスの変数にはその気になればUnicode以外の文字コードでも直接保存できます。つまり文字列は内部的に事実上バイナリとして保存されているのです。その代わりStringクラスで文字コード情報を持てるようにする(以下のコード例の#encoding)などの改良が行われました。

実装が複雑になることもあり、CSI方式を導入した言語はRuby以外にはなかなかないようですが、多様性に富んだチャレンジングな多言語化方式とされています。なお、Ruby 1.9でオレオレ文字コードを導入してみた豪の者を見つけました。

また、1.9ではマジックコメントが導入され、スクリプトファイルの冒頭(冒頭がshebangの場合は2行目)に# coding: euc-jpなどの方法でファイルのエンコーディングを指定できるようになりました。

#!/usr/bin/env ruby -
# coding: utf-8
str='ab漢字'
puts str[2]
puts __ENCODING__
puts str.encoding

161011_1500_pn3QyE

ただし1.9の場合、マジックコメントを指定しない場合のデフォルトのファイルエンコーディングはUS-ASCIIです。UTF-8ではありません。

#!/usr/bin/env ruby -
puts __ENCODING__

161014_2315_pFG4MN

⚓ Ruby 2.0での文字コード周りの変更とその後

Rubyが2.0になってから、このマジックコメントを指定しない場合のデフォルトのファイルエンコーディングがUS-ASCIIからUTF-8に変更されました

#!/usr/bin/env ruby -
puts __ENCODING__

161014_2321_vty3fY

2.0でのデフォルト文字コードの変更はもちろん大きなものであり、他にも文字コードについての変更点はありますが、CSIの部分に関しては引き続き変わっていません。つまり2.0の文字列は1.9のときと同様、内部では事実上バイナリなのです。

Rubyでは、1.9でのCSI導入、2.0でのデフォルトのファイルエンコーディングのUTF-8への変更という段階的な方法で多言語化を切り替えてきました。2.0以降、これらの点について変更はありません。

私がRuby 2.0からUTF-8完全対応だと思い込んでいたのはいろんな意味で誤りでした。後述するようにRubyは1.9より前からUTF-8を含む複数の文字コードを扱えましたし、2.0でUTF-8になったのは「デフォルトのファイルエンコーディング」の部分です。

⚓ 参考: Ruby 1.8以前の場合

Ruby 1.8以前は文字列をデフォルトでASCII単位でとして扱い、文字列メソッドもバイト単位で動作しました。たとえば1.8でstr="ab漢字"の後でstr[2]の値を取り出すと0xB4(EUCの「漢」の最初のバイト)になりました。

str='ab漢字'
p sprintf('%#x', str[2])

このkanji.rbはEUC-JPで保存されているとします。

161011_1321_mpCxn4

1.8以前でこうしたスクリプトを実行するには、スクリプトファイルの文字コードに応じた-Ku(UTF-8)や-Ke(EUC-JP)や-Ks(Shift_JIS)などのオプション指定が欠かせませんでした(2.0から非推奨)。指定したオプションは$KCODEで確認でき、正規表現は$KCODEに応じて文字数の数え方が変わりました。文字列ごとに異なる文字コードを指定することはできませんでした($KCODEは1.9で廃止)。

⚓ 注意

UCS正規化とCSIには、それぞれメリットとデメリットがあります。どちらが優れているというものではなく、ユースケースによって変わります。

一般にUCS正規化では文字コードの実装を一本化できますが、Unicodeにない文字などは標準的な方法では直接扱えないので別のライブラリなどで対応する必要があります。

CSIでは標準的な方法で複数の文字コードを同時に扱うことができますが、正規表現の文字数の数え方などを文字コードに応じて実装側で切り替えるなど、実装が複雑になることが考えられます。

⚓ 参考文献

今回の記事では『プログラマーのための文字コード技術入門』に大変お世話になりました。

追記(2020/11/27): 2018年に同書の改訂版が出ました↓。

同書の裏帯に書かれているように、特定の文字コード体系を押したりせず、評価を読者の判断に任せている点に好感が持てます。同書ではRuby 1.8と当時最新だったRuby 1.9を比較しており、決して新しい内容ではありませんが、今回取り上げたRuby 1.9でのCSI導入についても詳しく解説されていて、現在でも読む価値のある良書です。Unicode、UTF-8が万能ではないということがわかったのは自分にとっては大きな収穫でした。

文字コードはどこまで行っても奥が深いので、理解の甘いところがありましたらTwitterで私までお知らせいただけると幸いです。

⚓ 追記(2020/11/27)

RubyでCSI正規化が採用された経緯についてMatz自らQuoraで回答していました🎉

関連記事

MySQLのencodingをutf8からutf8mb4に変更して寿司ビール問題に対応する


AWS Organizationで一括請求を使うときの不満点

$
0
0

AWSライトユーザーのbabaです。

AWS Organizationは大変便利な機能ですよね。一括請求ができるため、以下のようなメリットを得られます。

  • アカウント単位で分離されるため、IAMで個別に設定するよりも、セキュリティ面や操作ミス対策を強固にできる。
  • アカウント単位で請求書を出せるため、正確な金額の把握や、受託案件でお客様にサーバ実費を請求するのもやりやすい(コストタグは手間がかかる割に転送料金など配分できない要素が多く、コストの把握には不十分)。
  • OrganizationAccountAccessRole でユーザ切り替えできるため、細かくアカウントを分割しても、毎回ログアウト→ログインする必要もなく操作性が良い。
  • 個別のアカウントにクレジットカードを登録しなくても一括請求できるので、管理上会社のクレジットカードを持てないメンバーにアカウント管理権限ごと預けることが容易。

また、簡易的な利用では、 OrganizationAccountAccessRole を使うだけで、rootユーザのパスワードを設定しないまま運用できるのも地味に便利です。rootユーザにはMFAを設定したくなりますが、アカウントの数だけ登録しているとGoogle Authenticator (Authy好きじゃないんです) がいっぱいになってしまいます。

最近(ようやく)CloudFrontの署名付きCookieを発行するための公開鍵登録にroot権限が不要になったので、随分rootが必要なケースが減りました。

⚓ 自分の運用

BPSでは、件数が多い受託開発ではお客様のAWSアカウントを使うことが多いため、自社サービスや開発用が中心で、管理しているアカウント数はそれほど多くありません(2桁に余裕で収まる程度)が、それでもこの機能なしではやっていけない程度には便利に使っています。

アカウントの切り替えも簡単

マネジメントコンソール右上の切り替えは、直近5件しか履歴が出ないので、以下のようなURLをアカウント数分だけブラウザブックマークに入れています。

https://signin.aws.amazon.com/switchrole?account=000000000000&roleName=OrganizationAccountAccessRole&displayName=sub-account1&color=99BCE3

⚓ 気になる点

大変便利なOrganization機能ですが、運用していくと細かい不満も色々でてきます。

⚓ 無料利用枠が使えない

AWSはやたらと強く無料利用枠を宣伝してきますが、これはOrganizationで一括請求をしている場合、全体で1アカウントとみなされます。そのため、1年以上運用している組織では、新規アカウントであっても大半の無料利用枠が使えなくなります。

また、無期限無料の利用枠も共有されるため、たとえばCloudWatch-Defaultのダッシュボードを作っただけで3ドル請求されます。少数のサーバのメトリクスを監視する程度ならほとんど無料枠内だろう、と思って油断すると馬鹿にならない金額になるので注意が必要です。

一括請求を有効したアカウントを作るとこんなメールが来る(しかも無料利用枠のページには一括請求の制限が書いていない)ので無料利用枠が使えそうに見えますが、騙されてはいけません。

仕様としてそういうものであること自体はまあ良いのですが、宣伝方法は誇大広告なので改善を期待したいところです。

現時点で取れる対策:無料利用というキーワードを脳内NGワードで無視するようにしています。

これらの表示は脳内フィルタで無視する

⚓ RIやSavingPlansを共有すると、実コストの把握が困難になる

デフォルトでは、マスターアカウントで購入したリザーブドインスタンス(RI)やSaving Plansはメンバーアカウントに共有されます。これにより、各アカウント内では使用量にばらつきがあるケースでも、全体を通した使用量で効率的にRI/SavingPlansを購入できるため、素晴らしい仕組みに見えます。

ただしこの方法を取ると、当然なのですがマスターアカウントに大量の請求がかかり、メンバーアカウントは安くなります。各アカウントごとにかかったコストが曖昧になるため、AWSアカウント分離のメリットの1つが失われてしまいます。

コスト計算のために以下のどちらかが実現できれば良いのですが、

  • A) 割り引かれたRI/SavingPlansの利用料金を、実際のアカウントごとの適用分で案分する
  • B) 各メンバーアカウントで、割引適用前の「定価の」コストを求める(割引額は全体での節約分と考え、個別コスト計算では考慮しない)

Aは前払いがあるので実現不能です。

Bはできそうに見えますが、実際にやろうとするとかなり大変です。まず、RI/SavingPlans適用前の料金は、Cost Explorerや請求書で確認することはできません。AWSサポートに確認したところ、以下の方法を案内されました。

⚓ 方法1. 請求書の明細から手計算する

「アカウント毎の料金明細」タブをクリックすると、アカウント単位でRI が適用された時間とインスタンスタイプが確認できるので、これを料金表と照らし合わせて自分で計算する。

これを1個ずつ計算する

まあ、やればできると思いますが、正直やりたくないです。

⚓ 方法2. コストと利用状況レポート (CUR) を使う

海外の担当部署に確認とのことで、2週間近くかけて満を持して案内された方法です。

コストと使用状況レポート(CUR)を使うと、詳細なログがS3に記録されるので、 pricing/publicOnDemandRatepricing/publicOnDemandCost を元に計算すれば金額が求められます。

この方法の大きな欠点は、「金額を計算するためにCURを使うとS3にデータが保存されるため、その利用料金が発生する」「データは非常に細かいログ形式なので、計算するスクリプトを組まないと利用は困難」という点です。

ただコストを知りたいだけで、なんでこんな負担を追わなくてはならないのか…

これを処理するスクリプトを自分で書く

複数アカウントで孤立的にRIを運用できるメリットよりも、コストが隠蔽されて解約忘れのリスクが増えるデメリットが上回ると感じています。というか、方法1は面倒なのでより良い方法を2週間かけて探してくれたらしいけど明らかに方法1より面倒ですね。

現時点で取れる対策:良い運用が作り出せるまで、当面マスターアカウントでRIを買うのはやめようと思います。


※なお、「Billing の設定」→「RI と Savings Plans の割引共有」から、メンバーアカウントごとに共有有無の設定は可能です。特に、受託案件で実費を請求する約束になっている場合など、正確な把握が必要な場合は無効化しておくと良いでしょう。

⚓ クレジットの共有をすると、実コストの把握に手間がかかるようになる

AWSでクレジット(クレジットカードではありません)を登録することがあると思います。たとえば、AWSパートナー登録では一定数のみクレジットが受け取れます。ケースによりますが、これは事実上前払いデポジットのようなものと考えられます。

一括請求のマスターアカウントにクレジットを登録すると、メンバーアカウントの利用分も含めてクレジットから消費されていくようになります。

問題は、クレジットを登録すると、請求書に記載の金額が「クレジット適用後の金額」になってしまうことです。確かにAWSからの「請求書」としてはそれで正しいのですが、前払いを消費しているのであってコストが安くなったわけではないので、各アカウントの費用としてはクレジット適用前を知る必要があります。特に、受託案件で実費を後日請求するようなアカウントでは、実際に発生したコストを請求できないと赤字になってしまいます。

AWSサポートに確認したところ、以下の2つの方法があることがわかりました。

⚓ 方法1. 請求書を展開する

メンバーアカウントにログインして請求書を表示し、印刷用に展開すると、 注意: クレジットの $000 が請求書の各製品に適用されました と表示されます。これを請求額に加算することで、実際にかかったコストを知ることが出来ます。

確実な方法ですが、メンバーアカウントごとにアカウントを切り替えてこの手順を繰り返す必要があり、多少手間がかかります。

⚓ 方法2. Cost Explorerを使う

コストエクスプローラーでは、「料金タイプ」→「クレジット」を切り替えることで、クレジット適用前後のコストを表示できます。また、アカウント単位でグルーピングもできるため、概要の把握にはこれが最も便利です。

設定で切り替えができる

ただしこの方法、計算誤差が発生します。Cost Explorerと実際の請求では計算方法が違う(実際の請求は0.01USD単位で切り上げや丸めが行われる)ため、数円程度の誤差が発生します。問い合わせたところ「正確な金額を知る必要があるケースではCost Explorerは使わないように」との回答をもらったのですが、普通仕事なら金額は正確にしないとまずいでしょう…


また、些細なことですが、Cost Explorerの表はtableタグが不思議な作りなのでコピペしづらく、CSVダウンロードすると行と列が逆になるので、地味にストレスが溜まるんですよね。

なお、RI/SavingPlansと違って、クレジットはメンバーアカウント単位で共有有無を切り替えることはできません。全メンバーアカウントに対して共有を止めるのはBillingの設定ページでできるのですが、一部のアカウントだけ止めることが出来ないため、受託案件で実費を後日請求するスタイルのアカウントが混ざっていると、運用が面倒になります。

⚓ 営業メールがたくさん来る

アカウントの数だけ、re:Inventなどの営業メールがたくさん届きます。Unsubscribeすれば良いのですがたくさんあると面倒で、毎回後回しにしてしまいます。

⚓ 終わりに

どれも「そういうもの」と言われればそれまでですし、各種APIが整備されているのだから開発者なら自動化して仕組みを作るべきだというのも正しいと思います。

ただ、AWSは所詮1ベンダーのサービスですし、利用者として声を上げていくことも必要なはずです。

個人的な感覚として、AWSは基本的にボトムアップ的な、提供側の都合でまずサービスが開始され、ユーザのフィードバックを受けて改善していく印象があります。例えばLambdaとRDSをそれぞれ提供しておいて、組み合わせるとコネクション数が溢れて相性が悪いという問題について、しばらく「EC2でコネクションプールするインタンスを立てる」というどう考えてもバッドノウハウとしか思えない(マネージドのメリットどこいった)方法が色々紹介されていた時期もありましたが、しばらくしたらRDS Proxyという解決策が提供されました(RDS ProxyがAuroraのリーダーエンドポイントに繋げないという欠陥も、遠くないうちに対応されるでしょう)。

この「最初は不完全でも改善していける」というのがAWSの良いところだと思っていますので、改善に期待しています。


CloudWatch Eventが重複起動することがあるひどいバグも、いい加減さっさと直してほしいです。これを仕様と言い張るのはだめでしょう。


モバイルモニタにはWacom Link Plusが便利

$
0
0

※ただのお買い物日記です。

しばらくLG 32UL500 + DELL P2421DCのデュアルディスプレイでやりくりしていたのですが、普段の作業に支障はないものの、Web会議のときに参加者の顔や資料を映すと他のウィンドウが隠れるのが気になっていました。そこで、会議中にちょっとした情報を表示するためのサブディスプレイを追加することにしました。

このメインディスプレイの下に小さいのを置きたい。

⚓ モバイルモニターの選定

机の広さには限りがありますし、スピーカーを小さくするわけにはいかないので、メインディスプレイの下における小さなものを選びます。Amazonで「モバイルモニター」を探せば、中国製の安いものが無数にヒットします。5月ころはかなり値上がりしていましたが、最近は底値付近まで戻ってきていますね。EVICIVやCocoparなど有名どころにしようかとも思いましたが、どうせどれも中身は同じようなものだし、保証期間もそれより先にメーカーがなくなるので気にするだけ無駄ということで、メーカー問わず色々見てみました。LenovoやASUSなどの高級品は最初から対象外で。

  • 13.3インチは小さいから15.6インチを第1候補に、でもスペース狭いから14インチのほうが安全だろうか
  • 4Kはあっても良いけど実用上はFHDで良さそう
  • タッチパネルあったらテストに便利そう
  • 144Hzとかの高リフレッシュレートモニター持っていないので、あったらrequestAnimationFrameのテストに便利そう
  • 標準HDMIやmini DPが入るのは便利、特にグラボからHDMIは複数出ないので、4Kならmini DP入るのは重要(DP++のHDMI変換は4K出せない)

などと色々迷った挙げ句、ippinkanという聞いたことのないメーカーの14インチFHDタッチ対応モデルを購入しました。

買って数日しか経っていないのにもう商品ページは消滅していたので、類似商品のページのスクショ


sRGB 100%とか永久保証とか色々書いてありますがまあ話半分くらいに見ておけば大丈夫です。

⚓ Wacom Link Plus

しかし、僕のPCはデスクトップPCで、グラボのGeForce GTX 1650にはDP Alt mode対応のUSB Type-Cは搭載されていません(搭載されているグラボはとてもお高い上に数えるほどしかない)。空いているポートはDisplayPortの1個のみです。DP++は対応しているはずなのでHDMI変換からmini HDMIに変換しても良いのですが、給電用USB Type-Cとタッチパネル用のUSB Micro-Bの合計3本のケーブルが必要になり、メインディスプレイの下に置くには少々かさばってしまいます。直ぐ側にオーディオインターフェースがあるので、それが操作しづらくなるのは避けたい。

そこで、5chで動作報告のあったWacom Link Plusを一緒に買ってみました。

3本のケーブルを1本にまとめる

これは、給電用のUSB Type-C、ディスプレイ入力としてMini Displayport(HDMIもついてますがWQHDまで)、PCに接続するUSB Micro-Bをつないで、それらをまとめてUSB Type-Cにできる夢のようなアダプタです。つまり、デスクトップPCでも、ノートPCのような「PD給電 + DP Alt mode + 普通にUSB通信」がセットになったUSB Type-Cポートを増やせることになります。これで、ケーブル1本でモバイルモニタを接続でき、見た目がスッキリしました。

スッキリ設置できました

ワコムタブレット用として売られていますが、もっと流行ってもいい商品だと思います。7000円の価値はある。

⚓ ディスプレイ

⚓ タッチパネル

このディスプレイに限らずですが、多くのタッチ対応外付けディスプレイでは、何もせずに接続すると「プライマリディスプレイをタッチした」扱いになります。流石にこのサイズのディスプレイをメインにする人はいないと思いますので、設定が必要です。

コントロールパネルの「タブレットPC設定」を開き(この項目はタッチ対応ディスプレイを接続しないとでてきません)、セットアップから画面に従って操作すると、2番目以降のモニタでも正しくタッチが認識されるようになります。

Chromeでマルチタッチなども普通に動作するようになりました。

⚓ グレアパネル

適当に買ったら光沢(グレア)でした。ノートPCでグレアパネルも経験しているのでワンチャンいけるかと思いましたが、安物グレアをなめてはいけない。強いめまいに襲われ、即座に反射防止フィルムを注文しました。

これを貼ったら、ごく普通のノートPCのように、見やすい表示になりました。

⚓ 色の設定

安物に表示品質を求めてはいけませんが、一応カラーキャリブレーションもしておきました。

加水分解でベトベトになったi1 Display Proの出番です

こんな感じで測定してみます。

平らに置けるので、測色計を乗せやすいのは便利

輝度ムラチェック

結果としては、目視できるレベルで下の輝度落ちはあるものの、思ったほど酷くなかったです。どうせ余り物パネルを適当に組み込んでいるだけだと思うので、2台買ったら全然違う色が出ると思いますが、とりあえずちょっとブラウザ開いておく程度なら実用レベルでした。

輝度ムラチェック結果

色チェック結果

明るさが10段階設定だったりかなり大雑把ですが、電源抜いても(数十秒しか試してませんが)設定が保持されているのは良かったです。

⚓ やりたいこと

本当は、Web会議の際に相手と目が合わないのが微妙に気になっていて、Webカメラのすぐ近く、メインディスプレイの「上」にWeb会議の参加者を映す専用のモニター置けると良いなと思っています。2段アームもありますが、あまりごついのは好きではないし要塞を作りたいわけではないので、こんな感じのアームでモバイルモニターを挟み込めばいいかなと思いつつ、耐荷重しょぼいから地震に弱そうで少し躊躇しています。良いアイデア募集中です。

⚓ まとめ

デスクトップPCでモバイルモニター使おうと思っている人にWacom Link Plusおすすめです。


ExcelでCSV保存したときに半角スペースがはてな(?)に文字化けする

$
0
0

更新情報

  • 2010/07/22: 初版公開
  • 2021/08/19: 更新

ExcelからCSV形式で保存するには、「名前を付けて保存」ダイアログでCSV(カンマ区切り)を選択すればOKです。

#エクスポートしたいところですが、保存しかできないようですね。連続でやるときにはすごく不便。

ところが、これをやると、文字化けが発生してしまうことがあります。

現象

半角スペースが含まれるExcelファイルで、Excel上で見るぶんには問題ないのですが、これをCSVエクスポートすると、1行目だけ文字化けします。

Excelでは上の行も下の行も見た目は同じ

CSVにエクスポートしてExcelで開くと、上の行だけ文字化けする

原因

これは、同じ半角スペースに見えても「CODE=32(0x20)」の「半角スペース」と、「CODE=160(0xA0)」の「NBSP」があるのが原因になっています。

0xA0のNBSPはShift-JISでは表現できないので、保存時に「?」に文字化けしてしまいます。

解決方法

これを解消するには、160のNBSPを32の半角スペースに一括置換すれば良いです。

1. 適当なセルに「=CHAR(160)」という数式を入力して、その結果をコピーします。

charcode=160の文字を作成

charcode=160の文字を作成

2. その後、Ctrl+Fの「置換」タブで、「検索する文字列」にコピーした内容をペースト、「置換後の文字列」に普通の半角スペースを入力して、「すべて置換」すればOKです。

これで、見た目は変わらないけど文字化けしないExcelファイルができました。

というか、普通に使っていればこんな問題は起きないんですが、なぜか送られてきたExcelがこんなことになっていて・・・
先方はどんな使い方をしていたんでしょうね?

The post ExcelでCSV保存したときに半角スペースがはてな(?)に文字化けする first appeared on TechRacho.

Google Nest Hubが「ネットワーク障害」になったけど直った

$
0
0

我が家では、 Google Nest Hub (初代) が発売直後から活躍中です。普段はフォトフレームとして使いつつ、カップラーメンを作るときにはキッチンタイマー。他の使い方はよく知りません。Google Home時代はタイマーの残り時間もわからなかったので、随分便利になりました。

TL;DR

おそらく新しいファームはDNSサーバがEDNS非対応だとエラーになる。DNSを1.1.1.1にしたら直った。

突然の「ネットワーク障害」表示

ところが、8月の上旬頃から以下のような問題が起きるようになりました。

  • 「ネットワーク障害」や「インターネット接続をご確認ください」と表示されて使えない。
  • 話しかけると「OK, Google」「エラーが発生しました。数秒後に再度お試しください」と言われる。
  • 設定したGoogle Photosの写真ではなく、プリセットの風景写真が表示されてしまう。

感覚的にはUIが変わってしばらくした頃からだったので、Fuchsia更新後だったのでしょうか。数日は「まあ放置すれば直るだろう」で実際直っていたのですが、だんだんエラーの頻度が上がり、そのうちに全然使えなくなってしまいました。

これでは可愛い孫の写真が見られないのではないかと家族から大不評だったので、どうにか復旧が必要です。

初期調査

我が家の環境は以下のようなものです。

  • Google Nest Hub (初代)
    • ファームウェアバージョン: 1.20210119.2.1390060
    • キャストファームウェア: 1.52.260996
  • ルーター
    • YAMAHA RTX810
  • WiFi AP
    • Aruba AP-515
    • Aruba Instant 8.6.0.11

まずは、トラブルシューティングを参考に以下のような対応をしてみました。

  • Nest Hubの再起動
  • Nest Hubの初期化と再設定
  • ルーターの再起動
  • WiFi APの再起動
  • WiFi APのファームウェアアップデート
  • WiFi APのファームウェアダウングレード (8.7系はバギーで他のデバイスまで不安定になったので, 8.6に戻した)
  • 5GHzと2.4GHz帯の切り替え
  • 別のスマホでのセットアップ

しかし、一向に改善しません。

特徴的な挙動として、エラーが出ている場合でもIPアドレスに対してpingは返ってきますし、画面のキャストは成功することがあります。つまり、ローカルネットワークには接続できており、インターネットに出られていないかのような挙動です。

稀にセットアップが完了したと見せかけてここに戻ることもある

Google Home系のデバイスで必要なブロードキャストがフィルタ解除はもちろんされていますし、ファイアウォールも正常動作していた頃から何も変更していません。

検索すると似たような症状の報告もいくつか見つかりました

物理的な故障なら(第2世代も出ているし)単に買い換えれば良いのですが、サーバやネットワークの問題なら買い替えても意味がありません。
調査内容を元にGoogleのサポートに問い合わせたところ、「現在類似の問い合わせは受けており、ファームの問題と思われるが修正時期は未定」「修正リリースを待て」「ダウングレードの方法は存在しない」という趣旨の回答をもらいました。

追加調査~解決

Googleサポートを信じて数日待ってみたのですが、修正はもちろん障害のアナウンスすら一向にされないので、自分でもう少し調べてみることにしました。

色々やってみる

UPnPの有効化:効果なし

ポートがうまく開放されていないのではと思い、適当にあたりを付けてUPnPを有効化してみました。

upnp use on

効果はありませんでした。

IPの振り直し:効果なし

何かのブラックリストに入ったりDHCPの衝突が起きたりしていないか確認のため、DHCPで配るアドレスを変更してみました。MACアドレスは適当です。

dhcp scope bind 1 192.168.2.90 00:00:5E:00:53:AA

効果はありませんでした。

IPv4オンリーのVLANに接続する:効果なし

インターネットにはIPoE DS-Liteで接続しており、IPv4とIPv6両方のアドレスが割り当てられています。IPv6が優先されることで何か問題が起きている可能性を疑い、IPv4しか割り当てていないVLANに接続してみました。

効果はありませんでした。

WPA3をWPA2にする:効果なし

手元のAndroidスマホで、WPA3だとパスワードの保存をしても毎回聞かれる(保存済みネットワークでは常に圏外と表示される)不具合があったので、似たような問題が起きているかもしれないと思ってWPA2を試してみました。

効果はありませんでした。

別のルーターを使ってみる:効果あり

ネットワークの問題を切り分けるため、手っ取り早くルーターもAPも全部変更してみます。あまり家庭ネットワークを落とすと家族からクレームが来ますので、ここはどの家庭でもいくつか転がっているであろうバッファローのルーターから直接PPPoEしてしまいましょう。メイン回線はIPoEなので、PPPoEセッションは余っています。

まずはRTX810の設定で、PPPoEパススルーを設定します。

pppoe pass-through member lan1 lan2

あとはバッファローのルーターを接続して、PPPoEのIDパスワードを入力し、そのAPに対してNest Hubを接続するだけです。

結果… 直りました

ログを見てみる

別のルーターで直るということは、ルーターやAPの設定に問題がある可能性が高まりました。そこで、ログを見てみます。

まずAPのログやダッシュボードを確認しましたが、怪しいものはありませんでした。なぜかNest HubはPlayStationと認識されていますが…

続いてRTX810のログを確認します。詳細ログをONにしましょう

syslog notice on
syslog debug on

noticeをONにすると、ファイアウォールでフィルタされたログが確認できます。debugをONにすると、たくさんのログが確認できます。

眺めていると、次のような事がわかりました。

  • Google HomeアプリからNest HubのTCP 8443や8009にアクセスしている。
  • [DNS] Received illegal request というログがたくさん出ている。
  • [ND] receive NS: unsupported option (0xe) というログがたまに出ている。
  • GoogleのIPv6アドレスのTCP 443から接続する通信が頻繁にフィルタリングされている。

このうち、[DNS] Received illegal request については、Nest Hubの電源を抜くと全く出なくなることから、おそらくこれが怪しそうです。

DNSサーバを切り替える

RTX810でDNSがillegalと言ってまず思いつくのは、EDNSに非対応ということです。digコマンドでは毎回 +noedns を付ける習慣が付いています。つまり、問題が起きているNest HubのファームバージョンではEDNSが必須であり、非対応時にフォールバックする仕組みが未実装または動作していないという仮説を立てました。

ということで、RTX810のDNSキャッシュサーバを使わないように設定します。Nest HubやGoogle Homeアプリに手動設定機能はないので、DHCPで配るアドレスを変更しましょう。指定するDNSは適当にCloudflareのPublic DNSでいいでしょう。IPv6アドレスはいらない気もしますが一応付けておきます。

dns server 1.1.1.1 2606:4700:4700::1111
dns notice order dhcp server

dns notice order dhcp server me とすると「Cloudflare DNS, RTX810自身」の順序で配布しますが、上のように me を除外することで外部のDNSだけを使うようにDHCP配布できます。

この設定をしたところ、直りました

仮説の検証

仮説が正しいことを確認するためには、以下をやると良さそうです。

  • Nest Hubが送っているパケットをキャプチャし、EDNSを使っていることを確認する。
  • EDNS対応・非対応のDNSサーバを用意して切り分ける。

しかし、一般のご家庭にPoEインジェクタやミラーポート付きスイッチは1個しかなく、また作業PCとWiFi APが別の部屋にあって配線が面倒なので、詳細調査は端折りました。そのため、EDNS云々は現状完全な当てずっぽうです。違ったらすみません。

振り返り

とりあえず直ったので結果オーライです。
本当は、全VLANでDNSキャッシュサーバ無効化するのももったいないので、AP側でDHCPするようにすれば良いと思うのですが、それは別の機会に試してみます。

それにしても、やはりRTX810は古すぎたのでしょうか。次に買い換えるならフレッツ光クロスが来るときに備えて10GbE対応のものにしたいのですが、10GbE対応でVLANなど一通りの機能が揃っていて一般家庭に置くのに都合が良い小型・静音でお安いルーターは売っていないものでしょうか。良い機種があれば知りたいです。またヤマハさんにもぜひ後継機を頑張って頂きたいです。


The post Google Nest Hubが「ネットワーク障害」になったけど直った first appeared on TechRacho.

情報処理技術者試験・情報処理安全確保支援士試験の試験委員になりました

$
0
0

少し前に、情報処理技術者試験委員会・情報処理安全確保支援士試験委員会の委員に就任しました。

活動内容などの詳細については守秘義務があるため、IPAが公表している内容以外は書けませんが、試験問題の作成などを400名余りの体制で行っています。

高校生の時に受験を始めた情報処理技術者試験は、若い頃の自分に勉強の方向性や目標を示してくれて、大いに成長を助けてくれました。まだまだ能力・経験ともに未熟者ではありますが、この素晴らしい試験制度を今後も継続・改善していくために、少しでも貢献したいと考える所存です。周りは本当にすごい方ばかりですので、自分を磨いて追いつけるようにがんばります。

情報処理技術者試験・情報処理安全確保支援士試験の概要

情報処理技術者試験は、「情報処理の促進に関する法律」に基づき経済産業省が、情報処理技術者としての「知識・技能」が一定以上の水準であることを認定している国家試験です。(IPAのWebサイトより)

情報処理技術者試験として12区分、情報処理安全確保支援士試験として1区分が用意されており、いわゆるITリテラシを図るITパスポートから専門職向けのスペシャリスト系試験、マネージャ系の試験など幅広い分野がカバーされています。

最近受験料が5700円→7500円に上がりましたが、民間試験だと誤字だらけのCBTで数万円するのも普通ですし、他の国家試験でも1万円を超えるものが多いことを考えると、まだまだお財布に優しい試験だと思います。

BPSでの受験補助制度

BPSでは、情報処理技術者試験を含むいくつかの試験について、受験料や合格手当を支給しています。条件次第で最大100万円をもらえるチャンスがありますので、社員の方はぜひご活用ください。


The post 情報処理技術者試験・情報処理安全確保支援士試験の試験委員になりました first appeared on TechRacho.

Viewing all 101 articles
Browse latest View live