apis 0.4.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.
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .yardoc
6
+ doc
7
+ .DS_Store
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ -m markdown
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in apis.gemspec
4
+ gemspec
5
+
6
+ gem 'rack-test', :require => 'rack/test'
7
+ gem 'yajl-ruby', :require => 'yajl'
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ ## Usage
2
+
3
+ connection = Apis::Connecton.new(:url => 'http://api.example.com') do
4
+ request do
5
+ use Apis::Request::JSON # |
6
+ use Apis::Request::OAuth2 # |
7
+ use Apis::Request::Logger # \/
8
+ end
9
+ adapter :net_http # -->
10
+ response do
11
+ use Apis::Response::JSON # |
12
+ use Apis::Response::Logger # \/
13
+ end
14
+ end
15
+
16
+ connection.get('/hello')
17
+
18
+ connection.request.replace Apis::Request::OAuth2, Apis::Request::OAuth
19
+
20
+ connection.post('/post_with_oauth_10') do |request|
21
+ request.params = {:q => 'm'}
22
+ end
23
+
24
+ ## Request Middleware
25
+
26
+ ### Example
27
+
28
+ class Request::Middleware
29
+ def call(env)
30
+ env[:params][:token] = 'abcdef'
31
+ end
32
+ end
33
+
34
+ ### Description
35
+
36
+ Basically, request middleware is an object that responds to `#call`.
37
+ It will be initialized with args passed if any.
38
+
39
+ ### Environment
40
+
41
+ * `:method` - HTTP method name
42
+ * `:body` – body of request sent to server
43
+ * `:headers` – headers hash
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+ Bundler::GemHelper.install_tasks
4
+ RSpec::Core::RakeTask.new
data/apis.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "apis/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "apis"
7
+ s.version = Apis::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Marjan Krekoten' (Мар'ян Крекотень)"]
10
+ s.email = ["m@hmarynka.com"]
11
+ s.homepage = "http://hmarynka.com/labs/apis"
12
+ s.summary = %q{Working bee of API wrapper}
13
+ s.description = %q{Rack-like HTTP client library inspired by Faraday done my way}
14
+
15
+ s.rubyforge_project = "apis"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_development_dependency 'rspec', '>= 2.5'
23
+ s.add_development_dependency 'sinatra'
24
+ s.add_development_dependency 'rack-test'
25
+ s.add_development_dependency 'unicorn'
26
+
27
+ # Middlewares
28
+ s.add_development_dependency 'multi_json'
29
+ s.add_development_dependency 'yajl-ruby'
30
+
31
+ s.add_dependency 'addressable'
32
+ end
data/lib/apis.rb ADDED
@@ -0,0 +1,55 @@
1
+ $: << lib_dir unless $:.include?(lib_dir = File.expand_path('..', __FILE__))
2
+
3
+ module Apis
4
+ class DuplicateMiddleware < StandardError; end
5
+ autoload :Connection, 'apis/connection'
6
+ autoload :ConnectionScope, 'apis/connection_scope'
7
+ autoload :Builder, 'apis/builder'
8
+ autoload :Response, 'apis/response'
9
+
10
+ module Registerable
11
+ def register(symbol, klass)
12
+ @lookup_table ||= {}
13
+ @lookup_table[symbol] = klass
14
+ end
15
+
16
+ def lookup(symbol)
17
+ @lookup_table ||= {}
18
+ self.const_get(@lookup_table[symbol])
19
+ end
20
+ end
21
+
22
+ module Adapter
23
+ autoload :Abstract, 'apis/adapter/abstract'
24
+ autoload :NetHTTP, 'apis/adapter/net_http'
25
+ autoload :RackTest, 'apis/adapter/rack_test'
26
+
27
+ extend Registerable
28
+
29
+ class << self
30
+ # Default connection adapter
31
+ # You can change it by assignin new value
32
+ def default
33
+ @default ||= :net_http
34
+ end
35
+ attr_writer :default
36
+ end
37
+
38
+ register :net_http, :NetHTTP
39
+ register :rack_test, :RackTest
40
+ end
41
+
42
+ module Middleware
43
+ module Request
44
+ extend Registerable
45
+ end
46
+
47
+ module Response
48
+ autoload :Json, 'apis/middleware/response/json'
49
+
50
+ extend Registerable
51
+
52
+ register :json, :Json
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,27 @@
1
+ module Apis
2
+ module Adapter
3
+ class Abstract
4
+ class NotImplemented < StandardError; end
5
+
6
+ attr_accessor :uri
7
+
8
+ def initialize(options = {})
9
+ options.each do |key, value|
10
+ send("#{key}=", value) if respond_to?("#{key}=")
11
+ end
12
+ end
13
+
14
+ # Performs request to resource
15
+ #
16
+ # @param [Symbol, String] method HTTP method to perform
17
+ # @param [String] path Relative path to resource host
18
+ # @param [Hash] params Params to be sent
19
+ # @param [Hash] headers Headers to be sent
20
+ #
21
+ # @return [Array] headers, body
22
+ def run(method, path, params = {}, headers = {})
23
+ raise Apis::Adapter::Abstract::NotImplemented
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ require 'net/http'
2
+
3
+ module Apis
4
+ module Adapter
5
+ class NetHTTP < Abstract
6
+ def connection
7
+ Net::HTTP.start(uri.host, uri.port)
8
+ end
9
+
10
+ def run(method, path, params = {}, headers = {})
11
+ _module = Net::HTTP.const_get(method.to_s.capitalize)
12
+ request = _module.new(path)
13
+ response = connection.request(
14
+ request,
15
+ params.empty? ? nil : Addressable::URI.new.tap { |uri| uri.query_values = params }.query
16
+ )
17
+ [response.code.to_i] + response
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ require 'rack/test'
2
+
3
+ module Apis
4
+ module Adapter
5
+ class RackTest < Abstract
6
+ include Rack::Test::Methods
7
+
8
+ attr_accessor :app
9
+
10
+ attr_reader :last_path, :last_params, :last_headers
11
+
12
+ def run(method, path, params = {}, headers = {})
13
+ @last_path, @last_params, @last_headers = path, params, headers
14
+ send(method, path, params, headers)
15
+ [last_response.status, last_response.headers, last_response.body]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,76 @@
1
+ module Apis
2
+ class Builder
3
+ attr_accessor :lookup_context
4
+ def initialize(options = {}, &block)
5
+ options.each do |key, value|
6
+ send("#{key}=", value) if respond_to?("#{key}=")
7
+ end
8
+ @stack = []
9
+ @mapping = {}
10
+ block_eval(&block) if block
11
+ end
12
+
13
+ def use(middleware)
14
+ insert(middleware)
15
+ end
16
+
17
+ def replace(old, middleware)
18
+ if index = index(old)
19
+ insert(middleware, index)
20
+ remove(old)
21
+ end
22
+ end
23
+
24
+ def insert(middleware, index = nil)
25
+ middleware = lookup_middleware(middleware)
26
+ raise Apis::DuplicateMiddleware, "#{middleware} already in stack" if include?(middleware)
27
+ index ||= @stack.length
28
+ @stack[index] = lambda do |parent|
29
+ middleware.new(parent)
30
+ end
31
+ @mapping[middleware] = index
32
+ end
33
+
34
+ def remove(middleware)
35
+ middleware = lookup_middleware(middleware)
36
+ @stack.delete_at(@mapping.delete(middleware))
37
+ end
38
+
39
+ def index(middleware)
40
+ middleware = lookup_middleware(middleware)
41
+ @mapping[middleware]
42
+ end
43
+
44
+ def length
45
+ @stack.length
46
+ end
47
+
48
+ def include?(middleware)
49
+ !!index(middleware)
50
+ end
51
+
52
+ def to_a
53
+ @mapping.to_a.sort { |a, b| a.last <=> b.last}.map { |e| e.first }
54
+ end
55
+ alias to_ary to_s
56
+
57
+ def to_app
58
+ unless @stack.empty?
59
+ inner_app = @stack.last.call(nil)
60
+ @stack.reverse[1..-1].inject(inner_app) { |parent, lazy| lazy.call(parent) }
61
+ else
62
+ []
63
+ end
64
+ end
65
+
66
+ def block_eval(&block)
67
+ instance_eval(&block)
68
+ end
69
+
70
+ def lookup_middleware(middleware)
71
+ @lookup_context && !(Class === middleware) ?
72
+ @lookup_context.lookup(middleware) :
73
+ middleware
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,96 @@
1
+ require 'addressable/uri'
2
+
3
+ module Apis
4
+ class Connection
5
+ def initialize(options = {})
6
+ @scope = Apis::ConnectionScope.new
7
+ @scope.headers, @scope.params = {}, {}
8
+
9
+ if String === options
10
+ self.uri = options
11
+ else
12
+ options.each do |key, value|
13
+ send("#{key}=", value) if respond_to?("#{key}=")
14
+ end
15
+ end
16
+
17
+ if block_given?
18
+ block = Proc.new
19
+ instance_eval(&block)
20
+ end
21
+ adapter(Apis::Adapter.default) unless @adapter
22
+ end
23
+
24
+ def uri
25
+ @scope.uri
26
+ end
27
+
28
+ def uri=(value)
29
+ @scope.uri = Addressable::URI.parse(value)
30
+ end
31
+
32
+ def headers
33
+ @scope.headers
34
+ end
35
+
36
+ def headers=(value)
37
+ @scope.headers.merge!(value)
38
+ end
39
+
40
+ def params
41
+ @scope.params
42
+ end
43
+
44
+ def params=(value)
45
+ @scope.params.merge!(value)
46
+ end
47
+
48
+ def request
49
+ block = block_given? ? Proc.new : nil
50
+ @request ||= Apis::Builder.new(:lookup_context => Apis::Middleware::Request, &block)
51
+ @request
52
+ end
53
+
54
+ def response
55
+ block = block_given? ? Proc.new : nil
56
+ @response ||= Apis::Builder.new(:lookup_context => Apis::Middleware::Response, &block)
57
+ @response
58
+ end
59
+
60
+ def adapter(value = nil)
61
+ if Symbol === value
62
+ value = Apis::Adapter.lookup(value)
63
+ end
64
+
65
+ @adapter = value.new(:uri => uri) if value
66
+ @adapter
67
+ end
68
+
69
+ [:get, :head, :post, :put, :delete].each do |method|
70
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
71
+ def #{method}(path = nil, params = {}, headers = {})
72
+ run_request(#{method.inspect}, path, params, headers, &(block_given? ? Proc.new : nil))
73
+ end
74
+ RUBY
75
+ end
76
+
77
+ def run_request(method, path = nil, params = {}, headers = {}, &block)
78
+ #block = block_given? ? Proc.new : nil
79
+ # TODO: refactor this, it's ugly
80
+ @scope.scoped do
81
+ self.params = params if params
82
+ self.headers = headers if headers
83
+ block.call(self) if block
84
+ path ||= uri.path.empty? ? '/' : uri.path
85
+ self.request.to_app.call(
86
+ :method => method,
87
+ :params => self.params,
88
+ :headers => self.headers
89
+ ) unless self.request.to_a.empty?
90
+ res = Apis::Response.new(*adapter.run(method, path, self.params, self.headers))
91
+ self.response.to_app.call(res) unless self.response.to_a.empty?
92
+ res
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,27 @@
1
+ module Apis
2
+ class ConnectionScope
3
+ def initialize
4
+ @data = {}
5
+ end
6
+
7
+ def method_missing(method, value = nil)
8
+ if method =~ /\=$/ && value
9
+ @data[method.to_s.gsub(/\=/, '').to_sym] = value
10
+ else
11
+ @data[method]
12
+ end
13
+ end
14
+
15
+ def scoped
16
+ backup = {}
17
+ @data.each do |key, value|
18
+ backup[key] = value.dup
19
+ end
20
+ yield
21
+ ensure
22
+ backup.each do |key, value|
23
+ @data[key] = value
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ require 'multi_json'
2
+
3
+ module Apis
4
+ module Middleware
5
+ module Response
6
+ class Json
7
+ attr_accessor :app
8
+ def initialize(app = nil)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ env.body = case env.body
14
+ when ''
15
+ nil
16
+ when 'true'
17
+ true
18
+ when 'false'
19
+ false
20
+ else
21
+ ::MultiJson.decode(env.body)
22
+ end
23
+ @app.call(env) if @app
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ module Apis
2
+ class Response
3
+ attr_accessor :status, :headers, :body
4
+ def initialize(status, headers, body)
5
+ @status, @headers, @body = status, headers, body
6
+ end
7
+
8
+ def to_env
9
+ {:status => status, :headers => headers, :body => body}
10
+ end
11
+
12
+ def to_a
13
+ [status, headers, body]
14
+ end
15
+ alias to_ary to_a
16
+
17
+ def [](key)
18
+ respond_to?(key) ? send(key) : nil
19
+ end
20
+
21
+ def []=(key, value)
22
+ send("#{key}=", value) if respond_to?("#{key}=")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module Apis
2
+ VERSION = "0.4.1"
3
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+ require 'addressable/uri'
3
+
4
+ describe Apis::Adapter::NetHTTP do
5
+ before(:all) do
6
+ start_server
7
+ end
8
+ after(:all) do
9
+ stop_server
10
+ end
11
+
12
+ [:get, :post, :put, :delete, :head].each do |method|
13
+ context method.to_s.upcase do
14
+ it "returns body" do
15
+ adapter = Apis::Adapter::NetHTTP.new(:uri => Addressable::URI.parse(server_host))
16
+ status, headers, body = adapter.run(method, '/')
17
+ body.should == method.to_s.upcase
18
+ end unless method == :head
19
+
20
+ it "returns headers" do
21
+ adapter = Apis::Adapter::NetHTTP.new(:uri => Addressable::URI.parse(server_host))
22
+ status, headers, body = adapter.run(method, '/')
23
+ headers['X-Requested-With-Method'].should == method.to_s.upcase
24
+ end
25
+
26
+ it "returns status" do
27
+ adapter = Apis::Adapter::NetHTTP.new(:uri => Addressable::URI.parse(server_host))
28
+ status, headers, body = adapter.run(method, '/')
29
+ status.should == 200
30
+ end
31
+
32
+ it "sends params" do
33
+ adapter = Apis::Adapter::NetHTTP.new(:uri => Addressable::URI.parse(server_host))
34
+ status, headers, body = adapter.run(method, "/#{method}", {:param => 'value'})
35
+ headers['X-Sent-Params'].should == '{"param"=>"value"}'
36
+ end
37
+ end
38
+ end
39
+
40
+ it 'registered under :net_http' do
41
+ Apis::Connection.new do
42
+ adapter :net_http
43
+ end.adapter.should be_instance_of(Apis::Adapter::NetHTTP)
44
+ end
45
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ class RackApp
4
+ def call(env)
5
+ [
6
+ 200,
7
+ {
8
+ 'X-Request-Method' => env['REQUEST_METHOD'],
9
+ 'X-Request-At' => env['PATH_INFO']
10
+ },
11
+ env['REQUEST_METHOD'] == 'HEAD' ? [] : [env['REQUEST_METHOD']]
12
+ ]
13
+ end
14
+ end
15
+
16
+ describe Apis::Adapter::RackTest do
17
+ [:get, :post, :put, :delete, :head].each do |method|
18
+ context method.to_s.upcase do
19
+ let(:adapter) { Apis::Adapter::RackTest.new(:app => RackApp.new) }
20
+ it "returns body" do
21
+ status, headers, body = adapter.run(method, '/')
22
+ body.should == method.to_s.upcase
23
+ end unless method == :head
24
+
25
+ it "returns headers" do
26
+ status, headers, body = adapter.run(method, '/')
27
+ headers['X-Request-Method'].should == method.to_s.upcase
28
+ end
29
+
30
+ it "returns status" do
31
+ status, headers, body = adapter.run(method, '/')
32
+ status.should == 200
33
+ end
34
+
35
+ it 'saves passed path' do
36
+ adapter.run(method, "/#{method}")
37
+ adapter.last_path.should == "/#{method}"
38
+ end
39
+
40
+ it 'saves passed params' do
41
+ adapter.run(method, '/', {:test => 'param'})
42
+ adapter.last_params.should == {:test => 'param'}
43
+ end
44
+
45
+ it 'saves passed headers' do
46
+ adapter.run(method, '/', {}, {'Content-Type' => 'text'})
47
+ adapter.last_headers.should == {'Content-Type' => 'text'}
48
+ end
49
+ end
50
+ end
51
+
52
+ it 'registered under :rack_test' do
53
+ Apis::Connection.new do
54
+ adapter :rack_test
55
+ end.adapter.should be_instance_of(Apis::Adapter::RackTest)
56
+ end
57
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Apis::Adapter do
4
+ it 'registers adapters shortnames' do
5
+ Apis::Adapter.register(:fake, :FakeAdapter)
6
+ Apis::Adapter.lookup(:fake).should == FakeAdapter
7
+ end
8
+
9
+ specify ':net_http is set as default adapter' do
10
+ Apis::Adapter.default.should == :net_http
11
+ end
12
+ end
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+
3
+ describe Apis::Builder do
4
+ it 'adds middleware to stack' do
5
+ builder = Apis::Builder.new
6
+ builder.use Middleware
7
+ builder.length.should == 1
8
+ end
9
+
10
+ it 'raises error when duplicate middleware added' do
11
+ builder = Apis::Builder.new
12
+ builder.use Middleware
13
+ expect { builder.use Middleware }.to raise_error(Apis::DuplicateMiddleware, 'Middleware already in stack')
14
+ end
15
+
16
+ it 'shows if middleware in stack' do
17
+ builder = Apis::Builder.new
18
+ builder.use Middleware
19
+ builder.include?(Middleware).should == true
20
+ end
21
+
22
+ it 'replaces middleware with another' do
23
+ builder = Apis::Builder.new
24
+ builder.use Middleware
25
+ builder.use RESTMiddleware
26
+ builder.replace Middleware, NewMiddleware
27
+ builder.include?(Middleware).should == false
28
+ builder.include?(NewMiddleware).should == true
29
+ builder.to_a.should == [NewMiddleware, RESTMiddleware]
30
+ end
31
+
32
+ it 'removes middleware from stack' do
33
+ builder = Apis::Builder.new
34
+ builder.use Middleware
35
+ builder.include?(Middleware).should == true
36
+ builder.remove(Middleware)
37
+ builder.include?(Middleware).should == false
38
+ end
39
+
40
+ it 'returns stacked middlewares in order' do
41
+ builder = Apis::Builder.new
42
+ builder.use Middleware
43
+ builder.use NewMiddleware
44
+ builder.use RESTMiddleware
45
+ app = builder.to_app
46
+ app.should be_instance_of(Middleware)
47
+ app.app.should be_instance_of(NewMiddleware)
48
+ app.app.app.should be_instance_of(RESTMiddleware)
49
+ end
50
+
51
+ it 'evals block' do
52
+ builder = Apis::Builder.new
53
+ builder.block_eval do |builder|
54
+ use Middleware
55
+ use NewMiddleware
56
+ use RESTMiddleware
57
+ end
58
+
59
+ app = builder.to_app
60
+ app.should be_instance_of(Middleware)
61
+ app.app.should be_instance_of(NewMiddleware)
62
+ app.app.app.should be_instance_of(RESTMiddleware)
63
+ end
64
+
65
+ it 'evals block passed to constructor' do
66
+ builder = Apis::Builder.new do
67
+ use Middleware
68
+ use NewMiddleware
69
+ use RESTMiddleware
70
+ end
71
+
72
+ app = builder.to_app
73
+ app.should be_instance_of(Middleware)
74
+ app.app.should be_instance_of(NewMiddleware)
75
+ app.app.app.should be_instance_of(RESTMiddleware)
76
+ end
77
+
78
+ it 'inserts middleware only once' do
79
+ builder = Apis::Builder.new do
80
+ use Middleware
81
+ end
82
+
83
+ app = builder.to_app
84
+ app.should be_instance_of(Middleware)
85
+ app.app.should be_nil
86
+ end
87
+
88
+ it 'lookups middleware by shortcut if lookup object givven' do
89
+ Lookup = Module.new do
90
+ extend Apis::Registerable
91
+ end
92
+ Lookup.register(:middleware, :Middleware)
93
+ builder = Apis::Builder.new(:lookup_context => Lookup) do
94
+ use :middleware
95
+ end
96
+
97
+ app = builder.to_app
98
+ app.should be_instance_of(Middleware)
99
+ end
100
+ end
@@ -0,0 +1,230 @@
1
+ require 'spec_helper'
2
+
3
+ describe Apis::Connection do
4
+ context 'configuration' do
5
+ context 'options' do
6
+ it 'sets url' do
7
+ Apis::Connection.new(:uri => 'http://api.example.org').uri.should == Addressable::URI.parse('http://api.example.org')
8
+ end
9
+
10
+ it 'sets url if it is only parameter' do
11
+ Apis::Connection.new('http://api.example.org').uri.should == Addressable::URI.parse('http://api.example.org')
12
+ end
13
+
14
+ it 'sets headers' do
15
+ Apis::Connection.new(
16
+ :headers => {
17
+ 'Content-Type' => 'text',
18
+ 'User-Agent' => 'apis'
19
+ }
20
+ ).headers.should == {
21
+ 'Content-Type' => 'text',
22
+ 'User-Agent' => 'apis'
23
+ }
24
+ end
25
+
26
+ it 'sets params' do
27
+ Apis::Connection.new(
28
+ :params => {:q => 'apis', :hl => 'uk'}
29
+ ).params.should == {:q => 'apis', :hl => 'uk'}
30
+ end
31
+ end
32
+
33
+ context 'methods' do
34
+ let(:connection) { Apis::Connection.new }
35
+ it 'sets url' do
36
+ connection.uri = 'http://api.example.org'
37
+ connection.uri.should == Addressable::URI.parse('http://api.example.org')
38
+ end
39
+
40
+ it 'sets headers' do
41
+ connection.headers = {
42
+ 'Content-Type' => 'text',
43
+ 'User-Agent' => 'apis'
44
+ }
45
+ connection.headers.should == {
46
+ 'Content-Type' => 'text',
47
+ 'User-Agent' => 'apis'
48
+ }
49
+ end
50
+
51
+ it 'sets params' do
52
+ connection.params = {:q => 'apis', :hl => 'uk'}
53
+ connection.params.should == {:q => 'apis', :hl => 'uk'}
54
+ end
55
+
56
+ it 'updates headers' do
57
+ connection.headers = {'Content-Type' => 'text'}
58
+ connection.headers = {'User-Agent' => 'apis'}
59
+ connection.headers.should == {
60
+ 'Content-Type' => 'text',
61
+ 'User-Agent' => 'apis'
62
+ }
63
+ end
64
+
65
+ it 'updates params' do
66
+ connection.params = {:q => 'apis'}
67
+ connection.params = {:hl => 'uk'}
68
+ connection.params.should == {:q => 'apis', :hl => 'uk'}
69
+ end
70
+ end
71
+
72
+ context 'stack building' do
73
+ it 'constructs request stack' do
74
+ connection = Apis::Connection.new do
75
+ request do
76
+ use Middleware
77
+ use NewMiddleware
78
+ use RESTMiddleware
79
+ end
80
+ end
81
+
82
+ connection.request.to_a.should == [Middleware, NewMiddleware, RESTMiddleware]
83
+ end
84
+
85
+ it 'constructs response stack' do
86
+ connection = Apis::Connection.new do
87
+ response do
88
+ use Middleware
89
+ use NewMiddleware
90
+ use RESTMiddleware
91
+ end
92
+ end
93
+
94
+ connection.response.to_a.should == [Middleware, NewMiddleware, RESTMiddleware]
95
+ end
96
+
97
+ it 'sets adapter' do
98
+ connection = Apis::Connection.new do
99
+ adapter FakeAdapter
100
+ end
101
+ connection.adapter.should be_instance_of(FakeAdapter)
102
+ end
103
+
104
+ it 'finds adapter using symbol shortcut' do
105
+ Apis::Adapter.register(:fake, :FakeAdapter)
106
+ connection = Apis::Connection.new do
107
+ adapter :fake
108
+ end
109
+ connection.adapter.should be_instance_of(FakeAdapter)
110
+ end
111
+
112
+ it 'uses default adapter if none specified' do
113
+ connection = Apis::Connection.new
114
+ connection.adapter nil
115
+ connection.adapter.should be_instance_of(Apis::Adapter::NetHTTP)
116
+ end
117
+ end
118
+ end
119
+
120
+ context 'performing request' do
121
+ before do
122
+ @connection = Apis::Connection.new(:uri => server_host) do
123
+ adapter FakeAdapter
124
+ end
125
+ end
126
+
127
+ [:get, :head, :post, :put, :delete].each do |method|
128
+ context method.to_s.upcase do
129
+ it 'passes method to adapter' do
130
+ @connection.send(method)
131
+ @connection.adapter.last_method.should == method
132
+ end
133
+
134
+ it 'passes path to adapter' do
135
+ @connection.send(method, "/#{method}")
136
+ @connection.adapter.last_path.should == "/#{method}"
137
+ end
138
+
139
+ it 'passes query params to adapter' do
140
+ @connection.send(method, "/#{method}", {:q => 'text'})
141
+ @connection.adapter.last_params.should == {:q => 'text'}
142
+ end
143
+
144
+ it 'passes params specified in block' do
145
+ @connection.send(method, "/#{method}") do |request|
146
+ request.params = {:test => 'params'}
147
+ end
148
+ @connection.adapter.last_params.should == {:test => 'params'}
149
+ end
150
+
151
+ it 'doesn\'t not overwrite params of connection' do
152
+ @connection.params = {:q => 'test'}
153
+ @connection.send(method, "/#{method}") do |request|
154
+ request.params = {:test => 'params'}
155
+ end
156
+ @connection.adapter.last_params.should == {:q => 'test', :test => 'params'}
157
+ @connection.params.should == {:q => 'test'}
158
+ end
159
+
160
+ it 'merges connection params with method params' do
161
+ @connection.params = {:test => 'param'}
162
+ @connection.send(method, "/#{method}", {:q => 'text'})
163
+ @connection.adapter.last_params.should == {:q => 'text', :test => 'param'}
164
+ end
165
+
166
+ it 'passes headers to adapter' do
167
+ @connection.send(method, "/#{method}", {}, {'Content-Type' => 'text'})
168
+ @connection.adapter.last_headers.should == {'Content-Type' => 'text'}
169
+ end
170
+
171
+ it 'merges connection headers with method headers' do
172
+ @connection.headers = {'User-Agent' => 'apis'}
173
+ @connection.send(method, "/#{method}", {}, {'Content-Type' => 'text'})
174
+ @connection.adapter.last_headers.should == {'Content-Type' => 'text', 'User-Agent' => 'apis'}
175
+ end
176
+
177
+ it 'doesn\'t overwrite headers of connection' do
178
+ @connection.headers = {:q => 'test'}
179
+ @connection.send(method, "/#{method}") do |request|
180
+ request.headers = {:test => 'params'}
181
+ end
182
+ @connection.adapter.last_headers.should == {:q => 'test', :test => 'params'}
183
+ @connection.headers.should == {:q => 'test'}
184
+ end
185
+ end
186
+ end
187
+ end
188
+
189
+ context 'request with middleware' do
190
+ context 'request middleware' do
191
+ before do
192
+ Apis::Middleware::Request.register(:middleware, :Middleware)
193
+ @connection = Apis::Connection.new(:uri => server_host) do
194
+ request do
195
+ use :middleware
196
+ end
197
+ adapter FakeAdapter
198
+ end
199
+ end
200
+
201
+ it 'calls midleware' do
202
+ @connection.get
203
+ @connection.adapter.last_headers.should == {'Middleware' => 'true'}
204
+ end
205
+
206
+ it "doesn't overwrite headers of connection" do
207
+ @connection.get
208
+ @connection.adapter.last_headers.should == {'Middleware' => 'true'}
209
+ @connection.headers.should == {}
210
+ end
211
+ end
212
+
213
+ context 'response midleware' do
214
+ before do
215
+ Apis::Middleware::Response.register(:res, :Response)
216
+ @connection = Apis::Connection.new(:uri => server_host) do
217
+ adapter FakeAdapter
218
+ response do
219
+ use :res
220
+ end
221
+ end
222
+ end
223
+
224
+ it 'calls midleware' do
225
+ response = @connection.get
226
+ response.body.should == 'altered'
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Apis::Middleware::Response::Json do
4
+ it 'replaces body with parsed data' do
5
+ MultiJson.engine = :yajl
6
+ json = Apis::Middleware::Response::Json.new
7
+ request = Apis::Response.new(200, {}, %|{"name":"Marjan Krekoten'","age": 23}|)
8
+ json.call(request)
9
+ request.body.should == {"name" => "Marjan Krekoten'", "age" => 23}
10
+ end
11
+
12
+ it 'registered under :json' do
13
+ Apis::Connection.new do
14
+ response do
15
+ use :json
16
+ end
17
+ end.response.to_a.should include(Apis::Middleware::Response::Json)
18
+ end
19
+ end
data/spec/apis_spec.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Apis do
4
+ end
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+
3
+ describe "The library itself" do
4
+ def check_for_tab_characters(filename)
5
+ failing_lines = []
6
+ File.readlines(filename).each_with_index do |line,number|
7
+ failing_lines << number + 1 if line =~ /\t/
8
+ end
9
+
10
+ unless failing_lines.empty?
11
+ "#{filename} has tab characters on lines #{failing_lines.join(', ')}"
12
+ end
13
+ end
14
+
15
+ def check_for_extra_spaces(filename)
16
+ failing_lines = []
17
+ File.readlines(filename).each_with_index do |line,number|
18
+ next if line =~ /^\s+#.*\s+\n$/
19
+ failing_lines << number + 1 if line =~ /\s+\n$/
20
+ end
21
+
22
+ unless failing_lines.empty?
23
+ "#{filename} has spaces on the EOL on lines #{failing_lines.join(', ')}"
24
+ end
25
+ end
26
+
27
+ RSpec::Matchers.define :be_well_formed do
28
+ failure_message_for_should do |actual|
29
+ actual.join("\n")
30
+ end
31
+
32
+ match do |actual|
33
+ actual.empty?
34
+ end
35
+ end
36
+
37
+ it "has no malformed whitespace" do
38
+ error_messages = []
39
+ Dir.chdir(File.expand_path("../..", __FILE__)) do
40
+ `git ls-files`.split("\n").each do |filename|
41
+ next if filename =~ /\.gitmodules|fixtures|\.md/
42
+ error_messages << check_for_tab_characters(filename)
43
+ error_messages << check_for_extra_spaces(filename)
44
+ end
45
+ end
46
+ error_messages.compact.should be_well_formed
47
+ end
48
+
49
+ it "can still be built" do
50
+ Dir.chdir(root) do
51
+ `gem build apis.gemspec`
52
+ $?.should == 0
53
+ `rm apis-*.gem`
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,60 @@
1
+ require File.expand_path('../../lib/apis', __FILE__)
2
+
3
+ class BaseMiddleware
4
+ attr_accessor :app
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+ end
9
+ class Middleware < BaseMiddleware
10
+ def call(env)
11
+ env[:params][:middleware] = 'true'
12
+ env[:headers]['Middleware'] = 'true'
13
+ @app.call(env) if @app
14
+ end
15
+ end
16
+ NewMiddleware = Class.new(BaseMiddleware)
17
+ RESTMiddleware = Class.new(BaseMiddleware)
18
+
19
+ class Response < BaseMiddleware
20
+ def call(env)
21
+ env[:body] = 'altered'
22
+ end
23
+ end
24
+
25
+ class FakeAdapter < Apis::Adapter::Abstract
26
+ attr_accessor :last_method, :last_path, :last_params, :last_headers
27
+ def run(method, path = nil, params = {}, headers = {})
28
+ @last_method, @last_path, @last_params, @last_headers = method, path, params, headers
29
+ [200, {}, 'body']
30
+ end
31
+ end
32
+
33
+ module DirHelper
34
+ def root
35
+ @root ||= File.expand_path('../..', __FILE__)
36
+ end
37
+ end
38
+
39
+ module SinatraHelper
40
+ def server_port
41
+ 1234
42
+ end
43
+
44
+ def server_host
45
+ "http://localhost:#{server_port}"
46
+ end
47
+
48
+ def start_server
49
+ %x{unicorn -p #{server_port} #{root}/spec/test_app.ru -D -P #{root}/spec/uni.pid} # 3&> /dev/null
50
+ end
51
+
52
+ def stop_server
53
+ %x{kill `cat #{root}/spec/uni.pid`}
54
+ end
55
+ end
56
+
57
+ RSpec.configure do |rspec|
58
+ rspec.include DirHelper
59
+ rspec.include SinatraHelper
60
+ end
data/spec/test_app.ru ADDED
@@ -0,0 +1,16 @@
1
+ require 'sinatra'
2
+
3
+ [:head, :get, :post, :put, :delete].each do |method|
4
+ send(method, '/') do
5
+ headers['X-Requested-With-Method'] = method.to_s.upcase
6
+ "#{method.to_s.upcase}" unless method == :head
7
+ end
8
+
9
+ send(method, "/#{method}") do
10
+ headers['X-Requested-With-Method'] = method.to_s.upcase
11
+ headers['X-Sent-Params'] = params.inspect
12
+ "#{method.to_s.upcase}" unless method == :head
13
+ end
14
+ end
15
+
16
+ run Sinatra::Application
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apis
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.4.1
6
+ platform: ruby
7
+ authors:
8
+ - "Marjan Krekoten' (\xD0\x9C\xD0\xB0\xD1\x80'\xD1\x8F\xD0\xBD \xD0\x9A\xD1\x80\xD0\xB5\xD0\xBA\xD0\xBE\xD1\x82\xD0\xB5\xD0\xBD\xD1\x8C)"
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-07 00:00:00 +03:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "2.5"
25
+ type: :development
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: sinatra
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "0"
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: rack-test
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: unicorn
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ type: :development
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: multi_json
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ type: :development
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: yajl-ruby
73
+ prerelease: false
74
+ requirement: &id006 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ type: :development
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: addressable
84
+ prerelease: false
85
+ requirement: &id007 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: "0"
91
+ type: :runtime
92
+ version_requirements: *id007
93
+ description: Rack-like HTTP client library inspired by Faraday done my way
94
+ email:
95
+ - m@hmarynka.com
96
+ executables: []
97
+
98
+ extensions: []
99
+
100
+ extra_rdoc_files: []
101
+
102
+ files:
103
+ - .gitignore
104
+ - .yardopts
105
+ - Gemfile
106
+ - README.md
107
+ - Rakefile
108
+ - apis.gemspec
109
+ - lib/apis.rb
110
+ - lib/apis/adapter/abstract.rb
111
+ - lib/apis/adapter/net_http.rb
112
+ - lib/apis/adapter/rack_test.rb
113
+ - lib/apis/builder.rb
114
+ - lib/apis/connection.rb
115
+ - lib/apis/connection_scope.rb
116
+ - lib/apis/middleware/response/json.rb
117
+ - lib/apis/response.rb
118
+ - lib/apis/version.rb
119
+ - spec/apis/adapter/net_http_spec.rb
120
+ - spec/apis/adapter/rack_test_spec.rb
121
+ - spec/apis/adapter_spec.rb
122
+ - spec/apis/builder_spec.rb
123
+ - spec/apis/connection_spec.rb
124
+ - spec/apis/middleware/response/json_spec.rb
125
+ - spec/apis_spec.rb
126
+ - spec/quality_spec.rb
127
+ - spec/spec_helper.rb
128
+ - spec/test_app.ru
129
+ has_rdoc: true
130
+ homepage: http://hmarynka.com/labs/apis
131
+ licenses: []
132
+
133
+ post_install_message:
134
+ rdoc_options: []
135
+
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: "0"
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: "0"
150
+ requirements: []
151
+
152
+ rubyforge_project: apis
153
+ rubygems_version: 1.6.2
154
+ signing_key:
155
+ specification_version: 3
156
+ summary: Working bee of API wrapper
157
+ test_files:
158
+ - spec/apis/adapter/net_http_spec.rb
159
+ - spec/apis/adapter/rack_test_spec.rb
160
+ - spec/apis/adapter_spec.rb
161
+ - spec/apis/builder_spec.rb
162
+ - spec/apis/connection_spec.rb
163
+ - spec/apis/middleware/response/json_spec.rb
164
+ - spec/apis_spec.rb
165
+ - spec/quality_spec.rb
166
+ - spec/spec_helper.rb
167
+ - spec/test_app.ru