class Bundler::LockfileParser - bundler: Ruby Standard Library Documentation

# class Bundler::LockfileParser

### Constants

CHECKSUMS
DEPENDENCIES
ENVIRONMENT_VERSION_SECTIONS
GEM
GIT
KNOWN_SECTIONS
NAME_VERSION
OPTIONS
PATH
PLATFORMS
PLUGIN
RUBY
SECTIONS_BY_VERSION_INTRODUCED
SOURCE
SPECS
TYPES

### Attributes

bundler_version[R]
checksums[R]
dependencies[R]
most_specific_locked_platform[R]
platforms[R]
ruby_version[R]
sources[R]
specs[R]

### Public Class Methods

bundled_with() click to toggle source

    # File bundler/lockfile_parser.rb, line 87
    def self.bundled_with
      lockfile = Bundler.default_lockfile
      return unless lockfile.file?

      lockfile_contents = Bundler.read_file(lockfile)
      return unless lockfile_contents.include?(BUNDLED)

      lockfile_contents.split(BUNDLED).last.strip
    end

new(lockfile) click to toggle source

    # File bundler/lockfile_parser.rb, line 97
    def initialize(lockfile)
      @platforms    = []
      @sources      = []
      @dependencies = {}
      @parse_method = nil
      @specs        = {}
      @lockfile_path = begin
        SharedHelpers.relative_lockfile_path
      rescue GemfileNotFound
        "Gemfile.lock"
      end
      @pos = Position.new(1, 1)

      if lockfile.match?(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/)
        raise LockfileError, "Your #{@lockfile_path} contains merge conflicts.\n" \
          "Run `git checkout HEAD -- #{@lockfile_path}` first to get a clean lock."
      end

      lockfile.split(/((?:\r?\n)+)/) do |line|
        # split alternates between the line and the following whitespace
        next @pos.advance!(line) if line.match?(/^\s*$/)

        if SOURCE.include?(line)
          @parse_method = :parse_source
          parse_source(line)
        elsif line == DEPENDENCIES
          @parse_method = :parse_dependency
        elsif line == CHECKSUMS
          # This is a temporary solution to make this feature disabled by default
          # for all gemfiles that don't already explicitly include the feature.
          @checksums = true
          @parse_method = :parse_checksum
        elsif line == PLATFORMS
          @parse_method = :parse_platform
        elsif line == RUBY
          @parse_method = :parse_ruby
        elsif line == BUNDLED
          @parse_method = :parse_bundled_with
        elsif /^[^\s]/.match?(line)
          @parse_method = nil
        elsif @parse_method
          send(@parse_method, line)
        end
        @pos.advance!(line)
      end
      @most_specific_locked_platform = @platforms.min_by do |bundle_platform|
        platform_specificity_match(bundle_platform, local_platform)
      end
      @specs = @specs.values.sort_by!(&:full_name).each do |spec|
        spec.most_specific_locked_platform = @most_specific_locked_platform
      end
    rescue ArgumentError => e
      Bundler.ui.debug(e)
      raise LockfileError, "Your lockfile is unreadable. Run `rm #{@lockfile_path}` " \
        "and then `bundle install` to generate a new lockfile. The error occurred while " \
        "evaluating #{@lockfile_path}:#{@pos}"
    end

sections_in_lockfile(lockfile_contents) click to toggle source

    # File bundler/lockfile_parser.rb, line 66
    def self.sections_in_lockfile(lockfile_contents)
      sections = lockfile_contents.scan(/^\w[\w ]*$/)
      sections.uniq!
      sections
    end

sections_to_ignore(base_version = nil) click to toggle source

    # File bundler/lockfile_parser.rb, line 76
    def self.sections_to_ignore(base_version = nil)
      base_version &&= base_version.release
      base_version ||= Gem::Version.create("1.0")
      attributes = []
      SECTIONS_BY_VERSION_INTRODUCED.each do |version, introduced|
        next if version <= base_version
        attributes += introduced
      end
      attributes
    end

