Skip to content

Instantly share code, notes, and snippets.

@deanebarker
Last active January 3, 2022 17:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save deanebarker/c58d76ab357cba88267f20582226bd33 to your computer and use it in GitHub Desktop.
Save deanebarker/c58d76ab357cba88267f20582226bd33 to your computer and use it in GitHub Desktop.
An example of a custom FluidValue to enable fluent(-ish) query specification
// Example:
// {% assign people = query.sortby['lastname'].direction['desc'].limit[2] %}
public class QueryValue : FluidValue
{
// This is only needed for testing
// In production, the query would be of some external resource
private IEnumerable<Person> _items;
// This is an anonymous method that handles the index
private Action<FluidValue> indexHandler;
// These hold the query parameters
private string sortBy;
private int limit = int.MaxValue;
private string direction = "asc";
public QueryValue()
{
// This is fake repository for testing
_items = new List<Person>()
{
new Person() { FirstName = "Annie", LastName = "Barker" },
new Person() { FirstName = "Deane", LastName = "Barker" },
new Person() { FirstName = "James", LastName = "Bond" },
new Person() { FirstName = "Bill", LastName = "Gates" }
};
}
public override FluidValues Type => FluidValues.Array;
public override ValueTask<FluidValue> GetValueAsync(string name, TemplateContext context)
{
// This is how we're going to handle the passed-in index, based on what the name is
indexHandler = name switch
{
"sort-by" => v => { sortBy = v.ToStringValue(); },
"limit" => v => { limit = (int)v.ToNumberValue(); },
"direction" => v => { direction = v.ToStringValue(); },
_ => null
};
// Question: how do we enforce the index? Meaning, someone could ...
// repo.direction.sortby.limit
// ... call a bunch of things and never pass in an index. How could we catch this?
// Do anything else you want here
return this;
}
public override ValueTask<FluidValue> GetIndexAsync(FluidValue index, TemplateContext context)
{
// Handle the index value, then null it
indexHandler?.Invoke(index);
indexHandler = null;
return this;
}
public override IEnumerable<FluidValue> Enumerate(TemplateContext context)
{
var queryResults = _items.AsQueryable();
// Note: this sucks, but I don't know how to make it more graceful
// I assume there's a better way
if(sortBy != null)
{
if(sortBy == "firstname")
{
queryResults = direction == "asc" ? queryResults.OrderBy(p => p.FirstName) : queryResults.OrderByDescending(p => p.FirstName);
}
if (sortBy == "lastname")
{
queryResults = direction == "asc" ? queryResults.OrderBy(p => p.LastName) : queryResults.OrderByDescending(p => p.LastName);
}
}
return queryResults.Take(limit).Select(p => new ObjectValue(p)).ToList();
}
public override bool Equals(FluidValue other) => false;
public override bool ToBooleanValue() => _items.Any();
public override decimal ToNumberValue() => _items.Count();
public override object ToObjectValue() => null;
public override string ToStringValue() => null;
public override void WriteTo(TextWriter writer, TextEncoder encoder, CultureInfo cultureInfo) {}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set;}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment