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 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
@@ -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
@@ -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: []