123456789_123456789_123456789_123456789_123456789_

Class: Gem::RequestSet::Lockfile::Parser

Relationships & Source Files
Inherits: Object
Defined in: lib/rubygems/request_set/lockfile/parser.rb

Class Method Summary

Instance Method Summary

Constructor Details

.new(tokenizer, set, platforms, filename = nil) ⇒ Parser

Parses lockfiles

[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 7

def initialize(tokenizer, set, platforms, filename = nil)
  @tokens    = tokenizer
  @filename  = filename
  @set       = set
  @platforms = platforms
end

Instance Method Details

#get(expected_types = nil, expected_value = nil)

This method is for internal use only.

Gets the next token for a ::Gem::RequestSet::Lockfile

[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 45

def get(expected_types = nil, expected_value = nil) # :nodoc:
  token = @tokens.shift

  if expected_types && !Array(expected_types).include?(token.type)
    unget token

    message = "unexpected token [#{token.type.inspect}, #{token.value.inspect}], " \
              "expected #{expected_types.inspect}"

    raise Gem::RequestSet::Lockfile::ParseError.new message, token.column, token.line, @filename
  end

  if expected_value && expected_value != token.value
    unget token

    message = "unexpected token [#{token.type.inspect}, #{token.value.inspect}], " \
              "expected [#{expected_types.inspect}, " \
              "#{expected_value.inspect}]"

    raise Gem::RequestSet::Lockfile::ParseError.new message, token.column, token.line, @filename
  end

  token
end

#parse

[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 14

def parse
  until @tokens.empty? do
    token = get

    case token.type
    when :section then
      @tokens.skip :newline

      case token.value
      when "DEPENDENCIES" then
        parse_DEPENDENCIES
      when "GIT" then
        parse_GIT
      when "GEM" then
        parse_GEM
      when "PATH" then
        parse_PATH
      when "PLATFORMS" then
        parse_PLATFORMS
      else
        token = get until @tokens.empty? || peek.first == :section
      end
    else
      raise "BUG: unhandled token #{token.type} (#{token.value.inspect}) at line #{token.line} column #{token.column}"
    end
  end
end

#parse_DEPENDENCIES

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 70

def parse_DEPENDENCIES # :nodoc:
  while !@tokens.empty? && peek.type == :text do
    token = get :text

    requirements = []

    case peek[0]
    when :bang then
      get :bang

      requirements << pinned_requirement(token.value)
    when :l_paren then
      get :l_paren

      loop do
        op      = get(:requirement).value
        version = get(:text).value

        requirements << "#{op} #{version}"

        break unless peek.type == :comma

        get :comma
      end

      get :r_paren

      if peek[0] == :bang
        requirements.clear
        requirements << pinned_requirement(token.value)

        get :bang
      end
    end

    @set.gem token.value, *requirements

    skip :newline
  end
end

#parse_dependency(name, op)

This method is for internal use only.

Parses the requirements following the dependency name and the op for the first token of the requirements and returns a ::Gem::Dependency object.

[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 298

def parse_dependency(name, op) # :nodoc:
  return Gem::Dependency.new name, op unless peek[0] == :text

  version = get(:text).value

  requirements = ["#{op} #{version}"]

  while peek.type == :comma do
    get :comma
    op      = get(:requirement).value
    version = get(:text).value

    requirements << "#{op} #{version}"
  end

  Gem::Dependency.new name, requirements
end

#parse_GEM

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 111

def parse_GEM # :nodoc:
  sources = []

  while peek.first(2) == [:entry, "remote"] do
    get :entry, "remote"
    data = get(:text).value
    skip :newline

    sources << Gem::Source.new(data)
  end

  sources << Gem::Source.new(Gem::DEFAULT_HOST) if sources.empty?

  get :entry, "specs"

  skip :newline

  set = Gem::Resolver::LockSet.new sources
  last_specs = nil

  while !@tokens.empty? && peek.type == :text do
    token = get :text
    name = token.value
    column = token.column

    case peek[0]
    when :newline then
      last_specs.each do |spec|
        spec.add_dependency Gem::Dependency.new name if column == 6
      end
    when :l_paren then
      get :l_paren

      token = get [:text, :requirement]
      type = token.type
      data = token.value

      if type == :text && column == 4
        version, platform = data.split "-", 2

        platform =
          platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY

        last_specs = set.add name, version, platform
      else
        dependency = parse_dependency name, data

        last_specs.each do |spec|
          spec.add_dependency dependency
        end
      end

      get :r_paren
    else
      raise "BUG: unknown token #{peek}"
    end

    skip :newline
  end

  @set.sets << set
end

#parse_GIT

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 174

def parse_GIT # :nodoc:
  get :entry, "remote"
  repository = get(:text).value

  skip :newline

  get :entry, "revision"
  revision = get(:text).value

  skip :newline

  type = peek.type
  value = peek.value
  if type == :entry && %w[branch ref tag].include?(value)
    get
    get :text

    skip :newline
  end

  get :entry, "specs"

  skip :newline

  set = Gem::Resolver::GitSet.new
  set.root_dir = @set.install_dir

  last_spec = nil

  while !@tokens.empty? && peek.type == :text do
    token = get :text
    name = token.value
    column = token.column

    case peek[0]
    when :newline then
      last_spec.add_dependency Gem::Dependency.new name if column == 6
    when :l_paren then
      get :l_paren

      token = get [:text, :requirement]
      type = token.type
      data = token.value

      if type == :text && column == 4
        last_spec = set.add_git_spec name, data, repository, revision, true
      else
        dependency = parse_dependency name, data

        last_spec.add_dependency dependency
      end

      get :r_paren
    else
      raise "BUG: unknown token #{peek}"
    end

    skip :newline
  end

  @set.sets << set
end

#parse_PATH

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 237

def parse_PATH # :nodoc:
  get :entry, "remote"
  directory = get(:text).value

  skip :newline

  get :entry, "specs"

  skip :newline

  set = Gem::Resolver::VendorSet.new
  last_spec = nil

  while !@tokens.empty? && peek.first == :text do
    token = get :text
    name = token.value
    column = token.column

    case peek[0]
    when :newline then
      last_spec.add_dependency Gem::Dependency.new name if column == 6
    when :l_paren then
      get :l_paren

      token = get [:text, :requirement]
      type = token.type
      data = token.value

      if type == :text && column == 4
        last_spec = set.add_vendor_gem name, directory
      else
        dependency = parse_dependency name, data

        last_spec.dependencies << dependency
      end

      get :r_paren
    else
      raise "BUG: unknown token #{peek}"
    end

    skip :newline
  end

  @set.sets << set
end

#parse_PLATFORMS

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 284

def parse_PLATFORMS # :nodoc:
  while !@tokens.empty? && peek.first == :text do
    name = get(:text).value

    @platforms << name

    skip :newline
  end
end

#peek (private)

This method is for internal use only.

Peeks at the next token for ::Gem::RequestSet::Lockfile

[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 325

def peek # :nodoc:
  @tokens.peek
end

#pinned_requirement(name) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 329

def pinned_requirement(name) # :nodoc:
  requirement = Gem::Dependency.new name
  specification = @set.sets.flat_map do |set|
    set.find_all(requirement)
  end.compact.first

  specification&.version
end

#skip(type) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 318

def skip(type) # :nodoc:
  @tokens.skip type
end

#unget(token) (private)

This method is for internal use only.

Ungets the last token retrieved by #get

[ GitHub ]

  
# File 'lib/rubygems/request_set/lockfile/parser.rb', line 341

def unget(token) # :nodoc:
  @tokens.unshift token
end