pry 0.9.6.2 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +6 -0
  2. data/CHANGELOG +19 -1
  3. data/CONTRIBUTORS +22 -16
  4. data/Rakefile +12 -6
  5. data/bin/pry +15 -12
  6. data/lib/pry.rb +39 -28
  7. data/lib/pry/command_context.rb +1 -0
  8. data/lib/pry/command_processor.rb +9 -2
  9. data/lib/pry/command_set.rb +7 -0
  10. data/lib/pry/config.rb +8 -0
  11. data/lib/pry/default_commands/basic.rb +7 -10
  12. data/lib/pry/default_commands/context.rb +36 -26
  13. data/lib/pry/default_commands/documentation.rb +31 -123
  14. data/lib/pry/default_commands/gems.rb +9 -4
  15. data/lib/pry/default_commands/input.rb +21 -11
  16. data/lib/pry/default_commands/introspection.rb +79 -88
  17. data/lib/pry/default_commands/ls.rb +165 -176
  18. data/lib/pry/default_commands/shell.rb +8 -8
  19. data/lib/pry/extended_commands/experimental.rb +1 -5
  20. data/lib/pry/extended_commands/user_command_api.rb +17 -5
  21. data/lib/pry/helpers.rb +1 -0
  22. data/lib/pry/helpers/base_helpers.rb +5 -1
  23. data/lib/pry/helpers/command_helpers.rb +22 -241
  24. data/lib/pry/helpers/options_helpers.rb +58 -0
  25. data/lib/pry/helpers/text.rb +10 -4
  26. data/lib/pry/history.rb +1 -1
  27. data/lib/pry/indent.rb +205 -0
  28. data/lib/pry/method.rb +412 -0
  29. data/lib/pry/pry_class.rb +56 -15
  30. data/lib/pry/pry_instance.rb +63 -16
  31. data/lib/pry/rbx_method.rb +20 -0
  32. data/lib/pry/rbx_path.rb +34 -0
  33. data/lib/pry/version.rb +1 -1
  34. data/pry.gemspec +16 -16
  35. data/test/helper.rb +8 -4
  36. data/test/test_command_helpers.rb +1 -69
  37. data/test/test_command_set.rb +29 -28
  38. data/test/test_default_commands/test_documentation.rb +23 -10
  39. data/test/test_default_commands/test_introspection.rb +58 -13
  40. data/test/test_default_commands/test_ls.rb +148 -0
  41. data/test/test_indent.rb +234 -0
  42. data/test/test_input_stack.rb +13 -0
  43. data/test/test_method.rb +291 -0
  44. data/test/test_pry.rb +10 -1
  45. metadata +82 -65
@@ -4,226 +4,215 @@ class Pry
4
4
  Ls = Pry::CommandSet.new do
5
5
 
6
6
  helpers do
7
- def should_trim?(target, options)
8
- if target.eval('self').is_a? Module
9
- options[:e] || target.eval('self') >= Object
10
- else
11
- options[:e]
12
- end
13
- end
14
7
 
15
- def trim_methods(target, options, visibility)
16
- if should_trim?(target, options)
17
- []
18
- else
19
- Object.send("#{visibility}_instance_methods")
20
- end
8
+ # http://ruby.runpaint.org/globals, and running "puts global_variables.inspect".
9
+ BUILTIN_GLOBALS = %w($" $$ $* $, $-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w $. $/ $\\
10
+ $: $; $< $= $> $0 $ARGV $CONSOLE $DEBUG $DEFAULT_INPUT $DEFAULT_OUTPUT
11
+ $FIELD_SEPARATOR $FILENAME $FS $IGNORECASE $INPUT_LINE_NUMBER
12
+ $INPUT_RECORD_SEPARATOR $KCODE $LOADED_FEATURES $LOAD_PATH $NR $OFS
13
+ $ORS $OUTPUT_FIELD_SEPARATOR $OUTPUT_RECORD_SEPARATOR $PID $PROCESS_ID
14
+ $PROGRAM_NAME $RS $VERBOSE $deferr $defout $stderr $stdin $stdout)
15
+
16
+ # $SAFE and $? are thread-local, the exception stuff only works in a rescue clause,
17
+ # everything else is basically a local variable with a $ in its name.
18
+ PSEUDO_GLOBALS = %w($! $' $& $` $@ $? $+ $_ $~ $1 $2 $3 $4 $5 $6 $7 $8 $9
19
+ $CHILD_STATUS $SAFE $ERROR_INFO $ERROR_POSITION $LAST_MATCH_INFO
20
+ $LAST_PAREN_MATCH $LAST_READ_LINE $MATCH $POSTMATCH $PREMATCH)
21
+
22
+ # Get all the methods that we'll want to output
23
+ def all_methods(obj, opts)
24
+ opts.M? ? Pry::Method.all_from_class(obj) : Pry::Method.all_from_obj(obj)
21
25
  end
