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

週刊Railsウォッチ: Ruby 3.4から暗黙の"it"ブロック変数が導入されるほか(20231222)

こんにちは、hachi8833です。今年最後の週刊Railsウォッチをお送りします。

週刊Railsウォッチについて

  • 各記事冒頭には🔗でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • お気づきの点がありましたら@hachi8833までメンションをいただければ確認・対応いたします🙏

TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)

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

🔗 Active Storageモデルのテーブル名にActive Recordのプレフィックス/サフィックス設定が効くよう修正

モデルに関連するすべてのActive StorageデータベースがActiveRecord::Base.table_name_prefixコンフィグを尊重するよう修正。

Chedli Bourguiba
同Changelogより

編集: issue #35811の部分修正(Active Recordのサフィックスについてのみ機能する: プレフィックスについてはエンジンのグローバルな問題なので別PRで扱う予定)。
このプルリクは、closeされた#35828のフォローアップ。
このプルリクは、#30095のフォローアップ。

動機/背景

このプルリクを作成した理由は、Active StorageのDBモデルのテーブル名がハードコードされていることが原因でActive Recordのプレフィックスやサフィックスの設定と互換性を持たせられないため。

もうひとつ問題になるのは、Active Storageで生成したマイグレーション(activestorage:installタスク)が上述のプレフィックス/サフィックス設定を尊重しているため、RailsアプリケーションでActive Storageのモデルを利用できなくなること。

詳細

この問題を修正するため、ハードコードされている行を削除する。ただしプレフィックスを指定する内部メソッドもオーバーライドしておく必要がある。完全な説明については#50167のコメントを参照。

追加情報

ハードコードされたテーブル名の参照を削除するため、一部のテストをリファクタリングした。これによってActive StorageのテストスイートがActive Recordのコンフィグと互換性を持つようになった。

rails/activerecord/lib/active_record/model_schema.rb

