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
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.8.0
|
data/bin/tm
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'taskmapper'
|
3
|
+
require 'taskmapper-pivotal'
|
4
|
+
|
5
|
+
# display list of tickets for last project
|
6
|
+
tm = TaskMapper.new(:pivotal)
|
7
|
+
project = tm.projects.last
|
8
|
+
project.tickets.each {|ticket|
|
9
|
+
puts "#{ticket.id} - #{ticket.title}"
|
10
|
+
}
|
11
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'taskmapper'
|
3
|
+
require 'taskmapper-pivotal'
|
4
|
+
require 'taskmapper-lighthouse'
|
5
|
+
|
6
|
+
# copy all tickets and comments from pivotal tracker to new lighthouse project (the hard way)
|
7
|
+
pivotal = TaskMapper.new(:pivotal)
|
8
|
+
lighthouse = TaskMapper.new(:lighthouse)
|
9
|
+
|
10
|
+
from = pivotal.project(97107)
|
11
|
+
|
12
|
+
to = lighthouse.project!(:name => "Copy Test on #{Time.now}",
|
13
|
+
:description => "A copy test")
|
14
|
+
|
15
|
+
from.tickets.each do |from_ticket|
|
16
|
+
to_ticket = to.ticket!({:title => pivotal_ticket.title,
|
17
|
+
:description => pivotal_ticket.description})
|
18
|
+
from_ticket.comments.each do |comment|
|
19
|
+
to_ticket.comment!(:body => comment.body)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'taskmapper'
|
3
|
+
require 'taskmapper-pivotal'
|
4
|
+
require 'taskmapper-lighthouse'
|
5
|
+
|
6
|
+
# copy all tickets and comments from pivotal tracker to new lighthouse project (the easy way)
|
7
|
+
pivotal = TaskMapper.new(:pivotal)
|
8
|
+
lighthouse = TaskMapper.new(:lighthouse)
|
9
|
+
|
10
|
+
from = pivotal.project(97107)
|
11
|
+
to = lighthouse.project!(:name => "Copy Test on #{Time.now}",
|
12
|
+
:description => "A copy test")
|
13
|
+
|
14
|
+
to.copy(from)
|
15
|
+
|
16
|
+
puts "Copy finished."
|
17
|
+
|
data/lib/taskmapper.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
%w{
|
2
|
+
rubygems
|
3
|
+
hashie
|
4
|
+
active_resource
|
5
|
+
}.each {|lib| require lib }
|
6
|
+
|
7
|
+
class TaskMapper
|
8
|
+
end
|
9
|
+
|
10
|
+
%w{
|
11
|
+
common
|
12
|
+
helper
|
13
|
+
project
|
14
|
+
ticket
|
15
|
+
comment
|
16
|
+
authenticator
|
17
|
+
provider
|
18
|
+
exception
|
19
|
+
dummy/dummy.rb
|
20
|
+
tester/tester.rb
|
21
|
+
}.each {|lib| require File.dirname(__FILE__) + '/taskmapper/' + lib }
|
22
|
+
|
23
|
+
|
24
|
+
# This is the TaskMapper class
|
25
|
+
#
|
26
|
+
class TaskMapper
|
27
|
+
attr_reader :provider, :symbol
|
28
|
+
attr_accessor :default_project
|
29
|
+
|
30
|
+
# This initializes the TaskMapper instance and prepares the provider
|
31
|
+
# If called without any arguments, it conveniently tries searching for the information in
|
32
|
+
# ~/.taskmapper.yml
|
33
|
+
# See the documentation for more information on the format of that file.
|
34
|
+
#
|
35
|
+
# What it DOES NOT do is auto-require the provider...so make sure you have the providers required.
|
36
|
+
def initialize(system = nil, authentication = nil)
|
37
|
+
if system.nil? or authentication.nil?
|
38
|
+
require 'yaml'
|
39
|
+
data = YAML.load_file File.expand_path(ENV['TASKMAPPER_CONFIG'] || '~/.taskmapper.yml')
|
40
|
+
system = system.nil? ? data['default'] || data.first.first : system.to_s
|
41
|
+
authentication = data[system]['authentication'] if authentication.nil? and data[system]['authentication']
|
42
|
+
end
|
43
|
+
self.extend TaskMapper::Provider.const_get(system.to_s.capitalize)
|
44
|
+
authorize authentication
|
45
|
+
@symbol = system.to_sym
|
46
|
+
@provider = TaskMapper::Provider.const_get(system.to_s.capitalize)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Providers should over-write this method
|
50
|
+
def authorize(authentication = {})
|
51
|
+
raise TaskMapper::Exception.new("This method must be reimplemented in the provider")
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# The command method call implementation
|
2
|
+
# This sets the option parser and passes the parsed options to the subcommands
|
3
|
+
def config(options)
|
4
|
+
ARGV << '--help' if ARGV.length == 0
|
5
|
+
begin
|
6
|
+
OptionParser.new do |opts|
|
7
|
+
opts.banner = 'Usage: ticket -p PROVIDER [options] config [config_options]'
|
8
|
+
opts.separator ''
|
9
|
+
opts.separator 'Options:'
|
10
|
+
|
11
|
+
opts.on('-a', '--add', 'Add a new entry to the configuration file based on taskmapper options.') do
|
12
|
+
options[:subcommand] = 'add'
|
13
|
+
end
|
14
|
+
|
15
|
+
opts.on('-e', '--edit', 'Edit an existing entry to the configuration file based on taskmapper options') do
|
16
|
+
options[:subcommand] = 'edit'
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on('-p', '--set-default-provider', 'Set the current provider as the default.', 'Requires provider to be specified, otherwise unsets the default') do
|
20
|
+
options[:subcommand] = 'set_default_provider'
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.separator ''
|
24
|
+
opts.separator 'Other options:'
|
25
|
+
|
26
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
27
|
+
puts opts
|
28
|
+
exit
|
29
|
+
end
|
30
|
+
end.parse(ARGV)
|
31
|
+
rescue OptionParser::MissingArgument => exception
|
32
|
+
puts "ticket #{options[:original_argv].join(' ')}\n\n"
|
33
|
+
puts "Error: An option was called that requires an argument, but was not given one"
|
34
|
+
puts exception.message
|
35
|
+
end
|
36
|
+
send(options[:subcommand], options)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Called on --add. It adds a new entry to the config file and will refuse if it already exists
|
40
|
+
def add(options)
|
41
|
+
require_provider unless options[:provider]
|
42
|
+
provider = options[:provider]
|
43
|
+
config_file = File.expand_path(options[:config])
|
44
|
+
config = if File.exists?(config_file)
|
45
|
+
YAML.load_file(config_file)
|
46
|
+
else
|
47
|
+
{}
|
48
|
+
end
|
49
|
+
if config[provider]
|
50
|
+
puts "#{provider} has already been specfied in #{options[:config]}. Refusing to add. Use --edit instead."
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
config[provider] = {}
|
54
|
+
config[provider]['authentication'] = options[:authentication] || {}
|
55
|
+
config[provider]['project'] = options[:project] if options[:project]
|
56
|
+
File.open(config_file, 'w') do |out|
|
57
|
+
YAML.dump(config, out)
|
58
|
+
end
|
59
|
+
puts "Wrote #{provider} to #{config_file}"
|
60
|
+
exit
|
61
|
+
end
|
62
|
+
|
63
|
+
# Called on --edit. It updates and edits an entry. If the entry is non-existent, it will add it.
|
64
|
+
def edit(options)
|
65
|
+
require_provider unless options[:provider]
|
66
|
+
provider = options[:provider]
|
67
|
+
config_file = File.expand_path(options[:config])
|
68
|
+
config = if File.exist?(config_file)
|
69
|
+
YAML.load_file(config_file)
|
70
|
+
else
|
71
|
+
{}
|
72
|
+
end
|
73
|
+
config[provider] ||= {}
|
74
|
+
config[provider]['authentication'] = options[:authentication] || {}
|
75
|
+
config[provider]['project'] = options[:project] if options[:project]
|
76
|
+
File.open(config_file, 'w') do |out|
|
77
|
+
YAML.dump(config, out)
|
78
|
+
end
|
79
|
+
puts "Wrote #{provider} to #{config_file}"
|
80
|
+
exit
|
81
|
+
end
|
82
|
+
|
83
|
+
# Called on --set-default-provider. It sets the current provider as the default
|
84
|
+
def set_default_provider(options)
|
85
|
+
provider = options[:provider]
|
86
|
+
config = YAML.load_file(config_file = File.expand_path(options[:config]))
|
87
|
+
puts "Warning! #{provider} is not defined in #{config_file}" unless provider.nil? or config[provider]
|
88
|
+
config['default'] = provider
|
89
|
+
File.open(config_file, 'w') do |out|
|
90
|
+
YAML.dump(config, out)
|
91
|
+
end
|
92
|
+
puts "Default provider has been set to '#{provider}'"
|
93
|
+
exit
|
94
|
+
end
|
95
|
+
|
96
|
+
# Called when a provider is not given.
|
97
|
+
def require_provider
|
98
|
+
puts "Provider must be specified!"
|
99
|
+
exit 1
|
100
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# the console command
|
2
|
+
def console(options)
|
3
|
+
send(:open_irb, options, ARGV)
|
4
|
+
end
|
5
|
+
|
6
|
+
# the actual method to do the irb opening
|
7
|
+
def open_irb(options, argv)
|
8
|
+
tm_lib = File.dirname(__FILE__) + '/../../../taskmapper.rb'
|
9
|
+
irb_name = RUBY_PLATFORM =~ /mswin32/ ? 'irb.bat' : 'irb'
|
10
|
+
requires = "-r rubygems -r #{tm_lib} "
|
11
|
+
cmd = ''
|
12
|
+
if File.exist?(config = File.expand_path(options[:config]))
|
13
|
+
ENV['TASKMAPPER_CONFIG']=config
|
14
|
+
end
|
15
|
+
providers = !options[:provider].nil? ? [options[:provider]] : YAML.load_file(config).keys
|
16
|
+
providers.delete 'default'
|
17
|
+
require 'rubygems'
|
18
|
+
require 'taskmapper'
|
19
|
+
providers.inject(requires) do |mem, p|
|
20
|
+
begin
|
21
|
+
require "taskmapper-#{p}"
|
22
|
+
requires << "-r taskmapper-#{p} "
|
23
|
+
rescue Exception => exception
|
24
|
+
#puts exception
|
25
|
+
begin
|
26
|
+
require "#{p}"
|
27
|
+
requires << "-r #{p} "
|
28
|
+
rescue Exception => exception
|
29
|
+
warn "Could not require the '#{p}' provider. Is it installed?"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
cmd << "#{irb_name} #{requires} --simple-prompt #{ARGV.join(' ')}"
|
34
|
+
exec cmd
|
35
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# The generate command
|
2
|
+
def generate(options)
|
3
|
+
if ARGV.length == 0
|
4
|
+
ARGV << '--help'
|
5
|
+
else
|
6
|
+
provider_name = ARGV.shift
|
7
|
+
if provider_name.start_with? '_'
|
8
|
+
options[:provider] = provider_name[1..-1]
|
9
|
+
options[:provider_dir] = options[:provider]
|
10
|
+
else
|
11
|
+
options[:provider] = provider_name
|
12
|
+
options[:provider_dir] = 'taskmapper-' + options[:provider]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
options[:mkdir] = true
|
16
|
+
begin
|
17
|
+
OptionParser.new do |opts|
|
18
|
+
opts.banner = 'Usage: tm generate PROVIDER_NAME [--lib-directory DIR] [--jeweler [jeweler_options]]'
|
19
|
+
opts.separator ''
|
20
|
+
opts.separator 'Options:'
|
21
|
+
|
22
|
+
opts.on('-J', '--jeweler [JEWELER_OPTIONS]', 'Sets the working ticket') do |option|
|
23
|
+
options[:jeweler] = ARGV
|
24
|
+
options[:mkdir] = false
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('-L', '--lib-directory DIR', 'Put the skeleton files inside this directory', ' * This assumes the directory already exists') do |dir|
|
28
|
+
options[:lib] = dir
|
29
|
+
options[:mkdir] = false
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.separator ''
|
33
|
+
opts.separator 'Other options:'
|
34
|
+
|
35
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
36
|
+
puts opts
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
opts.separator ''
|
40
|
+
opts.separator 'NOTE: taskmapper- will be prepended to your provider name'
|
41
|
+
opts.separator 'unless you set the first character as _ (it will be removed)'
|
42
|
+
end.order!
|
43
|
+
rescue OptionParser::MissingArgument => exception
|
44
|
+
puts "tm #{options[:original_argv].join(' ')}\n\n"
|
45
|
+
puts "Error: An option was called that requires an argument, but was not given one"
|
46
|
+
puts exception.message
|
47
|
+
rescue OptionParser::InvalidOption => exception
|
48
|
+
options[:jeweler] = exception.recover(ARGV)
|
49
|
+
options[:mkdir] = false
|
50
|
+
end
|
51
|
+
options[:lib] ||= options[:provider_dir] + '/lib/'
|
52
|
+
create_directories(options)
|
53
|
+
copy_skeleton(options)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Copy over the skeleton files
|
57
|
+
def copy_skeleton(options)
|
58
|
+
skeleton_path = File.dirname(__FILE__) + '/generate/'
|
59
|
+
provider = File.read(skeleton_path + 'provider.rb').gsub('yoursystem', options[:provider].downcase)
|
60
|
+
create_file(options, options[:provider_dir] + '.rb', provider)
|
61
|
+
skeleton_path << 'provider/'
|
62
|
+
provider = File.read(skeleton_path + 'provider.rb').gsub('Yoursystem', options[:provider].capitalize).gsub('yoursystem', options[:provider].downcase)
|
63
|
+
create_file(options, 'provider/' + options[:provider].downcase + '.rb', provider)
|
64
|
+
%w(project.rb ticket.rb comment.rb).each do |p|
|
65
|
+
provider = File.read(skeleton_path + p).gsub('Yoursystem', options[:provider].capitalize).gsub('yoursystem', options[:provider].downcase)
|
66
|
+
create_file(options, 'provider/' + p, provider)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Create the directories so copy_skeleton can do its job
|
71
|
+
def create_directories(options)
|
72
|
+
if options[:jeweler]
|
73
|
+
jeweler_options = options[:jeweler].inject('') do |mem, j|
|
74
|
+
j="'#{j}'" if j.include?(' ')
|
75
|
+
mem + j + ' '
|
76
|
+
end
|
77
|
+
puts "Running jeweler #{jeweler_options} #{options[:provider_dir]}"
|
78
|
+
puts `jeweler #{jeweler_options} #{options[:provider_dir]}`
|
79
|
+
elsif options[:mkdir]
|
80
|
+
begin
|
81
|
+
Dir.mkdir(options[:provider_dir])
|
82
|
+
puts "\tcreate\t#{options[:provider_dir]}"
|
83
|
+
rescue Exception => e
|
84
|
+
puts "\t#{e.message}"
|
85
|
+
end
|
86
|
+
begin
|
87
|
+
Dir.mkdir(options[:lib])
|
88
|
+
puts "\tcreate\t#{options[:lib]}"
|
89
|
+
rescue Exception => e
|
90
|
+
puts "\t#{e.message}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
begin
|
94
|
+
Dir.mkdir(options[:lib] + '/provider')
|
95
|
+
puts "\tcreate\t#{options[:lib] + 'provider'}"
|
96
|
+
rescue Exception => e
|
97
|
+
puts "\t#{e.message}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Create files
|
102
|
+
def create_file(options, filename, data)
|
103
|
+
file_path = options[:lib] + '/' + filename
|
104
|
+
if File.exist?(file_path) and File.size(file_path) > 0
|
105
|
+
puts "\texists with content...skipping\t#{filename}"
|
106
|
+
return false;
|
107
|
+
end
|
108
|
+
puts "\tcreate\t#{filename}"
|
109
|
+
f = File.open(file_path, 'a+')
|
110
|
+
f.write data
|
111
|
+
f.close
|
112
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module TaskMapper::Provider
|
2
|
+
module Yoursystem
|
3
|
+
# The comment class for taskmapper-yoursystem
|
4
|
+
#
|
5
|
+
# Do any mapping between TaskMapper and your system's comment model here
|
6
|
+
# versions of the ticket.
|
7
|
+
#
|
8
|
+
class Comment < TaskMapper::Provider::Base::Comment
|
9
|
+
#API = Yoursystem::Comment # The class to access the api's comments
|
10
|
+
# declare needed overloaded methods here
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module TaskMapper::Provider
|
2
|
+
module Yoursystem
|
3
|
+
# Project class for taskmapper-yoursystem
|
4
|
+
#
|
5
|
+
#
|
6
|
+
class Project < TaskMapper::Provider::Base::Project
|
7
|
+
#API = Yoursystem::Project # The class to access the api's projects
|
8
|
+
# declare needed overloaded methods here
|
9
|
+
|
10
|
+
|
11
|
+
# copy from this.copy(that) copies that into this
|
12
|
+
def copy(project)
|
13
|
+
project.tickets.each do |ticket|
|
14
|
+
copy_ticket = self.ticket!(:title => ticket.title, :description => ticket.description)
|
15
|
+
ticket.comments.each do |comment|
|
16
|
+
copy_ticket.comment!(:body => comment.body)
|
17
|
+
sleep 1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module TaskMapper::Provider
|
2
|
+
# This is the Yoursystem Provider for taskmapper
|
3
|
+
module Yoursystem
|
4
|
+
include TaskMapper::Provider::Base
|
5
|
+
#TICKET_API = Yoursystem::Ticket # The class to access the api's tickets
|
6
|
+
#PROJECT_API = Yoursystem::Project # The class to access the api's projects
|
7
|
+
|
8
|
+
# This is for cases when you want to instantiate using TaskMapper::Provider::Yoursystem.new(auth)
|
9
|
+
def self.new(auth = {})
|
10
|
+
TaskMapper.new(:yoursystem, auth)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Providers must define an authorize method. This is used to initialize and set authentication
|
14
|
+
# parameters to access the API
|
15
|
+
def authorize(auth = {})
|
16
|
+
@authentication ||= TaskMapper::Authenticator.new(auth)
|
17
|
+
# Set authentication parameters for whatever you're using to access the API
|
18
|
+
end
|
19
|
+
|
20
|
+
# declare needed overloaded methods here
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|