dreck 0.0.1
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.
- checksums.yaml +7 -0
- data/.yardopts +1 -0
- data/LICENSE +21 -0
- data/README.md +61 -0
- data/lib/dreck.rb +19 -0
- data/lib/dreck/exceptions.rb +17 -0
- data/lib/dreck/parser.rb +72 -0
- data/lib/dreck/result.rb +123 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: eaaec798888ca9a462d16bf380853aafa47eba29
|
4
|
+
data.tar.gz: e3657ca419dc6a331a292af9778402d1e6d37e6e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f77bdc510acefb7c00223ed0e49d160c9f19749e92a9ea100539cdcfd0cb9798b1f11314828a906a67771f4cc3e7460939c2499b046680d93bc839a3df35ed85
|
7
|
+
data.tar.gz: 0ced355c2e535154d7702ca4e47eb1dc3469aaa8789b05ec367d84e16b462827a784aab812efeefcb316c0f8923cd7bdad936d8a4a2387589f8337951fdc8285
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private --markup-provider=redcarpet --markup=markdown - *.md LICENSE
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 William Woodruff <william @ yossarian.net>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
dreck
|
2
|
+
=====
|
3
|
+
|
4
|
+
A stupid parser for trailing arguments.
|
5
|
+
|
6
|
+
### Motivation
|
7
|
+
|
8
|
+
There are lots of good argument parsers for Ruby:
|
9
|
+
[OptionParser](https://ruby-doc.org/stdlib/libdoc/optparse/rdoc/OptionParser.html),
|
10
|
+
[Slop](https://github.com/leejarvis/slop), [trollop](https://manageiq.github.io/trollop/),
|
11
|
+
[cocaine](https://github.com/thoughtbot/cocaine), etc.
|
12
|
+
|
13
|
+
Most of these are great for parsing and typechecking options, but none provide a good
|
14
|
+
interface to the arguments that often *trail* the options.
|
15
|
+
|
16
|
+
Dreck does exactly that. Give it a specification of arguments (and their
|
17
|
+
types) to expect, and it will give you a nicely typechecked result.
|
18
|
+
|
19
|
+
### Installation
|
20
|
+
|
21
|
+
```bash
|
22
|
+
$ gem install dreck
|
23
|
+
```
|
24
|
+
|
25
|
+
### Usage
|
26
|
+
|
27
|
+
Dreck is best used in conjunction with an option parser like
|
28
|
+
[Slop](https://github.com/leejarvis/slop), which can provide an array of
|
29
|
+
arguments with options already filtered out:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
opts = Slop.parse do |s|
|
33
|
+
# ...
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.args # => [ "/dev/urandom", "/dev/sda", "512" ]
|
37
|
+
|
38
|
+
results = Dreck.parse opts.args, strict: true do
|
39
|
+
file :input
|
40
|
+
symbol :output
|
41
|
+
int :blocksize
|
42
|
+
end
|
43
|
+
|
44
|
+
results[:input] # => "/dev/urandom"
|
45
|
+
results[:output] # => :"/dev/sda"
|
46
|
+
results[:blocksize] # => 512
|
47
|
+
```
|
48
|
+
|
49
|
+
Lists are also supported:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
result = Dreck.parse opts.args do
|
53
|
+
string :uuid
|
54
|
+
list :file, :inputs, count: 2
|
55
|
+
list :int, :nums
|
56
|
+
end
|
57
|
+
|
58
|
+
result[:id] # => "01aa84ab-5b2c-4861-adc9-fcc6990a5ca5"
|
59
|
+
result[:inputs] # => ["/tmp/foo", "/tmp/bar"]
|
60
|
+
result[:nums] # => [1, 2, 3, 4, 5]
|
61
|
+
```
|
data/lib/dreck.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "dreck/result"
|
4
|
+
|
5
|
+
# The primary namespace for {Dreck}.
|
6
|
+
module Dreck
|
7
|
+
# {Dreck}'s current version.
|
8
|
+
VERSION = "0.0.1"
|
9
|
+
|
10
|
+
# Parse the given arguments and produce a result.
|
11
|
+
# @param args [Array<String>] the arguments to parse
|
12
|
+
# @param strict [Boolean] whether or not to be strict about argument absorption
|
13
|
+
# @return [Result] the parsing result
|
14
|
+
def self.parse(args = ARGV, strict: true, &block)
|
15
|
+
result = Result.new(args, strict: strict)
|
16
|
+
result.instance_eval(&block)
|
17
|
+
result.parse!
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dreck
|
4
|
+
# A generic error class for all {Dreck} errors.
|
5
|
+
class DreckError < RuntimeError
|
6
|
+
end
|
7
|
+
|
8
|
+
# Raised during argument absorption if arguments are either left over
|
9
|
+
# or are insufficient to populate the expected results.
|
10
|
+
class AbsorptionError < DreckError
|
11
|
+
end
|
12
|
+
|
13
|
+
# Raised during parsing if a given type cannot be produced from the corresponding
|
14
|
+
# argument.
|
15
|
+
class ParserError < DreckError
|
16
|
+
end
|
17
|
+
end
|
data/lib/dreck/parser.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "parser"
|
4
|
+
|
5
|
+
module Dreck
|
6
|
+
# Type and other constraint testing methods for {Dreck}.
|
7
|
+
class Parser
|
8
|
+
# @return [Array<Symbol>] the scalar types recognized by {Dreck}.
|
9
|
+
# @api private
|
10
|
+
SCALAR_TYPES = %i[int float path file directory symbol string].freeze
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# @param int [String] the string to coerce into an integer
|
14
|
+
# @return [Integer] the integer value of the string
|
15
|
+
# @raise [ParserError] if the string cannot be converted
|
16
|
+
def parse_int(int)
|
17
|
+
Integer int rescue raise ParserError, "#{int}: not an integer"
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param float [String] the string to coerce into a float
|
21
|
+
# @return [Float] the floating-point value of the string
|
22
|
+
# @raise [ParserError] if the string cannot be converted
|
23
|
+
def parse_float(float)
|
24
|
+
Float float rescue raise ParserError, "#{float}: not a float"
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param path [String] the string to check for path-ness
|
28
|
+
# @return [String] the string itself, if it is a path
|
29
|
+
# @raise [ParserError] if the string is not a valid path on disk
|
30
|
+
def parse_path(path)
|
31
|
+
raise ParserError, "#{path}: no such path" unless File.exist?(path)
|
32
|
+
path
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param file [String] the string to check for file-ness
|
36
|
+
# @return [String] the string itself, if it is a filename
|
37
|
+
# @raise [ParserError] if the string is not a valid regular file on disk
|
38
|
+
def parse_file(file)
|
39
|
+
raise ParserError, "#{file}: no such file" unless File.file?(file)
|
40
|
+
file
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param dir [String] the string to check for directory-ness
|
44
|
+
# @return [String] the string itself, if it is a directory
|
45
|
+
# @raise [ParserError] if the string is not a valid directory on disk
|
46
|
+
def parse_directory(dir)
|
47
|
+
raise ParserError, "#{dir}: no such directory" unless File.directory?(dir)
|
48
|
+
dir
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param sym [String] the string to coerce into a symbol
|
52
|
+
# @return [Symbol] the coerced symbol
|
53
|
+
def parse_symbol(sym)
|
54
|
+
sym.to_sym
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param str [String] the string to coerce into a string
|
58
|
+
# @return [String] the coerced string
|
59
|
+
# @note This does nothing.
|
60
|
+
def parse_string(str)
|
61
|
+
str
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param type [Symbol] the type of each member of the list
|
65
|
+
# @param list [Array<String>] the value of each member
|
66
|
+
# @return [Object] the coerced results
|
67
|
+
def parse_list(type, list)
|
68
|
+
list.map { |arg| send "parse_#{type}", arg }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/dreck/result.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "exceptions"
|
4
|
+
require_relative "parser"
|
5
|
+
|
6
|
+
module Dreck
|
7
|
+
# Represents the state and results of a {Dreck} parse.
|
8
|
+
class Result
|
9
|
+
# @return [Array] the type, name, and count information for arguments
|
10
|
+
attr_reader :expected
|
11
|
+
|
12
|
+
# @return [Hash] the parsing results hash
|
13
|
+
attr_reader :results
|
14
|
+
|
15
|
+
# @param args [Array<String>] the arguments to parse
|
16
|
+
# @param strict [Boolean] whether or not to be strict about argument absorption
|
17
|
+
def initialize(args, strict: true)
|
18
|
+
@args = args.dup
|
19
|
+
@strict = strict
|
20
|
+
@expected = []
|
21
|
+
@results = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param key [Symbol] the named argument to look up
|
25
|
+
# @return [Object] the parsed value
|
26
|
+
def [](key)
|
27
|
+
@results[key]
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Boolean] whether the results were parsed strictly
|
31
|
+
def strict?
|
32
|
+
@strict
|
33
|
+
end
|
34
|
+
|
35
|
+
Parser::SCALAR_TYPES.each do |type|
|
36
|
+
define_method type do |sym|
|
37
|
+
@expected << [type, sym]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Specifies a list of arguments of a given type to be parsed.
|
42
|
+
# @param type [Symbol] the type of the arguments
|
43
|
+
# @param sym [Symbol] the name of the argument in {results}
|
44
|
+
# @param count [Integer] the number of arguments in the list
|
45
|
+
def list(type, sym, count: nil)
|
46
|
+
@expected << [:list, type, sym, count]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Perform the actual parsing.
|
50
|
+
# @return [Result] this instance
|
51
|
+
# @api private
|
52
|
+
def parse!
|
53
|
+
check_absorption!
|
54
|
+
|
55
|
+
@expected.each do |type, *rest|
|
56
|
+
case type
|
57
|
+
when :list
|
58
|
+
parse_list!(*rest)
|
59
|
+
else
|
60
|
+
parse_type!(type, *rest)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
# Check whether the expected arguments absorb all supplied arguments.
|
68
|
+
# @raise [AbsoptionError] if absorption fails and {strict?} is not true
|
69
|
+
# @api private
|
70
|
+
def check_absorption!
|
71
|
+
count = count_expected
|
72
|
+
|
73
|
+
return if count.nil?
|
74
|
+
raise AbsoptionError, "too few arguments" if count > @args.size && strict?
|
75
|
+
raise AbsoptionError, "too many arguments" if count < @args.size && strict?
|
76
|
+
end
|
77
|
+
|
78
|
+
# Count the number of arguments expected to be supplied.
|
79
|
+
# @raise [Integer, nil] the number of expected arguments, or nil if a list
|
80
|
+
# of indeterminate size is specified
|
81
|
+
# @api private
|
82
|
+
def count_expected
|
83
|
+
@expected.inject(0) do |n, exp|
|
84
|
+
case exp.first
|
85
|
+
when :list
|
86
|
+
# if the list is greedy, all arguments have to be absorbed
|
87
|
+
break unless exp[3]
|
88
|
+
|
89
|
+
n + exp[3]
|
90
|
+
else
|
91
|
+
n + 1
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Parse a one or more expected arguments of a given type and add them to
|
97
|
+
# the results.
|
98
|
+
# @param type [Symbol] the type of the individual elements of the list
|
99
|
+
# @param sym [Symbol] the key to store the results under in {results}
|
100
|
+
# @param count [Integer, nil] the size of the list, or nil if the list
|
101
|
+
# absorbs all following arguments
|
102
|
+
# @api private
|
103
|
+
def parse_list!(type, sym, count)
|
104
|
+
args = if count
|
105
|
+
@args.shift count
|
106
|
+
else
|
107
|
+
@args
|
108
|
+
end
|
109
|
+
|
110
|
+
@results[sym] = Parser.parse_list type, args
|
111
|
+
end
|
112
|
+
|
113
|
+
# Parse one expected argument of a given scalar type and add it to the results.
|
114
|
+
# @param type [Symbol] the type of the expected argument
|
115
|
+
# @param sym [Symbol] the key to store the result under in {results}
|
116
|
+
# @api private
|
117
|
+
def parse_type!(type, sym)
|
118
|
+
arg = @args.shift
|
119
|
+
|
120
|
+
@results[sym] = Parser.send("parse_#{type}", arg)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dreck
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- William Woodruff
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-06-22 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Typechecks and coerces non-option arguments.
|
14
|
+
email: william@tuffbizz.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- ".yardopts"
|
20
|
+
- LICENSE
|
21
|
+
- README.md
|
22
|
+
- lib/dreck.rb
|
23
|
+
- lib/dreck/exceptions.rb
|
24
|
+
- lib/dreck/parser.rb
|
25
|
+
- lib/dreck/result.rb
|
26
|
+
homepage: https://github.com/woodruffw/dreck
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 2.3.0
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.6.11
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: dreck - A stupid parser for trailing arguments.
|
50
|
+
test_files: []
|