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,97 @@
|
|
1
|
+
module TaskMapper::Provider
|
2
|
+
module Base
|
3
|
+
# The comment class
|
4
|
+
#
|
5
|
+
# This will probably one of the most troublesome parts of creating a provider for taskmapper
|
6
|
+
# since there are so many different ways comments are handled by different APIs.
|
7
|
+
# Keep in mind that if you do need to change/overwrite the methods, you will most likely
|
8
|
+
# only need to overwrite #find_by_id, #find_by_attributes, #search, and possibly #initialize
|
9
|
+
# as long as their parameters conform to what the find method expects.
|
10
|
+
#
|
11
|
+
# Here are the expected attributes:
|
12
|
+
#
|
13
|
+
# * author
|
14
|
+
# * body
|
15
|
+
# * id
|
16
|
+
# * created_at
|
17
|
+
# * updated_at
|
18
|
+
# * ticket_id
|
19
|
+
# * project_id
|
20
|
+
class Comment < Hashie::Mash
|
21
|
+
include TaskMapper::Provider::Common
|
22
|
+
include TaskMapper::Provider::Helper
|
23
|
+
extend TaskMapper::Provider::Helper
|
24
|
+
attr_accessor :system, :system_data
|
25
|
+
API = nil # Replace with your comment's API's Class
|
26
|
+
|
27
|
+
# Find comment
|
28
|
+
# You can also retrieve an array of all comments by not specifying any query.
|
29
|
+
#
|
30
|
+
# * find(<project_id>, <ticket_id>) or find(<project_id>, <ticket_id>, :all) - Returns an array of all comments
|
31
|
+
# * find(<project_id>, <ticket_id>, ##) - Returns a comment based on that id or some other primary (unique) attribute
|
32
|
+
# * find(<project_id>, <ticket_id>, [#, #, #]) - Returns many comments with these ids
|
33
|
+
# * find(<project_id>, <ticket_id>, :first, :name => 'Project name') - Returns the first comment based on the comment's attribute(s)
|
34
|
+
# * find(<project_id>, <ticket_id>, :last, :name => 'Some project') - Returns the last comment based on the comment's attribute(s)
|
35
|
+
# * find(<project_id>, <ticket_id>, :all, :name => 'Test Project') - Returns all comments based on the given attribute(s)
|
36
|
+
def self.find(project_id, ticket_id, *options)
|
37
|
+
first, attributes = options
|
38
|
+
if first.nil? or (first == :all and attributes.nil?)
|
39
|
+
self.find_by_attributes(project_id, ticket_id)
|
40
|
+
elsif first.is_a? Array
|
41
|
+
first.collect { |id| self.find_by_id(project_id, ticket_id, id) }
|
42
|
+
elsif first == :first
|
43
|
+
comments = attributes.nil? ? self.find_by_attributes(project_id, ticket_id) : self.find_by_attributes(project_id, ticket_id, attributes)
|
44
|
+
comments.first
|
45
|
+
elsif first == :last
|
46
|
+
comments = attributes.nil? ? self.find_by_attributes(project_id, ticket_id) : self.find_by_attributes(project_id, ticket_id, attributes)
|
47
|
+
comments.last
|
48
|
+
elsif first == :all
|
49
|
+
self.find_by_attributes(project_id, ticket_id, attributes)
|
50
|
+
else
|
51
|
+
self.find_by_id(project_id, ticket_id, first)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# The first of whatever comment
|
56
|
+
def self.first(project_id, ticket_id, *options)
|
57
|
+
self.find(project_id, ticket_id, :first, *options)
|
58
|
+
end
|
59
|
+
|
60
|
+
# The last of whatever comment
|
61
|
+
def self.last(project_id, ticket_id, *options)
|
62
|
+
self.find(project_id, ticket_id, :last, *options)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Accepts an integer id and returns the single comment instance
|
66
|
+
# Must be defined by the provider
|
67
|
+
def self.find_by_id(project_id, ticket_id, id)
|
68
|
+
if self::API.is_a? Class
|
69
|
+
self.new self::API.find(id, :params => {:project_id => project_id, :ticket_id => ticket_id})
|
70
|
+
else
|
71
|
+
raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Accepts an attributes hash and returns all comments matching those attributes in an array
|
76
|
+
# Should return all comments if the attributes hash is empty
|
77
|
+
# Must be defined by the provider
|
78
|
+
def self.find_by_attributes(project_id, ticket_id, attributes = {})
|
79
|
+
if self::API.is_a? Class
|
80
|
+
self.search(project_id, ticket_id, attributes)
|
81
|
+
else
|
82
|
+
raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# This is a helper method to find
|
87
|
+
def self.search(project_id, ticket_id, options = {}, limit = 1000)
|
88
|
+
if self::API.is_a? Class
|
89
|
+
comments = self::API.find(:all, :params => {:project_id => project_id, :ticket_id => ticket_id}).collect { |comment| self.new comment }
|
90
|
+
search_by_attribute(comments, options, limit)
|
91
|
+
else
|
92
|
+
raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module TaskMapper::Provider
|
2
|
+
# Common module
|
3
|
+
# contains method definitions common to all or most of the models
|
4
|
+
module Common
|
5
|
+
module ClassMethods
|
6
|
+
# Create a something.
|
7
|
+
# Basically, a .new and .save in the same call. The default method assumes it is passed a
|
8
|
+
# single hash with attribute information
|
9
|
+
def create(*options)
|
10
|
+
if self::API.is_a? Class
|
11
|
+
something = self::API.new(*options)
|
12
|
+
something.save
|
13
|
+
self.new something
|
14
|
+
else
|
15
|
+
raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Automatic extension of class methods on include
|
21
|
+
def self.included(base)
|
22
|
+
base.extend ClassMethods
|
23
|
+
end
|
24
|
+
|
25
|
+
# The initializer
|
26
|
+
# It tries to do a default system for ActiveResource-based api providers
|
27
|
+
def initialize(*options)
|
28
|
+
@system_data ||= {}
|
29
|
+
@cache ||= {}
|
30
|
+
first = options.shift
|
31
|
+
case first
|
32
|
+
when Hash
|
33
|
+
super(first.to_hash)
|
34
|
+
else
|
35
|
+
@system_data[:client] = first
|
36
|
+
self.prefix_options ||= @system_data[:client].prefix_options if @system_data[:client].prefix_options
|
37
|
+
super(first.attributes)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Update the something and save
|
42
|
+
# As opposed to update which just updates the attributes
|
43
|
+
def update!(*options)
|
44
|
+
update(*options)
|
45
|
+
save
|
46
|
+
end
|
47
|
+
|
48
|
+
# Save changes to this project
|
49
|
+
# Returns true (success) or false (failure) or nil (no changes)
|
50
|
+
def save
|
51
|
+
if @system_data and (something = @system_data[:client]) and something.respond_to?(:attributes)
|
52
|
+
changes = 0
|
53
|
+
something.attributes.each do |k, v|
|
54
|
+
if self.send(k) != v
|
55
|
+
something.send(k + '=', self.send(k))
|
56
|
+
changes += 1
|
57
|
+
end
|
58
|
+
end
|
59
|
+
something.save if changes > 0
|
60
|
+
else
|
61
|
+
raise TaskMapper::Exception.new("#{self.class.name}::#{this_method} method must be implemented by the provider")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Delete this project
|
66
|
+
# Returns true (success) or false(failure)
|
67
|
+
def destroy
|
68
|
+
if @system_data and @system_data[:client] and @system_data[:client].respond_to?(:destroy)
|
69
|
+
return @system_data[:client].destroy
|
70
|
+
else
|
71
|
+
raise TaskMapper::Exception.new("#{self.class.name}::#{this_method} method must be implemented by the provider")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def respond_to?(symbol, include_private = false)
|
76
|
+
result = super(symbol)
|
77
|
+
return true if result or @system_data.nil? or @system_data[:client].nil?
|
78
|
+
@system_data[:client].respond_to?(symbol, include_private)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module TaskMapper::Provider
|
2
|
+
module Dummy
|
3
|
+
# This is the Comment class for the Dummy provider
|
4
|
+
class Comment < TaskMapper::Provider::Base::Comment
|
5
|
+
|
6
|
+
|
7
|
+
def self.find_by_id(id)
|
8
|
+
self.new({:id => id})
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.find_by_attributes(*options)
|
12
|
+
[self.new(*options)]
|
13
|
+
end
|
14
|
+
|
15
|
+
# You don't need to define an initializer, this is only here to initialize dummy data
|
16
|
+
def initialize(project_id, ticket_id, *options)
|
17
|
+
data = {:id => rand(1000), :status => ['lol', 'rofl', 'lmao', 'lamo', 'haha', 'heh'][rand(6)],
|
18
|
+
:priority => rand(10), :summary => 'Tickets ticket ticket ticket', :resolution => false,
|
19
|
+
:created_at => Time.now, :updated_at => Time.now, :description => 'Ticket ticket ticket ticket laughing',
|
20
|
+
:assignee => 'lol-man'}
|
21
|
+
@system = :dummy
|
22
|
+
super(data.merge(options.first || {}))
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module TaskMapper::Provider
|
2
|
+
# This is the Dummy Provider
|
3
|
+
#
|
4
|
+
# It doesn't really do anything, it exists in order to test the basic functionality of taskmapper
|
5
|
+
# and to provide an example the basics of what is to be expected from the providers.
|
6
|
+
#
|
7
|
+
# Note that the initial provider name is a module rather than a class. TaskMapper.new
|
8
|
+
# extends on an instance-based fashion. If you would rather initialize using code that is
|
9
|
+
# closer to:
|
10
|
+
#
|
11
|
+
# TaskMapper::Provider::Dummy.new(authentication)
|
12
|
+
#
|
13
|
+
# You will have to do a little magic trick and define new on the provider as a wrapper
|
14
|
+
# around the TaskMapper.new call.
|
15
|
+
module Dummy
|
16
|
+
include TaskMapper::Provider::Base
|
17
|
+
# An example of what to do if you would like to do TaskMapper::Provider::Dummy.new(...)
|
18
|
+
# rather than TaskMapper.new(:dummy, ...)
|
19
|
+
def self.new(authentication = {})
|
20
|
+
TaskMapper.new(:dummy, authentication)
|
21
|
+
# maybe do some other stuff
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
%w| project ticket comment |.each do |f|
|
27
|
+
require File.dirname(__FILE__) + '/' + f +'.rb'
|
28
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module TaskMapper::Provider
|
2
|
+
module Dummy
|
3
|
+
# This is the Project class for the Dummy provider
|
4
|
+
class Project < TaskMapper::Provider::Base::Project
|
5
|
+
|
6
|
+
def self.find_by_id(id)
|
7
|
+
self.new({:id => id})
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.find_by_attributes(*options)
|
11
|
+
[self.new(*options)]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.create(*attributes)
|
15
|
+
self.new(*attributes)
|
16
|
+
end
|
17
|
+
|
18
|
+
# You should define @system and @system_data here.
|
19
|
+
# The data stuff is just to initialize fake data. In a real provider, you would use the API
|
20
|
+
# to grab the information and then initialize based on that info.
|
21
|
+
# @system_data would hold the API's model/instance for reference
|
22
|
+
def initialize(*options)
|
23
|
+
data = {:id => rand(1000).to_i, :name => 'Dummy', :description => 'Mock!-ing Bird',
|
24
|
+
:created_at => Time.now, :updated_at => Time.now}
|
25
|
+
@system = :dummy
|
26
|
+
super(data.merge(options.first || {}))
|
27
|
+
end
|
28
|
+
|
29
|
+
# Nothing to save so we always return true
|
30
|
+
# ...unless it's an odd numbered second on Friday the 13th. muhaha!
|
31
|
+
def save
|
32
|
+
time = Time.now
|
33
|
+
!(time.wday == 5 and time.day == 13 and time.to_i % 2 == 1)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Nothing to update, so we always return true
|
37
|
+
def update!(*options)
|
38
|
+
return true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module TaskMapper::Provider
|
2
|
+
module Dummy
|
3
|
+
# The Dummy Provider's Ticket class
|
4
|
+
class Ticket < TaskMapper::Provider::Base::Ticket
|
5
|
+
@system = :dummy
|
6
|
+
|
7
|
+
def self.find_by_id(project_id, ticket_id)
|
8
|
+
self.new(project_id, {:id => ticket_id})
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.find_by_attributes(*ticket_attributes)
|
12
|
+
[self.new(*ticket_attributes)]
|
13
|
+
end
|
14
|
+
|
15
|
+
# You don't need to define an initializer, this is only here to initialize dummy data
|
16
|
+
def initialize(project_id, *options)
|
17
|
+
data = {:id => rand(1000), :status => ['lol', 'rofl', 'lmao', 'lamo', 'haha', 'heh'][rand(6)],
|
18
|
+
:priority => rand(10), :summary => 'Tickets ticket ticket ticket', :resolution => false,
|
19
|
+
:created_at => Time.now, :updated_at => Time.now, :description => 'Ticket ticket ticket ticket laughing',
|
20
|
+
:assignee => 'lol-man', :project_id => project_id}
|
21
|
+
@system = :dummy
|
22
|
+
super(data.merge(options.first || {}))
|
23
|
+
end
|
24
|
+
|
25
|
+
# Nothing to save so we always return true
|
26
|
+
# ...unless it's the Ides of March and the second is divisible by three. muhaha!
|
27
|
+
def save
|
28
|
+
time = Time.now
|
29
|
+
!(time.wday == 15 and time.day == 3 and time.to_i % 3 == 0)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Nothing to close, so we always return true
|
33
|
+
def close
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
# Nothing to destroy so we always return true
|
38
|
+
def destroy
|
39
|
+
true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module TaskMapper::Provider
|
2
|
+
# Contains a series of helper methods
|
3
|
+
module Helper
|
4
|
+
|
5
|
+
# Current method name reflection
|
6
|
+
# http://www.ruby-forum.com/topic/75258#895630
|
7
|
+
# Call when raising 'implemented by the provider' exceptions
|
8
|
+
def this_method
|
9
|
+
caller[0][/`([^']*)'/, 1]
|
10
|
+
end
|
11
|
+
|
12
|
+
# A helper method for easy finding
|
13
|
+
def easy_finder(api, symbol, options, at_index = 0)
|
14
|
+
if api.is_a? Class
|
15
|
+
return api if options.length == 0 and symbol == :first
|
16
|
+
options.insert(at_index, symbol) if options[at_index].is_a?(Hash)
|
17
|
+
api.find(*options)
|
18
|
+
else
|
19
|
+
raise TaskMapper::Exception.new("#{Helper.name}::#{this_method} method must be implemented by the provider")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# This is a search filter that all parameters are passed through
|
24
|
+
# Redefine this method in your classes if you need specific functionality
|
25
|
+
def search_filter(k)
|
26
|
+
k
|
27
|
+
end
|
28
|
+
|
29
|
+
def provider_parent(klass)
|
30
|
+
TaskMapper::Provider.const_get(klass.to_s.split('::')[-2])
|
31
|
+
end
|
32
|
+
|
33
|
+
# Goes through all the things and returns results that match the options attributes hash
|
34
|
+
def search_by_attribute(things, options = {}, limit = 1000)
|
35
|
+
things.find_all do |thing|
|
36
|
+
options.inject(true) do |memo, kv|
|
37
|
+
break unless memo
|
38
|
+
key, value = kv
|
39
|
+
begin
|
40
|
+
memo &= thing.send(key) == value
|
41
|
+
rescue NoMethodError
|
42
|
+
memo = false
|
43
|
+
end
|
44
|
+
memo
|
45
|
+
end and (limit -= 1) > 0
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns a filter-like string from a hash
|
50
|
+
# If array_join is given, arrays are joined rather than having their own separated key:values
|
51
|
+
#
|
52
|
+
# ex: filter_string({:name => 'some value', :tags => ['abc', 'def']}) = "name:'some value' tag:abc tag:def"
|
53
|
+
def filter_string(filter = {}, array_join = nil)
|
54
|
+
filter.inject('') do |mem, kv|
|
55
|
+
key, value = kv
|
56
|
+
if value.is_a?(Array)
|
57
|
+
if !array_join.nil?
|
58
|
+
mem += value.inject('') { |m, v|
|
59
|
+
v = "\"#{v}\"" if v.to_s.include?(' ')
|
60
|
+
m+= "#{key}:#{v}"
|
61
|
+
}
|
62
|
+
return mem
|
63
|
+
else
|
64
|
+
value = value.join(array_join)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
value = "\"#{value}\"" if value.to_s.include?(' ')
|
68
|
+
mem += "#{key}:#{value} "
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module TaskMapper::Provider
|
2
|
+
module Base
|
3
|
+
# This is the base Project class for providers
|
4
|
+
#
|
5
|
+
# Providers should inherit this class and redefine the methods
|
6
|
+
#
|
7
|
+
# Each provider should have their own @system defined.
|
8
|
+
# For example, taskmapper-unfuddle's @system is :unfuddle and taskmapper-lighthouse's
|
9
|
+
# @system is :lighthouse.
|
10
|
+
#
|
11
|
+
# Methods that must be implemented by the provider
|
12
|
+
#
|
13
|
+
# * self.find_by_id
|
14
|
+
# * self.find_by_attributes
|
15
|
+
#
|
16
|
+
# Methods that might need to be implemented by the provider
|
17
|
+
# * tickets
|
18
|
+
# * ticket
|
19
|
+
# * initialize
|
20
|
+
# * update
|
21
|
+
# * destroy
|
22
|
+
# * self.create
|
23
|
+
#
|
24
|
+
# Methods that would probably be okay if the provider left it alone:
|
25
|
+
#
|
26
|
+
# * self.find - although you can define your own to optimize it a bit
|
27
|
+
# * update!
|
28
|
+
#
|
29
|
+
# A provider should define as many attributes as feasibly possible. The list below are
|
30
|
+
# some guidelines as to what attributes are necessary, if your provider's api does not
|
31
|
+
# implement them, point it to an attribute that is close to it. (for example, a name
|
32
|
+
# can point to title. Remember to alias it in your class!)
|
33
|
+
#
|
34
|
+
# * id
|
35
|
+
# * name
|
36
|
+
# * created_at
|
37
|
+
# * updated_at
|
38
|
+
# * description
|
39
|
+
class Project < Hashie::Mash
|
40
|
+
include TaskMapper::Provider::Common
|
41
|
+
include TaskMapper::Provider::Helper
|
42
|
+
extend TaskMapper::Provider::Helper
|
43
|
+
attr_accessor :system, :system_data
|
44
|
+
API = nil #Replace with your api digestor's class.
|
45
|
+
|
46
|
+
# Find project
|
47
|
+
# You can also retrieve an array of all projects by not specifying any query.
|
48
|
+
#
|
49
|
+
# * find() or find(:all) - Returns an array of all projects
|
50
|
+
# * find(##) - Returns a project based on that id or some other primary (unique) attribute
|
51
|
+
# * find([#, #, #]) - Returns many projects with these ids
|
52
|
+
# * find(:first, :name => 'Project name') - Returns the first project based on the project's attribute(s)
|
53
|
+
# * find(:last, :name => 'Some project') - Returns the last project based on the project's attribute(s)
|
54
|
+
# * find(:all, :name => 'Test Project') - Returns all projects based on the given attribute(s)
|
55
|
+
def self.find(*options)
|
56
|
+
first = options.shift
|
57
|
+
attributes = options.shift
|
58
|
+
if first.nil? or (first == :all and attributes.nil?)
|
59
|
+
self.find_by_attributes
|
60
|
+
elsif first.is_a? Array
|
61
|
+
first.collect { |id| self.find_by_id(id) }
|
62
|
+
elsif first == :first
|
63
|
+
projects = attributes.nil? ? self.find_by_attributes : self.find_by_attributes(attributes)
|
64
|
+
projects.first
|
65
|
+
elsif first == :last
|
66
|
+
projects = attributes.nil? ? self.find_by_attributes : self.find_by_attributes(attributes)
|
67
|
+
projects.last
|
68
|
+
elsif first == :all
|
69
|
+
self.find_by_attributes(attributes)
|
70
|
+
else
|
71
|
+
self.find_by_id(first)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# The first of whatever project
|
76
|
+
def self.first(*options)
|
77
|
+
self.find(:first, *options)
|
78
|
+
end
|
79
|
+
|
80
|
+
# The last of whatever project
|
81
|
+
def self.last(*options)
|
82
|
+
self.find(:last, *options)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Accepts an integer id and returns the single project instance
|
86
|
+
# Must be defined by the provider
|
87
|
+
def self.find_by_id(id)
|
88
|
+
if self::API.is_a? Class
|
89
|
+
self.new self::API.find(id)
|
90
|
+
else
|
91
|
+
raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Accepts an attributes hash and returns all projects matching those attributes in an array
|
96
|
+
# Should return all projects if the attributes hash is empty
|
97
|
+
# Must be defined by the provider
|
98
|
+
def self.find_by_attributes(attributes = {})
|
99
|
+
if self::API.is_a? Class
|
100
|
+
self.search(attributes)
|
101
|
+
else
|
102
|
+
raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# This is a helper method to find
|
107
|
+
def self.search(options = {}, limit = 1000)
|
108
|
+
if self::API.is_a? Class
|
109
|
+
projects = self::API.find(:all).collect { |project| self.new project }
|
110
|
+
search_by_attribute(projects, options, limit)
|
111
|
+
else
|
112
|
+
raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Asks the provider's api for the tickets associated with the project,
|
117
|
+
# returns an array of Ticket objects.
|
118
|
+
def tickets(*options)
|
119
|
+
options.insert 0, id
|
120
|
+
easy_finder(provider_parent(self.class)::Ticket, :all, options, 1)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Very similar to tickets, and is practically an alias of it
|
124
|
+
# however this returns the ticket class if no parameter is given
|
125
|
+
# unlike tickets which returns an array of all tickets when given no parameters
|
126
|
+
def ticket(*options)
|
127
|
+
options.insert(0, id) if options.length > 0
|
128
|
+
easy_finder(provider_parent(self.class)::Ticket, :first, options, 1)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Create a ticket
|
132
|
+
def ticket!(*options)
|
133
|
+
options[0].merge!(:project_id => id) if options.first.is_a?(Hash)
|
134
|
+
provider_parent(self.class)::Ticket.create(*options)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Define some provider specific initalizations
|
138
|
+
def initialize(*options)
|
139
|
+
# @system_data = {'some' => 'data}
|
140
|
+
super(*options)
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|