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

週刊Railsウォッチ(20190909-1/2前編)Rails 6のキャッシュバージョニング、Rubyのキーワード引数周りが変わる、Faker 2がリリースほか

こんにちは、hachi8833です。消費税アップが迫ってきましたね。


つっつきボイス:「そうそう、消費税アップ来ますね😅」「皆さんの中で消費税対応されてる方は?」「経理部とやりとりしたりしてますね」「もう終わりました?」「まだまだです🤣」「食品みたいに軽減税率対応のものを扱ってると面倒そうですね」「どちらにしろ2%アップはやってくるので、商品マスターデータを更新するかどうかとか考えないといけないかも☺️」


同PDFより

「今回の場合、消費税を『還元する』とか『サービスする』的な触れ込みをしてはいけないという指示とかが上のPDFにもあるんですよね↑」「ちとわかりにくいですけど😆」「他にもいろいろ注意事項あるので、消費税を扱ってるかどうかにかかわらず読んどかないとですね☺️」

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

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

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

今回は6.0.1マイルストーン最近マージされたPR中心に見繕ってみました。

リグレッション修正: @prepared_statement_statusをスレッドローカル変数化してインスタンス固有にした

# activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L13
+ require "concurrent/atomic/thread_local_var"
...
      attr_accessor :pool
-     attr_reader :visitor, :owner, :logger, :lock, :prepared_statements
+     attr_reader :visitor, :owner, :logger, :lock
      alias :in_use? :owner
...
      def initialize(connection, logger = nil, config = {}) # :nodoc:
        super()
        @connection          = connection
        @owner               = nil
        @instrumenter        = ActiveSupport::Notifications.instrumenter
        @logger              = logger
        @config              = config
        @pool                = ActiveRecord::ConnectionAdapters::NullPool.new
        @idle_since          = Concurrent.monotonic_time
        @visitor = arel_visitor
        @statements = build_statement_pool
        @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new

        if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
-         @prepared_statements = true
+         @prepared_statement_status = Concurrent::ThreadLocalVar.new(true)
          @visitor.extend(DetermineIfPreparableVisitor)
        else
-         @prepared_statements = false
+         @prepared_statement_status = Concurrent::ThreadLocalVar.new(false)
        end

        @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
          config.fetch(:advisory_locks, true)
        )
      end
...
+     def prepared_statements
+       @prepared_statement_status.value
+     end
...
      def unprepared_statement
-       old_prepared_statements, @prepared_statements = @prepared_statements, false
-       yield
-     ensure
-       @prepared_statements = old_prepared_statements
+       @prepared_statement_status.bind(false) { yield }
      end

この問題をデモする簡単なアプリ: https://github.com/careport/racy

この問題は、システムテストで単一のDBコネクションが全サーバースレッドで共有されていることから始まっている。unprepared_statementがコネクションの@prepapred_statementsインスタンス変数を改変するので、これを用いるやりとりがよくないことになっている。その改変が、コネクションを共有する他のスレッドから透けて見えるため、一部のクエリのパラメータが誤ってパラメータ化される。
この問題が853f568で顕在化しない理由はわからない。引き続き調べてみるが、Active Recordの内部に詳しい人ならすぐわかるかもしれない。
同issueより大意


つっつきボイス:「スレッド関連かな?」「システムテストで1つのデータベースコネクションを全サーバースレッドで共有しているときに問題が生じたとissueにありました↑」「Concurrent::ThreadLocalVarなんてのがある」「これはRubyのメソッドかな?」「ruby-concurrencyっていうgemみたいです」

参考: ruby-concurrency/concurrent-ruby: Modern concurrency tools including agents, futures, promises, thread pools, supervisors, and more. Inspired by Erlang, Clojure, Scala, Go, Java, JavaScript, and classic concurrency patterns.
参考: Class: Concurrent::ThreadLocalVar — Concurrent Ruby


同リポジトリより

Concurrent::ThreadLocalVarのドキュメントにJavaのThreadLocalへのリンクがあるから↓、これとパラダイムが近いということかも」「一瞬このライブラリがJavaなのかと思った😆」

参考: ThreadLocal (Java Platform SE 7 )

