pry 0.9.7.4-i386-mswin32 → 0.9.8-i386-mswin32
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.
- data/.gitignore +2 -3
- data/CHANGELOG +43 -0
- data/README.markdown +3 -1
- data/Rakefile +51 -32
- data/bin/pry +2 -80
- data/lib/pry.rb +33 -26
- data/lib/pry/cli.rb +152 -0
- data/lib/pry/code.rb +351 -0
- data/lib/pry/command.rb +422 -0
- data/lib/pry/command_set.rb +259 -129
- data/lib/pry/commands.rb +0 -1
- data/lib/pry/config.rb +43 -9
- data/lib/pry/default_commands/context.rb +109 -92
- data/lib/pry/default_commands/documentation.rb +174 -63
- data/lib/pry/default_commands/easter_eggs.rb +26 -2
- data/lib/pry/default_commands/gems.rb +65 -37
- data/lib/pry/default_commands/input.rb +175 -243
- data/lib/pry/default_commands/introspection.rb +173 -112
- data/lib/pry/default_commands/ls.rb +96 -114
- data/lib/pry/default_commands/shell.rb +175 -70
- data/lib/pry/helpers/base_helpers.rb +7 -2
- data/lib/pry/helpers/command_helpers.rb +71 -77
- data/lib/pry/helpers/options_helpers.rb +10 -41
- data/lib/pry/helpers/text.rb +24 -4
- data/lib/pry/history.rb +55 -17
- data/lib/pry/history_array.rb +2 -0
- data/lib/pry/hooks.rb +252 -0
- data/lib/pry/indent.rb +9 -5
- data/lib/pry/method.rb +149 -50
- data/lib/pry/plugins.rb +12 -4
- data/lib/pry/pry_class.rb +69 -26
- data/lib/pry/pry_instance.rb +187 -115
- data/lib/pry/version.rb +1 -1
- data/lib/pry/wrapped_module.rb +73 -0
- data/man/pry.1 +195 -0
- data/man/pry.1.html +204 -0
- data/man/pry.1.ronn +141 -0
- data/pry.gemspec +29 -32
- data/test/helper.rb +32 -36
- data/test/test_cli.rb +78 -0
- data/test/test_code.rb +201 -0
- data/test/test_command.rb +327 -0
- data/test/test_command_integration.rb +512 -0
- data/test/test_command_set.rb +338 -12
- data/test/test_completion.rb +1 -1
- data/test/test_default_commands.rb +1 -2
- data/test/test_default_commands/test_context.rb +27 -5
- data/test/test_default_commands/test_documentation.rb +20 -8
- data/test/test_default_commands/test_input.rb +84 -45
- data/test/test_default_commands/test_introspection.rb +74 -17
- data/test/test_default_commands/test_ls.rb +9 -36
- data/test/test_default_commands/test_shell.rb +240 -13
- data/test/test_hooks.rb +490 -0
- data/test/test_indent.rb +2 -0
- data/test/test_method.rb +60 -0
- data/test/test_pry.rb +29 -904
- data/test/test_pry_defaults.rb +380 -0
- data/test/test_pry_history.rb +24 -24
- data/test/test_syntax_checking.rb +63 -0
- data/test/test_wrapped_module.rb +71 -0
- metadata +50 -39
- data/lib/pry/command_context.rb +0 -53
- data/lib/pry/command_processor.rb +0 -181
- data/lib/pry/extended_commands/user_command_api.rb +0 -65
- data/test/test_command_processor.rb +0 -176
data/lib/pry/code.rb
ADDED
@@ -0,0 +1,351 @@
|
|
1
|
+
class Pry
|
2
|
+
class << self
|
3
|
+
# Convert the given object into an instance of `Pry::Code`, if it isn't
|
4
|
+
# already one.
|
5
|
+
#
|
6
|
+
# @param [Code, Method, UnboundMethod, Proc, Pry::Method, String, Array,
|
7
|
+
# IO] obj
|
8
|
+
def Code(obj)
|
9
|
+
case obj
|
10
|
+
when Code
|
11
|
+
obj
|
12
|
+
when ::Method, UnboundMethod, Proc, Pry::Method
|
13
|
+
Code.from_method(obj)
|
14
|
+
else
|
15
|
+
Code.new(obj)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# `Pry::Code` is a class that encapsulates lines of source code and their
|
21
|
+
# line numbers and formats them for terminal output. It can read from a file
|
22
|
+
# or method definition or be instantiated with a `String` or an `Array`.
|
23
|
+
#
|
24
|
+
# In general, the formatting methods in `Code` return a new `Code` object
|
25
|
+
# which will format the text as specified when `#to_s` is called. This allows
|
26
|
+
# arbitrary chaining of formatting methods without mutating the original
|
27
|
+
# object.
|
28
|
+
class Code
|
29
|
+
class << self
|
30
|
+
# Instantiate a `Code` object containing code loaded from a file or
|
31
|
+
# Pry's line buffer.
|
32
|
+
#
|
33
|
+
# @param [String] fn The name of a file, or "(pry)".
|
34
|
+
# @param [Symbol] code_type (:ruby) The type of code the file contains.
|
35
|
+
# @return [Code]
|
36
|
+
def from_file(fn, code_type=nil)
|
37
|
+
if fn == Pry.eval_path
|
38
|
+
f = Pry.line_buffer.drop(1)
|
39
|
+
else
|
40
|
+
if File.readable?(fn)
|
41
|
+
f = File.open(fn, 'r')
|
42
|
+
code_type = type_from_filename(fn)
|
43
|
+
else
|
44
|
+
raise CommandError, "Cannot open #{fn.inspect} for reading."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
new(f, 1, code_type || :ruby)
|
48
|
+
ensure
|
49
|
+
f.close if f.respond_to?(:close)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Instantiate a `Code` object containing code extracted from a
|
53
|
+
# `::Method`, `UnboundMethod`, `Proc`, or `Pry::Method` object.
|
54
|
+
#
|
55
|
+
# @param [::Method, UnboundMethod, Proc, Pry::Method] meth The method
|
56
|
+
# object.
|
57
|
+
# @param [Fixnum, nil] The line number to start on, or nil to use the
|
58
|
+
# method's original line numbers.
|
59
|
+
# @return [Code]
|
60
|
+
def from_method(meth, start_line=nil)
|
61
|
+
meth = Pry::Method(meth)
|
62
|
+
start_line ||= meth.source_line || 1
|
63
|
+
new(meth.source, start_line, meth.source_type)
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
# Guess the CodeRay type of a file from its extension, or nil if
|
68
|
+
# unknown.
|
69
|
+
#
|
70
|
+
# @param [String] filename
|
71
|
+
# @return [Symbol, nil]
|
72
|
+
def type_from_filename(filename)
|
73
|
+
map = {
|
74
|
+
%w(.c .h) => :c,
|
75
|
+
%w(.cpp .hpp .cc .h cxx) => :cpp,
|
76
|
+
%w(.rb .ru .irbrc .gemspec .pryrc) => :ruby,
|
77
|
+
%w(.py) => :python,
|
78
|
+
%w(.diff) => :diff,
|
79
|
+
%w(.css) => :css,
|
80
|
+
%w(.html) => :html,
|
81
|
+
%w(.yaml .yml) => :yaml,
|
82
|
+
%w(.xml) => :xml,
|
83
|
+
%w(.php) => :php,
|
84
|
+
%w(.js) => :javascript,
|
85
|
+
%w(.java) => :java,
|
86
|
+
%w(.rhtml) => :rhtml,
|
87
|
+
%w(.json) => :json
|
88
|
+
}
|
89
|
+
|
90
|
+
_, type = map.find do |k, _|
|
91
|
+
k.any? { |ext| ext == File.extname(filename) }
|
92
|
+
end
|
93
|
+
|
94
|
+
type
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
attr_accessor :code_type
|
99
|
+
|
100
|
+
# Instantiate a `Code` object containing code from the given `Array`,
|
101
|
+
# `String`, or `IO`. The first line will be line 1 unless specified
|
102
|
+
# otherwise. If you need non-contiguous line numbers, you can create an
|
103
|
+
# empty `Code` object and then use `#push` to insert the lines.
|
104
|
+
#
|
105
|
+
# @param [Array<String>, String, IO] lines
|
106
|
+
# @param [Fixnum?] (1) start_line
|
107
|
+
# @param [Symbol?] (:ruby) code_type
|
108
|
+
def initialize(lines=[], start_line=1, code_type=:ruby)
|
109
|
+
if lines.is_a? String
|
110
|
+
lines = lines.lines
|
111
|
+
end
|
112
|
+
|
113
|
+
@lines = lines.each_with_index.map { |l, i| [l.chomp, i + start_line] }
|
114
|
+
@code_type = code_type
|
115
|
+
end
|
116
|
+
|
117
|
+
# Append the given line. `line_num` is one more than the last existing
|
118
|
+
# line, unless specified otherwise.
|
119
|
+
#
|
120
|
+
# @param [String] line
|
121
|
+
# @param [Fixnum?] line_num
|
122
|
+
# @return [String] The inserted line.
|
123
|
+
def push(line, line_num=nil)
|
124
|
+
line_num = @lines.last.last + 1 unless line_num
|
125
|
+
@lines.push([line.chomp, line_num])
|
126
|
+
line
|
127
|
+
end
|
128
|
+
alias << push
|
129
|
+
|
130
|
+
# Filter the lines using the given block.
|
131
|
+
#
|
132
|
+
# @yield [line]
|
133
|
+
# @return [Code]
|
134
|
+
def select(&blk)
|
135
|
+
alter do
|
136
|
+
@lines = @lines.select(&blk)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Remove all lines that aren't in the given range, expressed either as a
|
141
|
+
# `Range` object or a first and last line number (inclusive). Negative
|
142
|
+
# indices count from the end of the array of lines.
|
143
|
+
#
|
144
|
+
# @param [Range, Fixnum] start_line
|
145
|
+
# @param [Fixnum?] end_line
|
146
|
+
# @return [Code]
|
147
|
+
def between(start_line, end_line=nil)
|
148
|
+
return self unless start_line
|
149
|
+
|
150
|
+
if start_line.is_a? Range
|
151
|
+
end_line = start_line.last
|
152
|
+
end_line -= 1 if start_line.exclude_end?
|
153
|
+
|
154
|
+
start_line = start_line.first
|
155
|
+
else
|
156
|
+
end_line ||= start_line
|
157
|
+
end
|
158
|
+
|
159
|
+
if start_line > 0
|
160
|
+
start_idx = @lines.index { |l| l.last >= start_line } || @lines.length
|
161
|
+
else
|
162
|
+
start_idx = start_line
|
163
|
+
end
|
164
|
+
|
165
|
+
if end_line > 0
|
166
|
+
end_idx = (@lines.index { |l| l.last > end_line } || 0) - 1
|
167
|
+
else
|
168
|
+
end_idx = end_line
|
169
|
+
end
|
170
|
+
|
171
|
+
alter do
|
172
|
+
@lines = @lines[start_idx..end_idx] || []
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Remove all lines except for the `lines` up to and excluding `line_num`.
|
177
|
+
#
|
178
|
+
# @param [Fixnum] line_num
|
179
|
+
# @param [Fixnum] (1) lines
|
180
|
+
# @return [Code]
|
181
|
+
def before(line_num, lines=1)
|
182
|
+
return self unless line_num
|
183
|
+
|
184
|
+
select do |l, ln|
|
185
|
+
ln >= line_num - lines && ln < line_num
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Remove all lines except for the `lines` on either side of and including
|
190
|
+
# `line_num`.
|
191
|
+
#
|
192
|
+
# @param [Fixnum] line_num
|
193
|
+
# @param [Fixnum] (1) lines
|
194
|
+
# @return [Code]
|
195
|
+
def around(line_num, lines=1)
|
196
|
+
return self unless line_num
|
197
|
+
|
198
|
+
select do |l, ln|
|
199
|
+
ln >= line_num - lines && ln <= line_num + lines
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Remove all lines except for the `lines` after and excluding `line_num`.
|
204
|
+
#
|
205
|
+
# @param [Fixnum] line_num
|
206
|
+
# @param [Fixnum] (1) lines
|
207
|
+
# @return [Code]
|
208
|
+
def after(line_num, lines=1)
|
209
|
+
return self unless line_num
|
210
|
+
|
211
|
+
select do |l, ln|
|
212
|
+
ln > line_num && ln <= line_num + lines
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Remove all lines that don't match the given `pattern`.
|
217
|
+
#
|
218
|
+
# @param [Regexp] pattern
|
219
|
+
# @return [Code]
|
220
|
+
def grep(pattern)
|
221
|
+
return self unless pattern
|
222
|
+
pattern = Regexp.new(pattern)
|
223
|
+
|
224
|
+
select do |l, ln|
|
225
|
+
l =~ pattern
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Format output with line numbers next to it, unless `y_n` is falsy.
|
230
|
+
#
|
231
|
+
# @param [Boolean?] (true) y_n
|
232
|
+
# @return [Code]
|
233
|
+
def with_line_numbers(y_n=true)
|
234
|
+
alter do
|
235
|
+
@with_line_numbers = y_n
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Format output with a marker next to the given `line_num`, unless `line_num`
|
240
|
+
# is falsy.
|
241
|
+
#
|
242
|
+
# @param [Fixnum?] (1) line_num
|
243
|
+
# @return [Code]
|
244
|
+
def with_marker(line_num=1)
|
245
|
+
alter do
|
246
|
+
@with_marker = !!line_num
|
247
|
+
@marker_line_num = line_num
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Format output with the specified number of spaces in front of every line,
|
252
|
+
# unless `spaces` is falsy.
|
253
|
+
#
|
254
|
+
# @param [Fixnum?] (0) spaces
|
255
|
+
# @return [Code]
|
256
|
+
def with_indentation(spaces=0)
|
257
|
+
alter do
|
258
|
+
@with_indentation = !!spaces
|
259
|
+
@indentation_num = spaces
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# @return [String]
|
264
|
+
def inspect
|
265
|
+
Object.instance_method(:to_s).bind(self).call
|
266
|
+
end
|
267
|
+
|
268
|
+
# Based on the configuration of the object, return a formatted String
|
269
|
+
# representation.
|
270
|
+
#
|
271
|
+
# @return [String]
|
272
|
+
def to_s
|
273
|
+
lines = @lines.map(&:dup)
|
274
|
+
|
275
|
+
if Pry.color
|
276
|
+
lines.each do |l|
|
277
|
+
l[0] = CodeRay.scan(l[0], @code_type).term
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
if @with_line_numbers
|
282
|
+
max_width = lines.last.last.to_s.length if lines.length > 0
|
283
|
+
lines.each do |l|
|
284
|
+
padded_line_num = l[1].to_s.rjust(max_width)
|
285
|
+
l[0] = "#{Pry::Helpers::Text.blue(padded_line_num)}: #{l[0]}"
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
if @with_marker
|
290
|
+
lines.each do |l|
|
291
|
+
if l[1] == @marker_line_num
|
292
|
+
l[0] = " => #{l[0]}"
|
293
|
+
else
|
294
|
+
l[0] = " #{l[0]}"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
if @with_indentation
|
300
|
+
lines.each do |l|
|
301
|
+
l[0] = "#{' ' * @indentation_num}#{l[0]}"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
lines.map { |l| "#{l.first}\n" }.join
|
306
|
+
end
|
307
|
+
|
308
|
+
# Return an unformatted String of the code.
|
309
|
+
#
|
310
|
+
# @return [String]
|
311
|
+
def raw
|
312
|
+
@lines.map(&:first).join("\n")
|
313
|
+
end
|
314
|
+
|
315
|
+
# Return the number of lines stored.
|
316
|
+
#
|
317
|
+
# @return [Fixnum]
|
318
|
+
def length
|
319
|
+
@lines ? @lines.length : 0
|
320
|
+
end
|
321
|
+
|
322
|
+
# Two `Code` objects are equal if they contain the same lines with the same
|
323
|
+
# numbers. Otherwise, call `to_s` and `chomp` and compare as Strings.
|
324
|
+
#
|
325
|
+
# @param [Code, Object] other
|
326
|
+
# @return [Boolean]
|
327
|
+
def ==(other)
|
328
|
+
if other.is_a?(Code)
|
329
|
+
@other_lines = other.instance_variable_get(:@lines)
|
330
|
+
@lines.each_with_index.all? do |(l, ln), i|
|
331
|
+
l == @other_lines[i].first && ln == @other_lines[i].last
|
332
|
+
end
|
333
|
+
else
|
334
|
+
to_s.chomp == other.to_s.chomp
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
# Forward any missing methods to the output of `#to_s`.
|
339
|
+
def method_missing(name, *args, &blk)
|
340
|
+
to_s.send(name, *args, &blk)
|
341
|
+
end
|
342
|
+
undef =~
|
343
|
+
|
344
|
+
protected
|
345
|
+
# An abstraction of the `dup.instance_eval` pattern used throughout this
|
346
|
+
# class.
|
347
|
+
def alter(&blk)
|
348
|
+
dup.tap { |o| o.instance_eval(&blk) }
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
data/lib/pry/command.rb
ADDED
@@ -0,0 +1,422 @@
|
|
1
|
+
class Pry
|
2
|
+
|
3
|
+
# The super-class of all commands, new commands should be created by calling
|
4
|
+
# {Pry::CommandSet#command} which creates a BlockCommand or {Pry::CommandSet#create_command}
|
5
|
+
# which creates a ClassCommand. Please don't use this class directly.
|
6
|
+
class Command
|
7
|
+
|
8
|
+
# represents a void return value for a command
|
9
|
+
VOID_VALUE = Object.new
|
10
|
+
|
11
|
+
# give it a nice inspect
|
12
|
+
def VOID_VALUE.inspect() "void" end
|
13
|
+
|
14
|
+
# Properties of the command itself (as passed as arguments to
|
15
|
+
# {CommandSet#command} or {CommandSet#create_command}).
|
16
|
+
class << self
|
17
|
+
attr_accessor :block
|
18
|
+
attr_accessor :name
|
19
|
+
attr_writer :description
|
20
|
+
attr_writer :command_options
|
21
|
+
|
22
|
+
# Define or get the command's description
|
23
|
+
def description(arg=nil)
|
24
|
+
@description = arg if arg
|
25
|
+
@description
|
26
|
+
end
|
27
|
+
|
28
|
+
# Define or get the command's options
|
29
|
+
def command_options(arg=nil)
|
30
|
+
@command_options ||= {}
|
31
|
+
@command_options.merge!(arg) if arg
|
32
|
+
@command_options
|
33
|
+
end
|
34
|
+
# backward compatibility
|
35
|
+
alias_method :options, :command_options
|
36
|
+
alias_method :options=, :command_options=
|
37
|
+
|
38
|
+
# Define or get the command's banner
|
39
|
+
def banner(arg=nil)
|
40
|
+
@banner = arg if arg
|
41
|
+
@banner || description
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# Make those properties accessible to instances
|
47
|
+
def name; self.class.name; end
|
48
|
+
def description; self.class.description; end
|
49
|
+
def block; self.class.block; end
|
50
|
+
def command_options; self.class.options; end
|
51
|
+
def command_name; command_options[:listing]; end
|
52
|
+
|
53
|
+
class << self
|
54
|
+
def inspect
|
55
|
+
"#<class(Pry::Command #{name.inspect})>"
|
56
|
+
end
|
57
|
+
|
58
|
+
# Create a new command with the given properties.
|
59
|
+
#
|
60
|
+
# @param String name the name of the command
|
61
|
+
# @param String description the description to appear in {help}
|
62
|
+
# @param Hash options behavioural options (@see {Pry::CommandSet#command})
|
63
|
+
# @param Module helpers a module of helper functions to be included.
|
64
|
+
# @param Proc &block (optional, a block, used for BlockCommands)
|
65
|
+
#
|
66
|
+
# @return Class (a subclass of Pry::Command)
|
67
|
+
#
|
68
|
+
def subclass(name, description, options, helpers, &block)
|
69
|
+
klass = Class.new(self)
|
70
|
+
klass.send(:include, helpers)
|
71
|
+
klass.name = name
|
72
|
+
klass.description = description
|
73
|
+
klass.command_options = options
|
74
|
+
klass.block = block
|
75
|
+
klass
|
76
|
+
end
|
77
|
+
|
78
|
+
# Should this command be called for the given line?
|
79
|
+
#
|
80
|
+
# @param String a line input at the REPL
|
81
|
+
# @return Boolean
|
82
|
+
def matches?(val)
|
83
|
+
command_regex =~ val
|
84
|
+
end
|
85
|
+
|
86
|
+
# Store hooks to be run before or after the command body.
|
87
|
+
# @see {Pry::CommandSet#before_command}
|
88
|
+
# @see {Pry::CommandSet#after_command}
|
89
|
+
def hooks
|
90
|
+
@hooks ||= {:before => [], :after => []}
|
91
|
+
end
|
92
|
+
|
93
|
+
def command_regex
|
94
|
+
prefix = convert_to_regex(Pry.config.command_prefix)
|
95
|
+
prefix = "(?:#{prefix})?" unless options[:use_prefix]
|
96
|
+
|
97
|
+
/^#{prefix}#{convert_to_regex(name)}(?!\S)/
|
98
|
+
end
|
99
|
+
|
100
|
+
def convert_to_regex(obj)
|
101
|
+
case obj
|
102
|
+
when String
|
103
|
+
Regexp.escape(obj)
|
104
|
+
else
|
105
|
+
obj
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
# Properties of one execution of a command (passed by {Pry#run_command} as a hash of
|
112
|
+
# context and expanded in {#initialize}
|
113
|
+
attr_accessor :output
|
114
|
+
attr_accessor :target
|
115
|
+
attr_accessor :captures
|
116
|
+
attr_accessor :eval_string
|
117
|
+
attr_accessor :arg_string
|
118
|
+
attr_accessor :context
|
119
|
+
attr_accessor :command_set
|
120
|
+
attr_accessor :_pry_
|
121
|
+
|
122
|
+
# Run a command from another command.
|
123
|
+
# @param [String] command_string The string that invokes the command
|
124
|
+
# @param [Array] args Further arguments to pass to the command
|
125
|
+
# @example
|
126
|
+
# run "show-input"
|
127
|
+
# @example
|
128
|
+
# run ".ls"
|
129
|
+
# @example
|
130
|
+
# run "amend-line", "5", 'puts "hello world"'
|
131
|
+
def run(command_string, *args)
|
132
|
+
complete_string = "#{command_string} #{args.join(" ")}"
|
133
|
+
command_set.process_line(complete_string, context)
|
134
|
+
end
|
135
|
+
|
136
|
+
def commands
|
137
|
+
command_set.commands
|
138
|
+
end
|
139
|
+
|
140
|
+
def text
|
141
|
+
Pry::Helpers::Text
|
142
|
+
end
|
143
|
+
|
144
|
+
def void
|
145
|
+
VOID_VALUE
|
146
|
+
end
|
147
|
+
|
148
|
+
include Pry::Helpers::BaseHelpers
|
149
|
+
include Pry::Helpers::CommandHelpers
|
150
|
+
|
151
|
+
|
152
|
+
# Instantiate a command, in preparation for calling it.
|
153
|
+
#
|
154
|
+
# @param Hash context The runtime context to use with this command.
|
155
|
+
def initialize(context={})
|
156
|
+
self.context = context
|
157
|
+
self.target = context[:target]
|
158
|
+
self.output = context[:output]
|
159
|
+
self.eval_string = context[:eval_string]
|
160
|
+
self.command_set = context[:command_set]
|
161
|
+
self._pry_ = context[:pry_instance]
|
162
|
+
end
|
163
|
+
|
164
|
+
# The value of {self} inside the {target} binding.
|
165
|
+
def target_self; target.eval('self'); end
|
166
|
+
|
167
|
+
# Revaluate the string (str) and perform interpolation.
|
168
|
+
# @param [String] str The string to reevaluate with interpolation.
|
169
|
+
#
|
170
|
+
# @return [String] The reevaluated string with interpolations
|
171
|
+
# applied (if any).
|
172
|
+
def interpolate_string(str)
|
173
|
+
dumped_str = str.dump
|
174
|
+
if dumped_str.gsub!(/\\\#\{/, '#{')
|
175
|
+
target.eval(dumped_str)
|
176
|
+
else
|
177
|
+
str
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Display a warning if a command collides with a local/method in
|
182
|
+
# the current scope.
|
183
|
+
# @param [String] command_name_match The name of the colliding command.
|
184
|
+
# @param [Binding] target The current binding context.
|
185
|
+
def check_for_command_name_collision(command_name_match)
|
186
|
+
if collision_type = target.eval("defined?(#{command_name_match})")
|
187
|
+
output.puts "#{Pry::Helpers::Text.bold('WARNING:')} Command name collision with a #{collision_type}: '#{command_name_match}'\n\n"
|
188
|
+
end
|
189
|
+
rescue Pry::RescuableException
|
190
|
+
end
|
191
|
+
|
192
|
+
# Extract necessary information from a line that Command.matches? this command.
|
193
|
+
#
|
194
|
+
# @param String the line of input
|
195
|
+
# @return [
|
196
|
+
# String the command name used, or portion of line that matched the command_regex
|
197
|
+
# String a string of all the arguments (i.e. everything but the name)
|
198
|
+
# Array the captures caught by the command_regex
|
199
|
+
# Array args the arguments got by splitting the arg_string
|
200
|
+
# ]
|
201
|
+
def tokenize(val)
|
202
|
+
val.replace(interpolate_string(val)) if command_options[:interpolate]
|
203
|
+
|
204
|
+
self.class.command_regex =~ val
|
205
|
+
|
206
|
+
# please call Command.matches? before Command#call_safely
|
207
|
+
raise CommandError, "fatal: called a command which didn't match?!" unless Regexp.last_match
|
208
|
+
captures = Regexp.last_match.captures
|
209
|
+
pos = Regexp.last_match.end(0)
|
210
|
+
|
211
|
+
arg_string = val[pos..-1]
|
212
|
+
|
213
|
+
# remove the one leading space if it exists
|
214
|
+
arg_string.slice!(0) if arg_string.start_with?(" ")
|
215
|
+
|
216
|
+
if arg_string
|
217
|
+
args = command_options[:shellwords] ? Shellwords.shellwords(arg_string) : arg_string.split(" ")
|
218
|
+
else
|
219
|
+
args = []
|
220
|
+
end
|
221
|
+
|
222
|
+
[val[0..pos].rstrip, arg_string, captures, args]
|
223
|
+
end
|
224
|
+
|
225
|
+
# Process a line that Command.matches? this command.
|
226
|
+
#
|
227
|
+
# @param String the line to process
|
228
|
+
# @return Object or Command::VOID_VALUE
|
229
|
+
def process_line(line)
|
230
|
+
command_name, arg_string, captures, args = tokenize(line)
|
231
|
+
|
232
|
+
check_for_command_name_collision(command_name) if Pry.config.collision_warning
|
233
|
+
|
234
|
+
self.arg_string = arg_string
|
235
|
+
self.captures = captures
|
236
|
+
|
237
|
+
call_safely(*(captures + args))
|
238
|
+
end
|
239
|
+
|
240
|
+
# Run the command with the given {args}.
|
241
|
+
#
|
242
|
+
# This is a public wrapper around {#call} which ensures all preconditions are met.
|
243
|
+
#
|
244
|
+
# @param *[String] the arguments to pass to this command.
|
245
|
+
# @return Object the return value of the {#call} method, or Command::VOID_VALUE
|
246
|
+
def call_safely(*args)
|
247
|
+
unless dependencies_met?
|
248
|
+
gems_needed = Array(command_options[:requires_gem])
|
249
|
+
gems_not_installed = gems_needed.select { |g| !gem_installed?(g) }
|
250
|
+
output.puts "\nThe command '#{name}' is #{Helpers::Text.bold("unavailable")} because it requires the following gems to be installed: #{(gems_not_installed.join(", "))}"
|
251
|
+
output.puts "-"
|
252
|
+
output.puts "Type `install-command #{name}` to install the required gems and activate this command."
|
253
|
+
return void
|
254
|
+
end
|
255
|
+
|
256
|
+
if command_options[:argument_required] && args.empty?
|
257
|
+
raise CommandError, "The command '#{name}' requires an argument."
|
258
|
+
end
|
259
|
+
|
260
|
+
ret = call_with_hooks(*args)
|
261
|
+
command_options[:keep_retval] ? ret : void
|
262
|
+
end
|
263
|
+
|
264
|
+
# Are all the gems required to use this command installed?
|
265
|
+
#
|
266
|
+
# @return Boolean
|
267
|
+
def dependencies_met?
|
268
|
+
@dependencies_met ||= command_dependencies_met?(command_options)
|
269
|
+
end
|
270
|
+
|
271
|
+
private
|
272
|
+
|
273
|
+
# Run the {#call} method and all the registered hooks.
|
274
|
+
#
|
275
|
+
# @param *String the arguments to #{call}
|
276
|
+
# @return Object the return value from #{call}
|
277
|
+
def call_with_hooks(*args)
|
278
|
+
self.class.hooks[:before].each do |block|
|
279
|
+
instance_exec(*args, &block)
|
280
|
+
end
|
281
|
+
|
282
|
+
ret = call(*args)
|
283
|
+
|
284
|
+
self.class.hooks[:after].each do |block|
|
285
|
+
ret = instance_exec(*args, &block)
|
286
|
+
end
|
287
|
+
|
288
|
+
ret
|
289
|
+
end
|
290
|
+
|
291
|
+
# Fix the number of arguments we pass to a block to avoid arity warnings.
|
292
|
+
#
|
293
|
+
# @param Number the arity of the block
|
294
|
+
# @param Array the arguments to pass
|
295
|
+
# @return Array a (possibly shorter) array of the arguments to pass
|
296
|
+
def correct_arg_arity(arity, args)
|
297
|
+
case
|
298
|
+
when arity < 0
|
299
|
+
args
|
300
|
+
when arity == 0
|
301
|
+
[]
|
302
|
+
when arity > 0
|
303
|
+
args.values_at(*(0..(arity - 1)).to_a)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
# A super-class for Commands that are created with a single block.
|
309
|
+
#
|
310
|
+
# This class ensures that the block is called with the correct number of arguments
|
311
|
+
# and the right context.
|
312
|
+
#
|
313
|
+
# Create subclasses using {Pry::CommandSet#command}.
|
314
|
+
class BlockCommand < Command
|
315
|
+
# backwards compatibility
|
316
|
+
alias_method :opts, :context
|
317
|
+
|
318
|
+
# Call the block that was registered with this command.
|
319
|
+
#
|
320
|
+
# @param *String the arguments passed
|
321
|
+
# @return Object the return value of the block
|
322
|
+
def call(*args)
|
323
|
+
instance_exec(*correct_arg_arity(block.arity, args), &block)
|
324
|
+
end
|
325
|
+
|
326
|
+
def help; description; end
|
327
|
+
end
|
328
|
+
|
329
|
+
# A super-class ofr Commands with structure.
|
330
|
+
#
|
331
|
+
# This class implements the bare-minimum functionality that a command should have,
|
332
|
+
# namely a --help switch, and then delegates actual processing to its subclasses.
|
333
|
+
#
|
334
|
+
# Create subclasses using {Pry::CommandSet#create_command}, and override the {options(opt)} method
|
335
|
+
# to set up an instance of Slop, and the {process} method to actually run the command. If
|
336
|
+
# necessary, you can also override {setup} which will be called before {options}, for example to
|
337
|
+
# require any gems your command needs to run, or to set up state.
|
338
|
+
class ClassCommand < Command
|
339
|
+
|
340
|
+
attr_accessor :opts
|
341
|
+
attr_accessor :args
|
342
|
+
|
343
|
+
# Set up {opts} and {args}, and then call {process}
|
344
|
+
#
|
345
|
+
# This function will display help if necessary.
|
346
|
+
#
|
347
|
+
# @param *String the arguments passed
|
348
|
+
# @return Object the return value of {process} or VOID_VALUE
|
349
|
+
def call(*args)
|
350
|
+
setup
|
351
|
+
|
352
|
+
self.opts = slop
|
353
|
+
self.args = self.opts.parse!(args)
|
354
|
+
|
355
|
+
if opts.present?(:help)
|
356
|
+
output.puts slop.help
|
357
|
+
void
|
358
|
+
else
|
359
|
+
process(*correct_arg_arity(method(:process).arity, args))
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
# Return the help generated by Slop for this command.
|
364
|
+
def help
|
365
|
+
slop.help
|
366
|
+
end
|
367
|
+
|
368
|
+
# Return an instance of Slop that can parse the options that this command accepts.
|
369
|
+
def slop
|
370
|
+
Slop.new do |opt|
|
371
|
+
opt.banner(unindent(self.class.banner))
|
372
|
+
options(opt)
|
373
|
+
opt.on(:h, :help, "Show this message.")
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
# A function called just before {options(opt)} as part of {call}.
|
378
|
+
#
|
379
|
+
# This function can be used to set up any context your command needs to run, for example
|
380
|
+
# requiring gems, or setting default values for options.
|
381
|
+
#
|
382
|
+
# @example
|
383
|
+
# def setup;
|
384
|
+
# require 'gist'
|
385
|
+
# @action = :method
|
386
|
+
# end
|
387
|
+
def setup; end
|
388
|
+
|
389
|
+
# A function to setup Slop so it can parse the options your command expects.
|
390
|
+
#
|
391
|
+
# NOTE: please don't do anything side-effecty in the main part of this method,
|
392
|
+
# as it may be called by Pry at any time for introspection reasons. If you need
|
393
|
+
# to set up default values, use {setup} instead.
|
394
|
+
#
|
395
|
+
# @example
|
396
|
+
# def options(opt)
|
397
|
+
# opt.banner "Gists methods or classes"
|
398
|
+
# opt.on(:c, :class, "gist a class") do
|
399
|
+
# @action = :class
|
400
|
+
# end
|
401
|
+
# end
|
402
|
+
def options(opt); end
|
403
|
+
|
404
|
+
# The actual body of your command should go here.
|
405
|
+
#
|
406
|
+
# The {opts} mehod can be called to get the options that Slop has passed,
|
407
|
+
# and {args} gives the remaining, unparsed arguments.
|
408
|
+
#
|
409
|
+
# The return value of this method is discarded unless the command was created
|
410
|
+
# with :keep_retval => true, in which case it is returned to the repl.
|
411
|
+
#
|
412
|
+
# @example
|
413
|
+
# def process
|
414
|
+
# if opts.present?(:class)
|
415
|
+
# gist_class
|
416
|
+
# else
|
417
|
+
# gist_method
|
418
|
+
# end
|
419
|
+
# end
|
420
|
+
def process; raise CommandError, "command '#{name}' not implemented" end
|
421
|
+
end
|
422
|
+
end
|