Module: CushionDefaults::ClassMethods

Defined in:
lib/cushion_defaults/class_methods.rb

Overview

A series of class methods to be plopped into any class that includes CushionDefaults.

Instance Attribute Summary collapse

Mass Default Setting Methods collapse

Reader/Writer collapse

Freeze/Thaw Defaults collapse

Pushy/Polite Defaults collapse

Instance Method Summary collapse

Instance Attribute Details

#defaultsObject

Reader for the @defaults@ @DefaultsHash@.



8
9
10
# File 'lib/cushion_defaults/class_methods.rb', line 8

def defaults
  @defaults
end

#descendentsObject (readonly)

Reader for the @defaults@ @DefaultsHash@.



8
9
10
# File 'lib/cushion_defaults/class_methods.rb', line 8

def descendents
  @descendents
end

Instance Method Details

#bang_reader(*syms) ⇒ Object

Identical to #cushion_reader with one important exception: after determining the default value, @bang_reader@ goes on to crystallize this default value for the instance.

This is especially useful with proc cushions, as the below examples make clear.

Note that this method is equivalent to calling @instance.crystallize_default(:sym)@.

Note also, finally, that bang_readers can exist and function even if no #cushion_writers are defined.

Examples:

Simple Example #1: Flowers for cushion_reader

class Flower
    include CushionDefaults
    self.defaults[:he_loves_me] = proc { (Time.now.sec % 2).zero? }
    cushion :he_loves_me
end

daisy = Flower.new

daisy.he_loves_me  # true

# slight delay...
daisy.he_loves_me  # false

# slight delay...
daisy.he_loves_me! # true

5.times { daisy.he_loves_me } # [true, true, true, true, true]

Simple Example #2: Wake Up Little Susie

class Dreamer
    include CushionDefaults
    self.defaults[:time_awoken] = proc { Time.now }
    cushion :time_awoken
 end

little_susie = Dreamer.new

little_susie.time_awoken  # returns Time.now, but doesn't set @time_awoken
little_susie.time_awoken! # sets @time_awoken to Time.now

little_susie.time_awoken  # returns the @time_awoken set in the line above
little_susie.time_awoken! # returns @time_awoken and does not set it to a new value

