Skip to content

Commit

Permalink
Moving configuration away from singleton pattern
Browse files Browse the repository at this point in the history
By making the configuration an instance variable it allowed more
flexibility in setting up configuration and testability. The
configuration object can be directly accessed in the app configuration
instead of using several translating setters. Testing the configuration
object is much simpler and doesn’t require resetting the state of the
object after complete testing.
  • Loading branch information
bwillis committed Feb 6, 2014
1 parent 00e2342 commit 2866bac
Show file tree
Hide file tree
Showing 17 changed files with 84 additions and 91 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Enhancements:
* New path strategy to support `/v3/posts` style versioning, thanks [Michael Elfassy](https://github.com/elfassy)
* Support Rails 4.1, thanks [Washington L Braga Jr](https://github.com/huoxito)
* Added v1->v2 template renaming migration script
* Improving configuration

## 1.3.0 (Sept 26, 2013)

Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ gem install versioncake

## Upgrade v1.0 -> v2.0

### Filename changes
The major breaking change to require a bump to v2.0 was the order of the extensions. To avoid priority issues with the format (#14), the version number and the format have been swapped.

`index.v1.json.jbuilder` -> `index.json.v1.jbuilder`
Expand All @@ -51,6 +52,17 @@ To make it easier to upgrade, run the following command to automatically rename

`verisoncake migrate` or `verisoncake migrate path/to/views`

### Configuration changes

The configuration options for Version Cake have been namespaced and slightly renamed. The following is a mapping of the old names to the new names:

| Old Name | New Name |
| --------------------------------------- |:--------------------------------------------:|
| config.view_versions | config.versioncake.supported_version_numbers |
| config.view_version_extraction_strategy | config.versioncake.extraction_strategy |
| config.view_version_string | config.versioncake.version_key |
| config.default_version | config.versioncake.default_version |

## Example

In this simple example we will outline the code that is introduced to support a change in a version.
Expand Down
42 changes: 21 additions & 21 deletions lib/versioncake/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,47 @@
require 'active_support/core_ext/array/wrap.rb'

module VersionCake
module Configuration
class Configuration

SUPPORTED_VERSIONS_DEFAULT = (1..10)
VERSION_KEY_DEFAULT = 'api_version'

mattr_reader :supported_version_numbers
attr_reader :extraction_strategies, :supported_version_numbers
attr_accessor :default_version, :version_key

mattr_accessor :extraction_strategies
self.extraction_strategies = []

mattr_accessor :default_version
self.default_version = nil
def initialize
@version_key = VERSION_KEY_DEFAULT
self.supported_version_numbers = SUPPORTED_VERSIONS_DEFAULT
self.extraction_strategy = :query_parameter
end

def self.extraction_strategy=(val)
@@extraction_strategies.clear
def extraction_strategy=(val)
@extraction_strategies = []
Array.wrap(val).each do |configured_strategy|
@@extraction_strategies << VersionCake::ExtractionStrategy.lookup(configured_strategy)
@extraction_strategies << VersionCake::ExtractionStrategy.lookup(configured_strategy)
end
end

def self.supported_version_numbers=(val)
@@supported_version_numbers = val.respond_to?(:to_a) ? val.to_a : Array.wrap(val)
@@supported_version_numbers.sort!.reverse!
def supported_version_numbers=(val)
@supported_version_numbers = val.respond_to?(:to_a) ? val.to_a : Array.wrap(val)
@supported_version_numbers.sort!.reverse!
end

def self.supported_versions(requested_version_number=nil)
supported_version_numbers.collect do |supported_version_number|
def supported_versions(requested_version_number=nil)
@supported_version_numbers.collect do |supported_version_number|
if requested_version_number.nil? || supported_version_number <= requested_version_number
:"v#{supported_version_number}"
end
end
end

def self.supports_version?(version)
supported_version_numbers.include? version
def supports_version?(version)
@supported_version_numbers.include? version
end

def self.latest_version
supported_version_numbers.first
def latest_version
@supported_version_numbers.first
end

self.extraction_strategy = :query_parameter
self.supported_version_numbers = SUPPORTED_VERSIONS_DEFAULT
end
end
22 changes: 3 additions & 19 deletions lib/versioncake/railtie.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,7 @@
require 'rails'

class ActionViewVersions < Rails::Railtie
initializer "view_versions" do |app|
ActiveSupport.on_load(:action_view) do
if app.config.respond_to?(:view_versions)
VersionCake::Configuration.supported_version_numbers = app.config.view_versions
end

if app.config.respond_to?(:view_version_extraction_strategy)
VersionCake::Configuration.extraction_strategy = app.config.view_version_extraction_strategy
end

if app.config.respond_to?(:view_version_string)
VersionCake::ExtractionStrategy.version_string = app.config.view_version_string
end

if app.config.respond_to?(:default_version)
VersionCake::Configuration.default_version = app.config.default_version
end
end
module VersionCake
class Railtie < ::Rails::Railtie
config.versioncake = VersionCake::Configuration.new
end
end
12 changes: 4 additions & 8 deletions lib/versioncake/strategies/extraction_strategy.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
if defined?(ActionPack::VERSION::MAJOR) && ActionPack::VERSION::MAJOR == 4 && ActionPack::VERSION::MINOR >= 1
require 'active_support/core_ext/module/attribute_accessors'
else
require 'active_support/core_ext/class/attribute_accessors.rb'
end
require 'active_support/core_ext/string/inflections.rb'

module VersionCake
class ExtractionStrategy

cattr_accessor :version_string
@@version_string = 'api_version'

def extract(request)
version = execute(request)
return version.to_i if version && /[0-9]+/.match(version)
end

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

def execute(request)
raise Exception, "ExtractionStrategy requires execute to be implemented"
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class HttpAcceptParameterStrategy < ExtractionStrategy

def execute(request)
if request.headers.key?("HTTP_ACCEPT") &&
match = request.headers["HTTP_ACCEPT"].match(/#{@@version_string}=([0-9]+)/)
match = request.headers["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_X_#{@@version_string.upcase}"
request.headers["HTTP_X_#{@@version_string.upcase}"]
if request.headers.key? "HTTP_X_#{version_key.upcase}"
request.headers["HTTP_X_#{version_key.upcase}"]
end
end

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

def execute(request)
if request.path_parameters.key? @@version_string.to_sym
request.path_parameters[@@version_string.to_sym]
if request.path_parameters.key? version_key.to_sym
request.path_parameters[version_key.to_sym]
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_string.to_sym
request.query_parameters[@@version_string.to_sym].to_s
if request.query_parameters.key? version_key.to_sym
request.query_parameters[version_key.to_sym].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_string.to_sym
request.request_parameters[@@version_string.to_sym]
if request.request_parameters.has_key? version_key.to_sym
request.request_parameters[version_key.to_sym]
end
end

Expand Down
16 changes: 10 additions & 6 deletions lib/versioncake/versioned_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@ class VersionedRequest

def initialize(request, version_override=nil)
@version = version_override || extract_version(request)
@is_latest_version = @version == VersionCake::Configuration.latest_version
@is_latest_version = @version == config.latest_version
end

def supported_versions
VersionCake::Configuration.supported_versions(@version)
config.supported_versions(@version)
end

private

def config
VersionCake::Railtie.config.versioncake
end

def apply_strategies(request)
version = nil
VersionCake::Configuration.extraction_strategies.each do |strategy|
config.extraction_strategies.each do |strategy|
version = strategy.extract(request)
break unless version.nil?
end
Expand All @@ -25,10 +29,10 @@ def apply_strategies(request)
def extract_version(request)
@extracted_version = apply_strategies(request)
if @extracted_version.nil?
@version = VersionCake::Configuration.default_version || VersionCake::Configuration.latest_version
elsif VersionCake::Configuration.supports_version? @extracted_version
@version = config.default_version || config.latest_version
elsif config.supports_version? @extracted_version
@version = @extracted_version
elsif @extracted_version > VersionCake::Configuration.latest_version
elsif @extracted_version > config.latest_version
raise ActionController::RoutingError.new("No route match for version")
else
raise ActionController::RoutingError.new("Version is deprecated")
Expand Down
2 changes: 1 addition & 1 deletion lib/versioncake/view_additions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# register an addition detail for the lookup context to understand,
# this will allow us to have the versions available upon lookup in
# the resolver.
ActionView::LookupContext.register_detail(:versions){ VersionCake::Configuration.supported_versions }
ActionView::LookupContext.register_detail(:versions){ VersionCake::Railtie.config.versioncake.supported_versions }

ActionView::PathResolver.class_eval do
# not sure why we are doing this yet, but looks like a good idea
Expand Down
5 changes: 3 additions & 2 deletions test/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class Application < Rails::Application
config.eager_load = false

config.active_support.deprecation = :stderr
config.view_versions = (1..3)
config.view_version_extraction_strategy = [:http_header, :http_accept_parameter, :query_parameter, :request_parameter]

config.versioncake.supported_version_numbers = (1..3)
config.versioncake.extraction_strategy = [:http_header, :http_accept_parameter, :query_parameter, :request_parameter]
end
end
2 changes: 1 addition & 1 deletion test/functional/custom_strategy_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class CustomStrategyTest < ActionController::TestCase
tests RendersController

setup do
VersionCake::Configuration.stubs(:extraction_strategy => lambda { |request| 2 })
VersionCake::Configuration.any_instance.stubs(:extraction_strategy => lambda { |request| 2 })
end

test "renders version 2 of the partial based on the header Accept" do
Expand Down
7 changes: 3 additions & 4 deletions test/functional/renders_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class RendersControllerTest < ActionController::TestCase
end

test "exposes the default version when the version is not set default is set" do
VersionCake::Configuration.stubs(:default_version => 1)
VersionCake::Configuration.any_instance.stubs(:default_version => 1)
get :index
assert_equal 1, @controller.derived_version
end
Expand All @@ -52,14 +52,13 @@ class RendersControllerTest < ActionController::TestCase
end

test "responds with 404 when the version is lower than the latest version, but not an available version" do
VersionCake::Configuration.stubs(:supported_version_numbers => [2,3])
assert_raise ActionController::RoutingError do
get :index, "api_version" => "1"
get :index, "api_version" => "0"
end
end

test "render the default version version of the partial" do
VersionCake::Configuration.stubs(:default_version => 1)
VersionCake::Configuration.any_instance.stubs(:default_version => 1)
get :index, "api_version" => "abc"
assert_equal "template v1", @response.body
end
Expand Down
34 changes: 15 additions & 19 deletions test/unit/configuration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,44 @@

class ConfigurationTest < ActiveSupport::TestCase
setup do
@previous_versions = VersionCake::Configuration.supported_version_numbers
end

teardown do
VersionCake::Configuration.supported_version_numbers = @previous_versions
@config = VersionCake::Configuration.new
end

test "supported_version_numbers can be set with range" do
VersionCake::Configuration.supported_version_numbers = (1..7)
assert_equal [7,6,5,4,3,2,1], VersionCake::Configuration.supported_version_numbers
@config.supported_version_numbers = (1..7)
assert_equal [7,6,5,4,3,2,1], @config.supported_version_numbers
end

test "supported_version_numbers can be set with an unordered array" do
VersionCake::Configuration.supported_version_numbers = [2,4,1,5,3,6,7]
assert_equal [7,6,5,4,3,2,1], VersionCake::Configuration.supported_version_numbers
@config.supported_version_numbers = [2,4,1,5,3,6,7]
assert_equal [7,6,5,4,3,2,1], @config.supported_version_numbers
end

test "supported_version_numbers can be set with a single value" do
VersionCake::Configuration.supported_version_numbers = 19
assert_equal [19], VersionCake::Configuration.supported_version_numbers
@config.supported_version_numbers = 19
assert_equal [19], @config.supported_version_numbers
end

test "supports_version? is only true for given supported versions" do
VersionCake::Configuration.supported_version_numbers = (1..7)
VersionCake::Configuration.supported_version_numbers.each do |supported_version|
assert VersionCake::Configuration.supports_version? supported_version
@config.supported_version_numbers = (1..7)
@config.supported_version_numbers.each do |supported_version|
assert @config.supports_version? supported_version
end
end

test "supports_version? is not true for other versions" do
VersionCake::Configuration.supported_version_numbers = (1..7)
@config.supported_version_numbers = (1..7)
[-2,-1,0,8,9,10].each do |unsupported_version|
assert !VersionCake::Configuration.supports_version?(unsupported_version)
assert !@config.supports_version?(unsupported_version)
end
end

test "latest_version retrieves the highest supported version" do
VersionCake::Configuration.supported_version_numbers = [4,1,3,9,2,54]
assert_equal 54, VersionCake::Configuration.latest_version
@config.supported_version_numbers = [4,1,3,9,2,54]
assert_equal 54, @config.latest_version
end

test "default supported_version_numbers should be a logic set of version numbers" do
assert_equal (1..10), VersionCake::Configuration::SUPPORTED_VERSIONS_DEFAULT
assert_equal (1..10).to_a.reverse, @config.supported_version_numbers
end
end
2 changes: 1 addition & 1 deletion test/unit/versioned_request_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class VersionedRequestTest < ActiveSupport::TestCase

test "a request for a deprecated version raises an exception" do
VersionCake::VersionedRequest.any_instance.stubs(:apply_strategies => 2)
VersionCake::Configuration.stubs(:supports_version? => false)
VersionCake::Configuration.any_instance.stubs(:supports_version? => false)
assert_raise ActionController::RoutingError do
VersionCake::VersionedRequest.new(stub())
end
Expand Down

0 comments on commit 2866bac

Please sign in to comment.