Introduce class_attribute to declare inheritable class attributes. Writing an attribute on a subclass behaves just like overriding the superclass reader method. Unifies and replaces most usage of cattr_accessor, class_inheritable_attribute, superclass_delegating_attribute, and extlib_inheritable_attribute.
This commit is contained in:
parent
2092351652
commit
8ae25a8e41
@ -1,5 +1,7 @@
|
||||
*Rails 3.0 (pending)*
|
||||
|
||||
* Introduce class_attribute to declare inheritable class attributes. Writing an attribute on a subclass behaves just like overriding the superclass reader method. Unifies and replaces most usage of cattr_accessor, class_inheritable_attribute, superclass_delegating_attribute, and extlib_inheritable_attribute. [Jeremy Kemper, Yehuda Katz]
|
||||
|
||||
* Time#- with a DateTime argument behaves the same as with a Time argument, i.e. returns the difference between self and arg as a Float #3476 [Geoff Buesing]
|
||||
|
||||
* YAML serialization for OrderedHash. #3608 [Gregor Schmidt]
|
||||
|
36
activesupport/lib/active_support/core_ext/class/attribute.rb
Normal file
36
activesupport/lib/active_support/core_ext/class/attribute.rb
Normal file
@ -0,0 +1,36 @@
|
||||
require 'active_support/core_ext/object/metaclass'
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
|
||||
class Class
|
||||
# Declare a class-level attribute whose value is inheritable and
|
||||
# overwritable by subclasses:
|
||||
#
|
||||
# class Base
|
||||
# class_attribute :setting
|
||||
# end
|
||||
#
|
||||
# class Subclass < Base
|
||||
# end
|
||||
#
|
||||
# Base.setting = true
|
||||
# Subclass.setting # => true
|
||||
# Subclass.setting = false
|
||||
# Subclass.setting # => false
|
||||
# Base.setting # => true
|
||||
#
|
||||
# This matches normal Ruby method inheritance: think of writing an attribute
|
||||
# on a subclass as overriding the reader method.
|
||||
#
|
||||
# For convenience, a query method is defined as well:
|
||||
#
|
||||
# Subclass.setting? # => false
|
||||
def class_attribute(*attrs)
|
||||
attrs.each do |attr|
|
||||
metaclass.send(:define_method, attr) { }
|
||||
metaclass.send(:define_method, "#{attr}?") { !!send(attr) }
|
||||
metaclass.send(:define_method, "#{attr}=") do |value|
|
||||
metaclass.send(:define_method, attr) { value }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
47
activesupport/test/core_ext/class/attribute_test.rb
Normal file
47
activesupport/test/core_ext/class/attribute_test.rb
Normal file
@ -0,0 +1,47 @@
|
||||
require 'abstract_unit'
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
|
||||
class ClassAttributeTest < ActiveSupport::TestCase
|
||||
class Base
|
||||
class_attribute :setting
|
||||
end
|
||||
|
||||
class Subclass < Base
|
||||
end
|
||||
|
||||
def setup
|
||||
@klass = Class.new { class_attribute :setting }
|
||||
@sub = Class.new(@klass)
|
||||
end
|
||||
|
||||
test 'defaults to nil' do
|
||||
assert_nil @klass.setting
|
||||
assert_nil @sub.setting
|
||||
end
|
||||
|
||||
test 'inheritable' do
|
||||
@klass.setting = 1
|
||||
assert_equal 1, @sub.setting
|
||||
end
|
||||
|
||||
test 'overridable' do
|
||||
@sub.setting = 1
|
||||
assert_nil @klass.setting
|
||||
|
||||
@klass.setting = 2
|
||||
assert_equal 1, @sub.setting
|
||||
|
||||
assert_equal 1, Class.new(@sub).setting
|
||||
end
|
||||
|
||||
test 'query method' do
|
||||
assert_equal false, @klass.setting?
|
||||
@klass.setting = 1
|
||||
assert_equal true, @klass.setting?
|
||||
end
|
||||
|
||||
test 'no instance delegates' do
|
||||
assert_raise(NoMethodError) { @klass.new.setting }
|
||||
assert_raise(NoMethodError) { @klass.new.setting? }
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user