pry 0.9.8pre2-i386-mingw32 → 0.9.8pre3-i386-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +8 -6
- data/lib/pry.rb +5 -4
- data/lib/pry/command.rb +396 -0
- data/lib/pry/command_set.rb +112 -95
- data/lib/pry/default_commands/documentation.rb +153 -82
- data/lib/pry/default_commands/easter_eggs.rb +25 -1
- data/lib/pry/default_commands/input.rb +4 -30
- data/lib/pry/default_commands/introspection.rb +69 -66
- data/lib/pry/default_commands/ls.rb +91 -94
- data/lib/pry/default_commands/shell.rb +1 -1
- data/lib/pry/helpers/base_helpers.rb +7 -2
- data/lib/pry/helpers/command_helpers.rb +29 -4
- data/lib/pry/helpers/options_helpers.rb +6 -40
- data/lib/pry/helpers/text.rb +1 -1
- data/lib/pry/method.rb +42 -4
- data/lib/pry/pry_class.rb +16 -6
- data/lib/pry/pry_instance.rb +15 -7
- data/lib/pry/version.rb +1 -1
- data/lib/pry/wrapped_module.rb +1 -1
- data/pry.gemspec +11 -11
- data/test/helper.rb +8 -12
- data/test/test_command.rb +317 -0
- data/test/test_command_set.rb +152 -18
- data/test/test_completion.rb +1 -1
- data/test/test_default_commands.rb +1 -2
- data/test/test_default_commands/test_introspection.rb +6 -6
- data/test/test_default_commands/test_ls.rb +1 -1
- data/test/test_default_commands/test_shell.rb +4 -2
- data/test/test_hooks.rb +16 -0
- data/test/test_method.rb +50 -0
- data/test/test_pry.rb +37 -39
- data/test/test_syntax_checking.rb +1 -1
- metadata +80 -75
- data/lib/pry/command_context.rb +0 -53
- data/lib/pry/command_processor.rb +0 -194
- data/test/test_command_processor.rb +0 -176
data/lib/pry/command_set.rb
CHANGED
@@ -8,41 +8,6 @@ class Pry
|
|
8
8
|
# This class is used to create sets of commands. Commands can be imported from
|
9
9
|
# different sets, aliased, removed, etc.
|
10
10
|
class CommandSet
|
11
|
-
class Command < Struct.new(:name, :description, :options, :block)
|
12
|
-
|
13
|
-
def call(context, *args)
|
14
|
-
context.command_name = options[:listing]
|
15
|
-
|
16
|
-
if stub_block = options[:stub_info]
|
17
|
-
context.instance_eval(&stub_block)
|
18
|
-
else
|
19
|
-
ret = context.instance_exec(*correct_arg_arity(block.arity, args), &block)
|
20
|
-
if options[:keep_retval]
|
21
|
-
ret
|
22
|
-
else
|
23
|
-
Pry::CommandContext::VOID_VALUE
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
def correct_arg_arity(arity, args)
|
30
|
-
case arity <=> 0
|
31
|
-
when -1
|
32
|
-
args
|
33
|
-
when 0
|
34
|
-
[]
|
35
|
-
when 1
|
36
|
-
# another jruby hack
|
37
|
-
if Pry::Helpers::BaseHelpers.jruby?
|
38
|
-
args[0..(arity - 1)]
|
39
|
-
else
|
40
|
-
args.values_at 0..(arity - 1)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
11
|
include Enumerable
|
47
12
|
include Pry::Helpers::BaseHelpers
|
48
13
|
|
@@ -117,29 +82,42 @@ class Pry
|
|
117
82
|
# # pry(main)> help number
|
118
83
|
# # number-N regex command
|
119
84
|
def command(name, description="No description.", options={}, &block)
|
85
|
+
options = default_options(name).merge!(options)
|
120
86
|
|
121
|
-
|
122
|
-
|
123
|
-
:keep_retval => false,
|
124
|
-
:argument_required => false,
|
125
|
-
:interpolate => true,
|
126
|
-
:shellwords => true,
|
127
|
-
:listing => name,
|
128
|
-
:use_prefix => true
|
129
|
-
}.merge!(options)
|
130
|
-
|
131
|
-
unless command_dependencies_met? options
|
132
|
-
gems_needed = Array(options[:requires_gem])
|
133
|
-
gems_not_installed = gems_needed.select { |g| !gem_installed?(g) }
|
87
|
+
commands[name] = Pry::BlockCommand.subclass(name, description, options, helper_module, &block)
|
88
|
+
end
|
134
89
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
90
|
+
# Defines a new Pry command class.
|
91
|
+
#
|
92
|
+
# @param [String, Regexp] name The name of the command. Can be
|
93
|
+
# Regexp as well as String.
|
94
|
+
# @param [String] description A description of the command.
|
95
|
+
# @param [Hash] options The optional configuration parameters, see {#command}
|
96
|
+
# @param &Block The class body's definition.
|
97
|
+
#
|
98
|
+
# @example
|
99
|
+
# Pry::Commands.command_class "echo", "echo's the input", :shellwords => false do
|
100
|
+
# def options(opt)
|
101
|
+
# opt.banner "Usage: echo [-u | -d] <string to echo>"
|
102
|
+
# opt.on :u, :upcase, "ensure the output is all upper-case"
|
103
|
+
# opt.on :d, :downcase, "ensure the output is all lower-case"
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# def process
|
107
|
+
# raise Pry::CommandError, "-u and -d makes no sense" if opts.present?(:u) && opts.present?(:d)
|
108
|
+
# result = args.join(" ")
|
109
|
+
# result.downcase! if opts.present?(:downcase)
|
110
|
+
# result.upcase! if opts.present?(:upcase)
|
111
|
+
# output.puts result
|
112
|
+
# end
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
def command_class(name, description="No description.", options={}, &block)
|
116
|
+
options = default_options(name).merge!(options)
|
141
117
|
|
142
|
-
commands[name] =
|
118
|
+
commands[name] = Pry::ClassCommand.subclass(name, description, options, helper_module, &block)
|
119
|
+
commands[name].class_eval(&block)
|
120
|
+
commands[name]
|
143
121
|
end
|
144
122
|
|
145
123
|
# Execute a block of code before a command is invoked. The block also
|
@@ -153,13 +131,7 @@ class Pry
|
|
153
131
|
# end
|
154
132
|
def before_command(name, &block)
|
155
133
|
cmd = find_command_by_name_or_listing(name)
|
156
|
-
|
157
|
-
|
158
|
-
wrapper_block = proc do |*args|
|
159
|
-
instance_exec(*args, &block)
|
160
|
-
instance_exec(*args, &prev_block)
|
161
|
-
end
|
162
|
-
cmd.block = wrapper_block
|
134
|
+
cmd.hooks[:before].unshift block
|
163
135
|
end
|
164
136
|
|
165
137
|
# Execute a block of code after a command is invoked. The block also
|
@@ -173,13 +145,7 @@ class Pry
|
|
173
145
|
# end
|
174
146
|
def after_command(name, &block)
|
175
147
|
cmd = find_command_by_name_or_listing(name)
|
176
|
-
|
177
|
-
|
178
|
-
wrapper_block = proc do |*args|
|
179
|
-
instance_exec(*args, &prev_block)
|
180
|
-
instance_exec(*args, &block)
|
181
|
-
end
|
182
|
-
cmd.block = wrapper_block
|
148
|
+
cmd.hooks[:after] << block
|
183
149
|
end
|
184
150
|
|
185
151
|
def each &block
|
@@ -267,27 +233,6 @@ class Pry
|
|
267
233
|
commands.delete(cmd.name)
|
268
234
|
end
|
269
235
|
|
270
|
-
# Runs a command.
|
271
|
-
# @param [Object] context Object which will be used as self during the
|
272
|
-
# command.
|
273
|
-
# @param [String] name Name of the command to be run
|
274
|
-
# @param [Array<Object>] args Arguments passed to the command
|
275
|
-
# @raise [NoCommandError] If the command is not defined in this set
|
276
|
-
def run_command(context, name, *args)
|
277
|
-
context.extend helper_module
|
278
|
-
command = commands[name]
|
279
|
-
|
280
|
-
if command.nil?
|
281
|
-
raise NoCommandError.new(name, self)
|
282
|
-
end
|
283
|
-
|
284
|
-
if command.options[:argument_required] && args.empty?
|
285
|
-
puts "The command '#{command.name}' requires an argument."
|
286
|
-
else
|
287
|
-
command.call context, *args
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
236
|
# Sets or gets the description for a command (replacing the old
|
292
237
|
# description). Returns current description if no description
|
293
238
|
# parameter provided.
|
@@ -328,7 +273,58 @@ class Pry
|
|
328
273
|
commands.keys
|
329
274
|
end
|
330
275
|
|
276
|
+
# Find a command that matches the given line
|
277
|
+
#
|
278
|
+
# @param [String] the line that may be a command invocation
|
279
|
+
# @return [Pry::Command, nil]
|
280
|
+
def find_command(val)
|
281
|
+
commands.values.detect{ |c| c.matches?(val) }
|
282
|
+
end
|
283
|
+
|
284
|
+
# Is the given line a command invocation?
|
285
|
+
#
|
286
|
+
# @param [String]
|
287
|
+
# @return [Boolean]
|
288
|
+
def valid_command?(val)
|
289
|
+
!!find_command(val)
|
290
|
+
end
|
291
|
+
|
292
|
+
# Process the given line to see whether it needs executing as a command.
|
293
|
+
#
|
294
|
+
# @param String the line to execute
|
295
|
+
# @param Hash the context to execute the commands with
|
296
|
+
# @return CommandSet::Result
|
297
|
+
#
|
298
|
+
def process_line(val, context={})
|
299
|
+
if command = find_command(val)
|
300
|
+
context = context.merge(:command_set => self)
|
301
|
+
retval = command.new(context).process_line(val)
|
302
|
+
Result.new(true, retval)
|
303
|
+
else
|
304
|
+
Result.new(false)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
# @nodoc used for testing
|
309
|
+
def run_command(context, name, *args)
|
310
|
+
command = commands[name] or raise NoCommandError.new(name, self)
|
311
|
+
command.new(context).call_safely(*args)
|
312
|
+
end
|
313
|
+
|
331
314
|
private
|
315
|
+
|
316
|
+
def default_options(name)
|
317
|
+
{
|
318
|
+
:requires_gem => [],
|
319
|
+
:keep_retval => false,
|
320
|
+
:argument_required => false,
|
321
|
+
:interpolate => true,
|
322
|
+
:shellwords => true,
|
323
|
+
:listing => name,
|
324
|
+
:use_prefix => true
|
325
|
+
}
|
326
|
+
end
|
327
|
+
|
332
328
|
def define_default_commands
|
333
329
|
|
334
330
|
command "help", "This menu." do |cmd|
|
@@ -345,7 +341,7 @@ class Pry
|
|
345
341
|
stagger_output(help_text)
|
346
342
|
else
|
347
343
|
if command = find_command(cmd)
|
348
|
-
output.puts command.
|
344
|
+
output.puts command.new.help
|
349
345
|
else
|
350
346
|
output.puts "No info for command: #{cmd}"
|
351
347
|
end
|
@@ -355,10 +351,9 @@ class Pry
|
|
355
351
|
command "install-command", "Install a disabled command." do |name|
|
356
352
|
require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller
|
357
353
|
command = find_command(name)
|
358
|
-
stub_info = command.options[:stub_info]
|
359
354
|
|
360
|
-
if
|
361
|
-
output.puts "
|
355
|
+
if command_dependencies_met?(command.options)
|
356
|
+
output.puts "Dependencies for #{command.name} are met. Nothing to do."
|
362
357
|
next
|
363
358
|
end
|
364
359
|
|
@@ -391,9 +386,31 @@ class Pry
|
|
391
386
|
end
|
392
387
|
next if gem_install_failed
|
393
388
|
|
394
|
-
command.options.delete :stub_info
|
395
389
|
output.puts "Installation of `#{name}` successful! Type `help #{name}` for information"
|
396
390
|
end
|
397
391
|
end
|
398
392
|
end
|
393
|
+
|
394
|
+
# Wraps the return result of process_commands, indicates if the
|
395
|
+
# result IS a command and what kind of command (e.g void)
|
396
|
+
class Result
|
397
|
+
attr_reader :retval
|
398
|
+
|
399
|
+
def initialize(is_command, retval = nil)
|
400
|
+
@is_command, @retval = is_command, retval
|
401
|
+
end
|
402
|
+
|
403
|
+
# Is the result a command?
|
404
|
+
# @return [Boolean]
|
405
|
+
def command?
|
406
|
+
@is_command
|
407
|
+
end
|
408
|
+
|
409
|
+
# Is the result a command and if it is, is it a void command?
|
410
|
+
# (one that does not return a value)
|
411
|
+
# @return [Boolean]
|
412
|
+
def void_command?
|
413
|
+
retval == Command::VOID_VALUE
|
414
|
+
end
|
415
|
+
end
|
399
416
|
end
|
@@ -3,124 +3,196 @@ class Pry
|
|
3
3
|
|
4
4
|
Documentation = Pry::CommandSet.new do
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
command_class "ri", "View ri documentation. e.g `ri Array#each`" do
|
7
|
+
banner <<-BANNER
|
8
|
+
Usage: ri [spec]
|
9
|
+
e.g. ri Array#each
|
10
|
+
|
11
|
+
Relies on the ri executable being available. See also: show-doc.
|
12
|
+
BANNER
|
13
|
+
|
14
|
+
def process
|
15
|
+
run ".ri", *args
|
16
|
+
end
|
8
17
|
end
|
9
18
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
USAGE
|
19
|
+
command_class "show-doc", "Show the comments above METH. Type `show-doc --help` for more info. Aliases: \?", :shellwords => false do |*args|
|
20
|
+
banner <<-BANNER
|
21
|
+
Usage: show-doc [OPTIONS] [METH]
|
22
|
+
Show the comments above method METH. Tries instance methods first and then methods by default.
|
23
|
+
e.g show-doc hello_method
|
24
|
+
BANNER
|
17
25
|
|
26
|
+
def options(opt)
|
27
|
+
method_options(opt)
|
18
28
|
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
|
19
29
|
end
|
20
30
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
31
|
+
def process
|
32
|
+
meth = method_object
|
33
|
+
raise Pry::CommandError, "No documentation found." if meth.doc.nil? || meth.doc.empty?
|
34
|
+
|
35
|
+
doc = process_comment_markup(meth.doc, meth.source_type)
|
36
|
+
output.puts make_header(meth, doc)
|
37
|
+
output.puts "#{text.bold("Owner:")} #{meth.owner || "N/A"}"
|
38
|
+
output.puts "#{text.bold("Visibility:")} #{meth.visibility}"
|
39
|
+
output.puts "#{text.bold("Signature:")} #{meth.signature}"
|
40
|
+
output.puts
|
41
|
+
render_output(opts.present?(:flood), false, doc)
|
42
|
+
end
|
30
43
|
end
|
31
44
|
|
32
45
|
alias_command "?", "show-doc"
|
33
46
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
opts, meth = parse_options!(args, :method_object) do |opt|
|
38
|
-
opt.banner unindent <<-USAGE
|
47
|
+
command_class "stat", "View method information and set _file_ and _dir_ locals. Type `stat --help` for more info.", :shellwords => false do |*args|
|
48
|
+
banner <<-BANNER
|
39
49
|
Usage: stat [OPTIONS] [METH]
|
40
50
|
Show method information for method METH and set _file_ and _dir_ locals.
|
41
51
|
e.g: stat hello_method
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
BANNER
|
53
|
+
|
54
|
+
def options(opt)
|
55
|
+
method_options(opt)
|
56
|
+
end
|
57
|
+
|
58
|
+
def process
|
59
|
+
meth = method_object
|
60
|
+
output.puts unindent <<-EOS
|
61
|
+
Method Information:
|
62
|
+
--
|
63
|
+
Name: #{meth.name}
|
64
|
+
Owner: #{meth.owner ? meth.owner : "Unknown"}
|
65
|
+
Visibility: #{meth.visibility}
|
66
|
+
Type: #{meth.is_a?(::Method) ? "Bound" : "Unbound"}
|
67
|
+
Arity: #{meth.arity}
|
68
|
+
Method Signature: #{meth.signature}
|
69
|
+
Source Location: #{meth.source_location ? meth.source_location.join(":") : "Not found."}
|
70
|
+
EOS
|
71
|
+
end
|
56
72
|
end
|
57
73
|
|
58
|
-
|
59
|
-
|
74
|
+
command_class "gist", "Gist a method or expression history to github. Type `gist --help` for more info.", :requires_gem => "gist", :shellwords => false do
|
75
|
+
attr_accessor :content
|
76
|
+
attr_accessor :code_type
|
77
|
+
attr_accessor :input_ranges
|
60
78
|
|
61
|
-
|
79
|
+
def setup
|
80
|
+
require 'gist'
|
81
|
+
end
|
62
82
|
|
63
|
-
|
83
|
+
def options(opt)
|
64
84
|
opt.banner unindent <<-USAGE
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
85
|
+
Usage: gist [OPTIONS] [METH]
|
86
|
+
Gist method (doc or source) or input expression to github.
|
87
|
+
Ensure the `gist` gem is properly working before use. http://github.com/defunkt/gist for instructions.
|
88
|
+
e.g: gist -m my_method
|
89
|
+
e.g: gist -d my_method
|
90
|
+
e.g: gist -i 1..10
|
91
|
+
USAGE
|
72
92
|
|
73
93
|
opt.on :d, :doc, "Gist a method's documentation.", true
|
74
94
|
opt.on :m, :method, "Gist a method's source.", true
|
95
|
+
opt.on :f, :file, "Gist a file.", true
|
75
96
|
opt.on :p, :public, "Create a public gist (default: false)", :default => false
|
76
|
-
opt.on :
|
97
|
+
opt.on :l, :lines, "Only gist a subset of lines (only works with -m and -f)", :optional => true, :as => Range, :default => 1..-1
|
98
|
+
opt.on :i, :in, "Gist entries from Pry's input expression history. Takes an index or range.", :optional => true,
|
99
|
+
:as => Range, :default => -5..-1 do |range|
|
100
|
+
input_ranges << absolute_index_range(range, _pry_.input_array.length)
|
101
|
+
end
|
77
102
|
end
|
78
103
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
104
|
+
def process
|
105
|
+
if opts.present?(:in)
|
106
|
+
in_option
|
107
|
+
elsif opts.present?(:file)
|
108
|
+
file_option
|
109
|
+
elsif opts.present?(:doc)
|
110
|
+
doc_option
|
111
|
+
elsif opts.present?(:method)
|
112
|
+
method_option
|
113
|
+
end
|
114
|
+
|
115
|
+
perform_gist
|
116
|
+
end
|
117
|
+
|
118
|
+
def in_option
|
119
|
+
self.code_type = :ruby
|
120
|
+
self.content = ""
|
121
|
+
|
122
|
+
input_ranges.each do |range|
|
123
|
+
input_expressions = _pry_.input_array[range] || []
|
124
|
+
input_expressions.each_with_index.map do |code, index|
|
125
|
+
corrected_index = index + range.first
|
126
|
+
if code && code != ""
|
127
|
+
self.content << code
|
128
|
+
if code !~ /;\Z/
|
129
|
+
self.content << "#{comment_expression_result_for_gist(Pry.config.gist.inspecter.call(_pry_.output_array[corrected_index]))}"
|
130
|
+
end
|
131
|
+
end
|
91
132
|
end
|
92
133
|
end
|
93
|
-
|
134
|
+
end
|
135
|
+
|
136
|
+
def file_option
|
137
|
+
whole_file = File.read(File.expand_path(opts[:f]))
|
138
|
+
if opts.present?(:lines)
|
139
|
+
self.content = restrict_to_lines(whole_file, opts[:l])
|
140
|
+
else
|
141
|
+
self.content = whole_file
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def doc_option
|
94
146
|
meth = get_method_or_raise(opts[:d], target, {})
|
95
|
-
content = meth.doc
|
96
|
-
code_type = meth.source_type
|
147
|
+
self.content = meth.doc
|
148
|
+
self.code_type = meth.source_type
|
97
149
|
|
98
150
|
text.no_color do
|
99
|
-
content = process_comment_markup(content, code_type)
|
151
|
+
self.content = process_comment_markup(self.content, self.code_type)
|
100
152
|
end
|
101
|
-
code_type = :plain
|
102
|
-
|
153
|
+
self.code_type = :plain
|
154
|
+
end
|
155
|
+
|
156
|
+
def method_option
|
103
157
|
meth = get_method_or_raise(opts[:m], target, {})
|
104
|
-
|
105
|
-
|
158
|
+
method_source = meth.source
|
159
|
+
if opts.present?(:lines)
|
160
|
+
self.content = restrict_to_lines(method_source, opts[:l])
|
161
|
+
else
|
162
|
+
self.content = method_source
|
163
|
+
end
|
164
|
+
|
165
|
+
self.code_type = meth.source_type
|
106
166
|
end
|
107
167
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
168
|
+
def perform_gist
|
169
|
+
type_map = { :ruby => "rb", :c => "c", :plain => "plain" }
|
170
|
+
|
171
|
+
# prevent Gist from exiting the session on error
|
172
|
+
begin
|
173
|
+
extname = opts.present?(:file) ? ".#{gist_file_extension(opts[:f])}" : ".#{type_map[self.code_type]}"
|
174
|
+
|
175
|
+
link = Gist.write([:extension => extname,
|
176
|
+
:input => self.content],
|
177
|
+
!opts[:p])
|
178
|
+
rescue SystemExit
|
179
|
+
end
|
180
|
+
|
181
|
+
if link
|
182
|
+
Gist.copy(link)
|
183
|
+
output.puts "Gist created at #{link} and added to clipboard."
|
184
|
+
end
|
114
185
|
end
|
115
186
|
|
116
|
-
|
117
|
-
|
118
|
-
|
187
|
+
def restrict_to_lines(content, lines)
|
188
|
+
line_range = one_index_range(lines)
|
189
|
+
content.lines.to_a[line_range].join
|
119
190
|
end
|
120
|
-
end
|
121
191
|
|
192
|
+
def gist_file_extension(file_name)
|
193
|
+
file_name.split(".").last
|
194
|
+
end
|
122
195
|
|
123
|
-
helpers do
|
124
196
|
def comment_expression_result_for_gist(result)
|
125
197
|
content = ""
|
126
198
|
result.lines.each_with_index do |line, index|
|
@@ -133,8 +205,7 @@ class Pry
|
|
133
205
|
content
|
134
206
|
end
|
135
207
|
end
|
136
|
-
|
137
|
-
|
138
208
|
end
|
139
209
|
end
|
140
210
|
end
|
211
|
+
|