ticketmaster 0.1.0 → 0.3.1

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.
@@ -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