Expression.Callbacks (expression v0.3.1)

The function callbacks for the standard function set available in FLOIP expressions.

This should be relatively swappable with another implementation. The only requirement is the handle/3 function.

FLOIP functions are case insensitive. All functions in this callback module are implemented as lowercase names.

Some functions accept a variable amount of arguments. Elixir doesn't support variable arguments in functions.

If a function accepts a variable number of arguments the convention is to call the <function_name>_vargs/2 callback where the context is given as the first argument and the argument list as a second argument.

Reserved names such as and, if, and or are suffixed with an underscore.

Link to this section Summary

Functions

Returns the absolute value of a number

Returns TRUE if and only if all its arguments evaluate to TRUE

Convert a string function name into an atom meant to handle that function

Returns the character specified by a number

Removes all non-printable characters from a text string

Returns a numeric code for the first character in a text string

Joins text strings into one text string

Defines a new date value

Converts date stored in text to an actual date, using strftime formatting.

Returns only the day of the month of a date (1 to 31)

Moves a date by the given number of months

Returns the first word in the given text - equivalent to WORD(text, 1)

Formats the given number in decimal format using a period and commas

Handle a function call while evaluating the AST.

Tests whether all the words are contained in text

Tests whether any of the words are contained in the text

Tests whether text starts with beginning

Tests whether expression contains a date formatted according to our environment

Tests whether expression is a date equal to date_string

Tests whether expression is a date after the date date_string

Tests whether expression contains a date before the date date_string

Tests whether an email is contained in text

Returns whether the contact is part of group with the passed in UUID

Tests whether expression contains a number

Tests whether expression contains a number equal to the value

Tests whether expression contains a number greater than min

Tests whether expression contains a number greater than or equal to min

Tests whether expression contains a number less than max

Tests whether expression contains a number less than or equal to max

Tests whether the text contains only phrase

Returns whether two text values are equal (case sensitive). In the case that they are, it will return the text as the match.

Tests whether expression matches the regex pattern

Tests whether expresssion contains a phone number. The optional country_code argument specifies the country to use for parsing.

Tests whether phrase is contained in expression

Tests whether there the expression has any characters in it

Tests whether expression contains a time.

Returns only the hour of a datetime (0 to 23)

Returns one value if the condition evaluates to TRUE, and another value if it evaluates to FALSE

Returns TRUE if the argument is a boolean.

Returns TRUE if the argument is a number.

Returns TRUE if the argument is a string.

Returns the first characters in a text string

Returns the number of characters in a text string

Converts a text string to lowercase

Returns the maximum value of all arguments

Returns the minimum value of all arguments

Returns only the minute of a datetime (0 to 59)

Returns only the month of a date (1 to 12)

Returns the current date time as UTC

Returns TRUE if any argument is TRUE

Formats a number as a percentage

Returns the result of a number raised to a power - equivalent to the ^ operator

Capitalizes the first letter of every word in a text string

Formats digits in text for reading in TTS

Removes the first word from the given text. The remaining text will be unchanged

Repeats text a given number of times

Returns the last characters in a text string

Returns only the second of a datetime (0 to 59)

Substitutes new_text for old_text in a text string. If instance_num is given, then only that instance will be substituted

Returns the sum of all arguments, equivalent to the + operator

Defines a time value which can be used for time arithmetic

Converts time stored in text to an actual time

Returns the current date

Returns the unicode character specified by a number

Returns a numeric code for the first character in a text string

Converts a text string to uppercase

Returns the day of the week of a date (1 for Sunday to 7 for Saturday)

Extracts the nth word from the given text string. If stop is a negative number, then it is treated as count backwards from the end of the text. If by_spaces is specified and is TRUE then the function splits the text into words only by spaces. Otherwise the text is split by punctuation characters as well

Returns the number of words in the given text string. If by_spaces is specified and is TRUE then the function splits the text into words only by spaces. Otherwise the text is split by punctuation characters as well

Extracts a substring of the words beginning at start, and up to but not-including stop. If stop is omitted then the substring will be all words from start until the end of the text. If stop is a negative number, then it is treated as count backwards from the end of the text. If by_spaces is specified and is TRUE then the function splits the text into words only by spaces. Otherwise the text is split by punctuation characters as well

Returns only the year of a date

Link to this section Functions

Link to this function

abs(ctx, number)

Returns the absolute value of a number

The absolute value of -1 is @ABS(-1)

Example

iex> Expression.Callbacks.abs(%{}, -1)
1
Link to this function

and_vargs(ctx, arguments)

Returns TRUE if and only if all its arguments evaluate to TRUE

@AND(contact.gender = "F", contact.age >= 18)

Example

iex> Expression.Callbacks.handle("and", [true, true], %{})
{:ok, true}
iex> Expression.Callbacks.and_vargs(%{}, [true, true])
true
iex> Expression.Callbacks.and_vargs(%{}, [true, false])
false
iex> Expression.Callbacks.and_vargs(%{}, [false, false])
false
Link to this function

