distant 0.1.0
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 +5 -0
- data/.rspec +2 -0
- data/Dockerfile +13 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +75 -0
- data/README.md +66 -0
- data/Rakefile +16 -0
- data/distant.gemspec +26 -0
- data/lib/distant/base.rb +112 -0
- data/lib/distant/config.rb +30 -0
- data/lib/distant/connection.rb +13 -0
- data/lib/distant.rb +22 -0
- data/spec/client/base_spec.rb +213 -0
- data/spec/client/config_spec.rb +61 -0
- data/spec/client/connection_spec.rb +18 -0
- data/spec/client/module_spec.rb +22 -0
- data/spec/spec_helper.rb +15 -0
- metadata +190 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 15b97b720febe3eda9c6c84b571b546f466f3aac
|
4
|
+
data.tar.gz: 6f64bff4470a4c4c21eeab79c12f8106a9b1407b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1e4847b3fd9615ed48963c20f1691c61c2bd5b603c8a9e173628705769a977d599d8e7865024d2ecfeee326627bb8afd94e09d9b8b8a44436e64da41bcd8d21c
|
7
|
+
data.tar.gz: 2ffabbfa4affb8d99054eaa0893a555ef80acbfa080c869ef5ce54884e251da3f8fd23fe76ee9d3c6a276bbb3942cd7114a70337a8a99553af7e919b29431727
|
data/.rspec
ADDED
data/Dockerfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
# BUILD: docker build --no-cache -t distant/base .
|
3
|
+
# RUN TESTS: docker run -t -v $(pwd):/opt/distant distant/base bundle exec rspec
|
4
|
+
|
5
|
+
FROM ruby:2.3.0-alpine
|
6
|
+
ADD ./ /opt/distant
|
7
|
+
WORKDIR /opt/distant
|
8
|
+
RUN set -x && \
|
9
|
+
adduser -D ci && \
|
10
|
+
chown -R ci:ci /opt/distant && \
|
11
|
+
apk add --update --upgrade git build-base
|
12
|
+
USER ci
|
13
|
+
RUN bundle install --verbose
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
distant (0.1.0)
|
5
|
+
activesupport
|
6
|
+
httparty
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activesupport (4.2.3)
|
12
|
+
i18n (~> 0.7)
|
13
|
+
json (~> 1.7, >= 1.7.7)
|
14
|
+
minitest (~> 5.1)
|
15
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
16
|
+
tzinfo (~> 1.1)
|
17
|
+
addressable (2.3.8)
|
18
|
+
byebug (5.0.0)
|
19
|
+
columnize (= 0.9.0)
|
20
|
+
columnize (0.9.0)
|
21
|
+
crack (0.4.2)
|
22
|
+
safe_yaml (~> 1.0.0)
|
23
|
+
diff-lcs (1.2.5)
|
24
|
+
docile (1.1.5)
|
25
|
+
httparty (0.13.7)
|
26
|
+
json (~> 1.8)
|
27
|
+
multi_xml (>= 0.5.2)
|
28
|
+
i18n (0.7.0)
|
29
|
+
json (1.8.3)
|
30
|
+
minitest (5.7.0)
|
31
|
+
multi_xml (0.5.5)
|
32
|
+
rake (10.4.2)
|
33
|
+
rspec (3.3.0)
|
34
|
+
rspec-core (~> 3.3.0)
|
35
|
+
rspec-expectations (~> 3.3.0)
|
36
|
+
rspec-mocks (~> 3.3.0)
|
37
|
+
rspec-core (3.3.2)
|
38
|
+
rspec-support (~> 3.3.0)
|
39
|
+
rspec-expectations (3.3.1)
|
40
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
41
|
+
rspec-support (~> 3.3.0)
|
42
|
+
rspec-mocks (3.3.2)
|
43
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
44
|
+
rspec-support (~> 3.3.0)
|
45
|
+
rspec-support (3.3.0)
|
46
|
+
safe_yaml (1.0.4)
|
47
|
+
shoulda-matchers (2.8.0)
|
48
|
+
activesupport (>= 3.0.0)
|
49
|
+
simplecov (0.10.0)
|
50
|
+
docile (~> 1.1.0)
|
51
|
+
json (~> 1.8)
|
52
|
+
simplecov-html (~> 0.10.0)
|
53
|
+
simplecov-html (0.10.0)
|
54
|
+
thread_safe (0.3.5)
|
55
|
+
tzinfo (1.2.2)
|
56
|
+
thread_safe (~> 0.1)
|
57
|
+
webmock (1.21.0)
|
58
|
+
addressable (>= 2.3.6)
|
59
|
+
crack (>= 0.3.2)
|
60
|
+
|
61
|
+
PLATFORMS
|
62
|
+
ruby
|
63
|
+
|
64
|
+
DEPENDENCIES
|
65
|
+
bundler (~> 1.7)
|
66
|
+
byebug
|
67
|
+
distant!
|
68
|
+
rake (~> 10.0)
|
69
|
+
rspec
|
70
|
+
shoulda-matchers
|
71
|
+
simplecov
|
72
|
+
webmock
|
73
|
+
|
74
|
+
BUNDLED WITH
|
75
|
+
1.11.2
|
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
# Distant Client
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
|
6
|
+
### In your Gemfile
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
gem 'distant'
|
10
|
+
```
|
11
|
+
|
12
|
+
### Usage
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
Distant.configure do |config|
|
16
|
+
config.base_uri = 'https://www.example.com/api/v0'
|
17
|
+
config.set_authentication_headers_with do |body|
|
18
|
+
{'X-Foo-Api-Key' => ENV['FOO_API_KEY']}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Organization < Distant::Base
|
23
|
+
get :all, '/organizations'
|
24
|
+
get :find, '/organizations/:id'
|
25
|
+
has_many :networks, '/organizations/:id/networks'
|
26
|
+
end
|
27
|
+
|
28
|
+
class Network < Distant::Base
|
29
|
+
belongs_to :organization
|
30
|
+
get :find, '/networks/:id'
|
31
|
+
has_many :clients, '/networks/:id/clients'
|
32
|
+
end
|
33
|
+
|
34
|
+
class Client < Distant::Base
|
35
|
+
belongs_to :network
|
36
|
+
get :find, '/clients/:id'
|
37
|
+
end
|
38
|
+
|
39
|
+
Organization.all.each do |org|
|
40
|
+
org.networks.each do |network|
|
41
|
+
network.clients.each do |client|
|
42
|
+
# DO THE THING
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
See [examples](examples) directory for examples of usage.
|
49
|
+
|
50
|
+
## RUNNING THE TESTS
|
51
|
+
|
52
|
+
### Within Docker
|
53
|
+
|
54
|
+
```bash
|
55
|
+
docker build --no-cache -t distant/base . && \
|
56
|
+
docker run -t -v $(pwd):/opt/distant distant/base bundle exec rspec
|
57
|
+
```
|
58
|
+
|
59
|
+
### On your host OS
|
60
|
+
|
61
|
+
NOTE: Requires Ruby 2.3.0+
|
62
|
+
|
63
|
+
```bash
|
64
|
+
bundle
|
65
|
+
bundle exec rspec
|
66
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
include Rake::DSL
|
2
|
+
|
3
|
+
task :environment do
|
4
|
+
require 'bundler/setup'
|
5
|
+
lib = File.expand_path('../lib', __FILE__)
|
6
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
7
|
+
require 'distant'
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'enter a REPL console within this project environment'
|
11
|
+
task :console => :environment do
|
12
|
+
require 'irb'
|
13
|
+
require 'irb/completion'
|
14
|
+
ARGV.clear
|
15
|
+
IRB.start
|
16
|
+
end
|
data/distant.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = 'distant'
|
3
|
+
spec.version = '0.1.0'
|
4
|
+
spec.authors = ['John Drago']
|
5
|
+
spec.email = 'jdrago.999@gmail.com'
|
6
|
+
spec.homepage = 'https://github.com/distant/distant'
|
7
|
+
spec.summary = 'Distant API Client'
|
8
|
+
spec.description = 'Distant API Client'
|
9
|
+
spec.required_rubygems_version = '>= 1.3.6'
|
10
|
+
|
11
|
+
spec.files = `git ls-files -z`.split("\x0")
|
12
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
13
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
14
|
+
spec.require_paths = ['lib']
|
15
|
+
|
16
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
17
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
18
|
+
spec.add_development_dependency 'rspec'
|
19
|
+
spec.add_development_dependency 'shoulda-matchers'
|
20
|
+
spec.add_development_dependency 'byebug'
|
21
|
+
spec.add_development_dependency 'simplecov'
|
22
|
+
spec.add_development_dependency 'webmock'
|
23
|
+
|
24
|
+
spec.add_dependency 'activesupport'
|
25
|
+
spec.add_dependency 'httparty'
|
26
|
+
end
|
data/lib/distant/base.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
|
2
|
+
module Distant
|
3
|
+
class ApiError < StandardError; end
|
4
|
+
class Base
|
5
|
+
|
6
|
+
def initialize(attrs={})
|
7
|
+
attrs.each do |key, val|
|
8
|
+
send "#{key}=", val
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.inherited(klass)
|
13
|
+
klass.extend(ClassMethods)
|
14
|
+
end
|
15
|
+
|
16
|
+
def connection
|
17
|
+
self.class.connection
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def connection
|
22
|
+
@@connection ||= Distant::Connection.new( Distant.config )
|
23
|
+
end
|
24
|
+
|
25
|
+
def marshal(data)
|
26
|
+
self.new(data)
|
27
|
+
end
|
28
|
+
|
29
|
+
def get(name, route)
|
30
|
+
define_singleton_method(name) do |*args|
|
31
|
+
path = path_closure_generator(route).call(*args)
|
32
|
+
headers = Distant.config.default_headers('')
|
33
|
+
.merge(Distant.config.auth_headers(''))
|
34
|
+
response_data = preprocess_response connection.get(path, headers)
|
35
|
+
if response_data.is_a? Array
|
36
|
+
response_data.map{ |item| marshal(item) }
|
37
|
+
else
|
38
|
+
marshal(response_data)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def has_many(plural, route)
|
44
|
+
define_method(plural) do
|
45
|
+
path = self.class.path_closure_generator(route).call(id: self.id)
|
46
|
+
headers = Distant.config.default_headers('')
|
47
|
+
.merge(Distant.config.auth_headers(''))
|
48
|
+
response_data = self.class.preprocess_response connection.get(path, headers)
|
49
|
+
class_ref = Kernel.const_get self.class.to_s.deconstantize + '::' + plural.to_s.singularize.classify
|
50
|
+
response_data.map{ |item| class_ref.marshal(item) }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def belongs_to(singular, route)
|
55
|
+
define_method(singular) do
|
56
|
+
foreign_key_attr = singular.to_s + '_id'
|
57
|
+
foreign_key_value = self.send(foreign_key_attr)
|
58
|
+
path = self.class.path_closure_generator(route).call(id: foreign_key_value)
|
59
|
+
headers = Distant.config.default_headers('')
|
60
|
+
.merge(Distant.config.auth_headers(''))
|
61
|
+
response_data = self.class.preprocess_response connection.get(path, headers)
|
62
|
+
class_ref = Kernel.const_get self.class.to_s.deconstantize + '::' + singular.to_s.classify
|
63
|
+
class_ref.marshal(response_data)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def path_closure_generator(route)
|
68
|
+
# Look for /foo/:bar/:bux and return ['bar', 'bux']
|
69
|
+
captures = route.scan(%r{:([^/\{\}\:\-]+)}).flatten
|
70
|
+
|
71
|
+
# Look for /foo/:bar/:bux and return '/foo/%{bar}/%{bux}'
|
72
|
+
template = route.gsub(%r{:([^/\{\}\:\-]+)}, "%{#{$1}}")
|
73
|
+
|
74
|
+
# Convert '/foo/%{bar}/%{bux}' to /foo/123/456 with {bar: 123, bux: 456}
|
75
|
+
class_eval <<-EOF
|
76
|
+
Proc.new do |#{captures.map{|cap| "#{cap}:"}.join(', ')}|
|
77
|
+
template % {
|
78
|
+
#{captures.map{ |cap| cap + ": " + cap }.join(', ')}
|
79
|
+
}
|
80
|
+
end
|
81
|
+
EOF
|
82
|
+
end
|
83
|
+
|
84
|
+
def preprocess_response(response)
|
85
|
+
case response.code
|
86
|
+
when 200..299
|
87
|
+
parsed = JSON.parse(response.body, symbolize_names: true)
|
88
|
+
recursive_underscore(parsed)
|
89
|
+
else
|
90
|
+
raise Distant::ApiError.new response
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def recursive_underscore(thing)
|
97
|
+
if thing.is_a? Hash
|
98
|
+
out = {}
|
99
|
+
thing.each do |key, val|
|
100
|
+
out[key.to_s.underscore.to_sym] = recursive_underscore(val)
|
101
|
+
end
|
102
|
+
out
|
103
|
+
elsif thing.is_a? Array
|
104
|
+
thing.map{ |item| recursive_underscore(item) }
|
105
|
+
else
|
106
|
+
thing
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
module Distant
|
3
|
+
class Config
|
4
|
+
attr_accessor :base_uri, :auth_header_generator, :default_header_generator
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
# Default just returns an empty hash:
|
8
|
+
self.auth_header_generator = Proc.new{ {} }
|
9
|
+
self.default_header_generator = Proc.new{ {} }
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_authentication_headers_with(&block)
|
13
|
+
raise ArgumentError.new 'block required' unless block_given?
|
14
|
+
self.auth_header_generator = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_default_headers_with(&block)
|
18
|
+
raise ArgumentError.new 'block required' unless block_given?
|
19
|
+
self.default_header_generator = block
|
20
|
+
end
|
21
|
+
|
22
|
+
def default_headers(body=nil)
|
23
|
+
self.default_header_generator.call(body)
|
24
|
+
end
|
25
|
+
|
26
|
+
def auth_headers(body=nil)
|
27
|
+
auth_header_generator.call(body)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
module Distant
|
3
|
+
class Connection
|
4
|
+
include HTTParty
|
5
|
+
|
6
|
+
attr_accessor :config
|
7
|
+
def initialize(config)
|
8
|
+
raise ArgumentError.new 'invalid config' unless config.is_a? Distant::Config
|
9
|
+
self.config = config
|
10
|
+
self.class.base_uri config.base_uri
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/distant.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
require 'active_support/core_ext/object'
|
3
|
+
require 'active_support/json'
|
4
|
+
require 'httparty'
|
5
|
+
require 'logger'
|
6
|
+
require 'ostruct'
|
7
|
+
require 'distant/config'
|
8
|
+
require 'distant/base'
|
9
|
+
require 'distant/connection'
|
10
|
+
|
11
|
+
module Distant
|
12
|
+
|
13
|
+
def self.configure(&block)
|
14
|
+
@@config = Distant::Config.new
|
15
|
+
block.call(@@config)
|
16
|
+
config
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.config
|
20
|
+
@@config
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Distant::Base do
|
4
|
+
before :all do
|
5
|
+
Distant.configure do |config|
|
6
|
+
config.base_uri = 'https://www.example.com/api/v0'
|
7
|
+
end
|
8
|
+
class Distant::BaseTest < Distant::Base
|
9
|
+
attr_accessor :id, :name
|
10
|
+
|
11
|
+
has_many :sub_tests, '/base/tests/:id/sub_tests'
|
12
|
+
end
|
13
|
+
class Distant::SubTest < Distant::Base
|
14
|
+
attr_accessor :id, :base_test_id
|
15
|
+
belongs_to :base_test, ''
|
16
|
+
end
|
17
|
+
end
|
18
|
+
describe '.path_closure_generator(route)' do
|
19
|
+
context 'when the route has' do
|
20
|
+
context 'no variables' do
|
21
|
+
before do
|
22
|
+
@route = '/foo/bar'
|
23
|
+
end
|
24
|
+
context 'the closure returned' do
|
25
|
+
before do
|
26
|
+
@closure = Distant::BaseTest.path_closure_generator(@route)
|
27
|
+
end
|
28
|
+
it 'accepts no arguments' do
|
29
|
+
expect{@closure.call()}.not_to raise_error
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
context 'one variable' do
|
34
|
+
before do
|
35
|
+
@route = '/foo/:id'
|
36
|
+
end
|
37
|
+
context 'the closure returned' do
|
38
|
+
before do
|
39
|
+
@closure = Distant::BaseTest.path_closure_generator(@route)
|
40
|
+
end
|
41
|
+
it 'accepts one corresponding argument' do
|
42
|
+
expect{@closure.call(id: 123)}.not_to raise_error
|
43
|
+
expect{@closure.call(blah: 123)}.to raise_error ArgumentError
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
context 'multiple variables' do
|
48
|
+
before do
|
49
|
+
@route = '/foo/:id/:bar_id'
|
50
|
+
end
|
51
|
+
context 'the closure returned' do
|
52
|
+
before do
|
53
|
+
@closure = Distant::BaseTest.path_closure_generator(@route)
|
54
|
+
end
|
55
|
+
it 'accepts all corresponding arguments' do
|
56
|
+
expect{@closure.call(id: 123, bar_id: 456)}.not_to raise_error
|
57
|
+
expect{@closure.call(id: 123)}.to raise_error ArgumentError
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#connection' do
|
65
|
+
before do
|
66
|
+
@obj = Distant::BaseTest.new
|
67
|
+
end
|
68
|
+
it 'calls the eponymous static method' do
|
69
|
+
expect(@obj.class).to receive(:connection)
|
70
|
+
@obj.connection
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '.connection' do
|
75
|
+
context 'when called' do
|
76
|
+
context 'the first time' do
|
77
|
+
before do
|
78
|
+
expect(Distant::Connection).to receive(:new).exactly(1).times
|
79
|
+
end
|
80
|
+
it 'returns a new Distant::Connection' do
|
81
|
+
Distant::BaseTest.connection
|
82
|
+
end
|
83
|
+
end
|
84
|
+
context 'subsequent times' do
|
85
|
+
before do
|
86
|
+
expect(Distant::Connection).to receive(:new).exactly(1).times.and_call_original
|
87
|
+
end
|
88
|
+
it 'returns the same Distant::Connection' do
|
89
|
+
first_connection = Distant::BaseTest.connection
|
90
|
+
expect(first_connection).to eq Distant::BaseTest.connection
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '.get(name, route)' do
|
97
|
+
before do
|
98
|
+
@route = '/base/tests'
|
99
|
+
@single_route = '/base/tests/:id'
|
100
|
+
Distant::BaseTest.get :all, @route
|
101
|
+
Distant::BaseTest.get :find, @single_route
|
102
|
+
end
|
103
|
+
it 'creates a static method named correctly' do
|
104
|
+
expect(Distant::BaseTest).to respond_to :all
|
105
|
+
end
|
106
|
+
context 'when the method created for a collection is called' do
|
107
|
+
before do
|
108
|
+
expect(Distant::BaseTest).to receive(:preprocess_response){ [{id: 123}] }
|
109
|
+
end
|
110
|
+
it 'makes a GET request with the correct route' do
|
111
|
+
expect_any_instance_of(Distant::Connection).to receive(:get).with(@route, {})
|
112
|
+
# Finally:
|
113
|
+
Distant::BaseTest.all
|
114
|
+
end
|
115
|
+
end
|
116
|
+
context 'when the method created for a single object is called' do
|
117
|
+
before do
|
118
|
+
expect(Distant::BaseTest).to receive(:preprocess_response){ {id: 123} }
|
119
|
+
end
|
120
|
+
it 'makes a GET request with the correct route' do
|
121
|
+
expect_any_instance_of(Distant::Connection).to receive(:get).with(@single_route.gsub(':id', '123'), {})
|
122
|
+
# Finally:
|
123
|
+
Distant::BaseTest.find(id: 123)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe '.has_many(plural, route)' do
|
129
|
+
before do
|
130
|
+
@route = '/base/:id/tests'
|
131
|
+
Distant::BaseTest.has_many :sub_tests, @route
|
132
|
+
end
|
133
|
+
it 'creates an instance method named after the plural collection' do
|
134
|
+
expect(Distant::BaseTest.new).to respond_to :sub_tests
|
135
|
+
end
|
136
|
+
context 'when the plural instance method is called' do
|
137
|
+
before do
|
138
|
+
expect(Distant::BaseTest).to receive(:preprocess_response){ [{base_test_id: 123, id: 456}]}
|
139
|
+
end
|
140
|
+
it 'makes a GET request with the correct route' do
|
141
|
+
expect_any_instance_of(Distant::Connection).to receive(:get).with('/base/123/tests', {})
|
142
|
+
result = Distant::BaseTest.new(id: 123).sub_tests
|
143
|
+
expect(result.first).to be_a Distant::SubTest
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe '.belongs_to(singular, route)' do
|
149
|
+
before do
|
150
|
+
@route = '/base/:id'
|
151
|
+
Distant::SubTest.belongs_to :base_test, @route
|
152
|
+
end
|
153
|
+
it 'creates an instance method named after the plural collection' do
|
154
|
+
expect(Distant::SubTest.new).to respond_to :base_test
|
155
|
+
end
|
156
|
+
context 'when the instance method is called' do
|
157
|
+
before do
|
158
|
+
expect(Distant::SubTest).to receive(:preprocess_response){ {id: 123, name: 'foo'}}
|
159
|
+
end
|
160
|
+
it 'makes a GET request with the correct route' do
|
161
|
+
expect_any_instance_of(Distant::Connection).to receive(:get).with('/base/123', {})
|
162
|
+
result = Distant::SubTest.new(id: 456, base_test_id: 123).base_test
|
163
|
+
expect(result).to be_a Distant::BaseTest
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '.preprocess_response(response)' do
|
169
|
+
context 'when the response' do
|
170
|
+
context 'fails' do
|
171
|
+
before do
|
172
|
+
@response = double('response')
|
173
|
+
expect(@response).to receive(:code){ 400 }
|
174
|
+
end
|
175
|
+
it 'raises an exception' do
|
176
|
+
expect{Distant::BaseTest.preprocess_response(@response)}.to raise_error Distant::ApiError
|
177
|
+
end
|
178
|
+
end
|
179
|
+
context 'succeeds' do
|
180
|
+
before do
|
181
|
+
@response = double('response')
|
182
|
+
expect(@response).to receive(:code){ 200 }
|
183
|
+
end
|
184
|
+
context 'and the response' do
|
185
|
+
context 'is valid JSON' do
|
186
|
+
before do
|
187
|
+
@response_data = [
|
188
|
+
{id: 123, name: 'Test'},
|
189
|
+
{fooId: 456, nickName: 'Testy McTesterson'}
|
190
|
+
]
|
191
|
+
@expected_data = [
|
192
|
+
{id: 123, name: 'Test'},
|
193
|
+
{foo_id: 456, nick_name: 'Testy McTesterson'}
|
194
|
+
]
|
195
|
+
expect(@response).to receive(:body){ @response_data.to_json }
|
196
|
+
end
|
197
|
+
it 'returns the parsed JSON response' do
|
198
|
+
expect(Distant::BaseTest.preprocess_response(@response)).to eq @expected_data
|
199
|
+
end
|
200
|
+
end
|
201
|
+
context 'is not valid JSON' do
|
202
|
+
before do
|
203
|
+
expect(@response).to receive(:body){ '<html>wat?</html>' }
|
204
|
+
end
|
205
|
+
it 'raises an exception' do
|
206
|
+
expect{Distant::BaseTest.preprocess_response(@response)}.to raise_error JSON::ParserError
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Distant::Config do
|
4
|
+
context 'accessors' do
|
5
|
+
let(:subject){ Distant::Config.new }
|
6
|
+
it { should respond_to :base_uri }
|
7
|
+
it { should respond_to :base_uri= }
|
8
|
+
it { should respond_to :set_authentication_headers_with }
|
9
|
+
it { should respond_to :set_default_headers_with }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#set_authentication_headers_with(&block)' do
|
13
|
+
before do
|
14
|
+
@config = described_class.new
|
15
|
+
end
|
16
|
+
context 'when called' do
|
17
|
+
context 'with a block' do
|
18
|
+
before do
|
19
|
+
@secret = SecureRandom.uuid
|
20
|
+
@config.set_authentication_headers_with do |body|
|
21
|
+
{secret: @secret, body: body}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
it 'will execute the block when #auth_headers(body) is called' do
|
25
|
+
body = SecureRandom.uuid
|
26
|
+
expect(@config.auth_headers(body)).to eq(secret: @secret, body: body)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
context 'without a block' do
|
30
|
+
it 'raises an exception' do
|
31
|
+
expect{@config.set_authentication_headers_with()}.to raise_error ArgumentError
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#set_default_headers_with(&block)' do
|
38
|
+
before do
|
39
|
+
@config = described_class.new
|
40
|
+
end
|
41
|
+
context 'when called' do
|
42
|
+
context 'with a block' do
|
43
|
+
before do
|
44
|
+
@secret = SecureRandom.uuid
|
45
|
+
@config.set_default_headers_with do |body|
|
46
|
+
{secret: @secret, body: body}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
it 'will execute the block when #auth_headers(body) is called' do
|
50
|
+
body = SecureRandom.uuid
|
51
|
+
expect(@config.default_headers(body)).to eq(secret: @secret, body: body)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
context 'without a block' do
|
55
|
+
it 'raises an exception' do
|
56
|
+
expect{@config.set_default_headers_with()}.to raise_error ArgumentError
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Distant::Connection do
|
4
|
+
describe 'initialization' do
|
5
|
+
context 'when a Distant::Config object' do
|
6
|
+
context 'is provided' do
|
7
|
+
it 'returns a new Distant::Connection object' do
|
8
|
+
expect(described_class.new(Distant::Config.new)).to be_a described_class
|
9
|
+
end
|
10
|
+
end
|
11
|
+
context 'is not provided' do
|
12
|
+
it 'raises an exception' do
|
13
|
+
expect{described_class.new('a string')}.to raise_error ArgumentError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Distant do
|
4
|
+
describe '.configure{|config| ... }' do
|
5
|
+
context 'the provided block' do
|
6
|
+
it 'is executed' do
|
7
|
+
@was_executed = false
|
8
|
+
Distant.configure do |config|
|
9
|
+
@was_executed = true
|
10
|
+
end
|
11
|
+
expect(@was_executed).to be_truthy
|
12
|
+
end
|
13
|
+
it 'is provided a config object as an argument' do
|
14
|
+
@config_obj = nil
|
15
|
+
Distant.configure do |config|
|
16
|
+
@config_obj = config
|
17
|
+
end
|
18
|
+
expect(@config_obj).to be_a Distant::Config
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start do
|
4
|
+
add_filter 'spec'
|
5
|
+
end
|
6
|
+
SimpleCov.minimum_coverage 100
|
7
|
+
|
8
|
+
require 'rspec'
|
9
|
+
require 'byebug'
|
10
|
+
require 'bundler/setup'
|
11
|
+
require 'webmock/rspec'
|
12
|
+
lib = File.expand_path('../../lib', __FILE__)
|
13
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
14
|
+
|
15
|
+
require 'distant'
|
metadata
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: distant
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Drago
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-04-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.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
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: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: shoulda-matchers
|
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: byebug
|
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: simplecov
|
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: webmock
|
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: activesupport
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: httparty
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: Distant API Client
|
140
|
+
email: jdrago.999@gmail.com
|
141
|
+
executables: []
|
142
|
+
extensions: []
|
143
|
+
extra_rdoc_files: []
|
144
|
+
files:
|
145
|
+
- ".gitignore"
|
146
|
+
- ".rspec"
|
147
|
+
- Dockerfile
|
148
|
+
- Gemfile
|
149
|
+
- Gemfile.lock
|
150
|
+
- README.md
|
151
|
+
- Rakefile
|
152
|
+
- distant.gemspec
|
153
|
+
- lib/distant.rb
|
154
|
+
- lib/distant/base.rb
|
155
|
+
- lib/distant/config.rb
|
156
|
+
- lib/distant/connection.rb
|
157
|
+
- spec/client/base_spec.rb
|
158
|
+
- spec/client/config_spec.rb
|
159
|
+
- spec/client/connection_spec.rb
|
160
|
+
- spec/client/module_spec.rb
|
161
|
+
- spec/spec_helper.rb
|
162
|
+
homepage: https://github.com/distant/distant
|
163
|
+
licenses: []
|
164
|
+
metadata: {}
|
165
|
+
post_install_message:
|
166
|
+
rdoc_options: []
|
167
|
+
require_paths:
|
168
|
+
- lib
|
169
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
175
|
+
requirements:
|
176
|
+
- - ">="
|
177
|
+
- !ruby/object:Gem::Version
|
178
|
+
version: 1.3.6
|
179
|
+
requirements: []
|
180
|
+
rubyforge_project:
|
181
|
+
rubygems_version: 2.5.1
|
182
|
+
signing_key:
|
183
|
+
specification_version: 4
|
184
|
+
summary: Distant API Client
|
185
|
+
test_files:
|
186
|
+
- spec/client/base_spec.rb
|
187
|
+
- spec/client/config_spec.rb
|
188
|
+
- spec/client/connection_spec.rb
|
189
|
+
- spec/client/module_spec.rb
|
190
|
+
- spec/spec_helper.rb
|