# 354d68eの597-611行目
 def compute_table_name 
   if base_class? 
     # Nested classes are prefixed with singular parent table name. 
     if module_parent < Base && !module_parent.abstract_class? 
       contained = module_parent.table_name 
       contained = contained.singularize if module_parent.pluralize_table_names 
       contained += "_" 
     end 

     "#{full_table_name_prefix}#{contained}#{undecorated_table_name(model_name)}#{full_table_name_suffix}" 
   else 
     # STI subclasses always use their superclass's table. 
     base_class.table_name 
   end 
 end 
  • 根本的な原因はActive Recordモデルのスキーマファイル内にあると信じているが、自分には解決方法を見つけられなかった...今のところは(編集: 解決策は#50167(コメント)にある)。

  • Action Textにも同じ問題があるが、今はActive Storageに集中して正しく動くようになってからそっちに移植したい
    同PRより


つっつきボイス:「プレフィックス(prefix)とサフィックス(suffix)を合わせてaffixって呼ぶのか」「これまでtable_name_prefixtable_name_suffixコンフィグがActive Storageモデルのテーブル名だと効かなかったのはテーブル名が決め打ちされていたからなんですね↓」「修正はそれを削除しただけ、なるほど↓」「Action TextやAction Mailboxにも同じ問題があるということは近々修正されそうですね」「データベースと密に結合している機能でテーブル名が決め打ちされてたというのはありそうな話」

# activestorage/app/models/active_storage/attachment.rb
class ActiveStorage::Attachment < ActiveStorage::Record
- self.table_name = "active_storage_attachments"
-
  ##
  # :method:
  #
...

参考: §3.8.3 config.active_record.table_name_prefix -- Rails アプリケーションの設定項目 - Railsガイド
参考: §3.8.4 config.active_record.table_name_suffix -- Rails アプリケーションの設定項目 - Railsガイド

その後Action TextとAction Mailboxにも同じ修正が入りました↓。

参考: Take AR affixes into account for Action Mailbox database models by chaadow · Pull Request #50300 · rails/rails
参考: Take AR affixes into account for Action Text database models by chaadow · Pull Request #50299 · rails/rails

🔗 Rails::Engineisolate_namespaceメソッドを修正

分離したエンジンがActiveRecord::Base.table_name_prefixを考慮するよう修正した。
これにより、エンジンが定義するモデル(Active Storageなど)がActive Recordのテーブル名プレフィックスを尊重するようになる。

Chedli Bourguiba
同Changelogより

修正: #50246
cc: @jonathanhefner

動機/背景

このプルリクは、分離したエンジンにおける長年の問題を解決するために作成された。
実は、分離したエンジンは独自のtable_name_prefixメソッドを定義しており、そこではActive Recordのテーブル名プレフィックスが考慮されていない。

詳細

このプルリクは、Active Recordが読み込まれた場合にのみエンジンのtable_name_prefixを再定義するようにする。これにより、内部利用のためにActive Recordテーブルを生成する分離されたエンジンで、ActiveRecord::Base.table_name_prefixをテーブル名にプレフィックスできるようになる。

追加情報

これにより、テーブルを生成するエンジン(active_storageaction_textaction_mailboxなど)が修正される(#50167)。

  • Active Storageはテーブル名がハードコードされているので、このプルリクを最初にマージする。
  • Action TextとAction Mailboxについては別のプルリクが必要。
    同PRより

つっつきボイス:「これも今の#50167と似ていそう」「この#50247を先に修正したんですね: Railsエンジンではisolate_namespaceで名前空間を指定できるけど、エンジンが使うテーブル名の方にはActive Recordのプレフィックス設定が効いていなかったのでisolate_namespaceを修正した、なるほど」「エンジン側のtable_name_prefixでプレフィックスが上書きされていたんですね」「このあたりの機能は使ったことなかったな〜」

# #railties/lib/rails/engine.rbL385
      def isolate_namespace(mod)
        engine_name(generate_railtie_name(mod.name))
        routes.default_scope = { module: ActiveSupport::Inflector.underscore(mod.name) }
        self.isolated = true
        unless mod.respond_to?(:railtie_namespace)
          name, railtie = engine_name, self
          mod.singleton_class.instance_eval do
            define_method(:railtie_namespace) { railtie }

            unless mod.respond_to?(:table_name_prefix)
              define_method(:table_name_prefix) { "#{name}_" }
+
+             ActiveSupport.on_load(:active_record) do
+               mod.singleton_class.redefine_method(:table_name_prefix) do
+                 "#{ActiveRecord::Base.table_name_prefix}#{name}_"
+               end
+             end
            end
...

参考: § 2.1 エンジンの内部 -- Rails エンジン入門 - Railsガイド

🔗 field_set_tagヘルパーメソッドのエイリアスfieldset_tagを追加

field_set_tagヘルパーのエイリアスfieldset_tagを追加して<fieldset>要素の命名に合わせた。

Sean Doyle
同Changelogより

動機/背景

field_set_tag<fieldset>要素をレンダリングする。fieldsetをレンダリングしたくてfieldset_tagを呼び出したのにNoMethodErrorが表示されて驚くことがある。

このヘルパーメソッド名は元々fieldset_tagという名前だった(2007年に0e6c8e5で定義された)が、3日後に73c7083でリネームされた。

詳細

このコミットは、field_set_tagfieldset_tagというエイリアスを追加して両方使えるようにする。

追加情報

また、content_tagを利用する形でこのメソッドを定義することで、<fieldset>の開始タグを手動で閉じる責務を負わないようにした。
同PRより


つっつきボイス:「<fieldset>要素の命名に合わせるならたしかにfieldset_tagの方がよさそう👍」「当時の73c7083を見ると3日後にfield_set_tagにリネームしたのはDHHだったんですね」

参考: <fieldset>: フィールドセット要素 - HTML: ハイパーテキストマークアップ言語 | MDN

🔗 #travel_toで設定したタイムゾーンがクリアされていなかったのを修正

#travel_to実行後にTime.now/DateTime.now/Date.todayが結果をシステムのタイムゾーンで返すよう修正。

#travel_toの現在の実装にはバグがある: このメソッドが引数のタイムゾーンを記憶して、スタブ化されたあらゆるメソッドが以後そのタイムゾーンで結果を返すようになる。ただし、本来期待される振る舞いは結果をシステムのタイムゾーンで返すこと。

Aleksei Chernenkov
同Changelogより

動機/背景

Faker gemのFaker::Time.backwardメソッドのバグ(#2861)が原因でDate.todayの結果が誤っていたのを修正するため。
同PRより


つっつきボイス:「#travel_to実行後にTime.nowとかが#travel_toで設定されたタイムゾーンで値を返していたのか」「これは明らかにバグ」「faker gemのissue #2861でこのバグが見つかったんですね」

# activesupport/lib/active_support/testing/time_helpers.rb#L172
+       # +now+ must be in local system timezone, because +Time.at(now)+
+       # and +now.to_date+ (see stubs below) will use +now+'s timezone too!
+       now = now.getlocal

faker-ruby/faker - GitHub

Rails: Timecopを使わなくても時間を止められた話

🔗 ActiveJob::Serializers::TimeWithZoneSerializerでタイムゾーンが消えていたのを修正

ActiveSupport::TimeWithZoneの引数をデシリアライズするときに、シリアライズされているタイムゾーンを維持するよう修正。

Joshua Young

同Changelogより

動機/背景

#50230を修正するため。

詳細

このプルリクはActiveJob::Serializers::TimeWithZoneSerializerを変更して、指定されたtime_with_zoneオブジェクトをシリアライズドハッシュ内に含めるようにする。このハッシュはデシリアライズ中に使われる。

追加情報

この変更には副作用が1つある。ActiveJob::Serializers::TimeWithZoneSerializerは今後(ActiveJob::Serializers::TimeObjectSerializerではなく)ActiveJob::Serializers::ObjectSerializerを直接継承するようになる。これは、独自の#serializeを定義するため。現在の継承を変えずに実装する別の方法は自分にはわからない。
同PRより


つっつきボイス:「シリアライズしてデシリアライズしたときにタイムゾーンが消えてた: これもバグですね」「タイムゾーン情報が消えるのは困る」

# activejob/lib/active_job/serializers/time_with_zone_serializer.rb#L3
module ActiveJob
  module Serializers
-   class TimeWithZoneSerializer < TimeObjectSerializer # :nodoc:
+   class TimeWithZoneSerializer < ObjectSerializer # :nodoc:
+     NANO_PRECISION = 9
+
+     def serialize(time_with_zone)
+       super(
+         "value" => time_with_zone.iso8601(NANO_PRECISION),
+         "time_zone" => time_with_zone.time_zone.tzinfo.name
+       )
+     end
+
      def deserialize(hash)
-       Time.iso8601(hash["value"]).in_time_zone
+       Time.iso8601(hash["value"]).in_time_zone(hash["time_zone"] || Time.zone)
      end

      private
        def klass
          ActiveSupport::TimeWithZone
        end
    end
  end
end

🔗 Active Recordでobject_idというカラム名を利用可能にした

修正: #50160

動機/背景

object_idという名前は、ポリモーフィック関連付けでobjectという名前が最適な場合に使われる可能性がある。この場合、object_idobject_typeという2つのカラムが作成される。

このプルリクを作成した理由は、自分たちのRailsアプリでポリモーフィック関連付け名にobjectという名前を利用しており、この名前が最適と思われるため。Rails 7.1まではこの名前が容認されており、何の問題もなかった。

object_idをカラム名として利用することについては、#38990で明示的に許されていた。

詳細

このプルリクは、不許可カラム名のリストからobject_idを削除する。
一貫性を維持するため、コメントやテストの「__id__を代わりに使う」記述も更新した。

追加情報

object_idが危険なメソッド名リストに追加されたのは#45883だった。

#38990では、object_idをカラム名として利用することが明示的に許されていた。
同PRより


つっつきボイス:「object_idというカラム名を使うのがありだったとは知らなかった」「dangerous_attribute_methodsウォッチ20230926)にいつの間にか含まれていたのを削除したんですね」「object_idという名前を使いたい場合があるのはわかるけど、使って大丈夫だとしても自分はフレームワークにありそうな名前はあまり使いたくない気持ち」「ぶつかりそうで怖いですよね」「object_idObjectにあるのでオーバーライドを避けたくて禁止リストに追加してたんでしょうね: その気持もわかる」

# activerecord/lib/active_record/attribute_methods.rb#L29
    class << self
      def dangerous_attribute_methods # :nodoc:
        @dangerous_attribute_methods ||= (
          Base.instance_methods +
          Base.private_instance_methods -
          Base.superclass.instance_methods -
          Base.superclass.private_instance_methods +
-         %i[__id__ dup freeze frozen? hash object_id class clone]
+         %i[__id__ dup freeze frozen? hash class clone]
        ).map { |m| -m.to_s }.to_set.freeze
      end
    end

🔗 MySQLでDATABASE_URLを変えずにconfig.active_record.protocol_adapters.mysqlでアダプタを指定できるようになった

DATABASE_URL環境変数を使う場合、URLに含まれるプロトコル名を特定のデータベースアダプタにマッピングする設定を有効にした。これにより、アプリケーションで利用するために選択するアダプタを、デプロイ環境で設定されるデータベースコネクションの詳細から分離できるようになる。

# ENV['DATABASE_URL'] = "mysql://localhost/example_database"
config.active_record.protocol_adapters.mysql = "trilogy"
# MySQLにtrilogyアダプタで接続する

Jean BoussierKevin McPhillips
同Changelogより

このプルリクでの方法は、自分が#50112で試みた方法とまったく異なっている。
cc: @byroot@matthewd

問題点

データベースコネクションをDATABASE_URLで定義する場合、URLの一部として利用されるアダプタをデプロイの環境変数設定で定義しておく必要がある。しかしアダプタは実際にはアプリケーションレベルで配慮する問題であり、環境変数で配慮すべきはDBMSとそのプロトコルである(つまり「このRailsアプリをMySQLに接続する」ではなく「このRailsアプリを特定のアダプタクラスを利用してMySQLに接続する」ということ)。

ここで最も明白なユースケースは、mysql2trilogyの切り替えや移行である(どちらもMySQLに接続するファーストパーティDBアダプタとしてRailsに含まれているため)。しかし、同じ懸念がカスタムアダプタにも当てはまる(おそらく将来のアダプタにも)。

たとえば環境変数でDATABASE_URL=mysql://host/db?options=whateverを指定している場合、この環境変数を変更せずにアプリケーション側でmysql -> mysql2mysql -> trilogyという切り替えを決定できるようにすべき。

解決方法

Active Recordコンフィグを追加して、データベースURLに含まれるプロトコル名からアダプタへのマッピングを提供する。これをActiveRecord::DatabaseConfigurations::UrlConfig(内部的にはActiveRecord::DatabaseConfigurations::ConnectionUrlResolverクラス)で利用することでプロトコル名からアダプタへのマッピングを行う。

config.active_record.protocol_adapters.mysql = "trilogy"

適切なデフォルト設定をいくつか組み込む(既にハードコードされているpostgres -> postgresql固有のハックも削除する)。
ここでは以下のようになる。

プロトコル アダプタ
mysql mysql2
sqlite sqlite3
postgres postgresql

レビュアーがチェックすべき点

  • ドキュメントをちゃんと書くのは難しい...
  • 自分はActiveSupport::InheritableOptionsを使ったが、厳密には必須ではない。これは型を問わないアクセスや読みやすさをある程度提供し、ハッシュは機能するものの許容度は下がる。
  • sqliteプロトコルをsqlite3アダプタに追加したが、正直これが正しいのかどうかわからない。
    同PRより

つっつきボイス:「プロトコル名はmysqlだけど、実際に使うアダプタ名をmysql2からtrilogyに切り替えられるようにしたかったということですね: 環境変数のURLに含まれるプロトコル名とは別に、実際に使うアダプタをコンフィグで指定可能にしたいというのはわかる」「trilogyはGitHubが作ったmysql2互換のクライアントアダプタですね(ウォッチ20220906)」


「ところで、このプルリクで使ったというActive SupportのInheritableOptionsがちょっと気になりました」「InheritableOptionsOrderedOptionsを継承しているところからしてオプションの順序は保証されるということかな」「OrderedOptionsはこういうことができるらしい↓」「存在しないプロパティもメソッドで追加できるのはJavaScriptのObjectみたいですね」「h.dogみたいに存在しないキーを読み出すとnilが返るけど、h.dog!のように!を付けて読み出すと存在しない場合はエラーになるのか: 自分は使ったことなかったけどRailsのどこかで使われているんでしょうね」

# api.rubyonrails.orgより
h = ActiveSupport::OrderedOptions.new
h.boy = 'John'
h.girl = 'Mary'
h.boy  # => 'John'
h.girl # => 'Mary'
h.dog  # => nil

参考: Rails API ActiveSupport::InheritableOptions

参考: Rails API ActiveSupport::OrderedOptions

参考: Object - JavaScript | MDN

🔗Rails

🔗 Stimulusでホットキーを設定する(Ruby Weeklyより)


つっつきボイス:「スクリーンキャスト記事です」「ホットキーを設定するインターフェイスがStimulusにあるんですね: ボタンにホットキーを指定できるのは直感的で便利👍」

# app/views/welcome/index.html.erb
<%= tag :div,
  "data-controller": "hotkeys ping",
  "data-hotkeys-bindings-value": { "ctrl+k": "ping#pong" }.to_json %>

<div class="container mt-5">
  <div class="row gx-2 gy-2 my-1">
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"esc": "press-key#toggle"}'>esc</button></div>
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"F1": "press-key#toggle"}'>F1</button></div>
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"F2": "press-key#toggle"}'>F2</button></div>
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"F3": "press-key#toggle"}'>F3</button></div>
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"F4": "press-key#toggle"}'>F4</button></div>
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"F5": "press-key#toggle"}'>F5</button></div>
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"F6": "press-key#toggle"}'>F6</button></div>
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"F7": "press-key#toggle"}'>F7</button></div>
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"F8": "press-key#toggle"}'>F8</button></div>
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"F9": "press-key#toggle"}'>F9</button></div>
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"F10": "press-key#toggle"}'>F10</button></div>
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"F11": "press-key#toggle"}'>F11</button></div>
    <div class="col"><button class="btn btn-secondary w-100 py-1" data-controller="hotkeys press-key" data-hotkeys-bindings-value='{"F12": "press-key#toggle"}'>F12</button></div>
  </div>