atom_function_name(function_name)

Convert a string function name into an atom meant to handle that function

Reserved words such as and, if, and or are automatically suffixed with an _ underscore.

Link to this function

char(ctx, code)

Returns the character specified by a number

As easy as @CHAR(65), @CHAR(66), @CHAR(67)

Example

iex> Expression.Callbacks.char(%{}, 65)
"A"
Link to this function

clean(ctx, binary)

Removes all non-printable characters from a text string

You entered @CLEAN(step.value)

Example

iex> Expression.Callbacks.clean(%{}, <<65, 0, 66, 0, 67>>)
"ABC"

Returns a numeric code for the first character in a text string

The numeric code of A is @CODE("A")

Example

iex> Expression.Callbacks.code(%{}, "A")
65
Link to this function

concatenate_vargs(ctx, arguments)

Joins text strings into one text string

Your name is @CONCATENATE(contact.first_name, " ", contact.last_name)

Example

iex> Expression.Callbacks.handle("concatenate", ["name", " ", "surname"], %{})
{:ok, "name surname"}
Link to this function

date(ctx, year, month, day)

Defines a new date value

This is a date @DATE(2012, 12, 25)

Example

iex> to_string(Expression.Callbacks.date(%{}, 2012, 12, 25))
"2012-12-25 00:00:00Z"
Link to this function

datevalue(ctx, date, format \\ "%Y-%m-%d %H:%M:%S")

Converts date stored in text to an actual date, using strftime formatting.

It will fallback to "%Y-%m-%d %H:%M:%S" if no formatting is supplied

You joined on @DATEVALUE(contact.joined_date, "%Y-%m%-d")

Example

iex> date = Expression.Callbacks.date(%{}, 2020, 12, 20)
iex> Expression.Callbacks.datevalue(%{}, date)
"2020-12-20 00:00:00"
iex> Expression.Callbacks.datevalue(%{}, date, "%Y-%m-%d")
"2020-12-20"

Returns only the day of the month of a date (1 to 31)

The current day is @DAY(contact.joined_date)

Example

iex> now = DateTime.utc_now()
iex> day = Expression.Callbacks.day(%{}, now)
iex> day == now.day
true
Link to this function

edate(ctx, date, months)

Moves a date by the given number of months

Next month's meeting will be on @EDATE(date.today, 1)

Example

iex> now = DateTime.utc_now()
iex> future = Timex.shift(now, months: 1)
iex> date = Expression.Callbacks.edate(%{}, now, 1)
iex> future == date
true
Link to this function

first_word(ctx, binary)

Returns the first word in the given text - equivalent to WORD(text, 1)

The first word you entered was @FIRST_WORD(step.value)

Example

iex> Expression.Callbacks.first_word(%{}, "foo bar baz")
"foo"
Link to this function

fixed(ctx, number, precision, no_commas \\ false)

Formats the given number in decimal format using a period and commas

You have @FIXED(contact.balance, 2) in your account

Example

iex> Expression.Callbacks.fixed(%{}, 4.209922, 2, false)
"4.21"
iex> Expression.Callbacks.fixed(%{}, 4000.424242, 4, true)
"4,000.4242"
iex> Expression.Callbacks.fixed(%{}, 3.7979, 2, false)
"3.80"
iex> Expression.Callbacks.fixed(%{}, 3.7979, 2)
"3.80"
Link to this function

handle(function_name, arguments, context)

Specs

handle(function_name :: binary(), arguments :: [any()], context :: map()) ::
  {:ok, any()} | {:error, :not_implemented}

Handle a function call while evaluating the AST.

Handlers in this module are either:

  1. The function name as is
  2. The function name with an underscore suffix if the function name is a reserved word
  3. The function name suffixed with _vargs if the takes a variable set of arguments
Link to this function

has_all_words(ctx, haystack, words)

Tests whether all the words are contained in text

The words can be in any order and may appear more than once.

@(has_all_words("the quick brown FOX", "the fox"))  true
@(has_all_words("the quick brown fox", "red fox"))  false

NOTE: the flowspec supports .match which isn't support here yet.

@(has_all_words("the quick brown FOX", "the fox").match)  the FOX

Example

iex> Expression.Callbacks.has_all_words(%{}, "the quick brown FOX", "the fox")

iex> Expression.Callbacks.has_all_words(%{}, "the quick brown FOX", "red fox")

Link to this function

has_any_word(ctx, haystack, words)

Tests whether any of the words are contained in the text

Only one of the words needs to match and it may appear more than once.

@(has_any_word("The Quick Brown Fox", "fox quick"))  true

Unsupported:

@(has_any_word("The Quick Brown Fox", "fox quick").match)  Quick Fox
@(has_any_word("The Quick Brown Fox", "red fox").match)  Fox

