ticketmaster 0.1.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ # Parses the configuration information and puts it into options
2
+ def parse_config!(options)
3
+ config = YAML.load_file File.expand_path(options[:config])
4
+ provider = (options[:provider] ||= config['default'] || config.first.first)
5
+ if provider and provider.length > 0
6
+ options[:project] ||= config[provider]['project']
7
+ options[:authentication] ||= config[provider]['authentication']
8
+ end
9
+ options
10
+ end
11
+
12
+ # A utility method used to separate name:value pairs
13
+ def attributes_hash(kvlist)
14
+ if kvlist.is_a?(String)
15
+ kvlist.split(',').reduce({}) do |mem, kv|
16
+ key, value = kv.split(':')
17
+ mem[key] = value
18
+ mem
19
+ end
20
+ elsif kvlist.is_a?(Array)
21
+ mem = {}
22
+ kvlist.each_slice(2) do |k, v|
23
+ mem[k] = v
24
+ end
25
+ mem
26
+ end
27
+ end
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'yaml'
5
+ require File.dirname(__FILE__) + '/common.rb'
6
+
7
+
8
+ commands ={ 'help' => 'Get the help text for a particular command',
9
+ 'console' => 'Open up a ticketmaster console session',
10
+ 'config' => 'Setup and configure a ticketmaster.yml file',
11
+ 'ticket' => 'Work with tickets (create, edit, delete, etc)',
12
+ 'project' => 'Work with projects (create, edit, delete, etc)',
13
+ }
14
+
15
+ helptext = lambda {
16
+ helpmsg = "\nAvailable commands:\n"
17
+ commands.sort.reduce(helpmsg) { |mem, cmd| mem << "\t#{cmd.join("\t\t")}\n" }
18
+ helpmsg << "\nSee 'ticket help COMMAND' for more information on a specific command."
19
+ }
20
+
21
+ ARGV << '--help' if ARGV.length == 0
22
+
23
+ options = {:original_argv => ARGV.dup}
24
+
25
+ if File.exist?(options[:config] = File.expand_path('ticketmaster.yml'))
26
+ elsif File.exist?(options[:config] = File.expand_path('config/ticketmaster.yml'))
27
+ elsif ENV['TICKETMASTER_CONFIG'] and File.exist?(options[:config] = File.expand_path(ENV['TICKETMASTER_CONFIG']))
28
+ else
29
+ options[:config] = File.expand_path('~/.ticketmaster.yml')
30
+ end
31
+
32
+ begin
33
+ OptionParser.new do |opts|
34
+ opts.banner = 'Usage: ticket [options] COMMAND [command_options]'
35
+ opts.separator ''
36
+ opts.separator 'Options:'
37
+
38
+ opts.on('-c', '--config CONFIG', 'Use CONFIG as the configuration file. default: ~/.ticketmaster.yml') do |c|
39
+ options[:config] = c
40
+ end
41
+
42
+ opts.on('-p', '--provider PROVIDER', 'Specifies the provider') { |p| options[:provider] = p }
43
+
44
+ opts.on('-A', '--authentication AUTH',
45
+ 'Specifies authentication information, comma-separated list of name:value pairs.',
46
+ 'Note: The whole list must be enclosed in quotes if there are any spaces.') do |a|
47
+ options[:authentication] = attributes_hash(a)
48
+ end
49
+
50
+ opts.on('-P', '--project PROJECT', 'Specifies the working project') { |p| options[:project] = p }
51
+
52
+ opts.separator ''
53
+ opts.separator 'Other options:'
54
+
55
+ opts.on_tail('-h', '--help', 'Show this message') do
56
+ puts opts
57
+ puts helptext.call
58
+ exit
59
+ end
60
+ end.order!
61
+ rescue OptionParser::MissingArgument => exception
62
+ puts "ticket #{ARGV.join(' ')}\n\n"
63
+ puts "Error: An option was called that requires an argument, but was not given one"
64
+ puts exception.message
65
+ end
66
+
67
+ command = ARGV.shift
68
+ if commands[command]
69
+ require File.dirname(__FILE__) + '/commands/' + command
70
+ send(command, options)
71
+ else
72
+ puts "'#{command}' is not a ticketmaster command\n\n", helptext.call
73
+ exit
74
+ end
@@ -0,0 +1,97 @@
1
+ module TicketMaster::Provider
2
+ module Base
3
+ # The comment class
4
+ #
5
+ # This will probably one of the most troublesome parts of creating a provider for ticketmaster
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 TicketMaster::Provider::Common
22
+ include TicketMaster::Provider::Helper
23
+ extend TicketMaster::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? Fixnum
41
+ self.find_by_id(project_id, ticket_id, first)
42
+ elsif first.is_a? Array
43
+ first.collect { |id| self.find_by_id(project_id, ticket_id, id) }
44
+ elsif first == :first
45
+ comments = attributes.nil? ? self.find_by_attributes(project_id, ticket_id) : self.find_by_attributes(project_id, ticket_id, attributes)
46
+ comments.first
47
+ elsif first == :last
48
+ comments = attributes.nil? ? self.find_by_attributes(project_id, ticket_id) : self.find_by_attributes(project_id, ticket_id, attributes)
49
+ comments.last
50
+ elsif first == :all
51
+ self.find_by_attributes(project_id, ticket_id, attributes)
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 TicketMaster::Exception.new("This method must be reimplemented in 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).collect { |thing| self.new thing }
81
+ else
82
+ raise TicketMaster::Exception.new("This method must be reimplemented in 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})
90
+ search_by_attribute(comments, options, limit)
91
+ else
92
+ raise TicketMaster::Exception.new("This method must be reimplemented in the provider")
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,82 @@
1
+ module TicketMaster::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 TicketMaster::Exception.new("This method must be reimplemented in 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 ActiveResource::Base
33
+ @system_data[:client] = first
34
+ self.prefix_options ||= @system_data[:client].prefix_options if @system_data[:client].prefix_options
35
+ super(first.attributes)
36
+
37
+ when Hash
38
+ super(first.to_hash)
39
+ end
40
+ end
41
+
42
+ # Update the something and save
43
+ # As opposed to update which just updates the attributes
44
+ def update!(*options)
45
+ update(*options)
46
+ save
47
+ end
48
+
49
+ # Save changes to this project
50
+ # Returns true (success) or false (failure) or nil (no changes)
51
+ def save
52
+ if @system_data and (something = @system_data[:client]) and something.respond_to?(:attributes)
53
+ changes = 0
54
+ something.attributes.each do |k, v|
55
+ if self.send(k) != v
56
+ something.send(k + '=', self.send(k))
57
+ changes += 1
58
+ end
59
+ end
60
+ something.save if changes > 0
61
+ else
62
+ raise TicketMaster::Exception.new("This method must be reimplemented in the provider")
63
+ end
64
+ end
65
+
66
+ # Delete this project
67
+ # Returns true (success) or false(failure)
68
+ def destroy
69
+ if @system_data and @system_data[:client] and @system_data[:client].respond_to?(:destroy)
70
+ return @system_data[:client].destroy
71
+ else
72
+ raise TicketMaster::Exception.new("This method must be reimplemented in the provider")
73
+ end
74
+ end
75
+
76
+ def respond_to?(symbol)
77
+ result = super(symbol)
78
+ return true if result or @system_data.nil? or @system_data[:client].nil?
79
+ @system_data[:client].respond_to?(symbol)
80
+ end
81
+ end
82
+ end
@@ -49,6 +49,11 @@ module TicketMaster::Provider
49
49
  time = Time.now
50
50
  !(time.wday == 5 and time.day == 13 and time.to_i % 2 == 1)
51
51
  end
52
+
53
+ # Nothing to update, so we always return true
54
+ def update(*options)
55
+ return true
56
+ end
52
57
  end
53
58
  end
54
59
  end
@@ -0,0 +1,61 @@
1
+ module TicketMaster::Provider
2
+ # Contains a series of helper methods
3
+ module Helper
4
+
5
+ # A helper method for easy finding
6
+ def easy_finder(api, symbol, options, at_index = 0)
7
+ if api.is_a? Class
8
+ return api if options.length == 0 and symbol == :first
9
+ options.insert(at_index, symbol) if options[at_index].is_a?(Hash)
10
+ api.find(*options)
11
+ else
12
+ raise TicketMaster::Exception.new("This method must be reimplemented in the provider")
13
+ end
14
+ end
15
+
16
+ # This is a search filter that all parameters are passed through
17
+ # Redefine this method in your classes if you need specific functionality
18
+ def search_filter(k)
19
+ k
20
+ end
21
+
22
+ # Goes through all the things and returns results that match the options attributes hash
23
+ def search_by_attribute(things, options = {}, limit = 1000)
24
+ things.find_all do |thing|
25
+ options.reduce(true) do |memo, kv|
26
+ break unless memo
27
+ key, value = kv
28
+ begin
29
+ memo &= thing.send(key) == value
30
+ rescue NoMethodError
31
+ memo = false
32
+ end
33
+ memo
34
+ end and (limit -= 1) > 0
35
+ end
36
+ end
37
+
38
+ # Returns a filter-like string from a hash
39
+ # If array_join is given, arrays are joined rather than having their own separated key:values
40
+ #
41
+ # ex: filter_string({:name => 'some value', :tags => ['abc', 'def']}) = "name:'some value' tag:abc tag:def"
42
+ def filter_string(filter = {}, array_join = nil)
43
+ filter.reduce('') do |mem, kv|
44
+ key, value = kv
45
+ if value.is_a?(Array)
46
+ if !array_join.nil?
47
+ mem += value.reduce('') { |m, v|
48
+ v = "\"#{v}\"" if v.to_s.include?(' ')
49
+ m+= "#{key}:#{v}"
50
+ }
51
+ return mem
52
+ else
53
+ value = value.join(array_join)
54
+ end
55
+ end
56
+ value = "\"#{value}\"" if value.to_s.include?(' ')
57
+ mem += "#{key}:#{value} "
58
+ end
59
+ end
60
+ end
61
+ end
@@ -10,15 +10,21 @@ module TicketMaster::Provider
10
10
  #
11
11
  # Methods that must be implemented by the provider
12
12
  #
13
- # * self.find
13
+ # * self.find_by_id
14
+ # * self.find_by_attributes
15
+ #
16
+ # Methods that might need to be implemented by the provider
14
17
  # * tickets
15
18
  # * ticket
16
- # * save
17
19
  # * initialize
20
+ # * update
21
+ # * destroy
22
+ # * self.create
18
23
  #
19
24
  # Methods that would probably be okay if the provider left it alone:
20
25
  #
21
- # * self.create
26
+ # * self.find - although you can define your own to optimize it a bit
27
+ # * update!
22
28
  #
23
29
  # A provider should define as many attributes as feasibly possible. The list below are
24
30
  # some guidelines as to what attributes are necessary, if your provider's api does not
@@ -31,55 +37,101 @@ module TicketMaster::Provider
31
37
  # * updated_at
32
38
  # * description
33
39
  class Project < Hashie::Mash
40
+ include TicketMaster::Provider::Common
41
+ include TicketMaster::Provider::Helper
42
+ extend TicketMaster::Provider::Helper
34
43
  attr_accessor :system, :system_data
35
- # Find a project, or find more projects.
44
+ API = nil #Replace with your api digestor's class.
45
+
46
+ # Find project
36
47
  # You can also retrieve an array of all projects by not specifying any query.
37
48
  #
38
- # The implementation should be able to accept these cases if feasible:
39
- #
40
- # * find(:all) - Returns an array of all projects
49
+ # * find() or find(:all) - Returns an array of all projects
41
50
  # * find(##) - Returns a project based on that id or some other primary (unique) attribute
42
- # * find(:first, :summary => 'Project name') - Returns a project based on the project's attributes
43
- # * find(:summary => 'Test Project') - Returns all projects based on the given attribute(s)
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)
44
55
  def self.find(*options)
45
- raise TicketMaster::Exception.new("This method must be reimplemented in the provider")
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? Fixnum
61
+ self.find_by_id(first)
62
+ elsif first.is_a? Array
63
+ first.collect { |id| self.find_by_id(id) }
64
+ elsif first == :first
65
+ projects = attributes.nil? ? self.find_by_attributes : self.find_by_attributes(attributes)
66
+ projects.first
67
+ elsif first == :last
68
+ projects = attributes.nil? ? self.find_by_attributes : self.find_by_attributes(attributes)
69
+ projects.last
70
+ elsif first == :all
71
+ self.find_by_attributes(attributes)
72
+ end
46
73
  end
47
74
 
48
- # Create a project.
49
- # Basically, a .new and .save in the same call. The default method assumes it is passed a
50
- # single hash with attribute information
51
- def self.create(*options)
52
- project = self.new(options.first)
53
- project.save
54
- project
75
+ # The first of whatever project
76
+ def self.first(*options)
77
+ self.find(:first, *options)
55
78
  end
56
79
 
57
- # The initializer
58
- def initialize(*options)
59
- super(options.shift)
60
- # do some other stuff
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 TicketMaster::Exception.new("This method must be reimplemented in 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).collect { |thing| self.new thing }
101
+ else
102
+ raise TicketMaster::Exception.new("This method must be reimplemented in 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)
110
+ search_by_attribute(projects, options, limit)
111
+ else
112
+ raise TicketMaster::Exception.new("This method must be reimplemented in the provider")
113
+ end
61
114
  end
62
115
 
63
116
  # Asks the provider's api for the tickets associated with the project,
64
117
  # returns an array of Ticket objects.
65
118
  def tickets(*options)
66
- raise TicketMaster::Exception.new("This method must be reimplemented in the provider")
119
+ options.insert 0, id
120
+ easy_finder(self.class.parent::Ticket, :all, options, 1)
67
121
  end
68
-
69
- # Mainly here because it is more natural to do:
70
- # project.ticket.create(..)
71
- #
72
- # Than
73
- # project.tickets.create(..)
74
- #
75
- # returns a ticket object or ticket class that responds to .new and .create.
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
76
126
  def ticket(*options)
77
- raise TicketMaster::Exception.new("This method must be reimplemented in the provider")
127
+ options.insert(0, id) if options.length > 0
128
+ easy_finder(self.class.parent::Ticket, :first, options, 1)
78
129
  end
79
130
 
80
- # Save changes to this project
81
- def save
82
- raise TicketMaster::Exception.new("This method must be reimplemented in the provider")
131
+ # Define some provider specific initalizations
132
+ def initialize(*options)
133
+ # @system_data = {'some' => 'data}
134
+ super(*options)
83
135
  end
84
136
 
85
137
  end