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

週刊Railsウォッチ(20191202前編)Rails 6のimplicit_order_columnはカスタマイズ可能、rubocop-rails 2.4.0リリース、Capistrano記事ほか

$
0
0

こんにちは、hachi8833です。git.ruby-lang.orgが先週から不調なようです。


つっつきボイス:「(DevToolsを覗いて)これは大変そう…😅裏でめっちゃ作業してそうな雰囲気」「お祈りします🙏

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

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

第16回目公開つっつき会は、今週12月05日(木)19:30〜にBPS会議スペースにて開催されます。

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

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

先週のウォッチに載せられなかった分も含んでます🙇

⚓params.member?を追加してHashの振る舞いに近づけた

実際はdelegate:member?を追加しただけの改修です。

# actionpack/lib/action_controller/metal/strong_parameters.rb#L214
-   delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?,
+   delegate :keys, :key?, :has_key?, :member?, :values, :has_value?, :value?, :empty?, :include?,
      :as_json, :to_s, :each_key, to: :@parameters
# actionpack/test/controller/parameters/accessors_test.rb#165
  test "member? returns true if the given key is present in the params" do
    assert @params.member?(:person)
  end

  test "member? returns false if the given key is not present in the params" do
    assert_not @params.member?(:address)
  end

つっつきボイス:「paramskey?が使えるならmember?もある方がいいという感じでしょうか?」「Rubyのハッシュにmember?なんてあったっけ?」「Rubyの公式ドキュメント↓を見ると、key?member?include?has_key?も互いにエイリアスになってる😆」「あそうか😳」 「それなら確かに全部使える方がいいですよね😋」「むしろなぜmember?だけなかったのかと😆」「4つもあるとは😆」「後からRubyに足されたとかかしら?🤔」「member?使ったことないな〜😆

参考: instance method Hash#has\key? (Ruby 2.6.0)


docs.ruby-lang.orgより

⚓コレクション読み込みチェックを最初のレコードではなく全レコードでチェックするよう修正

現在、コレクションが読み込まれたかどうかは最初のレコードしかチェックしていない。特定のシナリオでafter_initializeフック経由でレコードをフェッチした場合、コレクションの最初のレコードは読み込まれていても残りのレコードが読み込まれていない場合がありうる。
データをフェッチするときの短絡を成功させるには、コレクション内の全レコードが読み込まれたことを検証する必要がある。
これにより、関連付けを参照するSTIがafter_initializeでプリロードに失敗する問題(#37730)が修正される。
同PRより大意

# activerecord/lib/active_record/associations/preloader.rb#L186
        def preloader_for(reflection, owners)
-         if owners.first.association(reflection.name).loaded?
+         if owners.all? { |o| o.association(reflection.name).loaded? }
            return AlreadyLoaded
          end
          reflection.check_preloadable!
          if reflection.options[:through]
            ThroughAssociation
          else
            Association
          end
        end

つっつきボイス:「たしかにlazy loadされているとこうなる😆」「firstだったのをall?にして、ちゃんと最後まで読み出せるかどうかを確認するように修正したと」「short circuitはとりあえず『短絡』としてみたんですが、どう表すのがいいのか迷い中です😅」「追加された以下のテストをちゃんと読めばfirstだと落ちるということがわかるんでしょうきっと😆」「踏んだことないエラー🤔」「STIでpolymorphic associationを使った場合のafter_initializeという組み合わせか〜」

# activerecord/test/cases/associations/eager_test.rb#629
  def test_preloading_with_has_one_through_an_sti_with_after_initialize
    author_a = Author.create!(name: "A")
    author_b = Author.create!(name: "B")
    post_a = StiPost.create!(author: author_a, title: "TITLE", body: "BODY")
    post_b = SpecialPost.create!(author: author_b, title: "TITLE", body: "BODY")
    comment_a = SpecialComment.create!(post: post_a, body: "TEST")
    comment_b = SpecialComment.create!(post: post_b, body: "TEST")
    reset_callbacks(StiPost, :initialize) do
      StiPost.after_initialize { author }
      comments = SpecialComment.where(id: [comment_a.id, comment_b.id]).includes(:author).to_a
      comments_with_author = comments.map { |c| [c.id, c.author.try(:id)] }
      assert_equal comments_with_author.size, comments_with_author.map(&:second).compact.size
    end
  end

#37730 issue↓を見る方が早そう」「見事歯抜けに🦷」「ステップ実行とかやってみたら原因追えるかも🤔


#37730より

⚓初期化時に訳文をeager loadingするよう修正

訳文読み込みによる初期レスポンスの速度低下を回避するため、アプリ初期化中に訳文をeager loadingする。
同PRより大意

# activesupport/lib/active_support/i18n_railtie.rb#L13
    config.i18n.load_path = []
    config.i18n.fallbacks = ActiveSupport::OrderedOptions.new

+   config.eager_load_namespaces << I18n
+

つっつきボイス:「i18nの訳文を後から読み込むと一発目の表示が遅くなるので修正したと」「たしかに〜」「最初に読み込むと今度はその分Railsサーバーの起動が遅くなりますけど😆」「どっちにするか選べるようにして欲しい人いそう」「config.eager_load_namespacesにデフォルトでi18nを加えるようになったのね」

config.eager_load_namespacesは前からあったようです↓(Rails 4.0.2以降)。

「railtieに入ったということはサーバーの起動速度にちょい影響しそうではある」「i18nはヨーロッパの人なら確実に使うでしょうし」「この間正式になったGoogleのCloud Runみたいな環境で動かすRailsの起動速度を極力速くしたい人にとっては、要らんお世話なのかもしれませんけどっ😆」「なるほど」「逆にGitHubみたいに既にサーバーが立ち上がった状態で普通にリクエストを受けるシステムなら事前にi18nを読み込んでおいてくれる方がレスポンス速度が安定してうれしいでしょうし😋」「こっちの方が本来のRailsのユースケースなので、今回の変更はごもっともという感じ☺

⚓implicit_order_columnに応じて主キーで第2ソート

# activerecord/lib/active_record/relation/finder_methods.rb#L561
      def ordered_relation
        if order_values.empty? && (implicit_order_column || primary_key)
-         order(arel_attribute(implicit_order_column || primary_key).asc)
+         if implicit_order_column && primary_key && implicit_order_column != primary_key
+           order(arel_attribute(implicit_order_column).asc, arel_attribute(primary_key).asc)
+         else
+           order(arel_attribute(implicit_order_column || primary_key).asc)
+         end
        else
          self
        end
      end

#34480のコメントにあったように、implicit_order_columnの現在の実装では、中で値が重複している場合に同じ順序でオブジェクトが返されるとは限らない。
このパッチは(implicit_order_columnとして設定されていない場合は)主キーを第2ソートとして追加することで出力の順序が一貫するようになった。
同PRより大意


つっつきボイス:「ここからは新しめの改修です」「むしろ#34480で足されていた元の機能↓の方が気になりますね: 自分は昔からこの機能があればいいのにって思ってましたし」「Rails 6で入ってたんですね😳

暗黙のorder用カラムを指定可能に
ソートのorderを明示せずにfirstlastなどのorderありfinder系メソッドを呼ぶと、Active Recordは主キーでソートする。主キーがオートインクリメントの整数値でない場合(UUIDなど)、これによって予想外の振る舞いが生じる可能性がある。今回の変更では、firstlastの結果が予測可能になるように、それらの暗黙のorderで使われるカラムをオーバーライドできるようになった。
#34480のActive Record Changelogより

class Project < ActiveRecord::Base
  self.implicit_order_column = "created_at"
end

後で調べると、6.0リリースノートでも#34480についてはごく簡単にしか触れられていませんでした↓。

⚓6.0のimplicit_order_columnカスタマイズ機能の使い所

「こういうふうに、ORDER BYを付けなかった場合にデフォルトで付けてくれる機能がある方が、世の中の人はおおむね幸せになれる😋」「なれます〜😋」「前はなかったのもびっくりですね😳

「ただしこの機能はケースバイケースでもあるんですよ: ORDER BYが付くとクエリが激重になることがあるから😇」「インデックスがらみとか?」「というより、RDBMSにとってはORDER BYが付かない方が見つけた順に結果を返せるから総じて速くなります」「そうそうっ」「ソートは重たい操作なので、ORDER BYを付けるとクエリが最初に返り始めるまでの時間が長くなりますし」「でかいテーブルが要注意なんですね😳

「デフォルトでORDER BYを付けるというのは環境によっては耐え難いほどクエリが遅くなることもあるので😎」「なるほど、だから機能を使うかどうかを選べるようになってるんですね」「新しいプロジェクトだったらあらかじめApplicationRecordで指定しちゃう手もありますし☺」「外すかどうかはデータが育ってから考えると」「そうそう、データが育っちゃった後でこの機能をいきなり使うと遅くて死ぬ可能性あります😇」「後からこの機能を使うのはコワい💀


「で元の#37626は何が修正されたんでしたっけ😆」「以下のAPIドキュメント↓を見ると、結果のソート順が主キーで確定するなら主キーでサブソートされるということみたい」「プルリクタイトルの『Additionally order by primary key if implicit_order_column is not uniq』とプルリクの内容が何だか微妙に食い違ってる気がする…😅」「Railsに限らないと思うんですけど、プルリクのタイトルってめちゃ走り書きなことがあって、これまでも先週の改修でちょくちょくダマされました😇」「🤣」「前はnon-uniqueなカラムを使うとソート順が確定しないことがあるという記述が消されてる」「created_atみたいにuniqueが保証されないカラムも指定できるようになった?」「意図がイマイチ汲み取りきれない😅

# activerecord/lib/active_record/model_schema.rb#L116
    # Sets the column to sort records by when no explicit order clause is used
    # during an ordered finder call. Useful when the primary key is not an
-   # auto-incrementing integer, for example when it's a UUID. Note that using
-   # a non-unique column can result in non-deterministic results.
+   # auto-incrementing integer, for example when it's a UUID. Records are subsorted
+   # by the primary key if it exists to ensure deterministic results.

「でもimplicit_order_columnがカスタマイズできるということがわかったのはうれしい😂」「いいこと知った😋」「新しいRailsプロジェクトなら最初から入れといてもいいぐらい😋」「付けてないことによる事故の方が多かったりしますし🚦」「特に、MySQLはオートインクリメントされたカラムを割とその順序で返してくれるけどPostgreSQLはそうでなかったりしますし」「そうそうっ😤」「でもPostgreSQLの方がSQLとして本来の姿なんですよね😆」「ORDERを指定しないところに順序なんかないっ😆

以下の記事によるとimplicit_order_columnではカラムを1つしか指定できないそうです。

参考: Rails6 のちょい足しな新機能を試す71(implict_order_column 編) - Qiita

⚓ConnectionAdapters::Resolverを削除

# activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb#L1042
      def establish_connection(config, pool_key = :default)
-       resolver = Resolver.new(Base.configurations)
-       pool_config = resolver.resolve_pool_config(config)
+       pool_config = resolve_pool_config(config)
        db_config = pool_config.db_config
# activerecord/lib/active_record/connection_handling.rb#L251
    private
      def resolve_config_for_connection(config_or_env)
        raise "Anonymous class is not allowed." unless name
        config_or_env ||= DEFAULT_ENV.call.to_sym
        pool_name = primary_class? ? "primary" : name
        self.connection_specification_name = pool_name

-       resolver = ConnectionAdapters::Resolver.new(Base.configurations)
-
-       db_config = resolver.resolve(config_or_env, pool_name)
+       db_config = Base.configurations.resolve(config_or_env, pool_name)
        db_config.configuration_hash[:name] = pool_name
        db_config
      end

つっつきボイス:「ロジックが重複していたので片方を削除したのね☺」「リファクタリング」「お引越し🚛

ConnectionAdapters::ResolverDatabaseConfiguratonsという2つのオブジェクトに同じロジックが多数実装されている。一方はconfig/database.ymlで定義された設定に用い、もう一方はestablish_connectionなどのメソッドにStringHashで生の設定を渡すのに用いる。
時間とともに2つのロジックが少し乖離してきたので、コードの複雑さを軽減して一貫性を高めるためにResolverを削除して主なメソッドをDatabaseConfigurations#resolveに置き換え、resolve_pool_configConnectionPoolに移動するなどした。
同PRより大意

「ついでにdatabase.ymlに関するAPIドキュメントがちょっと足されているみたい↓」「お〜😋」「こうやってコメントがちゃんと書かれるとありがたい🙏

# activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb#L1149
        # Returns an instance of PoolConfig for a given adapter.
        # Accepts a hash one layer deep that contains all connection information.
        #
        # == Example
        #
        #   config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
        #   pool_config = Base.configurations.resolve_pool_config(:production)
        #   pool_config.db_config.configuration_hash
        #   # => { host: "localhost", database: "foo", adapter: "sqlite3" }
        #

⚓Rails

⚓rubocop-rails 2.4.0がリリース


docs.rubocop.orgより


つっつきボイス:「rubocop-railsは本家rubocopとバージョンの歩調を合わせないことにしたという話がありましたね」「そうそう、本家に構わずガンガン上げていくぜと💪

新機能:

  • Rails/ApplicationController copとRails/ApplicationMailer copを追加
  • Rails/RakeEnvironment copを追加
  • Rails/SafeNavigationWithBlankを追加

⚓Rails機能のエンジンへの切り出しにRuboCopを活用


同記事より


つっつきボイス:「Railsエンジンの切り離し?」「上の図↑はエンジンのデータを自由に読み書きできちゃまずいよねという話みたい」「ちゃんとしたインターフェイスを経由してやりとりしないと😆

isolate_namespace↓ってあったそういえば」「おぉ?」「これを使うことで名前空間が分かれる: エンジンを書くときはこうしないとRails側とかぶっちゃうとか何とかだったと思う」「きっとRailsガイドにある気がします😋」「マウンタブルエンジンについては丁寧なガイドがあるはず☺」「密結合したエンジンなんてエンジンじゃないし🤣」「🤣

参考: 2.1.1 重要なファイル — Rails エンジン入門 - Rails ガイド

Engineクラスの定義に含まれるisolate_namespaceの行を変更・削除しないことを強く推奨します。この行が変更されると、生成されたエンジン内のクラスがアプリケーションと衝突する可能性があります。
Railsガイドより

「Railsアプリが太ってきたときにどう分割するかという方法はいくつかあるんですが、そのひとつが機能のエンジン化ですね」「おぉ」「複数のRailsアプリにする方法とは別に、1つのRailsアプリの中でエンジンに切り出すという方法☺」「記事を書いた人は、その辺の作業を支援するためにcopをいくつか作ったみたいです↓」「危ないときに立ち会ってくれるcop👮」「マウンタブルエンジンとかそんなにしょっちゅうは書かないから😆、そういうのがあるとよさそう」

記事では、他の切り離し方法として「リードオンリーActive Record」や「明示的に定義された依存関係だけをテストで読み込む」「Active Recordのsaveなどにフックをかける」なども紹介されています。

⚓Bundlerを健全に使うには

# 同記事より
# 正確なバージョン指定
gem 'nokogiri', '1.0.3'
gem 'webrat', '0.3.1'

# ペシミスティックなバージョン指定
gem 'nokogiri', '~> 1.0.3'
gem 'webrat', '~> 0.3.1'

# バージョン指定なし
gem 'nokogiri'
gem 'webrat'

つっつきボイス:「2012年という古い古い記事ですが、先週出した翻訳記事↓で言及されていたので」「gemのバージョンをきっちり指定する場合とかペシミスティックに指定する場合とかについての話ね☺

Rails 6+Webpacker開発環境をJS強者ががっつりセットアップしてみた(翻訳)

「ところで、gemとかのバージョンをピンポイントに指定するのって、それはそれでたまにリスクがあったりするんですよ」「マジですか?!😳」「ピンポイントに指定したバージョンが何かのミスで消えちゃったりしたら当然失敗するようになるので😭: もっとも最近はそういうことはあまりありませんけど☺

「上の翻訳記事の方ではJSのモジュールのバージョンを全部ピンポイントにしないと気が済まないという感じでした」「Rubyもそうですけどね☺」「ただGemfile.lockにはピンポイントで書かれてそちらが正ですけど、Gemfileの方にはこのぐらいのバージョンなら動くよねという期待を込めてバージョンを書いたりしますし😆」「最近もGCPのgemがめちゃ古くないと動かないケースあった…😇

⚓Capistranoを使う(Ruby Weeklyより)

# 同記事より
# lib/capistrano/tasks/login.rake

desc "Login into a server based on the deployment stage"
task :login do
  on roles(:app) do |server|
    user = fetch(:user)
    path = fetch(:deploy_to)

    uri = [
      user,
      user && ‘@’,
      server.hostname,
      server.port && “:”,
      server.port
    ].compact.join
  end
end

つっつきボイス:「こっちは新しくて普通にCapistranoやった記事ですが、Capistranoの新しい記事というのが珍しそうだったので」「そうそう、Capistranoの記事って意外に少ない😆」「記事書いた人もきっと同じ思いだったに違いない🤣」「『ないなら自分で書くわい』みたいな😆」「これは翻訳したいです😋

「Capistranoのコードを書く人は、どちらかというとサーバーサイドのインフラエンジニアですよね: 書き方はいろいろだけど、いったん書き方が枯れて安定してしまえばそれで全然いいという感じですし☺」「インフラエンジニアがCapistranoコードでテンプレートを一度がっつり書いておけば、後はみんなでそれを使い回す😆」「秘伝のタレ的なconfigがあったりしますね😆」「実際Capistrano自体もそんなに変わってませんし☺


capistranorb.comより


前編は以上です。

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

週刊Railsウォッチ(20191125)Ruby 3.0は2020年12月にリリース決定、Rails 5.2.4rc2とRuby 2.7.0-preview3がリリースほか

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

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

Rails公式ニュース

Ruby Weekly


週刊Railsウォッチ(20191204後編)Rubyコードをトランスパイルするruby-next、Cloud Run正式リリース、2019年Web年鑑レポート、V言語ほか

$
0
0

こんにちは、hachi8833です。1日遅れの後編です🙇

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

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

第16回目公開つっつき会は、明日12月05日(木)19:30〜にBPS会議スペースにて開催されます。参加者が増えましたので参加枠を少し増やしました。

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

⚓Ruby

⚓ruby-next: Rubyコードを古いバージョンや別実装にトランスパイル(Ruby Weeklyより)


つっつきボイス:「11月にナッシュビルで行われたRubyConf 2019でEvil Martiansが発表したスライドです」「Ruby 2.7で入る機能も紹介されてるっぽい」「ruby-nextはgem」「あ〜こうやって変換する↓ということか😳」「ポリフィルとトランスパイルの2段構えみたい🤔」「まだまだ実験段階っぽいけど面白〜い😍」「使うかどうかわからんけどっ🤣


babeljs.ioより

「JSのBabel的なヤツですね」「政治的事情とかで新しいRubyが使えないときなんかに使うとか😆」「yield_selfとかtallyを早く使いたいときとか😆」「使おうと思ったらRubyが古かったとかあるある😆

「さすがEvil Martians、Rabbitのウサギとカメが悪い火星人と逃げる人間になってる🤣

Rails 6のDocker開発環境構築をEvil Martians流にやってみた

ruby-nextのリポジトリを見た感じでは、ポリフィルだけならすぐ使えるらしく、トランスパイラの方はインストールが少々面倒そうです。

参考: Polyfillって何? · ECMAScriptとは何か?

後で動画も見つけました。

⚓外部サービスのテストをアダプタ化する(Hacklinesより)

# 同記事より
 module SmsAdapters
   class InMemory
     cattr_accessor :messages
     self.messages = []
+
+    def self.clear_messages
+      self.messages = []
+    end

     def send_message(message)
       self.class.messages << OpenStruct.new(message)
     end
   end
 end

つっつきボイス:「こちらもThoughtbotの記事ですが新しいです」「この記事でやってるのは、いわゆる…何だっけ😆」「モックでもスタブでもなくって、ええと😅」「たしか名前があるはず」「思い出した、fakeだ↓」「なんてググりづらい用語😭」「Fake Objectなんて言ったりしますね☺

参考: xUnit Test PatternsのTest Doubleパターン(Mock、Stub、Fake、Dummy等の定義) - 千里霧中

  • Test Double Pattern
    • Test Stub
    • Test Spy
    • Mock Object
    • Fake Object
    • (Dummy Object)
      goyoki.hatenablog.comより

これらの定義は以下のXUnitPatterns.comが本家なんですね😳


xunitpatterns.comより

⚓その他Ruby


つっつきボイス:「下はruby-jp Slackで見かけたRubyをハックするイベントです」「ささださんいるから会場はやはりクックパッド🍳」「こういうイベントをずっと継続しているのはエライ!」

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

⚓Clund Runが正式リリース(Publickeyより)


つっつきボイス:「あれ今までベータだったんだ😳」「割と大きいニュースかなと思いましたがどうでしょう?」「まあCloud Runは、みんなが欲しかったのはこれなんじゃね?みたいなところはありますよね☺」「AWS Lambdaとも互換取れるんでしたっけ?」「Cloud Runについては前も扱いましたけど(ウォッチ20190508)、Cloud Runには完全にコンテナベースのものと、ファンクションだけ動かすものと2種類ありますよね」「そうでした!」「こういうふうに料金体系もレスポンス速度も違う↓ので、どっちを使うかは考えどころですね☺」「なるほど」


cloud.google.comより

「GKEの方はコンテナで割と何でもまるっと動かせる感じで、GKEじゃない方はLambdaに似た感じ」「ruby-jpでもCloud RunはRailsを動かせそうだという書き込みを見かけました」「GKE版の方ならたぶんやれるでしょうね」「まあCloud Runはいいんじゃないでしょうか☺


「話変わりますけど、ちょっと前にBigQueryをえらく推してる記事をはてブで見かけたんですが今ちょっと見つからない…」「BigQueryはGCPとか存在しない頃からずっとGoogleにあるサービスですけど🤣」「あ、そうでしたか😅」「なので自分的には今更感ありますね☺

後で見つけました↓。

参考: 近年のデータ分析基盤構築における失敗はBigQueryを採用しなかったことに全て起因している - データエンジニアの酩酊日記

「BigQueryはもともと超絶ムキムキなサービスですけど、ただ値段も結構お高いので料金体系をちゃんと気にかけて使わないと一瞬で破産します💸」「そういえばこんな記事もありましたね↓」

参考: BigQueryで150万円溶かした人の顔 - Qiita

⚓Istio使ってみた話


istio.ioより


つっつきボイス:「以前のウォッチで盛り上がったIstio(ウォッチ20190212)を運用してみた話のスライドで、あまりIstioの記事を見かけてなかったのでピックアップしてみました」「マイクロサービスやるなら欲しい機能がひととおり入っているものが、Istioとかでやっと出てきたという感じですね☺」「『便利だが運用負荷は思い』と」「というよりIstioみたいな機能がないと障害対応とかまったく無理😇」「たしかに〜」

「マイクロサービスのインフラを自前で持とうとするともの凄く大変なので、AWSのApp MeshとかGoogleのStackdriverみたいなサービスがもてはやされるわけです😆」「なるほど」「まあどれを使ってもいいんですけど、こういうのを自前でやる意義がどのぐらいあるのかと思いますし🤣」「自前はつらそう😭」「プライベートクラウドを自前でスクラッチから構築するのと変わらないぐらい大変ですよ」

参考: AWS App Mesh(マイクロサービスをモニタリングおよびコントロールする)| AWS
参考: Stackdriver - ハイブリッド モニタリング  |  Stackdriver  |  Google Cloud

「ただクラウドにロックインされたくないとか、1社のクラウドだけに依存するわけにいかない事情がある場合は、マルチクラウドにするとかIstioみたいな選択肢も必要になるでしょうね☺」「相当でかいシステムになりそう…」「でかくなくてもやれますけど、どうしても自前でホスティングしないといけないようなユースケースではそうなりますね」

⚓RIPE NCCのIPv4が底をついた


つっつきボイス:「ああIPv4が底をついた話😆」「今回黄色のエリア(RIPE NCC)で空になったそうです」「アジアのAPNICは最初に底ついてるし、今IPv4が残っているのはラテンアメリカとアフリカぐらいなんじゃ?」「お〜」「今年の夏に書いたこの記事↓でもそのあたりを説明していますけど、APNICはとっくに底をついているのでRIPE NCCとかARINからIPv4を割り当ててもらって食いつないでたのが、今回ついにRIPE NCCのもなくなったと「何という綱渡り😳」「まあ以前からそうなので今更感ありますけど😆

IPアドレスから地域特定するGeoIP系技術について調べてみた(追記あり)

参考: RIPE NCC - Wikipedia

⚓Lambda API: サーバーレスアプリ向け軽量Webフレームワーク


同リポジトリより

// Require the framework and instantiate it
const api = require('lambda-api')()

// Define a route
api.get('/status', async (req,res) => {
  return { status: 'ok' }
})

// Declare your Lambda handler
exports.handler = async (event, context) => {
  // Run the request
  return await api.run(event, context)
}

つっつきボイス:「まだ中見てませんがzero dependencyを謳ってるみたいです」「普通にJSでルーティング書いて動かしてる感だけど、zero dependencyはどうやらライブラリに依存しないということのようだ」「おぉ」「普通にやってるとNode.jsのモジュールとかにめちゃめちゃ依存するから、そういうのをなしで文字どおりライトウェイトでやりたいということでしょうね☺」「なるほど!」

「Express.jsだと依存が30個もあるけどこれなら依存ゼロでやれると: package.jsondevDependencies:はさすがに多少あるけど"dependencies": {}だぜとアピールしてる」「開発中は多少使うけど、ランタイムは依存ゼロなんですね😋」「README長すぎて読みきれないけど😆

「うんとちょろいものだったらこのlambda-apiでやれそう🤔」「たしかに〜」「実際Node.jsとかのdependencyっていろいろめんどくさいですし😩」「zero dependencyの何がうれしいって、他のライブラリのコードを気にしなくてよくなることですね❤」「そうそうっ😤」「他のライブラリのコードが更新されて動かなくなるみたいな心配がなくなるのはありがたいっすね😋

「依存性といえば、Go言語のコアなprivateコードにアクセスできなくなって大変だったという話↓を小耳に挟みましたね」「お気の毒すぎる😭」「Rubyで言えば標準のgemが消えるレベルの話😇」「でもNode.js系だと割とありがちなので、そういう世界から脱出するにはいいかも☺

参考: Today I Learned — Fix: go get private repository return error reading sum.golang.org/lookup … 410 gone

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

⚓2019年Web年鑑レポート


almanac.httparchive.orgより


つっつきボイス:「社内Slackでこちらのサイトを教わりました: 今年のWebの動向を振り返る的なサイトのようです」「お〜なかなか面白そうな読み物😋」「誰が書いてるんだろう?」「一人の著者ではなさそう?」「GoogleとかAkamaiとかスポンサーがついてますね」「非営利団体が運営してるっぽい」

⚓CSSで3D


developer.mozilla.orgより


つっつきボイス:「リンク先で触ると右のダイスがくりっと回ります」「rotate3d、ちょっと前に受けたマイクロソフトの試験問題に出てたな😆」「ゲームなら使うだろうけど業務コードでこういうの書きたくないし😆

「コードも書くデザイナーがこういうのを使うかも: エフェクトを作る専門職ってたしかあるんですよ」「おぉ?」「ゲームとかだとエフェクトをデザイナーが考案してエンジニアがそれを実装するみたいな役割分担をしてたりしますけど、そういうところのエンジニアならきっとこういうのを使う😆

⚓その他フロントエンド


つっつきボイス:「TechRacho的に気になりました」「前は漫画を一コマ使うのもダメだったのが少し緩和されそうな雰囲気」「まあ現実に守ってない人多すぎですけど😆」「元々デジタルデータは劣化しないから規制しないとみたいな話から始まってたところはありますね: 写真ならまだしもスクショだと一切劣化なしに配布できますし」「ですね😆」「十数年前だったらこういう話は前面に出てこなかったかも」「時代は移るというか」「まだ案を出す段階みたいですし反対派もいるから今後どうなるかはわかりませんけど☺

「美術品としての絵画なんかだと、結構なお金を払って掲載しているのにデジタルコピーされたら損害が出るから、反対が出そうですし🤔


はみだし: 「この間読んだ記事↓なんですが、サルバドール・ダリが晩年に絵が描けなくなった頃に贋作家にガンガン贋作を作らせて、それにダリが自分で署名して自作に仕立て上げちゃったんだそうです😇」「本人が署名したら『本物の偽物』になっちゃうし😆」「あかん😆

参考: 『贋作王ダリ―シュールでスキャンダラスな天才画家の真実』(アスペクト) - 著者:スタン・ラウリセンス 翻訳:楡井 浩一 - 谷川 渥による解説 | 好きな書評家、読ませる書評。ALL REVIEWS

⚓言語・ツール

⚓Vプログラミング言語


vlang.ioより

Go言語をさらに改良してRustやSwift(知らないけど)の風味も加えた塩梅です。今年8月頃にバズってたのに気づきませんでした😅


つっつきボイス:「V言語?」「最近になってやっと知ったんですが、エラー処理とかのデフォルトでイミュータブルというあたりの言語設計が個人的に突き刺さって、つい自分用にV言語のスライドとか作っちゃいましたのでそのうち社内で発表すると思います😆」「😆」「一見Goっぽい感じ」「GCやらずにRustっぽくやってるらしくてバイナリがめちゃ小さくてコンパイルが速いとかで、C++勢がチラ見してるようです」

「まだWIPだけどCのコードをVに変換できるようにするらしい😆」「DoomというゲームをCからV言語に変換できたようです」「ほんまかいな😆」「プリミティブなものを書くのにはいいのかも☺」「たしかに設計言語は目指してないっぽいです😆

「V言語でびっくりしたのは、ソースコードに[live]と書いて↓ソースを更新するとコンパイル済みバイナリにその場で反映されるというホットコードリロード↓でした」「あ〜実行中のバイナリを更新できるのね」「ちょっと不気味🥶」「その技術自体は以前から存在してますけど、それを安全にやるのはかなり難しいでしょうね〜☺」「セキュリティとか」「というよりクラッシュしかねない😆

module main

import time
import os

[live]
fn print_message() {
    println('Hello! Modify this message while the program is running.')
}

fn main() {
    for {
        print_message()
        time.sleep_ms(500)
    }
}

「V言語、もうGUIライブラリまであるとは😳」「もともとVoltというデスクトップアプリ↓を作る過程でできた言語みたいです(Voltのソースは非公開)」「JSにもVoltとかあるし紛らわしいな〜😆」「いろいろ頑張ってる感」

「きっとGo言語に疲れたから作ったんでしょう😆」「Goも10歳になってだんだん後方互換性で身動き取れなくなりつつありますね😆」「Goはもっと小さいライブラリとかサービスを書く感じなのに、でかいものを作る人が当初の予想よりずっと多かった感😆」「それはあります😆」「あんなにモジュールをたくさん使う想定とかなかったのではと😆

参考: シンプルで高速な「The V Programming Language」が爆誕 | ソフトアンテナブログ


volt-app.comより

V言語はドキュメントとかサイトとかがっつり作っていてそこが凄いと思いました。自分もGoby言語やっててそういう部分を整備するのが一番大変だなって痛感します😅

Goby: Rubyライクな言語(7)Ripperライブラリを追加した

⚓FacebookがVSCodeを採用

つっつきボイス:「リモートベースの開発も含めてVSCodeでやるみたいです」「AWSのCloud9みたいにリモートベースで開発できることの方がおそらく重要じゃないかって思いますね」「おぉ?」「何しろ開発環境の構築ってそれ自体は1円もお金になりませんから🤣」「🤣」「そういうのを抜きにして即開発に参加できるようになるのは大きい」「開発環境構築はエンジニアとしては知見がたまりますし楽しみでもありますけど、ビジネス上はコスト的に無意味💵」「😆」「だからそういう作業はとことん縮小したい」「ゼロが理想」

⚓Pythonの運営体制


つっつきボイス:「Pythonがジェダイっぽく合議制みたいな形に移行するようです」「なぜかMatzに質問してるし😆」「これまでいろんなソフトウェアが現れては消えたりしたのを見てきたんですけど、合議制でうまくいったところもあれば独裁制でうまくいったところもありますし、逆に合議制でつぶれたところもあれば独裁制でつぶれたところもあったりしますね〜☺」「結局どれもありうるという😆

⚓その他ツール

  • サイト: EditorConfig — 複数エディタで共有できる設定ファイル


editorconfig.orgより


つっつきボイス:「この間出した記事↓でeditorconfig.orgを今頃知りました」「editorconfigは前からあるんですけどね〜、微妙なんですよ😆」「やっぱり大した設定はできないんでしょうか?」「そう😆」「夢の世界を目指したのはわかるけど結局設定足りないし😆」「Rubyとか特に自由度高いからブレブレになるし😆」「最大公約数は無理か〜😆」「ないよりはマシな程度😆」「本来はエディタというよりGitHubとかのリポジトリに入れてチームの設定を共通化するのに使いたいものなんですけど、細かい設定がなさすぎて夢の世界には程遠い😆

Rails 6+Webpacker開発環境をJS強者ががっつりセットアップしてみた(翻訳)

⚓その他

⚓GitHub Actions

つっつきボイス:「GitHub Actionsは手元で動かせないと手探り感凄そう😅

以下はつっつき後のツィートです。

⚓ゼロ知識証明


つっつきボイス:「ゼロ知識証明?」「『情報の中身について公開せずに、その情報を自分が知っていることを相手に伝える』のか!」「おほ😋」「こういうのは面白いっすね〜❤

参考: ゼロ知識証明 - Wikipedia

「これと少し似た感じでいうと、XORを使ったデータ復元みたいなのも好きですね😍」「おぉ?」「以下の記事↓とかのRAID5のパリティ計算なんかがそうなんですけど、完全な情報がないのにちゃんと復元できる」「あ〜!」「どこが欠けても復元できるというのを初めて見ると、とっても不思議な気持ちになれます😆
参考: 見習い復旧屋の技術日記 RAID5 のパリティの計算方法


3309masa.blog130.fc2.comより

「そういうデータ復元が可能であることを最初に証明したのが情報理論でお馴染みのシャノンでしたっけ?」「ですね☺: 情報のビット数を冗長にすることでエラーを復元できるんですけど、冗長符号が何ビットあると何ビットまでのエラーに耐えられるかみたいな理論だったと思います」「たぶんだけどこの辺↓だったと思う」「source coding theoremっていう名前が😆」「こういうのをちゃんと学ぶとへぇ〜って思いましたね😋」「ワイ数学科だったけど群論だったし〜😆

参考: シャノンの情報源符号化定理 –情報源符号化定理の証明 - Wikipedia

⚓その他のその他

つっつきボイス:「辞書づくりにXMLフォーマットを使うのは納得😋

⚓番外

⚓はじめアルゴリズムより面白そう


つっつきボイス:「『動物のお医者さん』の数学版みたいなノリだとruby-jp Slackで見かけて、思わずAmazonでポチっちゃいました😆」(以下漫☆画太郎先生を讃える話などが延々)

あと以下のツイートも言葉での分析が的確だなと思いました😋


後編は以上です。

おたより発掘

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

週刊Railsウォッチ(20191202前編)Rails 6のimplicit_order_columnはカスタマイズ可能、rubocop-rails 2.4.0リリース、Capistrano記事ほか

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

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

Ruby Weekly

Hacklines

Hacklines

Publickey

publickey_banner_captured

週刊Railsウォッチ(20191209前編)Pumaのphased-restartとUnicornのgraceful restart、RailsのTZハックが不要になった話ほか

$
0
0

こんにちは、hachi8833です。ChainerがPyTorchに乗り換えられたそうです。


つっつきボイス:「ついさっき上のツィートが流れてきました🛶」「Chainerといえばメジャーな機械学習フレームワークだったのに」

参考: Chainer を振り返って

「中の人のブログを見るとメンテナンスモードに移行するってあるし😳」「機械学習やってる人には結構大きな変更でしょうね〜」

「Rubyで機械学習やる人は最近増えてきましたけど、RubyからPythonのライブラリを呼べるpycall.rbで有名なmrknさんがSciRubyというプロジェクト↓をやっていますね」「おぉ」「pycallはRubyからPythonライブラリ経由でPythonコードにアクセスして、Python側で作ったデータにRubyからアクセスできるようにするというものですね: 同じメモリ内でやるのでデータをメモリコピーしなくていい😋」


sciruby.comより

「mrknさんは最近だとデータ処理系のApache Arrow↓もやってたりしますね(ウォッチ20190402)」


arrow.apache.orgより


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

今回は週刊Railsウォッチ第17回公開つっつき会を元にお送りいたします。定員枠を初めて広げました。お集まりいただいた多くの皆さま、ありがとうございました!😂🙇

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

ActiveSupport::Duration#inspectの結果がおかしいのを修正

# 同PRより
(1.day / 24).inspect   #=> "0 seconds" # inspectの結果がおかしい
(1.day / 24).to_i      #=> 3600
(1.day / 24) == 1.hour #=> true
(1.hour).inspect       #=> "1 hour"
(1.hour).parts         #=> {:hours=>1}
(1.day / 24).parts     #=> {}

つっつきボイス:「Active SupportのDuration周りのバグだったようです」「ActiveSupport::Durationは、名前のとおり期間を抽象化したものですね」

    assert_equal "3600 seconds",                    (1.day / 24).inspect

「追加されたテストコード↑を見ると、1.day / 24inspectしたら3600秒になるべきだったのに0秒が返ってたのか〜」「他にもいくつか類似の修正PRがありましたので上に貼っておきました」「Durationとしては分でも秒でも同じ長さのはずなんでしょうけど、内部状態の更新あたりが微妙に失敗してたのかもしれませんね😅」「単位が絡むと面倒そう…」

# activesupport/lib/active_support/duration.rb#L377
    def inspect #:nodoc:
-     return "0 seconds" if parts.empty?
+     return "#{value} seconds" if parts.empty?

      parts.
        sort_by { |unit,  _ | PARTS.index(unit) }.
        map     { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
        to_sentence(locale: ::I18n.default_locale)
    end

「Railsでは時刻から時刻を引くとDurationになるんでしたっけ?」「そうそう、Time同士の差を取るとActiveSupport::Durationオブジェクトになりますね☺️」

Rails5: ActiveSupport::Durationでの数値へのパッチ

redirect_to.action_controllerの通知のペイロードにActionDispatch::Requestを追加した

| Key         | Value                         |
| ----------- | ----------------------------- |
| `:status`   | HTTP response code            |
| `:location` | URL to redirect to            |
| `:request`  | The `ActionDispatch::Request` |

つっつきボイス:「これはredirect_toの引数が増えたのかと思ったらinstrumentが出てきてるからログ周りの話か」「あ、なるほど」「instrumentationはログとは少し違いますが、redirect_to.action_controllerのinstrumentationでrequestも拾えるようになったということだと思います☺️」

# #L
    def redirect_to(*)
-     ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
+     ActiveSupport::Notifications.instrument("redirect_to.action_controller", request: request) do |payload|
        result = super
        payload[:status]   = response.status
        payload[:location] = response.filtered_location
        result
      end
    end

RailsのInstrumentationを「バス」で理解する

「Instrumentation(測定、計測といった意味)について簡単に説明しますね: Railsではpub/sub(パブリッシャー/サブスクライバ)的な方法でInstrumentationが使えるんですけど、上のようなパブリッシャーをサブスクライバで拾うような形になります」

「pub/subってどう説明するのがいいかな…いわゆる差し込みフリータイプの電源タップ↑では電源プラグをどこにいくつ挿してもよくて、誰も使っていなくても電気が常に流れていますけど、コンピュータなんかにおける『バス』↓という概念もそれに似ていて、pub/subはそういうバスで考えるとわかりやすいと思います」「おぉ〜」

参考: バス (コンピュータ) - Wikipedia


Wikipedia-jaより

「Instrumentationではパブリッシャー(pub)がこういうバス的なところにいます: バスに誰もいなくても大勢いいてもお構いなしで」「そしてそのバス的なところにサブスクライバ(sub)が登録されていればそいつらがpubからメッセージを拾える、というような形になります」「ふむふむ」「おおむねそんな感じだったと思います😆」

「InstrumentationはRailsのログ出力にとても良く使われる仕組みですね: いろんなレベルのログを片っ端から出力すると遅くなるので、ログレベルに応じて自分の欲しいものだけをサブスクライブすることで、負荷を上げずにログを柔軟に取れるようにしているわけです」「Railsフレームワークのコードでinstrumentって出てきたらそこがログに出せるポイントになっているということです」


はみ出し:「そういえばInstrumentationっていう用語はWMI↓で初めて知ったんですけど、長ったらしくて収まりが悪いせいか未だに日本語にもカタカナにもなってなくて、Railsガイドでも英語表記で統一しました」「pub/subの方がしっくりくるな〜😆」「instrumentって楽器?」「元々は機材とか装置みたいな意味なんですけど楽器の意味もあって、musical instrumentと書けば確実に楽器ですね」

参考: Windows Management Instrumentation - Wikipedia

service_urlが非推奨化、今後はurl

Variant#service_urlPreview#service_urlを非推奨化に。
今後はBlobと一貫するurlを使う
同PRより大意


つっつきボイス:「Active Storageの改修だそうです」「VariantPreviewってのがあるのね😳」「Active Storageをまだ使ってない身としてはそうですか〜としか言えないし😆」「差分を見た感じではservice_が冗長だったってことなんでしょうね☺️」「使う人が増える前にメソッド名直しちゃえという感じなのかも☺️」「service_以外のurlがないならurlだけでええやろってことかな😆」

「Active StorageはRails 6でrails newするなら使ってもいいんじゃないかと思いますね: 前のRailsから移行してActive Storageにも移行するのは大変そうですけど😆」

URIかURLか

「めちゃ細かいこと言うとurlでいいんだろうかと🤣」「uriじゃないのかと😆」

「もともとUniform Resource Locatorの略でURLですけど、もう随分前に『本来やりたかったのはlocaltorみたいな物理的な場所ではなく識別子(identifier)だよな』ってURI(Uniform Resource Identifier)を使うべきという空気になったのに、未だにURLが広く使われているという🤣」「URL消えませんね🤣」「もう滅びない感🤣」

「ただRFCではURLもURIも一応両方あると思います☺️」「そうでしたか😳」「でURIがURLを包含していた気がする…この辺かな↓」「URIの方が意味が広いんですね」「URLは//<user>:<password>@<host>:<port>/<url-path>↓みたいなよく見かける形」

 //<user>:<password>@<host>:<port>/<url-path>

「でURIはtel:+1-816-555-1212みたいなのも含む↓」「これはURIだけどURLではないと」「気になる人はこの辺のRFCを読んでみるといいと思います☺️ 」

    The following example URIs illustrate several URI schemes and variations in their common syntax components:
      ftp://ftp.is.co.za/rfc/rfc1808.txt
      http://www.ietf.org/rfc/rfc2396.txt
      ldap://[2001:db8::7]/c=GB?objectClass?one
      mailto:John.Doe@example.com
      news:comp.infosystems.www.servers.unix
      tel:+1-816-555-1212
      telnet://192.0.2.16:80/
      urn:oasis:names:specification:docbook:dtd:xml:4.1.2

参考: Uniform Resource Identifier (URI): 一般的構文 — RFC3986 日本語訳の複製

URI は、それが位置指定子か、名前か、あるいはその両方かという点において、更に分類できる。 “Uniform Resource Locator” (URL) という用語は、リソースを識別するのに加えて、その主なアクセスメカニズム (例えば、そのネットワーク上の “位置”) を記述する事によってリソースの場所を見つける方法を提供するような、URI の部分集合を指す。 “Uniform Resource Name” (URN) という用語は、例えそのリソースが存在しなくなったり、あるいは利用不可能になっても全体において一意で永続的である事が要求される “urn” スキーム [RFC2141] の下での両方の URI、また名前の特性を持つあらゆる他の URI を参照するために歴史的に使用されている。
triple-underscore.github.ioより

HashWithIndifferentAccess#convert_valueのアロケーションを削減

productionのプロファイリングでActiveSupport::HashWithIndifferentAccess#convert_valueがかなり高いようなので調べたところ、妙なbindingアクセスを見つけた。
履歴を調べたところ、#36758@64a4301あたりが由来らしい。
元々、キーワード引数をホットスポットで用いることでアロケーションを削減するのが狙いだったが、forがキーワードなのでbinding.local_variable_getでしのいでいた。
しかしbindingは呼び出しのたびに新しくBindingをアロケートするのでかなり遅くなる。
convert_valueはprivateメソッドなので、引数をリネームするだけでこの辺りの問題を回避できると思われる。
同PRより大意

# activesupport/lib/active_support/hash_with_indifferent_access.rb#L365
    private
    ...
-     def convert_value(value, for: nil) # :doc:
-       conversion = binding.local_variable_get(:for)
-
+     def convert_value(value, conversion: nil) # :doc:
        if value.is_a? Hash
          if conversion == :to_hash
            value.to_hash
          else
            value.nested_under_indifferent_access
          end
        elsif value.is_a?(Array)
          if conversion != :assignment || value.frozen?
            value = value.dup
          end
-         value.map! { |e| convert_value(e, for: conversion) }
+         value.map! { |e| convert_value(e, conversion: conversion) }
        else
          value
        end
      end

参考: ActiveSupport::HashWithIndifferentAccess


つっつきボイス:「お、HashWithIndifferentAccess出たな😆」「😆」「便利なんですけど、とにかく名前長い😆: IDEなら補完が効くからまあいいんですけど」

ここでいったん録音が途切れました🙇

「…binding.local_variable_getはいわゆるリフレクション的なアプローチで、本来のオブジェクト指向的なアクセスではなく、今動いているRubyのプロセスからアクセス権限とか全無視で無理やりぶっこ抜けます😆」「😆」「こういうRubyインターナルなものはやってて楽しいんですけど、やりすぎるといろいろぶっ壊れます😇」

参考: リフレクション (情報工学) - Wikipedia

「改修の方は今時間なくてちゃんと見てませんが、binding.local_variable_getみたいな方法で速くしていたのが裏目に出て遅くなるのはよくあるパターンかも☺️」

ネストしたconfig_forハッシュへの非シンボルアクセス(非推奨)を削除

# railties/lib/rails/application.rb#L222
    def config_for(name, env: Rails.env)
      if name.is_a?(Pathname)
        yaml = name
      else
        yaml = Pathname.new("#{paths["config"].existent.first}/#{name}.yml")
      end

      if yaml.exist?
        require "erb"
-       config = YAML.load(ERB.new(yaml.read).result) || {}
-       config = (config["shared"] || {}).merge(config[env] || {})
+       config = YAML.load(ERB.new(yaml.read).result, symbolize_names: true) || {}
+       config = (config[:shared] || {}).merge(config[env.to_sym] || {})

        ActiveSupport::OrderedOptions.new.tap do |options|
-         options.update(NonSymbolAccessDeprecatedHash.new(config))
+         options.update(config)
        end
      else
        raise "Could not load configuration. No such file - #{yaml}"
      end
    rescue Psych::SyntaxError => e
      raise "YAML syntax error occurred while parsing #{yaml}. " \
        "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
        "Error: #{e.message}"
    end

つっつきボイス:「これはdeprecatedな機能を削除したのね」

Railsのdeprecationプロセス

「ちょっと解説すると、基本的にRailsでは何かの機能をなくすときに、いきなり消すのではなくて、まずdeprecation warningをログに出力するコードを追加して、次のメジャーバージョンアップで実際に消すという形で進めていきます」「みんなはそれを見て、消される前に対応することになりますね」

connected_toのキーワード引数databaseを非推奨化(代替なし)

connected_toのキーワード引数databaseはシャーディングのキーとして想定されていない場合に大量のバグが発生する。databaseキーワード引数が使われているテストとユースケースを検討した結果、これは削除すべきという結論に達した。
今後シャーディングをサポートする計画はあるが、それまでdatabaseキーワード引数は意味なしとなる。コネクションを新たに作成するアプリではestablish_connectionconnects_toを利用できる。
connected_todatabaseキーワード引数をリクエスト中または使い捨てでないコネクションで使うと、databaseというキーでバグの原因となる。
同PRより大意

# activerecord/lib/active_record/connection_handling.rb#L98
    def connected_to(database: nil, role: nil, prevent_writes: false, &blk)
+     if database
+       ActiveSupport::Deprecation.warn("The database key in `connected_to` is deprecated. It will be removed in Rails 6.2.0 without replacement.")
+     end
+
      if database && role
        raise ArgumentError, "connected_to can only accept a `database` or a `role` argument, but not both arguments."
      elsif database
        if database.is_a?(Hash)
          role, database = database.first
          role = role.to_sym
        end
        db_config = resolve_config_for_connection(database)
        handler = lookup_connection_handler(role)
        handler.establish_connection(db_config)
        with_handler(role, &blk)
      elsif role
        if role == writing_role
          with_handler(role.to_sym) do
            connection_handler.while_preventing_writes(prevent_writes, &blk)
          end
        else
          with_handler(role.to_sym, &blk)
        end
      else
        raise ArgumentError, "must provide a `database` or a `role`."
      end
    end

つっつきボイス:「シャーディングでうまくいかないことがあったみたいです」「a lot of bugsですか😆」

「ちょっと説明すると、Rails 6ではマルチプルデータベース機能が入ったんですけど、たとえばマスターとレプリカがある場合にconnected_toを使ってそのブロックの中でだけ一時的に接続先を変更することができるようになっていて、そこがシャーディングで不具合が起きてたということですね☺️」

「シャーディングをどのレイヤでやっているかにもよりますね: 最近のPostgreSQLだとデータベースレベルでシャーディングができたりしますし」「Railsは6.1でシャーディングをサポートするつもりらしいのでそのときに考えるということのようです」

「Railsでシャーディングをやりたいことって結構あるんでしょうか?」「それはもうめちゃありますよ: Railsでやるかどうかは別として、シャーディングしないとやっていけないようなユースケースはいろいろあります☺️」「なるほど」「まあシャーディングをgemでやるのかRailsの機能に入れるのかというのは議論の余地がありそうですけど、ユーザーがどれだけいるかわからないようなAction MailboxもRailsに機能として入ってくるぐらいなので、たぶんシャーディングもRailsに入るんでしょうね☺️」

シャーディングについて

「シャーディング(sharding)はデータベースでは水平分割とも言われる手法ですね: 雑に説明すると、たとえばIDが奇数のデータベースとIDが偶数のデータベースを用意しておくと、IDで検索したときにきれいに分散アクセスできる、みたいな感じです」「おぉ」「ソシャゲ開発の全盛期にはシャーディングがよく使われていましたけど最近あまり聞きませんね: 単に普及して当たり前になったのか、実はつらくて止めたのかは知りませんけど😆」

参考: 分割 (データベース) - Wikipedia
参考: シャーディング - Qiita

はみ出し: キーワード引数とRails

「以下は最初上のconnected_toのキーワード引数の話かな?と思ってつっつきで引用してみましたが、別の箇所でした😅」「最近話題の、Ruby 2.7でキーワード引数周りのbreaking changesに絡んだ話ですね☺️」「こちらの翻訳記事をどぞ↓」

Ruby 2.7: ハッシュからキーワード引数への自動変換が非推奨に(翻訳)

Rails

RailsのTZハックが不要になった話


つっつきボイス:「これはTwitterで見かけましたね☺️」「2本立ての記事で、TZ環境変数を設定したらRailsが速くなったけど実はRuby 2.6で解決されていたのでハック不要だったというお話」「おぉ」「これはいい話でしたね〜😋」「はてブでも結構上がってたかも」

jnchitoさんの振り返り記事

ここと次の記事で録音が途切れていました🙇。

Webpackerでやらかしがちなミス

# 同記事より
app/
  javascript/
    packs/
      application.js
      components/     # lots of files
      images/         # lots of files
      stylesheets/    # lots of files
      ...

つっつきボイス:「WebpackerのOverpackingってわかる😆」「とってもありそう😆」「詰め過ぎ💼」

シンプルなルール: Webpackerのpacksディレクトリのファイルのうち、対応する記述がアプリケーションのjavascript_pack_tagにないものはoverpackingである。
同記事より抜粋

Rails 6+Webpacker開発環境をJS強者ががっつりセットアップしてみた(翻訳)

Rails 6の細かな新機能をひたすら試す記事


qiita.comより


つっつきボイス:「ひとつひとつの記事は短いですけど、Rails 6のリリースノートに載っているかいないかみたいな細かい機能をひたすら試していてびっくりしました😳」「これは凄いな〜👍」「110件目って…」

今年の4月からずっとやってるんですね😳。

Pumaのphased-restart


つっつきボイス:「Pumaサーバーにphased-restartというのがあるってこれで初めて知りました」「Unicornサーバーのgraceful restartとは別なのかな?Pumaだとphased-restartをするとダウンタイムなしでコードを再読み込みできるということか」


puma.ioより
* サイト: unicorn: Rack HTTP server for fast clients and Unix

「サーバーの再起動周りは何かとややこしい😅」「日本語記事の方を見た感じではPumaのphased-restartはmaster processを使い回し、新しいworker processはサーバー起動時のmaster processからforkされるとある: これだけ見るとUnicornのgraceful restartと同じような感じ?🤔」「Unicornのgraceful restartは、たしかmaster processが死ぬんだった覚えがありますね🤔」「お〜、そうでしたっけ?」「だったはずです」「ということはPumaのphased-restartはUnicornのとは違うということなんでしょうね」

Railsサーバーの再起動について

「Unicornサーバーの再起動はあんまりいい思い出がないんですが😭、ちょっとだけこの辺の話をしますね」「はい〜😋」

「RailsのWebサーバーとして使われるUnicornやPumaは、起動するとまずmaster processが立ち上がって、外からのリクエストはたしかいったんmaster processが受けてそこからworker processをforkしてそこに振る形になります」「おぉ」

「こういうサーバーの再起動で何が問題になるかというと、上の記事にもあるように環境変数の更新なんですね😆: ご存知のとおり、Linuxでは親プロセスが死ぬとそこからforkした子プロセスも死ぬ仕組みになっているんですが、トランザクショナルな処理を実行中のworkerがいるときにmasterを殺してしまうと処理中のworkerも死んでデータが不整合になってしまうわけです😅」

「その辺を回避する仕組みがたとえばUnicornのgraceful restartで、中途半端な状態でworkerが殺されないようにいい感じに処理中のworkerを避けて落としていって、処理中のworkerがいなくなったら晴れて新しい環境に入れ替わることになってるんですが、実際には期待どおりに動かないこともあって😅」「😅」

「一般にサーバーの再起動は重たい処理なので、とてもリクエスト数の多いRailsサーバーをカジュアルに再起動するとその間サービスは止まりますし、再起動でメモリがいったん全解放されてしまうのでサービス復帰まで時間かかります」「他にも、リクエストの多いRailsサーバーを普通に再起動するとリクエストのキューが大量にたまって、復帰したけどリクエストをさばききれなくなってサーバーが繰り返し落ちるなんてこともあったりします😇」「このあたりはH2Oの作者のブログなどに詳しく載っていたと思います↓」

その他Rails

先ほど見つけたツィートです。


前編は以上です。

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

週刊Railsウォッチ(20191204後編)Rubyコードをトランスパイルするruby-next、Cloud Run正式リリース、2019年Web年鑑レポート、V言語ほか

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

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

Rails公式ニュース

Ruby Weekly

週刊Railsウォッチ(20191210後編)Ruby 2.7の変更点記事、mrubyで動くmitamae、画像系コラボレーションツールほか

$
0
0

こんにちは、hachi8833です。もういくつ寝るとRuby 2.7が出るんでしょうか。

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

今回も週刊Railsウォッチ第17回公開つっつき会を元にお送りいたします。お集まりいただいた多くの皆さま、ありがとうございました!😂🙇

⚓Ruby

⚓Ruby 2.7の変更点などなど


つっつきボイス:「ツイートの記事は、2.7で復活した機能や入りそうで入らなかった機能の話で、2つ目はjnchitoさんのQiita記事です」「Ruby 2.7でさらに速くなるのが期待できますね: Railsをちゃんと書いていれば、常に最新のRubyにするだけでRailsが速くなりますし😋」「ですね😋

「まあRubyの新機能を業務コードで率先して使うかどうかはまた別の話ですけどっ😆」「😆」「新しい機能はやっぱり楽しいんですけど、よっぽどうまくいくことがわかるまでは業務コードで使うのはためらいがある☺」「作り捨てのバッチあたりでならやってみてもいいかも😋

「キーワード引数の非シンボルキーは、例のキーワード引数の絡みで復活してましたね」「2.7で挙動が少し変わるようです↓」

# 同記事より
def hoge(*a, **k)
  p a
  p k
end
# (略)

# Ruby 2.6.2
hoge("a"=>1, b: 2)
#=> [{"a"=>1}]
#=> {:b=>2}

# Ruby 2.7
hoge("a"=>1, b: 2)
#=> []
#=> {"a"=>1, :b=>2}

Ruby 2.7: ハッシュからキーワード引数への自動変換が非推奨に(翻訳)

「Ruby 2.6で非推奨になっていた例のフリップフロップ..が実は2.6.4で復活していた!?」「え😅」「なくてもいいかもと言われていたフリップフロップ」「フリップフロップ全然使ってないしな〜解説無理😆」「どうやら意外に使われていたらしき☺

# 同記事より
10.times{|i| print i if (i==3)..(i==6)}
#=> "3456"

参考: Rubyのフリップフロップ - monamonamonad.github.io

Ruby: これはなくてもいいかも?と思う10の機能(翻訳)

「パイプライン演算子|>とメソッド参照演算子.:は既報通り2.7には入らないことになりましたね」「少なくとも2.7には入らないけど議論が進めば入るかも?🤔


「あと以下のお片付けならぬお型付けアドベントが気になりました😆」「まだそんなに埋まってないかな😆」(注: その後エントリ増えてました😋


「あとこちらもRuby 2.7や3の新機能がらみの記事です↓」「RubyConf 2019のMatzキーノートのまとめですね😋

なおこの記事では以下のRubyパターンマッチング記事をおすすめしていました↓。また「MatzによるとRubyのパターンマッチングは遅いのでパフォーマンスに注意して賢く使いましょう」という記述がありました。

参考: Introduction to Pattern Matching in Ruby | Toptal


toptal.comより

⚓Ruby 2.7のEnumerator#produceRuby Weeklyより)

# 同記事より
Enumerator.produce([0, 1]) { 
  |base_1, base_2| [base_2, base_1 + base_2]
}.take(10).map(&:first)
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
# 同記事より
# ある月から平日に該当する日を取得
Enumerator.produce {
  Date.today+rand(30)
}.detect{ |date| !date.sunday? || !date.saturday? }
# => #<Date: 2019-11-15 ((2458803j,0s,0n),+0s,2299161j)>

つっつきボイス:「ruby-jp Slackで2.7のproduceが盛り上がってました」「フィボナッチやってる😆」「もしかするとElixirあたりにも同じ名前のメソッドあったりするかな?関数型言語あたりで見たことあるような気がする🤔」「injectを別の方法でやってる感」「今日は残念ながら出席できなかったkazzさんにproduce見せたらかなり面白がってました😋」「うまく書ければはまりそう😋

参考: instance method Enumerable#inject` (Ruby 2.6.0 リファレンスマニュアル)

後で2.7.0-preview3でproduceinjectをちょっと動かしてみました。

Enumerator.produce([1, 1]) {|base_1, base_2|
  [base_2, base_1 + base_2]
}.take(10).map(&:first)
#=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

(0..7).inject([1, 1]) {|fib, i|
  fib << fib[i] + fib[i+1]
}
#=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

Ruby: `each`よりも`map`などのコレクションを積極的に使おう(社内勉強会)


追いかけボイス: Haskellのiterate関数がRubyのEnumerator#produceにちょっと似てるかもしれないと教わりました🙇

参考: The Fibonacci sequence - HaskellWiki

-- wiki.haskell.orgより
fibs = map fst $ iterate (\(a,b) -> (b,a+b)) (0,1)

⚓mitamae: mrubyでやれるitamae

# 同リポジトリより
# cat recipe.rb
include_recipe 'included'

directory '/tmp/etc'

file '/tmp/etc/hello' do
  content 'This is mitamae'
end

template '/tmp/etc/config.yml' do
  source 'config.yml.erb'
end

つっつきボイス:「この間BPSの社内Slackに貼っていただいたヤツです」「そうそう、mrubyで動くこのmitamaeをこの間知って、Rubyがなくても単独バイナリとして動かせるのがいいなと思いました👍

「itamaeだとitamaeを動かすためにRubyをインストールする必要があったりして大変です😅」「itamaeはいわゆるIaaC(Infrastructure as a Code)的なサーバーのプロビジョニングツールで、設定をRubyのコードで書くと↓実行するだけで一発でプロビジョニングできて、いい感じに差分管理もできます😋

# https://github.com/itamae-kitchen/itamae/wiki/Definitionsより
define :install_and_enable_package, version: nil do
  v = params[:version]
  package params[:name] do
    version v if v
    action :install
  end

  service params[:name] do
    action :enable
  end
end

「スクラッチのサーバーなんかだとRubyが入ってなかったりしますし、制約が厳しいOSだったりすると既存のRubyのバージョンが恐ろしく古いこともあるので、Itamaeを動かすためにRubyをどうやってインストールしようか考えるのが面倒😭」「😭」「そういうことを考えないで済むという点でこのmitamaeはいいなって思った次第です❤」「mrubyのいい使い方😋」「まだ動かしてないんですけど、READMEを見た感じではitamae sshが入ってないぐらいで後は普通のitamaeが動きそう👍」「コミットを見るとk0kubunさんがほとんどやってるみたいですね😳」「社内ではansibleが主流なんですけど😆、itamae使うならこれがいいかな〜」

追記(2019/12/10)

本記事タイトルで「mitamae gem」となっていたのは誤りでした。「mitamae」に修正しました🙇

⚓ruby-packer: Ruby CLIをシングルバイナリに


つっつきボイス:「まだ試していませんが、こちらはCRubyでシングルバイナリをコンパイルするようです」「CRubyでやったらどんだけバイナリでかくなるのやら😆」「どこまでリンクするかですよね」「やってみないとわかりませんが20MBぐらいになるのかな?🤔

後で雑にやってみました。63行程度のRubyコードをrubyc subtract.rb -o subtractしたところ怒涛のようにCのコンパイルが走って11MBになりました。Go言語で書くのとさほど変わらないぐらいのバイナリサイズという印象です。試していませんがRailsも一応コンパイルできるようです。

-rwxr-xr-x 1 hachi8833 staff  11M 12 10 12:20 subtract
-rwxr-xr-x 1 hachi8833 staff 1.1K 12 20  2018 subtract.rb

⚓シングルバイナリについて

「Rubyのシングルバイナリか〜、気持ちわかる☺」「READMEによると、この辺のライブラリがビルドに必要っぽい↓」

  • gdbm
  • libffi
  • ncurses
  • openssl
  • readline
  • yaml
  • zlib

「Linuxの内部に関わる話ですけど、このruby-packerはいわゆるライブラリに依存しないシングルバイナリをビルドできるというもののようですね: Rubyに限らず一般にプログラムはOSに入っているreadlineやらlibxmlといったライブラリを動的リンクして使うことがよくあります」「ふむふむ」「そういう動的にリンクされるライブラリは、Macだと拡張子が.dylib、Linuxだと.so、Windowsだと.dllになりますが、やってることは同じですね☺

「そうしたライブラリに依存するプログラムは、ライブラリのバージョンが変わったりライブラリがなくなったりすると不具合が出たり動かなくなったりするので、それらを1個のバイナリに全部焼き込んで依存しないようにしたシングルバイナリという形態のプログラムには一定の需要があります🧐」「おぉ」「その分バイナリのサイズはでかくなりますけど、その代わり同じOSならライブラリ構成とかが全然違っていても入れるだけで動かせます👍

⚓BusyBoxは便利

「この手のシングルバイナリで有名なのはBusyBoxというヤツ↓」「これ知らなかった😳」「BusyBoxは、lsとかsshみたいにサーバー管理に必要そうなLinuxコマンドとかがひととおり何でも入っているシングルバイナリのツールで、面白いのはコマンドのシンボリックリンクをBusyBoxバイナリにリンクしておけば呼ばれ方に応じて振る舞いが変わるところ」「こんないいものがあったなんて😂

参考: BusyBox - Wikipedia


busybox.netより

「gccすら入れられないような組み込み環境なんかでもBusyBoxさえ持ち込めば何とかなったりしますし、対応しているCPUアーキテクチャもやたら多くて、なかなか楽しい十徳ナイフ的なツールです🥰


はみだし:「もともと以下の記事↓で紹介したdipというツールがRubyなしでも動くシングルバイナリも提供されていて、どうやってそのシングルバイナリをビルドしているのか知りたくて追っているうちにruby-packerを見つけました: ただdipではruby-packerを使っているのかどうかよくわからなくて😅」「dipのリポジトリを今ざざっと見た限りではわかんないですね〜😆

docker-composeを便利にするツール「dip」を使ってみた

追記(2019/12/11)

情報ありがとうございます😂!TravisCIでruby-packerによるビルドをやってるようですね。

⚓その他Ruby


同リポジトリより


つっつきボイス:「お、Faradayってず〜っとバージョンが0.いくつのままでしたね😆」「一度バージョンが1.0になった後戻ったような覚えありますけどっ😆」「Ruby 2.7に対応したら1.0にするということのようです」「ま自分はHTTPClient派ですけど🤣」「そういえばウォッチでも話題になりましたね(ウォッチ20180615)」

「Faradayってクローラーとか作るときに使うんでしたっけ?」「クローラーも作れると思いますけど、もっとリッチなことができます☺」「Faradayはミドルウェアが使えますね↓」「そういえばあったわFaradayのミドルウェア」

「Faradayは直接使うより他のgemのdependencyで引っかかる印象があるんですよ: こっちのライブラリとあっちのライブラリがそれぞれFaradayの違うバージョンに依存してたりとか😭」「あぁ〜😳

⚓DB

⚓PostgreSQLカンファレンス2019まとめ


postgresql.jpより


つっつきボイス:「そうだぽすぐれカンファレンスやってた!」「Togetterでセッションごとにまとめてくれているので見やすそう😋」「PostgreSQLカンファレンスは大規模ですよね: 特に Project Tsurugi(劔)の話↓とか」「つるぎ?」「これを主催している神林さんは日本のPostgreSQL界隈ではめちゃ強い人💪で、たしか未踏プロジェクトにも出てたと思います」「おぉ」「何度か会ったことありますけど、PostgreSQLをL1キャッシュレベルで高速化するような人でした😆」「つぇ〜🤩

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

⚓今年のre:Invent


reinvent.awseventsjapan.comより


つっつきボイス:「re:inventちょうど今やってますね〜(注: つっつき中)」「やってるやってる〜😋」「既にいくつか記事も出てるので少し貼ってみました↓」「re:inventは自分もまだ追いかけられていませんが、重要なイベントなので後でキャッチアップして社内でre:inventサーベイするつもりです❤: EC2 Image Builder↓もよさそうですし」

「CodeGuru↓とか興味ある😋」「自動でコードレビューするやつでしたっけ」「でもお高い〜💸100行あたり0.75ドルだから、Railsのコードをうっかりかけたら死にそう😆」「死ねる😇」「インデント直すとかでも1000行で7.5ドルか〜😅」「よさそうだけどCodeGuru破産コワいし、今ならCodeClimateみたいな他のサービスの方がコストが安定しそうではありますね☺

⚓Buildpacksとは


つっつきボイス:「Buildpackはそこそこ前からあるようですが、どのぐらい普及しているんでしょう?」「Herokuは前からBuildpackやってますけどね☺」「上のスライドも開けてみたらBuildpackの話で、それで知りました」「まあDockerfileがこれだけ普及しましたし、Dockerfileは中身を追いかけられるのがいいところですし(追いかけるのつらいけどっ😆)」

⚓その他インフラ

FROM nginx:alpine

RUN apk add curl

HEALTHCHECK --interval=5s --timeout=3s CMD curl -f http://localhost/ || exit 1

つっつきボイス:「おぉ〜HEALTHCHECKは知らなかった😳」「どういうものなんでしょう?」「docker psするとこうやって表示できるのね↓」「どこかで使えそう😋」「おかしくなるとまずDockerリスタートしちゃいますけどっ😆

$ docker ps --format "table {{.Image}}\t{{.Status}}\t{{.Names}}"
IMAGE                    STATUS                      NAMES
kakakakakku/nginx:base   Up 19 seconds (unhealthy)   nginx05
kakakakakku/nginx:base   Up 27 seconds (healthy)     nginx04
kakakakakku/nginx:ng     Up 11 minutes (unhealthy)   nginx03
kakakakakku/nginx:ok     Up 11 minutes (healthy)     nginx02
kakakakakku/nginx:base   Up 11 minutes               nginx01

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

⚓Adbe XDのプラグイン

Mimic
URLを入力するだけで既存のWebサイトからXDプロジェクトに色、フォント情報、画像情報を一覧化してくれるプラグインです。

とか

VizzyCharts
csvファイルから数値を読み込んで、折れ線グラフ、円グラフ、棒グラフの3パターンが作成できます。

が社内デザインチームで色めき立っていました。


つっつきボイス:「VizzyChartsはCSSから値を読み込んでグラフを生成…ってExcelでできるのでは🤣」「🤣」「まあそれをXDだけでできるのがいいところかも😆

「今日お集まりの方の中でAdobe XD使ってる方は…いないっぽい😆: デザイナーからはどういうフォーマットでデザインを受け取ってます?」「やっぱりXDですね☺


adobe.comより

「自分たちはZeplin↓使うことが多いかな」


zeplin.ioより

「うちはFigma↓ですね」「これ知らなかった😳」「オンラインではよさそう😋」「これはXDで作ってFigmaにアップするんでしょうか?」「いえ、Figmaのデスクトップアプリがあるのでそこで作ってそのままプロトタイピングまでやれます」「へぇ〜」「マイクロインタラクションイージングとかはそんなに突き詰められないんですけど、オンラインで同期書き込みできるので同じものを複数人で見ながら操作したりできるのがいいですね😋」「Zeplinもそれができるのがありがたいです🙏」「Googleスプレッドシートみたい」


figma.comより

「少し変わった使い方としては、スクラムのカンバンをFigmaで作ってそのままFigma上で使ったりしてましたね」「おぉ〜そんな使い方が😳」「いわゆるカンバン機能みたいにして使うと😆」「デザインツールというよりコラボレーションツールみたいですね😋」「まあライフハックというか、draw.ioとかcacco.comみたいな感じで図形を編集しながらオンラインでやりとりできます😋」「カクー?」「caccoは日本のサービスですね」


cacoo.comより

「あとはGoogleというかG SuiteのJamboard↓も付箋をいっぱい貼れて便利ですね😋


gsuite.google.comより

「こういうコラボレーションツールもいろいろあるのでどれを使うか悩ましいですよね😄」(以下延々)

⚓言語・ツール

⚓新機能あれこれ

つっつきボイス:「今回は細かめの新機能を見繕ってみました😋

「gitに入った機能ですか」「あ〜、こうやって文字単位でもdiffを出してくれるのね↓」「RubyMineみたいなIDEにはもうある機能だと思いますけど😆」「gitでできるとCIでもやれたりするのはよさそう😋


同記事より


「BPS社内SlackでGitHubのコードジャンプに喜びの声が上がってました↑」


「みんなが待ち望んでいた機能🎉」「Slackのオートフォーマット機能のせいでこれまでどんだけ生産性が落ちていたかと😆」「Slackに何か貼り付けるとおかしなことになったりして、どうやったらこれを殺せるかSlackチャネルで真剣に相談してましたし😆

⚓その他

⚓\e[iがポイント


つっつきボイス:「これも楽しい記事😋」「ターミナルソフトウェアが制御文字をどのぐらい扱うかという話ですね☺」「BEL文字とか🛎、最近だとベルの代わりに画面が一瞬ピカッと光るとかもありますね⚡

参考: ベル文字 - Wikipedia

「昔々だとLinuxでとても時間のかかるコマンドを実行するときは最後にベルを鳴らしたりしましたね😆: ベル文字だとPCの基板上にあるスピーカーを直接鳴らすので、たとえばデータセンターでディスプレイを切り替えて作業していてもベルが鳴ってくれるんですよ🛎」「そういえば私もベル鳴らすコマンドを作ってシェルに入れました↓😆

# Mac用
afplay /System/Library/Sounds/Glass.aiff

⚓リニエンシー制度


つっつきボイス:「リニエンシー制度はちょっと前から日本にも導入されているらしいんですけど、談合を最初に白状した会社はお目こぼしにすることでいわゆる囚人のジレンマを解決するというものだそうです」「通報にインセンティブを付ける発想ですね☺

参考: 囚人のジレンマ - Wikipedia

⚓番外

⚓電池なしで動くものたち


つっつきボイス:「デイリーポータルZのこの記事面白かった!😋」「機械式計算機の使い方が誰もわからないオーパーツというかロストテクノロジー状態から使い方を模索していくのがサイコーです😆」「ちょっと長いですけど😆」「実はマニュアルこっそり持ってたってことは…ないかさすがに😆


「こちらの人工ニューロンも電池が要らないデバイスということで🔋


後編は以上です。おかげさまでつっつき後の親睦会も大勢で盛り上がりました🍻。ありがとうございます!

おたより発掘

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

週刊Railsウォッチ(20191209前編)Pumaのphased-restartとUnicornのgraceful restart、RailsのTZハックが不要になった話ほか

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

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

Ruby Weekly

RubyMine: Could not find rake-xxxx in any of the sources Run `bundle install` to install missing gems.のエラーを解決した話

$
0
0

はじめに

あるRailsプロジェクトをRubyMineのデバッグモードで動かそうとした時に

Could not find rake-10.5.0 in any of the sources Run bundle install to install missing gems.

というエラーが出てハマったので、その時のエラー解決までの体験記事です。

動作環境

  • ruby:2.2.4p230 (2015-12-16 revision 53155) [x86_64-darwin17]
  • rbenv:1.1.2-2-g4e92322
  • bundler:1.16.0
  • RubyMine:2019.1.1
  • OS:macOS High Sierra 10.13.6

エラーが発生した経緯

あるRailsプロジェクトを約半年ぶりに動かすことになり、
とりあえず、デバッグモードで起動しようとすると↓のエラーに出会いました。

/bin/bash -c "env RBENV_VERSION=2.6.3 /Users/Hirakawa/.rbenv/bin/rbenv exec ruby /Users/Hirakawa/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/ruby-debug-ide-0.7.0.beta7/bin/rdebug-ide --key-value --disable-int-handler --evaluation-timeout 10 --evaluation-control --time-limit 100 --memory-limit 0 --rubymine-protocol-extensions --port 51513 --host 0.0.0.0 --dispatcher-port 51514 -- /Users/Hirakawa/Desktop/repositories/sample_project/bin/rails server -b 0.0.0.0 -p 3000 -e development"
Fast Debugger (ruby-debug-ide 0.7.0.beta7, debase 0.2.3.beta5, file filtering is supported) listens on 0.0.0.0:51513
Array values in the parameter to `Gem.paths=` are deprecated.
Please use a String or nil.
An Array ({"GEM_PATH"=>["/Users/Hirakawa/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0", "/Users/Hirakawa/.gem/ruby/2.6.0"]}) was passed in from /Users/Hirakawa/Desktop/repositories/sample_project/bin/rails:3:in `load'
Could not find rake-10.5.0 in any of the sources
Run `bundle install` to install missing gems.

エラー解消までの道のり

とりあえず、エラーに書いてある通りbundle installを実行。

$ bundle install
Using rake 10.5.0
Using CFPropertyList 2.3.2
(省略)
Bundle complete! 69 Gemfile dependencies, 219 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

bundle installうまくいった 🙂
よし、デバッグモードで再起動すれば。。。エラー内容変わらない 💀

エラー文から検索してこちらの
Could not find XXXX in any of the sources エラーの修正方法
を参考に解消を試みましたがRubyやBundlerのpathに問題はありませんでした。

$ which ruby
/Users/Hirakawa/.rbenv/shims/ruby

$ which bundle
/Users/Hirakawa/.rbenv/shims/bundle

$ bundle show rake
/Users/Hirakawa/.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/rake-10.5.0

ここで原因がわからずに頭を抱えていたところ、kazzさんに助けていただき、
RubyMineのConfigurationsの設定が原因だとわかりました。

以下、具体的な解決方法となります。

解決方法

Run > Edit Configurations を開きます。

左側のRails > Developmentを選択して
画像右下の「Ruby SDK:」の「Use project SDK」という項目にチェックを入れてOKを押してください。

僕の場合はこれでデバッグで起動するようになりました。

まとめ

この記事のタイトルのエラーと出会ったらRubyやBundlerのpathにくわえて
RubyMineの「Ruby SDK:」の設定が「Use project SDK」になっているかも確認してみるとよさそうですね。

関連記事

RubyMineからGitが使えて便利

週刊Railsウォッチ(20191216前編)Rails 6.0.2がリリース、平成Ruby会議01開催、古いRailsのfindメソッド置き換えほか

$
0
0

こんにちは、hachi8833です。

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

今回は新しい試みとして、TechRacho記事でもお馴染みのWingdoor様が福岡エンジニアカフェで主催したつっつき会に東京からリモート接続する形で開催いたしました。Windoor様およびご参加いただいた皆さまありがとうございます!


つっつきボイス:(接続テストの後)「こういう遠隔イベントはやっぱりZoom↓が強い💪」(ご挨拶・会社紹介など)「それでは始めま〜す🔔」「よろしくお願いしま〜す🎉」「今回はモバイル開発やPHPやJSなどをやってる方もいらっしゃるようなのでそのつもりで進めます」


zoom.usより

「そういえば今度の土曜日は平成Ruby会議01ですね(注: イベントは終了しました)」「お、もう今週か😳」「そういえばなぜ平成😆」「平成生まれのRubyエンジニアが集う場ですって」「私アウト😇」「私アウト😇」「私アウト😇」「…の続きは、平成生まれだけでなく各世代で交流ですって😆


heiseirb.github.ioより

「キーノートは@koicさんに@yui-knkさん、2スロットもあるアツいカンファレンスですね🔥」「スライドもいろいろ上がってくると思いますので楽しみ😋


行ってまいりました。最高です❤。スライドは以下に続々アップ中です。

⚓臨時ニュース: Rails 6.0.2がリリース


rubyonrails.orgより

こちらはつっつきの後で出たリリースです。細かな改修のみで、セキュリティ関連の修正は見当たりませんでした。作り中のアプリで早速bundle updateしました。

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

以下から見繕いました。ドキュメントの修正が目立ちました。

⚓class_namesヘルパーを追加

<div class="<%= class_names(active: item.for_sale?) %>">
# actionview/lib/action_view/helpers/tag_helper.rb#L293
+     # Returns a string of class names built from +args+.
+     #
+     # ==== Examples
+     #   class_names("foo", "bar")
+     #    # => "foo bar"
+     #   class_names({ foo: true, bar: false })
+     #    # => "foo"
+     #   class_names(nil, false, 123, "", "foo", { bar: true })
+     #    # => "foo bar"
+     def class_names(*args)
+       safe_join(build_tag_values(*args), " ")
+     end

つっつきボイス:「class_name?」「これで何するんだろ?」「これはCSSのクラスでは?」「あ〜Rubyのクラスではなかったか😆」「ハッシュの値がtrueだったらキーをCSSのクラスとして出力するとかができるようになったのね☺」「元の書き方とどっちが読みやすいかはちと微妙ですけど😆

<!--Before:-->
<div class="<%= item.for_sale? ? 'active' : '' %>">

<!--After:-->
<div class="<%= class_names(active: item.for_sale?) %>">

「他の言語でこういう書き方してたのを取り入れたのかな?🤔」「…Vue.jsでこういう書き方があったような気がします」「お、そうでしたか😳

おたより発掘

⚓インスタンス変数をloadに移動

インスタンス変数@records@loaded#exec_queriesではなく#loadに置くべき。#load#exec_queriesを実行し、@recordloaded?がtrueの場合しか代入されず、@loaded#loaded?がtrueの場合しか設定されず、どっちみち#loadで代入すべきなのでこの方が明快。なおこれはとある内部gemでインスタンス変数が代入される場所がおかしかったことで気づいた。
同PRより

# activerecord/lib/active_record/relation.rb#L628
    def load(&block)
-     exec_queries(&block) unless loaded?
+     unless loaded?
+       @records = exec_queries(&block)
+       @loaded = true
+     end

      self
    end
...
      def exec_queries(&block)
        skip_query_cache_if_necessary do
-         @records =
+         records =
            if where_clause.contradiction?
              []
            elsif eager_loading?
@@ -826,12 +829,11 @@ def exec_queries(&block)
              klass.find_by_sql(arel, &block).freeze
            end

-         preload_associations(@records) unless skip_preloading_value
+         preload_associations(records) unless skip_preloading_value

-         @records.each(&:readonly!) if readonly_value
+         records.each(&:readonly!) if readonly_value

-         @loaded = true
-         @records
+         records
        end
      end

つっつきボイス:「これはリファクタリングだなってわかりました」「インスタンス変数のメモ化周りを整理したということでしょうね☺

⚓Active Jobにdefault_retry_jitter設定を追加

  • config.active_job.default_retry_jitterは、失敗したジョブをリトライする際に算出されるdelay timeに”jitter”(ランダムなオフセット値)を適用する。デフォルトは0.15。
    guides/source/configuring.mdより大意

つっつきボイス:「おぉジッターじゃないですか: ジッターって自分で設定するものなんだろか?😆」「デフォルト値があってカスタマイズもできるみたいですね」

「お集まりの皆さんはジッター(jitter)ってわかります?」「電気方面で見たことある気がします」「ネットワーク用語などでは『ばらつき』みたいなネガティブな意味で使われるんですけど、ここではActive Jobのリトライのジッターなので違うニュアンスですね」「おぉ?」「たとえばジョブのリトライを5秒待つ場合、普通は5秒きっかり待つんですけど、それに1秒のジッターを与えるということは待ち時間を4秒から6秒の間でばらつかせるということだと思います」

参考: ジッター - Wikipedia

「ここはある程度想像ですけど、ジッターを与える理由があるとすれば、ワーカーがたくさんあってそれらが同時に失敗した後リトライがまったく同時に発生してしまうと負荷が集中してしまうので、ジッターを与えることで負荷をそこそこ分散させる、なんてのが考えられますね☺」「なるほど!」「特にサーバー負荷が高いせいでジョブが同時にたくさん失敗した場合は、ジッターがないとリトライのタイミングが揃いすぎてまた同じことが繰り返し起きてしまいますので😇」「ならしたいというか均質化したいというか」「みんな出口に同時に押し寄せると詰まるから適度に譲り合うみたいな😆

「ジッターがゼロだと永遠にリトライが終わらないことがあるかもしれないので、そういう場合にジッターを付ければ、たまたま最初にリトライしたジョブが成功して、ジッターの設定次第ですがジョブが少しずつでも片付いていくでしょうね: あえて設定するかどうかは別ですが😆


jitterはもともと「神経過敏でピリピリする」といったニュアンスのようです。

ジッターの連想でMichel Legrandアレンジの「Jitterbug Waltz」です。Jitterbugと「ジルバ」が同じものだと知ってびっくりした覚えがあります。

⚓入力値が正しくならない問題のリグレッションを修正

  hidden_field_tag('token', value: [1, 2, 3])
↓
<input type="hidden" value="">
# actionview/lib/action_view/helpers/tag_helper.rb#L94
        def tag_option(key, value, escape)
          case value
          when Array, Hash
-           value = build_tag_values(value)
+           value = build_tag_values(value) if key.to_s == "class"
            value = escape ? safe_join(value, " ") : value.join(" ")
          else
            value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
          end
          value = value.gsub('"', """) if value.include?('"')
          %(#{key}="#{value}")
        end
        private
          def build_tag_values(*args)
            tag_values = []

            args.each do |tag_value|
              case tag_value
-             when String
-               tag_values << tag_value if tag_value.present?
              when Hash
                tag_value.each do |key, val|
                  tag_values << key if val
                end
              when Array
                tag_values << build_tag_values(*tag_value).presence
+             else
+               tag_values << tag_value.to_s if tag_value.present?
              end
            end

            tag_values.compact.flatten
          end

つっつきボイス:「リグレッションを修正したようです」「hidden fieldのvalue: [1, 2, 3]が消える…だと?」「valueにarrayを渡すなんてことがあるとは😳

「arrayを渡すとどうなるのが正解なんでしょうね?🤔」「テストを見ると本来はスペース区切りになるのか↓」「そうでしたか!」「まあどうせ文字列になりますし😆」「自分ならこういう使い方はまずやりませんけど、そういう挙動なんだへぇ〜という気持ち😆

# actionview/test/template/tag_helper_test.rb#283
   str = content_tag("p", "limelight", class: [1, 2, 3])
    assert_equal "<p class=\"1 2 3\">limelight</p>", str

⚓データベースURLで=をサポート

=はPostgreSQLコネクションでデータベースURLフォーマットを用いてoptionsを渡すのに必要。
同PRより大意


つっつきボイス:「ぽすぐれ対応みたいです」「なるほど、こういうoptions=でオプションを渡せるようになったと」

# 同PRより
# config/database.yml

development:
  url: postgresql://localhost/railsdevapp_development?options=-cmysetting.debug=on

「以前も話題にしましたけど、postgresql://みたいな書き方ってデータベースごとに独自過ぎますよね😆」「オレオレ度高い😆

⚓番外: 不要なrequireを削除

# activesupport/lib/active_support/callbacks.rb#L3
require "active_support/concern"
require "active_support/descendants_tracker"
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/class/attribute"
- require "active_support/core_ext/kernel/reporting"
- require "active_support/core_ext/kernel/singleton_class"
require "active_support/core_ext/string/filters"
require "thread"

つっつきボイス:「とてもささやかな修正ですが年末お掃除感🧹あったので」「最近地味に進められている、不要なrequireを消すヤツですね」「想像ですけどZeitwerk導入で要らなくなったのかな?🤔」「自動で探せそうな気もしますね☺

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

⚓番外: Active Recordの最適化

つっつきの後で見かけたツイートです。

⚓Rails

⚓RailsでネストしたAPIパラメータのバリデーションをActiveModel::Validationsでやってみた(Awesome Rubyより)


つっつきボイス:「ネストしたJSONパラメータのバリデータをこんな感じに作ってみたという記事っぽい☺

# 同記事より
# lib/affiliate/lead/params_validation/main.rb
module Affiliate
  module Lead
    module ParamsValidation
      class Main < Base
        validate :validate_customer

        private

        def validate_customer
          customer_validator = Customer.new(data[:customer])

          return if customer_validator.valid?

          add_nested_errors_for(:customer, customer_validator)
        end
      end
    end
  end
end

# lib/affiliate/lead/params_validation/customer.rb
module Affiliate
  module Lead
    module ParamsValidation
      class Customer < Base
        validates_presence_of :name

        validate :validate_address

        private

        def validate_address
          address_validator = Address.new(data[:address])

          return if address_validator.valid?

          add_nested_errors_for(:address, address_validator)
        end
      end
    end
  end
end

# lib/affiliate/lead/params_validation/customer/address.rb
module Affiliate
  module Lead
    module ParamsValidation
      class Customer
        class Address < Base
          validates_presence_of :postal_code
        end
      end
    end
  end
end

「こういうのってSwaggerというか最近だとOpenAPIとか使ってやるのかな?(ウォッチ20180806)」「同じ記事でgrape↓もおすすめされていて、ウォッチでもちらっとだけ取り上げたことがあったのを思い出しました(ウォッチ20170804)」「grapeはかなり昔からRailsで使われてますね☺」「APIやるgemなのか」「今はRailsもAPIモードありますけど😆」「grapeの定義の仕方は個人的にちょい苦手😅

「今回モバイル系の方もいらっしゃるのでおたずねしますけど、クライアントとサーバーの間の仕様の握りって何を使ってます?Excel仕様書?😆」「Excel仕様書もあるにはあります😆」「うちはGraphQLとか

「GraphQLでこういうネストの深いJSONとか複雑なデータ構造を扱うときってつらくなったりします?」「印刷して収まる範囲なら何とか」「シンプルなAPIならGraphQLでさくっとできそうですね😋」「GraphQLだとクライアントが投げるクエリによってはしれっと重くなったりしません?」「まあありますね😆」「インデックス張ってないところを取ろうとしたときとか😆」「クライアントは今のところそんなに重くないんで何とかなってます

「GraphQLの記事はひところに比べて落ち着いた印象ありますけど」「観測範囲では使われているところでは使われていますね☺」「まあちょろいSELECTクエリのために実装してAPI仕様を渡すよりはGraphQLの方が楽といえば楽かも😆

RailsでGraphQL APIをつくる: Part 1 – GraphQLとは何か(翻訳)

⚓secret_key_baseをお漏らししないために


つっつきボイス:「RubyのMarshal.loadにユーザ入力由来の値が入ると危険、そりゃそうだ😆」「あぁなるほど、ActiveSupport::MessageVerifierActiveSupport::MessageEncryptorがデフォルトでMarshalになっているのが潜在的に危険だからデフォルトをJSONにしようねという話か」「まあ言われてみれば😆

「今回はPHPやってる人もいますけど、PHPにも任意のオブジェクトをstringに変換する機能がありましたよね?たしかvar_dumpだったかな?」「あります」「あの辺がRubyで言うMarshalに相当します」

追記(2019/12/16)

こちら↓などの方が近そうです🙇

⚓tapping_device gemに新機能

tap_on!tap_sql!です。

# 同記事より
def index
  @posts = Post.all
  tap_on!(@posts) do |payload|
    puts(payload.method_name_and_location)
  end
end
# 同記事より
tap_sql!(@posts, exclude_by_paths: [/activerecord/]) do |payload|
  puts("Method `#{payload.method_name}` generates sql: #{payload.sql}")
  puts("  From #{payload.filepath}:#{payload.line_number}")
end

つっつきボイス:「こちらは私の友だちのst0012さんが最近熱心にメンテしている、主にRailsを想定したRubyのデバッグ用gemで、それに新機能を足したという記事です☺

tap_sql!はSQLを生成したタイミングで取れるみたいな?」「そんな感じです」

Method `count` generates sql: SELECT COUNT(*) FROM "posts"
  From /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:3
Method `each` generates sql: SELECT "posts".* FROM "posts"
  From /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:16
Method `count` generates sql: SELECT COUNT(*) FROM "posts" WHERE "posts"."user_id" = ?
  From /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:31

tap_on!はメソッドがどこで呼ばれたかを追えるようです」「ほほぉ😋

Method: :eager_load_values, line: /Users/st0012/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/relation.rb:668
Method: :includes_values, line: /Users/st0012/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/relation.rb:669
Method: :eager_loading?, line: /Users/st0012/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/relation/calculations.rb:226
Method: :includes_values, line: /Users/st0012/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/relation/calculations.rb:226
Method: :has_include?, line: /Users/st0012/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/relation/calculations.rb:128
Method: :distinct_value, line: /Users/st0012/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/relation/calculations.rb:234

「新機能の実装がえらくしんどいと言ってました↓」「こういうふうにオブジェクトに触られた瞬間を全部追いかけたいと思ったらこういうgemを使うしかないですね〜☺

⚓実装をどっちに置くか


つっつきボイス:「この記事はRails以外でも通用する感じですね☺」「永遠の課題というか😆」「モバイルの人たちにも関係する話だ」

「最近だとAPIサーバー的な、データを処理するロジックだけサーバーでやって、後はクライアントでやるというのが世の中的に増えてきてる感じですが、モバイルの方から見ていかがでしょう?」「記事ではサーバーに寄せる方針でやってるようですね🤔」「実はサーバーもクライアントも一人で作っているのであれば、この辺は割とどうでもよかったりする😆」「たしかに😆

「ただね〜、バックエンドとフロントエンドを別の会社が作って連携するとなるとね…特にiOSとAndroidも別会社だったりするとなおさら😆」「😆」「そういう状況でサーバー側に機能を寄せるといろいろつらくなりがちなので、サーバーはAPIに専念する方がマシという気持ちにはなる☺」「そこら辺は案件によると言ってしまえばそれまでですが、割とサーバーよりはクライアント側で頑張る感じかなと」「なるほど」

「サーバー側で頑張るならいっそWebViewにしちゃうのがいいかなって思ったり😆」「ruby-jp Slackでもその辺が話題になってたみたいですね」「ある意味永遠のテーマ😆」「モデルをいくつも使うような複雑なバリデーションならサーバー側に寄せたいし」「iOSとAndroidが両方あるとそれぞれにロジック書くのはつらいし」「iOSとAndroidで表示がぶれるとイヤですね〜😅」「そこはクライアント側で頑張って欲しいですけど😆

参考: WebViewとネイティブのメリットデメリット - Qiita


「以下はruby-jp Slackで見かけた別のスライドですが、Monomicrolithという異様な言葉が気になりました😆」「これはKubeConのスライドでマイクロサービス寄りの話なので上とはつながりはないかな〜☺」「そうでした😅」「お集まりの皆さんの中でマイクロサービスで開発してる方は…?」「見事にいない😆


kubecon-jp.connpass.comより

「マイクロサービスをきれいに作れる気がしないとか大規模なシステムでないと効果が薄いとかはありますけど、絶対に守りたい機能をマイクロサービスで固く固く作ってソースもめったに変えないとかならあってもいいかも☺

⚓非推奨になったfind系メソッドを殺して回る(Hacklinesより)


つっつきボイス:「おぉ〜超なつかしい書き方↓、久しぶりに見た😆」「😆」「Rails 2の頃のfindメソッドですけど、自分Rails 3からだったので自分から書いたことありませんし(古いRailsで仕方なしに書いたことはありますけどっ😆)」

# 同記事より
Post.find(:all, conditions: { published_on: 2.weeks.ago }, limit: 5 ...)

「Synvertというgemを使って古い記法を修正したそうです↓」「へぇ〜こうやって変換してくれるんだ😳」「そういえばウォッチでも扱ったかな(ウォッチ20170908)」「AST解析してやってくれるのね😋

# 同記事より
# Handles find with hash options
$ synvert -r rails/convert_models_2_3_to_3_0

# Handles dynamic finders
$ synvert -r rails/convert_dynamic_finders

「信頼していいんでしょうか?😅」「まあ機械的に置換できるものならやっちゃっていいと思いますよ☺」「思わず記事の日付確認しちゃったけど新しい記事だし😆」「Rails 2ってRuby 1.8ぐらいの頃でしょうか?」「ですね: 今は亡きRuby Enterprise Editionとか使われてた頃😆」「😆」「ソースコードの冒頭に# coding: UTF-8とか書いてた時代😆

Ruby Enterprise Editionは2012年に終了していたんですね↓😳

参考: Welcome — Ruby Enterprise Edition

参考: Ruby で UTF-8 なのにマジックコメントが必要なケース - Qiita

⚓その他Rails



つっつきボイス:「↑そういえばこの情報って意外とまとまってそうでまとまってませんでしたね」「今はbin/railsが正というか推奨」「でも今でもbundle execって書きますけどっ😆

「binstubっていう、プロジェクトのbin/の下のコマンドは、以前はGitにコミットしないでくれということになってて.gitignoreにも登録されてたんですけど、Rails 5ぐらいからbinstubもプロジェクトにコミットすべきという話が出てコミットされるようになりましたね☺

binstubについてピンポイントの情報が見つからなかったので、やや近いStack Overflowを貼ります。

参考: Should I add the Rails 4 bin/ directory to git? - Stack Overflow


前編は以上です。

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

週刊Railsウォッチ(20191210後編)Ruby 2.7の変更点記事、mrubyで動くmitamae、画像系コラボレーションツールほか

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

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

Rails公式ニュース

Ruby Weekly

Awesome Ruby

Hacklines

Hacklines

週刊Railsウォッチ(20191217後編)Ruby 2.7の変更点とパターンマッチング、依存性自動アップデートツール、Stack Overflowアンケート2019ほか

$
0
0

こんにちは、hachi8833です。平成Ruby会議01の余韻がまだ残っています🔔

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

今回も前編に続き、TechRacho記事でもお馴染みのWingdoor様による福岡エンジニアカフェでのイベントに東京からリモート接続する形のつっつき会を元にお送りいたします。

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

第18回目公開つっつき会は、来年1月12日(木)19:30〜にBPS会議スペースにて開催されます。今回から会場のPubスペースが隣の部屋に移ります。

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

⚓Ruby

⚓2.7の変更点たち、特にパターンマッチング

つっつきボイス:「jnchitoさんが前編後編に分けてRuby 2.7のパターンマッチング記事をすごくがっつり書いていました🎉」「🎉」「大急ぎで読んでみたんですが結構大がかりな印象でした😅」「パターンマッチングでうまくハマるとどやぁという気持ちになれそう😋」「2.7でもまだexperimentalですけど😆」「😆」「パターンマッチングのために新たにキーワードを足さないようすんごく頑張ったのがわかりました😋

「パターンマッチングはそんなに突飛な機能ではないからキライではないですね〜☺」「関数型言語だとたいていあるみたいですね」「あとは業務でどこまで使うかですけど😆」「今後細かく変わりそうですし😆

参考: パターンマッチング - Wikipedia


その他のRuby 2.7関連の記事として、Ruby 2.7 Advent Calendar 2019 - Qiitaから気になったものをそこそこ拾ってみました。

⚓sigcdump: RubyをCレベルでバックトレース


つっつきボイス:「sigcdump?」「k0kubunさんが最近作ったバックトレース用gemのようです」「シグナルを追うのかな?と思ったら、シグナルをぶちこむとその瞬間のバックトレースがtmpやstdoutにごぼっと出力されるのね↓」「瞬間瞬間の状態を追えると😳」「RubyのJITをやってるk0kubunさんらしいgemですね😋

# 同リポジトリより
irb(main)[01:0]> require 'sigcdump'
=> true
irb(main)[02:0]> Sigcdump.dump('-')
Sigcdump at 2019-12-10 00:48:56 -0800 process 61249 (irb)
/Users/k0kubun/.rbenv/versions/ruby/lib/ruby/gems/2.7.0/gems/sigcdump-0.1.0/lib/sigcdump/sigcdump.bundle(rb_sigcdump_dump_internal+0xe7) [0x10b129aa7]
/Users/k0kubun/.rbenv/versions/ruby/bin/ruby(vm_call_cfunc+0x182) [0x10768c3f2]
/Users/k0kubun/.rbenv/versions/ruby/bin/ruby(vm_exec_core+0x3e2e) [0x10767287e]
/Users/k0kubun/.rbenv/versions/ruby/bin/ruby(rb_vm_exec+0xa79) [0x107687819]
/Users/k0kubun/.rbenv/versions/ruby/bin/ruby(rb_f_eval+0x304) [0x107681384]
/Users/k0kubun/.rbenv/versions/ruby/bin/ruby(vm_call_cfunc+0x182) [0x10768c3f2]
/Users/k0kubun/.rbenv/versions/ruby/bin/ruby(vm_exec_core+0x3e2e) [0x10767287e]
/Users/k0kubun/.rbenv/versions/ruby/bin/ruby(rb_vm_exec+0xa79) [0x107687819]
/Users/k0kubun/.rbenv/versions/ruby/bin/ruby(loop_i+0x29) [0x107697159]
/Users/k0kubun/.rbenv/versions/ruby/bin/ruby(rb_vrescue2+0x114) [0x1074d4f24]
...

⚓SysRqキーとは

「こういうのはカーネル開発なんかでよくやるヤツ🧐: SysRqというキーがキーボードにあって、皆さんおそらく一度も使ったことがないと思うんですけど、実ははSysRqキーを使ってカーネルのメモリダンプを吐かせるみたいなキモいことができたりします」「へぇ〜!😳」「今のLinuxだとデフォルトでは無効になっているかもしれませんが、SysRqのコンフィグを有効にした状態でLinuxを起動すると、SysRqと何かのキーのコンビネーションでメモリダンプとかできたりします☺」「ナマコを突っついてはらわたを吐き出すところをつい想像しちゃいました😆

参考: マジックSysRqキー - Wikipedia
参考: 実は使いどころがわからないWindowsのキー、1位は「SysRq」 -INTERNET Watch Watch

「というわけでSysRqキーは一応意味のあるキーだったりします: 古式ゆかしいPC-98だともっとわけのわからないキーありますけど😆」「覚えてないキーだらけ😆」「HelpキーとかXFERとかNFERとか😆」「あったあった😆」「何に使うのかわからずじまい😆

参考: PC-98 キーボード

⚓Torch-rb: RubyでPyTorch


Wikimedia commonsより


つっつきボイス:「この間Chainerの後を継いだPyTorchを早速torch-rbでやってみた記事です☺

# 同記事より
class Net < Torch::NN::Module
  def initialize
    super
    @conv1 = Torch::NN::Conv2d.new(1, 32, 3, stride: 1)
    @conv2 = Torch::NN::Conv2d.new(32, 64, 3, stride: 1)
    @dropout1 = Torch::NN::Dropout2d.new(p: 0.25)
    @dropout2 = Torch::NN::Dropout2d.new(p: 0.5)
    @fc1 = Torch::NN::Linear.new(9216, 128)
    @fc2 = Torch::NN::Linear.new(128, 10)
  end

  def forward(x)
    x = @conv1.call(x)
    x = Torch::NN::F.relu(x)
    x = @conv2.call(x)
    x = Torch::NN::F.max_pool2d(x, 2)
    x = @dropout1.call(x)
    x = Torch.flatten(x, start_dim: 1)
    x = @fc1.call(x)
    x = Torch::NN::F.relu(x)
    x = @dropout2.call(x)
    x = @fc2.call(x)
    output = Torch::NN::F.log_softmax(x)
    output
  end
end

torch-rbは@ankaneさん作なんですね😳。最近は機械学習向けのRubyライブラリを怒涛のように作っているそうです。

⚓その他Ruby


同サイトより

つっつきボイス:「planetrubyが公開した、来年開催されるRuby関連カンファレンスのカレンダーです📅」「そうそう皆さん、4月はRubyKaigiですよお忘れなく😋」「早めに押さえないと宿が取れないかもしれない説」「長野県松本市ですね😋」「松本市だから気合入れれば都内から日帰りできなくもないですけど😆


rubykaigi.orgより


つっつきボイス:「おGitLab特集だ😋」「TechRachoをよくブクマしてくださっていたsue445さんがついに商業誌デビュー🎉

⚓DB

⚓rqlite: 分散SQLite(DB Weeklyより)


同リポジトリより


つっつきボイス:「分散SQLiteという触れ込みですがもしかするとキワモノ?」「どの辺が分散なんだろ?😆」「デフォルトでSQLiteのインメモリデータベースを使うということらしいけど🤔

「SQLite自体は特に書き込みの場合同時に1プロセスしかファイルを開けなかった覚えがありますね🧐」「distributed databaseでググると普通の分散データベースが出てきますけど↓」「ですよね: これはそういうのとは違うっぽい」「でもクラスタとか書いてあるし」「謎🙃」「誰か分かる人?」「わかりません😇」「😆

参考: 分散データベース - Wikipedia

⚓その他DB


つっつきボイス:「そういえばPostgreSQLも12が出てだいぶ経ちますね」「この記事では余分なログを減らせることをbenignログと呼んでますね」「benignは良性/悪性腫瘍の良性という意味みたいですけど見かけない単語😆」「大量のログでいつの間にかストレージがあふれるとかあるあるですね☺」「割とマイクロコスメティックな変更っぽい😆」「よく見るとPerconaのブログだ😳

2019-11-28 13:24:26.501 UTC [15168] LOG: incomplete startup packet
2019-11-28 13:24:26.517 UTC [15170] LOG: incomplete startup packet
2019-11-28 13:24:26.532 UTC [15172] LOG: incomplete startup packet
2019-11-28 13:24:26.548 UTC [15174] LOG: incomplete startup packet
2019-11-28 13:24:26.564 UTC [15176] LOG: incomplete startup packet
2019-11-28 13:24:26.580 UTC [15178] LOG: incomplete startup packet
2019-11-28 13:24:26.595 UTC [15180] LOG: incomplete startup packet
2019-11-28 13:24:26.611 UTC [15182] LOG: incomplete startup packet
2019-11-28 13:24:26.627 UTC [15184] LOG: incomplete startup packet
2019-11-28 13:24:26.645 UTC [15186] LOG: incomplete startup packet
2019-11-28 13:24:26.666 UTC [15188] LOG: incomplete startup packet
2019-11-28 13:24:26.687 UTC [15190] LOG: incomplete startup packet
2019-11-28 13:24:26.710 UTC [15192] LOG: incomplete startup packet
2019-11-28 13:24:26.729 UTC [15194] LOG: incomplete startup packet
2019-11-28 13:24:26.748 UTC [15196] LOG: incomplete startup packet

PostgreSQL 12ではパケットのサイズがゼロであることを検出すると無視するので、上はログファイルに出力されなくなる。
同記事より大意

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

⚓GitHub製GitHub Actionsがdeprecatedに


つっつきボイス:「AWSとGCPがそれぞれ公式のGitHub Actionsを出したので、今までGitHubが出してたActionsが用済みになったそうです」「今までのは公式ではなかった😆」「社内ではGitHub Actions使ってるプロジェクトあるけど自分がまだ触われてないな〜」


github.co.jpより

GitHubのアイコンデザインのセンスが個人的に好きです❤


「お集まりの中でGitHub Actions使ってる方はいます?」「はい、別のプロジェクトで使われていたのを他の人も使うようになる形で移行した感じです😋: ファイル移行はそれなりにコストかかりますが」「何から移行しました?」「TravisCIです」「なるほどTravisか〜、それなりにお金かかりますよね😆」「(GitHub Actionsに移行して)お金も助かるし実行時間も短くなった感じで💵」「マイクロソフトのありあまるコンピューティングリソースを有効活用している感ありますよね😆」「ですね😆」「無料でこれだけ使えるなら強いなって思いますし💪

⚓その他クラウド

つっつきボイス:「WAFって何だっけと思ったらWeb Application Firewallでした↓」「AWSにはありますね一応😆」「AWSのWAFの設定は割と大変😅」「WAFはどういうものでしょうか?」「HTTPリクエストをいい感じにチェックして、明らかにSQLインジェクションだったりしたら止めてくれたり🧐」「なるほど!」「WAFはCloudFrontの手前あたりに挟むことが多いですね」

参考: AWS WAF(ウェブアプリケーションファイアウォール)| AWS

「WAFはいろいろカスタマイズできるようなんですが、せいぜい例外を設定するぐらいで、頑張ってカスタマイズする系の記事があんまり見当たらないし、ぶっちゃけWAFはあんまり自力で設定したくない😆」「😆

「WAFを後から入れたせいでハマるということもよくあるので、入れるなら最初から入れたい😤」「おぉ」「WordPressのサイトに後からWAFを入れると今まで動いてたのが動かなくなった、なんてことも起きますし😇: TechRachoに仮にWAF入れるとたぶん投稿できなくなる可能性あります」「ひぇっ😨」「しかもWAFが原因であることを突き止めるのが大変ですし😅: リクエストがサーバーまで届かなくなるのでサーバーのログではわからなくてWAFのログを見ないといけないとか」「うむむ😥



つっつきボイス:「ワラタ😆」「まあ考えてみれば時間あたり何回できるかみたいな方がベンチマークらしいですよね☺」「昔に比べてコンパイル時間が短くなったといえばX(X Window System)かな〜😆」「昔は平気で一晩かかってましたね」「コマンドもmake worldでしたし」「え、それ知らない😅」「X.Orgになる前だったかな😆」「XFree86とかありましたね」「ほとんど記憶にない…😅」(以下コンパイル話が続く)

参考: X Window System - Wikipedia


x.orgより

⚓JavaScript

⚓WebAssemblyがW3CのRECに(Publickeyより)


つっつきボイス:「WebAssembly(wasm)がついに」「でもW3Cがやってるの?😆」「それもそうですね😳」「なぜWHATWGではないのかと😆」「まだ仲直りしきってない?😆

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

mrubyをWebAssemblyで動かす(翻訳)

「まあWebAssemblyはだいぶ動くようになってきてますし」「WebAssemblyでmrubyを動かしたりは結構行われているみたいですね↑」「当初LinuxカーネルをWebAssemblyで動かす記事↓で有名になりましたね☺」「ここまでやれればもう全部ブラウザでやればいいんじゃね?って思いますし😆

参考: LKL.js: Linux kernelを直接JavaScript上で動かす - Blog posts by @retrage01

⚓その他JS

つっつきボイス:「mizchiさんの教え」「ま普通は===使いますし😆

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

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

⚓CSS Writing Mode Level 3がRECに


つっつきボイス:「これもW3C😆」「15年の歳月を経てとうとう縦書きが入ってくる🎉」「15年って😆」「BPSの超縦書↓も縦書きの仕様に絡んでたはず」「超縦書のチームでも話題になってたと思います」「村井純教授(慶応義塾大)もコメントを寄せてますね☺

参考: 村井純 - Wikipedia

弊社が参加しているWeb仕様に関する標準化団体 W3C にて、このような状況にどう対応するべきかの知見を標準仕様側にもフィードバックする活動を行っています。
特に、W3CにおいてCSS仕様を検討している CSS Working Group には積極的な参加を続けております。2017年4月には慶應義塾大学様のご協力もいただき、年3回程度開催される対面会議 CSS Working Group Face to Face Meeting (通称 F2F) の日本への招致も行いました。
bpsinc.jp/cho-tate-winより抜粋

「Writing Modeは横書きと縦書きがあるんですけど、使ったことある方います?」「趣味でやってみたことあるんですが、そのときはうまくいきませんでした😇」「おぉ、やってる人いる!」「lrとかrlとかlr-tbとかtb-rlとか、とにかくいろいろ指定がありますよね😆

参考: CSS Writing Modes Level 3
参考: CSS Writing Modes Module Level 3 日本語翻訳

⚓言語・ツール

⚓Renovate: マルチ言語の依存性アップデートサービス


npmjs.comより

以下の翻訳記事でJS強者がこのRenovateをおすすめしていたので。

Rails 6+Webpacker開発環境をJS強者ががっつりセットアップしてみた(翻訳)


つっつきボイス:「Renovate?」「複数言語を対象に依存関係のアップデートの面倒をまとめて見てくれるようです😆」「サイトを見た感じでは、プロジェクトが依存しているライブラリをがんがん引っ張ってアップデートしてくれる感じ🤔

「たしかPHPにも最近それ系の管理ツールってありますよね?」「Composerでしたっけ」「なるほど、依存関係をよしなにチェックして自動アップデートするようなのはPHPにもあります?」「Dependabotだったと思います」「おぉ、DependabotはRubyやJSなどにも対応してますね😋


getcomposer.orgより


dependabot.comより

「ライブラリアップデートは自動的にやって欲しい、ホントに😭」「ホントに😭」「でも依存関係同士に含まれているFaraday(gem)が微妙にバージョン違ってコンフリクトしたことありますけどっ😆」「😆

⚓Stack Overflowのアンケート2019


insights.stackoverflow.comより


つっつきボイス:「今年も出た」「年末感ありますね」「かなり長いですが読んでて楽しいです😋」「言語の好き嫌いはどうやっても宗教戦争になりますし🤣」「シェアとか人気みたいな情報って相当多めに割り引いて読みたいですね☺

「キライな言語ランキングとかってだいたい悲しい結果にしかなりませんし😆」「憎まれっ子世にはばかるみたいな😆」(以下しばらくランキングチェックで盛り上がる)

「そういえばStack Overflowって昔ほど見かけなくなった気がする🤔」「そうかも😳」「以前は何を検索してもStack Overflowが検索結果の上位に出てきた気がするんですけど」「非英語圏からの初歩的な質問が山のように増えたからなのかなと思ったりしますけど、よくわからない😅」「日本語版Stack Overflowは結局定着したんだろうか?🤔

⚓その他

⚓GopherプロトコルでSSRF攻撃

参考: Gopher - Wikipedia


つっつきボイス:「徳丸先生の記事です」「SSRFはねぇ〜、どこまで対策するかというのもあるし、知らないと踏みそうでコワいんですよ😅」「記事にも『完全な防御は無理』ってありますね🤔」「というより、SSRFはアプリケーションエンジニアが相当気をつけておかないと、ふとしたはずみで踏む可能性があるんですよ😇」「おぉ😳


blog.tokumaru.orgより

「上なんかがまさにそうですけど、EC2リソースに対してURLアクセスでインスタンスメタデータを取ってこれてしまうので結構危ない🚫」「そういえば以前も話題に出ましたね(ウォッチ20190107)」「だからはてブみたいにユーザーが任意のURLを入力できるサービスを作るときは気をつけないと割と簡単に取られちゃったりしますね😇

参考: SSRF(Server Side Request Forgery)徹底入門 | 徳丸浩の日記

「対策として"HttpEndpoint": "disabled"って指定できるのか😳」「でもAWSのインスタンスメタデータって欲しいときがあるのが悩ましい😅: capistranoでインスタンスメタデータから自分のEC2インスタンスIDを引っ張ってくるとかよく行われますし」「あぁ〜😳


「そもそもGopherプロトコルが今どき使えるって知らなかったし😆」「ruby-jp Slackでもこれ知ってる人何人いるのかって話題になってました😆」「Gopherは知ってるけど使ったことないわ〜😆

参考: Gopher - Wikipedia

昨今はGopherといえばこちらでしょうね↓。

⚓番外

⚓リモート飲み会出席用ロボ

参考: 分身ロボット「OriHime」


つっつきボイス:「いよいよオーラスです」「OriHimeというロボットは、もともと身体に障害のある方や、子育て/単身赴任/入院時なんかをサポートする目的で開発されたらしいんですが、その場にいなくても臨場感を体験できるものだそうです」「もう飲み会の出席これでいいや😆」「これこそ未来!🤣」「いっぺんVR飲み会ってやってみたい🍶」「OriHimeをずらり居酒屋に並べる感じで😆」「このOriHimeの姿、どこかで見たような気がする🤔」「ウルトラマンっぽいですけど😆

「これ系で言うと、ビデオ会議で出席者をアニメキャラのアバターに差し替えてくれるヤツがいいなって思いますね😆」「😆」「声もアニキャラに差し替えて」「動きにも追従して」「そしたらそれだけでほっこりした気持ちになれる😆

参考: テレビ会議にアバターを使うとコミュニケーションが活性化できる|いごはち@学びの実践家|note

「ご参加の皆さまお疲れさまでした」「Zoomでのリモートつっつき、思った以上にうまくいってよかった🎉」「お疲れさまでした〜


後半は以上です。

おたより発掘

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

週刊Railsウォッチ(20191216前編)Rails 6.0.2がリリース、平成Ruby会議01開催、古いRailsのfindメソッド置き換えほか

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

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

Ruby Weekly

Postgres Weekly

postgres_weekly_banner

DB Weekly

db_weekly_banner

Publickey

publickey_banner_captured

Ruby: Object#tap、Object#then を使ってみよう

$
0
0

令和元年最初の年末です。いかがお過ごしでしょうか。私は年末進行まっただ中です😢
今回は聞いたことあるけどあんまり使う機会のなさそうな #tap#yield_self#then の話をしてみたいと思います。

Object#tap

仕様としてはAPIリファレンスを参照すると

self を引数としてブロックを評価し、self を返します。

ということなので、疑似実装としては以下のようになると思います。

class Object
  def tap(&block)
    yield self
    self
  end
end

#tap は古くから存在するメソッドで、1.8.3の頃から存在するようです。

Object#yield_self / Object#then

仕様としてはAPIリファレンスには

self を引数としてブロックを評価し、ブロックの結果を返します。

とあります。擬似的な実装は以下のようになると思います。

class Object
  def yield_self(&block)
    yield self
  end
end

#yield_self は ruby2.5から導入され、#then はそのエイリアスとして ruby2.6 から導入されました。

#then はMatzが直々のコミットです。導入された経緯は RubyKaigi2018のキーノートがとても印象的です(5:32〜)。

両者の違いをみてみる

method 実行内容 戻り値
#tap self を引数としたブロックの評価 self
#then / #yield_self self を引数としたブロックの評価 ブロックの結果

異なるのは戻り値となります。
レシーバー自身が戻るのが #tap
ブロックの評価値が戻るのが #then / #yield_self となります。

実際に使ってみる

では、#tap#then を使ってみて、両者を比較してみましょう。

  • #tap
hash = {}
#  => {}
hash.tap{ |h| h[:value] = 42 }
#  => {:value=>42}
hash
#  => {:value=>42}

  • #then
hash = {}
#  => {}
hash.then{ |h| h[:value] = 42 }
#  => 42
hash
#  => {:value=>42}

このように、ブロックを評価して hash[:value]42 を設定することは同じですが、#tap の場合は、ブロックの内容にかかわらずレシーバー自身が返り、 #then の場合はブロックの評価結果が返ります。

Array#eachArray#map と似てませんか

似てるなと思った方は勘が鋭いです。やってみましょう。

  • Array#each
[1, 2, 3].each{ |i| i*2 }
#  => [1, 2, 3]

ブロックの中で各要素に対する副作用が発生しなければ、レシーバーそのものが返ります。

  • Array#map
[1, 2, 3].map{ |i| i*2 }
#  => [2, 4, 6]

各要素をブロックで評価された、新しいArrayが返ります。

このように、 #tap は、 #each のオブジェクト版。 #then#map のオブジェクト版といえるのではないでしょうか。

うまい使いどころ

#tap#then のうまい使い方をご紹介します。

Railsコンソールでのデバッグ

Railsコンソールには reload! というコマンドがあり、実行するとプロジェクト内のソースコードを読み込み直してくれます。

ところが、変数に設定したオブジェクトの中身までは変更してくれません。

参考: Rails consoleでreload!する場合の注意点 - Qiita

これを回避してみたいと思います。

Railsコンソールのreload!はオブジェクトに効かない

まずは以下の例を見てみましょう。

以下のようなクラスがあるとします。

class User
  attr_accessor :age
end

Railsコンソールで以下のように実行します。

# (rails console)
> user = User.new
> user.age = 30
> user.age
#  ==> 30

Railsコンソールを開いたままで User クラスを変更します。

class User
  attr_accessor :age
  def age
    '18歳だよ'
  end
end 

Railsコンソールで reload! して #age の振る舞いを見てみます

> reload!
> user.age
#  ==> 30  

# (直ってないやん。仕方が無いのでオブジェクトの作り直し)
> user = User.new
> user.age
#  ==>  18歳だよ

#tapを使って回避する

変数にオブジェクトを格納するからマズいのです。
#tap を使ってワンライナーにしてみましょう。

> reload! && User.new.tap{ |user| user.age = 30 }.age
#  ==> 30

# (ソースの変更後、上キーを押して履歴実行)
> reload! && User.new.tap{ |user| user.age = 30 }.age
#  ==> 18歳だよ

実装変更の確認を高速にできるようになりました

ViewHelperをスッキリ書く

数値に , をつけてくれる number_with_delimiter というViewHelperがあります。

<%= number_with_delimiter(@number) %>

ところが number_with_delimiter の引数はnilを許容しません。普通ですと

<% if @number_or_nil %>
  <%= number_with_delimiter(@number_or_nil) %>
<% end %>

とか

<%= @number_or_nil.present? ? number_with_delimiter(@number_or_nil) : '' %>

みたいな読むのがつらいコードになっていくと思います。
(カンマ区切りの数値の出力が1箇所だけならいいですね)

#then&. を組み合わせると以下のようにスッキリ書けます。

<%= @number_or_nil&.then{ |num| number_with_delimiter(num) } %>

@number_or_nil が nil の場合、&. の後ろが評価されないため nil となります。
@number_or_nil が 数値の場合、ブロックの評価値である number_with_delimiter の変換結果が返ります。

nilがあり得るオブジェクトのデコレーターを適用したイメージになると思います。

APIで返すべきものを強調する

例えば以下のような実装があるとします

def spaghetti
  pot = Pot.new
  pot.put(water)
  spaghetti = Spaghetti.pickup(300)
  pot.put(spaghetti)
  pot.boil(7.minutes)
end

このコードは、 Pot#boil の評価値が返ります。スパゲッティかもしれませんし、煮え湯かもしれませんし、鍋かもしれませんし、7分かもしれませんし、nilかもしれません。
でも、#spaghetti が返却するべきはスパゲッティです。

たぶんバグってますね。
最終行に spaghetti と書いてもいいのですが、以下のように書いてもよいと思います。

def spaghetti
  Spaghetti.pickup(300).tap do |spaghetti|
    Pot.new
      .tap{ |pot| pot.put(water) }
      .tap{ |pot| pot.put(spaghetti) }
      .boil(7.minutes)
  end
end

とにかく#spaghetti はスパゲッティが返ります。
具体的には、ポットに水を入れて、スパゲッティを入れて、7分間ゆでられています。

#tap#then を見かけたら、結局何が返ってくるか?を頭に入れてから、ブロックの中身を読んでみるとよいと思います。

まとめ

#tap#then は一見複雑そうに見えますが、「まず何が戻るのか見る。それからブロックの中身を読む」を心がけると逆に可読性が上がる場合があります。

必ず可読性が上がるわけではありませんが、使えるかも?とおもったら是非お試しください。

関連記事

Ruby: `each`よりも`map`などのコレクションを積極的に使おう(社内勉強会)


週刊Railsウォッチ(20191223前編)Railsセキュリティ修正6.0.2.1と5.2.4.1リリース、Ruby 2.7.0-rc2リリース、ActiveRecordのコールバック回避ほか

$
0
0

こんにちは、hachi8833です。今年最後のRailsウォッチ前編をお送りします。Ruby 2.7のカウントダウンが始まっていますので、クリスマスにはリリースされるでしょう。

その後21日にrc2がリリースされました。

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

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

第18回目公開つっつき会は、来年1月09日(木)19:30〜にBPS会議スペースにて開催されます。今回から会場のPubスペースが隣の部屋に移ります。

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

⚓臨時ニュース: Railsセキュリティ修正6.0.2.1と5.2.4.1がリリース

Rackで情報漏えい/セッションハイジャックの脆弱性が見つかったとのことです。どちらもRackのバージョンアップとAction Packのみの修正です。

ActionDispatch::Session::MemcacheStoreがまだ脆弱なので、dalli gemもアップデートが必須。
Changelogより大意

オレオレアプリは対応しました。

追記(2019/12/24): 上記セキュリティ修正でredis-storeがエラーになる可能性

redis-storeをお使いの方は、本件が解決するまでセキュリティアップデートを待つ必要がありそうです。
なお、同issueのコメントではredis-storeの修正で解決できると追記されていますが、現時点ではまだredis-storeにマージされていません。

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

お詫び: 今回の録画に音声が入っていなかったため、最小限のつっつきボイスとなっています🙇🙇

以下から見繕いました。

⚓delegateがRuby 2.7の...記法に対応

# activesupport/lib/active_support/core_ext/module/delegation.rb#L202
-     definition = /[^\]]=$/.match?(method) ? "arg" : "*args, &block"
+     definition = if /[^\]]=$/.match?(method)
+       "arg"
+     elsif RUBY_VERSION >= "2.7"
+       "..."
+     else
+       "*args, &block"
+     end

以下は先週も貼りましたが一応。

参考: Ruby 2.7 の変更点 - 「...」で全引数渡し - @tmtms のメモ
参考: Ruby 2.7 adds shorthand syntax for arguments forwarding – Saeloun Blog


つっつきボイス:「...はRuby 2.7の新機能か😳」「"*args, &block"と同等なんですね」

参考: $@ - シェルスクリプトに渡されたすべての引数

[Ruby] Kernelの特殊変数をできるだけ$記号なしで書いてみる

⚓bundle exec rubyでテストを実行できるよう修正

require: cannot load such file -- abstract_unit (LoadError)のようなエラーはRailsが初めての人にとってうれしくないので、いい感じにシンプルにした。またテストの依存関係も明確になり、サブフォルダでテストを実行できるようにもなった。
同PRより大意


つっつきボイス:「Railsに慣れていない人がくじけないようにとのことです」「修正ファイル144個は多いな〜😆」「修正されたissue #34025↓を見るとbundle exec ruby test/cache/stores/mem_cache_store_test.rbって実行してエラーになってたけど、こんなふうに実行したことないし😆

⚓yearが未定義の場合の扱いを修正

# actionview/lib/action_view/helpers/date_helper.rb#L832
      def select_year
-       if !2019/12/23time || 2019/12/23time == 0
+       if !year || 2019/12/23time == 0
          val = "1"
          middle_year = Date.today.year
        else
          val = middle_year = year
        end
        if @options[:use_hidden] || @options[:discard_year]
          build_hidden(:year, val)
        else
          options                     = {}
          options[:start]             = @options[:start_year] || middle_year - 5
          options[:end]               = @options[:end_year] || middle_year + 5
          options[:step]              = options[:start] < options[:end] ? 1 : -1
          options[:leading_zeros]     = false
          options[:max_years_allowed] = @options[:max_years_allowed] || 1000
          if (options[:end] - options[:start]).abs > options[:max_years_allowed]
            raise ArgumentError, "There are too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter."
          end
          build_select(:year, build_year_options(val, options))
        end
      end

つっつきボイス:「yearが定義されていない場合今までエラーになってたのね😳」「テストを見るとわかりやすいかも↓」

# actionview/test/template/date_helper_test.rb#473
  def test_select_year_with_empty_hash_value_and_no_start_year
    expected = +%(<select id="date_year" name="date[year]">\n)
    expected << %(<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n)
    expected << "</select>\n"

    Date.stub(:current, Date.new(2018, 12, 18)) do
      assert_dom_equal expected, select_year({ year: nil, month: 4, day: nil }, { end_year: 2018 })
    end
  end

⚓マイグレーションのremove_columnsをリバーシブルに

removetype:を書いておけばリバースできるようになるそうです。

# 同PRより
class InvertibleChangeTableMigration < SilentMigration
  def change
    change_table("horses") do |t|
      t.column :name, :string
      t.remove :remind_at, type: :datetime
    end
  end
end

つっつきボイス:「マイグレーションのchangeremoveを書けるようになったそうです」「自分は使わないかな〜😆

⚓番外: 細かな修正

# railties/lib/rails/generators/rails/app/templates/config/puma.rb#L7
-threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
+threads_count = Integer(ENV.fetch("RAILS_MAX_THREADS", "5"))
threads threads_count, threads_count

つっつきボイス:「#28499は↑今までブロック渡しだったのを引数に変えた」「#38020はAction Textのドキュメントに補足↓」「そういえばWebpacker関連の記述って今のRailsガイドにほとんどありませんね😆」「WIPなガイドは他にもありますし☺

# guides/source/action_text_overview.md#L47
-Run `rails action_text:install` to add the Yarn package and copy over the necessary migration.
-Also, you need to set up Active Storage for embedded images and other attachments.
-Please refer to the [Active Storage Overview](active_storage_overview.html) guide.
+Run `rails action_text:install` to add the Yarn package and copy over the necessary migration. Also, you need to set up Active Storage for embedded images and other attachments. Please refer to the [Active Storage Overview](active_storage_overview.html) guide.
+
+After the installation is complete, a Rails app using Webpacker should have the following changes:
+
+1. Both `trix` and `@rails/actiontext` should be required in your JavaScript pack.
+
+```js
+// application.js
+require("trix")
+require("@rails/actiontext")
+```
+
+2. The`trix` stylesheet should be imported into `actiontext.scss`.
+
+```scss
+@import "trix/dist/trix";
+```
+
+Additionally this `actiontext.scss` file should be imported into your stylesheet pack.
+
+```
+// application.scss
+@import "./actiontext.scss";
+```

⚓Rails

⚓RailsとDDD

RailsはActiveRecordが密結合で実現しているためDDDのモデリングの考え方と相性が悪く、レイヤードアーキテクチャをやろうとすると型がないことなどDDDの考え方が適応しにくい
同記事より


つっつきボイス:「上みたいな記事を割と見かけるんですがやっぱり難しいんでしょうか?」「RailsでDDDをやるのは簡単ではありませんね🧐: Active Recordモデルを永続化に専念させるとか、いろいろやらないといけなくなりますし」「そうでしたか…RailsとDDDの記事は新しいのが少ないのはそういう事情があるのかも?🤔」「どうしてもやりたいならHanamiとかにする方がいいんじゃね?😆

参考: Rails 歴5年の僕が Laravel で開発するようになって思ったこと。|Kurashicom Engineers’ Blog|note

⚓Active Recordのコールバックを回避(Ruby Weeklyより)

Active Recordのコールバックにビジネスロジックを実装すべきでないと強く信じている。
同記事より抜粋

# 同記事より
class Address
  attr_accessor :disable_geolocation
  before_save :set_geolation, unless: :disable_geolocation

  private

  def set_geolocation
    # Hit some API or something...
  end
end

つっつきボイス:「disable_geolocationがtrueならコールバックしないようにしてる」「そういえば銀座Rails#15でもコールバック消し去りたい話が出てたのを見た覚えがあったんですが、この辺のツイートにあったのをやっと見つけました↓」「kamipoさんとyahondaさんの対談ですね☺

「とりあえず言っておきたいのは、コールバックとconcernsは相性が悪い」「あぁ!」

「そういえばkazzさんはコントローラのbefore_actionコールバックは例外的に好きって言ってました」「コントローラのbefore_actionも気をつけないとカオスになりますよ💀」「うぅ😢

⚓RubyConf 2019@Nashvilleまとめ


同記事より


つっつきボイス:「手描きのまとめが素敵だなと思って❤」「でも他の人が読むにはつらいという😆

⚓その他Rails

つっつきボイス:「RailsとReactとTerraformとスクラム開発やれたらマジ有能」


つっつきボイス:「Rackは思ったより難しくないし記事も多いので、どんどん作って動かしてみるのがいいと思います😋


同リポジトリより

参考: 海外記事翻訳シリーズ 【第 2 回】 Rack 仕様
参考: Rails と Rack - Railsガイド
参考: 第23回 Rackとは何か(1)Rackの生まれた背景:Ruby Freaks Lounge|gihyo.jp … 技術評論社


前編は以上です。

おたより発掘

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

週刊Railsウォッチ(20191217後編)Ruby 2.7の変更点とパターンマッチング、依存性自動アップデートツール、Stack Overflowアンケート2019ほか

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

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

Rails公式ニュース

Ruby Weekly

週刊Railsウォッチ(20191225後編)Ruby 2.7リリースまで後少し、キーワード引数変更の公式ドキュメント、Railsセキュリティ修正でredis-storeがエラーほか

$
0
0

こんにちは、hachi8833です。今年最後のRailsウォッチ後編です。日本時間12/25夕方の時点のRubyはまだトンテンカンテンやっているようです👀。数日前まではrdocなどドキュメントの更新が増えてリリース間近かと思いきや、直近のコミットを見ているとさまざまな修正が続々入っていてどきどきしています。コミッターの皆さま本当にお疲れさまです。


github.com/ruby/ruby/commits/masterより

2.7 rc2のNEWS↓を改めて見てみると改良・変更が山のように多いですね😳。アグレッシブ…


新機能のnumbered parameterからexperimentalが取れましたね。つい「なんぱら」と呼んでしまいます。


キーワード引数のwarningメッセージも細かく改善されています。

# class.c#L2054
-  rb_warn("The last argument is used as keyword parameters");
+  rb_warn("Using the last argument as keyword parameters is deprecated");

以下のように改行を挟んだメソッドチェーンの間にコメントを置けるようになったのも初めて知りました。

foo
  # .bar
  .baz # => foo.baz

なお直近の@81e3770↓では、(..1)minをエラーにするよう変更されていました。(1..)maxはその前からエラーになってたようなので合わせたのかも🤔

# 同コミットより
+   assert_raise(RangeError) { (..1).min }
+   assert_raise(RangeError) { (...1).min }

2.7の新機能について詳しくはjnchitoさんの一連の記事をご覧いただくのが早いですね。


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

お詫び: 今回のつっつき動画に音声が入っていなかったため、最小限のつっつきボイスとなっています🙇

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

第18回目公開つっつき会は、来年1月09日(木)19:30〜にBPS会議スペースにて開催されます。今回から会場のPubスペースが隣の部屋に移ります。

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

⚓臨時ニュース: Railsセキュリティアップデート6.0.2.1 / 5.2.4.1でredis-storeがエラーになる可能性

前回のウォッチ20191223にも追記しましたが、こちらにも再録します。

rack gemのセキュリティアップデート2.0.8がリリースされ、Railsも6.0.2.1でrack >= 2.0.8が指定された。アップデートしてみたところ、redis-storeがマウンタブルエンジン(sidekiqのwebインターフェイスらしい)のところで動かなくなったっぽい。
同issueより大意

redis-storeをお使いの方は、本件が解決するまでセキュリティアップデートを待つ必要がありそうです。
なお、同issueのコメントではredis-storeの修正2つで解決できると追記されていますが、本記事公開時点ではまだredis-storeにマージされていません。

⚓Ruby

⚓2.7のキーワード引数変更と待望の公式解説


つっつきボイス:「jnchitoさんがキーワード引数の変更記事をがっつり書いてくれていました🙏」「ありがたい!😂」「エッジケースがいっぱいあるけどjnchitoさんが書いたコードで影響を受けるのはわずかで済みそうだとのことです」「たしかに、こんな書き方普通しないよなみたいなの多いし😆」「皆さんがこんなに苦労しているのを見て、キーワード引数変更の難しさに納得しました😭

「と思ってたら、ついこの間(12/12)Rubyの公式サイトにキーワード引数変更の詳しい解説記事が出ていました↓」「みんなが欲しかったヤツ🎉」「著者はmameさん、レビュアーはJeremy EvansさんとBenoit Dalozeさん😳」「jnchitoさん記事では言及されていないので入れ違いだったのかも🤔


ruby-lang.orgより


後で上の記事を気合い入れて読んでみましたが、みっちりまとまった力作記事でした❤。キーワード引数変更についての重要な一次資料ですね。

公式解説の見出しより:

  • 概要
  • 典型的なケース
  • どこが非推奨になるか
  • 自分のコードはRuby 2.7で壊れるか?(A:『壊れない可能性はある』)
  • 引数の委譲の扱い
    • Ruby 2.6以前
    • Ruby 3
    • Ruby 2.7
    • Ruby 2.6/2.7/3で共通に使える委譲方法(A: Module#ruby2_keywordsを使う)
  • その他の微細な変更
    • キーワード引数で非シンボルキーが使えるようになる
    • 空ハッシュにdouble splatを付けると(**{})引数が従来の[{}]ではなく空([])になる
    • 非キーワード引数を受け取らないことを示す新しい構文(**nil)が導入される
  • 自動変換を非推奨にする理由

追記: 本日つい先ほど、本命のガチ解説記事が出ました↓。日本語で真っ先に読めるのはありがたいですね😂

⚓Rubyコンカレンシーの進捗レポート(Ruby Weeklyより)

Ruby 3のコンカレンシーモデルについてMatzとkoichiとそれぞれ話す機会があったのでこの記事にまとめる。
同記事より抜粋

記事の見出しより:

  • コンカレンシーとパラレリズムとは
  • 非決定性が持ち込まれるとどうなるか
  • asyncfalconでRuby Webアプリを改善できるが、Ruby実装全体に効く軽量なフックで拡張したい
  • async-ioが提供するIOラッパーは多くの場合暫定的な置き換えとなる
  • コンカレンシーの選択肢:
    • Auto Fiber(#13618など)
    • ラッパー
    • 新しいメソッド
    • 新しいクラス
  • Isolateについて
  • キーワードを追加するかどうか
  • ブロッキングの発生
  • 改変の検出と通知
  • 本件はRuby Association GrantGitHub Sponsorsによって支えられている

つっつきボイス:「Auto Fiberってあったそういえば」「単なるFiberとどう違うんでしたっけ?」「RubyのFiberはcoroutineなので、fiberでマルチスレッドみたいなことをしたければ自分で待ち合わせ処理を書く必要がある、Auto Fiberはマルチスレッドみたいにコンテキスト切り替えを自動でやってくれる感じ☺


参考: class Fiber (Ruby 2.6.0 リファレンスマニュアル)

Thread クラスが表すスレッドと違い、明示的に指定しない限りファイバーのコンテキストは切り替わりません。またファイバーは親子関係を持ちます。Fiber#resume を呼んだファイバーが親になり呼ばれたファイバーが子になります。親子関係を壊すような遷移(例えば自分の親の親のファイバーへ切り替えるような処理)はできません。例外 FiberError が発生します。
docs.ruby-lang.orgより

参考: RubyのGuildとAuto Fiberはどういう名前になれば良いと思いますか? - Quora

(中略)AutoFiberについて。
AutoFiberはnormalpersonことEric Wongが最初にこの機能を提案した時の名前です。機能としてはI/O処理のタイミングでコンテキスト切り替えを行うが、スレッドとは異なり、タイムスライスではコンテキスト切り替えしないというものです。
FiberはCS界ではcoroutineの実現として使われており、暗黙のコンテキスト切り替えを行うAutoFiberはcoroutineではない、すなわちFiberではないという理由で、この名前には難色が示されています。
必要な名前は、一種のスレッドであることを明示しながら、より制限され、より安全である(確率が高く)、より軽量であることを示す名前が期待されます。これがなかなか難しい。
私自身は、最近は「Fiberがcoroutineであることを明示することは実は自明ではなく、さらにcoroutineという単語の定義すら簒奪されつつある昨今、これをAutoFiberと呼んでもよいのではないだろうか」という気になっています。(以下略)
Quoraより

⚓その他Ruby


つっつきボイス:「あぁ、ダブルクォート3つは"""に解釈されるのか😳」「たしかに出力は変わらないけど😆


以下はつっつき後のツイートです🎉


⚓DB

⚓dolt: Git感覚のリレーショナルデータベース(DB Weeklyより)

# 同リポジトリより
$ dolt init
Successfully initialized dolt data repository.
$ dolt sql -q "create table state_populations ( state varchar(14), population int, primary key (state) )"
$ dolt sql -q "show tables"
+-------------------+
| tables            |
+-------------------+
| state_populations |
+-------------------+
$ dolt sql -q 'insert into state_populations (state, population) values
("Delaware", 59096),
("Maryland", 319728),
("Tennessee", 35691),
("Virginia", 691937),
("Connecticut", 237946),
("Massachusetts", 378787),
("South Carolina", 249073),
("New Hampshire", 141885),
("Vermont", 85425),
("Georgia", 82548),
("Pennsylvania", 434373),
("Kentucky", 73677),
("New York", 340120),
("New Jersey", 184139),
("North Carolina", 393751),
("Maine", 96540),
("Rhode Island", 68825)'
Rows inserted: 17

Doitかと思ったらDoltでした。


つっつきボイス:「今度こそキワモノでしょうか?」「へぇ〜、GitのようなインターフェースでSQL的な更新かけてバージョン管理するというのはちょっと面白いですね😋: 住所データベースみたいに更新が頻繁でなくて固くメンテしたいようなユースケースとかなら使いみちあるかも🤔」「それ用のリポジトリまで作っちゃったそうですが、試しに登録してみたらユーザー名に数字を使えないらしくhachi8833は撥ねられました😢」「😆


dolthub.comより

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

⚓AWS RDSプロキシとは


つっつきボイス:「RDSプロキシはこの間のre:Invent 2019で発表されてたヤツですね: 結構よさそうだけどお値段はどうかな?🤔」「↓vCPUになってる理由とか謎😆: 近々追ってみよう」

$0.015 per RDS Proxy vCPU hour
RDS Proxy Pricing - Amazon Web Servicesより

「RDSプロキシは平成Ruby会議の以下のスライド↓で紹介されていたことで知りました」


はみだし: 上のスライドの動きがあまりに見事だったので発表者のAquaLampさんにお尋ねしたところ、パワポの変形(morph)機能でやれるとのことでした。

⚓JavaScript

⚓Node.jsのはかなさ


つっつきボイス:「Node.jsのLTSの寿命って意外に短いんですね…」「まあ他の言語でもそんなに変わりませんけど☺」「アップグレードで動かなくなるライブラリはつらい😭

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

⚓styled-componentとは

# 同記事より
src
├── App
│   ├── Header
│   │   ├── Logo.js    
│   │   ├── Title.js   
│   │   ├── Subtitle.js
│   │   └── index.js
│   └── Footer
│       ├── List.js
│       ├── ListItem.js
│       ├── Wrapper.js
│       └── index.js
├── shared
│   ├── Button.js
│   ├── Card.js
│   ├── InfiniteList.js
│   ├── EmojiPicker
│   └── Icons
└── index.js

つっつきボイス:「styled-componentsが好きでない人が社内にいるらしいと聞いて」「まあ一部ですけど😆」「CSSだけだとグローバルになってしまいがちですし、コンポーネント的に閉じ込めたい気持ちはワカル☺」「割と最近登場したみたいだけど今後どうなるかな?🤔

⚓その他フロントエンド

⚓その他

⚓年末らしい記事


つっつきボイス:「クリスマスですが年末っぽい記事をいくつか」「Makerのトレンドは自作派にとってうれしい記事!😍

「自分はスイッチサイエンス↓で買うことが多いかな〜😋」「ここの品は調査が行き届いてて怪しげなものを売ってないのがいいですね👍: といいつつ秋月電子通商とか千石電商も昔から定番ですが」「自作やってたの昔過ぎて最近の通販事情知らなかった😅

参考: トップページ - スイッチサイエンス


switch-science.comより

「そういえばニキシー管↓で表示したことあったな〜😆」「このノイズ多そうな真空管みたいな表示装置、ニキシー管っていうんですね…初めて知りました😅

参考: ニキシー管 - Wikipedia

「昔のビデオデッキなんかに使われてた蛍光表示管↓というのもありましたね🤓」「大昔の電卓ってこれでした😳」「たしか蛍光表示管も真空技術を使ってたかな☺

参考: 蛍光表示管 - Wikipedia

「ところで今度はこれ作りません?↓」「作りましょうっ😆

⚓番外

⚓宇宙ネタ2題


つっつきボイス:「久しぶりの宇宙ネタ😆」「ナゾロジーさんは目の付け所のいい良科学記事が多くて大好きです❤


後編は以上です。Railsウォッチをお読みいただいている皆さま、いつもありがとうございます!🙇来年もよろしくお願いします&メリークリスマス。

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

週刊Railsウォッチ(20191223前編)Railsセキュリティ修正6.0.2.1と5.2.4.1リリース、Ruby 2.7.0-rc2リリース、ActiveRecordのコールバック回避ほか

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

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

Ruby 公式ニュース

Ruby Weekly

DB Weekly

db_weekly_banner

Artichoke RubyをUbuntu上でビルドしてみた

$
0
0

こんにちは、hachi8833です。「Rustで書かれたRuby」であるArtichoke RubyをRustでビルドしてみました。Gobyもそうですが、ついこういうのをやってみたくなってしまう自分😅


同リポジトリより

artichoke: チョウセンアザミ(キク科の多年草)

特徴

  • Rustで書かれている
  • Ruby 2.6.3(MRI)互換を目指す
  • Playgroundサイトですぐ試せる
    • WebAssemblyベースでビルドされている

設計と目標(Readmeより)

  • WebAssemblyのビルドをサポート
  • 信頼できない環境でのRuby組み込みや実行をサポート
  • Rubyアプリをシングルバイナリ配布する
  • 最新のRuby dependencyを実装
  • VMでの実験的サポート
    • 動的コード生成
    • 先読みコンパイル
    • パラレル
    • GILの排除
    • 洗練されたメモリ管理
    • GC

環境

Macbook + Parallels Desktop上の環境
Linux 5.0.0-25-generic #26~18.04.1-Ubuntu SMP Thu Aug 1 13:51:02 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
Rust: 1.40.0

このLinux環境にはLinuxbrewをインストールしてありますが、自分の場合はRust周りをLinuxbrewベースでインストールすると微妙にうまくいかなかったので、Linuxbrewは使わずにaptベースでやってみました。

clangも必要なので、sudo apt install clangでインストールしました。

インストールとビルド

Rust環境の構築

結局ここが一番メインになります。

参考: Rustのインストール — 公式の日本語訳

現時点で最新の方法でインストールしますが、Rustのインストール方法はちょくちょく変わっているようなので、公式をチェックしてください。

  • 以下を実行してrustupをインストールします。特にカスタマイズはしないので1) Proceed with installation (default)を選択します。
curl https://sh.rustup.rs -sSf | sh

rustupは公式のインストーラであり、rustコンパイラ(rustc)やパッケージ管理(cargo)も含めて必要なものがインストールされます。

  • Artichokeのリポジトリからローカルの適当なディレクトリにgit cloneします。
  • artichokeのディレクトリに移動し、以下を実行してビルドします。

cargo run -p artichoke-frontend --bin airb

なお、自分が最初にビルドしたときはpkg-configがないと言われたので、sudo apt-get install pkg-configでインストールしました。

以下のようにビルドされ、IRB風のREPLが起動します。

$ cargo run -p artichoke-frontend --bin airb
   Compiling libc v0.2.66
   Compiling cfg-if v0.1.10
   Compiling log v0.4.8
   Compiling proc-macro2 v1.0.7
   Compiling unicode-xid v0.2.0
   Compiling autocfg v0.1.7
   Compiling lazy_static v1.4.0
   Compiling syn v1.0.12
   Compiling memchr v2.2.1
   Compiling byteorder v1.3.2
   Compiling bitflags v1.2.1
   Compiling glob v0.3.0
   Compiling version_check v0.1.5
   Compiling semver-parser v0.7.0
   Compiling regex-syntax v0.6.12
   Compiling unicode-width v0.1.7
   Compiling failure_derive v0.1.6
   Compiling shlex v0.1.1
   Compiling proc-macro2 v0.4.30
   Compiling rustc-demangle v0.1.16
   Compiling peeking_take_while v0.1.2
   Compiling vec_map v0.8.1
   Compiling quick-error v1.2.2
   Compiling unicode-xid v0.1.0
   Compiling strsim v0.8.0
   Compiling getrandom v0.1.13
   Compiling ansi_term v0.11.0
   Compiling termcolor v1.0.5
   Compiling scopeguard v1.0.0
   Compiling bindgen v0.50.1
   Compiling rayon-core v1.7.0
   Compiling pkg-config v0.3.17
   Compiling target-lexicon v0.10.0
   Compiling bindgen v0.51.1
   Compiling ppv-lite86 v0.2.6
   Compiling either v1.5.3
   Compiling same-file v1.0.5
   Compiling unicode-segmentation v1.6.0
   Compiling fs_extra v1.1.0
   Compiling nix v0.14.1
   Compiling void v1.0.2
   Compiling once_cell v1.2.0
   Compiling arrayvec v0.5.1
   Compiling smallvec v1.1.0
   Compiling utf8parse v0.1.1
   Compiling downcast v0.10.0
   Compiling thread_local v0.3.6
   Compiling path-dedot v1.1.13
   Compiling crossbeam-utils v0.7.0
   Compiling crossbeam-epoch v0.8.0
   Compiling num-traits v0.2.10
   Compiling num-integer v0.1.41
   Compiling nom v4.2.3
   Compiling semver v0.9.0
   Compiling clang-sys v0.28.1
   Compiling textwrap v0.11.0
   Compiling humantime v1.3.0
   Compiling c2-chacha v0.2.3
   Compiling walkdir v2.2.9
   Compiling heck v0.3.1
   Compiling rustc_version v0.2.3
   Compiling artichoke-core v0.1.0 (/media/psf/deve/ruby/artichoke/artichoke-core)
   Compiling num_cpus v1.11.1
   Compiling jobserver v0.1.17
   Compiling atty v0.2.13
   Compiling time v0.1.42
   Compiling dirs-sys v0.3.4
   Compiling quote v1.0.2
   Compiling aho-corasick v0.7.6
   Compiling fxhash v0.2.1
   Compiling rustc-hash v1.0.1
   Compiling regex-automata v0.1.8
   Compiling memoffset v0.5.3
   Compiling quote v0.6.13
   Compiling cc v1.0.48
   Compiling clap v2.33.0
   Compiling rand_core v0.5.1
   Compiling dirs v2.0.2
   Compiling crossbeam-queue v0.2.1
   Compiling regex v1.3.1
   Compiling bstr v0.2.8
   Compiling cexpr v0.3.6
   Compiling libloading v0.5.2
   Compiling backtrace-sys v0.1.32
   Compiling rand_pcg v0.2.1
   Compiling rand_chacha v0.2.1
   Compiling rustyline v5.0.6
   Compiling synstructure v0.12.3
   Compiling syn-mid v0.4.0
   Compiling chrono v0.4.10
   Compiling env_logger v0.6.2
   Compiling rustversion v1.0.1
   Compiling rand v0.7.2
   Compiling artichoke-vfs v0.5.0-alpha (/media/psf/deve/ruby/artichoke/artichoke-vfs)
   Compiling proc-macro-error v0.4.4
   Compiling proc-macro-error-attr v0.4.3
   Compiling crossbeam-deque v0.7.2
   Compiling rayon v1.3.0
   Compiling structopt-derive v0.4.0
   Compiling structopt v0.3.7
   Compiling backtrace v0.3.40
   Compiling failure v0.1.6
   Compiling which v2.0.1
   Compiling artichoke-backend v0.1.0 (/media/psf/deve/ruby/artichoke/artichoke-backend)
   Compiling onig_sys v69.2.0
   Compiling artichoke-backend v0.1.0 (/media/psf/deve/ruby/artichoke/artichoke-backend)
   Compiling onig v5.0.0
   Compiling artichoke-frontend v0.1.0 (/media/psf/deve/ruby/artichoke/artichoke-frontend)
    Finished dev [unoptimized + debuginfo] target(s) in 1m 31s
     Running `target/debug/airb`
artichoke 0.1.0 (2020-01-10 revision 2283) [x86_64-linux]
[Rust 1.40.0 (rev 73528e3) on x86_64-unknown-linux-gnu]
>>>

ビルドされたバイナリ(airb)はtarget/debug/の下に配置されます。

動かしてみた

上の記事を元に2.6のコードを少し動かしてみました。

$ ./target/debug/airb
artichoke 0.1.0 (2020-01-10 revision 2283) [x86_64-linux]
[Rust 1.40.0 (rev 73528e3) on x86_64-unknown-linux-gnu]
>>> def m(*varargs, **keywords)
  puts "varargs:  #{varargs}"
  puts "keywords: #{keywords}"
end
=> :m
>>> m
varargs:  []
keywords: {}
=> nil
>>> m("a" => 1, b: 1)
Backtrace:
    (airb):6: keyword argument hash with non symbol keys (ArgumentError)
    (airb):6

キーワード引数で非シンボルを渡した場合の警告(Ruby 2.7で出なくなったヤツですね)は出ますが、メッセージがいつもと違う…これはmrubyの警告です。

>>> (1..6).step(2).to_a
Backtrace:
    (airb):7: undefined method 'step' (NoMethodError)
    (airb):7

artichokeではstepが未実装ですね。Rangeにどんなメソッドがあるかを見てみました。

(1..2).methods.sort
=> [:!, :!=, :!~, :==, :===, :Array, :Hash, :Integer, :String, :__case_eqq, :__id__, :__send__, :__to_int, :__to_str, :__update_hash, :all?, :any?, :begin, :block_given?, :catch, :class, :clone, :collect, :collect_concat, :count, :cover?, :cycle, :define_singleton_method, :detect, :drop, :drop_while, :dup, :each, :each_cons, :each_slice, :each_with_index, :each_with_object, :end, :entries, :enum_for, :eql?, :equal?, :eval, :exclude_end?, :extend, :find, :find_all, :find_index, :first, :flat_map, :format, :freeze, :frozen?, :global_variables, :grep, :group_by, :hash, :include?, :initialize, :initialize_copy, :inject, :inspect, :instance_eval, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :iterator?, :itself, :kind_of?, :lambda, :last, :lazy, :load, :local_variables, :loop, :m, :map, :max, :max_by, :member?, :method, :methods, :min, :min_by, :minmax, :minmax_by, :nil?, :none?, :object_id, :one?, :partition, :print, :private_methods, :proc, :protected_methods, :public_methods, :puts, :raise, :rand, :reduce, :reject, :remove_instance_variable, :require, :require_relative, :respond_to?, :reverse_each, :select, :send, :singleton_class, :singleton_method, :singleton_methods, :size, :sort, :sort_by, :sprintf, :srand, :take, :take_while, :tap, :then, :throw, :to_a, :to_enum, :to_h, :to_s, :uniq, :warn, :yield_self, :zip]

なお、以下はRuby 2.7.0のirbで同じことをやってみたものです。

irb(main):001:0> (1..2).methods.sort
=> [:!, :!=, :!~, :%, :<=>, :==, :===, :=~, :__id__, :__send__, :all?, :any?, :begin, :bsearch, :chain, :chunk, :chunk_while, :class, :clone, :collect, :collect_concat, :count, :cover?, :cycle, :define_singleton_method, :detect, :display, :drop, :drop_while, :dup, :each, :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object, :end, :entries, :enum_for, :eql?, :equal?, :exclude_end?, :extend, :filter, :filter_map, :find, :find_all, :find_index, :first, :flat_map, :freeze, :frozen?, :grep, :grep_v, :group_by, :hash, :include?, :inject, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :last, :lazy, :map, :max, :max_by, :member?, :method, :methods, :min, :min_by, :minmax, :minmax_by, :nil?, :none?, :object_id, :one?, :partition, :private_methods, :protected_methods, :public_method, :public_methods, :public_send, :reduce, :reject, :remove_instance_variable, :respond_to?, :reverse_each, :select, :send, :singleton_class, :singleton_method, :singleton_methods, :size, :slice_after, :slice_before, :slice_when, :sort, :sort_by, :step, :sum, :taint, :tainted?, :take, :take_while, :tally, :tap, :then, :to_a, :to_enum, :to_h, :to_s, :to_set, :trust, :uniq, :untaint, :untrust, :untrusted?, :yield_self, :zip]

Artichoke Rubyに実装済みのメソッドは思ったより多いようですが、未実装のメソッドもあちこちにあるようです。issueもかなり立っているので、先は長そうです。

気づいた点

1. バイナリがかなり大きい

シングルバイナリ配布を目指しているとはいえ、REPLバイナリのサイズが50MBというのはかなり大きい気がします。ただし昨年9月頃に試したときは85MBもあったので、かなり改善されていますね。

-rwxrwxr-x 1 hachi8833 hachi8833  50M  1月 10 10:58 airb

ちなみにUbuntuのRuby 2.7.0はバイナリ単体で19MB、参考までにGobyは機能はまだまだ少ないながらREPLも含めて13MBでした。

-rwxr-xr-x 1 hachi8833 hachi8833 190K  1月 10 13:32 ruby
-rwxr-xr-x 1 hachi8833 staff 13M 12 12 22:18 goby

なぜこんなに大きいのかなと思って軽く掘ってみると、artichoke-backend/の下にRuby 2.6.3を丸ごと飲み込んでいることに気づきました😳若干パッチは当たっていますが)。

2. mrubyも実装に使っているらしい

#212を見ると、mrubyにバインドする形でRustで書いたメソッドを追加していることもあるようです。

artichoke-backendのREADMEをざっと見た限りでは、mruby(mruby-sysというmrubyとRustのバインディング)をベースに動かしているようです。

artichoke-coreはどうやら、mrubyに依存しない形でのインタプリタ作りをここで進めているようです。

mruby依存から脱却中?

mrubyとCRubyで端的に動作が異なるコードはないかと思って探すと、以下の記事で1 / 2の結果が異なることを知りました。mrubyではFloatに、CRubyではIntegerになるそうです。以下の記事が書かれた2013年頃のRubyにはBigNumがありましたね。

昨年9月頃の時点でartichokeのairbで動かしてみたときは以下の結果になりました(すみません、当時のバージョンがわかりません🙇)。

>>> 1/2
=> 0.5
>>> (1/2).class
=> Float

その時点でのartichokeではこのあたりがmrubyに依存していたようです。

しかし今回artichoke 0.1.0でやってみると、結果が変わり、mrubyのFloatともCRubyのIntegerとも違うFixnumになっていました。

$ ./target/debug/airb
artichoke 0.1.0 (2020-01-10 revision 2283) [x86_64-linux]
[Rust 1.40.0 (rev 73528e3) on x86_64-unknown-linux-gnu]
>>> 1/2
=> 0
>>> (1/2).class
=> Fixnum

まだ途中経過だと思われますが、artichoke-rubyはものすごい勢いでメンテされているので、今後はmruby依存から脱却してCRubyに近づいていくのだろうと推測しています。

おまけ

shiikaは、純粋にRustで書かれたRubyライクな言語だそうで、LLVMを出力するそうです。まだ自分の環境でビルドできていません😅

関連記事

mrubyをWebAssemblyで動かす(翻訳)

週刊Railsウォッチ(20200114前編)config_forのbreaking change、Active Storage variantをDBでトラッキング、SprocketsとWebpackの違いほか

$
0
0

こんにちは、hachi8833です。すっかり遅ればせながらあけましておめでとうございます🌅。2020年代も週刊Railsウォッチをよろしくお願いします🙇


つっつきボイス:「お〜リニューアルへの反応が、と思ったら今日のツイートでしたか😆: 一応昨年12月26日にリニューアルしたんですけどね☺」「😆

TechRachoのサイトデザインをリニューアルしました

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

今回は第18回公開つっつき会を元にお送りいたします。ご参加いただいた皆さん、ありがとうございます!

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

昨年末の公式情報を中心に見繕いました。

⚓新機能: Active Storage variantをデータベースでトラッキングするようになった

現在はvariantをリクエストされるとそれがサービス内に存在するかどうかをチェックし、ない場合はその場で生成して保存し、再利用に備えている。
この存在チェックによるvariant提供の遅延は容認できなくなることがある。サードパーティのサービスでは通常の環境で最大500msに達する。しかも、アップロード前に特定のキーでオブジェクトが存在するかどうかをチェックすると、S3の最終consistencyがトリガーされる。

Amazon S3では、全リージョンのS3バケットで新しいオブジェクトをPUTSするときのread-after-write consistencyを提供していますが、1つ注意点があります。オブジェクト作成前に(オブジェクトが存在するかどうかの確認のために)キー名をHEADやGETでリクエストすると、S3はread-after-writeの最終consistencyを提供します。

つまり、あるvariantを最初にリクエストして生成しS3に保存すると、以後そのvariantのサービスURLへのリダイレクトが失敗し続ける可能性がある。これによって画像が散発的に破損する。
このPRは存在チェックを行わないことで上の懸念を修正する。あるvariantが初めてリクエストされれば生成して保存し、以後はアプリケーションのデータベースでトラッキングする。これにより、ストレージサービスへのリモート呼び出しを行わずに、variantが生成済みかどうかを確認できるようになった。
同PRより大意


つっつきボイス:「ここで言ってるvariantは、画像をアップロードしたときに複数のサイズを生成して指定できるようにしているアレのことかなと」「サムネイル画像とかに使うヤツですね😋」「Active Storageでvariantが使えるようになってる?🤔」「Rails 6で入ってたと思います(ウォッチ20191111)」「たしかにvariant機能がないと画像アップロードとしてはとてもつらくなるでしょうし😆

「結構差分大きいな😳」「17ファイルも更新されてますね」「改修内容もわかりみ: hoge_bigみたいな画像があるかどうかを毎回S3にAPI経由で問い合わせて、なかったら作るとかやってるとそのチェックが重くなるでしょうね☺」「ふむふむ」「チェック先がファイルシステムとかだったら一瞬でしょうけど、API経由では遅くなるからデータベースに存在情報を持たせておけば、どんなストレージを使っていてもまずデータベースを参照することになるから高速化が期待できる」「なるほど!」「よくあるやり方☺

「ただこれをやると、今度はデータベースとの不整合が発生したときに面倒なことになりますけどっ😆」「S3にあるvariantを誰かが削除しちゃったりとか」「今日出席できなかったkazzさんは、データベースよりもメモリ上に置きたいな〜って」「それは無理でしょ😆: データベースが複数台構成で動かなくなると思います」「あ、そうか😳」「置き場所としてはRedisやmemcachedあたりか、さもなければデータベース上ぐらいでしょうね☺

参考: Redis
参考: memcached - Wikipedia

「あとサービスによってはvariantの生成がめちゃ重くなることがありますし、Pixivみたいな画像中心のアプリだとvariantが膨大になるでしょうし」「想像つきます😆」「Active StorageもそうですしCarrerWaveでもShrineでもPaperclipでもそうなんですけど、variantをいつ生成するかというのが共通のテーマになってますね: アップロード時にvariantをプリレンダリングするのか、あるいはここでやってるみたいにオンデマンドで生成するのかはだいたい悩ましいポイントです😭」「たしかに」「画像をバンバン扱うようなシステムではこの辺が大事🧐

「プリレンダリングならいったん生成が終われば後は速いんですけど、たとえば後でvariantの定義が変わると全部再生成しないといけなくなるのが厄介😇」「あ〜」「bigとsmallに後からmiddleを足すとか😆: 画像の数やサイズが大きいとめっちゃ重くなりますし😆」「それ用のrakeタスク的なものはだいたい必要になるので探せばたぶん何かしらあると思います」

⚓shrine gem

「shrineって、これも画像アップロード用のgemでしょうか?」「はい: 今だと一番いい感じに動いてくれるアップローダでしょうね」「おぉ〜😂」「Shrineは評判もいいですね」「欲しい機能はひととおりあるしメンテもされてますし☺: 去年バージョン3が出たんだったかな」


shrinerb.comより


「ちなみにBPSでやっているRails製の漫画のサービス↓では、漫画の画像サイズが大きいのでたしかプリレンダリングでやってたと思います: オンデマンドでやると最初の人のアクセスが激重になるので」「へ〜」(以下PDFやepubの話で盛り上がる)

⚓TagBuilderに条件値をハッシュで渡せるようになった

このPRはTagBuilderに条件値を渡すサポートを追加する。https://github.com/JedWatson/classnamesにヒントを得て、GitHubアプリケーションで使っているロジックを切り出して使っている。
RailsのビューでCSSクラスの適用に条件をつけるのはよくある手法だが、以下のように三項演算子の式展開だらけになりがち。

content_tag(
  "My username",
  class: "always #{'sometimes' if current_user.special?} another"
)

TagBuilderにハッシュのサポートを追加したことで、今後は以下のように書ける。

content_tag(
  "My username",
  class: ["always", "another", { 'sometimes' => current_user.special? }]
)

同PRより大意


つっつきボイス:「この書き方はどこかで見たような🤔: あ、去年見かけたVue.jsだかReactっぽい書き方できるヤツ?(ウォッチ20191216)」「それっぽいですね」「従来は条件付けした文字列を無理やり式展開で押し込んでたけど、配列の中で条件をハッシュで渡せるようになったと」「あ、そういうことですか😳」「たしかに論理的にはこっちで書ける方がいいですよね😋: 式展開と違ってコードのトラッキングもやりやすいですし👍」「下の書き方の方が好き❤」「rubocopのチェックとかも効きやすいでしょうし☺

⚓Rails::Application#config_forが共有設定をネスト含みでマージするようになった

自分は環境固有の設定のためにgemをいくつか使っているが、これをRailsの機能に統合できればと思う。しかし自分の設定はネストが深く、現在のRails::Application#config_forでは設定をマージできない。

# config/application.yml
shared:
  foo:
    bar:
      baz: 1
development:
  foo:
    bar:
      qux: 2

# 現在
config_for(:application)[:foo][:bar] #=> { qux: 2 }
# こうしたい
config_for(:application)[:foo][:bar] #=> { baz: 1, qux: 2 }

これがbreaking changeであることは承知している。デフォルトの振る舞い変更が難しいとかあれば、オプションとして制御できるようにするのはどうだろう。
同PRより大意


つっつきボイス:「これはRails標準のconfigの変更か: 書いてあるとおり超breaking changeですね😇」「おぉ?」

「以前からsettingslogicというgemやrails_configというgemがあるんですけど、それでも同じようなことをやれますね」「昔そのあたりをブログに書きましたけど探さないでください🤣」「🤣

編集部注: rails_config gemは現在configに名前が変わりましたが、紛らわしいので本記事では旧名のrails_configで表記します。

⚓Rails設定用gemの挙動

「settingslogicってたしかこんな挙動(メモ書きを始める)」

# default.yml
- production
  - categories
    - hoge
      - piyo
    - huga
    - foo

# production.yml
- production
  - categories
    - hoge

「settingslogicは、キーがかぶるとそれ以下を全部消して上書きする: rails_configの場合はキーがかぶるとマージ(と言っていいかどうかですけど)的に扱う」「まあマージでいいんじゃないでしょうか☺」「そしてsettingslogicはこの挙動が事故りやすいという😆」「そうそうっ😤」「default.ymlを更新するとproductionで上書きされて反映されなかったりするというのがありがちなパターン😇: 追加したはずのキーがなくてproductionでエラーを吐くとか😆」「地獄感ありあり☠

# settingslogicを使う場合
- production
  - categories
    - hoge

# rails_configを使う場合
- production
  - categories
    - hoge
      - piyo
    - huga
    - foo

「rails_configならdefaultの方で少なくともエラーにならないように設定しておいてからproductionの値を設定するんですけど、settingslogicはproductionに入れ忘れると死ぬ😇: そして従来のconfig_forも上書きする挙動なのでそれと同じことが起きます」

⚓breaking changeの影響は?

「たぶん今回の変更は同じキーがあったときにマージしたいということなので、config_forをrails_config風味にしたいということかなと😆: 思いっきりbreaking change」「どのぐらい影響出るんでしょう?🤔」「う〜ん、設定にもよるので予測難しいけどこれで死ぬことが割とあるかもしれませんね👹: こういうconfigはgemとかでカスタマイズされてることもよくあるので」「う〜む😅

「このPRはもうmasterにマージされちゃってますし😇」「設定を上書きじゃなくてマージする挙動になるならそんなに事故らないかな?」「でもたとえばdevelopmentとproductionで異なるSMTPサーバーを指定していた場合に、マージされるとdevelopment用のSMTPサーバーの設定もproductionに残ることになるから、これはこれで事故るんじゃないかな〜🤔」「あ〜ごめんなさい事故りますねたぶん😅」「知らないうちに設定が変わるのコワイ😱」「こんな変更をあっさりマージしちゃって大丈夫?っていう気持ちにちょっとなりますね…」「ちょっとドキドキする😓」「ドキドキする😇

「settingslogicとかを使ってる人にも影響あるんでしょうか?」「いえ、settingslogicなんかはRails本体のconfigとは違うところで設定するので関係ないですね」「そうでしたか😳」「Rails本体とはクラスの空間が違うので両者を共存することはできます☺

「このPRがmasterにマージされたのは昨年12月9日か〜」「6.0.2.1には入ったんだろうか?😅

後で調べると6-0-stableにはまだ入っていませんでした↓。

参考: rails/CHANGELOG.md at 6-0-stable · rails/rails

config_forではたとえばsharedの設定をdevelopmentで上書きできなくなるということですよね?」「ということでしょうね〜」「まあビジネスアプリの重要な設定をRailアプリ本体のconfigに直接書くことは普通しないと思いますし、自分はそういうときはrails_config gemとかを使うので、少なくとも自分のアプリは踏まないと信じる🤣」「🤣」「ただ他のgemが踏むかもしれませんけどっ😆

⚓Active SupportのRange#include?をdateやtimeの値に利用することが非推奨化された

dateやtimeのrangeに値が含まれているかどうかのチェックにRange#include?を利用することは非推奨化される。今後はRange#cover?を使うことが推奨される。
CHANGELOGより大意

参考: cover? — ActiveSupport::CompareWithRange


つっつきボイス:「今後はTimeWithZoneではcover?を使えと」

# activesupport/lib/active_support/core_ext/range/include_time_with_zone.rb#L11
    def include?(value)
-     if self.begin.is_a?(TimeWithZone)
-       cover?(value)
-     elsif self.end.is_a?(TimeWithZone)
+     if self.begin.is_a?(TimeWithZone) || self.end.is_a?(TimeWithZone)
+       ActiveSupport::Deprecation.warn(<<-MSG.squish)
+         Using `Range#include?` to check the inclusion of a value in
+         a date time range is deprecated.
+         It is recommended to use `Range#cover?` instead of `Range#include?` to
+         check the inclusion of a value in a date time range.
+       MSG
        cover?(value)
      else
        super
      end
    end
  end
end

「deprecatedの理由は何だろう?🤔」「issueに書いてありそう↓」「そっちでしたか😅」「include?だと実装の問題があるから使って欲しくないということみたい」

この拡張はどちらかというと削除したい。include?は、Range.to_aを返さない値で動くべきではない。現在の拡張を非推奨にして代わりにcover?を使うよう周知をお願いしてもよい?
同issueコメントより

⚓ActiveSupport::NumberHelper::RoundingHelper:round_modeが追加

    ```ruby
    number_to_currency(1234567890.50, precision: 0, round_mode: :half_down) # => "$1,234,567,890"
    number_to_percentage(302.24398923423, precision: 5, round_mode: :down) # => "302.24398%"
    number_to_rounded(389.32314, precision: 0, round_mode: :ceil) # => "390"
    number_to_human_size(483989, precision: 2, round_mode: :up) # => "480 KB"
    number_to_human(489939, precision: 2, round_mode: :floor) # => "480 Thousand"
    485000.to_s(:human, precision: 2, round_mode: :half_even) # => "480 Thousand"
    ```

つっつきボイス:「数値ヘルパーに:round_modeオプション、たしかに欲しいヤツ😋」「切り上げ切り捨て四捨五入ですね」「Ruby自体にそういう機能はありますけど😆」「その機能をnumber_to_なんちゃらヘルパーのオプションとして使えるようにしたんですね」「ビューの責務を考えたら、ヘルパーのオプションによる丸め処理はあくまでビューの表示のためのものだというのを示すのにはいいのかもしれませんね: 数値自体を丸めてるんじゃなくて表示の書式を整えてるだけだよ、みたいな☺」「このオプション、後で思い出せるかな😆

⚓Rails

⚓「WebpackをSprocketsのように使うな」


つっつきボイス:「Webpacker周りを追っててこの記事にたどりつきました: ここではお集まりの皆さまだけにこの記事のドラフト翻訳をお見せしますが、近日TechRachoで公開しますのでお楽しみに😋」「SprocketsとWebpackerは別物ですから😆」「今更ではありますけど、そのあたりをまとめて説明してくれてるのがいいなと思いました😋

参考: アセットパイプライン - Railsガイド
参考: rails/sprockets-rails: Sprockets Rails integration

「記事によると、Sprocketsでないとできないことがあるようです」「元々SprocketsとWebpackは役割が違いますからそうでしょうね: 個人的にはSprocketsとWebpackが両方あると混乱しそうだからやめときたいですけど😆」「Sprocketsを残すかどうかずっとモヤモヤしてたんですけど、これで踏ん切りがつきそうです😆

「RubyでJSのコードを扱いたい場合、特にRailsのコンテキストで扱いたい場合はたぶんSprocketsの方が向いていますね: WebpackはRailsのコンテキストでは動かないので🧐」「まさにそのあたりの話が記事にありました」「Railsのアプリケーションconfigを使ってJSの自動生成部分に値を埋め込みたい、みたいなのをやりたいときとかありますよね: たとえばアセットのプリコンパイル結果にRailsの動作モードを入れたいなんてのは(やるべきではありませんが)、Sprocketsじゃないとやれませんね☺

「そういえばRailsがWebpackに対応して間もない頃にダイジェストとか設定とかをWebpackでどう扱うかが議論になってた覚えがありますね😆: Railsのコンテキストにある値をWebpackでどうやってJSに渡すか、そのためにJSONを作って渡すとか何とか」「ちょっと無理やり感😆」「でもやりたいシチュエーションはありますから☺」「WebpackだとRailsのconfigの値を渡せないので、Railsのconfigと重複するのを承知でWebpackのconfigにも値を書くのか、とか」「それは気持ち悪いです😆」「でしょ😆」「この記事に『SprocketsをWebpackのように使うな』って書いてあるのはわかります😋

「RailsガイドにもWebpackerとSprocketsをどう扱うかという情報がほぼなくて困ってました😢」「まあRails 6でやるならもうSprocketsのことは忘れてWebpackしか存在しない世界だと思って進めるのがいいんじゃないでしょうか😆」「それもそうですね😂」「記事の人も、自分は併用を極力避けるけどSprocketsでないとできないことがあるのは知っておく価値はあるという感じでした」「そういう記事をひととおり読んでみれば、併用するのはやめておこうという気持ちになれますよ🤣


「ついでにyarn autocleanで不要なファイルをクリーンアップできるらしいことを知りました↓」「CIとかデプロイでは普通に使いそうですね☺

⚓Zeitwerkへの移行


つっつきボイス:「短い記事です」「これ系の記事はいっぱいありますし☺」「最後の方にある以下をRailsコンソールで実行するとdevelopmentモードでもZeitwerkの読み込みをチェックできるそうです」「読み込み順序に問題があればこれで確認できますね😋

# 同記事より
Zeitwerk::Loader.eager_load_all

⚓ネストあり/なしルーティングをRESTfulらしく実装する


つっつきボイス:「なるほどこの辺のお話↓」


同記事より

「ネステッドだと上から4番目の/magazines/:magazine_id/ads/:idみたいになりますよね: 知ってる人はとっくに知ってますが😆」「ふむふむ」「そしてネステッドとそうでないルーティングが両方定義されている場合はネステッドの部分を判別して書かないといけない: 以下のif !params[:landlord_id]はネステッドのときだけ通る↓、とか🧐」「なるほど」「この辺も知ってる人には今更ですが😆

# 同記事より
def show
  if !params[:landlord_id]
    id = params[:id]
    tenant = Tenant.find(id)
    render json: tenant
  else
    id = params[:id]
    landlord_id = params[:landlord_id]
    tenant = Landlord.find(landlord_id).tenants.find(id)
    render json: tenant
  end
end

「ただ個人的にはネステッドとそうでないルーティングを両方書くのは事故の元なのでやりたくありませんけどっ😆」「😆」「同じリソースに複数のルーティングがあるのってどうかと思いますし😆

「両方書かないといけなくなるシチュエーションってあるんでしょうか?」「本来は1つのURLでアクセスできるべきなんですけど、まああるとすればSEOの都合でもっと短いURLにしてくれという注文が来るとか😆、あとはフロントエンドのフレームワークが別のURLを要求してくるとかですかね〜」「なるほど!」「そもそも自分はネステッドルート好きじゃありませんし、おすすめもしませんが🤣

「ネステッドで厄介なのは、たとえば記事にあるこういうURL↓でlandlord_idの部分が今後変わったりしたときですね😇」「おぉ?🤔」「普通であれば、あるtenantのidに紐付けられるlandlord idが変われば上のURLはアクセスできなくなりますけど、下ならパーマネントなURLとしてtenantのidにアクセスできますね…あ、そういうパーマネントURLも用意して欲しいという要件はありそう」「たしかに」「ネステッドとそうでないルーティングってそこまで考えずに設計されてることもよくあったりしますし🤣

# 同記事より
Nested Path: localhost:3000/landlords/:landlord_id/tenants/:id
Un-nested Path: localhost:3000/tenants/:id

⚓GitHub Actions記事


つっつきボイス:「GitHub Actionsはまだ触れていないけど速くて安くていい感じみたいですね😋

「記事にもありますけどEOLが切れると致命的なんですよね: 業務システムだと古いRubyを使ってるとか普通によくあるので、ある日突然CIが動かなくなるとか😇」「😇

⚓初心者向けRailsパフォーマンス最適化のコツ5つ


つっつきボイス:「タイトルにもあるようにパフォーマンス初心者(noobs)向けの記事です」「定番の嵐😆」「せやなの嵐😆

見出しより大意:

  • 1. 半端に最適化しないこと
  • 2. プロファイリングとベンチマークをやってからにすること
  • 3. すごい技を繰り出すよりも足を引っ張る要素を取り除くのが肝心
  • 4. ほぼほぼデータベース(の使いこなし)でつまずく
  • 5. 速くなったっぽいなら速いのだ
  • 6. ほぼほぼRubyのせいではない

「4.といえば、データベースを使いこなせてなくてmap使って遅くなってるケースをちょくちょく見かけますね🤣」「あ〜ありそう🤣」「データベース側でsumすれば瞬殺なのにわざわざオブジェクトに落としてmapするとか🔰

「『ほぼほぼRubyのせいではない』も😆」「PHPでもどの言語でもちゃんと書けば速くなりますし🚄」「言語のせいにしてはいかんと☺

⚓2020年のベストクロスプラットフォームモバイル開発ツール


つっつきボイス:「あ〜モバイル開発か」「出たC#😆」「他にも懐かしいものがチラホラ」(以下延々)

見出しより:

「なぜか最後はRails😆」「RubyMotionっていうRubyでモバイルアプリを書けるアレかと思ったら違った😆

⚓その他Rails

つっつきボイス:「Ryan Biggさんは以下の翻訳記事↓でお世話になりました」

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


つっつきボイス:「見えないテキストボックスって、Railsガイドで言うハニーポットフィールド(おとりのフィールド)のことだと思うんですが、だんだん通用しなくなってきたんですね」「この辺のCAPTCHA方面は今いろいろアツいですよ😆」「自前で作るよりGoogleのCAPTCHA入れとけばいいんじゃね?😆

参考: Rails セキュリティガイド - Railsガイド
参考: CAPTCHA - Wikipedia

「ハニーポットって一般用語?」「invisible_captcha gemにもhoneypot:ってあるな↓」「ここに入力があったら例外吐けばいいんだから実装はそんなに大変じゃなさそうですけど☺

# https://github.com/markets/invisible_captchaより
class TopicsController < ApplicationController
  invisible_captcha only: [:create, :update], honeypot: :subtitle
end

「あとはどのぐらい効果があるかですね: ブルートフォース的なのはこれではじけるとしても悪意のあるアクセスをどのぐらい防げるのかな、とか」「ないよりはマシぐらいなのかな🤔」「ブラウザのオートコンプリートで入っちゃったりしますかね?🤔」「hiddenフィールドだったら入らないでしょうけど普通の入力フィールドだと入っちゃうでしょうね: invisible_captchaのREADMEにもオートコンプリートはオフにしろと書いてありますし」「ほんとだ」


つっつきボイス:「同書は初心者よりベテランにおすすめだそうです🎉」「特集1はたしかに初心者向けだけど特集2のWebpack/Sprocketsあたりから急にレベル高くなってる😆」「Rails経験があってもフロントの知見がないと手こずりそう😆」「途中からトップギア🏎



前編は以上です。

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

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

Rails公式ニュース

週刊Railsウォッチ(20200115後編)Ruby 2.7関連情報、Bootstrap 5は今年前半リリースか、PostgreSQLでやってはいけないリストほか

$
0
0

こんにちは、hachi8833です。Sprocketsは外してWebpackに一本化する決心がつきました。

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

今回も第18回公開つっつき会を元にお送りいたします。ご参加いただいた皆さん、ありがとうございます!

⚓Ruby

⚓Ruby 2.7関連ドキュメントや記事など

2.7情報は今年最初のRubyWeeklyを見る方が早そうです↓。


つっつきボイス:「Ruby Referenceは、英語の公式Rubyドキュメントの新しい部分をzverokさんがセミオートでかき集めてビルドしているドキュメントサイトです(ウォッチ20180413)」「そういえばあったな〜」

⚓Ruby Referenceを眺めて

「ちょうどRuby Referenceで見えたけど、このOpen3↓ってなぜ3なのかが一見わからないけど使ってみるとああなるほどってわかるヤツ😆」「これ何でしょう?」「stdinとstdoutとstderrを同時に開くライブラリですね☺」「だから3なのか〜」「と思うんですけどね(他に考えられない)😆」「capture3とかcapture2も謎😆」「もう少しマシな名前なかったのかと😆


rubyreferences.github.ioより

「たしかOpen3ってRuby以外にもあったはず↓」「なるほどPerlでしたか😳

参考: IPC::Open3 - perldoc.perl.org

⚓RubyのJITとパターンマッチ


つっつきボイス:「上はk0kubunさんのJITプログレス記事です」「2.6でJITが入ってから2.7までの進捗!」

見出しより:

  • Ruby 2.7リリース
  • Ruby 2.7で自分が実装した部分
  • Ruby 2.7で自分がコミットしなかった部分
  • Ruby 3.0で自分がやる予定の部分

「RailsでのJITは手こずってるみたいですね」「そこは確かに難しいでしょうね」「たとえば最適化をRubyに全部おまかせにするんじゃなくて、JITを走らせる/走らせたくないタイミングとかについてコードの側からある程度ヒントを与えるとかしないと、Railsみたいなリクエスト/レスポンス型のアプリの最適化は難しいのかなってちょっと思いますね🤔

後でk0kubunさん記事を急いで読んでみると「JITでのRails最適化はまだやれると思う」「以下の戦略でRailsベンチマークによく効く最適化の導入を考えている」とありました。

  • インストラクションベースの最適化を推し進める
  • インライン化を推し進める
  • Cで書かれたメソッドの種類の自動識別

⚓さっき拾ったツイートより

つっつきボイス:「kamipoさんがRailsのコード例で回答してくれてますね」「なるほど、インスタンス変数でチェックしてメソッド呼び出しを避ける最適化↓」「Rubyのメソッド呼び出しの遅さはライブラリのコードだと気になるでしょうね☺」「こういう部分にJITが効いてくれたら嬉しい!」

# 同PRより
      def read_attribute_before_type_cast(attr_name)
-       sync_with_transaction_state
+       sync_with_transaction_state if @transaction_state&.finalized?
        @attributes[attr_name.to_s].value_before_type_cast
      end

self.classで呼ぶと参照し直しになって遅くなる面もありますね」「20%速くなるってスゲエ😳」「それだけ呼び出し回数が多いということか😳

# 同PRより
      def pk_attribute?(name)
-       name == self.class.primary_key
+       name == @primary_key
      end

⚓Ruby Trunkより

いずれもまだcloseしてないissueです。

つっつきボイス:「#16488はAction Mailerに絡んだruby2_keywords周りの修正のようです(#38105)」「こういうエッジケースは指摘をもらわないとなかなか気づけないでしょうし☺」「そういえば2.7でruby2_keywordsが入ってましたね😳」「Ruby 3でbreaking changesになる部分をRuby 2.6以下のロジックでアクセスできるようにするものだったと思います」「後方互換性用でしょうか?」「あと移行もですね☺

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

module YourModule
  ruby2_keywords def delegating_method(*args)
    other_method(*args)
  end
end

「#16468はちょっと毛色を変えて素数判定周りです」「Prime.prime?のアルゴリズムを変更すると速くなるぞと😋

参考: ミラー–ラビン素数判定法 - Wikipedia

⚓unodos: 数列を推測するgem


つっつきボイス:「ruby-jpで見かけました」「なるほど、フィボナッチ的な数列を渡すと式を推測してくれると」「数学科出身のkazzさんにこのgemの話をしたら、そんなに大変じゃなく作れそうって言ってました」

# 同リポジトリより
require 'unodos'
Unodos[1,2].take(5) # => [1,2,3,4,5]
Unodos[1,2,4].take(5) # => [1,2,4,8,16]
Unodos[1,1,2,3,5].take(8) # => [1,1,2,3,5,8,13,21]
Unodos[1,1,2,4,3,9,4,16,5].take(10) # => [1,1,2,4,3,9,4,16,5,25]

「推測結果をruleで見られる↓のがいいですね😋」「自分の期待する式になるかどうかはわかりませんけど😆」「数列からどこまで複雑な式を生成できるかを遊んでみたら楽しそう❤」「ああやっとわかったかも😆」「つかそうやって遊ぶためのgemなのかなって😆」「コードゴルフ的にどれだけ長い式を出せるかという飛距離を競うとかありそう😆

# to see the generated rule
Unodos[4,1,0,1,4,9].rule # => "a[n]=4-4*n+n**2"
Unodos[1,2,4,5,7,8].rule # => "a[n]=-a[n-1]+3*n"

「と言ってる間にピザが到着しました🍕」「21:00になったら飲み食いしながらウォッチドラフトを眺めましょうか😆

⚓その他Ruby


つっつきボイス:「おおWindowsのRubyInstallerだ」「今もメンテされてるのがエライ🙏」「WSL2が出た後の世界で使うことがあるかどうかですけど😆」「そもそも矢印キーで履歴辿れなかったんですね😳」「Windowsのターミナルに対応するのは大変ですよ〜😆

参考: WSL2で劇的に変わるあなたのWebアプリケーション開発環境【その2:導入編】 | SIOS Tech. Lab


つっつきボイス:「公開つっつき会もアンチハラスメントポリシーを設定してもいい頃かも🤔」「RubyKaigiに準拠でおk😆」「公開しておくことに意義がありますし☺

後で見つけたツイートの「ぼっち対策」ってどんな感じで行われたのか気になります。私もぼっちなので。

⚓DB

⚓(公式)PostgreSQLでやってはいけないことリスト(Postgres Weeklyより)


つっつきボイス:「何しろ公式なので😆」「『table inheritance使うな』とか」「table inheritance、実はそんなにキライじゃないです😆: productionで使ったことありますし」「おぉ😳」「そういえばGitLabで問題になったことありませんでしたっけ?😆」「まあまあ😆」「moneyって型があるとは」「ぽすぐれはいろんな型ありますし☺

「そういえば!=という条件が遅いっていう話をkamipoさんがどこかにMySQLがらみか何かで書いてた気がします: 実際そうで、!=は基本的にデータを全精査しないと取れませんし」「言われてみればという感じ☺

参考: パフォーマンスの遅いSQL。インデックスを使わないSQLとは? | Oracle初心者でもスッキリわかる

使ってはいけないもの(同Wikiより):

  • エンコード
    • SQL_ASCII
  • ツール
    • psql -W or --password
    • rule
    • table inheritance
  • SQL
    • NOT IN
    • 大文字のテーブル名やカラム名
    • BETWEEN(特にtimestampで)
  • Date/Time
    • タイムゾーンなしのtimestamp
    • timez
    • CURRENT_TIME
    • timestamp(0)timestamptz(0)
  • テキスト
    • char(n)(固定幅のidにも使わないこと)
    • 上限指定のあるvarchar(n)をデフォルトにする
  • その他
    • money
    • serial

⚓PostgreSQLのささやかなベストプラクティス(Postgres Weeklyより)


つっつきボイス:「短い記事で、割と定番かなと」「運用向けという感じですね」

見出しより:

  • 接続文字列と環境変数を使う
  • credentialは定期的にローテートする
  • 主キーにはBIGINTかUUIDを使う
  • connection poolingを使う

「そういえば主キーにUUIDを使うのってどのぐらい普及してるんでしょうね: RailsでもUUID使うべきという人がいたりしますけど、ルーティングのURLがめちゃめちゃ長くなりますし😆」「😆」「まあRailsデフォルトの推測可能なサロゲートキーはそれはそれで悩ましいですけど😅

参考: UUID - Wikipedia

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

⚓AWSのモダンアプリケーション開発ホワイトペーパー


つっつきボイス:「はてブであがってた記事で、昨年末のre:inventでの発表内容にさらに手を加えたものだそうです」「まあこういうドキュメントは3年経っても『モダン』がついたままだったりしますけど😆」「最新はすぐ最新でなくなるの法則😆」「年号でやろう😆」「2020年版とか😆」「会場の本棚にもある定番のタネンバウム本↓も、いつのモダンやねんという感じですし🤣」「名著ですけど😆」「それは間違いない😆」「本の奥付見ると2004年ってなってるし😆」「モダンジャズが全然モダンじゃないのと同じ😆」「最早モダンにレトロな響きしか感じられない😆

参考: モダンオペレーティングシステム Tanenbaum, Andrew S(著) - ピアソン・エデュケーション | 版元ドットコム

⚓その他クラウド

つっつきボイス:「そもそも今まで無料だったとは💰」「今の請求項目はダミーみたいですけどもう少ししたら本気出すみたいです」「そういえばAWSのEIPだと、使っているIPは無料で使ってないIPは有料ですね🧐

参考: AWS Elastic IP の料金を理解する
参考: GCP 外部 IP アドレス料金

GCPは無料枠に該当しない場合に料金がかかるようになるようです。

⚓JavaScript

⚓Bootstrap 5は今年前半にリリースされそう


getbootstrap.comより

v4の12カラムはv5でも変わらないようです。


つっつきボイス:「個人的にこれが気になってました」「おそらくearly 2020リリースだろうということで、あとjQueryとIE10以下サポートが消えるそうです」「ついに消えますか!」「かつてはflexboxすら使えないIE9の地獄のブラウザハックを避けたかったのでBootstrapが欲しかったというところがありましたけど、その頃に比べれば、ないとやっていけないというほどではなくなったかもですね☺」「Bootstrap 3から4への移行に比べれば4から5への移行はそんなに大変ではないのかなという雰囲気ですね」「逆に5にしないといけない理由もそんなにないかも😆

v5でのJekyllからHugoへの移行ってBootstrapとどう絡むんだろうと思ったら、主にドキュメントサイトの話のようでした(#28014)。

⚓その他JS

つっつきボイス:「~.構文ってまだ確定じゃないですよね?」「プロポーザルの段階みたいです」「皆さんもRubyでやってるみたいに記号があるとつい使いたくなったりしません?😆」「あると使いたくなる的な😆

参考: ハンマーを持つ人には全てが釘に見える - 橋本商会


つっつきボイス:「社内Slackで見かけました: 前にもウォッチで取り上げたJSのprivate記法がついに入ったそうです(ウォッチ20190902)」「例の#でprivateを表すヤツ😆」「あくまでTypeScriptの中でですけど☺

class Greeter {
    #name: string;
    constructor(name: string) {
        this.#name = name;
    }
    greet() {
        console.log(`hello ${this.#name}`);
    }
}

const greeter = new Greeter("world");
console.log(greeter);                 // logs 'Greeter {}'
console.log(Object.keys(greeter));    // logs '[]'
greeter.greet();                      // logs 'hello world'

「変数がprivateかどうかを名前でわかるようにしたいという気持ちはちょっとワカル: たいてい物議を醸しますけど😆

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

⚓HTTP関連の動向

上の記事で言及されているWeb Authenticationのガイドも別途見つけました↓。


つっつきボイス:「asnokaze.hatenablog.comさんのブログがHTTPの仕様に関する記事が豊富だったので」「この人は有名ですね☺」「Web PackagingとかSigned HTTP Exchangesとかいろいろ提案されてるみたいでクラクラしてきました😅」「や〜っぱりHTTPは難しいですよ😆: 大学の授業で教えるときに去年動いたコードが今年突然動かなくなったりするの勘弁して欲しい😭」「Web Authenticationって一般用語かと思ったら固有名詞でした😅」「WebAuthnはブラウザの機能としてだいぶ前から進めているヤツですね☺

参考: WebAuthn - Wikipedia

⚓去年の流行りのスニペット


つっつきボイス:「楽しいスニペットがいろいろ並んでて、いたずら系も混じってます😆」「CodePenはプログラマーじゃなくても動かして楽しめるあたりがやっぱりよくできてますね😋」「こういうCSSアニメーションとか定番↓」

See the Pen
CSS Loading Animations
by Alex (@AlexWarnes)
on CodePen.

「ちょうど昨日学生にanime.js↓の話しましたよ😎」「お、こんなのあるんですね」「キレイなものを作るには相当気合が必要ですけどって学生にも言いましたし😆」(以下学生向けのコーディング課題などについて延々)

See the Pen
Easings animations with anime.js
by Julian Garnier (@juliangarnier)
on CodePen.

⚓その他フロントエンド

つっつきボイス:「パスワードチェックアップ拡張!」「お漏らし済みのパスワードを使うとChromeで怒られるようになるそうです」「ハッシュが一致するかどうかをチェックする感じでしょうね」「早速インストールしよっと😋」「だいぶ前に米国のPSNで流出したときにガチで引っかかったことあったから怒られる自信あるっ😆: お大丈夫と出た🎉」「私も『あんたのパスワード知ってるよ』って大昔に使ってたパスワードを通知する英文メール受け取ったことあります😅」「平文パスワードを保存している業界は天に召されて欲しい😇

参考: PlayStation Network個人情報流出事件 - Wikipedia


つっつきボイス:「お、このドキュメント良さそう❤


apple.comより

⚓言語・ツール

⚓乱数

参考: メルセンヌ・ツイスタ - Wikipedia — 通称「MT」


つっつきボイス:「MTって何だろうと思ったらメルセンヌ・ツイスタでした」「ちょうど弊社のメンバーが昨年のアドベントカレンダーで乱数記事出してましたね↓」「乱数は結構楽しいですよ〜😋: 速い乱数とか真の乱数とは何かとか」「Unixの/dev/random/dev/urandomの違いとか😆」「なんで2つあるんだろうと思って調べて納得するという」「そして忘れる🤣」「違いがあるということだけ覚えてる😆

参考: /dev/random - Wikipedia

記事のLaTeXや埋め込みがリニューアル後にちょっと乱れてますが近々修正します🙇

乱数について本気出して考えてみる

「他にも/dev/st0(巻き戻し可能なテーブデバイス)と/dev/nst0(巻き戻ししない)の違いとかね😆」「自宅でDDS3のテープデバイス使ってたことありますけど、音を立てて動くのが楽しいですよ❤: tarコマンドを文字どおりにテープバックアップに使ってましたし😆」「tarコマンドってtape archiveの略なんですよね😆」「mtコマンドで頭出ししたりとか😆」「仕事でDATにバックアップしてたことならありました😆」(以下延々)

参考: tape backup
参考: 【 tar 】コマンド――アーカイブファイルを作成する/展開する:Linux基本コマンドTips(40) - @IT
参考: mt - コマンド (プログラム) の説明 - Linux コマンド集 一覧表
参考: デジタル・データ・ストレージ - Wikipedia

⚓その他

このあたりからは親睦会で飲み食いしながらのつっつきとなりました😋

⚓2020年問題たち


つっつきボイス:「あ〜ガラケーのね😆」「絶賛発生中😆」「Perlでも2020年にバグが出たそうです」「ありゃ〜😆

「ガラケーで撮った写真を捨てられなくて持ち続けてる人って結構いると思いますよ」「メモリカード挿せないタイプだと捨てるに捨てられない😇」「大事な人の写真だったりするとなおさら😢」「ガラケーの時計が0時で止まるってひどい😆」「わかりやすく止まるだけマシかも😆

⚓その他のその他

つっつきボイス:「EnChromaは眼球保護メガネの研究中に偶然発見されたそうです」「矯正できるのが驚き😳」「そのままだと見えない色を認識可能な帯域に変換できればやれそうな感じですね」


つっつきボイス:「バッテリー爆発しない?😆」「数年で動かなくなるとか?😆」「そういえばSONYも昔NEWSっていうワークステーション出してましたね: たしか最初期のRubyはNEWSで開発したとか」

参考: NEWS (ソニー) - Wikipedia


つっつきボイス:「年末年始にまどマギの劇場版を見返してたら、こんな感じの赤外線キーボードをまどかが普通に使ってたな〜😆」「まどかマニアック😆

参考: 魔法少女まどか☆マギカ - Wikipedia

「こういうのだと、割と前からLeap Motionっていうジェスチャー入力デバイスがあって実は家にもあるんですけど、スキャンの解像度が高いせいなのか、触れないぐらいアツアツになるやんちゃデバイス🔥」「😆」「😆」「でも結構よくできてますし単価も安いし、何よりも注文するとすぐ届くのがエラくて、大学のUI系研究室とかに結構売れてましたね☺」「へぇ〜」「あと最近のOculus QuestっていうVRゴーグルは、インカムのカメラだけで手の動きをトラッキングできるようになってますし」「そういうの欲しいなってずっと思ってるんですけど、目ぼしいアプリがどのぐらいあるかが心配で😆」「自分はもっぱら寝ながらNetFlix見るのに使ってます😆」「PS VRで寝ながら動画を見たことならありますけどゴーグルがでかすぎて邪魔で😅

参考: Leap Motion - Wikipedia
参考: Oculus Questの機能 | Oculus


つっつきボイス:「コラッツの問題は、偶数だったら2で割って奇数だったら3かけて1足すとどんな数でも最後に必ず1, 4, 2, 1…となるかどうかという超難問」「へぇ〜」「ポール・エルデシュさんも『あの問題に関わると人生棒に振るからやめとけ』って言うレベル😆」「数学の難問ってそんなんばっかですか😆」「棒振り系多いですね😆

参考: コラッツの問題 - Wikipedia
参考: テレンス・タオ - Wikipedia


つっつきボイス:「LiDARってライダーって読むみたいで、ジャングルに埋もれた遺跡を空中からスキャンして発見するとかで大活躍してるらしいです」「自動車の自動運転で外界を認識するのにも使ってるみたいですね☺」「そんな凄いセンサーが10万以下で買えるということでIoT界の好き者たちがアツい眼差しを注いでいますね: そのツイートした方もIoT強者なんですけどその人のライブラリを使ったことあります😋

参考: LIDAR - Wikipedia
参考: ペルーのマチュピチュより古い遺跡の発見 - trendswatcher.net

「こういう長距離センサーは精度とかよりもノイズ除去周りの技術が決め手になったりしますね: 謎の誤検出を除去するのは大変😅」「最後はノイズとの戦いなんですね☺」「ライブラリのノイズ除去がイケてないとフィルタから自作しないといけなくなったりしますし😢」「自分はIoTでライブラリに助けてもらった経験ってあんまり覚えがありませんけど😆」「最近はかなりインテリジェントに処理してくれますよ☺: 特に屋外で使うセンサーは太陽光反射とかに対応しないといけませんし、そういうのはハードウェアで処理できないとソフトウェアだけではつらい」「そういえば大学でlaser range finder使ってた人いたな〜」「光波測距儀って書くと何だかカッコいい😍」(以下延々)

参考: TeraRanger One ToF Rangefinder B型(フレーム) - ロボショップ
参考: 光波測距儀 - Wikipedia

⚓番外

⚓3Dの次は

つっつきボイス:「変形ロボ!🤖」「ターミネーター2!」

⚓不気味の谷を超えたか


つっつきボイス:「このフェイク人間が個人的にヒットでした😆」「これはジワるw」「似すぎてて何だかチューリングテストされてる気分😆」「中国語の部屋でしたっけ」「紅白のAI美空ひばりですか😆」「あれってAIじゃないと思うんだけど😆

参考: 中国語の部屋 - Wikipedia

「この方面はやっぱりゲーム業界が進んでますね: レンダリングをそれっぽく見せる技術と、人間らしく振る舞わせる手法とか特にネトゲが強い💪」「映画の方が進んでるかと思ってました」「状況に合わせて最適に振る舞う手法はやっぱりゲーム業界が進んでると思いますね☺」「あ〜そうかも」「FPSとかMMO系なんかだと、たとえば20人が参加している状況で通信がちょっと途切れたりしても、キャラクターを人間らしく振る舞わせておくと通信が切れてないように見える、というのを昔からやってたりしますし☺」「演出の妙というか😆」「最近の映画も凄いですよ: 最新のスター・ウォーズで、役者が既に物故したモフ・ターキンをフルCGで再現してたんですけど自分マジで全然気づきませんでした😳」「たしかに精緻な表現は映画の方が進んでるかも☺

参考: ファーストパーソン・シューティングゲーム - Wikipedia
参考: MMORPG - Wikipedia
参考: グランド・モフ・ウィルハフ・ターキン - Wikipedia

⚓ダークエネルギーもなかった?

つっつきボイス: 「ダークマターではなくて?」「ダークエネルギーは宇宙の全エネルギーの3/4を占めてるとか見積もられてたんですけど、その仮定ががっつり違ってたみたいでした」「宇宙物理界隈は5年もするとがらっと変わったりするから大変😆

参考: ダークエネルギー - Wikipedia


後編は以上です。ご参加いただいた皆さまありがとうございました!😂

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

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

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Postgres Weekly

postgres_weekly_banner

週刊Railsウォッチ(20200120前編)福岡でも公開つっつき会、Railsのconnection_specification_nameでprimaryという名前が非推奨に、structure.sqlとschema.rbほか

$
0
0

こんにちは、hachi8833です。Chromium入りEdgeが出ましたね。


つっつきボイス:「アイコンがChromiumと色違いというのがまた何とも😆」「大丈夫かしら😆」「Chromium入って〼という表示なのかも😆

「新Edge入れた?」「別に使わないし入れてませんけど😆」「自分もまだWindowsに入れてないので例のbrowserstack.com↓でUser Agent(UA)あたりを見てみるか」(開く)「UA確認に使ってるそのサイトは何でしょう?」「確認くんです」


browserstack.comより

「おし、browserstackでEdge 79 Betaが動くようになってる」「UAにChromeの文字入ってますけど↓😆」「UAの文字って前からいろいろ怪しいじゃないですか😆」「まあ確かに😆

  • browserstackの最新Edge: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.53 Safari/537.36 Edg/80.0.361.33
  • Windows上の少し前のEdge: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763

「この”Edg”↑って何😆」「スペルミスなんじゃ?」


追いかけボイス: 後に”Edg”について以下の情報がありました。

参考: ユーザーエージェント(UA)文字列は時代遅れ? ~「Google Chrome」で凍結・非推奨に - やじうまの杜 - 窓の杜
参考: Microsoft Edge User Agent String - Microsoft Edge Development | Microsoft Docs


なお以下はつっつき後に見つけたツイートです。

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

⚓お知らせ: 公開つっつき会2本立て

⚓その1: 週刊Railsウォッチ「第19回公開つっつき会」(無料)

いよいよ第19回を迎えた公開つっつき会は、来月2月6日(木)19:30〜よりBPS株式会社Pubスペースにて開催いたします。

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

⚓その2: 福岡でのリモート公開つっつき会開催のお知らせ(Wingdoor@福岡)

上に先んじて、来週1月30日(木)19:30〜に福岡の株式会社ウイングドアにて、TechRachoでお馴染みのmorimorihogeが現地入りして第2回公開つっつき会を行います。通常の公開つっつき会と基本的に同じ要領で開催いたします。福岡近郊の皆さまのお気軽なご参加をお待ちしております。

  • 会場: 株式会社ウイングドア(〒810-0001 福岡県福岡市中央区天神4丁目1−28 天神リベラ 3F)
  • 日時: 1月30日(木)19:30〜21:00(終了後は21:20頃まで懇親会もございます)
  • 費用: 無料
  • 持ち物: 必要なものは特にありませんが、手元でのドラフト参照・検索用にノートPCなどがあると便利です。

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

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

⚓マルチDBでprimaryという接続種別名を非推奨に

  • ActiveRecord::Baseconnection_specification_name"primary"という名前が非推奨になった。今後は"ActiveRecord::Base"を使う。この変更はActiveRecord::Base.connection_handler.retrieve_connectionActiveRecord::Base.connection_handler.remove_connectionの呼び出しに影響する。これらのメソッドを"primary"で呼んでいるのであれば、"ActiveRecord::Base"に切り替えること。
    changelogより大意

つっつきボイス:「もしかしてポリコレ絡み?😆」「そっちではなさそうです」「デフォルトは名前指定なしと」「デフォルトの接続は1つだよってことにしたいのかな🤔」「さてこれはbreaking changeに…なりますね」「😅

マルチDBが進化するにつれてconnection_specification_nameやデータベースオブジェクトのspec_nameのデフォルトが”primary”である点が混乱気味になってきた(私のせい🙇)。
さらにややこしいのは、接続を確立するすべての非ActiveRecord::Base抽象クラスでこのクラス名を使っていること。たとえばclass MyOtherDatabaseModel < ApplicationRecordとすると、ActiveRecord::Baseでは”primary”が使われるのに、connection_specification_nameで”MyOtherDatabaseModel”が使われる。
このPRでは以下のようになる:

  • handler.establish_connection(:primary)を呼んでも”primary”でdeprecation warningは表示しない。さらに、:primaryという設定名を使ってない接続で実際のActiveRecord::Base接続があやまって上書きされる可能性があるというバグも修正される。
  • handler.establish_connection :primaryhandler.remove_connection "primary"が一度も呼ばれていない状況でhandler.retrieve_connection "primary"を呼ぶと、ActiveRecord::Baseの接続を返してdeprecation warningを表示する。
    同PRより大意

⚓パラレルテストを以前の挙動に戻した


つっつきボイス:「どうやら上の#38190と関連しているようです」

このコミットは#38029#38151で判明したバグに対する、ある意味応急処置的な修正。#38151は、3-tier configを持つアプリでreplicaが使われている場合に問題になる可能性がある(configの順序が変わることで、暗黙で使われるデフォルト接続が変わるため)。
アプリでestablish_connectionを引数なしで呼ぶかApplicationRecordconnects_toを呼び、かつDBのパラレルテストを使っていると、configが正しくなくなる可能性がある。
原因は、#38151のコードがconfigをなめるときにreplica以外のconfigが更新されてリストの末尾に置かれるため。欲しい接続を指定していない場合、Railsはリストの最初の接続をその環境で使う。以下の設定で考える。

test:
  primary:
    database: my_db
  replica:
    database: my_db
    replica: true

このconfigの順序が「replica」「primary」に変わるので、Railsでestablish_connectionを引数なしで呼ぶと、リストの冒頭にあるreplicaが使われる。
(略)
この問題の実際の修正は、引数がenvまたはなしのestablish_connection呼び出しを非推奨にして、(primaryなどの)明示的なconfigを必須にすること。ここは、Rails起動時に:primaryをデフォルト接続として優先することとも関連する。さらに、connection_specification_nameの”primary”を非推奨にして常にクラス名にすることも必要になるだろう(でないとマジで果てしなく混乱するので)。
背後の問題の方も修正するが、従来の振る舞いも復活させたい。
同PRより大意

⚓新機能: GitHub::Deprecation.disallowed_warnings=

このPRで導入されるGitHub::Deprecation.disallowed_warnings=は、そのアプリのdeprecation warningのうち、許されないconfigにマッチするルールを設定できる。ActiveSupport::Deprecation.disallowed_behaviorは、許されないdeprecation warningにマッチしたときの振る舞いを指定する。
同PRより大意


つっつきボイス:「ちょっとわかりにくかったんですが、許されない書き方をdeprecation期間後も阻止するということみたいです」「そういえば最近のRailsでもこれに似た改修が入ってたかも」「たぶんRuby 2.7対応にも欲しいヤツ😋

ユースケース
アプリのdeprecation warningを取り除くとき、非推奨コードが今後も決して導入されないようにしておきたい。そのためにCIやrubocopルールにlintテストを追加することも多いが、理想どおりにいかないことも多い。たとえば、Rails 6ではデフォルトuniquenessバリデータではstring型カラムのdeprecation warningが生成されるが、6.1以降はcase sensitiveな比較を強制しなくなる。rubocopルールにenforce case_sensitive: trueを追加しても、背後がstring型のカラム属性だけをこれでチェックするのは難しい。
この問題に対する私たちのソリューションは、コードベースから削除された後もそうした非推奨コードを許さないよう特定のdeprecation warningを表示できるようにすることだ。許されない非推奨コードがある場合は、devやtest環境でガチで失敗させて例外を表示する。productionではエラーらしくdeprecationをログ出力する。
設定を一時的に緩めることも、条件を指定して一時的に緩めることもできる。
このPRなしでも同じことをやる方法はいろいろあるが、面倒になる。
同PRより大意

同PRのコメントでは、以前のウォッチ(ウォッチ20181001)でも紹介したShopifyのdeprecation_toolkit gem↓にも言及されていますが、仕組みは異なるようです。

「deprecateな機能を使ってるとwarningって出るんですか?」「出ます出ます」「Rubyのレベルというか標準出力で出ますし、Rails標準のconfigだとログにも出ます☺」「自分のプロジェクトでRailsアップグレードする機会があんまりないから目にしないのかな〜😅」「PumaとかUnicornのログを開いたらきっとある😆」「オレオレRailsアプリを2.7にしたら起動しただけでコンソールにいっぱい出ました😰」「2.7はまだ様子見と決めてますっ😆

⚓抽象クラスの数値バリデータを修正

# activerecord/lib/active_record/validations/numericality.rb#L5
    class NumericalityValidator < ActiveModel::Validations::NumericalityValidator # :nodoc:
-     def initialize(options)
-       super
-       @klass = options[:class]
-     end
-
      def validate_each(record, attribute, value, precision: nil)
-       precision = column_precision_for(attribute) || Float::DIG
+       precision = column_precision_for(attribute, record) || Float::DIG
        super
      end

      private
-       def column_precision_for(attribute)
-         if @klass < ActiveRecord::Base
-           @klass.type_for_attribute(attribute.to_s)&.precision
-         end
+       def column_precision_for(attribute, record)
+         record.class.type_for_attribute(attribute.to_s)&.precision
        end
    end

つっつきボイス:「抽象クラスで数値バリデータを定義できなかったのが修正された」「前はできなかったと」「self.abstract_class = trueなのにcreate!してるのかと思って焦ったけどサブクラスでしたか😆」「こう書きたい気持ちはちょっとわかる: 抽象クラスなんだけど共通のバリデータを置きたいんでしょうね☺」「気持ちワカル」「名前が同じでも継承先で振る舞いが違ってたりするとこじれそうなので、あんまり好きじゃないな〜😆

参考: 抽象型 - Wikipedia

# 同PRより
  class AnimalsBase < ApplicationRecord
    self.abstract_class = true

    validates :age, numericality: { min: 18 }
  end

  class Dog < AnimalsBase
  end

  Dog.create!(age: 0) => ActiveRecord::TableNotSpecified: AnimalsBase has no table configured. Set one with AnimalsBase.table_name=

「Rubyで抽象クラスってあんまり使わない印象あるんですけどどうでしょう?🤔」「RailsのSTI(Single Table Inheritance)なんかでは普通に抽象クラス使いますよ🧐」「抽象クラスは継承を強要されることになるので、使わなくていいなら使いたくない😆」「まあSTIのときぐらいでしょうけど☺

参考: Active Record の関連付け - Railsガイド

「なにしろRubyには抽象クラスのための機能がありませんから😆」「抽象クラスっていう概念、どっちかというとJavaから来ている感ありますね」「Javaは抽象クラスを明示的に要求する構文ありますし」「いつだったかjoker1007さんが抽象クラスのgem作ってたの思い出しました↓(ウォッチ20180202)」「自分みたいにオブジェクト指向をJavaで学んでいたら抽象クラスはまあわかるけど、Rubyで学んでたらピンとこないかもですね☺

参考: joker1007/abstriker

⚓最適化3つ

つっつきボイス:「最適化系の読みやすい修正をいくつか」

# activesupport/lib/active_support/core_ext/object/json.rb#L172
-   Hash[subset.map { |k, v| [k.to_s, options ? v.as_json(options.dup) : v.as_json] }]
+   result = {}
+   subset.each do |k, v|
+     result[k.to_s] = options ? v.as_json(options.dup) : v.as_json
+   end
+   result
  end

concatの方がオブジェクトを生成しないので速いんだったかな↓」「presenceだとメソッド呼び出しの分遅いし、compact.flattenは対象が大きいと遅くなりそうですし☺

# actionview/lib/action_view/helpers/tag_helper.rb#L335
      private
        def build_tag_values(*args)
          tag_values = []
          args.each do |tag_value|
            case tag_value
            when Hash
              tag_value.each do |key, val|
-               tag_values << key if val
+               tag_values << key.to_s if val && key.present?
              end
            when Array
-             tag_values << build_tag_values(*tag_value).presence
+             tag_values.concat build_tag_values(*tag_value)
            else
              tag_values << tag_value.to_s if tag_value.present?
            end
          end

-         tag_values.compact.flatten
+         tag_values
        end

「これはyieldより&blockの方が速いということか↓」「PRにも余分なスタックフレームのpushを避けるためとあるから高速化ですね」「あはぁ〜そうかも😆」「ブロックを作ってyieldすると1階層余分になっちゃうと」「{ }はハッシュじゃなくてブロックか😳」「{ yield }で受けるのは業務ではあんまり使わないかな☺

# activesupport/lib/active_support/core_ext/benchmark.rb#L13
- def ms
-   1000 * realtime { yield }
+ def ms(&block)
+   1000 * realtime(&block)
  end

⚓番外3つ

つっつきボイス:「#38246はRailsガイドのJavaScriptガイドにWIPが付いたということで、やはり未完成😆」「😆」「へ〜yamlでドキュメントのフラグ立ててるのか↓」

# guides/source/documents.yaml#L134
      name: Working with JavaScript in Rails
+     work_in_progress: true
      url: working_with_javascript_in_rails.html
      description: This guide covers the built-in Ajax/JavaScript functionality of Rails.

「#38240はテストの数値にoctet使うのやめようということだそうです↓」「1時4分をデジタル時計っぽく01, 04と書いたら8進数になってた😆」「危うくバグになりそうだけど7以下だからたまたまセーフ😆」「9時とかだったらアウト😆

# actionmailer/test/message_delivery_test.rb#L78
  test "should enqueue a delivery with a delay" do
-   travel_to Time.new(2004, 11, 24, 01, 04, 44) do
+   travel_to Time.new(2004, 11, 24, 1, 4, 44) do
      assert_performed_with(job: ActionMailer::MailDeliveryJob, at: Time.current + 10.minutes, args: ["DelayedMailer", "test_message", "deliver_now", args: [1, 2, 3]]) do
        @mail.deliver_later wait: 10.minutes
      end
    end
  end

「ところでこういう直さなくても動くトリビアな修正って、コアメンバー以外の人が投げてもすぐにマージされない可能性ありそうですね」「#38240ぐらいの修正ならわかりやすいから通りそうな気もしますけど☺」「Railsに初めてプルリクするならドキュメントのタイポ修正とかの方がむしろマージされやすいかも?🤔


# Gemfile#L44
gem "listen", "~> 3.2", require: false
gem "libxml-ruby", platforms: :ruby
gem "connection_pool", require: false
+gem "rexml", require: false

「rexmlっていうgemがRuby 3.0に標準で入るらしいです」「rexmlって見たことあるな」「XMLパーサー」

⚓Rails

⚓Railsで署名暗号化済みcookieをテストする(Ruby Weeklyより)

# 同記事より
class CookiesControllerTest < ActionDispatch::IntegrationTest
  test "should set cookies when getting the index" do
    get root_url
    assert_response :success
    assert_equal cookies["simple"], "Hello, I am easy to read."
    jar = ActionDispatch::Cookies::CookieJar.build(request, cookies.to_hash)
    assert_equal jar.signed["protected"], "Hello, I can be read, but I can't be tampered with."
    assert_equal jar.encrypted["private"], "Hello, I can't be read or tampered with."
  end
end

つっつきボイス:「記事は普通にテストのやり方の紹介でした」「cookieをちゃんと復元してテストしようという感じですね☺」「この辺はたまに追いかけてみるといいと思います😋

⚓structure.sqlのメリットデメリット(Ruby Weeklyより)


つっつきボイス:「structure.sqlって何だったっけ?と思って拾いました」「schema.rbみたいなヤツ?」「たしかschema.rbの代わりに使えるフォーマットが割と昔からRailsにあったと思いますが、それがstructure.sqlだったと思います☺」「そういえば😳

「schema.rbはあくまでRDBMSに依存しないRailsにとっての抽象化形式だけど、structure.sqlは文字どおりSQLで書けるので、使っているRDBMSに特化できるし、たしかrails db:setupなんかはstructure.sqlの方が速かった覚えありますね」「は〜なるほど」

「たしかrails db:setupはマイグレーションは見ないでschema.rbだけ見る: 通常のマイグレーションではマイグレーションファイルを順々に処理しますけど」「そうだった😳」「structure.sqlは要は生SQLなので、たとえばMySQLに特化したSQLとかストアドプロシージャも書こうと思えば書ける」「structure.sqlのメリットもデメリットもその辺にある感じですね」「手書きするとしてもどうせpg_dumpの結果あたりを使うでしょうし😆

「structure.sqlは昔からありますし、特にgemとか入れずにRailsの機能だけでできます☺」「Railsガイドにも載ってますね↓」「使ってるのはあんまり見たことありませんけど」「まあRails wayではないので🛤」「複合キーとか使う場合はstructure.sqlにするってガイドに載ってる👀」「Railsのマイグレーション機能でサポートされていないRDBMSの機能を使うのであればそうなりますね🧐

参考: 6.2 スキーマダンプの種類 — Active Record マイグレーション - Railsガイド

「まあschema.rbって途中参加したプロジェクトだとみっちり読みますけど、structure.sqlが使えるならそっちの方がRailsのデフォルトでもよかったかなという気もしますね😆」「😆

「銀座Rails↓でデータベースビューの話したときに言及したscenicっていうgemはcreate_viewっていうメソッドをマイグレーションに追加するんですよ」「ふむふむ」「rails db:setupで参照するschema.rbは自動生成されたものなので、たとえばマイグレーションのActiveRecord::Base.connection.executeでカスタム実行したSQLはそのままではrails db:setupで実行できないという大いなる問題がある😆: scenicはそれをやるために必要なんですね🧐」「知らなかった!😳」「structure.sqlならやれるんだし、データベースを切り替えることってめったにないので、それならschema.rbじゃなくてもよかったかなって思ったりするわけです😆」「😆

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

⚓closure_tree: Active Recordモデルで階層を扱う(Ruby Weeklyより)


つっつきボイス:「モデルで階層を扱うgemですが、TechRachoにもこのgemを使った記事がありました↓」「こっちの方が見やすい😋」「ネステッドツリーですね☺

Railsで木構造を扱うには

「この図↓いいですね〜😋

「なおancestryっていうgemの方がclosure_treeより有名かも↓」「こんなのもあるんですね😳」「このへんのドキュメントは1回ちゃんと読みましたよ: 実装するときは本読みながらじゃないとできませんけど、一度読んでおけば原理が何となく頭に残ってやりやすくなりますね☺


同リポジトリより

参考: 多階層カテゴリでancestryを使ったら便利すぎた - Qiita

⚓Webpack関連記事


つっつきボイス:「上は先週出したWebpacker翻訳記事とかぶるところもありますけど」「Webpackを中心に書いてるっぽいですね☺

Rails 6: Webpacker+Yarn+Sprocketsを十分理解してJavaScriptを書く: 前編(翻訳)

「あとSprocketsのアップグレードガイド↓を今更見つけたんですけど、WIPかつ『内容は保証しない』だそうです😅

「そういえばSprocketsってこういう行↓の順序がさりげにすごく重要だったりして大変😅」「そうそうっ😤」「ちょっと順序変わるとなぜかプリコンパイルされなかったり😇」「Sprocketsもう思い出せない😆

// 同ドキュメントより
//= link_tree ../images
//= link application.css
//= link application.js
//
// maybe another non-standard file?
//= link marketing.css

「Webpackerの公式ドキュメントって情報があちこちに散らばってて追いにくくて😢: 最近見つけた以下の記事↓はRails 5.x時代のものなんですけどWebpackerについてよくまとまってて感激しました😂」「ほほぉ😋」「自分で振る舞いを追いかけながらまとめたんでしょうね☺

「記事ではWebpackerを剥がしてWebpackだけでやろうとしてるんですけど、そのためにjavascript_pack_tag的なヘルパーを自作しているのを見て、自分はそこまではしたくないなと思いました😆」「😆」「Webpackerはもともとそのためのものですからね☺」「ヘルパー自作するとほんのり車輪の再発明感が😆」「Webpackも別にRails用に作られたものではないので、まあしゃーない😆

⚓VueのI18nをRailsパイプラインに統合する(Hacklinesより)


同記事より


つっつきボイス:「JSのI18nってどうやったらいいのかいつも迷うヤツだ😭」「JSのI18nだけならそれこそjQueryのSelect2プラグインなんかが前からありますけどね☺」「ありゃそうでしたか😳」「リポジトリのdist/js/i18n/ja.jsあたりを見れば基本的な訳文がありますよ↓」「ほんとだ〜!」「これがJS的にI18nのベストプラクティスなのかどうか知りたいところですけど😆、これは太古の昔からあります」

!function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var n=jQuery.fn.select2.amd;n.define(“select2/i18n/ja”,[],function(){return{errorLoading:function(){return”結果が読み込まれませんでした”},inputTooLong:function(n){return n.input.length-n.maximum+” 文字を削除してください”},inputTooShort:function(n){return”少なくとも “+(n.minimum-n.input.length)+” 文字を入力してください”},loadingMore:function(){return”読み込み中…”},maximumSelected:function(n){return n.maximum+” 件しか選択できません”},noResults:function(){return”対象が見つかりません”},searching:function(){return”検索しています…”},removeAllItems:function(){return”すべてのアイテムを削除”}}}),n.define,n.require}();

「JSだけならいいんですけど、RailsのI18nをVue.jsに持ってきたいとかをやるのはつらいかなと思って」「あ、I18nファイルをRailsとJSで共有する話か😳」「かなと」「あ〜それは本気でつらいヤツ😭」「APIで問い合わせかけてもツライし😭」「記事真面目に読んでないけど、もしそれならたぶんやめといた方がいい気が😇」「どうやってもこじらせそう😇」「でも文言ドライにしたいしな〜😢

「I18nの共有って両方とも一人でやるならとてもいいんだけど、アプリが育ってフロントとバックエンドを手分けするようになったら速攻で破綻しそう😆」「ワカル😅」「最近はフロントエンジニアとバックエンドエンジニアに分かれて作業することも増えてきているし、そう考えると果たしてこの方法で両方とも幸せになるんだろうかと」「ですね😅

「やっていくうちに双方でキーの名前がだんだん食い違ってきたり😆」「でキーコンバーター作ったり😆」「フロントとバックエンドが別会社だったりするとさらに深刻😇」「その後でAndroidアプリとiOSアプリもやるなんてなったらもう地獄👹」「あ〜きっとそうなる😅」「表示幅もデバイスごとに違ったりもするんだし、そんなに頑張って統合しなくてもよくね?ってちょっと思ったりしますね☺」(以下延々)

⚓その他Rails


前編は以上です。

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

週刊Railsウォッチ(20200115後編)Ruby 2.7関連情報、Bootstrap 5は今年前半リリースか、PostgreSQLでやってはいけないリストほか

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

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

Rails公式ニュース

Ruby Weekly

Hacklines

Hacklines

週刊Railsウォッチ(20200121後編)RubyKaigi 2020受付開始、RubyGemsとBundlerの今後、ファイル同期ツールMutagenほか

$
0
0

こんにちは、hachi8833です。

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

⚓お知らせ: 公開つっつき会2本立て

⚓その1: 週刊Railsウォッチ「第19回公開つっつき会」(無料)

いよいよ第19回を迎えた公開つっつき会は、来月2月6日(木)19:30〜よりBPS株式会社Pubスペースにて開催いたします。

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

⚓その2: 福岡でのリモート公開つっつき会開催のお知らせ(Wingdoor@福岡)

上に先んじて、来週1月30日(木)19:30〜に福岡の株式会社ウイングドアにて、TechRachoでお馴染みのmorimorihogeが現地入りして第2回公開つっつき会を行います。通常の公開つっつき会と基本的に同じ要領で開催いたします。福岡近郊の皆さまのお気軽なご参加をお待ちしております。

  • 会場: 株式会社ウイングドア(〒810-0001 福岡県福岡市中央区天神4丁目1−28 天神リベラ 3F)
  • 日時: 1月30日(木)19:30〜21:00(終了後は21:20頃まで懇親会もございます)
  • 費用: 無料
  • 持ち物: 必要なものは特にありませんが、手元でのドラフト参照・検索用にノートPCなどがあると便利です。

⚓Ruby

RubyKaigi 2020@松本の受付が始まりましたね。


rubykaigi.doorkeeper.jpより


つっつきボイス:「そうそう、Super Early Birdのうちに申し込まなきゃ」「残り600人切った😳」「今年は東京から電車でも車でもバスでも行けるから選択肢増えましたし😍」「宿の空きが結構厳しいらしいので自分は昨年から押さえてあります😆

取りました😋

⚓Falconとは


同リポジトリより


つっつきボイス:「今日の社内勉強会でFalconの話題が出てたので」「去年のRubyKaigiにも登場してたのに見落としてた😅」「FalconはRubyのFiberを使ったWebサーバーで、Pumaより速いらしいという評判ですけど、この間の銀座Rails#17の発表↓でFalconのマルチファイバーについて軽く触れたら、親睦会の後でjoker1007さんからその部分にツッコミもらったので、Falconでマルチファイバーをどう使ってるかもっと調べとこうと思ってたところ😆」 「そうでしたか😳」「なおAuto Fiberはまだ作業中ですね(ウォッチ20191225)」

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

参考: class Fiber (Ruby 2.7.0 リファレンスマニュアル)
参考: 【Ruby】Fiber(ファイバー)を理解する - Qiita

「今のところPumaの評判がいいですし」「PumaはRailsのデフォルトWebサーバーなのでそのまま使えるのがいいですね😋


puma.ioより

⚓テストできないコードをテストするには(Hacklinesより)


つっつきボイス:「untestable!😳」「以下は『ずっとループしてる』『APIとしての入出力がない』『そもそもどう動かすか』みたいにテストがしんどくなるコードの例↓」「untestableなものは基本的にテスト無理なので、要素を分解してアプローチするぐらいしかないでしょ😭」「記事もそういう結論みたいです😢」「記事のコード例はまだ単純な方で、現実にはもっと巨大なコードだったりするし」「2000行とかもう無理😇

# 同記事より
#### 3秒おきに乱数を1つ出力 ####
while sleep(3) do
  puts rand(10)
end

⚓BundlerとRubyGems


つっつきボイス:「自分はHerokuでRails動かしてるので、Bundler周りが気になって拾いました」「Bundlerか〜」「そういえば銀座Rails#17で@hsbtさんがちょうどBundler周りの話してましたよ↓」「あ〜やっぱり行けばよかった😭」「ではそっちを中心に話しましょ☺

「思い出せる範囲で言うと、今はRubyGemsとBundlerって分かれてますけど、両者がかなり結合しているので、一方だけを更新しようとしてもだめで両方のテストを通さないといけないという二度手間が発生しているらしいです」「あ〜それはつらそう😢」「しかも今はBundlerがRuby本体に入ったので、RubyGemsとBundlerを同じリポジトリ配下に置いて管理しようみたいな話もあるそうです」「それも大変そう…」

「で確かBundler 3がbreaking changeになります」「ありゃ😅」「たとえば今はbundleだけ実行するとbundle installが走りますけど、そこをヘルプを表示するように変えるとか、そんな話もしていましたね☺」「bundleだけでインストールが走ったら焦りそう😅」「やったことない😆


「もうひとつは、HerokuでRuby 2.7が使えるかどうか調べていてHerokuの最近の記事↑を見つけたんですけど、Gemfile.lockの末尾にあるBUNDLED WITHというBundlerバージョンが刻まれた行をHeroku側で削除することにしたんだそうです」「あ〜BUNDLED WITHは嬉しくないヤツ😆」「これが原因でBundlerが止まることよくありましたし😆

「そういえば最近のHerokuはDockerコンテナでデプロイできますヨ↓」「気づいてなかった😅」「最近はそっちの方が推奨になってた気もするけどどうだったかな…🤔」「Dockerデプロイ調べてみます😋

参考: Building Docker Images with heroku.yml | Heroku Dev Center


つっつき後に@hsbtさんのスライドを見つけました↓。

アジェンダ:

  • Ruby言語でライブラリを使う方法
  • RubyGemsとBundlerについて
  • Gemificationプロジェクトについて
  • Bundler統合の困難な点
  • RubyGems 4.0とBundler 2.1の将来計画
  • Ruby 3.0へのロードマップ

参考: 銀座Rails#17まとめ - Togetter

⚓Rubyのオブジェクトを3行でデコレート(Hacklinesより)

# 同記事より
object.singleton_class.instance_eval do
  attr_accessor :label
end

つっつきボイス:「とても短い記事です」「普通にinstance_eval使ってるだけなので特に新しさはないかな☺」「『迷惑かけないよう注意』だそうです」「新たに生成されるオブジェクトにはlabelが作られるけど、既存のオブジェクトでは作られないということか」「それ地獄感ある😆」「さらにマルチプロセスマルチスレッドになるともっと地獄を呼ぶ😆」「productionコードで使ったらダメ⛔

⚓その他Ruby

つっつきボイス:「そういえば一昨年ぐらいだったかな?Rubyで博士号取った人いましたね」「へぇ〜!😳

後で探しましたがうまく見つけられませんでした😢



同リポジトリより

# 同リポジトリより
eval s=%q~n=t=5;s[200..-1].bytes{n=n*72+(_1+10)%94};"eval s=%q\37#{s}\37".bytes{|c|24.times{t+=2<<0**(_1%6);n[(c+11)%95*24+_1]<1||`git commit -m q --allow-empty --date=#{Time.at(t*43200).strftime"%\106T%T"}`}}#Ud*{X'a<TY`3paY(*qvk$6w\%.e_|0dmldWu)pZv+|gWZ',f:uT9V4p5<w=#Yy#h3ihUro".%/<h%n<\wp8[8rq$5+'u^(a6x#-nn:$j9xxh;;8\7Y.Z{(9:m%*c;\,qz*zk2xxt0(mky'g"nc)1t[.gX/a<e,r5,"(tW6_#tbvU3u{UgzioV0bc[ ;\/z-x}hmT8!a4&0\9{{=] zTrsgks`0qt<f[dp!pt$$o5!d95){//Z;`2.!n!;h}o=mg'rm1VU+b,qkn<a[[()#m}m./=7yre~

つっつきボイス:「GitHubの草でクワインやったみたいです」「mameさんだ」「やべ〜😆」「発想がスゴい💪」「1970年というUnixタイムでお馴染みの年から始めるあたりも😆

参考: クワイン (プログラミング) - Wikipedia
* サイト: UNIX時間 - Wikipedia


つっつきボイス:「jnchitoさんからのアンサー記事がみっちりまとまってます」「懇親会でグルーピングしやすくするとか、いろいろ参考になる😋」「イベントやったことない人は一度見ておくといいですね☺

「ぼっち対策は気合入れないと結構大変😅」「アンチハラスメントポリシーも大事」「方法論は昔からいろいろありますし自分もやったことありますけど、継続するのが大変😭」「人数増えたらサポート要員も増えますし」「一人でやったら死にそう😅」「セッションの中でも折に触れて説明するとか、懇親会前の雰囲気作りも重要」「ちゃんとやれればコミュ障の人にとって大きな助けになりますけど、すべての人を満足させられるかどうかはまた別なので😅」(以下延々)


⚓DB

⚓RDSとEC2の単価(DB Weeklyより)


同記事より


つっつきボイス:「何の話かなと思ったら、EC2とRDSで同じことやってるのにRDSの方が単価高いってぼやいてるみたい😆」「😆」「自分でEC2にデータベース立てればそりゃ安いけど、マネージドのRDSをやめてまでマルチAZのEC2データベースを自力で立てる気にはなれませんね〜☺」「そもそもEC2もRDSも長期的には値段下がってますし、ただ差が開いているというだけで😆」(以下延々)

⚓DynamoDBのCSVエクスポート/インポート(DB Weeklyより)


つっつきボイス:「Canonical(Ubuntuの会社)が作ったツールみたいです」「dynocsvってHerokuかと思ったらDynamoDBじゃないですか😆」「紛らわしい😅

「昔DynamoDBからCSVにエクスポートするツールを調べたことがあって、当時一番マシだったのが以下のDynamoDBtoCSVというツール↓だったんですけど、デフォルトのprovisioned capacityモードだとデータ量が増大して単位時間あたりのクエリ数上限を超えたときにAPIがエラーを返してやり直しになったりとか、苦労しました😭」「Node.jsのツールなんですね」

「でもその後になってJSONを吐き出せるAWSのData Pipelineテンプレート↓ができたので今ならこれがベスト: DynamoDBのデータサイズがでかくなってもよしなに最後まで吐き出してくれます😋」「おぉ〜」「ただしJSONは分割されるので自分で結合しないといけませんけど😅

参考: AWS Data Pipeline を使用した DynamoDB データのインポートとエクスポート - AWS Data Pipeline

⚓その他DB


つっつきボイス:「XAトランザクションは異種RDBMS間のトランザクションの仕組みで、銀座Rails#17でもちょっと話が出たんですけど、ツワモノたちの結論は『XAトランザクションはやめとけ』でした😆」「😆

「元々MySQL同士ならデータベースサーバーが分かれていても普通にトランザクション張れるんですけど、XAトランザクションはOracleとPostgreSQLみたいに種類が違うデータベースサーバー同士のトランザクションなので別物」「聞くからにヤバそう😅

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

⚓Microsoft Teams vs Slack


つっつきボイス:「Slackのdaily active user数がMicrosoft Teamsに抜かれたそうです」「まあOffice 365ユーザーはとっても多いですから😆」「Office買えばもれなく使えるならTeams使いますよね😆

⚓Mutagen: ファイル同期ツール


同サイトより

mutagen: {名} : 突然変異原


つっつきボイス:「ruby-jpでmrknさんがMutagenについてちらっと書いてて知りました」「ほほう?」「記事をざっと見た感じ、rsyncのようなファイル同期じゃなくて、このツールをバックグラウンドプロセスとして常駐させておくとずっと双方向同期してくれるのか: もしや…これは今の自分が欲しかったツールかも?!」「やった🎉

参考: rsync - Wikipedia

「自分の場合移動が多くて、しかもThinkPad X1 Streamがまともに動かなくなって別のWindowsマシンで作業せざるを得なくなったんですけど、WindowsのDocker環境がホント使いにくくて😭: しょうがないのでJetBrains IDEのディレクトリ同期機能を使って裏でrsyncディレクトリ同期するという地獄味あふれる使い方になっちゃってるんですよ😇」「ありゃ〜😅」「もしかするとそのあたりをMutagenでうまくやれるかも?と期待しちゃいます😋

「このツールは相互に同期してくれるところがよさそうなんですよ: たとえば手元にRailsのソースコードがあってリモートがLinux環境だとすると、JetBrainsの同期機能ではリモートに同期はできても、サーバーでないとできない作業(rails generateとか)はローカルでできない😇」「あ〜たしかに😳」「無理やりローカルでrails generateしても、リモートと完全に同じにはならない😢」「うーむ😅」「JetBrains IDEはローカルをリモートに自動同期する機能はあるんですけど、リモートをローカルに同期するのはメニューからの手動になってしかも遅い🐢

「リモートをボリュームマウントじゃだめですか?」「それがなぜかDockerボリュームをどうしてもマウントできなくて😭、時間もないのでrsyncで凌いでるんですけど何とかしたいと思ってた😤」(以下延々)

⚓JavaScript

⚓2019年に人気上昇したのは


同サイトより


つっつきボイス:「ここの集計ではVue.jsが伸び率総合1位でした」「そんなにVue伸びてたっけ?🤔」「Reactは全世界レベルだけどVueは日本とかアジア止まりみたいな話も聞きますけど😆」「React疲れしてる人をよく見かけるような😆」「ReactじゃなくてもいいものをReactで作るから疲れるんでは?😆」「ところでこの集計で月をJとかFって表すの略しすぎ😆」(以下延々)

集計にはGitHubの他に以下のサイトのデータも使われてるようです。

「GitHubの数字はスター数を元にしているみたいだけど、もし仮にGitHubのクローン数を取れるAPIを使ったら数値のほとんどをCIが稼いだりして🤣」「🤣

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

⚓Googleとサードパーティcookie


つっつきボイス:「これはマジでいろんなものが動かなくなるから勘弁して欲しい😭」「サードパーティcookieの禁止ってよくわかってないんですけど🤔」「ん〜、CORS(Cross Origin Resource Sharing)をfalseにできなくするというか、cookieの可視性をオープンにできなくなるというか」「他のサイト由来のcookieを読めなくなる?」「そんな感じ: 今はoriginの指定がないcookieを読めるんだけど、読めなくなるのがデフォルトになるということだと思います☺」「だから広告とかに影響が出るんですね😳

参考: サードパーティ Cookie を禁止する | Firefox ヘルプ
参考: サードパーティクッキーって何だっけ? 今さら聞けないHTTP Cookieのキホン | 初代編集長ブログ―安田英久 | Web担当者Forum


徳丸先生もびっくりしてますね。

Google、サードパーティー製CookieのChromeでのサポートを2年以内に終了へ – ITmedia NEWS

マジですか

2020/01/15 14:44

⚓その他

⚓祝日注意


つっつきボイス:「今年の祝日はいろいろイレギュラーになるみたいです」「そうだった!😳」「オリンピックのあおりで体育の日が今年だけ7月に移動したり」「オリンピックめ〜😅

つっつき後に調べてみると、holiday_jp-rubyもholiday_japanも既に対応済みですね↓。

⚓番外

⚓裏垢


つっつきボイス:「これは面白かった😆」「そんなにコワいかな〜🤔」「まあ裏垢作るなら相当慎重にやらないと😆


今回は以上です。

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

週刊Railsウォッチ(20200120前編)福岡でも公開つっつき会、Railsのconnection_specification_nameでprimaryという名前が非推奨に、structure.sqlとschema.rbほか

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

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

Ruby Weekly

Hacklines

Hacklines

DB Weekly

db_weekly_banner


Docker内のbundler-auditが古い脆弱性警告を出す問題

$
0
0

Evil Martians流のRails開発Docker環境を使っていて気づいた点をメモ。

Rails 6のDocker開発環境構築をEvil Martians流にやってみた

Evil Martiansのdipを使う前提です。

docker-composeを便利にするツール「dip」を使ってみた

問題

Dockerにログインした状態でbundler-auditを実行すると、とっくに修正済みのはずの脆弱性警告が表示されます。

root@1528a26e7813:/app# bundler-audit
Name: rails-html-sanitizer
Version: 1.3.0
Advisory: CVE-2015-7579
Criticality: Unknown
URL: https://groups.google.com/forum/#!topic/rubyonrails-security/OU9ugTZcbjc
Title: XSS vulnerability in rails-html-sanitizer
Solution: upgrade to ~> 1.0.3

Name: rails-html-sanitizer
Version: 1.3.0
Advisory: CVE-2015-7578
Criticality: Unknown
URL: https://groups.google.com/forum/#!topic/rubyonrails-security/uh--W4TDwmI
Title: Possible XSS vulnerability in rails-html-sanitizer
Solution: upgrade to ~> 1.0.3

Name: rails-html-sanitizer
Version: 1.3.0
Advisory: CVE-2015-7580
Criticality: Unknown
URL: https://groups.google.com/forum/#!topic/rubyonrails-security/uh--W4TDwmI
Title: Possible XSS vulnerability in rails-html-sanitizer
Solution: upgrade to ~> 1.0.3

Vulnerabilities found!

Dockerの中でbundler-audit updateを実行しても変わりません。

root@0df0fbecba41:/app# bundle audit --update
Updating ruby-advisory-db ...
Skipping update
#(略)

解決方法

bundler-auditのissue↓によると、Dockerにgitをインストールする必要があるとのことでした。そういえば今の設定では入ってませんでした。gitが入ってないと通知してくれればいいのに😢

Evil Martians流であれば以下で修正できます。それ以外の方は頑張ってgitをDockerに追加してください。

  • Aptfilegitを追加する
  • docker rmi <イメージID>で既存のイメージを削除
  • dip compose buildでリビルド
  • dip shでログインし、bundler-audit update

dip + Evil Martians流はイメージのリビルドがやりやすくて助かりました😂rails newyarn install --check-filesをやり直す必要もありません。当初手動でgitを入れて結果を何とかしてDockerイメージにコミットしようとしてもがきましたが、dip + Evil Martians流ならリビルドする方が早かったのでした。

おまけ

特にRailsの開発環境の場合はgemのCネイティブ拡張のビルドのためにビルドツールも必要ですし、今回のようにgitが必要になる場合もあります。Apline Linuxやmulti stage buildでDockerイメージを薄くすることに頑張りすぎない方がいいかもという気がしてきました。

週刊Railsウォッチ(20200127前編)Railsでキーワード引数warning退治始まる、ライブラリとフレームワークの違い、ShopifyのRails高速化記事ほか

$
0
0

こんにちは、hachi8833です。Burikaigiが気になります。


つっつきボイス:「富山のBurikaigiがメシのうまさを大フィーチャーしてるので🍽」「冬の富山といえばブリでしょう🐟」「まだ行ったことなくて😢」「松江もいいけど富山もね😋

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

⚓お知らせ: 公開つっつき会2本立て

⚓その1: 週刊Railsウォッチ「第19回公開つっつき会」(無料)

いよいよ第19回を迎えた公開つっつき会は、来月2月6日(木)19:30〜よりBPS株式会社Pubスペースにて開催いたします。

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

⚓臨時のお知らせ: 福岡でのリモート公開つっつき会は「延期」いたします(Wingdoor@福岡)

今週木曜に予定されていた福岡での公開リモートつっつき会は延期となりました🙇


connpass.comより

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

公式情報とコミットリストから見繕いました。

⚓Ruby 2.7キーワード引数のwarning退治

参考: Ruby 2.7 の変更点 - Module - @tmtms のメモ

# activejob/lib/active_job/arguments.rb#66
-     private_constant :PERMITTED_TYPES, :RESERVED_KEYS, :GLOBALID_KEY, :SYMBOL_KEYS_KEY, :WITH_INDIFFERENT_ACCESS_KEY
+     private_constant :PERMITTED_TYPES, :RESERVED_KEYS, :GLOBALID_KEY,
+       :SYMBOL_KEYS_KEY, :RUBY2_KEYWORDS_KEY, :WITH_INDIFFERENT_ACCESS_KEY
+
+     unless Hash.respond_to?(:ruby2_keywords_hash?) && Hash.respond_to?(:ruby2_keywords_hash)
+       using Module.new {
+         refine Hash do
+           class << Hash
+             if RUBY_VERSION >= "2.7"
+               def ruby2_keywords_hash?(hash)
+                 !new(*[hash]).default.equal?(hash)
+               end
+             else
+               def ruby2_keywords_hash?(hash)
+                 false
+               end
+             end
+
+             def ruby2_keywords_hash(hash)
+               _ruby2_keywords_hash(**hash)
+             end
+
+             private def _ruby2_keywords_hash(*args)
+               args.last
+             end
+             ruby2_keywords(:_ruby2_keywords_hash) if respond_to?(:ruby2_keywords, true)
+           end
+         end
+       }
+     end

つっつきボイス:「Ruby 2.7のキーワード引数対応は大変な作業😅」「そのあたりのコミットを上にまとめました」「修行感ある😆」「kamipoさんのツイート↓も喜びに満ちてますね」「たしかにruby2_keywords_hash?は欲しい機能でしょうね😋

「あ〜確かにnon UTF-8が混じる可能性ある🥺: 自分の認識ではinspectはもともとヒューマンリーダブルにするためのメソッドなのであんまりバイナリセーフではないかも」「ふ〜む」「バイナリセーフにしたかったらMarshalあたりなのかな?🤔

Object#inspect
オブジェクトを人間が読める形式に変換した文字列を返します。
docs.ruby-lang.orgより

# docs.ruby-lang.orgより
[ 1, 2, 3..4, 'five' ].inspect   # => "[1, 2, 3..4, \"five\"]"
Time.new.inspect                 # => "2008-03-08 19:43:39 +0900"

ふと英語のmarshalとMarshallの違いが気になりました。

marshal: 整列させる; 整理する; 案内する. [軍事] 元帥; 隊長.
Marshall: {人名} : マーシャル◆ファミリーネーム◆【語源】「馬丁」から「位の高い役人」まで広い意味を持つ、マーシャル諸島

「ちなみにちなみに素のRails 6.0.2.1とRuby 2.7では起動しただけでコンソールにこれだけwarningが出ました↓😆」「これを今後つぶしていくのは大変だな…」

/bundle/ruby/2.7.0/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/stack.rb:37: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/bundle/ruby/2.7.0/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/static.rb:110: warning: The called method `initialize' is defined here
#(略)
/bundle/ruby/2.7.0/gems/activerecord-6.0.2.1/lib/active_record/type.rb:27: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/bundle/ruby/2.7.0/gems/activerecord-6.0.2.1/lib/active_record/type/adapter_specific_registry.rb:9: warning: The called method `add_modifier' is defined here
Started GET "/" for 172.19.0.1 at 2020-01-23 08:53:58 +0000
/bundle/ruby/2.7.0/gems/activemodel-6.0.2.1/lib/active_model/type/integer.rb:13: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/bundle/ruby/2.7.0/gems/activemodel-6.0.2.1/lib/active_model/type/value.rb:8: warning: The called method `initialize' is defined here
/bundle/ruby/2.7.0/gems/activerecord-6.0.2.1/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb:12: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/bundle/ruby/2.7.0/gems/activemodel-6.0.2.1/lib/active_model/type/value.rb:8: warning: The called method `initialize' is defined here
Processing by Rails::WelcomeController#index as HTML
/bundle/ruby/2.7.0/gems/actionview-6.0.2.1/lib/action_view/view_paths.rb:11: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/bundle/ruby/2.7.0/gems/actionview-6.0.2.1/lib/action_view/lookup_context.rb:140: warning: The called method `template_exists?' is defined here
/bundle/ruby/2.7.0/gems/actionview-6.0.2.1/lib/action_view/unbound_template.rb:24: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/bundle/ruby/2.7.0/gems/actionview-6.0.2.1/lib/action_view/template.rb:130: warning: The called method `initialize' is defined here

⚓MySQL 8.0.19でCIが落ちる問題を修正

# activerecord/test/cases/migration/change_schema_test.rb#L146
        elsif current_adapter?(:Mysql2Adapter)
-         assert_match "int(11)", default.sql_type
-         assert_match "tinyint", one.sql_type
-         assert_match "int", four.sql_type
-         assert_match "bigint", eight.sql_type
+         assert_match %r/\Aint/, default.sql_type
+         assert_match %r/\Atinyint/, one.sql_type
+         assert_match %r/\Aint/, four.sql_type
+         assert_match %r/\Abigint/, eight.sql_type

つっつきボイス:「MySQLの出力する文言が変わったというよくある修正☺」「修正後はアサーションに正規表現が入ってきましたね」

# activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb#L80
-   assert_includes error.message, <<~MSG.squish
-     Column `old_car_id` on table `engines` does not match column `id` on `old_cars`,
-     which has type `int(11)`. To resolve this issue, change the type of the `old_car_id`
-     column on `engines` to be :integer. (For example `t.integer :old_car_id`).
-   MSG
+   assert_match(
+     %r/Column `old_car_id` on table `engines` does not match column `id` on `old_cars`, which has type `int(\(11\))?`\./,
+     error.message
+   )
+   assert_match(
+     %r/To resolve this issue, change the type of the `old_car_id` column on `engines` to be :integer\. \(For example `t.integer :old_car_id`\)\./,
+     error.message
+   )
+   assert_not_nil error.cause
  ensure
    @conn.execute("ALTER TABLE engines DROP COLUMN old_car_id") rescue nil
  end

⚓#remove_connection#remove_connection_poolに変わる

  • #remove_connectionが非推奨になり、ハンドラで呼ばれるときは#remove_connection_poolが推奨になる。
    #remove_connectionが非推奨になった理由は、Hashの代わりにDatabaseConfigを返すことをサポートするため。#remove_connectionは6.2で削除されるので今後は#remove_connection_poolを使うこと。
    Changelogより大意

つっつきボイス:「poolが付いたのは何でなんでしょう?🤔」「おそらくですが、今後はコネクションプール単位でのみ扱うという意図があるのかもしれませんね☺: 今はマルチプルデータベースが基本になっていますし」「ふむふむ」「configuration_hashを参照しているあたり、コネクションプール単位じゃなくてコネクション単位だと不整合が生じそうですし」

「コネクションプール自体は前からあったんでしょうけど」「以前は同じデータベースにつなぐコネクションプールしかありませんでしたね: この修正はたぶんRails 6でマルチプルデータベースになったことと関連しているでしょうし、remove_connectionだとコネクションを削除するように見えて誤解を招くと考えたんじゃないかな?🤔」「なるほど!」「まああんまりアプリ開発者が呼び出すコードではなさそうですけど😆

# activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb#L1129
      def remove_connection(owner, pool_key = :default)
+       remove_connection_pool(owner, pool_key)&.configuration_hash
+     end
+     deprecate remove_connection: "Use #remove_connection_pool, which now returns a DatabaseConfig object instead of a Hash"
+
+     def remove_connection_pool(owner, pool_key = :default)
        if pool_manager = get_pool_manager(owner)
          pool_config = pool_manager.remove_pool_config(pool_key)

          if pool_config
            pool_config.disconnect!
-           pool_config.db_config.configuration_hash
+           pool_config.db_config
          end
        end
      end

cc/ @rafaelfranca @matthewd @jhawthorn @tenderlove
AR Baseのremove_connectionの値を返すように変更してよいかどうか今のところ不明だが、ドキュメントに記載がないのでこのやり方にして他を非推奨にする方法を選んだことで、オブジェクトをどこにでも引き回せるようになった。configuration_hashを返すことの問題は、返された設定がestablish_connectionに渡されるとRailsが新しいオブジェクトを生成することだった。このpublicメソッドの戻り値の変更について何か意見は?
同PRより抜粋・大意

⚓新機能: ActionCable::Channelにストリームを個別に終了する機能が追加

ユーザーが特定のストリームのサブスクライブを解除できるよう、ActionCable::Channel#stop_stream_fromActionCable::Channel#stop_stream_forを追加した。
まとめ:
ユーザーがstream_from``stream_forを繰り返し呼ぶと1つのチャンネルで複数のストリームをフォローできるようになる。把握している限りでは、ActionCableには全ストリームのフォローを解除するActionCable::Channel#stop_all_streamsしかなかったので、ユーザーが特定のストリームをサブスクライブ解除できるようメソッドを2つ追加した。
同PRより大意


つっつきボイス:「いわゆるpub/sub」「ぱっと見startとstopかと思ったらstopだけでした😅」「fromとforは対象が違うのか」

# actioncable/lib/action_cable/channel/streams.rb#L105
+     # 名前付きブロードキャストのストリームをサブスクライブ解除
+     def stop_stream_from(broadcasting)
+       callback = streams.delete(broadcasting)
+       if callback
+         pubsub.unsubscribe(broadcasting, callback)
+         logger.info "#{self.class.name} stopped streaming from #{broadcasting}"
+       end
+     end
+
+     # モデルのストリームをサブスクライブ解除
+     def stop_stream_for(model)
+       stop_stream_from(broadcasting_for(model))
+     end

「この||= {}は?↓」「これは単にストリームが空のときは空ハッシュを返したいということです😆」「修正前は配列だったんですね」「そこが今回の修正にも関連していますね: 配列だとeachとかで取り出さないといけないけど、ハッシュにすればbroadcastingを引数にして特定の名前付きストリームを後から指定できるようになりますし」「なるほど!」「delegate :pubsub, to: :connectionとかせやなという感じ☺

# actioncable/lib/action_cable/channel/streams.rb#L127
      private
        delegate :pubsub, to: :connection

        def streams
-         @_streams ||= []
+         @_streams ||= {}
        end

「そんなにやらないかもしれないけど、特定のストリームのstopってたしかに使いたいときありそう、つか今までなかったのね😆

⚓番外: マイグレーションファイルの空行を削除

# activerecord/lib/rails/generators/active_record/migration/templates/migration.rb.tt#L33
<%- if migration_action -%>
  <%- if attribute.reference? -%>
    remove_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %><%= foreign_key_type %>
  <%- else -%>
    <%- if attribute.has_index? -%>
    remove_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
    <%- end -%>
-   <%- if !attribute.virtual? %>
+   <%- if !attribute.virtual? -%>
    remove_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %>
    <%- end -%>
  <%- end -%>

つっつきボイス:「公式の更新情報にしてはめちゃトリビアかなと思って😆」「誰もが消すヤツ😆: あ〜-が脱落してたのか」「ERBの-%>って改行しない指定でしたっけ?」「ですです」

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

“-“: 行末が-%>のとき改行を出力しない。また、行頭が<%-のとき行頭の空白文字を削除する
docs.ruby-lang.orgより

⚓Rails

⚓PunditとCanCanCanを比較しながら学ぶ


つっつきボイス:「どちらもいわゆる権限管理のgemですね」「どっちに乗るかで常に争いの火種になるヤツ🔫」「まあまあ😆」「使いたい方を使えばいいんじゃないかと😆: なお自分はCanCanCanキライじゃないんだけど、そうではない人もいましてですね🤣」「かといってPunditが好きというわけでもないと聞きました🤣」「マシという程度かも😆

「印象としてはPunditの方が崩壊しずらいけど、CanCanCanはリクエストから原因を比較的追いかけやすい感じですかね、といってもここまでやられると↓つらそう😇」「ヤメテ〜😆


同記事より


目次より、比較のポイント:

  • パーミッションのコード構成
  • レコードのフェッチ方法
  • テストのやりやすさ
  • パフォーマンス

個人的にはこの種のgemはパフォーマンスで選ばない方がいいと思う。既存のアプリでgemを乗り換えるのはたぶん意味ない。
個人的にはPunditがおすすめか: パーミッションをコントローラのアクション単位で定義できるし。
同記事より抜粋・大意

⚓Active Recordメソッドを要件に合わせて選ぶ

関係ありませんが、medium.comのアカウントの試用期間が切れました。期間過ぎると有料だったとは…😇


つっつきボイス:「初心者に優しそうな記事です」「Active Recordはメソッド多すぎですから😆

「なるほどfind()find_by()where()とかか: この辺はぜひRails初心者に知っておいていただきたいですね〜、find()はレコードがないと404をraiseするけどfind_by()はしないとか☺

  • .find()
  • .find_by()
  • .where()

「そうそう、take()はなにげに速いんですよね🚄

  • take()
  • first()last()

「とは言うものの、こういうのは結局自分で動きを追いかけないとわかってこないんですよ😅: あとSQLもある程度わかっておく必要ありますし」「たしかに」「自分の書いたコードがどんなSQLを生成するかに関心を持ってないとなかなかこういう部分を追いかけませんし☺

「こういうのはコードレビューを受けるうちにだんだん身に付いてくると思います: この記事のレベルに達していないコードにはこういう記事のリンクを渡して読んでもらう方が早いかも😆」「身に付けておきたい常識ということですね😊

参考: find、find_by、whereの違い - Qiita

⚓ライブラリとフレームワークの違い


同記事より


つっつきボイス:「ライブラリとフレームワークの違い、これもよく言われているヤツですね☺: ところで最近のイベントでも似たようなテーマで発表しているのを見た気がしますね」「銀座Railsとかでしょうか?」「どれだったかな〜思い出せん😆

「この種の議論にはいろんな視点がありますけど、そのイベントで聞いた見解は割と自分にとってもしっくり来たんですよ😋」「ふむふむ」

「と思ったらこの記事で引用されている図↓(引用元: differencebetween.net)にもそれに通じる見解が書いてあった: 2段目のフレームワークの『自分のコードがフレームワークを呼ぶのではなく、フレームワークが自分のコードを呼ぶ』のあたりが自分にもしっくりくる👍


同記事より

「ライブラリメソッドはユーザーのコードから呼び出すものだし、Railsで言えば、Rails wayに沿ってコードを書けばRailsフレームワークがそのコードを呼び出す: これが自分が持つライブラリとフレームワークのイメージに近いですね☺

⚓中間テーブルを疑え


つっつきボイス:「Andy Crollさんの短い記事です」「HABTM(has_and_belongs_to_many)久しぶりに見た😆」「やめとけってよく言われるヤツですね😆」「とっくにdeprecatedと思ってたけど、いつの記事?」「今年出た記事ですね」「2020年にHABTM見るとは思わなかった😆

# 同記事より
class User < ApplicationRecord
  has_and_belongs_to_many :organisations
end

後で探すと、Rails 6になってもHABTMは一応残ってますね。

現時点のmasterで確認すると「has_and_belongs_to_manyの中間テーブルにカラムを追加することがdeprecated」ということでした。

WARNING: The use of extra attributes on the join table in a has_and_belongs_to_many association is deprecated. If you require this sort of complex behavior on the table that joins two models in a many-to-many relationship, you should use a has_many :through association instead of has_and_belongs_to_many.
github.com/rails/railsより

参考: 仕事のねた: rails3でHABTMが非推奨になってる — 2011年の記事です


「記事の中身はというと、HABTMよりも、名前に意味のあるモデルでやろうという基本的な話かな☺」「ふむふむ」「中間テーブルが持つエンティティに意味があるケースは多いんですけど、Railsのhas_many through:がそのまま使えるとも限らないんですよ」「記事にもhas_many through:はHABTMよりもメソッドが少ないってありますね😳」「HABTM使わなくなって長いんでもう思い出せませんけどっ🤣

⚓GitHub ActionsでRailsとPostgreSQLをセットアップ


つっつきボイス:「チラ見した感じでは結構いい記事っぽい!❤」「やった!」「こういうテンプレ的なものがあるのはいいですね〜😋: GitHub Actionsやりたい人によさそう👍」「たしかに😋」「CIのコンフィグとか、全部自分で考えて構築するよりも誰かが確立したテンプレでやりたいですし☺」「翻訳しようかな😋」「完璧かどうか知りませんけど😆、少なくとも記事のとおりにやれば動くコンフィグ書けそうですし」

目次より:

  • 必要なもの
  • 完全なワークフローの例
  • オプション: secretを環境変数として使う
  • GitHub Actionsを使ってみた所見と現状の問題点:
    • .nvmrcや.ruby-versionのサポートがない
    • ged/ruby-pgなどはlibpq-devを自分でapt installしないといけない(デフォルトにすべき)
    • 新バージョンが出たら通知してくれたらいいのに(ボットがプルリク投げてくれたらさらに嬉しい)
    • ワークフローを手動でトリガできない

⚓Shopify流: Railsで速いコードを書く方法

つっつきボイス:「翻訳記事です」「Shopifyはスゴいですよね〜: エンジニア200人ぐらいいるらしいですけど、Railsエンジニアがそんなにいて崩壊しないところとか💪」「マジで😳

「『クエリキャッシュを信用しすぎない』はたしかに☺

「Large Hadron Migrator?」「Shopifyの独自ツールみたいですね☺」「ハドロンってやっぱり粒子加速器のアレだ: ハドロン、バリオン、メソンとか😆」「へ〜、どうやらLHMは、テーブルをALTERするとロックされるみたいなよくある問題をよしなにうまくやってくれるものみたい」「あ〜以前のウォッチでも話ありましたね」「LHMのThrottlerは、スレーブというかレプリカにそのまま投げると確実に詰まるような巨大データをよしなに絞ってくれるっぽい」「なるほど」


同リポジトリより

参考: 大型ハドロン衝突型加速器 - Wikipedia

「きっと特定のRDBMS用だろうと思ったらやっぱりMySQL用ですね: Shopifyは自分で作って自分で使ってるからいいんですけど、外部の人間としてはこのgemの挙動をきっちり理解しないと怖くて使えない😱」「😆」「データベースのマイグレーションに失敗すると悲惨ですから😇

「それに最近耳にした噂&うろ覚えレベルでは(おそらく銀座Railsあたり?)、MySQL 8あたりからはALTER TABLEしてもロックしなくなったらしいとか、あとAWS Auroraもいけるらしいとか何とかあるみたいですし」「MySQLもいろいろ変わってきてるんですね☺」「MySQLのissueとか注意点はいろいろあがってるので、彼らも修正しようと頑張ってますし☺

「お、このツールはforkを辿るとSoundCloudのリポジトリになってる: こういうgemを公開しているとは知らなかった」「ホントだ😳: gem作ってるということはどっかでRails使ってるんでしょうね」「でしょうね」

「SoundCloudって?」「およ、このアイコン↑見たことありません?」「Instagramの音楽版みたいなSNSでしたっけ」「そんな感じですね: 音楽ずっとやってたならむしろ詳しいかと思ってましたけど😆」「近年は音楽方面を努めて見ないようにしてるので😅

⚓その他Rails

つっつきボイス:「この間BPSの社内Slackに貼った記事なんですけど、手元では既に翻訳したので近々公開します」「そうそう、オブジェクト指向トレースの記事😋」「記事で使っているtapping_deviceにグラフ表示を追加する構想もあるそうです❤

「Rubyってそもそもこういうデバッグ作業がツラいんですよね😭: 特にRailsみたいに動的にクラスやモジュールをロードするような世界だとキレイなグラフを描くのは難しいですし」「う〜む」「自分たちが書いたコードに絞り込めるならやれると思うので、それができるならいいなと思いますね☺

「いわゆるDDDというか、ビジネスロジックをちゃんとオブジェクト指向で開発していれば、こういうツールでのグラフ化はもっとやりやすくなるでしょうけど: Railsフレームワークの動きは見ないようにして、自分たちのロジックだけにフォーカスして呼び出し依存を調べたりとか☺」「なるほど」


「最近流行りのエントリです😆」「ソニックガーデンの伊藤さん編サイコー😆」「😆」「それにしてもよくこんなにたくさん書けるなって思いますね☺」「怒涛の勢い🌊」「自分もこのお題で書いてみようかな?😋

「Rubyのブロックが最初わからなかったとあるのが意外でした」「Rubyのブロックは最初はわからないと思いますよ: あれは結局のところ他の言語で言うlambdaですし」「お〜」「今でこそJavaにもlambdaありますけど昔はありませんでしたし☺

参考: Java 8 のイディオム: 完璧なラムダ式がたった 1 行である理由

「Rails 3.1ぐらいで初めてRuby触ったときもeachで回してdoするとか最初意味わかりませんでしたもん😆」「へぇ〜!」「ブロックが引数として渡されるという概念が当時の自分になかったんですよね: PHPならわかったんで最初はPHPの関数渡しみたいなものかな?って思ったり😆

「ルーティングとRESTも最初わからなかったともありますね」「自分はRESTについては、あのあまりにも有名な論文↓で先に知ってたので😆」「先行する論文があったんですね😳」「ノリとしては『HTTPメソッドを見直そう』という感じで、論文が出た2000年頃ってAjaxが流行りだしたあたり」「なるほど」「そういうのを思い思いにやるんじゃなくて、RESTfulにやるのが大事だよね、という流れでしたね☺

参考: Architectural Styles and the Design of Network-based Software Architectures
参考: REST論文からアーキテクチャを進化させる方法論を学ぶ - Qiita


「Javaのようなきれいなアーキテクチャのある世界からRailsを見たらこういう見解になるかもしれませんね☺: 自分はPHP沼方面から来たんですけど、PHPとかPerlのようなスクリプト言語系で、しかもみんながオレオレフレームワークを作りまくってたような何でもありな世界からRailsに来ると、ジェネレートみたいにスクリプト言語の柔軟性を活かしながら、Java的な固さも取り入れていて、これがRailsなのかな〜って思ったりしましたね😉」「なるほど〜」


前編は以上です。

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

週刊Railsウォッチ(20200121後編)RubyKaigi 2020受付開始、RubyGemsとBundlerの今後、ファイル同期ツールMutagenほか

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

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

Rails公式ニュース

週刊Railsウォッチ(20200128後編)もう一つのgemマネージャgel、”Did you mean”の仕組みを追う、DXOpalでブラウザゲームほか

$
0
0

こんにちは、hachi8833です。

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

⚓お知らせ: 公開つっつき会2本立て

⚓その1: 週刊Railsウォッチ「第19回公開つっつき会」(無料)

いよいよ第19回を迎えた公開つっつき会は、来月2月6日(木)19:30〜よりBPS株式会社Pubスペースにて開催いたします。

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

⚓臨時のお知らせ: 福岡でのリモート公開つっつき会は「延期」いたします(Wingdoor@福岡)

今週木曜に予定されていた福岡での公開リモートつっつき会は延期となりました🙇


connpass.comより

⚓Ruby

⚓DXOpal: Rubyでブラウザゲーム


つっつきボイス:「OpalをベースにRubyでブラウザゲームを作れるライブラリだそうです」「お〜Opal頑張るな〜😳


opalrb.comより

「記事で紹介されているるびまのDXOpal記事↓は2018年のなので割と最近ですね」「Opalでここまでやるとは😆」「ゲームづくり初心者へのお題にするのはキツいでしょうか?」「Opalを使う時点でRubyからトランスパイルされたJavaScriptと戦わないといけなくなるから、入門者には相当ハードル高いのでは😆

参考: るびま0057: Rubyで始めるゲームプログラミング - DXOpal編 -

⚓RubyのRationalとBigDecimalのバグ

# #16518より
> 1 == BigDecimal(1)
=> true
> Rational(1, 1) == Rational(BigDecimal(1), 1)
=> true
> Rational(1, 6) == Rational(BigDecimal(1), 6)
=> true
> Rational(1, 60) == Rational(BigDecimal(1), 60)
=> false

つっつきボイス:「これもst0012さんが見つけたRubyのバグです」「ああBPS社内Slackに貼ってあったヤツ: これは相当びっくりする謎挙動😆」「issueでも驚かれてますね」「どうしてこうなるのかマジわからない😆」「もともと以下のRails issue↓を追っていて見つけたんだそうです」「DateTimeの丸めで起きるバグとか踏みたくない〜😅」「1秒ずれてる…」

# #38041より
# Rails 5.2.4.1: OK
"2019-12-19 10:00".to_datetime - BigDecimal(1).hour
=> Thu, 19 Dec 2019 09:00:00 +0000
# OK

# Rails 6.0.2.1: WRONG
"2019-12-19 10:00".to_datetime - BigDecimal(1).hour
=> Thu, 19 Dec 2019 08:59:59 +0000

⚓gel: もうひとつのgemマネージャ


つっつきボイス:「先週のウォッチで紹介した@hsbtさんのスライド↑で知りました」「アイコン溶けてる🍨」「rubygemsにもbundlerにも依存せずにbundlerと同じような感じでやれるようで、スライドにもこのgemから学ぶべきとありますね」「まあ今はbundlerとrubygemsが統合される流れになってますけど」「現状だとメンテが大変という話が先週もありましたね(ウォッチ20200121)」「この辺は今後も進化し続けるでしょうし、新しいものを取り入れていくのがRubyやRailsの文化ですし、いいんじゃないでしょうか☺

⚓その他Ruby

# 同記事より
Traceback (most recent call last):
fun.rb:5:in `<main>': undefined local variable or method
                      `greeet' for main:Object (NameError)
Did you mean?  greet

つっつきボイス:「Rubyの”Did you mean”の仕組みを追った長めの記事です」「あそこはなかなか込み入ってます😆」「考えてみたら結構すごいことやってるんですよね」「アルゴリズムとかいろいろ面白いですし: 推測が外れることもありますけど😆

「最近だとfishみたいな新し目のシェルにもDid you mean的な機能あったりしますし」「そういえば」

参考: 環境構築:ユーザフレンドリーで補完機能の強力なfishへ移行(Login shellをbashからfishへ移行)

⚓DB

⚓PostgreSQL全文検索のエッジケース(DB Weeklyより)

-- 同記事より
CREATE FUNCTION skills_tsv_update_trigger() RETURNS trigger AS $$
begin
  new.tsv :=
    to_tsvector('pg_catalog.english', COALESCE(new.title, ''))
  return new;
end

$$ LANGUAGE plpgsql;

つっつきボイス:「to_tsvectorっていう関数で生成してるんですね」「text search vectorなのか」

「たとえばroofingという語はあるのにroofiで全文検索すると何も出てこないという問題だそうです」「JSON extractとかでもよくありそうな、使い方が実は違ってる的な問題?😆」「英語で活用される部分がこぼれちゃったとか?」「'pg_catalog.english'とかあるし、そんな雰囲気」「バイナリにしないといけないとか?」「でもそれだと自然言語検索になりませんし」「それもそうですね」

-- 同記事より
SELECT * FROM skills
WHERE
    tsv @@ to_tsquery('pg_catalog.english', 'roofi:*')
-- no records found

「結論としては'pg_catalog.simple'についてもto_tsvectorする行を足して結合したらそれっぽく解決できた↓」「なるほど」「英語の自然言語検索って自分たちはめったに実装しないからな〜😆

-- 同記事より
CREATE FUNCTION skills_tsv_update_trigger() RETURNS trigger AS $$
begin
  new.tsv :=
    to_tsvector('pg_catalog.english', COALESCE(new.title, '')) ||
    to_tsvector('pg_catalog.simple', COALESCE(new.title, ''))
  return new;
end

$$ LANGUAGE plpgsql;

「この記事書いた人は全文検索使いまくってるそうですけど、SQLのRDBMSで全文検索ってあんまりやらない印象ありますね」「全文検索はSQLの標準機能じゃないので、MySQLでもぽすぐれでもたいてい拡張を使わないとできませんね🧐

⚓tbls: データベースの仕様をMarkdownで一気に出力(DB Weeklyより)


同リポジトリより

試しに動かしてみました

tbls doc postgres://hachi8833:$DB_PASSWORD@localhost:5432/enno_development?sslmode=disable
dbdoc/README.md
dbdoc/ar_internal_metadata.md
dbdoc/patterns.md
dbdoc/schema_migrations.md
dbdoc/spell_checks.md
dbdoc/import.patterns.md


つっつきボイス:「Graphvizっぽい画像かな: まあよくあるツールです😆」「でしたか😅」「いっぱいありますよ: Railsにもコマンドラインレベルならrails aboutとかありますし☺」「ツールのdependencyがやけに多いなと思ったら、BigQueryやらRedshifやらたくさんのデータベースに対応しているみたいですね」「まあRailsのSTI(Single Table Inheritance)とかはちゃんと図を出せなさそうな気がしますけど😆」「そうかも😅」「使うならCIとかで自動更新しないと古いのが残ったままでハマりの元になりそうですし☺

「Markdown形式の仕様書が求められるならこのツールでいいけど、日本だとだいたい例のA5:SQL Mk-2↓になりますよね😆ウォッチ20181210)」「名前の覚えにくい例のツールですね😆」「何といってもExcel形式で生成されますし☺

参考: A5:SQL Mk-2 - フリーの汎用SQL開発ツール/ER図ツール .. 松原正和


a5m2.mmatsubara.comより

⚓その他DB(DB Weeklyより)


つっつきボイス:「Microsoftの公式アカウントがぽすぐれ愛のYouTube動画を公開してるのが何とも😆」「最近のMicrosoftはSQL Serverにそんなにこだわってない感じですし」「ですね」「これはMicrosoft Igniteというイベントのスピーチらしい↓」「このイベント知らなかった😳」「ミートゥー😆」「ignite(点火)とnightをかけてるんでしょうね」

参考: Microsoft Ignite The Tour 2019-2020

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

⚓大阪がリージョン化へ(Publickeyより)


つっつきボイス:「まあ東京で仕事してたらあまり関係ありませんけど😆」「今までは公式のリージョンじゃなかったんですね」「非公式の大阪リージョンはありましたけど、今までは限定的だったのがついにここまで🎉

「たしか大阪リージョンができたときはAZ(Availability Zone)が2つしかなくて、サービスも標準より少なかった覚えがあります☺」「なるほど」「AWSのサイトで見るともう大阪使えるようになってますね↓: ap-northeast-3か」「2はソウル」「できた順だからしょうがない😆


docs.aws.amazon.comより

⚓G SuiteアカウントでWindows 10にログイン(Publickeyより)


つっつきボイス:「これはちょっと色めき立ちますね❤」「前にマイクロソフトがGoogleアカウントでWindowsやOffice 365にログインできるようになったニュースがありましたけど(ウォッチ20180910)、これはGoogle側の対応なんですね」「この辺は技術的にはSAML↓か何かで共有とかすればやれると思います」「さむるですか」

参考: Security Assertion Markup Language - Wikipedia

⚓その他インフラ


つっつきボイス:「ロボット業界ニュースに出ていたドコモ6Gの記事です」「まあホワイトペーパーなので一種夢のお絵かきと思っておけば😆: でも2030年はそんなに遠い未来でもないか🤔

「お、DOCOMO Open House↓でやってたのか」「このイベントは?」「ドコモは昔からこうやって未来のコンセプト動画をぶち上げることを数年おきにやっているんですよ☺」「へ〜!」「近未来のインフラの姿とかをドラえもん的に描くヤツです📺: 当たってる予想もあればそうでないのもあったりして結構面白いですよ😋」「なるほど〜」「DOCOMOみたいな企業はニーズを作るところから手掛けるので☺

参考: DOCOMO Open House 2020 - ようこそ、5Gリアルワールドへ。そしてその先へ。 - — 終了しました

⚓JavaScript

⚓ava: JSテストフレームワーク

参考: テストフレームワーク「ava」の基本的な使い方をまとめてみる。 - “BOKU”のITな日常


つっつきボイス:「avaは最近よく使われているみたいなんですけど、JavaのJが取れたみたいな感じですね😆」「単なるテストランナーだけじゃなくてテストスイートも入ってるみたい: ↓こういうハイライトはありがたいな〜😋」「おぉ」「必要そうなものはだいたい入っている感じかな: きっとvscode連携とかでみんな使うようになるんでしょうし😆」「でしょうね😆


同リポジトリより

⚓きれいに書き直したJSコードがrevertされた理由(JavaScript Weeklyより)

// 同記事より
let {top, bottom, left, right} = Directions;

function createHandle(directions) {
  // 20 lines of code
}

let fourCorners = [
  createHandle([top, left]),
  createHandle([top, right]),
  createHandle([bottom, left]),
  createHandle([bottom, right]),
];
let fourSides = [
  createHandle([top]),
  createHandle([left]),
  createHandle([right]),
  createHandle([bottom]),
];
let twoSides = [
  createHandle([left]),
  createHandle([right]),
];

function createBox(shape, handles) {
  // 20 lines of code
}

let Rectangle = createBox(Shapes.Rectangle, fourCorners);
let Oval = createBox(Shapes.Oval, fourSides);
let Header = createBox(Shapes.Rectangle, twoSides);
let TextBox = createBox(Shapes.Rectangle, fourCorners);

つっつきボイス:「きれいに書き直したら翌日『戻せ』と言われたんだそうです」「こういうのはプロジェクトでもときどき問題になりますね: 意味のない書き直しをされると時間も食われるし、アウトプットにもならないし、開発も進まないし😇」「記事の人も当時はせっかくDRYに書いたのにと思っていて、それがよくなかった理由を納得するのに数年かかったそうです😅」「たとえよかれと思ってやったとしても、レビュアーの工数も吸い取られるし、いいことないですよホント😢」「自分も気をつけないと😅

⚓その他JS

// 同記事より
const uuidv3 = require('uuid/v3');

// ... using predefined DNS namespace (for domain names)
uuidv3('hello.example.com', uuidv3.DNS); // ⇨ '9125a8dc-52ee-365b-a5aa-81b0b3681cf6'

// ... using predefined URL namespace (for, well, URLs)
uuidv3('http://example.com/hello', uuidv3.URL); // ⇨ 'c6235813-3ba4-3801-ae84-e0a6ebb7d138'

// ... using a custom namespace
//
// Note: Custom namespaces should be a UUID string specific to your application!
// E.g. the one here was generated using this modules `uuid` CLI.
const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341';
uuidv3('Hello, World!', MY_NAMESPACE); // ⇨ 'e8b5a51d-11c8-3310-a6ab-367563f20686'

つっつきボイス:「JSでUUID生成だそうです」「ID生成器は速度も重要な要素ですね☺」「READMEにbut not this smallってあるのは何だろう?」「ああ、やりたいのはこういうスモールさじゃないぞ↓ということね😆」「😆」「UUID生成は自分で書きたくないな〜」

// https://gist.github.com/jed/982883#gistcomment-45186 より
function b(a){return a?(0|Math.random()*16).toString(16):(""+1e7+-1e3+-4e3+-8e3+-1e11).replace(/1|0/g,b)}

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

⚓Reporting API


つっつきボイス:「jxck.ioさんのReporting API記事です」「へぇ、Report-ToヘッダーでReportingエンドポイントを外におけるのか」

# 同記事より
Report-To: {
             "max_age": 36000,
             "endpoints": [
               {
                 "url": "https://reports.example.com/default-endpoint"
               }
             ]
           },
           {
             "group": "csp-endpoint",
             "include_subdomains": true,
             "max_age": 36000,
             "endpoints": [
               {
                 "url": "https://reports.example.com/csp-endpoint1",
                 "priority": 1,
                 "weight": 50
               },
               {
                 "url": "https://reports.example.com/csp-endpoint2",
                 "priority": 1,
                 "weight": 50
               },
               {
                 "url": "https://failover.example.com/csp-endpoint",
                 "priority": 2
               }
             ]
           }

「structured header(SH)も、ブラウザの機能が今後ますます増えていくことを考えたらこういうのは欲しいですね😋」「何かあったときにブラウザからレポートしてくれると」「deprecateされたAPIをブラウザが呼んだときに発生するDeprecation Reportも欲しいヤツ: deprecatedかどうかはブラウザからじゃないとわからないので」「おぉ」「Feature Policy Violationとかも流れてきてくれたら嬉しい😂

参考: Switch reporting to Structured Headers · Issue #177 · w3c/reporting

// 同記事より
{
  "type": "deprecation",
    "url": "https://blog.jxck.io/entries/2018-05-15/webauthentication-api.html",
    "body": {
      "id": "ChromeLoadTimesWasAlternateProtocolAvailable",
      "anticipatedRemoval": null,
      "message": "chrome.loadTimes() is deprecated, instead use standardized API: nextHopProtocol in Navigation Timing 2. https://www.chromestatus.com/features/5637885046816768.",
      "sourceFile": "chrome-extension://mpbpobfflnpcgagjijhmgnchggcjblin/content.js",
      "lineNumber": 5,
      "columnNumber": 19
    }
}

{
  "type": "deprecation",
    "age": 27,
    "url": "https://example.com/",
    "user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0",
    "body": {
      "id": "websql",
      "anticipatedRemoval": "1/1/2020",
      "message": "WebSQL is deprecated and will be removed in Chrome 97 around January 2020",
      "sourceFile": "https://example.com/index.js",
      "lineNumber": 1234,
      "columnNumber": 42
    }
}

「まだまだ仕様が安定していないのは今はしょうがない」「あとモニタリング系サービスがReportingをサポートするというアイデアもいいですね: ScoutやNew Relicのようなサービスがこれをサポートしたら端末ごとの情報収集とかもできますし😍


scoutapm.comより

⚓最近のフォトショ


つっつきボイス:「画像はリンク先で見ていただくとして、最近のプラグインのインテリジェントぶりがすごいですね😳」「へぇ〜、細かい髪の毛だけじゃなくて、自動車の窓の向こう側まで切り取るとか、アルファ値も処理してるっぽい」「窓ガラスの向こうがきれいに切り取られてますね😳」「こういうプラグインがあるということを知っておくのがとりあえず大事😆

参考: アルファ値 (デジタル画像) - Wikipedia

⚓その他フロントエンド


つっつきボイス:「とりあえずネコがカワイイ😼」「(デモ画像を見ながら)まさにバーチャルブラウザか: Dockerイメージを起動するとlocalhostでリッスンして、その中で仮想ブラウザが動いてる」「あ〜そういうことですか!」「WebRTCでつないでくれるのね↓」

参考: WebRTC - Wikipedia

「nekoは何か既存のライブラリをうまく使ってるんだろうか🤔」「ツイートの続きを見るとPionというのがありますけどこれかな?」「PionはC++のライブラリでどことなくFirefoxっぽいけどよくわからん😆


つっつきボイス:「個人開発向けの本ということですけど、コードを書いた後のデプロイの解説が評判いいみたいです」「へ〜、こういうノウハウがまとまっているのはなかなか良さそう❤

「目次が見たいな」「Amazonにはスクショの目次しかありませんでした😇」「なるほど、デプロイだけで1章あるのか」「後半がまるまるデプロイですね: 買ってみちゃおうかな👍」「中身見ないとわからないけど、Google AnalyticsやCloudflareとか、あと運用の話が入ってるのはいいですね〜👍」「やった!」「あとはボリュームがどのぐらい充実してるかですね☺

「この辺は開発を長くやってればだいたいわかってくるかもしれませんけど」「インフラエンジニアとしてでしょうか?」「いや、開発者全員が知るべきですっ😆

買いました😋

⚓言語・ツール

⚓verona: マイクロソフトの研究用言語

// 同リポジトリより
// x は隔離されたオブジェクトグラフ
var c = cown(x)
// cはxへのアクセスを調停するcown
// xへのダイレクトアクセスはここで失われる

when (var x = c)
{
  // ここで名前xを用いてcown(c)の内部にアクセスする
  Builtin.print("Hello\n")
}
Builtin.print("Goodbye\n")

つっつきボイス:「Concurrent Ownershipとかいう新しい概念の有効性を確認する目的で試作した言語らしいです」「Rustをフィーチャーした言語を作るみたいな話があったけどそれかな↓」「ああこれです」「見た感じ普通にプログラミング言語でしょうね☺

参考: マイクロソフト、「Rust」に基づくプログラミング言語プロジェクト「Project Verona」がGitHubに - ZDNet Japan

 Microsoftは、Project Veronaを「リサーチプログラミング言語」だととらえており、オープンソース化によって、並行オーナーシップ(所有)というコンセプトについて探求したいと考えている学術分野の協力者を呼び込みたいと考えている。
japan.zdnet.comより

「ちなみにconcurrent ownershipはどうも米国の法律用語(不動産方面)をもじっているみたいで、ググるとそっちばかり出てきました」「所有者が同時に複数いるという点ではこれもまさにそれでしょうね: リソースの保護とか管理とか」

参考: Definition of a Concurrent Ownership - Real Estate

⚓その他言語

つっつきボイス:「ガチ理論寄りのワークショップだそうです」「日本ソフトウェア科学会で理論寄りは珍しいかも🤔」「今回の開催地は佐賀県だそうです」「まあこういうワークショップは『次はどこでやろうかな😋🍽🍱🍶』みたいなノリで運営されてたりしますけど😆


つっつきボイス:「この記事読んだ気がすると思ったら2018年のですし😆」「ありゃ😅

「QRコードは一応目で読もうと思えば読めないこともありませんね☺: どの方向にスキャンするとか決まってるので」「今日の勉強会でメールのMIME multipartを手書きした話をちょっと思い出しました」「MIMEのmultipartデータはテキストエディタで誰でも作れますよ😆(lengthを合わせるとかは必要かもしれないけど)」「そうでしたか」「MIMEのmultipartは元々メールの仕様↓ですけど今はもうWebのフォーム送信とかあちこちで使われてますね☺」「そういえばMIMEの略語ってこれでしたね」「MIMEは見てのとおりメールが元祖なのに、メールじゃないものにめっちゃ使われているという😆

参考: RFC 2046 - Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types

⚓その他

⚓「インターネット電池社会」モデル


つっつきボイス:「元記事の誤植訂正でインターネット電池社会というコンソーシアムを知りました」「村井先生はこういうのを立ち上げるのが上手いですね😋」(以下延々)

⚓その他のその他

つっつきボイス:「オランダはあくまで通称😆」「オランダって呼んでるの日本ぐらい?」「オランダはもともと州の名前だったのが俗称の国名として使われたみたいで、言ってみれば日本を『関東』とか『本州』と呼ぶみたいなものかなと思って😆」「nlロケールには影響なさそうでよかった😆


つっつきボイス:「日本ではストロングゼロが蔓延してますけど😆」「あれは本当にヤバいみたいですね」「500mlで売るのはヤバい」

⚓番外

⚓ここまで来たか


つっつきボイス:「葉脈の顕微鏡動画みたい🌿」「このスケールで観測できたのがスゴい😳: さすがに電子のサイズだと不確定性原理とかで無理かな😆

参考: 不確定性原理 - Wikipedia


後編は以上です。

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

週刊Railsウォッチ(20200127前編)Railsでキーワード引数warning退治始まる、ライブラリとフレームワークの違い、ShopifyのRails高速化記事ほか

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

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

Ruby Weekly

Publickey

publickey_banner_captured

DB Weekly

db_weekly_banner

JavaScript Weekly

javascriptweekly_logo_captured

Ruby: 「オブジェクト指向トレース」とtapping_device gemで効率よくデバッグ(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

Ruby: 「オブジェクト指向トレース」とtapping_device gemで効率よくデバッグ(翻訳)

デバッグはプログラマーにとって日常的な仕事のひとつです。プログラミングのスキルのみならず、デバッグのスキルも磨き上げることが重要です。しかしリファクタリングやテストといったコーディングスキルの伸びに比べると、デバッグスキルがイマイチ伸びないと思ったことはありませんか?私も振り返ってみると、数年前のデバッグツールや手順をずっと使い続けていたことに気づきました。

私はここ数週間デバッグを効率よく行う方法を模索し続けてきました。本記事では、デバッグスキルを伸ばすのが難しい理由と、この問題に別の方面から取り組む方法について解説します。

(簡単のため、本記事では「バグ」という言葉を「以前問題なく動いていたプログラムを壊すコード片」という意味に限定して用います)

プログラムのデバッグ手順について

実際のデバッグの方法は、「比較」という極めて素朴な行為です。プログラムの「こう動くべき」動作と「実際の」動作を比較し、プログラムの実行中にそのズレがどこから始まるかを探し当てることでデバッグします。ほとんどの場合、それらがバグを見つける手がかり(バグそのものでなければですが)となります。これは以下の3つの手順で行います。

  1. 期待する実行パスを構築する
  2. 実際の実行パスを構築する
  3. 両者のパスを比較する

以下の図で例を示します。

実行パスを構築する

プログラムの「実行パス」とは、ある意味メソッドや関数の呼び出しのパス、あるいはもっとシンプルに「呼び出しパス」と考えることもできます。しかしプログラムのサイズによってはメソッド呼び出しが膨大になる可能性もありますので、普通は「ビジネスロジックに関連するメソッドの呼び出しパス」に限定します。たとえば、OrdersController#createリクエスト中にはArray#[]とかObject#inspectといった呼び出しが多数発生しますが、実行パスを構築するうえではOrderの作成にのみ注目し、それ以外は無視します。

実行パスを比較する

ほとんどの条件下では、プログラムのバグは振る舞いが最初に異なる場所(戻り値が異なる、違うメソッドが呼ばれるなど)で見つかります。そこで以下のケースでは、バグはおそらくbarメソッドかその依存ファイルの中に潜んでいます。

この方法の困難な点

ほとんどの開発者がこのアプローチでデバッグしていると私は信じていますが、アプローチは完全に同じではありません。理想としては実行パスをできるかぎり詳細に構築すべきであり、それによって正確な比較に必要な情報を十分に得られます。しかし現実には時間も足りませんし勤労意欲が落ちるときもありますので、そのバグに関連すると「思われる」メソッドをエイヤでいくつか選んで、後は選んだメソッドが正しいことを祈るのが関の山です。

つまり、デバッグ手法の効率性は、パスを構築するときにいかに適切なメソッドを選ぶかにかかっています。そしてこれは以下の要素に影響されます。

  1. その言語なりフレームワークなりにおける自分の経験
  2. コードベースをどれだけ理解しているか
  3. 運の強さ?

デバッグスキルの向上をそれほど心がけていなくても、デバッグに要する時間が時間とともに短くなっていく理由はこれです。

私見では、この現象の根本原因はデバッグ戦略とうまく調和する効率的なツールがないからだと思います。その点はputsデバッグであろうとpryデバッガーであろうと同じです。ほとんどの場合、プログラムの振る舞いを精査する作業には膨大な時間を要しますが、それにはいくつもの理由があります。

1. 引数を手動で集める必要がある。

# 超つらい
def foo(var1, var2)
  puts("var1: #{var1}")
  puts("var2: #{var2}")
  ...
end

2. 戻り値を手動で集める必要がある。

# これとか
def new_cart(attributes)
  Cart.new(attributes).tap { |c| puts(c) }
end

# これとか
cart = new_cart(attributes)
puts(cart)

3. さまざまなメソッド間を行ったり来たりしながら、putsやブレークポイントを置いたりする必要がある。

情報を集める作業を手動で行う割合が増えるほど、推測やバクチに頼るようになります。この作業はつらいだけでなく、さまざまな情報をかき集めるうちに以下のような別の問題まで発生します。

  1. 必要な情報(正しいメソッドを正しい場所で呼んでいるかなど)を効率的に集めるには、プログラムを十分理解する必要があります。経験の浅い開発者ほど理解は困難になります。
  2. ブレークポイントやトレース用メソッドを追加するとコードが汚染されます。たとえば、あるメソッドがあるオブジェクトのステートを変更しているとすると、コードのトレース時に(戻り値を取りたいなどの理由で)そのメソッドを呼び出すと、プログラムの振る舞いが変わってしまうかもしれません。
  3. 私の経験では、コードが散らかっていると自分がそもそも何をしたかったのか(=メソッド呼び出しを調べて深掘りする)を忘れてしまいがちです。

「オブジェクト指向トレース」

ここでデバッグ時に私たちが行っていることをよく観察すると、うまく活用できそうなパターンがいくつか見えてきます。たとえば以下がそうです。

  1. Webアプリケーションのある機能をデバッグしているのであれば、特定のエンドポイントのロジック(特定のコントローラのアクションなど)から調査を開始する手が考えられます。これはデバッグを開始するのに最適な場所となります。
  2. Rubyのようなオブジェクト指向プログラミング言語では、ある機能が1つまたは複数のクラスにリンクしているのが普通です。たとえばorderにバグがあるならOrderCreationServiceあたりを調べるべきでしょう。つまりオブジェクトのいくつかのメソッド呼び出しを観察することで実行パスの大半を構築できるわけです。

「オブジェクト指向トレース」という概念は、以上の仮定と、Rubyの強力なTracePoint機能の上に成り立ちます。いくつか例を示して説明します。

OrdersController#createというエンドポイントがあり、ここでorderを作成するとします。

class OrdersController < ApplicationController
  def create
    @cart = Cart.find(order_params[:cart_id])
    promotion = Promotion.find_by(id: order_params[:promotion_id])
    @order = OrderCreationService.new.perform(@cart, promotion)
    ......
  end

このorder作成の実行パスを構築してみたいと思います。これは以下のように書けます。

class OrdersController < ApplicationController
  def create
    @cart = Cart.find(order_params[:cart_id])
    promotion = Promotion.find_by(id: order_params[:promotion_id])

    TracePoint.new(:call) do |tp|
      if tp.self.class.name == "OrderCreationService"
        puts("Called :#{tp.callee_id} from #{tp.path}:#{tp.lineno}")
      end
    end.enable do
      @order = OrderCreationService.new.perform(@cart, promotion)
    end

上のコードから以下のような結果が出力されます。

Called :initialize from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:2
Called :perform from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:6
Called :validate_cart from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:17
Called :apply_discount from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:23
Called :create_order from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:27

これでOrderを1つ作成するためにOrderCreationServiceが何をしているかが判明し、かなり明確な実行パスを直ちに得られました。しかし、ここでRubyのメタプログラミング技を少々用いれば、メソッド呼び出しごとに引数を取るなどの詳細な情報を取れるのです!

class OrdersController < ApplicationController
  def create
    @cart = Cart.find(order_params[:cart_id])
    promotion = Promotion.find_by(id: order_params[:promotion_id])

    TracePoint.new(:call) do |tp|
      if tp.self.class.name == "OrderCreationService"
        puts("Called :#{tp.callee_id} from #{tp.path}:#{tp.lineno}")
        tp.binding.local_variables.each do |name|
          value = tp.binding.local_variable_get(name)
          puts("  Arg #{name}: #{value.inspect}")
        end
      end
    end.enable do
      @order = OrderCreationService.new.perform(@cart, promotion)
    end
Called :initialize from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:2
  Arg options: {}

Called :perform from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:6
  Arg cart: #<Cart id: 1, total: 10, customer_id: 1, promotion_id: nil, reserved_until: nil, created_at: "2020-01-19 08:41:51", updated_at: "2020-01-19 08:41:51">
  Arg promotion: #<Promotion id: 1, amount: 0.5e1, customer_id: nil, created_at: "2020-01-19 08:41:51", updated_at: "2020-01-19 08:41:51">

Called :validate_cart from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:17
  Arg cart: #<Cart id: 1, total: 10, customer_id: 1, promotion_id: nil, reserved_until: nil, created_at: "2020-01-19 08:41:51", updated_at: "2020-01-19 08:41:51">

Called :apply_discount from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:23
  Arg cart: #<Cart id: 1, total: 10, customer_id: 1, promotion_id: nil, reserved_until: nil, created_at: "2020-01-19 08:41:51", updated_at: "2020-01-19 08:41:51">
  Arg promotion: #<Promotion id: 1, amount: 0.5e1, customer_id: nil, created_at: "2020-01-19 08:41:51", updated_at: "2020-01-19 08:41:51">

Called :create_order from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:27
  Arg cart: #<Cart id: 1, total: 5, customer_id: 1, promotion_id: 1, reserved_until: nil, created_at: "2020-01-19 08:41:51", updated_at: "2020-01-19 08:41:51">

めちゃクールですよね?呼び出されたメソッドや、受け取った引数がすっかり明らかになりました。それでは最後の仕上げに戻り値も情報に加えてみましょう。

class OrdersController < ApplicationController
  def create
    @cart = Cart.find(order_params[:cart_id])
    promotion = Promotion.find_by(id: order_params[:promotion_id])

    TracePoint.new(:return) do |tp|
      if tp.self.class.name == "OrderCreationService"
        puts("Called :#{tp.callee_id} from #{tp.path}:#{tp.lineno}")
        tp.binding.local_variables.each do |name|
          value = tp.binding.local_variable_get(name)
          puts("  Arg #{name}: #{value.inspect}")
        end
        puts("  => #{tp.return_value}")
      end
    end.enable do
      @order = OrderCreationService.new.perform(@cart, promotion)
    end
Called :initialize from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:4
  Arg options: {}
  => {}

Called :validate_cart from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:21
  Arg cart: #<Cart id: 1, total: 10, customer_id: 1, promotion_id: nil, reserved_until: nil, created_at: "2020-01-19 08:59:13", updated_at: "2020-01-19 08:59:13">
  =>

Called :apply_discount from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:25
  Arg cart: #<Cart id: 1, total: 5, customer_id: 1, promotion_id: 1, reserved_until: nil, created_at: "2020-01-19 08:59:13", updated_at: "2020-01-19 08:59:13">
  Arg promotion: #<Promotion id: 1, amount: 0.5e1, customer_id: nil, created_at: "2020-01-19 08:59:13", updated_at: "2020-01-19 08:59:13">
  => true

Called :create_order from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:29
  Arg cart: #<Cart id: 1, total: 5, customer_id: 1, promotion_id: 1, reserved_until: nil, created_at: "2020-01-19 08:59:13", updated_at: "2020-01-19 08:59:13">
  => #<Order:0x00007f91455ebd10>

Called :perform from /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:13
  Arg cart: #<Cart id: 1, total: 5, customer_id: 1, promotion_id: 1, reserved_until: nil, created_at: "2020-01-19 08:59:13", updated_at: "2020-01-19 08:59:13">
  Arg promotion: #<Promotion id: 1, amount: 0.5e1, customer_id: nil, created_at: "2020-01-19 08:59:13", updated_at: "2020-01-19 08:59:13">
  => #<Order:0x00007f91455ebd10>

上の例を注意深く読んでいただければ、戻り値が加わった以外にも、コードや出力で変わった点があることに気づくでしょう。

  1. TracePointreturnイベントをトラッキングするようになった(その前の2つの例ではcallイベントだった)。
  2. 出力の順序がその前の結果とどことなく異なっている

理由は、メソッド呼び出しの完了(return)を、戻り値を取り出せるようになるまで待つ必要があるからです。つまり出力は(呼び出された順ではなく)メソッドが戻った順に並びます。

def perform
  # ...
  create_order
end

# call order: perform -> create_order
# return order: create_order -> perform

このわずかな違いを理解しておけば、出力を読んでいてもさほど混乱はしないでしょう。実際、私はこれがいくつかの場合にさらに有用であることに気づきました。たとえば、同じ値をさまざまなメソッドが更新して戻す様子を観察したいのであれば、戻り値順に並ぶ方が自然です。

オブジェクト指向トレースを用いれば、プログラムの呼び出しパス、呼び出しごとの引数、そして戻り値も一発で得られます。オブジェクト指向トレースは実に効率的であるのみならず、前述した問題のいくつかを回避することもできます。

  1. 従来だと、OrderCreationServiceのメソッド内部を把握して必要な情報を集めるために、OrderCreationServiceが何を呼び出すかを理解する必要があったが、OrderCreationService#performが呼び出される場所を把握するだけでできるようになった(これなら楽勝です😋)。
  2. OrderCreationServiceのロジックを詳しく把握したりテストする必要がなければ、コードを一切変更する必要がない。そのおかげで、デバッグ中でもコードを汚さずに済みます。

TappingDevice

欲しい機能はTracePointに既にひととおりありますが、もっと楽にやるための改良点がいくつかあります。

  1. ボイラープレートコード(デバッグ用のテンプレートコード)が10行もあるので覚えるのがダルい。
  2. TracePointに慣れないうちはいくつかエッジケースを踏んでしまうかもしれません(私もそうでした)。それを回避しようとするとボイラープレートコードが長くなりがちです。

そうした理由から、私は皆さんに代わってtapping_deviceを作って楽にやれるようにしました。さっき最後に書いたコード例を思い出せますか?私は無理です😆。では、あのボイラープレートのコード片を1個のメソッド呼び出しにしてみたらどうでしょう?

  def create
    @cart = Cart.find(order_params[:cart_id])
    promotion = Promotion.find_by(id: order_params[:promotion_id])
    service = OrderCreationService.new

    TracePoint.new(:return) do |tp|
      if tp.self.class.name == "OrderCreationService"
        puts("Called :#{tp.callee_id} from #{tp.path}:#{tp.lineno}")
        tp.binding.local_variables.each do |name|
          value = tp.binding.local_variable_get(name)
          puts("  Arg #{name}: #{value.inspect}")
        end
        puts("  => #{tp.return_value}")
      end
    end.enable do
      @order = service.perform(@cart, promotion)
    end
    # ...
  end

上のように書く代わりに、以下のようにたった2行追加するだけでやれます。

  include TappingDevice::Trackable # includeしておく

  def create
    @cart = Cart.find(order_params[:cart_id])
    promotion = Promotion.find_by(id: order_params[:promotion_id])
    service = OrderCreationService.new

    print_calls_in_detail(service) # <--- この場合はこれだけあればよい
    @order = service.perform(@cart, promotion)
    # ...
  end

これで以下のように同じ情報を得られます!

:validate_cart # OrderCreationService
    from: /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:8
    <= {:cart=>#<Cart id: 1, total: 10, customer_id: 1, promotion_id: nil, reserved_until: nil, created_at: "2020-01-20 07:09:22", updated_at: "2020-01-20 07:09:22">}
    => nil
:apply_discount # OrderCreationService
    from: /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:10
    <= {:cart=>#<Cart id: 1, total: 5, customer_id: 1, promotion_id: 1, reserved_until: nil, created_at: "2020-01-20 07:09:22", updated_at: "2020-01-20 07:09:22">, :promotion=>#<Promotion id: 1, amount: 0.5e1, customer_id: nil, created_at: "2020-01-20 07:09:22", updated_at: "2020-01-20 07:09:22">}
    => true
:create_order # OrderCreationService
    from: /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:11
    <= {:cart=>#<Cart id: 1, total: 5, customer_id: 1, promotion_id: 1, reserved_until: nil, created_at: "2020-01-20 07:09:22", updated_at: "2020-01-20 07:09:22">}
    => #<Order id: 1, number: nil, total: 5, customer_id: 1, promotion_id: 1, created_at: "2020-01-20 07:09:22", updated_at: "2020-01-20 07:09:22">
:perform # OrderCreationService
    from: /Users/st0012/projects/tapping_device-demo/app/controllers/orders_controller.rb:10
    <= {:cart=>#<Cart id: 1, total: 5, customer_id: 1, promotion_id: 1, reserved_until: nil, created_at: "2020-01-20 07:09:22", updated_at: "2020-01-20 07:09:22">, :promotion=>#<Promotion id: 1, amount: 0.5e1, customer_id: nil, created_at: "2020-01-20 07:09:22", updated_at: "2020-01-20 07:09:22">}
    => #<Order id: 1, number: nil, total: 5, customer_id: 1, promotion_id: 1, created_at: "2020-01-20 07:09:22", updated_at: "2020-01-20 07:09:22">

tapping_deviceは、print_calls_in_detailの他にも、print_tracesなどのさまざまな情報レベルに応じたAPIを提供しています。

さて、OrderCreationServiceには何も不審な点が見つからなかったとしましょう。次は@cartで何か問題が起きているのではないかと疑ってみることにします。print_tracesを用いれば、@cartがプログラムの他の部分とやりとりする様子を確認できます。

  include TappingDevice::Trackable

  def create
    @cart = Cart.find(order_params[:cart_id])
    promotion = Promotion.find_by(id: order_params[:promotion_id])
    service = OrderCreationService.new

    print_traces(@cart, exclude_by_paths: [/gems/]) # /gems/を除外してActive Recordの内部メソッド呼び出しを出力しないようにする
    @order = service.perform(@cart, promotion)
Passed as 'cart' in 'OrderCreationService#perform' at /Users/st0012/projects/tapping_device-demo/app/controllers/orders_controller.rb:9
Passed as 'cart' in 'OrderCreationService#validate_cart' at /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:8
Called :reserved_until from: /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:18
Called :errors from: /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:9
Passed as 'cart' in 'OrderCreationService#apply_discount' at /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:10
Called :apply_discount from: /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:24
Called :total from: /Users/st0012/projects/tapping_device-demo/app/models/cart.rb:6
Called :update! from: /Users/st0012/projects/tapping_device-demo/app/models/cart.rb:6
Passed as 'cart' in 'OrderCreationService#create_order' at /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:11
Called :total from: /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:28
Called :customer from: /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:28
Called :promotion from: /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:28

@cartを呼び出したメソッドはもちろん、@cartを引数に取ったメソッドも出力されるようになりました【要チェック】。これでデバッグ時間が少しは減ることでしょう😉

tapping_deviceの利用法について詳しく知りたい方は、readmeでさまざまなヘルパーをご覧いただけます。私がtapping_deviceでRailsのissueを修正したときのやり方については別記事『Debug Rails issues effectively with tapping_device』をどうぞ。

まとめ

デバッグ作業のほとんどは、プログラムの動作(あるいはかつて動作していた)に関する情報収集ですが、これを手動でやっていては相当時間を吸われてしまいます。プログラムの動作について雑に当たりをつけて人間らしく乗り切ろうとすると、問題解決にさらに時間を吸われてしまいます。

しかしオブジェクト指向トレースを援用すれば、オブジェクト指向のパラダイムとRubyの超強力なTracePointの力を借りて、プログラムを効果的に精査できるようになります。私はこの方法で、駆け出し開発者がデバッグするときのつらみも軽減でき、ベテラン開発者のデバッグプロセスも大きく加速できると信じています。

本記事やtapping_deviceについてお気づきの点がありましたら、お気軽に元記事にコメントをどうぞ。皆さんからのご相談を歓迎します!他のデバッグ戦略や、もっとよいデバッグ戦略をご存知でしたら、ぜひお知らせください😉

関連記事

Rails 6: Action Textのファイルアップロードを分解調査する(翻訳)

Railsのフラグメントキャッシュを分解調査する(翻訳)

週刊Railsウォッチ(20200203前編)Railsの各種高速化コミット、OpenAPIの使い所、パンくずリストgem loaf、Railsビュー最適化ほか

$
0
0

こんにちは、hachi8833です。先週はコロナウイルスの件で福岡でのリモート公開つっつき会が延期になりましたので、規模を縮小して公開でないリモートつっつき会をWingdoorの皆さまと開催いたしました🙇

参考: 新型コロナウイルスに関するQ&A|厚生労働省

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

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

いよいよ第19回を迎えた公開つっつき会は、今週2月6日(木)19:30〜よりBPS株式会社Pubスペースにて開催いたします。

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

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

公式の更新情報は出ていないので、直近のコミットリストから見繕いました。最適化・高速化のプルリクが目に付きました。

⚓高速化: hash_rowsのビルドにtransform_valuesを使うよう変更

# activerecord/lib/active_record/result.rb#L141
      def hash_rows
        @hash_rows ||=
          begin
            # We freeze the strings to prevent them getting duped when
            # used as keys in ActiveRecord::Base's @attributes hash
            columns = @columns.map(&:-@)
            length  = columns.length
+           template = nil

            @rows.map { |row|
-             # In the past we used Hash[columns.zip(row)]
-             #  though elegant, the verbose way is much more efficient
-             #  both time and memory wise cause it avoids a big array allocation
-             #  this method is called a lot and needs to be micro optimised
-             hash = {}
-
-             index = 0
-             while index < length
-               hash[columns[index]] = row[index]
-               index += 1
+             if template
+               # We use transform_values to build subsequent rows from the
+               # hash of the first row. This is faster because we avoid any
+               # reallocs and in Ruby 2.7+ avoid hashing entirely.
+               index = -1
+               template.transform_values do
+                 row[index += 1]
+               end
+             else
+               # In the past we used Hash[columns.zip(row)]
+               #  though elegant, the verbose way is much more efficient
+               #  both time and memory wise cause it avoids a big array allocation
+               #  this method is called a lot and needs to be micro optimised
+               hash = {}
+
+               index = 0
+               while index < length
+                 hash[columns[index]] = row[index]
+                 index += 1
+               end
+
+               # It's possible to select the same column twice, in which case
+               # we can't use a template
+               template = hash if hash.length == length
+
+               hash
              end
-
-             hash
            }
          end
      end

つっつきボイス:「hash_rowsはprivateだそうです」「transform_valuesはRuby 2.4で追加されたらしい↓けど、Ruby 2.7でさらに速くなったみたい😋」「😋

「字が細かくて今読めないんですが、これはどういうメソッドでしたっけ?」「transform_valuesはハッシュの値をeachとかで回すんじゃなくて一括で変更できるみたいなヤツだったと思います: まあ覚えてたら使うかなというメソッド☺」「なるほど」「使いみちとしては、値に共通のプレフィックスやサフィックスや引用符を付けたいときとか、値をまとめてエスケープしたいときとかかな」

参考: サンプルコードでわかる!Ruby 2.4の新機能と変更点 – Qiita

「Railsでこのtransform_valuesを使って速くしたということですね😊」「templateという一時変数を使ってるあたりのコードがよくわからななかったけど、どうやら以下のコメントあたりかな↓: Active Recordで同じカラムを2回selectできて、その場合はテンプレートは使えない、とそのまんまだった😆」「ふむふむ」「普段はテンプレートを使うけど、Active Recordで直接selectしたりして同じカラムを複数含む場合は従来の処理に切り替えるということと理解しました☺」「速くなってよかった❤

# activerecord/lib/active_record/result.rb#L172
+               # It's possible to select the same column twice, in which case
+               # we can't use a template
+               template = hash if hash.length == length
+
+               hash
              end

このコミットの変更点は、hash_rowsは最初のrowについては以前と同じにビルドし、以後のrowについては最初のrowをテンプレートとしてtransform_valuesでビルドするようになったこと。
これはRuby 2.4(transform_valuesの最初のバージョンはここで登場)以降わずかに速くなった。理由はハッシュがreallocationされなくなったことと、ハッシュのupdateロジックがおそらくaddよりも少しシンプルになったため。
Ruby 2.7以降はこれがさらに速くなった。理由はtransform_valuesがキーをハッシュ化せずに実行できるようになって値のリストだけをイテレートするようになったこと。
今回の場合rowは1つしかないので、インスタンス変数のセット/リードと条件が1つ余分に増えたのみ。
同PRより大意

結果は以下のとおりでした。


同コミットより

⚓高速化: キャッシュのexpanded_key周り

# activesupport/lib/active_support/cache.rb#L678
        def expanded_key(key)
          return key.cache_key.to_s if key.respond_to?(:cache_key)
          case key
          when Array
            if key.size > 1
-             key = key.collect { |element| expanded_key(element) }
+             key.collect { |element| expanded_key(element) }
            else
-             key = expanded_key(key.first)
+             expanded_key(key.first)
            end
          when Hash
-           key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
-         end
-
-         key.to_param
+           key.collect { |k, v| "#{k}=#{v}" }.sort
+         else
+           key
+         end.to_param
        end

つっつきボイス:「expanded_keyもprivateですね」「とりあえずexpanded_keyのソース見てみる↓」「どの辺がexpanded?😆

# activesupport/lib/active_support/cache.rb#L678
        def expanded_key(key)
          return key.cache_key.to_s if key.respond_to?(:cache_key)

          case key
          when Array
            if key.size > 1
              key.collect { |element| expanded_key(element) }
            else
              expanded_key(key.first)
            end
          when Hash
            key.collect { |k, v| "#{k}=#{v}" }.sort
          else
            key
          end.to_param
        end

キーをexpandしてstring値と一貫させる。オブジェクトがcache_keyに応答する場合はcache_keyを呼び、そうでなければto_paramを呼び出す。キーがハッシュの場合はアルファベット順でキーがソートされる。
同APIより

expanded_keyはキャッシュに保存するキーを取得するメソッドらしい🤔」「プルリクではkeyへの代入が削られてますね」「sort_bysortに変えたり」「修正前だとkey = key.sort_by { |k, _| k.to_s }でハッシュが作られるけど、ハッシュを作らずにやる方法に変えたり」「挙動を変えないリファクタリングで高速化しているようですね☺」「sortとcollectの順序も変わったみたい」「たしかにsortしてからcollectするよりcollectしてからsortする方が速いでしょうね😋

このPRはCache::Store#expanded_keyの実装をクリーンアップする。いくつかの代入を削除して、ハッシュキーのcollectやsortの順序を変更した。
同PRより大意

「コスメティックなリファクタリングにも見えるけど、ベンチマークの結果を見るとArrayとHashがだいぶ速くなった💪」「Stringではほとんど変わらないけど☺

⚓リファクタリング: callbacks.rbのメモリ使用量を大幅に削減

# activesupport/lib/active_support/callbacks.rb#L294
        def initialize(name, filter, kind, options, chain_config)
          @chain_config = chain_config
          @name    = name
          @kind    = kind
          @filter  = filter
          @key     = compute_identifier filter
-         @if      = check_conditionals(Array(options[:if]))
-         @unless  = check_conditionals(Array(options[:unless]))
+         @if      = check_conditionals(options[:if])
+         @unless  = check_conditionals(options[:unless])
        end
...
        private
+         EMPTY_ARRAY = [].freeze
+         private_constant :EMPTY_ARRAY
+
          def check_conditionals(conditionals)
+           return EMPTY_ARRAY if conditionals.blank?
+
+           conditionals = Array(conditionals)
            if conditionals.any? { |c| c.is_a?(String) }
              raise ArgumentError, <<-MSG.squish
                Passing string to be evaluated in :if and :unless conditional
                options is not supported. Pass a symbol for an instance method,
                or a lambda, proc or block, instead.
              MSG
            end

-           conditionals
+           conditionals.freeze
          end

つっつきボイス:「修正前はメモリ使用量7MBぐらいだったのが修正後はKB単位にまで減ってますね」「@if@unlessで余計なarrayを生成しないようにしたということか😋」「おぉ」「修正前はインスタンス変数の中で作られていたからインスタンスがあるとオブジェクトがずっと残っちゃっていたけど、修正後は生成をprivateメソッドの定義に移したからメソッドが終わればオブジェクトが解放される、だからメモリ使用量が減ったと」「なるほど」「さらにEMPTY_ARRAYの場合は生成せずに即戻るようになったので、ここで速くなったんでしょうね: @ifとか@unlessを使わない場合は生成不要ですし☺」「@ifとか@unlessはめったに使わないでしょうし😋

⚓テーブル名をRegexp.escapeで処理

# activerecord/lib/active_record/relation/query_methods.rb#L1255
      def table_name_matches?(from)
-       /(?:A|(?<!FROM)s)(?:b#{table.name}b|#{connection.quote_table_name(table.name)})(?!.)/i.match?(from.to_s)
+       table_name = Regexp.escape(table.name)
+       quoted_table_name = Regexp.escape(connection.quote_table_name(table.name))
        /(?:A|(?<!FROM)s)(?:b#{table_name}b|#{quoted_table_name})(?!.)/i.match?(from.to_s)
      end

そういえばMSSQLではテーブル名などで通常使えない文字(空白など)がある場合に[]でも囲めるのでした。

RDBMS オブジェクト識別子
Oracle "で囲む
PostgreSQL "で囲む
MySQL バッククォートで囲む
MSSQL "、バッククォート、[]で囲む

Web+DB Press Vol.112『RDBMS徹底比較』より抜粋


つっつきボイス:「ああ、Microsoft SQL Serverのテーブル名やカラム名を囲む[]をエスケープしたのね☺」「え?😅」「[]を使ってるの見たことありませんけどMSSQL使いには常識なのかな?🤔

⚓マルチDBのRelationでconnected_toが強制的にレコードを読み出すよう修正

# activerecord/lib/active_record/connection_handling.rb#L258
      def swap_connection_handler(handler, &blk) # :nodoc:
        old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
-       yield
+       return_value = yield
+       return_value.load if return_value.is_a? ActiveRecord::Relation
+       return_value
      ensure
        ActiveRecord::Base.connection_handler = old_handler
      end

connected_toブロックがリレーションを返してそれをinspectしなかった場合や、返す前にリレーションを読み込んだ場合、データベースにクエリをかける前にブロックが終了する。これが原因で、クエリ対象のデータベースコネクションが正しくなくなる。
その結果、レコードがreplicaではなくprimaryから取得され、データベースのパフォーマンスに影響する可能性もある。
リレーションはデータベースにlazyにクエリをかける。以下のようにブロックからリレーションを返すと、posts.firstのクエリはlazyかつブロックの外で実行されるので、writingコネクションから送信される。

posts = ActiveRecord::Base.connected_to(role: :reading) { Post.where(id: 1) }

to_aのように)ブロック内でリレーションを読み込むクエリではリレーションのレコードをeager loadingするのでこのバグは顕在化しない。
修正後のconnected_toは、戻り値がRelationかどうかをチェックして、該当の場合はloadを呼び出すようになった。
同PRより大意


つっつきボイス:「Rails 6のマルチプルデータベースはいろいろややこしそう😅」「eager loadingしなかった場合にクエリを出さずに終わっちゃうことがあったとは😳」「Post.where(id: 1)は本来readingロールのレプリカから読んで欲しかったのに、クエリがlazyなのでconnected_toのスコープを抜けてからposts.firstするとwritingロールのプライマリから取ってきてしまってた、というバグか😇」「これを踏むのは大規模なマルチDBでしょうけど、パフォーマンスチェックしてあれ?と思ったときに備えて、このあたりの挙動は知っておく必要ありそう🤔

⚓Rails

⚓Vue+Rails 6でのCRUD


つっつきボイス:「RailsとVue.jsで普通にCRUDを作ってみたという感じですね☺: rails new--webpack=vue付けて、VuetifyはVueがやってくれて、とか」「やってみた系の記事かな」「Vuetify.jsはMaterial Designフレームワークみたいです」

「これも記事に出てくるaxiosというJSライブラリはPromiseベースみたい」「最近はasync/awaitが広まってきたせいか、まだPromise使ってんの?みたいな風潮にちょっとなってたりするのかななんて😆」「😆」「自分がPromiseの謎記法に慣れちゃったのもありますが☺

// 同記事より
#app/javascript/packs/components/user.vue
getUser(item) {
  axios.get(`https://localhost:3000/${item.id}`)
   .then(response => {
     this.dessert = response.data;
    })
   .catch(error => {
    console.log(error);
    })
}
# 同記事より
#app/controllers/users_controller.rb
def show
  @user = User.find(params[:id])
  render json: { data: @user, status: :ok, message: 'Success' }
end

参考: Async/await

JavaScript: 5分でわかるPromiseの基礎(翻訳)

⚓Railsビューのパフォーマンス最適化(Ruby Weeklyより)

「こちらはビューのパフォーマンス記事」「Railsで重くなるのはたいていビューかデータベースですね🧐

「そういえばLaravelのテンプレートエンジンがすごく重くなるときがあるみたいな噂聞きましたけど、エンジンの名前何でしたっけ?」「Bladeかな」「そうそうBlade、MVC系のフレームワークはビューテンプレートあたりの書き方次第では重くなることもありますし、Railsのビューもちゃんと書けば速いけど気をつけないとですね☺」「ですね」

「ビューで重たいメソッドを気軽にeachで書いちゃうと行数に比例して遅くなりますし: データベースのN+1ではないビューのN+1的なヤツですが」「😆」「気をつけてないと割とこういうの書いちゃうんですよね: 管理画面ぐらいなら別に構わないかなとも思いますけど😆

参考: Bladeテンプレート 5.5 Laravel


同記事目次より:

  • データベースクエリの見直し
  • HTMLリロードを避ける
  • キャッシュを効かせる
    • ビューのキャッシュ
    • データベースクエリのキャッシュ
    • データベースのインデックス
  • まとめ

⚓ドロップダウンボックスをチェックボックスに変更するまでの作業(Ruby Weeklyより)

なぜかRuby Weeklyでのタイトル「Changing a UI Control and DB Schema on a Production Rails App」からだいぶ変わっています。


つっつきボイス:「ああ、ビューでこういう変更が入ったときにこんな作業が発生したぜという記事😆」「has_oneだったのをhas_manyにした的な」


同記事より

「こういう変更よくありますよね😆」「あるある😆」「誰もがやったことある変更😆」「カテゴリは最初1個表示だったのに複数に変えて欲しいとか😆」「最初に言ってくれれば…😢」「慣れてくると、いずれhas_manyになるだろうみたいな勘がビビッと働いてhas_oneで書いとくこともありますけど😆

⚓OpenAPIとcommittee gem


つっつきボイス:「BPS社内で話題になっていたので」「最近OpenAPIでAPI設計しようかみたいな話がちらほら出たりしてますね☺

「OpenAPIは以前Swaggerと言われてたものですけど、使ったことあります?」「使ってません😆」「前は使ってた?😆」「使おうかなと思って調べたところまでです😅」「まあ今やるならSwaggerよりOpenAPIの方でしょうね☺」「Swagger 3.0がOpenAPIということみたい🤔

参考: 本当に使ってよかったOpenAPI (Swagger) ツール | Future Tech Blog – フューチャーアーキテクト
参考: OpenAPI Specification | Swagger

⚓OpenAPIの使い所

「Swagger使うとドキュメント書きで幸せになれるらしいと聞いて調べました😆」「まあどこまでドキュメント化するかとかも含めて、そんな夢のツールというほどでもない🤣」「🤣」「ただAPI仕様書のフォーマットという意味ではなかなかいいと思いますね😋」「おぉ」「ExcelでAPI仕様書書かなくていいですし😆

「OpenAPIは、CRUDベースでJSON受け取ってJSON返すだけのシンプルなAPIを記述するにはいいんですけど、ものすごく複雑なJSON受け取って複雑なJSONを返すようなAPIだと…ね😇」「😆」「URLのエンドポイントは1個なのに、カラムの内容に応じてレスポンスの形式まで変わっちゃうようなAPIとか😆」「😆」「そういうのをOpenAPIで記述しようとすると超ツラい😭」「逆にRailsのようなRESTfulなAPIなら、OpenAPIでとてもキレイに書けます❤

「あとOpenAPIのありがたい点は、書式がそれほど厳しくないことですね😋」「へぇ〜」「少しぐらい雑に書いてもドキュメント生成してくれるので、厳密に書くのを諦めつつも使うことがたしかできたと思います」「ふむふむ」「OpenAPIで自動生成されるスタブのAPIサーバーがどのぐらいちゃんと動くのかは知りませんけど😆、書式が決まっていてExcelを使わないという点でOpenAPIは好きですね🥰

⚓loaf: Railsでパンくずリスト(Ruby Weeklyより)


同リポジトリより


つっつきボイス:「いわゆるパンくずリストの新しめのgemだそうです」「ローフ?」「パン一斤を指してa loaf of breadという言い方をするので、それをもじったっぽい」「なるほど、breadclumb(パンくず)だけに😆

参考: パンくずリスト - Wikipedia

# 同リポジトリより
class Blog::CategoriesController < ApplicationController

  breadcrumb 'Article Categories', :blog_categories_path, only: [:show]

  def show
    breadcrumb @category.title, blog_category_path(@category)
  end
end

「どことなく見たことのあるDSL↑😆: こういうパンくずリストgemって他にも相当昔からあったな〜、あgretelだ↓」「ヘンゼルとグレーテルの話そのまんま😆


同リポジトリより

「ありゃ〜gretelはメンテ終わってるし😇」「WilHall/gretelがactiveって書いてあるけどこっちも1年ほど更新されてないっぽい…」「それでloaf作ったのかも?🤔」「移行先の★が少ないということは最近もしかするとパンくずリストってあんまり使われてなかったりして😆」「まあパンくずリストなら自分で実装してもいいくらいですし😆

「ちなみにgretelはビューにコードを書くけど、loafはコントローラに書くところが違ってますね、と思ったらloafはビューにも書けるらしい😆

「パンくずリストってきれいに設計するのが割と難しいですよね😅」「SEO的な理由で欲しいと言われたりとか」「今どきのWebサイトってツリー状にきれいに階層化されてることってあまりありませんし、パンくずリスト付けると、入ってきた動線と違うパスが表示されたりして何となく気持ち悪いとか😆」「あまり気にしてませんでしたけど気持ち悪さはわかります😆」「パンくずリストって、気持ちとしては自分が辿ってきた動線を戻れるように表示されて欲しいのに、今どきはあらゆるところからあらゆるところへ戻ったりしますし😆」「😆」「なのできれいなパンくずリストにするのは割と諦めの境地かも😇

「ちなみにパンくずというと自分はMOTHER1思い出しますが😆

参考: MOTHER1小ネタ

⚓その他Rails

つっつきボイス:「Rails 6のマルチDBやってみたスライドを見つけたので」「本番で使うとは勇者😆」「自分が今RailsでマルチDBやるなら絶対switch_point使いますけど😆ウォッチ20180723)」「上の『先週の改修』にあったレプリカからプライマリに切り替わっちゃうあたりとかも含めてまだ改良の余地ありそうですし、個人アプリで試すならともかく本番に入れる気にはまだなりませんね😅


同リポジトリより


つっつきボイス:「jnchitoさんのテスト記事です」「こういう記事を書いたということは、アサーションのないテストとかを実際に目にしたんでしょうね…」

「『エラーを検出できないテストを書かない』、これやられるとテストの工数がまるまる無駄になってしまいますし😢

「『〜ではないことだけをテストしない』、正常系と異常系を両方テストしないとたいてい見落としが発生しますね😇」「どんなに自明だと思っても両方書かないと」

「境界値テストはどこまでやるか悩ましいけど必要ならやらないと」「年齢だったらわざとマイナスの数値入れるとか😆」「まそこまでやるかどうかですが😆

「『呼び出されないlet』もやめて欲しいヤツ: 使わないテストデータが残ってると消していいかどうかもわからなくて超ツラい😢

「『絞り込みの甘いテスト』もあるある😆」「テストコードを書いたときはうまく動いていても、機能やテストを追加すると絞り込みが甘くなったりすることがたまにありますね☺」「たとえばcountして数を確認するテストなんかは1種類だけ書いてもこういうのを検出できないことがあったりするので、2種類書いておけば片方だけ落ちて検出できたりとか」「ふむふむ」

「not系のテストは、気をつけないと必ずパスしちゃうテストにたまになったり😆」「『テストファーストは必須でない』もわかる!」「自分はTDDってそれほど好きじゃないんですけど、テストを失敗させれば少なくともそのテストが動いていることは確認できるので、これも同意ですね☺

「『DRYを追求しない』も大賛成」「DRYにされるとコードを足したり削ったりしにくい😭」「『ループ処理を使わず、愚直にユーザー名をベタ書きする』も賛成!」「テストコードは変にロジカルにしないで基本ベタに書きましょう🧐」「テストコードにリテラルがんがん書いてOK😋

RSpecえかきうた

「カバレッジといえば、simplecovの設定ミスっててカバレッジがちょっと漏れてたりすることあった😆けど最近は見ないかな〜」「最近のRubyだとコードから行単位で情報取れたりするので精度上がってそう😋」「テストコードがあってもそれ自体が検証されてなくて後でメンテする人がかえって泥沼になった事件あった🤣」「テストが検証されてなかったとか普通思いませんし🤣

「どのエントリも納得😋」「jnchitoさんは、こういう当たり前のことをみっちり書いてくれるのがとってもありがたい🙏


前編は以上です。

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

週刊Railsウォッチ(20200128後編)もう一つのgemマネージャgel、”Did you mean”の仕組みを追う、DXOpalでブラウザゲームほか

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

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

Rails公式ニュース

Ruby Weekly

Viewing all 1079 articles
Browse latest View live