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
@@ -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,2 @@
1
+ class TaskMapper::Exception < Exception
2
+ 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