api-versions 0.0.5 → 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.
- data/.gitignore +1 -0
- data/README.md +123 -74
- data/api-versions.gemspec +1 -1
- data/lib/api-versions.rb +7 -50
- data/lib/api-versions/dsl.rb +31 -0
- data/lib/api-versions/version.rb +2 -4
- data/lib/api-versions/version_check.rb +27 -0
- data/spec/dummy/app/controllers/api/v3/bar_controller.rb +2 -0
- data/spec/dummy/config/routes.rb +13 -18
- data/spec/routing/routing_spec.rb +10 -3
- metadata +6 -4
- data/spec/dummy/.rspec +0 -1
data/.gitignore
CHANGED
data/README.md
CHANGED
|
@@ -2,88 +2,137 @@ API-Versions [ api/v2/authorizations#show
|
|
44
|
-
PUT /api/authorizations/:id(.:format) api/v2/authorizations#update
|
|
45
|
-
DELETE /api/authorizations/:id(.:format) api/v2/authorizations#destroy
|
|
14
|
+
``` ruby
|
|
15
|
+
# You can leave default_version out, but if you do the first version used will become the default
|
|
16
|
+
api vendor_string: "myvendor", default_version: 1 do
|
|
17
|
+
version 1 do
|
|
18
|
+
cache as: 'v1' do
|
|
19
|
+
resources :authorizations
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
version 2 do
|
|
24
|
+
inherit from: 'v1'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`rake routes` outputs:
|
|
30
|
+
|
|
31
|
+
api_authorizations GET /api/authorizations(.:format) api/v1/authorizations#index
|
|
32
|
+
POST /api/authorizations(.:format) api/v1/authorizations#create
|
|
33
|
+
new_api_authorization GET /api/authorizations/new(.:format) api/v1/authorizations#new
|
|
34
|
+
edit_api_authorization GET /api/authorizations/:id/edit(.:format) api/v1/authorizations#edit
|
|
35
|
+
api_authorization GET /api/authorizations/:id(.:format) api/v1/authorizations#show
|
|
36
|
+
PUT /api/authorizations/:id(.:format) api/v1/authorizations#update
|
|
37
|
+
DELETE /api/authorizations/:id(.:format) api/v1/authorizations#destroy
|
|
38
|
+
GET /api/authorizations(.:format) api/v2/authorizations#index
|
|
39
|
+
POST /api/authorizations(.:format) api/v2/authorizations#create
|
|
40
|
+
GET /api/authorizations/new(.:format) api/v2/authorizations#new
|
|
41
|
+
GET /api/authorizations/:id/edit(.:format) api/v2/authorizations#edit
|
|
42
|
+
GET /api/authorizations/:id(.:format) api/v2/authorizations#show
|
|
43
|
+
PUT /api/authorizations/:id(.:format) api/v2/authorizations#update
|
|
44
|
+
DELETE /api/authorizations/:id(.:format) api/v2/authorizations#destroy
|
|
45
|
+
|
|
46
46
|
|
|
47
47
|
Then the client simply sets the Accept header "application/vnd.myvendor+json;version=1". If no version is specified, the default version you set will be assumed. You'll of course still need to copy all of your controllers over, even if they haven't changed from version to version. At least you'll remove a bit of the mess in your routes file.
|
|
48
48
|
|
|
49
|
-
A more complicated example
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
49
|
+
A more complicated example:
|
|
50
|
+
|
|
51
|
+
``` ruby
|
|
52
|
+
api vendor_string: "myvendor", default_version: 1 do
|
|
53
|
+
version 1 do
|
|
54
|
+
cache as: 'v1' do
|
|
55
|
+
resources :authorizations, only: :create
|
|
56
|
+
resources :foo
|
|
57
|
+
resources :bar
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
version 2 do
|
|
62
|
+
cache as: 'v2' do
|
|
63
|
+
inherit from: 'v1'
|
|
64
|
+
resources :my_new_resource
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# V3 has everything in V2, and everything in V1 as well by virtue of V1 being cached in V2.
|
|
69
|
+
version 3 do
|
|
70
|
+
inherit from: 'v2'
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
And finally `rake routes` outputs:
|
|
76
|
+
|
|
77
|
+
api_authorizations POST /api/authorizations(.:format) api/v1/authorizations#create
|
|
78
|
+
api_foo_index GET /api/foo(.:format) api/v1/foo#index
|
|
79
|
+
POST /api/foo(.:format) api/v1/foo#create
|
|
80
|
+
new_api_foo GET /api/foo/new(.:format) api/v1/foo#new
|
|
81
|
+
edit_api_foo GET /api/foo/:id/edit(.:format) api/v1/foo#edit
|
|
82
|
+
api_foo GET /api/foo/:id(.:format) api/v1/foo#show
|
|
83
|
+
PUT /api/foo/:id(.:format) api/v1/foo#update
|
|
84
|
+
DELETE /api/foo/:id(.:format) api/v1/foo#destroy
|
|
85
|
+
api_bar_index GET /api/bar(.:format) api/v1/bar#index
|
|
86
|
+
POST /api/bar(.:format) api/v1/bar#create
|
|
87
|
+
new_api_bar GET /api/bar/new(.:format) api/v1/bar#new
|
|
88
|
+
edit_api_bar GET /api/bar/:id/edit(.:format) api/v1/bar#edit
|
|
89
|
+
api_bar GET /api/bar/:id(.:format) api/v1/bar#show
|
|
90
|
+
PUT /api/bar/:id(.:format) api/v1/bar#update
|
|
91
|
+
DELETE /api/bar/:id(.:format) api/v1/bar#destroy
|
|
92
|
+
POST /api/authorizations(.:format) api/v2/authorizations#create
|
|
93
|
+
GET /api/foo(.:format) api/v2/foo#index
|
|
94
|
+
POST /api/foo(.:format) api/v2/foo#create
|
|
95
|
+
GET /api/foo/new(.:format) api/v2/foo#new
|
|
96
|
+
GET /api/foo/:id/edit(.:format) api/v2/foo#edit
|
|
97
|
+
GET /api/foo/:id(.:format) api/v2/foo#show
|
|
98
|
+
PUT /api/foo/:id(.:format) api/v2/foo#update
|
|
99
|
+
DELETE /api/foo/:id(.:format) api/v2/foo#destroy
|
|
100
|
+
GET /api/bar(.:format) api/v2/bar#index
|
|
101
|
+
POST /api/bar(.:format) api/v2/bar#create
|
|
102
|
+
GET /api/bar/new(.:format) api/v2/bar#new
|
|
103
|
+
GET /api/bar/:id/edit(.:format) api/v2/bar#edit
|
|
104
|
+
GET /api/bar/:id(.:format) api/v2/bar#show
|
|
105
|
+
PUT /api/bar/:id(.:format) api/v2/bar#update
|
|
106
|
+
DELETE /api/bar/:id(.:format) api/v2/bar#destroy
|
|
107
|
+
api_my_new_resource_index GET /api/my_new_resource(.:format) api/v2/my_new_resource#index
|
|
108
|
+
POST /api/my_new_resource(.:format) api/v2/my_new_resource#create
|
|
109
|
+
new_api_my_new_resource GET /api/my_new_resource/new(.:format) api/v2/my_new_resource#new
|
|
110
|
+
edit_api_my_new_resource GET /api/my_new_resource/:id/edit(.:format) api/v2/my_new_resource#edit
|
|
111
|
+
api_my_new_resource GET /api/my_new_resource/:id(.:format) api/v2/my_new_resource#show
|
|
112
|
+
PUT /api/my_new_resource/:id(.:format) api/v2/my_new_resource#update
|
|
113
|
+
DELETE /api/my_new_resource/:id(.:format) api/v2/my_new_resource#destroy
|
|
114
|
+
POST /api/authorizations(.:format) api/v3/authorizations#create
|
|
115
|
+
GET /api/foo(.:format) api/v3/foo#index
|
|
116
|
+
POST /api/foo(.:format) api/v3/foo#create
|
|
117
|
+
GET /api/foo/new(.:format) api/v3/foo#new
|
|
118
|
+
GET /api/foo/:id/edit(.:format) api/v3/foo#edit
|
|
119
|
+
GET /api/foo/:id(.:format) api/v3/foo#show
|
|
120
|
+
PUT /api/foo/:id(.:format) api/v3/foo#update
|
|
121
|
+
DELETE /api/foo/:id(.:format) api/v3/foo#destroy
|
|
122
|
+
GET /api/bar(.:format) api/v3/bar#index
|
|
123
|
+
POST /api/bar(.:format) api/v3/bar#create
|
|
124
|
+
GET /api/bar/new(.:format) api/v3/bar#new
|
|
125
|
+
GET /api/bar/:id/edit(.:format) api/v3/bar#edit
|
|
126
|
+
GET /api/bar/:id(.:format) api/v3/bar#show
|
|
127
|
+
PUT /api/bar/:id(.:format) api/v3/bar#update
|
|
128
|
+
DELETE /api/bar/:id(.:format) api/v3/bar#destroy
|
|
129
|
+
GET /api/my_new_resource(.:format) api/v3/my_new_resource#index
|
|
130
|
+
POST /api/my_new_resource(.:format) api/v3/my_new_resource#create
|
|
131
|
+
GET /api/my_new_resource/new(.:format) api/v3/my_new_resource#new
|
|
132
|
+
GET /api/my_new_resource/:id/edit(.:format) api/v3/my_new_resource#edit
|
|
133
|
+
GET /api/my_new_resource/:id(.:format) api/v3/my_new_resource#show
|
|
134
|
+
PUT /api/my_new_resource/:id(.:format) api/v3/my_new_resource#update
|
|
135
|
+
DELETE /api/my_new_resource/:id(.:format) api/v3/my_new_resource#destroy
|
|
87
136
|
|
|
88
137
|
License
|
|
89
138
|
=======
|
data/api-versions.gemspec
CHANGED
|
@@ -4,7 +4,7 @@ require "api-versions/version"
|
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |s|
|
|
6
6
|
s.name = "api-versions"
|
|
7
|
-
s.version =
|
|
7
|
+
s.version = ApiVersions::VERSION
|
|
8
8
|
s.authors = ["Erich Menge"]
|
|
9
9
|
s.email = ["erich.menge@me.com"]
|
|
10
10
|
s.homepage = "https://github.com/erichmenge/api-versions"
|
data/lib/api-versions.rb
CHANGED
|
@@ -1,58 +1,15 @@
|
|
|
1
1
|
require "api-versions/version"
|
|
2
|
+
require "api-versions/version_check"
|
|
3
|
+
require "api-versions/dsl"
|
|
2
4
|
|
|
3
5
|
module ApiVersions
|
|
4
|
-
def
|
|
5
|
-
[
|
|
6
|
-
|
|
7
|
-
end
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def cache_resources(args, &block)
|
|
11
|
-
@resource_cache ||= {}
|
|
12
|
-
@resource_cache.merge!(args[:as] => block)
|
|
13
|
-
block.call
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def api_version=(version)
|
|
17
|
-
ApiVersions::ApiVersionCheck.api_version = version
|
|
18
|
-
end
|
|
19
|
-
alias_method :default_api_version, :api_version=
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def vendor=(vendor)
|
|
23
|
-
ApiVersions::ApiVersionCheck.api_vendor = vendor
|
|
24
|
-
end
|
|
25
|
-
alias_method :api_vendor, :vendor=
|
|
26
|
-
|
|
27
|
-
def api_version_check(*args)
|
|
28
|
-
ApiVersions::ApiVersionCheck.new(*args)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
class ApiVersionCheck
|
|
32
|
-
|
|
33
|
-
cattr_accessor :api_version, :api_vendor
|
|
34
|
-
|
|
35
|
-
def initialize(args = {})
|
|
36
|
-
@process_version = args[:version]
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def matches?(request)
|
|
40
|
-
accepts_proper_format?(request) && (matches_version?(request) || unversioned?(request))
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
private
|
|
44
|
-
|
|
45
|
-
def accepts_proper_format?(request)
|
|
46
|
-
!!(request.headers['Accept'] =~ /^application\/vnd\.#{self.class.api_vendor}\+.+/)
|
|
47
|
-
end
|
|
6
|
+
def api(options = {}, &block)
|
|
7
|
+
VersionCheck.default_version = options[:default_version]
|
|
8
|
+
VersionCheck.vendor_string = options[:vendor_string]
|
|
48
9
|
|
|
49
|
-
|
|
50
|
-
!!(request.headers['Accept'] =~ /version\s*?=\s*?#{@process_version}\b/)
|
|
51
|
-
end
|
|
10
|
+
raise "Please set a vendor_string for the api method" if options[:vendor_string].nil?
|
|
52
11
|
|
|
53
|
-
|
|
54
|
-
@process_version == self.class.api_version && !(request.headers['Accept'] =~ /version\s*?=\s*?\d*\b/i)
|
|
55
|
-
end
|
|
12
|
+
namespace(:api) { DSL.new(self, &block) }
|
|
56
13
|
end
|
|
57
14
|
end
|
|
58
15
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module ApiVersions
|
|
2
|
+
class DSL
|
|
3
|
+
extend Forwardable
|
|
4
|
+
|
|
5
|
+
def initialize(context, &block)
|
|
6
|
+
@context = context
|
|
7
|
+
self.class.def_delegators :@context, *(@context.public_methods - public_methods)
|
|
8
|
+
instance_eval &block
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def version(version_number, &block)
|
|
12
|
+
VersionCheck.default_version ||= version_number
|
|
13
|
+
|
|
14
|
+
constraints VersionCheck.new(version: version_number) do
|
|
15
|
+
scope({ module: "v#{version_number}" }, &block)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def inherit(args)
|
|
20
|
+
[*args[:from]].each do |block|
|
|
21
|
+
@resource_cache[block].call
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def cache(args, &block)
|
|
26
|
+
@resource_cache ||= {}
|
|
27
|
+
@resource_cache[args[:as]] = block
|
|
28
|
+
block.call
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/api-versions/version.rb
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module ApiVersions
|
|
2
|
+
class VersionCheck
|
|
3
|
+
cattr_accessor :default_version, :vendor_string
|
|
4
|
+
|
|
5
|
+
def initialize(args = {})
|
|
6
|
+
@process_version = args[:version]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def matches?(request)
|
|
10
|
+
accepts_proper_format?(request) && (matches_version?(request) || unversioned?(request))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def accepts_proper_format?(request)
|
|
16
|
+
!!(request.headers['Accept'] =~ /^application\/vnd\.#{self.class.vendor_string}\+.+/)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def matches_version?(request)
|
|
20
|
+
!!(request.headers['Accept'] =~ /version\s*?=\s*?#{@process_version}\b/)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def unversioned?(request)
|
|
24
|
+
@process_version == self.class.default_version && !(request.headers['Accept'] =~ /version\s*?=\s*?\d*\b/i)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/spec/dummy/config/routes.rb
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
Dummy::Application.routes.draw do
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
api vendor_string: "myvendor" do
|
|
3
|
+
version 1 do
|
|
4
|
+
cache as: 'v1' do
|
|
5
|
+
resources :bar
|
|
6
|
+
end
|
|
7
|
+
end
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
end
|
|
9
|
+
version 2 do
|
|
10
|
+
cache as: 'v2' do
|
|
11
|
+
resources :foo
|
|
12
|
+
inherit from: 'v1'
|
|
13
|
+
end
|
|
12
14
|
end
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
constraints api_version_check(:version => 2) do
|
|
17
|
-
scope :module => :v2 do
|
|
18
|
-
cache_resources :as => :v2 do
|
|
19
|
-
resources :foo
|
|
20
|
-
inherit_resources :from => :v1
|
|
21
|
-
end
|
|
22
|
-
end
|
|
16
|
+
version 3 do
|
|
17
|
+
inherit from: 'v2'
|
|
23
18
|
end
|
|
24
19
|
end
|
|
25
20
|
end
|
|
@@ -60,11 +60,18 @@ describe 'API Routing' do
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
it "should default" do
|
|
63
|
-
original_version = ApiVersions::
|
|
64
|
-
ApiVersions::
|
|
63
|
+
original_version = ApiVersions::VersionCheck.default_version
|
|
64
|
+
ApiVersions::VersionCheck.default_version = 2
|
|
65
65
|
merge_and_stub new_api_foo_path, 'get', 'Accept' => 'application/vnd.myvendor+json'
|
|
66
66
|
expect(get: new_api_foo_path).to route_to(controller: 'api/v2/foo', action: 'new')
|
|
67
|
-
ApiVersions::
|
|
67
|
+
ApiVersions::VersionCheck.default_version = original_version
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe "V3" do
|
|
72
|
+
it "should copy foo" do
|
|
73
|
+
merge_and_stub new_api_bar_path, 'get', 'Accept' => 'application/vnd.myvendor+json;version=3'
|
|
74
|
+
expect(get: new_api_bar_path).to route_to(controller: 'api/v3/bar', action: 'new')
|
|
68
75
|
end
|
|
69
76
|
end
|
|
70
77
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: api-versions
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-08-
|
|
12
|
+
date: 2012-08-30 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rspec-rails
|
|
@@ -41,11 +41,13 @@ files:
|
|
|
41
41
|
- Rakefile
|
|
42
42
|
- api-versions.gemspec
|
|
43
43
|
- lib/api-versions.rb
|
|
44
|
+
- lib/api-versions/dsl.rb
|
|
44
45
|
- lib/api-versions/version.rb
|
|
45
|
-
-
|
|
46
|
+
- lib/api-versions/version_check.rb
|
|
46
47
|
- spec/dummy/app/controllers/api/v1/bar_controller.rb
|
|
47
48
|
- spec/dummy/app/controllers/api/v2/bar_controller.rb
|
|
48
49
|
- spec/dummy/app/controllers/api/v2/foo_controller.rb
|
|
50
|
+
- spec/dummy/app/controllers/api/v3/bar_controller.rb
|
|
49
51
|
- spec/dummy/app/controllers/application_controller.rb
|
|
50
52
|
- spec/dummy/config.ru
|
|
51
53
|
- spec/dummy/config/application.rb
|
|
@@ -81,10 +83,10 @@ signing_key:
|
|
|
81
83
|
specification_version: 3
|
|
82
84
|
summary: Allows you to cache routing and then inherit it in other modules.
|
|
83
85
|
test_files:
|
|
84
|
-
- spec/dummy/.rspec
|
|
85
86
|
- spec/dummy/app/controllers/api/v1/bar_controller.rb
|
|
86
87
|
- spec/dummy/app/controllers/api/v2/bar_controller.rb
|
|
87
88
|
- spec/dummy/app/controllers/api/v2/foo_controller.rb
|
|
89
|
+
- spec/dummy/app/controllers/api/v3/bar_controller.rb
|
|
88
90
|
- spec/dummy/app/controllers/application_controller.rb
|
|
89
91
|
- spec/dummy/config.ru
|
|
90
92
|
- spec/dummy/config/application.rb
|
data/spec/dummy/.rspec
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
--color
|