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:

Attributes

frozen_defaults[R]

Set of defaults frozen

polite_defaults[R]

Set of defaults marked as polite.

pushy_defaults[R]

Set of defaults marked as pushy.

Public Class Methods

new(owner) click to toggle source

@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

+(y) click to toggle source

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
[]=(key,val) click to toggle source

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

Calls superclass method
# 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
add_methods_as_needed(key) click to toggle source

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
clear() click to toggle source

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.

Calls superclass method
# 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
default_frozen?(key) click to toggle source

@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
delete(key) click to toggle source

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.

Calls superclass method
# 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
freeze_default!(key) click to toggle source

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
has_ish_key?(key) click to toggle source

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
ish_keys() click to toggle source

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
not_pushy!(sym) click to toggle source

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
not_pushy?(sym) click to toggle source

Inverse of {#pushy?}

# File lib/cushion_defaults/defaults_hash.rb, line 207
def not_pushy?(sym)
  !pushy?(sym)
end
pushy!(sym) click to toggle source

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
pushy?(sym) click to toggle source

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
pushy_in_parent?(sym) click to toggle source

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
remove_methods_as_needed(key) click to toggle source

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
thaw_default!(key) click to toggle source

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
where_is_that_default_again(sym) click to toggle source

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

super_defaults() click to toggle source

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