Permalink
Browse files

fix broken delegation

  • Loading branch information...
rkh committed May 1, 2011
1 parent c562619 commit ad9b819824277df19a6bb74893d783438ecc9d52
Showing with 189 additions and 10 deletions.
  1. +5 −0 CHANGES
  2. +27 −8 lib/sinatra/base.rb
  3. +3 −2 sinatra.gemspec
  4. +154 −0 test/delegator_test.rb
View
@@ -1,3 +1,8 @@
+= 1.2.6 / 2011-05-01
+
+ * Fix broken delegation, backport delegation tests from Sinatra 1.3.
+ (Konstantin Haase)
+
= 1.2.5 / 2011-04-30
* Restore compatibility with Ruby 1.8.6. (Konstantin Haase)
View
@@ -7,7 +7,7 @@
require 'tilt'
module Sinatra
- VERSION = '1.2.5'
+ VERSION = '1.2.6'
# The request object. See Rack::Request for more info:
# http://rack.rubyforge.org/doc/classes/Rack/Request.html
@@ -1465,13 +1465,26 @@ def self.register(*extensions, &block) #:nodoc:
# methods to be delegated to the Sinatra::Application class. Used primarily
# at the top-level.
module Delegator #:nodoc:
+ TEMPLATE = <<-RUBY
+ def %1$s(*args, &b)
+ return super if respond_to? :%1$s
+ ::Sinatra::Delegator.target.send("%2$s", *args, &b)
+ end
+ RUBY
+
def self.delegate(*methods)
methods.each do |method_name|
- define_method(method_name) do |*args|
- # On Ruby 1.8.6, blocks cannot take block arguments directly.
- block = Proc.new if block_given?
- return super(*args, &block) if respond_to? method_name
- ::Sinatra::Application.send(method_name, *args, &block)
+ # Replaced with way shorter and better implementation in 1.3.0
+ # using define_method instead, however, blocks cannot take block
+ # arguments on 1.8.6.
+ begin
+ code = TEMPLATE % [method_name, method_name]
+ eval code, binding, '(__DELEGATE__)', 1
+ rescue SyntaxError
+ code = TEMPLATE % [:_delegate, method_name]
+ eval code, binding, '(__DELEGATE__)', 1
+ alias_method method_name, :_delegate
+ undef_method :_delegate
end
private method_name
end
@@ -1481,6 +1494,12 @@ def self.delegate(*methods)
:before, :after, :error, :not_found, :configure, :set, :mime_type,
:enable, :disable, :use, :development?, :test?, :production?,
:helpers, :settings
+
+ class << self
+ attr_accessor :target
+ end
+
+ self.target = Application
end
# Create a new Sinatra application. The block is evaluated in the new app's
@@ -1493,11 +1512,11 @@ def self.new(base=Base, options={}, &block)
# Extend the top-level DSL with the modules provided.
def self.register(*extensions, &block)
- Application.register(*extensions, &block)
+ Delegator.target.register(*extensions, &block)
end
# Include the helper modules provided in Sinatra's request context.
def self.helpers(*extensions, &block)
- Application.helpers(*extensions, &block)
+ Delegator.target.helpers(*extensions, &block)
end
end
View
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.name = 'sinatra'
- s.version = '1.2.5'
- s.date = '2011-04-30'
+ s.version = '1.2.6'
+ s.date = '2011-05-01'
s.description = "Classy web-development dressed in a DSL"
s.summary = "Classy web-development dressed in a DSL"
@@ -40,6 +40,7 @@ Gem::Specification.new do |s|
test/builder_test.rb
test/coffee_test.rb
test/contest.rb
+ test/delegator_test.rb
test/encoding_test.rb
test/erb_test.rb
test/erubis_test.rb
View
@@ -0,0 +1,154 @@
+class DelegatorTest < Test::Unit::TestCase
+ class Mirror
+ attr_reader :last_call
+ def method_missing(*a, &b)
+ @last_call = [*a.map(&:to_s)]
+ @last_call << b if b
+ end
+ end
+
+ def self.delegates(name)
+ it "delegates #{name}" do
+ m = mirror { send name }
+ assert_equal m.last_call, [name.to_s]
+ end
+
+ it "delegates #{name} with arguments" do
+ m = mirror { send name, "foo", "bar" }
+ assert_equal m.last_call, [name.to_s, "foo", "bar"]
+ end
+
+ it "delegates #{name} with block" do
+ block = proc { }
+ m = mirror { send(name, &block) }
+ assert_equal m.last_call, [name.to_s, block]
+ end
+ end
+
+ setup do
+ @target_was = Sinatra::Delegator.target
+ end
+
+ def teardown
+ Sinatra::Delegator.target = @target_was
+ end
+
+ def delegation_app(&block)
+ mock_app { Sinatra::Delegator.target = self }
+ delegate(&block)
+ end
+
+ def mirror(&block)
+ mirror = Mirror.new
+ Sinatra::Delegator.target = mirror
+ delegate(&block)
+ end
+
+ def delegate(&block)
+ assert Sinatra::Delegator.target != Sinatra::Application
+ Object.new.extend(Sinatra::Delegator).instance_eval(&block) if block
+ Sinatra::Delegator.target
+ end
+
+ def target
+ Sinatra::Delegator.target
+ end
+
+ it 'defaults to Sinatra::Application as target' do
+ assert_equal Sinatra::Delegator.target, Sinatra::Application
+ end
+
+ %w[get put post delete options].each do |verb|
+ it "delegates #{verb} correctly" do
+ delegation_app do
+ send verb, '/hello' do
+ 'Hello World'
+ end
+ end
+
+ request = Rack::MockRequest.new(@app)
+ response = request.request(verb.upcase, '/hello', {})
+ assert response.ok?
+ assert_equal 'Hello World', response.body
+ end
+ end
+
+ it "delegates head correctly" do
+ delegation_app do
+ head '/hello' do
+ response['X-Hello'] = 'World!'
+ 'remove me'
+ end
+ end
+
+ request = Rack::MockRequest.new(@app)
+ response = request.request('HEAD', '/hello', {})
+ assert response.ok?
+ assert_equal 'World!', response['X-Hello']
+ assert_equal '', response.body
+ end
+
+ it "registers extensions with the delegation target" do
+ app, mixin = mirror, Module.new
+ Sinatra.register mixin
+ assert_equal app.last_call, ["register", mixin.to_s ]
+ end
+
+ it "registers helpers with the delegation target" do
+ app, mixin = mirror, Module.new
+ Sinatra.helpers mixin
+ assert_equal app.last_call, ["helpers", mixin.to_s ]
+ end
+
+ it "should work with method_missing proxies for options" do
+ mixin = Module.new do
+ def respond_to?(method, *)
+ method.to_sym == :options or super
+ end
+
+ def method_missing(method, *args, &block)
+ return super unless method.to_sym == :options
+ {:some => :option}
+ end
+ end
+
+ value = nil
+ mirror do
+ extend mixin
+ value = options
+ end
+
+ assert_equal({:some => :option}, value)
+ end
+
+ it "delegates crazy method names" do
+ Sinatra::Delegator.delegate "foo:bar:"
+ method = mirror { send "foo:bar:" }.last_call.first
+ assert_equal "foo:bar:", method
+ end
+
+ delegates 'get'
+ delegates 'patch'
+ delegates 'put'
+ delegates 'post'
+ delegates 'delete'
+ delegates 'head'
+ delegates 'options'
+ delegates 'template'
+ delegates 'layout'
+ delegates 'before'
+ delegates 'after'
+ delegates 'error'
+ delegates 'not_found'
+ delegates 'configure'
+ delegates 'set'
+ delegates 'mime_type'
+ delegates 'enable'
+ delegates 'disable'
+ delegates 'use'
+ delegates 'development?'
+ delegates 'test?'
+ delegates 'production?'
+ delegates 'helpers'
+ delegates 'settings'
+end

0 comments on commit ad9b819

Please sign in to comment.