opt_parse_builder 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 (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