dn 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/.gitignore +17 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +22 -0
- data/README.md +109 -0
- data/Rakefile +14 -0
- data/dn.gemspec +29 -0
- data/lib/designer_news.rb +16 -0
- data/lib/designer_news/client.rb +48 -0
- data/lib/designer_news/client/users.rb +9 -0
- data/lib/designer_news/default_options.rb +38 -0
- data/lib/designer_news/middleware/raise_error.rb +15 -0
- data/lib/designer_news/mixins/authentication.rb +7 -0
- data/lib/designer_news/mixins/configurable.rb +40 -0
- data/lib/designer_news/mixins/connection.rb +15 -0
- data/lib/designer_news/model.rb +41 -0
- data/lib/designer_news/response/error.rb +111 -0
- data/lib/designer_news/user.rb +10 -0
- data/lib/designer_news/version.rb +3 -0
- data/spec/cassettes/Users/_me/returns_the_User_info_as_JSON.json +1 -0
- data/spec/designer_news/client/users_spec.rb +16 -0
- data/spec/designer_news/client_spec.rb +15 -0
- data/spec/designer_news/error.rb +69 -0
- data/spec/designer_news_spec.rb +12 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +64 -0
- metadata +188 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4d82ed18c153399b1d161dff253506ca08c07fd6
|
4
|
+
data.tar.gz: dcd1ee82cc7e08461b66b47cb52c30abcd2ec9ab
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 51a898dc802f4133c7ab6852f24d443e28b1295ff319644ecce914f9f2d182e1195ee0d57df4a0b403bfe139720523691990afd27ce10c6d8e96661a90751dd0
|
7
|
+
data.tar.gz: d0793141f7bf1ad8592fcc047f01de411cbed334b562c1b94a040f444d1dc0149babe10c75fce9599aa1d00c38580058f6b56f7deced22ae29121d8d5e7f47f8
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 John McDowall
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
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
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# Designer News Ruby API Client
|
2
|
+
|
3
|
+
This is the Ruby client library that wraps the [Designer News API](http://developers.news.layervault.com). It assumes you have used another oAuth 2 library to obtain and manage a valid access token. It is based on
|
4
|
+
the [Ruby gem for the LayerVault API](https://github.com/layervault/layervault_ruby_client).
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Put this in your Gemfile and smoke it:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'dn'
|
12
|
+
```
|
13
|
+
|
14
|
+
Or install it:
|
15
|
+
|
16
|
+
```shell
|
17
|
+
$ gem install dn
|
18
|
+
```
|
19
|
+
|
20
|
+
## Supported oAuth flows
|
21
|
+
|
22
|
+
Currently only [Resource Owner Credentials](https://github.com/applicake/doorkeeper/wiki/Using-Resource-Owner-Password-Credentials-flow) and [Client Credentials](https://github.com/applicake/doorkeeper/wiki/Client-Credentials-flow) are supported.
|
23
|
+
|
24
|
+
## Requesting an Access Token
|
25
|
+
|
26
|
+
0. Please send an email to news@layervault.com to request your API credentials. Please include your callback URL. One day, this might be automated.
|
27
|
+
0. Receive a friendly note back from the LayerVault staff with your application credentials.
|
28
|
+
0. Note your ```client_id``` and ```client_secret```.
|
29
|
+
0. Try the example Access Token request below.
|
30
|
+
|
31
|
+
```
|
32
|
+
curl -i https://api-news.layervault.com/oauth/token \
|
33
|
+
-F grant_type="password" \
|
34
|
+
-F username="<username_goes_here>" \
|
35
|
+
-F password="<password_goes_here>" \
|
36
|
+
-F client_id="<client_id_goes_here>" \
|
37
|
+
-F client_secret="<client_secret_goes_here>"
|
38
|
+
```
|
39
|
+
3. You now have an access token. You can make API requests by calling via CURL like so:
|
40
|
+
|
41
|
+
```
|
42
|
+
curl -H 'Authorization: Bearer <your access token>' \
|
43
|
+
'https://api-news.layervault.com/api/v1/me'
|
44
|
+
```
|
45
|
+
|
46
|
+
## Initializing the Client
|
47
|
+
|
48
|
+
You can initialize the client via Environment Variables or by passing configurations options into the client when you create it, like this:
|
49
|
+
|
50
|
+
``` ruby
|
51
|
+
@client = DesignerNews::Client.new({
|
52
|
+
access_token: 'your_access_token',
|
53
|
+
api_endpoint: 'your_api_endpoint'
|
54
|
+
})
|
55
|
+
```
|
56
|
+
|
57
|
+
Or you can also say:
|
58
|
+
|
59
|
+
``` ruby
|
60
|
+
DesignerNews.client.access_token = 'access_token'
|
61
|
+
DesignerNews.client.api_endpoint = 'api_endpoint'
|
62
|
+
```
|
63
|
+
|
64
|
+
### Environment Variables
|
65
|
+
|
66
|
+
``` ruby
|
67
|
+
# You Designer News API access token
|
68
|
+
ENV['DESIGNER_NEWS_ACCESS_TOKEN']
|
69
|
+
|
70
|
+
# The API Endpoint you wish to target calls against (defaults to https://api-news.layervault.com/api/v1/)
|
71
|
+
ENV['DESIGNER_NEWS_API_ENDPOINT']
|
72
|
+
|
73
|
+
# Defaults to Designer News Ruby Gem #{DesignerNews::VERSION}
|
74
|
+
ENV['DESIGNER_NEWS_USER_AGENT']
|
75
|
+
```
|
76
|
+
|
77
|
+
### The User Agent
|
78
|
+
|
79
|
+
You should set the User agent to include your email address so that in the event your client does something wrong we can contact you.
|
80
|
+
|
81
|
+
## Making API calls
|
82
|
+
|
83
|
+
You can use the `DesignerNews.client.<api_operation>` methods to call the API to perform actions. Alternatively, each API object has simple object model that allows you to say:
|
84
|
+
|
85
|
+
``` ruby
|
86
|
+
DesignerNews.client.access_token = 'access_token'
|
87
|
+
p = DesignerNews::Organization.for('layervault')
|
88
|
+
p.create_project('my new project')
|
89
|
+
```
|
90
|
+
|
91
|
+
And so on.
|
92
|
+
|
93
|
+
### Simple Object Model
|
94
|
+
|
95
|
+
There's a very simple object model provided by classes that implement [Hashie](https://github.com/intridea/hashie) objects that wrap the JSON responses from the ```DesignerNews.client``` interface. The objects mostly all follow a ```.for``` pattern that accepts the appropriate number of arguments for the level of nesting the object represents.
|
96
|
+
|
97
|
+
#### Associations
|
98
|
+
|
99
|
+
When using the simple object model, associations will be hydrated into the correct child objects for the immediate child relationships only, allowing a simple level of traversal down the object model hierarchy. There is no lazy loding support that will automatically hydrated any deeper associations - you must perform new queries.
|
100
|
+
|
101
|
+
## Local Development
|
102
|
+
|
103
|
+
Coming soon.
|
104
|
+
|
105
|
+
## Client Methods Summary
|
106
|
+
|
107
|
+
### General
|
108
|
+
- DesignerNews.client.me
|
109
|
+
|
data/Rakefile
ADDED
data/dn.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'designer_news/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dn"
|
8
|
+
spec.version = DesignerNews::VERSION
|
9
|
+
spec.authors = ["Kelly Sutton"]
|
10
|
+
spec.email = ["kelly@layervault.com"]
|
11
|
+
spec.description = %q{The Designer News Ruby API client.}
|
12
|
+
spec.summary = %q{Provides Ruby native wrappers for the Designer News API.}
|
13
|
+
spec.homepage = "https://github.com/layervault/dn_ruby_client"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "faraday"
|
22
|
+
spec.add_dependency "hashie"
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
spec.add_development_dependency "multi_json"
|
27
|
+
spec.add_development_dependency "vcr"
|
28
|
+
spec.add_development_dependency "webmock", "1.13"
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "designer_news/version"
|
2
|
+
require "designer_news/client"
|
3
|
+
require "designer_news/mixins/configurable"
|
4
|
+
require "designer_news/default_options"
|
5
|
+
|
6
|
+
module DesignerNews
|
7
|
+
class << self
|
8
|
+
include DesignerNews::Configurable
|
9
|
+
|
10
|
+
def client
|
11
|
+
@client ||= DesignerNews::Client.new(options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
DesignerNews.setup
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'multi_json'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require 'designer_news/mixins/authentication'
|
6
|
+
require 'designer_news/mixins/configurable'
|
7
|
+
require 'designer_news/mixins/connection'
|
8
|
+
|
9
|
+
require 'designer_news/client/users'
|
10
|
+
|
11
|
+
require 'designer_news/model'
|
12
|
+
require 'designer_news/user'
|
13
|
+
|
14
|
+
module DesignerNews
|
15
|
+
|
16
|
+
class ClientParamsError < Exception ; end
|
17
|
+
|
18
|
+
class Client
|
19
|
+
include DesignerNews::Authentication
|
20
|
+
include DesignerNews::Configurable
|
21
|
+
include DesignerNews::Connection
|
22
|
+
|
23
|
+
include DesignerNews::Client::Users
|
24
|
+
|
25
|
+
def initialize(options={})
|
26
|
+
DesignerNews::Configurable.keys.each do |key|
|
27
|
+
instance_variable_set(:"@#{key}", options[key] || DesignerNews.instance_variable_get(:"@#{key}"))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def get(url, options = {})
|
32
|
+
request :get, url, options
|
33
|
+
end
|
34
|
+
|
35
|
+
def post(url, options = {})
|
36
|
+
request :post, url, options
|
37
|
+
end
|
38
|
+
|
39
|
+
def put(url, options = {})
|
40
|
+
request :put, url, options
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete(url, options = {})
|
44
|
+
request :delete, url, options
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'designer_news/version'
|
2
|
+
require 'designer_news/middleware/raise_error'
|
3
|
+
|
4
|
+
module DesignerNews
|
5
|
+
module Default
|
6
|
+
|
7
|
+
API_ENDPOINT = "https://api-news.layervault.com/api/v1/".freeze
|
8
|
+
USER_AGENT = "Designer News Ruby Gem #{DesignerNews::VERSION}".freeze
|
9
|
+
|
10
|
+
MIDDLEWARE = Faraday::Builder.new do |builder|
|
11
|
+
builder.use DesignerNews::Middleware::RaiseError
|
12
|
+
builder.request :url_encoded
|
13
|
+
builder.adapter Faraday.default_adapter
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def options
|
18
|
+
Hash[DesignerNews::Configurable.keys.map{|key| [key, send(key)]}]
|
19
|
+
end
|
20
|
+
|
21
|
+
def access_token
|
22
|
+
ENV['DESIGNER_NEWS_ACCESS_TOKEN']
|
23
|
+
end
|
24
|
+
|
25
|
+
def api_endpoint
|
26
|
+
ENV['DESIGNER_NEWS_API_ENDPOINT'] || API_ENDPOINT
|
27
|
+
end
|
28
|
+
|
29
|
+
def user_agent
|
30
|
+
ENV['DESIGNER_NEWS_USER_AGENT'] || USER_AGENT
|
31
|
+
end
|
32
|
+
|
33
|
+
def middleware
|
34
|
+
MIDDLEWARE
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'designer_news/response/error'
|
3
|
+
|
4
|
+
module DesignerNews
|
5
|
+
module Middleware
|
6
|
+
class RaiseError < Faraday::Response::Middleware
|
7
|
+
private
|
8
|
+
def on_complete(response)
|
9
|
+
if error = DesignerNews::Response::Error.from_response(response)
|
10
|
+
raise error
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module DesignerNews
|
2
|
+
module Configurable
|
3
|
+
attr_accessor :access_token, :user_agent, :middleware
|
4
|
+
attr_writer :api_endpoint
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def keys
|
8
|
+
@keys ||= [
|
9
|
+
:access_token,
|
10
|
+
:api_endpoint,
|
11
|
+
:user_agent,
|
12
|
+
:middleware
|
13
|
+
]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def configure
|
18
|
+
yield self
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset!
|
22
|
+
DesignerNews::Configurable.keys.each do |key|
|
23
|
+
instance_variable_set(:"@#{key}", DesignerNews::Default.options[key])
|
24
|
+
end
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
alias setup reset!
|
29
|
+
|
30
|
+
def api_endpoint
|
31
|
+
::File.join(@api_endpoint, "")
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def options
|
37
|
+
Hash[DesignerNews::Configurable.keys.map{|key| [key, instance_variable_get(:"@#{key}")]}]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module DesignerNews
|
2
|
+
module Connection
|
3
|
+
def request(method, path, data, options={})
|
4
|
+
@last_response = response = connection.send(method, URI.encode(path.to_s), data, options)
|
5
|
+
JSON.parse(response.body)
|
6
|
+
end
|
7
|
+
|
8
|
+
def connection
|
9
|
+
@connection ||= Faraday.new(url: @api_endpoint, builder: @middleware) do |conn|
|
10
|
+
conn.headers[:user_agent] = user_agent
|
11
|
+
conn.headers[:Authorization] = "Bearer #{access_token}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module DesignerNews
|
4
|
+
class Model < Hashie::Mash
|
5
|
+
class << self
|
6
|
+
def build_associations(hash, *associations)
|
7
|
+
mapping = {}
|
8
|
+
|
9
|
+
associations.each do |association|
|
10
|
+
mapping[association] = hash.fetch(association.to_s, {})
|
11
|
+
hash.delete(association) if mapping[association]
|
12
|
+
end
|
13
|
+
|
14
|
+
instance = new(hash)
|
15
|
+
|
16
|
+
associations.each do |association|
|
17
|
+
klass = association_to_class(association)
|
18
|
+
objs = mapping[association].map { |p| klass.new(p) }
|
19
|
+
instance[association.to_s] = objs
|
20
|
+
end
|
21
|
+
|
22
|
+
instance
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def association_to_class(association)
|
28
|
+
class_name = association.to_s.chomp('s').capitalize
|
29
|
+
DesignerNews.const_get(class_name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_context(context_arguments={})
|
34
|
+
self.context = {}
|
35
|
+
context_arguments.each do |argument, value|
|
36
|
+
self.context[argument] = value
|
37
|
+
end
|
38
|
+
self
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module DesignerNews
|
2
|
+
module Response
|
3
|
+
class Error < StandardError
|
4
|
+
|
5
|
+
def self.from_response(response)
|
6
|
+
status = response[:status].to_i
|
7
|
+
body = response[:body].to_s
|
8
|
+
headers = response[:response_headers]
|
9
|
+
|
10
|
+
if klass = case status
|
11
|
+
when 400 then DesignerNews::Response::BadRequest
|
12
|
+
when 401 then DesignerNews::Response::Unauthorized
|
13
|
+
when 403 then DesignerNews::Response::Forbidden
|
14
|
+
when 404 then DesignerNews::Response::NotFound
|
15
|
+
when 406 then DesignerNews::Response::NotAcceptable
|
16
|
+
when 400..499 then DesignerNews::Response::ClientError
|
17
|
+
when 500 then DesignerNews::Response::InternalServerError
|
18
|
+
when 501 then DesignerNews::Response::NotImplemented
|
19
|
+
when 502 then DesignerNews::Response::BadGateway
|
20
|
+
when 503 then DesignerNews::Response::ServiceUnavailable
|
21
|
+
when 500..599 then DesignerNews::Response::ServerError
|
22
|
+
end
|
23
|
+
klass.new(response)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(response=nil)
|
28
|
+
@response = response
|
29
|
+
super(build_error_message)
|
30
|
+
end
|
31
|
+
|
32
|
+
def errors
|
33
|
+
if data && data.is_a?(Hash)
|
34
|
+
data[:errors] || []
|
35
|
+
else
|
36
|
+
[]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def build_error_message
|
43
|
+
return nil if @response.nil?
|
44
|
+
|
45
|
+
message = "#{@response[:method].to_s.upcase} "
|
46
|
+
message << "#{@response[:url].to_s}: "
|
47
|
+
message << "#{@response[:status]} - "
|
48
|
+
message << "#{response_message}" unless response_message.nil?
|
49
|
+
message << "#{response_error}" unless response_error.nil?
|
50
|
+
message << "#{response_error_summary}" unless response_error_summary.nil?
|
51
|
+
message
|
52
|
+
end
|
53
|
+
|
54
|
+
def response_message
|
55
|
+
case data
|
56
|
+
when Hash
|
57
|
+
data[:message]
|
58
|
+
when String
|
59
|
+
data
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def response_error
|
64
|
+
"Error: #{data[:error]}" if data.is_a?(Hash) && data[:error]
|
65
|
+
end
|
66
|
+
|
67
|
+
def response_error_summary
|
68
|
+
return nil unless data.is_a?(Hash) && !Array(data[:errors]).empty?
|
69
|
+
|
70
|
+
summary = "\nError summary:\n"
|
71
|
+
summary << data[:errors].map do |hash|
|
72
|
+
hash.map { |k,v| " #{k}: #{v}" }
|
73
|
+
end.join("\n")
|
74
|
+
|
75
|
+
summary
|
76
|
+
end
|
77
|
+
|
78
|
+
def data
|
79
|
+
@data ||=
|
80
|
+
if (body = @response[:body]) && !body.empty?
|
81
|
+
if body.is_a?(String) &&
|
82
|
+
@response[:response_headers] &&
|
83
|
+
@response[:response_headers][:content_type] =~ /json/
|
84
|
+
|
85
|
+
MultiJson.load(body)
|
86
|
+
else
|
87
|
+
body
|
88
|
+
end
|
89
|
+
else
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class ClientError < Error; end
|
96
|
+
|
97
|
+
class BadRequest < ClientError; end
|
98
|
+
class Unauthorized < ClientError; end
|
99
|
+
class Forbidden < ClientError; end
|
100
|
+
class TooManyRequests < Forbidden; end
|
101
|
+
class NotFound < ClientError; end
|
102
|
+
class NotAcceptable < ClientError; end
|
103
|
+
|
104
|
+
class ServerError < Error; end
|
105
|
+
|
106
|
+
class InternalServerError < ServerError; end
|
107
|
+
class NotImplemented < ServerError; end
|
108
|
+
class BadGateway < ServerError; end
|
109
|
+
class ServiceUnavailable < ServerError; end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{"http_interactions":[{"request":{"method":"get","uri":"http://localhost:3000/api/v1/me","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"User-Agent":["Designer News Ruby Gem 0.1.0"],"Authorization":["Bearer <<ACCESS_TOKEN>>"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],"Accept":["*/*"]}},"response":{"status":{"code":200,"message":"OK "},"headers":{"Content-Type":["application/json; charset=utf-8"],"X-Ua-Compatible":["IE=Edge"],"Etag":["\"caf821832fe3d3bbb3a888ad026641c1\""],"Cache-Control":["max-age=0, private, must-revalidate"],"X-Request-Id":["1b816b072dd350f8ae93c142a83b02c0"],"X-Runtime":["0.005801"],"Server":["WEBrick/1.3.1 (Ruby/2.0.0/2013-06-27)"],"Date":["Sun, 23 Feb 2014 18:56:10 GMT"],"Content-Length":["260"],"Connection":["Keep-Alive"]},"body":{"encoding":"UTF-8","base64_string":"eyJtZSI6eyJmaXJzdF9uYW1lIjoiS2VsbHkiLCJsYXN0X25hbWUiOiJTdXR0\nb24iLCJjcmVhdGVkX2F0IjoiMjAxMi0xMS0xNVQwNDo0ODo0NVoiLCJwb3J0\ncmFpdF91cmwiOiIvc3lzdGVtL3VzZXJzL3JlbmRlcmVkX3BvcnRyYWl0cy8w\nMDAvMDAwLzAwMS9vcmlnaW5hbC9wb3J0cmFpdC0yMDEzLTEwLTI5XzIyXzA4\nXzQ0X18wMDAwLTEyMDEzMTAyOS01NDMwLTF2bDNtdmoucG5nPzEzODMwODQ1\nMjUiLCJqb2IiOiJGb3VuZGVyIGF0IExheWVyVmF1bHQifX0=\n"},"http_version":null},"recorded_at":"Sun, 23 Feb 2014 18:56:10 GMT"}],"recorded_with":"VCR 2.8.0"}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Users', :vcr do
|
4
|
+
|
5
|
+
before do
|
6
|
+
DesignerNews.reset!
|
7
|
+
@client = DesignerNews::Client.new
|
8
|
+
end
|
9
|
+
|
10
|
+
context '.me' do
|
11
|
+
it 'returns the User info as JSON' do
|
12
|
+
@client.me
|
13
|
+
assert_requested :get, designer_news_url("me")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'The API client' do
|
4
|
+
context 'Authentication' do
|
5
|
+
before do
|
6
|
+
DesignerNews.reset!
|
7
|
+
@client = DesignerNews.client
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'accepts an access token for authentication' do
|
11
|
+
@client.access_token = '32efabc231238'
|
12
|
+
expect(@client).to be_token_authenticated
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DesignerNews::Response::Error do
|
4
|
+
it "raises a BadRequest exception when the status is 400" do
|
5
|
+
response = { method: :get, response_headers: { content_type: 'json' }, status: 400 }
|
6
|
+
ex = DesignerNews::Response::Error.from_response(response)
|
7
|
+
expect(ex).to be_instance_of DesignerNews::Response::BadRequest
|
8
|
+
end
|
9
|
+
|
10
|
+
it "raises a Unauthorized exception when the status is 401" do
|
11
|
+
response = { method: :get, response_headers: { content_type: 'json' }, status: 401}
|
12
|
+
ex = DesignerNews::Response::Error.from_response(response)
|
13
|
+
expect(ex).to be_instance_of DesignerNews::Response::Unauthorized
|
14
|
+
end
|
15
|
+
|
16
|
+
it "raises a Frobidden exception when the status is 403" do
|
17
|
+
response = { method: :get, response_headers: { content_type: 'json' }, status: 403}
|
18
|
+
ex = DesignerNews::Response::Error.from_response(response)
|
19
|
+
expect(ex).to be_instance_of DesignerNews::Response::Forbidden
|
20
|
+
end
|
21
|
+
|
22
|
+
it "raises a NotFound exception when the status is 404" do
|
23
|
+
response = { method: :get, response_headers: { content_type: 'json' }, status: 404}
|
24
|
+
ex = DesignerNews::Response::Error.from_response(response)
|
25
|
+
expect(ex).to be_instance_of DesignerNews::Response::NotFound
|
26
|
+
end
|
27
|
+
|
28
|
+
it "raises a NotAcceptable exception when the status is 406" do
|
29
|
+
response = { method: :get, response_headers: { content_type: 'json' }, status: 406}
|
30
|
+
ex = DesignerNews::Response::Error.from_response(response)
|
31
|
+
expect(ex).to be_instance_of DesignerNews::Response::NotAcceptable
|
32
|
+
end
|
33
|
+
|
34
|
+
it "raises a ClientError exception when the status is 407" do
|
35
|
+
response = { method: :get, response_headers: { content_type: 'json' }, status: 407}
|
36
|
+
ex = DesignerNews::Response::Error.from_response(response)
|
37
|
+
expect(ex).to be_instance_of DesignerNews::Response::ClientError
|
38
|
+
end
|
39
|
+
|
40
|
+
it "raises a InternalServerError exception when the status is 500" do
|
41
|
+
response = { method: :get, response_headers: { content_type: 'json' }, status: 500}
|
42
|
+
ex = DesignerNews::Response::Error.from_response(response)
|
43
|
+
expect(ex).to be_instance_of DesignerNews::Response::InternalServerError
|
44
|
+
end
|
45
|
+
|
46
|
+
it "raises a NotImplemented exception when the status is 501" do
|
47
|
+
response = { method: :get, response_headers: { content_type: 'json' }, status: 501}
|
48
|
+
ex = DesignerNews::Response::Error.from_response(response)
|
49
|
+
expect(ex).to be_instance_of DesignerNews::Response::NotImplemented
|
50
|
+
end
|
51
|
+
|
52
|
+
it "raises a BadGateway exception when the status is 502" do
|
53
|
+
response = { method: :get, response_headers: { content_type: 'json' }, status: 502}
|
54
|
+
ex = DesignerNews::Response::Error.from_response(response)
|
55
|
+
expect(ex).to be_instance_of DesignerNews::Response::BadGateway
|
56
|
+
end
|
57
|
+
|
58
|
+
it "raises a ServiceUnavailable exception when the status is 503" do
|
59
|
+
response = { method: :get, response_headers: { content_type: 'json' }, status: 503}
|
60
|
+
ex = DesignerNews::Response::Error.from_response(response)
|
61
|
+
expect(ex).to be_instance_of DesignerNews::Response::ServiceUnavailable
|
62
|
+
end
|
63
|
+
|
64
|
+
it "raises a ServiceError exception when the status is 504" do
|
65
|
+
response = { method: :get, response_headers: { content_type: 'json' }, status: 504}
|
66
|
+
ex = DesignerNews::Response::Error.from_response(response)
|
67
|
+
expect(ex).to be_instance_of DesignerNews::Response::ServerError
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "The Designer News API client" do
|
4
|
+
|
5
|
+
context "Creating the client" do
|
6
|
+
it "instantiates a client with proper configuration" do
|
7
|
+
DesignerNews.reset!
|
8
|
+
expect(DesignerNews.client).to be_kind_of DesignerNews::Client
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rspec'
|
3
|
+
require 'json'
|
4
|
+
require 'designer_news'
|
5
|
+
require 'vcr'
|
6
|
+
require 'webmock/rspec'
|
7
|
+
|
8
|
+
VCR.configure do |c|
|
9
|
+
c.configure_rspec_metadata!
|
10
|
+
|
11
|
+
c.filter_sensitive_data("<DESIGNER_NEWS_LOGIN>") do
|
12
|
+
ENV['DESIGNER_NEWS_LOGIN']
|
13
|
+
end
|
14
|
+
|
15
|
+
c.filter_sensitive_data("<DESIGNER_NEWS_PASSWORD>") do
|
16
|
+
ENV['DESIGNER_NEWS_PASSWORD']
|
17
|
+
end
|
18
|
+
|
19
|
+
c.filter_sensitive_data("<<ACCESS_TOKEN>>") do
|
20
|
+
ENV['DESIGNER_NEWS_ACCESS_TOKEN']
|
21
|
+
end
|
22
|
+
|
23
|
+
c.filter_sensitive_data("<DESIGNER_NEWS_CLIENT_ID>") do
|
24
|
+
ENV['DESIGNER_NEWS_CLIENT_ID']
|
25
|
+
end
|
26
|
+
|
27
|
+
c.filter_sensitive_data("<DESIGNER_NEWS_CLIENT_SECRET>") do
|
28
|
+
ENV['DESIGNER_NEWS_CLIENT_SECRET']
|
29
|
+
end
|
30
|
+
|
31
|
+
c.default_cassette_options = {
|
32
|
+
:serialize_with => :json,
|
33
|
+
:preserve_exact_body_bytes => true,
|
34
|
+
:decode_compressed_response => true,
|
35
|
+
:match_requests_on => [:method,
|
36
|
+
VCR.request_matchers.uri_without_param(:md5, :key, :etag, :access_token, :remote_url)]
|
37
|
+
}
|
38
|
+
|
39
|
+
c.cassette_library_dir = 'spec/cassettes'
|
40
|
+
c.hook_into :webmock
|
41
|
+
end
|
42
|
+
|
43
|
+
RSpec.configure do |c|
|
44
|
+
c.treat_symbols_as_metadata_keys_with_true_values = true
|
45
|
+
c.around(:each, :vcr) do |example|
|
46
|
+
name = example.metadata[:full_description].gsub(/\//, "_").split(/\s+/, 2).join("/").gsub(/[^\w\/]+/, "_").downcase
|
47
|
+
VCR.use_cassette(name) { example.call }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def designer_news_url(url)
|
52
|
+
url =~ /^http/ ? url : "#{ENV['DESIGNER_NEWS_API_ENDPOINT']}/#{url}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def random_md5
|
56
|
+
identifier = ""
|
57
|
+
chars = "0123456789abcdef"
|
58
|
+
32.times { identifier << chars[rand(chars.size)] }
|
59
|
+
identifier
|
60
|
+
end
|
61
|
+
|
62
|
+
def fixture_path_for(filename)
|
63
|
+
File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', filename)
|
64
|
+
end
|
metadata
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dn
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kelly Sutton
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
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: hashie
|
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: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: multi_json
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: vcr
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: webmock
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.13'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.13'
|
125
|
+
description: The Designer News Ruby API client.
|
126
|
+
email:
|
127
|
+
- kelly@layervault.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- .gitignore
|
133
|
+
- Gemfile
|
134
|
+
- LICENSE.txt
|
135
|
+
- README.md
|
136
|
+
- Rakefile
|
137
|
+
- dn.gemspec
|
138
|
+
- lib/designer_news.rb
|
139
|
+
- lib/designer_news/client.rb
|
140
|
+
- lib/designer_news/client/users.rb
|
141
|
+
- lib/designer_news/default_options.rb
|
142
|
+
- lib/designer_news/middleware/raise_error.rb
|
143
|
+
- lib/designer_news/mixins/authentication.rb
|
144
|
+
- lib/designer_news/mixins/configurable.rb
|
145
|
+
- lib/designer_news/mixins/connection.rb
|
146
|
+
- lib/designer_news/model.rb
|
147
|
+
- lib/designer_news/response/error.rb
|
148
|
+
- lib/designer_news/user.rb
|
149
|
+
- lib/designer_news/version.rb
|
150
|
+
- spec/cassettes/Users/_me/returns_the_User_info_as_JSON.json
|
151
|
+
- spec/designer_news/client/users_spec.rb
|
152
|
+
- spec/designer_news/client_spec.rb
|
153
|
+
- spec/designer_news/error.rb
|
154
|
+
- spec/designer_news_spec.rb
|
155
|
+
- spec/spec.opts
|
156
|
+
- spec/spec_helper.rb
|
157
|
+
homepage: https://github.com/layervault/dn_ruby_client
|
158
|
+
licenses:
|
159
|
+
- MIT
|
160
|
+
metadata: {}
|
161
|
+
post_install_message:
|
162
|
+
rdoc_options: []
|
163
|
+
require_paths:
|
164
|
+
- lib
|
165
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - '>='
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - '>='
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
requirements: []
|
176
|
+
rubyforge_project:
|
177
|
+
rubygems_version: 2.1.9
|
178
|
+
signing_key:
|
179
|
+
specification_version: 4
|
180
|
+
summary: Provides Ruby native wrappers for the Designer News API.
|
181
|
+
test_files:
|
182
|
+
- spec/cassettes/Users/_me/returns_the_User_info_as_JSON.json
|
183
|
+
- spec/designer_news/client/users_spec.rb
|
184
|
+
- spec/designer_news/client_spec.rb
|
185
|
+
- spec/designer_news/error.rb
|
186
|
+
- spec/designer_news_spec.rb
|
187
|
+
- spec/spec.opts
|
188
|
+
- spec/spec_helper.rb
|