trac4r 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,35 @@
1
+ trac4r: Ruby wrapper for the Trac XML-RPC API
2
+ =============================================
3
+
4
+ Author:: Niklas Cathor
5
+ License:: See LICENSE in source distribution
6
+
7
+ For more information on the Trac XML-RPC see the {plugin's page on trac-hacks.com}[http://trac-hacks.org/wiki/XmlRpcPlugin#UsingfromRuby]
8
+
9
+ Thanks to the original author, Niklas Cathor, who has done most of the work.
10
+
11
+ == Install
12
+
13
+ # Only if you haven't set up gemcutter yet
14
+ sudo gem install gemcutter
15
+ sudo gem tumble
16
+
17
+ # Once Gemcutter is setup
18
+ sudo gem install trac4r
19
+
20
+ == Overview
21
+
22
+ This wraps the Trac XML-RPC plugin.
23
+
24
+ require 'rubygems'
25
+ require 'trac4r'
26
+
27
+ # Note that you need to point to the XMLRPC root and not the root of the trac web interface
28
+ trac = Trac.new("http://www.example.com/trac/project/xmlrpc","username","password")
29
+ trac.tickets.list # get all tickets
30
+ trac.tickets.get 2334 # Get ticket #2334
31
+
32
+ == More Info
33
+
34
+ * {RDoc}[http://davetron5000.github.com/trac4r]
35
+ * {Source}[http://www.github.com/davetron5000/trac4r]
@@ -0,0 +1,44 @@
1
+ module Trac
2
+ class TracException < Exception
3
+ # Creates new TracException
4
+ #
5
+ # [+http_error+] the error message, possibly mulit-line
6
+ # [+host+] host on which we are connecting to Trac
7
+ # [+port+] port we're using
8
+ # [+path+] the path to the Trac API
9
+ # [+method+] the XML RPC method being called
10
+ # [+args+] the args (as an array) that were sent with the call
11
+ def initialize(http_error,host,port,path,method,args)
12
+ http_error = http_error.sub 'HTTP-Error: ',''
13
+ if http_error =~ /\n/
14
+ http_error.split(/\n/).each do |line|
15
+ if line =~ /^\d\d\d/
16
+ http_error = line
17
+ break
18
+ end
19
+ end
20
+ end
21
+ @http_status,@http_message = http_error.split(/\s+/,2)
22
+ @host = host
23
+ @port = port
24
+ @path = path
25
+ @method = method
26
+ @args = args
27
+ end
28
+
29
+ # Gives a more useful message for common problems
30
+ def message
31
+ if @http_status == '404'
32
+ "Couldn't find Trac API at #{url}, check your configuration"
33
+ elsif @http_status == '401'
34
+ "Your username/password didn't authenticate, check your configuration"
35
+ else
36
+ "#{@http_message} (#{@http_status}) when trying URL http://#{@host}:#{@port}#{@path} and method #{@command}(#{@args.join('.')})"
37
+ end
38
+ end
39
+
40
+ def url
41
+ "http://#{@host}:#{@port}#{@path}"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,62 @@
1
+ =begin
2
+ Copyright (c) 2008 Niklas E. Cathor <niklas@brueckenschlaeger.de>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a
5
+ copy of this software and associated documentation files (the "Software"),
6
+ to deal in the Software without restriction, including without limitation
7
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
+ and/or sell copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
22
+ DON'T USE IT IF YOU'RE AN ASSHOLE!
23
+ =end
24
+
25
+ require 'xmlrpc/client'
26
+
27
+ module Trac
28
+ class Query
29
+ def initialize url,user,pass
30
+ if user && pass
31
+ url = url.sub 'xmlrpc','login/xmlrpc'
32
+ end
33
+ uri = URI.parse(url)
34
+ use_ssl = (uri.scheme == 'https') ? true : false
35
+ @host = uri.host
36
+ @path = uri.path
37
+ @port = uri.port
38
+ @connection = XMLRPC::Client.new(@host,
39
+ @path,
40
+ @port,
41
+ nil,
42
+ nil,
43
+ user,
44
+ pass,
45
+ use_ssl,
46
+ nil)
47
+ end
48
+
49
+ def query command, *args
50
+ begin
51
+ return @connection.call(command,*args)
52
+ rescue => e
53
+ if e.message =~ /HTTP-Error/
54
+ errorcode = e.message.sub 'HTTP-Error: ',''
55
+ raise TracException.new(errorcode,@host,@port,@path,command,args)
56
+ else
57
+ raise
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,45 @@
1
+ module Trac
2
+ # This class represents a ticket as it is retrieved from the database
3
+ class Ticket
4
+ attr_accessor(:id, # Integer
5
+ :severity, # String
6
+ :milestone, # String
7
+ :status, # String
8
+ :type, # String
9
+ :priority, # String
10
+ :version, # String
11
+ :reporter, # String
12
+ :owner, # String
13
+ :cc, # String
14
+ :summary, # String
15
+ :description, # String
16
+ :keywords, # String
17
+ :created_at, # XMLRPC::DateTime
18
+ :updated_at) # XMLRPC::DateTime
19
+ # returns a new ticket
20
+ def initialize id=0
21
+ @id = id
22
+ @severity=@milestone=@status=@type=@priority=@version=@reporter=@owner= @cc= @summary=@description=@keywords=""
23
+ end
24
+
25
+ # checks if all attributes are set
26
+ def check
27
+ instance_variables.each do |v|
28
+ return false if instance_variable_get(v.to_sym).nil?
29
+ end
30
+ return true
31
+ end
32
+
33
+ # loads a ticket from the XMLRPC response
34
+ def self.load params
35
+ ticket = self.new params[0]
36
+ ticket.created_at = params[1]
37
+ ticket.updated_at = params[2]
38
+ attributes = params[3]
39
+ attributes.each do |key,value|
40
+ ticket.instance_variable_set("@#{key}".to_sym,value)
41
+ end
42
+ return ticket
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,159 @@
1
+ =begin
2
+ Copyright (c) 2008 Niklas E. Cathor <niklas@brueckenschlaeger.de>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a
5
+ copy of this software and associated documentation files (the "Software"),
6
+ to deal in the Software without restriction, including without limitation
7
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
+ and/or sell copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
22
+
23
+ DON'T USE IT IF YOU'RE AN ASSHOLE!
24
+ =end
25
+
26
+ module Trac
27
+ class Tickets
28
+ def initialize trac
29
+ @trac = trac
30
+ end
31
+
32
+ # returns a list of all tickets (the ids), by performing two
33
+ # queries, one for closed tickets, one for opened. use
34
+ # list :include_closed => false
35
+ # to only get open tickets.
36
+ def list options={ }
37
+ include_closed = options[:include_closed] || true
38
+ tickets = @trac.query("ticket.query","status!=closed")
39
+ tickets += @trac.query("ticket.query","status=closed") if include_closed
40
+ return tickets
41
+ end
42
+
43
+ # like `list', but only gets closed tickets
44
+ def list_closed
45
+ @trac.query("ticket.query","status=closed")
46
+ end
47
+
48
+ # returns all tickets (not just the ids) in a hash
49
+ # warning: to avoid heavy traffic load the results are cached
50
+ # and will only be updated after 5 minutes. use
51
+ # get_all :cached_results => false
52
+ # to avoid this.
53
+ # other options:
54
+ # :include_closed - see Tickets#list for a description
55
+ def get_all options={ }
56
+ include_closed = options[:include_closed] || true
57
+ cached_results = options[:cached_results] || true
58
+ if(cached_results == true &&
59
+ @cache_last_update &&
60
+ @cache_last_update > Time.now - 300)
61
+ return @cache
62
+ end
63
+ tickets = { }
64
+ list(:include_closed => include_closed).each do |ticket|
65
+ tickets[ticket] = get ticket
66
+ end
67
+ @cache = tickets
68
+ @cache_last_update = Time.now
69
+ return tickets
70
+ end
71
+
72
+ # fetch a ticket. Returns instance of Trac::Ticket
73
+ def get id
74
+ Ticket.load @trac.query("ticket.get",id)
75
+ end
76
+
77
+ # create a new ticket returning the ticket id
78
+ def create summary,description,attributes={ },notify=false
79
+ @trac.query("ticket.create",summary,description,attributes,notify)
80
+ end
81
+
82
+ # update ticket returning the ticket in the same orm as ticket.get
83
+ def update id,comment,attributes={ },notify=false
84
+ attr = {}
85
+ attributes.each_pair do |key, value|
86
+ unless(value.nil? || value.size == 0 || value.empty?)
87
+ attr[key] = value
88
+ end
89
+ end
90
+ @trac.query("ticket.update",id,comment,attr,notify)
91
+ end
92
+
93
+ # delete ticket by id
94
+ def delete id
95
+ @trac.query("ticket.delete",id)
96
+ end
97
+
98
+ # return the changelog as a list of tuples of the form (time,author,
99
+ # field,oldvalue,newvalue,permanent). While the other tuples elements
100
+ # are quite self-explanatory, the permanent flag is used to distinguish
101
+ # collateral changes that are not yet immutable (like attachments,
102
+ # currently).
103
+ def changelog ticket_id,w=0
104
+ @trac.query("ticket.changeLog",ticket_id,w)
105
+ end
106
+
107
+ # returns a list of ids of tickets that have changed since `time'
108
+ def changes time
109
+ @trac.query("ticket.getRecentChanges",time)
110
+ end
111
+
112
+ # returns a list of attachments for the given ticket
113
+ def attachments ticket_id
114
+ @trac.query("ticket.listAttachments",ticket_id)
115
+ end
116
+
117
+ # returns the content of an attachment
118
+ def get_attachment ticket_id,filename
119
+ @trac.query("ticket.getAttachment",ticket_id,filename)
120
+ end
121
+
122
+ # adds an attachment to a ticket
123
+ def put_attachment ticket_id,filename,description,data,replace=true
124
+ @trac.query("ticket.putAttachment",ticket_id,filename,description,data,replace)
125
+ end
126
+
127
+ # deletes given attachment
128
+ def delete_attachment ticket_id,filename
129
+ @trac.query("ticket.deleteAttachment",ticket_id,filename)
130
+ end
131
+
132
+ # returns the actions that can be performed on the ticket
133
+ def actions ticket_id
134
+ @trac.query("ticket.getAvailableActions",ticket_id)
135
+ end
136
+
137
+
138
+ # returns all settings (possible values for status, version,
139
+ # priority, resolution, component, type, severity or milestone)
140
+ # as a hash in the form: { :status => ["assigned","closed",...], ... }
141
+ # this method only gets the settings once per session. To update
142
+ # them please refer to Tickets#get_settings
143
+ def settings
144
+ @settings || get_settings
145
+ end
146
+
147
+
148
+ # returns the settings in the same form as Tickets#settings, but
149
+ # refreshes them every time we call it.
150
+ def get_settings
151
+ @settings = { }
152
+ ['status','version','priority','resolution',
153
+ 'component','type','severity','milestone'].each do |setting|
154
+ @settings[setting.to_sym] = @trac.query("ticket.#{setting}.getAll")
155
+ end
156
+ return @settings
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,86 @@
1
+ =begin
2
+ Copyright (c) 2008 Niklas E. Cathor <niklas@brueckenschlaeger.de>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a
5
+ copy of this software and associated documentation files (the "Software"),
6
+ to deal in the Software without restriction, including without limitation
7
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
+ and/or sell copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
22
+
23
+ DON'T USE IT IF YOU'RE AN ASSHOLE!
24
+ =end
25
+
26
+ module Trac
27
+ class Wiki
28
+ def initialize trac
29
+ @trac = trac
30
+ end
31
+
32
+ # returns a list of all pages
33
+ def list
34
+ @trac.query('wiki.getAllPages')
35
+ end
36
+
37
+ # convert a raw page to html (e.g. for preview)
38
+ def raw_to_html content
39
+ @trac.query('wiki.wikiToHtml',content)
40
+ end
41
+
42
+ # returns a whole page in HTML
43
+ def get_html name
44
+ @trac.query('wiki.getPageHTML',name)
45
+ end
46
+
47
+ # returns a whole page in raw format
48
+ def get_raw name
49
+ @trac.query('wiki.getPage',name)
50
+ end
51
+
52
+ # sends a page. if the page doesn't exist yet it is created
53
+ # otherwise it will be overwritten with the new content.
54
+ def put name,content,attributes={ }
55
+ @trac.query('wiki.putPage',name,content,attributes)
56
+ end
57
+
58
+ # deletes page with given name, returning true on success.
59
+ def delete name
60
+ @trac.query('wiki.deletePage',name)
61
+ end
62
+
63
+ # returns a list of all attachments of a page
64
+ def attachments page
65
+ @trac.query("wiki.listAttachments",page)
66
+ end
67
+
68
+ # returns the content of an attachment
69
+ def get_attachment path
70
+ @trac.query("wiki.getAttachment",path)
71
+ end
72
+
73
+ # uploads given `data' as an attachment to given `page'.
74
+ # returns true on success.
75
+ # unlike the XMLRPC-Call this method defaults `replace'
76
+ # to false as we don't want to destroy anything.
77
+ def put_attachment page,filename,description,data,replace=false
78
+ @trac.query("wiki.putAttachmentEx",page,filename,description,data,replace)
79
+ end
80
+
81
+ # deletes attachment with given `path'
82
+ def delete_attachment path
83
+ @trac.query("wiki.deleteAttachment",path)
84
+ end
85
+ end
86
+ end
data/lib/trac4r.rb ADDED
@@ -0,0 +1,79 @@
1
+ =begin
2
+ Copyright (c) 2008 Niklas E. Cathor <niklas@brueckenschlaeger.de>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a
5
+ copy of this software and associated documentation files (the "Software"),
6
+ to deal in the Software without restriction, including without limitation
7
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
+ and/or sell copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
22
+ DON'T USE IT IF YOU'RE AN ASSHOLE!
23
+ =end
24
+
25
+ filedir = File.dirname(__FILE__)
26
+
27
+ require "trac4r/query"
28
+ require "trac4r/tickets"
29
+ require "trac4r/ticket"
30
+ require "trac4r/wiki"
31
+ require "trac4r/error"
32
+
33
+
34
+ # This module wraps the XMLRPC interface of
35
+ # trac (http://trac.edgewall.org/) into a ruby library.
36
+ # You can now easily access trac from any ruby
37
+ # application without having to handle all the
38
+ # (trivial) XMLRPC calls.
39
+ #
40
+ # Example (receive list of opened tickets):
41
+ # require 'trac4r/trac'
42
+ # trac = Trac.new "http://dev.example.com/trac/my_awesome_project"
43
+ # trac.tickets.list :include_closed => false #=> [7,9,3,5,14,...]
44
+ #
45
+ # Receive one single ticket
46
+ # ticket = trac.tickets.get 9 #=> #<Trac::Ticket:0xb76de9b4 ... >
47
+ # ticket.summary #=> 'foo'
48
+ # ticket.description #=> 'bar'
49
+ # See documentation for Trac::Ticket for what methods you can call on ticket.
50
+ #
51
+ # Create a new ticket
52
+ # trac.tickets.create "summary", "description", :type => 'defect', :version => '1.0', :milestone => 'bug free' #=> 10
53
+ # summary and description are required, the rest is optional. It can be one of the following:
54
+ # :severity, :milestone, :status, :type, :priority, :version, :reporter, :owner, :cc, :keywords
55
+ #
56
+ module Trac
57
+ # returns a new instance of Trac::Base
58
+ def self.new url, user=nil,pass=nil
59
+ Base.new url,user,pass
60
+ end
61
+
62
+ class Base
63
+ attr_reader :wiki, :tickets, :user, :pass
64
+ def initialize url,user,pass
65
+ @user = user
66
+ @pass = pass
67
+ if url.split('/').last != 'xmlrpc'
68
+ url = url+'/xmlrpc'
69
+ end
70
+ @connection = Query.new(url,user,pass)
71
+ @wiki = Wiki.new(@connection)
72
+ @tickets = Tickets.new(@connection)
73
+ end
74
+
75
+ def api_version
76
+ @connection.query("system.getAPIVersion")
77
+ end
78
+ end
79
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trac4r
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Niklas Cathro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-20 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Basic ruby client library for accessing Trac instances via its XML RPC API
17
+ email:
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - lib/trac4r/error.rb
26
+ - lib/trac4r/query.rb
27
+ - lib/trac4r/ticket.rb
28
+ - lib/trac4r/tickets.rb
29
+ - lib/trac4r/wiki.rb
30
+ - lib/trac4r.rb
31
+ - README.rdoc
32
+ has_rdoc: true
33
+ homepage: http://github.com/csexton/trac4r/
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --title
39
+ - trac4r Trac Ruby Client
40
+ - --main
41
+ - README.rdoc
42
+ - -ri
43
+ require_paths:
44
+ - lib
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project: trac4r
61
+ rubygems_version: 1.3.5
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Ruby Client Library for Trac
65
+ test_files: []
66
+