stackd 0.0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -4
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +1 -1
- data/LICENSE +24 -0
- data/README.md +184 -0
- data/bin/bundler +16 -0
- data/bin/console +14 -0
- data/bin/htmldiff +16 -0
- data/bin/ldiff +16 -0
- data/bin/rake +16 -0
- data/bin/rdoc +16 -0
- data/bin/restclient +16 -0
- data/bin/ri +16 -0
- data/bin/rspec +16 -0
- data/bin/safe_yaml +16 -0
- data/bin/setup +7 -0
- data/bin/stackd +16 -0
- data/lib/stackd.rb +27 -2
- data/lib/stackd/auth_request.rb +138 -0
- data/lib/stackd/client.rb +29 -0
- data/lib/stackd/concerns/require_attr.rb +20 -0
- data/lib/stackd/concerns/tattr_accessor.rb +51 -0
- data/lib/stackd/config.rb +23 -0
- data/lib/stackd/error.rb +27 -0
- data/lib/stackd/server_error.rb +4 -0
- data/lib/stackd/token.rb +103 -0
- data/lib/stackd/util/http.rb +135 -0
- data/lib/stackd/util/portal.rb +14 -0
- data/lib/stackd/version.rb +1 -1
- data/stackd.gemspec +28 -15
- metadata +141 -17
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 712a5a41ba1e4378a6f294d4ab7f4bd42dfa60df
|
4
|
+
data.tar.gz: 262301250ceb765546dd1120f259540e064ed25b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0c670d327edea7dc4d9fd30337b370094abe354e5f571f0b9f201d49bfeefef479508bfbd09c305f24646a597bef4e3b96fb0440abd21b99f97a1aee072c2ad4
|
7
|
+
data.tar.gz: 22e64533ead572f8c16a2c2c190492cf4772eab612b47fdc6dafd8fe6defde8f0b881fa74960b7d50b602a1b3bc5ca320c2184cf0565066f00be21ccb50ea304
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
4
|
+
distribute this software, either in source code form or as a compiled
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
6
|
+
means.
|
7
|
+
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
9
|
+
of this software dedicate any and all copyright interest in the
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
11
|
+
of the public at large and to the detriment of our heirs and
|
12
|
+
successors. We intend this dedication to be an overt act of
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
14
|
+
software under copyright law.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
For more information, please refer to <http://unlicense.org>
|
data/README.md
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
# Stackd Ruby Client
|
2
|
+
|
3
|
+
[![Codeship Status for stackd/stackd-ruby](https://codeship.com/projects/ffa792f0-d110-0132-a1ec-2af27bf90e4e/status?branch=master)](https://codeship.com/projects/77196)
|
4
|
+
|
5
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/stackd`. To experiment with that code, run `bin/console` for an interactive prompt.
|
6
|
+
|
7
|
+
TODO: Delete this and the text above, and describe your gem
|
8
|
+
|
9
|
+
### Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'stackd'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install stackd
|
24
|
+
|
25
|
+
### Usage
|
26
|
+
|
27
|
+
###### Creating a client
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
$stackd = Stackd::Client.new id: 'YOUR_CLIENT_ID',
|
31
|
+
secret: 'YOUR_CLIENT_SECRET'
|
32
|
+
```
|
33
|
+
|
34
|
+
###### Getting an app token
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
app_token = $stackd.auth_requests.client scope: 'a,b,c'
|
38
|
+
# => <Stackd::Token app_id: "YOUR_CLIENT_ID",
|
39
|
+
# scope: "a,b,c",
|
40
|
+
# access_token: "e1979b29529ac",
|
41
|
+
# expires_in: 3600>
|
42
|
+
```
|
43
|
+
|
44
|
+
###### Getting a user token
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class MyController
|
48
|
+
def authorize
|
49
|
+
auth_request = $stackd.auth_requests.new redirect_uri: 'YOUR_CALLBACK_URL',
|
50
|
+
scope: 'a,b,c'
|
51
|
+
|
52
|
+
session[:auth_state] = auth_request.state
|
53
|
+
|
54
|
+
redirect_to auth_request.url
|
55
|
+
# stackd.com/authorize?client_id=...&state=...&redirect_uri=...&scope=...
|
56
|
+
end
|
57
|
+
|
58
|
+
def callback
|
59
|
+
auth_request = $stackd.auth_requests.new state: session[:state]
|
60
|
+
|
61
|
+
user_token = auth_request.callback code: params[:code],
|
62
|
+
state: params[:state]
|
63
|
+
# => <Stackd::Token user_id: "09545bce-3c31-4593-99bd-1dfe8b98df93",
|
64
|
+
# scope: "a,b",
|
65
|
+
# access_token: "e1979b29529ac",
|
66
|
+
# refresh_token: "ca92592b9791e",
|
67
|
+
# expires_in: 3600>
|
68
|
+
end
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
###### Refreshing a user token
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
refreshed_user_token = $stackd.auth_requests.refresh user_token
|
76
|
+
```
|
77
|
+
|
78
|
+
###### Persisting tokens (example)
|
79
|
+
|
80
|
+
```
|
81
|
+
rails generate model TokenData \
|
82
|
+
user_id:string \
|
83
|
+
scope:string \
|
84
|
+
access_token:string \
|
85
|
+
refresh_token:string \
|
86
|
+
expires_in:integer
|
87
|
+
```
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
Stackd::Token.on_grant do |token|
|
91
|
+
TokenData.create! user_id: token.user_id,
|
92
|
+
scope: token.scope,
|
93
|
+
access_token: token.access_token,
|
94
|
+
refresh_token: token.refresh_token,
|
95
|
+
expires_in: token.expires_in
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
class TokenData
|
101
|
+
def self.fresh_token_by criteria
|
102
|
+
# ...
|
103
|
+
token = $stackd.tokens.new user_id: user_id,
|
104
|
+
scope: scope,
|
105
|
+
access_token: access_token,
|
106
|
+
refresh_token: refresh_token,
|
107
|
+
expires_in: expires_in,
|
108
|
+
token_type: 'bearer'
|
109
|
+
|
110
|
+
if expired?
|
111
|
+
$stackd.auth_requests.refresh token
|
112
|
+
else
|
113
|
+
token
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def expired?
|
118
|
+
Time.now > created_at + expires_in.seconds
|
119
|
+
end
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
###### Making requests
|
124
|
+
|
125
|
+
https://stackd.com/docs
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
token.post :rappers, name: 'Marshall Mathers'
|
129
|
+
# POST /rappers
|
130
|
+
# {"name": "Marshall Mathers"}
|
131
|
+
# => { ... }
|
132
|
+
```
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
token.patch :rappers, ':id', name: 'EMIN3M'
|
136
|
+
# PATCH /rappers/:id
|
137
|
+
# {"name": "EMIN3M"}
|
138
|
+
# => { ... }
|
139
|
+
```
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
token.get :rapper, ':id'
|
143
|
+
# GET /rappers/:id
|
144
|
+
# => { ... }
|
145
|
+
```
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
token.get :rappers, q: 'Slim Shady'
|
149
|
+
# GET /rappers?q=Slim%20Shady
|
150
|
+
# => { rappers: [ ... ], ... }
|
151
|
+
```
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
token.get '/any/url'
|
155
|
+
# GET /any/url
|
156
|
+
```
|
157
|
+
|
158
|
+
**Application errors (4xx)**
|
159
|
+
|
160
|
+
Will throw a `Stackd::Error`:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
# <Stackd::Error error="error_code"
|
164
|
+
# error_description="Something went wrong."
|
165
|
+
# error_uri="https://stackd.com/docs/errors#error_code">
|
166
|
+
```
|
167
|
+
|
168
|
+
**Server errors (5xx)**
|
169
|
+
|
170
|
+
Will throw a `Stackd::ServerError`.
|
171
|
+
|
172
|
+
### Development
|
173
|
+
|
174
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment. Run `bundle exec stackd` to use the code located in this directory, ignoring other installed copies of this gem.
|
175
|
+
|
176
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
177
|
+
|
178
|
+
### Contributing
|
179
|
+
|
180
|
+
1. Fork it ( https://github.com/stackd/stackd-ruby/fork )
|
181
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
182
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
183
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
184
|
+
5. Create a new Pull Request
|
data/bin/bundler
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'bundler' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('bundler', 'bundler')
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "stackd"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/htmldiff
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'htmldiff' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('diff-lcs', 'htmldiff')
|
data/bin/ldiff
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'ldiff' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('diff-lcs', 'ldiff')
|
data/bin/rake
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rake' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rake', 'rake')
|
data/bin/rdoc
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rdoc' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rdoc', 'rdoc')
|
data/bin/restclient
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'restclient' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rest-client', 'restclient')
|
data/bin/ri
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'ri' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rdoc', 'ri')
|
data/bin/rspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rspec' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rspec-core', 'rspec')
|
data/bin/safe_yaml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'safe_yaml' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('safe_yaml', 'safe_yaml')
|
data/bin/setup
ADDED
data/bin/stackd
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'stackd' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('stackd', 'stackd')
|
data/lib/stackd.rb
CHANGED
@@ -1,4 +1,29 @@
|
|
1
|
-
require "stackd/version"
|
2
|
-
|
3
1
|
module Stackd
|
2
|
+
autoload :Version, 'stackd/version'
|
3
|
+
autoload :Config, 'stackd/config'
|
4
|
+
autoload :Client, 'stackd/client'
|
5
|
+
autoload :AuthRequest, 'stackd/auth_request'
|
6
|
+
autoload :Token, 'stackd/token'
|
7
|
+
autoload :Error, 'stackd/error'
|
8
|
+
autoload :ServerError, 'stackd/server_error'
|
9
|
+
|
10
|
+
module Util
|
11
|
+
autoload :Portal, 'stackd/util/portal'
|
12
|
+
autoload :HTTP, 'stackd/util/http'
|
13
|
+
end
|
14
|
+
|
15
|
+
module Concerns
|
16
|
+
autoload :TattrAccessor, 'stackd/concerns/tattr_accessor'
|
17
|
+
autoload :RequireAttr, 'stackd/concerns/require_attr'
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def config
|
22
|
+
@config ||= Stackd::Config.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def configure
|
26
|
+
yield config
|
27
|
+
end
|
28
|
+
end
|
4
29
|
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
3
|
+
require 'unirest'
|
4
|
+
|
5
|
+
module Stackd
|
6
|
+
class AuthRequest
|
7
|
+
include Concerns::TattrAccessor
|
8
|
+
include Concerns::RequireAttr
|
9
|
+
|
10
|
+
tattr_accessor client: Client,
|
11
|
+
state: String,
|
12
|
+
redirect_uri: String
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def password client, params
|
16
|
+
if params[:username].nil?
|
17
|
+
raise ArgumentError.new "username is required"
|
18
|
+
end
|
19
|
+
|
20
|
+
if params[:password].nil?
|
21
|
+
raise ArgumentError.new "password is required"
|
22
|
+
end
|
23
|
+
|
24
|
+
request_token client, grant_type: 'password',
|
25
|
+
username: params[:username],
|
26
|
+
password: params[:password],
|
27
|
+
scope: params[:scope]
|
28
|
+
end
|
29
|
+
|
30
|
+
def client client, params = {}
|
31
|
+
request_token client, grant_type: 'client_credentials',
|
32
|
+
scope: params[:scope]
|
33
|
+
end
|
34
|
+
|
35
|
+
def refresh client, token, params = {}
|
36
|
+
if token.nil?
|
37
|
+
raise ArgumentError.new "Token is required"
|
38
|
+
end
|
39
|
+
|
40
|
+
if token.refresh_token.nil?
|
41
|
+
raise ArgumentError.new "Token must have refresh_token"
|
42
|
+
end
|
43
|
+
|
44
|
+
request_token client, grant_type: 'refresh_token',
|
45
|
+
refresh_token: token.refresh_token,
|
46
|
+
scope: params[:scope]
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def request_token client, params = {}
|
52
|
+
if params[:grant_type].nil?
|
53
|
+
raise ArgumentError.new "grant_type not provided"
|
54
|
+
end
|
55
|
+
|
56
|
+
res = Unirest.post "#{Stackd.config.api_url}/token", {
|
57
|
+
auth: {
|
58
|
+
user: client.id,
|
59
|
+
password: client.secret
|
60
|
+
},
|
61
|
+
headers: {'Content-Type' => 'application/x-www-form-urlencoded'},
|
62
|
+
parameters: params.reject { |k,v| v.nil? }
|
63
|
+
}
|
64
|
+
|
65
|
+
if res.code == 200
|
66
|
+
token = client.tokens.new res.body.symbolize_keys
|
67
|
+
|
68
|
+
on_grant_callbacks = \
|
69
|
+
Stackd::Token.instance_variable_get :@_on_grant_callbacks
|
70
|
+
on_grant_callbacks.each {|c| c.(token) }
|
71
|
+
|
72
|
+
token
|
73
|
+
else
|
74
|
+
raise Error.new res.body.symbolize_keys
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def initialize client, attrs = {}
|
80
|
+
if client.nil?
|
81
|
+
raise ArgumentError.new "client not provided"
|
82
|
+
else
|
83
|
+
self.client = client
|
84
|
+
end
|
85
|
+
|
86
|
+
attrs.each do |key, val|
|
87
|
+
public_send :"#{key}=", val
|
88
|
+
end
|
89
|
+
|
90
|
+
if state.nil?
|
91
|
+
self.state = SecureRandom.urlsafe_base64 9
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def url params = {}
|
96
|
+
uri = auth_url
|
97
|
+
|
98
|
+
uri.query_values = {
|
99
|
+
response_type: 'code',
|
100
|
+
client_id: client.id,
|
101
|
+
state: state,
|
102
|
+
redirect_uri: params[:redirect_uri],
|
103
|
+
scope: params[:scope]
|
104
|
+
}.reject { |k,v| v.nil? }
|
105
|
+
|
106
|
+
uri.to_s
|
107
|
+
end
|
108
|
+
|
109
|
+
def callback params
|
110
|
+
unless params[:code] || params[:error]
|
111
|
+
raise ArgumentError.new 'no code or error'
|
112
|
+
end
|
113
|
+
|
114
|
+
if params[:state] != state
|
115
|
+
raise StateMismatchError.new "#{params[:state]} != #{state}"
|
116
|
+
end
|
117
|
+
|
118
|
+
if params[:error]
|
119
|
+
raise Error.new params
|
120
|
+
else
|
121
|
+
self.class.send :request_token, client, {
|
122
|
+
grant_type: 'authorization_code',
|
123
|
+
code: params[:code],
|
124
|
+
redirect_uri: redirect_uri
|
125
|
+
}
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def auth_url
|
132
|
+
Addressable::URI.parse Stackd.config.auth_url
|
133
|
+
end
|
134
|
+
|
135
|
+
class StateMismatchError < StandardError
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Stackd
|
2
|
+
class Client
|
3
|
+
include Concerns::TattrAccessor
|
4
|
+
include Concerns::RequireAttr
|
5
|
+
|
6
|
+
tattr_accessor id: String,
|
7
|
+
secret: String
|
8
|
+
|
9
|
+
def initialize attrs
|
10
|
+
attrs.each do |attr, val|
|
11
|
+
public_send :"#{attr}=", val
|
12
|
+
end
|
13
|
+
|
14
|
+
begin
|
15
|
+
require_attr! :id, :secret
|
16
|
+
rescue Concerns::RequireAttr::AttrNotSetError => e
|
17
|
+
raise ArgumentError.new e.message
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def auth_requests
|
22
|
+
@auth_requests ||= Util::Portal.new self, AuthRequest
|
23
|
+
end
|
24
|
+
|
25
|
+
def tokens
|
26
|
+
@tokens ||= Util::Portal.new self, Token
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Stackd
|
4
|
+
module Concerns
|
5
|
+
module RequireAttr
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def require_attr! *attrs
|
9
|
+
attrs.each do |key|
|
10
|
+
if public_send(key).nil?
|
11
|
+
raise AttrNotSetError.new "#{key} not set"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class AttrNotSetError < StandardError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Stackd
|
4
|
+
module Concerns
|
5
|
+
module TattrAccessor
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
@_tattrs = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
class_methods do
|
13
|
+
def tattr_accessor tattrs
|
14
|
+
tattrs.each do |tattr, type|
|
15
|
+
attr_reader tattr
|
16
|
+
|
17
|
+
@_tattrs[tattr] = type
|
18
|
+
|
19
|
+
define_method :"#{tattr}=" do |value|
|
20
|
+
unless value.nil? || self.class.tattr_type_match?(tattr, value)
|
21
|
+
tattr_type = self.class.tattr_type tattr
|
22
|
+
raise TypeMismatchError.new [
|
23
|
+
"tattr: #{tattr}",
|
24
|
+
"tattr_type: #{tattr_type}",
|
25
|
+
"value: #{value.inspect}"
|
26
|
+
].join ', '
|
27
|
+
end
|
28
|
+
|
29
|
+
instance_variable_set :"@#{tattr}", value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def tattr_type_match? tattr, value
|
35
|
+
value.is_a? tattr_type(tattr)
|
36
|
+
end
|
37
|
+
|
38
|
+
def tattr_type tattr
|
39
|
+
@_tattrs[tattr]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def tattr? key
|
44
|
+
self.class.instance_variable_get(:@_tattrs).has_key? key
|
45
|
+
end
|
46
|
+
|
47
|
+
class TypeMismatchError < StandardError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Stackd
|
2
|
+
class Config
|
3
|
+
include Concerns::TattrAccessor
|
4
|
+
|
5
|
+
tattr_accessor auth_url: String,
|
6
|
+
token_url: String,
|
7
|
+
api_url: String
|
8
|
+
|
9
|
+
def initialize attrs = {}
|
10
|
+
attrs.each do |key, val|
|
11
|
+
public_send :"#{key}=", val
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def auth_url
|
16
|
+
@auth_url || "https://stackd.com/authorize"
|
17
|
+
end
|
18
|
+
|
19
|
+
def api_url
|
20
|
+
@api_url || "https://api.stackd.com"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/stackd/error.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Stackd
|
2
|
+
class Error < StandardError
|
3
|
+
include Concerns::TattrAccessor
|
4
|
+
include Concerns::RequireAttr
|
5
|
+
|
6
|
+
tattr_accessor error: String,
|
7
|
+
error_description: String,
|
8
|
+
error_uri: String,
|
9
|
+
error_params: Hash
|
10
|
+
|
11
|
+
def initialize attrs
|
12
|
+
attrs.each do |key, val|
|
13
|
+
public_send :"#{key}=", val if tattr? key
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
require_attr! :error
|
18
|
+
rescue Concerns::RequireAttr::AttrNotSetError => e
|
19
|
+
raise ArgumentError.new e.message
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def message
|
24
|
+
error_description
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/stackd/token.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'active_support/core_ext/object/to_query'
|
2
|
+
|
3
|
+
module Stackd
|
4
|
+
class Token
|
5
|
+
include Concerns::TattrAccessor
|
6
|
+
include Concerns::RequireAttr
|
7
|
+
|
8
|
+
@_on_grant_callbacks = []
|
9
|
+
|
10
|
+
tattr_accessor client: Client,
|
11
|
+
access_token: String,
|
12
|
+
token_type: String,
|
13
|
+
expires_in: Integer,
|
14
|
+
refresh_token: String,
|
15
|
+
scope: String,
|
16
|
+
service_id: String,
|
17
|
+
user_id: String
|
18
|
+
|
19
|
+
def self.on_grant &block
|
20
|
+
@_on_grant_callbacks.push block
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize client, attrs = {}
|
24
|
+
self.client = client
|
25
|
+
|
26
|
+
attrs.each do |key, val|
|
27
|
+
public_send :"#{key}=", val if tattr? key
|
28
|
+
end
|
29
|
+
|
30
|
+
begin
|
31
|
+
require_attr! :client, :access_token, :token_type
|
32
|
+
rescue Concerns::RequireAttr::AttrNotSetError => e
|
33
|
+
raise ArgumentError.new e.message
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def get *args
|
38
|
+
Util::HTTP.get self, get_url(*args)
|
39
|
+
end
|
40
|
+
|
41
|
+
def post *args
|
42
|
+
url_args = reject_body_arg *args
|
43
|
+
body_arg = get_body_arg *args
|
44
|
+
content_type = content_type_for body_arg
|
45
|
+
|
46
|
+
Util::HTTP.post self, get_url(*url_args), body_arg, content_type
|
47
|
+
end
|
48
|
+
|
49
|
+
def put *args
|
50
|
+
url_args = reject_body_arg *args
|
51
|
+
body_arg = get_body_arg *args
|
52
|
+
content_type = content_type_for body_arg
|
53
|
+
|
54
|
+
Util::HTTP.put self, get_url(*url_args), body_arg, content_type
|
55
|
+
end
|
56
|
+
|
57
|
+
def patch *args
|
58
|
+
url_args = reject_body_arg *args
|
59
|
+
body_arg = get_body_arg *args
|
60
|
+
content_type = content_type_for body_arg
|
61
|
+
|
62
|
+
Util::HTTP.patch self, get_url(*url_args), body_arg, content_type
|
63
|
+
end
|
64
|
+
|
65
|
+
def delete *args
|
66
|
+
url_args = reject_body_arg *args
|
67
|
+
body_arg = get_body_arg *args
|
68
|
+
content_type = content_type_for body_arg
|
69
|
+
|
70
|
+
Util::HTTP.delete self, get_url(*url_args), body_arg, content_type
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def get_url *args
|
76
|
+
args.inject Stackd.config.api_url do |url, part|
|
77
|
+
if part.is_a? Hash
|
78
|
+
"#{url}?#{part.to_query}"
|
79
|
+
else part = part.to_s
|
80
|
+
if part.index(Stackd.config.api_url) == 0
|
81
|
+
part
|
82
|
+
elsif part.index('/') == 0
|
83
|
+
"#{url}#{part}"
|
84
|
+
else
|
85
|
+
"#{url}/#{part}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def reject_body_arg *args
|
92
|
+
args.reject {|a| a.is_a? Hash }
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_body_arg *args
|
96
|
+
args.last if args.last.is_a? Hash
|
97
|
+
end
|
98
|
+
|
99
|
+
def content_type_for body_arg
|
100
|
+
:json unless body_arg.nil?
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'uri'
|
3
|
+
require 'unirest'
|
4
|
+
require 'active_support/core_ext/hash/keys'
|
5
|
+
|
6
|
+
module Stackd
|
7
|
+
module Util
|
8
|
+
module HTTP
|
9
|
+
def self.get credentials, url
|
10
|
+
request_without_body :get, credentials, url
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.post credentials, url, body, content_type
|
14
|
+
request_with_body :post, credentials, url, body, content_type
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.put credentials, url, body, content_type
|
18
|
+
request_with_body :put, credentials, url, body, content_type
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.patch credentials, url, body, content_type
|
22
|
+
request_with_body :patch, credentials, url, body, content_type
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.delete credentials, url
|
26
|
+
request_without_body :delete, credentials, url
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def self.request_without_body method, credentials, url
|
32
|
+
validate_params! credentials, url
|
33
|
+
|
34
|
+
basic_auth = basic_auth_for credentials
|
35
|
+
headers = headers_for credentials
|
36
|
+
|
37
|
+
res = Unirest.public_send method, url, headers: headers,
|
38
|
+
auth: basic_auth
|
39
|
+
|
40
|
+
process_response res
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.request_with_body method, credentials, url, body, content_type
|
44
|
+
validate_params! credentials, url, body, content_type
|
45
|
+
|
46
|
+
basic_auth = basic_auth_for credentials
|
47
|
+
headers = headers_for credentials, content_type
|
48
|
+
parameters = parameters_for body, content_type
|
49
|
+
|
50
|
+
res = Unirest.public_send method, url, headers: headers,
|
51
|
+
auth: basic_auth,
|
52
|
+
parameters: parameters
|
53
|
+
|
54
|
+
process_response res
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.validate_params! credentials, url, body = nil, content_type = nil
|
58
|
+
unless creds_are_valid? credentials
|
59
|
+
raise ArgumentError.new \
|
60
|
+
"credentials must be a Stackd::Token, Hash with :user + " + \
|
61
|
+
":password, credentials: #{credentials.inspect}"
|
62
|
+
end
|
63
|
+
|
64
|
+
unless url.is_a? String
|
65
|
+
raise ArgumentError.new "url must be String. url: #{url.inspect}"
|
66
|
+
end
|
67
|
+
|
68
|
+
unless body.nil? || body.is_a?(Hash)
|
69
|
+
raise ArgumentError.new \
|
70
|
+
"body must be a Hash or nil. body: #{body.inspect}"
|
71
|
+
end
|
72
|
+
|
73
|
+
unless content_type.nil? || [:json, :form].include?(content_type)
|
74
|
+
raise ArgumentError.new \
|
75
|
+
"content_type must be :json, :form, or nil. " + \
|
76
|
+
"content_type: #{content_type.inspect}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.creds_are_valid? credentials
|
81
|
+
credentials.nil? || \
|
82
|
+
credentials.is_a?(Client) || \
|
83
|
+
credentials.is_a?(Token) || \
|
84
|
+
(credentials.is_a?(Hash) && \
|
85
|
+
credentials[:user].is_a?(String) && \
|
86
|
+
credentials[:password].is_a?(String))
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.basic_auth_for credentials
|
90
|
+
if credentials.is_a? Hash
|
91
|
+
credentials
|
92
|
+
elsif credentials.is_a? Client
|
93
|
+
{ user: credentials.id, password: credentials.secret }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.headers_for credentials, content_type = nil
|
98
|
+
headers = {}
|
99
|
+
|
100
|
+
if credentials.is_a? Token
|
101
|
+
headers.merge!(
|
102
|
+
{ 'Authorization' => "Bearer #{credentials.access_token}" })
|
103
|
+
end
|
104
|
+
|
105
|
+
unless content_type.nil?
|
106
|
+
content_type_value = content_type == :json \
|
107
|
+
? 'application/json' \
|
108
|
+
: 'application/x-www-form-urlencoded'
|
109
|
+
|
110
|
+
headers.merge!({ 'Content-Type' => content_type_value })
|
111
|
+
end
|
112
|
+
|
113
|
+
headers
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.parameters_for body, content_type
|
117
|
+
if content_type == :json
|
118
|
+
JSON.generate body
|
119
|
+
else
|
120
|
+
body
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.process_response res
|
125
|
+
if res.code >= 500
|
126
|
+
raise ServerError.new res.body.deep_symbolize_keys
|
127
|
+
elsif res.code >= 400
|
128
|
+
raise Error.new res.body.deep_symbolize_keys
|
129
|
+
else
|
130
|
+
res.body.deep_symbolize_keys
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/lib/stackd/version.rb
CHANGED
data/stackd.gemspec
CHANGED
@@ -1,18 +1,31 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'stackd/version'
|
4
5
|
|
5
|
-
Gem::Specification.new do |
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
s.homepage = "https://stackd.com/"
|
11
|
-
s.summary = %q{Stackd gem}
|
12
|
-
s.description = %q{Stackd gem}
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "stackd"
|
8
|
+
spec.version = Stackd::VERSION
|
9
|
+
spec.authors = ["Stephen Ausman"]
|
10
|
+
spec.email = ["stephen@stackd.com"]
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
spec.summary = "Stackd Ruby client"
|
13
|
+
spec.description = "https://stackd.com"
|
14
|
+
spec.homepage = "https://github.com/stackd/stackd-ruby"
|
15
|
+
spec.license = "Unlicense"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
}
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.8"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.2"
|
26
|
+
spec.add_development_dependency "webmock", "~> 1.21"
|
27
|
+
|
28
|
+
spec.add_dependency "unirest", "~> 1.1"
|
29
|
+
spec.add_dependency "addressable", "~> 2.3"
|
30
|
+
spec.add_dependency "activesupport", "~> 4.2"
|
18
31
|
end
|
metadata
CHANGED
@@ -1,50 +1,174 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stackd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
|
-
-
|
7
|
+
- Stephen Ausman
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
14
|
-
|
15
|
-
|
11
|
+
date: 2015-12-21 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.8'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
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.2'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: webmock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.21'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.21'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: unirest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.1'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: addressable
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.3'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.3'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: activesupport
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '4.2'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '4.2'
|
111
|
+
description: https://stackd.com
|
112
|
+
email:
|
113
|
+
- stephen@stackd.com
|
16
114
|
executables: []
|
17
115
|
extensions: []
|
18
116
|
extra_rdoc_files: []
|
19
117
|
files:
|
20
|
-
- .gitignore
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rspec"
|
120
|
+
- ".travis.yml"
|
21
121
|
- Gemfile
|
122
|
+
- LICENSE
|
123
|
+
- README.md
|
22
124
|
- Rakefile
|
125
|
+
- bin/bundler
|
126
|
+
- bin/console
|
127
|
+
- bin/htmldiff
|
128
|
+
- bin/ldiff
|
129
|
+
- bin/rake
|
130
|
+
- bin/rdoc
|
131
|
+
- bin/restclient
|
132
|
+
- bin/ri
|
133
|
+
- bin/rspec
|
134
|
+
- bin/safe_yaml
|
135
|
+
- bin/setup
|
136
|
+
- bin/stackd
|
23
137
|
- lib/stackd.rb
|
138
|
+
- lib/stackd/auth_request.rb
|
139
|
+
- lib/stackd/client.rb
|
140
|
+
- lib/stackd/concerns/require_attr.rb
|
141
|
+
- lib/stackd/concerns/tattr_accessor.rb
|
142
|
+
- lib/stackd/config.rb
|
143
|
+
- lib/stackd/error.rb
|
144
|
+
- lib/stackd/server_error.rb
|
145
|
+
- lib/stackd/token.rb
|
146
|
+
- lib/stackd/util/http.rb
|
147
|
+
- lib/stackd/util/portal.rb
|
24
148
|
- lib/stackd/version.rb
|
25
149
|
- stackd.gemspec
|
26
|
-
homepage: https://
|
27
|
-
licenses:
|
150
|
+
homepage: https://github.com/stackd/stackd-ruby
|
151
|
+
licenses:
|
152
|
+
- Unlicense
|
153
|
+
metadata: {}
|
28
154
|
post_install_message:
|
29
155
|
rdoc_options: []
|
30
156
|
require_paths:
|
31
157
|
- lib
|
32
158
|
required_ruby_version: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
159
|
requirements:
|
35
|
-
- -
|
160
|
+
- - ">="
|
36
161
|
- !ruby/object:Gem::Version
|
37
162
|
version: '0'
|
38
163
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
164
|
requirements:
|
41
|
-
- -
|
165
|
+
- - ">="
|
42
166
|
- !ruby/object:Gem::Version
|
43
167
|
version: '0'
|
44
168
|
requirements: []
|
45
169
|
rubyforge_project:
|
46
|
-
rubygems_version:
|
170
|
+
rubygems_version: 2.4.5.1
|
47
171
|
signing_key:
|
48
|
-
specification_version:
|
49
|
-
summary: Stackd
|
172
|
+
specification_version: 4
|
173
|
+
summary: Stackd Ruby client
|
50
174
|
test_files: []
|