diff --git a/lib/rspec/parameterized/core.rb b/lib/rspec/parameterized/core.rb index ad3aa38..e4738c7 100644 --- a/lib/rspec/parameterized/core.rb +++ b/lib/rspec/parameterized/core.rb @@ -1,9 +1,8 @@ require "rspec/parameterized/core/version" -require 'parser' -require 'unparser' -require 'proc_to_ast' require 'rspec/parameterized/core/helper_methods' require 'rspec/parameterized/core/example_helper_methods' +require 'rspec/parameterized/core/errors' +require 'rspec/parameterized/core/composite_parser' module RSpec module Parameterized @@ -143,11 +142,9 @@ def define_cases(parameter, *args, &block) end def params_inspect(obj) - begin - obj.is_a?(Proc) ? obj.to_raw_source : obj.inspect - rescue Parser::SyntaxError - return obj.inspect - end + RSpec::Parameterized::Core::CompositeParser.to_raw_source(obj) + rescue ParserSyntaxError + return obj.inspect end def set_verbose_parameters(&block) diff --git a/lib/rspec/parameterized/core/composite_parser.rb b/lib/rspec/parameterized/core/composite_parser.rb new file mode 100644 index 0000000..67075e3 --- /dev/null +++ b/lib/rspec/parameterized/core/composite_parser.rb @@ -0,0 +1,75 @@ +module RSpec + module Parameterized + module Core + # Proxy class for parser and prism + module CompositeParser + # @param obj [Object] + # @return [String] + # @raise [RSpec::Parameterized::Core::ParserSyntaxError] + def self.to_raw_source(obj) + return to_raw_source_with_prism(obj) if use_prism? + + to_raw_source_with_parser(obj) + end + + # Whether use parser or prism + # + # @return [true] Use prism + # @return [false] Use parser + def self.use_prism? + Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") + end + + # @param obj [Object] + # @return [String] + # @raise [RSpec::Parameterized::Core::ParserSyntaxError] + def self.to_raw_source_with_parser(obj) + obj.is_a?(Proc) ? obj.to_raw_source : obj.inspect + rescue Parser::SyntaxError => e + raise ParserSyntaxError + end + private_class_method :to_raw_source_with_parser + + # @param obj [Object] + # @return [String] + def self.to_raw_source_with_prism(obj) + return obj.inspect unless obj.is_a?(Proc) + + filename, linenum = obj.source_location + ast = parse_with_prism(filename, linenum) + + return "" unless ast + + ast.source.source.strip + end + private_class_method :to_raw_source_with_prism + + # @param filename [String] + # @param linenum [Integer] + # + # @return [Prism::ParseResult,nil] + def self.parse_with_prism(filename, linenum) + buf = [] + File.open(filename, "rb").each_with_index do |line, index| + next if index < linenum - 1 + buf << line + + ret = Prism.parse(buf.join) + return ret if ret.success? + end + + nil + end + private_class_method :parse_with_prism + end + end + end +end + +if RSpec::Parameterized::Core::CompositeParser.use_prism? + require 'prism' +else + require 'parser' + require 'unparser' + require 'proc_to_ast' +end diff --git a/lib/rspec/parameterized/core/errors.rb b/lib/rspec/parameterized/core/errors.rb new file mode 100644 index 0000000..90da09f --- /dev/null +++ b/lib/rspec/parameterized/core/errors.rb @@ -0,0 +1,9 @@ +module RSpec + module Parameterized + module Core + class Error < StandardError; end + + class ParserSyntaxError < Error; end + end + end +end diff --git a/lib/rspec/parameterized/core/lazy_arg.rb b/lib/rspec/parameterized/core/lazy_arg.rb index 5d77e70..5d96cab 100644 --- a/lib/rspec/parameterized/core/lazy_arg.rb +++ b/lib/rspec/parameterized/core/lazy_arg.rb @@ -11,8 +11,8 @@ def apply(obj) end def inspect - "#{@block.to_raw_source}" - rescue Parser::SyntaxError + CompositeParser.to_raw_source(@block) + rescue ParserSyntaxError super.inspect end end diff --git a/rspec-parameterized-core.gemspec b/rspec-parameterized-core.gemspec index 74b5c9d..8bad8af 100644 --- a/rspec-parameterized-core.gemspec +++ b/rspec-parameterized-core.gemspec @@ -32,12 +32,18 @@ I was inspired by [udzura's mock](https://gist.github.com/1881139).} spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] + spec.add_dependency "rspec", ">= 2.13", "< 4" + + # parser dependencies spec.add_dependency "parser" spec.add_dependency "proc_to_ast", ">= 0.2.0" - spec.add_dependency "rspec", ">= 2.13", "< 4" spec.add_dependency "unparser" + # prism dependencies + spec.add_dependency "prism" + spec.add_development_dependency "rake", ">= 12.0.0" + spec.add_development_dependency "rspec-its" # For more information and examples about making a new gem, check out our # guide at: https://bundler.io/guides/creating_gem.html diff --git a/spec/rspec/parameterized/core/composite_parser_spec.rb b/spec/rspec/parameterized/core/composite_parser_spec.rb new file mode 100644 index 0000000..6a3087e --- /dev/null +++ b/spec/rspec/parameterized/core/composite_parser_spec.rb @@ -0,0 +1,45 @@ +describe RSpec::Parameterized::Core::CompositeParser do + describe ".to_raw_source" do + subject { RSpec::Parameterized::Core::CompositeParser.to_raw_source(arg) } + + context "arg is not proc" do + let(:arg) do + 123 + end + + it { should eq "123" } + end + + context "arg is proc" do + context "simple case" do + let(:arg) do + ->(a) { a + 1 } + end + + it { should eq "->(a) { a + 1 }" } + its(:encoding) { should eq Encoding::UTF_8 } + end + + context "arg is multibyte characters" do + let(:arg) do + ->(a) { a + "ほげほげ" } + end + + it { should eq '->(a) { a + "ほげほげ" }' } + its(:encoding) { should eq Encoding::UTF_8 } + end + + context "multiple lines" do + let(:arg) do + ->(a) { + a + + 1 + } + end + + it { should eq "->(a) {\n a +\n 1\n }" } + its(:encoding) { should eq Encoding::UTF_8 } + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1bbfbe7..f14aac6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,7 @@ require 'rspec-parameterized-core' +require 'rspec/its' + RSpec.configure do |config| config.treat_symbols_as_metadata_keys_with_true_values = true config.run_all_when_everything_filtered = true