clubhouse_ruby 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .env
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in clubhouse_ruby.gemspec
4
+ gemspec
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
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "clubhouse_ruby"
5
+ require "dotenv"
6
+ require "irb"
7
+
8
+ Dotenv.load
9
+ IRB.start
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
@@ -0,0 +1,3 @@
1
+ module ClubhouseRuby
2
+ VERSION = "0.2.0"
3
+ 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: []