amp-front 0.1.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 (78) hide show
  1. data/.document +5 -0
  2. data/.gitignore +24 -0
  3. data/Ampfile +3 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +36 -0
  6. data/LICENSE +20 -0
  7. data/README.md +50 -0
  8. data/Rakefile +64 -0
  9. data/VERSION +1 -0
  10. data/design_docs/commands.md +91 -0
  11. data/design_docs/dependencies.md +35 -0
  12. data/design_docs/plugins.md +47 -0
  13. data/features/amp.feature +8 -0
  14. data/features/amp_help.feature +36 -0
  15. data/features/amp_plugin_list.feature +10 -0
  16. data/features/step_definitions/amp-front_steps.rb +23 -0
  17. data/features/support/env.rb +4 -0
  18. data/lib/amp-front.rb +30 -0
  19. data/lib/amp-front/dispatch/commands/base.rb +158 -0
  20. data/lib/amp-front/dispatch/commands/builtin/help.rb +23 -0
  21. data/lib/amp-front/dispatch/commands/builtin/plugin.rb +24 -0
  22. data/lib/amp-front/dispatch/commands/validations.rb +171 -0
  23. data/lib/amp-front/dispatch/runner.rb +86 -0
  24. data/lib/amp-front/help/entries/__default__.erb +31 -0
  25. data/lib/amp-front/help/entries/ampfiles.md +42 -0
  26. data/lib/amp-front/help/entries/commands.erb +6 -0
  27. data/lib/amp-front/help/entries/new-commands.md +81 -0
  28. data/lib/amp-front/help/help.rb +312 -0
  29. data/lib/amp-front/plugins/base.rb +87 -0
  30. data/lib/amp-front/support/module_extensions.rb +92 -0
  31. data/lib/amp-front/third_party/maruku.rb +136 -0
  32. data/lib/amp-front/third_party/maruku/attributes.rb +227 -0
  33. data/lib/amp-front/third_party/maruku/defaults.rb +71 -0
  34. data/lib/amp-front/third_party/maruku/errors_management.rb +92 -0
  35. data/lib/amp-front/third_party/maruku/helpers.rb +260 -0
  36. data/lib/amp-front/third_party/maruku/input/charsource.rb +326 -0
  37. data/lib/amp-front/third_party/maruku/input/extensions.rb +69 -0
  38. data/lib/amp-front/third_party/maruku/input/html_helper.rb +189 -0
  39. data/lib/amp-front/third_party/maruku/input/linesource.rb +111 -0
  40. data/lib/amp-front/third_party/maruku/input/parse_block.rb +615 -0
  41. data/lib/amp-front/third_party/maruku/input/parse_doc.rb +234 -0
  42. data/lib/amp-front/third_party/maruku/input/parse_span_better.rb +746 -0
  43. data/lib/amp-front/third_party/maruku/input/rubypants.rb +225 -0
  44. data/lib/amp-front/third_party/maruku/input/type_detection.rb +147 -0
  45. data/lib/amp-front/third_party/maruku/input_textile2/t2_parser.rb +163 -0
  46. data/lib/amp-front/third_party/maruku/maruku.rb +33 -0
  47. data/lib/amp-front/third_party/maruku/output/to_ansi.rb +223 -0
  48. data/lib/amp-front/third_party/maruku/output/to_html.rb +991 -0
  49. data/lib/amp-front/third_party/maruku/output/to_markdown.rb +164 -0
  50. data/lib/amp-front/third_party/maruku/output/to_s.rb +56 -0
  51. data/lib/amp-front/third_party/maruku/string_utils.rb +191 -0
  52. data/lib/amp-front/third_party/maruku/structures.rb +167 -0
  53. data/lib/amp-front/third_party/maruku/structures_inspect.rb +87 -0
  54. data/lib/amp-front/third_party/maruku/structures_iterators.rb +61 -0
  55. data/lib/amp-front/third_party/maruku/textile2.rb +1 -0
  56. data/lib/amp-front/third_party/maruku/toc.rb +199 -0
  57. data/lib/amp-front/third_party/maruku/usage/example1.rb +33 -0
  58. data/lib/amp-front/third_party/maruku/version.rb +40 -0
  59. data/lib/amp-front/third_party/trollop.rb +766 -0
  60. data/spec/amp-front_spec.rb +25 -0
  61. data/spec/command_specs/base_spec.rb +123 -0
  62. data/spec/command_specs/command_spec.rb +97 -0
  63. data/spec/command_specs/help_spec.rb +33 -0
  64. data/spec/command_specs/spec_helper.rb +37 -0
  65. data/spec/command_specs/validations_spec.rb +267 -0
  66. data/spec/dispatch_specs/runner_spec.rb +116 -0
  67. data/spec/dispatch_specs/spec_helper.rb +15 -0
  68. data/spec/help_specs/help_entry_spec.rb +78 -0
  69. data/spec/help_specs/help_registry_spec.rb +77 -0
  70. data/spec/help_specs/spec_helper.rb +15 -0
  71. data/spec/plugin_specs/base_spec.rb +36 -0
  72. data/spec/plugin_specs/spec_helper.rb +15 -0
  73. data/spec/spec.opts +1 -0
  74. data/spec/spec_helper.rb +33 -0
  75. data/spec/support_specs/module_extensions_spec.rb +104 -0
  76. data/spec/support_specs/spec_helper.rb +15 -0
  77. data/test/third_party_tests/test_trollop.rb +1181 -0
  78. metadata +192 -0
