drudge 0.4.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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +59 -0
- data/LICENSE.txt +22 -0
- data/README.md +107 -0
- data/Rakefile +27 -0
- data/drudge.gemspec +35 -0
- data/features/optional-arguments.feature +64 -0
- data/features/simple-commands.feature +185 -0
- data/features/step_definitions/scripts_steps.rb +19 -0
- data/features/support/env.rb +5 -0
- data/features/variable-length-argument-lists.feature +111 -0
- data/lib/drudge.rb +8 -0
- data/lib/drudge/class_dsl.rb +106 -0
- data/lib/drudge/command.rb +100 -0
- data/lib/drudge/dispatch.rb +41 -0
- data/lib/drudge/errors.rb +30 -0
- data/lib/drudge/ext.rb +17 -0
- data/lib/drudge/kit.rb +45 -0
- data/lib/drudge/parsers.rb +91 -0
- data/lib/drudge/parsers/parse_results.rb +254 -0
- data/lib/drudge/parsers/primitives.rb +278 -0
- data/lib/drudge/parsers/tokenizer.rb +70 -0
- data/lib/drudge/version.rb +3 -0
- data/spec/drudge/class_dsl_spec.rb +125 -0
- data/spec/drudge/command_spec.rb +81 -0
- data/spec/drudge/kit_spec.rb +50 -0
- data/spec/drudge/parsers/parse_results_spec.rb +47 -0
- data/spec/drudge/parsers/primitives_spec.rb +262 -0
- data/spec/drudge/parsers/tokenizer_spec.rb +71 -0
- data/spec/drudge/parsers_spec.rb +149 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/capture.rb +16 -0
- data/spec/support/fixtures.rb +13 -0
- data/spec/support/parser_matchers.rb +42 -0
- metadata +219 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8a8ec87116986aae788c0749caf6dac45b6472a1
|
4
|
+
data.tar.gz: eb37d215512ce79df1574add8a64f12525232dd4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 64a5c2ca69867c1d5d136b2d77e173b1b24cb5890074039775e1c22369f29aa1dc0cc7e9b7f20177aff79c5fca4f4aae7b6dcfb586bc9427dc5cf542fd90df82
|
7
|
+
data.tar.gz: a797072dc6182895e0ab116032eea9f93040599449ce1e36306221e00c374b48dcb6d85e068a423f04a6a1f0382aebb42966dd3c3c8f8c5ec964619589e735c2
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
drudge (0.3.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
aruba (0.5.4)
|
10
|
+
childprocess (>= 0.3.6)
|
11
|
+
cucumber (>= 1.1.1)
|
12
|
+
rspec-expectations (>= 2.7.0)
|
13
|
+
builder (3.2.2)
|
14
|
+
childprocess (0.5.1)
|
15
|
+
ffi (~> 1.0, >= 1.0.11)
|
16
|
+
coderay (1.1.0)
|
17
|
+
cucumber (1.3.10)
|
18
|
+
builder (>= 2.1.2)
|
19
|
+
diff-lcs (>= 1.1.3)
|
20
|
+
gherkin (~> 2.12)
|
21
|
+
multi_json (>= 1.7.5, < 2.0)
|
22
|
+
multi_test (>= 0.0.2)
|
23
|
+
diff-lcs (1.2.5)
|
24
|
+
ffi (1.9.3)
|
25
|
+
gem-release (0.7.1)
|
26
|
+
gherkin (2.12.2)
|
27
|
+
multi_json (~> 1.3)
|
28
|
+
method_source (0.8.2)
|
29
|
+
multi_json (1.8.4)
|
30
|
+
multi_test (0.0.3)
|
31
|
+
pry (0.9.12.6)
|
32
|
+
coderay (~> 1.0)
|
33
|
+
method_source (~> 0.8)
|
34
|
+
slop (~> 3.4)
|
35
|
+
rake (10.1.1)
|
36
|
+
rspec (2.14.1)
|
37
|
+
rspec-core (~> 2.14.0)
|
38
|
+
rspec-expectations (~> 2.14.0)
|
39
|
+
rspec-mocks (~> 2.14.0)
|
40
|
+
rspec-core (2.14.8)
|
41
|
+
rspec-expectations (2.14.5)
|
42
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
43
|
+
rspec-mocks (2.14.6)
|
44
|
+
slop (3.4.7)
|
45
|
+
yard (0.8.7.3)
|
46
|
+
|
47
|
+
PLATFORMS
|
48
|
+
ruby
|
49
|
+
|
50
|
+
DEPENDENCIES
|
51
|
+
aruba (>= 0.4.6)
|
52
|
+
bundler (~> 1.3)
|
53
|
+
cucumber
|
54
|
+
drudge!
|
55
|
+
gem-release
|
56
|
+
pry
|
57
|
+
rake
|
58
|
+
rspec (>= 2.14, < 3.0)
|
59
|
+
yard (>= 0.8.6.1)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013, 2014 Ognen Ivanovski
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# Drudge
|
2
|
+
|
3
|
+
A gem that enables you to write command line automation tools using Ruby 2.0.
|
4
|
+
|
5
|
+
|
6
|
+
## Usage
|
7
|
+
|
8
|
+
Given a binary file called `cli`:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
#!/usr/bin/env ruby
|
12
|
+
|
13
|
+
require 'drudge'
|
14
|
+
|
15
|
+
class Cli < Drudge
|
16
|
+
|
17
|
+
desc "Greets people"
|
18
|
+
def greet(from, opening = 'Hi', *messages, to)
|
19
|
+
puts "#{from} says #{opening}, #{messages.join(', ')} to #{to}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Cli.dispatch
|
24
|
+
```
|
25
|
+
|
26
|
+
Running:
|
27
|
+
|
28
|
+
```
|
29
|
+
$ ./cli greet Santa 'good day to you' Joe
|
30
|
+
Santa says Hi, good day to you to Joe
|
31
|
+
|
32
|
+
$ ./cli greet Santa Greetings 'how do you do?' Joe
|
33
|
+
Santa says Greetings, how do you do? to Joe
|
34
|
+
|
35
|
+
$ ./cli
|
36
|
+
error: expected a command:
|
37
|
+
|
38
|
+
cli
|
39
|
+
^
|
40
|
+
|
41
|
+
|
42
|
+
$ ./cli great Santa Joe
|
43
|
+
error: unknown command 'great':
|
44
|
+
|
45
|
+
cli great Santa Joe
|
46
|
+
~~~~~
|
47
|
+
|
48
|
+
$ ./cli greet
|
49
|
+
error: expected a value for <from>:
|
50
|
+
|
51
|
+
cli greet
|
52
|
+
^
|
53
|
+
|
54
|
+
$ ./cli greet Santa
|
55
|
+
error: expected a value for <to>:
|
56
|
+
|
57
|
+
cli greet Santa
|
58
|
+
^
|
59
|
+
```
|
60
|
+
|
61
|
+
## Approach
|
62
|
+
|
63
|
+
The philosphy of **Drudge** is to provide a very thin layer over *glue* over normal
|
64
|
+
Ruby constructs such as classes and methods that exposes them via a command
|
65
|
+
line interface.
|
66
|
+
|
67
|
+
This layer interprets the command line instruction and invokes the identifed
|
68
|
+
ruby method using a **very simple** resolution method. From then on, it's just
|
69
|
+
normal Ruby! No special life-cycles etc.
|
70
|
+
|
71
|
+
Even though this layer is simple, it is built to produce excellent error
|
72
|
+
messages on wrong command line input.
|
73
|
+
|
74
|
+
Drudge is built for Ruby 2.0 with keyword arguments in mind.
|
75
|
+
|
76
|
+
## Why not Thor?
|
77
|
+
|
78
|
+
Drudge was inspired by the great work folks did in **Thor**.
|
79
|
+
|
80
|
+
The problem with Thor is that it tries to be two things at once: a build tool
|
81
|
+
(aimed on replacing rake) and an automation tool.
|
82
|
+
|
83
|
+
This introduces a number of unnecessary complexities:
|
84
|
+
|
85
|
+
- there are rake-like *namespaces* but also sub commands for automation. These two
|
86
|
+
concepts have a number of nasty interactions which result in many small but annoying bugs
|
87
|
+
|
88
|
+
- it is meant to be used a a stand-alone tool (by invoking `thor` which will look for a
|
89
|
+
`Thorfile`) but also as a library for building your own tools. This too
|
90
|
+
produces complexities and unwanted interactions in the Thor codebase
|
91
|
+
|
92
|
+
- Thor skews the normal Ruby class/method model towards the command line
|
93
|
+
interface and introduces some 'suprises' for the user (e.g. the
|
94
|
+
Thor-subclass gets instantieated every time a method/command is called,
|
95
|
+
something that is not usually expected)
|
96
|
+
|
97
|
+
In contrast, Drudge's aim is simple: a library for building command-line
|
98
|
+
automation tools with the aim of transferring you (conceptionally) from the command line
|
99
|
+
interface into Ruby and then letting you use build your tool in a familiar
|
100
|
+
environement.
|
101
|
+
|
102
|
+
## License
|
103
|
+
|
104
|
+
Released under the MIT License. See the [LICENSE][] file for further details.
|
105
|
+
|
106
|
+
[license]: LICENSE.txt
|
107
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "cucumber"
|
3
|
+
require "cucumber/rake/task"
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
|
6
|
+
|
7
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
8
|
+
t.cucumber_opts = "features"
|
9
|
+
end
|
10
|
+
|
11
|
+
namespace :spec do
|
12
|
+
|
13
|
+
default_opts = %w[-Ilib -Ispec --color]
|
14
|
+
|
15
|
+
desc "Runs specs with progress output"
|
16
|
+
RSpec::Core::RakeTask.new(:run) do |t|
|
17
|
+
t.rspec_opts = default_opts
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Runs specs in documentation mode"
|
21
|
+
RSpec::Core::RakeTask.new(:pretty) do |t|
|
22
|
+
t.rspec_opts = default_opts + %w[--format documentation]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Runs specs"
|
27
|
+
task :spec => "spec:run"
|
data/drudge.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'drudge/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "drudge"
|
8
|
+
spec.version = Drudge::VERSION
|
9
|
+
spec.authors = ["Ognen Ivanovski"]
|
10
|
+
spec.email = ["ognen.ivanovski@me.com"]
|
11
|
+
spec.description = %q{A library for building command-line
|
12
|
+
automation tools with the aim of transferring you (conceptionally) from the command line
|
13
|
+
interface into Ruby and then letting you use build your tool in a familiar
|
14
|
+
environement.}
|
15
|
+
spec.summary = %q{A gem that enables you to write command line automation tools using Ruby 2.0.}
|
16
|
+
spec.homepage = "https://github.com/ognen/drudge"
|
17
|
+
spec.license = "MIT"
|
18
|
+
|
19
|
+
spec.required_ruby_version = '>= 2.0.0'
|
20
|
+
|
21
|
+
spec.files = `git ls-files`.split($/)
|
22
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
23
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
27
|
+
spec.add_development_dependency "rake"
|
28
|
+
spec.add_development_dependency "pry"
|
29
|
+
spec.add_development_dependency "rspec", ">= 2.14", "< 3.0"
|
30
|
+
spec.add_development_dependency "cucumber"
|
31
|
+
spec.add_development_dependency "aruba", ">= 0.4.6"
|
32
|
+
spec.add_development_dependency "yard", ">= 0.8.6.1"
|
33
|
+
spec.add_development_dependency "gem-release"
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
Feature: Optional Arguments
|
2
|
+
Ruby supports optional (positional) arguments.
|
3
|
+
I want (again) a close mapping between method-optional arguments and
|
4
|
+
the command line.
|
5
|
+
|
6
|
+
Scenario: Optonal argument at the end of the arg list
|
7
|
+
Given a Ruby script called "cli" with:
|
8
|
+
"""
|
9
|
+
require 'drudge'
|
10
|
+
|
11
|
+
class Cli < Drudge
|
12
|
+
|
13
|
+
desc "Greets people"
|
14
|
+
def greet(message, from = "Santa")
|
15
|
+
puts "#{from} says: #{message}"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
Cli.dispatch
|
21
|
+
"""
|
22
|
+
When I run `cli greet Hello`
|
23
|
+
Then the output should contain "Santa says: Hello"
|
24
|
+
|
25
|
+
Scenario: Optional arguments in the middle of the arg list
|
26
|
+
Given a Ruby script called "cli" with:
|
27
|
+
"""
|
28
|
+
require 'drudge'
|
29
|
+
|
30
|
+
class Cli < Drudge
|
31
|
+
|
32
|
+
desc "Greets people"
|
33
|
+
def greet(message, from = "Santa", recipient)
|
34
|
+
puts "#{from} says to #{recipient}: #{message}"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
Cli.dispatch
|
40
|
+
"""
|
41
|
+
When I run `cli greet Hello Sam`
|
42
|
+
Then the output should contain "Santa says to Sam: Hello"
|
43
|
+
|
44
|
+
|
45
|
+
Scenario: Providing value for an optional arguemnt overrides the default
|
46
|
+
Given a Ruby script called "cli" with:
|
47
|
+
"""
|
48
|
+
require 'drudge'
|
49
|
+
|
50
|
+
class Cli < Drudge
|
51
|
+
|
52
|
+
desc "Greets people"
|
53
|
+
def greet(message, from = "Santa", recipient)
|
54
|
+
puts "#{from} says to #{recipient}: #{message}"
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
Cli.dispatch
|
60
|
+
"""
|
61
|
+
When I run `cli greet Hello Farmer Sam`
|
62
|
+
Then the output should contain "Farmer says to Sam: Hello"
|
63
|
+
|
64
|
+
|
@@ -0,0 +1,185 @@
|
|
1
|
+
Feature: Simple Commands
|
2
|
+
In order to write command line tasks quickly and painlessly
|
3
|
+
I want to have a very close mapping between Ruby classes and methods
|
4
|
+
and command scripts / commands.
|
5
|
+
|
6
|
+
|
7
|
+
Scenario: A simple command with no arguments is just a method call
|
8
|
+
Given a Ruby script called "cli" with:
|
9
|
+
"""
|
10
|
+
require 'drudge'
|
11
|
+
|
12
|
+
class Cli < Drudge
|
13
|
+
|
14
|
+
desc "verifies the project"
|
15
|
+
def verify
|
16
|
+
puts "Verified."
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
Cli.dispatch
|
22
|
+
"""
|
23
|
+
When I run `cli verify`
|
24
|
+
Then the output should contain "Verified."
|
25
|
+
|
26
|
+
|
27
|
+
Scenario: A method with an argument maps to a command in a Ruby script with one required argument
|
28
|
+
Given a Ruby script called "cli" with:
|
29
|
+
"""
|
30
|
+
require 'drudge'
|
31
|
+
|
32
|
+
class Cli < Drudge
|
33
|
+
|
34
|
+
desc "greets someone"
|
35
|
+
def greet(someone)
|
36
|
+
puts "Hello #{someone}!"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Cli.dispatch
|
41
|
+
"""
|
42
|
+
When I run `cli greet Santa`
|
43
|
+
Then the output should contain "Hello Santa!"
|
44
|
+
|
45
|
+
Scenario: Too many arguments are reported as an error
|
46
|
+
Given a Ruby script called "cli" with:
|
47
|
+
"""
|
48
|
+
require 'drudge'
|
49
|
+
|
50
|
+
class Cli < Drudge
|
51
|
+
|
52
|
+
desc "greet someone someoneelse"
|
53
|
+
def greet(someone)
|
54
|
+
puts "Hello #{someone}!"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
Cli.dispatch
|
59
|
+
"""
|
60
|
+
When I run `cli greet Santa Clause`
|
61
|
+
Then the output should contain:
|
62
|
+
"""
|
63
|
+
error: extra command line arguments provided:
|
64
|
+
|
65
|
+
cli greet Santa Clause
|
66
|
+
~~~~~~
|
67
|
+
"""
|
68
|
+
|
69
|
+
Scenario: A required parameter must be provided
|
70
|
+
Given a Ruby script called "cli" with:
|
71
|
+
"""
|
72
|
+
require 'drudge'
|
73
|
+
|
74
|
+
class Cli < Drudge
|
75
|
+
|
76
|
+
desc "greets someone"
|
77
|
+
def greet(someone)
|
78
|
+
puts "Hello #{someone}!"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
Cli.dispatch
|
83
|
+
"""
|
84
|
+
When I run `cli greet`
|
85
|
+
Then the output should contain:
|
86
|
+
"""
|
87
|
+
error: expected a value for <someone>:
|
88
|
+
|
89
|
+
cli greet
|
90
|
+
^
|
91
|
+
"""
|
92
|
+
|
93
|
+
Scenario: The user is notified if a command is improperly entered
|
94
|
+
Given a Ruby script called "cli" with:
|
95
|
+
"""
|
96
|
+
require 'drudge'
|
97
|
+
|
98
|
+
class Cli < Drudge
|
99
|
+
|
100
|
+
desc "greets someone"
|
101
|
+
def greet(someone)
|
102
|
+
puts "Hello #{someone}!"
|
103
|
+
end
|
104
|
+
|
105
|
+
desc "says something"
|
106
|
+
def say(something)
|
107
|
+
puts "Saying #{something}!"
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
Cli.dispatch
|
113
|
+
"""
|
114
|
+
When I run `cli great`
|
115
|
+
Then the output should contain:
|
116
|
+
"""
|
117
|
+
error: unknown command 'great':
|
118
|
+
|
119
|
+
cli great
|
120
|
+
~~~~~
|
121
|
+
"""
|
122
|
+
|
123
|
+
Scenario: The user is notified if a command is missing
|
124
|
+
Given a Ruby script called "cli" with:
|
125
|
+
"""
|
126
|
+
require 'drudge'
|
127
|
+
|
128
|
+
class Cli < Drudge
|
129
|
+
|
130
|
+
desc "greets someone"
|
131
|
+
def greet(someone)
|
132
|
+
puts "Hello #{someone}!"
|
133
|
+
end
|
134
|
+
|
135
|
+
desc "says something"
|
136
|
+
def say(something)
|
137
|
+
puts "Saying #{something}!"
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
Cli.dispatch
|
143
|
+
"""
|
144
|
+
When I run `cli`
|
145
|
+
Then the output should contain:
|
146
|
+
"""
|
147
|
+
error: expected a command:
|
148
|
+
|
149
|
+
cli
|
150
|
+
^
|
151
|
+
"""
|
152
|
+
|
153
|
+
Scenario: The error reported relates to the command being executed
|
154
|
+
Given a Ruby script called "cli" with:
|
155
|
+
"""
|
156
|
+
require 'drudge'
|
157
|
+
|
158
|
+
class Cli < Drudge
|
159
|
+
|
160
|
+
desc "greets someone"
|
161
|
+
def greet(someone)
|
162
|
+
puts "Hello #{someone}!"
|
163
|
+
end
|
164
|
+
|
165
|
+
desc "hello worlds"
|
166
|
+
def hello(world)
|
167
|
+
puts "Hello #{world}!"
|
168
|
+
end
|
169
|
+
|
170
|
+
desc "Third"
|
171
|
+
def third
|
172
|
+
puts "Hello"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
Cli.dispatch
|
177
|
+
"""
|
178
|
+
When I run `cli hello world err`
|
179
|
+
Then the output should contain:
|
180
|
+
"""
|
181
|
+
error: extra command line arguments provided:
|
182
|
+
|
183
|
+
cli hello world err
|
184
|
+
~~~
|
185
|
+
"""
|