courgette 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/Rakefile +8 -0
- data/bin/courgette.rb +40 -0
- data/courgette.gemspec +28 -0
- data/lib/courgette/capturer.rb +88 -0
- data/lib/courgette/commands/graph.rb +46 -0
- data/lib/courgette/commands/stats.rb +42 -0
- data/lib/courgette/directory_analyser.rb +38 -0
- data/lib/courgette/file_to_sexpr.rb +15 -0
- data/lib/courgette/graph.rb +20 -0
- data/lib/courgette/graph2dot.rb +56 -0
- data/lib/courgette/name_resolution.rb +27 -0
- data/lib/courgette/node_grouper.rb +31 -0
- data/lib/courgette/reference_to_dependency.rb +20 -0
- data/lib/courgette/scope.rb +14 -0
- data/lib/courgette/version.rb +4 -0
- data/lib/courgette.rb +2 -0
- data/test/courgette/test_name_resolution.rb +72 -0
- data/test/courgette/test_scope.rb +31 -0
- metadata +151 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 57adf4c4137fcf9342cc36a2da2d18bae46adff3
         | 
| 4 | 
            +
              data.tar.gz: c7d05925b78857a07c8765859e9c945dbcffd3e8
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 17e68370df6b304fdf7973b9f969e53d5bc9f30764a2eebdf0b33442b48bfddf6d4b40a81bcb4bd35679448e23a1962e222688339fc1797670d75943f732d5a4
         | 
| 7 | 
            +
              data.tar.gz: bf96fc9d079926ea905030564e21a12f2543fbe546249056c53c85565deed8d05ac56e49b608e68d2fc3750009480a258506e7968aa8f5cd67aa5a6640e0ff2c
         | 
    
        data/Gemfile
    ADDED
    
    
    
        data/Rakefile
    ADDED
    
    
    
        data/bin/courgette.rb
    ADDED
    
    | @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'rubygems'
         | 
| 4 | 
            +
            require 'commander/import'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require 'courgette/commands/graph'
         | 
| 7 | 
            +
            require 'courgette/commands/stats'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            program :version, '1.0.0'
         | 
| 10 | 
            +
            program :description, 'Dependency graph fetcher'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            default_command :stats
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            command :stats do |c|
         | 
| 15 | 
            +
              c.syntax = 'courgette stats [options]'
         | 
| 16 | 
            +
              c.summary = ''
         | 
| 17 | 
            +
              c.description = ''
         | 
| 18 | 
            +
              c.example 'description', 'command example'
         | 
| 19 | 
            +
              c.option '--glob STRING', String, 'Glob to run metrics over (default: **/*.rb)'
         | 
| 20 | 
            +
              c.action do |args, options|
         | 
| 21 | 
            +
                options.default :glob => '**/*.rb'
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                Courgette::Commands::Stats.new(options).run
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            command :graph do |c|
         | 
| 28 | 
            +
              c.syntax = 'courgette graph [options]'
         | 
| 29 | 
            +
              c.summary = ''
         | 
| 30 | 
            +
              c.description = ''
         | 
| 31 | 
            +
              c.example 'description', 'command example'
         | 
| 32 | 
            +
              c.option '--glob STRING', String, 'Glob to run metrics over (default: **/*.rb)'
         | 
| 33 | 
            +
              c.option '--name STRING', String, 'Glob to run metrics over (default: graph)'
         | 
| 34 | 
            +
              c.option '--format STRING', String, 'Format (default: png)'
         | 
| 35 | 
            +
              c.action do |args, options|
         | 
| 36 | 
            +
                options.default :glob => '**/*.rb', :format => 'png', :name => 'graph'
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                Courgette::Commands::Graph.new(options).run
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
    
        data/courgette.gemspec
    ADDED
    
    | @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            # coding: utf-8
         | 
| 2 | 
            +
            lib = File.expand_path('../lib', __FILE__)
         | 
| 3 | 
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 4 | 
            +
            require 'courgette/version'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Gem::Specification.new do |spec|
         | 
| 7 | 
            +
              spec.name          = "courgette"
         | 
| 8 | 
            +
              spec.version       = Courgette::VERSION
         | 
| 9 | 
            +
              spec.authors       = ["Hugo Peixoto"]
         | 
| 10 | 
            +
              spec.email         = ["hugo.peixoto@gmail.com"]
         | 
| 11 | 
            +
              spec.description   = %q{Static ruby dependency analyser}
         | 
