aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/lib/active_support/callbacks.rb2
-rw-r--r--activesupport/lib/active_support/configurable.rb24
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb23
-rw-r--r--activesupport/lib/active_support/core_ext/hash/keys.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/module/attr_internal.rb19
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_param.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/uri.rb8
-rw-r--r--activesupport/lib/active_support/memoizable.rb2
-rw-r--r--activesupport/lib/active_support/ordered_options.rb18
-rw-r--r--activesupport/test/callbacks_test.rb23
-rw-r--r--activesupport/test/configurable_test.rb18
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb12
-rw-r--r--activesupport/test/core_ext/module/attr_accessor_with_default_test.rb2
-rw-r--r--activesupport/test/memoizable_test.rb14
15 files changed, 163 insertions, 30 deletions
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index d811c3b2f0..0c1d46c7ec 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -482,7 +482,7 @@ module ActiveSupport
chain.delete_if {|c| c.matches?(type, filter) }
end
- options[:prepend] ? chain.unshift(*mapped) : chain.push(*mapped)
+ options[:prepend] ? chain.unshift(*(mapped.reverse)) : chain.push(*mapped)
end
end
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
index 5b85f9394a..58ed37b018 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -9,9 +9,29 @@ module ActiveSupport
module Configurable
extend ActiveSupport::Concern
+ class Configuration < ActiveSupport::InheritableOptions
+ def compile_methods!
+ self.class.compile_methods!(keys.reject {|key| respond_to?(key)})
+ end
+
+ # compiles reader methods so we don't have to go through method_missing
+ def self.compile_methods!(keys)
+ keys.each do |key|
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{key}; _get(#{key.inspect}); end
+ RUBY
+ end
+ end
+ end
+
module ClassMethods
def config
- @_config ||= ActiveSupport::InheritableOptions.new(superclass.respond_to?(:config) ? superclass.config : {})
+ @_config ||= if superclass.respond_to?(:config)
+ superclass.config.inheritable_copy
+ else
+ # create a new "anonymous" class that will host the compiled reader methods
+ Class.new(Configuration).new
+ end
end
def configure
@@ -48,7 +68,7 @@ module ActiveSupport
# user.config.level # => 1
#
def config
- @_config ||= ActiveSupport::InheritableOptions.new(self.class.config)
+ @_config ||= self.class.config.inheritable_copy
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb
index 688cba03db..5414b3a18f 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute.rb
@@ -72,11 +72,21 @@ class Class
remove_possible_method(:#{name})
define_method(:#{name}) { val }
end
+
+ if singleton_class?
+ class_eval do
+ remove_possible_method(:#{name})
+ def #{name}
+ defined?(@#{name}) ? @#{name} : singleton_class.#{name}
+ end
+ end
+ end
val
end
+ remove_method :#{name} if method_defined?(:#{name})
def #{name}
- defined?(@#{name}) ? @#{name} : singleton_class.#{name}
+ defined?(@#{name}) ? @#{name} : self.class.#{name}
end
def #{name}?
@@ -87,4 +97,15 @@ class Class
attr_writer name if instance_writer
end
end
+
+ private
+ def singleton_class?
+ # in case somebody is crazy enough to overwrite allocate
+ allocate = Class.instance_method(:allocate)
+ # object.class always points to a real (non-singleton) class
+ allocate.bind(self).call.class != self
+ rescue TypeError
+ # MRI/YARV/JRuby all disallow creating new instances of a singleton class
+ true
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb
index 045a6944fa..d8748b1138 100644
--- a/activesupport/lib/active_support/core_ext/hash/keys.rb
+++ b/activesupport/lib/active_support/core_ext/hash/keys.rb
@@ -35,11 +35,13 @@ class Hash
# as keys, this will fail.
#
# ==== Examples
- # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
- # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
+ # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
+ # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key: name"
# { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
def assert_valid_keys(*valid_keys)
- unknown_keys = keys - [valid_keys].flatten
- raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
+ valid_keys.flatten!
+ each_key do |k|
+ raise(ArgumentError, "Unknown key: #{k}") unless valid_keys.include?(k)
+ end
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb b/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
index bfedbbb256..0b6731883c 100644
--- a/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
+++ b/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
@@ -18,14 +18,13 @@ class Module
#
# attr_accessor_with_default(:element_name) { name.underscore }
#
- def attr_accessor_with_default(sym, default = nil, &block)
- raise 'Default value or block required' unless !default.nil? || block
- define_method(sym, block_given? ? block : Proc.new { default })
+ def attr_accessor_with_default(sym, default = Proc.new)
+ define_method(sym, block_given? ? default : Proc.new { default })
module_eval(<<-EVAL, __FILE__, __LINE__ + 1)
- def #{sym}=(value) # def age=(value)
- class << self; attr_reader :#{sym} end # class << self; attr_reader :age end
- @#{sym} = value # @age = value
- end # end
+ def #{sym}=(value) # def age=(value)
+ class << self; attr_accessor :#{sym} end # class << self; attr_accessor :age end
+ @#{sym} = value # @age = value
+ end # end
EVAL
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/attr_internal.rb b/activesupport/lib/active_support/core_ext/module/attr_internal.rb
index 28bc30ae26..00db75bfec 100644
--- a/activesupport/lib/active_support/core_ext/module/attr_internal.rb
+++ b/activesupport/lib/active_support/core_ext/module/attr_internal.rb
@@ -1,16 +1,12 @@
class Module
# Declares an attribute reader backed by an internally-named instance variable.
def attr_internal_reader(*attrs)
- attrs.each do |attr|
- module_eval "def #{attr}() #{attr_internal_ivar_name(attr)} end", __FILE__, __LINE__
- end
+ attrs.each {|attr_name| attr_internal_define(attr_name, :reader)}
end
# Declares an attribute writer backed by an internally-named instance variable.
def attr_internal_writer(*attrs)
- attrs.each do |attr|
- module_eval "def #{attr}=(v) #{attr_internal_ivar_name(attr)} = v end", __FILE__, __LINE__
- end
+ attrs.each {|attr_name| attr_internal_define(attr_name, :writer)}
end
# Declares an attribute reader and writer backed by an internally-named instance
@@ -29,4 +25,15 @@ class Module
def attr_internal_ivar_name(attr)
Module.attr_internal_naming_format % attr
end
+
+ def attr_internal_define(attr_name, type)
+ internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, '')
+ class_eval do # class_eval is necessary on 1.9 or else the methods a made private
+ # use native attr_* methods as they are faster on some Ruby implementations
+ send("attr_#{type}", internal_name)
+ end
+ attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
+ alias_method attr_name, internal_name
+ remove_method internal_name
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb
index f2e7c2351e..ecb2bca82c 100644
--- a/activesupport/lib/active_support/core_ext/object/to_param.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_param.rb
@@ -35,7 +35,8 @@ end
class Hash
# Converts a hash into a string suitable for use as a URL query string. An optional <tt>namespace</tt> can be
- # passed to enclose the param names (see example below).
+ # passed to enclose the param names (see example below). The string pairs "key=value" that conform the query
+ # string are sorted lexicographically in ascending order.
#
# ==== Examples
# { :name => 'David', :nationality => 'Danish' }.to_param # => "name=David&nationality=Danish"
@@ -44,6 +45,6 @@ class Hash
def to_param(namespace = nil)
collect do |key, value|
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
- end * '&'
+ end.sort * '&'
end
end
diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb
index b7fe0a6209..ee991e3439 100644
--- a/activesupport/lib/active_support/core_ext/uri.rb
+++ b/activesupport/lib/active_support/core_ext/uri.rb
@@ -20,3 +20,11 @@ if RUBY_VERSION >= '1.9'
end
end
end
+
+module URI
+ class << self
+ def parser
+ @parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb
index 9fb506fea1..0a7bcd5bb8 100644
--- a/activesupport/lib/active_support/memoizable.rb
+++ b/activesupport/lib/active_support/memoizable.rb
@@ -95,6 +95,8 @@ module ActiveSupport
#
if private_method_defined?(#{original_method.inspect}) # if private_method_defined?(:_unmemoized_mime_type)
private #{symbol.inspect} # private :mime_type
+ elsif protected_method_defined?(#{original_method.inspect}) # elsif protected_method_defined?(:_unmemoized_mime_type)
+ protected #{symbol.inspect} # protected :mime_type
end # end
EOS
end
diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb
index 37e357552c..124e1a74f8 100644
--- a/activesupport/lib/active_support/ordered_options.rb
+++ b/activesupport/lib/active_support/ordered_options.rb
@@ -18,6 +18,9 @@ require 'active_support/ordered_hash'
#
module ActiveSupport #:nodoc:
class OrderedOptions < OrderedHash
+ alias_method :_get, :[] # preserve the original #[] method
+ protected :_get # make it protected
+
def []=(key, value)
super(key.to_sym, value)
end
@@ -36,8 +39,19 @@ module ActiveSupport #:nodoc:
end
class InheritableOptions < OrderedOptions
- def initialize(parent)
- super() { |h,k| parent[k] }
+ def initialize(parent = nil)
+ if parent.kind_of?(OrderedOptions)
+ # use the faster _get when dealing with OrderedOptions
+ super() { |h,k| parent._get(k) }
+ elsif parent
+ super() { |h,k| parent[k] }
+ else
+ super()
+ end
+ end
+
+ def inheritable_copy
+ self.class.new(self)
end
end
end
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 292383e3d8..51b28b6a43 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -149,6 +149,27 @@ module CallbacksTest
end
end
+ class AfterSaveConditionalPerson < Record
+ after_save Proc.new { |r| r.history << [:after_save, :string1] }
+ after_save Proc.new { |r| r.history << [:after_save, :string2] }
+ def save
+ run_callbacks :save
+ end
+ end
+
+ class AfterSaveConditionalPersonCallbackTest < Test::Unit::TestCase
+ def test_after_save_runs_in_the_reverse_order
+ person = AfterSaveConditionalPerson.new
+ person.save
+ assert_equal [
+ [:after_save, :string2],
+ [:after_save, :string1]
+ ], person.history
+ end
+ end
+
+
+
class ConditionalPerson < Record
# proc
before_save Proc.new { |r| r.history << [:before_save, :proc] }, :if => Proc.new { |r| true }
@@ -352,6 +373,8 @@ module CallbacksTest
end
end
+
+
class ResetCallbackTest < Test::Unit::TestCase
def test_save_conditional_person
person = CleanPerson.new
diff --git a/activesupport/test/configurable_test.rb b/activesupport/test/configurable_test.rb
index cef67e3cf9..9c773c1944 100644
--- a/activesupport/test/configurable_test.rb
+++ b/activesupport/test/configurable_test.rb
@@ -39,4 +39,22 @@ class ConfigurableActiveSupport < ActiveSupport::TestCase
assert_equal :baz, instance.config.foo
assert_equal :bar, Parent.config.foo
end
+
+ test "configuration is crystalizeable" do
+ parent = Class.new { include ActiveSupport::Configurable }
+ child = Class.new(parent)
+
+ parent.config.bar = :foo
+ assert !parent.config.respond_to?(:bar)
+ assert !child.config.respond_to?(:bar)
+ assert !child.new.config.respond_to?(:bar)
+
+ parent.config.compile_methods!
+ assert_equal :foo, parent.config.bar
+ assert_equal :foo, child.new.config.bar
+
+ assert_respond_to parent.config, :bar
+ assert_respond_to child.config, :bar
+ assert_respond_to child.new.config, :bar
+ end
end \ No newline at end of file
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index fc8d8170a1..0f35eb9e78 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -282,7 +282,7 @@ class HashExtTest < Test::Unit::TestCase
{ :failure => "stuff", :funny => "business" }.assert_valid_keys(:failure, :funny)
end
- assert_raise(ArgumentError, "Unknown key(s): failore") do
+ assert_raise(ArgumentError, "Unknown key: failore") do
{ :failore => "stuff", :funny => "business" }.assert_valid_keys([ :failure, :funny ])
{ :failore => "stuff", :funny => "business" }.assert_valid_keys(:failure, :funny)
end
@@ -462,20 +462,24 @@ class HashExtToParamTests < Test::Unit::TestCase
assert_equal '', {}.to_param
assert_equal 'hello=world', { :hello => "world" }.to_param
assert_equal 'hello=10', { "hello" => 10 }.to_param
- assert_equal 'hello=world&say_bye=true', ActiveSupport::OrderedHash[:hello, "world", "say_bye", true].to_param
+ assert_equal 'hello=world&say_bye=true', {:hello => "world", "say_bye" => true}.to_param
end
def test_number_hash
- assert_equal '10=20&30=40&50=60', ActiveSupport::OrderedHash[10, 20, 30, 40, 50, 60].to_param
+ assert_equal '10=20&30=40&50=60', {10 => 20, 30 => 40, 50 => 60}.to_param
end
def test_to_param_hash
- assert_equal 'custom=param-1&custom2=param2-1', ActiveSupport::OrderedHash[ToParam.new('custom'), ToParam.new('param'), ToParam.new('custom2'), ToParam.new('param2')].to_param
+ assert_equal 'custom2=param2-1&custom=param-1', {ToParam.new('custom') => ToParam.new('param'), ToParam.new('custom2') => ToParam.new('param2')}.to_param
end
def test_to_param_hash_escapes_its_keys_and_values
assert_equal 'param+1=A+string+with+%2F+characters+%26+that+should+be+%3F+escaped', { 'param 1' => 'A string with / characters & that should be ? escaped' }.to_param
end
+
+ def test_to_param_orders_by_key_in_ascending_order
+ assert_equal 'a=2&b=1&c=0', ActiveSupport::OrderedHash[*%w(b 1 c 0 a 2)].to_param
+ end
end
class HashToXmlTest < Test::Unit::TestCase
diff --git a/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb b/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb
index 9494ca9ef6..b9b60c4d6d 100644
--- a/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb
+++ b/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb
@@ -26,6 +26,6 @@ class AttrAccessorWithDefaultTest < Test::Unit::TestCase
end
def test_invalid_args
- assert_raise(RuntimeError) {@target.attr_accessor_with_default :foo}
+ assert_raise(ArgumentError) {@target.attr_accessor_with_default :foo}
end
end
diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb
index b11fa8d346..bceac1315b 100644
--- a/activesupport/test/memoizable_test.rb
+++ b/activesupport/test/memoizable_test.rb
@@ -36,6 +36,13 @@ class MemoizableTest < ActiveSupport::TestCase
memoize :name, :age
+ protected
+
+ def memoize_protected_test
+ 'protected'
+ end
+ memoize :memoize_protected_test
+
private
def is_developer?
@@ -237,6 +244,13 @@ class MemoizableTest < ActiveSupport::TestCase
assert_raise(RuntimeError) { company.memoize :name }
end
+ def test_protected_method_memoization
+ person = Person.new
+
+ assert_raise(NoMethodError) { person.memoize_protected_test }
+ assert_equal "protected", person.send(:memoize_protected_test)
+ end
+
def test_private_method_memoization
person = Person.new