22
26
 
23
- def ls_color_map
24
- {
25
- "local variables" => Pry.config.ls.local_var_color,
26
- "instance variables" => Pry.config.ls.instance_var_color,
27
- "class variables" => Pry.config.ls.class_var_color,
28
- "global variables" => Pry.config.ls.global_var_color,
29
- "just singleton methods" => Pry.config.ls.method_color,
30
- "public methods" => Pry.config.ls.method_color,
31
- "private methods" => Pry.config.ls.method_color,
32
- "protected methods" => Pry.config.ls.method_color,
33
- "public instance methods" => Pry.config.ls.instance_method_color,
34
- "private instance methods" => Pry.config.ls.instance_method_color,
35
- "protected instance methods" => Pry.config.ls.instance_method_color,
36
- "constants" => Pry.config.ls.constant_color
37
- }
27
+ def resolution_order(obj, opts)
28
+ opts.M? ? Pry::Method.instance_resolution_order(obj) : Pry::Method.resolution_order(obj)
38
29
  end
39
- end
40
-
41
- command "ls", "Show the list of vars and methods in the current scope. Type `ls --help` for more info." do |*args|
42
- options = {}
43
- # Set target local to the default -- note that we can set a different target for
44
- # ls if we like: e.g ls my_var
45
- target = target()
46
-
47
- OptionParser.new do |opts|
48
- opts.banner = %{Usage: ls [OPTIONS] [VAR]\n\
49
- List information about VAR (the current context by default).
50
- Shows local and instance variables by default.
51
- --
52
- }
53
- opts.on("-g", "--globals", "Display global variables.") do
54
- options[:g] = true
55
- end
56
30
 
