pjdavis-roart 0.1.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.
data/History.txt ADDED
@@ -0,0 +1,2 @@
1
+ == 0.1.0 / 2009-08-13
2
+ 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,66 @@
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
+
49
+ == REQUIREMENTS:
50
+
51
+ * mechanize
52
+ * A working RT3 install.
53
+
54
+ == INSTALL:
55
+
56
+ $ gem sources -a http://gems.github.com
57
+ $ sudo gem install pjdavis-roart
58
+
59
+ == LICENSE:
60
+
61
+ (C) PJ Davis <pj.davis@gmail.com>
62
+
63
+ This program is free software; you can redistribute it and/or modify it under
64
+ the terms of the WTFPL, Version 2, as
65
+ published by Sam Hocevar. See http://sam.zoy.org/wtfpl/ for more details.
66
+
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
@@ -0,0 +1,54 @@
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 get(uri)
29
+ @agent.get(uri).body
30
+ end
31
+
32
+ protected
33
+
34
+ def login
35
+ agent = WWW::Mechanize.new
36
+ page = agent.get(@conf[:server])
37
+ form = page.form('login')
38
+ form.user = @conf[:user]
39
+ form.pass = @conf[:pass]
40
+ page = agent.submit form
41
+ agent
42
+ end
43
+
44
+ def add_methods!
45
+ @conf.each do |key, value|
46
+ (class << self; self; end).send :define_method, key do
47
+ return value
48
+ end
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,11 @@
1
+ class Array
2
+
3
+ def to_hash
4
+ h = Hash.new
5
+ self.each do |element|
6
+ h.update(element.to_sym => nil)
7
+ end
8
+ h
9
+ end
10
+
11
+ end
@@ -0,0 +1,18 @@
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
+ end
@@ -0,0 +1,11 @@
1
+ module Roart
2
+
3
+ class RoartError < StandardError; end
4
+
5
+ class ArgumentError < RoartError; end
6
+
7
+ class TicketSystemError < RoartError; end
8
+
9
+ class TicketSystemInterfaceError < RoartError; end
10
+
11
+ end
@@ -0,0 +1,11 @@
1
+ module Roart
2
+
3
+ def self.check_keys!(hash, required)
4
+ unless required.inject(true) do |inc, attr|
5
+ inc ? hash.keys.include?(attr.to_sym) : nil
6
+ end
7
+ raise ArgumentError, "Not all required fields entered"
8
+ end
9
+ end
10
+
11
+ end
@@ -0,0 +1,280 @@
1
+ module Roart
2
+
3
+ module Tickets
4
+
5
+ DefaultAttributes = %w(queue owner creator subject status priority initial_priority final_priority requestors cc admin_cc created starts started due resolved told last_updated time_estimated time_worked time_left full logs)
6
+ RequiredAttributes = %w(queue creator subject status created)
7
+
8
+ end
9
+
10
+ class Ticket
11
+
12
+ def initialize(attributes)
13
+ Roart::check_keys!(attributes, Roart::Tickets::RequiredAttributes)
14
+ if attributes.is_a?(Hash)
15
+ @attributes = Roart::Tickets::DefaultAttributes.to_hash.merge(attributes)
16
+ else
17
+ raise ArgumentError, "Expects a hash."
18
+ end
19
+ add_methods!
20
+ end
21
+
22
+ # Loads all information for a ticket from RT and lets full to true.
23
+ # This changes the ticket object and adds methods for all the fields on the ticket.
24
+ # Custom fields will be prefixed with 'cf' so a custom field of 'phone'
25
+ # would be cf_phone
26
+ #
27
+ def load_full!
28
+ unless self.full
29
+ ticket = self.class.find(self.id)
30
+ @attributes = ticket.instance_variable_get("@attributes")
31
+ add_methods!
32
+ end
33
+ end
34
+
35
+ class << self #class methods
36
+
37
+ # Gives or Sets the connection object for the RT Server.
38
+ # Accepts 3 parameters :server, :user, and :pass. Call this
39
+ # at the top of your subclass to create the connection,
40
+ # class Ticket < Roart::Ticket
41
+ # connection :server => 'server', :user => 'user', :pass => 'pass'
42
+ # end
43
+ #
44
+ def connection(options=nil)
45
+ if options && @connection.nil?
46
+ @connection = Roart::Connection.new(options)
47
+ else
48
+ @connection
49
+ end
50
+ @connection
51
+ end
52
+
53
+ # Searches for a ticket or group of tickets with an active
54
+ # record like interface.
55
+ #
56
+ # Find has 3 different ways to search for tickets
57
+ #
58
+ # * search for tickets by the id. This will search for the Ticket with the exact id and will automatically load the entire ticket into the object (full will return true).
59
+ # * search for all tickets with a hash for search options by specifying :all along with your options. This will return an array of tickets or an empty array if no tickets are found that match the options.
60
+ # * search for a single ticket with a hash for search options by specifying :first along with your options. This will return a single ticket object or nil if no tickets are found.
61
+ #
62
+ # A hash of options for search paramaters are passed in as the last argument.
63
+ #
64
+ # ====Parameters
65
+ # * <tt>:queue</tt> or <tt>:queues</tt> - the name of a queue in the ticket system. This can be specified as a string, a symbol or an array of strings or symbols. The array will search for tickets included in either queue.
66
+ # * <tt>:status</tt> - the status of the tickets to search for. This can be specified as a string, a symbol or an array of strings or symbols.
67
+ # * <tt>:subject</tt>, <tt>:content</tt>, <tt>content_type</tt>, <tt>file_name</tt> - takes a string and searches for that string in the respective field.
68
+ # * <tt>:created</tt>, <tt>:started</tt>, <tt>:resolved</tt>, <tt>:told</tt>, <tt>:last_updated</tt>, <tt>:starts</tt>, <tt>:due</tt>, <tt>:updated</tt> - looks for dates for the respective fields. Can take a Range, Array, String, Time. Range will find all tickets between the two dates (after the first, before the last). Array works the same way, using #first and #last on the array. The elements should be either db-time formatted strings or Time objects. Time will be formatted as a db string. String will be passed straight to the search.
69
+ # * <tt>:custom_fields</tt> - takes a hash of custom fields to search for. the key should be the name of the field exactly how it is in RT and the value will be what to search for.
70
+ #
71
+ # ==== Examples
72
+ #
73
+ # # find first
74
+ # MyTicket.find(:first)
75
+ # MyTicket.find(:first, :queue => 'My Queue')
76
+ # MyTicket.find(:first, :status => [:new, :open])
77
+ # MyTicket.find(:first, :queue => 'My Queue', :status => :resolved)
78
+ # MyTicket.find(:first, :custom_fields => {:phone => '8675309'})
79
+ #
80
+ # # find all
81
+ # MyTicket.find(:all, :subject => 'is down')
82
+ # MyTicket.find(:all, :created => [Time.now - 300, Time.now])
83
+ # MyTicket.find(:all, :queues => ['my queue', 'issues'])
84
+ #
85
+ # # find by id
86
+ # MyTicket.find(12345)
87
+ #
88
+ def find(*args)
89
+ options = args.last.is_a?(Hash) ? args.pop : {}
90
+ case args.first
91
+ when :first then find_initial(options)
92
+ when :all then find_all(options)
93
+ else find_by_ids(args, options)
94
+ end
95
+ end
96
+
97
+ protected
98
+
99
+ def instantiate(ticket)
100
+ object = nil
101
+ if ticket.is_a?(Array)
102
+ array = Array.new
103
+ ticket.each do |tick|
104
+ object = self.allocate
105
+ object.instance_variable_set("@attributes", tick)
106
+ object.send("add_methods!")
107
+ array << object
108
+ end
109
+ return array
110
+ elsif ticket.is_a?(Hash)
111
+ object = self.allocate
112
+ object.instance_variable_set("@attributes", ticket)
113
+ object.send("add_methods!")
114
+ end
115
+ object
116
+ end
117
+
118
+ def find_initial(options={})
119
+ options.update(:limit => 1)
120
+ find_all(options).first
121
+ end
122
+
123
+ def find_all(options)
124
+ uri = construct_search_uri(options)
125
+ tickets = get_tickets_from_search_uri(uri)
126
+ end
127
+
128
+ def find_by_ids(args, options)
129
+ get_ticket_by_id(args.first)
130
+ end
131
+
132
+ def page_array(uri)
133
+ page = self.connection.get(uri)
134
+ raise TicketSystemError, "Can't get ticket." unless page
135
+ page = page.split("\n")
136
+ status = page.delete_at(0)
137
+ if status.include?("200")
138
+ page.delete_if{|x| !x.include?(":")}
139
+ page
140
+ else
141
+ raise TicketSystemInterfaceError, "Error Getting Ticket: #{status}"
142
+ end
143
+ end
144
+
145
+ def get_tickets_from_search_uri(uri)
146
+ page = page_array(uri)
147
+ page.extend(Roart::TicketPage)
148
+ page = page.to_search_array
149
+ self.instantiate(page)
150
+ end
151
+
152
+ def get_ticket_from_uri(uri)
153
+ page = page_array(uri)
154
+ page.extend(Roart::TicketPage)
155
+ page = page.to_hash
156
+ page.update(:full => true)
157
+ self.instantiate(page)
158
+ end
159
+
160
+ def get_ticket_by_id(id)
161
+ uri = "#{self.connection.server}/REST/1.0/ticket/"
162
+ uri << id.to_s
163
+ get_ticket_from_uri(uri)
164
+ end
165
+
166
+ def construct_search_uri(options)
167
+ uri = "#{self.connection.server}/REST/1.0/search/ticket?"
168
+ uri << 'orderby=-Created&' if options.delete(:order)
169
+ unless options.empty?
170
+ uri << 'query= '
171
+ query = Array.new
172
+
173
+ add_queue!(query, options[:queues] || options[:queue])
174
+ add_dates!(query, options)
175
+ add_searches!(query, options)
176
+ add_status!(query, options[:status])
177
+ add_custom_fields!(query, options[:custom_fields])
178
+
179
+ query << options[:conditions].to_s.chomp if options[:conditions]
180
+
181
+ uri << query.join(" AND ")
182
+ end
183
+ uri
184
+ end
185
+
186
+ def add_queue!(uri, queue)
187
+ return false unless queue
188
+ if queue.is_a?(Array)
189
+ queues = Array.new
190
+ queue.each do |name|
191
+ queues << "Queue = '#{name}'"
192
+ end
193
+ uri << '( ' + queues.join(' OR ') + ' )'
194
+ elsif queue.is_a?(String) || queue.is_a?(Symbol)
195
+ uri << "Queue = '#{queue.to_s}'"
196
+ end
197
+ end
198
+
199
+ def add_custom_fields!(uri, options)
200
+ return false unless options
201
+ options.each do |field, value|
202
+ if value.is_a?(Array)
203
+ valpart = Array.new
204
+ for val in value
205
+ valpart << "'CF.{#{field}}' = '#{val.to_s}'"
206
+ end
207
+ uri << '( ' + valpart.join(" OR ") + ' )'
208
+ elsif value.is_a?(String)
209
+ uri << "'CF.{#{field}}' = '#{value.to_s}'"
210
+ end
211
+ end
212
+ end
213
+
214
+ def add_status!(uri, options)
215
+ return false unless options
216
+ parts = Array.new
217
+ if options.is_a?(Array)
218
+ statpart = Array.new
219
+ for status in options
220
+ statpart << "Status = '#{status.to_s}'"
221
+ end
222
+ parts << '( ' + statpart.join(" OR ") + ' )'
223
+ elsif options.is_a?(String) || options.is_a?(Symbol)
224
+ parts << "Status = '#{options.to_s}'"
225
+ end
226
+ uri << parts
227
+ end
228
+
229
+ def add_searches!(uri, options)
230
+ search_fields = %w( subject content content_type file_name)
231
+ options.each do |key, value|
232
+ if search_fields.include?(key.to_s)
233
+ key = key.to_s.camelize
234
+ parts = Array.new
235
+ if value.is_a?(Array)
236
+ value.each do |v|
237
+ parts << "#{key} LIKE '#{v}'"
238
+ end
239
+ uri << '( ' + parts.join(" AND ") + ' )'
240
+ elsif value.is_a?(String)
241
+ uri << "#{key} LIKE '#{value}'"
242
+ end
243
+ end
244
+ end
245
+ end
246
+
247
+ def add_dates!(uri, options)
248
+ date_field = %w( created started resolved told last_updated starts due updated )
249
+ options.each do |key, value|
250
+ if date_field.include?(key.to_s)
251
+ key = key.to_s.camelize
252
+ parts = Array.new
253
+ if value.is_a?(Range) or value.is_a?(Array)
254
+ parts << "#{key} > '#{value.first.is_a?(Time) ? value.first.strftime("%Y-%m-%d %H:%M:%S") : value.first.to_s}'"
255
+ parts << "#{key} < '#{value.last.is_a?(Time) ? value.last.strftime("%Y-%m-%d %H:%M:%S") : value.last.to_s}'"
256
+ elsif value.is_a?(String)
257
+ parts << "#{key} > '#{value.to_s}'"
258
+ elsif value.is_a?(Time)
259
+ parts << "#{key} > '#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
260
+ end
261
+ uri << '( ' + parts.join(" AND ") + ' )'
262
+ end
263
+ end
264
+ end
265
+
266
+ end
267
+
268
+ protected
269
+
270
+ def add_methods!
271
+ @attributes.each do |key, value|
272
+ (class << self; self; end).send :define_method, key do
273
+ return value
274
+ end
275
+ end
276
+ end
277
+
278
+ end
279
+
280
+ end
@@ -0,0 +1,38 @@
1
+ module Roart
2
+
3
+ module TicketPage
4
+
5
+ def to_hash
6
+ hash = Hash.new
7
+ self.delete_if{|x| !x.include?(":")}
8
+ self.each do |ln|
9
+ ln = ln.split(":")
10
+ key = ln.delete_at(0).strip.underscore
11
+ value = ln.join(":").strip
12
+ hash[key.to_sym] = value
13
+ end
14
+ hash[:id] = hash[:id].split("/").last.to_i
15
+ hash.update(:history => false)
16
+ hash
17
+ end
18
+
19
+ def to_search_array
20
+ array = Array.new
21
+ self.delete_if{|x| !x.include?(":")}
22
+ self.each do |ln|
23
+ hash = Hash.new
24
+ ln = ln.split(":")
25
+ id = ln.delete_at(0).strip.underscore
26
+ sub = ln.join(":").strip
27
+ hash[:id] = id.to_i
28
+ hash[:subject] = sub
29
+ hash[:full] = false
30
+ hash[:history] = false
31
+ array << hash
32
+ end
33
+ array
34
+ end
35
+
36
+ end
37
+
38
+ end
data/lib/roart.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ module Roart
3
+
4
+ # :stopdoc:
5
+ VERSION = '0.1.0'
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
data/roart.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{roart}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["PJ Davis"]
9
+ s.date = %q{2009-08-14}
10
+ s.description = %q{Interface for working with Request Tracker (RT) tickets inspired by ActiveRecord.}
11
+ s.email = %q{pj.davis@gmail.com}
12
+ s.extra_rdoc_files = ["History.txt", "README.rdoc"]
13
+ s.files = ["History.txt", "README.rdoc", "Rakefile", "lib/roart.rb", "lib/roart/connection.rb", "lib/roart/core/array.rb", "lib/roart/core/string.rb", "lib/roart/errors.rb", "lib/roart/roart.rb", "lib/roart/ticket.rb", "lib/roart/ticket_page.rb", "roart.gemspec", "spec/roart/connection_spec.rb", "spec/roart/core/array_spec.rb", "spec/roart/core/string_spec.rb", "spec/roart/roart_spec.rb", "spec/roart/ticket_page_spec.rb", "spec/roart/ticket_spec.rb", "spec/roart_spec.rb", "spec/spec_helper.rb", "test/test_roart.rb"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://github.com/pjdavis/roart}
16
+ s.rdoc_options = ["--main", "README.rdoc"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{roart}
19
+ s.rubygems_version = %q{1.3.2}
20
+ s.summary = %q{Interface for working with Request Tracker (RT) tickets inspired by ActiveRecord}
21
+ s.test_files = ["test/test_roart.rb"]
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ s.add_runtime_dependency(%q<mechanize>, [">= 0.9.0"])
29
+ s.add_development_dependency(%q<bones>, [">= 2.5.1"])
30
+ else
31
+ s.add_dependency(%q<mechanize>, [">= 0.9.0"])
32
+ s.add_dependency(%q<bones>, [">= 2.5.1"])
33
+ end
34
+ else
35
+ s.add_dependency(%q<mechanize>, [">= 0.9.0"])
36
+ s.add_dependency(%q<bones>, [">= 2.5.1"])
37
+ end
38
+ end
@@ -0,0 +1,7 @@
1
+
2
+ require File.join(File.dirname(__FILE__), %w[ .. spec_helper])
3
+
4
+ describe "Connection" do
5
+
6
+
7
+ end
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__), %w[ .. .. spec_helper])
2
+
3
+ describe 'array extentions' do
4
+
5
+ it 'should turn an array to a nil valued hash' do
6
+ array = %w[key1 key2]
7
+ hash = array.to_hash
8
+ hash.has_key?(:key1).should be_true
9
+ hash.has_key?(:key2).should be_true
10
+ hash[:key1].should be_nil
11
+ end
12
+
13
+ end
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__), %w[ .. .. spec_helper])
2
+
3
+ describe 'string extentions' do
4
+
5
+ it 'should underscore a word' do
6
+ 'SomeGuy'.underscore.should == 'some_guy'
7
+ end
8
+
9
+ it 'should camelcase a word' do
10
+ 'some_guy'.camelize.should == 'SomeGuy'
11
+ end
12
+
13
+ end
@@ -0,0 +1,11 @@
1
+
2
+ require File.join(File.dirname(__FILE__), %w[ .. spec_helper])
3
+
4
+ describe "Roart" do
5
+
6
+ it "should raise an error if there aren't required fields" do
7
+ attributes = {:subject => "Not Enough Stuff"}
8
+ lambda { Roart::check_keys!(attributes, Roart::Tickets::RequiredAttributes) }.should raise_error(Roart::ArgumentError)
9
+ end
10
+
11
+ end
@@ -0,0 +1,49 @@
1
+ require File.join(File.dirname(__FILE__), %w[ .. spec_helper])
2
+
3
+ describe 'ticket page' do
4
+
5
+ describe 'ticket hash' do
6
+
7
+ it 'should convert an array to a hash' do
8
+ array = ["id : 10", "subject : asdf"]
9
+ array.extend(Roart::TicketPage)
10
+ hash = array.to_hash
11
+ hash.has_key?(:id).should be_true
12
+ hash[:id].should == 10
13
+ hash[:subject].should == 'asdf'
14
+ end
15
+
16
+ it 'should not have a history' do
17
+ array = ["id : 10", "subject : asdf"]
18
+ array.extend(Roart::TicketPage)
19
+ hash = array.to_hash
20
+ hash[:history].should be_false
21
+ end
22
+
23
+ end
24
+
25
+ describe 'search array' do
26
+
27
+ before do
28
+ @array = ["123 : Subject", "234 : Subject"]
29
+ @array.extend(Roart::TicketPage)
30
+ @array = @array.to_search_array
31
+ end
32
+
33
+ it 'should make an array of search results' do
34
+ @array.size.should == 2
35
+ end
36
+
37
+ it 'should put search elements into the search array' do
38
+ @array.first[:id].should == 123
39
+ @array.last[:id].should == 234
40
+ end
41
+
42
+ it 'should keep history false' do
43
+ @array.first[:history].should be_false
44
+ @array.last[:history].should be_false
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,299 @@
1
+ require File.join(File.dirname(__FILE__), %w[ .. spec_helper])
2
+
3
+ describe "Ticket" do
4
+
5
+ it "should have a connection" do
6
+ Roart::Connection.should_receive(:new).with('some options').and_return(true)
7
+ Roart::Ticket.connection('some options').should == true
8
+ end
9
+
10
+ it "should find the first ticket" do
11
+ Roart::Ticket.should_receive(:find_initial).with(:my => 'options').and_return('a ticket')
12
+ Roart::Ticket.find(:first, :my => 'options').should == 'a ticket'
13
+ end
14
+
15
+ it 'should find all tickets' do
16
+ Roart::Ticket.should_receive(:find_all).with(:my => 'options').and_return(%w[array of tickets])
17
+ Roart::Ticket.find(:all, :my => 'options').should == ['array', 'of', 'tickets']
18
+ end
19
+
20
+ it 'should find a ticket by id' do
21
+ Roart::Ticket.should_receive(:find_by_ids).with([12345],{}).and_return('a ticket')
22
+ Roart::Ticket.find(12345).should == 'a ticket'
23
+ end
24
+
25
+ describe "find initial" do
26
+
27
+ it 'should set options to include :limit => 1' do
28
+ Roart::Ticket.should_receive(:find_all).with({:limit => 1}).and_return(['ticket', 'not seen'])
29
+ Roart::Ticket.send(:find_initial)
30
+ end
31
+
32
+ it 'should not overwrite the options hash' do
33
+ Roart::Ticket.should_receive(:find_all).with({:queue => 'queue', :limit => 1}).and_return(['ticket', 'not seen'])
34
+ Roart::Ticket.send(:find_initial, :queue => 'queue')
35
+ end
36
+
37
+ it 'should return 1 ticket object' do
38
+ Roart::Ticket.should_receive(:find_all).with({:limit => 1}).and_return(['ticket', 'not seen'])
39
+ Roart::Ticket.send(:find_initial).should == 'ticket'
40
+ end
41
+
42
+ end
43
+
44
+ describe 'page array' do
45
+
46
+ it 'should raise an error if not a 200 response' do
47
+ connection = mock('connection', :get => 'some error message')
48
+ Roart::Ticket.should_receive(:connection).and_return(connection)
49
+ lambda do
50
+ Roart::Ticket.send(:page_array, 'www.whatever')
51
+ end.should raise_error(Roart::TicketSystemInterfaceError)
52
+ end
53
+
54
+ it 'should raise an error if nothing is returned' do
55
+ connection = mock('connection', :get => nil)
56
+ Roart::Ticket.should_receive(:connection).and_return(connection)
57
+ lambda do
58
+ Roart::Ticket.send(:page_array, 'www.whatever')
59
+ end.should raise_error(Roart::TicketSystemError)
60
+ end
61
+
62
+ it 'should give back an array of strings' do
63
+ connection = mock('connection', :get => "200 OK\n23:SomeTicket\n33:Another")
64
+ Roart::Ticket.should_receive(:connection).and_return(connection)
65
+ Roart::Ticket.send(:page_array, 'uri').should == ['23:SomeTicket', '33:Another']
66
+ end
67
+
68
+ end
69
+
70
+ describe 'getting tickets from URI' do
71
+
72
+ describe 'search' do
73
+
74
+ it 'should give an array of tickets' do
75
+ Roart::Ticket.should_receive(:page_array).with('uri').and_return(['23:SomeTicket', '33:Another'])
76
+ Roart::Ticket.send(:get_tickets_from_search_uri, 'uri').size.should == 2
77
+ end
78
+
79
+ it 'should not have full tickets' do
80
+ Roart::Ticket.should_receive(:page_array).with('uri').and_return(['23:SomeTicket', '33:Another'])
81
+ Roart::Ticket.send(:get_tickets_from_search_uri, 'uri').each do |ticket|
82
+ ticket.full.should_not be_true
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+ describe 'full ticket' do
89
+
90
+ it 'should give a ticket' do
91
+ Roart::Ticket.should_receive(:page_array).with('uri').and_return(['id:23', 'subject:someticket'])
92
+ Roart::Ticket.send(:get_ticket_from_uri, 'uri').is_a?(Roart::Ticket).should be_true
93
+ end
94
+
95
+ it 'should be a full ticket' do
96
+ Roart::Ticket.should_receive(:page_array).with('uri').and_return(['id:23', 'subject:someticket'])
97
+ Roart::Ticket.send(:get_ticket_from_uri, 'uri').full.should be_true
98
+ end
99
+
100
+ end
101
+
102
+ describe 'getting ticket from id' do
103
+
104
+ it 'should give a ticket' do
105
+ connection = mock('connection', :server => "server")
106
+ Roart::Ticket.should_receive(:connection).and_return(connection)
107
+ Roart::Ticket.should_receive(:get_ticket_from_uri).with('server' + '/REST/1.0/ticket/' + 12345.to_s).and_return('a ticket')
108
+ Roart::Ticket.send(:get_ticket_by_id, 12345).should == 'a ticket'
109
+ end
110
+
111
+ end
112
+
113
+ describe 'find methods' do
114
+
115
+ it 'should get all tickets from an options hash' do
116
+ Roart::Ticket.should_receive('construct_search_uri').with('searches').and_return('uri')
117
+ Roart::Ticket.should_receive('get_tickets_from_search_uri').with('uri').and_return(['tickets'])
118
+ Roart::Ticket.send(:find_all, 'searches').should == ['tickets']
119
+ end
120
+
121
+ it 'should find tickets from id' do
122
+ Roart::Ticket.should_receive(:get_ticket_by_id).with('id').and_return('a ticket')
123
+ Roart::Ticket.send(:find_by_ids, ['id'], {}).should == 'a ticket'
124
+ end
125
+
126
+ end
127
+
128
+ describe 'building a search uri' do
129
+
130
+ before do
131
+ @connection = mock('connection', :server => "server")
132
+ @search_string = "/REST/1.0/search/ticket?"
133
+ Roart::Ticket.should_receive(:connection).and_return(@connection)
134
+ @query = @connection.server + @search_string + 'query= '
135
+ end
136
+
137
+ describe 'queues' do
138
+
139
+ it 'should include a queue' do
140
+ Roart::Ticket.send(:construct_search_uri, {:queue => 'myQueue'}).should == @query + "Queue = 'myQueue'"
141
+ end
142
+
143
+ it 'should take multiple queues' do
144
+ Roart::Ticket.send(:construct_search_uri, {:queue => ['myQueue', 'another']}).should == @query + "( Queue = 'myQueue' OR Queue = 'another' )"
145
+ end
146
+
147
+ end
148
+
149
+ describe 'dates' do
150
+
151
+ before do
152
+ @time = Time.now
153
+ end
154
+
155
+ it 'should accept a date' do
156
+ Roart::Ticket.send(:construct_search_uri, {:created => @time}).should == @query + "( Created > '#{dbtime(@time)}' )"
157
+ end
158
+
159
+ it 'should accept an array of dates' do
160
+ Roart::Ticket.send(:construct_search_uri, {:created => [@time, @time + 300]}).should == @query + "( Created > '#{dbtime(@time)}' AND Created < '#{dbtime(@time + 300)}' )"
161
+ end
162
+
163
+ it 'should accept a range of dates' do
164
+ Roart::Ticket.send(:construct_search_uri, {:created => (@time..(@time + 300))}).should == @query + "( Created > '#{dbtime(@time)}' AND Created < '#{dbtime(@time + 300)}' )"
165
+ end
166
+
167
+ it 'should accept an array of strings' do
168
+ Roart::Ticket.send(:construct_search_uri, {:created => %w[cat dog]}).should == @query + "( Created > 'cat' AND Created < 'dog' )"
169
+ end
170
+
171
+ it 'should accept a string' do
172
+ Roart::Ticket.send(:construct_search_uri, {:created => 'time'}).should == @query + "( Created > 'time' )"
173
+ end
174
+
175
+ describe 'date fields' do
176
+
177
+ it 'should search started' do
178
+ Roart::Ticket.send(:construct_search_uri, {:started => 'time'}).should == @query + "( Started > 'time' )"
179
+ end
180
+
181
+ it 'should search resolved' do
182
+ Roart::Ticket.send(:construct_search_uri, {:resolved => 'time'}).should == @query + "( Resolved > 'time' )"
183
+ end
184
+
185
+ it 'should search told' do
186
+ Roart::Ticket.send(:construct_search_uri, {:told => 'time'}).should == @query + "( Told > 'time' )"
187
+ end
188
+
189
+ it 'should search last_updated' do
190
+ Roart::Ticket.send(:construct_search_uri, {:last_updated => 'time'}).should == @query + "( LastUpdated > 'time' )"
191
+ end
192
+
193
+ it 'should search starts' do
194
+ Roart::Ticket.send(:construct_search_uri, {:starts => 'time'}).should == @query + "( Starts > 'time' )"
195
+ end
196
+
197
+ it 'should search due' do
198
+ Roart::Ticket.send(:construct_search_uri, {:due => 'time'}).should == @query + "( Due > 'time' )"
199
+ end
200
+
201
+ it 'should search updated' do
202
+ Roart::Ticket.send(:construct_search_uri, {:updated => 'time'}).should == @query + "( Updated > 'time' )"
203
+ end
204
+
205
+ end
206
+
207
+ end
208
+
209
+ describe 'searches' do
210
+
211
+ it 'should accept a string' do
212
+ Roart::Ticket.send(:construct_search_uri, {:subject => 'fish'}).should == @query + "Subject LIKE 'fish'"
213
+ end
214
+
215
+ it 'should accept an array' do
216
+ Roart::Ticket.send(:construct_search_uri, {:subject => %w[cramanation station]}).should == @query + "( Subject LIKE 'cramanation' AND Subject LIKE 'station' )"
217
+ end
218
+
219
+ describe 'search fields' do
220
+
221
+ it 'should search content' do
222
+ Roart::Ticket.send(:construct_search_uri, {:content => 'fish'}).should == @query + "Content LIKE 'fish'"
223
+ end
224
+
225
+ it 'should search content_type' do
226
+ Roart::Ticket.send(:construct_search_uri, {:content_type => 'fish'}).should == @query + "ContentType LIKE 'fish'"
227
+ end
228
+
229
+ it 'should search file_name' do
230
+ Roart::Ticket.send(:construct_search_uri, {:file_name => 'fish'}).should == @query + "FileName LIKE 'fish'"
231
+ end
232
+
233
+ end
234
+
235
+ end
236
+
237
+ describe 'status' do
238
+
239
+ it 'should accept a string' do
240
+ Roart::Ticket.send(:construct_search_uri, {:status => 'new'}).should == @query + "Status = 'new'"
241
+ end
242
+
243
+ it 'should accept a symbol' do
244
+ Roart::Ticket.send(:construct_search_uri, {:status => :new}).should == @query + "Status = 'new'"
245
+ end
246
+
247
+ it 'should accept an array' do
248
+ Roart::Ticket.send(:construct_search_uri, {:status => ['new', 'open']}).should == @query + "( Status = 'new' OR Status = 'open' )"
249
+ end
250
+
251
+ end
252
+
253
+ describe 'custom_fields' do
254
+
255
+ it 'should accept a hash with string values' do
256
+ Roart::Ticket.send(:construct_search_uri, {:custom_fields => {:phone => '8675309'}}).should == @query + "'CF.{phone}' = '8675309'"
257
+ end
258
+
259
+ it 'should accept a hash with string values' do
260
+ Roart::Ticket.send(:construct_search_uri, {:custom_fields => {:phone => ['8675309', '5553265']}}).should == @query + "( 'CF.{phone}' = '8675309' OR 'CF.{phone}' = '5553265' )"
261
+ end
262
+
263
+ end
264
+
265
+ describe 'multiple find options' do
266
+
267
+ it 'should allow multiple find options' do
268
+ query = Roart::Ticket.send(:construct_search_uri, {:status => 'new', :queue => 'MyQueue'})
269
+ query.include?("Status = 'new'").should be_true
270
+ query.include?("Queue = 'MyQueue'").should be_true
271
+ end
272
+
273
+ it "should search for :queue => 'A Queue', :status => [:new, :open]" do
274
+ query = Roart::Ticket.send(:construct_search_uri, {:queue => 'A Queue', :status => [:new, :open]})
275
+ query.include?("( Status = 'new' OR Status = 'open' )").should be_true
276
+ query.include?("Queue = 'A Queue'").should be_true
277
+ end
278
+
279
+ end
280
+
281
+ end
282
+
283
+ end
284
+
285
+ describe 'ticket methods' do
286
+
287
+ it 'should be able to load the full ticket' do
288
+ search_array = ['1:subject']
289
+ search_array.extend(Roart::TicketPage)
290
+ full_ticket = Roart::Ticket.send(:instantiate, {:id => 1, :subject => 'subject', :full => true})
291
+ ticket = Roart::Ticket.send(:instantiate, search_array.to_search_array ).first
292
+ Roart::Ticket.should_receive(:find).with(1).and_return(full_ticket)
293
+ ticket.load_full!
294
+ ticket.full.should be_true
295
+ end
296
+
297
+ end
298
+
299
+ end
@@ -0,0 +1,8 @@
1
+
2
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
3
+
4
+ describe Roart do
5
+
6
+ end
7
+
8
+ # EOF
@@ -0,0 +1,22 @@
1
+
2
+ dir = File.dirname(__FILE__)
3
+
4
+ $:.unshift(File.join(dir, '/../lib/'))
5
+ require dir + '/../lib/roart'
6
+
7
+ def dbtime(time)
8
+ time.strftime("%Y-%m-%d %H:%M:%S")
9
+ end
10
+
11
+ Spec::Runner.configure do |config|
12
+ # == Mock Framework
13
+ #
14
+ # RSpec uses it's own mocking framework by default. If you prefer to
15
+ # use mocha, flexmock or RR, uncomment the appropriate line:
16
+ #
17
+ # config.mock_with :mocha
18
+ # config.mock_with :flexmock
19
+ # config.mock_with :rr
20
+ end
21
+
22
+ # EOF
File without changes
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pjdavis-roart
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - PJ Davis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-14 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: mechanize
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.9.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: bones
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.5.1
34
+ version:
35
+ description: Interface for working with Request Tracker (RT) tickets inspired by ActiveRecord.
36
+ email: pj.davis@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - History.txt
43
+ - README.rdoc
44
+ files:
45
+ - History.txt
46
+ - README.rdoc
47
+ - Rakefile
48
+ - lib/roart.rb
49
+ - lib/roart/connection.rb
50
+ - lib/roart/core/array.rb
51
+ - lib/roart/core/string.rb
52
+ - lib/roart/errors.rb
53
+ - lib/roart/roart.rb
54
+ - lib/roart/ticket.rb
55
+ - lib/roart/ticket_page.rb
56
+ - roart.gemspec
57
+ - spec/roart/connection_spec.rb
58
+ - spec/roart/core/array_spec.rb
59
+ - spec/roart/core/string_spec.rb
60
+ - spec/roart/roart_spec.rb
61
+ - spec/roart/ticket_page_spec.rb
62
+ - spec/roart/ticket_spec.rb
63
+ - spec/roart_spec.rb
64
+ - spec/spec_helper.rb
65
+ - test/test_roart.rb
66
+ has_rdoc: true
67
+ homepage: http://github.com/pjdavis/roart
68
+ licenses:
69
+ post_install_message:
70
+ rdoc_options:
71
+ - --main
72
+ - README.rdoc
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ version:
87
+ requirements: []
88
+
89
+ rubyforge_project: roart
90
+ rubygems_version: 1.3.5
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: Interface for working with Request Tracker (RT) tickets inspired by ActiveRecord
94
+ test_files:
95
+ - test/test_roart.rb