versionable_api 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -6,13 +6,13 @@ VersionableApi is a small gem that helps you create versionable apis (initially
6
6
 
7
7
  The most common way to start trying to version APIs is to create URIs (and routes and controllers) that look somewhat like this:
8
8
  ```
9
- /api/v1/person.json
9
+ /api/v1/people.json
10
10
  ```
11
11
  and route that to a controller in `app/controllers/api/v1/people_controller.rb`
12
12
 
13
13
  Then, when you want to make a change to the person API, you create:
14
14
  ```
15
- /api/v2/person.json
15
+ /api/v2/people.json
16
16
  ```
17
17
  and you create `app/controllers/api/v2/people_controller.rb`.
18
18
 
@@ -20,20 +20,20 @@ But do you make `Api::V2::PeopleController` inherit from `Api::V1::PeopleControl
20
20
 
21
21
  # How VersionableApi tries to solve this problem
22
22
 
23
- `VersionableApi` proposes that you create tiny controllers and then put version-specific behavior in modules that are included in that controller. `VersionableApi` provides a module that does a tiny bit of magic to determine which "versioned" method gets called based on an HTTP Accept header and will look for lower versions of the methods in case a particular method on a controller hasn't revved yet.
23
+ `VersionableApi` proposes that you create tiny controllers and then put version-specific behavior in modules that are included in that controller. `VersionableApi` provides a module that does a tiny bit of magic to determine which "versioned" method gets called based on an HTTP Accept header.
24
24
 
25
25
  Instead of putting the version of the API you want to call in the request URI, it's specified in the Accept Header by adding `;version=X` to one of the acceptable types. The easiest way is to specify an accept type of `*/*;version=X` (where X is the version you want).
26
26
 
27
27
  # Maintaining backwards compatibility with clients who are already using the "old" URI style
28
28
 
29
- If you're transitioning an existing API to using VersionableApi and you need to be able to handle 'old' style routes (like `/api/v2/something.json`) VersionableApi provides a simple piece of Rack middleware that can help.
29
+ If you're transitioning an existing API to using VersionableApi and you need to be able to handle 'old' style routes (like `/api/v2/something.json`) `VersionableApi` provides a simple piece of Rack middleware that can help.
30
30
 
31
31
  The `VersionableApi::ApiVersionInterceptor` can intercept requests to the 'old' api style and massage them to fit your new style. You can include it by adding the following line inside your `config/application.rb` class:
32
32
  ```
33
33
  config.middleware.use "VersionableApi::ApiVersionInterceptor"
34
34
  ```
35
35
 
36
- By default, it will look for requests to paths that look like `/api/v#/something` and transform them into `/api/something` with `*/*;version=#` prepended to the HTTP_ACCEPT header and then forward the request on to your Rails app. You can configure most of how it behaves via initialization parameters if you don't like the defaults, for example:
36
+ By default, it will look for requests to paths that look like `/api/v#/something` and transform them into `/api/something` with `*/*;version=#` prepended to the `HTTP_ACCEPT` header and then forward the request on to your Rails app. You can configure most of how it behaves via initialization parameters if you don't like the defaults, for example:
37
37
  ```
38
38
  config.middleware.use "VersionableApi::ApiVersionInterceptor", {version_regex: /\/API\/version-(?<version>\d+)\/(?<path>.*)/}
39
39
  ```
@@ -87,7 +87,7 @@ the `Api::V1::People#show_v1` method will handle the request.
87
87
  ```
88
88
  GET /api/people.json {HTTP_ACCEPT: text/json;version=2}
89
89
  ```
90
- the request will get handled by the `Api::V1::People#index_v1` action. Even though the request specified that it's using Version 2, since there isn't an explicit Version 2 of the `index` action, control will fall back to the Version 1 version.
90
+ a 404 will be returned because there is no version 2 of the `index` action.
91
91
 
92
92
 
93
93
  # How to use it
@@ -1,14 +1,16 @@
1
1
  module VersionableApi
2
2
  module ApiVersioning
3
3
 
4
- # Public: The default version, classes including ApiVersioning should specify
5
- # this themselves if they don't want the default version to be 1
4
+ # Public: The default version. Classes including ApiVersioning should specify
5
+ # this themselves if they don't want the default version to be 1. If you would
6
+ # like to force the caller to specify a version in their request, override
7
+ # this method have have it return nil.
6
8
  #
7
9
  # Returns the duplicated String.
8
10
  def default_version
9
11
  1
10
12
  end
11
-
13
+
12
14
  # Public: Returns a versioned action name based on the requested action name
13
15
  # and an optional version specification on the request. If no versioned
14
16
  # action matching what we think the request is trying to access is defined on
@@ -22,18 +24,18 @@ module VersionableApi
22
24
  #
23
25
  # Returns a versioned action name, "_handle_action_missing", or nil
24
26
  def method_for_action(action)
25
- version = (requested_version || self.default_version || 1).to_i
26
- method = nil
27
- version.downto(1) do |v|
28
- name = "#{action}_v#{v}"
29
- method ||= self.respond_to?(name) ? name : nil
27
+ version = (requested_version || self.default_version)
28
+
29
+ unless version.nil?
30
+ version = version.to_i
31
+ name = "#{action}_v#{version}"
32
+ method = self.respond_to?(name) ? name : nil
30
33
  end
31
34
 
32
35
  method ||= self.respond_to?("action_missing") ? "_handle_action_missing" : nil
33
-
34
36
  end
35
37
 
36
-
38
+
37
39
  # Public: Finds the API version requested in the request
38
40
  #
39
41
  # Returns the requested version, or nil if no version was specifically requested
@@ -1,3 +1,3 @@
1
1
  module VersionableApi
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
  require 'ostruct'
3
3
 
4
4
  describe VersionableApi::ApiVersioning do
5
- class Testable
5
+ class Testable
6
6
  include VersionableApi::ApiVersioning
7
7
  attr_accessor :request
8
8
  def initialize
@@ -11,62 +11,75 @@ describe VersionableApi::ApiVersioning do
11
11
  end
12
12
  end
13
13
 
14
- before do
14
+ class TestableRequireVersion < Testable
15
+ def default_version
16
+ nil
17
+ end
18
+ end
19
+
20
+ before do
15
21
  @test_me = Testable.new
16
22
  end
17
23
 
18
- it "should have a default version of 1" do
19
- assert_equal 1, @test_me.default_version
24
+ describe "default version" do
25
+ it "should have a default version of 1" do
26
+ assert_equal 1, @test_me.default_version
27
+ end
28
+ it "should require a version from the client if default version is nil" do
29
+ @test_me = TestableRequireVersion.new
30
+ assert_nil @test_me.default_version
31
+ assert_nil @test_me.method_for_action("show")
32
+ end
20
33
  end
21
34
 
22
- describe "#requested_version" do
23
- it "should determine the requested version from the request headers" do
35
+ describe "#requested_version" do
36
+ it "should determine the requested version from the request headers" do
24
37
  @test_me.request.headers["HTTP_ACCEPT"] = "*/*;version=10"
25
38
  assert_equal 10, @test_me.requested_version
26
39
  end
27
40
 
28
- it "should return nil if no explicit version is requested" do
41
+ it "should return nil if no explicit version is requested" do
29
42
  assert_nil @test_me.requested_version
30
43
  end
31
44
 
32
- it "should return nil if the version requested is non-numeric" do
45
+ it "should return nil if the version requested is non-numeric" do
33
46
  @test_me.request.headers["HTTP_ACCEPT"] = "*/*;version=FOO"
34
47
  assert_nil @test_me.requested_version
35
48
  end
36
49
  end
37
50
 
38
- describe "#method_for_action" do
39
- before do
51
+ describe "#method_for_action" do
52
+ before do
40
53
  def @test_me.show_v1; 1; end;
41
54
  def @test_me.show_v2; 2; end;
42
55
  def @test_me.index_v1; 1; end;
43
56
  end
44
57
 
45
- it "should return the default version when no version is requested" do
58
+ it "should return the default version when no version is requested" do
46
59
  assert_equal "show_v1", @test_me.method_for_action("show")
47
60
  end
48
61
 
49
- it "should return the requested version if specified and available" do
62
+ it "should return the requested version if specified and available" do
50
63
  @test_me.request.headers["HTTP_ACCEPT"] = "*/*;version=2"
51
64
  assert_equal "show_v2", @test_me.method_for_action("show")
52
65
  @test_me.request.headers["HTTP_ACCEPT"] = "*/*;version=1"
53
66
  assert_equal "show_v1", @test_me.method_for_action("show")
54
67
  end
55
68
 
56
- it "should return the highest available version if the version specified is higher than the available versions" do
69
+ it "should return nil if the version specified is higher than the available versions" do
57
70
  @test_me.request.headers["HTTP_ACCEPT"] = "*/*;version=10"
58
- assert_equal "show_v2", @test_me.method_for_action("show")
71
+ assert_nil @test_me.method_for_action("show")
59
72
  @test_me.request.headers["HTTP_ACCEPT"] = "*/*;version=2"
60
- assert_equal "index_v1", @test_me.method_for_action("index")
73
+ assert_nil @test_me.method_for_action("index")
61
74
  end
62
75
 
63
- it "should return _handle_action_missing if #action_missing is defined and unkown action is called for" do
76
+ it "should return _handle_action_missing if #action_missing is defined and unkown action is called for" do
64
77
  def @test_me.action_missing; true; end;
65
78
  @test_me.request.headers["HTTP_ACCEPT"] = "*/*;version=2"
66
79
  assert_equal "_handle_action_missing", @test_me.method_for_action("foobar")
67
80
  end
68
81
 
69
- it "should return nil if #action_missing is not defined and an unkown action is specified" do
82
+ it "should return nil if #action_missing is not defined and an unkown action is specified" do
70
83
  @test_me.request.headers["HTTP_ACCEPT"] = "*/*;version=2"
71
84
  assert_nil @test_me.method_for_action("foobar")
72
85
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: versionable_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
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: 2013-08-29 00:00:00.000000000 Z
12
+ date: 2013-11-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -57,12 +57,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
57
57
  - - ! '>='
58
58
  - !ruby/object:Gem::Version
59
59
  version: '0'
60
+ segments:
61
+ - 0
62
+ hash: -1992996191972482845
60
63
  required_rubygems_version: !ruby/object:Gem::Requirement
61
64
  none: false
62
65
  requirements:
63
66
  - - ! '>='
64
67
  - !ruby/object:Gem::Version
65
68
  version: '0'
69
+ segments:
70
+ - 0
71
+ hash: -1992996191972482845
66
72
  requirements: []
67
73
  rubyforge_project:
68
74
  rubygems_version: 1.8.23