taskmapper 0.8.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.
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
+