Class: CushionDefaults::DefaultsHash

Inherits:
Hash
  • Object
show all
Defined in:
lib/cushion_defaults/defaults_hash.rb

Overview

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 #+ method has been overridden.

  • The #has_ish_key?, which returns true if this or a parent has the key specified.

Instance Attribute Summary collapse

Custom/New Hash Methods collapse

Frozen Defaults collapse

Pushy Defaults collapse

Debug collapse

Instance Method Summary collapse

Constructor Details

#initialize(owner) ⇒ DefaultsHash

Returns a new instance of DefaultsHash.

Parameters:

  • owner (Class)

    class for which this DefaultsHash holds the defaults.



23
24
25
26
27
28
29
30
# 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

Instance Attribute Details

#frozen_defaultsObject (readonly)

Set of defaults frozen



20
21
22
# File 'lib/cushion_defaults/defaults_hash.rb', line 20

def frozen_defaults
  @frozen_defaults
end

#polite_defaultsObject (readonly)

Set of defaults marked as polite.



18
19
20
# File 'lib/cushion_defaults/defaults_hash.rb', line 18

def polite_defaults
  @polite_defaults
end

#pushy_defaultsObject (readonly)

Set of defaults marked as pushy.



16
17
18
# File 'lib/cushion_defaults/defaults_hash.rb', line 16

def pushy_defaults
  @pushy_defaults
end

Instance Method Details

#+(y) ⇒ DefaultsHash

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.

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@.

Parameters:

  • y (Hash)

    hash whose key/values are to be copied over

Returns:



45
46
47
48
49
50
51
52
53
# 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) ⇒ Object

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).

Raises:



60
61
62
63
64
65
66
67
68
69
70
71
72
# 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) ⇒ Object

Tell owner to add readers and writers for a newly-added key, as configured.

Parameters:

  • key (#to_sym)

    name of new default

See Also:



231
232
233
234
# 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

#clearObject

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.



90
91
92
93
94
95
96
97
98
99
# 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) ⇒ boolean

Returns true if the current default (not its value) is frozen, false otherwise.

Returns:

  • (boolean)

    true if the current default (not its value) is frozen, false otherwise.



136
137
138
139
# File 'lib/cushion_defaults/defaults_hash.rb', line 136

def default_frozen? key
  key = key.to_sym
  @frozen_defaults.include? key
end

#delete(key) ⇒ Object

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.



76
77
78
79
80
81
82
83
84
85
86
# 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) ⇒ Set

Freezes the specified default (not its value).

Returns:

  • (Set)

    @frozen_defaults if key was not already present, nil otherwise

See Also:

  • CushionDefaults::DefaultsHash.{{#thaw_default!}
  • CushionDefaults::DefaultsHash.{ClassMethods{ClassMethods#freeze_default}
  • CushionDefaults::DefaultsHash.{ClassMethods{ClassMethods#deep_freeze_default}


147
148
149
# File 'lib/cushion_defaults/defaults_hash.rb', line 147

def freeze_default! key
  @frozen_defaults.add? key
end

#has_ish_key?(key) ⇒ Boolean

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@.

Returns:

  • (Boolean)

See Also:



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# 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_keysObject

Returns the keys of this class’ @defaults@ as well as those of parent classes (if extant).

See Also:



124
125
126
127
128
129
130
131
# 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) ⇒ Object

Mark sym as not pushy / polite.



174
175
176
177
178
179
180
181
182
183
# 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) ⇒ Boolean

Inverse of #pushy?

Returns:

  • (Boolean)


207
208
209
# File 'lib/cushion_defaults/defaults_hash.rb', line 207

def not_pushy?(sym)
  !pushy?(sym)
end

#pushy!(sym) ⇒ Object

Mark sym as pushy



163
164
165
166
167
168
169
170
171
# 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) ⇒ Boolean

Determine whether this or a parent defaults hash has declared sym to be pushy.

Returns:

  • (Boolean)


186
187
188
189
190
191
192
193
194
195
# 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) ⇒ Boolean

Determine whether a parent defaults hash has declared sym to be pushy.

Returns:

  • (Boolean)


198
199
200
201
202
203
204
# 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) ⇒ Object

Tell owner to remove methods for a newly-deleted key, as configured.



242
243
244
245
246
# 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) ⇒ Set

Thaws the specified default (not its value).

Returns:

  • (Set)

    @frozen_defaults if key was present, nil otherwise

See Also:

  • CushionDefaults::DefaultsHash.{{#freeze_default!}
  • CushionDefaults::DefaultsHash.{ClassMethods{ClassMethods#thaw_default}


156
157
158
# File 'lib/cushion_defaults/defaults_hash.rb', line 156

def thaw_default! key
  @frozen_defaults.delete? key
end

#where_is_that_default_again(sym) ⇒ Object

Returns the nearest class that has a default set for sym, or nil if no default is set for it.



214
215
216
217
218
219
220
221
222
# 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