Example

iex> Expression.Callbacks.has_any_word(%{}, "The Quick Brown Fox", "fox quick")

iex> Expression.Callbacks.has_any_word(%{}, "The Quick Brown Fox", "yellow")

Link to this function

has_beginning(ctx, text, beginning)

Tests whether text starts with beginning

Both text values are trimmed of surrounding whitespace, but otherwise matching is strict without any tokenization.

Supported:

@(has_beginning("The Quick Brown", "the quick"))  true
@(has_beginning("The Quick Brown", "the   quick"))  false
@(has_beginning("The Quick Brown", "quick brown"))  false

Unsupported

@(has_beginning("The Quick Brown", "the quick").match)  The Quick

Example

iex> Expression.Callbacks.has_beginning(%{}, "The Quick Brown", "the quick")

iex> Expression.Callbacks.has_beginning(%{}, "The Quick Brown", "the quick")

iex> Expression.Callbacks.has_beginning(%{}, "The Quick Brown", "quick brown")

Link to this function

has_date(_, expression)

Tests whether expression contains a date formatted according to our environment

This is very naively implemented with a regular expression.

Supported:

@(has_date("the date is 15/01/2017"))  true
@(has_date("there is no date here, just a year 2017"))  false

Unsupported:

@(has_date("the date is 15/01/2017").match)  2017-01-15T13:24:30.123456-05:00

Example

iex> Expression.Callbacks.has_date(%{}, "the date is 15/01/2017")

iex> Expression.Callbacks.has_date(%{}, "there is no date here, just a year 2017")

Link to this function

has_date_eq(ctx, expression, date_string)

Tests whether expression is a date equal to date_string

Supported:

@(has_date_eq("the date is 15/01/2017", "2017-01-15"))  true
@(has_date_eq("there is no date here, just a year 2017", "2017-06-01"))  false
@(has_date_eq("there is no date here, just a year 2017", "not date"))  ERROR

Not supported:

@(has_date_eq("the date is 15/01/2017", "2017-01-15").match)  2017-01-15T13:24:30.123456-05:00
@(has_date_eq("the date is 15/01/2017 15:00", "2017-01-15").match)  2017-01-15T15:00:00.000000-05:00

Examples

iex> Expression.Callbacks.has_date_eq(%{}, "the date is 15/01/2017", "2017-01-15")

iex> Expression.Callbacks.has_date_eq(%{}, "there is no date here, just a year 2017", "2017-01-15")

Link to this function

has_date_gt(ctx, expression, date_string)

Tests whether expression is a date after the date date_string

@(has_date_gt("the date is 15/01/2017", "2017-01-01"))  true
@(has_date_gt("the date is 15/01/2017", "2017-03-15"))  false
@(has_date_gt("there is no date here, just a year 2017", "2017-06-01"))  false
@(has_date_gt("there is no date here, just a year 2017", "not date"))  ERROR

Not supported:

@(has_date_gt("the date is 15/01/2017", "2017-01-01").match)  2017-01-15T13:24:30.123456-05:00

Example

iex> Expression.Callbacks.has_date_gt(%{}, "the date is 15/01/2017", "2017-01-01")

iex> Expression.Callbacks.has_date_gt(%{}, "the date is 15/01/2017", "2017-03-15")

Link to this function

has_date_lt(ctx, expression, date_string)

Tests whether expression contains a date before the date date_string

@(has_date_lt("the date is 15/01/2017", "2017-06-01"))  true
@(has_date_lt("there is no date here, just a year 2017", "2017-06-01"))  false
@(has_date_lt("there is no date here, just a year 2017", "not date"))  ERROR

Not supported:

@(has_date_lt("the date is 15/01/2017", "2017-06-01").match)  2017-01-15T13:24:30.123456-05:00

Example

iex> Expression.Callbacks.has_date_lt(%{}, "the date is 15/01/2017", "2017-06-01")

iex> Expression.Callbacks.has_date_lt(%{}, "the date is 15/01/2021", "2017-03-15")

Link to this function

has_email(ctx, expression)

Tests whether an email is contained in text

@(has_email("my email is foo1@bar.com, please respond"))  true
@(has_email("i'm not sharing my email"))  false

Not supported:

@(has_email("my email is foo1@bar.com, please respond").match)  foo1@bar.com
@(has_email("my email is <foo@bar2.com>").match)  foo@bar2.com

Example:

iex> Expression.Callbacks.has_email(%{}, "my email is foo1@bar.com, please respond")

iex> Expression.Callbacks.has_email(%{}, "i'm not sharing my email")

Link to this function

has_group(ctx, groups, uuid)

Returns whether the contact is part of group with the passed in UUID

@(has_group(array(), "97fe7029-3a15-4005-b0c7-277b884fc1d5"))  false

Not supported:

