diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 0201362c..20f68ace 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -822,20 +822,43 @@ module Sinatra # Sets an option to the given value. If the value is a proc, # the proc will be called every time the option is accessed. - def set(option, value = (not_set = true), &block) + def set(option, value = (not_set = true), ignore_setter = false, &block) raise ArgumentError if block and !not_set - value = block if block - if value.kind_of?(Proc) - metadef(option, &value) - metadef("#{option}?") { !!__send__(option) } - metadef("#{option}=") { |val| metadef(option, &Proc.new{val}) } - elsif not_set + value, not_set = block, false if block + + if not_set raise ArgumentError unless option.respond_to?(:each) option.each { |k,v| set(k, v) } - elsif respond_to?("#{option}=") - __send__ "#{option}=", value - else - set option, Proc.new{value} + return self + end + + if respond_to?("#{option}=") and not ignore_setter + return __send__("#{option}=", value) + end + + setter = proc { |val| set option, val, true } + getter = proc { value } + + case value + when Proc + getter = value + when Symbol, Fixnum, FalseClass, TrueClass, NilClass + # we have a lot of enable and disable calls, let's optimize those + class_eval "def self.#{option}() #{value.inspect} end" + getter = nil + when Hash + setter = proc do |val| + val = value.merge val if Hash === val + set option, val, true + end + end + + (class << self; self; end).class_eval do + define_method("#{option}=", &setter) if setter + define_method(option, &getter) if getter + unless method_defined? "#{option}?" + class_eval "def #{option}?() !!#{option} end" + end end self end @@ -1212,11 +1235,6 @@ module Sinatra end end - def metadef(message, &block) - (class << self; self; end). - send :define_method, message, &block - end - public CALLERS_TO_IGNORE = [ # :nodoc: /\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code diff --git a/test/settings_test.rb b/test/settings_test.rb index 7ab20c15..5cf6391f 100644 --- a/test/settings_test.rb +++ b/test/settings_test.rb @@ -115,6 +115,32 @@ class SettingsTest < Test::Unit::TestCase assert_equal 'oops', @base.foo end + it 'merges values of multiple set calls if those are hashes' do + @base.set :foo, :a => 1 + sub = Class.new(@base) + sub.set :foo, :b => 2 + assert_equal({:a => 1, :b => 2}, sub.foo) + end + + it 'merging does not affect the superclass' do + @base.set :foo, :a => 1 + sub = Class.new(@base) + sub.set :foo, :b => 2 + assert_equal({:a => 1}, @base.foo) + end + + it 'is possible to change a value from a hash to something else' do + @base.set :foo, :a => 1 + @base.set :foo, :bar + assert_equal(:bar, @base.foo) + end + + it 'merges values with values of the superclass if those are hashes' do + @base.set :foo, :a => 1 + @base.set :foo, :b => 2 + assert_equal({:a => 1, :b => 2}, @base.foo) + end + it "sets multiple settings to true with #enable" do @base.enable :sessions, :foo, :bar assert @base.sessions