...

🔗 shoulda-matchersをRSpecで使う(Ruby Weeklyより)


つっつきボイス:「マッチャーの記事は最近珍しいと思って取り上げてみました」「shoulda-matchersは相当昔からあるgemですね」「こうやってshouldで書くの懐かしい↓」「今はexpectで書くのが普通ですよね」

# 同記事より
it { should accept_nested_attributes_for(:publishers) }
it { should accept_nested_attributes_for(:publishers).allow_destroy(true) }
it { should accept_nested_attributes_for(:publishers).update_only(true) }

thoughtbot/shoulda-matchers - GitHub

have_db_columnとかhave_db_indexのようなマッチャーもあるのか↓」「have_implicit_order_columnとか知らないものがいっぱいありますね」「shoulda-matchersにはかなりいろんなマッチャーがあったのを思い出した」「温故知新的によさそうな記事👍」

# 同記事より
it { should have_db_column(:title).of_type(:string) }

it { should have_db_index([:author_id, :title]) }

it { should have_implicit_order_column(:updated_at) }

🔗 based_uuid: Base32エンコードのUUID(Ruby Weeklyより)

pch/based_uuid - GitHub


つっつきボイス:「Base32というエンコード、そういえばありますね」「ユーザーフレンドリーなUUIDってどういう意味でしょうか?」「user_763j02ryxh8dbs56mgcjqrmmgtのようにハイフン-を含めないUUIDにすることで、エディタやターミナルでダブルクリックしたときにUUIDが一発で選択できるという意味かな?」「あ、そういう意味なのか」