@(has_group(contact.groups, "b7cf0d83-f1c9-411c-96fd-c511a4cfa86d").match)  {name: Testers, uuid: b7cf0d83-f1c9-411c-96fd-c511a4cfa86d}

Example:

iex> contact = %{ ...> "groups" => [%{ ...> "uuid" => "b7cf0d83-f1c9-411c-96fd-c511a4cfa86d" ...> }] ...> } iex> Expression.Callbacks.has_group(%{}, contact["groups"], "b7cf0d83-f1c9-411c-96fd-c511a4cfa86d")

iex> Expression.Callbacks.has_group(%{}, contact["groups"], "00000000-0000-0000-0000-000000000000")

Link to this function

has_number(ctx, expression)

Tests whether expression contains a number

@(has_number("the number is 42"))  true
@(has_number("the number is forty two"))  false

Not supported:

@(has_number("the number is 42").match)  42
@(has_number("العدد ٤٢").match)  42

Example

iex> {:ok, true} = Expression.Callbacks.has_number(%{}, "the number is 42 and 5") iex> {:ok, true} = Expression.Callbacks.has_number(%{}, "العدد ٤٢") iex> {:ok, true} = Expression.Callbacks.has_number(%{}, "٠.٥") iex> {:ok, true} = Expression.Callbacks.has_number(%{}, "0.6")

Link to this function

has_number_eq(ctx, expression, decimal)

Tests whether expression contains a number equal to the value

@(has_number_eq("the number is 42", 42))  true
@(has_number_eq("the number is 42", 40))  false
@(has_number_eq("the number is not there", 40))  false
@(has_number_eq("the number is not there", "foo"))  ERROR

Not supported:

@(has_number_eq("the number is 42", 42).match)  42

Example

iex> {:ok, true} = Expression.Callbacks.has_number_eq(%{}, "the number is 42", 42) iex> {:ok, true} = Expression.Callbacks.has_number_eq(%{}, "the number is 42", 42.0) iex> {:ok, true} = Expression.Callbacks.has_number_eq(%{}, "the number is 42", "42") iex> {:ok, true} = Expression.Callbacks.has_number_eq(%{}, "the number is 42.0", "42") iex> {:ok, false} = Expression.Callbacks.has_number_eq(%{}, "the number is 40", "42") iex> {:ok, false} = Expression.Callbacks.has_number_eq(%{}, "the number is 40", "foo") iex> {:ok, false} = Expression.Callbacks.has_number_eq(%{}, "four hundred", "foo")

Link to this function

has_number_gt(ctx, expression, decimal)

Tests whether expression contains a number greater than min

@(has_number_gt("the number is 42", 40))  true
@(has_number_gt("the number is 42", 42))  false
@(has_number_gt("the number is not there", 40))  false
@(has_number_gt("the number is not there", "foo"))  ERROR

Not supported:

@(has_number_gt("the number is 42", 40).match)  42

Example

iex> {:ok, true} = Expression.Callbacks.has_number_gt(%{}, "the number is 42", 40) iex> {:ok, true} = Expression.Callbacks.has_number_gt(%{}, "the number is 42", 40.0) iex> {:ok, true} = Expression.Callbacks.has_number_gt(%{}, "the number is 42", "40") iex> {:ok, true} = Expression.Callbacks.has_number_gt(%{}, "the number is 42.0", "40") iex> {:ok, false} = Expression.Callbacks.has_number_gt(%{}, "the number is 40", "40") iex> {:ok, false} = Expression.Callbacks.has_number_gt(%{}, "the number is 40", "foo") iex> {:ok, false} = Expression.Callbacks.has_number_gt(%{}, "four hundred", "foo")

Link to this function

has_number_gte(ctx, expression, decimal)

Tests whether expression contains a number greater than or equal to min

@(has_number_gte("the number is 42", 42))  true
@(has_number_gte("the number is 42", 45))  false
@(has_number_gte("the number is not there", 40))  false
@(has_number_gte("the number is not there", "foo"))  ERROR

Not supported:

@(has_number_gte("the number is 42", 42).match)  42

Example

iex> {:ok, true} = Expression.Callbacks.has_number_gte(%{}, "the number is 42", 42) iex> {:ok, true} = Expression.Callbacks.has_number_gte(%{}, "the number is 42", 42.0) iex> {:ok, true} = Expression.Callbacks.has_number_gte(%{}, "the number is 42", "42") iex> {:ok, false} = Expression.Callbacks.has_number_gte(%{}, "the number is 42.0", "45") iex> {:ok, false} = Expression.Callbacks.has_number_gte(%{}, "the number is 40", "45") iex> {:ok, false} = Expression.Callbacks.has_number_gte(%{}, "the number is 40", "foo") iex> {:ok, false} = Expression.Callbacks.has_number_gte(%{}, "four hundred", "foo")

Link to this function

has_number_lt(ctx, expression, decimal)

