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

249 lines
5.1 KiB
Ruby
Raw Normal View History

module Pry::Config::Behavior
ASSIGNMENT = "=".freeze
NODUP = [TrueClass, FalseClass, NilClass, Symbol, Numeric, Module, Proc].freeze
INSPECT_REGEXP = /#{Regexp.escape "default=#<"}/
ReservedKeyError = Class.new(RuntimeError)
2014-03-17 12:27:57 +01:00
module Builder
2017-12-22 22:22:10 +01:00
#
# 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)
2014-03-17 12:27:57 +01:00
new(default).tap do |config|
2015-08-17 10:14:19 +01:00
attributes.each do |key,value|
config[key] = Hash === value ? from_hash(value, nil) : value
end
2014-03-17 12:27:57 +01:00
end
end
end
def self.included(klass)
2014-03-17 12:27:57 +01:00
klass.extend(Builder)
end
2014-01-27 07:16:10 +01:00
def initialize(default = Pry.config)
@default = default
2014-03-15 02:57:07 +01:00
@lookup = {}
@reserved_keys = methods.map(&:to_s).freeze
end
2014-03-14 05:40:09 +01:00
#
# @return [Pry::Config::Behavior]
# returns the default used incase a key isn't found in self.
2014-03-14 05:40:09 +01:00
#
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
2015-07-28 05:13:13 +01:00
#
# 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
2014-12-13 15:50:33 +00:00
__push(key,value)
end
#
# Removes a key from self.
#
# @param [String] key
2015-07-28 05:13:13 +01:00
# a key (as a String)
#
# @return [void]
#
2014-12-13 15:50:33 +00:00
def forget(key)
key = key.to_s
__remove(key)
end
#
# @param [Hash, #to_h, #to_hash] other
2015-07-28 05:13:13 +01:00
# 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
2015-07-28 05:13:13 +01:00
# a hash to compare against the lookup table of self.
#
2014-03-23 09:57:59 +01:00
def ==(other)
@lookup == __try_convert_to_hash(other)
2014-03-23 09:57:59 +01:00
end
alias_method :eql?, :==
#
# @param [String] key
# a key (as a String)
#
# @return [Boolean]
# returns true when "key" is a member of self.
#
2014-01-27 07:02:55 +01:00
def key?(key)
key = key.to_s
2014-03-15 02:57:07 +01:00
@lookup.key?(key)
end
#
# Clear the lookup table of self.
#
# @return [void]
#
2014-03-15 03:05:19 +01:00
def clear
2014-03-15 02:57:07 +01:00
@lookup.clear
true
end
#
# @return [Array<String>]
# returns an array of keys in self.
#
2014-02-02 23:23:23 +01:00
def keys
2014-03-15 02:57:07 +01:00
@lookup.keys
2014-02-02 23:23:23 +01:00
end
def eager_load!
default = @default
while default
2017-06-24 09:27:41 +01:00
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
2014-03-15 02:57:07 +01:00
@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)
2014-03-23 09:57:59 +01:00
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
2014-12-13 15:50:33 +00:00
def __dup(value)
if NODUP.any? { |klass| klass === value }
value
else
value.dup
end
end
def __push(key,value)
2015-01-22 22:52:20 +01:00
unless singleton_class.method_defined? key
define_singleton_method(key) { self[key] }
define_singleton_method("#{key}=") { |val| @lookup[key] = val }
end
2014-12-13 15:50:33 +00:00
@lookup[key] = value
end
def __remove(key)
@lookup.delete(key)
end
end