| 12 | 
            +
              spec.summary       = %q{Courgette analyses a set of files, and calculates a dependency graph}
         | 
| 13 | 
            +
              spec.homepage      = "https://github.com/hugopeixoto/courgette"
         | 
| 14 | 
            +
              spec.license       = "MIT"
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              spec.files         = `git ls-files`.split($/)
         | 
| 17 | 
            +
              spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
         | 
| 18 | 
            +
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         | 
| 19 | 
            +
              spec.require_paths = ["lib"]
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              spec.add_dependency "ruby_parser"
         | 
| 22 | 
            +
              spec.add_dependency "commander"
         | 
| 23 | 
            +
              spec.add_dependency "graph"
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              spec.add_development_dependency "bundler", "~> 1.3"
         | 
| 26 | 
            +
              spec.add_development_dependency "rake"
         | 
| 27 | 
            +
              spec.add_development_dependency "minitest"
         | 
| 28 | 
            +
            end
         | 
| @@ -0,0 +1,88 @@ | |
| 1 | 
            +
            require 'courgette/scope'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Courgette
         | 
| 4 | 
            +
              class Capturer
         | 
| 5 | 
            +
                def initialize
         | 
| 6 | 
            +
                  @references  = []
         | 
| 7 | 
            +
                  @definitions = []
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def capture sexpr
         | 
| 11 | 
            +
                  iterate sexpr, []
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def references
         | 
| 15 | 
            +
                  @references.uniq! { |reference| reference.to_a }
         | 
| 16 | 
            +
                  @references
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def definitions
         | 
| 20 | 
            +
                  @definitions.uniq! { |definition| definition.to_a }
         | 
| 21 | 
            +
                  @definitions
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                private
         | 
| 25 | 
            +
                Reference = Struct.new :name, :context
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def add_definition definition
         | 
| 28 | 
            +
                  @definitions << definition.flatten
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def add_reference context, reference
         | 
| 32 | 
            +
                  @references << Reference.new(reference, context)
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                def scope sexpr
         | 
| 36 | 
            +
                  Courgette::Scope.new.scope sexpr
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def iterate_many sexprs, context
         | 
| 40 | 
            +
                  sexprs.each do |sexpr|
         | 
| 41 | 
            +
                    iterate sexpr, context
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def iterate_cdecl sexpr, context
         | 
| 46 | 
            +
                  s = scope sexpr[1]
         | 
| 47 | 
            +
                  new_context = context + [s]
         | 
| 48 | 
            +
                  add_definition new_context
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  iterate_many sexpr[2..-1], context
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def iterate_definition sexpr, context
         | 
| 54 | 
            +
                  s = scope sexpr[1]
         | 
| 55 | 
            +
                  new_context = context + [s]
         | 
| 56 | 
            +
                  add_definition new_context
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  iterate_many sexpr[2..-1], new_context
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                def iterate_call sexpr, context
         | 
| 62 | 
            +
                  iterate sexpr[1], context
         | 
| 63 | 
            +
                  iterate_many sexpr[3..-1], context
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
             | 
| 67 | 
            +
                def iterate sexpr, context
         | 
| 68 | 
            +
                  return unless sexpr.is_a? Enumerable
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  case sexpr[0]
         | 
| 71 | 
            +
                  when :const, :colon2
         | 
| 72 | 
            +
                    add_reference context, scope(sexpr)
         | 
| 73 | 
            +
                  when :module, :class
         | 
| 74 | 
            +
                    iterate_definition sexpr, context
         | 
| 75 | 
            +
                  when :cdecl
         | 
| 76 | 
            +
                    iterate_cdecl sexpr, context
         | 
| 77 | 
            +
                  when :defn
         | 
| 78 | 
            +
                    iterate_many sexpr[3..-1], context
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  when :call
         | 
| 81 | 
            +
                    iterate_call sexpr, context
         | 
| 82 | 
            +
                  when :nil, :lit
         | 
| 83 | 
            +
                  else
         | 
| 84 | 
            +
                    iterate_many sexpr[1..-1], context
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
            end
         | 
| @@ -0,0 +1,46 @@ | |
| 1 | 
            +
            require 'courgette/directory_analyser'
         | 
| 2 | 
            +
            require 'courgette/graph2dot'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Courgette
         | 
| 5 | 
            +
              module Commands
         | 
| 6 | 
            +
                class Graph
         | 
