pry 0.10.4 → 0.11.0

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -18
  3. data/LICENSE +1 -1
  4. data/README.md +32 -31
  5. data/bin/pry +3 -7
  6. data/lib/pry/basic_object.rb +6 -0
  7. data/lib/pry/cli.rb +39 -34
  8. data/lib/pry/code/code_file.rb +8 -2
  9. data/lib/pry/code.rb +6 -1
  10. data/lib/pry/code_object.rb +23 -0
  11. data/lib/pry/color_printer.rb +20 -11
  12. data/lib/pry/command.rb +40 -16
  13. data/lib/pry/command_set.rb +9 -2
  14. data/lib/pry/commands/cat/exception_formatter.rb +11 -10
  15. data/lib/pry/commands/cat/file_formatter.rb +7 -3
  16. data/lib/pry/commands/code_collector.rb +16 -14
  17. data/lib/pry/commands/easter_eggs.rb +9 -9
  18. data/lib/pry/commands/edit/file_and_line_locator.rb +1 -1
  19. data/lib/pry/commands/edit.rb +7 -3
  20. data/lib/pry/commands/find_method.rb +1 -1
  21. data/lib/pry/commands/gem_open.rb +1 -1
  22. data/lib/pry/commands/gem_readme.rb +25 -0
  23. data/lib/pry/commands/gem_search.rb +40 -0
  24. data/lib/pry/commands/hist.rb +2 -2
  25. data/lib/pry/commands/jump_to.rb +7 -7
  26. data/lib/pry/commands/ls/constants.rb +12 -1
  27. data/lib/pry/commands/ls/formatter.rb +1 -0
  28. data/lib/pry/commands/ls/jruby_hacks.rb +2 -2
  29. data/lib/pry/commands/ls/self_methods.rb +2 -0
  30. data/lib/pry/commands/ls.rb +3 -1
  31. data/lib/pry/commands/play.rb +2 -2
  32. data/lib/pry/commands/reload_code.rb +2 -2
  33. data/lib/pry/commands/ri.rb +4 -0
  34. data/lib/pry/commands/shell_command.rb +34 -8
  35. data/lib/pry/commands/show_info.rb +10 -2
  36. data/lib/pry/commands/watch_expression/expression.rb +1 -1
  37. data/lib/pry/commands/whereami.rb +7 -6
  38. data/lib/pry/config/behavior.rb +140 -49
  39. data/lib/pry/config/default.rb +21 -33
  40. data/lib/pry/config/memoization.rb +44 -0
  41. data/lib/pry/config.rb +3 -16
  42. data/lib/pry/core_extensions.rb +12 -2
  43. data/lib/pry/editor.rb +1 -1
  44. data/lib/pry/exceptions.rb +1 -1
  45. data/lib/pry/forwardable.rb +23 -0
  46. data/lib/pry/helpers/base_helpers.rb +6 -10
  47. data/lib/pry/helpers/documentation_helpers.rb +1 -0
  48. data/lib/pry/helpers/options_helpers.rb +1 -1
  49. data/lib/pry/helpers/text.rb +69 -75
  50. data/lib/pry/history.rb +22 -1
  51. data/lib/pry/history_array.rb +1 -1
  52. data/lib/pry/hooks.rb +48 -107
  53. data/lib/pry/indent.rb +6 -2
  54. data/lib/pry/input_completer.rb +138 -120
  55. data/lib/pry/last_exception.rb +2 -2
  56. data/lib/pry/method/disowned.rb +1 -0
  57. data/lib/pry/method/patcher.rb +0 -3
  58. data/lib/pry/method.rb +15 -15
  59. data/lib/pry/output.rb +37 -38
  60. data/lib/pry/pager.rb +11 -8
  61. data/lib/pry/plugins.rb +20 -5
  62. data/lib/pry/pry_class.rb +30 -4
  63. data/lib/pry/pry_instance.rb +8 -6
  64. data/lib/pry/repl.rb +38 -8
  65. data/lib/pry/repl_file_loader.rb +1 -1
  66. data/lib/pry/rubygem.rb +3 -1
  67. data/lib/pry/slop/LICENSE +20 -0
  68. data/lib/pry/slop/commands.rb +196 -0
  69. data/lib/pry/slop/option.rb +208 -0
  70. data/lib/pry/slop.rb +661 -0
  71. data/lib/pry/terminal.rb +16 -5
  72. data/lib/pry/test/helper.rb +12 -3
  73. data/lib/pry/version.rb +1 -1
  74. data/lib/pry/{module_candidate.rb → wrapped_module/candidate.rb} +7 -13
  75. data/lib/pry/wrapped_module.rb +7 -7
  76. data/lib/pry.rb +4 -4
  77. metadata +14 -19
