pry 0.10.4 → 0.11.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +45 -18
- data/LICENSE +1 -1
- data/README.md +28 -26
- data/bin/pry +3 -7
- data/lib/pry.rb +3 -2
- data/lib/pry/basic_object.rb +6 -0
- data/lib/pry/cli.rb +39 -34
- data/lib/pry/code.rb +6 -1
- data/lib/pry/code/code_file.rb +8 -2
- data/lib/pry/code_object.rb +23 -0
- data/lib/pry/color_printer.rb +11 -8
- data/lib/pry/command.rb +40 -16
- data/lib/pry/command_set.rb +9 -2
- data/lib/pry/commands/cat/exception_formatter.rb +11 -10
- data/lib/pry/commands/cat/file_formatter.rb +7 -3
- data/lib/pry/commands/code_collector.rb +16 -14
- data/lib/pry/commands/easter_eggs.rb +9 -9
- data/lib/pry/commands/edit.rb +6 -2
- data/lib/pry/commands/edit/file_and_line_locator.rb +1 -1
- data/lib/pry/commands/find_method.rb +1 -1
- data/lib/pry/commands/gem_open.rb +1 -1
- data/lib/pry/commands/gem_readme.rb +25 -0
- data/lib/pry/commands/gem_search.rb +40 -0
- data/lib/pry/commands/hist.rb +2 -2
- data/lib/pry/commands/jump_to.rb +7 -7
- data/lib/pry/commands/ls/formatter.rb +1 -0
- data/lib/pry/commands/ls/jruby_hacks.rb +2 -2
- data/lib/pry/commands/ls/self_methods.rb +2 -0
- data/lib/pry/commands/play.rb +2 -2
- data/lib/pry/commands/reload_code.rb +2 -2
- data/lib/pry/commands/ri.rb +4 -0
- data/lib/pry/commands/shell_command.rb +34 -8
- data/lib/pry/commands/show_info.rb +10 -2
- data/lib/pry/commands/watch_expression/expression.rb +1 -1
- data/lib/pry/commands/whereami.rb +6 -6
- data/lib/pry/config.rb +3 -16
- data/lib/pry/config/behavior.rb +139 -49
- data/lib/pry/config/default.rb +21 -33
- data/lib/pry/config/lazy.rb +25 -0
- data/lib/pry/editor.rb +1 -1
- data/lib/pry/exceptions.rb +1 -1
- data/lib/pry/helpers/base_helpers.rb +6 -10
- data/lib/pry/helpers/documentation_helpers.rb +1 -0
- data/lib/pry/helpers/options_helpers.rb +1 -1
- data/lib/pry/helpers/text.rb +69 -76
- data/lib/pry/history.rb +22 -1
- data/lib/pry/history_array.rb +1 -1
- data/lib/pry/hooks.rb +48 -107
- data/lib/pry/indent.rb +6 -2
- data/lib/pry/input_completer.rb +118 -118
- data/lib/pry/method.rb +13 -13
- data/lib/pry/method/disowned.rb +1 -0
- data/lib/pry/method/patcher.rb +0 -3
- data/lib/pry/output.rb +37 -38
- data/lib/pry/pager.rb +11 -8
- data/lib/pry/plugins.rb +20 -5
- data/lib/pry/pry_class.rb +29 -3
- data/lib/pry/pry_instance.rb +8 -6
- data/lib/pry/repl.rb +37 -5
- data/lib/pry/repl_file_loader.rb +1 -1
- data/lib/pry/rubygem.rb +3 -1
- data/lib/pry/slop.rb +661 -0
- data/lib/pry/slop/LICENSE +20 -0
- data/lib/pry/slop/commands.rb +196 -0
- data/lib/pry/slop/option.rb +208 -0
- data/lib/pry/terminal.rb +16 -5
- data/lib/pry/test/helper.rb +11 -2
- data/lib/pry/version.rb +1 -1
- data/lib/pry/wrapped_module.rb +5 -5
- data/lib/pry/{module_candidate.rb → wrapped_module/candidate.rb} +2 -4
- metadata +14 -20
@@ -7,7 +7,7 @@ class Pry
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def from_code_object(code_object, filename_argument)
|
10
|
-
if File.
|
10
|
+
if File.exist?(code_object.source_file.to_s)
|
11
11
|
[code_object.source_file, code_object.source_line]
|
12
12
|
else
|
13
13
|
raise CommandError, "Cannot find a file for #{filename_argument}!"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Pry::Command::GemReadme < Pry::ClassCommand
|
2
|
+
match 'gem-readme'
|
3
|
+
description 'Show the readme bundled with a rubygem'
|
4
|
+
group 'Gems'
|
5
|
+
command_options argument_required: true
|
6
|
+
banner <<-BANNER
|
7
|
+
gem-readme gem
|
8
|
+
Show the readme bundled with a rubygem
|
9
|
+
BANNER
|
10
|
+
|
11
|
+
def process(name)
|
12
|
+
spec = Gem::Specification.find_by_name(name)
|
13
|
+
glob = File.join(spec.full_gem_path, 'README*')
|
14
|
+
readme = Dir[glob][0]
|
15
|
+
if File.exist?(readme.to_s)
|
16
|
+
_pry_.pager.page File.read(readme)
|
17
|
+
else
|
18
|
+
raise Pry::CommandError, "Gem '#{name}' doesn't appear to have a README"
|
19
|
+
end
|
20
|
+
rescue Gem::LoadError
|
21
|
+
raise Pry::CommandError, "Gem '#{name}' wasn't found. Are you sure it is installed?"
|
22
|
+
end
|
23
|
+
|
24
|
+
Pry::Commands.add_command(self)
|
25
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Pry::Command::GemSearch < Pry::ClassCommand
|
2
|
+
match 'gem-search'
|
3
|
+
description 'Search for a gem with the rubygems.org JSON API'
|
4
|
+
group 'Gems'
|
5
|
+
command_options argument_required: true
|
6
|
+
banner <<-BANNER
|
7
|
+
gem-search [options] gem
|
8
|
+
Search for a gem with the rubygems.org HTTP API
|
9
|
+
BANNER
|
10
|
+
|
11
|
+
API_ENDPOINT = 'https://rubygems.org/api/v1/search.json'
|
12
|
+
|
13
|
+
def setup
|
14
|
+
require 'json' unless defined?(JSON)
|
15
|
+
require 'net/http' unless defined?(Net::HTTP)
|
16
|
+
end
|
17
|
+
|
18
|
+
def options(opt)
|
19
|
+
opt.on :l, :limit, 'Limit the number of results (max: 30)',
|
20
|
+
default: 10,
|
21
|
+
as: Integer,
|
22
|
+
argument: true
|
23
|
+
end
|
24
|
+
|
25
|
+
def process(str)
|
26
|
+
uri = URI.parse(API_ENDPOINT)
|
27
|
+
uri.query = URI.encode_www_form(query: str)
|
28
|
+
gems = JSON.load Net::HTTP.get(uri)
|
29
|
+
_pry_.pager.page list_as_string(gems, opts[:limit])
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def list_as_string(gems, limit = 10)
|
34
|
+
gems[0..limit-1].map do |gem|
|
35
|
+
name, version, info = gem.values_at 'name', 'version', 'info'
|
36
|
+
"#{text.bold(name)} #{text.bold('v'+version)} \n#{info}\n\n"
|
37
|
+
end.join
|
38
|
+
end
|
39
|
+
Pry::Commands.add_command(self)
|
40
|
+
end
|
data/lib/pry/commands/hist.rb
CHANGED
data/lib/pry/commands/jump_to.rb
CHANGED
@@ -9,18 +9,18 @@ class Pry
|
|
9
9
|
BANNER
|
10
10
|
|
11
11
|
def process(break_level)
|
12
|
-
break_level
|
13
|
-
nesting_level
|
12
|
+
break_level = break_level.to_i
|
13
|
+
nesting_level = _pry_.binding_stack.size - 1
|
14
|
+
max_nest_level = nesting_level - 1
|
14
15
|
|
15
16
|
case break_level
|
16
17
|
when nesting_level
|
17
18
|
output.puts "Already at nesting level #{nesting_level}"
|
18
|
-
when
|
19
|
-
_pry_.binding_stack
|
20
|
-
|
19
|
+
when 0..max_nest_level
|
20
|
+
_pry_.binding_stack = _pry_.binding_stack[0..break_level]
|
21
21
|
else
|
22
|
-
|
23
|
-
|
22
|
+
output.puts "Invalid nest level. Must be between 0 and " \
|
23
|
+
"#{max_nest_level}. Got #{break_level}."
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -19,7 +19,7 @@ module Pry::Command::Ls::JRubyHacks
|
|
19
19
|
m.name.sub(/\A(is|get|set)(?=[A-Z_])/, '').gsub(/[_?=]/, '').downcase
|
20
20
|
end
|
21
21
|
|
22
|
-
grouped.
|
22
|
+
grouped.flat_map do |key, values|
|
23
23
|
values = values.sort_by do |m|
|
24
24
|
rubbishness(m.name)
|
25
25
|
end
|
@@ -28,7 +28,7 @@ module Pry::Command::Ls::JRubyHacks
|
|
28
28
|
values.select do |x|
|
29
29
|
(!found.any? { |y| x == y }) && found << x
|
30
30
|
end
|
31
|
-
end
|
31
|
+
end
|
32
32
|
end
|
33
33
|
|
34
34
|
# When removing jruby aliases, we want to keep the alias that is
|
data/lib/pry/commands/play.rb
CHANGED
@@ -12,7 +12,7 @@ class Pry
|
|
12
12
|
|
13
13
|
play --lines 149..153 # assumes current context
|
14
14
|
play -i 20 --lines 1..3 # assumes lines of the input expression at 20
|
15
|
-
play -o 4 # the output of
|
15
|
+
play -o 4 # the output of an expression at 4
|
16
16
|
play Pry#repl -l 1..-1 # play the contents of Pry#repl method
|
17
17
|
play -e 2 # play from specified line until end of valid expression
|
18
18
|
play hello.rb # play a file
|
@@ -91,7 +91,7 @@ class Pry
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def file_content
|
94
|
-
if default_file && File.
|
94
|
+
if default_file && File.exist?(default_file)
|
95
95
|
@cc.restrict_to_lines(File.read(default_file), @cc.line_range)
|
96
96
|
else
|
97
97
|
raise CommandError, "File does not exist! File was: #{default_file.inspect}"
|
@@ -31,7 +31,7 @@ class Pry
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def reload_current_file
|
34
|
-
if !File.
|
34
|
+
if !File.exist?(current_file)
|
35
35
|
raise CommandError, "Current file: #{current_file} cannot be found on disk!"
|
36
36
|
end
|
37
37
|
|
@@ -49,7 +49,7 @@ class Pry
|
|
49
49
|
def check_for_reloadability(code_object, identifier)
|
50
50
|
if !code_object || !code_object.source_file
|
51
51
|
raise CommandError, "Cannot locate #{identifier}!"
|
52
|
-
elsif !File.
|
52
|
+
elsif !File.exist?(code_object.source_file)
|
53
53
|
raise CommandError,
|
54
54
|
"Cannot reload #{identifier} as it has no associated file on disk. " \
|
55
55
|
"File found was: #{code_object.source_file}"
|
data/lib/pry/commands/ri.rb
CHANGED
@@ -4,7 +4,7 @@ class Pry
|
|
4
4
|
group 'Input and Output'
|
5
5
|
description "All text following a '.' is forwarded to the shell."
|
6
6
|
command_options :listing => '.<shell command>', :use_prefix => false,
|
7
|
-
|
7
|
+
:takes_block => true
|
8
8
|
|
9
9
|
banner <<-'BANNER'
|
10
10
|
Usage: .COMMAND_NAME
|
@@ -30,18 +30,44 @@ class Pry
|
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
def parse_destination(dest)
|
34
|
+
return "~" if dest.empty?
|
35
|
+
return dest unless dest == "-"
|
36
|
+
state.old_pwd || raise(CommandError, "No prior directory available")
|
37
|
+
end
|
38
38
|
|
39
|
-
|
39
|
+
def process_cd(dest)
|
40
|
+
begin
|
40
41
|
state.old_pwd = Dir.pwd
|
41
|
-
Dir.chdir
|
42
|
+
Dir.chdir(File.expand_path(path_from_cd_path(dest) || dest))
|
42
43
|
rescue Errno::ENOENT
|
43
44
|
raise CommandError, "No such directory: #{dest}"
|
44
45
|
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def cd_path_env
|
49
|
+
ENV['CDPATH']
|
50
|
+
end
|
51
|
+
|
52
|
+
def cd_path_exists?
|
53
|
+
cd_path_env && cd_path_env.length.nonzero?
|
54
|
+
end
|
55
|
+
|
56
|
+
def path_from_cd_path(dest)
|
57
|
+
return if !(dest && cd_path_exists?) || special_case_path?(dest)
|
58
|
+
|
59
|
+
cd_path_env.split(File::PATH_SEPARATOR).each do |path|
|
60
|
+
if File.directory?(path) && path.split(File::SEPARATOR).last == dest
|
61
|
+
return path
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
return nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def special_case_path?(dest)
|
69
|
+
['.', '..', '-'].include?(dest) || dest =~ /\A[#{File::PATH_SEPARATOR}~]/
|
70
|
+
end
|
45
71
|
end
|
46
72
|
|
47
73
|
Pry::Commands.add_command(Pry::Command::ShellCommand)
|
@@ -22,7 +22,15 @@ class Pry
|
|
22
22
|
raise CommandError, no_definition_message if !code_object
|
23
23
|
@original_code_object = code_object
|
24
24
|
|
25
|
-
if
|
25
|
+
if !obj_name && code_object.c_module? && !opts[:all]
|
26
|
+
result = "Warning: You're inside an object, whose class is defined by means\n" +
|
27
|
+
" of the C Ruby API. Pry cannot display the information for\n" +
|
28
|
+
" this class."
|
29
|
+
if code_object.candidates.any?
|
30
|
+
result += "\n However, you can view monkey-patches applied to this class.\n" +
|
31
|
+
" Just execute the same command with the '--all' switch."
|
32
|
+
end
|
33
|
+
elsif show_all_modules?(code_object)
|
26
34
|
# show all monkey patches for a module
|
27
35
|
|
28
36
|
result = content_and_headers_for_all_module_candidates(code_object)
|
@@ -85,7 +93,7 @@ class Pry
|
|
85
93
|
end
|
86
94
|
|
87
95
|
def no_definition_message
|
88
|
-
"Couldn't locate a definition for #{obj_name}
|
96
|
+
"Couldn't locate a definition for #{obj_name}"
|
89
97
|
end
|
90
98
|
|
91
99
|
# Generate a header (meta-data information) for all the code
|
@@ -159,12 +159,12 @@ class Pry
|
|
159
159
|
end
|
160
160
|
|
161
161
|
def class_code
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
162
|
+
@class_code ||=
|
163
|
+
begin
|
164
|
+
mod = @method ? Pry::WrappedModule(@method.owner) : target_class
|
165
|
+
idx = mod.candidates.find_index { |v| expand_path(v.source_file) == @file }
|
166
|
+
idx && Pry::Code.from_module(mod, idx)
|
167
|
+
end
|
168
168
|
end
|
169
169
|
|
170
170
|
def valid_method?
|
data/lib/pry/config.rb
CHANGED
@@ -1,24 +1,11 @@
|
|
1
|
-
|
1
|
+
require_relative 'basic_object'
|
2
|
+
class Pry::Config < Pry::BasicObject
|
2
3
|
require_relative 'config/behavior'
|
4
|
+
require_relative 'config/lazy'
|
3
5
|
require_relative 'config/default'
|
4
6
|
require_relative 'config/convenience'
|
5
7
|
include Pry::Config::Behavior
|
6
|
-
|
7
8
|
def self.shortcuts
|
8
9
|
Convenience::SHORTCUTS
|
9
10
|
end
|
10
|
-
|
11
|
-
#
|
12
|
-
# FIXME
|
13
|
-
# @param [Pry::Hooks] hooks
|
14
|
-
#
|
15
|
-
def hooks=(hooks)
|
16
|
-
if hooks.is_a?(Hash)
|
17
|
-
warn "Hash-based hooks are now deprecated! Use a `Pry::Hooks` object " \
|
18
|
-
"instead! http://rubydoc.info/github/pry/pry/master/Pry/Hooks"
|
19
|
-
self["hooks"] = Pry::Hooks.from_hash(hooks)
|
20
|
-
else
|
21
|
-
self["hooks"] = hooks
|
22
|
-
end
|
23
|
-
end
|
24
11
|
end
|
data/lib/pry/config/behavior.rb
CHANGED
@@ -2,130 +2,200 @@ module Pry::Config::Behavior
|
|
2
2
|
ASSIGNMENT = "=".freeze
|
3
3
|
NODUP = [TrueClass, FalseClass, NilClass, Symbol, Numeric, Module, Proc].freeze
|
4
4
|
INSPECT_REGEXP = /#{Regexp.escape "default=#<"}/
|
5
|
+
ReservedKeyError = Class.new(RuntimeError)
|
5
6
|
|
6
7
|
module Builder
|
7
|
-
|
8
|
+
#
|
9
|
+
# @param [Hash] attributes
|
10
|
+
# a hash to initialize an instance of self with.
|
11
|
+
#
|
12
|
+
# @param [Pry::Config, nil] default
|
13
|
+
# a default, or nil for none.
|
14
|
+
#
|
15
|
+
# @return [Pry::Config]
|
16
|
+
# returns an instance of self.
|
17
|
+
#
|
18
|
+
def from_hash(attributes, default = nil)
|
8
19
|
new(default).tap do |config|
|
9
|
-
|
20
|
+
attributes.each do |key,value|
|
21
|
+
config[key] = Hash === value ? from_hash(value, nil) : value
|
22
|
+
end
|
10
23
|
end
|
11
24
|
end
|
12
25
|
end
|
13
26
|
|
14
27
|
def self.included(klass)
|
15
|
-
unless defined?(RESERVED_KEYS)
|
16
|
-
const_set :RESERVED_KEYS, instance_methods(false).map(&:to_s).freeze
|
17
|
-
end
|
18
28
|
klass.extend(Builder)
|
19
29
|
end
|
20
30
|
|
21
31
|
def initialize(default = Pry.config)
|
22
32
|
@default = default
|
23
33
|
@lookup = {}
|
34
|
+
@reserved_keys = methods.map(&:to_s).freeze
|
24
35
|
end
|
25
36
|
|
26
37
|
#
|
27
38
|
# @return [Pry::Config::Behavior]
|
28
|
-
# returns the default used
|
39
|
+
# returns the default used incase a key isn't found in self.
|
29
40
|
#
|
30
41
|
def default
|
31
42
|
@default
|
32
43
|
end
|
33
44
|
|
45
|
+
#
|
46
|
+
# @param [String] key
|
47
|
+
# a key (as a String)
|
48
|
+
#
|
49
|
+
# @return [Object, BasicObject]
|
50
|
+
# returns an object from self or one of its defaults.
|
51
|
+
#
|
34
52
|
def [](key)
|
35
|
-
|
53
|
+
key = key.to_s
|
54
|
+
@lookup[key] or (@default and @default[key])
|
36
55
|
end
|
37
56
|
|
57
|
+
#
|
58
|
+
# Add a key and value pair to self.
|
59
|
+
#
|
60
|
+
# @param [String] key
|
61
|
+
# a key (as a String).
|
62
|
+
#
|
63
|
+
# @param [Object,BasicObject] value
|
64
|
+
# a value.
|
65
|
+
#
|
66
|
+
# @raise [Pry::Config::ReservedKeyError]
|
67
|
+
# when 'key' is a reserved key name.
|
68
|
+
#
|
38
69
|
def []=(key, value)
|
39
70
|
key = key.to_s
|
40
|
-
if
|
41
|
-
raise
|
71
|
+
if @reserved_keys.include?(key)
|
72
|
+
raise ReservedKeyError, "It is not possible to use '#{key}' as a key name, please choose a different key name."
|
42
73
|
end
|
43
|
-
|
74
|
+
__push(key,value)
|
44
75
|
end
|
45
76
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
self[key] = value = value.dup if key == 'hooks'
|
58
|
-
value
|
59
|
-
else
|
60
|
-
nil
|
61
|
-
end
|
77
|
+
#
|
78
|
+
# Removes a key from self.
|
79
|
+
#
|
80
|
+
# @param [String] key
|
81
|
+
# a key (as a String)
|
82
|
+
#
|
83
|
+
# @return [void]
|
84
|
+
#
|
85
|
+
def forget(key)
|
86
|
+
key = key.to_s
|
87
|
+
__remove(key)
|
62
88
|
end
|
63
89
|
|
90
|
+
#
|
91
|
+
# @param [Hash, #to_h, #to_hash] other
|
92
|
+
# a hash to merge into self.
|
93
|
+
#
|
94
|
+
# @return [void]
|
95
|
+
#
|
64
96
|
def merge!(other)
|
65
|
-
other =
|
97
|
+
other = __try_convert_to_hash(other)
|
66
98
|
raise TypeError, "unable to convert argument into a Hash" unless other
|
67
99
|
other.each do |key, value|
|
68
100
|
self[key] = value
|
69
101
|
end
|
70
102
|
end
|
71
103
|
|
104
|
+
#
|
105
|
+
# @param [Hash, #to_h, #to_hash] other
|
106
|
+
# a hash to compare against the lookup table of self.
|
107
|
+
#
|
72
108
|
def ==(other)
|
73
|
-
@lookup ==
|
109
|
+
@lookup == __try_convert_to_hash(other)
|
74
110
|
end
|
75
111
|
alias_method :eql?, :==
|
76
112
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
113
|
+
#
|
114
|
+
# @param [String] key
|
115
|
+
# a key (as a String)
|
116
|
+
#
|
117
|
+
# @return [Boolean]
|
118
|
+
# returns true when "key" is a member of self.
|
119
|
+
#
|
81
120
|
def key?(key)
|
82
121
|
key = key.to_s
|
83
122
|
@lookup.key?(key)
|
84
123
|
end
|
85
124
|
|
125
|
+
#
|
126
|
+
# Clear the lookup table of self.
|
127
|
+
#
|
128
|
+
# @return [void]
|
129
|
+
#
|
86
130
|
def clear
|
87
131
|
@lookup.clear
|
88
132
|
true
|
89
133
|
end
|
90
|
-
alias_method :refresh, :clear
|
91
|
-
|
92
|
-
def forget(key)
|
93
|
-
@lookup.delete(key.to_s)
|
94
|
-
end
|
95
134
|
|
135
|
+
#
|
136
|
+
# @return [Array<String>]
|
137
|
+
# returns an array of keys in self.
|
138
|
+
#
|
96
139
|
def keys
|
97
140
|
@lookup.keys
|
98
141
|
end
|
99
142
|
|
143
|
+
def eager_load!
|
144
|
+
local_last_default = last_default
|
145
|
+
local_last_default.lazy_keys.each do |key|
|
146
|
+
self[key] = local_last_default.public_send(key)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def last_default
|
151
|
+
last = @default
|
152
|
+
last = last.default while last and last.default
|
153
|
+
last
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# @return [Hash]
|
158
|
+
# returns a duplicate copy of the lookup table used by self.
|
159
|
+
#
|
100
160
|
def to_hash
|
101
161
|
@lookup.dup
|
102
162
|
end
|
103
163
|
alias_method :to_h, :to_hash
|
104
164
|
|
105
|
-
|
106
165
|
def inspect
|
107
166
|
key_str = keys.map { |key| "'#{key}'" }.join(",")
|
108
|
-
"#<#{
|
167
|
+
"#<#{__clip_inspect(self)} keys=[#{key_str}] default=#{@default.inspect}>"
|
109
168
|
end
|
110
169
|
|
111
170
|
def pretty_print(q)
|
112
171
|
q.text inspect[1..-1].gsub(INSPECT_REGEXP, "default=<")
|
113
172
|
end
|
114
173
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
174
|
+
def method_missing(name, *args, &block)
|
175
|
+
key = name.to_s
|
176
|
+
if key[-1] == ASSIGNMENT
|
177
|
+
short_key = key[0..-2]
|
178
|
+
self[short_key] = args[0]
|
179
|
+
elsif key?(key)
|
180
|
+
self[key]
|
181
|
+
elsif @default.respond_to?(name)
|
182
|
+
value = @default.public_send(name, *args, &block)
|
183
|
+
self[key] = __dup(value)
|
123
184
|
else
|
124
|
-
|
185
|
+
nil
|
125
186
|
end
|
126
187
|
end
|
127
188
|
|
128
|
-
def
|
189
|
+
def respond_to_missing?(key, include_private=false)
|
190
|
+
key?(key) or @default.respond_to?(key) or super(key, include_private)
|
191
|
+
end
|
192
|
+
|
193
|
+
private
|
194
|
+
def __clip_inspect(obj)
|
195
|
+
"#{obj.class}:0x%x" % obj.object_id
|
196
|
+
end
|
197
|
+
|
198
|
+
def __try_convert_to_hash(obj)
|
129
199
|
if Hash === obj
|
130
200
|
obj
|
131
201
|
elsif obj.respond_to?(:to_h)
|
@@ -136,4 +206,24 @@ private
|
|
136
206
|
nil
|
137
207
|
end
|
138
208
|
end
|
209
|
+
|
210
|
+
def __dup(value)
|
211
|
+
if NODUP.any? { |klass| klass === value }
|
212
|
+
value
|
213
|
+
else
|
214
|
+
value.dup
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def __push(key,value)
|
219
|
+
unless singleton_class.method_defined? key
|
220
|
+
define_singleton_method(key) { self[key] }
|
221
|
+
define_singleton_method("#{key}=") { |val| @lookup[key] = val }
|
222
|
+
end
|
223
|
+
@lookup[key] = value
|
224
|
+
end
|
225
|
+
|
226
|
+
def __remove(key)
|
227
|
+
@lookup.delete(key)
|
228
|
+
end
|
139
229
|
end
|