Small but powerful change: Strategy options now inherit from a default, are Mashes, and can be declaratively configured.

This commit is contained in:
Michael Bleigh 2011-09-26 11:44:17 -05:00
parent 98bc2b9a4a
commit 53bc3f5741
4 changed files with 117 additions and 4 deletions

View File

@ -74,6 +74,12 @@ module OmniAuth
self.mock_auth[provider.to_sym] = mock
end
# This is a convenience method to be used by strategy authors
# so that they can add special cases to the camelization utility
# method that allows OmniAuth::Builder to work.
#
# @param name [String] The underscored name, e.g. `oauth`
# @param camelized [String] The properly camelized name, e.g. 'OAuth'
def add_camelization(name, camelized)
self.camelizations[name.to_s] = camelized.to_s
end

View File

@ -1,4 +1,5 @@
require 'omniauth'
require 'hashie/mash'
module OmniAuth
class NoSessionError < StandardError; end
@ -12,12 +13,58 @@ module OmniAuth
base.class_eval do
attr_reader :app, :name, :env, :options, :response
end
base.extend ClassMethods
end
module ClassMethods
# Returns an inherited set of default options set at the class-level
# for each strategy.
def default_options
return @default_options if @default_options
existing = superclass.respond_to?(:default_options) ? superclass.default_options : {}
@default_options = OmniAuth::Strategy::Options.new(existing)
end
# This allows for more declarative subclassing of strategies by allowing
# default options to be set using a simple configure call.
#
# @param options [Hash] If supplied, these will be the default options (deep-merged into the superclass's default options).
# @yield [Options] The options Mash that allows you to set your defaults as you'd like.
#
# @example Using a yield to configure the default options.
#
# class MyStrategy
# include OmniAuth::Strategy
#
# configure do |c|
# c.foo = 'bar'
# end
# end
#
# @example Using a hash to configure the default options.
#
# class MyStrategy
# include OmniAuth::Strategy
# configure foo: 'bar'
# end
def configure(options = nil)
yield default_options and return unless options
default_options.deep_merge!(options)
end
end
# Initializes the strategy by passing in the Rack endpoint,
# the unique URL segment name for this strategy, and any
# additional arguments. An `options` hash is automatically
# created from the last argument if it is a hash.
#
# @param app [Rack application] The application on which this middleware is applied.
# @param name [String] A unique URL segment to describe this particular strategy. For example, `'openid'`.
# @yield [Strategy] Yields itself for block-based configuration.
def initialize(app, name, *args, &block)
@app = app
@name = name.to_sym
@options = args.last.is_a?(Hash) ? args.pop : {}
@name = name.to_s
@options = self.class.default_options.deep_merge(args.last.is_a?(Hash) ? args.pop : {})
yield self if block_given?
end
@ -26,10 +73,17 @@ module OmniAuth
"#<#{self.class.to_s}>"
end
# Duplicates this instance and runs #call! on it.
# @param [Hash] The Rack environment.
def call(env)
dup.call!(env)
end
# The logic for dispatching any additional actions that need
# to be taken. For instance, calling the request phase if
# the request path is recognized.
#
# @param env [Hash] The Rack environment.
def call!(env)
raise OmniAuth::NoSessionError.new("You must provide a session to use OmniAuth.") unless env['rack.session']
@ -75,6 +129,8 @@ module OmniAuth
callback_phase
end
# Returns true if the environment recognizes either the
# request or callback path.
def on_auth_path?
on_request_path? || on_callback_path?
end
@ -95,6 +151,9 @@ module OmniAuth
request.request_method == 'OPTIONS'
end
# This is called in lieu of the normal request process
# in the event that OmniAuth has been configured to be
# in test mode.
def mock_call!(env)
return mock_request_call if on_request_path?
return mock_callback_call if on_callback_path?
@ -115,7 +174,7 @@ module OmniAuth
def mock_callback_call
setup_phase
mocked_auth = OmniAuth.mock_auth_for(name.to_sym)
mocked_auth = OmniAuth.mock_auth_for(name.to_s)
if mocked_auth.is_a?(Symbol)
fail!(mocked_auth)
else
@ -126,6 +185,10 @@ module OmniAuth
end
end
# The setup phase looks for the `:setup` option to exist and,
# if it is, will call either the Rack endpoint supplied to the
# `:setup` option or it will call out to the setup path of the
# underlying application. This will default to `/auth/:provider/setup`.
def setup_phase
if options[:setup].respond_to?(:call)
options[:setup].call(env)
@ -183,7 +246,7 @@ module OmniAuth
end
def auth_hash
AuthHash.new(:provider => name.to_s)
AuthHash.new(:provider => name)
end
def full_host
@ -238,5 +301,7 @@ module OmniAuth
OmniAuth.config.on_failure.call(self.env)
end
class Options < Hashie::Mash; end
end
end

View File

@ -29,6 +29,37 @@ end
describe OmniAuth::Strategy do
let(:app){ lambda{|env| [404, {}, ['Awesome']]}}
describe '.default_options' do
it 'should be inherited from a parent class' do
superklass = Class.new
superklass.send :include, OmniAuth::Strategy
superklass.configure do |c|
c.foo = 'bar'
end
klass = Class.new(superklass)
klass.default_options.foo.should == 'bar'
end
end
describe '.configure' do
subject { klass = Class.new; klass.send :include, OmniAuth::Strategy; klass }
it 'should take a block and allow for default options setting' do
subject.configure do |c|
c.wakka = 'doo'
end
subject.default_options.should == {"wakka" => "doo"}
end
it 'should take a hash and deep merge it' do
subject.configure :abc => {:def => 123}
subject.configure :abc => {:hgi => 456}
subject.default_options.should == {'abc' => {'def' => 123, 'hgi' => 456}}
end
end
describe '#initialize' do
context 'options extraction' do
it 'should be the last argument if the last argument is a Hash' do
@ -38,6 +69,11 @@ describe OmniAuth::Strategy do
it 'should be a blank hash if none are provided' do
ExampleStrategy.new(app, 'test').options.should == {}
end
it 'should be the default options if any are provided' do
ExampleStrategy.stub!(:default_options).and_return(OmniAuth::Strategy::Options.new(:abc => 123))
ExampleStrategy.new(app, 'test').options.abc.should == 123
end
end
end

View File

@ -11,6 +11,12 @@ describe OmniAuth do
end
context 'configuration' do
describe '.defaults' do
it 'should be a hash of default configuration' do
OmniAuth::Configuration.defaults.should be_kind_of(Hash)
end
end
it 'should be callable from .configure' do
OmniAuth.configure do |c|
c.should be_kind_of(OmniAuth::Configuration)