57
- opts.on("-c", "--constants", "Display constants.") do
58
- options[:c] = true
59
- end
60
-
61
- opts.on("-l", "--locals", "Display locals.") do
62
- options[:l] = true
63
- end
64
-
65
- opts.on("-i", "--ivars", "Display instance variables.") do
66
- options[:i] = true
67
- end
68
-
69
- opts.on("-k", "--class-vars", "Display class variables.") do
70
- options[:k] = true
71
- end
72
-
73
- opts.on("-m", "--methods", "Display methods (public methods by default).") do
74
- options[:m] = true
75
- end
76
-
77
- opts.on("-M", "--instance-methods", "Display instance methods (only relevant to classes and modules).") do
78
- options[:M] = true
79
- end
80
-
81
- opts.on("-P", "--public", "Display public methods (with -m).") do
82
- options[:P] = true
83
- end
84
-
85
- opts.on("-r", "--protected", "Display protected methods (with -m).") do
86
- options[:r] = true
87
- end
88
-
89
- opts.on("-p", "--private", "Display private methods (with -m).") do
90
- options[:p] = true
91
- end
92
-
93
- opts.on("-j", "--just-singletons", "Display just the singleton methods (with -m).") do
94
- options[:j] = true
95
- end
96
-
97
- opts.on("-s", "--super", "Include superclass entries excluding Object (relevant to constant and methods options).") do
98
- options[:s] = true
31
+ # Get the name of the klass for pretty display in the title column of ls -m
32
+ # as there can only ever be one singleton class of a non-class, we just call
33
+ # that "self".
34
+ def class_name(klass)
35
+ if klass == klass.ancestors.first
36
+ (klass.name || "") == "" ? klass.to_s : klass.name
37
+ elsif klass.ancestors.include?(Module)
38
+ begin
39
+ "#{class_name(ObjectSpace.each_object(klass).detect{ |x| class << x; self; end == klass })}.self"
40
+ rescue # ObjectSpace is not enabled by default in jruby
41
+ klass.to_s.sub(/#<Class:(.*)>/, '\1.self')
42
+ end
43
+ else
44
+ "self"
99
45
  end
46
+ end
100
47
 
101
- opts.on("-e", "--everything", "Include superclass entries including Object (must be combined with -s switch).") do
102
- options[:e] = true
103
- end
48
+ # Get a lambda that can be used with .take_while to prevent over-eager
49
+ # traversal of the Object's ancestry graph.
50
+ def below_ceiling(obj, opts)
51
+ ceiling = if opts.q?
52
+ [opts.M? ? obj.ancestors[1] : obj.class.ancestors[1]] + Pry.config.ls.ceiling
53
+ elsif opts.v?
54
+ []
55
+ else
56
+ Pry.config.ls.ceiling.dup
57
+ end
58
+
59
+ lambda { |klass| !ceiling.include?(klass) }
60
+ end
104
61
 
105
- opts.on("-a", "--all", "Display all types of entries.") do
106
- options[:a] = true
62
+ # Format and colourise a list of methods.
63
+ def format_methods(methods)
64
+ methods.sort_by(&:name).map do |method|
65
+ if method.name == 'method_missing'
66
+ color(:method_missing, 'method_missing')
67
+ elsif method.visibility == :private
68
+ color(:private_method, method.name)
69
+ elsif method.visibility == :protected
70
+ color(:protected_method, method.name)
71
+ else
72
+ color(:public_method, method.name)
73
+ end
107
74
  end
75
+ end
108
76
 
109
- opts.on("-v", "--verbose", "Verbose ouput.") do
110
- options[:v] = true
111
- end
77
+ def format_variables(type, vars)
78
+ vars.sort_by(&:downcase).map{ |var| color(type, var) }
79
+ end
112
80
 
113
- opts.on("-f", "--flood", "Do not use a pager to view text longer than one screen.") do
114
- options[:f] = true
81
+ def format_constants(mod, constants)
82
+ constants.sort_by(&:downcase).map do |name|
83
+ if const = (!mod.autoload?(name) && mod.const_get(name) rescue nil)
84
+ if (const < Exception rescue false)
85
+ color(:exception_constant, name)
86
+ elsif (Module === mod.const_get(name) rescue false)
87
+ color(:class_constant, name)
88
+ else
89
+ color(:constant, name)
90
+ end
91
+ end
115
92
  end
93
+ end
116
94
 
117
- opts.on("--grep REG", "Regular expression to be used.") do |reg|
118
- options[:grep] = Regexp.new(reg)
95
+ def format_globals(globals)
96
+ globals.sort_by(&:downcase).map do |name|
97
+ if PSEUDO_GLOBALS.include?(name)
98
+ color(:pseudo_global, name)
99
+ elsif BUILTIN_GLOBALS.include?(name)
100
+ color(:builtin_global, name)
101
+ else
102
+ color(:global_var, name)
103
+ end
119
104
  end
105
+ end
120
106
 
121
- opts.on_tail("-h", "--help", "Show this message.") do
122
- output.puts opts
123
- options[:h] = true
107
+ def format_locals(locals)
108
+ locals.sort_by(&:downcase).map do |name|
109
+ if _pry_.special_locals.include?(name.to_sym)
110
+ color(:pry_var, name)
111
+ else
112
+ color(:local_var, name)
113
+ end
124
114
  end
125
- end.order(args) do |new_target|
126
- target = Pry.binding_for(target.eval("#{new_target}")) if !options[:h]
127
115
  end
128
116
 
129
- # exit if we've displayed help
130
- next if options[:h]
131
-
132
- # default is locals/ivars/class vars.
133
- # Only occurs when no options or when only option is verbose
134
- options.merge!({
135
- :l => true,
136
- :i => true,
137
- :k => true
138
- }) if options.empty? || (options.size == 1 && options[:v]) || (options.size == 1 && options[:grep])
117
+ # Add a new section to the output. Outputs nothing if the section would be empty.
118
+ def output_section(heading, body)
119
+ return if body.compact.empty?
120
+ output.puts "#{text.bold(color(:heading, heading))}: #{body.compact.join(Pry.config.ls.separator)}"
121
+ end
139
122
 
140
- options[:grep] = // if !options[:grep]
123
+ # Color output based on config.ls.*_color
124
+ def color(type, str)
125
+ text.send(Pry.config.ls.send(:"#{type}_color"), str)
126
+ end
127
+ end
141
128
 
129
+ command "ls", "Show the list of vars and methods in the current scope. Type `ls --help` for more info.",
130
+ :shellwords => false, :interpolate => false do |*args|
142
131
 
143
- # Display public methods by default if -m or -M switch is used.
144
- options[:P] = true if (options[:m] || options[:M]) && !(options[:p] || options[:r] || options[:j])
132
+ opts = Slop.parse!(args, :strict => true) do |opt|
133
+ opt.banner unindent <<-USAGE
134
+ Usage: ls [-m|-M|-p|-pM] [-q|-v] [-c|-i] [Object]
135
+ ls [-g] [-l]
145
136
 
146
- info = {}
147
- target_self = target.eval('self')
137
+ ls shows you which methods, constants and variables are accessible to Pry. By default it shows you the local variables defined in the current shell, and any public methods or instance variables defined on the current object.
148
138
 
149
- # ensure we have a real boolean and not a `nil` (important when
150
- # interpolating in the string)
151
- options[:s] = !!options[:s]
139
+ The colours used are configurable using Pry.config.ls.*_color, and the separator is Pry.config.ls.separator.
152
140
 
153
- # Numbers (e.g 0, 1, 2) are for ordering the hash values in Ruby 1.8
154
- i = -1
141
+ Pry.config.ls.ceiling is used to hide methods defined higher up in the inheritance chain, this is by default set to [Object, Module, Class] so that methods defined on all Objects are omitted. The -v flag can be used to ignore this setting and show all methods, while the -q can be used to set the ceiling much lower and show only methods defined on the object or its direct class.
142
+ USAGE
155
143
 
156
- # Start collecting the entries selected by the user
157
- info["local variables"] = [Array(target.eval("local_variables")).sort, i += 1] if options[:l] || options[:a]
158
- info["instance variables"] = [Array(target.eval("instance_variables")).sort, i += 1] if options[:i] || options[:a]
144
+ opt.on :m, "methods", "Show public methods defined on the Object (default)"
145
+ opt.on :M, "module", "Show methods defined in a Module or Class"
159
146
 
160
- info["class variables"] = [if target_self.is_a?(Module)
161
- Array(target.eval("class_variables")).sort
162
- else
163
- Array(target.eval("self.class.class_variables")).sort
164
- end, i += 1] if options[:k] || options[:a]
147
+ opt.on :p, "ppp", "Show public, protected (in yellow) and private (in green) methods"
148
+ opt.on :q, "quiet", "Show only methods defined on object.singleton_class and object.class"
149
+ opt.on :v, "verbose", "Show methods and constants on all super-classes (ignores Pry.config.ls.ceiling)"
165
150
 
166
- info["global variables"] = [Array(target.eval("global_variables")).sort, i += 1] if options[:g] || options[:a]
151
+ opt.on :g, "globals", "Show global variables, including those builtin to Ruby (in cyan)"
152
+ opt.on :l, "locals", "Show locals, including those provided by Pry (in red)"
167
153
 
168
- info["public methods"] = [Array(target.eval("public_methods(#{options[:s]})")).uniq.sort - trim_methods(target, options, :public), i += 1] if (options[:m] && options[:P]) || options[:a]
154
+ opt.on :c, "constants", "Show constants, highlighting classes (in blue), and exceptions (in purple)"
169
155
 
170
- info["protected methods"] = [Array(target.eval("protected_methods(#{options[:s]})")).sort - trim_methods(target, options, :protected), i += 1] if (options[:m] && options[:r]) || options[:a]
156
+ opt.on :i, "ivars", "Show instance variables (in blue) and class variables (in bright blue)"
171
157
 
172
- info["private methods"] = [Array(target.eval("private_methods(#{options[:s]})")).sort - trim_methods(target, options, :private), i += 1] if (options[:m] && options[:p]) || options[:a]
158
+ opt.on :G, "grep", "Filter output by regular expression", :optional => false
173
159
 
174
- info["just singleton methods"] = [Array(target.eval("methods(#{options[:s]})")).sort, i += 1] if (options[:m] && options[:j]) && !options[:a]
160
+ opt.on :h, "help", "Show help"
161
+ end
175
162
 
176
- info["public instance methods"] = [Array(target.eval("public_instance_methods(#{options[:s]})")).uniq.sort - trim_methods(target, options, :public), i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:P]) || options[:a])
163
+ next output.puts(opts) if opts.h?
177
164
 
