pry 0.9.7.4-i386-mingw32 → 0.9.8-i386-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.gitignore +2 -3
  2. data/CHANGELOG +43 -0
  3. data/README.markdown +3 -1
  4. data/Rakefile +51 -32
  5. data/bin/pry +2 -80
  6. data/lib/pry.rb +33 -26
  7. data/lib/pry/cli.rb +152 -0
  8. data/lib/pry/code.rb +351 -0
  9. data/lib/pry/command.rb +422 -0
  10. data/lib/pry/command_set.rb +259 -129
  11. data/lib/pry/commands.rb +0 -1
  12. data/lib/pry/config.rb +43 -9
  13. data/lib/pry/default_commands/context.rb +109 -92
  14. data/lib/pry/default_commands/documentation.rb +174 -63
  15. data/lib/pry/default_commands/easter_eggs.rb +26 -2
  16. data/lib/pry/default_commands/gems.rb +65 -37
  17. data/lib/pry/default_commands/input.rb +175 -243
  18. data/lib/pry/default_commands/introspection.rb +173 -112
  19. data/lib/pry/default_commands/ls.rb +96 -114
  20. data/lib/pry/default_commands/shell.rb +175 -70
  21. data/lib/pry/helpers/base_helpers.rb +7 -2
  22. data/lib/pry/helpers/command_helpers.rb +71 -77
  23. data/lib/pry/helpers/options_helpers.rb +10 -41
  24. data/lib/pry/helpers/text.rb +24 -4
  25. data/lib/pry/history.rb +55 -17
  26. data/lib/pry/history_array.rb +2 -0
  27. data/lib/pry/hooks.rb +252 -0
  28. data/lib/pry/indent.rb +9 -5
  29. data/lib/pry/method.rb +149 -50
  30. data/lib/pry/plugins.rb +12 -4
  31. data/lib/pry/pry_class.rb +69 -26
  32. data/lib/pry/pry_instance.rb +187 -115
  33. data/lib/pry/version.rb +1 -1
  34. data/lib/pry/wrapped_module.rb +73 -0
  35. data/man/pry.1 +195 -0
  36. data/man/pry.1.html +204 -0
  37. data/man/pry.1.ronn +141 -0
  38. data/pry.gemspec +29 -32
  39. data/test/helper.rb +32 -36
  40. data/test/test_cli.rb +78 -0
  41. data/test/test_code.rb +201 -0
  42. data/test/test_command.rb +327 -0
  43. data/test/test_command_integration.rb +512 -0
  44. data/test/test_command_set.rb +338 -12
  45. data/test/test_completion.rb +1 -1
  46. data/test/test_default_commands.rb +1 -2
  47. data/test/test_default_commands/test_context.rb +27 -5
  48. data/test/test_default_commands/test_documentation.rb +20 -8
  49. data/test/test_default_commands/test_input.rb +84 -45
  50. data/test/test_default_commands/test_introspection.rb +74 -17
  51. data/test/test_default_commands/test_ls.rb +9 -36
  52. data/test/test_default_commands/test_shell.rb +240 -13
  53. data/test/test_hooks.rb +490 -0
  54. data/test/test_indent.rb +2 -0
  55. data/test/test_method.rb +60 -0
  56. data/test/test_pry.rb +29 -904
  57. data/test/test_pry_defaults.rb +380 -0
  58. data/test/test_pry_history.rb +24 -24
  59. data/test/test_syntax_checking.rb +63 -0
  60. data/test/test_wrapped_module.rb +71 -0
  61. metadata +50 -39
  62. data/lib/pry/command_context.rb +0 -53
  63. data/lib/pry/command_processor.rb +0 -181
  64. data/lib/pry/extended_commands/user_command_api.rb +0 -65
  65. data/test/test_command_processor.rb +0 -176
@@ -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
@@ -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