class Gemterms::GemFiler

Accepts a Gemfile and Gemfile.lock to produce a Gemterm::Project based upon the rubygems defined. Can output statistics on that basis.

Attributes

bundle[R]
disable_api[RW]
project[R]
use_remotes[RW]

Public Class Methods

new(licenser) click to toggle source
# File lib/gemterms/gem_filer.rb, line 14
def initialize(licenser)
  @licenser = licenser
  @use_remotes = @disable_api = false
end

Public Instance Methods

process(gemfile, lockfile) click to toggle source
# File lib/gemterms/gem_filer.rb, line 59
def process(gemfile, lockfile)
  @gemfile = gemfile
  @lockfile = lockfile
  @project = Project.new

  read_bundle_specs
  load_specs
  @project
end
read_bundle() click to toggle source
# File lib/gemterms/gem_filer.rb, line 19
def read_bundle
  # Read the Bundle from the Gem and Lockfiles
  Bundler.settings[:frozen] = true
  @bundle = Bundler::Dsl.evaluate(@gemfile, @lockfile, {})
end
read_bundle_specs() click to toggle source
# File lib/gemterms/gem_filer.rb, line 25
    def read_bundle_specs
      read_bundle
      missing = []
      @specs = bundle.resolve.materialize(bundle.dependencies, missing)

      # Happy path - all the specs are available locally.
      if missing.length == 0
        # Use requested specs instead. This is due to the fact that
        # bundler cleverly excludes itself. However, if it's an explicit
        # spec we want to evaluate it's licence too (ps. It's MIT)
        @specs = bundle.requested_specs
        return true
      end
      
      # If we can, try and get the additional gem data from RubyGem sources
      # (typically https://rubygems.org). Otherwise give the user a warning.
      if use_remotes
puts "Sourcing gem information from gem sources. This may take some time."
        read_bundle
        @specs = bundle.resolve_remotely!
      else
        # @todo This path will be missing bundler - if it was supplied.
        puts missing.length == 1 ? "The following gem isn't installed:" : "The following gems aren't installed:"
        puts <<-INST
  #{missing.map { |s| s.full_name } * ', '}

We cannot report on uninstalled gems. You can use `bundle install` to install.
Alternatively, if you run with the `--use-remotes` option, gemterms will use 
your RubyGem sources to load gem metadata (note, this will be slower).

INST
      end
    end

Protected Instance Methods

load_spec_as_component(spec) click to toggle source
# File lib/gemterms/gem_filer.rb, line 84
def load_spec_as_component(spec)
  if spec.licenses.nil? || spec.licenses == []
    licenses = []
    if (spec.source.class == Bundler::Source::Rubygems) && !disable_api 
      puts "Getting missing license data from gem source (use --disable-api to skip) " if @sources.length == 0
      STDOUT.print(".") & STDOUT.flush
      licenses = rubygem_licences_from_spec(spec)
    end
  else
    licenses = spec.licenses
  end
  component = Component.new(spec.name, spec.version, @licenser.rubygem_licenses(licenses))
end
load_specs() click to toggle source
# File lib/gemterms/gem_filer.rb, line 71
def load_specs
  # @todo Do we include *all* dependencies of a given gem here? Technically
  # they are not in use, but they are linked to the gem. Maybe one for
  # --very-strict mode
  @sources = {}
  @specs.each do |spec|
    spec = spec.__materialize__ if Bundler::LazySpecification == spec
    @project << load_spec_as_component(spec)
  end
  puts "\n\n" if @sources.length > 0
  @project
end
rubygem_licences_from_spec(spec) click to toggle source

Iterates over the remotes in the spec, using the API to access rubygem data. If a particular remote ever fails, it’s not tried again.

@param [ Gem::Specification ] spec The specification to source @return [ Array<String> ] the array of license strings (can be empty)

# File lib/gemterms/gem_filer.rb, line 103
def rubygem_licences_from_spec(spec)
  # Try every remote until we (hopefully) get a result
  licenses = spec.source.remotes.each do |remote|
    begin
      source = @sources[remote]
      next if source == :unavailable
      @sources[remote] = source = RubyGems.new(remote) if source.nil?
      licenses = rubygem_licenses_from_versions(spec, source)
      return licenses unless licenses.nil?
    rescue SourceUnavailableError => sae
      @sources[source] = :unavailable
      nil
    end
  end
  []
end
rubygem_licenses_from_versions(spec, source) click to toggle source
# File lib/gemterms/gem_filer.rb, line 120
def rubygem_licenses_from_versions(spec, source)
  versions = source.versions(spec.name)

  # Try for an exact match. If this has license information, we'll use it.
  version = versions.detect { |v| v["number"] == spec.version.to_s }
  licenses = version.nil? ? nil : version["licenses"]

  # Try for any later version. e.g. Rails 4 is marked as MIT licensed,
  # but earlier versions aren't. We assume MIT for the earlier versions.
  # @todo this should be disabled when a --strict mode is introduced.
  if licenses.nil? || licenses == []
    version = versions.detect do |v| 
      (Gem::Version.new(v["number"]) > spec.version) && 
        !v["licenses"].nil? && v["licenses"].length > 0
    end
    licenses = version.nil? ? nil : version["licenses"] 
  end
  
  licenses
end