class RubyNext::Commands::CoreExt

Attributes

filter[R]
list[R]
list?[R]
min_version[R]
names[R]
original_command[R]
out_path[R]

Public Instance Methods

parse!(args) click to toggle source
# File lib/ruby-next/commands/core_ext.rb, line 24
def parse!(args)
  print_help = false
  @min_version = MIN_SUPPORTED_VERSION
  @original_command = "ruby-next core_ext #{args.join(" ")}"
  @names = []
  @list = false
  @out_path = File.join(Dir.pwd, "core_ext.rb")

  optparser = base_parser do |opts|
    opts.banner = "Usage: ruby-next core_ext [options]"

    opts.on("-o", "--output=OUTPUT", "Specify output file or stdout (default: ./core_ext.rb)") do |val|
      @out_path = val
    end

    opts.on("-l", "--list", "List all available extensions") do
      @list = true
    end

    opts.on("--min-version=VERSION", "Specify the minimum Ruby version to support") do |val|
      @min_version = Gem::Version.new(val)
    end

    opts.on("-n", "--name=NAME", "Filter extensions by name") do |val|
      names << val
    end

    opts.on("-h", "--help", "Print help") do
      print_help = true
    end
  end

  optparser.parse!(args)

  if print_help
    $stdout.puts optparser.help
    exit 0
  end

  @filter = /(#{names.join("|")})/i unless names.empty?
end
run() click to toggle source
# File lib/ruby-next/commands/core_ext.rb, line 14
def run
  log "Select core extensions for Ruby v#{min_version}" \
      "#{filter ? " and matching #{filter.inspect}" : ""}"

  matching_patches.then do |patches|
    next print_list(patches) if list?
    generate_core_ext(patches)
  end
end

Private Instance Methods

generate_core_ext(patches) click to toggle source
# File lib/ruby-next/commands/core_ext.rb, line 88
      def generate_core_ext(patches)
        grouped_patches = patches.group_by(&:mod).sort_by { |(mod, patch)| mod.singleton_class? ? mod.inspect : mod.name }

        buffer = []

        buffer << "# frozen_string_literal: true\n"

        buffer << generation_meta

        grouped_patches.each do |mod, patches|
          singleton = mod.singleton_class?
          extend_name = singleton ? patches.first.singleton.name : mod.name
          prepend_name = singleton ? "#{patches.first.singleton.name}.singleton_class" : mod.name

          prepended, extended = patches.partition(&:prepend?)

          prepended.map do |patch|
            name = "RubyNext::Core::#{patch.name}"

            buffer << <<~RUBY
              module #{name}
              #{indent_and_trim(patch.body)}
              end

              #{prepend_name}.prepend #{name}
            RUBY

            name
          end

          class_or_module = mod.is_a?(Class) ? "class" : "module"

          buffer << "#{class_or_module} #{extend_name}"

          buffer << "  class << self" if singleton

          indent_size = singleton ? 4 : 2

          buffer << extended.map do |patch|
            indent_and_trim(patch.body, indent_size)
          end.join("\n\n")

          buffer << "  end" if singleton

          buffer << "end\n"
        end

        contents = buffer.join("\n")

        return $stdout.puts(contents) if out_path == "stdout"

        unless CLI.dry_run?
          FileUtils.mkdir_p File.dirname(out_path)
          File.write(out_path, contents)
        end

        log "Generated: #{out_path}"
      end
generation_meta() click to toggle source
# File lib/ruby-next/commands/core_ext.rb, line 147
      def generation_meta
        <<~MSG
          # Generated by Ruby Next v#{RubyNext::VERSION} using the following command:
          #
          #   #{original_command}
          #
        MSG
      end
indent_and_trim(src, size = 2) click to toggle source
# File lib/ruby-next/commands/core_ext.rb, line 156
def indent_and_trim(src, size = 2)
  new_src = src.dup
  # indent code using <size> spaces
  new_src.gsub!(/^/, " " * size)
  # remove empty lines
  new_src.gsub!(/^\s+$/, "")
  # remove traling blank lines
  new_src.delete_suffix!("\n")
  new_src
end
matching_patches() click to toggle source
# File lib/ruby-next/commands/core_ext.rb, line 68
def matching_patches
  RubyNext::Core.patches.extensions
    .values
    .flatten
    .select do |patch|
    next if min_version && Gem::Version.new(patch.version) <= min_version
    next if filter && !filter.match?(patch.name)
    true
  end
end
print_list(patches) click to toggle source