require 'uri'

module URI
  # Extends stdlib URI to include 'Redis' as a valid scheme with the correct behaviour
  # The API is a little weird and doesn't follow our styleguide
  #
  # A snipet on how to create custom URIs can be found here:
  # https://github.com/ruby/ruby/blob/trunk/lib/uri.rb
  #
  # All the methods in this class overrides methods from:
  # https://github.com/ruby/ruby/blob/trunk/lib/uri/generic.rb
  class Redis < URI::Generic
    # Default port for Redis (when defined or empty will make URL not include it)
    DEFAULT_PORT ||= 6379

    # Components definition that are part of this URI type
    CUSTOM_COMPONENT ||= [
      :scheme,
      :password,
      :host,
      :port,
      :path,
      :query
    ].freeze

    # Include this class as the handler for 'redis' and 'rediss' schemes
    # This allows URI('redis://') or URI('rediss://') to delegate to this class
    %w(REDIS REDISS).each do |scheme|
      # https://github.com/ruby/uri/pull/26 modified how schemes are registered.
      if Gem::Version.new(URI::VERSION) >= Gem::Version.new('0.11.0')
        ::URI.register_scheme scheme, Redis
      else
        @@schemes[scheme] = Redis
      end
    end

    def self.build(args)
      super(Util.make_components_hash(self, args))
    end

    # We are overriding this class to point to CUSTOM_COMPONENT instead of COMPONENT
    # to prevent CONSTANT modification warning.
    def self.component
      CUSTOM_COMPONENT
    end

    # Syntax suggar so we can call `URI::Redis.parse()` instead of just `URI()`
    def self.parse(value)
      URI.parse(value)
    end

    # Outputs the authentication part of the URL: `:password` followed by `@` divider
    # (added by the URL builder)
    def userinfo
      @password.nil? ? nil : ':' + @password
    end

    protected

    def check_password(value)
      return true if value.nil? || value.empty?

      # check the password component for RFC2396 compliance
      # and against the URI::Parser Regexp for :USERINFO
      return true if parser.regexp[:USERINFO].match?(value)

      raise InvalidComponentError, "bad password component"
    end
  end
end