unknown_sections_in_lockfile(lockfile_contents) click to toggle source

    # File bundler/lockfile_parser.rb, line 72
    def self.unknown_sections_in_lockfile(lockfile_contents)
      sections_in_lockfile(lockfile_contents) - KNOWN_SECTIONS
    end

### Public Instance Methods

may_include_redundant_platform_specific_gems?() click to toggle source

    # File bundler/lockfile_parser.rb, line 155
    def may_include_redundant_platform_specific_gems?
      bundler_version.nil? || bundler_version < Gem::Version.new("1.16.2")
    end

### Private Instance Methods

parse_bundled_with(line) click to toggle source

    # File bundler/lockfile_parser.rb, line 286
    def parse_bundled_with(line)
      line.strip!
      return unless Gem::Version.correct?(line)
      @bundler_version = Gem::Version.create(line)
    end

parse_checksum(line) click to toggle source

    # File bundler/lockfile_parser.rb, line 236
    def parse_checksum(line)
      return unless line =~ NAME_VERSION

      spaces = $1
      return unless spaces.size == 2
      checksums = $6
      return unless checksums
      name = $2
      version = $3
      platform = $4

      version = Gem::Version.new(version)
      platform = platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
      full_name = Gem::NameTuple.new(name, version, platform).full_name
      return unless spec = @specs[full_name]

      checksums.split(",") do |lock_checksum|
        column = line.index(lock_checksum) + 1
        checksum = Checksum.from_lock(lock_checksum, "#{@lockfile_path}:#{@pos.line}:#{column}")
        spec.source.checksum_store.register(spec, checksum)
      end
    end

parse_dependency(line) click to toggle source

    # File bundler/lockfile_parser.rb, line 207
    def parse_dependency(line)
      return unless line =~ NAME_VERSION
      spaces = $1
      return unless spaces.size == 2
      name = -$2
      version = $3
      pinned = $5

      version = version.split(",").each(&:strip!) if version

      dep = Bundler::Dependency.new(name, version)

      if pinned && dep.name != "bundler"
        spec = @specs.find {|_, v| v.name == dep.name }
        dep.source = spec.last.source if spec

        # Path sources need to know what the default name / version
        # to use in the case that there are no gemspecs present. A fake
        # gemspec is created based on the version set on the dependency
        # TODO: Use the version from the spec instead of from the dependency
        if version && version.size == 1 && version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path)
          dep.source.name    = name
          dep.source.version = $1
        end
      end

      @dependencies[dep.name] = dep
    end

parse_platform(line) click to toggle source

    # File bundler/lockfile_parser.rb, line 282
    def parse_platform(line)
      @platforms << Gem::Platform.new($1.strip) if line =~ /^  (.*)$/
    end

parse_ruby(line) click to toggle source

    # File bundler/lockfile_parser.rb, line 292
    def parse_ruby(line)
      line.strip!
      @ruby_version = line
    end

parse_source(line) click to toggle source

    # File bundler/lockfile_parser.rb, line 168
    def parse_source(line)
      case line
      when SPECS
        return unless TYPES.key?(@type)
        @current_source = TYPES[@type].from_lock(@opts)
        @sources << @current_source
      when OPTIONS
        value = $2
        value = true if value == "true"
        value = false if value == "false"

        key = $1

        if @opts[key]
          @opts[key] = Array(@opts[key])
          @opts[key] << value
        else
          @opts[key] = value
        end
      when *SOURCE
        @current_source = nil
        @opts = {}
        @type = line
      else
        parse_spec(line)
      end
    end

parse_spec(line) click to toggle source

    # File bundler/lockfile_parser.rb, line 259
    def parse_spec(line)
      return unless line =~ NAME_VERSION
      spaces = $1
      name = -$2
      version = $3

      if spaces.size == 4
        # only load platform for non-dependency (spec) line
        platform = $4

        version = Gem::Version.new(version)
        platform = platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
        @current_spec = LazySpecification.new(name, version, platform, @current_source)
        @current_source.add_dependency_names(name)

        @specs[@current_spec.full_name] = @current_spec
      elsif spaces.size == 6
        version = version.split(",").each(&:strip!) if version
        dep = Gem::Dependency.new(name, version)
        @current_spec.dependencies << dep
      end
    end