rack-rest_api_versioning 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.
- data/README.rdoc +41 -0
- data/Rakefile +33 -0
- data/VERSION +1 -0
- data/lib/rack/rest_api_versioning.rb +38 -0
- data/test/rest_api_versioning_test.rb +67 -0
- metadata +77 -0
data/README.rdoc
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
= Rack REST API Versioning
|
2
|
+
|
3
|
+
This Rack middleware adds support for RESTful API versioning. According to RESTful best
|
4
|
+
practices, the API version should be specified using content negotiation and custom vendor
|
5
|
+
MIME types. For example, if your application's API supports multiple versions, a client can
|
6
|
+
ask for a specific version like this:
|
7
|
+
|
8
|
+
GET /foo HTTP/1.1
|
9
|
+
Accept: application/vnd.foo.bar-v2+xml
|
10
|
+
|
11
|
+
Where "application/vnd.foo.bar-v2+xml" is vendor MIME type your application defines.
|
12
|
+
|
13
|
+
See this article: http://barelyenough.org/blog/2008/05/versioning-rest-web-services/ for more
|
14
|
+
information about this practice.
|
15
|
+
|
16
|
+
This middleware is used like any other rack middleware:
|
17
|
+
|
18
|
+
use Rack::RestApiVersioning
|
19
|
+
|
20
|
+
When used, it extracts the version from the Accept header, and makes it available in the
|
21
|
+
Rack environment hash as "api_version":
|
22
|
+
|
23
|
+
app = lambda { |env
|
24
|
+
puts env['api_version']
|
25
|
+
[200, {}, []]
|
26
|
+
}
|
27
|
+
|
28
|
+
To simplify testing and debugging, this middleware also allows the version to be overriden
|
29
|
+
using simple query string parameter:
|
30
|
+
|
31
|
+
GET /foo?version=2 HTTP/1.1
|
32
|
+
|
33
|
+
This will ask for version 2, no matter what the Accept header is set to.
|
34
|
+
|
35
|
+
Finally, a default version can be specified. This will be used if no version can be extracted:
|
36
|
+
|
37
|
+
use Rack::RestApiVersioning, :default_version => '2.0'
|
38
|
+
|
39
|
+
== Legal
|
40
|
+
|
41
|
+
Copyright (c) 2010 Adam Cigánek <adam.ciganek@gmail.com>. Released under the terms of MIT License: www.opensource.org/licenses/mit-license.php
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
desc 'Default: run unit tests.'
|
7
|
+
task :default => :test
|
8
|
+
|
9
|
+
desc 'Run unit tests.'
|
10
|
+
Rake::TestTask.new(:test) do |t|
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
begin
|
16
|
+
require 'jeweler'
|
17
|
+
|
18
|
+
Jeweler::Tasks.new do |gemspec|
|
19
|
+
gemspec.name = 'rack-rest_api_versioning'
|
20
|
+
gemspec.summary = 'A Rack middleware to support "the proper way" to version a RESTful API.'
|
21
|
+
gemspec.description = "This midleware allows an application to support multiple versions of a RESTful API following the best RESTful practices to encode the version information in the HTTP request. It's inspired by this article: http://barelyenough.org/blog/2008/05/versioning-rest-web-services/."
|
22
|
+
|
23
|
+
gemspec.email = 'adam.ciganek@gmail.com'
|
24
|
+
gemspec.homepage = 'http://github.com/madadam/rack-rest_api_versioning'
|
25
|
+
gemspec.authors = ['Adam Cigánek']
|
26
|
+
|
27
|
+
gemspec.add_dependency 'rack'
|
28
|
+
end
|
29
|
+
|
30
|
+
Jeweler::GemcutterTasks.new
|
31
|
+
rescue LoadError
|
32
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
33
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class RestApiVersioning
|
5
|
+
def initialize(app, options = {})
|
6
|
+
@app = app
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
extract_version!(env)
|
12
|
+
@app.call(env)
|
13
|
+
end
|
14
|
+
|
15
|
+
# 1
|
16
|
+
# 1.2
|
17
|
+
# 1.2.3
|
18
|
+
VERSION_STRING = '(\d+(?:\.\d+)*)'
|
19
|
+
|
20
|
+
# application/vnd.foo.bar-v1+xml
|
21
|
+
# application/vnd.foo.bar-v1.2.3+json
|
22
|
+
# ...
|
23
|
+
HTTP_ACCEPT_PATTERN = /.*\-v#{VERSION_STRING}/
|
24
|
+
|
25
|
+
# ?version=1.2.3
|
26
|
+
QUERY_STRING_PATTERN = /\bversion=#{VERSION_STRING}/
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def extract_version!(env)
|
31
|
+
version = env['QUERY_STRING'].to_s[QUERY_STRING_PATTERN, 1] ||
|
32
|
+
env['HTTP_ACCEPT'].to_s[HTTP_ACCEPT_PATTERN, 1] ||
|
33
|
+
@options[:default_version]
|
34
|
+
|
35
|
+
env['api_version'] = version
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rack'
|
3
|
+
require 'rack/test'
|
4
|
+
|
5
|
+
require 'rack/rest_api_versioning'
|
6
|
+
|
7
|
+
class RestApiVersioningTest < Test::Unit::TestCase
|
8
|
+
include Rack::Test::Methods
|
9
|
+
|
10
|
+
def app
|
11
|
+
@app
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_extracts_version_from_content_type
|
15
|
+
app! { use Rack::RestApiVersioning }
|
16
|
+
|
17
|
+
get '/', {}, 'HTTP_ACCEPT' => 'application/vnd.acme-v1+json'
|
18
|
+
assert_equal '1', last_request.env['api_version']
|
19
|
+
|
20
|
+
get '/', {}, 'HTTP_ACCEPT' => 'application/vnd.acme-v2+json'
|
21
|
+
assert_equal '2', last_request.env['api_version']
|
22
|
+
|
23
|
+
get '/', {}, 'HTTP_ACCEPT' => 'application/vnd.acme-v1.2.3+json'
|
24
|
+
assert_equal '1.2.3', last_request.env['api_version']
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_fallbacks_to_default_version_if_no_version_can_be_extracted
|
28
|
+
app! { use Rack::RestApiVersioning, :default_version => '4.5' }
|
29
|
+
|
30
|
+
get '/', {}, 'HTTP_ACCEPT' => 'application/vnd.acme+json'
|
31
|
+
assert_equal '4.5', last_request.env['api_version']
|
32
|
+
|
33
|
+
get '/', {}, 'HTTP_ACCEPT' => 'application/json'
|
34
|
+
assert_equal '4.5', last_request.env['api_version']
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_does_not_fallback_to_default_version_if_version_can_be_extracted
|
38
|
+
app! { use Rack::RestApiVersioning, :default_version => '6.7' }
|
39
|
+
|
40
|
+
get '/', {}, 'HTTP_ACCEPT' => 'application/vnd.acme-v2+json'
|
41
|
+
assert_equal '2', last_request.env['api_version']
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_allows_version_to_be_overriden_with_query_string_parameter
|
45
|
+
app! { use Rack::RestApiVersioning }
|
46
|
+
|
47
|
+
get '/', 'version' => '1.2'
|
48
|
+
assert_equal '1.2', last_request.env['api_version']
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_version_in_query_string_takes_precedence_over_version_in_content_type
|
52
|
+
app! { use Rack::RestApiVersioning }
|
53
|
+
|
54
|
+
get '/', 'version' => '1.2', 'HTTP_ACCEPT' => 'application/vnd.acme-v1.3'
|
55
|
+
assert_equal '1.2', last_request.env['api_version']
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def app!(&block)
|
61
|
+
@app = Rack::Builder.new do
|
62
|
+
instance_eval(&block)
|
63
|
+
run lambda { |env| [200, {}, []] }
|
64
|
+
end.to_app
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-rest_api_versioning
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- "Adam Cig\xC3\xA1nek"
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-08 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rack
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
description: "This midleware allows an application to support multiple versions of a RESTful API following the best RESTful practices to encode the version information in the HTTP request. It's inspired by this article: http://barelyenough.org/blog/2008/05/versioning-rest-web-services/."
|
33
|
+
email: adam.ciganek@gmail.com
|
34
|
+
executables: []
|
35
|
+
|
36
|
+
extensions: []
|
37
|
+
|
38
|
+
extra_rdoc_files:
|
39
|
+
- README.rdoc
|
40
|
+
files:
|
41
|
+
- README.rdoc
|
42
|
+
- Rakefile
|
43
|
+
- VERSION
|
44
|
+
- lib/rack/rest_api_versioning.rb
|
45
|
+
- test/rest_api_versioning_test.rb
|
46
|
+
has_rdoc: true
|
47
|
+
homepage: http://github.com/madadam/rack-rest_api_versioning
|
48
|
+
licenses: []
|
49
|
+
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options:
|
52
|
+
- --charset=UTF-8
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
version: "0"
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 1.3.6
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: A Rack middleware to support "the proper way" to version a RESTful API.
|
76
|
+
test_files:
|
77
|
+
- test/rest_api_versioning_test.rb
|