みなさんこんにちは。freee Developers Hub 編集部の bucyou (ぶちょー) と申します。今年も、freee Advent Calendar 2025 の時期がやってまいりました。この企画は、12月1日から12月25日まで、1日1記事ずつ公開していくブログ企画になります。
今年で11年目です。お気軽な記事から、今のホットなトピック、技術研究など様々なトピックをお送りしていきたいと思っております。この記事は、freee Advent Calendar 2025 における1日目の記事になります。
フリー株式会社として開催している Advent Calendar については、他にも QA, DEI の企画もございます。
さて、私は普段、関西支社の所属となっておりまして、普段はそちらを開放して開催されている kyobashi.rb に参加することが多いです。ありがたいことに程よい大きさのコミュニティで、刺激的な場になっています。主催されているのは、freee からは hachi さんと、Rubyコミッターの ydah さんです。内容はかなり自由であり、電子工作の話から、パーサーの話、普段の業務で発見した出来事など多種多様です。Ruby が全く出てこない回もあります。なぜか、ローレイヤーな話題で盛り上がることが多いのです。
私も負けじと発表の時間をいただくことがあります(アットホームな規模感なので、手を挙げれば登壇しやすい雰囲気です)。今日は、そこでやろうと思ってた内容をここに残しておこうと思います。
背景
去年と今年の大きな変化は何かといえば、周りがみんなLLMによる支援を受けながらコーディングをすることが当たり前になったことでしょう。多少プロンプトや、ツールをうまく整える必要はあるものの、それさえ乗り越えればコーディング作業をいい感じに進めてくれるようになりました。
特に、RSpec によるテスト記述については、粒度さえコントロールできれば今までより格段に早くコーディングが行えるようになったと思います。
表層やビジネスとしては喜ばしいところで、自分が書くよりも格段にマシな表現を整えてくれるところもあるのでありがたい限りなのです。一方で、深層としては、なんとなく書いている「あれ」とか「これ」が結局何やってるんだろうという疑問を、エンジニアとしてちゃんと解決しておきたいという気持ちが出てきてしまいました。
そこで今日は、Claude Code の手も借りながら、RSpec の Mock に関するコードが、結局何をやっているのかを知っていこうという活動をしようと思います。