| 7 | 
            +
                  def initialize options
         | 
| 8 | 
            +
                    @options = options
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def run
         | 
| 12 | 
            +
                    factory.load graph
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    formats.each do |fmt|
         | 
| 15 | 
            +
                      factory.save name, fmt
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  private
         | 
| 20 | 
            +
                  def glob
         | 
| 21 | 
            +
                    @options.glob
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def formats
         | 
| 25 | 
            +
                    Array(@options.format)
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def name
         | 
| 29 | 
            +
                    @options.name
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def factory
         | 
| 33 | 
            +
                    @factory ||= Courgette::Graph2Dot.new do
         | 
| 34 | 
            +
                      boxes
         | 
| 35 | 
            +
                      node_attribs << filled
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  def graph
         | 
| 40 | 
            +
                    @graph ||= Courgette::DirectoryAnalyser.new.tap do |da|
         | 
| 41 | 
            +
                      da.analyse glob
         | 
| 42 | 
            +
                    end.graph
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
            end
         | 
| @@ -0,0 +1,42 @@ | |
| 1 | 
            +
            require 'courgette/directory_analyser'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Courgette
         | 
| 4 | 
            +
              module Commands
         | 
| 5 | 
            +
                class Stats
         | 
| 6 | 
            +
                  def initialize options
         | 
| 7 | 
            +
                    @options = options
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def run
         | 
| 11 | 
            +
                    stats.each do |fanout, fanin, reference|
         | 
| 12 | 
            +
                      print_reference fanout, fanin, reference
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def print_reference fo, fi, ref
         | 
| 17 | 
            +
                    print "%8d %8d: %s\n" % [fo, fi, ref.join("::")]
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  private
         | 
| 21 | 
            +
                  def glob
         | 
| 22 | 
            +
                    @options.glob
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def stats
         | 
| 26 | 
            +
                    @stats ||= graph.nodes.map do |node|
         | 
| 27 | 
            +
                      [
         | 
| 28 | 
            +
                        graph.dependency_count(node),
         | 
| 29 | 
            +
                        graph.depender_count(node),
         | 
| 30 | 
            +
                        node
         | 
| 31 | 
            +
                      ]
         | 
| 32 | 
            +
                    end.sort
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  def graph
         | 
| 36 | 
            +
                    @graph ||= Courgette::DirectoryAnalyser.new.tap do |da|
         | 
| 37 | 
            +
                      da.analyse glob
         | 
| 38 | 
            +
                    end.graph
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
            end
         | 
| @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            require 'courgette/capturer'
         | 
| 2 | 
            +
            require 'courgette/reference_to_dependency'
         | 
| 3 | 
            +
            require 'courgette/file_to_sexpr'
         | 
| 4 | 
            +
            require 'courgette/graph'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Courgette
         | 
| 7 | 
            +
              class DirectoryAnalyser
         | 
| 8 | 
            +
                def initialize
         | 
| 9 | 
            +
                  @capturer   = Courgette::Capturer.new
         | 
| 10 | 
            +
                  @file2sexpr = Courgette::FileToSexpr.new
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def analyse pattern
         | 
| 14 | 
            +
                  Dir.glob(pattern) do |file|
         | 
| 15 | 
            +
                    capturer.capture file2sexpr.convert(file)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def graph
         | 
| 20 | 
            +
                  Courgette::Graph.new definitions, dependencies
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                private
         | 
| 24 | 
            +
                attr_reader :capturer, :file2sexpr
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def definitions
         | 
| 27 | 
            +
                  capturer.definitions
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def dependencies
         | 
| 31 | 
            +
                  r2d = Courgette::ReferenceToDependency.new definitions
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  capturer.references.map do |reference|
         | 
| 34 | 
            +
                    r2d.transform reference
         | 
| 35 | 
            +
                  end.compact
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
            end
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            module Courgette
         | 
| 2 | 
            +
              class Graph
         | 
| 3 | 
            +
                attr_reader :nodes, :edges
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize nodes, edges
         | 
| 6 | 
            +
                  @nodes = nodes
         | 
| 7 | 
            +
                  @edges = edges
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def dependency_count node
         | 
| 11 | 
            +
                  edges.select { |r| r.referrer == node }.count
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def depender_count node
         | 
| 15 | 
            +
                  edges.select { |r| r.reference == node }.count
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                private
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         | 
| @@ -0,0 +1,56 @@ | |
| 1 | 
            +
            require 'courgette/node_grouper'
         | 
