brightspark3-rira 0.0.3

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/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
+