Tests whether expression contains a number less than max

@(has_number_lt("the number is 42", 44))  true
@(has_number_lt("the number is 42", 40))  false
@(has_number_lt("the number is not there", 40))  false
@(has_number_lt("the number is not there", "foo"))  ERROR

Not supported:

@(has_number_lt("the number is 42", 44).match)  42

Example

iex> {:ok, true} = Expression.Callbacks.has_number_lt(%{}, "the number is 42", 44) iex> {:ok, true} = Expression.Callbacks.has_number_lt(%{}, "the number is 42", 44.0) iex> {:ok, false} = Expression.Callbacks.has_number_lt(%{}, "the number is 42", "40") iex> {:ok, false} = Expression.Callbacks.has_number_lt(%{}, "the number is 42.0", "40") iex> {:ok, false} = Expression.Callbacks.has_number_lt(%{}, "the number is 40", "40") iex> {:ok, false} = Expression.Callbacks.has_number_lt(%{}, "the number is 40", "foo") iex> {:ok, false} = Expression.Callbacks.has_number_lt(%{}, "four hundred", "foo")

Link to this function

has_number_lte(ctx, expression, decimal)

Tests whether expression contains a number less than or equal to max

@(has_number_lte("the number is 42", 42))  true
@(has_number_lte("the number is 42", 40))  false
@(has_number_lte("the number is not there", 40))  false
@(has_number_lte("the number is not there", "foo"))  ERROR

Not supported:

@(has_number_lte("the number is 42", 42).match)  42

Example

iex> {:ok, true} = Expression.Callbacks.has_number_lte(%{}, "the number is 42", 42) iex> {:ok, true} = Expression.Callbacks.has_number_lte(%{}, "the number is 42", 42.0) iex> {:ok, true} = Expression.Callbacks.has_number_lte(%{}, "the number is 42", "42") iex> {:ok, false} = Expression.Callbacks.has_number_lte(%{}, "the number is 42.0", "40") iex> {:ok, false} = Expression.Callbacks.has_number_lte(%{}, "the number is 40", "foo") iex> {:ok, false} = Expression.Callbacks.has_number_lte(%{}, "four hundred", "foo")

Link to this function

has_only_phrase(ctx, expression, phrase)

Tests whether the text contains only phrase

The phrase must be the only text in the text to match

@(has_only_phrase("Quick Brown", "quick brown"))  true
@(has_only_phrase("The Quick Brown Fox", "quick brown"))  false
@(has_only_phrase("the Quick Brown fox", ""))  false
@(has_only_phrase("", "").match) 
@(has_only_phrase("The Quick Brown Fox", "red fox"))  false

Not supported:

@(has_only_phrase("Quick Brown", "quick brown").match)  Quick Brown

Example

iex> Expression.Callbacks.has_only_phrase(%{}, "Quick Brown", "quick brown")

iex> Expression.Callbacks.has_only_phrase(%{}, "", "")

iex> Expression.Callbacks.has_only_phrase(%{}, "The Quick Brown Fox", "quick brown")

Link to this function

has_only_text(ctx, expression, expression)

Returns whether two text values are equal (case sensitive). In the case that they are, it will return the text as the match.

@(has_only_text("foo", "foo"))  true
@(has_only_text("foo", "FOO"))  false
@(has_only_text("foo", "bar"))  false
@(has_only_text("foo", " foo "))  false
@(has_only_text(results.webhook.category, "Failure"))  false

Not supported:

@(has_only_text("foo", "foo").match)  foo
@(has_only_text(run.status, "completed").match)  completed
@(has_only_text(results.webhook.category, "Success").match)  Success

Example

iex> Expression.Callbacks.has_only_text(%{}, "foo", "foo")

iex> Expression.Callbacks.has_only_text(%{}, "", "")

iex> Expression.Callbacks.has_only_text(%{}, "foo", "FOO")

Link to this function

has_pattern(ctx, expression, pattern)

Tests whether expression matches the regex pattern

Both text values are trimmed of surrounding whitespace and matching is case-insensitive.

@(has_pattern("Buy cheese please", "buy (w+)"))  true
@(has_pattern("Sell cheese please", "buy (w+)"))  false

Not supported:

@(has_pattern("Buy cheese please", "buy (w+)").match)  Buy cheese
@(has_pattern("Buy cheese please", "buy (w+)").extra)  {0: Buy cheese, 1: cheese}

Examples

iex> Expression.Callbacks.has_pattern(%{}, "Buy cheese please", "buy (\w+)")

iex> Expression.Callbacks.has_pattern(%{}, "Sell cheese please", "buy (\w+)")

Link to this function

has_phone(map, expression, country_code \\ "")

Tests whether expresssion contains a phone number. The optional country_code argument specifies the country to use for parsing.

@(has_phone("my number is +12067799294 thanks"))  true
@(has_phone("my number is none of your business", "US"))  false

