sickle 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/README.md +155 -9
- data/Rakefile +8 -0
- data/lib/sickle.rb +250 -1
- data/lib/sickle/version.rb +1 -1
- data/sickle.gemspec +2 -0
- data/spec/runner_spec.rb +68 -0
- data/spec/test_app.rb +71 -0
- data/spec/test_run.rb +8 -0
- metadata +36 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 03ca9b3349e1160e67bf4a7377b1c8ecbae4994c
|
4
|
+
data.tar.gz: e9e1e24472aa2adb8187c71252b419fbffb4b13f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c5ee9bb8e2d698b345aa890e4f12085b2d69b38223be0fbbcc914f11d3a1b73a6c9fe2ceb59a74e1f8f3156dbc904c4f66b33c6a6dbb85074e4c451fcd1a722
|
7
|
+
data.tar.gz: 99d5204b414e825d52c9ef011c3f6490677f9d6bc32c904fb0cc05a03b9dc610ec6785d86fa1edef53c60c5be9eef0065960d7787ce2514042ecb6f9ce55ac2a
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,24 +1,170 @@
|
|
1
1
|
# Sickle
|
2
2
|
|
3
|
-
|
3
|
+
## Description
|
4
4
|
|
5
|
-
|
5
|
+
Sickle is dead simple library for building complex command line tools.
|
6
6
|
|
7
|
-
|
7
|
+
#### Features:
|
8
8
|
|
9
|
-
|
9
|
+
* Based on classes and modules
|
10
|
+
* Support for commands
|
11
|
+
* Support for namespaces
|
12
|
+
* Support for command options and global options
|
13
|
+
* Usage and help for free
|
14
|
+
* No external dependencies (only stdlib optparse)
|
10
15
|
|
11
|
-
And then execute:
|
12
16
|
|
13
|
-
|
17
|
+
## Installation
|
14
18
|
|
15
|
-
|
19
|
+
You are probably building command line tool that will be released as gem, just add that line to you gemspec.
|
16
20
|
|
17
|
-
|
21
|
+
```ruby
|
22
|
+
spec.add_dependency 'sickle'
|
23
|
+
```
|
18
24
|
|
19
25
|
## Usage
|
20
26
|
|
21
|
-
|
27
|
+
### Basic usage
|
28
|
+
|
29
|
+
Simple create a class with methods and some options
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require "sickle"
|
33
|
+
|
34
|
+
class App
|
35
|
+
include Sickle::Runner
|
36
|
+
|
37
|
+
global_option :verbose # global flag
|
38
|
+
|
39
|
+
desc "install one of the available apps" # command description
|
40
|
+
option :force # flag for `install` command
|
41
|
+
option :host, :default => "localhost" # option
|
42
|
+
def install(name)
|
43
|
+
if options[:host] # access options
|
44
|
+
# do something
|
45
|
+
end
|
46
|
+
# the rest
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "list all apps, search is possible"
|
50
|
+
def list(search = "")
|
51
|
+
# ...
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
App.run(ARGV) # start parsing ARGV
|
57
|
+
```
|
58
|
+
|
59
|
+
This will allow for execution command like:
|
60
|
+
|
61
|
+
```bash
|
62
|
+
$ mytool install foo
|
63
|
+
$ mytool install foo --force --verbose --host 127.0.0.1
|
64
|
+
$ mytool list
|
65
|
+
$ mytool list rails --verbose
|
66
|
+
```
|
67
|
+
|
68
|
+
Help is for free:
|
69
|
+
|
70
|
+
```
|
71
|
+
$ mytool help
|
72
|
+
USAGE:
|
73
|
+
mytool COMMAND [ARG1, ARG2, ...] [OPTIONS]
|
74
|
+
|
75
|
+
TASKS:
|
76
|
+
help [COMMAND]
|
77
|
+
install NAME # install one of the available apps
|
78
|
+
list [SEARCH] # list all apps, search is possible
|
79
|
+
|
80
|
+
GLOBAL OPTIONS:
|
81
|
+
--verbose (default: false)
|
82
|
+
```
|
83
|
+
|
84
|
+
There is also detailed help for command:
|
85
|
+
|
86
|
+
```bash
|
87
|
+
$ mytool help install
|
88
|
+
USAGE:
|
89
|
+
mytool install NAME
|
90
|
+
|
91
|
+
DESCRIPTION:
|
92
|
+
install one of the available apps
|
93
|
+
|
94
|
+
OPTIONS:
|
95
|
+
--force (default: false)
|
96
|
+
--host (default: localhost)
|
97
|
+
```
|
98
|
+
|
99
|
+
|
100
|
+
### Advanced usage - multiple modules
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
module Users
|
104
|
+
include Sickle::Runner
|
105
|
+
|
106
|
+
desc "list all users"
|
107
|
+
def list
|
108
|
+
# ...
|
109
|
+
end
|
110
|
+
|
111
|
+
desc "create new user"
|
112
|
+
def create(name)
|
113
|
+
# ...
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
module Projects
|
118
|
+
include Sickle::Runner
|
119
|
+
|
120
|
+
desc "list all projects"
|
121
|
+
def list
|
122
|
+
# ...
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
module Global
|
127
|
+
include Sickle::Runner
|
128
|
+
|
129
|
+
desc "have some fun at top level"
|
130
|
+
def fun
|
131
|
+
# ...
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class App
|
136
|
+
include Sickle::Runner
|
137
|
+
|
138
|
+
desc "top level command"
|
139
|
+
def main
|
140
|
+
# ...
|
141
|
+
end
|
142
|
+
|
143
|
+
include_modules :users => Users, # bind commands from Users module under "users" namespace
|
144
|
+
:p => Projects # bind commands from Projects module under "p" namespace
|
145
|
+
|
146
|
+
include Global # bind command from Global module at top level namespace
|
147
|
+
end
|
148
|
+
|
149
|
+
App.run(ARGV)
|
150
|
+
|
151
|
+
```
|
152
|
+
|
153
|
+
Run `$ mytool help` to see how commands are namespaced:
|
154
|
+
|
155
|
+
```bash
|
156
|
+
$ mytool help
|
157
|
+
USAGE:
|
158
|
+
mytool COMMAND [ARG1, ARG2, ...] [OPTIONS]
|
159
|
+
|
160
|
+
TASKS:
|
161
|
+
fun # have some fun at top level
|
162
|
+
help [COMMAND]
|
163
|
+
main # top level command
|
164
|
+
p:list # list all projects
|
165
|
+
users:create NAME # create new user
|
166
|
+
users:list # list all users
|
167
|
+
```
|
22
168
|
|
23
169
|
## Contributing
|
24
170
|
|
data/Rakefile
CHANGED
data/lib/sickle.rb
CHANGED
@@ -1,5 +1,254 @@
|
|
1
1
|
require "sickle/version"
|
2
|
+
require 'optparse'
|
2
3
|
|
3
4
|
module Sickle
|
4
|
-
|
5
|
+
class << self
|
6
|
+
def push_desc(desc)
|
7
|
+
@__desc = desc
|
8
|
+
end
|
9
|
+
|
10
|
+
def pop_desc
|
11
|
+
d = @__desc
|
12
|
+
@__desc = nil
|
13
|
+
d
|
14
|
+
end
|
15
|
+
|
16
|
+
def push_option(name, opts)
|
17
|
+
@__options ||= {}
|
18
|
+
@__options[name] = Option.new(name, opts)
|
19
|
+
end
|
20
|
+
|
21
|
+
def pop_options
|
22
|
+
o = @__options || {}
|
23
|
+
@__options = {}
|
24
|
+
o
|
25
|
+
end
|
26
|
+
|
27
|
+
def push_namespace(n)
|
28
|
+
namespace << n
|
29
|
+
end
|
30
|
+
|
31
|
+
def pop_namespace
|
32
|
+
namespace.pop
|
33
|
+
end
|
34
|
+
|
35
|
+
def namespace
|
36
|
+
@__namespace ||= []
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module Runner
|
41
|
+
def self.included(base)
|
42
|
+
base.extend(Sickle::ClassMethods)
|
43
|
+
|
44
|
+
if base.is_a?(Class)
|
45
|
+
base.send(:include, Sickle::Help)
|
46
|
+
base.method_added(:help)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def options
|
51
|
+
@__options ||= {}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Command
|
56
|
+
attr_accessor :meth, :name, :desc, :options
|
57
|
+
|
58
|
+
def initialize(meth, name, desc, options)
|
59
|
+
@meth, @name, @desc, @options = meth, name, desc, options
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Option
|
64
|
+
attr_accessor :name, :opts, :default
|
65
|
+
|
66
|
+
def initialize(name, opts)
|
67
|
+
@name, @opts = name, opts
|
68
|
+
|
69
|
+
@default = opts[:default] || false
|
70
|
+
|
71
|
+
if @default == true || @default == false
|
72
|
+
@type = :boolean
|
73
|
+
else
|
74
|
+
@type = @default.class.to_s.downcase.to_sym
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def register(parser, results)
|
79
|
+
if @type == :boolean
|
80
|
+
parser.on("--#{@name}", opts[:desc]) do
|
81
|
+
results[@name] = true
|
82
|
+
end
|
83
|
+
else
|
84
|
+
parser.on("--#{@name} #{@name.upcase}") do |v|
|
85
|
+
results[@name] = coerce(v)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def coerce(value)
|
91
|
+
case @default
|
92
|
+
when Fixnum
|
93
|
+
value.to_i
|
94
|
+
when Float
|
95
|
+
value.to_f
|
96
|
+
else
|
97
|
+
value
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
module Help
|
104
|
+
def help(command = nil)
|
105
|
+
if command
|
106
|
+
__display_help_for_command(command)
|
107
|
+
else
|
108
|
+
__display_help
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def __display_help_for_command(name)
|
113
|
+
if cmd = self.class.__commands[name]
|
114
|
+
puts "USAGE:"
|
115
|
+
u, _ = __display_command_usage(name, cmd)
|
116
|
+
puts " #{$0} #{u}"
|
117
|
+
puts
|
118
|
+
puts "DESCRIPTION:"
|
119
|
+
puts cmd.desc.split("\n").map {|e| " #{e}"}.join("\n")
|
120
|
+
puts
|
121
|
+
unless cmd.options.empty?
|
122
|
+
puts "OPTIONS:"
|
123
|
+
cmd.options.each do |_, opt|
|
124
|
+
puts __display_option(opt)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
__display_global_options
|
129
|
+
else
|
130
|
+
puts "\e[31mCommand '#{name}' not found\e[0m"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def __display_command_usage(name, command)
|
135
|
+
params = command.meth.parameters.map do |(r, p)|
|
136
|
+
r == :req ? p.upcase : "[#{p.upcase}]"
|
137
|
+
end
|
138
|
+
|
139
|
+
["#{name} #{params.join(" ")}", command]
|
140
|
+
end
|
141
|
+
|
142
|
+
def __display_help
|
143
|
+
puts "USAGE:"
|
144
|
+
puts " #{$0} COMMAND [ARG1, ARG2, ...] [OPTIONS]"
|
145
|
+
puts
|
146
|
+
|
147
|
+
puts "TASKS:"
|
148
|
+
cmds = self.class.__commands.sort.map do |name, command|
|
149
|
+
__display_command_usage(name, command)
|
150
|
+
end
|
151
|
+
max = cmds.map {|a| a[0].length }.max
|
152
|
+
cmds.each do |(cmd, c)|
|
153
|
+
desc = c.desc ? "# #{c.desc}" : ""
|
154
|
+
puts " #{cmd.ljust(max)} #{desc}"
|
155
|
+
end
|
156
|
+
|
157
|
+
__display_global_options
|
158
|
+
end
|
159
|
+
|
160
|
+
def __display_global_options
|
161
|
+
unless self.class.__global_options.empty?
|
162
|
+
puts
|
163
|
+
puts "GLOBAL OPTIONS:"
|
164
|
+
self.class.__global_options.sort.each do |name, opt|
|
165
|
+
puts __display_option(opt)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def __display_option(opt)
|
171
|
+
" --#{opt.name} (default: #{opt.default})"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
module ClassMethods
|
176
|
+
def included(base)
|
177
|
+
__commands.each do |name, command|
|
178
|
+
name = (Sickle.namespace + [name]).join(":")
|
179
|
+
base.__commands[name] = command
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def desc(label)
|
184
|
+
Sickle.push_desc(label)
|
185
|
+
end
|
186
|
+
|
187
|
+
def global_option(name, opts = {})
|
188
|
+
__global_options[name.to_s] = Option.new(name, opts)
|
189
|
+
end
|
190
|
+
|
191
|
+
def option(name, opts = {})
|
192
|
+
Sickle.push_option(name, opts)
|
193
|
+
end
|
194
|
+
|
195
|
+
def include_modules(hash)
|
196
|
+
hash.each do |key, value|
|
197
|
+
Sickle.push_namespace(key)
|
198
|
+
send(:include, value)
|
199
|
+
Sickle.pop_namespace
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def __commands
|
204
|
+
@__commands ||= {}
|
205
|
+
end
|
206
|
+
|
207
|
+
def __global_options
|
208
|
+
@__global_options ||= {}
|
209
|
+
end
|
210
|
+
|
211
|
+
def run(argv)
|
212
|
+
# puts "ARGV: #{argv.inspect}"
|
213
|
+
|
214
|
+
if command_name = argv.shift
|
215
|
+
if command = __commands[command_name]
|
216
|
+
all = __global_options.values + command.options.values
|
217
|
+
|
218
|
+
results = {}
|
219
|
+
args = OptionParser.new do |parser|
|
220
|
+
all.each do |option|
|
221
|
+
option.register(parser, results)
|
222
|
+
end
|
223
|
+
end.parse!(argv)
|
224
|
+
|
225
|
+
all.each do |o|
|
226
|
+
results[o.name] ||= o.default
|
227
|
+
end
|
228
|
+
|
229
|
+
# puts "args: #{args.inspect}"
|
230
|
+
# puts "results: #{results.inspect}"
|
231
|
+
|
232
|
+
|
233
|
+
obj = self.new
|
234
|
+
obj.instance_variable_set(:@__options, results)
|
235
|
+
command.meth.bind(obj).call(*args)
|
236
|
+
else
|
237
|
+
puts "\e[31mCommand '#{command_name}' not found\e[0m"
|
238
|
+
puts
|
239
|
+
run(["help"])
|
240
|
+
end
|
241
|
+
else
|
242
|
+
run(["help"])
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
|
248
|
+
def method_added(a)
|
249
|
+
meth = instance_method(a)
|
250
|
+
__commands[a.to_s] = Command.new(meth, a, Sickle.pop_desc, Sickle.pop_options)
|
251
|
+
end
|
252
|
+
end
|
5
253
|
end
|
254
|
+
|
data/lib/sickle/version.rb
CHANGED
data/sickle.gemspec
CHANGED
data/spec/runner_spec.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require "minitest/spec"
|
2
|
+
require "minitest/autorun"
|
3
|
+
require "turn"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), "test_app")
|
6
|
+
|
7
|
+
describe Sickle do
|
8
|
+
describe "DSL" do
|
9
|
+
it "list of global options" do
|
10
|
+
App.__global_options.keys.must_equal ["verbose", "debug"]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "list of commands" do
|
14
|
+
App.__commands.keys.must_equal(
|
15
|
+
%w(task1 task2 conflict sub:sub1 sub:conflict other:other1 other:conflict nosub))
|
16
|
+
end
|
17
|
+
|
18
|
+
it "correct commands descriptions" do
|
19
|
+
App.__commands["task1"].desc.must_equal "Run task 1"
|
20
|
+
App.__commands["task2"].desc.must_equal "Run task 2"
|
21
|
+
App.__commands["sub:sub1"].desc.must_equal "Run task Sub1"
|
22
|
+
App.__commands["other:other1"].desc.must_equal "Run task other sub1"
|
23
|
+
App.__commands["nosub"].desc.must_equal "No sub for me"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "correct commands options" do
|
27
|
+
App.__commands["task1"].options.keys.must_equal [:quiet]
|
28
|
+
App.__commands["task2"].options.keys.must_equal [:fast, :slow, :number]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "Runner" do
|
33
|
+
it "task1" do
|
34
|
+
App.run(["task1", "x", "y"]).must_equal(
|
35
|
+
["task1", "x", "y", "def", false, false, false])
|
36
|
+
App.run(["task1", "x", "y", "z", "--verbose"]).must_equal(
|
37
|
+
["task1", "x", "y", "z", false, true, false])
|
38
|
+
end
|
39
|
+
|
40
|
+
it "task2" do
|
41
|
+
App.run(["task2"]).must_equal(
|
42
|
+
["task2", 10, false, false, false, false])
|
43
|
+
App.run(%w(task2 --fast)).must_equal(
|
44
|
+
["task2", 10, true, false, false, false])
|
45
|
+
App.run(%w(task2 --slow)).must_equal(
|
46
|
+
["task2", 10, false, true, false, false])
|
47
|
+
App.run(%w(task2 --verbose)).must_equal(
|
48
|
+
["task2", 10, false, false, true, false])
|
49
|
+
App.run(%w(task2 --debug)).must_equal(
|
50
|
+
["task2", 10, false, false, false, true])
|
51
|
+
App.run(%w(task2 --fast --slow --verbose)).must_equal(
|
52
|
+
["task2", 10, true, true, true, false])
|
53
|
+
App.run(%w(task2 --number 40)).must_equal(
|
54
|
+
["task2", 40, false, false, false, false])
|
55
|
+
end
|
56
|
+
|
57
|
+
it "sub:sub1" do
|
58
|
+
App.run(%w(sub:sub1)).must_equal(
|
59
|
+
["sub1"])
|
60
|
+
end
|
61
|
+
|
62
|
+
it "conflict" do
|
63
|
+
App.run(%w(conflict)).must_equal ["nosub:conflict"]
|
64
|
+
App.run(%w(sub:conflict)).must_equal ["sub1:conflict"]
|
65
|
+
App.run(%w(other:conflict)).must_equal ["other1:conflict"]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/spec/test_app.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "lib", "sickle")
|
2
|
+
|
3
|
+
module Sub
|
4
|
+
include Sickle::Runner
|
5
|
+
|
6
|
+
desc "Run task Sub1"
|
7
|
+
def sub1
|
8
|
+
p ["sub1"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def conflict
|
12
|
+
p ["sub1:conflict"]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Other
|
17
|
+
include Sickle::Runner
|
18
|
+
|
19
|
+
desc "Run task other sub1"
|
20
|
+
def other1(blah)
|
21
|
+
p ["other1", blah]
|
22
|
+
end
|
23
|
+
|
24
|
+
def conflict
|
25
|
+
p ["other1:conflict"]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module NoSub
|
30
|
+
include Sickle::Runner
|
31
|
+
|
32
|
+
desc "No sub for me"
|
33
|
+
def nosub
|
34
|
+
p ["nosub"]
|
35
|
+
end
|
36
|
+
|
37
|
+
def conflict
|
38
|
+
p ["nosub:conflict"]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class App
|
43
|
+
include Sickle::Runner
|
44
|
+
|
45
|
+
global_option :verbose
|
46
|
+
global_option :debug
|
47
|
+
|
48
|
+
desc "Run task 1"
|
49
|
+
option :quiet
|
50
|
+
def task1(a, b, c = "def")
|
51
|
+
p ["task1", a, b, c, options[:quiet], options[:verbose], options[:debug]]
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "Run task 2"
|
55
|
+
option :fast
|
56
|
+
option :slow
|
57
|
+
option :number, :default => 10
|
58
|
+
def task2
|
59
|
+
p ["task2", options[:number], options[:fast], options[:slow], options[:verbose], options[:debug]]
|
60
|
+
end
|
61
|
+
|
62
|
+
def conflict
|
63
|
+
p ["app:conflict"]
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
include_modules :sub => Sub,
|
68
|
+
:other => Other
|
69
|
+
|
70
|
+
include NoSub
|
71
|
+
end
|
data/spec/test_run.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sickle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tymon Tobolski
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: turn
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
41
69
|
description: sickle
|
42
70
|
email:
|
43
71
|
- i@teamon.eu
|
@@ -53,6 +81,9 @@ files:
|
|
53
81
|
- lib/sickle.rb
|
54
82
|
- lib/sickle/version.rb
|
55
83
|
- sickle.gemspec
|
84
|
+
- spec/runner_spec.rb
|
85
|
+
- spec/test_app.rb
|
86
|
+
- spec/test_run.rb
|
56
87
|
homepage: ''
|
57
88
|
licenses:
|
58
89
|
- MIT
|
@@ -77,4 +108,7 @@ rubygems_version: 2.0.0
|
|
77
108
|
signing_key:
|
78
109
|
specification_version: 4
|
79
110
|
summary: sickle
|
80
|
-
test_files:
|
111
|
+
test_files:
|
112
|
+
- spec/runner_spec.rb
|
113
|
+
- spec/test_app.rb
|
114
|
+
- spec/test_run.rb
|