Expression.Callbacks behaviour (expression v1.1.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
Callbacks
Handle a function call while evaluating the AST.
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
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. This is Unicode safe.
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. This is Unicode safe.
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 Callbacks
handle(function_name, arguments, context)
@callback 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:
- The function name as is
- The function name with an underscore suffix if the function name is a reserved word
- The function name suffixed with
_vargs
if the takes a variable set of arguments
Link to this section Functions
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
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
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.
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"
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"
code(ctx, arg)
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
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"}
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"
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"
day(ctx, date)
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
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
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"
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"
handle(function_name, arguments, context)
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") true iex> Expression.Callbacks.has_all_words(%{}, "the quick brown FOX", "red fox") false
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") true iex> Expression.Callbacks.has_any_word(%{}, "The Quick Brown Fox", "yellow") false
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") true iex> Expression.Callbacks.has_beginning(%{}, "The Quick Brown", "the quick") false iex> Expression.Callbacks.has_beginning(%{}, "The Quick Brown", "quick brown") false
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") true iex> Expression.Callbacks.has_date(%{}, "there is no date here, just a year 2017") false
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") true iex> Expression.Callbacks.has_date_eq(%{}, "there is no date here, just a year 2017", "2017-01-15") false
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") true iex> Expression.Callbacks.has_date_gt(%{}, "the date is 15/01/2017", "2017-03-15") false
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") true iex> Expression.Callbacks.has_date_lt(%{}, "the date is 15/01/2021", "2017-03-15") false
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") true iex> Expression.Callbacks.has_email(%{}, "i'm not sharing my email") false
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") true iex> Expression.Callbacks.has_group(%{}, contact["groups"], "00000000-0000-0000-0000-000000000000") false
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> true = Expression.Callbacks.has_number(%{}, "the number is 42 and 5") iex> true = Expression.Callbacks.has_number(%{}, "العدد ٤٢") iex> true = Expression.Callbacks.has_number(%{}, "٠.٥") iex> true = Expression.Callbacks.has_number(%{}, "0.6")
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> true = Expression.Callbacks.has_number_eq(%{}, "the number is 42", 42) iex> true = Expression.Callbacks.has_number_eq(%{}, "the number is 42", 42.0) iex> true = Expression.Callbacks.has_number_eq(%{}, "the number is 42", "42") iex> true = Expression.Callbacks.has_number_eq(%{}, "the number is 42.0", "42") iex> false = Expression.Callbacks.has_number_eq(%{}, "the number is 40", "42") iex> false = Expression.Callbacks.has_number_eq(%{}, "the number is 40", "foo") iex> false = Expression.Callbacks.has_number_eq(%{}, "four hundred", "foo")
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> true = Expression.Callbacks.has_number_gt(%{}, "the number is 42", 40) iex> true = Expression.Callbacks.has_number_gt(%{}, "the number is 42", 40.0) iex> true = Expression.Callbacks.has_number_gt(%{}, "the number is 42", "40") iex> true = Expression.Callbacks.has_number_gt(%{}, "the number is 42.0", "40") iex> false = Expression.Callbacks.has_number_gt(%{}, "the number is 40", "40") iex> false = Expression.Callbacks.has_number_gt(%{}, "the number is 40", "foo") iex> false = Expression.Callbacks.has_number_gt(%{}, "four hundred", "foo")
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> true = Expression.Callbacks.has_number_gte(%{}, "the number is 42", 42) iex> true = Expression.Callbacks.has_number_gte(%{}, "the number is 42", 42.0) iex> true = Expression.Callbacks.has_number_gte(%{}, "the number is 42", "42") iex> false = Expression.Callbacks.has_number_gte(%{}, "the number is 42.0", "45") iex> false = Expression.Callbacks.has_number_gte(%{}, "the number is 40", "45") iex> false = Expression.Callbacks.has_number_gte(%{}, "the number is 40", "foo") iex> false = Expression.Callbacks.has_number_gte(%{}, "four hundred", "foo")
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> true = Expression.Callbacks.has_number_lt(%{}, "the number is 42", 44) iex> true = Expression.Callbacks.has_number_lt(%{}, "the number is 42", 44.0) iex> false = Expression.Callbacks.has_number_lt(%{}, "the number is 42", "40") iex> false = Expression.Callbacks.has_number_lt(%{}, "the number is 42.0", "40") iex> false = Expression.Callbacks.has_number_lt(%{}, "the number is 40", "40") iex> false = Expression.Callbacks.has_number_lt(%{}, "the number is 40", "foo") iex> false = Expression.Callbacks.has_number_lt(%{}, "four hundred", "foo")
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> true = Expression.Callbacks.has_number_lte(%{}, "the number is 42", 42) iex> true = Expression.Callbacks.has_number_lte(%{}, "the number is 42", 42.0) iex> true = Expression.Callbacks.has_number_lte(%{}, "the number is 42", "42") iex> false = Expression.Callbacks.has_number_lte(%{}, "the number is 42.0", "40") iex> false = Expression.Callbacks.has_number_lte(%{}, "the number is 40", "foo") iex> false = Expression.Callbacks.has_number_lte(%{}, "four hundred", "foo")
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") true iex> Expression.Callbacks.has_only_phrase(%{}, "", "") true iex> Expression.Callbacks.has_only_phrase(%{}, "The Quick Brown Fox", "quick brown") false
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") true iex> Expression.Callbacks.has_only_text(%{}, "", "") true iex> Expression.Callbacks.has_only_text(%{}, "foo", "FOO") false
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+)") true iex> Expression.Callbacks.has_pattern(%{}, "Sell cheese please", "buy (\w+)") false
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") true iex> Expression.Callbacks.has_phone(%{}, "my number is 2067799294 thanks", "US") true iex> Expression.Callbacks.has_phone(%{}, "my number is 206 779 9294 thanks", "US") true iex> Expression.Callbacks.has_phone(%{}, "my number is none of your business", "US") false
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") true iex> Expression.Callbacks.has_phrase(%{}, "the quick brown fox", "quick fox") false iex> Expression.Callbacks.has_phrase(%{}, "the quick brown fox", "") true
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") true iex> Expression.Callbacks.has_text(%{}, "") false iex> Expression.Callbacks.has_text(%{}, " \n") false iex> Expression.Callbacks.has_text(%{}, 123) true iex> Expression.Callbacks.has_text(%{}, nil) false
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") true iex> Expression.Callbacks.has_time(%{}, "the time is 10:00 pm") true iex> Expression.Callbacks.has_time(%{}, "the time is 10:30:45") true iex> Expression.Callbacks.has_time(%{}, "there is no time here, just the number 25") false
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
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"}
implements(module \\ __MODULE__, function_name, arguments)
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
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
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
left(ctx, binary, size)
Returns the first characters in a text string. This is Unicode safe.
You entered PIN @LEFT(step.value, 4)
Example
iex> Expression.Callbacks.left(%{}, "foobar", 4)
"foob"
iex> Expression.Callbacks.left(%{}, "Умерла Мадлен Олбрайт - первая женщина на посту главы Госдепа США", 20)
"Умерла Мадлен Олбрай"
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
lower(ctx, binary)
Converts a text string to lowercase
Welcome @LOWER(contact)
```
# Example
iex> Expression.Callbacks.lower(%{}, "Foo Bar")
"foo bar"
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}
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}
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
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
now(ctx)
Returns the current date time as UTC
It is currently @NOW()
Example
iex> DateTime.utc_now() == Expression.Callbacks.now(%{})
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}
percent(ctx, float)
@spec percent(Expression.Context.t(), float()) :: binary()
@spec percent(Expression.Context.t(), binary()) :: binary()
@spec 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%"
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)
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"
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"
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"
rept(ctx, value, amount)
Repeats text a given number of times
Stars! @REPT("*", 10)
Example
iex> Expression.Callbacks.rept(%{}, "*", 10)
"**********"
right(ctx, binary, size)
Returns the last characters in a text string. This is Unicode safe.
Your input ended with ...@RIGHT(step.value, 3)
Example
iex> Expression.Callbacks.right(%{}, "testing", 3)
"ing"
iex> Expression.Callbacks.right(%{}, "Умерла Мадлен Олбрайт - первая женщина на посту главы Госдепа США", 20)
"ту главы Госдепа США"
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
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"
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}
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}
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}
today(ctx)
Returns the current date
Today's date is @TODAY()
Example
iex> today = Date.utc_today()
iex> today == Expression.Callbacks.today(%{})
true
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) "é"
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
upper(ctx, binary)
Converts a text string to uppercase
WELCOME @UPPER(contact)!!
Example
iex> Expression.Callbacks.upper(%{}, "foo")
"FOO"
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
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"
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
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"
word_slice(ctx, binary, start, stop, by_spaces \\ false)
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)