Not supported:

@(has_phone("my number is +12067799294").match)  +12067799294
@(has_phone("my number is 2067799294", "US").match)  +12067799294
@(has_phone("my number is 206 779 9294", "US").match)  +12067799294

Example

iex> Expression.Callbacks.has_phone(%{}, "my number is +12067799294 thanks")

iex> Expression.Callbacks.has_phone(%{}, "my number is 2067799294 thanks", "US")

iex> Expression.Callbacks.has_phone(%{}, "my number is 206 779 9294 thanks", "US")

iex> Expression.Callbacks.has_phone(%{}, "my number is none of your business", "US")

Link to this function

has_phrase(ctx, expression, phrase)

Tests whether phrase is contained in expression

The words in the test phrase must appear in the same order with no other words in between.

@(has_phrase("the quick brown fox", "brown fox"))  true
@(has_phrase("the Quick Brown fox", "quick fox"))  false
@(has_phrase("the Quick Brown fox", "").match) 

Not supported:

@(has_phrase("the quick brown fox", "brown fox").match)  brown fox

Examples

iex> Expression.Callbacks.has_phrase(%{}, "the quick brown fox", "brown fox")

iex> Expression.Callbacks.has_phrase(%{}, "the quick brown fox", "quick fox")

iex> Expression.Callbacks.has_phrase(%{}, "the quick brown fox", "")

Link to this function

has_text(ctx, expression)

Tests whether there the expression has any characters in it

