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 CHANGED
@@ -3,3 +3,4 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  spec/dummy/log/*
6
+ log/*
data/README.md CHANGED
@@ -2,88 +2,137 @@ API-Versions [![Build Status](https://secure.travis-ci.org/erichmenge/api-versio
2
2
  ======================================================================================================================================
3
3
 
4
4
  If you have multiple versions of an API in your Rails app, it is not very DRY to include the same resources over and over again.
5
+
6
+ Also, URL resources shouldn't change. Instead, API versions should be specified in the headers. The api-versions gem provides a nice DSL for this.
7
+
5
8
  In your Gemfile:
6
9
 
7
- gem "api-versions", "~> 0.0.4"
10
+ gem "api-versions", "~> 0.1.0"
8
11
 
9
12
  In your routes.rb file:
10
13
 
11
- namespace :api do
12
- default_api_version 1 # If no API version is specified in the Accept header, this is what will be used.
13
- api_vendor "myvendor" # For HTTP Accept Header application/vnd.myvendor+json;version=1
14
-
15
- constraints api_version_check(:version => 1) do
16
- scope :module => :v1 do
17
- cache_resources :as => :v1 do
18
- resources :authorizations
19
- end
20
- end
21
- end
22
-
23
- constraints api_version_check(:version => 2) do
24
- scope :module => :v2 do
25
- inherit_resources :from => :v1
26
- end
27
- end
28
- end
29
-
30
- rake routes outputs:
31
-
32
- api_authorizations GET /api/authorizations(.:format) api/v1/authorizations#index
33
- POST /api/authorizations(.:format) api/v1/authorizations#create
34
- new_api_authorization GET /api/authorizations/new(.:format) api/v1/authorizations#new
35
- edit_api_authorization GET /api/authorizations/:id/edit(.:format) api/v1/authorizations#edit
36
- api_authorization GET /api/authorizations/:id(.:format) api/v1/authorizations#show
37
- PUT /api/authorizations/:id(.:format) api/v1/authorizations#update
38
- DELETE /api/authorizations/:id(.:format) api/v1/authorizations#destroy
39
- GET /api/authorizations(.:format) api/v2/authorizations#index
40
- POST /api/authorizations(.:format) api/v2/authorizations#create
41
- GET /api/authorizations/new(.:format) api/v2/authorizations#new
42
- GET /api/authorizations/:id/edit(.:format) api/v2/authorizations#edit
43
- GET /api/authorizations/:id(.:format) 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
- namespace :api do
52
- default_api_version 1
53
- api_vendor "myvendor"
54
-
55
- constraints api_version_check(:version => 1) do
56
- scope :module => :v1 do
57
- cache_resources :as => :v1 do
58
- resources :authorizations, :only => [ :create ]
59
- resources :foo
60
- resources :bar
61
- end
62
- end
63
- end
64
-
65
- # Version 2 of the API has everything in Version 1, plus my_new_resource
66
- # Version 2 will cache this entire package of resources
67
- constraints api_version_check(:version => 2) do
68
- scope :module => :v2 do
69
- cache_resources :as => :v2 do
70
- resources :my_new_resource
71
- inherit_resources :from => :v1
72
- end
73
- end
74
- end
75
-
76
- # Version 3 of the API has everything in API Version 2, and by
77
- # virtue of API Version 2 having everything in Version 1, Version 3
78
- # also has everything in Version 1.
79
-
80
- constraints api_version_check(:version => 3) do
81
- scope :module => :v3 do
82
- inherit_resources :from => :v2
83
- end
84
- end
85
-
86
- end
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 = Api::Versions::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 inherit_resources(args)
5
- [*args[:from]].each do |block|
6
- @resource_cache[block].call
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
- def matches_version?(request)
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
- def unversioned?(request)
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
@@ -1,5 +1,3 @@
1
- module Api
2
- module Versions
3
- VERSION = "0.0.5"
4
- end
1
+ module ApiVersions
2
+ VERSION = "0.1.0"
5
3
  end
@@ -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
@@ -0,0 +1,2 @@
1
+ class Api::V3::BarController < ApplicationController
2
+ end
@@ -1,25 +1,20 @@
1
1
  Dummy::Application.routes.draw do
2
- namespace :api do
3
- default_api_version 1
4
- api_vendor "myvendor"
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
- constraints api_version_check(:version => 1) do
7
- scope :module => :v1 do
8
- cache_resources :as => :v1 do
9
- resources :bar
10
- end
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
- # Version 2 of the API has everything in Version 1, plus my_new_resource
15
- # Version 2 will cache this entire package of resources
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::ApiVersionCheck.api_version
64
- ApiVersions::ApiVersionCheck.api_version = 2
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::ApiVersionCheck.api_version = original_version
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.5
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-28 00:00:00.000000000 Z
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
- - spec/dummy/.rspec
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