「これも想像ですけど、URLにハイフンが含まれるとわずらわしいと思って作ったのかもしれませんね: URLを貼り付けると自動的にリンクになるアプリケーションがありますけど、ものによってはハイフンのところで自動リンクが途切れてしまうことがある」「たまにそういうアプリありますね」「URLにハイフンを含めないで済むUUIDが欲しい気持ちはちょっとわかるかも」

user_763j02ryxh8dbs56mgcjqrmmgt #=> e61c802c-7bb1-4357-929a-9064af8a521a
bpo_12dm1qresn83st62reqdw7f7cv  #=> 226d037c-3b35-40f3-a30b-0ebb78779d9b

参考: Base32 - Wikipedia

🔗Ruby

🔗 Ruby 3.3.0-rc1 リリース(Ruby公式ニュースより)


つっつきボイス:「Rubyはクリスマスに新バージョンがリリースされるのが恒例なので、そろそろrc1が出てくる時期ですね」「今度の3.3はパフォーマンス改善が中心で、特にPrismの導入が大きい」「default gemsやbundled gemsの編成変更やバージョンアップも見逃せないですね」

🔗 _1と同等のitブロックパラメータがRuby 3.4で導入される(Ruby Weeklyより)


つっつきボイス:「Ruby Weeklyでこれ見かけて驚きました」「暗黙のブロック変数として_1と同等のitが来年のRuby 3.4で入るのか!」「MatzもOK出しているんですね」「一般的なRubyのスタイルガイドでは、_で始まる変数名は"利用しない"ことを明示するのに使うのが普通なので、_1よりもitの方が紛らわしくないと思います」「今の_1はなくならないんですね」「Ruby 2.7で導入された_1_2などのnumbered parameterは、これはこれで便利ですし自分も使っていますよ: 特にワンライナーを書き捨てるときに便利👍」