「最近スレッド周りの修正が毎週のように出てきますね😳」「探せば探すほど出てきちゃうのかも」「修正も大変ですけど、スレッド絡みの現象を再現するのはもっと大変ですね😅: Active Recordのこの辺で問題が起きそうと見当をつけることはできても、こういう再現テストを書ける人ってマジでスゴいと思いますし🙇」「スレッドのバグって、100万回回すと数回起きるみたいなのもありますね😇」

rails newのプロジェクト名に丸かっこ()があるとビューテンプレートが見つからなくなる問題を修正

# actionview/lib/action_view/template/resolver.rb#L352
      def build_regex(path, details)
-       query = escape_entry(File.join(@path, path))
+       query = Regexp.escape(File.join(@path, path))
        exts = EXTENSIONS.map do |ext, prefix|
          match =
            if ext == :variants && details[ext] == :any
              ".*?"
            else
              arr = details[ext].compact
              arr.uniq!
              arr.map! { |e| Regexp.escape(e) }
              arr.join("|")
            end
          prefix = Regexp.escape(prefix)
          "(#{prefix}(?<#{ext}>#{match}))?"
        end.join
        %r{\A#{query}#{exts}\z}
      end

なお、issue #37107については以下のPRが先にあったのですが、入れ違いで上のPRが投げられていたため以下はcloseされていました。


つっつきボイス:「プロジェクト名に丸かっこ()を入れるのってちょっとどうかなと思ったので😆」「なんと🤣」「issueの方がわかりやすいかも」「これか↓」「()に限らずメタ文字が入るとビューを参照できなくなってたそうです」

# 同issueより
rails new "path-escape-bug (testing)"
cd "path-escape-bug (testing)"
rails g controller public example
bin/rails s
# open "http://localhost:3000/public/example" in your browser

「これが正常に動くと思う人っていなさそうだけど、それでも書きたい人がいるのかも😆」「なお#37119はまだマージされてませんね」「Railsプロジェクト名にダブルクォートとか入れたい人っているんだろうか🤣」「仕様上メタ文字はプロジェクト名で使えないことにする方が早い気もしますし😆」

  def test_can_find_when_special_chars_in_path
    special_chars = ((" ".."~").grep(/\W/) - ["|", "/", "\\"]).join("_")
    dir = "test#{special_chars}"
    with_file "#{dir}/hello_world", "Hello funky path!"

    templates = resolver.find_all("hello_world", dir, false, locale: [:en], formats: [:html], variants: [:phone], handlers: [:erb])
    assert_equal 1, templates.size
    assert_equal "Hello funky path!", templates[0].source
    assert_equal "#{dir}/hello_world", templates[0].virtual_path
  end

「ところで、テストのこの(" ".."~")という書き方↑でメタ文字をカバーできるのかな?🤔」「ASCIIコード表見ながらIRBを動かしてみると、スペース文字から~までを取って、/\W/で英数字を除去して、["|", "/", "\\"]を差し引いて_でjoinすると...たしかにできた!」「おぉ〜」「こうやってRangeで取るのは初めて見たな〜: 脳内にASCIIコードがないと思いつかないし😆」「numbered parameterで書きたかったけどそういえばRuby 2.6にはなかった😆」「このファイル名、Windowsで通るんだろうか😆」「そのうち絵文字もありになったり?😆」

special_chars = ((" ".."~").grep(/\W/) - ["|", "/", "\\"]).join("_")
#» " _!_\"_#_$_%_&_'_(_)_*_+_,_-_._:_;_<_=_>_?_@_[_]_^_`_{_}_~"

参考: ASCIIコード表

Active Jobキュー名の文字列をfreezeさせた

# 同PRより
Retained String Report
-----------------------------------
  ...
217  "default"
200  /tmp/bundle/ruby/2.6.0/bundler/gems/rails-59e746d4d07b/activejob/lib/active_job/qu
# activejob/lib/active_job/queue_name.rb#L49
      def queue_name_from_part(part_name) #:nodoc:
        queue_name = part_name || default_queue_name
        name_parts = [queue_name_prefix.presence, queue_name]
-       name_parts.compact.join(queue_name_delimiter)
+       -name_parts.compact.join(queue_name_delimiter)
      end

つっつきボイス:「キュー名の"default"という文字列が重複してたそうで、大したことはないけど修正はすごく簡単だったので修正したとPRにありました」「memory profilerで見つけたんでしょうね」「シンボルで返すようにしたのかな?と思ったら、-でfreezeさせたのね」「+がdupを返すんでしたっけ」「この辺の記号、どっちがどっちだったか不安になりがち😅」

参考: instance method String#-@ (Ruby 2.6.0)

「たしかに-だとobject_idは一致する↓」

h1 = -"hoge"
#» "hoge"
h2 = -"hoge"
#» "hoge"
h1.object_id
#» 70279179364960
h2.object_id
#» 70279179364960

h3 = "hoge"
#» "hoge"
h4 = "hoge"
#» "hoge"
h3.object_id
#» 70136481465760
h4.object_id
#» 70136481387620

Style/BracesAroundHashParameters copを無効にした

-# .rubocop.yml#L37
-# Do not use braces for hash literals when they are the last argument of a
-# method call.
-Style/BracesAroundHashParameters:
-  Enabled: true
-  EnforcedStyle: context_dependent

つい最近のRubyのキーワード引数の扱い変更↓に伴う変更のようです。

参考: RuboCop | Style/BracesAroundHashParameters EnforcedStyle - Qiita


つっつきボイス:「Rubyのキーワード引数の扱いが変わることになったのを反映したとのことです」「#14183でずっと議論してたヤツね: キーワード引数とハッシュが混じるとえらいことになる、breaking change不可避の問題↓」「issue長いな〜」「closeはしたけどまだ少々動いてるっぽいですね」「ここはみんな困っている問題だから、何らかの形で修正しないといけないヤツですね」

参考: キーワード引数の現状と将来構想 - HackMD

Ruby2.5.xのパラメータの制約についてまとめてみた

# actionpack/test/dispatch/cookies_test.rb#L900
    key_generator = @request.env["action_dispatch.key_generator"]
    old_secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"])
-   old_value = ActiveSupport::MessageVerifier.new(old_secret).generate({bar: "baz"})
+   old_value = ActiveSupport::MessageVerifier.new(old_secret).generate({ bar: "baz" })

「このスペース追加↑って何なんでしょう?」「スペースはあっても解釈は変わらなかったと思うけど🤔」「Style/BracesAroundHashParametersのcopがコンフリクトしてたので外してオートコレクトしたらスペースが入ったということみたい」「d94263f...5665fb5にはこういう修正が入ってる↓」「あ、こっちの{}追加の方がメインだったのか😅」

# activesupport/test/message_encryptor_test.rb#L127
  def test_rotating_serializer
    old_message = ActiveSupport::MessageEncryptor.new(secrets[:old], cipher: "aes-256-gcm", serializer: JSON).
-     encrypt_and_sign(ahoy: :hoy)
+     encrypt_and_sign({ahoy: :hoy})

    encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm", serializer: JSON)
    encryptor.rotate secrets[:old]
    assert

参考: RuboCop | Style/BracesAroundHashParameters EnforcedStyle - Qiita

「Rubyの#2395のコメント↓を見ると『昨日developer meetingで受理された』とあって、それが6日前(つっつき当日から見て)ということだから、つい1週間前だったんですね」


#2395コメントより

「話は変わりますけど、RubyとRailsはコミッターが割と近い関係にあるから、言語とフレームワークがこうやって密に連携しているのがありがたいですよね😋」「たしかに!」「PHPとかだとかなり分かれてる感ありますし☺️」

番外: SolarisではProcess.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)を使ってはいけない


