1
0
Fork 0
mirror of https://github.com/pry/pry.git synced 2022-11-09 12:35:05 -05:00
pry--pry/lib/pry/config/behavior.rb
Kyrylo Silin 4b6fc303bb config: simplify structure
* 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
2018-10-28 17:45:29 +08:00

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