Class: Fluid
- Inherits:
-
Object
- Object
- Fluid
- Defined in:
- lib/fluid.rb
Overview
Fluid (dynamically scoped) variables for Ruby. See README.txt.
Defined Under Namespace
Classes: Environment, FluidVar, GlobalVar, Var
Constant Summary collapse
- Version =
"1.0.1"
- @@environment =
All fluid variables are managed by one environment.
Environment.new
Class Method Summary collapse
-
.defvar(name, value = nil) ⇒ Object
A global declaration of a fluid variable.
-
.has?(name) ⇒ Boolean
Answers whether name is already a fluid variable.
-
.let(*var_specs) ⇒ Object
Puts the variable specifications in effect, then executes the block, undoes the variable specifications, and returns the block’s value.
-
.method_missing(symbol, *args) ⇒ Object
:nodoc:.
Class Method Details
.defvar(name, value = nil) ⇒ Object
A global declaration of a fluid variable. After executing this code:
Fluid.defvar(:global, 5)
Fluid.global will normally everywhere have the value 5, unless it's
changed by assignment or Fluid.let.
However, Fluid.defvar has effect only the first time it's executed.
That is, given this sequence:
Fluid.defvar(:global, 5)
Fluid.defvar(:global, 6666666)
Fluid.global has value 5. The second Fluid.defvar is ignored.
The "a-bit-more-clever-fluid-tracing.rb" example shows how
this can be useful.
A Fluid.defvar executed while a Fluid.let block is in effect will
have no effect:
Fluid.let(:not_global, 1) {
Fluid.defvar(:not_global, 5555) # Fluid.not_global == 1
}
# The defvar has had no effect, so Fluid.not_global
# has no value after the block.
Fluid.defvar can take a block as an argument:
Fluid.defvar(:var) { long_and_expensive_computation }
The block is only executed if its value would be assigned to the
variable.
The first argument to Fluid.defvar may not be the name of
a global variable.
295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/fluid.rb', line 295 def Fluid.defvar(name, value = nil) if Var.global?(name) raise NameError, "Fluid.defvar of a global can never have an effect, so it's not allowed." end var = Var.build(name, @@environment) unless @@environment.has?(var.name) value = yield if block_given? var.push_binding(value) end end |
.has?(name) ⇒ Boolean
Answers whether name is already a fluid variable.
308 309 310 311 |
# File 'lib/fluid.rb', line 308 def Fluid.has?(name) return true if Var.global?(name) @@environment.has?(Var.ensure_symbol(name)) end |
.let(*var_specs) ⇒ Object
Puts the variable specifications in effect, then executes the block, undoes the variable specifications, and returns the block’s value.
The simplest form of variable specification is a variable name (symbol or string). In this case, the variable starts out with the value nil:
Fluid.let(:will_have_value_nil) {...}
You can also specify the initial value of the variable:
Fluid.let(:will_have_value_1, 1) {...}
Multiple variables can be specified in a single let. Each one must be in an array, even if given no value:
Fluid.let([:will_have_value_1, 1],
[:starts_as_nil]) {...}
From the moment the block begins to execute until the moment it returns, getters and setters for the variables are made class methods of Fluid:
Fluid.let(:var, 1) {
Fluid.var # has value 1
Fluid.var = 2 # change value to 2
}
References to the variable needn’t be in the lexical scope of the Fluid.let. They can be anywhere in the program. (To be more precise: references can be made whenever the original Fluid.let is on the stack.)
When Fluid.let’s block exits, the value of the variable is no longer accessible through Fluid.var. An attempt to access it will raise a NameError. If, however, there’s another Fluid.let binding the same name still on the stack, that version’s value is back in effect. For example:
Fluid.let(:var, 1) { # Fluid.var => 1
Fluid.var = 2 # Fluid.var => 2
Fluid.let(:var, "hello") { # Fluid.var => "hello"
Fluid.var *= 2 # Fluid.var => "hellohello"
} # Fluid.var => 2
} # Fluid.var => raises NameError
Variable values are undone even if the block exits with a throw or an exception.
If a variable name begins with ‘$’, Fluid.let realizes it’s a global variable and gives that variable a new value. No getters or setters are created. The old value is still restored when the block exits.
Destructors
There may be an argument past the initial value, the value destructor. It may be either a Proc or the name of a method. If it’s the name of a method, it’s sent as a message to the value of the second argument. Like this:
Fluid.let(out, File.open("logfile", "w"), :close)
(You wouldn’t really do that, though, since File.open does the same thing for you.)
If the third argument is a block, it’s called with the value of the second argument as its single parameter.
Fluid.let(:out, File.open("logfile", 'w'),
proc {|io| io.close}) {...}
249 250 251 252 253 254 255 256 257 |
# File 'lib/fluid.rb', line 249 def Fluid.let(*var_specs) unwind_list = create_dynamic_context(var_specs) begin return yield if block_given? ensure unwind_dynamic_context unwind_list end end |
.method_missing(symbol, *args) ⇒ Object
:nodoc:
313 314 315 316 |
# File 'lib/fluid.rb', line 313 def Fluid.method_missing(symbol, *args) # :nodoc: symbol = symbol.to_s.gsub(/=/, '') raise NameError, "'#{symbol}' has not been defined with Fluid.let or Fluid.defvar." end |