Parameters:

  • syms (*#to_sym)

    instance variables that should have @bang_readers@

See Also:



227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/cushion_defaults/class_methods.rb', line 227

def bang_reader(*syms)
  syms.each do |sym|
    sym = sym.to_sym
    bang_sym = "#{sym}!".to_sym
    if self_or_parent_instance_method?(bang_sym)
      CushionDefaults.log("#{self} or a parent class already has a bang method #{bang_sym}", :warn)
    end
    define_method(bang_sym) do
      crystallize_default(sym)
    end
    CushionDefaults.log("bang_reader #{bang_sym} established for #{self}")
  end
end

#cushion(*syms) ⇒ Object

Sets up both #cushion_readers and #cushion_writers for @syms@. Will set up #bang_readers if Configuration#bang_things_up is true.

Parameters:



380
381
382
383
384
# File 'lib/cushion_defaults/class_methods.rb', line 380

def cushion(*syms)
  cushion_reader(*syms)

  cushion_writer(*syms)
end

#cushion_defaultsObject

Defines #cushion_readers and #cushion_writers for all of this class’ @defaults@.

Only defines @cushion_readers@ and @cushion_writers@ for this class defaults. All other getters are assumed to have been defined further up the class tree.

Thus, if class @A@ defines the default @var1@, and class @B@ defines the default @var2@, calling this method within class @B@ will only generate a getter for @var2@.

See also:

  • #cushion

  • #cushion_readers_for_defaults



408
409
410
411
# File 'lib/cushion_defaults/class_methods.rb', line 408

def cushion_defaults
  cushion *defaults.keys
  CushionDefaults.log("cushions established for #{self}'s defaults': #{defaults.keys.join(', ')}", :info)
end

#cushion_reader(*syms) ⇒ Object

Sets up a cushion_reader for each :sym in @syms@.

Each reader method checks if its instance variable (:sym) is defined. If it is, it returns that. If not, it returns the default value.

The only exception to this rule is when we have pushy defaults. If a default is pushy, we return it regardless of what this object’s instance variable may or may not be.

Note that if the default responds to :call (a proc, e.g.), then the default is instead called with the instance variable and the symbolic representation of the default requested. This allows for proc cushions.

Will set up #bang_readers if Configuration#bang_things_up is true.

The readers are named according to the same format as @attr_reader@.

Parameters:

  • syms (*#to_sym)

    instance variables that should have @cushion_readers@

See Also:



106
107
108
109
110
# File 'lib/cushion_defaults/class_methods.rb', line 106

def cushion_reader(*syms)
  # send it with force==true
  syms.each { |sym| _update_cushion_reader(sym.to_sym, true) }
  bang_reader *syms if CushionDefaults.conf.bang_things_up
end

#cushion_readers_for_defaultsObject

Defines #cushion_readers for all of this class’ @defaults@.

Only defines @cushion_readers@ for defaults specified for this class. All other readers are assumed to have been defined further up the class tree.

Thus, if class @A@ defines the default @var1@, and class @B@ defines the default @var2@, calling this method within class @B@ will only generate a reader for @var2@.



393
394
395
# File 'lib/cushion_defaults/class_methods.rb', line 393

def cushion_readers_for_defaults
  cushion_reader *defaults.keys
end

#cushion_writer(*syms) ⇒ Object

Sets up a cushion_writer for each :sym in @syms@.

Each writer method carries out one of several checks.

First, it sees if the default is nil or an empty string, and if we care whether this is the case. If CushionDefaults::Configuration#ignore_attempts_to_set_nil is true, we will not set the instance variable to nil; and correspondingly, if CushionDefaults::Configuration#blank_str_is_nil is true, we will not set the instance variable to ”. This failure will be logged at the debug level unless CushionDefaults::Configuration#whiny_ignores, in which case it will be logged at warn.

Assuming the above checks pass, we next check whether the key we’re interested in is pushy. If it is, it warns us that the new value will have no effect since it will be overridden by a pushy default.

Finally, assuming it was not flagged as nilish, the variable is set to the new value.

The writers are named according to the same format as @attr_writer@.



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/cushion_defaults/class_methods.rb', line 261

def cushion_writer(*syms)
  syms.each do |sym|
    method_name = "#{sym}=".to_sym
    if self_or_parent_instance_method?(method_name)
      CushionDefaults.log("#{self} or a parent class already has what looks like a setter method for #{sym}", :warn)
    end

    instance_variable_sym = "@#{sym}".to_sym

    defaults_cache = @defaults

    define_method(method_name) do |y|
      if CushionDefaults.nilish? y
        if CushionDefaults.conf.whiny_ignores
          CushionDefaults.log("You are attempting to set a nilish value for #{sym}. This will not be recorded, and any value set will be deleted.", :warn)
        end
      else
        if CushionDefaults.conf.we_have_a_pushy? && defaults_cache.pushy?(sym)
          CushionDefaults.log("You are setting a value for #{sym}, but this is a pushy default and this value will not be returned by any cushion_readers.", :warn)
        end
        instance_variable_set(instance_variable_sym, y)
      end
    end
    CushionDefaults.log("cushion_writer #{sym}= established for #{self}")
  end
end

#deep_freeze_default(*syms) ⇒ Object

In addition to preventing the @default@ from being set to a new value (see #freeze_default), also freezes the value to which @default@ is currently set.

Does not throw an error when you attempt to freeze an already-frozen default, but it does log at warning level.

Equivalent to calling: @freeze_default :sym; defaults.freeze@



451
452
453
454
455
456
457
# File 'lib/cushion_defaults/class_methods.rb', line 451

def deep_freeze_default(*syms)
  syms.each do |sym|
    sym = sym.to_sym
    freeze_default sym
    defaults[sym].freeze
  end
end

#deep_freeze_defaultsObject

Calls #deep_freeze_default for all defaults.



462
463
464
# File 'lib/cushion_defaults/class_methods.rb', line 462

def deep_freeze_defaults
  deep_freeze_default *defaults.keys
end

#defaults_from_yaml(file_name = nil) ⇒ Object

Load in the defaults for this class from a YAML file. If @file_name@ is specified, this YAML file is loaded. Otherwise, CushionDefaults::Configuration#yaml_file_for is evaluated for the current class. By default, the yaml file for @Klass@ is expected to be at @config/cushion_defaults/klass.yaml@.

Parameters:

  • file_name (String) (defaults to: nil)

    name of the YAML file to be loaded in for this class

See Also:



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/cushion_defaults/class_methods.rb', line 44

def defaults_from_yaml(file_name = nil)
  if file_name
    yaml_path = "#{CushionDefaults.conf.yaml_source_full_path}#{file_name}.yaml"
  else
    yaml_path = CushionDefaults.conf.yaml_file_for(self)
  end

  yaml = YAML::load(File.open(yaml_path)) rescue {}

  if yaml.empty?
    CushionDefaults.log("No YAML configuration for class #{self} found at #{yaml_path}", CushionDefaults.conf.whiny_yaml ? :warn : :debug)
  end

  initialize_defaults_hash
  log_str = "New defaults added for #{self}:#{' [none]' if yaml.empty?}"
  # If automatic readers and writers are enabled, this will set them up as a consequence.
  yaml.each do |key, val|
    log_str << "\n\t\t#{key}: #{val}"
    @defaults[key.to_sym] = val
  end
  CushionDefaults.log(log_str, :info)
end

#freeze_default(*syms) ⇒ Object

Note:

A frozen default can still be overridden lower in the class hierarchy.

Prevents a default from being set to a new value. Freezing a default is permanent in the life of the program.

Note that while a frozen default is guaranteed to maintain its identity, its attributes can still be modified (e.g., by bang! methods). To prevent any modification to a default value, call #deep_freeze_default.

Does not throw an error when you attempt to freeze an already-frozen default, but it does log at warning level.

See Also:

  • #thaw_default(sym)


426
427
428
429
430
431
432
433
434
# File 'lib/cushion_defaults/class_methods.rb', line 426

def freeze_default(*syms)
  syms.each do |sym|
    sym = sym.to_sym
    unless defaults.freeze_default! sym
      # It returns nil if the key was already present in the Set
      CushionDefaults.log("Cannot freeze #{sym}: it was already frozen for #{self}", :warn)
    end
  end
end

#freeze_defaultsObject

Calls #freeze_default for all defaults.



439
440
441
# File 'lib/cushion_defaults/class_methods.rb', line 439

def freeze_defaults
  freeze_default *defaults.keys
end

#inherited(inheritor) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Ensure that if class @Klass@ includes @CushionDefaults@, then any class that subclasses @Klass@ will include it as well.

Called automatically whenever any class that includes @CushionDefaults@ is inherited.



557
558
559
560
561
# File 'lib/cushion_defaults/class_methods.rb', line 557

def inherited(inheritor)
  inheritor.send :include, CushionDefaults
  @descendents << inheritor
  super inheritor
end

#initialize_defaults_hashObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Either set up or wipe @defaults. Should not usually be called directly.



69
70
71
72
73
74
75
76
# File 'lib/cushion_defaults/class_methods.rb', line 69

def initialize_defaults_hash
  if @defaults
    # We need to maintain the identity of the hash, as child classes may have stored a reference to it
    @defaults.clear
  else
    @defaults = DefaultsHash.new(self)
  end
end

#make_polite(*syms) ⇒ Object

Declare each sym to be polite. A polite default operates as normal. This is useful to override an ancestor class’ pushy declaration.

This will automatically create #cushion_reader‘s for all variables made polite.



510
511
512
513
514
515
# File 'lib/cushion_defaults/class_methods.rb', line 510

def make_polite(*syms)
  syms.each do |sym|
    @defaults.not_pushy!(sym)
    _update_cushion_reader(sym, true)
  end
end

#make_pushy(*syms) ⇒ Object

Declare each sym to be pushy. Pushy defaults will be returned by #cushion_readers regardless of what the instance variables are set to.

This will automatically create #cushion_reader‘s for all variables made pushy.



499
500
501
502
503
504
# File 'lib/cushion_defaults/class_methods.rb', line 499

def make_pushy(*syms)
  syms.each do |sym|
    @defaults.pushy!(sym)
    _update_cushion_reader(sym, true)
  end
end

#remove_bang(sym) ⇒ Object

Deprecated.
Note:

This method will delete any method of the form @sym!@, not just bang readers.

Undefines any bang method for sym in this class (if present). Does not affect ancestor classes.

Parameters:

  • sym (#to_sym)

    instance variable to delete bang method for



315
316
317
318
319
320
321
322
# File 'lib/cushion_defaults/class_methods.rb', line 315

def remove_bang(sym)
  sym = "#{sym}!".to_sym
  if self_has_method?(sym)
    undef_method(sym)
    @have_bang_readers.delete(sym)
    CushionDefaults.log("bang reader #{sym} removed from #{self}", :info)
  end
end

#remove_cushion_bang(sym) ⇒ Object

Note:

This method will delete any method of the form @sym!@, not just bang readers.

Undefines any bang method for sym in this class (if present). Does not affect ancestor classes.

Parameters:

  • sym (#to_sym)

    instance variable to delete bang method for



327
328
329
330
331
332
333
# File 'lib/cushion_defaults/class_methods.rb', line 327

def remove_cushion_bang(sym)
  sym = "#{sym}!".to_sym
  if @have_bang_readers.delete?(sym) && self_has_method?(sym)
    undef_method(sym)
    CushionDefaults.log("bang reader #{sym} removed from #{self}", :info)
  end
end

#remove_cushion_reader(sym) ⇒ Object

Undefines the cushion_reader for sym in this class (if present). Does not affect ancestor classes.

Parameters:

  • sym (#to_sym)

    instance variable to delete cushion_reader for



303
304
305
306
307
308
309
# File 'lib/cushion_defaults/class_methods.rb', line 303

def remove_cushion_reader(sym)
  sym = sym.to_sym
  if @have_cushion_readers.delete?(sym) && self_has_method?(sym)
    undef_method(sym)
    CushionDefaults.log("cushion_reader #{sym} removed from #{self}", :info)
  end
end

#remove_cushion_writer(sym) ⇒ Object

Note:

This method will delete any method of the format ‘sym=’, not just cushion_writers.

Undefines any writer for sym in this class (if present). Does not affect ancestor classes.

Parameters:

  • sym (#to_sym)

    instance variable to delete writer for



351
352
353
354
355
356
357
# File 'lib/cushion_defaults/class_methods.rb', line 351

def remove_cushion_writer(sym)
  write_sym = "#{sym}=".to_sym
  if @have_cushion_writers.delete?(write_sym) && self_has_method?(write_sym)
    undef_method(write_sym)
    CushionDefaults.log("cushion_writer #{sym}= removed from #{self}", :info)
  end
end

#remove_reader(sym) ⇒ Object

Deprecated.
Note:

This method will delete any method of the form @sym@, not just cushion_readers.

Undefines the reader for sym in this class (if present). Does not affect ancestor classes.

Parameters:

  • sym (#to_sym)

    instance variable to delete reader for



292
293
294
295
296
297
298
299
# File 'lib/cushion_defaults/class_methods.rb', line 292

def remove_reader(sym)
  sym = sym.to_sym
  if self_has_method?(sym)
    undef_method(sym)
    @have_cushion_readers.delete(sym)
    CushionDefaults.log("cushion_reader #{sym} removed from #{self}", :info)
  end
end

#remove_writer(sym) ⇒ Object

Deprecated.
Note:

This method will delete any method of the format ‘sym=’, not just cushion_writers.

Undefines any writer for sym in this class (if present). Does not affect ancestor classes.

Parameters:

  • sym (#to_sym)

    instance variable to delete writer for



339
340
341
342
343
344
345
346
# File 'lib/cushion_defaults/class_methods.rb', line 339

def remove_writer(sym)
  write_sym = "#{sym}=".to_sym
  if self_has_method?(write_sym)
    undef_method(write_sym)
    @have_cushion_writers.delete(write_sym)
    CushionDefaults.log("cushion_writer #{sym}= removed from #{self}", :info)
  end
end

#set_default(key, val) ⇒ Object

Equivalent to @@defaults = val@

Parameters:

  • key (#to_sym)

    key for new default.

  • val (Object)

    Default value for @key@.



32
33
34
# File 'lib/cushion_defaults/class_methods.rb', line 32

def set_default(key, val)
  @defaults[key.to_sym] = val
end

#thaw_default(*syms) ⇒ Object

Thaws a frozen default, allowing it to be set to a new value.

Note that if the value of the @default@ is itself frozen (using #deep_freeze_default or @Object#freeze@), thaw_default allows you to set @default@ to a new value but does not and cannot allow you to modify the value to which @default@ points until and unless @default@ is set to a new value.

Does not throw an error when you attempt to thaw a non-frozen default, but it does log at warning level.



475
476
477
478
479
480
481
482
483
484
# File 'lib/cushion_defaults/class_methods.rb', line 475

def thaw_default(*syms)
  syms.each do |sym|
    sym = sym.to_sym
    unless defaults.thaw_default! sym
      # It returns nil if the key was not present in the Set
      CushionDefaults.log("Cannot thaw #{sym}: it was already thawed for #{self}", :warn)
    end
    CushionDefaults.log("After thawing, currently frozen defaults are #{defaults.frozen_defaults.to_a.join(', ')}")
  end
end

#thaw_defaultsObject

Calls #thaw_default for all defaults.



489
490
491
# File 'lib/cushion_defaults/class_methods.rb', line 489

def thaw_defaults
  thaw_default *defaults.keys
end