| 2 | 
            +
            require 'graph'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Courgette
         | 
| 5 | 
            +
              class Graph2Dot
         | 
| 6 | 
            +
                def initialize &block
         | 
| 7 | 
            +
                  @dot = digraph &block
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def load graph
         | 
| 11 | 
            +
                  calculate_distinct_namespaces graph.nodes
         | 
| 12 | 
            +
                  set_colorscheme
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  graph.nodes.each do |node|
         | 
| 15 | 
            +
                    add_node node
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  graph.edges.each do |edge|
         | 
| 19 | 
            +
                    add_edge edge
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def save *args
         | 
| 24 | 
            +
                  dot.save *args
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                private
         | 
| 28 | 
            +
                attr_reader :dot
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def set_colorscheme
         | 
| 31 | 
            +
                  dot.colorscheme(:set2, [4, [8, @grouper.groups.count + 1].min].max)
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def calculate_distinct_namespaces nodes
         | 
| 35 | 
            +
                  @grouper = Courgette::NodeGrouper.new nodes, 8
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def add_node node
         | 
| 39 | 
            +
                  color(node) << dot.node(label(node))
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def add_edge edge
         | 
| 43 | 
            +
                  dot.edge(label(edge.referrer), label(edge.reference))
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def color node
         | 
| 47 | 
            +
                  idx = @grouper.group node
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  dot.send "c#{idx}"
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def label node
         | 
| 53 | 
            +
                  node.flatten.join("::")
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
            end
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            module Courgette
         | 
| 2 | 
            +
              class NameResolution
         | 
| 3 | 
            +
                def initialize definitions
         | 
| 4 | 
            +
                  @definitions = definitions
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def resolve reference
         | 
| 8 | 
            +
                  definitions.find do |definition|
         | 
| 9 | 
            +
                    match_name?(definition, reference) &&
         | 
| 10 | 
            +
                      match_scope?(definition, reference)
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                private
         | 
| 15 | 
            +
                attr_reader :definitions
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def match_name? definition, reference
         | 
| 18 | 
            +
                  definition.last == reference.name.last
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def match_scope? definition, reference
         | 
| 22 | 
            +
                  (0..reference.context.length).any? do |level|
         | 
| 23 | 
            +
                    reference.context[0...level].flatten + reference.name == definition
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
            end
         | 
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            module Courgette
         | 
| 2 | 
            +
              class NodeGrouper
         | 
| 3 | 
            +
                def initialize nodes, max_groups
         | 
| 4 | 
            +
                  @nodes = nodes
         | 
| 5 | 
            +
                  @max_groups = max_groups
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  calculate
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def group node
         | 
| 11 | 
            +
                  idx   = namespaces.index node[0...-1][0...1]
         | 
| 12 | 
            +
                  idx ||= -1
         | 
| 13 | 
            +
                  idx   = [2 + idx, @max_groups].min
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def groups
         | 
| 17 | 
            +
                  @namespaces
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                private
         | 
| 21 | 
            +
                attr_reader :nodes, :namespaces
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def calculate
         | 
| 24 | 
            +
                  @namespaces = nodes.
         | 
| 25 | 
            +
                    group_by { |n| n[0...-1][0...1] }.
         | 
| 26 | 
            +
                    reject { |k, v| k.empty? || v.length < 4 }.
         | 
| 27 | 
            +
                    map(&:first).
         | 
| 28 | 
            +
                    uniq
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            require 'courgette/name_resolution'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Courgette
         | 
| 4 | 
            +
              class ReferenceToDependency
         | 
| 5 | 
            +
                def initialize definitions
         | 
| 6 | 
            +
                  @resolver = Courgette::NameResolution.new definitions
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def transform reference
         | 
| 10 | 
            +
                  definition = @resolver.resolve reference
         | 
| 11 | 
            +
                  return if definition.nil?
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  Dependency.new definition, reference.context.flatten
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                private
         | 
| 17 | 
            +
                attr_reader :resolver
         | 
| 18 | 
            +
                Dependency = Struct.new :reference, :referrer
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         | 
    
        data/lib/courgette.rb
    ADDED
    
    
| @@ -0,0 +1,72 @@ | |
| 1 | 
            +
            require 'minitest/autorun'
         | 
| 2 | 
            +
            require 'courgette/name_resolution'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            FakeReference = Struct.new :name, :context
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            class TestCourgetteNameResolution < MiniTest::Unit::TestCase
         | 
| 7 | 
            +
              def create definitions
         | 
| 8 | 
            +
                @resolver = Courgette::NameResolution.new definitions
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              def test_root_level
         | 
| 12 | 
            +
                create [[:RootA], [:RootB]]
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                ref = FakeReference.new [:RootA], []
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                assert_equal [:RootA], @resolver.resolve(ref)
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              def test_root_unknown
         | 
| 20 | 
            +
                create [[:Root]]
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                ref = FakeReference.new [:Unknown], []
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                assert_equal nil, @resolver.resolve(ref)
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              def test_top_definition_referenced_from_nested
         | 
| 28 | 
            +
                create [[:Root]]
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                ref = FakeReference.new [:Root], [[:A, :B]]
         | 
| 31 | 
            +
                assert_equal [:Root], @resolver.resolve(ref)
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              def test_nested_definition_referenced_from_nested
         | 
| 35 | 
            +
                create [[:Scoped, :Name]]
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                ref = FakeReference.new [:Name], [[:Scoped]]
         | 
| 38 | 
            +
                assert_equal [:Scoped, :Name], @resolver.resolve(ref)
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              def test_double_scope
         | 
| 42 | 
            +
                definition = [:Potato, :Scoped, :Name]
         | 
| 43 | 
            +
                create [definition]
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                ref = FakeReference.new [:Name], [[:Potato], [:Scoped]]
         | 
| 46 | 
            +
                assert_equal definition, @resolver.resolve(ref)
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              def test_grouped_double_scope
         | 
| 50 | 
            +
                definition = [:Potato, :Scoped, :Name]
         | 
| 51 | 
            +
                create [definition]
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                ref = FakeReference.new [:Name], [[:Potato, :Scoped]]
         | 
| 54 | 
            +
                assert_equal definition, @resolver.resolve(ref)
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              def test_scoped_definition_referenced_double
         | 
| 58 | 
            +
                definition = [:Potato, :Name]
         | 
| 59 | 
            +
                create [definition]
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                ref = FakeReference.new [:Name], [[:Potato], [:Scoped]]
         | 
| 62 | 
            +
                assert_equal definition, @resolver.resolve(ref)
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
              def test_scoped_definition_referenced_grouped
         | 
| 66 | 
            +
                definition = [:Potato, :Name]
         | 
| 67 | 
            +
                create [definition]
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                ref = FakeReference.new [:Name], [[:Potato, :Scoped]]
         | 
| 70 | 
            +
                assert_equal nil, @resolver.resolve(ref)
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
            end
         | 
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            require 'minitest/autorun'
         | 
| 2 | 
            +
            require 'courgette/scope'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class TestCourgetteScope < MiniTest::Unit::TestCase
         | 
| 5 | 
            +
              def setup
         | 
| 6 | 
            +
                @scope = Courgette::Scope.new
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def test_const
         | 
| 10 | 
            +
                assert_equal [:Name], @scope.scope([:const, :Name])
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              def test_other
         | 
| 14 | 
            +
                assert_equal [:potato], @scope.scope(:potato)
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              def test_colon2
         | 
| 18 | 
            +
                assert_equal [:Scoped, :Name],
         | 
| 19 | 
            +
                  @scope.scope([:colon2, :Scoped, :Name])
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              def test_compose
         | 
| 23 | 
            +
                assert_equal [:Really, :Scoped, :Name],
         | 
| 24 | 
            +
                  @scope.scope(
         | 
| 25 | 
            +
                    [:colon2,
         | 
| 26 | 
            +
                      [:colon2,
         | 
| 27 | 
            +
                        [:const, :Really],
         | 
| 28 | 
            +
                        [:const, :Scoped]],
         | 
| 29 | 
            +
                      [:const, :Name]])
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,151 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: courgette
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.1.0
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Hugo Peixoto
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2014-02-07 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: ruby_parser
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - '>='
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '0'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - '>='
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: commander
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - '>='
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '0'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - '>='
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '0'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: graph
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - '>='
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '0'
         | 
| 48 | 
            +
              type: :runtime
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - '>='
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '0'
         | 
| 55 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            +
              name: bundler
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - ~>
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: '1.3'
         | 
| 62 | 
            +
              type: :development
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - ~>
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: '1.3'
         | 
| 69 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 70 | 
            +
              name: rake
         | 
| 71 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 72 | 
            +
                requirements:
         | 