178
- info["protected instance methods"] = [Array(target.eval("protected_instance_methods(#{options[:s]})")).uniq.sort - trim_methods(target, options, :protected), i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:r]) || options[:a])
165
+ obj = args.empty? ? target_self : target.eval(args.join(" "))
179
166
 
180
- info["private instance methods"] = [Array(target.eval("private_instance_methods(#{options[:s]})")).uniq.sort - trim_methods(target, options, :private), i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:p]) || options[:a])
167
+ # exclude -q, -v and --grep because they don't specify what the user wants to see.
168
+ has_opts = (opts.m? || opts.M? || opts.p? || opts.g? || opts.l? || opts.c? || opts.i?)
181
169
 
182
- # dealing with 1.8/1.9 compatibility issues :/
183
- csuper = options[:s]
184
- if Module.method(:constants).arity == 0
185
- csuper = nil
186
- end
170
+ show_methods = opts.m? || opts.M? || opts.p? || !has_opts
171
+ show_constants = opts.c? || (!has_opts && Module === obj)
172
+ show_ivars = opts.i? || !has_opts
173
+ show_locals = opts.l? || (!has_opts && args.empty?)
187
174
 
188
- info["constants"] = [Array(target_self.is_a?(Module) ? target.eval("constants(#{csuper})") :
189
- target.eval("self.class.constants(#{csuper})")).uniq.sort, i += 1] if options[:c] || options[:a]
175
+ grep_regex, grep = [Regexp.new(opts[:G] || "."), lambda{ |x| x.grep(grep_regex) }]
190
176
 
