pry 0.10.4-java → 0.11.0-java

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.rb +4 -4
  7. data/lib/pry/basic_object.rb +6 -0
  8. data/lib/pry/cli.rb +39 -34
  9. data/lib/pry/code.rb +6 -1
  10. data/lib/pry/code/code_file.rb +8 -2
  11. data/lib/pry/code_object.rb +23 -0
  12. data/lib/pry/color_printer.rb +20 -11
  13. data/lib/pry/command.rb +40 -16
  14. data/lib/pry/command_set.rb +9 -2
  15. data/lib/pry/commands/cat/exception_formatter.rb +11 -10
  16. data/lib/pry/commands/cat/file_formatter.rb +7 -3
  17. data/lib/pry/commands/code_collector.rb +16 -14
  18. data/lib/pry/commands/easter_eggs.rb +9 -9
  19. data/lib/pry/commands/edit.rb +7 -3
  20. data/lib/pry/commands/edit/file_and_line_locator.rb +1 -1
  21. data/lib/pry/commands/find_method.rb +1 -1
  22. data/lib/pry/commands/gem_open.rb +1 -1
  23. data/lib/pry/commands/gem_readme.rb +25 -0
  24. data/lib/pry/commands/gem_search.rb +40 -0
  25. data/lib/pry/commands/hist.rb +2 -2
  26. data/lib/pry/commands/jump_to.rb +7 -7
  27. data/lib/pry/commands/ls.rb +3 -1
  28. data/lib/pry/commands/ls/constants.rb +12 -1
  29. data/lib/pry/commands/ls/formatter.rb +1 -0
  30. data/lib/pry/commands/ls/jruby_hacks.rb +2 -2
  31. data/lib/pry/commands/ls/self_methods.rb +2 -0
  32. data/lib/pry/commands/play.rb +2 -2
  33. data/lib/pry/commands/reload_code.rb +2 -2
  34. data/lib/pry/commands/ri.rb +4 -0
  35. data/lib/pry/commands/shell_command.rb +34 -8
  36. data/lib/pry/commands/show_info.rb +10 -2
  37. data/lib/pry/commands/watch_expression/expression.rb +1 -1
  38. data/lib/pry/commands/whereami.rb +7 -6
  39. data/lib/pry/config.rb +3 -16
  40. data/lib/pry/config/behavior.rb +140 -49
  41. data/lib/pry/config/default.rb +21 -33
  42. data/lib/pry/config/memoization.rb +44 -0
  43. data/lib/pry/core_extensions.rb +12 -2
  44. data/lib/pry/editor.rb +1 -1
  45. data/lib/pry/exceptions.rb +1 -1
  46. data/lib/pry/forwardable.rb +23 -0
  47. data/lib/pry/helpers/base_helpers.rb +6 -10
  48. data/lib/pry/helpers/documentation_helpers.rb +1 -0
  49. data/lib/pry/helpers/options_helpers.rb +1 -1
  50. data/lib/pry/helpers/text.rb +69 -75
  51. data/lib/pry/history.rb +22 -1
  52. data/lib/pry/history_array.rb +1 -1
  53. data/lib/pry/hooks.rb +48 -107
  54. data/lib/pry/indent.rb +6 -2
  55. data/lib/pry/input_completer.rb +138 -120
  56. data/lib/pry/last_exception.rb +2 -2
  57. data/lib/pry/method.rb +15 -15
  58. data/lib/pry/method/disowned.rb +1 -0
  59. data/lib/pry/method/patcher.rb +0 -3
  60. data/lib/pry/output.rb +37 -38
  61. data/lib/pry/pager.rb +11 -8
  62. data/lib/pry/plugins.rb +20 -5
  63. data/lib/pry/pry_class.rb +30 -4
  64. data/lib/pry/pry_instance.rb +8 -6
  65. data/lib/pry/repl.rb +38 -8
  66. data/lib/pry/repl_file_loader.rb +1 -1
  67. data/lib/pry/rubygem.rb +3 -1
  68. data/lib/pry/slop.rb +661 -0
  69. data/lib/pry/slop/LICENSE +20 -0
  70. data/lib/pry/slop/commands.rb +196 -0
  71. data/lib/pry/slop/option.rb +208 -0
  72. data/lib/pry/terminal.rb +16 -5
  73. data/lib/pry/test/helper.rb +12 -3
  74. data/lib/pry/version.rb +1 -1
  75. data/lib/pry/wrapped_module.rb +7 -7
  76. data/lib/pry/{module_candidate.rb → wrapped_module/candidate.rb} +7 -13
  77. metadata +14 -19
