class GemLeaves

Amateurish prototype of a tool to check the installed gems looking for leaves that can be removed.

I call leaves those gems with no reverse dependency that can be removed without breaking anything. Of course, users may desire to keep something around even if nothing depends on it, so this tool looks for and loads a simple YAML configuration file:

ignore:
  RedCloth: >= 0
  capistrano: >= 2
  mongrel_cluster: >= 0
  piston: >= 0
  rake: >= 0.7
  rubygems-update: >= 0
  ruport-util: = 0.10.0
  sources: >= 0

gem_leaves can generate its own configuration file merging the leaves list with the content of an already loaded configuration file (if any). The tool looks for the default configuration file, .gem_leaves.yml, in the current working directory and the user’s home directory.

Constants

VERSION

Public Class Methods

new(args) click to toggle source
   # File lib/gem_leaves.rb
31 def initialize(args)
32   @options = {:color => "d0ed0e"}
33   @configuration = {}
34   @leaves = []
35   parse_options(args)
36 end

Public Instance Methods

run() click to toggle source
   # File lib/gem_leaves.rb
38 def run
39   load_config_file
40   find_leaves
41   show_leaves
42   generate_config_file
43 end

Protected Instance Methods

diagrammatical_output() click to toggle source

Build a DOT diagram of the installed gems, highlighting the leaves with a customizable color, leaving those gems that must be kept according to the current configuration in the standard color.

    # File lib/gem_leaves.rb
144 def diagrammatical_output
145   diagram = "digraph leaves_diagram {\n"
146   Gem::Specification.all.each do |g|
147     if @options[:reverse]
148       g.dependent_gems.each {|d| diagram << %Q(  "#{d[0].full_name}" -> "#{g.full_name}"\n)}
149     else
150       g.dependent_gems.each {|d| diagram << %Q(  "#{g.full_name}" -> "#{d[0].full_name}"\n)}
151     end
152     if g.dependent_gems.empty?
153       str = %Q(  "#{g.full_name}")
154       if @leaves.include?(g)
155         str += %Q( [color="##{@options[:color]}", style=filled]\n)
156       else
157         str += "\n"
158       end
159       diagram << str
160     end
161   end
162   diagram << "}"
163   puts diagram
164 end
find_home() click to toggle source

Lifted from RubyGems 0.9.5; it’s a semi-portable way to find the user’s home directory.

    # File lib/gem_leaves.rb
 88 def find_home
 89   ['HOME', 'USERPROFILE'].each do |homekey|
 90     return ENV[homekey] if ENV[homekey]
 91   end
 92   if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
 93     return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
 94   end
 95   begin
 96     File.expand_path("~")
 97   rescue StandardError => ex
 98     if File::ALT_SEPARATOR
 99       "C:/"
100     else
101       "/"
102     end
103   end
104 end
find_leaves() click to toggle source

Looks at the installed gems to find the leaves.

    # File lib/gem_leaves.rb
107 def find_leaves
108   ignorable = @configuration['ignore'].map do |k, v|
109     Gem::Specification.find_all_by_name k, v
110   end.flatten
111   @leaves = Gem::Specification.select {|s| s.dependent_gems.empty?} - ignorable
112 end
generate_config_file() click to toggle source

Writes the new configuration file merging the optionally already loaded one with the leaves just found.

    # File lib/gem_leaves.rb
168 def generate_config_file
169   if @options[:new_config_file]
170     @leaves.sort.each do |leaf|
171       @configuration['ignore']["#{leaf.name}"] = "= #{leaf.version}"
172     end
173     File.open(@options[:new_config_file], 'w') do |out|
174       YAML.dump(@configuration, out)
175     end
176   end
177 end
load_config_file() click to toggle source

Looks for a YAML configuration file in:

  1. the PWD;

  2. the user’s home directory.

Optionally load a specific configuration file whose name has been set by the user.

   # File lib/gem_leaves.rb
72 def load_config_file
73   if @options[:ignore]
74     @configuration = {'ignore' => {}}
75   else
76     if @options[:config_file].nil?
77       cf = ['.gem_leaves.yml', File.join(find_home, '.gem_leaves.yml')]
78       cf.each {|f| (@configuration = YAML.load_file(f); return) rescue nil}
79       @configuration = {'ignore' => {}}
80     else
81       @configuration = YAML.load_file(@options[:config_file])
82     end
83   end
84 end
parse_options(args) click to toggle source

Parses the command line arguments.

   # File lib/gem_leaves.rb
48 def parse_options(args)
49   OptionParser.new('Usage: gem_leaves [OPTIONS]') do |p|
50     p.separator ''
51     p.on('-c', '--config-file=FILE', 'Load the named configuration file') {|v| @options[:config_file] = v}
52     p.on('-C', '--color=COLOR', "Set the leaves' color in the DOT diagram") {|v| @options[:color] = v}
53     p.on('-d', '--diagram', 'Generate a DOT diagram on stdout') {|v| @options[:diagram] = v}
54     p.on('-g', '--generate-config-file=FILE',
55       "Generate a new configuration file merging",
56       "the leaves' list with the content of the",
57       "old configuration file (if any)") {|v| @options[:new_config_file] = v}
58     p.on('-i', '--ignore', 'Ignore every configuration file') {|v| @options[:ignore] = v}
59     p.on('-p', '--pipe', 'Pipe Format (name --version ver)') {|v| @options[:pipe] = v}
60     p.on('-r', '--reverse', "Reverse the edges in the DOT diagram") {|v| @options[:reverse] = v}
61     p.parse(args)
62   end
63 end
pipe_output() click to toggle source

Simply puts the list of leaves to STDOUT in a format suitable to pipe it to RubyGems.

    # File lib/gem_leaves.rb
127 def pipe_output
128   @leaves.sort.each do |leaf|
129     puts "#{leaf.name} --version #{leaf.version}"
130   end
131 end
show_leaves() click to toggle source

Show the leaves using one of the supported output format.

    # File lib/gem_leaves.rb
115 def show_leaves
116   if @options[:diagram]
117     diagrammatical_output
118   elsif @options[:pipe]
119     pipe_output
120   else
121     textual_output
122   end
123 end
textual_output() click to toggle source

Simply puts the list of leaves to STDOUT. It optionally merges the content of the already loaded configuration file with the leaves list.

    # File lib/gem_leaves.rb
135 def textual_output
136   @leaves.sort.each do |leaf|
137     puts "#{leaf.name} (#{leaf.version})"
138   end
139 end