diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66ef1de..35965b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,16 @@ on: branches: [main, master] jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '4.0' + bundler-cache: true + - run: bundle exec rubocop + test: runs-on: ubuntu-latest strategy: diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..502968b --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,5 @@ +inherit_gem: + rubocop-shopify: rubocop.yml + +AllCops: + SuggestExtensions: false diff --git a/Gemfile b/Gemfile index cb5d9a7..0e2a8f0 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,12 @@ +# frozen_string_literal: true + source "https://rubygems.org" -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } +git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } # Specify your gem's dependencies in ast_transform.gemspec gemspec + +# Transitive dependency of rubocop: parallel >= 2 requires Ruby >= 3.3, but we +# still support 3.2. Drop this pin when our Ruby floor moves to 3.3. +gem "parallel", "< 2", require: false diff --git a/Gemfile.lock b/Gemfile.lock index e4723d3..d307024 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,6 +16,9 @@ GEM diff-lcs (1.6.2) docile (1.4.1) io-console (0.8.2) + json (2.21.1) + language_server-protocol (3.17.0.6) + lint_roller (1.1.0) method_source (1.1.0) minitest (5.27.0) minitest-reporters (1.7.1) @@ -23,6 +26,7 @@ GEM builder minitest (>= 5.0) ruby-progressbar + parallel (1.28.0) parser (3.3.10.2) ast (~> 2.4.1) racc @@ -32,9 +36,27 @@ GEM method_source (~> 1.0) reline (>= 0.6.0) racc (1.8.1) + rainbow (3.1.1) rake (13.3.1) + regexp_parser (2.12.0) reline (0.6.3) io-console (~> 0.5) + rubocop (1.88.2) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (>= 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.49.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.50.0) + parser (>= 3.3.7.2) + prism (~> 1.7) + rubocop-shopify (2.18.0) + rubocop (~> 1.62) ruby-progressbar (1.13.0) simplecov (0.22.0) docile (~> 1.1) @@ -42,6 +64,9 @@ GEM simplecov_json_formatter (~> 0.1) simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) unparser (0.8.1) diff-lcs (~> 1.6) parser (>= 3.3.0) @@ -56,8 +81,10 @@ DEPENDENCIES bundler (>= 2.1) minitest (~> 5.14) minitest-reporters (~> 1.4) + parallel (< 2) pry (>= 0.14) rake (~> 13.0) + rubocop-shopify (~> 2.18) simplecov (~> 0.22) BUNDLED WITH diff --git a/Rakefile b/Rakefile index 415d4bf..53c16a0 100644 --- a/Rakefile +++ b/Rakefile @@ -1,16 +1,17 @@ # frozen_string_literal: true -require 'bundler/gem_tasks' -require 'rake/testtask' + +require "bundler/gem_tasks" +require "rake/testtask" Rake::TestTask.new(:test) do |t| # Ensure we load test_loader first for ASTTransform.install - filepath = File.expand_path('test/test_loader.rb', __dir__) + filepath = File.expand_path("test/test_loader.rb", __dir__) t.ruby_opts << "-r #{filepath}" t.warning = false - t.libs << 'test' - t.libs << 'lib' - t.test_files = FileList['test/**/*_test.rb'] + t.libs << "test" + t.libs << "lib" + t.test_files = FileList["test/**/*_test.rb"] end -task :default => :test +task default: :test diff --git a/ast_transform.gemspec b/ast_transform.gemspec index e93e0c3..dd6d03e 100644 --- a/ast_transform.gemspec +++ b/ast_transform.gemspec @@ -1,3 +1,4 @@ +# frozen_string_literal: true lib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) @@ -9,28 +10,30 @@ Gem::Specification.new do |spec| spec.authors = ["Jean-Philippe Duchesne"] spec.email = ["jpduchesne89@gmail.com"] - spec.summary = 'An AST transformation framework.' + spec.summary = "An AST transformation framework." spec.description = spec.summary spec.homepage = "https://github.com/rspockframework/ast-transform" spec.license = "MIT" - spec.files = `git ls-files -z`.split("\x0").reject do |f| + spec.files = %x(git ls-files -z).split("\x0").reject do |f| f.match(%r{^(test|spec|features)/}) end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.required_ruby_version = '>= 3.2' + spec.required_ruby_version = ">= 3.2" # Development dependencies - spec.add_development_dependency "bundler", ">= 2.1" - spec.add_development_dependency "rake", "~> 13.0" - spec.add_development_dependency "minitest", "~> 5.14" - spec.add_development_dependency "minitest-reporters", "~> 1.4" - spec.add_development_dependency "pry", ">= 0.14" - spec.add_development_dependency "simplecov", "~> 0.22" + spec.add_development_dependency("bundler", ">= 2.1") + spec.add_development_dependency("minitest", "~> 5.14") + spec.add_development_dependency("minitest-reporters", "~> 1.4") + spec.add_development_dependency("pry", ">= 0.14") + spec.add_development_dependency("rake", "~> 13.0") + # rubocop-shopify >= 3.0 requires Ruby >= 3.3; bump alongside our own floor. + spec.add_development_dependency("rubocop-shopify", "~> 2.18") + spec.add_development_dependency("simplecov", "~> 0.22") # Runtime dependencies - spec.add_runtime_dependency "parser", ">= 3.0" - spec.add_runtime_dependency "prism", ">= 1.5" - spec.add_runtime_dependency "unparser", ">= 0.6" + spec.add_runtime_dependency("parser", ">= 3.0") + spec.add_runtime_dependency("prism", ">= 1.5") + spec.add_runtime_dependency("unparser", ">= 0.6") end diff --git a/bin/console b/bin/console index 7aa6b78..10e617c 100755 --- a/bin/console +++ b/bin/console @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true require "bundler/setup" require "ast_transform" diff --git a/dev.yml b/dev.yml new file mode 100644 index 0000000..96a944d --- /dev/null +++ b/dev.yml @@ -0,0 +1,11 @@ +name: ast_transform +commands: + up: + desc: Install gems + run: bundle install + test: + desc: Run this repo's tests + run: bundle exec rake test + style: + desc: Run RuboCop + run: bundle exec rubocop diff --git a/lib/ast_transform.rb b/lib/ast_transform.rb index 9a4e1aa..e07cf69 100644 --- a/lib/ast_transform.rb +++ b/lib/ast_transform.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true require "ast_transform/version" -require 'ast_transform/instruction_sequence' -require 'ast_transform/instruction_sequence/mixin' -require 'ast_transform/instruction_sequence/bootsnap_mixin' +require "ast_transform/instruction_sequence" +require "ast_transform/instruction_sequence/mixin" +require "ast_transform/instruction_sequence/bootsnap_mixin" module ASTTransform DEFAULT_OUTPUT_PATH = Pathname.new("").join("tmp", "ast_transform").to_s @@ -19,22 +19,18 @@ def acronym(acronym) end def install - @installed ||= begin - if defined?(Bootsnap) && ASTTransform::InstructionSequence.using_bootsnap_compilation? - class << Bootsnap::CompileCache::ISeq - prepend ::ASTTransform::InstructionSequence::BootsnapMixin - end - else - class << RubyVM::InstructionSequence - prepend ::ASTTransform::InstructionSequence::Mixin - end + @installed ||= if defined?(Bootsnap) && ASTTransform::InstructionSequence.using_bootsnap_compilation? + class << Bootsnap::CompileCache::ISeq + prepend ::ASTTransform::InstructionSequence::BootsnapMixin + end + else + class << RubyVM::InstructionSequence + prepend ::ASTTransform::InstructionSequence::Mixin end end end - def output_path=(path) - @output_path = path - end + attr_writer :output_path def output_path @output_path || DEFAULT_OUTPUT_PATH diff --git a/lib/ast_transform/abstract_transformation.rb b/lib/ast_transform/abstract_transformation.rb index f2e06db..782cfe9 100644 --- a/lib/ast_transform/abstract_transformation.rb +++ b/lib/ast_transform/abstract_transformation.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true -require 'ast_transform/transformation_helper' + +require "ast_transform/transformation_helper" module ASTTransform class AbstractTransformation < Parser::AST::Processor diff --git a/lib/ast_transform/instruction_sequence.rb b/lib/ast_transform/instruction_sequence.rb index 795d2b3..a4dcdf7 100644 --- a/lib/ast_transform/instruction_sequence.rb +++ b/lib/ast_transform/instruction_sequence.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + module ASTTransform module InstructionSequence class << self @@ -22,12 +23,12 @@ def source_to_transformed_iseq(source, source_path) def write_pathname(file_path) project_path = File.expand_path("") relative_source_file_pathname = Pathname.new(file_path).relative_path_from(Pathname.new(project_path)) - Pathname.new("").join(project_path, 'tmp', 'ast_transform', relative_source_file_pathname) + Pathname.new("").join(project_path, "tmp", "ast_transform", relative_source_file_pathname) end def write(string, pathname) FileUtils.mkdir_p(pathname.dirname) - File.open(pathname, 'w') do |file| + File.open(pathname, "w") do |file| file.write(string) end end diff --git a/lib/ast_transform/instruction_sequence/bootsnap_mixin.rb b/lib/ast_transform/instruction_sequence/bootsnap_mixin.rb index 105be30..ece746c 100644 --- a/lib/ast_transform/instruction_sequence/bootsnap_mixin.rb +++ b/lib/ast_transform/instruction_sequence/bootsnap_mixin.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require 'ast_transform/transformer' -require 'ast_transform/instruction_sequence/mixin_utils' -require 'pathname' +require "ast_transform/transformer" +require "ast_transform/instruction_sequence/mixin_utils" +require "pathname" module ASTTransform module InstructionSequence @@ -14,7 +14,7 @@ def input_to_storage(source, source_path) iseq = ASTTransform::InstructionSequence.source_to_transformed_iseq(source, source_path) iseq.to_binary rescue SyntaxError - raise ::Bootsnap::CompileCache::Uncompilable, 'syntax error' + raise ::Bootsnap::CompileCache::Uncompilable, "syntax error" end end end diff --git a/lib/ast_transform/instruction_sequence/mixin.rb b/lib/ast_transform/instruction_sequence/mixin.rb index a25d839..2d80b4e 100644 --- a/lib/ast_transform/instruction_sequence/mixin.rb +++ b/lib/ast_transform/instruction_sequence/mixin.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true -require 'pathname' -require 'ast_transform/transformer' -require 'ast_transform/transformation' -require 'ast_transform/instruction_sequence/mixin_utils' + +require "pathname" +require "ast_transform/transformer" +require "ast_transform/transformation" +require "ast_transform/instruction_sequence/mixin_utils" module ASTTransform module InstructionSequence @@ -15,7 +16,7 @@ def load_iseq(source_path) # via magic comments, so we never need to set it ourselves. source = File.binread(source_path) - return ASTTransform::MixinUtils.try_super(self, :load_iseq, source_path) unless source.include?('transform!'.b) + return ASTTransform::MixinUtils.try_super(self, :load_iseq, source_path) unless source.include?("transform!".b) ASTTransform::InstructionSequence.source_to_transformed_iseq(source, source_path) end diff --git a/lib/ast_transform/instruction_sequence/mixin_utils.rb b/lib/ast_transform/instruction_sequence/mixin_utils.rb index eed2694..b37c772 100644 --- a/lib/ast_transform/instruction_sequence/mixin_utils.rb +++ b/lib/ast_transform/instruction_sequence/mixin_utils.rb @@ -5,7 +5,7 @@ module MixinUtils class << self def try_super(target, method_sym, *args, &block) super_method = target.method(method_sym).super_method - super_method ? super_method.call(*args, &block) : nil + super_method&.call(*args, &block) end end end diff --git a/lib/ast_transform/kwargs_builder.rb b/lib/ast_transform/kwargs_builder.rb index db5abb5..cc6443a 100644 --- a/lib/ast_transform/kwargs_builder.rb +++ b/lib/ast_transform/kwargs_builder.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'prism/translation/parser' +require "prism/translation/parser" module ASTTransform # Extends the default Prism parser builder to distinguish keyword arguments diff --git a/lib/ast_transform/source_map.rb b/lib/ast_transform/source_map.rb index 4833ad7..cdc67b8 100644 --- a/lib/ast_transform/source_map.rb +++ b/lib/ast_transform/source_map.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true -require 'parser' + +require "parser" module ASTTransform class SourceMap @@ -28,7 +29,9 @@ def for_file_path(file_path) private def source_maps - @@source_maps ||= {} + # Class instance var (not @@): read/written only through this method + # inside class << self, and SourceMap has no subclasses to share with. + @source_maps ||= {} end end @@ -97,7 +100,7 @@ def extract_source_map_data(node, indexes) # # @return [Hash] A Hash containing line numbers from executed code to source code. def build_source_map - (1..line_count).each.with_object({}) {|it, hash| hash[it] = source_line(it) } + (1..line_count).each.with_object({}) { |it, hash| hash[it] = source_line(it) } end # Retrieves the source line for the given +line_number+ in the executed code. @@ -226,6 +229,7 @@ def dig_last_valid_node(node, indexes) def dig_node(node, indexes) indexes.inject(node) do |node, index| return nil unless node.is_a?(Parser::AST::Node) + node.children[index] end end diff --git a/lib/ast_transform/transformation.rb b/lib/ast_transform/transformation.rb index 17c891f..1fe4caa 100644 --- a/lib/ast_transform/transformation.rb +++ b/lib/ast_transform/transformation.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true -require 'ast_transform' -require 'ast_transform/abstract_transformation' -require 'ast_transform/transformer' -require 'unparser' + +require "ast_transform" +require "ast_transform/abstract_transformation" +require "ast_transform/transformer" +require "unparser" module ASTTransform class Transformation < ASTTransform::AbstractTransformation @@ -18,9 +19,9 @@ def process_node(node) count_before_reject = children.size - children.reject!.with_index { |child_node, index| + children.reject!.with_index do |child_node, index| transform_node?(child_node) && transformable_node?(next_child(node, index)) - } + end processed = process_all(children) @@ -68,19 +69,14 @@ def extract_transformations(node) def extract_transformation(node) return unless node.is_a?(Parser::AST::Node) - return unless node.children.count >= 2 - - if node.children[1] == :new - require_transformation(node) - code = Unparser.unparse(node) + return if node.children.count < 2 - TOPLEVEL_BINDING.eval(code) - else - require_transformation(node) - code = "#{Unparser.unparse(node)}.new" + require_transformation(node) + code = Unparser.unparse(node) + # A bare constant reference is instantiated; an explicit .new is kept as written. + code = "#{code}.new" unless node.children[1] == :new - TOPLEVEL_BINDING.eval(code) - end + TOPLEVEL_BINDING.eval(code) end def require_transformation(node) @@ -100,11 +96,12 @@ def require_path(const_name) acronyms = ASTTransform.acronyms acronym_regex = acronyms.empty? ? /(?=a)b/ : /#{acronyms.join("|")}/ return const_name unless /[A-Z-]|::/.match?(const_name) - word = const_name.to_s.gsub("::".freeze, "/".freeze) - word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" } - word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze) - word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze) - word.tr!("-".freeze, "_".freeze) + + word = const_name.to_s.gsub("::", "/") + word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{acronym_regex})(?=\b|[^a-z])/) { "#{::Regexp.last_match(1) && "_"}#{::Regexp.last_match(2).downcase}" } + word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2') + word.gsub!(/([a-z\d])([A-Z])/, '\1_\2') + word.tr!("-", "_") word.downcase! word end diff --git a/lib/ast_transform/transformation_helper.rb b/lib/ast_transform/transformation_helper.rb index 40b2a47..df0f20f 100644 --- a/lib/ast_transform/transformation_helper.rb +++ b/lib/ast_transform/transformation_helper.rb @@ -1,11 +1,14 @@ # frozen_string_literal: true -require 'parser' + +require "parser" module ASTTransform module TransformationHelper - def self.included(base) - base.extend(Methods) - base.include(Methods) + class << self + def included(base) + base.extend(Methods) + base.include(Methods) + end end module Methods diff --git a/lib/ast_transform/transformer.rb b/lib/ast_transform/transformer.rb index 14ea5c7..99508e1 100644 --- a/lib/ast_transform/transformer.rb +++ b/lib/ast_transform/transformer.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true -require 'prism' -require 'prism/translation/parser' -require 'unparser' -require 'ast_transform/kwargs_builder' -require 'ast_transform/source_map' + +require "prism" +require "prism/translation/parser" +require "unparser" +require "ast_transform/kwargs_builder" +require "ast_transform/source_map" module ASTTransform class Transformer @@ -20,7 +21,7 @@ def initialize(*transformations) # @param file_path [String] The file_path. This is important for source mapping in backtraces. # # @return [Parser::AST::Node] The AST. - def build_ast(source, file_path: 'tmp') + def build_ast(source, file_path: "tmp") buffer = create_buffer(source, file_path) parser.parse(buffer) end diff --git a/lib/ast_transform/version.rb b/lib/ast_transform/version.rb index ffb0fcb..b2aa222 100644 --- a/lib/ast_transform/version.rb +++ b/lib/ast_transform/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ASTTransform VERSION = "2.1.4" end diff --git a/test/ast_transform/abstract_transformation_test.rb b/test/ast_transform/abstract_transformation_test.rb index fc751cb..b432e80 100644 --- a/test/ast_transform/abstract_transformation_test.rb +++ b/test/ast_transform/abstract_transformation_test.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true -require 'test_helper' -require 'transformation_helper' -require 'ast_transform/abstract_transformation' + +require "test_helper" +require "transformation_helper" +require "ast_transform/abstract_transformation" module ASTTransform class AbstractTransformationTest < Minitest::Test diff --git a/test/ast_transform/instruction_sequence/mixin_utils_test.rb b/test/ast_transform/instruction_sequence/mixin_utils_test.rb index b18e026..4564cfd 100644 --- a/test/ast_transform/instruction_sequence/mixin_utils_test.rb +++ b/test/ast_transform/instruction_sequence/mixin_utils_test.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true -require 'test_helper' -require 'ast_transform/instruction_sequence/mixin_utils' + +require "test_helper" +require "ast_transform/instruction_sequence/mixin_utils" module ASTTransform class MixinUtilsTest < Minitest::Test @@ -8,24 +9,24 @@ class MixinUtilsTest < Minitest::Test class Base def foo - 'Base#foo' + "Base#foo" end end class FooBar < Base def foo - 'FooBar#foo' + "FooBar#foo" end def bar - 'FooBar#bar' + "FooBar#bar" end end test "#try_super when super method is defined" do foo_bar = FooBar.new - assert_equal 'Base#foo', ASTTransform::MixinUtils.try_super(foo_bar, :foo) + assert_equal "Base#foo", ASTTransform::MixinUtils.try_super(foo_bar, :foo) end test "#try_super when super method is not defined" do diff --git a/test/ast_transform/instruction_sequence_test.rb b/test/ast_transform/instruction_sequence_test.rb index b7a927e..47a92b5 100644 --- a/test/ast_transform/instruction_sequence_test.rb +++ b/test/ast_transform/instruction_sequence_test.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require 'test_helper' -require 'tempfile' -require 'ast_transform/instruction_sequence' -require 'ast_transform/instruction_sequence/mixin' -require 'ast_transform/transformation' +require "test_helper" +require "tempfile" +require "ast_transform/instruction_sequence" +require "ast_transform/instruction_sequence/mixin" +require "ast_transform/transformation" module ASTTransform class InstructionSequenceTest < Minitest::Test @@ -24,12 +24,13 @@ def setup iseq = ASTTransform::InstructionSequence.source_to_transformed_iseq(source, source_path) - assert_equal source_path, iseq.path, + assert_equal source_path, + iseq.path, "ISeq should report the original source path, not the tmp/ rewritten path" end test "source_to_transformed_iseq handles binary-encoded source" do - source = "# café résumé\n1 + 2\n".dup.force_encoding('ASCII-8BIT') + source = "# café résumé\n1 + 2\n".dup.force_encoding("ASCII-8BIT") source_path = File.expand_path("test/fixtures/encoding_test.rb") iseq = ASTTransform::InstructionSequence.source_to_transformed_iseq(source, source_path) @@ -38,26 +39,26 @@ def setup end test "load_iseq skips non-ASCII files without transform!" do - tmpfile = Tempfile.new(['non_ascii', '.rb']) + tmpfile = Tempfile.new(["non_ascii", ".rb"]) tmpfile.write("# café résumé\nx = 1\n") tmpfile.close iseq = @loader.load_iseq(tmpfile.path) - assert_nil iseq + assert_nil(iseq) ensure tmpfile&.unlink end test "load_iseq processes non-ASCII files with transform! without encoding errors" do - tmpfile = Tempfile.new(['non_ascii_transform', '.rb']) + tmpfile = Tempfile.new(["non_ascii_transform", ".rb"]) tmpfile.write("# café résumé\nx = 1\n# transform!\n") tmpfile.close iseq = @loader.load_iseq(tmpfile.path) - assert_instance_of RubyVM::InstructionSequence, iseq - assert_equal tmpfile.path, iseq.path + assert_instance_of(RubyVM::InstructionSequence, iseq) + assert_equal(tmpfile.path, iseq.path) ensure tmpfile&.unlink end diff --git a/test/ast_transform/kwargs_builder_test.rb b/test/ast_transform/kwargs_builder_test.rb index 2adac97..e355613 100644 --- a/test/ast_transform/kwargs_builder_test.rb +++ b/test/ast_transform/kwargs_builder_test.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'test_helper' -require 'ast_transform/kwargs_builder' +require "test_helper" +require "ast_transform/kwargs_builder" module ASTTransform class KwargsBuilderTest < Minitest::Test @@ -13,7 +13,7 @@ def setup end test "#associate emits :kwargs for bare keyword arguments" do - ast = parse('foo(bar: 1, baz: 2)') + ast = parse("foo(bar: 1, baz: 2)") node = find_node(ast, :kwargs) refute_nil node, "expected a :kwargs node for bare keyword arguments" @@ -21,7 +21,7 @@ def setup end test "#associate emits :hash for explicit hash with braces" do - ast = parse('foo({ bar: 1, baz: 2 })') + ast = parse("foo({ bar: 1, baz: 2 })") hash_node = find_node(ast, :hash) refute_nil hash_node, "expected a :hash node for explicit hash" @@ -38,7 +38,7 @@ def setup end test "#associate emits :hash for standalone hash literals" do - ast = parse('x = { a: 1, b: 2 }') + ast = parse("x = { a: 1, b: 2 }") hash_node = find_node(ast, :hash) refute_nil hash_node, "expected a :hash node for hash literal" @@ -47,7 +47,7 @@ def setup end test "#associate preserves keyword argument pairs" do - ast = parse('foo(bar: 1, baz: 2)') + ast = parse("foo(bar: 1, baz: 2)") node = find_node(ast, :kwargs) assert_equal 2, node.children.length @@ -59,7 +59,7 @@ def setup end test "#associate emits :kwargs for keyword arguments in constructor calls" do - ast = parse('Foo.new(bar: 1, baz: 2)') + ast = parse("Foo.new(bar: 1, baz: 2)") node = find_node(ast, :kwargs) refute_nil node, "expected a :kwargs node in constructor call" @@ -67,7 +67,7 @@ def setup end test "#associate emits :kwargs for double-splat keyword arguments" do - ast = parse('foo(**opts)') + ast = parse("foo(**opts)") node = find_node(ast, :kwargs) refute_nil node, "expected a :kwargs node for double-splat" @@ -76,14 +76,14 @@ def setup private def parse(source) - buffer = Parser::Source::Buffer.new('test') + buffer = Parser::Source::Buffer.new("test") buffer.source = source @parser.parse(buffer) end def find_node(ast, type) return ast if ast.type == type - return nil unless ast.respond_to?(:children) + return unless ast.respond_to?(:children) ast.children.each do |child| next unless child.is_a?(Parser::AST::Node) diff --git a/test/ast_transform/source_map_test.rb b/test/ast_transform/source_map_test.rb index 2375896..ae7ba57 100644 --- a/test/ast_transform/source_map_test.rb +++ b/test/ast_transform/source_map_test.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true -require 'test_helper' -require 'transformation_helper' -require 'ast_transform/abstract_transformation' -require 'ast_transform/transformer' + +require "test_helper" +require "transformation_helper" +require "ast_transform/abstract_transformation" +require "ast_transform/transformer" module ASTTransform class SourceMapTest < Minitest::Test @@ -22,11 +23,11 @@ def run(node) method_call HEREDOC - actual_transformed_source = transformer.transform_file_source(source, 'src', 'transformed') + actual_transformed_source = transformer.transform_file_source(source, "src", "transformed") assert_equal "method_call + 1", actual_transformed_source - source_map = ASTTransform::SourceMap.for_file_path('transformed') + source_map = ASTTransform::SourceMap.for_file_path("transformed") assert_equal 1, source_map.line(1) end @@ -44,12 +45,12 @@ def run(node) method_call HEREDOC - actual_transformed_source = transformer.transform_file_source(source, 'src', 'transformed') + actual_transformed_source = transformer.transform_file_source(source, "src", "transformed") assert_equal "method_call + 1", actual_transformed_source - transformer.transform_file_source(source, 'src', 'transformed') - source_map = ASTTransform::SourceMap.for_file_path('transformed') + transformer.transform_file_source(source, "src", "transformed") + source_map = ASTTransform::SourceMap.for_file_path("transformed") assert_equal 1, source_map.line(1) end @@ -68,11 +69,11 @@ def run(node) method_call2 HEREDOC - actual_transformed_source = transformer.transform_file_source(source, 'src', 'transformed') + actual_transformed_source = transformer.transform_file_source(source, "src", "transformed") assert_equal "method_call1 + method_call2", actual_transformed_source - source_map = ASTTransform::SourceMap.for_file_path('transformed') + source_map = ASTTransform::SourceMap.for_file_path("transformed") assert_equal 1, source_map.line(1) end @@ -95,11 +96,11 @@ def run(node) method_call2 HEREDOC - actual_transformed_source = transformer.transform_file_source(source, 'src', 'transformed') + actual_transformed_source = transformer.transform_file_source(source, "src", "transformed") assert_equal expected_transformed_source, actual_transformed_source - source_map = ASTTransform::SourceMap.for_file_path('transformed') + source_map = ASTTransform::SourceMap.for_file_path("transformed") assert_equal 1, source_map.line(1) assert_equal 1, source_map.line(2) @@ -112,11 +113,11 @@ def run(node) method_call HEREDOC - transformer.transform_file_source(source, '/original/path.rb', '/transformed/path.rb') + transformer.transform_file_source(source, "/original/path.rb", "/transformed/path.rb") - source_map = ASTTransform::SourceMap.for_file_path('/original/path.rb') + source_map = ASTTransform::SourceMap.for_file_path("/original/path.rb") refute_nil source_map, "source map should be retrievable by source file path" - assert_equal '/original/path.rb', source_map.source_file_path + assert_equal "/original/path.rb", source_map.source_file_path end test "#line returns nil when transformation creates nodes that don't contain previous nodes" do @@ -132,11 +133,11 @@ def run(node) method_call1 + method_call2 HEREDOC - actual_transformed_source = transformer.transform_file_source(source, 'src', 'transformed') + actual_transformed_source = transformer.transform_file_source(source, "src", "transformed") assert_equal "1 + 2", actual_transformed_source - source_map = ASTTransform::SourceMap.for_file_path('transformed') + source_map = ASTTransform::SourceMap.for_file_path("transformed") assert_nil source_map.line(1) end diff --git a/test/ast_transform/transformation_test.rb b/test/ast_transform/transformation_test.rb index 451bd7b..43c4ba6 100644 --- a/test/ast_transform/transformation_test.rb +++ b/test/ast_transform/transformation_test.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true -require 'test_helper' -require 'transformation_helper' -require 'ast_transform/transformation' + +require "test_helper" +require "transformation_helper" +require "ast_transform/transformation" module ASTTransform class TransformationTest < Minitest::Test diff --git a/test/ast_transform/transformer_test.rb b/test/ast_transform/transformer_test.rb index 05e4b34..db76fc8 100644 --- a/test/ast_transform/transformer_test.rb +++ b/test/ast_transform/transformer_test.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true -require 'pathname' -require 'test_helper' -require 'transformation_helper' -require 'ast_transform/abstract_transformation' -require 'ast_transform/transformer' + +require "pathname" +require "test_helper" +require "transformation_helper" +require "ast_transform/abstract_transformation" +require "ast_transform/transformer" module ASTTransform class TransformerTest < Minitest::Test @@ -15,6 +16,7 @@ class MethodCallToFooTransformation < ASTTransform::AbstractTransformation def process_node(node) return unless node.type == :send + node.updated(:send, [nil, :foo]) end end @@ -24,6 +26,7 @@ class FooToBarTransformation < ASTTransform::AbstractTransformation def process_node(node) return unless node.type == :send && node.children == [nil, :foo] + node.updated(:send, [nil, :bar]) end end @@ -33,6 +36,7 @@ class BarToFooBarTransformation < ASTTransform::AbstractTransformation def process_node(node) return unless node.type == :send && node.children == [nil, :bar] + node.updated(:send, [nil, :foo_bar]) end end @@ -56,16 +60,16 @@ def setup end test "#build_ast_from_file returns the expected AST" do - pathname = Pathname.new('').join('tmp', 'test', 'ast_transform', 'transformer_test.rb') + pathname = Pathname.new("").join("tmp", "test", "ast_transform", "transformer_test.rb") FileUtils.mkdir_p(pathname.dirname) - File.open(pathname, 'w') do |file| + File.open(pathname, "w") do |file| file.write(@source) end ast = @transformer.build_ast_from_file(pathname.to_s) - assert_equal @source_ast, ast + assert_equal(@source_ast, ast) ensure File.delete(pathname.to_s) if File.exist?(pathname.to_s) end @@ -75,37 +79,37 @@ def setup end test "#transform with multiple transformations" do - assert_equal 'foo_bar', @multi_transformer.transform(@source) + assert_equal "foo_bar", @multi_transformer.transform(@source) end test "#transform_file returns the expected transformed code" do - pathname = Pathname.new('').join('tmp', 'test', 'ast_transform', 'transformer_test.rb') - transformed_pathname = Pathname.new('').join('tmp', 'test', 'ast_transform', 'transformed_transformer_test.rb') + pathname = Pathname.new("").join("tmp", "test", "ast_transform", "transformer_test.rb") + transformed_pathname = Pathname.new("").join("tmp", "test", "ast_transform", "transformed_transformer_test.rb") FileUtils.mkdir_p(pathname.dirname) - File.open(pathname, 'w') do |file| + File.open(pathname, "w") do |file| file.write(@source) end transformed_source = @multi_transformer.transform_file(pathname.to_s, transformed_pathname.to_s) - assert_equal 'foo_bar', transformed_source + assert_equal("foo_bar", transformed_source) ensure File.delete(pathname.to_s) if File.exist?(pathname.to_s) end test "#transform_file_source returns the expected transformed code" do - pathname = Pathname.new('').join('tmp', 'test', 'ast_transform', 'transformer_test.rb') - transformed_pathname = Pathname.new('').join('tmp', 'test', 'ast_transform', 'transformed_transformer_test.rb') + pathname = Pathname.new("").join("tmp", "test", "ast_transform", "transformer_test.rb") + transformed_pathname = Pathname.new("").join("tmp", "test", "ast_transform", "transformed_transformer_test.rb") FileUtils.mkdir_p(pathname.dirname) - File.open(pathname, 'w') do |file| + File.open(pathname, "w") do |file| file.write(@source) end transformed_source = @multi_transformer.transform_file_source(@source, pathname.to_s, transformed_pathname.to_s) - assert_equal 'foo_bar', transformed_source + assert_equal("foo_bar", transformed_source) ensure File.delete(pathname.to_s) if File.exist?(pathname.to_s) end diff --git a/test/ast_transform_test.rb b/test/ast_transform_test.rb index 98b5c9c..612e212 100644 --- a/test/ast_transform_test.rb +++ b/test/ast_transform_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "test_helper" class ASTTransformTest < Minitest::Test diff --git a/test/declarative.rb b/test/declarative.rb index 7e9bbd6..971a898 100644 --- a/test/declarative.rb +++ b/test/declarative.rb @@ -1,16 +1,18 @@ # frozen_string_literal: true + # TODO: Give Credits to Rails, borrowed from ActiveSupport::Testing::Declarative module ASTTransform module Declarative def test(name, &block) - test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym - defined = method_defined? test_name + test_name = "test_#{name.gsub(/\s+/, "_")}".to_sym + defined = method_defined?(test_name) raise "#{test_name} is already defined in #{self}" if defined + if block_given? define_method(test_name, &block) else define_method(test_name) do - flunk "No implementation provided for #{name}" + flunk("No implementation provided for #{name}") end end end diff --git a/test/minitest/reporters/rake_rerun_reporter.rb b/test/minitest/reporters/rake_rerun_reporter.rb index 896d25f..85a5c43 100644 --- a/test/minitest/reporters/rake_rerun_reporter.rb +++ b/test/minitest/reporters/rake_rerun_reporter.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'minitest/reporters' -require 'ast_transform/source_map' + +require "minitest/reporters" +require "ast_transform/source_map" module Minitest module Reporters class RakeRerunReporter < Minitest::Reporters::ProgressReporter - def initialize(options = {}) @rerun_user_prefix = options.fetch(:rerun_prefix, "") super @@ -26,7 +26,7 @@ def record(test) def print_rerun_command(test) message = rerun_message_for(test) - unless message.nil? || message.strip == '' + unless message.nil? || message.strip == "" puts puts yellow(message) end @@ -39,15 +39,16 @@ def rerun_message_for(test) end def location(exception) - last_before_assertion = '' + last_before_assertion = "" exception.backtrace.reverse_each do |ss| break if ss =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/ + last_before_assertion = ss break if ss =~ /_test.rb\:/ end - last_before_assertion.sub(/:in .*$/, '') + last_before_assertion.sub(/:in .*$/, "") end end end diff --git a/test/string_helper.rb b/test/string_helper.rb index a1ca0dc..cfbec1d 100644 --- a/test/string_helper.rb +++ b/test/string_helper.rb @@ -4,7 +4,7 @@ module ASTTransform module Helpers module StringHelper def strip_end_line(str) - str.gsub(/\n$/, '') + str.gsub(/\n$/, "") end end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 5d1c532..2df118c 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,4 +1,6 @@ -$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) +# frozen_string_literal: true + +$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__)) require "ast_transform" require "declarative" @@ -6,7 +8,7 @@ # Minitest begin require "rubygems" - gem "minitest" + gem("minitest") rescue Gem::LoadError # do nothing end @@ -21,4 +23,4 @@ require "minitest/reporters/rake_rerun_reporter" Minitest::Reporters.use!([Minitest::Reporters::RakeRerunReporter.new]) -Minitest.autorun \ No newline at end of file +Minitest.autorun diff --git a/test/test_loader.rb b/test/test_loader.rb index 9f8539d..a045b1e 100644 --- a/test/test_loader.rb +++ b/test/test_loader.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true -require 'simplecov' -require 'simplecov_json_formatter' + +require "simplecov" +require "simplecov_json_formatter" SimpleCov.start do - add_filter('/test/') + add_filter("/test/") formatter SimpleCov::Formatter::MultiFormatter.new([ SimpleCov::Formatter::HTMLFormatter, SimpleCov::Formatter::JSONFormatter, @@ -15,6 +16,6 @@ # Pry # NOTE: Must be loaded before ASTTransform.install, otherwise we get a bunch of require_relative errors -require 'pry' +require "pry" ASTTransform.install diff --git a/test/transformation_helper.rb b/test/transformation_helper.rb index 46ffd23..6af5e70 100644 --- a/test/transformation_helper.rb +++ b/test/transformation_helper.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true -require 'ast_transform/transformer' -require 'ast_transform/transformation_helper' -require 'string_helper' + +require "ast_transform/transformer" +require "ast_transform/transformation_helper" +require "string_helper" module ASTTransform module Helpers