taskmapper 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.travis.yml +4 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +52 -0
- data/LICENSE +20 -0
- data/NOTES +18 -0
- data/README.md +296 -0
- data/Rakefile +40 -0
- data/TODO +1 -0
- data/VERSION +1 -0
- data/bin/tm +7 -0
- data/examples/tm_example.rb +11 -0
- data/examples/tm_example_2.rb +11 -0
- data/examples/tm_example_3.rb +21 -0
- data/examples/tm_example_4.rb +17 -0
- data/lib/taskmapper.rb +53 -0
- data/lib/taskmapper/authenticator.rb +2 -0
- data/lib/taskmapper/cli/commands/config.rb +100 -0
- data/lib/taskmapper/cli/commands/console.rb +35 -0
- data/lib/taskmapper/cli/commands/generate.rb +112 -0
- data/lib/taskmapper/cli/commands/generate/provider.rb +5 -0
- data/lib/taskmapper/cli/commands/generate/provider/comment.rb +14 -0
- data/lib/taskmapper/cli/commands/generate/provider/project.rb +26 -0
- data/lib/taskmapper/cli/commands/generate/provider/provider.rb +25 -0
- data/lib/taskmapper/cli/commands/generate/provider/ticket.rb +12 -0
- data/lib/taskmapper/cli/commands/help.rb +9 -0
- data/lib/taskmapper/cli/commands/help/config +27 -0
- data/lib/taskmapper/cli/commands/help/console +13 -0
- data/lib/taskmapper/cli/commands/help/generate +19 -0
- data/lib/taskmapper/cli/commands/help/help +7 -0
- data/lib/taskmapper/cli/commands/help/project +13 -0
- data/lib/taskmapper/cli/commands/help/ticket +14 -0
- data/lib/taskmapper/cli/commands/project.rb +140 -0
- data/lib/taskmapper/cli/commands/ticket.rb +145 -0
- data/lib/taskmapper/cli/common.rb +28 -0
- data/lib/taskmapper/cli/init.rb +77 -0
- data/lib/taskmapper/comment.rb +97 -0
- data/lib/taskmapper/common.rb +81 -0
- data/lib/taskmapper/dummy/comment.rb +27 -0
- data/lib/taskmapper/dummy/dummy.rb +28 -0
- data/lib/taskmapper/dummy/project.rb +42 -0
- data/lib/taskmapper/dummy/ticket.rb +43 -0
- data/lib/taskmapper/exception.rb +2 -0
- data/lib/taskmapper/helper.rb +72 -0
- data/lib/taskmapper/project.rb +145 -0
- data/lib/taskmapper/provider.rb +82 -0
- data/lib/taskmapper/tester/comment.rb +18 -0
- data/lib/taskmapper/tester/project.rb +19 -0
- data/lib/taskmapper/tester/tester.rb +28 -0
- data/lib/taskmapper/tester/ticket.rb +19 -0
- data/lib/taskmapper/ticket.rb +154 -0
- data/spec/project_spec.rb +84 -0
- data/spec/rcov.opts +1 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/taskmapper-cli_spec.rb +60 -0
- data/spec/taskmapper-exception_spec.rb +160 -0
- data/spec/taskmapper_spec.rb +13 -0
- data/spec/ticket_spec.rb +56 -0
- data/taskmapper.gemspec +118 -0
- metadata +189 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
module TaskMapper::Provider
|
2
|
+
module Yoursystem
|
3
|
+
# Ticket class for taskmapper-yoursystem
|
4
|
+
#
|
5
|
+
|
6
|
+
class Ticket < TaskMapper::Provider::Base::Ticket
|
7
|
+
#API = Yoursystem::Ticket # The class to access the api's tickets
|
8
|
+
# declare needed overloaded methods here
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# The help command.
|
2
|
+
def help(options)
|
3
|
+
cmd = ARGV.shift || 'help'
|
4
|
+
page = File.dirname(__FILE__) + '/help/' + cmd
|
5
|
+
if File.exist?(page)
|
6
|
+
puts File.read(page)
|
7
|
+
puts "\nFor parameter listing and details, try executing the command with --help.\n\ttm #{cmd} --help"
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
This command is used to configure taskmapper settings for use in your terminal.
|
2
|
+
|
3
|
+
By default, taskmapper searches for the config information in a file named 'taskmapper.yml' in the current directory. Next it searches for 'config/taskmapper.yml' and if that fails, it uses the home directory's ~/.taskmapper.yml (note the . in front on this one). There is also the TASKMAPPER environment variable that you can set to point to another configuration file anywhere if necessary.
|
4
|
+
|
5
|
+
This command helps facilitate the creation of a taskmapper.yml.
|
6
|
+
|
7
|
+
Warning: Due to the way the authentication is parsed, if any keys or values contain a comma (,) or colin (:), it can not be parsed through the -A command. Usually this isn't a problem, and if it is, it can be resolved though putting the values in a config file.
|
8
|
+
|
9
|
+
Example:
|
10
|
+
tm -c ~/.taskmapper.yml -p dummy -A username:cheese,password:cakes -P 555 config --add
|
11
|
+
tm -p dummy config --set-default-provider
|
12
|
+
|
13
|
+
The format for taskmapper.yml is:
|
14
|
+
default: <default provider>
|
15
|
+
<provider name>:
|
16
|
+
authentication:
|
17
|
+
<authentication information>
|
18
|
+
project: <project>
|
19
|
+
[...]
|
20
|
+
|
21
|
+
For example, a taskmapper.yml with the Dummy provider would look something like this:
|
22
|
+
default: dummy
|
23
|
+
dummy:
|
24
|
+
authentication:
|
25
|
+
username: name
|
26
|
+
password: doesnt-matter
|
27
|
+
project: 555
|
@@ -0,0 +1,13 @@
|
|
1
|
+
This command is used to open an irb session with the taskmapper.
|
2
|
+
|
3
|
+
The configuration for this command should mostly be dependent on the taskmapper.yml files.
|
4
|
+
See taskmapper help config for more information on how to set it up.
|
5
|
+
|
6
|
+
Example:
|
7
|
+
tm console
|
8
|
+
tm -p dummy console
|
9
|
+
tm -p dummy console -d --tracer
|
10
|
+
|
11
|
+
By default, it attempts to load all providers listed in the config unless a provider is explicitly given.
|
12
|
+
|
13
|
+
All options passed to console is passed to the irb session. See irb --help for those options and their details
|
@@ -0,0 +1,19 @@
|
|
1
|
+
This command is used to generate a new provider.
|
2
|
+
|
3
|
+
It generates some basic files to get you started on creating your own provider.
|
4
|
+
|
5
|
+
NOTE: This command, in an attempt to keep provider names consistent, will prepend 'taskmapper-' to the given provider name. You can cancel this by prepending a _ before your provider name, which will be removed if found.
|
6
|
+
|
7
|
+
If you have not created a gem directory or skeleton and want to use Jeweler (http://github.com/technicalpickles/jeweler) you can execute:
|
8
|
+
|
9
|
+
$ tm generate myprovider --jeweler [JEWELER ARGS]
|
10
|
+
|
11
|
+
And it will create your whole directory and skeleton using jeweler.
|
12
|
+
|
13
|
+
If you like the old "classic" gem creation process or are using some other gem processor, you can create the directory to store your files, cd into it and run this command to put the skeleton files inside the directory's lib/.
|
14
|
+
|
15
|
+
Example:
|
16
|
+
mkdir myprovider myprovider/lib
|
17
|
+
cd myprovider
|
18
|
+
[...]
|
19
|
+
tm generate myprovider
|
@@ -0,0 +1,13 @@
|
|
1
|
+
This command is used to work with projects.
|
2
|
+
|
3
|
+
It can be used to do any of the CRUD actions with projects that are provided by the provider.
|
4
|
+
|
5
|
+
It will attempt to load data through the config files if they are available. See 'tm help config' for more information.
|
6
|
+
|
7
|
+
Examples:
|
8
|
+
tm -p lighthouse -A account:taskmapper,token:abc project --create name "new project"
|
9
|
+
tm project --read 946
|
10
|
+
tm --project 946 project --read
|
11
|
+
tm -p dummy -A "user:common coder,pass:w3rd out" project --destroy 712
|
12
|
+
tm -p dummy -P 712 project --destroy
|
13
|
+
tm -p dummy project --update name "new project name" description "this is the project description"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
This command is used to work with tickets.
|
2
|
+
|
3
|
+
It can be used to do any of the CRUD actions with projects that are provided by the provider.
|
4
|
+
|
5
|
+
It will attempt to load data through the config files if they are available. See 'tm help config' for more information.
|
6
|
+
|
7
|
+
If any of the keys or values contain a space, you will have to enclose that key or value in quotes. For example, if you set name to ProjectName it does not need to be quoted, but if you set name to Project Name it will have to be quoted to "Project Name" or 'Project Name'
|
8
|
+
|
9
|
+
Examples:
|
10
|
+
tm -p lighthouse -A account:taskmapper,token:abc ticket --create name "new ticket" description "this is a new ticket"
|
11
|
+
tm --project 946 ticket --read --ticket 2
|
12
|
+
tm -p dummy -A "user:common coder,pass: w3rd out" ticket --destroy --ticket 12
|
13
|
+
tm -p dummy -P 712 ticket --destroy --ticket 4
|
14
|
+
tm -p dummy project --ticket 6 --update attribute value name "free dummies"
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
# The command method call for project
|
3
|
+
# This sets the option parser and passes the parsed options to the subcommands
|
4
|
+
def project(options)
|
5
|
+
ARGV << '--help' if ARGV.length == 0
|
6
|
+
begin
|
7
|
+
OptionParser.new do |opts|
|
8
|
+
opts.banner = 'Usage: tm -p PROVIDER [options] project [project_options]'
|
9
|
+
opts.separator ''
|
10
|
+
opts.separator 'Options:'
|
11
|
+
|
12
|
+
opts.on('-C', '--create ATTRIBUTES', 'Create a new project') do |attribute|
|
13
|
+
options[:project_attributes] = {attribute => ARGV.shift}.merge(attributes_hash(ARGV))
|
14
|
+
options[:subcommand] = 'create'
|
15
|
+
end
|
16
|
+
|
17
|
+
opts.on('-R', '--read [PROJECT]', 'Read out project and its attributes') do |id|
|
18
|
+
options[:project] = id if id
|
19
|
+
options[:subcommand] = 'read'
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on('-U', '--update ATTRIBUTES', 'Update project information') do |attribute|
|
23
|
+
options[:project_attributes] = {attribute => ARGV.shift}.merge(attributes_hash(ARGV))
|
24
|
+
options[:subcommand] = 'update'
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('-D', '--destroy [PROJECT]', 'Destroy the project. Not reversible!') do |id|
|
28
|
+
options[:project] = id if id
|
29
|
+
options[:subcommand] = 'destroy'
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('-I', '--info [PROJECT_ID]', 'Get project info. Same as --read. ') do |id|
|
33
|
+
options[:project] = id if id
|
34
|
+
options[:subcommand] = 'read'
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on('-S', '--search [ATTRIBUTES]', 'Search for a project based on attributes') do |attribute|
|
38
|
+
options[:project_attributes] = attribute ? {attribute => ARGV.shift}.merge(attributes_hash(ARGV)) : {}
|
39
|
+
options[:subcommand] = 'search'
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('-L', '--list-all', 'List all projects. Same as --search without any parameters') do
|
43
|
+
options[:project_attributes] = {}
|
44
|
+
options[:subcommand] = 'search'
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on('-P', '--project [PROJECT_ID]', 'Set the project id') do |id|
|
48
|
+
options[:project] = id
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.separator ''
|
52
|
+
opts.separator 'Other options:'
|
53
|
+
|
54
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
55
|
+
puts opts
|
56
|
+
exit
|
57
|
+
end
|
58
|
+
end.order!
|
59
|
+
rescue OptionParser::MissingArgument => exception
|
60
|
+
puts "tm #{options[:original_argv].join(' ')}\n\n"
|
61
|
+
puts "Error: An option was called that requires an argument, but was not given one"
|
62
|
+
puts exception.message
|
63
|
+
end
|
64
|
+
parse_config!(options)
|
65
|
+
begin
|
66
|
+
require 'taskmapper'
|
67
|
+
require "taskmapper-#{options[:provider]}"
|
68
|
+
rescue
|
69
|
+
require options[:provider]
|
70
|
+
end
|
71
|
+
send(options[:subcommand], options)
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# The create subcommand
|
76
|
+
def create(options)
|
77
|
+
tm = TaskMapper.new(options[:provider], options[:authentication])
|
78
|
+
project = tm.project.create(options[:project_attributes])
|
79
|
+
read_project project
|
80
|
+
exit
|
81
|
+
end
|
82
|
+
|
83
|
+
# The read subcommand
|
84
|
+
def read(options)
|
85
|
+
tm = TaskMapper.new(options[:provider], options[:authentication])
|
86
|
+
project = tm.project.find(options[:project])
|
87
|
+
read_project project
|
88
|
+
exit
|
89
|
+
end
|
90
|
+
|
91
|
+
# The update subcommand
|
92
|
+
def update(options)
|
93
|
+
tm = TaskMapper.new(options[:provider], options[:authentication])
|
94
|
+
project = tm.project.find(options[:project])
|
95
|
+
if project.update!(options[:project_attributes])
|
96
|
+
puts "Successfully updated Project #{project.name} (#{project.id})"
|
97
|
+
else
|
98
|
+
puts "Sorry, it seems there was an error when trying to update the attributes"
|
99
|
+
end
|
100
|
+
read_project project
|
101
|
+
exit
|
102
|
+
end
|
103
|
+
|
104
|
+
# The destroy subcommand.
|
105
|
+
def destroy(options)
|
106
|
+
tm = TaskMapper.new(options[:provider], options[:authentication])
|
107
|
+
project = tm.project.find(options[:project])
|
108
|
+
puts "Are you sure you want to delete Project #{project.name} (#{project.id})? (yes/no) [no]"
|
109
|
+
ARGV.clear
|
110
|
+
confirm = readline.chomp.downcase
|
111
|
+
if confirm != 'y' and confirm != 'yes'
|
112
|
+
puts "Did not receive a 'yes' confirmation. Exiting..."
|
113
|
+
exit
|
114
|
+
elsif project.destroy
|
115
|
+
puts "Successfully deleted Project #{project.name} (#{project.id})"
|
116
|
+
else
|
117
|
+
puts "Sorry, it seems there was an error when trying to delete the project"
|
118
|
+
end
|
119
|
+
exit
|
120
|
+
end
|
121
|
+
|
122
|
+
# The search and list subcommands
|
123
|
+
def search(options)
|
124
|
+
tm = TaskMapper.new(options[:provider], options[:authentication])
|
125
|
+
projects = tm.projects(options[:project_attributes])
|
126
|
+
puts "Found #{projects.length} projects"
|
127
|
+
projects.each_with_index do |project, index|
|
128
|
+
puts "#{index+1}) Project #{project.name} (#{project.id})"
|
129
|
+
#read_project project
|
130
|
+
#puts
|
131
|
+
end
|
132
|
+
exit
|
133
|
+
end
|
134
|
+
|
135
|
+
# A utility method used to output project attributes
|
136
|
+
def read_project(project)
|
137
|
+
project.system_data[:client].attributes.sort.each do |key, value|
|
138
|
+
puts "#{key} : #{value}"
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
# The command method call for project
|
3
|
+
# This sets the option parser and passes the parsed options to the subcommands
|
4
|
+
def ticket(options)
|
5
|
+
ARGV << '--help' if ARGV.length == 0
|
6
|
+
begin
|
7
|
+
OptionParser.new do |opts|
|
8
|
+
opts.banner = 'Usage: tm -p PROVIDER -P PROJECT [options] ticket [ticket_options]'
|
9
|
+
opts.separator ''
|
10
|
+
opts.separator 'Options:'
|
11
|
+
|
12
|
+
opts.on('-T', '--ticket TICKET', 'Sets the working ticket') do |id|
|
13
|
+
options[:ticket] = id
|
14
|
+
end
|
15
|
+
opts.on('-C', '--create ATTRIBUTES', 'Create a new ticket') do |attribute|
|
16
|
+
options[:ticket_attributes] = {attribute => ARGV.shift}.merge(attributes_hash(ARGV))
|
17
|
+
options[:subcommand] = 'create'
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on('-R', '--read [TICKET]', 'Read out ticket and its attributes. Requires --ticket to be set') do |id|
|
21
|
+
options[:ticket] = id if id
|
22
|
+
options[:subcommand] = 'read'
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on('-U', '--update ATTRIBUTES', 'Update ticket information. Requires --ticket to be set') do |attribute|
|
26
|
+
options[:ticket_attributes] = {attribute => ARGV.shift}.merge(attributes_hash(ARGV))
|
27
|
+
options[:subcommand] = 'update'
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on('-D', '--destroy', 'Destroy/Delete the ticket. Not reversible! Requires --ticket to be set') do
|
31
|
+
options[:subcommand] = 'destroy'
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on('-I', '--info', 'Get ticket info. Same as --read. ') do
|
35
|
+
options[:subcommand] = 'read'
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on('-S', '--search [ATTRIBUTES]', 'Search for a ticket based on attributes') do |attribute|
|
39
|
+
options[:ticket_attributes] = attribute ? {attribute => ARGV.shift}.merge(attributes_hash(ARGV)) : {}
|
40
|
+
options[:subcommand] = 'search'
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on('-L', '--list-all', 'List all tickets. Same as --search without any parameters') do
|
44
|
+
options[:ticket_attributes] = {}
|
45
|
+
options[:subcommand] = 'search'
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on('-P', '--project [PROJECT_ID]', 'Set the project id') do |id|
|
49
|
+
options[:project] = id
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.separator ''
|
53
|
+
opts.separator 'Other options:'
|
54
|
+
|
55
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
56
|
+
puts opts
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
end.order!
|
60
|
+
rescue OptionParser::MissingArgument => exception
|
61
|
+
puts "tm #{options[:original_argv].join(' ')}\n\n"
|
62
|
+
puts "Error: An option was called that requires an argument, but was not given one"
|
63
|
+
puts exception.message
|
64
|
+
end
|
65
|
+
parse_config!(options)
|
66
|
+
begin
|
67
|
+
require 'taskmapper'
|
68
|
+
require "taskmapper-#{options[:provider]}"
|
69
|
+
rescue
|
70
|
+
require options[:provider]
|
71
|
+
end
|
72
|
+
send(options[:subcommand], options)
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# The create subcommand
|
77
|
+
def create(options)
|
78
|
+
tm = TaskMapper.new(options[:provider], options[:authentication])
|
79
|
+
ticket = tm.ticket.create(options[:ticket_attributes].merge({:project_id => options[:project]}))
|
80
|
+
read_ticket ticket
|
81
|
+
exit
|
82
|
+
end
|
83
|
+
|
84
|
+
# The read subcommand
|
85
|
+
def read(options)
|
86
|
+
tm = TaskMapper.new(options[:provider], options[:authentication])
|
87
|
+
project = tm.project(options[:project])
|
88
|
+
ticket = project.ticket(options[:ticket])
|
89
|
+
read_ticket ticket
|
90
|
+
exit
|
91
|
+
end
|
92
|
+
|
93
|
+
# The update subcommand
|
94
|
+
def update(options)
|
95
|
+
tm = TaskMapper.new(options[:provider], options[:authentication])
|
96
|
+
project = tm.project(options[:project])
|
97
|
+
ticket = project.ticket(options[:ticket])
|
98
|
+
if ticket.update!(options[:ticket_attributes])
|
99
|
+
puts "Successfully updated Ticket #{ticket.title} (#{ticket.id})"
|
100
|
+
else
|
101
|
+
puts "Sorry, it seems there was an error when trying to update the attributes"
|
102
|
+
end
|
103
|
+
read_ticket ticket
|
104
|
+
exit
|
105
|
+
end
|
106
|
+
|
107
|
+
# The destroy subcommand.
|
108
|
+
def destroy(options)
|
109
|
+
tm = TaskMapper.new(options[:provider], options[:authentication])
|
110
|
+
project = tm.project(options[:project])
|
111
|
+
ticket = project.ticket(options[:ticket])
|
112
|
+
puts "Are you sure you want to delete Ticket #{ticket.title} (#{ticket.id})? (yes/no) [no]"
|
113
|
+
ARGV.clear
|
114
|
+
confirm = readline.chomp.downcase
|
115
|
+
if confirm != 'y' and confirm != 'yes'
|
116
|
+
puts "Did not receive a 'yes' confirmation. Exiting..."
|
117
|
+
exit
|
118
|
+
elsif ticket.destroy
|
119
|
+
puts "Successfully deleted Ticket #{ticket.title} (#{ticket.id})"
|
120
|
+
else
|
121
|
+
puts "Sorry, it seems there was an error when trying to delete the project"
|
122
|
+
end
|
123
|
+
exit
|
124
|
+
end
|
125
|
+
|
126
|
+
# The search and list subcommands
|
127
|
+
def search(options)
|
128
|
+
tm = TaskMapper.new(options[:provider], options[:authentication])
|
129
|
+
project = tm.project(options[:project])
|
130
|
+
tickets = project.tickets(options[:ticket_attributes])
|
131
|
+
puts "Found #{tickets.length} tickets"
|
132
|
+
tickets.each_with_index do |ticket, index|
|
133
|
+
puts "#{index+1}) Ticket #{ticket.title} (#{ticket.id})"
|
134
|
+
#read_ticket ticket
|
135
|
+
#puts
|
136
|
+
end
|
137
|
+
exit
|
138
|
+
end
|
139
|
+
|
140
|
+
# A utility method used to output project attributes
|
141
|
+
def read_ticket(ticket)
|
142
|
+
ticket.system_data[:client].attributes.sort.each do |key, value|
|
143
|
+
puts "#{key} : #{value}"
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Parses the configuration information and puts it into options
|
2
|
+
def parse_config!(options)
|
3
|
+
config = YAML.load_file File.expand_path(options[:config])
|
4
|
+
provider = (options[:provider] ||= config['default'] || config.keys.first)
|
5
|
+
if provider and provider.length > 0
|
6
|
+
options[:project] ||= config[provider]['project']
|
7
|
+
options[:authentication] ||= config[provider]['authentication']
|
8
|
+
end
|
9
|
+
options
|
10
|
+
end
|
11
|
+
|
12
|
+
# A utility method used to separate name:value pairs
|
13
|
+
def attributes_hash(kvlist)
|
14
|
+
require 'enumerator' if RUBY_VERSION < "1.8.7"
|
15
|
+
if kvlist.is_a?(String)
|
16
|
+
kvlist.split(',').inject({}) do |mem, kv|
|
17
|
+
key, value = kv.split(':')
|
18
|
+
mem[key] = value
|
19
|
+
mem
|
20
|
+
end
|
21
|
+
elsif kvlist.is_a?(Array)
|
22
|
+
mem = {}
|
23
|
+
kvlist.each_slice(2) do |k, v|
|
24
|
+
mem[k] = v
|
25
|
+
end
|
26
|
+
mem
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'yaml'
|
5
|
+
require File.dirname(__FILE__) + '/common.rb'
|
6
|
+
|
7
|
+
|
8
|
+
commands ={ 'help' => 'Get the help text for a particular command',
|
9
|
+
'console' => 'Open up a taskmapper console session',
|
10
|
+
'config' => 'Setup and configure a taskmapper.yml file',
|
11
|
+
'ticket' => 'Work with tickets (create, edit, delete, etc)',
|
12
|
+
'project' => 'Work with projects (create, edit, delete, etc)',
|
13
|
+
'generate' => 'Generate skeleton library files for a new provider',
|
14
|
+
}
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
helptext = lambda {
|
19
|
+
helpmsg = "\nAvailable commands:\n"
|
20
|
+
commands.sort.inject(helpmsg) { |mem, cmd| mem << "\t#{cmd.join(" \t")}\n" }
|
21
|
+
helpmsg << "\nSee 'tm help COMMAND' for more information on a specific command."
|
22
|
+
}
|
23
|
+
|
24
|
+
ARGV << '--help' if ARGV.length == 0
|
25
|
+
|
26
|
+
options = {:original_argv => ARGV.dup}
|
27
|
+
|
28
|
+
if File.exist?(options[:config] = File.expand_path('taskmapper.yml'))
|
29
|
+
elsif File.exist?(options[:config] = File.expand_path('config/taskmapper.yml'))
|
30
|
+
elsif ENV['TASKMAPPER_CONFIG'] and File.exist?(options[:config] = File.expand_path(ENV['TASKMAPPER_CONFIG']))
|
31
|
+
else
|
32
|
+
options[:config] = File.expand_path('~/.taskmapper.yml')
|
33
|
+
end
|
34
|
+
|
35
|
+
begin
|
36
|
+
OptionParser.new do |opts|
|
37
|
+
opts.banner = 'Usage: tm [options] COMMAND [command_options]'
|
38
|
+
opts.separator ''
|
39
|
+
opts.separator 'Options:'
|
40
|
+
|
41
|
+
opts.on('-c', '--config CONFIG', 'Use CONFIG as the configuration file. default: ~/.taskmapper.yml') do |c|
|
42
|
+
options[:config] = c
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on('-p', '--provider PROVIDER', 'Specifies the provider') { |p| options[:provider] = p }
|
46
|
+
|
47
|
+
opts.on('-A', '--authentication AUTH',
|
48
|
+
'Specifies authentication information, comma-separated list of name:value pairs.',
|
49
|
+
'Note: The whole list must be enclosed in quotes if there are any spaces.') do |a|
|
50
|
+
options[:authentication] = attributes_hash(a)
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on('-P', '--project PROJECT', 'Specifies the working project') { |p| options[:project] = p }
|
54
|
+
|
55
|
+
opts.separator ''
|
56
|
+
opts.separator 'Other options:'
|
57
|
+
|
58
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
59
|
+
puts opts
|
60
|
+
puts helptext.call
|
61
|
+
exit
|
62
|
+
end
|
63
|
+
end.order!
|
64
|
+
rescue OptionParser::MissingArgument => exception
|
65
|
+
puts "tm #{ARGV.join(' ')}\n\n"
|
66
|
+
puts "Error: An option was called that requires an argument, but was not given one"
|
67
|
+
puts exception.message
|
68
|
+
end
|
69
|
+
|
70
|
+
command = ARGV.shift
|
71
|
+
if commands[command]
|
72
|
+
require File.dirname(__FILE__) + '/commands/' + command
|
73
|
+
send(command, options)
|
74
|
+
else
|
75
|
+
puts "'#{command}' is not a taskmapper command\n\n", helptext.call
|
76
|
+
exit
|
77
|
+
end
|