sickle 0.0.1 → 0.1.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 +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
|