つっつきボイス:「Solaris環境ではProcess#clock_gettime(CLOCK_PROCESS_CPUTIME_ID)は使うなとchangelogに追加されてました」「ソラリス!😆」「互換性がなかったんでしょうね」「Ruby on Solarisやってる人いるんだ😆」

「SolarisっていうOS、そもそも皆さんご存知です?」「いやぁ😅」「SolarisといえばあのSunの特徴的な薄いブルーというか紫色の筐体で、秋葉のジャンク屋に安く積んであったりするとつい買っちゃったりしますよね😆」「見たことならありますけど触るまでは😅」

参考: Solaris - Wikipedia

「Solarisっていう名前の由来って、もしかすると旧ソ連が国力を挙げて制作した映画『惑星ソラリス』と関係あるんだろうか?ってうっすら気になってました」「由来はわかんない😆」「この映画を見た人はあまりの尺の長さに必ず寝ちゃう催眠フィルムだと一部で評判でした😆」「😆」

後でSolarisという名前の由来をググると以下が出てきましたが、今のWikipediaには英語版日本語版ともにこの記述はないので、たぶん違うんでしょうね。

参考: Solaris系OSはなぜ ソラリス とゆうのですか?教えてください。 - Solaris(ソラ... - Yahoo!知恵袋

参考: 惑星ソラリス - Wikipedia

Rails

Rails 6のcollection_cache_versioning

以前も簡単に取り上げました(ウォッチ20190507)。

コンフィグでActiveRecord::Base.collection_cache_versioning = trueを指定しないと有効になりません。

参考: 3.7 Active Recordを設定する -- Rails アプリケーションを設定する - Rails ガイド


つっつきボイス:「これってどういう機能だったっけ」「上のガイドに一応あります」「ここは英語で見ておきたいな↓」

参考: Configuring Rails Applications — Ruby on Rails Guides

config.active_record.collection_cache_versioning enables the same cache key to be reused when the object being cached of type ActiveRecord::Relation changes by moving the volatile information (max updated at and count) of the relation's cache key into the cache version to support recycling cache key. Defaults to false.
Ruby on Rails Guidesより

「元記事のタイトルにもあるけど、キャッシュキーがrecyclableなのがポイントらしい: そうそう、キャッシュバージョンとキャッシュキーを分けられるようになったということですね↓」「おぉ?」

# 同記事より
>> ActiveRecord::Base.cache_versioning = true

>> post = Post.last

>> cache_key = post.cache_key
=> "posts/1"

>> before_update_cache_version = post.cache_version
=> "20190527080152975653"

>> Rails.cache.fetch(cache_key, version: before_update_cache_version) { post }
=> #<Post id: 1, title: "First Post", created_at: "2019-05-22 17:23:22", updated_at: "2019-05-27 08:01:52">

「以前はキーとバージョンが一体化していたので、キャッシュバージョンを更新するとキャッシュ自体が別キャッシュとして作られてたんですが、バージョニングできるようになったことでキーとバージョンを別々に更新できるようになったということだと思います」「記事の最後にこう書いてますね😋↓: cache_versionはタイムスタンプとかを含んでいてvolatileだから、キャッシュが同じでもこの部分は変わる可能性があり、cache_keyは一意に生成される」「Rails 6から挙動がこう変わるから、コンフィグでActiveRecord::Base.collection_cache_versioning = trueを指定しないと有効にならないようにしたんでしょうね」

Rails 6より前
ActiveRecord::Relationのcache_keyフォーマットは{table_name}/query-{query-hash}-{count}-{max(updated_at)}だった
Rails 6の場合
以下に分割される
・安定的なcache_key:{table_name}/query-{query-hash}
・変わりうるcache_version: {count}-{max(updated_at)}

「Redisとかにキャッシュを乗せている場合、ここが変わるとデプロイしたときに全部のキャッシュが死んでしまうでしょうね😇」「あ〜」「Railsって、rails app:updateみたいなコマンドで、互換性を保つ設定をconfig/initializers/new_framework_defaults.rbに生成するみたいな機能がありましたよね」「ありますね: で後から対応できたときに順に消していくという」「このキャッシュ機能のコンフィグもそういうときに使うんでしょうね☺️」「Rails 6でrails newする分にはいいけど、アップグレードでは注意が必要そう」

参考: 1.4 アップデートタスク -- Rails アップグレードガイド - Rails ガイド

GoRails.comのスクリーンキャスト


gorails.com/seriesより

RailsCastsの更新が止まって無料化されて久しいですが、GoRailsを最近見てなかったので覗いてみました。今もマメに更新されているようです。

このスクリーンキャストとかがちょっと気になりました↓。


つっつきボイス:「GoRails?」「RailsCastsに代わるスクリーンキャストというと今のところここぐらいなのかなと思って見に行くと、サイトが前より心持ちシンプルになってて、かなりマメに更新してました」

「こういうオンライン講座的なものって皆さんは見たりします?自分は動画になってるとコピペとかできないんで見ない方ですけど😆」「ITとは少し違いますけどCoursera↓とかは見たりしますね」「こーせら?」「日本語化もされてるんですね😋」


ja.cousera.orgより

「GoRails割と頑張ってるみたいで、ここのリポジトリ↓を見る方が新着情報もわかりやすいかなと」「どちらかというと、動画で今見えてるものをさくっとコピペしたいんですよね〜」「作り込み難しそうですね😅」「まあ今ならスクショ取ってGoogleドライブに置いてOCRする手もあるかもしれませんけど😆」

「初心者の方はこういう動画の講座を好む印象がちょっとあるかな〜」「あと英語圏もスクリーンキャスト好きですよね」「ステップバイステップの操作って記事で書こうとすると量が増えてすんごく大変なんですよ😭: 動画はその点では製作しやすいから、動画でやりたい気持ちはわかるかも☺️」

Rails 6のDNS Rebinding対策で開発中にlvh.meが効かなくなる(Awesome Rubyより)

参考: 【Rails】ローカル環境の開発でサブドメインがある場合「localhost」ではなく「lvh.me」を使う - FujiYasuの日記
参考: DNS Rebinding ~今日の用語特別版~ | 徳丸浩の日記


つっつきボイス:「おぉ、この記事にある問題、今日まさに社内のメンバーが踏んでましたよ😆: Rails 6にはDNSリバインディング対策が入ったので、デフォルトのlocalhost以外のドメインはconfig.hostsに明示的に追加しないとdevelopment環境でアクセスできなくなるという↓」「おぉ〜」

# 同記事より
# config/enviroments/development.rb
config.hosts << '.lvh.me'

Trestle: Bootstrap 4ベースのレスポンシブ管理画面(Awesome Rubyより)


trestle.ioより


つっつきボイス:「Railsの管理画面は山ほどありますけど、これはどうかなと思って」「みんなこういうのいっぱい作りますよね☺️」「出たなDSL↓」

Trestle.resource(:posts) do
  # Add a link to this admin in the main navigation
  menu do
    group :blog_management, priority: :first do
      item :posts, icon: "fa fa-file-text-o"
    end
  end

  # Define custom scopes for the index view
  scopes do
    scope :all, default: true
    scope :published
    scope :drafts, -> { Post.unpublished }
  end

  # Define the index view table listing
  table do
    column :title, link: true
    column :author, ->(post) { post.author.name }
    column :published, align: :center do |post|
      status_tag(icon("fa fa-check"), :success) if post.published?
    end
    column :updated_at, header: "Last Updated", align: :center
    actions
  end

  # Define the form structure for the new & edit actions
  form do
    # Organize fields into tabs and sidebars
    tab :post do
      text_field :title

      # Define custom form fields for easy re-use
      editor :body
    end

    tab :metadata do
      # Layout fields based on a 12-column grid
      row do
        col(sm: 6) { select :author, User.all }
        col(sm: 6) { tag_select :tags }
      end
    end

    sidebar do
      # Render a custom partial: app/views/admin/posts/_sidebar.html.erb
      render "sidebar"
    end
  end
end

「こういうDSLって自分で作ってるときが一番楽しいんですよね🤣」「達成感は半端ない🤣」「見た目はよさそうだけど、凝ったカスタマイズをしようとすると辛くなりそうな予感が😆」「業務のプロジェクトでこういうのが流れてきたら頭抱えそう😅: でも自分しか使わないアプリならむしろこれでいいかなって思いますし😋」「今度オレオレRailsアプリで使ってみようかな😍」「自分だけが使うなら許せる😆」

「こういう管理画面とかをプログラマーがボタンぽちぽちして自動生成できたらうれしい気持ちはあるんですけど、それならRailsでやらなくてもよくね?って思ったりしますし、コードを書いて作るのはコードでないとできないことがあるからですし」「たしかに〜」

「グラフのカスタマイズとかもついやりたくなりますけど、ああいうのもGoogleデータポータル↓とかでやる方がよかったりしそうですよね😆」「データポータルは使ったことないな〜: Googleアナリティクスをこねこねする的な印象ありますけど、こういうのってなかなか思ったように作れなかったりしそうですし😅」「内部のMySQLだかPostgreSQLだかを把握するとうまく使えるようになるかなって思ったり☺️」「半端にGUIになってるとやりづらかったりしますね☺️」「競合はTableauあたりかな🤔」

Rails Attributes APIで巨大JSONをPostgreSQLに保存する(Ruby Weeklyより)


つっつきボイス:「JSONをRails側でGzip圧縮解凍、ってわざわざRails側でやる必要あるんだろうか?😆」「たぶんぽすぐれのそういう圧縮機能とかデータストアを探して使う方がよくね?😆」「実用では使わないと思いますけど、練習にはいいと思いますね☺️」

# 同記事より
  def value_to_hash(value)
    JSON.parse(
      ActiveSupport::Gzip.decompress(value),
      symbolize_names: true
    ) || {}
  end

  def value_to_binary(value)
    ActiveSupport::Gzip.compress(value)
  end

参考: PostgreSQL: Documentation: 9.0: Binary Data Types

その他Rails



つっつきボイス:「Twitterで見かけたRubyMineの質問に@yusukeさんがすごい勢いで回答してました」「JetBrainsの日本の代理店をやってる方ですね☺️」「いつも見ててくれてるらしい😍」


なお、その後のRails Tutorial(英語版の方)では、進行中のRails 6対応版のうち第3章までのみが公開されています↓。

Ruby

k0kubunさんの「Optimizing Ruby with JIT」

Buildersconでのスライドです。


つっつきボイス:「こういうRubyのコアをきっちり解説してくれるのはうれしい😂」「しかも日本語で🇯🇵」「NESでの測定では2.5倍速くなったそうです」

Faker 2がリリース(Ruby Weeklyより)


同リポジトリより

Changelogの日付が米国式風(年-日-月)になっていて、一瞬間違ってるのかと思いました😅。現在は2.2.2まで進んでいます。2.0でオプション引数がごっそりキーワード引数に置き換わっています。

# 同リポジトリより
Faker::Books::Dune.quote(character = nil)
↓
Faker::Books::Dune.quote(character: nil)

つっつきボイス:「Fakerはどこが変わったのかしら?」「2.0でオプション引数をキーワード引数に置き換えたのがbreaking changesだそうです」

Fakerだけでもロケール次第で日本語のフェイクデータを作れるんですね↓。デフォルトの日本語データが意外にささやかなサイズでした。

「Fakerは日本語拡張が使いたい部分ですね😋: こういうのとか↓」「あ、デフォルト以外にこんな大きい日本語Fakerがあるんですね😅」「名前データでかい!」「日本語住所は対応してないようですが」「バージョンアップで影響受けないといいけど(受けます: 後述)」

# ruby-faker-japaneseより
    姓:高垣        名:芳人
    姓:尾関        名:きね子
    姓:夜久野      名:利常
    姓:飯星        名:邦則
    姓:草柳        名:大造
    姓:納米        名:ナツコ
    姓:金高        名:喜久蔵
    姓:キーティング        名:秋江
    姓:島居        名:悦二郎
    姓:戸河内      名:さかえ
    姓:片里        名:朝長
    姓:上依知      名:洌
    姓:湧井        名:宗矩
    姓:隆速        名:美禅

「ついでに@willnetさんのgimeiというgem↓を今頃知りました」「gimeiというのもありますね〜: 日本語の名前専門かと思ったら、いつの間にか日本語のダミー住所も生成できるのか」

# gimeiより
address = Gimei.address
address.kanji                 # => 岡山県大島郡大和村稲木町
address.to_s                  # => 岡山県大島郡大和村稲木町
address.hiragana              # => おかやまけんおおしまぐんやまとそんいなぎちょう
address.katakana              # => オカヤマケンオオシマグンヤマトソンイナギチョウ
address.romaji                # => Okayamaken Ooshimagunyamatoson Inagicho

追記: つっつきの後にkoicさんが以下の記事を出していました。Faker 2に移行するときのbreaking changesに対応するRuboCop Fakerを作ってくださったそうです🙇。

参考: Faker 2.2.2 がリリースされた - koicの日記
参考: RuboCop Faker を作った - koicの日記

Rumale: Rubyで機械学習


同リポジトリより

# 同リポジトリより
require 'rumale'

# Load the training dataset.
samples, labels = Rumale::Dataset.load_libsvm_file('pendigits')

# Map training data to RBF kernel feature space.
transformer = Rumale::KernelApproximation::RBF.new(gamma: 0.0001, n_components: 1024, random_seed: 1)
transformed = transformer.fit_transform(samples)

# Train linear SVM classifier.
classifier = Rumale::LinearModel::SVC.new(reg_param: 0.0001, max_iter: 1000, batch_size: 50, random_seed: 1)
classifier.fit(transformed, labels)

# Save the model.
File.open('transformer.dat', 'wb') { |f| f.write(Marshal.dump(transformer)) }
File.open('classifier.dat', 'wb') { |f| f.write(Marshal.dump(classifier)) }

つっつきボイス:「ruby-jp Slackでちらっと見かけたRubyの機械学習gemです」「よく作ったな〜これ😳」「モチベーションが知りたい」「Pythonに依存しないんでしょうか?」「READMEにPythonのScikit-Learnに近いインターフェースになってるってありますし、Pythonを使ってるとは書いてないからおそらくそうなんでしょうね🤔」「Rubyでここまでやるのスゴい!」

Red Data Toolsプロジェクトとは別のようですね。

custom_cops_generator: RuboCopのカスタムcopを生成

# 同リポジトリより
$ custom_cops_generator rubocop-foobar
Creating gem 'rubocop-foobar'...
      create  rubocop-foobar/Gemfile
      create  rubocop-foobar/lib/rubocop/foobar.rb
      create  rubocop-foobar/lib/rubocop/foobar/version.rb
      create  rubocop-foobar/rubocop-foobar.gemspec
      create  rubocop-foobar/Rakefile
      create  rubocop-foobar/README.md
      create  rubocop-foobar/bin/console
      create  rubocop-foobar/bin/setup
      create  rubocop-foobar/.gitignore
Initializing git repo in /tmp/tmp.Gu7G94wX00/rubocop-foobar
Gem 'rubocop-foobar' was successfully created. For more information on making a RubyGem visit https://bundler.io/guides/creating_gem.html
create rubocop-foobar/lib/rubocop-foobar.rb
create rubocop-foobar/lib/rubocop/foobar/inject.rb
create rubocop-foobar/lib/rubocop/cop/foobar_cops.rb
create rubocop-foobar/config/default.yml
create rubocop-foobar/spec/spec_helper.rb
create rubocop-foobar/.rspec
update lib/rubocop/foobar.rb
update lib/rubocop/foobar.rb
update lib/rubocop/foobar/version.rb
update rubocop-foobar.gemspec
update rubocop-foobar.gemspec
update Rakefile
update Gemfile

It's done! You can start developing a new extension of RuboCop in rubocop-foobar.
For the next step, you can use the cop generator.

  $ bundle exec rake 'new_cop[Foobar/SuperCoolCopName]'

つっつきボイス:「これもruby-jpでちらっと見かけましたけど、カスタムcopを作りたいことって結構あるんでしょうか?」「プロジェクト固有のコーディングルールを設定したいときとかでしょうね: 事業会社とかで1つのRailsプロジェクトに何十人も職業的メンバーがいるみたいな環境ならあるかも☺️」「たしかShopifyがcop的なものを作って頑張っているみたいな話がありましたね↓: それこそ100人以上のRailsエンジニアがコードを書いている環境」

参考: RubyKaigi、shopifyのテストの話が良かった · hoshinotsuyoshi.com - 自由なブログだよ

その他Ruby


前編は以上です。

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

週刊Railsウォッチ(20190902)Ruby 2.6.4セキュリティ修正リリース、スライド「All About Ruby in 2019」、Shrine gem 3.0に入る新機能ほか

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

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

Rails公式ニュース

Ruby Weekly

Awesome Ruby


CONTACT

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