taskmapper 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.document +5 -0
  2. data/.travis.yml +4 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +52 -0
  5. data/LICENSE +20 -0
  6. data/NOTES +18 -0
  7. data/README.md +296 -0
  8. data/Rakefile +40 -0
  9. data/TODO +1 -0
  10. data/VERSION +1 -0
  11. data/bin/tm +7 -0
  12. data/examples/tm_example.rb +11 -0
  13. data/examples/tm_example_2.rb +11 -0
  14. data/examples/tm_example_3.rb +21 -0
  15. data/examples/tm_example_4.rb +17 -0
  16. data/lib/taskmapper.rb +53 -0
  17. data/lib/taskmapper/authenticator.rb +2 -0
  18. data/lib/taskmapper/cli/commands/config.rb +100 -0
  19. data/lib/taskmapper/cli/commands/console.rb +35 -0
  20. data/lib/taskmapper/cli/commands/generate.rb +112 -0
  21. data/lib/taskmapper/cli/commands/generate/provider.rb +5 -0
  22. data/lib/taskmapper/cli/commands/generate/provider/comment.rb +14 -0
  23. data/lib/taskmapper/cli/commands/generate/provider/project.rb +26 -0
  24. data/lib/taskmapper/cli/commands/generate/provider/provider.rb +25 -0
  25. data/lib/taskmapper/cli/commands/generate/provider/ticket.rb +12 -0
  26. data/lib/taskmapper/cli/commands/help.rb +9 -0
  27. data/lib/taskmapper/cli/commands/help/config +27 -0
  28. data/lib/taskmapper/cli/commands/help/console +13 -0
  29. data/lib/taskmapper/cli/commands/help/generate +19 -0
  30. data/lib/taskmapper/cli/commands/help/help +7 -0
  31. data/lib/taskmapper/cli/commands/help/project +13 -0
  32. data/lib/taskmapper/cli/commands/help/ticket +14 -0
  33. data/lib/taskmapper/cli/commands/project.rb +140 -0
  34. data/lib/taskmapper/cli/commands/ticket.rb +145 -0
  35. data/lib/taskmapper/cli/common.rb +28 -0
  36. data/lib/taskmapper/cli/init.rb +77 -0
  37. data/lib/taskmapper/comment.rb +97 -0
  38. data/lib/taskmapper/common.rb +81 -0
  39. data/lib/taskmapper/dummy/comment.rb +27 -0
  40. data/lib/taskmapper/dummy/dummy.rb +28 -0
  41. data/lib/taskmapper/dummy/project.rb +42 -0
  42. data/lib/taskmapper/dummy/ticket.rb +43 -0
  43. data/lib/taskmapper/exception.rb +2 -0
  44. data/lib/taskmapper/helper.rb +72 -0
  45. data/lib/taskmapper/project.rb +145 -0
  46. data/lib/taskmapper/provider.rb +82 -0
  47. data/lib/taskmapper/tester/comment.rb +18 -0
  48. data/lib/taskmapper/tester/project.rb +19 -0
  49. data/lib/taskmapper/tester/tester.rb +28 -0
  50. data/lib/taskmapper/tester/ticket.rb +19 -0
  51. data/lib/taskmapper/ticket.rb +154 -0
  52. data/spec/project_spec.rb +84 -0
  53. data/spec/rcov.opts +1 -0
  54. data/spec/spec.opts +1 -0
  55. data/spec/spec_helper.rb +9 -0
  56. data/spec/taskmapper-cli_spec.rb +60 -0
  57. data/spec/taskmapper-exception_spec.rb +160 -0
  58. data/spec/taskmapper_spec.rb +13 -0
  59. data/spec/ticket_spec.rb +56 -0
  60. data/taskmapper.gemspec +118 -0
  61. metadata +189 -0
data/TODO ADDED
@@ -0,0 +1 @@
1
+ 1) A Cache system for the finders
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.8.0
data/bin/tm ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # The 'taskmapper' gem was installed using rubygems
4
+ # This file is the CLI application to use with 'taskmapper'
5
+
6
+ require File.dirname(__FILE__) + '/../lib/taskmapper/cli/init.rb'
7
+
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'taskmapper'
3
+ require 'taskmapper-lighthouse'
4
+
5
+ # display list of projects
6
+ tm = TaskMapper.new(:lighthouse)
7
+ tm.projects.each {|project|
8
+ puts "#{project.id} - #{project.name}"
9
+ }
10
+
11
+
@@ -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
+
@@ -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,2 @@
1
+ class TaskMapper::Authenticator < Hashie::Mash
2
+ 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,5 @@
1
+ #require YOUR_PROVIDER_API
2
+
3
+ %w{ yoursystem ticket project comment }.each do |f|
4
+ require File.dirname(__FILE__) + '/provider/' + f + '.rb';
5
+ 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
+