brightspark3-rira 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,9 @@
1
+ Rira
2
+ ====
3
+
4
+ Rira is a ruby gem for interfacing Jira. Check out the Jira documentation for the {origin of the name}[http://confluence.atlassian.com/pages/viewpage.action?pageId=192544].
5
+
6
+ COPYRIGHT
7
+ =========
8
+
9
+ Copyright (c) 2008 László Bácsi. See LICENSE for details.
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 3
3
+ :major: 0
4
+ :minor: 0
data/lib/rira/base.rb ADDED
@@ -0,0 +1,85 @@
1
+ module Rira
2
+ class Base
3
+ def initialize(url, username, password, options = {})
4
+ @url, @username, @password = url, username, password
5
+ @token = xmlrpc_client.call("jira1.login", @username, @password)
6
+ rescue XMLRPC::FaultException => e
7
+ raise RPCError.new(e)
8
+ end
9
+
10
+ def xmlrpc_client
11
+ XMLRPC::Client.new2(@url)
12
+ end
13
+
14
+ MAPPINGS = {
15
+ :projects => ['project', "getProjectsNoSchemes", "getProjects"],
16
+ :projects_no_schemes => ['project', "getProjectsNoSchemes", "getProjects"],
17
+ :components => ['component', "getComponents"],
18
+ :versions => ['version', "getVersions"],
19
+ :issue_types => ['issue_type', "getIssueTypes"],
20
+ :issue_types_for_project => ['issue_type', "getIssueTypesForProject"],
21
+ :sub_task_issue_types => ['issue_type', "getSubTaskIssueTypes"],
22
+ :sub_task_issue_types_for_project => ['issue_type', "getSubTaskIssueTypesForProject"],
23
+ :create_issue => ['issue', "createIssue"],
24
+ :update_issue => ['issue', "updateIssue"],
25
+ :issue => ['issue', "getIssue"],
26
+ :issues_from_filter => ['issue', "getIssuesFromFilter"],
27
+ :search => ['issue', "getIssuesFromTextSearch"],
28
+ :search_with_project => ['issue', "getIssuesFromTextSearchWithProject"],
29
+ :add_comment => [nil, "addComment"],
30
+ :comments => ['comment', "getComments"],
31
+ :priorities => ['priority', "getPriorities"],
32
+ :resolutions => ['resolution', "getResolutions"],
33
+ :statuses => ['status', "getStatuses"],
34
+ :favourite_filters => ['filter', "getFavouriteFilters"],
35
+ :saved_filters => ['filter', "getSavedFilters"],
36
+ :server_info => ['server_info', "getServerInfo"],
37
+ :user => ['user', "getUser"],
38
+ :logout => [nil, "logout"],
39
+ }
40
+
41
+ def method_missing(method, *args)
42
+ model, *methods = MAPPINGS[method]
43
+
44
+ super(method, *args) if methods.empty?
45
+
46
+ retried = false
47
+ begin
48
+ case result = xmlrpc_client.call("jira1.#{methods.first}", @token, *args)
49
+ when Hash
50
+ model_or_struct(model, result)
51
+ when Array
52
+ result.map do |item|
53
+ if item.is_a?(Hash)
54
+ model_or_struct(model, item)
55
+ else
56
+ item
57
+ end
58
+ end
59
+ else
60
+ result
61
+ end
62
+ rescue XMLRPC::FaultException => e
63
+ if e.faultString =~ /session timed out/ and not retried
64
+ @token = xmlrpc_client.call("jira1.login", @username, @password)
65
+ retried = true
66
+ retry
67
+ else
68
+ methods.shift
69
+ retry unless methods.empty?
70
+ raise RPCError.new(e)
71
+ end
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def model_or_struct(model, result)
78
+ if model
79
+ Model.new(self, model, result)
80
+ else
81
+ OpenStruct.new(result)
82
+ end
83
+ end
84
+ end
85
+ end
data/lib/rira/model.rb ADDED
@@ -0,0 +1,95 @@
1
+ require 'time'
2
+
3
+ module Rira
4
+ class Model
5
+ ASSOCIATIONS = {
6
+ "add_comment" => ["issue", "key"],
7
+ "comments" => ["issue", "key"],
8
+ "components" => ["project", "key"],
9
+ "issues" => ["filter", "id",
10
+ "issues_from_filter"],
11
+ "search" => ["project", "keys",
12
+ "search_with_project"],
13
+ "issue_types" => ["project", "id", "issue_types_for_project"],
14
+ "sub_task_issue_types" => ["project", "id",
15
+ "sub_task_issue_types_for_project"],
16
+ "versions" => ["project", "key"],
17
+ "update" => ["issue", "key",
18
+ "update_issue"],
19
+ }
20
+
21
+ # FIXME: DRY this up a little bit
22
+ CONVERTERS = {
23
+ 'resolution' => lambda {|s,x| s.base.resolutions.detect {|r| r.id == x}},
24
+ 'priority' => lambda {|s,x| s.base.priorities.detect {|p| p.id == x}},
25
+ 'status' => lambda {|s,x| s.base.statuses.detect {|p| p.id == x}},
26
+ 'project' => lambda {|s,x| s.base.projects.detect {|p| p.key == x}},
27
+ 'type' => lambda { |s,x|
28
+ p = s.project
29
+ (p.issue_types + p.sub_task_issue_types).detect {|it| it.id == x}
30
+ },
31
+ 'lead' => lambda {|s,x| s.base.user(x)},
32
+ 'author' => lambda {|s,x| s.base.user(x)},
33
+ 'reporter' => lambda {|s,x| s.base.user(x)},
34
+ 'assignee' => lambda {|s,x| s.base.user(x)},
35
+ 'components' => lambda {|s,x| x.map {|c| Model.new(s.base, 'component', c)}},
36
+ 'affects_versions' => lambda {|s,x| x.map {|v| Model.new(s.base, 'version', v)}},
37
+ 'fix_versions' => lambda {|s,x| x.map {|v| Model.new(s.base, 'version', v)}},
38
+ 'created' => lambda {|s,x| Time.parse(x)},
39
+ 'updated' => lambda {|s,x| Time.parse(x)},
40
+ 'release_date' => lambda {|s,x| Date.parse(x)},
41
+ 'build_date' => lambda {|s,x| Date.parse(x)},
42
+ 'archived' => lambda {|s,x| x == 'true'},
43
+ 'released' => lambda {|s,x| x == 'true'},
44
+ 'sub_task' => lambda {|s,x| x == 'true'},
45
+ }
46
+
47
+ BOOLEANS = %w{archived released sub_task}
48
+
49
+ attr_reader :base, :model
50
+
51
+ def initialize(base, model, hash)
52
+ @base, @model, @attributes = base, model, hash
53
+ @attributes.keys.each do |key|
54
+ new_key = key.gsub(/[A-Z]/) {"_#{$&.downcase}"}
55
+ if key != new_key
56
+ @attributes[new_key] = @attributes.delete(key)
57
+ end
58
+ end
59
+ end
60
+
61
+ def id
62
+ @attributes['id']
63
+ end
64
+
65
+ def type
66
+ CONVERTERS['type'].call(self, @attributes['type'])
67
+ end
68
+
69
+ def respond_to?(method)
70
+ @attributes.include? method.to_s
71
+ end
72
+
73
+ def method_missing(method, *args)
74
+ method = method.to_s
75
+ if method[-1] == ?? and BOOLEANS.include?(method[0..-2])
76
+ method = method[0..-2]
77
+ end
78
+ if args.empty? and @attributes.has_key?(method)
79
+ att = @attributes[method]
80
+ if converter = CONVERTERS[method]
81
+ converter.call(self, att)
82
+ else
83
+ att
84
+ end
85
+ elsif map = ASSOCIATIONS[method] and map.first == @model
86
+ _, att, rp = map
87
+ scope = att[-1] == ?s ? Array(@attributes[att[0..-2]]) : @attributes[att]
88
+ args.unshift(scope)
89
+ @base.send(rp || method, *args)
90
+ else
91
+ super(method.to_sym, *args)
92
+ end
93
+ end
94
+ end
95
+ end
data/lib/rira.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'xmlrpc/client'
2
+ require 'ostruct'
3
+
4
+ require File.join(File.dirname(__FILE__), 'rira', 'model')
5
+ require File.join(File.dirname(__FILE__), 'rira', 'base')
6
+
7
+ def Rira(url)
8
+ unless url =~ %r{/rpc/xmlrpc$}
9
+ url += '/' unless url[-1] == ?/
10
+ url += 'rpc/xmlrpc'
11
+ end
12
+ Rira::Client.new(url)
13
+ end
14
+
15
+ module Rira
16
+ class RPCError < StandardError
17
+ def initialize(fault_exception)
18
+ super("#{fault_exception.faultCode}: #{fault_exception.faultString}")
19
+ end
20
+ end
21
+
22
+ class Client
23
+ def initialize(url)
24
+ @url = url
25
+ end
26
+
27
+ def login(username, password)
28
+ Rira::Base.new(@url, username, password)
29
+ end
30
+ end
31
+ end
data/test/rira_test.rb ADDED
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class RiraTest < Test::Unit::TestCase
4
+ JIRA_URL = "http://jira.lackac.hu"
5
+ JIRA_USER = "rubi"
6
+ JIRA_PASS = "j1r4b0t"
7
+
8
+ context "A Rira Client" do
9
+ should "be able to login with correct password" do
10
+ rira = Rira(JIRA_URL + "/rpc/xmlrpc")
11
+ assert_nothing_raised do
12
+ rira = rira.login(JIRA_USER, JIRA_PASS)
13
+ end
14
+ assert rira.instance_variable_get('@token').length > 0
15
+ end
16
+
17
+ should "be created with the short URL (without /rpc/xmlrpc)" do
18
+ rira = Rira(JIRA_URL)
19
+ assert_nothing_raised { rira.login(JIRA_USER, JIRA_PASS) }
20
+ end
21
+
22
+ should "not get token with wrong password" do
23
+ rira = Rira(JIRA_URL)
24
+ assert_raise(Rira::RPCError) { rira.login("wrong", "authentication") }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'mocha'
5
+
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'rira'
8
+
9
+ class Test::Unit::TestCase
10
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: brightspark3-rira
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - "L\xC3\xA1szl\xC3\xB3 B\xC3\xA1csi"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-04 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Nice interface for Jira, the Ruby way.
17
+ email: lackac@lackac.hu
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - README.rdoc
26
+ - VERSION.yml
27
+ - lib/rira
28
+ - lib/rira/base.rb
29
+ - lib/rira/model.rb
30
+ - lib/rira.rb
31
+ - test/rira_test.rb
32
+ - test/test_helper.rb
33
+ has_rdoc: true
34
+ homepage: http://github.com/lackac/rira
35
+ post_install_message:
36
+ rdoc_options:
37
+ - --inline-source
38
+ - --charset=UTF-8
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project:
56
+ rubygems_version: 1.2.0
57
+ signing_key:
58
+ specification_version: 2
59
+ summary: Nice interface for Jira, the Ruby way.
60
+ test_files: []
61
+