lib/yard/cli/diff.rb in yard-0.9.16 vs lib/yard/cli/diff.rb in yard-0.9.17
- old
+ new
@@ -1,270 +1,270 @@
-# frozen_string_literal: true
-require 'tmpdir'
-require 'fileutils'
-require 'open-uri'
-
-module YARD
- module CLI
- # CLI command to return the objects that were added/removed from 2 versions
- # of a project (library, gem, working copy).
- # @since 0.6.0
- class Diff < Command
- def initialize
- super
- @list_all = false
- @use_git = false
- @compact = false
- @modified = true
- @verifier = Verifier.new
- @old_git_commit = nil
- @old_path = Dir.pwd
- log.show_backtraces = true
- end
-
- def description
- 'Returns the object diff of two gems or .yardoc files'
- end
-
- def run(*args)
- registry = optparse(*args).map do |gemfile|
- if @use_git
- load_git_commit(gemfile)
- all_objects
- elsif load_gem_data(gemfile)
- log.info "Found #{gemfile}"
- all_objects
- else
- log.error "Cannot find gem #{gemfile}"
- nil
- end
- end.compact
-
- return if registry.size != 2
-
- first_object = nil
- [["Added objects", "A", added_objects(*registry)],
- ["Modified objects", "M", modified_objects(*registry)],
- ["Removed objects", "D", removed_objects(*registry)]].each do |name, short, objects|
- next if short == "M" && @modified == false
- next if objects.empty?
- last_object = nil
- all_objects_notice = false
- log.puts name + ":" unless @compact
- objects.sort_by(&:path).each do |object|
- if !@list_all && last_object && object.parent == last_object
- log.print " (...)" unless all_objects_notice
- all_objects_notice = true
- next
- elsif @compact
- log.puts if first_object
- else
- log.puts
- end
- all_objects_notice = false
- log.print "" + (@compact ? "#{short} " : " ") +
- object.path + " (#{object.file}:#{object.line})"
- last_object = object
- first_object = true
- end
- unless @compact
- log.puts; log.puts
- end
- end
- log.puts if @compact
- end
-
- private
-
- def all_objects
- return Registry.all if @verifier.expressions.empty?
- @verifier.run(Registry.all)
- end
-
- def added_objects(registry1, registry2)
- registry2.reject {|o| registry1.find {|o2| o2.path == o.path } }
- end
-
- def modified_objects(registry1, registry2)
- registry1.select do |obj|
- case obj
- when CodeObjects::MethodObject
- registry2.find {|o| obj == o && o.source != obj.source }
- when CodeObjects::ConstantObject
- registry2.find {|o| obj == o && o.value != obj.value }
- end
- end.compact
- end
-
- def removed_objects(registry1, registry2)
- registry1.reject {|o| registry2.find {|o2| o2.path == o.path } }
- end
-
- def load_git_commit(commit)
- Registry.clear
- commit_path = 'git_commit' + commit.gsub(/\W/, '_')
- tmpdir = File.join(Dir.tmpdir, commit_path)
- log.info "Expanding #{commit} to #{tmpdir}..."
- Dir.chdir(@old_path)
- FileUtils.mkdir_p(tmpdir)
- FileUtils.cp_r('.', tmpdir)
- Dir.chdir(tmpdir)
- log.info("git says: " + `git reset --hard #{commit}`.chomp)
- generate_yardoc(tmpdir)
- ensure
- Dir.chdir(@old_path)
- cleanup(commit_path)
- end
-
- def load_gem_data(gemfile)
- require_rubygems
- Registry.clear
-
- # First check for argument as .yardoc file
- [File.join(gemfile, '.yardoc'), gemfile].each do |yardoc|
- log.info "Searching for .yardoc db at #{yardoc}"
- next unless File.directory?(yardoc)
- Registry.load_yardoc(yardoc)
- Registry.load_all
- return true
- end
-
- # Next check installed RubyGems
- gemfile_without_ext = gemfile.sub(/\.gem$/, '')
- log.info "Searching for installed gem #{gemfile_without_ext}"
- YARD::GemIndex.each.find do |spec|
- next unless spec.full_name == gemfile_without_ext
- yardoc = Registry.yardoc_file_for_gem(spec.name, "= #{spec.version}")
- if yardoc
- Registry.load_yardoc(yardoc)
- Registry.load_all
- else
- log.enter_level(Logger::ERROR) do
- olddir = Dir.pwd
- Gems.run(spec.name, spec.version.to_s)
- Dir.chdir(olddir)
- end
- end
- return true
- end
-
- # Look for local .gem file
- gemfile += '.gem' unless gemfile =~ /\.gem$/
- log.info "Searching for local gem file #{gemfile}"
- if File.exist?(gemfile)
- File.open(gemfile, 'rb') do |io|
- expand_and_parse(gemfile, io)
- end
- return true
- end
-
- # Remote gemfile from rubygems.org
- url = "http://rubygems.org/downloads/#{gemfile}"
- log.info "Searching for remote gem file #{url}"
- begin
- open(url) {|io| expand_and_parse(gemfile, io) }
- return true
- rescue OpenURI::HTTPError
- nil # noop
- end
- false
- end
-
- def expand_and_parse(gemfile, io)
- dir = expand_gem(gemfile, io)
- generate_yardoc(dir)
- cleanup(gemfile)
- end
-
- def generate_yardoc(dir)
- Dir.chdir(dir) do
- log.enter_level(Logger::ERROR) { Yardoc.run('-n', '--no-save') }
- end
- end
-
- def expand_gem(gemfile, io)
- tmpdir = File.join(Dir.tmpdir, gemfile)
- FileUtils.mkdir_p(tmpdir)
- log.info "Expanding #{gemfile} to #{tmpdir}..."
-
- if Gem::VERSION >= '2.0.0'
- require 'rubygems/package/tar_reader'
- reader = Gem::Package::TarReader.new(io)
- reader.each do |pkg|
- next unless pkg.full_name == 'data.tar.gz'
- Zlib::GzipReader.wrap(pkg) do |gzio|
- tar = Gem::Package::TarReader.new(gzio)
- tar.each do |entry|
- file = File.join(tmpdir, entry.full_name)
- FileUtils.mkdir_p(File.dirname(file))
- File.open(file, 'wb') do |out|
- out.write(entry.read)
- begin
- out.fsync
- rescue NotImplementedError
- nil # noop
- end
- end
- end
- end
- break
- end
- else
- Gem::Package.open(io) do |pkg|
- pkg.each do |entry|
- pkg.extract_entry(tmpdir, entry)
- end
- end
- end
-
- tmpdir
- end
-
- def require_rubygems
- require 'rubygems'
- require 'rubygems/package'
- rescue LoadError => e
- log.error "Missing RubyGems, cannot run this command."
- raise(e)
- end
-
- def cleanup(gemfile)
- dir = File.join(Dir.tmpdir, gemfile)
- log.info "Cleaning up #{dir}..."
- FileUtils.rm_rf(dir)
- end
-
- def optparse(*args)
- opts = OptionParser.new
- opts.banner = "Usage: yard diff [options] oldgem newgem"
- opts.separator ""
- opts.separator "Example: yard diff yard-0.5.6 yard-0.5.8"
- opts.separator ""
- opts.separator "If the files don't exist locally, they will be grabbed using the `gem fetch`"
- opts.separator "command. If the gem is a .yardoc directory, it will be used. Finally, if the"
- opts.separator "gem name matches an installed gem (full name-version syntax), that gem will be used."
-
- opts.on('-a', '--all', 'List all objects, even if they are inside added/removed module/class') do
- @list_all = true
- end
- opts.on('--compact', 'Show compact results') { @compact = true }
- opts.on('--git', 'Compare versions from two git commit/branches') do
- @use_git = true
- end
- opts.on('--query QUERY', 'Only diff filtered objects') do |query|
- @verifier.add_expressions(query)
- end
- opts.on('--no-modified', 'Ignore modified objects') do
- @modified = false
- end
- common_options(opts)
- parse_options(opts, args)
- unless args.size == 2
- log.puts opts.banner
- exit(0)
- end
-
- args
- end
- end
- end
-end
+# frozen_string_literal: true
+require 'tmpdir'
+require 'fileutils'
+require 'open-uri'
+
+module YARD
+ module CLI
+ # CLI command to return the objects that were added/removed from 2 versions
+ # of a project (library, gem, working copy).
+ # @since 0.6.0
+ class Diff < Command
+ def initialize
+ super
+ @list_all = false
+ @use_git = false
+ @compact = false
+ @modified = true
+ @verifier = Verifier.new
+ @old_git_commit = nil
+ @old_path = Dir.pwd
+ log.show_backtraces = true
+ end
+
+ def description
+ 'Returns the object diff of two gems or .yardoc files'
+ end
+
+ def run(*args)
+ registry = optparse(*args).map do |gemfile|
+ if @use_git
+ load_git_commit(gemfile)
+ all_objects
+ elsif load_gem_data(gemfile)
+ log.info "Found #{gemfile}"
+ all_objects
+ else
+ log.error "Cannot find gem #{gemfile}"
+ nil
+ end
+ end.compact
+
+ return if registry.size != 2
+
+ first_object = nil
+ [["Added objects", "A", added_objects(*registry)],
+ ["Modified objects", "M", modified_objects(*registry)],
+ ["Removed objects", "D", removed_objects(*registry)]].each do |name, short, objects|
+ next if short == "M" && @modified == false
+ next if objects.empty?
+ last_object = nil
+ all_objects_notice = false
+ log.puts name + ":" unless @compact
+ objects.sort_by(&:path).each do |object|
+ if !@list_all && last_object && object.parent == last_object
+ log.print " (...)" unless all_objects_notice
+ all_objects_notice = true
+ next
+ elsif @compact
+ log.puts if first_object
+ else
+ log.puts
+ end
+ all_objects_notice = false
+ log.print "" + (@compact ? "#{short} " : " ") +
+ object.path + " (#{object.file}:#{object.line})"
+ last_object = object
+ first_object = true
+ end
+ unless @compact
+ log.puts; log.puts
+ end
+ end
+ log.puts if @compact
+ end
+
+ private
+
+ def all_objects
+ return Registry.all if @verifier.expressions.empty?
+ @verifier.run(Registry.all)
+ end
+
+ def added_objects(registry1, registry2)
+ registry2.reject {|o| registry1.find {|o2| o2.path == o.path } }
+ end
+
+ def modified_objects(registry1, registry2)
+ registry1.select do |obj|
+ case obj
+ when CodeObjects::MethodObject
+ registry2.find {|o| obj == o && o.source != obj.source }
+ when CodeObjects::ConstantObject
+ registry2.find {|o| obj == o && o.value != obj.value }
+ end
+ end.compact
+ end
+
+ def removed_objects(registry1, registry2)
+ registry1.reject {|o| registry2.find {|o2| o2.path == o.path } }
+ end
+
+ def load_git_commit(commit)
+ Registry.clear
+ commit_path = 'git_commit' + commit.gsub(/\W/, '_')
+ tmpdir = File.join(Dir.tmpdir, commit_path)
+ log.info "Expanding #{commit} to #{tmpdir}..."
+ Dir.chdir(@old_path)
+ FileUtils.mkdir_p(tmpdir)
+ FileUtils.cp_r('.', tmpdir)
+ Dir.chdir(tmpdir)
+ log.info("git says: " + `git reset --hard #{commit}`.chomp)
+ generate_yardoc(tmpdir)
+ ensure
+ Dir.chdir(@old_path)
+ cleanup(commit_path)
+ end
+
+ def load_gem_data(gemfile)
+ require_rubygems
+ Registry.clear
+
+ # First check for argument as .yardoc file
+ [File.join(gemfile, '.yardoc'), gemfile].each do |yardoc|
+ log.info "Searching for .yardoc db at #{yardoc}"
+ next unless File.directory?(yardoc)
+ Registry.load_yardoc(yardoc)
+ Registry.load_all
+ return true
+ end
+
+ # Next check installed RubyGems
+ gemfile_without_ext = gemfile.sub(/\.gem$/, '')
+ log.info "Searching for installed gem #{gemfile_without_ext}"
+ YARD::GemIndex.each.find do |spec|
+ next unless spec.full_name == gemfile_without_ext
+ yardoc = Registry.yardoc_file_for_gem(spec.name, "= #{spec.version}")
+ if yardoc
+ Registry.load_yardoc(yardoc)
+ Registry.load_all
+ else
+ log.enter_level(Logger::ERROR) do
+ olddir = Dir.pwd
+ Gems.run(spec.name, spec.version.to_s)
+ Dir.chdir(olddir)
+ end
+ end
+ return true
+ end
+
+ # Look for local .gem file
+ gemfile += '.gem' unless gemfile =~ /\.gem$/
+ log.info "Searching for local gem file #{gemfile}"
+ if File.exist?(gemfile)
+ File.open(gemfile, 'rb') do |io|
+ expand_and_parse(gemfile, io)
+ end
+ return true
+ end
+
+ # Remote gemfile from rubygems.org
+ url = "http://rubygems.org/downloads/#{gemfile}"
+ log.info "Searching for remote gem file #{url}"
+ begin
+ open(url) {|io| expand_and_parse(gemfile, io) }
+ return true
+ rescue OpenURI::HTTPError
+ nil # noop
+ end
+ false
+ end
+
+ def expand_and_parse(gemfile, io)
+ dir = expand_gem(gemfile, io)
+ generate_yardoc(dir)
+ cleanup(gemfile)
+ end
+
+ def generate_yardoc(dir)
+ Dir.chdir(dir) do
+ log.enter_level(Logger::ERROR) { Yardoc.run('-n', '--no-save') }
+ end
+ end
+
+ def expand_gem(gemfile, io)
+ tmpdir = File.join(Dir.tmpdir, gemfile)
+ FileUtils.mkdir_p(tmpdir)
+ log.info "Expanding #{gemfile} to #{tmpdir}..."
+
+ if Gem::VERSION >= '2.0.0'
+ require 'rubygems/package/tar_reader'
+ reader = Gem::Package::TarReader.new(io)
+ reader.each do |pkg|
+ next unless pkg.full_name == 'data.tar.gz'
+ Zlib::GzipReader.wrap(pkg) do |gzio|
+ tar = Gem::Package::TarReader.new(gzio)
+ tar.each do |entry|
+ file = File.join(tmpdir, entry.full_name)
+ FileUtils.mkdir_p(File.dirname(file))
+ File.open(file, 'wb') do |out|
+ out.write(entry.read)
+ begin
+ out.fsync
+ rescue NotImplementedError
+ nil # noop
+ end
+ end
+ end
+ end
+ break
+ end
+ else
+ Gem::Package.open(io) do |pkg|
+ pkg.each do |entry|
+ pkg.extract_entry(tmpdir, entry)
+ end
+ end
+ end
+
+ tmpdir
+ end
+
+ def require_rubygems
+ require 'rubygems'
+ require 'rubygems/package'
+ rescue LoadError => e
+ log.error "Missing RubyGems, cannot run this command."
+ raise(e)
+ end
+
+ def cleanup(gemfile)
+ dir = File.join(Dir.tmpdir, gemfile)
+ log.info "Cleaning up #{dir}..."
+ FileUtils.rm_rf(dir)
+ end
+
+ def optparse(*args)
+ opts = OptionParser.new
+ opts.banner = "Usage: yard diff [options] oldgem newgem"
+ opts.separator ""
+ opts.separator "Example: yard diff yard-0.5.6 yard-0.5.8"
+ opts.separator ""
+ opts.separator "If the files don't exist locally, they will be grabbed using the `gem fetch`"
+ opts.separator "command. If the gem is a .yardoc directory, it will be used. Finally, if the"
+ opts.separator "gem name matches an installed gem (full name-version syntax), that gem will be used."
+
+ opts.on('-a', '--all', 'List all objects, even if they are inside added/removed module/class') do
+ @list_all = true
+ end
+ opts.on('--compact', 'Show compact results') { @compact = true }
+ opts.on('--git', 'Compare versions from two git commit/branches') do
+ @use_git = true
+ end
+ opts.on('--query QUERY', 'Only diff filtered objects') do |query|
+ @verifier.add_expressions(query)
+ end
+ opts.on('--no-modified', 'Ignore modified objects') do
+ @modified = false
+ end
+ common_options(opts)
+ parse_options(opts, args)
+ unless args.size == 2
+ log.puts opts.banner
+ exit(0)
+ end
+
+ args
+ end
+ end
+ end
+end