mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
4b6fc303bb
* Replace `require_relative` with `require` The project tries to use the `require` form everywhere where possible, which is the common form * `require` from `pry.rb` Spreaded `require` statements where we require internal classes is confusing * Fixed namespace definition for Config classes https://github.com/rubocop-hq/ruby-style-guide#namespace-definition recommends to use explicit nesting
255 lines
6 KiB
Ruby
255 lines
6 KiB
Ruby
class Pry
|
|
class Config < Pry::BasicObject
|
|
module Behavior
|
|
ASSIGNMENT = "=".freeze
|
|
NODUP = [TrueClass, FalseClass, NilClass, Symbol, Numeric, Module, Proc].freeze
|
|
INSPECT_REGEXP = /#{Regexp.escape "default=#<"}/
|
|
ReservedKeyError = Class.new(RuntimeError)
|
|
|
|
module Builder
|
|
#
|
|
# Returns a new Behavior, non-recursively (unlike {#from_hash}).
|
|
#
|
|
# @param
|
|
# (see #from_hash)
|
|
#
|
|
# @return
|
|
# (see #from_hash)
|
|
#
|
|
def assign(attributes, default = nil)
|
|
new(default).tap do |behavior|
|
|
behavior.merge!(attributes)
|
|
end
|
|
end
|
|
|
|
#
|
|
# Returns a new Behavior, recursively walking attributes.
|
|
#
|
|
# @param [Hash] attributes
|
|
# a hash to initialize an instance of self with.
|
|
#
|
|
# @param [Pry::Config, nil] default
|
|
# a default, or nil for none.
|
|
#
|
|
# @return [Pry::Config]
|
|
# returns an instance of self.
|
|
#
|
|
def from_hash(attributes, default = nil)
|
|
new(default).tap do |config|
|
|
attributes.each do |key,value|
|
|
config[key] = Hash === value ? from_hash(value, nil) : value
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.included(klass)
|
|
klass.extend(Builder)
|
|
end
|
|
|
|
def initialize(default = Pry.config)
|
|
@default = default
|
|
@lookup = {}
|
|
@reserved_keys = methods.map(&:to_s).freeze
|
|
end
|
|
|
|
#
|
|
# @return [Pry::Config::Behavior]
|
|
# returns the default used incase a key isn't found in self.
|
|
#
|
|
def default
|
|
@default
|
|
end
|
|
|
|
#
|
|
# @param [String] key
|
|
# a key (as a String)
|
|
#
|
|
# @return [Object, BasicObject]
|
|
# returns an object from self or one of its defaults.
|
|
#
|
|
def [](key)
|
|
key = key.to_s
|
|
key?(key) ? @lookup[key] : (@default and @default[key])
|
|
end
|
|
|
|
#
|
|
# Add a key and value pair to self.
|
|
#
|
|
# @param [String] key
|
|
# a key (as a String).
|
|
#
|
|
# @param [Object,BasicObject] value
|
|
# a value.
|
|
#
|
|
# @raise [Pry::Config::ReservedKeyError]
|
|
# when 'key' is a reserved key name.
|
|
#
|
|
def []=(key, value)
|
|
key = key.to_s
|
|
if @reserved_keys.include?(key)
|
|
raise ReservedKeyError, "It is not possible to use '#{key}' as a key name, please choose a different key name."
|
|
end
|
|
|
|
__push(key,value)
|
|
end
|
|
|
|
#
|
|
# Removes a key from self.
|
|
#
|
|
# @param [String] key
|
|
# a key (as a String)
|
|
#
|
|
# @return [void]
|
|
#
|
|
def forget(key)
|
|
key = key.to_s
|
|
__remove(key)
|
|
end
|
|
|
|
#
|
|
# @param [Hash, #to_h, #to_hash] other
|
|
# a hash to merge into self.
|
|
#
|
|
# @return [void]
|
|
#
|
|
def merge!(other)
|
|
other = __try_convert_to_hash(other)
|
|
raise TypeError, "unable to convert argument into a Hash" unless other
|
|
|
|
other.each do |key, value|
|
|
self[key] = value
|
|
end
|
|
end
|
|
|
|
#
|
|
# @param [Hash, #to_h, #to_hash] other
|
|
# a hash to compare against the lookup table of self.
|
|
#
|
|
def ==(other)
|
|
@lookup == __try_convert_to_hash(other)
|
|
end
|
|
alias_method :eql?, :==
|
|
|
|
#
|
|
# @param [String] key
|
|
# a key (as a String)
|
|
#
|
|
# @return [Boolean]
|
|
# returns true when "key" is a member of self.
|
|
#
|
|
def key?(key)
|
|
key = key.to_s
|
|
@lookup.key?(key)
|
|
end
|
|
|
|
#
|
|
# Clear the lookup table of self.
|
|
#
|
|
# @return [void]
|
|
#
|
|
def clear
|
|
@lookup.clear
|
|
true
|
|
end
|
|
|
|
#
|
|
# @return [Array<String>]
|
|
# returns an array of keys in self.
|
|
#
|
|
def keys
|
|
@lookup.keys
|
|
end
|
|
|
|
def eager_load!
|
|
default = @default
|
|
while default
|
|
default.memoized_methods.each {|method| self[key] = default.public_send(key)} if default.respond_to?(:memoized_methods)
|
|
default = @default.default
|
|
end
|
|
end
|
|
|
|
def last_default
|
|
last = @default
|
|
last = last.default while last and last.default
|
|
last
|
|
end
|
|
|
|
#
|
|
# @return [Hash]
|
|
# returns a duplicate copy of the lookup table used by self.
|
|
#
|
|
def to_hash
|
|
@lookup.dup
|
|
end
|
|
alias_method :to_h, :to_hash
|
|
|
|
def inspect
|
|
key_str = keys.map { |key| "'#{key}'" }.join(",")
|
|
"#<#{__clip_inspect(self)} keys=[#{key_str}] default=#{@default.inspect}>"
|
|
end
|
|
|
|
def pretty_print(q)
|
|
q.text inspect[1..-1].gsub(INSPECT_REGEXP, "default=<")
|
|
end
|
|
|
|
def method_missing(name, *args, &block)
|
|
key = name.to_s
|
|
if key[-1] == ASSIGNMENT
|
|
short_key = key[0..-2]
|
|
self[short_key] = args[0]
|
|
elsif key?(key)
|
|
self[key]
|
|
elsif @default.respond_to?(name)
|
|
value = @default.public_send(name, *args, &block)
|
|
self[key] = __dup(value)
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
def respond_to_missing?(key, include_all=false)
|
|
key = key.to_s.chomp(ASSIGNMENT)
|
|
key?(key) or @default.respond_to?(key) or super(key, include_all)
|
|
end
|
|
|
|
private
|
|
|
|
def __clip_inspect(obj)
|
|
"#{obj.class}:0x%x" % obj.object_id
|
|
end
|
|
|
|
def __try_convert_to_hash(obj)
|
|
if Hash === obj
|
|
obj
|
|
elsif obj.respond_to?(:to_h)
|
|
obj.to_h
|
|
elsif obj.respond_to?(:to_hash)
|
|
obj.to_hash
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
def __dup(value)
|
|
if NODUP.any? { |klass| klass === value }
|
|
value
|
|
else
|
|
value.dup
|
|
end
|
|
end
|
|
|
|
def __push(key,value)
|
|
unless singleton_class.method_defined? key
|
|
define_singleton_method(key) { self[key] }
|
|
define_singleton_method("#{key}=") { |val| @lookup[key] = val }
|
|
end
|
|
@lookup[key] = value
|
|
end
|
|
|
|
def __remove(key)
|
|
@lookup.delete(key)
|
|
end
|
|
end
|
|
end
|
|
end
|