clubhouse_ruby 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +278 -0
- data/Rakefile +6 -0
- data/bin/console +9 -0
- data/bin/setup +13 -0
- data/clubhouse_ruby.gemspec +27 -0
- data/lib/clubhouse_ruby.rb +28 -0
- data/lib/clubhouse_ruby/constants.rb +58 -0
- data/lib/clubhouse_ruby/path_builder.rb +90 -0
- data/lib/clubhouse_ruby/request.rb +77 -0
- data/lib/clubhouse_ruby/version.rb +3 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 99130661e8bef360cc89dc809b212764a013ed45
|
4
|
+
data.tar.gz: 398600d53686e920fb38f95cd54955b000af0006
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2dcdc681c343e90ecebc0def690f3307d607ecf1ca38806aa696b2e4882a6801e869021682fde9ce656bacec5097dda58069ec63ef803c659a642751508afb0d
|
7
|
+
data.tar.gz: 5502abcfcd208946601679d9125e2789b0645b22c104c688936d90aacb3a00cabb7c1558a82207a335cd38debcf048bf5330b22ac6ec82694f76add07d4e5eff
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Philip Castiglione
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,278 @@
|
|
1
|
+
# ClubhouseRuby
|
2
|
+
|
3
|
+
ClubhouseRuby is a lightweight Ruby wrapper of the
|
4
|
+
[Clubhouse API](https://clubhouse.io/api/v1/).
|
5
|
+
|
6
|
+
[Clubhouse](https://clubhouse.io) is a radical project management tool
|
7
|
+
particularly well suited to software development. If you're not familiar with
|
8
|
+
them, go check them out! :heart:
|
9
|
+
|
10
|
+
This gem is built with the philosophy that a good API wrapper is a simpler
|
11
|
+
alternative to a comprehensive client library and can provide a nice interface
|
12
|
+
to the API using dynamic Ruby metaprogramming techniques rather than mapping
|
13
|
+
functionality from the API to the library piece by piece.
|
14
|
+
|
15
|
+
This enables the wrapper to be loosely coupled to the current implementation of
|
16
|
+
the API, which makes it more resilient to change. Also, this approach takes much
|
17
|
+
less code and maintenance effort, allowing the developer to be lazy. A
|
18
|
+
reasonable person might fairly assume this to be the true rationale behind the
|
19
|
+
philosophy. They'd be right.
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Add this line to your application's Gemfile:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
gem 'clubhouse_ruby'
|
27
|
+
```
|
28
|
+
|
29
|
+
And then execute:
|
30
|
+
|
31
|
+
$ bundle
|
32
|
+
|
33
|
+
Or install it yourself as:
|
34
|
+
|
35
|
+
$ gem install clubhouse_ruby
|
36
|
+
|
37
|
+
Or transcribe the code by carving it character by character into the
|
38
|
+
mechanically articulated hand built stone sculpture you've developed that
|
39
|
+
operates as an effective turing machine when lubricated with oil.
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
This gem is a lightweight API wrapper. That means you'll need to refer to the
|
44
|
+
[API documentation](https://clubhouse.io/api/v1/) to figure out what resources
|
45
|
+
and actions exist.
|
46
|
+
|
47
|
+
On the plus side, once you know what you want to do, using this gem should be
|
48
|
+
simple.
|
49
|
+
|
50
|
+
Instantiate an object to interface with the API:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
clubhouse = ClubhouseRuby::Clubhouse.new(<YOUR CLUBHOUSE API TOKEN>)
|
54
|
+
```
|
55
|
+
|
56
|
+
The API can also provide responses in CSV format instead of the default JSON:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
clubhouse = ClubhouseRuby::Clubhouse.new(<YOUR CLUBHOUSE API TOKEN>, response_format: :csv)
|
60
|
+
```
|
61
|
+
|
62
|
+
Then, call methods on the object matching the resource(s) and action you are
|
63
|
+
interested in. For example, if you want to list all available epics, you need to
|
64
|
+
access the endpoint at https://api.clubhouse.io/api/v1/epics. The
|
65
|
+
clubhouse_ruby gem uses an explicit action:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
clubhouse.epics.list
|
69
|
+
# => {
|
70
|
+
# code: "200",
|
71
|
+
# status: "OK",
|
72
|
+
# content: [
|
73
|
+
# {
|
74
|
+
# "id" => 1,
|
75
|
+
# "name" => "An Odyssian Epic",
|
76
|
+
# "description" => "Outrageously epic.",
|
77
|
+
# "created_at" => "...",
|
78
|
+
# "updated_at" => "...",
|
79
|
+
# "deadline "=> nil,
|
80
|
+
# "state" => "to do",
|
81
|
+
# "position" => 1,
|
82
|
+
# "archived" => false,
|
83
|
+
# "follower_ids" => [...],
|
84
|
+
# "owner_ids" => [...],
|
85
|
+
# "comments" => [...]
|
86
|
+
# }
|
87
|
+
# ]
|
88
|
+
# }
|
89
|
+
```
|
90
|
+
|
91
|
+
If the endpoint you want requires parameters, say if you wanted to create an
|
92
|
+
epic, you provide a hash to the action call following the resource:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
clubhouse.epics.create(name: "My New Epic", state: "to do")
|
96
|
+
# => {
|
97
|
+
# code: "201",
|
98
|
+
# status: "Created",
|
99
|
+
# content: {
|
100
|
+
# "id" => 2,
|
101
|
+
# "name" => "My New Epic",
|
102
|
+
# "description" => "",
|
103
|
+
# "created_at" => "...",
|
104
|
+
# "updated_at" => "...",
|
105
|
+
# "deadline" => nil,
|
106
|
+
# "state" => "to do",
|
107
|
+
# "position" => 2,
|
108
|
+
# "archived" => false,
|
109
|
+
# "follower_ids" => [],
|
110
|
+
# "owner_ids" => [],
|
111
|
+
# "comments" => []
|
112
|
+
# }
|
113
|
+
# }
|
114
|
+
```
|
115
|
+
|
116
|
+
If the endpoint you want is nested, you can build a path by chaining method
|
117
|
+
calls, providing any required parent resource id as an argument to that method
|
118
|
+
in the chain. For example, if you wanted to list all the stories associated
|
119
|
+
with a particular project:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
clubhouse.projects(<project_id>).stories.list
|
123
|
+
# => {
|
124
|
+
# code: "200",
|
125
|
+
# status: "OK",
|
126
|
+
# content: [
|
127
|
+
# {
|
128
|
+
# "archived" => false,
|
129
|
+
# "created_at" => "...",
|
130
|
+
# "id" => 1,
|
131
|
+
# "name" => "Rescue Prince",
|
132
|
+
# "story_type" => "feature",
|
133
|
+
# "description" => "The prince is trapped in a tower and needs freeing.",
|
134
|
+
# "position" => 1,
|
135
|
+
# "workflow_state_id" => "...",
|
136
|
+
# "estimate" => 0,
|
137
|
+
# "updated_at" => "...",
|
138
|
+
# "deadline" => nil,
|
139
|
+
# "project_id" => <project_id>,
|
140
|
+
# "labels" => [
|
141
|
+
# {
|
142
|
+
# "id" => "...",
|
143
|
+
# "name" => "Urgent",
|
144
|
+
# "created_at" => "...",
|
145
|
+
# "updated_at" => "..."
|
146
|
+
# }
|
147
|
+
# ],
|
148
|
+
# "requested_by_id" => "...",
|
149
|
+
# "owner_ids" => [...],
|
150
|
+
# "follower_ids" => [...],
|
151
|
+
# "epic_id" => "...",
|
152
|
+
# "file_ids" => [...],
|
153
|
+
# "linked_file_ids" => [...],
|
154
|
+
# "comments" => [...],
|
155
|
+
# "tasks" => [...],
|
156
|
+
# "story_links" => [...]
|
157
|
+
# },
|
158
|
+
# {...},
|
159
|
+
# {...}
|
160
|
+
# ]
|
161
|
+
# }
|
162
|
+
```
|
163
|
+
|
164
|
+
You can build a path in steps rather than all at once, and execution is deferred
|
165
|
+
until the action call:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
clubhouse.projects(<project_id>)
|
169
|
+
clubhouse.stories
|
170
|
+
clubhouse.list
|
171
|
+
# => as above
|
172
|
+
```
|
173
|
+
|
174
|
+
If you are building a path and you make a mistake, you can clear the path:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
clubhouse.projects(project_id)
|
178
|
+
clubhouse.epics
|
179
|
+
clubhouse.clear_path
|
180
|
+
# => []
|
181
|
+
```
|
182
|
+
|
183
|
+
You don't need to clear the path after a complete request, as that happens
|
184
|
+
automatically.
|
185
|
+
|
186
|
+
Note that the chained methods are always resources (with an id for a parent when
|
187
|
+
accessing nested resources) followed by a final action that matches the methods
|
188
|
+
in the Clubhouse API documentation.
|
189
|
+
|
190
|
+
These resources and methods are enumerated in the source code
|
191
|
+
[here](https://github.com/PhilipCastiglione/clubhouse_ruby/blob/master/lib/clubhouse_ruby/constants.rb)
|
192
|
+
but generally you should find the url you are interested in from the docs.
|
193
|
+
|
194
|
+
## Errors
|
195
|
+
|
196
|
+
Errors are passed through from the API relatively undecorated:
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
clubhouse = ClubhouseRuby::Clubhouse.new("unrecognized token")
|
200
|
+
clubhouse.epics.list
|
201
|
+
# => {
|
202
|
+
# code: "401",
|
203
|
+
# status: "Unauthorized",
|
204
|
+
# content: {
|
205
|
+
# "message" => "Unauthorized",
|
206
|
+
# "tag" => "unauthorized"
|
207
|
+
# }
|
208
|
+
# }
|
209
|
+
```
|
210
|
+
|
211
|
+
Arbitrary combinations of resources not building a path that matches a url the
|
212
|
+
API knows about will fail. Note the clubhouse API gives little away, returning
|
213
|
+
forbidden:
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
clubhouse.epics(epic_id).stories.list
|
217
|
+
# => {
|
218
|
+
# code: "403",
|
219
|
+
# status: "Forbidden",
|
220
|
+
# content: {
|
221
|
+
# "message" => "Sorry, you do not have access to this resource.",
|
222
|
+
# "tag" => "user_denied_access"
|
223
|
+
# }
|
224
|
+
# }
|
225
|
+
```
|
226
|
+
|
227
|
+
Attempting to access a nested resource without providing the parent id as an
|
228
|
+
argument is a bad request:
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
clubhouse.projects.stories.list
|
232
|
+
# => {
|
233
|
+
# code: "400",
|
234
|
+
# status: "Bad Request",
|
235
|
+
# content: {
|
236
|
+
# "message" => "The request included invalid or missing parameters.",
|
237
|
+
# "errors" => {
|
238
|
+
# "project-public-id" => [
|
239
|
+
# "not", [
|
240
|
+
# "integer?",
|
241
|
+
# "projects"
|
242
|
+
# ]
|
243
|
+
# ]
|
244
|
+
# }
|
245
|
+
# }
|
246
|
+
# }
|
247
|
+
```
|
248
|
+
|
249
|
+
## Development
|
250
|
+
|
251
|
+
After checking out the repo, run `bin/setup` to install dependencies and
|
252
|
+
following the instructions. Specifically, you can choose to provide a genuine
|
253
|
+
Clubhouse API token in the `.env` file. This will be important if you want to
|
254
|
+
use `bin/console` for an interactive prompt that allows you to experiment with
|
255
|
+
the gem and real API responses.
|
256
|
+
|
257
|
+
Use `rake spec` to run the tests. The tests don't make external requests but
|
258
|
+
rather use VCR for stubbed responses. If you want to play with the tests and
|
259
|
+
get real API responses (perhaps to extend the suite or for a new feature) then
|
260
|
+
you'll need to have an API token in the env as described above.
|
261
|
+
|
262
|
+
Note that the current test suite is far from exhaustive and could do with some
|
263
|
+
love.
|
264
|
+
|
265
|
+
**NB: If you have implemented a feature that requires a new cassette, make sure
|
266
|
+
you change the uri referenced by the cassette you added to remove the API token
|
267
|
+
if you have updated the environment to use your token. Otherwise your API token
|
268
|
+
will be in publically visible from the code in this repo.**
|
269
|
+
|
270
|
+
## Contributing
|
271
|
+
|
272
|
+
Bug reports and pull requests are entirely welcome on GitHub at
|
273
|
+
https://github.com/philipcastiglione/clubhouse_ruby.
|
274
|
+
|
275
|
+
## License
|
276
|
+
|
277
|
+
The gem is available as open source under the terms of the
|
278
|
+
[MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
set -euo pipefail
|
3
|
+
IFS=$'\n\t'
|
4
|
+
|
5
|
+
bundle install
|
6
|
+
|
7
|
+
touch .env
|
8
|
+
if [[ $(grep API_TOKEN .env) == "" ]]; then
|
9
|
+
echo "API_TOKEN=<YOUR CLUBHOUSE API TOKEN>" >> .env
|
10
|
+
echo ;
|
11
|
+
echo "DEVELOPER: Please edit .env to contain your clubhouse api token if you wish to use the console."
|
12
|
+
echo ;
|
13
|
+
fi
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'clubhouse_ruby/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "clubhouse_ruby"
|
8
|
+
spec.version = ClubhouseRuby::VERSION
|
9
|
+
spec.authors = ["Philip Castiglione"]
|
10
|
+
spec.email = ["philipcastiglione@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{A lightweight ruby wrapper for the Clubhouse API: https://clubhouse.io/api/v1}
|
13
|
+
spec.homepage = "https://github.com/PhilipCastiglione/clubhouse_ruby"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
24
|
+
spec.add_development_dependency "dotenv", "~> 2.1"
|
25
|
+
spec.add_development_dependency "webmock", "~> 2.1"
|
26
|
+
spec.add_development_dependency "vcr", "~> 3.0"
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "clubhouse_ruby/version"
|
2
|
+
require "clubhouse_ruby/constants"
|
3
|
+
require "clubhouse_ruby/path_builder"
|
4
|
+
require "clubhouse_ruby/request"
|
5
|
+
|
6
|
+
module ClubhouseRuby
|
7
|
+
class Clubhouse
|
8
|
+
include PathBuilder
|
9
|
+
|
10
|
+
attr_accessor :token, :response_format
|
11
|
+
|
12
|
+
# This is the basic object to interact with the clubhouse api. An api token
|
13
|
+
# is required, and optionally the response format can be set.
|
14
|
+
#
|
15
|
+
def initialize(token, response_format: :json)
|
16
|
+
raise ArgumentError unless input_valid?(token, response_format)
|
17
|
+
|
18
|
+
self.token = token
|
19
|
+
self.response_format = response_format
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def input_valid?(token, response_format)
|
25
|
+
!token.nil? && FORMATS.keys.include?(response_format)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'csv'
|
3
|
+
|
4
|
+
module ClubhouseRuby
|
5
|
+
API_URL = "https://api.clubhouse.io/api/v1/".freeze
|
6
|
+
|
7
|
+
# Response formats the clubhouse api knows about
|
8
|
+
FORMATS = {
|
9
|
+
json: {
|
10
|
+
headers: { header: 'Content-Type', content: 'application/json' },
|
11
|
+
parser: JSON
|
12
|
+
},
|
13
|
+
csv: {
|
14
|
+
headers: { header: 'Accept', content: 'text/csv' },
|
15
|
+
parser: CSV
|
16
|
+
}
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
# Action words are nice for our internal api and match the api path too
|
20
|
+
ACTIONS = {
|
21
|
+
get: :Get,
|
22
|
+
update: :Put,
|
23
|
+
delete: :Delete,
|
24
|
+
list: :Get,
|
25
|
+
create: :Post
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
# These are the resource for the clubhouse api and can form part of the path
|
29
|
+
RESOURCES = [
|
30
|
+
:epics,
|
31
|
+
:files,
|
32
|
+
:labels,
|
33
|
+
:linked_files,
|
34
|
+
:projects,
|
35
|
+
:story_links,
|
36
|
+
:stories,
|
37
|
+
:tasks,
|
38
|
+
:comments,
|
39
|
+
:users,
|
40
|
+
:workflows
|
41
|
+
].freeze
|
42
|
+
|
43
|
+
# These are the annoying edge cases in the clubhouse api that are don't fit
|
44
|
+
EXCEPTIONS = {
|
45
|
+
search: {
|
46
|
+
path: :search,
|
47
|
+
action: :Post
|
48
|
+
},
|
49
|
+
bulk_create: {
|
50
|
+
path: :bulk,
|
51
|
+
action: :Post
|
52
|
+
},
|
53
|
+
bulk_update: {
|
54
|
+
path: :bulk,
|
55
|
+
action: :Put
|
56
|
+
}
|
57
|
+
}.freeze
|
58
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module ClubhouseRuby
|
2
|
+
module PathBuilder
|
3
|
+
def self.included(_)
|
4
|
+
class_exec do
|
5
|
+
attr_accessor :path
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# Uh oh! This will allow the class including this module to "build a path"
|
10
|
+
# by chaining calls to resources, terminated with a method linked to an
|
11
|
+
# action that will execute the api call.
|
12
|
+
#
|
13
|
+
# For example:
|
14
|
+
#
|
15
|
+
# `foo.stories(story_id).comments.update(id: comment_id, text: "comment text")`
|
16
|
+
#
|
17
|
+
# This example will execute a call to:
|
18
|
+
#
|
19
|
+
# `https://api.clubhouse.io/api/v1/stories/{story-id}/comments/{comment-id}`
|
20
|
+
#
|
21
|
+
# with arguments:
|
22
|
+
#
|
23
|
+
# `{ text: "comment text" }`
|
24
|
+
#
|
25
|
+
def method_missing(name, *args)
|
26
|
+
if known_action?(name)
|
27
|
+
execute_request(ACTIONS[name], args.first)
|
28
|
+
elsif known_resource?(name)
|
29
|
+
build_path(name, args.first)
|
30
|
+
elsif known_exception?(name)
|
31
|
+
build_path(EXCEPTIONS[name][:path], nil)
|
32
|
+
execute_request(EXCEPTIONS[name][:action], args.first)
|
33
|
+
else
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# You can build a path without executing in stages like this:
|
39
|
+
#
|
40
|
+
# `foo.stories(story_id)`
|
41
|
+
#
|
42
|
+
# This will partly populate foo:path, but won't execute the call (which
|
43
|
+
# clears it). In case you made a mistake and want to start again, you can
|
44
|
+
# clear the path using this public method.
|
45
|
+
#
|
46
|
+
def clear_path
|
47
|
+
self.path = []
|
48
|
+
end
|
49
|
+
|
50
|
+
# We'd better not lie when asked.
|
51
|
+
#
|
52
|
+
def respond_to_missing?(name, include_private = false)
|
53
|
+
ACTIONS.keys.include?(name) ||
|
54
|
+
RESOURCES.include?(name) ||
|
55
|
+
EXCEPTIONS.keys.include?(name) ||
|
56
|
+
super
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def known_action?(name)
|
62
|
+
ACTIONS.keys.include?(name)
|
63
|
+
end
|
64
|
+
|
65
|
+
def known_resource?(name)
|
66
|
+
RESOURCES.include?(name)
|
67
|
+
end
|
68
|
+
|
69
|
+
def known_exception?(name)
|
70
|
+
EXCEPTIONS.keys.include?(name)
|
71
|
+
end
|
72
|
+
|
73
|
+
def execute_request(action, params)
|
74
|
+
req = Request.new(
|
75
|
+
self,
|
76
|
+
action: action,
|
77
|
+
params: params
|
78
|
+
)
|
79
|
+
clear_path
|
80
|
+
req.fetch
|
81
|
+
end
|
82
|
+
|
83
|
+
def build_path(resource, id)
|
84
|
+
self.path ||= []
|
85
|
+
self.path << resource
|
86
|
+
self.path << id if id
|
87
|
+
self
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module ClubhouseRuby
|
4
|
+
class Request
|
5
|
+
attr_accessor :uri, :action, :response_format, :params
|
6
|
+
|
7
|
+
# Prepares a fancy request object and ensures the inputs make as much sense
|
8
|
+
# as possible. It's still totally possible to just provide a path that
|
9
|
+
# doesn't match a real url though.
|
10
|
+
#
|
11
|
+
def initialize(clubhouse, action:, params: {})
|
12
|
+
raise ArgumentError unless validate_input(clubhouse, action, params)
|
13
|
+
|
14
|
+
self.params = params || {}
|
15
|
+
self.uri = construct_uri(clubhouse)
|
16
|
+
self.action = action
|
17
|
+
self.response_format = clubhouse.response_format
|
18
|
+
end
|
19
|
+
|
20
|
+
# Executes the http(s) request and provides the response with some
|
21
|
+
# additional decoration in a Hash.
|
22
|
+
#
|
23
|
+
def fetch
|
24
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |https|
|
25
|
+
req = Net::HTTP.const_get(action).new(uri)
|
26
|
+
|
27
|
+
set_body(req)
|
28
|
+
set_format_header(req)
|
29
|
+
|
30
|
+
wrap_response(https.request(req))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def validate_input(clubhouse, action, params)
|
37
|
+
clubhouse.is_a?(Clubhouse) &&
|
38
|
+
!clubhouse.path.nil? &&
|
39
|
+
!clubhouse.token.nil? &&
|
40
|
+
!clubhouse.response_format.nil? &&
|
41
|
+
ACTIONS.values.include?(action) &&
|
42
|
+
(params.is_a?(Hash) || params.nil?)
|
43
|
+
end
|
44
|
+
|
45
|
+
def construct_uri(clubhouse)
|
46
|
+
base_url = API_URL
|
47
|
+
path = clubhouse.path.map(&:to_s).map { |p| p.gsub('_', '-') }.join('/')
|
48
|
+
object_id = "/#{self.params.delete(:id)}" if self.params.key?(:id)
|
49
|
+
token = clubhouse.token
|
50
|
+
URI("#{base_url}#{path}#{object_id}?token=#{token}")
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_format_header(req)
|
54
|
+
format_header = FORMATS[response_format][:headers][:header]
|
55
|
+
format_content = FORMATS[response_format][:headers][:content]
|
56
|
+
req[format_header] = format_content
|
57
|
+
end
|
58
|
+
|
59
|
+
def set_body(req)
|
60
|
+
req.body = params.to_json if params
|
61
|
+
end
|
62
|
+
|
63
|
+
def wrap_response(res)
|
64
|
+
begin
|
65
|
+
content = FORMATS[response_format][:parser].parse(res.body) if res.body
|
66
|
+
rescue FORMATS[response_format][:parser]::ParserError
|
67
|
+
content = res.body
|
68
|
+
end
|
69
|
+
|
70
|
+
{
|
71
|
+
code: res.code,
|
72
|
+
status: res.message,
|
73
|
+
content: content
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: clubhouse_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Philip Castiglione
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.12'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.12'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: dotenv
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: vcr
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
- philipcastiglione@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- ".travis.yml"
|
107
|
+
- Gemfile
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- bin/console
|
112
|
+
- bin/setup
|
113
|
+
- clubhouse_ruby.gemspec
|
114
|
+
- lib/clubhouse_ruby.rb
|
115
|
+
- lib/clubhouse_ruby/constants.rb
|
116
|
+
- lib/clubhouse_ruby/path_builder.rb
|
117
|
+
- lib/clubhouse_ruby/request.rb
|
118
|
+
- lib/clubhouse_ruby/version.rb
|
119
|
+
homepage: https://github.com/PhilipCastiglione/clubhouse_ruby
|
120
|
+
licenses:
|
121
|
+
- MIT
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 2.6.4
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: 'A lightweight ruby wrapper for the Clubhouse API: https://clubhouse.io/api/v1'
|
143
|
+
test_files: []
|