data/lib/pry/pager.rb CHANGED
@@ -14,11 +14,12 @@ class Pry::Pager
14
14
  end
15
15
 
16
16
  # Send the given text through the best available pager (if `Pry.config.pager` is
17
- # enabled).
18
- # If you want to send text through in chunks as you generate it, use `open` to
19
- # get a writable object instead.
20
- # @param [String] text A piece of text to run through a pager.
21
- # @param [IO] output (`$stdout`) An object to send output to.
17
+ # enabled). If you want to send text through in chunks as you generate it, use `open`
18
+ # to get a writable object instead.
19
+ #
20
+ # @param [String] text
21
+ # Text to run through a pager.
22
+ #
22
23
  def page(text)
23
24
  open do |pager|
24
25
  pager << text
@@ -27,7 +28,6 @@ class Pry::Pager
27
28
 
28
29
  # Yields a pager object (`NullPager`, `SimplePager`, or `SystemPager`). All
29
30
  # pagers accept output with `#puts`, `#print`, `#write`, and `#<<`.
30
- # @param [IO] output (`$stdout`) An object to send output to.
31
31
  def open
32
32
  pager = best_available
33
33
  yield pager
@@ -48,7 +48,6 @@ class Pry::Pager
48
48
  # `#print`, `#write`, and `#<<`. You must call `#close` when you're done
49
49
  # writing output to a pager, and you must rescue `Pry::Pager::StopPaging`.
50
50
  # These requirements can be avoided by using `.open` instead.
51
- # @param [#<<] output ($stdout) An object to send output to.
52
51
  def best_available
53
52
  if !_pry_.config.pager
54
53
  NullPager.new(_pry_.output)
@@ -139,7 +138,11 @@ class Pry::Pager
139
138
  if @system_pager.nil?
140
139
  @system_pager = begin
141
140
  pager_executable = default_pager.split(' ').first
142
- `which #{pager_executable}`
141
+ if Pry::Helpers::BaseHelpers.windows? || Pry::Helpers::BaseHelpers.windows_ansi?
142
+ `where #{pager_executable}`
143
+ else
144
+ `which #{pager_executable}`
145
+ end
143
146
  $?.success?
144
147
  rescue
145
148
  false
data/lib/pry/plugins.rb CHANGED
@@ -60,6 +60,16 @@ class Pry
60
60
 
61
61
  alias active? active
62
62
  alias enabled? enabled
63
+
64
+ def supported?
65
+ pry_version = Gem::Version.new(VERSION)
66
+ spec.dependencies.each do |dependency|
67
+ if dependency.name == "pry"
68
+ return dependency.requirement.satisfied_by?(pry_version)
69
+ end
70
+ end
71
+ true
72
+ end
63
73
  end
64
74
 
65
75
  def initialize
@@ -68,11 +78,11 @@ class Pry
68
78
 
69
79
  # Find all installed Pry plugins and store them in an internal array.
70
80
  def locate_plugins
71
- Gem.refresh
72
- (Gem::Specification.respond_to?(:each) ? Gem::Specification : Gem.source_index.find_name('')).each do |gem|
81
+ gem_list.each do |gem|
73
82
  next if gem.name !~ PRY_PLUGIN_PREFIX
74
83
  plugin_name = gem.name.split('-', 2).last
