roart 0.1.4
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.
- data/History.txt +17 -0
- data/README.rdoc +85 -0
- data/Rakefile +35 -0
- data/lib/roart.rb +49 -0
- data/lib/roart/callbacks.rb +25 -0
- data/lib/roart/connection.rb +62 -0
- data/lib/roart/core/array.rb +11 -0
- data/lib/roart/core/hash.rb +15 -0
- data/lib/roart/core/string.rb +21 -0
- data/lib/roart/errors.rb +11 -0
- data/lib/roart/history.rb +115 -0
- data/lib/roart/roart.rb +27 -0
- data/lib/roart/ticket.rb +381 -0
- data/lib/roart/ticket_page.rb +65 -0
- data/roart.gemspec +37 -0
- data/spec/roart/callbacks_spec.rb +49 -0
- data/spec/roart/connection_spec.rb +7 -0
- data/spec/roart/core/array_spec.rb +13 -0
- data/spec/roart/core/hash_spec.rb +16 -0
- data/spec/roart/core/string_spec.rb +13 -0
- data/spec/roart/history_spec.rb +60 -0
- data/spec/roart/roart_spec.rb +11 -0
- data/spec/roart/ticket_page_spec.rb +59 -0
- data/spec/roart/ticket_spec.rb +440 -0
- data/spec/roart_spec.rb +8 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/test_data/full_history.txt +126 -0
- data/spec/test_data/single_history.txt +26 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- metadata +118 -0
data/History.txt
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
==0.1.4 / 2009-08-19
|
2
|
+
Implemented callbacks, you can now hook into the ticket life-cycle with before and after create and update.
|
3
|
+
Saving works like ActiveRecord now, with save returning true if successful and false otherwise, and save! raising an error if unsuccessful.
|
4
|
+
Creating tickets is more ActiveRecord like now, with new not saving to the ticketing system automatically, you must call save or save!. There is a create function to do this now. Although this does break backward compatability, I'm not going to bump the minor because a) nobody has forked this yet, and b) i skipped 0.
|
5
|
+
You can now specify a default queue when you subclass Roart::Ticket. just add a default_queue 'queue name' to your class and that queue will automatically be searched unless you specify a queue.
|
6
|
+
|
7
|
+
==0.1.3 / 2009-08-18
|
8
|
+
Can now update tickets with #save. Returns true if saved, raises an error if not.
|
9
|
+
|
10
|
+
==0.1.2 / 2009-08-18
|
11
|
+
Added Create ticket functionality. Ticket.new will create a ticket in the RT system and return that ticket.
|
12
|
+
|
13
|
+
== 0.1.1 / 2009-08-17
|
14
|
+
Added History functionality. #histories now loads an array of history objects into memory and you can access them like you would a regular array. A few connivence methods (last, count) have also been added.
|
15
|
+
|
16
|
+
== 0.1.0 / 2009-08-13
|
17
|
+
Initial Commit. Base functionality added. Can search for tickets, and see all info on tickets. Ticket History not yet implemented.
|
data/README.rdoc
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
== Roart
|
2
|
+
|
3
|
+
\___\__o/
|
4
|
+
/ \
|
5
|
+
|
6
|
+
|
7
|
+
by PJ Davis
|
8
|
+
http://github.com/pjdavis/roart
|
9
|
+
|
10
|
+
== DESCRIPTION:
|
11
|
+
If you are using Best Practical's Request Tracker (RT) and you need to interact with tickets from other applications, Roart provides an interface that is slightly reminiscent of ActiveRecord.
|
12
|
+
|
13
|
+
|
14
|
+
== FEATURES/PROBLEMS:
|
15
|
+
|
16
|
+
* Access RT Tickets through an ActiveRecord like API
|
17
|
+
|
18
|
+
* This has only been tested against RT 3.6. Changes to the REST interface in later versions of RT may break stuff.
|
19
|
+
|
20
|
+
== SYNOPSIS:
|
21
|
+
|
22
|
+
* Create a class to interact with your ticket system
|
23
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require 'roart'
|
26
|
+
|
27
|
+
class Ticket < Roart::Ticket
|
28
|
+
connection :server => 'http://my.rt.server.com', :user => 'myuser', :pass => 'mypass'
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
* Search for tickets
|
33
|
+
|
34
|
+
my_tickets = Ticket.find(:all, :queue => 'Scutters', :status => [:new, :open])
|
35
|
+
my_tickets.each do |ticket|
|
36
|
+
puts ticket.subject
|
37
|
+
end
|
38
|
+
|
39
|
+
#-> New John Wayne packages
|
40
|
+
#-> Medi-lab training
|
41
|
+
|
42
|
+
* See all info for a ticket
|
43
|
+
|
44
|
+
my_ticket = Ticket.find(:first, :queue => 'Issues', :status => :new)
|
45
|
+
ticket.creator #-> rimmer@reddwarf.com
|
46
|
+
ticket.subject #-> Where is the Bomb?
|
47
|
+
|
48
|
+
* Get history for a ticket
|
49
|
+
|
50
|
+
my_ticket.histories #-> Returns an array of history objects
|
51
|
+
|
52
|
+
* Create a new ticket
|
53
|
+
|
54
|
+
issue = Ticket.new(:queue => 'some_queue', :subject => 'This is not working for me')
|
55
|
+
issue.id #-> 'ticket/new'
|
56
|
+
issue.save
|
57
|
+
issue.id #-> 23423
|
58
|
+
|
59
|
+
* Update a ticket
|
60
|
+
|
61
|
+
ticket = Ticket.find(23452)
|
62
|
+
ticket.subject #-> "Some Subject for a ticket."
|
63
|
+
ticket.subject #-> "Smoke me a kipper, I'll be back for breakfast."
|
64
|
+
ticket.save
|
65
|
+
ticket.subject #->"Smoke me a kipper, I'll be back for breakfast."
|
66
|
+
|
67
|
+
|
68
|
+
== REQUIREMENTS:
|
69
|
+
|
70
|
+
* mechanize
|
71
|
+
* A working RT3 install.
|
72
|
+
|
73
|
+
== INSTALL:
|
74
|
+
|
75
|
+
$ gem sources -a http://gems.github.com
|
76
|
+
$ sudo gem install pjdavis-roart
|
77
|
+
|
78
|
+
== LICENSE:
|
79
|
+
|
80
|
+
(C) PJ Davis <pj.davis@gmail.com>
|
81
|
+
|
82
|
+
This program is free software; you can redistribute it and/or modify it under
|
83
|
+
the terms of the WTFPL, Version 2, as
|
84
|
+
published by Sam Hocevar. See http://sam.zoy.org/wtfpl/ for more details.
|
85
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Look in the tasks/setup.rb file for the various options that can be
|
2
|
+
# configured in this Rakefile. The .rake files in the tasks directory
|
3
|
+
# are where the options are used.
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bones'
|
7
|
+
Bones.setup
|
8
|
+
rescue LoadError
|
9
|
+
begin
|
10
|
+
load 'tasks/setup.rb'
|
11
|
+
rescue LoadError
|
12
|
+
raise RuntimeError, '### please install the "bones" gem ###'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
ensure_in_path 'lib'
|
17
|
+
require 'roart'
|
18
|
+
|
19
|
+
task :default => 'spec:run'
|
20
|
+
|
21
|
+
PROJ.name = 'roart'
|
22
|
+
PROJ.ignore_file = '.gitignore'
|
23
|
+
PROJ.authors = 'PJ Davis'
|
24
|
+
PROJ.email = 'pj.davis@gmail.com'
|
25
|
+
PROJ.url = 'http://github.com/pjdavis/roart'
|
26
|
+
PROJ.version = Roart::VERSION
|
27
|
+
PROJ.rubyforge.name = 'roart'
|
28
|
+
PROJ.exclude = %w(.git pkg coverage)
|
29
|
+
PROJ.description = "Interface for working with Request Tracker (RT) tickets inspired by ActiveRecord."
|
30
|
+
PROJ.rdoc.main = 'README.rdoc'
|
31
|
+
depend_on 'mechanize'
|
32
|
+
|
33
|
+
PROJ.spec.opts << '--color'
|
34
|
+
|
35
|
+
# EOF
|
data/lib/roart.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
module Roart
|
3
|
+
|
4
|
+
# :stopdoc:
|
5
|
+
VERSION = '0.1.4'
|
6
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
7
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
8
|
+
# :startdoc:
|
9
|
+
|
10
|
+
# Returns the version string for the library.
|
11
|
+
#
|
12
|
+
def self.version
|
13
|
+
VERSION
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the library path for the module. If any arguments are given,
|
17
|
+
# they will be joined to the end of the libray path using
|
18
|
+
# <tt>File.join</tt>.
|
19
|
+
#
|
20
|
+
def self.libpath( *args )
|
21
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the lpath for the module. If any arguments are given,
|
25
|
+
# they will be joined to the end of the path using
|
26
|
+
# <tt>File.join</tt>.
|
27
|
+
#
|
28
|
+
def self.path( *args )
|
29
|
+
args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Utility method used to require all files ending in .rb that lie in the
|
33
|
+
# directory below this file that has the same name as the filename passed
|
34
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
35
|
+
# the _filename_ does not have to be equivalent to the directory.
|
36
|
+
#
|
37
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
38
|
+
dir ||= ::File.basename(fname, '.*')
|
39
|
+
search_me = ::File.expand_path(
|
40
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
41
|
+
|
42
|
+
Dir.glob(search_me).sort.each {|rb| require rb}
|
43
|
+
end
|
44
|
+
|
45
|
+
end # module Roart
|
46
|
+
|
47
|
+
Roart.require_all_libs_relative_to(__FILE__)
|
48
|
+
|
49
|
+
# EOF
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Roart
|
2
|
+
|
3
|
+
# Callbacks are implemented to do a bit of logic either before or after a part of the object life cycle. These can be overridden in your Ticket class and will be called at the approprate times.
|
4
|
+
#
|
5
|
+
module Callbacks
|
6
|
+
|
7
|
+
# called just before a ticket that has not been saved to the ticketing system is saved.
|
8
|
+
#
|
9
|
+
def before_create; end
|
10
|
+
|
11
|
+
# Called immediately a ticket that has not been saved is saved.
|
12
|
+
#
|
13
|
+
def before_update; end
|
14
|
+
|
15
|
+
# called just before a ticket that has been updated is saved to the ticketing system
|
16
|
+
#
|
17
|
+
def after_create; end
|
18
|
+
|
19
|
+
# called just after a ticket that has been updated is saved to the ticketing system
|
20
|
+
#
|
21
|
+
def after_update; end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'mechanize'
|
3
|
+
|
4
|
+
module Roart
|
5
|
+
|
6
|
+
module Connections
|
7
|
+
RequiredConfig = %w(server user pass)
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
class Connection
|
12
|
+
|
13
|
+
attr_reader :agent
|
14
|
+
|
15
|
+
def initialize(conf)
|
16
|
+
|
17
|
+
if conf.is_a?(String)
|
18
|
+
raise "Loading Config File not yet implemented"
|
19
|
+
elsif conf.is_a?(Hash)
|
20
|
+
Roart::check_keys!(conf, Roart::Connections::RequiredConfig)
|
21
|
+
@conf = conf
|
22
|
+
end
|
23
|
+
|
24
|
+
@agent = login
|
25
|
+
add_methods!
|
26
|
+
end
|
27
|
+
|
28
|
+
def rest_path
|
29
|
+
self.server + '/REST/1.0/'
|
30
|
+
end
|
31
|
+
|
32
|
+
def get(uri)
|
33
|
+
@agent.get(uri).body
|
34
|
+
end
|
35
|
+
|
36
|
+
def post(uri, payload)
|
37
|
+
@agent.post(uri, payload).body
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def login
|
43
|
+
agent = WWW::Mechanize.new
|
44
|
+
page = agent.get(@conf[:server])
|
45
|
+
form = page.form('login')
|
46
|
+
form.user = @conf[:user]
|
47
|
+
form.pass = @conf[:pass]
|
48
|
+
page = agent.submit form
|
49
|
+
agent
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_methods!
|
53
|
+
@conf.each do |key, value|
|
54
|
+
(class << self; self; end).send :define_method, key do
|
55
|
+
return value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Hash
|
2
|
+
def to_content_format
|
3
|
+
fields = self.map do |key,value|
|
4
|
+
unless value.nil?
|
5
|
+
if key.to_s.match(/^cf_.+/)
|
6
|
+
"CF-#{key.to_s[3..key.to_s.length].camelize.humanize}: #{value}"
|
7
|
+
else
|
8
|
+
"#{key.to_s.camelize}: #{value}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
content = fields.compact.join("\n")
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# used from ActiveSupport
|
2
|
+
# Copyright (c) 2005-2009 David Heinemeier Hansson
|
3
|
+
|
4
|
+
class String
|
5
|
+
|
6
|
+
def underscore
|
7
|
+
self.gsub(/::/, '/').
|
8
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
9
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
10
|
+
tr("-", "_").
|
11
|
+
downcase
|
12
|
+
end
|
13
|
+
|
14
|
+
def camelize
|
15
|
+
self.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
16
|
+
end
|
17
|
+
|
18
|
+
def humanize
|
19
|
+
self.gsub(/_id$/, "").gsub(/_/, " ").capitalize
|
20
|
+
end
|
21
|
+
end
|
data/lib/roart/errors.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
module Roart
|
2
|
+
|
3
|
+
module Histories
|
4
|
+
|
5
|
+
DefaultAttributes = %w(creator type description content created)
|
6
|
+
RequiredAttributes = %w(creator type)
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
class HistoryArray < Array
|
11
|
+
|
12
|
+
def ticket
|
13
|
+
@default_options[:ticket]
|
14
|
+
end
|
15
|
+
|
16
|
+
def all
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def count
|
21
|
+
self.size
|
22
|
+
end
|
23
|
+
|
24
|
+
def last
|
25
|
+
self[self.size - 1]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
class History
|
31
|
+
|
32
|
+
#TODO Figure out why i can't include Roart::MethodFunctions
|
33
|
+
def add_methods!
|
34
|
+
@attributes.each do |key, value|
|
35
|
+
(class << self; self; end).send :define_method, key do
|
36
|
+
return value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class << self
|
42
|
+
|
43
|
+
def default(options)
|
44
|
+
history = self.dup
|
45
|
+
history.instance_variable_set("@default_options", options)
|
46
|
+
history.all
|
47
|
+
end
|
48
|
+
|
49
|
+
def ticket
|
50
|
+
@default_options[:ticket]
|
51
|
+
end
|
52
|
+
|
53
|
+
def all
|
54
|
+
@histories ||= get_all
|
55
|
+
end
|
56
|
+
|
57
|
+
def default_options
|
58
|
+
@default_options
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def instantiate(attrs)
|
64
|
+
object = nil
|
65
|
+
if attrs.is_a?(Array)
|
66
|
+
array = Array.new
|
67
|
+
attrs.each do |attr|
|
68
|
+
object = self.allocate
|
69
|
+
object.instance_variable_set("@attributes", attr.merge(self.default_options))
|
70
|
+
object.send("add_methods!")
|
71
|
+
array << object
|
72
|
+
end
|
73
|
+
return array
|
74
|
+
elsif attrs.is_a?(Hash)
|
75
|
+
object = self.allocate
|
76
|
+
object.instance_variable_set("@attributes", attrs.merge(self.default_options))
|
77
|
+
object.send("add_methods!")
|
78
|
+
end
|
79
|
+
object
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_all
|
83
|
+
page = get_page
|
84
|
+
raise TicketSystemError, "Can't get history." unless page
|
85
|
+
raise TicketSystemInterfaceError, "Error getting history for Ticket: #{ticket.id}." unless page.split("\n")[0].include?("200")
|
86
|
+
history_array = get_histories_from_page(page)
|
87
|
+
history_array
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_histories_from_page(page)
|
91
|
+
full_history = HistoryArray.new
|
92
|
+
for history in page.split(/^--$/)
|
93
|
+
history = history.split("\n")
|
94
|
+
history.extend(Roart::TicketPage)
|
95
|
+
full_history << self.instantiate(history.to_history_hash)
|
96
|
+
end
|
97
|
+
full_history.instance_variable_set("@default_options", @default_options)
|
98
|
+
full_history
|
99
|
+
end
|
100
|
+
|
101
|
+
def get_page
|
102
|
+
@default_options[:ticket].class.connection.get(uri_for(@default_options[:ticket]))
|
103
|
+
end
|
104
|
+
|
105
|
+
def uri_for(ticket)
|
106
|
+
uri = self.default_options[:ticket].class.connection.rest_path + "ticket/#{ticket.id}/history?format=l"
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|