From 7690290068f97baae5fe586b58d3604958f860e8 Mon Sep 17 00:00:00 2001 From: Carl Brasic Date: Mon, 29 Aug 2022 14:19:24 -0500 Subject: [PATCH] Replace `method_source` gem with stdlib equivalent The `method_source` gem was added in https://github.com/rails/rails/pull/19216. It was used to determine the last line number of a given test method to support running tests by line number. But this is not something that requires an external dependency: Ripper can do this easily, and it has the added advantage of not using repeated calls to `eval` to do it. Drop `method_source` and replace it with a simple handler for Ripper's `on_def` parser event. I don't believe that there are any mainstream rubies at this point that can run Rails and don't support Ripper but correct me if I'm wrong. --- Gemfile.lock | 2 -- railties/lib/rails/test_unit/runner.rb | 27 +++++++++++++++-- railties/railties.gemspec | 1 - railties/test/application/test_runner_test.rb | 30 ++++++++++++++++++- 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 28cfa23d9c6..4a040abaa26 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -94,7 +94,6 @@ PATH railties (7.1.0.alpha) actionpack (= 7.1.0.alpha) activesupport (= 7.1.0.alpha) - method_source rake (>= 12.2) thor (~> 1.0) zeitwerk (~> 2.6) @@ -315,7 +314,6 @@ GEM marcel (1.0.2) matrix (0.4.2) memoist (0.16.2) - method_source (1.0.0) mini_magick (4.11.0) mini_mime (1.1.2) mini_portile2 (2.8.0) diff --git a/railties/lib/rails/test_unit/runner.rb b/railties/lib/rails/test_unit/runner.rb index 69e7a023a5e..97533352fc5 100644 --- a/railties/lib/rails/test_unit/runner.rb +++ b/railties/lib/rails/test_unit/runner.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true require "shellwords" -require "method_source" require "rake/file_list" require "active_support" require "active_support/core_ext/module/attribute_accessors" +require "ripper" module Rails module TestUnit @@ -169,10 +169,33 @@ module Rails private def definition_for(method) file, start_line = method.source_location - end_line = method.source.count("\n") + start_line - 1 + end_line = MethodEndFinder.call(source: File.read(file), target: method.name) return file, start_line..end_line end + + # Finds the line number where the definition of method named +target+ ends. + module MethodEndFinder # :nodoc: + def self.call(source:, target:) + catch(:line_number_is) do + DefParser.new(source: source, target: target).parse + return nil + end + end + + class DefParser < Ripper # :nodoc: + def initialize(source:, target:) + @target = String(target) + super(source) + end + + def on_def(method_name, *) + if method_name == @target + throw(:line_number_is, lineno) + end + end + end + end end end end diff --git a/railties/railties.gemspec b/railties/railties.gemspec index beefc91ec5f..af25f42f7d3 100644 --- a/railties/railties.gemspec +++ b/railties/railties.gemspec @@ -42,7 +42,6 @@ Gem::Specification.new do |s| s.add_dependency "rake", ">= 12.2" s.add_dependency "thor", "~> 1.0" - s.add_dependency "method_source" s.add_dependency "zeitwerk", "~> 2.6" s.add_development_dependency "actionview", version diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index 179ad3876af..f7dfdb08251 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -376,7 +376,7 @@ module ApplicationTests end end - def test_more_than_one_line_filter + def test_more_than_one_line_filter_macro_syntax app_file "test/models/post_test.rb", <<-RUBY require "test_helper" @@ -404,6 +404,34 @@ module ApplicationTests end end + def test_more_than_one_line_filter_test_method_syntax + app_file "test/models/post_test.rb", <<-RUBY + require "test_helper" + + class PostTest < ActiveSupport::TestCase + def test_first_filter + puts 'PostTest:FirstFilter' + assert true + end + + def test_second_filter + puts 'PostTest:SecondFilter' + assert true + end + + def test_line_filter_does_not_run_this + assert true + end + end + RUBY + + run_test_command("test/models/post_test.rb:4:9").tap do |output| + assert_match "PostTest:FirstFilter", output + assert_match "PostTest:SecondFilter", output + assert_match "2 runs, 2 assertions", output + end + end + def test_more_than_one_line_filter_with_multiple_files app_file "test/models/account_test.rb", <<-RUBY require "test_helper"