| 73 | 
            +
                - - '>='
         | 
| 74 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 75 | 
            +
                    version: '0'
         | 
| 76 | 
            +
              type: :development
         | 
| 77 | 
            +
              prerelease: false
         | 
| 78 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
                requirements:
         | 
| 80 | 
            +
                - - '>='
         | 
| 81 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                    version: '0'
         | 
| 83 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 84 | 
            +
              name: minitest
         | 
| 85 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 86 | 
            +
                requirements:
         | 
| 87 | 
            +
                - - '>='
         | 
| 88 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 89 | 
            +
                    version: '0'
         | 
| 90 | 
            +
              type: :development
         | 
| 91 | 
            +
              prerelease: false
         | 
| 92 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 93 | 
            +
                requirements:
         | 
| 94 | 
            +
                - - '>='
         | 
| 95 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 96 | 
            +
                    version: '0'
         | 
| 97 | 
            +
            description: Static ruby dependency analyser
         | 
| 98 | 
            +
            email:
         | 
| 99 | 
            +
            - hugo.peixoto@gmail.com
         | 
| 100 | 
            +
            executables:
         | 
| 101 | 
            +
            - courgette.rb
         | 
| 102 | 
            +
            extensions: []
         | 
| 103 | 
            +
            extra_rdoc_files: []
         | 
| 104 | 
            +
            files:
         | 
| 105 | 
            +
            - Gemfile
         | 
| 106 | 
            +
            - Rakefile
         | 
| 107 | 
            +
            - bin/courgette.rb
         | 
| 108 | 
            +
            - courgette.gemspec
         | 
| 109 | 
            +
            - lib/courgette.rb
         | 
| 110 | 
            +
            - lib/courgette/capturer.rb
         | 
| 111 | 
            +
            - lib/courgette/commands/graph.rb
         | 
| 112 | 
            +
            - lib/courgette/commands/stats.rb
         | 
| 113 | 
            +
            - lib/courgette/directory_analyser.rb
         | 
| 114 | 
            +
            - lib/courgette/file_to_sexpr.rb
         | 
| 115 | 
            +
            - lib/courgette/graph.rb
         | 
| 116 | 
            +
            - lib/courgette/graph2dot.rb
         | 
| 117 | 
            +
            - lib/courgette/name_resolution.rb
         | 
| 118 | 
            +
            - lib/courgette/node_grouper.rb
         | 
| 119 | 
            +
            - lib/courgette/reference_to_dependency.rb
         | 
| 120 | 
            +
            - lib/courgette/scope.rb
         | 
| 121 | 
            +
            - lib/courgette/version.rb
         | 
| 122 | 
            +
            - test/courgette/test_name_resolution.rb
         | 
| 123 | 
            +
            - test/courgette/test_scope.rb
         | 
| 124 | 
            +
            homepage: https://github.com/hugopeixoto/courgette
         | 
| 125 | 
            +
            licenses:
         | 
| 126 | 
            +
            - MIT
         | 
| 127 | 
            +
            metadata: {}
         | 
| 128 | 
            +
            post_install_message: 
         | 
| 129 | 
            +
            rdoc_options: []
         | 
| 130 | 
            +
            require_paths:
         | 
| 131 | 
            +
            - lib
         | 
| 132 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 133 | 
            +
              requirements:
         | 
| 134 | 
            +
              - - '>='
         | 
| 135 | 
            +
                - !ruby/object:Gem::Version
         | 
| 136 | 
            +
                  version: '0'
         | 
| 137 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 138 | 
            +
              requirements:
         | 
| 139 | 
            +
              - - '>='
         | 
| 140 | 
            +
                - !ruby/object:Gem::Version
         | 
| 141 | 
            +
                  version: '0'
         | 
| 142 | 
            +
            requirements: []
         | 
| 143 | 
            +
            rubyforge_project: 
         | 
| 144 | 
            +
            rubygems_version: 2.0.3
         | 
| 145 | 
            +
            signing_key: 
         | 
| 146 | 
            +
            specification_version: 4
         | 
| 147 | 
            +
            summary: Courgette analyses a set of files, and calculates a dependency graph
         | 
| 148 | 
            +
            test_files:
         | 
| 149 | 
            +
            - test/courgette/test_name_resolution.rb
         | 
| 150 | 
            +
            - test/courgette/test_scope.rb
         | 
| 151 | 
            +
            has_rdoc: 
         |