Skip to content

Commit

Permalink
first version with no test failures
Browse files Browse the repository at this point in the history
  • Loading branch information
bwillis committed Jan 28, 2015
1 parent e33ef40 commit 104443f
Show file tree
Hide file tree
Showing 37 changed files with 418 additions and 221 deletions.
67 changes: 67 additions & 0 deletions lib/generators/templates/versioncake.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
VersionCake.setup do |config|
# Set the version key that clients will send example: `X-API-VERSION: 5`
# config.version_key = 'version'

# Enable Rails versioned filename mapping
# config.rails_view_versioning = true

# Extraction Strategies
# Define how you will accept version from the request.
#
# Defaults to all:
# [:http_accept_parameter, :http_header, :request_parameter, :path_parameter, :query_parameter]
#
# Custom strategy example:
# You can create your own extraction strategy by giving a proc or class that responds to execute:
# ```
# lambda {|request| request.headers["HTTP_X_MY_VERSION"] }
# ```
# or
# ```
# class ExtractorStrategy
# def execute(request)
# request.headers["HTTP_X_MY_VERSION"]
# end
# end
# ```
# config.extraction_strategy = [:http_accept_parameter, :http_header, :request_parameter, :path_parameter, :query_parameter]

# Version when no version in present in the request. If none is
# specified then it will error?
# config.missing_version = 5

# Versioned Resources
# Define what server resources are supported, deprecated or obsolete
# Resources listed are priority based upon creation. To version all
# resources you can define a catch all at the bottom of the block.
config.resources do |r|
# r.resource uri_regex, obsolete, deprecated, supported
r.resource %r{.*}, [], [], (1..5)
end
end

# TODO: Remove!

# Versioned resources are described by a URI pattern
# versioned_resources = {
# * => {
# supported: (1..LATEST)
# }
# %r{users} => {
# obsolute: 2,
# deprecated: 3,
# supported: 4,
# rewrite_uri: 'user'
# },
# %r{user} => {
# supported: (5..LATEST)
# }
# }
#
# v1/users => 404
# v2/users => 410 (GONE)
# v3/users => 'v3/user' with warning
# v4/users => 'v4/user'
# v5/users => 404
# v5/user => 'v5/user'
# v6/user => 'v6/user'
12 changes: 12 additions & 0 deletions lib/generators/versioncake/install_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require 'rails/generators'

module Versioncake
class InstallGenerator < Rails::Generators::Base
source_root File.expand_path('../../templates', __FILE__)

desc 'Creates a Version Cake initializer in your application.'
def copy_initializer
template 'versioncake.rb', 'config/initializers/versioncake.rb'
end
end
end
26 changes: 23 additions & 3 deletions lib/versioncake.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,28 @@

require 'versioncake/exceptions'
require 'versioncake/configuration'
require 'versioncake/controller_additions'
require 'versioncake/view_additions'
require 'versioncake/versioned_request'
require 'versioncake/railtie'
require 'versioncake/version_checker'
require 'versioncake/version_context'
require 'versioncake/versioned_resource'
require 'versioncake/rack/middleware'
require 'versioncake/cli'
require 'versioncake/test_helpers'

if defined?(Rails)
require 'versioncake/controller_additions'
require 'versioncake/view_additions'
require 'versioncake/engine'
end

module VersionCake

mattr_accessor :config

self.config = VersionCake::Configuration.new

# Yield self on setup for nice config blocks
def self.setup
yield self.config
end
end
21 changes: 19 additions & 2 deletions lib/versioncake/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ class Configuration
SUPPORTED_VERSIONS_DEFAULT = (1..10)
VERSION_KEY_DEFAULT = 'api_version'

attr_reader :extraction_strategies, :supported_version_numbers
attr_accessor :default_version, :version_key
attr_reader :extraction_strategies, :supported_version_numbers, :versioned_resources
attr_accessor :missing_version, :version_key, :rails_view_versioning

def initialize
@versioned_resources = []
@version_key = VERSION_KEY_DEFAULT
@rails_view_versioning = true
self.supported_version_numbers = SUPPORTED_VERSIONS_DEFAULT
self.extraction_strategy = :query_parameter
end
Expand Down Expand Up @@ -44,5 +46,20 @@ def latest_version
@supported_version_numbers.first
end

def resources
builder = ResourceBuilder.new
yield builder
@versioned_resources = builder.resources
end
end

class ResourceBuilder
attr_reader :resources
def initialize
@resources = []
end
def resource(regex, obsolete, unsupported, supported)
@resources << VersionCake::VersionedResource.new(regex, obsolete, unsupported, supported)
end
end
end
53 changes: 25 additions & 28 deletions lib/versioncake/controller_additions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,53 @@ module VersionCake
module ControllerAdditions
extend ActiveSupport::Concern

attr_accessor :versioned_request

# set_version is the prepend filter that will determine the version of the
# requests.
included do
prepend_before_filter :set_version
prepend_before_filter :check_version!
end

# The explicit version requested by a client, this may not
# be the rendered version and may also be nil.
def requested_version
versioned_request.extracted_version
end

# The requested version by a client or if it's nil the latest or default
# version configured.
def derived_version
versioned_request.version
def request_version
@request_version ||= version_context.version
end

# A boolean check to determine if the latest version is requested.
def is_latest_version
versioned_request.is_latest_version?
def is_latest_version?
version_context.is_latest_version?
end

protected

# The current requests version information.
def versioned_request
set_version
@versioned_request
def version_context
request.env['versioncake.context']
end

