rally-wsapi 0.0.1
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.
- checksums.yaml +7 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +77 -0
- data/LICENSE +21 -0
- data/README.md +111 -0
- data/Rakefile +12 -0
- data/VERSION +1 -0
- data/lib/rally-wsapi.rb +1 -0
- data/lib/wsapi/mapper.rb +42 -0
- data/lib/wsapi/models/object.rb +33 -0
- data/lib/wsapi/models/project.rb +7 -0
- data/lib/wsapi/models/subscription.rb +7 -0
- data/lib/wsapi/models/user.rb +27 -0
- data/lib/wsapi/session.rb +156 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f8186336211f73579be3d906c7fd2e159636fd23
|
4
|
+
data.tar.gz: f4145b3095c6822f9d7f94d6eb85bd0907b3fdda
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c85d7b133bdd3005a97ba1c07026d6ffa0d7d3cb2e83f8ffb1ad5655b8bf45464bff79c0160c3da47eb05b4bb83fcc2522111f4f056070cfc460f9c59281ba13
|
7
|
+
data.tar.gz: 47334a19d03a163f2aefe565b3c3f21e1c6c6a59d1fb1fa6e96feac91051563c08ca8b4c7ee3cfb67aa896497d061acfb53b8c4441e7fafc9f626a6f132af47e
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
GEM
|
2
|
+
specs:
|
3
|
+
addressable (2.3.6)
|
4
|
+
builder (3.2.2)
|
5
|
+
crack (0.4.2)
|
6
|
+
safe_yaml (~> 1.0.0)
|
7
|
+
descendants_tracker (0.0.4)
|
8
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
9
|
+
diff-lcs (1.2.5)
|
10
|
+
faraday (0.9.0)
|
11
|
+
multipart-post (>= 1.2, < 3)
|
12
|
+
git (1.2.8)
|
13
|
+
github_api (0.11.3)
|
14
|
+
addressable (~> 2.3)
|
15
|
+
descendants_tracker (~> 0.0.1)
|
16
|
+
faraday (~> 0.8, < 0.10)
|
17
|
+
hashie (>= 1.2)
|
18
|
+
multi_json (>= 1.7.5, < 2.0)
|
19
|
+
nokogiri (~> 1.6.0)
|
20
|
+
oauth2
|
21
|
+
hashie (3.2.0)
|
22
|
+
highline (1.6.21)
|
23
|
+
jeweler (2.0.1)
|
24
|
+
builder
|
25
|
+
bundler (>= 1.0)
|
26
|
+
git (>= 1.2.5)
|
27
|
+
github_api
|
28
|
+
highline (>= 1.6.15)
|
29
|
+
nokogiri (>= 1.5.10)
|
30
|
+
rake
|
31
|
+
rdoc
|
32
|
+
json (1.8.1)
|
33
|
+
jwt (1.0.0)
|
34
|
+
mini_portile (0.6.0)
|
35
|
+
multi_json (1.10.1)
|
36
|
+
multi_xml (0.5.5)
|
37
|
+
multipart-post (2.0.0)
|
38
|
+
nokogiri (1.6.3.1)
|
39
|
+
mini_portile (= 0.6.0)
|
40
|
+
oauth2 (1.0.0)
|
41
|
+
faraday (>= 0.8, < 0.10)
|
42
|
+
jwt (~> 1.0)
|
43
|
+
multi_json (~> 1.3)
|
44
|
+
multi_xml (~> 0.5)
|
45
|
+
rack (~> 1.2)
|
46
|
+
rack (1.5.2)
|
47
|
+
rake (10.3.2)
|
48
|
+
rdoc (4.1.1)
|
49
|
+
json (~> 1.4)
|
50
|
+
rspec (3.0.0)
|
51
|
+
rspec-core (~> 3.0.0)
|
52
|
+
rspec-expectations (~> 3.0.0)
|
53
|
+
rspec-mocks (~> 3.0.0)
|
54
|
+
rspec-core (3.0.4)
|
55
|
+
rspec-support (~> 3.0.0)
|
56
|
+
rspec-expectations (3.0.4)
|
57
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
58
|
+
rspec-support (~> 3.0.0)
|
59
|
+
rspec-mocks (3.0.4)
|
60
|
+
rspec-support (~> 3.0.0)
|
61
|
+
rspec-support (3.0.4)
|
62
|
+
safe_yaml (1.0.3)
|
63
|
+
thread_safe (0.3.4)
|
64
|
+
webmock (1.18.0)
|
65
|
+
addressable (>= 2.3.6)
|
66
|
+
crack (>= 0.3.2)
|
67
|
+
|
68
|
+
PLATFORMS
|
69
|
+
ruby
|
70
|
+
|
71
|
+
DEPENDENCIES
|
72
|
+
faraday
|
73
|
+
jeweler
|
74
|
+
multi_json
|
75
|
+
rake
|
76
|
+
rspec
|
77
|
+
webmock
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Flowdock
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# rally-wsapi
|
2
|
+
|
3
|
+
Rally WSAPI client written in Ruby.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
In order to authenticate to WSAPI, you will need to obtain an API key for the Rally account. This can be done via [Rally OAuth](https://github.com/RallySoftware/rally-oauth-examples), for example.
|
8
|
+
|
9
|
+
Once you have the API key, you can initialize the session object with the key:
|
10
|
+
```
|
11
|
+
s = Wsapi::Session.new("deadbeefdeadbeef")
|
12
|
+
```
|
13
|
+
|
14
|
+
The constructor also accepts the following options:
|
15
|
+
* `:workspace_id`, if not given, user's default workspace is used for queries
|
16
|
+
* `:version`, WSAPI version, default is `3.0`
|
17
|
+
|
18
|
+
|
19
|
+
## Method reference for Session
|
20
|
+
|
21
|
+
Some methods accept an optional hash that can have the following options:
|
22
|
+
* `:query`, add conditions for fetching objects. E.g. `(UserName = "John")`, see WSAPI documentation for details about the syntax
|
23
|
+
* `:start`, default: `1`, fetch results starting from given number
|
24
|
+
* `:workspace`, override workspace setting of the session
|
25
|
+
* `:pagesize`, default: `200`, page size for results
|
26
|
+
* `:fetch`, default: `true`, fetch full objects
|
27
|
+
|
28
|
+
#### Get the authenticated user
|
29
|
+
```
|
30
|
+
get_current_user
|
31
|
+
```
|
32
|
+
|
33
|
+
#### Get a user
|
34
|
+
```
|
35
|
+
get_user(user_id)
|
36
|
+
```
|
37
|
+
|
38
|
+
#### Get a user by username
|
39
|
+
```
|
40
|
+
get_user_by_username(username)
|
41
|
+
```
|
42
|
+
|
43
|
+
#### Get the subscription of the authenticated user
|
44
|
+
```
|
45
|
+
get_user_subscription
|
46
|
+
```
|
47
|
+
|
48
|
+
#### Get a subscription
|
49
|
+
```
|
50
|
+
get_project(subscription_id)
|
51
|
+
```
|
52
|
+
|
53
|
+
#### Get a project
|
54
|
+
```
|
55
|
+
get_project(project_id)
|
56
|
+
```
|
57
|
+
|
58
|
+
#### Get projects of the authenticated user
|
59
|
+
```
|
60
|
+
get_projects(opts = {})
|
61
|
+
```
|
62
|
+
|
63
|
+
#### Get team members in a project
|
64
|
+
```
|
65
|
+
get_team_members(project_id, opts = {})
|
66
|
+
```
|
67
|
+
|
68
|
+
#### Get editors in a project
|
69
|
+
```
|
70
|
+
get_editors(project_id, opts = {})
|
71
|
+
```
|
72
|
+
|
73
|
+
## Result objects
|
74
|
+
|
75
|
+
There's a couple of convenience classes for the following object types:
|
76
|
+
|
77
|
+
* `User`
|
78
|
+
* `Subscription`
|
79
|
+
* `Project`
|
80
|
+
|
81
|
+
Other object types are represented by the generic `Object` class which the specific types above extend.
|
82
|
+
|
83
|
+
#### Object
|
84
|
+
|
85
|
+
Methods:
|
86
|
+
* `id`, identifier of the object
|
87
|
+
* `name`, name of the object
|
88
|
+
* `url`, URL of the object
|
89
|
+
* `workspace`, name of the object's workspace
|
90
|
+
|
91
|
+
|
92
|
+
#### User
|
93
|
+
|
94
|
+
Methods:
|
95
|
+
* `username`, username
|
96
|
+
* `first_name`, first name
|
97
|
+
* `last_name`, last name
|
98
|
+
* `name`, full name
|
99
|
+
* `email`, email address
|
100
|
+
* `admin?`, is the user admin in the subscription?
|
101
|
+
|
102
|
+
#### Subscription
|
103
|
+
|
104
|
+
Methods:
|
105
|
+
* `subscription_id`, subcription identifier
|
106
|
+
|
107
|
+
#### Project
|
108
|
+
|
109
|
+
Methods:
|
110
|
+
* `subscription`, `Subscription` of the project
|
111
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'jeweler'
|
2
|
+
|
3
|
+
Jeweler::Tasks.new do |s|
|
4
|
+
s.name = "rally-wsapi"
|
5
|
+
s.summary = "Simple client for Rally WSAPI"
|
6
|
+
s.email = "antti@flowdock.com"
|
7
|
+
s.homepage = "http://github.com/flowdock/rally-wsapi"
|
8
|
+
s.description = "Simple client for Rally WSAPI"
|
9
|
+
s.authors = ["Antti Pitkänen"]
|
10
|
+
s.files = FileList["[A-Z]*", "{bin,generators,lib,test}/**/*"]
|
11
|
+
s.licenses = ["MIT"]
|
12
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/rally-wsapi.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative './wsapi/session'
|
data/lib/wsapi/mapper.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
require_relative './models/object'
|
4
|
+
require_relative './models/subscription'
|
5
|
+
require_relative './models/user'
|
6
|
+
require_relative './models/project'
|
7
|
+
|
8
|
+
module Wsapi
|
9
|
+
class Mapper
|
10
|
+
def self.get_errors(json)
|
11
|
+
if result = json["QueryResult"]
|
12
|
+
result["Errors"]
|
13
|
+
elsif result = json["OperationResult"]
|
14
|
+
result["Errors"]
|
15
|
+
else
|
16
|
+
[]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.get_object(response)
|
21
|
+
json = MultiJson.load(response.body)
|
22
|
+
if get_errors(json).empty? && json.size == 1
|
23
|
+
Wsapi::Object.from_data(json.keys.first, json.values.first)
|
24
|
+
else
|
25
|
+
raise ApiError.new("Errors: #{get_errors(json).inspect}", response)
|
26
|
+
end
|
27
|
+
rescue MultiJson::LoadError
|
28
|
+
raise ApiError.new("Invalid JSON response from WSAPI: #{response.body}", response)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.get_objects(response)
|
32
|
+
json = MultiJson.load(response.body)
|
33
|
+
if get_errors(json).empty? && query_result = json["QueryResult"]
|
34
|
+
query_result["Results"].map { |object| Wsapi::Object.from_data(object["_type"], object) }
|
35
|
+
else
|
36
|
+
raise ApiError.new("Errors: #{get_errors(json).inspect}", response)
|
37
|
+
end
|
38
|
+
rescue MultiJson::LoadError
|
39
|
+
raise ApiError.new("Invalid JSON response from WSAPI: #{response.body}", response)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Wsapi
|
2
|
+
class Object
|
3
|
+
attr_reader :raw_data
|
4
|
+
|
5
|
+
def initialize(raw_data)
|
6
|
+
@raw_data = raw_data
|
7
|
+
end
|
8
|
+
|
9
|
+
def name
|
10
|
+
@raw_data['_refObjectName']
|
11
|
+
end
|
12
|
+
|
13
|
+
def id
|
14
|
+
@raw_data['ObjectID']
|
15
|
+
end
|
16
|
+
|
17
|
+
def url
|
18
|
+
@raw_data['_ref']
|
19
|
+
end
|
20
|
+
|
21
|
+
def workspace
|
22
|
+
@raw_data["Workspace"]["_refObjectName"]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.from_data(type, raw_data)
|
26
|
+
if type && Wsapi.const_defined?(type)
|
27
|
+
Wsapi.const_get(type).new(raw_data)
|
28
|
+
else
|
29
|
+
Object.new(raw_data)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Wsapi
|
2
|
+
class User < Wsapi::Object
|
3
|
+
def username
|
4
|
+
@raw_data["UserName"]
|
5
|
+
end
|
6
|
+
|
7
|
+
def first_name
|
8
|
+
@raw_data["FirstName"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def last_name
|
12
|
+
@raw_data["LastName"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
"#{@raw_data['FirstName']} #{@raw_data['LastName']}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def email
|
20
|
+
@raw_data["EmailAddress"]
|
21
|
+
end
|
22
|
+
|
23
|
+
def admin?
|
24
|
+
@raw_data["SubscriptionAdmin"]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'faraday'
|
3
|
+
|
4
|
+
require_relative './mapper'
|
5
|
+
|
6
|
+
module Wsapi
|
7
|
+
class StandardErrorWithResponse < StandardError
|
8
|
+
attr_reader :response
|
9
|
+
def initialize(msg, response = nil)
|
10
|
+
@response = response
|
11
|
+
super(msg)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
class AuthorizationError < StandardErrorWithResponse; end
|
15
|
+
class ApiError < StandardErrorWithResponse; end
|
16
|
+
class ObjectNotFoundError < StandardErrorWithResponse; end
|
17
|
+
class IpAddressLimited < StandardErrorWithResponse; end
|
18
|
+
|
19
|
+
WSAPI_URL = ENV['WSAPI_URL'] || 'https://rally1.rallydev.com/slm/webservice/'
|
20
|
+
|
21
|
+
class WsapiAuthentication < Faraday::Middleware
|
22
|
+
def initialize(logger, session_id)
|
23
|
+
@session_id = session_id
|
24
|
+
super(logger)
|
25
|
+
end
|
26
|
+
|
27
|
+
def call(env)
|
28
|
+
env[:request_headers]['ZSESSIONID'] = @session_id
|
29
|
+
@app.call(env)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Session
|
34
|
+
attr_accessor :workspace_id
|
35
|
+
|
36
|
+
def initialize(session_id, opts = {})
|
37
|
+
@api_version = opts[:version] || "3.0"
|
38
|
+
@session_id = session_id
|
39
|
+
@workspace_id = opts[:workspace_id]
|
40
|
+
@conn = Faraday.new(ssl: { verify: false} ) do |faraday|
|
41
|
+
faraday.request :url_encoded # form-encode POST params
|
42
|
+
faraday.use WsapiAuthentication, @session_id
|
43
|
+
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_user_subscription
|
48
|
+
response = wsapi_request(wsapi_resource_url("Subscription"))
|
49
|
+
Mapper.get_object(response)
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_subscription(id)
|
53
|
+
response = wsapi_request(wsapi_resource_url("Subscription/#{id}"))
|
54
|
+
Mapper.get_object(response)
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_projects(opts = {})
|
58
|
+
fetch_with_pages(opts) do |page_query|
|
59
|
+
wsapi_request(wsapi_resource_url("Project"), opts.merge(page_query))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_project(id)
|
64
|
+
response = wsapi_request(wsapi_resource_url("Project/#{id}"))
|
65
|
+
Mapper.get_object(response)
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_current_user
|
69
|
+
response = wsapi_request(wsapi_resource_url("User"))
|
70
|
+
Mapper.get_object(response)
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_user(id)
|
74
|
+
response = wsapi_request(wsapi_resource_url("User/#{id}"))
|
75
|
+
Mapper.get_object(response)
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_user_by_username(username)
|
79
|
+
response = wsapi_request(wsapi_resource_url("User"), query: "(UserName = \"#{username}\")", pagesize: 1)
|
80
|
+
(Mapper.get_objects(response) ||[]).first
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_team_members(project_id, opts = {})
|
84
|
+
fetch_with_pages(opts) do |page_query|
|
85
|
+
wsapi_request(wsapi_resource_url("Project/#{project_id}/TeamMembers"), opts.merge(page_query))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_editors(project_id, opts = {})
|
90
|
+
fetch_with_pages(opts) do |page_query|
|
91
|
+
wsapi_request(wsapi_resource_url("Project/#{project_id}/Editors"), opts.merge(page_query))
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def workspace_url
|
98
|
+
wsapi_resource_url("Workspace/#{@workspace_id}")
|
99
|
+
end
|
100
|
+
|
101
|
+
def wsapi_resource_url(resource)
|
102
|
+
File.join(WSAPI_URL, "v#{@api_version}", resource)
|
103
|
+
end
|
104
|
+
|
105
|
+
def wsapi_request(url, opts = {})
|
106
|
+
response = @conn.get do |req|
|
107
|
+
req.url url
|
108
|
+
req.params['workspace'] = workspace_url if @workspace_id
|
109
|
+
req.params['query'] = opts[:query] if opts[:query]
|
110
|
+
req.params['start'] = opts[:start] || 1
|
111
|
+
req.params['pagesize'] = opts[:pagesize] || 200
|
112
|
+
req.params['fetch'] = opts[:fetch] || true # by default, fetch full objects
|
113
|
+
end
|
114
|
+
raise AuthorizationError.new("Unauthorized", response) if response.status == 401
|
115
|
+
raise ApiError.new("Internal server error", response) if response.status == 500
|
116
|
+
raise ObjectNotFoundError.new("Object not found") if object_not_found?(response)
|
117
|
+
raise IpAddressLimited.new("IP Address limited", response) if ip_address_limited?(response)
|
118
|
+
response
|
119
|
+
end
|
120
|
+
|
121
|
+
def ip_address_limited?(response)
|
122
|
+
limit_message = /Your IP address, (?:\d+\.?)+, is not within the allowed range that your subscription administrator has configured./
|
123
|
+
response.status > 401 && response.body.match(limit_message)
|
124
|
+
end
|
125
|
+
|
126
|
+
def object_not_found?(response)
|
127
|
+
if response.status == 200
|
128
|
+
result = MultiJson.load(response.body)["OperationResult"]
|
129
|
+
if result && error = result["Errors"].first
|
130
|
+
error.match("Cannot find object to read")
|
131
|
+
else
|
132
|
+
false
|
133
|
+
end
|
134
|
+
else
|
135
|
+
false
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def fetch_with_pages(opts = {}, &block)
|
140
|
+
page_query = {
|
141
|
+
start: opts[:start] || 1,
|
142
|
+
pagesize: opts[:pagesize] || 100
|
143
|
+
}
|
144
|
+
resultCount = nil
|
145
|
+
objects = []
|
146
|
+
while(!resultCount || resultCount > objects.size) do
|
147
|
+
response = yield(page_query)
|
148
|
+
resultCount = MultiJson.load(response.body)["QueryResult"]["TotalResultCount"]
|
149
|
+
objects += Mapper.get_objects(response)
|
150
|
+
page_query[:start] += page_query[:pagesize]
|
151
|
+
end
|
152
|
+
objects
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rally-wsapi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Antti Pitkänen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: multi_json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: faraday
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Simple client for Rally WSAPI
|
56
|
+
email: antti@flowdock.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files:
|
60
|
+
- LICENSE
|
61
|
+
- README.md
|
62
|
+
files:
|
63
|
+
- Gemfile
|
64
|
+
- Gemfile.lock
|
65
|
+
- LICENSE
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- VERSION
|
69
|
+
- lib/rally-wsapi.rb
|
70
|
+
- lib/wsapi/mapper.rb
|
71
|
+
- lib/wsapi/models/object.rb
|
72
|
+
- lib/wsapi/models/project.rb
|
73
|
+
- lib/wsapi/models/subscription.rb
|
74
|
+
- lib/wsapi/models/user.rb
|
75
|
+
- lib/wsapi/session.rb
|
76
|
+
homepage: http://github.com/flowdock/rally-wsapi
|
77
|
+
licenses:
|
78
|
+
- MIT
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.2.1
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: Simple client for Rally WSAPI
|
100
|
+
test_files: []
|