class CushionDefaults::DefaultsHash
Slight expansion of @Hash@.
Records a link to @owner@ and will traverse up the chain of owners looking for a default value for @x@ if no default value for @x@ is specified.
In general, a program should treat this class like a normal Hash, with two salient differences:
-
The {DefaultsHash#+} method has been overridden.
-
The {DefaultsHash#has_ish_key?}, which returns true if this or a parent has the key specified.
Attributes
Set of defaults frozen
Set of defaults marked as polite.
Set of defaults marked as pushy.
Public Class Methods
@param owner [Class] class for which this DefaultsHash
holds the defaults.
# File lib/cushion_defaults/defaults_hash.rb, line 23 def initialize(owner) self.default_proc = proc { |_hash, key| super_defaults[key] } # look in the superclass' @defaults@ hash, if a key is not found in this class' @defaults@ hash @owner = owner @pushy_defaults = Set.new @polite_defaults = Set.new @frozen_defaults = Set.new end
Public Instance Methods
Allows addition of hashes. Works as expected.
Note that this also enables +=.
If @key@ is in @self@ and @y@, then the value of @y@ will replace that in @self@ in the resulting @Hash@.
@note This method was incorrectly programmed to modify self in place. In a future release, this will be changed,
and the current behavior should not be depended upon.
@param y [Hash] hash whose key/values are to be copied over @return [DefaultsHash] self
# File lib/cushion_defaults/defaults_hash.rb, line 45 def +(y) y.each do |key, val| if has_key? key CushionDefaults.log("As #{@owner} already has the default #{key} defined, this call to + is overwriting it.", :info) end self[key] = val end self end
Custom key/value set method. Prevents writing a default when a parent has it marked as pushy (and it is not otherwise marked as polite), and it also tells @owner to add methods as needed (if writers or readers are to be automatically added).
@raise [FrozenDefaultError] if key is frozen
# File lib/cushion_defaults/defaults_hash.rb, line 60 def []=(key,val) key = key.to_sym if !@polite_defaults.include?(key) && pushy_in_parent?(key) raise ArgumentError, 'You cannot set a default value marked as pushy in a parent class without first marking it as polite.' end if has_key?(key) && default_frozen?(key) raise FrozenDefaultError.new(@owner, key), "#{@owner}.defaults[:#{key} is frozen!" end unless has_ish_key?(key) add_methods_as_needed(key) end super(key, val) end
Tell owner to add readers and writers for a newly-added key, as configured. @param key [#to_sym] name of new default @see Configuration#update_readers
@see Configuration#update_writers
@see ClassMethods#cushion
# File lib/cushion_defaults/defaults_hash.rb, line 231 def add_methods_as_needed(key) @owner.send :_update_cushion_reader, key @owner.cushion_writer key.to_sym if CushionDefaults.conf.update_writers end
Custom clear method. We need to check whether or not we should delete the methods associated with the keys, and we need to wipe @pushy_defaults and @polite_defaults.
# File lib/cushion_defaults/defaults_hash.rb, line 90 def clear keys.each do |key| remove_methods_as_needed(key.to_sym) @owner.send :_cascade_polite_symbol, key if @pushy_defaults.delete? key @owner.send :_cascade_pushy_symbol, key if @pushy_defaults.delete? key end @frozen_defaults.clear super CushionDefaults.log("All defaults cleared for #{@owner}.", :info) end
@return [boolean] true if the current default (not its value) is frozen, false otherwise.
# File lib/cushion_defaults/defaults_hash.rb, line 136 def default_frozen? key key = key.to_sym @frozen_defaults.include? key end
Custom delete method. We need to check whether or not we should delete the methods associated with the key, and we need to remove the key from @pushy_defaults and @polite_defaults if it exists.
# File lib/cushion_defaults/defaults_hash.rb, line 76 def delete(key) key = key.to_sym return unless has_key? key remove_methods_as_needed(key) #TODO Need some better way to handle when @pushy_defaults is emptied or cleared. Need to talk to children. @owner.send :_cascade_polite_symbol, key if @pushy_defaults.delete? key @owner.send :_cascade_pushy_symbol, key if @polite_defaults.delete? key @frozen_defaults.delete(key) super(key) CushionDefaults.log("Default for #{key} in #{@owner} deleted.") end
Freezes the specified default (not its value).
@see {#thaw_default!} @see {ClassMethods#freeze_default} @see {ClassMethods#deep_freeze_default} @return [Set] @frozen_defaults if key was not already present, nil otherwise
# File lib/cushion_defaults/defaults_hash.rb, line 147 def freeze_default! key @frozen_defaults.add? key end
Determine if this @DefaultsHash@ “ish” has a key. In other words, whether it or any Hashes up the chain of @super_defaults@ has the key @key@.
@see ish_keys
# File lib/cushion_defaults/defaults_hash.rb, line 105 def has_ish_key?(key) # Obviously, this method gives much better performance than calling @ish_keys.include?(key)@. if has_key?(key) true # super_defaults could theoretically be either a DefaultsHash or a regular Hash elsif super_defaults if super_defaults.respond_to? :has_ish_key? super_defaults.has_ish_key?(key) else super_defaults.has_key?(key) end else false end end
Returns the keys of this class’ @defaults@ as well as those of parent classes (if extant).
@see has_ish_key?
# File lib/cushion_defaults/defaults_hash.rb, line 124 def ish_keys if super_defaults # super_defaults could be either a DefaultsHash or a regular Hash (keys + (super_defaults.respond_to?(:ish_keys) ? super_defaults.ish_keys : super_defaults.keys)).uniq else keys end end
Mark sym as not pushy / polite.
# File lib/cushion_defaults/defaults_hash.rb, line 174 def not_pushy!(sym) @pushy_defaults.delete(sym) @polite_defaults.add(sym) @owner.send :_cascade_polite_symbol, sym # we may need now to define a reader for it @owner.send :_update_cushion_reader, sym CushionDefaults.log("#{sym} in #{@owner} is now polite.") end
Inverse of {#pushy?}
# File lib/cushion_defaults/defaults_hash.rb, line 207 def not_pushy?(sym) !pushy?(sym) end
Mark sym as pushy
# File lib/cushion_defaults/defaults_hash.rb, line 163 def pushy!(sym) CushionDefaults.conf.we_have_a_pushy! @pushy_defaults.add(sym) @polite_defaults.delete(sym) @owner.send :_cascade_pushy_symbol, sym CushionDefaults.log(":#{sym} in #{@owner} is now pushy.") end
Determine whether this or a parent defaults hash has declared sym to be pushy.
# File lib/cushion_defaults/defaults_hash.rb, line 186 def pushy?(sym) if @polite_defaults.include?(sym) # white list has priority false elsif @pushy_defaults.include?(sym) true else pushy_in_parent?(sym) end end
Determine whether a parent defaults hash has declared sym to be pushy.
# File lib/cushion_defaults/defaults_hash.rb, line 198 def pushy_in_parent?(sym) if super_defaults.respond_to? :pushy? super_defaults.pushy?(sym) else false end end
Tell owner to remove methods for a newly-deleted key, as configured. @param key [#to_sym] name of deleted default @see Configuration#update_readers
@see Configuration#update_writers
@see ClassMethods#remove_reader
@see ClassMethods#remove_writer
# File lib/cushion_defaults/defaults_hash.rb, line 242 def remove_methods_as_needed(key) @owner.remove_cushion_reader key.to_sym if CushionDefaults.conf.update_readers @owner.remove_bang_reader key.to_sym if CushionDefaults.conf.update_readers @owner.remove_cushion_writer key.to_sym if CushionDefaults.conf.update_writers end
Thaws the specified default (not its value).
@see {#freeze_default!} @see {ClassMethods#thaw_default} @return [Set] @frozen_defaults if key was present, nil otherwise
# File lib/cushion_defaults/defaults_hash.rb, line 156 def thaw_default! key @frozen_defaults.delete? key end
Returns the nearest class that has a default set for sym, or nil if no default is set for it.
# File lib/cushion_defaults/defaults_hash.rb, line 214 def where_is_that_default_again(sym) if has_key? sym @owner elsif super_defaults.respond_to? :where_is_that_default_again super_defaults.where_is_that_default_again sym else nil end end
Protected Instance Methods
Return (unless cached) a reference to the superclass’ @defaults@ hash.
# File lib/cushion_defaults/defaults_hash.rb, line 251 def super_defaults @super_defaults ||= @owner.superclass.respond_to?(:defaults) ? @owner.superclass.defaults : {} end