191
- text = ""
177
+ raise Pry::CommandError, "-l does not make sense with a specified Object" if opts.l? && !args.empty?
178
+ raise Pry::CommandError, "-g does not make sense with a specified Object" if opts.g? && !args.empty?
179
+ raise Pry::CommandError, "-q does not make sense with -v" if opts.q? && opts.v?
180
+ raise Pry::CommandError, "-M only makes sense with a Module or a Class" if opts.M? && !(Module === obj)
181
+ raise Pry::CommandError, "-c only makes sense with a Module or a Class" if opts.c? && !args.empty? && !(Module === obj)
192
182
 
193
- # verbose output?
194
- if options[:v]
195
- # verbose
196
183
 
197
- info.sort_by { |k, v| v.last }.each do |k, v|
198
- if !v.first.empty?
199
- text << "#{k}:\n--\n"
200
- filtered_list = v.first.grep options[:grep]
201
- text << text().send(ls_color_map[k], (filtered_list.join(Pry.config.ls.separator)))
202
- text << "\n\n"
203
- end
204
- end
184
+ if opts.g?
185
+ output_section("global variables", grep[format_globals(target.eval("global_variables"))])
186
+ end
205
187
 
206
- if !options[:f]
207
- stagger_output(text)
208
- else
209
- output.puts text
210
- end
188
+ if show_constants
189
+ mod = Module === obj ? obj : Object
190
+ constants = mod.constants
191
+ constants -= (mod.ancestors - [mod]).map(&:constants).flatten unless opts.v?
192
+ output_section("constants", grep[format_constants(mod, constants)])
193
+ end
211
194
 