ℹ️ この記事の内容は 2025/12/1 時点に main にある RSpec のソースコードを元に記載しています。
おさらい: allow ってなんだっけ?
テストを書くときに、対象のモジュールやクラスを動かすために必要なオブジェクトを、全て用意するのが難しい場合があります。そこで、ニセモノのオブジェクトをツールとして用意してやると、うまく扱えるようになります。
例えば、外部のオブジェクトを参照するための SomethingClient が内部で gRPC 通信を行っているとすると、テストで毎回ホンモノの gRPC 通信を行ってしまうと非常に都合が悪いことになります。仮にモックのない世界だと、サーバを用意してやる必要はありますし、テストのたびにそのサーバが呼ばれることになるので安定しないテストになります。特に単体テストは通常、いつ、誰が、何度実行しても同じ結果になるべきという「決定性」が求められるため、外部通信やランダム性を持つ処理については対処が必要です。
そこで、RSpec の場合は、以下のように記載することで SomethingClient の挙動を変えることができます。
before do my_something_client = instance_double(SomethingClient) allow(SomethingClient).to receive(:new).and_return(my_something_client) fetched_object = double('Something') allow(my_something_client).to receive(:fetch).and_return(fetched_object) end it 'MyProcedureの実行に成功する' do # あまり良い設計ではないが、MyProcedure の new で SomethingClient.new がされているものとする procedure = MyProcedure.new # 例えば、fetch でエラーが出なければ true を返すといった挙動 # before で fetch に対する振る舞いを記載しているので、問題なく動く expect(procedure.exec).to eq(true) end
ドキュメント: https://rspec.info/features/3-12/rspec-mocks/
これができるのは、ぱっと見は魔法のようですが、Ruby は非常に柔軟な言語であり、クラスやモジュールの振る舞いを後から書き換えることは容易に行うことができます。例えば、RSpec を使わなくても、以下のようにモックを作ることができます。
class SomethingClient def initialize puts "面倒な処理" end def fetch_user(id) puts ">> 本物の通信が発生: ID #{id}" "Real User Data for ID: #{id}" {name: 'Yamada Taro'} end end def SomethingClient.new # ここで返すオブジェクトが「モック」になります。 # Object.new で空のオブジェクトを作り、そこにメソッドを動的に生やします。 mock_object = Object.new # このオブジェクトだけに fetch_user メソッドを定義します(特異メソッド)。 # これが RSpec の `allow(client).to receive(:fetch_user)` に相当します。 def mock_object.fetch_user(id) puts ">> [MOCK] 通信をスキップしました" {name: "Mock Dayo"} end # 作成したモックを返す mock_object end # 元々のほうではなく、上書きされたほうが使われる client = SomethingClient.new result = client.fetch_user(1) puts result
これを実行すると、以下のような出力になります。
>> [MOCK] 通信をスキップしました
{name: "Mock Dayo"}
要するにモンキーパッチと呼ばれるものです。では RSpec や、Mock ライブラリを使わず、モンキーパッチを単純に使うだけで良いのではないか? となるかもしれませんが、それはおすすめできないでしょう。以下のような問題が発生します。
- 上書きした後に戻す手段が提供されない。手動でやると書き換えたらそのままになる。
- 実際のクラスに存在しないメソッドを生やすことができてしまう。テストと実際の乖離。
- 可読性の観点。ぱっと見何をやっているかわからない。
ライブラリが持つ機能を利用することで、これらの問題を解消することができます。
ℹ️
モックをするときに、昔はモック対象に対して直接 .stub を行う記法が存在していたようです。こちらの方法は、モンキーパッチを行うことになるため設計的には適切ではありません。
RSpec 3 ではこの記法が非推奨 (デフォルトで無効) となり、 allow によるモックが標準的になりました。この中で、技術用語の「スタブ」を使うより、自然言語である allow を使うという提案がなされました。
ただし、この記法を持っているフレームワークはあまり存在せず、少し戸惑うかもしれません。私は、Ruby での開発を始めた当初、かなり戸惑いました。
allow のコードを読もう
RSpec のコードは、モノレポ構成となっており、 https://github.com/rspec/rspec の中に rspec-mocks が含まれています。こちらのコードをクローンして読んでみましょう。
allow のコードに辿り着くために、 rspec コマンドの実行から順を追って読んでいきましょう。
設定フェーズ
rspec コマンドが実行されると、rspec-core/exe/rspec が呼ばれます。
#!/usr/bin/env ruby require 'rspec/core' RSpec::Core::Runner.invoke
rspec/rspec-core/exe/rspec at 0c37a88d4ff511debd563d68e10c1c7672318c3c · rspec/rspec · GitHub
実行されると、 rspec-core/lib/rspec/core/runner が初期化されますが、 Runner の中で RSpec.configuration が呼ばれます。
def initialize(options, configuration=RSpec.configuration, world=RSpec.world) @options = options @configuration = configuration @world = world end
その中で、 mock_with によって、モック用のフレームワークが読み込まれるようになっています。
MOCKING_ADAPTERS = { :rspec => :RSpec, :flexmock => :Flexmock, :rr => :RR, :mocha => :Mocha, :nothing => :Null } # ... 省略 ... def mock_with(framework) framework_module = if framework.is_a?(Module) framework else const_name = MOCKING_ADAPTERS.fetch(framework) do raise ArgumentError, "Unknown mocking framework: #{framework.inspect}. " \ "Pass a module or one of #{MOCKING_ADAPTERS.keys.inspect}" end RSpec::Support.require_rspec_core "mocking_adapters/#{const_name.to_s.downcase}" RSpec::Core::MockingAdapters.const_get(const_name) end new_name, old_name = [framework_module, @mock_framework].map do |mod| mod.respond_to?(:framework_name) ? mod.framework_name : :unnamed end end
ここでわかることは、モックに利用するライブラリは rspec だけでなく、 mocha, flexmock など複数のものに対応しているということです。お好みのライブラリがあれば、別のものに切り替えることができるようになっているようです。特に指定がない場合は rspec-mocks が読み込まれるようになっています。
https://rspec.info/features/3-12/rspec-core/mock-framework-integration/use-mocha/
ドキュメントにもしっかり書いてありますね。これは知らなかったです。
ExampleGroup での利用
ExampleGroup (describe や、 context によって作られるインスタンス) が作成されると、 it などお馴染みの機能をセットアップしますが、この中で Configuration の、 configure_mock_framework を呼びます。
def self.ensure_example_groups_are_configured unless defined?(@@example_groups_configured) RSpec.configuration.configure_mock_framework RSpec.configuration.configure_expectation_framework # rubocop:disable Style/ClassVars @@example_groups_configured = true # rubocop:enable Style/ClassVars end end
ここで、 ExampleGroup に対して、モックが include されるという動きになります。この処理は、クラス変数により1度だけ実行されるように設定されています。
def configure_mock_framework RSpec::Core::ExampleGroup.include(mock_framework) end
モックライブラリを rspec にしている場合、RSpec::Core::MockingAdapters::RSpec が呼び出されます。
require 'rspec/mocks' module RSpec module Core module MockingAdapters # @private module RSpec include ::RSpec::Mocks::ExampleMethods def self.framework_name :rspec end def self.configuration ::RSpec::Mocks.configuration end def setup_mocks_for_rspec ::RSpec::Mocks.setup end def verify_mocks_for_rspec ::RSpec::Mocks.verify end def teardown_mocks_for_rspec ::RSpec::Mocks.teardown end end end end end
各 Example (it や、 specify などによって作られるインスタンス) では、 before の段階で example_group の setup_mocks_for_rspec を行い、 after の段階で、 teardown_mocks_for_rspec が呼ばれるのが読めます。このタイミングで、 verify_mocks が行われ、必要に応じてモックの検証が行われます。(今回は、verify_mocks については深く扱いません。)
def run_before_example @example_group_instance.setup_mocks_for_rspec hooks.run(:before, :example, self) end # ... (省略) ... def run_after_example assign_generated_description if defined?(::RSpec::Matchers) hooks.run(:after, :example, self) verify_mocks ensure @example_group_instance.teardown_mocks_for_rspec end
RSpec::Core::MockingAdapters::RSpec は、 RSpec::Mocks::ExampleMethod を include しています。include されているモジュールを読みにいくと、 double や、 instance_double といったお馴染みの機能が定義されており、 allow についてもここで定義されていることが読み取れます。
実際に、Example を実行する時は、 example_group のインスタンスの instance_exec 1 によりブロックを実行するので、ExampleGroup で定義されているメソッドを実行できるようになっています。
def run(example_group_instance, reporter) @example_group_instance = example_group_instance # ... (省略) ... # ExampleGroup のコンテキストで block を実行する @example_group_instance.instance_exec(self, &@example_block) # ... (省略) ... end
allow の実行
ExampleMethods の中に allow がいることが分かりましたので、それを読んでいきましょう。
def allow(target) AllowanceTarget.new(target) end
どうやら、 allow の正体は、 AllowanceTarget のインスタンスであることが分かりました。こちらも追っていくと、比較的意味がわかりやすいクラスが出てきます。 not_to や、 to_not は許容しない。 to については、なんらかの setup_allowance というメソッドに委譲していそうだということがわかりました。
class AllowanceTarget < TargetBase def expression :allow end delegate_to :setup_allowance disallow_negation :not_to disallow_negation :to_not end
to が動的に定義され、その引数を matcher として受け取っていることが分かります。
def delegate_to(matcher_method) define_method(:to) do |matcher, &block| unless matcher_allowed?(matcher) raise_unsupported_matcher(:to, matcher) end define_matcher(matcher, matcher_method, &block) end end
さらに define_matcher をたどると、以下のようなコードが出てきます。 つまり、allow(SomethingClient).to(matcher) の正体は、 matcher 自体の name メソッドを呼び出すことだと分かりました。 delegate_to の定義を見ると :setup_allowance に委譲されているため、最終的に matcher.setup_allowance が、target と to に渡した block を引数として実行されることになります。
def define_matcher(matcher, name, &block) matcher.__send__(name, target, &block) end
matcher の生成
では、 to に渡す、 matcher とは何か? というと、以下の文で言うと後半の receive 以下ということになります。
# -> ここから先がmatcher allow(SomethingClient).to receive(:new).and_return(my_something_client)
こちらも、ExampleMethods で定義されています。こちらも、なんらかのクラスのインスタンスを作るものだったんですね。
def receive(method_name, &block) Matchers::Receive.new(method_name, block) end
しかも、 Matchers::Receive ではメタプログラミング的な手法で、動的にメソッドを作っています。 MessageExpectation から、 public_instance_methods を呼び出して、メソッドを作り出します。ここの面白いところは、
MessageExpectation自体はメソッド名の参照に使っているだけで、ここでは呼ばない。Receive自体のインスタンスを返すことで、メソッドチェーンを可能にする。(with(1).and_return(something)のような呼び出しを可能にする。)@recorded_customizationsには、ExpectationCustomizationのインスタンスを記録するだけ。
というところにあるでしょう。ここでは、 ruby2_keywords(method) という「いにしえ」のメソッドが呼ばれています。 これは Ruby 3.0 でキーワード引数の挙動が変化しており、互換性を保つために存在しています。現在リリースされている最新バージョンの rspec-mocks におけるサポートバージョンは、Ruby 1.8.7 以上であることから、互換性の観点でこの実装がされています。main branch を見る限りは、Ruby 3.0 以降を必須としているため、そのうちこのコードも消えるのでしょうか? コントリビューションチャンス!? 2
own_methods = (instance_methods - superclass.instance_methods) MessageExpectation.public_instance_methods(false).each do |method| next if own_methods.include?(method) define_method(method) do |*args, &block| @recorded_customizations << ExpectationCustomization.new(method, args, block) self end ruby2_keywords(method) if respond_to?(:ruby2_keywords, true) end
これで、 receive(:new).and_return(my_something_client) のように、チェーンしていても Receive のインスタンスであるということがわかりました。これが、 matcher として機能します。
setup_allowance
次に、Receive の setup_allowance が何をやっているのかを見にいきましょう。
RSpec::Mocks.space.proxy_for(subject) によって、特定のターゲットオブジェクトに対する Proxy があるかを検索し、なければ作ります。 自前のクラスをモックする場合は、 VerifyingPartialClassDoubleProxy のインスタンスが利用されます。( space は今のスコープにおけるメモリ管理に利用しているもので、複数の Proxy を管理している。)
Proxy は、状況に応じて様々な種類が使いわけられます。
Proxy (基底クラス)
├─ PartialDoubleProxy
│ ├─ PartialClassDoubleProxy
│ │ └─ include PartialClassDoubleProxyMethods
│ └─ VerifyingPartialDoubleProxy
│ ├─ include VerifyingProxyMethods
│ └─ VerifyingPartialClassDoubleProxy
│ └─ include PartialClassDoubleProxyMethods
setup_method_substitute では、 Proxy に対して add_stub を行います。後述にはなりますが、ここでは MessageExpectation のインスタンスが返ってきます。
そして、 @recorded_customizations に対して、 playback_onto を実行します。シンプルに、 MessageExpectation のインスタンスを呼んでいます。 ExpectationCustomization には、 and_return といった Receive を修飾する情報が与えられており、 setup_allowance のタイミングで実際の処理が行われていることがわかりました。
メソッドチェーンを行っている最中は単純に記録しておき、それを使うタイミングになるまでは何もせずに待っておくというテクニックを使っているわけですね。
def setup_allowance(subject, &block) warn_if_any_instance("allow", subject) setup_mock_proxy_method_substitute(subject, :add_stub, block) end # ... (省略) ... def setup_mock_proxy_method_substitute(subject, method, block) # ここで、用途に合わせて適切な Proxy が生成される proxy = ::RSpec::Mocks.space.proxy_for(subject) setup_method_substitute(proxy, method, block) end # ... (省略) ... def setup_method_substitute(host, method, block, *args) args << @message.to_sym block = move_block_to_last_customization(block) # 今回の場合は proxy.add_stub になる expectation = host.__send__(method, *args, &(@block || block)) @recorded_customizations.each do |customization| customization.playback_onto(expectation) end expectation end
def playback_onto(expectation) # and_return などが呼び出される expectation.__send__(@method_name, *@args, &@block) end
Proxy 自体はメソッドの上書き状態を管理します。 @method_doubles の管理に利用する Hash は、呼び出したときに存在しない場合は、 MethodDouble を新たに作ろうとします。 MethodDouble をどこで作っているんだろう? と迷いそうなコードですが、Ruby の Hash.new は、ブロックを受け取ると未定義な要素を呼び出した場合の挙動を定義できます。
def initialize(object, order_group, options={}) # ... (省略) ... @method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) } end # ... (省略) ... def method_double_for(message) @method_doubles[message.to_sym] end # ... (省略) ... def add_stub(method_name, opts={}, &implementation) location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line } method_double_for(method_name).add_stub @error_generator, @order_group, location, opts, &implementation end
MethodDouble では、元のメソッドの退避を行い、プロキシメソッドを定義しています。 definition_target には、通常、対象のオブジェクトのシングルトンクラスを定義しており、 class_exec によりそのスコープで define_method を行います。
# @private def configure_method @original_visibility = visibility # ここで元メソッドを退避 (今回はここは詳しく説明しない) @method_stasher.stash unless @method_is_proxied define_proxy_method end # @private def define_proxy_method return if @method_is_proxied save_original_implementation_callable! # https://docs.ruby-lang.org/ja/latest/method/Module/i/class_exec.html definition_target.class_exec(self, method_name, @original_visibility || visibility) do |method_double, method_name, visibility| define_method(method_name) do |*args, &block| method_double.proxy_method_invoked(self, *args, &block) end # This can't be `if respond_to?(:ruby2_keywords, true)`, # see https://github.com/rspec/rspec-mocks/pull/1385#issuecomment-755340298 ruby2_keywords(method_name) if Module.private_method_defined?(:ruby2_keywords) __send__(visibility, method_name) end @method_is_proxied = true rescue FrozenError raise ArgumentError, "Cannot proxy frozen objects, rspec-mocks relies on proxies for method stubbing and expectations." end # ... (省略) ... def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation) configure_method # stub は、MessageExpectation のインスタンス stub = message_expectation_class.new(error_generator, expectation_ordering, expected_from, self, :stub, opts, &implementation) stubs.unshift stub stub end
上書きされたメソッドでは proxy_method_invoked が呼ばれ、最終的には Proxy の message_received が呼ばれることが分かります。
def proxy_method_invoked(_obj, *args, &block) @proxy.message_received method_name, *args, &block end ruby2_keywords :proxy_method_invoked if respond_to?(:ruby2_keywords, true)
ちょっとややこしくなってきたので、Claude Code に Space, Proxy, MethodDouble の関係を、図にしてもらいました。
Space ──1:N──▶ Proxy ──1:N──▶ MethodDouble ──1:N──▶ MessageExpectation
│ │ │
│ │ └─ 1メソッドの stub/expectation を管理
│ └─ 1オブジェクトの全メソッドを管理
└─ 全オブジェクトのProxyを管理 (現在のスコープごと)
モックの利用
今までの一連の流れで、ようやく allow(SomethingClient).to receive(:new).and_return(my_something_client) という呼び出しをすることで、 SomethingClient.new の処理が置き換わり @proxy.message_received が呼び出されるということが分かりました。
あとは最後に、これが何をやっているかを読んでおきましょう。そこそこ複雑そうな処理ですが、順を追っていくと、条件に合致する expectation (こちらは allow ではなく expect でモックを検査するときに利用する) と、 stub を探します。
今回の例だと、stub だけがヒットするはずなので、最初の条件式が該当します。ここでは単純に、 stub.invoke(nil, []) となりそうです。 stub は、 Proxy#add_stub で追加されたもので、実態は、 message_expectation_class (MethodDouble の場合、 MessageExpectation 固定) のインスタンスとなります。
def message_received(message, *args, &block) record_message_received message, *args, &block expectation = find_matching_expectation(message, *args) # 条件に一致する stub を探す # with がある場合などは、引数に合わせて一致する stub を探しにいくようになっている stub = find_matching_method_stub(message, *args) if (stub && expectation && expectation.called_max_times?) || (stub && !expectation) expectation.increase_actual_received_count! if expectation && expectation.actual_received_count_matters? if (expectation = find_almost_matching_expectation(message, *args)) expectation.advise(*args) unless expectation.expected_messages_received? end stub.invoke(nil, *args, &block) # ... (省略) ... end end ruby2_keywords :message_received if respond_to?(:ruby2_keywords, true)
MessageExpectation も複雑ですが、要点としては and_return によって、 @implementation.terminal_action が定義されているというのがポイントになります。これにより、 AndReturnImplementation が実行され、値が返される挙動になります。
def invoke(parent_stub, *args, &block) if invoking_internals safe_invoke_without_incrementing_received_count(parent_stub, *args, &block) else # 通常はこちらが呼ばれる invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block) end end # ...(省略)... def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block) # ... (省略) ... if implementation.present? implementation.call(*args, &block) elsif parent_stub parent_stub.invoke(nil, *args, &block) end # ... (省略) ... end
and_return によって、 terminal_implementation_action が、 AndReturnImplementation となります。
def and_return(first_value, *values, &_block) # ... (省略) ... self.terminal_implementation_action = AndReturnImplementation.new(values) nil end # ... (省略) ... def terminal_implementation_action=(action) implementation.terminal_action = action end
and_returnでは位置引数を複数とるので、中身は shift によって1つずつ取り出されるようになっています。これで呼び出される回数によって、別のものを返す挙動もできるようになっています。(allow(SomethingClient).to receive(:new).and_return(my_object_1, my_object_2) とすると、1回目は my_object_1 となり、2回目は my_object_2 となる。)
class Implementation attr_accessor :initial_action, :inner_action, :terminal_action def call(*args, &block) actions.map do |action| action.call(*args, &block) end.last end ruby2_keywords :call if respond_to?(:ruby2_keywords, true) def present? actions.any? end private def actions [initial_action, inner_action, terminal_action].compact end end # ... (省略) ... class AndReturnImplementation def initialize(values_to_return) @values_to_return = values_to_return end def call(*_args_to_ignore, &_block) if @values_to_return.size > 1 @values_to_return.shift else @values_to_return.first end end end
お片付け
テストが終わったら、モックしたクラスは元に戻って欲しいです。それぞれの Example の after では、 teardown_mocks_for_rspec が呼び出されます。 これにより、 space の reset_all が呼び出され、すべての proxies が reset されることが読み取れます。
def teardown_mocks_for_rspec ::RSpec::Mocks.teardown end
def self.teardown space.reset_all @space_stack.pop @space = @space_stack.last || @root_space end
rspec/rspec-mocks/lib/rspec/mocks.rb at main · rspec/rspec · GitHub
def reset_all proxies.each_value { |proxy| proxy.reset } any_instance_recorders.each_value { |recorder| recorder.stop_all_observation! } any_instance_recorders.clear @constant_mutators.reverse.each { |mut| mut.idempotently_reset } end
rspec/rspec-mocks/lib/rspec/mocks/space.rb at main · rspec/rspec · GitHub
PartialDoubleProxy の reset では、管理している @method_doubles に対して、 reset を実行します。
def reset @method_doubles.each_value { |d| d.reset } super end
rspec/rspec-mocks/lib/rspec/mocks/proxy.rb at main · rspec/rspec · GitHub
stash していたメソッドを元に戻し、保存していた stub 情報をすべて削除します。
def reset restore_original_method clear end
rspec/rspec-mocks/lib/rspec/mocks/method_double.rb at main · rspec/rspec · GitHub
まとめ: コードリーディングから見えること
ということで、終始メタプログラミングの魔術を見ていった感じでした。ここまでわかるようになると、自前で Expectation を拡張できることも分かりそうなので、ビジネス表現に合わせた DSL の拡張もできるようになりそうです。
普段から使っているものの中身がどうなっているのかを詳細に見ていくのはなかなか楽しいですね! 特に、Ruby2.x の互換性を保つコードがまだ多く残っていることもわかりました。これからのバージョンに向け、よりシンプルなコードになっていくと予想されます。
今回は、Claude Code にも手伝ってもらいコードリーディングを行なっていったのですが、歴史的な背景を含めて検索するのは、ひとりでは到底難しかったので、OSS コードリーディングのお供としてもエージェントを使うのは良さそうでした!
記事の分量の関係で省略してしまいましたが、メソッド退避の挙動や、RuboCop などでは使うなと言われる allow_any_instance_of がより複雑そうなことをやっている様子なども見てとれます。
また明日
初日からヘビーな記事なってしまった気がしますが、明日は nil0ka さんによる、AI麻雀?の記事です。お楽しみに。
-
instance_execは、インスタンスのスコープでブロックを実行することができます。https://docs.ruby-lang.org/ja/latest/method/BasicObject/i/instance_exec.html↩ - 次期バージョンは Ruby2 系のサポートを止めることを明示しています。https://github.com/rspec/rspec/pull/258↩