# 同issueより
[1, 2, 3].each { puts _1 }
# 以下は上と同等
[1, 2, 3].each { puts it }

「導入のために裸のitという名前に警告を出すことも検討しているようですね」「itはRSpecで普通に使われていますね: 提案されている暗黙のitはブロック内でしか使わないから衝突はしないと思いますけど」


採用されるかどうかわかりませんが、同issueではitsというエイリアス案も出されていますね↓。

# for each user, get its name
user_names = users.map { its.name }

🔗 Ruby debug 1.9.9でIRBコマンドにフルアクセス可能に


つっつきボイス:「ruby debugでIRBにアクセスできるようになったそうです」「RUBY_DEBUG_IRB_CONSOLE=1で使えるようになるんですね」「ページャを新たにサポートしたことでコンソールで表示が壊れる可能性が考えられるので、まずはオプションにしたんでしょうね」「新機能の中ではデバッガで複数行入力できるのはよさそう!👍: これ欲しかった」

参考: ページャとは - 意味をわかりやすく - IT用語辞典 e-Words


今回は以上です。Happy Holidays!

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

週刊Railsウォッチ: Rails 7.1で「Apple でサインイン」を実装、JetBrains AIほか(20231215後編)

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

Rails公式ニュース

Ruby Weekly


CONTACT

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