api-versions 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ spec/dummy/log/*
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.9.3
5
+
6
+ branches:
7
+ only:
8
+ - master
9
+ - develop
10
+
11
+ notifications:
12
+ email:
13
+ recipients:
14
+ - api-versions@erichmenge.com
data/Gemfile CHANGED
@@ -2,3 +2,4 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in api-versions.gemspec
4
4
  gemspec
5
+ gem 'rails'
data/README.md CHANGED
@@ -1,29 +1,30 @@
1
- API-Versions
2
- ================
1
+ API-Versions [![Build Status](https://secure.travis-ci.org/erichmenge/api-versions.png)](http://travis-ci.org/erichmenge/api-versions)
2
+ ======================================================================================================================================
3
+
3
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.
4
5
  In your Gemfile:
5
6
 
6
7
  gem "api-versions", "~> 0.0.4"
7
-
8
+
8
9
  In your routes.rb file:
9
10
 
10
11
  namespace :api do
11
12
  default_api_version 1 # If no API version is specified in the Accept header, this is what will be used.
12
13
  api_vendor "myvendor" # For HTTP Accept Header application/vnd.myvendor+json;version=1
13
-
14
+
14
15
  constraints api_version_check(:version => 1) do
15
- scope :module => :v1 do
16
+ scope :module => :v1 do
16
17
  cache_resources :as => :v1 do
17
- resources :authorizations, :only => [ :create ]
18
+ resources :authorizations
18
19
  end
19
20
  end
20
21
  end
21
-
22
+
22
23
  constraints api_version_check(:version => 2) do
23
- scope :module => :v2 do
24
+ scope :module => :v2 do
24
25
  inherit_resources :from => :v1
25
26
  end
26
- end
27
+ end
27
28
  end
28
29
 
29
30
  rake routes outputs:
@@ -50,9 +51,9 @@ A more complicated example
50
51
  namespace :api do
51
52
  default_api_version 1
52
53
  api_vendor "myvendor"
53
-
54
+
54
55
  constraints api_version_check(:version => 1) do
55
- scope :module => :v1 do
56
+ scope :module => :v1 do
56
57
  cache_resources :as => :v1 do
57
58
  resources :authorizations, :only => [ :create ]
58
59
  resources :foo
@@ -60,24 +61,24 @@ A more complicated example
60
61
  end
61
62
  end
62
63
  end
63
-
64
+
64
65
  # Version 2 of the API has everything in Version 1, plus my_new_resource
65
66
  # Version 2 will cache this entire package of resources
66
67
  constraints api_version_check(:version => 2) do
67
- scope :module => :v2 do
68
- cache_resources :as => :v2 do
68
+ scope :module => :v2 do
69
+ cache_resources :as => :v2 do
69
70
  resources :my_new_resource
70
71
  inherit_resources :from => :v1
71
72
  end
72
73
  end
73
74
  end
74
-
75
+
75
76
  # Version 3 of the API has everything in API Version 2, and by
76
77
  # virtue of API Version 2 having everything in Version 1, Version 3
77
78
  # also has everything in Version 1.
78
-
79
+
79
80
  constraints api_version_check(:version => 3) do
80
- scope :module => :v3 do
81
+ scope :module => :v3 do
81
82
  inherit_resources :from => :v2
82
83
  end
83
84
  end
data/Rakefile CHANGED
@@ -1 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new do |t|
5
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
6
+ # Put spec opts in a file named .rspec in root
7
+ end
8
+
9
+ task :default => :spec
data/api-versions.gemspec CHANGED
@@ -18,4 +18,5 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
+ s.add_development_dependency "rspec-rails"
21
22
  end
data/lib/api-versions.rb CHANGED
@@ -1,42 +1,35 @@
1
1
  require "api-versions/version"
2
2
 
3
- class Engine < Rails::Engine
4
- initializer "api_versions" do
5
- config.to_prepare { ActionDispatch::Routing::Mapper.send :include, ApiVersions }
6
- end
7
- end
8
-
9
3
  module ApiVersions
10
-
11
4
  def inherit_resources(args)
12
5
  [*args[:from]].each do |block|
13
6
  @resource_cache[block].call
14
7
  end
15
8
  end
16
-
9
+
17
10
  def cache_resources(args, &block)
18
11
  @resource_cache ||= {}
19
12
  @resource_cache.merge!(args[:as] => block)
20
13
  block.call
21
14
  end
22
-
15
+
23
16
  def api_version=(version)
24
17
  ApiVersions::ApiVersionCheck.api_version = version
25
18
  end
26
19
  alias_method :default_api_version, :api_version=
27
-
28
-
20
+
21
+
29
22
  def vendor=(vendor)
30
23
  ApiVersions::ApiVersionCheck.api_vendor = vendor
31
24
  end
32
25
  alias_method :api_vendor, :vendor=
33
-
26
+
34
27
  def api_version_check(*args)
35
28
  ApiVersions::ApiVersionCheck.new(*args)
36
29
  end
37
-
30
+
38
31
  class ApiVersionCheck
39
-
32
+
40
33
  cattr_accessor :api_version, :api_vendor
41
34
 
42
35
  def initialize(args = {})
@@ -46,11 +39,11 @@ module ApiVersions
46
39
  def matches?(request)
47
40
  accepts_proper_format?(request) && (matches_version?(request) || unversioned?(request))
48
41
  end
49
-
42
+
50
43
  private
51
44
 
52
45
  def accepts_proper_format?(request)
53
- !!(request.headers['Accept'] =~ /^application\/vnd\.#{self.class.api_vendor}\+json/)
46
+ !!(request.headers['Accept'] =~ /^application\/vnd\.#{self.class.api_vendor}\+.+/)
54
47
  end
55
48
 
56
49
  def matches_version?(request)
@@ -60,6 +53,7 @@ module ApiVersions
60
53
  def unversioned?(request)
61
54
  @process_version == self.class.api_version && !(request.headers['Accept'] =~ /version\s*?=\s*?\d*\b/i)
62
55
  end
63
-
64
56
  end
65
57
  end
58
+
59
+ ActionDispatch::Routing::Mapper.send :include, ApiVersions
@@ -1,5 +1,5 @@
1
1
  module Api
2
2
  module Versions
3
- VERSION = "0.0.4"
3
+ VERSION = "0.0.5"
4
4
  end
5
5
  end
data/spec/dummy/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,2 @@
1
+ class Api::V1::BarController < ApplicationController
2
+ end
@@ -0,0 +1,5 @@
1
+ class Api::V2::BarController < ApplicationController
2
+ def new
3
+
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ class Api::V2::FooController < ApplicationController
2
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery
3
+ end
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run Dummy::Application
@@ -0,0 +1,54 @@
1
+ require File.expand_path('../boot', __FILE__)
2
+
3
+ # Pick the frameworks you want:
4
+ require "action_controller/railtie"
5
+ # require "rails/test_unit/railtie"
6
+
7
+ Bundler.require
8
+ require "api-versions"
9
+
10
+ module Dummy
11
+ class Application < Rails::Application
12
+ # Settings in config/environments/* take precedence over those specified here.
13
+ # Application configuration should go into files in config/initializers
14
+ # -- all .rb files in that directory are automatically loaded.
15
+
16
+ # Custom directories with classes and modules you want to be autoloadable.
17
+ # config.autoload_paths += %W(#{config.root}/extras)
18
+
19
+ # Only load the plugins named here, in the order given (default is alphabetical).
20
+ # :all can be used as a placeholder for all plugins not explicitly named.
21
+ # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
22
+
23
+ # Activate observers that should always be running.
24
+ # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
25
+
26
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
27
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
28
+ # config.time_zone = 'Central Time (US & Canada)'
29
+
30
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
31
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
32
+ # config.i18n.default_locale = :de
33
+
34
+ # Configure the default encoding used in templates for Ruby 1.9.
35
+ config.encoding = "utf-8"
36
+
37
+ # Configure sensitive parameters which will be filtered from the log file.
38
+ config.filter_parameters += [:password]
39
+
40
+ # Enable escaping HTML in JSON.
41
+ config.active_support.escape_html_entities_in_json = true
42
+
43
+ # Use SQL instead of Active Record's schema dumper when creating the database.
44
+ # This is necessary if your schema can't be completely dumped by the schema dumper,
45
+ # like if you have constraints or database-specific column types
46
+ # config.active_record.schema_format = :sql
47
+
48
+ # Enforce whitelist mode for mass assignment.
49
+ # This will create an empty whitelist of attributes available for mass-assignment for all models
50
+ # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
51
+ # parameters by using an attr_accessible or attr_protected declaration.
52
+ end
53
+ end
54
+
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ gemfile = File.expand_path('../../../../Gemfile', __FILE__)
3
+
4
+ if File.exist?(gemfile)
5
+ ENV['BUNDLE_GEMFILE'] = gemfile
6
+ require 'bundler'
7
+ Bundler.setup
8
+ end
9
+
10
+ $:.unshift File.expand_path('../../../../lib', __FILE__)
@@ -0,0 +1,5 @@
1
+ # Load the rails application
2
+ require File.expand_path('../application', __FILE__)
3
+
4
+ # Initialize the rails application
5
+ Dummy::Application.initialize!
@@ -0,0 +1,29 @@
1
+ Dummy::Application.configure do
2
+ # Settings specified here will take precedence over those in config/application.rb
3
+
4
+ # The test environment is used exclusively to run your application's
5
+ # test suite. You never need to work with it otherwise. Remember that
6
+ # your test database is "scratch space" for the test suite and is wiped
7
+ # and recreated between test runs. Don't rely on the data there!
8
+ config.cache_classes = true
9
+
10
+ # Configure static asset server for tests with Cache-Control for performance
11
+ config.serve_static_assets = true
12
+ config.static_cache_control = "public, max-age=3600"
13
+
14
+ # Log error messages when you accidentally call methods on nil
15
+ config.whiny_nils = true
16
+
17
+ # Show full error reports and disable caching
18
+ config.consider_all_requests_local = true
19
+ config.action_controller.perform_caching = false
20
+
21
+ # Raise exceptions instead of rendering exception templates
22
+ config.action_dispatch.show_exceptions = false
23
+
24
+ # Disable request forgery protection in test environment
25
+ config.action_controller.allow_forgery_protection = false
26
+
27
+ # Print deprecation notices to the stderr
28
+ config.active_support.deprecation = :stderr
29
+ end
@@ -0,0 +1,25 @@
1
+ Dummy::Application.routes.draw do
2
+ namespace :api do
3
+ default_api_version 1
4
+ api_vendor "myvendor"
5
+
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
12
+ end
13
+
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
23
+ end
24
+ end
25
+ end
File without changes
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'API Routing' do
4
+ def merge_and_stub(path, method, hash)
5
+ env_hash = Rack::MockRequest.env_for(path, method: method).merge(hash)
6
+ Rack::MockRequest.stub(:env_for).and_return env_hash
7
+ end
8
+
9
+ describe "V1" do
10
+ context "when no header is set" do
11
+ it "doesn't route" do
12
+ expect(get: new_api_bar_path).to_not be_routable
13
+ end
14
+ end
15
+
16
+ context "when the header is set incorrectly" do
17
+ it "doesn't route" do
18
+ merge_and_stub new_api_bar_path, 'get', 'Accept' => 'application/vnd.mybadvendor+json;version=1'
19
+ expect(get: '/api/bar/new').to_not be_routable
20
+ end
21
+ end
22
+
23
+ context "when it is set correctly" do
24
+ it "should not route something from V2" do
25
+ merge_and_stub new_api_foo_path, 'get', 'Accept' => 'application/vnd.myvendor+json;version=1'
26
+ expect(get: new_api_foo_path).to_not be_routable
27
+ end
28
+
29
+ it "should route" do
30
+ merge_and_stub new_api_bar_path, 'get', 'Accept' => 'application/vnd.myvendor+json;version=1'
31
+ expect(get: new_api_bar_path).to route_to(controller: 'api/v1/bar', action: 'new')
32
+ end
33
+
34
+ it "should default" do
35
+ merge_and_stub new_api_bar_path, 'get', 'Accept' => 'application/vnd.myvendor+json'
36
+ expect(get: new_api_bar_path).to route_to(controller: 'api/v1/bar', action: 'new')
37
+ end
38
+
39
+ it "should default with nothing after the semi-colon" do
40
+ merge_and_stub new_api_bar_path, 'get', 'Accept' => 'application/vnd.myvendor+json; '
41
+ expect(get: new_api_bar_path).to route_to(controller: 'api/v1/bar', action: 'new')
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "V2" do
47
+ it "should copy bar" do
48
+ merge_and_stub new_api_bar_path, 'get', 'Accept' => 'application/vnd.myvendor+json;version=2'
49
+ expect(get: new_api_bar_path).to route_to(controller: 'api/v2/bar', action: 'new')
50
+ end
51
+
52
+ it "should add foo" do
53
+ merge_and_stub new_api_foo_path, 'get', 'Accept' => 'application/vnd.myvendor+json;version=2'
54
+ expect(get: new_api_foo_path).to route_to(controller: 'api/v2/foo', action: 'new')
55
+ end
56
+
57
+ it "should not default" do
58
+ merge_and_stub new_api_foo_path, 'get', 'Accept' => 'application/vnd.myvendor+json'
59
+ expect(get: new_api_foo_path).to_not be_routable
60
+ end
61
+
62
+ it "should default" do
63
+ original_version = ApiVersions::ApiVersionCheck.api_version
64
+ ApiVersions::ApiVersionCheck.api_version = 2
65
+ merge_and_stub new_api_foo_path, 'get', 'Accept' => 'application/vnd.myvendor+json'
66
+ expect(get: new_api_foo_path).to route_to(controller: 'api/v2/foo', action: 'new')
67
+ ApiVersions::ApiVersionCheck.api_version = original_version
68
+ end
69
+ end
70
+
71
+ describe "Header syntax" do
72
+ after(:each) do
73
+ expect(get: new_api_bar_path).to route_to(controller: 'api/v1/bar', action: 'new')
74
+ end
75
+
76
+ it "should allow spaces after the semi-colon" do
77
+ merge_and_stub new_api_bar_path, 'get', 'Accept' => 'application/vnd.myvendor+json; version=1'
78
+ end
79
+
80
+ it "should allow other formats besides json" do
81
+ merge_and_stub new_api_bar_path, 'get', 'Accept' => 'application/vnd.myvendor+xml; version=1'
82
+ end
83
+
84
+ it "should allow spacing around the equal sign" do
85
+ merge_and_stub new_api_bar_path, 'get', 'Accept' => 'application/vnd.myvendor+json; version = 1'
86
+ end
87
+
88
+ it "should allow spacing around the plus" do
89
+ merge_and_stub new_api_bar_path, 'get', 'Accept' => 'application/vnd.myvendor+xml; version=1'
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,23 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= 'test'
3
+ require File.expand_path("../dummy/config/environment", __FILE__)
4
+ require 'rspec/rails'
5
+ require 'rspec/autorun'
6
+
7
+ # Requires supporting ruby files with custom matchers and macros, etc,
8
+ # in spec/support/ and its subdirectories.
9
+ Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
10
+
11
+ RSpec.configure do |config|
12
+
13
+ # If true, the base class of anonymous controllers will be inferred
14
+ # automatically. This will be the default behavior in future versions of
15
+ # rspec-rails.
16
+ config.infer_base_class_for_anonymous_controllers = true
17
+
18
+ # Run specs in random order to surface order dependencies. If you find an
19
+ # order dependency and want to debug it, you can fix the order by providing
20
+ # the seed, which is printed after each run.
21
+ # --seed 1234
22
+ config.order = "random"
23
+ end
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
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-08 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2012-08-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec-rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
14
30
  description: Useful for API versioning.
15
31
  email:
16
32
  - erich.menge@me.com
@@ -19,12 +35,27 @@ extensions: []
19
35
  extra_rdoc_files: []
20
36
  files:
21
37
  - .gitignore
38
+ - .travis.yml
22
39
  - Gemfile
23
40
  - README.md
24
41
  - Rakefile
25
42
  - api-versions.gemspec
26
43
  - lib/api-versions.rb
27
44
  - lib/api-versions/version.rb
45
+ - spec/dummy/.rspec
46
+ - spec/dummy/app/controllers/api/v1/bar_controller.rb
47
+ - spec/dummy/app/controllers/api/v2/bar_controller.rb
48
+ - spec/dummy/app/controllers/api/v2/foo_controller.rb
49
+ - spec/dummy/app/controllers/application_controller.rb
50
+ - spec/dummy/config.ru
51
+ - spec/dummy/config/application.rb
52
+ - spec/dummy/config/boot.rb
53
+ - spec/dummy/config/environment.rb
54
+ - spec/dummy/config/environments/test.rb
55
+ - spec/dummy/config/routes.rb
56
+ - spec/dummy/log/.gitkeep
57
+ - spec/routing/routing_spec.rb
58
+ - spec/spec_helper.rb
28
59
  homepage: https://github.com/erichmenge/api-versions
29
60
  licenses: []
30
61
  post_install_message:
@@ -45,8 +76,22 @@ required_rubygems_version: !ruby/object:Gem::Requirement
45
76
  version: '0'
46
77
  requirements: []
47
78
  rubyforge_project: api-versions
48
- rubygems_version: 1.8.11
79
+ rubygems_version: 1.8.24
49
80
  signing_key:
50
81
  specification_version: 3
51
82
  summary: Allows you to cache routing and then inherit it in other modules.
52
- test_files: []
83
+ test_files:
84
+ - spec/dummy/.rspec
85
+ - spec/dummy/app/controllers/api/v1/bar_controller.rb
86
+ - spec/dummy/app/controllers/api/v2/bar_controller.rb
87
+ - spec/dummy/app/controllers/api/v2/foo_controller.rb
88
+ - spec/dummy/app/controllers/application_controller.rb
89
+ - spec/dummy/config.ru
90
+ - spec/dummy/config/application.rb
91
+ - spec/dummy/config/boot.rb
92
+ - spec/dummy/config/environment.rb
93
+ - spec/dummy/config/environments/test.rb
94
+ - spec/dummy/config/routes.rb
95
+ - spec/dummy/log/.gitkeep
96
+ - spec/routing/routing_spec.rb
97
+ - spec/spec_helper.rb