* [Introduction](#introduction) * [Defining symbols](#defining-symbols) * [A symbol must be defined only once](#a-symbol-must-be-defined-once) * [Symbol values are hard coded](#symbol-values-are-hard-coded) * [Referencing symbols](#referencing-symbols) * [References using special syntax](#references-using-special-syntax) * [References using the plain symbol name](#references-using-the-plain-symbol-name) * [References in files](#references-in-files) # Introduction A _symbol_ is a named constant, useful for defining a value once, and then refer to it from multiple places. Just like ordinary variables and constants in a programming language. [setup] def string GOOD = 'very good' def string MY_FILE = my-file.txt file @[MY_FILE]@ = < The possibility to have a digit as the first character of a symbol name might be removed in the future. > All symbol names in this document are upper case. The reason for this is to clarity. ## A symbol must be defined only once Trying to define a symbol twice causes a validation error. [setup] def string MY_SYMBOL = 'a value' def string MY_SYMBOL = 'a better value' Exactly will complain when executing this case: VALIDATE In [setup] Line 5: `def string MY_SYMBOL = 'a better value'' Symbol `MY_SYMBOL' has already been defined ... ## Symbol values are hard coded The value of a symbol is determined at "compile time", not at "run time". Or, in the terminology of Exactly: determined at "validation time", not at "execution time". What this means is that it is impossible to pick up a value from the environment, for example - the name of the current directory - the current date - the contents of a file - the output from a program This is a weakness. Maybe there will be a remedy for this in the future! # Referencing symbols Symbols are referenced using either the plain symbol name, or the special syntax @[_NAME_]@. The plain symbol name is used in contexts where the name is unambiguous. ## References using special syntax @[_NAME_]@ is a reference to the symbol _NAME_. @[_NAME_]@ might appear as a naked expression, or inside double quotes ("): def string MY_SYMBOL = "my value" def string MY_2ND_SYMBOL = @[MY_SYMBOL]@ def string MY_3RD_SYMBOL = "@[MY_2ND_SYMBOL]@ and some more" def list MY_LIST_SYMBOL = @[MY_SYMBOL]@ "@[MY_SYMBOL]@ and some more text" run my-prog @[MY_SYMBOL]@ "then @[MY_SYMBOL]@" "then last" Strings within single quotes (') are literary: '@[I_AM_NOT_A_SYMBOL__I_AM_A_PLAIN_STRING]@' If _NAME_ is not a valid symbol name, then Exactly will not complain, but just treat @[_NAME_]@ as a literal string. > The reason for this is that Exactly has limited capabilities for quoting. If a references with an "invalid" symbol name caused Exactly to fail, that would prevent using strings of the form "@[...]@" in tests. ## References using the plain symbol name The plain symbol name is used in contexts where the name is unambiguous: ### `path` values The "relativity" option `-rel` takes a plain symbol name as argument: def path DIR_WITH_FILES_FOR_EXPECTED_CONTENTS = ... ... stdout equals -contents-of -rel DIR_WITH_FILES_FOR_EXPECTED_CONTENTS stdout.txt ### Values of logic types The following types are "logic" types: - `line-matcher` - `file-matcher` - `text-transformer` Values of these types are always referred using the plain symbol name, since all context where such values are used are unambiguous: def line-matcher MY_LINE_MATCHER = ... def file-matcher MY_FILE_MATCHER = ... def text-transformer MY_STRING_TRANSFORMER = ... ... contents my-output-file.txt : any line : MY_LINE_MATCHER dir-contents my-output-dir : -selection MY_FILE_MATCHER num-files == 1 contents my-output-file.txt : -transformed-by MY_STRING_TRANSFORMER num-lines == 2 ## References in files Symbols references will only be substituted in files who's contents is given as a here document. In the first line of 'my-file.txt', "@[GOOD]@" will be substituted with "very good": def string GOOD = 'very good' file my-file.txt = <