@@ -0,0 +1,87 @@
1
+ #--
2
+ # Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
3
+ #
4
+ # This file is part of Maruku.
5
+ #
6
+ # Maruku is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Maruku is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Maruku; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+ #++
20
+
21
+
22
+
23
+ class String
24
+ def inspect_more(a=nil,b=nil)
25
+ inspect
26
+ end
27
+ end
28
+
29
+ class Object
30
+ def inspect_more(a=nil,b=nil)
31
+ inspect
32
+ end
33
+ end
34
+
35
+ class Array
36
+ def inspect_more(compact, join_string, add_brackets=true)
37
+ s = map {|x|
38
+ x.kind_of?(String) ? x.inspect :
39
+ x.kind_of?(MaRuKu::MDElement) ? x.inspect(compact) :
40
+ (raise "WTF #{x.class} #{x.inspect}")
41
+ }.join(join_string)
42
+
43
+ add_brackets ? "[#{s}]" : s
44
+ end
45
+ end
46
+
47
+ class Hash
48
+ def inspect_ordered(a=nil,b=nil)
49
+ "{"+keys.map{|x|x.to_s}.sort.map{|x|x.to_sym}.
50
+ map{|k| k.inspect + "=>"+self[k].inspect}.join(',')+"}"
51
+ end
52
+ end
53
+
54
+ module MaRuKu
55
+ class MDElement
56
+ def inspect(compact=true)
57
+ if compact
58
+ i2 = inspect2
59
+ return i2 if i2
60
+ end
61
+
62
+ "md_el(:%s,%s,%s,%s)" %
63
+ [
64
+ self.node_type,
65
+ children_inspect(compact),
66
+ @meta_priv.inspect_ordered,
67
+ self.al.inspect
68
+ ]
69
+ end
70
+
71
+ def children_inspect(compact=true)
72
+ s = @children.inspect_more(compact,', ')
73
+ if @children.empty?
74
+ "[]"
75
+ elsif s.size < 70
76
+ s
77
+ else
78
+ "[\n"+
79
+ add_tabs(@children.inspect_more(compact,",\n",false))+
80
+ "\n]"
81
+ end
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+
@@ -0,0 +1,61 @@
1
+ #--
2
+ # Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
3
+ #
4
+ # This file is part of Maruku.
5
+ #
6
+ # Maruku is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Maruku is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Maruku; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+ #++
20
+
21
+
22
+ module MaRuKu
23
+
24
+ class MDElement
25
+
26
+ # Yields to each element of specified node_type
27
+ # All elements if e_node_type is nil.
28
+ def each_element(e_node_type=nil, &block)
29
+ @children.each do |c|
30
+ if c.kind_of? MDElement
31
+ if (not e_node_type) || (e_node_type == c.node_type)
32
+ block.call c
33
+ end
34
+ c.each_element(e_node_type, &block)
35
+ end
36
+ end
37
+ end
38
+
39
+ # Apply passed block to each String in the hierarchy.
40
+ def replace_each_string(&block)
41
+ for c in @children
42
+ if c.kind_of? MDElement
43
+ c.replace_each_string(&block)
44
+ end
45
+ end
46
+
47
+ processed = []
48
+ until @children.empty?
49
+ c = @children.shift
50
+ if c.kind_of? String
51
+ result = block.call(c)
52
+ [*result].each do |e| processed << e end
53
+ else
54
+ processed << c
55
+ end
56
+ end
57
+ @children = processed
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1 @@
1
+ require 'maruku/input_textile2/t2_parser'
@@ -0,0 +1,199 @@
1
+ #--
2
+ # Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
3
+ #
4
+ # This file is part of Maruku.
5
+ #
6
+ # Maruku is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Maruku is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Maruku; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+ #++
20
+
21
+
22
+ module MaRuKu
23
+
24
+ class MDDocument
25
+ # an instance of Section (see below)
26
+ attr_accessor :toc
27
+ end
28
+
29
+ # This represents a section in the TOC.
30
+ class Section
31
+ # a Fixnum, is == header_element.level
32
+ attr_accessor :section_level
33
+
34
+ # An array of fixnum, like [1,2,5] for Section 1.2.5
35
+ attr_accessor :section_number
36
+
37
+ # reference to header (header has h.meta[:section] to self)
38
+ attr_accessor :header_element
39
+
40
+ # Array of immediate children of this element
41
+ attr_accessor :immediate_children
42
+
43
+ # Array of Section inside this section
44
+ attr_accessor :section_children
45
+
46
+ def initialize
47
+ @immediate_children = []
48
+ @section_children = []
49
+ end
50
+ end
51
+
52
+ class Section
53
+ def inspect(indent=1)
54
+ s = ""
55
+ if @header_element
56
+ s += "\_"*indent + "(#{@section_level})>\t #{@section_number.join('.')} : "
57
+ s += @header_element.children_to_s +
58
+ " (id: '#{@header_element.attributes[:id]}')\n"
59
+ else
60
+ s += "Master\n"
61
+ end
62
+
63
+ @section_children.each do |c|
64
+ s+=c.inspect(indent+1)
65
+ end
66
+ s
67
+ end
68
+
69
+ # Numerate this section and its children
70
+ def numerate(a=[])
71
+ self.section_number = a
72
+ section_children.each_with_index do |c,i|
73
+ c.numerate(a.clone.push(i+1))
74
+ end
75
+ if h = self.header_element
76
+ h.attributes[:section_number] = self.section_number
77
+ end
78
+ end
79
+
80
+ include REXML
81
+ # Creates an HTML toc.
82
+ # Call this on the root
83
+ def to_html
84
+ div = Element.new 'div'
85
+ div.attributes['class'] = 'maruku_toc'
86
+ div << create_toc
87
+ div
88
+ end
89
+
90
+ def create_toc
91
+ ul = Element.new 'ul'
92
+ # let's remove the bullets
93
+ ul.attributes['style'] = 'list-style: none;'
94
+ @section_children.each do |c|
95
+ li = Element.new 'li'
96
+ if span = c.header_element.render_section_number
97
+ li << span
98
+ end
99
+ a = c.header_element.wrap_as_element('a')
100
+ a.delete_attribute 'id'
101
+ a.attributes['href'] = "##{c.header_element.attributes[:id]}"
102
+ li << a
103
+ li << c.create_toc if c.section_children.size>0
104
+ ul << li
105
+ end
106
+ ul
107
+ end
108
+
109
+ # Creates a latex toc.
110
+ # Call this on the root
111
+ def to_latex
112
+ to_latex_rec + "\n\n"
113
+ end
114
+
115
+ def to_latex_rec
116
+ s = ""
117
+ @section_children.each do |c|
118
+ s += "\\noindent"
119
+ number = c.header_element.section_number
120
+ s += number if number
121
+ text = c.header_element.children_to_latex
122
+ id = c.header_element.attributes[:id]
123
+ s += "\\hyperlink{#{id}}{#{text}}"
124
+ s += "\\dotfill \\pageref*{#{id}} \\linebreak\n"
125
+ s += c.to_latex_rec if c.section_children.size>0
126
+
127
+ end
128
+ s
129
+ end
130
+
131
+ end
132
+
133
+ class MDDocument
134
+
135
+ def create_toc
136
+ each_element(:header) do |h|
137
+ h.attributes[:id] ||= h.generate_id
138
+ end
139
+
140
+ stack = []
141
+
142
+ # the ancestor section
143
+ s = Section.new
144
+ s.section_level = 0
145
+
146
+ stack.push s
147
+
148
+ i = 0;
149
+ while i < @children.size
150
+ while i < @children.size
151
+ if @children[i].node_type == :header
152
+ level = @children[i].level
153
+ break if level <= stack.last.section_level+1
154
+ end
155
+
156
+ stack.last.immediate_children.push @children[i]
157
+ i += 1
158
+ end
159
+
160
+ break if i>=@children.size
161
+
162
+ header = @children[i]
163
+ level = header.level
164
+
165
+ if level > stack.last.section_level
166
+ # this level is inside
167
+
168
+ s2 = Section.new
169
+ s2.section_level = level
170
+ s2.header_element = header
171
+ header.instance_variable_set :@section, s2
172
+
173
+ stack.last.section_children.push s2
174
+ stack.push s2
175
+
176
+ i+=1
177
+ elsif level == stack.last.section_level
178
+ # this level is a sibling
179
+ stack.pop
180
+ else
181
+ # this level is a parent
182
+ stack.pop
183
+ end
184
+
185
+ end
186
+
187
+ # If there is only one big header, then assume
188
+ # it is the master
189
+ if s.section_children.size == 1
190
+ s = s.section_children.first
191
+ end
192
+
193
+ # Assign section numbers
194
+ s.numerate
195
+
196
+ s
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,33 @@
1
+ require 'maruku'
2
+
3
+ text = <<EOF
4
+ Chapter 1
5
+ =========
6
+
7
+ It was a stormy and rainy night.
8
+
9
+ EOF
10
+
11
+ invalid = <<EOF
12
+
13
+ This is a [bad link.
14
+
15
+ EOF
16
+
17
+ Maruku.new(text).to_html
18
+
19
+ s = ""
20
+
21
+ begin
22
+ Maruku.new(invalid, {:on_error => :raise, :error_stream => s})
23
+ puts "Error! It should have thrown an exception."
24
+ rescue
25
+ # puts "ok, got error"
26
+ end
27
+
28
+ begin
29
+ Maruku.new(invalid, {:on_error => :warning, :error_stream => s})
30
+ rescue
31
+ puts "Error! It should not have thrown an exception."
32
+ end
33
+
@@ -0,0 +1,40 @@
1
+ #--
2
+ # Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
3
+ #
4
+ # This file is part of Maruku.
5
+ #
6
+ # Maruku is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Maruku is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Maruku; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+ #++
20
+
21
+ module MaRuKu
22
+ Version = '0.6.0'
23
+
24
+ MarukuURL = 'http://maruku.rubyforge.org/'
25
+
26
+ # If true, use also PHP Markdown extra syntax
27
+ #
28
+ # Note: it is not guaranteed that if it's false
29
+ # then no special features will be used.
30
+ #
31
+ # So please, ignore it for now.
32
+ def markdown_extra?
33
+ true
34
+ end
35
+
36
+ def new_meta_data?
37
+ true
38
+ end
39
+
40
+ end
@@ -0,0 +1,766 @@
1
+ ## lib/trollop.rb -- trollop command-line processing library
2
+ ## Author:: William Morgan (mailto: wmorgan-trollop@masanjin.net)
3
+ ## Copyright:: Copyright 2007 William Morgan
4
+ ## License:: the same terms as ruby itself
5
+
6
+ require 'date'
7
+
8
+ module Trollop
9
+
10
+ VERSION = "1.16.2"
11
+
12
+ ## Thrown by Parser in the event of a commandline error. Not needed if
13
+ ## you're using the Trollop::options entry.
14
+ class CommandlineError < StandardError; end
15
+
16
+ ## Thrown by Parser if the user passes in '-h' or '--help'. Handled
17
+ ## automatically by Trollop#options.
18
+ class HelpNeeded < StandardError; end
19
+
20
+ ## Thrown by Parser if the user passes in '-h' or '--version'. Handled
21
+ ## automatically by Trollop#options.
22
+ class VersionNeeded < StandardError; end
23
+
24
+ ## Regex for floating point numbers
25
+ FLOAT_RE = /^-?((\d+(\.\d+)?)|(\.\d+))([eE][-+]?[\d]+)?$/
26
+
27
+ ## Regex for parameters
28
+ PARAM_RE = /^-(-|\.$|[^\d\.])/
29
+
30
+ ## The commandline parser. In typical usage, the methods in this class
31
+ ## will be handled internally by Trollop::options. In this case, only the
32
+ ## #opt, #banner and #version, #depends, and #conflicts methods will
33
+ ## typically be called.
34
+ ##
35
+ ## If you want to instantiate this class yourself (for more complicated
36
+ ## argument-parsing logic), call #parse to actually produce the output hash,
37
+ ## and consider calling it from within
38
+ ## Trollop::with_standard_exception_handling.
39
+ class Parser
40
+
41
+ ## The set of values that indicate a flag option when passed as the
42
+ ## +:type+ parameter of #opt.
43
+ FLAG_TYPES = [:flag, :bool, :boolean]
44
+
45
+ ## The set of values that indicate a single-parameter (normal) option when
46
+ ## passed as the +:type+ parameter of #opt.
47
+ ##
48
+ ## A value of +io+ corresponds to a readable IO resource, including
49
+ ## a filename, URI, or the strings 'stdin' or '-'.
50
+ SINGLE_ARG_TYPES = [:int, :integer, :string, :double, :float, :io, :date]
51
+
52
+ ## The set of values that indicate a multiple-parameter option (i.e., that
53
+ ## takes multiple space-separated values on the commandline) when passed as
54
+ ## the +:type+ parameter of #opt.
55
+ MULTI_ARG_TYPES = [:ints, :integers, :strings, :doubles, :floats, :ios, :dates]
56
+
57
+ ## The complete set of legal values for the +:type+ parameter of #opt.
58
+ TYPES = FLAG_TYPES + SINGLE_ARG_TYPES + MULTI_ARG_TYPES
59
+
60
+ INVALID_SHORT_ARG_REGEX = /[\d-]/ #:nodoc:
61
+
62
+ ## The values from the commandline that were not interpreted by #parse.
63
+ attr_reader :leftovers
64
+
65
+ ## The complete configuration hashes for each option. (Mainly useful
66
+ ## for testing.)
67
+ attr_reader :specs
68
+
69
+ ## Initializes the parser, and instance-evaluates any block given.
70
+ def initialize *a, &b
71
+ @version = nil
72
+ @leftovers = []
73
+ @specs = {}
74
+ @long = {}
75
+ @short = {}
76
+ @order = []
77
+ @constraints = []
78
+ @stop_words = []
79
+ @stop_on_unknown = false
80
+
81
+ #instance_eval(&b) if b # can't take arguments
82
+ cloaker(&b).bind(self).call(*a) if b
83
+ end
84
+
85
+ ## Define an option. +name+ is the option name, a unique identifier
86
+ ## for the option that you will use internally, which should be a
87
+ ## symbol or a string. +desc+ is a string description which will be
88
+ ## displayed in help messages.
89
+ ##
90
+ ## Takes the following optional arguments:
91
+ ##
92
+ ## [+:long+] Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the +name+ option into a string, and replacing any _'s by -'s.
93
+ ## [+:short+] Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from +name+.
94
+ ## [+:type+] Require that the argument take a parameter or parameters of type +type+. For a single parameter, the value can be a member of +SINGLE_ARG_TYPES+, or a corresponding Ruby class (e.g. +Integer+ for +:int+). For multiple-argument parameters, the value can be any member of +MULTI_ARG_TYPES+ constant. If unset, the default argument type is +:flag+, meaning that the argument does not take a parameter. The specification of +:type+ is not necessary if a +:default+ is given.
95
+ ## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Trollop::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the the commandline the value will be +false+.
96
+ ## [+:required+] If set to +true+, the argument must be provided on the commandline.
97
+ ## [+:multi+] If set to +true+, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.)
98
+ ##
99
+ ## Note that there are two types of argument multiplicity: an argument
100
+ ## can take multiple values, e.g. "--arg 1 2 3". An argument can also
101
+ ## be allowed to occur multiple times, e.g. "--arg 1 --arg 2".
102
+ ##
103
+ ## Arguments that take multiple values should have a +:type+ parameter
104
+ ## drawn from +MULTI_ARG_TYPES+ (e.g. +:strings+), or a +:default:+
105
+ ## value of an array of the correct type (e.g. [String]). The
106
+ ## value of this argument will be an array of the parameters on the
107
+ ## commandline.
108
+ ##
109
+ ## Arguments that can occur multiple times should be marked with
110
+ ## +:multi+ => +true+. The value of this argument will also be an array.
111
+ ## In contrast with regular non-multi options, if not specified on
112
+ ## the commandline, the default value will be [], not nil.
113
+ ##
114
+ ## These two attributes can be combined (e.g. +:type+ => +:strings+,
115
+ ## +:multi+ => +true+), in which case the value of the argument will be
116
+ ## an array of arrays.
117
+ ##
118
+ ## There's one ambiguous case to be aware of: when +:multi+: is true and a
119
+ ## +:default+ is set to an array (of something), it's ambiguous whether this
120
+ ## is a multi-value argument as well as a multi-occurrence argument.
121
+ ## In thise case, Trollop assumes that it's not a multi-value argument.
122
+ ## If you want a multi-value, multi-occurrence argument with a default
123
+ ## value, you must specify +:type+ as well.
124
+
125
+ def opt name, desc="", opts={}
126
+ raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? name
127
+
128
+ ## fill in :type
129
+ opts[:type] = # normalize
130
+ case opts[:type]
131
+ when :boolean, :bool; :flag
132
+ when :integer; :int
133
+ when :integers; :ints
134
+ when :double; :float
135
+ when :doubles; :floats
136
+ when Class
137
+ case opts[:type].name
138
+ when 'TrueClass', 'FalseClass'; :flag
139
+ when 'String'; :string
140
+ when 'Integer'; :int
141
+ when 'Float'; :float
142
+ when 'IO'; :io
143
+ when 'Date'; :date
144
+ else
145
+ raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
146
+ end
147
+ when nil; nil
148
+ else
149
+ raise ArgumentError, "unsupported argument type '#{opts[:type]}'" unless TYPES.include?(opts[:type])
150
+ opts[:type]
151
+ end
152
+
153
+ ## for options with :multi => true, an array default doesn't imply
154
+ ## a multi-valued argument. for that you have to specify a :type
155
+ ## as well. (this is how we disambiguate an ambiguous situation;
156
+ ## see the docs for Parser#opt for details.)
157
+ disambiguated_default =
158
+ if opts[:multi] && opts[:default].is_a?(Array) && !opts[:type]
159
+ opts[:default].first
160
+ else
161
+ opts[:default]
162
+ end
163
+
164
+ type_from_default =
165
+ case disambiguated_default
166
+ when Integer; :int
167
+ when Numeric; :float
168
+ when TrueClass, FalseClass; :flag
169
+ when String; :string
170
+ when IO; :io
171
+ when Date; :date
172
+ when Array
173
+ if opts[:default].empty?
174
+ raise ArgumentError, "multiple argument type cannot be deduced from an empty array for '#{opts[:default][0].class.name}'"
175
+ end
176
+ case opts[:default][0] # the first element determines the types
177
+ when Integer; :ints
178
+ when Numeric; :floats
179
+ when String; :strings
180
+ when IO; :ios
181
+ when Date; :dates
182
+ else
183
+ raise ArgumentError, "unsupported multiple argument type '#{opts[:default][0].class.name}'"
184
+ end
185
+ when nil; nil
186
+ else
187
+ raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
188
+ end
189
+
190
+ raise ArgumentError, ":type specification and default type don't match (default type is #{type_from_default})" if opts[:type] && type_from_default && opts[:type] != type_from_default
191
+
192
+ opts[:type] = opts[:type] || type_from_default || :flag
193
+
194
+ ## fill in :long
195
+ opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.gsub("_", "-")
196
+ opts[:long] =
197
+ case opts[:long]
198
+ when /^--([^-].*)$/
199
+ $1
200
+ when /^[^-]/
201
+ opts[:long]
202
+ else
203
+ raise ArgumentError, "invalid long option name #{opts[:long].inspect}"
204
+ end
205
+ raise ArgumentError, "long option name #{opts[:long].inspect} is already taken; please specify a (different) :long" if @long[opts[:long]]
206
+
207
+ ## fill in :short
208
+ opts[:short] = opts[:short].to_s if opts[:short] unless opts[:short] == :none
209
+ opts[:short] = case opts[:short]
210
+ when /^-(.)$/; $1
211
+ when nil, :none, /^.$/; opts[:short]
212
+ else raise ArgumentError, "invalid short option name '#{opts[:short].inspect}'"
213
+ end
214
+
215
+ if opts[:short]
216
+ raise ArgumentError, "short option name #{opts[:short].inspect} is already taken; please specify a (different) :short" if @short[opts[:short]]
217
+ raise ArgumentError, "a short option name can't be a number or a dash" if opts[:short] =~ INVALID_SHORT_ARG_REGEX
218
+ end
219
+
220
+ ## fill in :default for flags
221
+ opts[:default] = false if opts[:type] == :flag && opts[:default].nil?
222
+
223
+ ## autobox :default for :multi (multi-occurrence) arguments
224
+ opts[:default] = [opts[:default]] if opts[:default] && opts[:multi] && !opts[:default].is_a?(Array)
225
+
226
+ ## fill in :multi
227
+ opts[:multi] ||= false
228
+
229
+ opts[:desc] ||= desc
230
+ @long[opts[:long]] = name
231
+ @short[opts[:short]] = name if opts[:short] && opts[:short] != :none
232
+ @specs[name] = opts
233
+ @order << [:opt, name]
234
+ end
235
+
236
+ ## Sets the version string. If set, the user can request the version
237
+ ## on the commandline. Should probably be of the form "<program name>
238
+ ## <version number>".
239
+ def version s=nil; @version = s if s; @version end
240
+
241
+ ## Adds text to the help display. Can be interspersed with calls to
242
+ ## #opt to build a multi-section help page.
243
+ def banner s; @order << [:text, s] end
244
+ alias :text :banner
245
+
246
+ ## Marks two (or more!) options as requiring each other. Only handles
247
+ ## undirected (i.e., mutual) dependencies. Directed dependencies are
248
+ ## better modeled with Trollop::die.
249
+ def depends *syms
250
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
251
+ @constraints << [:depends, syms]
252
+ end
253
+
254
+ ## Marks two (or more!) options as conflicting.
255
+ def conflicts *syms
256
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
257
+ @constraints << [:conflicts, syms]
258
+ end
259
+
260
+ ## Defines a set of words which cause parsing to terminate when
261
+ ## encountered, such that any options to the left of the word are
262
+ ## parsed as usual, and options to the right of the word are left
263
+ ## intact.
264
+ ##
265
+ ## A typical use case would be for subcommand support, where these
266
+ ## would be set to the list of subcommands. A subsequent Trollop
267
+ ## invocation would then be used to parse subcommand options, after
268
+ ## shifting the subcommand off of ARGV.
269
+ def stop_on *words
270
+ @stop_words = [*words].flatten
271
+ end
272
+
273
+ ## Similar to #stop_on, but stops on any unknown word when encountered
274
+ ## (unless it is a parameter for an argument). This is useful for
275
+ ## cases where you don't know the set of subcommands ahead of time,
276
+ ## i.e., without first parsing the global options.
277
+ def stop_on_unknown
278
+ @stop_on_unknown = true
279
+ end
280
+
281
+ ## Parses the commandline. Typically called by Trollop::options,
282
+ ## but you can call it directly if you need more control.
283
+ ##
284
+ ## throws CommandlineError, HelpNeeded, and VersionNeeded exceptions.
285
+ def parse cmdline=ARGV
286
+ vals = {}
287
+ required = {}
288
+
289
+ opt :version, "Print version and exit" if @version unless @specs[:version] || @long["version"]
290
+ opt :help, "Show this message" unless @specs[:help] || @long["help"]
291
+
292
+ @specs.each do |sym, opts|
293
+ required[sym] = true if opts[:required]
294
+ vals[sym] = opts[:default]
295
+ vals[sym] = [] if opts[:multi] && !opts[:default] # multi arguments default to [], not nil
296
+ end
297
+
298
+ resolve_default_short_options
299
+
300
+ ## resolve symbols
301
+ given_args = {}
302
+ @leftovers = each_arg cmdline do |arg, params|
303
+ sym = case arg
304
+ when /^-([^-])$/
305
+ @short[$1]
306
+ when /^--([^-]\S*)$/
307
+ @long[$1]
308
+ else
309
+ raise CommandlineError, "invalid argument syntax: '#{arg}'"
310
+ end
311
+ raise CommandlineError, "unknown argument '#{arg}'" unless sym
312
+
313
+ if given_args.include?(sym) && !@specs[sym][:multi]
314
+ raise CommandlineError, "option '#{arg}' specified multiple times"
315
+ end
316
+
317
+ given_args[sym] ||= {}
318
+
319
+ given_args[sym][:arg] = arg
320
+ given_args[sym][:params] ||= []
321
+
322
+ # The block returns the number of parameters taken.
323
+ num_params_taken = 0
324
+
325
+ unless params.nil?
326
+ if SINGLE_ARG_TYPES.include?(@specs[sym][:type])
327
+ given_args[sym][:params] << params[0, 1] # take the first parameter
328
+ num_params_taken = 1
329
+ elsif MULTI_ARG_TYPES.include?(@specs[sym][:type])
330
+ given_args[sym][:params] << params # take all the parameters
331
+ num_params_taken = params.size
332
+ end
333
+ end
334
+
335
+ num_params_taken
336
+ end
337
+
338
+ ## check for version and help args
339
+ raise VersionNeeded if given_args.include? :version
340
+ raise HelpNeeded if given_args.include? :help
341
+
342
+ ## check constraint satisfaction
343
+ @constraints.each do |type, syms|
344
+ constraint_sym = syms.find { |sym| given_args[sym] }
345
+ next unless constraint_sym
346
+
347
+ case type
348
+ when :depends
349
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} requires --#{@specs[sym][:long]}" unless given_args.include? sym }
350
+ when :conflicts
351
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} conflicts with --#{@specs[sym][:long]}" if given_args.include?(sym) && (sym != constraint_sym) }
352
+ end
353
+ end
354
+
355
+ required.each do |sym, val|
356
+ raise CommandlineError, "option --#{@specs[sym][:long]} must be specified" unless given_args.include? sym
357
+ end
358
+
359
+ ## parse parameters
360
+ given_args.each do |sym, given_data|
361
+ arg = given_data[:arg]
362
+ params = given_data[:params]
363
+
364
+ opts = @specs[sym]
365
+ raise CommandlineError, "option '#{arg}' needs a parameter" if params.empty? && opts[:type] != :flag
366
+
367
+ vals["#{sym}_given".intern] = true # mark argument as specified on the commandline
368
+
369
+ case opts[:type]
370
+ when :flag
371
+ vals[sym] = !opts[:default]
372
+ when :int, :ints
373
+ vals[sym] = params.map { |pg| pg.map { |p| parse_integer_parameter p, arg } }
374
+ when :float, :floats
375
+ vals[sym] = params.map { |pg| pg.map { |p| parse_float_parameter p, arg } }
376
+ when :string, :strings
377
+ vals[sym] = params.map { |pg| pg.map { |p| p.to_s } }
378
+ when :io, :ios
379
+ vals[sym] = params.map { |pg| pg.map { |p| parse_io_parameter p, arg } }
380
+ when :date, :dates
381
+ vals[sym] = params.map { |pg| pg.map { |p| parse_date_parameter p, arg } }
382
+ end
383
+
384
+ if SINGLE_ARG_TYPES.include?(opts[:type])
385
+ unless opts[:multi] # single parameter
386
+ vals[sym] = vals[sym][0][0]
387
+ else # multiple options, each with a single parameter
388
+ vals[sym] = vals[sym].map { |p| p[0] }
389
+ end
390
+ elsif MULTI_ARG_TYPES.include?(opts[:type]) && !opts[:multi]
391
+ vals[sym] = vals[sym][0] # single option, with multiple parameters
392
+ end
393
+ # else: multiple options, with multiple parameters
394
+ end
395
+
396
+ ## modify input in place with only those
397
+ ## arguments we didn't process
398
+ cmdline.clear
399
+ @leftovers.each { |l| cmdline << l }
400
+
401
+ ## allow openstruct-style accessors
402
+ class << vals
403
+ def method_missing(m, *args)
404
+ self[m] || self[m.to_s]
405
+ end
406
+ end
407
+ vals
408
+ end
409
+
410
+ def parse_date_parameter param, arg #:nodoc:
411
+ begin
412
+ begin
413
+ time = Chronic.parse(param)
414
+ rescue NameError
415
+ # chronic is not available
416
+ end
417
+ time ? Date.new(time.year, time.month, time.day) : Date.parse(param)
418
+ rescue ArgumentError => e
419
+ raise CommandlineError, "option '#{arg}' needs a date"
420
+ end
421
+ end
422
+
423
+ ## Print the help message to +stream+.
424
+ def educate stream=$stdout
425
+ width # just calculate it now; otherwise we have to be careful not to
426
+ # call this unless the cursor's at the beginning of a line.
427
+
428
+ left = {}
429
+ @specs.each do |name, spec|
430
+ left[name] = "--#{spec[:long]}" +
431
+ (spec[:short] && spec[:short] != :none ? ", -#{spec[:short]}" : "") +
432
+ case spec[:type]
433
+ when :flag; ""
434
+ when :int; " <i>"
435
+ when :ints; " <i+>"
436
+ when :string; " <s>"
437
+ when :strings; " <s+>"
438
+ when :float; " <f>"
439
+ when :floats; " <f+>"
440
+ when :io; " <filename/uri>"
441
+ when :ios; " <filename/uri+>"
442
+ when :date; " <date>"
443
+ when :dates; " <date+>"
444
+ end
445
+ end
446
+
447
+ leftcol_width = left.values.map { |s| s.length }.max || 0
448
+ rightcol_start = leftcol_width + 6 # spaces
449
+
450
+ unless @order.size > 0 && @order.first.first == :text
451
+ stream.puts "#@version\n" if @version
452
+ stream.puts "Options:"
453
+ end
454
+
455
+ @order.each do |what, opt|
456
+ if what == :text
457
+ stream.puts wrap(opt)
458
+ next
459
+ end
460
+
461
+ spec = @specs[opt]
462
+ stream.printf " %#{leftcol_width}s: ", left[opt]
463
+ desc = spec[:desc] + begin
464
+ default_s = case spec[:default]
465
+ when $stdout; "<stdout>"
466
+ when $stdin; "<stdin>"
467
+ when $stderr; "<stderr>"
468
+ when Array
469
+ spec[:default].join(", ")
470
+ else
471
+ spec[:default].to_s
472
+ end
473
+
474
+ if spec[:default]
475
+ if spec[:desc] =~ /\.$/
476
+ " (Default: #{default_s})"
477
+ else
478
+ " (default: #{default_s})"
479
+ end
480
+ else
481
+ ""
482
+ end
483
+ end
484
+ stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
485
+ end
486
+ end
487
+
488
+ def width #:nodoc:
489
+ @width ||= if $stdout.tty?
490
+ begin
491
+ require 'curses'
492
+ Curses::init_screen
493
+ x = Curses::cols
494
+ Curses::close_screen
495
+ x
496
+ rescue Exception
497
+ 80
498
+ end
499
+ else
500
+ 80
501
+ end
502
+ end
503
+
504
+ def wrap str, opts={} # :nodoc:
505
+ if str == ""
506
+ [""]
507
+ else
508
+ str.split("\n").map { |s| wrap_line s, opts }.flatten
509
+ end
510
+ end
511
+
512
+ ## The per-parser version of Trollop::die (see that for documentation).
513
+ def die arg, msg
514
+ if msg
515
+ $stderr.puts "Error: argument --#{@specs[arg][:long]} #{msg}."
516
+ else
517
+ $stderr.puts "Error: #{arg}."
518
+ end
519
+ $stderr.puts "Try --help for help."
520
+ exit(-1)
521
+ end
522
+
523
+ private
524
+
525
+ ## yield successive arg, parameter pairs
526
+ def each_arg args
527
+ remains = []
528
+ i = 0
529
+
530
+ until i >= args.length
531
+ if @stop_words.member? args[i]
532
+ remains += args[i .. -1]
533
+ return remains
534
+ end
535
+ case args[i]
536
+ when /^--$/ # arg terminator
537
+ remains += args[(i + 1) .. -1]
538
+ return remains
539
+ when /^--(\S+?)=(.*)$/ # long argument with equals
540
+ yield "--#{$1}", [$2]
541
+ i += 1
542
+ when /^--(\S+)$/ # long argument
543
+ params = collect_argument_parameters(args, i + 1)
544
+ unless params.empty?
545
+ num_params_taken = yield args[i], params
546
+ i += 1 + num_params_taken
547
+ else # long argument no parameter
548
+ yield args[i], nil
549
+ i += 1
550
+ end
551
+ when /^-(\S+)$/ # one or more short arguments
552
+ shortargs = $1.split(//)
553
+ shortargs.each_with_index do |a, j|
554
+ if j == (shortargs.length - 1)
555
+ params = collect_argument_parameters(args, i + 1)
556
+ unless params.empty?
557
+ num_params_taken = yield "-#{a}", params
558
+ i += 1 + num_params_taken
559
+ else # argument no parameter
560
+ yield "-#{a}", nil
561
+ i += 1
562
+ end
563
+ else
564
+ yield "-#{a}", nil
565
+ end
566
+ end
567
+ else
568
+ if @stop_on_unknown
569
+ remains += args[i .. -1]
570
+ return remains
571
+ else
572
+ remains << args[i]
573
+ i += 1
574
+ end
575
+ end
576
+ end
577
+
578
+ remains
579
+ end
580
+
581
+ def parse_integer_parameter param, arg
582
+ raise CommandlineError, "option '#{arg}' needs an integer" unless param =~ /^\d+$/
583
+ param.to_i
584
+ end
585
+
586
+ def parse_float_parameter param, arg
587
+ raise CommandlineError, "option '#{arg}' needs a floating-point number" unless param =~ FLOAT_RE
588
+ param.to_f
589
+ end
590
+
591
+ def parse_io_parameter param, arg
592
+ case param
593
+ when /^(stdin|-)$/i; $stdin
594
+ else
595
+ require 'open-uri'
596
+ begin
597
+ open param
598
+ rescue SystemCallError => e
599
+ raise CommandlineError, "file or url for option '#{arg}' cannot be opened: #{e.message}"
600
+ end
601
+ end
602
+ end
603
+
604
+ def collect_argument_parameters args, start_at
605
+ params = []
606
+ pos = start_at
607
+ while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos]) do
608
+ params << args[pos]
609
+ pos += 1
610
+ end
611
+ params
612
+ end
613
+
614
+ def resolve_default_short_options
615
+ @order.each do |type, name|
616
+ next unless type == :opt
617
+ opts = @specs[name]
618
+ next if opts[:short]
619
+
620
+ c = opts[:long].split(//).find { |d| d !~ INVALID_SHORT_ARG_REGEX && !@short.member?(d) }
621
+ if c # found a character to use
622
+ opts[:short] = c
623
+ @short[c] = name
624
+ end
625
+ end
626
+ end
627
+
628
+ def wrap_line str, opts={}
629
+ prefix = opts[:prefix] || 0
630
+ width = opts[:width] || (self.width - 1)
631
+ start = 0
632
+ ret = []
633
+ until start > str.length
634
+ nextt =
635
+ if start + width >= str.length
636
+ str.length
637
+ else
638
+ x = str.rindex(/\s/, start + width)
639
+ x = str.index(/\s/, start) if x && x < start
640
+ x || str.length
641
+ end
642
+ ret << (ret.empty? ? "" : " " * prefix) + str[start ... nextt]
643
+ start = nextt + 1
644
+ end
645
+ ret
646
+ end
647
+
648
+ ## instance_eval but with ability to handle block arguments
649
+ ## thanks to why: http://redhanded.hobix.com/inspect/aBlockCostume.html
650
+ def cloaker &b
651
+ (class << self; self; end).class_eval do
652
+ define_method :cloaker_, &b
653
+ meth = instance_method :cloaker_
654
+ remove_method :cloaker_
655
+ meth
656
+ end
657
+ end
658
+ end
659
+
660
+ ## The easy, syntactic-sugary entry method into Trollop. Creates a Parser,
661
+ ## passes the block to it, then parses +args+ with it, handling any errors or
662
+ ## requests for help or version information appropriately (and then exiting).
663
+ ## Modifies +args+ in place. Returns a hash of option values.
664
+ ##
665
+ ## The block passed in should contain zero or more calls to +opt+
666
+ ## (Parser#opt), zero or more calls to +text+ (Parser#text), and
667
+ ## probably a call to +version+ (Parser#version).
668
+ ##
669
+ ## The returned block contains a value for every option specified with
670
+ ## +opt+. The value will be the value given on the commandline, or the
671
+ ## default value if the option was not specified on the commandline. For
672
+ ## every option specified on the commandline, a key "<option
673
+ ## name>_given" will also be set in the hash.
674
+ ##
675
+ ## Example:
676
+ ##
677
+ ## require 'trollop'
678
+ ## opts = Trollop::options do
679
+ ## opt :monkey, "Use monkey mode" # a flag --monkey, defaulting to false
680
+ ## opt :goat, "Use goat mode", :default => true # a flag --goat, defaulting to true
681
+ ## opt :num_limbs, "Number of limbs", :default => 4 # an integer --num-limbs <i>, defaulting to 4
682
+ ## opt :num_thumbs, "Number of thumbs", :type => :int # an integer --num-thumbs <i>, defaulting to nil
683
+ ## end
684
+ ##
685
+ ## ## if called with no arguments
686
+ ## p opts # => { :monkey => false, :goat => true, :num_limbs => 4, :num_thumbs => nil }
687
+ ##
688
+ ## ## if called with --monkey
689
+ ## p opts # => {:monkey_given=>true, :monkey=>true, :goat=>true, :num_limbs=>4, :help=>false, :num_thumbs=>nil}
690
+ ##
691
+ ## See more examples at http://trollop.rubyforge.org.
692
+ def options args=ARGV, *a, &b
693
+ @last_parser = Parser.new(*a, &b)
694
+ with_standard_exception_handling(@last_parser) { [@last_parser, @last_parser.parse(args)] }
695
+ end
696
+
697
+ ## If Trollop::options doesn't do quite what you want, you can create a Parser
698
+ ## object and call Parser#parse on it. That method will throw CommandlineError,
699
+ ## HelpNeeded and VersionNeeded exceptions when necessary; if you want to
700
+ ## have these handled for you in the standard manner (e.g. show the help
701
+ ## and then exit upon an HelpNeeded exception), call your code from within
702
+ ## a block passed to this method.
703
+ ##
704
+ ## Note that this method will call System#exit after handling an exception!
705
+ ##
706
+ ## Usage example:
707
+ ##
708
+ ## require 'trollop'
709
+ ## p = Trollop::Parser.new do
710
+ ## opt :monkey, "Use monkey mode" # a flag --monkey, defaulting to false
711
+ ## opt :goat, "Use goat mode", :default => true # a flag --goat, defaulting to true
712
+ ## end
713
+ ##
714
+ ## opts = Trollop::with_standard_exception_handling p do
715
+ ## o = p.parse ARGV
716
+ ## raise Trollop::HelpNeeded if ARGV.empty? # show help screen
717
+ ## o
718
+ ## end
719
+ ##
720
+ ## Requires passing in the parser object.
721
+
722
+ def with_standard_exception_handling parser
723
+ begin
724
+ yield
725
+ rescue CommandlineError => e
726
+ $stderr.puts "Error: #{e.message}."
727
+ $stderr.puts "Try --help for help."
728
+ exit(-1)
729
+ rescue HelpNeeded
730
+ parser.educate
731
+ exit
732
+ rescue VersionNeeded
733
+ puts parser.version
734
+ exit
735
+ end
736
+ end
737
+
738
+ ## Informs the user that their usage of 'arg' was wrong, as detailed by
739
+ ## 'msg', and dies. Example:
740
+ ##
741
+ ## options do
742
+ ## opt :volume, :default => 0.0
743
+ ## end
744
+ ##
745
+ ## die :volume, "too loud" if opts[:volume] > 10.0
746
+ ## die :volume, "too soft" if opts[:volume] < 0.1
747
+ ##
748
+ ## In the one-argument case, simply print that message, a notice
749
+ ## about -h, and die. Example:
750
+ ##
751
+ ## options do
752
+ ## opt :whatever # ...
753
+ ## end
754
+ ##
755
+ ## Trollop::die "need at least one filename" if ARGV.empty?
756
+ def die arg, msg=nil
757
+ if @last_parser
758
+ @last_parser.die arg, msg
759
+ else
760
+ raise ArgumentError, "Trollop::die can only be called after Trollop::options"
761
+ end
762
+ end
763
+
764
+ module_function :options, :die, :with_standard_exception_handling
765
+
766
+ end # module