data/lib/pry/slop.rb ADDED
@@ -0,0 +1,661 @@
1
+ class Pry::Slop
2
+ require_relative 'slop/option'
3
+ require_relative 'slop/commands'
4
+ include Enumerable
5
+ VERSION = '3.4.0'
6
+
7
+ # The main Error class, all Exception classes inherit from this class.
8
+ class Error < StandardError; end
9
+
10
+ # Raised when an option argument is expected but none are given.
11
+ class MissingArgumentError < Error; end
12
+
13
+ # Raised when an option is expected/required but not present.
14
+ class MissingOptionError < Error; end
15
+
16
+ # Raised when an argument does not match its intended match constraint.
17
+ class InvalidArgumentError < Error; end
18
+
19
+ # Raised when an invalid option is found and the strict flag is enabled.
20
+ class InvalidOptionError < Error; end
21
+
22
+ # Raised when an invalid command is found and the strict flag is enabled.
23
+ class InvalidCommandError < Error; end
24
+
25
+ # Returns a default Hash of configuration options this Slop instance uses.
26
+ DEFAULT_OPTIONS = {
27
+ :strict => false,
28
+ :help => false,
29
+ :banner => nil,
30
+ :ignore_case => false,
31
+ :autocreate => false,
32
+ :arguments => false,
33
+ :optional_arguments => false,
34
+ :multiple_switches => true,
35
+ :longest_flag => 0
36
+ }
37
+
38
+ class << self
39
+
40
+ # items - The Array of items to extract options from (default: ARGV).
41
+ # config - The Hash of configuration options to send to Slop.new().
42
+ # block - An optional block used to add options.
43
+ #
44
+ # Examples:
45
+ #
46
+ # Slop.parse(ARGV, :help => true) do
47
+ # on '-n', '--name', 'Your username', :argument => true
48
+ # end
49
+ #
50
+ # Returns a new instance of Slop.
51
+ def parse(items = ARGV, config = {}, &block)
52
+ parse! items.dup, config, &block
53
+ end
54
+
55
+ # items - The Array of items to extract options from (default: ARGV).
56
+ # config - The Hash of configuration options to send to Slop.new().
57
+ # block - An optional block used to add options.
58
+ #
59
+ # Returns a new instance of Slop.
60
+ def parse!(items = ARGV, config = {}, &block)
61
+ config, items = items, ARGV if items.is_a?(Hash) && config.empty?
62
+ slop = Pry::Slop.new config, &block
63
+ slop.parse! items
64
+ slop
65
+ end
66
+
67
+ # Build a Slop object from a option specification.
68
+ #
69
+ # This allows you to design your options via a simple String rather
70
+ # than programatically. Do note though that with this method, you're
71
+ # unable to pass any advanced options to the on() method when creating
72
+ # options.
73
+ #
74
+ # string - The optspec String
75
+ # config - A Hash of configuration options to pass to Slop.new
76
+ #
77
+ # Examples:
78
+ #
79
+ # opts = Slop.optspec(<<-SPEC)
80
+ # ruby foo.rb [options]
81
+ # ---
82
+ # n,name= Your name
83
+ # a,age= Your age
84
+ # A,auth Sign in with auth
85
+ # p,passcode= Your secret pass code
86
+ # SPEC
87
+ #
88
+ # opts.fetch_option(:name).description #=> "Your name"
89
+ #
90
+ # Returns a new instance of Slop.
91
+ def optspec(string, config = {})
92
+ config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/]
93
+ lines = optspec.split("\n").reject(&:empty?)
94
+ opts = Slop.new(config)
95
+
96
+ lines.each do |line|
97
+ opt, description = line.split(' ', 2)
98
+ short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') }
99
+ opt = opts.on(short, long, description)
100
+
101
+ if long && long.end_with?('=')
102
+ long.sub!(/\=$/, '')
103
+ opt.config[:argument] = true
104
+ end
105
+ end
106
+
107
+ opts
108
+ end
109
+
110
+ end
111
+
112
+ # The Hash of configuration options for this Slop instance.
113
+ attr_reader :config
114
+
115
+ # The Array of Slop::Option objects tied to this Slop instance.
116
+ attr_reader :options
117
+
118
+ # Create a new instance of Slop and optionally build options via a block.
119
+ #
120
+ # config - A Hash of configuration options.
121
+ # block - An optional block used to specify options.
122
+ def initialize(config = {}, &block)
123
+ @config = DEFAULT_OPTIONS.merge(config)
124
+ @options = []
125
+ @commands = {}
126
+ @trash = []
127
+ @triggered_options = []
128
+ @unknown_options = []
129
+ @callbacks = {}
130
+ @separators = {}
131
+ @runner = nil
132
+
133
+ if block_given?
134
+ block.arity == 1 ? yield(self) : instance_eval(&block)
135
+ end
136
+
137
+ if config[:help]
138
+ on('-h', '--help', 'Display this help message.', :tail => true) do
139
+ $stderr.puts help
140
+ end
141
+ end
142
+ end
143
+
144
+ # Is strict mode enabled?
145
+ #
146
+ # Returns true if strict mode is enabled, false otherwise.
147
+ def strict?
148
+ config[:strict]
149
+ end
150
+
151
+ # Set the banner.
152
+ #
153
+ # banner - The String to set the banner.
154
+ def banner=(banner)
155
+ config[:banner] = banner
156
+ end
157
+
158
+ # Get or set the banner.
159
+ #
160
+ # banner - The String to set the banner.
161
+ #
162
+ # Returns the banner String.
163
+ def banner(banner = nil)
164
+ config[:banner] = banner if banner
165
+ config[:banner]
166
+ end
167
+
168
+ # Set the description (used for commands).
169
+ #
170
+ # desc - The String to set the description.
171
+ def description=(desc)
172
+ config[:description] = desc
173
+ end
174
+
175
+ # Get or set the description (used for commands).
176
+ #
177
+ # desc - The String to set the description.
178
+ #
179
+ # Returns the description String.
180
+ def description(desc = nil)
181
+ config[:description] = desc if desc
182
+ config[:description]
183
+ end
184
+
185
+ # Add a new command.
186
+ #
187
+ # command - The Symbol or String used to identify this command.
188
+ # options - A Hash of configuration options (see Slop::new)
189
+ #
190
+ # Returns a new instance of Slop mapped to this command.
191
+ def command(command, options = {}, &block)
192
+ @commands[command.to_s] = Pry::Slop.new(options, &block)
193
+ end
194
+
195
+ # Parse a list of items, executing and gathering options along the way.
196
+ #
197
+ # items - The Array of items to extract options from (default: ARGV).
198
+ # block - An optional block which when used will yield non options.
199
+ #
200
+ # Returns an Array of original items.
201
+ def parse(items = ARGV, &block)
202
+ parse! items.dup, &block
203
+ items
204
+ end
205
+
206
+ # Parse a list of items, executing and gathering options along the way.
207
+ # unlike parse() this method will remove any options and option arguments
208
+ # from the original Array.
209
+ #
210
+ # items - The Array of items to extract options from (default: ARGV).
211
+ # block - An optional block which when used will yield non options.
212
+ #
213
+ # Returns an Array of original items with options removed.
214
+ def parse!(items = ARGV, &block)
215
+ if items.empty? && @callbacks[:empty]
216
+ @callbacks[:empty].each { |cb| cb.call(self) }
217
+ return items
218
+ end
219
+
220
+ if cmd = @commands[items[0]]
221
+ return cmd.parse! items[1..-1]
222
+ end
223
+
224
+ items.each_with_index do |item, index|
225
+ @trash << index && break if item == '--'
226
+ autocreate(items, index) if config[:autocreate]
227
+ process_item(items, index, &block) unless @trash.include?(index)
228
+ end
229
+ items.reject!.with_index { |item, index| @trash.include?(index) }
230
+
231
+ missing_options = options.select { |opt| opt.required? && opt.count < 1 }
232
+ if missing_options.any?
233
+ raise MissingOptionError,
234
+ "Missing required option(s): #{missing_options.map(&:key).join(', ')}"
235
+ end
236
+
237
+ if @unknown_options.any?
238
+ raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}"
239
+ end
240
+
241
+ if @triggered_options.empty? && @callbacks[:no_options]
242
+ @callbacks[:no_options].each { |cb| cb.call(self) }
243
+ end
244
+
245
+ @runner.call(self, items) if @runner.respond_to?(:call)
246
+
247
+ items
248
+ end
249
+
250
+ # Add an Option.
251
+ #
252
+ # objects - An Array with an optional Hash as the last element.
253
+ #
254
+ # Examples:
255
+ #
256
+ # on '-u', '--username=', 'Your username'
257
+ # on :v, :verbose, 'Enable verbose mode'
258
+ #
259
+ # Returns the created instance of Slop::Option.
260
+ def on(*objects, &block)
261
+ option = build_option(objects, &block)
262
+ options << option
263
+ option
264
+ end
265
+ alias option on
266
+ alias opt on
267
+
268
+ # Fetch an options argument value.
269
+ #
270
+ # key - The Symbol or String option short or long flag.
271
+ #
272
+ # Returns the Object value for this option, or nil.
273
+ def [](key)
274
+ option = fetch_option(key)
275
+ option.value if option
276
+ end
277
+ alias get []
278
+
279
+ # Returns a new Hash with option flags as keys and option values as values.
280
+ #
281
+ # include_commands - If true, merge options from all sub-commands.
282
+ def to_hash(include_commands = false)
283
+ hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }]
284
+ if include_commands
285
+ @commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) }
286
+ end
287
+ hash
288
+ end
289
+ alias to_h to_hash
290
+
291
+ # Enumerable interface. Yields each Slop::Option.
292
+ def each(&block)
293
+ options.each(&block)
294
+ end
295
+
296
+ # Specify code to be executed when these options are parsed.
297
+ #
298
+ # callable - An object responding to a call method.
299
+ #
300
+ # yields - The instance of Slop parsing these options
301
+ # An Array of unparsed arguments
302
+ #
303
+ # Example:
304
+ #
305
+ # Slop.parse do
306
+ # on :v, :verbose
307
+ #
308
+ # run do |opts, args|
309
+ # puts "Arguments: #{args.inspect}" if opts.verbose?
310
+ # end
311
+ # end
312
+ def run(callable = nil, &block)
313
+ @runner = callable || block
314
+ unless @runner.respond_to?(:call)
315
+ raise ArgumentError, "You must specify a callable object or a block to #run"
316
+ end
317
+ end
318
+
319
+ # Check for an options presence.
320
+ #
321
+ # Examples:
322
+ #
323
+ # opts.parse %w( --foo )
324
+ # opts.present?(:foo) #=> true
325
+ # opts.present?(:bar) #=> false
326
+ #
327
+ # Returns true if all of the keys are present in the parsed arguments.
328
+ def present?(*keys)
329
+ keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 }
330
+ end
331
+
332
+ # Override this method so we can check if an option? method exists.
333
+ #
334
+ # Returns true if this option key exists in our list of options.
335
+ def respond_to_missing?(method_name, include_all=false)
336
+ options.any? { |o| o.key == method_name.to_s.chop } || super
337
+ end
338
+
339
+ # Fetch a list of options which were missing from the parsed list.
340
+ #
341
+ # Examples:
342
+ #
343
+ # opts = Slop.new do
344
+ # on :n, :name=
345
+ # on :p, :password=
346
+ # end
347
+ #
348
+ # opts.parse %w[ --name Lee ]
349
+ # opts.missing #=> ['password']
350
+ #
351
+ # Returns an Array of Strings representing missing options.
352
+ def missing
353
+ (options - @triggered_options).map(&:key)
354
+ end
355
+
356
+ # Fetch a Slop::Option object.
357
+ #
358
+ # key - The Symbol or String option key.
359
+ #
360
+ # Examples:
361
+ #
362
+ # opts.on(:foo, 'Something fooey', :argument => :optional)
363
+ # opt = opts.fetch_option(:foo)
364
+ # opt.class #=> Slop::Option
365
+ # opt.accepts_optional_argument? #=> true
366
+ #
367
+ # Returns an Option or nil if none were found.
368
+ def fetch_option(key)
369
+ options.find { |option| [option.long, option.short].include?(clean(key)) }
370
+ end
371
+
372
+ # Fetch a Slop object associated with this command.
373
+ #
374
+ # command - The String or Symbol name of the command.
375
+ #
376
+ # Examples:
377
+ #
378
+ # opts.command :foo do
379
+ # on :v, :verbose, 'Enable verbose mode'
380
+ # end
381
+ #
382
+ # # ruby run.rb foo -v
383
+ # opts.fetch_command(:foo).verbose? #=> true
384
+ def fetch_command(command)
385
+ @commands[command.to_s]
386
+ end
387
+
388
+ # Add a callback.
389
+ #
390
+ # label - The Symbol identifier to attach this callback.
391
+ #
392
+ # Returns nothing.
393
+ def add_callback(label, &block)
394
+ (@callbacks[label] ||= []) << block
395
+ end
396
+
397
+ # Add string separators between options.
398
+ #
399
+ # text - The String text to print.
400
+ def separator(text)
401
+ if @separators[options.size]
402
+ @separators[options.size] << "\n#{text}"
403
+ else
404
+ @separators[options.size] = text
405
+ end
406
+ end
407
+
408
+ # Print a handy Slop help string.
409
+ #
410
+ # Returns the banner followed by available option help strings.
411
+ def to_s
412
+ heads = options.reject(&:tail?)
413
+ tails = (options - heads)
414
+ opts = (heads + tails).select(&:help).map(&:to_s)
415
+ optstr = opts.each_with_index.map { |o, i|
416
+ (str = @separators[i + 1]) ? [o, str].join("\n") : o
417
+ }.join("\n")
418
+
419
+ if @commands.any?
420
+ optstr << "\n" if !optstr.empty?
421
+ optstr << "\nAvailable commands:\n\n"
422
+ optstr << commands_to_help
423
+ optstr << "\n\nSee `<command> --help` for more information on a specific command."
424
+ end
425
+
426
+ banner = config[:banner]
427
+ banner = "Usage: #{File.basename($0, '.*')}#{' [command]' if @commands.any?} [options]" if banner.nil?
428
+ if banner
429
+ "#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}"
430
+ else
431
+ optstr
432
+ end
433
+ end
434
+ alias help to_s
435
+
436
+ private
437
+
438
+ # Convenience method for present?(:option).
439
+ #
440
+ # Examples:
441
+ #
442
+ # opts.parse %( --verbose )
443
+ # opts.verbose? #=> true
444
+ # opts.other? #=> false
445
+ #
446
+ # Returns true if this option is present. If this method does not end
447
+ # with a ? character it will instead call super().
448
+ def method_missing(method, *args, &block)
449
+ meth = method.to_s
450
+ if meth.end_with?('?')
451
+ meth.chop!
452
+ present?(meth) || present?(meth.gsub('_', '-'))
453
+ else
454
+ super
455
+ end
456
+ end
457
+
458
+ # Process a list item, figure out if it's an option, execute any
459
+ # callbacks, assign any option arguments, and do some sanity checks.
460
+ #
461
+ # items - The Array of items to process.
462
+ # index - The current Integer index of the item we want to process.
463
+ # block - An optional block which when passed will yield non options.
464
+ #
465
+ # Returns nothing.
466
+ def process_item(items, index, &block)
467
+ return unless item = items[index]
468
+ option, argument = extract_option(item) if item.start_with?('-')
469
+
470
+ if option
471
+ option.count += 1 unless item.start_with?('--no-')
472
+ option.count += 1 if option.key[0, 3] == "no-"
473
+ @trash << index
474
+ @triggered_options << option
475
+
476
+ if option.expects_argument?
477
+ argument ||= items.at(index + 1)
478
+
479
+ if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/
480
+ raise MissingArgumentError, "#{option.key} expects an argument"
481
+ end
482
+
483
+ execute_option(option, argument, index, item)
484
+ elsif option.accepts_optional_argument?
485
+ argument ||= items.at(index + 1)
486
+
487
+ if argument && argument =~ /\A([^\-?]|-\d)+/
488
+ execute_option(option, argument, index, item)
489
+ else
490
+ option.call(nil)
491
+ end
492
+ elsif config[:multiple_switches] && argument
493
+ execute_multiple_switches(option, argument, index)
494
+ else
495
+ option.value = option.count > 0
496
+ option.call(nil)
497
+ end
498
+ else
499
+ @unknown_options << item if strict? && item =~ /\A--?/
500
+ block.call(item) if block && !@trash.include?(index)
501
+ end
502
+ end
503
+
504
+ # Execute an option, firing off callbacks and assigning arguments.
505
+ #
506
+ # option - The Slop::Option object found by #process_item.
507
+ # argument - The argument Object to assign to this option.
508
+ # index - The current Integer index of the object we're processing.
509
+ # item - The optional String item we're processing.
510
+ #
511
+ # Returns nothing.
512
+ def execute_option(option, argument, index, item = nil)
513
+ if !option
514
+ if config[:multiple_switches] && strict?
515
+ raise InvalidOptionError, "Unknown option -#{item}"
516
+ end
517
+ return
518
+ end
519
+
520
+ if argument
521
+ unless item && item.end_with?("=#{argument}")
522
+ @trash << index + 1 unless option.argument_in_value
523
+ end
524
+ option.value = argument
525
+ else
526
+ option.value = option.count > 0
527
+ end
528
+
529
+ if option.match? && !argument.match(option.config[:match])
530
+ raise InvalidArgumentError, "#{argument} is an invalid argument"
531
+ end
532
+
533
+ option.call(option.value)
534
+ end
535
+
536
+ # Execute a `-abc` type option where a, b and c are all options. This
537
+ # method is only executed if the multiple_switches argument is true.
538
+ #
539
+ # option - The first Option object.
540
+ # argument - The argument to this option. (Split into multiple Options).
541
+ # index - The index of the current item being processed.
542
+ #
543
+ # Returns nothing.
544
+ def execute_multiple_switches(option, argument, index)
545
+ execute_option(option, nil, index)
546
+ argument.split('').each do |key|
547
+ next unless opt = fetch_option(key)
548
+ opt.count += 1
549
+ execute_option(opt, nil, index, key)
550
+ end
551
+ end
552
+
553
+ # Extract an option from a flag.
554
+ #
555
+ # flag - The flag key used to extract an option.
556
+ #
557
+ # Returns an Array of [option, argument].
558
+ def extract_option(flag)
559
+ option = fetch_option(flag)
560
+ option ||= fetch_option(flag.downcase) if config[:ignore_case]
561
+ option ||= fetch_option(flag.gsub(/([^-])-/, '\1_'))
562
+
563
+ unless option
564
+ case flag
565
+ when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/
566
+ option, argument = fetch_option($1), ($2 || false)
567
+ option.argument_in_value = true if option
568
+ end
569
+ end
570
+
571
+ [option, argument]
572
+ end
573
+
574
+ # Autocreate an option on the fly. See the :autocreate Slop config option.
575
+ #
576
+ # items - The Array of items we're parsing.
577
+ # index - The current Integer index for the item we're processing.
578
+ #
579
+ # Returns nothing.
580
+ def autocreate(items, index)
581
+ flag = items[index]
582
+ if !fetch_option(flag) && !@trash.include?(index)
583
+ option = build_option(Array(flag))
584
+ argument = items[index + 1]
585
+ option.config[:argument] = (argument && argument !~ /\A--?/)
586
+ option.config[:autocreated] = true
587
+ options << option
588
+ end
589
+ end
590
+
591
+ # Build an option from a list of objects.
592
+ #
593
+ # objects - An Array of objects used to build this option.
594
+ #
595
+ # Returns a new instance of Slop::Option.
596
+ def build_option(objects, &block)
597
+ config = {}
598
+ config[:argument] = true if @config[:arguments]
599
+ config[:optional_argument] = true if @config[:optional_arguments]
600
+
601
+ if objects.last.is_a?(Hash)
602
+ config.merge!(objects.last)
603
+ objects.pop
604
+ end
605
+ short = extract_short_flag(objects, config)
606
+ long = extract_long_flag(objects, config)
607
+ desc = objects[0].respond_to?(:to_str) ? objects.shift : nil
608
+
609
+ Option.new(self, short, long, desc, config, &block)
610
+ end
611
+
612
+ # Extract the short flag from an item.
613
+ #
614
+ # objects - The Array of objects passed from #build_option.
615
+ # config - The Hash of configuration options built in #build_option.
616
+ def extract_short_flag(objects, config)
617
+ flag = clean(objects.first)
618
+
619
+ if flag.size == 2 && flag.end_with?('=')
620
+ config[:argument] ||= true
621
+ flag.chop!
622
+ end
623
+
624
+ if flag.size == 1
625
+ objects.shift
626
+ flag
627
+ end
628
+ end
629
+
630
+ # Extract the long flag from an item.
631
+ #
632
+ # objects - The Array of objects passed from #build_option.
633
+ # config - The Hash of configuration options built in #build_option.
634
+ def extract_long_flag(objects, config)
635
+ flag = objects.first.to_s
636
+ if flag =~ /\A(?:--?)?[a-zA-Z][a-zA-Z0-9_-]+\=?\??\z/
637
+ config[:argument] ||= true if flag.end_with?('=')
638
+ config[:optional_argument] = true if flag.end_with?('=?')
639
+ objects.shift
640
+ clean(flag).sub(/\=\??\z/, '')
641
+ end
642
+ end
643
+
644
+ # Remove any leading -- characters from a string.
645
+ #
646
+ # object - The Object we want to cast to a String and clean.
647
+ #
648
+ # Returns the newly cleaned String with leading -- characters removed.
649
+ def clean(object)
650
+ object.to_s.sub(/\A--?/, '')
651
+ end
652
+
653
+ def commands_to_help
654
+ padding = 0
655
+ @commands.each { |c, _| padding = c.size if c.size > padding }
656
+ @commands.map do |cmd, opts|
657
+ " #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}"
658
+ end.join("\n")
659
+ end
660
+
661
+ end
data/lib/pry/terminal.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  class Pry::Terminal
2
3
  class << self
3
4
  # Return a pair of [rows, columns] which gives the size of the window.
@@ -41,11 +42,21 @@ class Pry::Terminal
41
42
 
42
43
  def screen_size_according_to_io_console
43
44
  return if Pry::Helpers::BaseHelpers.jruby?
44
- require 'io/console'
45
- $stdout.winsize if $stdout.tty? and $stdout.respond_to?(:winsize)
46
- rescue LoadError
47
- # They probably don't have the io/console stdlib or the io-console gem.
48
- # We'll keep trying.
45
+
46
+ begin
47
+ require 'io/console'
48
+
49
+ begin
50
+ if $stdout.respond_to?(:tty?) && $stdout.tty? && $stdout.respond_to?(:winsize)
51
+ $stdout.winsize
52
+ end
53
+ rescue Errno::EOPNOTSUPP
54
+ # $stdout is probably a socket, which doesn't support #winsize.
55
+ end
56
+ rescue LoadError
57
+ # They probably don't have the io/console stdlib or the io-console gem.
58
+ # We'll keep trying.
59
+ end
49
60
  end
50
61
 
51
62
  def screen_size_according_to_env