212
- # plain
213
- else
214
- list = info.sort_by { |k, v| v.last }.map { |k, v| [k, [v.first.grep(options[:grep])], v.last] }
215
- list = list.each { |k, v| text << text().send(ls_color_map[k], v.first.join(Pry.config.ls.separator)); text << Pry.config.ls.separator }
195
+ if show_methods
196
+ # methods is a hash {Module/Class => [Pry::Methods]}
197
+ methods = all_methods(obj, opts).select{ |method| opts.p? || method.visibility == :public }.group_by(&:owner)
216
198
 
217
- if !options[:f]
218
- stagger_output(text)
219
- else
220
- output.puts text
221
- end
222
- list
223
- end
224
- end
199
+ # reverse the resolution order so that the most useful information appears right by the prompt
200
+ resolution_order(obj, opts).take_while(&below_ceiling(obj, opts)).reverse.each do |klass|
201
+ methods_here = format_methods((methods[klass] || []).select{ |m| m.name =~ grep_regex })
202
+ output_section "#{class_name(klass)} methods", methods_here
203
+ end
204
+ end
225
205
 
206
+ if show_ivars
207
+ klass = (Module === obj ? obj : obj.class)
208
+ output_section("instance variables", format_variables(:instance_var, grep[obj.__send__(:instance_variables)]))
209
+ output_section("class variables", format_variables(:class_var, grep[klass.__send__(:class_variables)]))
210
+ end
226
211
 
212
+ if show_locals
213
+ output_section("locals", format_locals(grep[target.eval("local_variables")]))
214
+ end
215
+ end
227
216
  end
228
217
  end
229
218
  end
@@ -9,7 +9,7 @@ class Pry
9
9
  begin
10
10
  Dir.chdir File.expand_path(dest)
11
11
  rescue Errno::ENOENT
12
- output.puts "No such directory: #{dest}"
12
+ raise CommandError, "No such directory: #{dest}"
13
13
  end
14
14
  else
15
15
  Pry.config.system.call(output, cmd, _pry_)
@@ -61,8 +61,8 @@ class Pry
61
61
  start_line = (ex_line - 1) - window_size
62
62
  start_line = start_line < 0 ? 0 : start_line
63
63
  end_line = (ex_line - 1) + window_size
64
- if ex_file && is_core_rbx_path?(ex_file)
65
- file_name = rbx_convert_path_to_full(ex_file)
64
+ if ex_file && RbxPath.is_core_path?(ex_file)
65
+ file_name = RbxPath.convert_path_to_full(ex_file)
66
66
  else
67
67
  file_name = ex_file
68
68
  end
@@ -79,22 +79,22 @@ class Pry
79
79
  next if opts.help?
80
80
 
81
81
  if opts.ex?
82
- next output.puts "No Exception or Exception has no associated file." if file_name.nil?
82
+ if file_name.nil?
83
+ raise CommandError, "No Exception or Exception has no associated file."
84
+ end
83
85
  else
84
86
  file_name = args.shift
85
87
  end
86
88
 
87
89
  if !file_name
88
- output.puts "Must provide a file name."
89
- next
90
+ raise CommandError, "Must provide a file name."
90
91
  end
91
92
 
92
93
  begin
93
94
  contents, _, _ = read_between_the_lines(file_name, start_line, end_line)
94
95
  contents = syntax_highlight_by_file_type_or_specified(contents, file_name, opts[:type])
95
96
  rescue Errno::ENOENT
96
- output.puts "Could not find file: #{file_name}"
97
- next
97
+ raise CommandError, "Could not find file: #{file_name}"
98
98
  end
99
99
 
100
100
  if opts.l?
@@ -4,11 +4,7 @@ class Pry
4
4
  Experimental = Pry::CommandSet.new do
5
5
 
6
6
  command "reload-method", "Reload the source specifically for a method", :requires_gem => "method_reload" do |meth_name|
7
- if (meth = get_method_object(meth_name, target, {})).nil?
8
- output.puts "Invalid method name: #{meth_name}."
9
- next
10
- end
11
-
7
+ meth = get_method_or_raise(meth_name, target, {}, :omit_help)
12
8
  meth.reload
13
9
  end
14
10
  end