75
- @plugins << Plugin.new(plugin_name, gem.name, gem, true) if !gem_located?(gem.name)
84
+ plugin = Plugin.new(plugin_name, gem.name, gem, false)
85
+ @plugins << plugin.tap(&:enable!) if plugin.supported? && !plugin_located?(plugin)
76
86
  end
77
87
  @plugins
78
88
  end
@@ -95,8 +105,13 @@ class Pry
95
105
  end
96
106
 
97
107
  private
98
- def gem_located?(gem_name)
99
- @plugins.any? { |plugin| plugin.gem_name == gem_name }
108
+ def plugin_located?(plugin)
109
+ @plugins.any? { |existing| existing.gem_name == plugin.gem_name }
110
+ end
111
+
112
+ def gem_list
113
+ Gem.refresh
114
+ Gem::Specification.respond_to?(:each) ? Gem::Specification : Gem.source_index.find_name('')
100
115
  end
101
116
  end
102
117
 
data/lib/pry/pry_class.rb CHANGED
@@ -5,7 +5,7 @@ class Pry
5
5
  LOCAL_RC_FILE = "./.pryrc"
6
6
 
7
7
  class << self
8
- extend Forwardable
8
+ extend Pry::Forwardable
9
9
  attr_accessor :custom_completions
10
10
  attr_accessor :current_line
11
11
  attr_accessor :line_buffer
@@ -32,6 +32,21 @@ class Pry
32
32
  def history
33
33
  @history ||= History.new
34
34
  end
35
+
36
+ #
37
+ # @example
38
+ # Pry.configure do |config|
39
+ # config.eager_load! # optional
40
+ # config.input = # ..
41
+ # config.foo = 2
42
+ # end
43
+ #
44
+ # @yield [config]
45
+ # Yields a block with {Pry.config} as its argument.
46
+ #
47
+ def configure
48
+ yield config
49
+ end
35
50
  end
36
51
 
37
52
  #
@@ -81,7 +96,7 @@ class Pry
81
96
  expanded = Pathname.new(File.expand_path(file)).realpath.to_s
82
97
  # For rbx 1.9 mode [see rubinius issue #2165]
83
98
  File.exist?(expanded) ? expanded : nil
84
- rescue Errno::ENOENT
99
+ rescue Errno::ENOENT, Errno::EACCES
85
100
  nil
86
101
  end
87
102
 
@@ -124,6 +139,11 @@ you can add "Pry.config.windows_console_warning = false" to your .pryrc.
124
139
  # note these have to be loaded here rather than in pry_instance as
125
140
  # we only want them loaded once per entire Pry lifetime.
126
141
  load_rc_files
142
+ end
143
+
144
+ def self.final_session_setup
145
+ return if @session_finalized
146
+ @session_finalized = true
127
147
  load_plugins if Pry.config.should_load_plugins
128
148
  load_requires if Pry.config.should_load_requires
129
149
  load_history if Pry.config.history.should_load
@@ -141,6 +161,9 @@ you can add "Pry.config.windows_console_warning = false" to your .pryrc.
141
161
  # Pry.start(Object.new, :input => MyInput.new)
142
162
  def self.start(target=nil, options={})
143
163
  return if ENV['DISABLE_PRY']
164
+ if ENV['FAIL_PRY']
165
+ raise 'You have FAIL_PRY set to true, which results in Pry calls failing'
166
+ end
144
167
  options = options.to_hash
145
168
 
146
169
  if in_critical_section?
@@ -150,8 +173,8 @@ you can add "Pry.config.windows_console_warning = false" to your .pryrc.
150
173
  end
151
174
 
152
175
  options[:target] = Pry.binding_for(target || toplevel_binding)
153
- options[:hooks] = Pry::Hooks.from_hash options.delete(:hooks) if options.key?(:hooks)
154
176
  initial_session_setup
177
+ final_session_setup
155
178
 
156
179
  # Unless we were given a backtrace, save the current one
157
180
  if options[:backtrace].nil?
@@ -233,7 +256,7 @@ you can add "Pry.config.windows_console_warning = false" to your .pryrc.
233
256
  # @param [String] command_string The Pry command (including arguments,
