opt_parse_builder 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rspec +1 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +5 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +35 -0
  8. data/LICENSE +23 -0
  9. data/README.md +434 -0
  10. data/Rakefile +13 -0
  11. data/examples/hello_world.rb +20 -0
  12. data/lib/opt_parse_builder.rb +156 -0
  13. data/lib/opt_parse_builder/argument.rb +62 -0
  14. data/lib/opt_parse_builder/argument_builder.rb +193 -0
  15. data/lib/opt_parse_builder/argument_bundle.rb +30 -0
  16. data/lib/opt_parse_builder/argument_bundle_builder.rb +34 -0
  17. data/lib/opt_parse_builder/argument_values.rb +60 -0
  18. data/lib/opt_parse_builder/banner_argument.rb +11 -0
  19. data/lib/opt_parse_builder/constant_argument.rb +16 -0
  20. data/lib/opt_parse_builder/errors.rb +10 -0
  21. data/lib/opt_parse_builder/formats_operand_name.rb +9 -0
  22. data/lib/opt_parse_builder/has_value.rb +21 -0
  23. data/lib/opt_parse_builder/null_argument.rb +4 -0
  24. data/lib/opt_parse_builder/option_argument.rb +33 -0
  25. data/lib/opt_parse_builder/optional_operand_argument.rb +29 -0
  26. data/lib/opt_parse_builder/parser.rb +345 -0
  27. data/lib/opt_parse_builder/parser_builder.rb +17 -0
  28. data/lib/opt_parse_builder/required_operand_argument.rb +32 -0
  29. data/lib/opt_parse_builder/separator_argument.rb +11 -0
  30. data/lib/opt_parse_builder/splat_operand_argument.rb +22 -0
  31. data/lib/opt_parse_builder/stable_sort.rb +13 -0
  32. data/lib/opt_parse_builder/version.rb +6 -0
  33. data/opt_parse_builder.gemspec +35 -0
  34. data/rake/bundler.rake +1 -0
  35. data/rake/default.rake +1 -0
  36. data/rake/rdoc.rake +7 -0
  37. data/rake/spec.rake +3 -0
  38. data/rake/test.rake +2 -0
  39. metadata +126 -0
