duty 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5b04eb166b84df79f038e4cd1f03181ff7294049
4
+ data.tar.gz: 8cec611a0d0fcbf8ae02de1ea3ed798633fed7e7
5
+ SHA512:
6
+ metadata.gz: 1fd3ce618dcc3c2e631715efcb138558345e7b7b6481e9655443f14c668b4de115f66983239e3cb769cd2ff73c9cec63f543c68e0c2c326a10505af9bf6ecff4
7
+ data.tar.gz: 4b7e91969a5d696d976c3d7ea0739fdbec15e16fef9c89a74f62f7e215a4a468e466419c7f58943406ccd9ba06a280f9fe7139420afc1cb1d392851af52be0d5
data/.duty.yml.sample ADDED
@@ -0,0 +1 @@
1
+ tasks: /path/to/my/project/specific/tasks
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ .duty.yml
3
+ pkg
data/.gitmodules ADDED
File without changes
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ duty
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.3
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Jan Owiesniak
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,104 @@
1
+ [![Build Status](https://travis-ci.org/JanOwiesniak/duty.svg?branch=master)](https://travis-ci.org/JanOwiesniak/duty) [![Inline docs](http://inch-ci.org/github/JanOwiesniak/duty.svg?branch=master)](http://inch-ci.org/github/JanOwiesniak/duty)
2
+
3
+ # Duty
4
+
5
+ Craft.
6
+ Don't battle.
7
+ Do your duty, let me handle the rest.
8
+
9
+ ## Installation
10
+
11
+ ```
12
+ $ gem install duty
13
+ ```
14
+
15
+ ## Add executable to your $PATH
16
+
17
+ ```
18
+ $ export PATH="$PATH:$HOME/path/to/duty/bin"
19
+ ```
20
+ ```
21
+
22
+ ## Add shell completion
23
+
24
+ This gem supports a simple shell completion for
25
+ [Bash](https://www.gnu.org/software/bash/) and [ZSH](http://www.zsh.org).
26
+ To enable this feature load the completion functions:
27
+
28
+ ```
29
+ source duty.completion
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ```
35
+ $ duty <task> [<args>]
36
+ ```
37
+
38
+ ## Naming conventions
39
+
40
+ Task names should be a combination of one verb joined with one or more nouns.
41
+
42
+ Examples:
43
+
44
+ * `start-feature`
45
+ * `continue-feature`
46
+
47
+ ## List of official duty plugins
48
+
49
+ * [duty-git](https://github.com/JanOwiesniak/duty-git)
50
+
51
+ ## Extend duty with your own tasks
52
+
53
+ * Create a new `tasks` dir
54
+ * Create one or more duty task files in there
55
+ * Create a .duty file e.g. in your home dir
56
+
57
+ ### How does a basic duty task looks like?
58
+
59
+ path/to/your/new/tasks/my_new_task.rb
60
+
61
+ ```ruby
62
+ require 'duty/tasks/base'
63
+
64
+ module Duty
65
+ module Tasks
66
+ class MyNewTask < Duty::Tasks::Base
67
+ end
68
+ end
69
+ end
70
+ ```
71
+
72
+ ### How does a .duty file looks like?
73
+
74
+ .duty
75
+
76
+ ```
77
+ tasks:
78
+ git: /path/to/my/git/specific/tasks
79
+ projectA: /path/to/my/projectA/specific/tasks
80
+ projectB: /path/to/my/projectB/specific/tasks
81
+ ```
82
+
83
+ ### How to use my own task?
84
+
85
+ Your new task will be immediately available from the CLI.
86
+
87
+ ```
88
+ duty
89
+ ```
90
+
91
+ Fire up the CLI and execute your new task.
92
+ Duty will tell you what you have to do next.
93
+
94
+ ```
95
+ duty <your-task>
96
+ ```
97
+
98
+ ## Contributing
99
+
100
+ 1. [Fork](http://github.com/JanOwiesniak/duty/fork)
101
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
102
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
103
+ 4. Push to the branch (`git push origin my-new-feature`)
104
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'spec'
5
+ t.test_files = FileList['spec/*_spec.rb']
6
+ t.verbose = true
7
+ end
8
+
9
+ task :default => :test
data/bin/duty ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Finds the path to the 'lib' directory of Duty. Follows symlinks, which
4
+ # comes in handy.
5
+ def find_lib_path
6
+ path = __FILE__
7
+ while File.symlink?(path)
8
+ path = File.expand_path(File.readlink(path), File.dirname(path))
9
+ end
10
+ File.join(File.dirname(File.expand_path(path)), '..', 'lib')
11
+ end
12
+
13
+ $LOAD_PATH.unshift(find_lib_path)
14
+
15
+ require 'duty'
16
+
17
+ Duty::CLI.new(ARGV).exec
@@ -0,0 +1,18 @@
1
+ if [ -n "$BASH_VERSION" ]; then
2
+ _duty_complete() {
3
+ COMPREPLY=()
4
+ local word=${COMP_WORDS[COMP_CWORD]}
5
+ local completions=$(duty --cmplt "${word}")
6
+ COMPREPLY=( $completions )
7
+ }
8
+ complete -F _duty_complete duty
9
+ elif [ -n "$ZSH_VERSION" ]; then
10
+ _duty_complete() {
11
+ local word completions
12
+ word="$1"
13
+ completions="$(duty --cmplt "${word}")"
14
+ reply=( "${(ps:\n:)completions}" )
15
+ }
16
+
17
+ compctl -K _duty_complete duty
18
+ fi
data/duty.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "duty/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "duty"
7
+ spec.version = Duty::VERSION
8
+ spec.authors = ["Jan Owiesniak"]
9
+ spec.email = ["owiesniak@mailbox.org"]
10
+
11
+ spec.summary = "Extendable Task Manager"
12
+ spec.description = "Duty provides a CLI for high-level tasks"
13
+ spec.homepage = "https://github.com/JanOwiesniak/duty"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+
20
+ spec.executables = spec.files.grep(%r{^bin\/}) { |f| File.basename(f) }
21
+
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "minitest", "~> 5.8"
26
+ end
data/lib/duty/cli.rb ADDED
@@ -0,0 +1,215 @@
1
+ require 'duty/registry'
2
+ require 'duty/plugins'
3
+ require 'duty/meta'
4
+
5
+ module Duty
6
+ class CLI
7
+ DUTY_CONFIG_FILENAME = '.duty.yml'
8
+ attr_reader :registry
9
+
10
+ def initialize(args)
11
+ @input = Input.new(args)
12
+ @output = Output.new
13
+ @registry = Duty::Registry.load(plugins)
14
+ end
15
+
16
+ def exec
17
+ stdout usage if help?
18
+ stdout completion if completion?
19
+ run_task
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :input, :output
25
+
26
+ def plugins
27
+ Duty::Plugins.load(DUTY_CONFIG_FILENAME)
28
+ end
29
+
30
+ def stdout(string)
31
+ $stdout.puts string
32
+ exit 0
33
+ end
34
+
35
+ def help?
36
+ input.help?
37
+ end
38
+
39
+ def usage
40
+ Duty::Meta::Help.new(self).to_s
41
+ end
42
+
43
+ def completion?
44
+ input.completion?
45
+ end
46
+
47
+ def completion
48
+ Duty::Meta::Completion.new(self, input.drop(1)).to_s
49
+ end
50
+
51
+ def run_task
52
+ begin
53
+ task.run
54
+ rescue NameError => e
55
+ stdout invalid_task(e.message)
56
+ end
57
+ end
58
+
59
+ def task
60
+ input.task_class.new(input.task_input, view)
61
+ end
62
+
63
+ def invalid_task(error_message)
64
+ "duty: `#{input.join(' ')}` is not a duty task. Failed with: #{error_message}"
65
+ end
66
+
67
+ def view
68
+ if verbose?
69
+ VerboseView.new(output)
70
+ else
71
+ View.new(output)
72
+ end
73
+ end
74
+
75
+ def verbose?
76
+ input.verbose?
77
+ end
78
+
79
+ class Input
80
+ def initialize(args)
81
+ @args = [args].flatten
82
+ end
83
+
84
+ def[](index)
85
+ @args[index]
86
+ end
87
+
88
+ def drop(index)
89
+ @args.drop(1)
90
+ end
91
+
92
+ def task_name
93
+ task, *rest = @args
94
+ task
95
+ end
96
+
97
+ def task_class
98
+ name = task_name.split('-').collect(&:capitalize).join
99
+ Object.const_get("Duty::Tasks::#{name}")
100
+ end
101
+
102
+ def task_input
103
+ task, *rest = @args
104
+ rest
105
+ end
106
+
107
+ def join(seperator='')
108
+ @args.join(seperator)
109
+ end
110
+
111
+ def verbose?
112
+ @args.include?('-v') || @args.include?('--verbose')
113
+ end
114
+
115
+ def completion?
116
+ @args.first == '--cmplt'
117
+ end
118
+
119
+ def help?
120
+ @args.empty? || @args == %w(-h) || @args == %w(--help)
121
+ end
122
+ end
123
+
124
+ class Output
125
+ def print(*args)
126
+ $stdout.puts(*args)
127
+ end
128
+
129
+ def error(*args)
130
+ $stderr.puts(*args)
131
+ end
132
+ end
133
+
134
+ class View
135
+ def initialize(output)
136
+ @output = output
137
+ end
138
+
139
+ def task_explain(task)
140
+ task_class = task.class
141
+ description = task_class.description
142
+ usage = task_class.usage
143
+
144
+ @output.print(description)
145
+ @output.print(usage)
146
+ end
147
+
148
+ def task_success(task)
149
+ task_name = task.class.name
150
+ success("#{task_name} task executed")
151
+ end
152
+
153
+ def task_failure(task)
154
+ task_name = task.class.name
155
+ failure("#{task_name} task aborted")
156
+ end
157
+
158
+ def command_success(command)
159
+ description = command.description
160
+ success(description)
161
+ end
162
+
163
+ def command_failure(command)
164
+ description = command.description
165
+ failure(description)
166
+ end
167
+
168
+ private
169
+
170
+ def success(msg)
171
+ @output.print([check_mark, msg].join(' '))
172
+ end
173
+
174
+ def failure(msg)
175
+ @output.error([cross_mark, msg].join(' '))
176
+ end
177
+
178
+ def cross_mark
179
+ unicode("2715")
180
+ end
181
+
182
+ def check_mark
183
+ unicode("2713")
184
+ end
185
+
186
+ def unicode(code)
187
+ ["0x#{code}".hex].pack('U')
188
+ end
189
+ end
190
+
191
+ class VerboseView < View
192
+ def command_success(command)
193
+ success(command_msg(command))
194
+ end
195
+
196
+ def command_failure(command)
197
+ failure(command_msg(command))
198
+ end
199
+
200
+ private
201
+
202
+ def command_msg(command)
203
+ [command.description, command_logs(command)].join(' ')
204
+ end
205
+
206
+ def command_logs(command)
207
+ elements = command.logger.flatten
208
+
209
+ if elements.any?
210
+ ["|>", elements.join(' | ')].join(' ')
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,41 @@
1
+ module Duty
2
+ module Meta
3
+ class Completion
4
+ def initialize(cli, args)
5
+ @registry = cli.registry
6
+ @humanizer = Humanizer.new
7
+ @input = args.join(" ").downcase
8
+ end
9
+
10
+ def to_s
11
+ possible_completions
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :registry, :humanizer
17
+
18
+ def possible_completions
19
+ matching_tasks.join("\n")
20
+ end
21
+
22
+ def matching_tasks
23
+ humanized_tasks.select do |cmd|
24
+ cmd.start_with?(@input)
25
+ end
26
+ end
27
+
28
+ def humanized_tasks
29
+ registry.plugins.map do |plugin|
30
+ tasks_for(plugin)
31
+ end.flatten
32
+ end
33
+
34
+ def tasks_for(plugin)
35
+ plugin.tasks.map do |task_class|
36
+ humanizer.task(task_class)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,44 @@
1
+ module Duty
2
+ module Meta
3
+ class Help
4
+ def initialize(cli)
5
+ @registry = cli.registry
6
+ @humanizer = Humanizer.new
7
+ end
8
+
9
+ def to_s
10
+ usage
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :registry, :humanizer
16
+
17
+ def usage
18
+ msg = <<-EOF
19
+ Usage: duty <task> [<args>]
20
+
21
+ Tasks:
22
+
23
+ #{plugins}
24
+ EOF
25
+ end
26
+
27
+ def plugins
28
+ registry.plugins.map do |plugin|
29
+ [namespace_for(plugin), tasks_for(plugin)].join("\n")
30
+ end.join("\n")
31
+ end
32
+
33
+ def namespace_for(plugin)
34
+ "[" + plugin.namespace + "]"
35
+ end
36
+
37
+ def tasks_for(plugin)
38
+ plugin.tasks.map do |task_class|
39
+ [" • ", humanizer.task(task_class).ljust(20), task_class.description].join
40
+ end.join("\n")
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,19 @@
1
+ require 'duty/registry'
2
+
3
+ module Duty
4
+ module Meta
5
+ class Humanizer
6
+ def task(klass)
7
+ klass.to_s.
8
+ gsub(/([A-Z])/, '-\1').
9
+ split('-').
10
+ reject(&:empty?).
11
+ map(&:downcase).
12
+ join('-').
13
+ split('::').
14
+ last.
15
+ gsub(/^-/,'')
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/duty/meta.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'duty/meta/completion'
2
+ require 'duty/meta/help'
3
+ require 'duty/meta/humanizer'
@@ -0,0 +1,50 @@
1
+ require 'duty/tasks'
2
+ require 'yaml'
3
+
4
+ module Duty
5
+ module Plugins
6
+ def self.load(filename)
7
+ if File.exists?(filename)
8
+ duty_config = YAML.load(File.read(filename))
9
+ tasks = duty_config["tasks"]
10
+ tasks.map do |namespace, task_dir|
11
+ Plugin.new(namespace, task_dir) if Dir.exists?(task_dir)
12
+ end
13
+ end
14
+ end
15
+
16
+ class Plugin
17
+ TASK_NAMESPACE = Duty::Tasks
18
+ def initialize(namespace, task_dir)
19
+ @namespace = namespace
20
+ @task_dir = task_dir
21
+ end
22
+
23
+ def namespace
24
+ @namespace
25
+ end
26
+
27
+ def require_tasks
28
+ task_files = File.expand_path(File.join(@task_dir, "*.rb"))
29
+ Dir[task_files].each do |path|
30
+ require path.gsub(/(\.rb)$/, '')
31
+ end
32
+ end
33
+
34
+ def tasks
35
+ task_names = TASK_NAMESPACE.constants - [:Base]
36
+ task_names.reduce([]) do |task_classes, task_name|
37
+ task_class = TASK_NAMESPACE.const_get(task_name)
38
+ task_classes << task_class if valid?(task_class)
39
+ task_classes
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def valid?(task_class)
46
+ task_class.superclass == TASK_NAMESPACE::Base
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,16 @@
1
+ module Duty
2
+ class Registry
3
+ def self.load(plugins = [])
4
+ new(plugins).tap {|registry| registry.require_tasks }
5
+ end
6
+
7
+ attr_reader :plugins
8
+ def initialize(plugins)
9
+ @plugins = plugins
10
+ end
11
+
12
+ def require_tasks
13
+ plugins.each {|plugin| plugin.require_tasks }
14
+ end
15
+ end
16
+ end
data/lib/duty/tasks.rb ADDED
@@ -0,0 +1,205 @@
1
+ module Duty
2
+ module Tasks
3
+ class Base
4
+ ExecutionError = Class.new(RuntimeError)
5
+
6
+ def initialize(arguments, view)
7
+ @arguments = arguments
8
+ @view = view
9
+ @parallel = []
10
+ end
11
+
12
+ def run
13
+ if !valid?
14
+ task_explain
15
+ else
16
+ begin
17
+ execute_in_thread
18
+ task_success
19
+ rescue ExecutionError
20
+ task_failure
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.name
26
+ self.to_s.split('::').last
27
+ end
28
+
29
+ def self.description
30
+ "TODO: Describe your task by overwriting `self.description`"
31
+ end
32
+
33
+ def self.usage
34
+ "TODO: Explain your task by overwriting `self.usage`"
35
+ end
36
+
37
+ def execute
38
+ ruby(Proc.new{},'Describe your task')
39
+ ruby(Proc.new{},'Explain your task')
40
+ ruby(Proc.new{ raise ExecutionError.new },'TODO: Implement your task by overwriting `execute`')
41
+ end
42
+
43
+ private
44
+
45
+ def execute_in_thread
46
+ Thread.new do
47
+ execute
48
+ @parallel.each {|thread| thread.join }
49
+ end.join
50
+ end
51
+
52
+ def valid?
53
+ !todo?(self.class.description) && !todo?(self.class.usage)
54
+ end
55
+
56
+ def todo?(string)
57
+ string.match(/TODO/)
58
+ end
59
+
60
+ def task_explain
61
+ @view.task_explain(self)
62
+ end
63
+
64
+ def task_success
65
+ @view.task_success(self)
66
+ end
67
+
68
+ def task_failure
69
+ @view.task_failure(self)
70
+ end
71
+
72
+ def command_success(command)
73
+ @view.command_success(command)
74
+ end
75
+
76
+ def command_failure(command)
77
+ @view.command_failure(command)
78
+ end
79
+
80
+ def parallel(&blk)
81
+ @parallel << Thread.new(&blk)
82
+ end
83
+
84
+ def ruby(desc = 'Unknown ruby command', &blk)
85
+ handle_errors(Ruby.run(desc, blk))
86
+ end
87
+
88
+ def sh(desc = 'Unknown shell command', &blk)
89
+ handle_errors(Shell.run(desc, blk))
90
+ end
91
+
92
+ def handle_errors(command)
93
+ if command.success?
94
+ command_success(command)
95
+ else
96
+ command_failure(command)
97
+ raise ExecutionError.new
98
+ end
99
+ end
100
+
101
+ class Command
102
+ def initialize(description, callable)
103
+ @description = description
104
+ @callable = callable
105
+ @success = false
106
+ @error = nil
107
+ @logger = []
108
+ end
109
+
110
+ def self.run(description, callable)
111
+ new(description, callable).tap {|command| command.execute}
112
+ end
113
+
114
+ def success?
115
+ @success
116
+ end
117
+
118
+ def description
119
+ @description
120
+ end
121
+
122
+ def logger
123
+ @logger
124
+ end
125
+
126
+ def error
127
+ @error
128
+ end
129
+ end
130
+
131
+ class Ruby < Command
132
+ def execute
133
+ begin
134
+ @callable.call
135
+ @success = true
136
+ rescue Exception => e
137
+ @error = "ERROR: #{e.inspect}"
138
+ ensure
139
+ @logger << summary
140
+ @logger << error
141
+ end
142
+ end
143
+
144
+ private
145
+
146
+ def summary
147
+ "[RUBY] #{@callable.inspect}"
148
+ end
149
+ end
150
+
151
+ class Shell < Command
152
+ require 'open3'
153
+ def execute
154
+ if !cmd
155
+ @success = true
156
+ return
157
+ end
158
+
159
+ begin
160
+ @stdout, @stderr, @status = Open3.capture3(cmd)
161
+ @success = true if status.success?
162
+ rescue Exception => e
163
+ @error = "ERROR: #{e.inspect}"
164
+ ensure
165
+ @logger << summary
166
+ @logger << error
167
+ end
168
+ end
169
+
170
+ private
171
+
172
+ def summary
173
+ [
174
+ "[SHELL]",
175
+ "COMMAND: #{cmd}",
176
+ "DIR: #{dir}",
177
+ "STDOUT: #{stdout}",
178
+ "STDERR: #{stderr}",
179
+ "STATUS: #{status}"
180
+ ]
181
+ end
182
+
183
+ def stdout
184
+ @stdout
185
+ end
186
+
187
+ def stderr
188
+ @stderr
189
+ end
190
+
191
+ def status
192
+ @status
193
+ end
194
+
195
+ def cmd
196
+ @cmd ||= @callable.call
197
+ end
198
+
199
+ def dir
200
+ Dir.pwd
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,3 @@
1
+ module Duty
2
+ VERSION = "0.1.0"
3
+ end
data/lib/duty.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'duty/cli'
2
+ require 'duty/tasks'
3
+ require 'duty/version'
4
+
5
+ module Duty
6
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: duty
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jan Owiesniak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.8'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.8'
41
+ description: Duty provides a CLI for high-level tasks
42
+ email:
43
+ - owiesniak@mailbox.org
44
+ executables:
45
+ - duty
46
+ - duty.completion
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".duty.yml.sample"
51
+ - ".gitignore"
52
+ - ".gitmodules"
53
+ - ".ruby-gemset"
54
+ - ".ruby-version"
55
+ - ".travis.yml"
56
+ - Gemfile
57
+ - LICENSE
58
+ - README.md
59
+ - Rakefile
60
+ - bin/duty
61
+ - bin/duty.completion
62
+ - duty.gemspec
63
+ - lib/duty.rb
64
+ - lib/duty/cli.rb
65
+ - lib/duty/meta.rb
66
+ - lib/duty/meta/completion.rb
67
+ - lib/duty/meta/help.rb
68
+ - lib/duty/meta/humanizer.rb
69
+ - lib/duty/plugins.rb
70
+ - lib/duty/registry.rb
71
+ - lib/duty/tasks.rb
72
+ - lib/duty/version.rb
73
+ homepage: https://github.com/JanOwiesniak/duty
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.4.8
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Extendable Task Manager
97
+ test_files: []