234
257
  # if any).
235
258
  # @param [Hash] options Optional named parameters.
236
- # @return [Object] The return value of the Pry command.
259
+ # @return [nil]
237
260
  # @option options [Object, Binding] :target The object to run the
238
261
  # command under. Defaults to `TOPLEVEL_BINDING` (main).
239
262
  # @option options [Boolean] :show_output Whether to show command
@@ -258,6 +281,7 @@ you can add "Pry.config.windows_console_warning = false" to your .pryrc.
258
281
 
259
282
  pry = Pry.new(:output => output, :target => target, :commands => options[:commands])
260
283
  pry.eval command_string
284
+ nil
261
285
  end
262
286
 
263
287
  def self.default_editor_for_platform
@@ -306,6 +330,8 @@ Readline version #{Readline::VERSION} detected - will not auto_resize! correctly
306
330
  # Set all the configurable options back to their default values
307
331
  def self.reset_defaults
308
332
  @initial_session = true
333
+ @session_finalized = nil
334
+
309
335
  self.config = Pry::Config.new Pry::Config::Default.new
310
336
  self.cli = false
311
337
  self.current_line = 1
@@ -16,7 +16,7 @@
16
16
  # This will show a list of available commands and their usage. For more
17
17
  # information about Pry you can refer to the following resources:
18
18
  #
19
- # * http://pry.github.com/
19
+ # * http://pryrepl.org/
20
20
  # * https://github.com/pry/pry
21
21
  # * the IRC channel, which is #pry on the Freenode network
22
22
  #
@@ -124,7 +124,7 @@ class Pry
124
124
  #
125
125
  # Generate completions.
126
126
  #
127
- # @param [String] input
127
+ # @param [String] str
128
128
  # What the user has typed so far
129
129
  #
130
130
  # @return [Array<String>]
@@ -269,7 +269,7 @@ class Pry
269
269
  @suppress_output = false
270
270
  inject_sticky_locals!
271
271
  begin
272
- if !process_command_safely(line.lstrip)
272
+ if !process_command_safely(line)
273
273
  @eval_string << "#{line.chomp}\n" if !line.empty? || !@eval_string.empty?
274
274
  end
275
275
  rescue RescuableException => e
@@ -373,7 +373,7 @@ class Pry
373
373
  # serialize something in the user's program, let's not assume we can serialize
374
374
  # the exception either.
375
375
  begin
376
- output.puts "(pry) output error: #{e.inspect}"
376
+ output.puts "(pry) output error: #{e.inspect}\n#{e.backtrace.join("\n")}"
377
377
  rescue RescuableException => e
378
378
  if last_result_is_exception?
379
379
  output.puts "(pry) output error: failed to show exception"
@@ -400,12 +400,14 @@ class Pry
400
400
  # @param [String] val The line to process.
401
401
  # @return [Boolean] `true` if `val` is a command, `false` otherwise
402
402
  def process_command(val)
403
+ val = val.lstrip if /^\s\S/ !~ val
403
404
  val = val.chomp
404
405
  result = commands.process_line(val,
405
406
  :target => current_binding,
406
407
  :output => output,
407
408
  :eval_string => @eval_string,
408
- :pry_instance => self
409
+ :pry_instance => self,
410
+ :hooks => hooks
409
411
  )
410
412
 
411
413
  # set a temporary (just so we can inject the value we want into eval_string)
@@ -433,7 +435,7 @@ class Pry
433
435
  # @return [Boolean] `true` if `val` is a command, `false` otherwise
434
436
  def process_command_safely(val)
435
437
  process_command(val)
436
- rescue CommandError, Slop::InvalidOptionError, MethodSource::SourceNotFoundError => e
438
+ rescue CommandError, Pry::Slop::InvalidOptionError, MethodSource::SourceNotFoundError => e
437
439
  Pry.last_internal_error = e
438
440
  output.puts "Error: #{e.message}"
439
441
  true
