Add new superclass_delegating_accessors. Similar to class inheritable attributes but with subtly different semantics. [Koz, tarmo]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8056 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
3ba23348de
commit
e86d1decc1
@ -1,5 +1,7 @@
|
|||||||
*SVN*
|
*SVN*
|
||||||
|
|
||||||
|
* Add new superclass_delegating_accessors. Similar to class inheritable attributes but with subtly different semantics. [Koz, tarmo]
|
||||||
|
|
||||||
* Change JSON to encode %w(< > &) as 4 digit hex codes to be in compliance with the JSON spec. Closes #9975 [josh, chuyeow, tpope]
|
* Change JSON to encode %w(< > &) as 4 digit hex codes to be in compliance with the JSON spec. Closes #9975 [josh, chuyeow, tpope]
|
||||||
|
|
||||||
* Fix JSON encoding/decoding bugs dealing with /'s. Closes #9990 [Rick, theamazingrando]
|
* Fix JSON encoding/decoding bugs dealing with /'s. Closes #9990 [Rick, theamazingrando]
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
require 'active_support/core_ext/class/attribute_accessors'
|
require 'active_support/core_ext/class/attribute_accessors'
|
||||||
require 'active_support/core_ext/class/inheritable_attributes'
|
require 'active_support/core_ext/class/inheritable_attributes'
|
||||||
require 'active_support/core_ext/class/removal'
|
require 'active_support/core_ext/class/removal'
|
||||||
|
require 'active_support/core_ext/class/delegating_attributes'
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
# These class attributes behave something like the class
|
||||||
|
# inheritable accessors. But instead of copying the hash over at
|
||||||
|
# the time the subclass is first defined, the accessors simply
|
||||||
|
# delegate to their superclass unless they have been given a
|
||||||
|
# specific value. This stops the strange situation where values
|
||||||
|
# set after class definition don't get applied to subclasses.
|
||||||
|
class Class
|
||||||
|
def superclass_delegating_reader(*names)
|
||||||
|
class_name_to_stop_searching_on = self.superclass.name.blank? ? "Object" : self.superclass.name
|
||||||
|
names.each do |name|
|
||||||
|
class_eval <<-EOS
|
||||||
|
def self.#{name}
|
||||||
|
if defined?(@#{name})
|
||||||
|
@#{name}
|
||||||
|
elsif superclass < #{class_name_to_stop_searching_on} && superclass.respond_to?(:#{name})
|
||||||
|
superclass.#{name}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
def #{name}
|
||||||
|
self.class.#{name}
|
||||||
|
end
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def superclass_delegating_writer(*names)
|
||||||
|
names.each do |name|
|
||||||
|
class_eval <<-EOS
|
||||||
|
def self.#{name}=(value)
|
||||||
|
@#{name} = value
|
||||||
|
end
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def superclass_delegating_accessor(*names)
|
||||||
|
superclass_delegating_reader(*names)
|
||||||
|
superclass_delegating_writer(*names)
|
||||||
|
end
|
||||||
|
end
|
105
activesupport/test/core_ext/class/delegating_attributes_test.rb
Normal file
105
activesupport/test/core_ext/class/delegating_attributes_test.rb
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
require File.dirname(__FILE__) + '/../../abstract_unit'
|
||||||
|
|
||||||
|
module DelegatingFixtures
|
||||||
|
class Parent
|
||||||
|
end
|
||||||
|
|
||||||
|
class Child < Parent
|
||||||
|
superclass_delegating_accessor :some_attribute
|
||||||
|
end
|
||||||
|
|
||||||
|
class Mokopuna < Child
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class DelegatingAttributesTest < Test::Unit::TestCase
|
||||||
|
include DelegatingFixtures
|
||||||
|
attr_reader :single_class
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@single_class = Class.new(Object)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_simple_reader_declaration
|
||||||
|
single_class.superclass_delegating_reader :only_reader
|
||||||
|
# The class and instance should have an accessor, but there
|
||||||
|
# should be no mutator
|
||||||
|
assert single_class.respond_to?(:only_reader)
|
||||||
|
assert single_class.public_instance_methods.include?("only_reader")
|
||||||
|
assert !single_class.respond_to?(:only_reader=)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_simple_writer_declaration
|
||||||
|
single_class.superclass_delegating_writer :only_writer
|
||||||
|
# The class should have a mutator, the instances shouldn't
|
||||||
|
# neither should have an accessor
|
||||||
|
assert single_class.respond_to?(:only_writer=)
|
||||||
|
assert !single_class.public_instance_methods.include?("only_writer=")
|
||||||
|
assert !single_class.public_instance_methods.include?("only_writer")
|
||||||
|
assert !single_class.respond_to?(:only_writer)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_simple_accessor_declaration
|
||||||
|
single_class.superclass_delegating_accessor :both
|
||||||
|
# Class should have accessor and mutator
|
||||||
|
# the instance should have an accessor only
|
||||||
|
assert single_class.respond_to?(:both)
|
||||||
|
assert single_class.respond_to?(:both=)
|
||||||
|
assert single_class.public_instance_methods.include?("both")
|
||||||
|
assert !single_class.public_instance_methods.include?("both=")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_working_with_simple_attributes
|
||||||
|
single_class.superclass_delegating_accessor :both
|
||||||
|
single_class.both= "HMMM"
|
||||||
|
assert_equal "HMMM", single_class.both
|
||||||
|
assert_equal "HMMM", single_class.new.both
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_working_with_accessors
|
||||||
|
single_class.superclass_delegating_reader :only_reader
|
||||||
|
single_class.instance_variable_set("@only_reader", "reading only")
|
||||||
|
assert_equal "reading only", single_class.only_reader
|
||||||
|
assert_equal "reading only", single_class.new.only_reader
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_working_with_simple_mutators
|
||||||
|
single_class.superclass_delegating_writer :only_writer
|
||||||
|
single_class.only_writer="written"
|
||||||
|
assert_equal "written", single_class.instance_variable_get("@only_writer")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_child_class_delegates_to_parent_but_can_be_overridden
|
||||||
|
parent = Class.new
|
||||||
|
parent.superclass_delegating_accessor :both
|
||||||
|
child = Class.new(parent)
|
||||||
|
parent.both= "1"
|
||||||
|
assert_equal "1", child.both
|
||||||
|
|
||||||
|
child.both="2"
|
||||||
|
assert_equal "1", parent.both
|
||||||
|
assert_equal "2", child.both
|
||||||
|
|
||||||
|
parent.both="3"
|
||||||
|
assert_equal "3", parent.both
|
||||||
|
assert_equal "2", child.both
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_delegation_stops_at_the_right_level
|
||||||
|
assert_nil Mokopuna.some_attribute
|
||||||
|
assert_nil Child.some_attribute
|
||||||
|
Child.some_attribute="1"
|
||||||
|
assert_equal "1", Mokopuna.some_attribute
|
||||||
|
ensure
|
||||||
|
Child.some_attribute=nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_delegation_stops_for_nil
|
||||||
|
Mokopuna.some_attribute = nil
|
||||||
|
Child.some_attribute="1"
|
||||||
|
|
||||||
|
assert_equal "1", Child.some_attribute
|
||||||
|
assert_nil Mokopuna.some_attribute
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user