Ruby constants are a nice place to put application configuration information, but they can be inflexible if you want to defer initialization until later — for example, if you want a constant to have a given value at application startup only if a certain environment variable or command–line parameter is set. I like the idea of the single–assignment variables that you get in many functional and logic languages, and I also like the idea that constants only really need to be constant after they’ve been read once. (See this paper for some interesting implications of that idea in Java programs.)

The quiescent gem is a little hack I put together to make it easy to have write-once, defaultable constants in your Ruby programs. A quiescing constant, or quiescent, has an optional default value (either an explicit value or a block to calculate that value), and can be assigned to at most once in a program execution. Once the quiescent is written to, its value quiesces and it becomes a normal constant. If it is read without being explicitly quiesced, it assumes the default value (or the result of executing the default-value block) and becomes a normal constant. Here’s an example to make things clearer:

Example of the quiescent gemlink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
require 'quiescent'

class Foo
  include Quiescent

  # This declares a constant named PostCode that 
  # quiesces to a default value of 53706 unless 
  # another is provided before the first time it
  # is read
  quiescent :PostCode, 53706

  # Let's assume that the awesome features are off
  # by default.
  quiescent :EnableTotallyAwesomeFeature, false
  quiescent :EnableSlightlyLessAwesomeFeature, false

  # This declares a constant named LazyThrees that 
  # quiesces to a list of all natural numbers less than
  # 100 that are divisible by three, as calculated
  # in the block, unless another value is provided. 
  # The block argument will execute at most once.
  quiescent :LazyThrees do
    (1..100).to_a.select {|x| x % 3 == 0}
  end

  # In this method, we'll see how to force quiescents
  # to quiesce by giving them values and reading their
  # values.
  def self.setup
    # We only want to do this once
    return if @setup_done
    @setup_done = true

    puts "The postal code is #{Foo::PostCode}"

    # You can provide non-default values with the
    # quiesce method...
    Foo.quiesce(:EnableTotallyAwesomeFeature, "sometimes")

    # ...or by using a special CONSTNAME= method, which
    # will be intercepted by method_missing.
    Foo.EnableSlightlyLessAwesomeFeature = true

    # Note that this only works for names corresponding
    # to declared quiescing constants...
    begin
      Foo.EnableCrummyFeature = true
    rescue Exception
      puts("whoa, failure in aisle 47")
    end

    # ...and only once for each quiescing constant.
    begin
      Foo.EnableSlightlyLessAwesomeFeature = false
    rescue Exception
      puts("nice try, pal")
    end
  end
end

Get quiescent from GitHub or from RubyGems.

  metaprogramming, quiescing constants, ruby • You may reply to this post on Twitter or