data/lib/pry/repl.rb CHANGED
@@ -1,8 +1,6 @@
1
- require 'forwardable'
2
-
3
1
  class Pry
4
2
  class REPL
5
- extend Forwardable
3
+ extend Pry::Forwardable
6
4
  def_delegators :@pry, :input, :output
7
5
 
8
6
  # @return [Pry] The instance of {Pry} that the user is controlling.
@@ -23,6 +21,8 @@ class Pry
23
21
  @pry = pry
24
22
  @indent = Pry::Indent.new
25
23
 
24
+ @readline_output = nil
25
+
26
26
  if options[:target]
27
27
  @pry.push_binding options[:target]
28
28
  end
@@ -168,20 +168,21 @@ class Pry
168
168
  # @return [String?] The next line of input, or `nil` on <Ctrl-D>.
169
169
  def read_line(current_prompt)
170
170
  handle_read_errors do
171
- if defined? Coolline and input.is_a? Coolline
171
+ if coolline_available?
172
172
  input.completion_proc = proc do |cool|
173
173
  completions = @pry.complete cool.completed_word
174
174
  completions.compact
175
175
  end
176
176
  elsif input.respond_to? :completion_proc=
177
- input.completion_proc = proc do |input|
178
- @pry.complete input
177
+ input.completion_proc = proc do |inp|
178
+ @pry.complete inp
179
179
  end
180
180
  end
181
181
 
182
- if defined?(Readline) and input == Readline
182
+ if readline_available?
183
+ set_readline_output
183
184
  input_readline(current_prompt, false) # false since we'll add it manually
184
- elsif defined? Coolline and input.is_a? Coolline
185
+ elsif coolline_available?
185
186
  input_readline(current_prompt)
186
187
  else
187
188
  if input.method(:readline).arity == 1
@@ -198,5 +199,34 @@ class Pry
198
199
  input.readline(*args)
199
200
  end
200
201
  end
202
+
203
+ def readline_available?
204
+ defined?(Readline) && input == Readline
205
+ end
206
+
207
+ def coolline_available?
208
+ defined?(Coolline) && input.is_a?(Coolline)
209
+ end
210
+
211
+ # If `$stdout` is not a tty, it's probably a pipe.
212
+ # @example
213
+ # # `piping?` returns `false`
214
+ # % pry
215
+ # [1] pry(main)
216
+ #
217
+ # # `piping?` returns `true`
218
+ # % pry | tee log
219
+ def piping?
220
+ return false unless $stdout.respond_to?(:tty?)
221
+ !$stdout.tty? && $stdin.tty? && !Pry::Helpers::BaseHelpers.windows?
222
+ end
223
+
224
+ # @return [void]
225
+ def set_readline_output
226
+ return if @readline_output
227
+ if piping?
228
+ @readline_output = (Readline.output = Pry.config.output)
229
+ end
230
+ end
201
231
  end
202
232
  end
@@ -13,7 +13,7 @@ class Pry
13
13
  class REPLFileLoader
14
14
  def initialize(file_name)
15
15
  full_name = File.expand_path(file_name)
16
- raise RuntimeError, "No such file: #{full_name}" if !File.exists?(full_name)
16
+ raise RuntimeError, "No such file: #{full_name}" if !File.exist?(full_name)
17
17
 
18
18
  define_additional_commands
19
19
  @content = File.read(full_name)
data/lib/pry/rubygem.rb CHANGED
@@ -57,7 +57,9 @@ class Pry
57
57
  # @param [String] name
58
58
  # @return [void]
59
59
  def install(name)
60
- gemrc_opts = Gem.configuration['gem'].split(' ')
60
+ require 'rubygems/dependency_installer'
61
+ gem_config = Gem.configuration['gem']
62
+ gemrc_opts = (gem_config.nil? ? "" : gem_config.split(' '))
61
63
  destination = if gemrc_opts.include?('--user-install')
62
64
  Gem.user_dir
63
65
  elsif File.writable?(Gem.dir)
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