austinmoody-fogbugz-api 0.0.2
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/LICENSE +22 -0
- data/README.rdoc +28 -0
- data/TODO +4 -0
- data/fogbugz-api.rb +1 -0
- data/lib/fogbugz-api.rb +375 -0
- metadata +71 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2008 Austin Moody
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
= Ruby FogBugz API Wrapper
|
2
|
+
|
3
|
+
My attempt at creating a wrapper for the FogBugz API in Ruby.
|
4
|
+
|
5
|
+
More about FogBugz: http://www.fogbugz.com
|
6
|
+
More about FogBugz API: http://www.fogcreek.com/FogBugz/docs/60/topics/advanced/API.html
|
7
|
+
|
8
|
+
This is very much a work in progress. Any questions, concerns, or if you want to help out please e-mail me: austin.moody@gmail.com
|
9
|
+
|
10
|
+
== Installation
|
11
|
+
|
12
|
+
GEM coming soon hopefully. For now nab the fogbugz-api.rb file and require in your Ruby script.
|
13
|
+
|
14
|
+
== Requirements
|
15
|
+
|
16
|
+
* Hpricot (http://code.whytheluckystiff.net/hpricot/)
|
17
|
+
* net/https
|
18
|
+
* A login to a FogBugz server
|
19
|
+
|
20
|
+
== Example Usage
|
21
|
+
|
22
|
+
fb = FogBugz.new("my.fogbugz.com",true)
|
23
|
+
|
24
|
+
fb.logon("me@email.com","mypassword")
|
25
|
+
|
26
|
+
case_search = fb.search("API errors")
|
27
|
+
|
28
|
+
fb.logoff
|
data/TODO
ADDED
data/fogbugz-api.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "lib/fogbugz-api"
|
data/lib/fogbugz-api.rb
ADDED
@@ -0,0 +1,375 @@
|
|
1
|
+
require 'rubygems' rescue nil
|
2
|
+
require 'hpricot'
|
3
|
+
require 'net/https'
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
class FogBugzError < StandardError; end
|
7
|
+
|
8
|
+
# FogBugz class
|
9
|
+
class FogBugz
|
10
|
+
|
11
|
+
# Version of the FogBuz API this was written for. If the minversion returned
|
12
|
+
# by FogBugz is greater than this value this library will not function.
|
13
|
+
# TODO
|
14
|
+
# 1. If API mismatch... destroy Object?
|
15
|
+
API_VERSION = 5
|
16
|
+
|
17
|
+
# This is an array of all possible values that can be returned on a case.
|
18
|
+
# For methods that ask for cols wanted for a case this array will be used if
|
19
|
+
# their is nothing else specified.
|
20
|
+
CASE_COLUMNS = %w(ixBug fOpen sTitle sLatestTextSummary ixBugEventLatestText
|
21
|
+
ixProject sProject ixArea sArea ixGroup ixPersonAssignedTo sPersonAssignedTo
|
22
|
+
sEmailAssignedTo ixPersonOpenedBy ixPersonResolvedBy ixPersonClosedBy
|
23
|
+
ixPersonLastEditedBy ixStatus sStatus ixPriority sPriority ixFixFor sFixFor
|
24
|
+
dtFixFor sVersion sComputer hrsOrigEst hrsCurrEst hrsElapsed c sCustomerEmail
|
25
|
+
ixMailbox ixCategory sCategory dtOpened dtResolved dtClosed ixBugEventLatest
|
26
|
+
dtLastUpdated fReplied fForwarded sTicket ixDiscussTopic dtDue sReleaseNotes
|
27
|
+
ixBugEventLastView dtLastView ixRelatedBugs sScoutDescription sScoutMessage
|
28
|
+
fScoutStopReporting fSubscribed events)
|
29
|
+
|
30
|
+
attr_reader :url, :token, :use_ssl, :api_version, :api_minversion, :api_url
|
31
|
+
|
32
|
+
# Creates an instance of the FogBugz class.
|
33
|
+
#
|
34
|
+
# * url: URL to your FogBugz installation. URL only as in my.fogbugz.com
|
35
|
+
# without the http or https.
|
36
|
+
# * use_ssl: Does this server use SSL? true/false
|
37
|
+
# * token: Already have a token for the server? You can provide that here.
|
38
|
+
#
|
39
|
+
# Connects to the specified FogBugz installation and grabs the api.xml file
|
40
|
+
# to get other information such as API version, API minimum version, and the
|
41
|
+
# API endpoint. Also sets http/https connection to the server and sets the
|
42
|
+
# token if provided. FogBugzError will be raise if the minimum API version
|
43
|
+
# returned by FogBugz is greater than API_VERSION of this class.
|
44
|
+
#
|
45
|
+
# Example Usage:
|
46
|
+
#
|
47
|
+
# fb = FogBugz.new("my.fogbugz.com",true)
|
48
|
+
#
|
49
|
+
def initialize(url,use_ssl=false,token=nil)
|
50
|
+
@url = url
|
51
|
+
@use_ssl = use_ssl
|
52
|
+
connect
|
53
|
+
|
54
|
+
# Attempt to grap api.xml file from the server specified by url. Will let
|
55
|
+
# us know API is functional and verion matches this class
|
56
|
+
result = Hpricot.XML(@connection.get("/api.xml").body)
|
57
|
+
|
58
|
+
@api_version = (result/"version").inner_html.to_i
|
59
|
+
@api_minversion = (result/"version").inner_html.to_i
|
60
|
+
@api_url = "/" + (result/"url").inner_html
|
61
|
+
|
62
|
+
# Make sure this class will work w/ API version
|
63
|
+
raise FogBugzError, "API version mismatch" if (API_VERSION < @api_minversion)
|
64
|
+
|
65
|
+
@token = token ? token : ""
|
66
|
+
end
|
67
|
+
|
68
|
+
# Validates a user with FogBugz. Saves the returned token for use with other
|
69
|
+
# commands.
|
70
|
+
#
|
71
|
+
# If a token was already specified with new it will be overwritten with the
|
72
|
+
# token picked up by a successful authentication.
|
73
|
+
def logon(email,password)
|
74
|
+
cmd = {"cmd" => "logon", "email" => email, "password" => password}
|
75
|
+
|
76
|
+
result = Hpricot.XML(@connection.post(@api_url, to_params(cmd)).body)
|
77
|
+
|
78
|
+
if (result/"error").length >= 1
|
79
|
+
# error code 1 = bad login
|
80
|
+
# error code 2 = ambiguous name
|
81
|
+
case (result/"error").first["code"]
|
82
|
+
when "1"
|
83
|
+
raise FogBugzError, (result/"error").inner_html
|
84
|
+
when "2"
|
85
|
+
ambiguous_users = []
|
86
|
+
(result/"person").each do |person|
|
87
|
+
ambiguous_users << CDATA_REGEXP.match(person.inner_html)[1]
|
88
|
+
end
|
89
|
+
raise FogBugzError, (result/"error").inner_html + " " + ambiguous_users.join(", ")
|
90
|
+
end # case
|
91
|
+
elsif (result/"token").length == 1
|
92
|
+
# successful login
|
93
|
+
@token = CDATA_REGEXP.match((result/"token").inner_html)[1]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def logoff
|
98
|
+
cmd = {"cmd" => "logoff", "token" => @token}
|
99
|
+
result = Hpricot.XML(@connection.post(@api_url, to_params(cmd)).body)
|
100
|
+
@token = ""
|
101
|
+
end
|
102
|
+
|
103
|
+
def filters
|
104
|
+
cmd = {"cmd" => "listFilters", "token" => @token}
|
105
|
+
result = Hpricot.XML(@connection.post(@api_url, to_params(cmd)).body)
|
106
|
+
return_value = Hash.new
|
107
|
+
(result/"filter").each do |filter|
|
108
|
+
# create hash for each new project
|
109
|
+
filter_name = filter.inner_html
|
110
|
+
return_value[filter_name] = Hash.new
|
111
|
+
return_value[filter_name]["name"] = filter_name
|
112
|
+
return_value[filter_name] = filter.attributes.merge(return_value[filter_name])
|
113
|
+
end
|
114
|
+
return_value
|
115
|
+
end
|
116
|
+
|
117
|
+
def projects(fWrite=false, ixProject=nil)
|
118
|
+
return_value = Hash.new
|
119
|
+
cmd = {"cmd" => "listProjects", "token" => @token}
|
120
|
+
{"fWrite"=>"1"}.merge(cmd) if fWrite
|
121
|
+
{"ixProject"=>ixProject}.merge(cmd) if ixProject
|
122
|
+
result = Hpricot.XML(@connection.post(@api_url,to_params(cmd)).body)
|
123
|
+
return list_process(result,"project","sProject")
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns an integer, which is ixProject for the project created
|
127
|
+
# TODO - change to accept Has of parameters?
|
128
|
+
def new_project(sProject, ixPersonPrimaryContact, fAllowPublicSubmit, ixGroup, fInbox)
|
129
|
+
|
130
|
+
# I would have thought that the fAllowPublicSubmit would have been
|
131
|
+
# true/false... instead seems to need to be 0 or 1.
|
132
|
+
cmd = {
|
133
|
+
"cmd" => "newProject",
|
134
|
+
"token" => @token,
|
135
|
+
"sProject" => sProject,
|
136
|
+
"ixPersonPrimaryContact" => ixPersonPrimaryContact.to_s,
|
137
|
+
"fAllowPublicSubmit" => fAllowPublicSubmit.to_s,
|
138
|
+
"ixGroup" => ixGroup.to_s,
|
139
|
+
"fInbox" => fInbox.to_s
|
140
|
+
}
|
141
|
+
result = Hpricot.XML(@connection.post(@api_url,to_params(cmd)).body)
|
142
|
+
return (result/"ixProject").inner_html.to_i
|
143
|
+
end
|
144
|
+
|
145
|
+
def areas(fWrite=false, ixProject=nil, ixArea=nil)
|
146
|
+
return_value = Hash.new
|
147
|
+
cmd = {"cmd" => "listAreas", "token" => @token}
|
148
|
+
cmd = {"fWrite"=>"1"}.merge(cmd) if fWrite
|
149
|
+
cmd = {"ixProject"=>ixProject}.merge(cmd) if ixProject
|
150
|
+
cmd = {"ixArea" => ixArea}.merge(cmd) if ixArea
|
151
|
+
result = Hpricot.XML(@connection.post(@api_url,to_params(cmd)).body)
|
152
|
+
return list_process(result,"area","sArea")
|
153
|
+
end
|
154
|
+
|
155
|
+
def fix_fors(ixProject=nil,ixFixFor=nil)
|
156
|
+
return_value = Hash.new
|
157
|
+
cmd = {"cmd" => "listFixFors", "token" => @token}
|
158
|
+
{"ixProject"=>ixProject}.merge(cmd) if ixProject
|
159
|
+
{"ixFixFor" => ixFixFor}.merge(cmd) if ixFixFor
|
160
|
+
result = Hpricot.XML(@connection.post(@api_url,to_params(cmd)).body)
|
161
|
+
return list_process(result,"fixfor","sFixFor")
|
162
|
+
end
|
163
|
+
|
164
|
+
def categories
|
165
|
+
cmd = {"cmd" => "listCategories", "token" => @token}
|
166
|
+
result = Hpricot.XML(@connection.post(@api_url,to_params(cmd)).body)
|
167
|
+
return list_process(result,"category","sCategory")
|
168
|
+
end
|
169
|
+
|
170
|
+
def listPriorities
|
171
|
+
cmd = {"cmd" => "listPriorities", "token" => @token}
|
172
|
+
result = Hpricot.XML(@connection.post(@api_url,to_params(cmd)).body)
|
173
|
+
return list_process(result,"priority","sPriority")
|
174
|
+
end
|
175
|
+
|
176
|
+
# Returns list of people in corresponding categories.
|
177
|
+
#
|
178
|
+
# * fIncludeNormal: Only include Normal users. If no options specified,
|
179
|
+
# fIncludeNormal=1 is assumed.
|
180
|
+
# * fIncludeCommunity: true/false Will include Community users in return.
|
181
|
+
# * fIncludeVirtual: true/false Will include Virtual users in return.
|
182
|
+
def people(fIncludeNormal="1", fIncludeCommunity=nil, fIncludeVirtual=nil)
|
183
|
+
cmd = {
|
184
|
+
"cmd" => "listPeople",
|
185
|
+
"token" => @token,
|
186
|
+
"fIncludeNormal" => fIncludeNormal
|
187
|
+
}
|
188
|
+
cmd = {"fIncludeCommunity" => "1"}.merge(cmd) if fIncludeCommunity
|
189
|
+
cmd = {"fIncludeVirtual" => "1"}.merge(cmd) if fIncludeVirtual
|
190
|
+
result = Hpricot.XML(@connection.post(@api_url, to_params(cmd)).body)
|
191
|
+
return list_process(result,"person","sFullName")
|
192
|
+
end
|
193
|
+
|
194
|
+
# Returns a list of statuses for a particular category.
|
195
|
+
#
|
196
|
+
# * ixCategory => category to return statuses for. If not specified, then all are returned.
|
197
|
+
# * fResolved => If = 1 then only resolved statuses are returned.
|
198
|
+
def statuses(ixCategory=nil,fResolved=nil)
|
199
|
+
cmd = {
|
200
|
+
"cmd" => "listStatuses",
|
201
|
+
"token" => @token
|
202
|
+
}
|
203
|
+
cmd = {"ixCategory"=>ixCategory}.merge(cmd) if ixCategory
|
204
|
+
cmd = {"fResolved"=>fResolved}.merge(cmd) if fResolved
|
205
|
+
result = Hpricot.XML(@connection.post(@api_url,to_params(cmd)).body)
|
206
|
+
return list_process(result,"status","sStatus")
|
207
|
+
end
|
208
|
+
|
209
|
+
# Returns a list of mailboxes that you have access to.
|
210
|
+
def mailboxes
|
211
|
+
cmd = {
|
212
|
+
"cmd" => "listMailboxes",
|
213
|
+
"token" => @token
|
214
|
+
}
|
215
|
+
result = Hpricot.XML(@connection.post(@api_url,to_params(cmd)).body)
|
216
|
+
# usually lists were keyed w/ a name field. Mailboxes just
|
217
|
+
# weren't working for me so I'm going with ixMailbox value
|
218
|
+
return list_process(result,"mailbox","ixMailbox")
|
219
|
+
end
|
220
|
+
|
221
|
+
# Searches for FogBugz cases
|
222
|
+
#
|
223
|
+
# * q: Query for searching. Should hopefully work just like the Search box
|
224
|
+
# within the FogBugz application.
|
225
|
+
# * cols: Columns of information to be returned for each case found. Consult
|
226
|
+
# FogBugz API documentation for a list. If this is not specified the
|
227
|
+
# CASE_COLUMNS will be used. This will request every possible datapoint (as
|
228
|
+
# of API version 5) for each case.
|
229
|
+
# * max: Maximum number of cases to be returned for your search. Will return
|
230
|
+
# all if not specified.
|
231
|
+
def search(q, cols=CASE_COLUMNS, max=nil)
|
232
|
+
# TODO - shoudl I worry about the "operations" returned
|
233
|
+
# in the <case>?
|
234
|
+
|
235
|
+
cmd = {
|
236
|
+
"cmd" => "search",
|
237
|
+
"token" => @token,
|
238
|
+
"q" => q,
|
239
|
+
"cols" => cols.join(",")
|
240
|
+
}
|
241
|
+
cmd = {"max" => max}.merge(cmd) if max
|
242
|
+
result = Hpricot.XML(@connection.post(@api_url,to_params(cmd)).body)
|
243
|
+
return_value = list_process(result,"case","ixBug")
|
244
|
+
# if one of the returned cols = events, then process
|
245
|
+
# this list and replace its spot in the Hash
|
246
|
+
# with this instead of a string of XML
|
247
|
+
return_value.each do |key,value|
|
248
|
+
return_value[key]["events"] = list_process(Hpricot.XML(return_value[key]["events"]),"event","ixBugEvent") if return_value[key].has_key?("events")
|
249
|
+
end
|
250
|
+
return_value
|
251
|
+
end
|
252
|
+
|
253
|
+
# Creates a FogBugz case.
|
254
|
+
#
|
255
|
+
# * params: must be a hash keyed with values from the FogBugz API docs.
|
256
|
+
# sTitle, ixProject (or sProject), etc...
|
257
|
+
# * cols: columns to be returned about the case which gets created. Ff not
|
258
|
+
# passed will use constant list (all) provided with Class
|
259
|
+
def new_case(params, cols=CASE_COLUMNS)
|
260
|
+
case_process("new",params,cols)
|
261
|
+
end
|
262
|
+
|
263
|
+
protected
|
264
|
+
|
265
|
+
CDATA_REGEXP = /<!\[CDATA\[(.*?)\]\]>/
|
266
|
+
|
267
|
+
# Makes connection to the FogBugz server
|
268
|
+
#
|
269
|
+
# Assumes port 443 for SSL connections and 80 for non-SSL connections.
|
270
|
+
# Possibly should provide a way to override this.
|
271
|
+
def connect
|
272
|
+
@connection = Net::HTTP.new(@url, @use_ssl ? 443 : 80)
|
273
|
+
@connection.use_ssl = @use_ssl
|
274
|
+
@connection.verify_mode = OpenSSL::SSL::VERIFY_NONE if @use_ssl
|
275
|
+
end
|
276
|
+
|
277
|
+
def case_process(cmd,params,cols)
|
278
|
+
cmd = {
|
279
|
+
"cmd" => cmd,
|
280
|
+
"token" => @token,
|
281
|
+
"cols" => cols.join(",")
|
282
|
+
}.merge(params)
|
283
|
+
result = Hpricot.XML(@connection.post(@api_url, to_params(cmd)).body)
|
284
|
+
return_value = list_process(result,"case","ixBug")
|
285
|
+
# if one of the returned cols = events, then process
|
286
|
+
# this list and replace its spot in the Hash
|
287
|
+
# with this instead of a string of XML
|
288
|
+
return_value.each do |key,value|
|
289
|
+
return_value[key]["events"] = list_process(Hpricot.XML(return_value[key]["events"]),"event","ixBugEvent") if return_value[key].has_key?("events")
|
290
|
+
end
|
291
|
+
return_value[return_value.keys[0]]
|
292
|
+
end
|
293
|
+
|
294
|
+
# method used by other list methods to process the XML returned by FogBugz API.
|
295
|
+
#
|
296
|
+
# * xml => XML to process
|
297
|
+
# * element => individual elements within the XML to create Hashes for within the returned value
|
298
|
+
# * element_name => key for each individual Hash within the return value.
|
299
|
+
#
|
300
|
+
# EXAMPLE XML
|
301
|
+
#<response>
|
302
|
+
# <categories>
|
303
|
+
# <category>
|
304
|
+
# <ixCategory>1</ixCategory>
|
305
|
+
# <sCategory><![CDATA[Bug]]></sCategory>
|
306
|
+
# <sPlural><![CDATA[Bugs]]></sPlural>
|
307
|
+
# <ixStatusDefault>2</ixStatusDefault>
|
308
|
+
# <fIsScheduleItem>false</fIsScheduleItem>
|
309
|
+
# </category>
|
310
|
+
# <category>
|
311
|
+
# <ixCategory>2</ixCategory>
|
312
|
+
# <sCategory><![CDATA[Feature]]></sCategory>
|
313
|
+
# <sPlural><![CDATA[Features]]></sPlural>
|
314
|
+
# <ixStatusDefault>8</ixStatusDefault>
|
315
|
+
# <fIsScheduleItem>false</fIsScheduleItem>
|
316
|
+
# </category>
|
317
|
+
# </categories>
|
318
|
+
#</response>
|
319
|
+
#
|
320
|
+
# EXAMPLE CAll
|
321
|
+
# list_process(xml, "category", "sCategory")
|
322
|
+
#
|
323
|
+
# EXAMPLE HASH RETURN
|
324
|
+
#
|
325
|
+
# {
|
326
|
+
# "Bug" => {
|
327
|
+
# "ixCategory" => 1,
|
328
|
+
# "sCategory" => "Bug",
|
329
|
+
# "sPlural" => "Bugs",
|
330
|
+
# "ixStatusDefault" => 2,
|
331
|
+
# "fIsScheduleItem" => false
|
332
|
+
# },
|
333
|
+
# "Feature" => {
|
334
|
+
# "ixCategory" => 2,
|
335
|
+
# "sCategory" => "Feature",
|
336
|
+
# "sPlural" => "Features",
|
337
|
+
# "ixStatusDefault" => 2,
|
338
|
+
# "fIsScheduleItem" => false
|
339
|
+
# }
|
340
|
+
# }
|
341
|
+
def list_process(xml, element, element_name)
|
342
|
+
return_value = Hash.new
|
343
|
+
(xml/"#{element}").each do |item|
|
344
|
+
if element_name[0,1] == "s"
|
345
|
+
item_name = CDATA_REGEXP.match((item/"#{element_name}").inner_html)[1]
|
346
|
+
else
|
347
|
+
item_name = (item/"#{element_name}").inner_html
|
348
|
+
end
|
349
|
+
return_value[item_name] = Hash.new
|
350
|
+
|
351
|
+
item.each_child do |attribute|
|
352
|
+
return_value[item_name][attribute.name] = attribute.inner_html
|
353
|
+
# convert values to proper types
|
354
|
+
return_value[item_name][attribute.name] = CDATA_REGEXP.match(attribute.inner_html)[1] if (attribute.name[0,1] == "s" or attribute.name[0,3] == "evt") and attribute.inner_html != "" and CDATA_REGEXP.match(attribute.inner_html) != nil
|
355
|
+
return_value[item_name][attribute.name] = return_value[item_name][attribute.name].to_i if (attribute.name[0,2] == "ix" or attribute.name[0,1] == "n")
|
356
|
+
return_value[item_name][attribute.name] = (return_value[item_name][attribute.name] == "true") ? true : false if attribute.name[0,1] == "f"
|
357
|
+
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
return_value
|
362
|
+
end
|
363
|
+
|
364
|
+
# Converts a hash such as
|
365
|
+
# {
|
366
|
+
# :cmd => "logon",
|
367
|
+
# :email => "austin.moody@gmail.com",
|
368
|
+
# :password => "yeahwhatever",
|
369
|
+
# }
|
370
|
+
# to
|
371
|
+
# "cmd=logon&email=austin.moody@gmail.com&password=yeahwhatever"
|
372
|
+
def to_params(hash)
|
373
|
+
hash.map{|key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
|
374
|
+
end
|
375
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: austinmoody-fogbugz-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Austin Moody
|
8
|
+
- Gregory McIntyre
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2008-07-03 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: hpricot
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0.6"
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: austin.moody@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.rdoc
|
33
|
+
- LICENSE
|
34
|
+
- TODO
|
35
|
+
files:
|
36
|
+
- README.rdoc
|
37
|
+
- LICENSE
|
38
|
+
- TODO
|
39
|
+
- fogbugz-api.rb
|
40
|
+
- lib
|
41
|
+
- lib/fogbugz-api.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/austinmoody/ruby-fogbugz-api/wikis
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --main
|
47
|
+
- README.rdoc
|
48
|
+
- --inline-source
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.2.0
|
67
|
+
signing_key:
|
68
|
+
specification_version: 2
|
69
|
+
summary: Ruby wrapper for FogBugz API
|
70
|
+
test_files: []
|
71
|
+
|