Fluid Values

Fluid only deals in FluidValue objects. Everything you inject into a scope will be converted into a FluidValue object. Indeed, the properties dictionary on Scope only holds FluidValue objects.

There’s a method on TemplateContext that takes a string and a FluidValue:

context.SetValue("name", StringValue.Create("Deane"));

(I know, I know – we haven’t been explicitly creating FluidValue objects in prior examples. We’ll explain this when we get to Value Convertors.)

Here are the built-in Fluid values:

  • StringValue wraps a string
  • NumberValue wraps a decimal
  • DateTimeValue wraps a DateTimeOffset
  • ObjectValue wraps an object (all Fluid Values extend from this)
  • BooleanValue wraps a bool
  • ArrayValue wraps a FluidValue[]
  • DictionaryValue wraps a dictionary interface
  • BlankValue, NilValue, EmptyValue don’t wrap anything, but exist for conditional checking

Most of the provided classes have a private field for _value which holds whatever the original value was.

Fluid Values provide a common interface. Some highlights:

  • A Type property which describes what the value is – a String, Number, etc.

  • A method called WriteTo which will write out whatever the value should generate. This is where the actual output is generated

  • A method called GetValue which will produce a value. Often, this is just the underlying value that’s being wrapped, but as we’ll see later, it can create a value a number of different ways

  • A method called GetIndex that is passed when an index is called (ex: people[0]). It gets a FluidValue (a NumberValue in the example), and you can use this to lookup and return any value you like.

  • A method called Enumerate using which you can return an IEnumerable<FluidValue> to be use to iterate values using a for tag

  • Methods to convert the Fluid Value into various primitives – numbers, string, etc.

So long as you provide this functionality, you can create your own values. For example, if we wanted to value that wrapped a string and appended “ is awesome” to it when it output, we could do this:

public class AwesomeValue : FluidValue
{
  private string _value;
  public AwesomeValue(string value)
  {
    _value = value;
  }

  public override void WriteTo(TextWriter writer, TextEncoder encoder, CultureInfo cultureInfo) => writer.Write(ToStringValue());

  public override string ToStringValue() => $"{_value} is awesome";

  // We don't really care about any of this, but FluidValue is
  // abstract, so we have to override it all
  public override FluidValues Type => FluidValues.String;
  public override bool Equals(FluidValue other) => false;
  public override bool ToBooleanValue() => false;
  public override decimal ToNumberValue() => 0;
  public override object ToObjectValue() => ToStringValue();
}

When we set this value in the context –

context.SetValue("name", new AwesomeValue("Deane"));

– it will output this wherever it appears:

Deane is awesome

We pushed an AwesomeValue into the Context, and it was placed in the Scope. When a template finds it, the WriteTo method will create the desired output.