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.
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
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
@@ -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