From 8031a53abd699ef3d1db01dc1783440c7229b1cf Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Fri, 29 Jan 2010 03:00:49 -0800 Subject: superclass_delegating_accessor rewritten to serve as the base for many other kinds of accessors (step 1 of unification) --- .../core_ext/class/delegating_attributes.rb | 80 ++++++++-------------- 1 file changed, 27 insertions(+), 53 deletions(-) (limited to 'activesupport/lib/active_support') diff --git a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb index 72e0eefb0a..19382abb76 100644 --- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb @@ -1,67 +1,41 @@ require 'active_support/core_ext/object/blank' require 'active_support/core_ext/array/extract_options' +require 'active_support/core_ext/object/metaclass' class Class - def superclass_delegating_reader(*names) - class_to_stop_searching_on = superclass.name.blank? ? "Object" : superclass.name - options = names.extract_options! + def superclass_delegating_accessor(name, options = {}) + # Create private _name and _name= methods that can still be used if the public + # methods are overridden. This allows + _superclass_delegating_accessor("_#{name}") - names.each do |name| - # def self.only_reader - # if defined?(@only_reader) - # @only_reader - # elsif superclass < Object && superclass.respond_to?(:only_reader) - # superclass.only_reader - # end - # end - class_eval <<-EOS, __FILE__, __LINE__ + 1 - def self.#{name} - if defined?(@#{name}) - @#{name} - elsif superclass < #{class_to_stop_searching_on} && superclass.respond_to?(:#{name}) - superclass.#{name} - end - end - EOS + # Generate the public methods name, name=, and name? + # These methods dispatch to the private _name, and _name= methods, making them + # overridable + metaclass.send(:define_method, name) { send("_#{name}") } + metaclass.send(:define_method, "#{name}?") { !!send("_#{name}") } + metaclass.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) } - unless options[:instance_reader] == false - class_eval <<-EOS, __FILE__, __LINE__ + 1 - def #{name} # def only_reader - self.class.#{name} # self.class.only_reader - end # end - def self.#{name}? # def self.only_reader? - !!#{name} # !!only_reader - end # end - def #{name}? # def only_reader? - !!#{name} # !!only_reader - end # end - EOS - end - end + # If an instance_reader is needed, generate methods for name and name= on the + # class itself, so instances will be able to see them + define_method(name) { send("_#{name}") } if options[:instance_reader] != false + define_method("#{name}?") { !!send("#{name}") } if options[:instance_reader] != false end - def superclass_delegating_writer(*names, &block) - options = names.extract_options! +private - names.each do |name| - class_eval <<-EOS, __FILE__, __LINE__ + 1 - def self.#{name}=(value) # def self.property=(value) - @#{name} = value # @property = value - end # end - EOS + # Take the object being set and store it in a method. This gives us automatic + # inheritance behavior, without having to store the object in an instance + # variable and look up the superclass chain manually. + def _stash_object_in_method(object, method, instance_reader = true) + metaclass.send(:define_method, method) { object } + define_method(method) { object } if instance_reader + end - self.send(:"#{name}=", yield) if block_given? + def _superclass_delegating_accessor(name, options = {}) + metaclass.send(:define_method, "#{name}=") do |value| + _stash_object_in_method(value, name, options[:instance_reader] != false) end + self.send("#{name}=", nil) end - # 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. - def superclass_delegating_accessor(*names, &block) - superclass_delegating_reader(*names) - superclass_delegating_writer(*names, &block) - end end -- cgit v1.2.3