dreck 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|