@(has_text("quick brown"))  true
@(has_text(""))  false
@(has_text(" 
"))  false
@(has_text(contact.fields.not_set))  false

Not supported:

@(has_text("quick brown").match)  quick brown
@(has_text(123).match)  123

Examples

iex> Expression.Callbacks.has_text(%{}, "quick brown")

iex> Expression.Callbacks.has_text(%{}, "")

iex> Expression.Callbacks.has_text(%{}, " \n")

iex> Expression.Callbacks.has_text(%{}, 123)

iex> Expression.Callbacks.has_text(%{}, nil)

Link to this function

has_time(ctx, expression)

Tests whether expression contains a time.

@(has_time("the time is 10:30"))  true
@(has_time("the time is 10:30:45").match)  10:30:45.000000
@(has_time("there is no time here, just the number 25"))  false

Not supported:

@(has_time("the time is 10:30").match)  10:30:00.000000
@(has_time("the time is 10 PM").match)  22:00:00.000000

Examples

iex> Expression.Callbacks.has_time(%{}, "the time is 10:30")

iex> Expression.Callbacks.has_time(%{}, "the time is 10:00 pm")

iex> Expression.Callbacks.has_time(%{}, "the time is 10:30:45")

iex> Expression.Callbacks.has_time(%{}, "there is no time here, just the number 25")

Link to this function

hour(ctx, date)

Returns only the hour of a datetime (0 to 23)

The current hour is @HOUR(NOW())

Example

iex> now = DateTime.utc_now()
iex> hour = Expression.Callbacks.hour(%{}, now)
iex> now.hour == hour
true
Link to this function

if_(ctx, condition, yes, no)

Returns one value if the condition evaluates to TRUE, and another value if it evaluates to FALSE

Dear @IF(contact.gender = "M", "Sir", "Madam")

Example

iex> Expression.Callbacks.handle("if", [true, "Yes", "No"], %{})
{:ok, "Yes"}
iex> Expression.Callbacks.handle("if", [false, "Yes", "No"], %{})
{:ok, "No"}
Link to this function

isbool(ctx, var)

Returns TRUE if the argument is a boolean.

@ISBOOL(block.value) will return TRUE if the block returned a boolean value.

Example

iex> Expression.Callbacks.isbool(%{}, true)
true
iex> Expression.Callbacks.isbool(%{}, false)
true
iex> Expression.Callbacks.isbool(%{}, 1)
false
iex> Expression.Callbacks.isbool(%{}, 0)
false
iex> Expression.Callbacks.isbool(%{}, "true")
false
iex> Expression.Callbacks.isbool(%{}, "false")
false
Link to this function

isnumber(ctx, var)

Returns TRUE if the argument is a number.

@ISNUMBER(contact.age) will return TRUE if the contact's age is a number.

Example

iex> Expression.Callbacks.isnumber(%{}, 1)
true
iex> Expression.Callbacks.isnumber(%{}, 1.0)
true
iex> Expression.Callbacks.isnumber(%{}, Decimal.new("1.0"))
true
iex> Expression.Callbacks.isnumber(%{}, "1.0")
true
iex> Expression.Callbacks.isnumber(%{}, "a")
false
Link to this function

isstring(ctx, binary)

Returns TRUE if the argument is a string.

@ISSTRING(contact.name) will return TRUE if the contact's name is a string.

Example

iex> Expression.Callbacks.isstring(%{}, "hello")
true
iex> Expression.Callbacks.isstring(%{}, false)
false
iex> Expression.Callbacks.isstring(%{}, 1)
false
iex> Expression.Callbacks.isstring(%{}, Decimal.new("1.0"))
false
Link to this function

left(ctx, binary, size)

Returns the first characters in a text string

You entered PIN @LEFT(step.value, 4)

Example

iex> Expression.Callbacks.left(%{}, "foobar", 4)
"foob"
Link to this function

len(ctx, binary)

Returns the number of characters in a text string

You entered @LEN(step.value) characters

Example

iex> Expression.Callbacks.len(%{}, "foo")
3
iex> Expression.Callbacks.len(%{}, "zoë")
3
Link to this function

lower(ctx, binary)

Converts a text string to lowercase

Welcome @LOWER(contact)
```

# Example

    iex> Expression.Callbacks.lower(%{}, "Foo Bar")
    "foo bar"
Link to this function

max_vargs(ctx, arguments)

Returns the maximum value of all arguments

Please complete at most @MAX(flow.questions, 10) questions

Example

iex> Expression.Callbacks.handle("max", [1, 2, 3], %{})
{:ok, 3}
Link to this function

min_vargs(ctx, arguments)

Returns the minimum value of all arguments

Please complete at least @MIN(flow.questions, 10) questions

Example

iex> Expression.Callbacks.handle("min", [1, 2, 3], %{})
{:ok, 1}
Link to this function

minute(ctx, date)

Returns only the minute of a datetime (0 to 59)

The current minute is @MINUTE(NOW())

Example

iex> now = DateTime.utc_now()
iex> minute = Expression.Callbacks.minute(%{}, now)
iex> now.minute == minute
true
Link to this function

month(ctx, date)

Returns only the month of a date (1 to 12)

The current month is @MONTH(NOW())

Example

iex> now = DateTime.utc_now()
iex> month = Expression.Callbacks.month(%{}, now)
iex> now.month == month
true

Returns the current date time as UTC

It is currently @NOW()

Example

iex> DateTime.utc_now() == Expression.Callbacks.now(%{})

Link to this function

or_vargs(ctx, arguments)

Returns TRUE if any argument is TRUE

@OR(contact.state = "GA", contact.state = "WA", contact.state = "IN")

Example

iex> Expression.Callbacks.handle("or", [true, false], %{})
{:ok, true}
iex> Expression.Callbacks.handle("or", [true, true], %{})
{:ok, true}
iex> Expression.Callbacks.handle("or", [false, false], %{})
{:ok, false}
Link to this function

percent(ctx, float)

Specs

percent(Expression.Context.t(), float()) :: binary()
percent(Expression.Context.t(), binary()) :: binary()
percent(Expression.Context.t(), Decimal.t()) :: binary()

Formats a number as a percentage

You've completed @PERCENT(contact.reports_done / 10) reports

Example

iex> Expression.Callbacks.percent(%{}, 2/10)
"20%"
iex> Expression.Callbacks.percent(%{}, "0.2")
"20%"
iex> Expression.Callbacks.percent(%{}, Decimal.new("0.2"))
"20%"
Link to this function

power(ctx, a, b)

Returns the result of a number raised to a power - equivalent to the ^ operator

2 to the power of 3 is @POWER(2, 3)
Link to this function

proper(ctx, binary)

Capitalizes the first letter of every word in a text string

Your name is @PROPER(contact)

Example

iex> Expression.Callbacks.proper(%{}, "foo bar")
"Foo Bar"
Link to this function

read_digits(ctx, binary)

Formats digits in text for reading in TTS

Your number is @READ_DIGITS(contact.tel_e164)

Example

iex> Expression.Callbacks.read_digits(%{}, "+271")
"plus two seven one"
Link to this function

remove_first_word(ctx, binary, separator \\ " ")

Removes the first word from the given text. The remaining text will be unchanged

You entered @REMOVE_FIRST_WORD(step.value)

Example

iex> Expression.Callbacks.remove_first_word(%{}, "foo bar")
"bar"
iex> Expression.Callbacks.remove_first_word(%{}, "foo-bar", "-")
"bar"
Link to this function

rept(ctx, value, amount)

Repeats text a given number of times

Stars! @REPT("*", 10)

Example

iex> Expression.Callbacks.rept(%{}, "*", 10)
"**********"
Link to this function

right(ctx, binary, size)

Returns the last characters in a text string

Your input ended with ...@RIGHT(step.value, 3)

Example

iex> Expression.Callbacks.right(%{}, "testing", 3)
"ing"
Link to this function

second(ctx, date)

Returns only the second of a datetime (0 to 59)

The current second is @SECOND(NOW())

Example

iex> now = DateTime.utc_now()
iex> second = Expression.Callbacks.second(%{}, now)
iex> now.second == second
true
Link to this function

substitute(map, subject, pattern, replacement)

Substitutes new_text for old_text in a text string. If instance_num is given, then only that instance will be substituted

@SUBSTITUTE(step.value, "can't", "can")

Example

iex> Expression.Callbacks.substitute(%{}, "I can't", "can't", "can do") "I can do"

Link to this function

sum_vargs(ctx, arguments)

Returns the sum of all arguments, equivalent to the + operator

You have @SUM(contact.reports, contact.forms) reports and forms

Example

iex> Expression.Callbacks.handle("sum", [1, 2, 3], %{})
{:ok, 6}
Link to this function

time(ctx, hours, minutes, seconds)

Defines a time value which can be used for time arithmetic

2 hours and 30 minutes from now is @(date.now + TIME(2, 30, 0))

Example

iex> Expression.Callbacks.time(%{}, 12, 13, 14)
%Time{hour: 12, minute: 13, second: 14}
Link to this function

timevalue(ctx, expression)

Converts time stored in text to an actual time

Your appointment is at @(date.today + TIME("2:30"))

Example

iex> Expression.Callbacks.timevalue(%{}, "2:30")
%Time{hour: 2, minute: 30, second: 0}

iex> Expression.Callbacks.timevalue(%{}, "2:30:55")
%Time{hour: 2, minute: 30, second: 55}

Returns the current date

Today's date is @TODAY()

Example

iex> today = Date.utc_today()
iex> today == Expression.Callbacks.today(%{})
true
Link to this function

unichar(ctx, code)

Returns the unicode character specified by a number

As easy as @UNICHAR(65), @UNICHAR(66) , @UNICHAR(67)

Example

iex> Expression.Callbacks.unichar(%{}, 65) "A" iex> Expression.Callbacks.unichar(%{}, 233) "é"

Link to this function

unicode(ctx, arg)

Returns a numeric code for the first character in a text string

The numeric code of A is @UNICODE("A")

Example

iex> Expression.Callbacks.unicode(%{}, "A")
65
iex> Expression.Callbacks.unicode(%{}, "é")
233
Link to this function

upper(ctx, binary)

Converts a text string to uppercase

WELCOME @UPPER(contact)!!

Example

iex> Expression.Callbacks.upper(%{}, "foo")
"FOO"
Link to this function

weekday(ctx, date)

Returns the day of the week of a date (1 for Sunday to 7 for Saturday)

Today is day no. @WEEKDAY(TODAY()) in the week

Example

iex> today = DateTime.utc_now()
iex> expected = Timex.weekday(today)
iex> weekday = Expression.Callbacks.weekday(%{}, today)
iex> weekday == expected
true
Link to this function

word(ctx, binary, n, by_spaces \\ false)

Extracts the nth word from the given text string. If stop is a negative number, then it is treated as count backwards from the end of the text. If by_spaces is specified and is TRUE then the function splits the text into words only by spaces. Otherwise the text is split by punctuation characters as well

Example

iex> Expression.Callbacks.word(%{}, "hello cow-boy", 2)
"cow"
iex> Expression.Callbacks.word(%{}, "hello cow-boy", 2, true)
"cow-boy"
iex> Expression.Callbacks.word(%{}, "hello cow-boy", -1)
"boy"
Link to this function

word_count(ctx, binary, by_spaces \\ false)

Returns the number of words in the given text string. If by_spaces is specified and is TRUE then the function splits the text into words only by spaces. Otherwise the text is split by punctuation characters as well

You entered @WORD_COUNT(step.value) words

Example

iex> Expression.Callbacks.word_count(%{}, "hello cow-boy")
3
iex> Expression.Callbacks.word_count(%{}, "hello cow-boy", true)
2
Link to this function

word_slice(ctx, binary, start)

Extracts a substring of the words beginning at start, and up to but not-including stop. If stop is omitted then the substring will be all words from start until the end of the text. If stop is a negative number, then it is treated as count backwards from the end of the text. If by_spaces is specified and is TRUE then the function splits the text into words only by spaces. Otherwise the text is split by punctuation characters as well

Example

iex> Expression.Callbacks.word_slice(%{}, "RapidPro expressions are fun", 2, 4)
"expressions are"
iex> Expression.Callbacks.word_slice(%{}, "RapidPro expressions are fun", 2)
"expressions are fun"
iex> Expression.Callbacks.word_slice(%{}, "RapidPro expressions are fun", 1, -2)
"RapidPro expressions"
iex> Expression.Callbacks.word_slice(%{}, "RapidPro expressions are fun", -1)
"fun"
Link to this function

word_slice(ctx, binary, start, stop, by_spaces \\ false)

Link to this function

year(ctx, date)

Returns only the year of a date

The current year is @YEAR(NOW())

Example

iex> %{year: year} = now = DateTime.utc_now()
iex> year == Expression.Callbacks.year(%{}, now)