Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

vistahigherlearning / logstash   deb

Repository URL to install this package:

/ opt / logstash / lib / pluginmanager / gemfile.rb

# encoding: utf-8
module LogStash
  class GemfileError < StandardError; end

  class Gemfile
    attr_accessor :gemset

    HEADER = \
      "# This is a Logstash generated Gemfile.\n" + \
      "# If you modify this file manually all comments and formatting will be lost.\n\n"

    # @params io [IO] any IO object that supports read, write, truncate, rewind
    def initialize(io)
      @io = io
      @gemset = nil
    end

    def load
      @gemset ||= DSL.parse(@io.read)
      backup
      self
    end

    def save
      raise(GemfileError, "a Gemfile must first be loaded") unless @gemset
      @io.truncate(0)
      @io.rewind
      @io.write(HEADER)
      @io.write(@gemset.to_s)
      @io.flush
    end

    def find(name)
      @gemset.find_gem(name)
    end

    # @param name [String] gem name
    # @param *requirements params following name use the same notation as the Gemfile gem DSL statement
    # @raise GemfileError if gem already exists in Gemfile
    def add(name, *requirements)
      @gemset.add_gem(Gem.parse(name, *requirements))
    end

    # update existing or add new
    # @param name [String] gem name
    # @param *requirements params following name use the same notation as the Gemfile gem DSL statement
    def update(name, *requirements)
      @gemset.update_gem(Gem.parse(name, *requirements))
    end

    # @return [Gem] removed gem or nil if not found
    def remove(name)
      @gemset.remove_gem(name)
    end

    def backup
      @original_backup = @gemset.copy
    end

    def restore
      @gemset = @original_backup
    end

    def restore!
      restore
      save
    end

    def locally_installed_gems
      @gemset.gems.select { |gem| gem.options.include?(:path) }
    end
  end

  class Gemset
    attr_accessor :sources, :gems, :gemspec

    def initialize
      @sources = []      # list of urls
      @gems = []         # list of Gem class
      @gems_by_name = {} # hash of name => Gem
      @gemspec = {}      # gemspec is a options hash
    end

    def to_s
      [sources_to_s, gemspec_to_s, gems_to_s].select{|s| !s.empty?}.join("\n") + "\n"
    end

    # @return [Gem] found gem or nil if not found
    def find_gem(name)
      @gems_by_name[name.downcase]
    end

    # @raise GemfileError if gem already exists
    def add_gem(_gem)
      raise(GemfileError, "duplicate gem #{_gem.name}") if find_gem(_gem.name)
      @gems << _gem
      @gems_by_name[_gem.name.downcase] = _gem
    end

    # update existing or add new
    def update_gem(_gem)
      if old = find_gem(_gem.name)
        @gems[@gems.index(old)] = _gem
      else
        @gems << _gem
      end
      @gems_by_name[_gem.name.downcase] = _gem
    end

    # @return [Gem] removed gem or nil if not found
    def remove_gem(name)
      if _gem = @gems_by_name.delete(name.downcase)
        @gems.delete_at(@gems.index(_gem))
      end
      _gem
    end

    # deep clone self
    def copy
      Marshal.load(Marshal.dump(self))
    end
    private

    def sources_to_s
      return "" if @sources.empty?
      @sources.map{|source| "source #{source.inspect}"}.join("\n")
    end

    def gems_to_s
      return "" if @gems.empty?
      @gems.map do |gem|
        requirements = gem.requirements.empty? ? nil : gem.requirements.map{|r| r.inspect}.join(", ")
        options = gem.options.empty? ? nil : gem.options.map{|k, v| "#{k.inspect} => #{v.inspect}"}.join(", ")
        "gem " + [gem.name.inspect, requirements, options].compact.join(", ")
      end.join("\n")
    end

    def gemspec_to_s
      return "" if @gemspec.empty?
      options = @gemspec.map{|k, v| "#{k.inspect} => #{v.inspect}"}.join(", ")
      "gemspec #{options}"
    end
  end

  # DSL is a minimal, incomplete Gemfile DSL subset parser, only what is currently required is implemented.
  class DSL
    attr_reader :gemset

    def initialize
      @gemset = Gemset.new
    end

    # @param gemfile_content [String] the Gemfile string content
    # @return [Gemset] parsed Gemfile content as a Gemset
    def self.parse(gemfile_content)
      dsl = self.new
      dsl.instance_eval(gemfile_content)
      dsl.gemset
    end

    # DSL methods

    def source(url)
      @gemset.sources << url
    end

    def gem(name, *requirements)
      parsed = Gem.parse(name, *requirements)
      @gemset.add_gem(parsed)
    end

    def gemspec(options = {})
      raise(GemfileError, "cannot declare multiple gemspec directives") unless @gemset.gemspec.empty?
      @gemset.gemspec = options
    end
  end

  class Gem
    attr_accessor :name, :requirements, :options

    def initialize(name, requirements = [], options = {})
      @name = name
      @requirements = requirements.map{|r| r.to_s.strip}.select{|r| !r.empty?}
      @options = options
    end

    def self.parse(name, *requirements)
      options = requirements.last.is_a?(Hash) ? requirements.pop : {}
      self.new(name, requirements, options)
    end
  end
end