# Sets the version of the request as well as several accessor variables.
# Check the version of the request and raise errors when it's invalid. Additionally,
# setup view versioning if configured.
#
# @param override_version a version number to use instead of the one extracted
# from the request
#
# @return No explicit return, but several attributes are exposed
def set_version(override_version=nil)
return if @versioned_request.present? && override_version.blank?
@versioned_request = VersionCake::VersionedRequest.new(
request,
VersionCake::Railtie.config.versioncake,
override_version
)
if !@versioned_request.is_version_supported?
raise UnsupportedVersionError.new('Unsupported version error')
def check_version!(override_version=nil)
case version_context.result
when :version_invalid, :version_too_high, :version_too_low, :unknown
raise UnsupportedVersionError.new('Unsupported version error')
when :obsolete
raise ObsoleteVersionError.new('The version given is obsolete')
when :no_version
raise MissingVersionError.new('No version was given')
end
@_lookup_context.versions = @versioned_request.supported_versions

if VersionCake.config.rails_view_versioning
@_lookup_context.versions = version_context.resource.supported_versions.map { |n| :"v#{n}" }
end
end

def set_version(version)
@request_version = version
end
end
end
Expand Down
7 changes: 7 additions & 0 deletions lib/versioncake/engine.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module VersionCake
class Engine < Rails::Engine
initializer 'version_cake.add_middleware' do |app|
app.middleware.use VersionCake::Rack::Middleware
end
end
end
4 changes: 4 additions & 0 deletions lib/versioncake/exceptions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
module VersionCake
class UnsupportedVersionError < ::ActionController::RoutingError
end
class ObsoleteVersionError < ::ActionController::RoutingError
end
class MissingVersionError < ::ActionController::RoutingError
end
end
29 changes: 29 additions & 0 deletions lib/versioncake/rack/middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module VersionCake
module Rack
class Middleware

def initialize(app)
@app = app
@versioned_resources = VersionCake.config.versioned_resources
@default_version = VersionCake.config.missing_version
@strategies = VersionCake.config.extraction_strategies
end

def call(env)
raw_request = ::Rack::Request.new env
resource = @versioned_resources.find { |resource| resource.uri.match raw_request.path }

if resource
request = VersionCake::VersionedRequest.new(raw_request, @strategies, @default_version)
request.execute
checker = VersionCake::VersionChecker.new(resource, request)
checker.execute

env['versioncake.context'] = VersionCake::VersionContext.new(request.version, resource, checker.result)
end

@app.call(env)
end
end
end
end
7 changes: 0 additions & 7 deletions lib/versioncake/railtie.rb

This file was deleted.

2 changes: 1 addition & 1 deletion lib/versioncake/strategies/extraction_strategy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def extract(request)
end

def version_key
VersionCake::Railtie.config.versioncake.version_key
VersionCake.config.version_key
end

# Execute should return a number or a numeric string if it successfully finds a version.
Expand Down
4 changes: 2 additions & 2 deletions lib/versioncake/strategies/http_accept_parameter_strategy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ module VersionCake
class HttpAcceptParameterStrategy < ExtractionStrategy

def execute(request)
if request.headers.key?("HTTP_ACCEPT") &&
match = request.headers["HTTP_ACCEPT"].match(/#{version_key}=([0-9]+)/)
if request.env.key?('HTTP_ACCEPT') &&
match = request.env['HTTP_ACCEPT'].match(/#{version_key}=([0-9]+)/)
match[1]
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/versioncake/strategies/http_header_strategy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ module VersionCake
class HttpHeaderStrategy < ExtractionStrategy

def execute(request)
if request.headers.key? "HTTP_#{version_key.upcase}"
request.headers["HTTP_#{version_key.upcase}"]
if request.env.key? "HTTP_#{version_key.upcase}"
request.env["HTTP_#{version_key.upcase}"]
end
end

Expand Down
8 changes: 6 additions & 2 deletions lib/versioncake/strategies/path_parameter_strategy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ module VersionCake
class PathParameterStrategy < ExtractionStrategy

def execute(request)
if request.path_parameters.key? version_key.to_sym
request.path_parameters[version_key.to_sym]
version = nil
request.path.split('/').find do |part|
next unless match = part.match(%r{v(?<version>\d+)})
version = match[:version]
break
end
version
end

end
Expand Down
4 changes: 2 additions & 2 deletions lib/versioncake/strategies/query_parameter_strategy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ module VersionCake
class QueryParameterStrategy < ExtractionStrategy

def execute(request)
if request.query_parameters.key? version_key.to_sym
request.query_parameters[version_key.to_sym].to_s
if request.GET.key? version_key
request.GET[version_key].to_s
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/versioncake/strategies/request_parameter_strategy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ module VersionCake
class RequestParameterStrategy < ExtractionStrategy

def execute(request)
if request.request_parameters.has_key? version_key.to_sym
request.request_parameters[version_key.to_sym]
if request.POST.has_key? version_key
request.POST[version_key]
end
end

Expand Down
20 changes: 20 additions & 0 deletions lib/versioncake/test_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module VersionCake
module TestHelpers
def set_request_version(version_number, version_status=:supported, supported_versions=[version_number])
if version_number.is_a? Symbol
version_status = version_number
if version_status == :supported
version_number = 1
else
version_number = 666
end
end

@request.env['versioncake.context'] = VersionCake::VersionContext.new(
version_number,
instance_double('VersionedResource', latest_version: supported_versions.last, supported_versions: supported_versions),
version_status
)
end
end
end
Loading

0 comments on commit 104443f

Please sign in to comment.