@@ -0,0 +1,13 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+
12
+ $:.unshift(File.dirname(__FILE__) + "/lib")
13
+ Dir["rake/**/*.rake"].sort.each { |path| load path }
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "opt_parse_builder"
4
+
5
+ ARG_PARSER = OptParseBuilder.build_parser do |args|
6
+ args.banner "A simple example"
7
+ args.add do |arg|
8
+ arg.key :path
9
+ arg.required_operand
10
+ end
11
+ args.add do |arg|
12
+ arg.key :verbose
13
+ arg.on "-v", "--verbose", "Be verbose"
14
+ end
15
+ args.separator "Some explanatory text at the bottom"
16
+ end
17
+
18
+ arg_values = ARG_PARSER.parse!
19
+ p arg_values.verbose
20
+ p arg_values.path
@@ -0,0 +1,156 @@
1
+ require "optparse"
2
+
3
+ require_relative "opt_parse_builder/argument"
4
+ require_relative "opt_parse_builder/argument_builder"
5
+ require_relative "opt_parse_builder/argument_bundle"
6
+ require_relative "opt_parse_builder/argument_bundle_builder"
7
+ require_relative "opt_parse_builder/argument_values"
8
+ require_relative "opt_parse_builder/banner_argument"
9
+ require_relative "opt_parse_builder/constant_argument"
10
+ require_relative "opt_parse_builder/errors"
11
+ require_relative "opt_parse_builder/formats_operand_name"
12
+ require_relative "opt_parse_builder/has_value"
13
+ require_relative "opt_parse_builder/null_argument"
14
+ require_relative "opt_parse_builder/option_argument"
15
+ require_relative "opt_parse_builder/optional_operand_argument"
16
+ require_relative "opt_parse_builder/parser"
17
+ require_relative "opt_parse_builder/parser_builder"
18
+ require_relative "opt_parse_builder/required_operand_argument"
19
+ require_relative "opt_parse_builder/separator_argument"
20
+ require_relative "opt_parse_builder/splat_operand_argument"
21
+ require_relative "opt_parse_builder/stable_sort"
22
+
23
+ # The namespace of this library, and the sole entry point. You never
24
+ # have to (and never should) explicitly refer to any other class or
25
+ # module of this library than this one. There are a few other classes
26
+ # you will use, but they will be created for you by methods of this
27
+ # module.
28
+ #
29
+ # Minimal example:
30
+ #
31
+ # arg_parser = OptParseBuilder.build_parser
32
+ # arg_parser.parse!
33
+ #
34
+ # An example with a little bit of everything
35
+ #
36
+ # arg_parser = OptParseBuilder.build_parser do |p|
37
+ # parser.banner "A short description of the program"
38
+ # parser.add do |arg|
39
+ # arg.key :output_path
40
+ # arg.required_operand
41
+ # end
42
+ # parser.add do |arg|
43
+ # arg.key :input_paths
44
+ # arg.splat_operand
45
+ # end
46
+ # parser.add do |arg|
47
+ # arg.key :quiet
48
+ # arg.on "-q", "--quiet", "Be quiet"
49
+ # end
50
+ # parser.add do |arg|
51
+ # arg.key :size
52
+ # arg.default 1024
53
+ # arg.on "--size=N", Integer
54
+ # arg.on "Size in bytes (default _DEFAULT_)"
55
+ # end
56
+ # parser.separator "Explanatory text at the bottom"
57
+ # end
58
+ # arg_values = arg_parser.parse!
59
+ # p arg_values.quiet # nil or true
60
+ # p arg_values.size # An Integer
61
+ # p arg_values.output_path # A string
62
+ # p arg_values.input_paths # An array of strings
63
+ module OptParseBuilder
64
+
65
+ # Create a new parser. If called without a block, returns a parser
66
+ # than you can then add arguments to:
67
+ #
68
+ # arg_parser = OptParseBuilder.build_parser
69
+ # arg_parser.add do |arg|
70
+ # arg.key :force
71
+ # arg.on "--force", "Force dangerous operation"
72
+ # end
73
+ #
74
+ # If called with a block, yields itself to the block:
75
+ #
76
+ # arg_parser = OptParseBuilder.build_parser do |args|
77
+ # args.add do |arg|
78
+ # arg.key :force
79
+ # arg.on "--force", "Force dangerous operation"
80
+ # end
81
+ # end
82
+ #
83
+ # Note that the parser constructed using the block form can still
84
+ # be added onto:
85
+ #
86
+ # arg_parser.add do |arg|
87
+ # arg.key :size
88
+ # arg.on "--size=N", Integer, "File size in bytes"
89
+ # end
90
+ #
91
+ def self.build_parser
92
+ parser_builder = ParserBuilder.new
93
+ yield parser_builder if block_given?
94
+ parser_builder.parser
95
+ end
96
+
97
+ # Build an argument that can be added to a parser. Yields an
98
+ # ArgumentBuilder. Returns the argument created by the builder.
99
+ #
100
+ # VERBOSE = OptParseBuilder.build_argument do |arg|
101
+ # arg.key :verbose
102
+ # arg.on "-v", "--verbose", "Print extra output"
103
+ # end
104
+ #
105
+ # arg_parser = OptParseBuilder.build_parser do |args|
106
+ # args.add VERBOSE
107
+ # end
108
+ #
109
+ # See the README for argument examples.
110
+ #
111
+ # Raises BuildError if the argument cannot be built or added.
112
+ #
113
+ # This is most useful when you are building a related suite of
114
+ # programs that share some command-line arguments in common. Most
115
+ # of the time you will just add the argument using the block form of
116
+ # OptParseBuilder#add.
117
+ def self.build_argument
118
+ builder = ArgumentBuilder.new
119
+ yield builder
120
+ builder.argument
121
+ end
122
+
123
+ # Build a bundle of arguments that can be added to a parser
124
+ # together. Yields an ArgumentBundleBuilder.
125
+ #
126
+ # This is useful when you have a group of arguments that go
127
+ # together:
128
+ #
129
+ # bundle = OptParseBuilder.build_bundle do |args|
130
+ # args.add do |arg|
131
+ # arg.key :x
132
+ # op.on "-x", Integer, "X coordinate"
133
+ # end
134
+ # args.add do |arg|
135
+ # arg.key :y
136
+ # op.on "-y", Integer, "Y coordinate"
137
+ # end
138
+ # end
139
+ #
140
+ # arg_parser = OptParseBuilder.build_parser do |args|
141
+ # args.add bundle
142
+ # end
143
+ #
144
+ # Raises BuildError if the argument cannot be built or added.
145
+ #
146
+ # This is most useful when you are building a related suite of
147
+ # programs that share some command-line arguments in common. Most
148
+ # of the time you will just add the arguments using the block form
149
+ # of OptParseBuilder#add.
150
+ def self.build_bundle
151
+ bundler = ArgumentBundleBuilder.new
152
+ yield bundler
153
+ bundler.argument
154
+ end
155
+
156
+ end
@@ -0,0 +1,62 @@
1
+ module OptParseBuilder
2
+
3
+ # The base class for all arguments. You don't create arguments
4
+ # explicitly; they are created by for you when you use the builder
5
+ # API.
6
+ class Argument
7
+
8
+ def key # :nodoc:
9
+ end
10
+
11
+ # Get an argument's value. Returns nil if the argument has no
12
+ # value. This is made public for the use of a handler proc (See
13
+ # ArgumentBuilder#handler).
14
+ def value
15
+ end
16
+
17
+ # Set the argument's value. Does nothing if the argument has no
18
+ # value. This is made public for the use of a handler proc (See
19
+ # ArgumentBuilder#handler).
20
+ def value=(_v)
21
+ end
22
+
23
+ def banner_lines # :nodoc:
24
+ []
25
+ end
26
+
27
+ def operand_notation # :nodoc:
28
+ end
29
+
30
+ def separator_lines # :nodoc:
31
+ []
32
+ end
33
+
34
+ def apply_option(_op) # :nodoc:
35
+ end
36
+
37
+ def shift_operand(_argv) # :nodoc:
38
+ end
39
+
40
+ def reset # :nodoc:
41
+ end
42
+
43
+ def to_a # :nodoc:
44
+ [self]
45
+ end
46
+
47
+ # Convert from a required operand to an optional one, returning a
48
+ # new argument. Raises an error if that isn't possible.
49
+ def optional
50
+ raise BuildError,
51
+ "cannot convert #{self.class.name} to an optional operand"
52
+ end
53
+
54
+ # Convert from a required operand to an optional one, returning a
55
+ # new argument. Raises an error if that isn't possible.
56
+ def required
57
+ raise BuildError,
58
+ "cannot convert #{self.class.name} to a required operand"
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,193 @@
1
+ module OptParseBuilder
2
+
3
+ # Builds arguments using a builder style DSL. You never create an
4
+ # instance of this class yourself. Instead, an instance is yielded
5
+ # to you by OptParseBuilder.
6
+ #
7
+ # See the README for examples.
8
+ class ArgumentBuilder
9
+
10
+ def initialize # :nodoc:
11
+ @key = nil
12
+ @default = nil
13
+ @on = []
14
+ @handler = nil
15
+ @operand_class = nil
16
+ @operand_help_name = nil
17
+ @banner_lines = []
18
+ @separator_lines = []
19
+ end
20
+
21
+ # Set the argument's key. Accepts either a string or a symbol.
22
+ def key(v)
23
+ @key = v.to_sym
24
+ end
25
+
26
+ # Set the argument's default value. This it the value an argument
27
+ # has before parsing, or if parsing does not set the value.
28
+ #
29
+ # If an argument's default value is not explicitly set, then the
30
+ # default value is `nil`.
31
+ def default(v)
32
+ @default = v
33
+ end
34
+
35
+ # Declares the argument to be an option that is handled by
36
+ # OptParse. The arguments are passed to OptParse exactly as you
37
+ # give them, except that the string _DEFAULT_ is replaced with the
38
+ # argument's default value.
39
+ #
40
+ # Simple example:
41
+ #
42
+ # arg = OptParseBuilder.build_argument do |arg|
43
+ # arg.key :quiet
44
+ # arg.on "-q", "Be very veru quiet", "We're hunting rabbit!"
45
+ # end
46
+ #
47
+ # You may split up a long argument list by calling this method
48
+ # more than once. This is equivalent to the above:
49
+ #
50
+ # arg = OptParseBuilder.build_argument do |arg|
51
+ # arg.key :quiet
52
+ # arg.on "-q", "Be very veru quiet",
53
+ # arg.on "We're hunting rabbit!"
54
+ # end
55
+ #
56
+ # So that the option's help may print the default without having
57
+ # to duplicate it, the string _DEFAULT_ is replaced with the
58
+ # argument's default value:
59
+ #
60
+ # arg = OptParseBuilder.build_argument do |arg|
61
+ # arg.key :size
62
+ # arg.default 1024
63
+ # arg.on "--size=N", Integer,
64
+ # arg.on "Size in bytes (default _DEFAULT_)"
65
+ # end
66
+ #
67
+ # When the `--help` text for this argument is printed, it will
68
+ # read:
69
+ #
70
+ # --size-N Size in bytes (default 1024)
71
+ def on(*option_args)
72
+ @on.concat(option_args)
73
+ end
74
+
75
+ # Set a handler, a proc that will will process the argument's
76
+ # value. The proc takes two arguments:
77
+ #
78
+ # * The Argument
79
+ # * The value
80
+ #
81
+ # If no handler is set, it defaults to
82
+ #
83
+ # `->(argument, value) { argument.value = value }
84
+ #
85
+ # Applies to these argument types:
86
+ #
87
+ # * Simple option
88
+ # * Option with value
89
+ #
90
+ # Example:
91
+ #
92
+ # arg = OptParseBuilder.build_argument do |arg|
93
+ # arg.key :square
94
+ # arg.on "-v", "Increase verbosity (can give more than once)"
95
+ # arg.handler do |argument, _value|
96
+ # argument.value += 1
97
+ # end
98
+ # end
99
+ def handler(&block)
100
+ @handler = block
101
+ end
102
+
103
+ # Add to the banner text shown first in the --help output. You
104
+ # may call this more than once; each call adds another line of
105
+ # text to the banner.
106
+ #
107
+ # Any type of argument may have banner text.
108
+ #
109
+ # See also OptParseBuilder#banner
110
+ def banner(line)
111
+ @banner_lines << line
112
+ end
113
+
114
+ # Add to the separator text shown last in the --help output. You
115
+ # may call this more than once; each call adds another line of
116
+ # text to the separator.
117
+ #
118
+ # Any type of argument may have separator text.
119
+ #
120
+ # See also OptParseBuilder#separator
121
+ def separator(line)
122
+ @separator_lines << line
123
+ end
124
+
125
+ # Declare the operand to be an optional operand. An optional
126
+ # operand consumes one argument. If the argument is not present,
127
+ # value is either the default (if provided), or nil (if no default
128
+ # was provided).
129
+ def optional_operand(help_name: nil)
130
+ check_operand_class_not_set
131
+ @operand_class = OptionalOperandArgument
132
+ @operand_help_name = help_name
133
+ end
134
+
135
+ # Declare the operand to be a required operand. A required
136
+ # operand consumes one argument, generating an error if there is
137
+ # not one.
138
+ def required_operand(help_name: nil)
139
+ check_operand_class_not_set
140
+ @operand_class = RequiredOperandArgument
141
+ @operand_help_name = help_name
142
+ end
143
+
144
+ # Declare the argument to be a "splat" operand. A splat operand
145
+ # consumes all remaining arguments.
146
+ def splat_operand(help_name: nil)
147
+ check_operand_class_not_set
148
+ @operand_class = SplatOperandArgument
149
+ @operand_help_name = help_name
150
+ end
151
+
152
+ def argument # :nodoc:
153
+ check_for_build_errors
154
+ bundle = ArgumentBundle.new
155
+ unless @banner_lines.empty?
156
+ bundle << BannerArgument.new(@banner_lines)
157
+ end
158
+ unless @separator_lines.empty?
159
+ bundle << SeparatorArgument.new(@separator_lines)
160
+ end
161
+ if !@on.empty?
162
+ bundle << OptionArgument.new(@key, @default, @on, @handler)
163
+ elsif @operand_class
164
+ bundle << @operand_class.new(
165
+ @key,
166
+ @default,
167
+ @operand_help_name,
168
+ )
169
+ else
170
+ if @key || @default
171
+ bundle << ConstantArgument.new(@key, @default)
172
+ end
173
+ end
174
+ bundle.simplify
175
+ end
176
+
177
+ private
178
+
179
+ def check_operand_class_not_set
180
+ if @operand_class
181
+ raise BuildError, "Argument is already an operand"
182
+ end
183
+ end
184
+
185
+ def check_for_build_errors
186
+ if !@on.empty? && @operand_class
187
+ raise BuildError,
188
+ "Argument cannot be both an option and an operand"
189
+ end
190
+ end
191
+
192
+ end
193
+ end
@@ -0,0 +1,30 @@
1
+ module OptParseBuilder
2
+ class ArgumentBundle < Argument # :nodoc:
3
+
4
+ def initialize
5
+ @arguments = []
6
+ end
7
+
8
+ def <<(argument)
9
+ @arguments << argument
10
+ end
11
+
12
+ def to_a
13
+ @arguments.reduce([]) do |a, arg|
14
+ a + arg.to_a
15
+ end
16
+ end
17
+
18
+ def simplify
19
+ case @arguments.count
20
+ when 0
21
+ NullArgument.new
22
+ when 1
23
+ @arguments.first
24
+ else
25
+ self
26
+ end
27
+ end
28
+
29
+ end
30
+ end