View Source Skitter.DSL.Strategy (Skitter v0.7.1)

Strategy and Hook definition DSL.

This module offers macros to define a strategy and its hooks. A strategy is defined through the use of the defstrategy/3 macro. Inside the body of defstrategy/3, defhook/2 can be used to define a hook. The other macros in this module can be used inside the body of defhook/2 to obtain information from the current Skitter.Strategy.context/0. We recommend reading the documentation of defstrategy/3 to get started.

Summary

Functions

Obtain the context struct.

Define a hook.

Obtain the context's deployment.

Obtain the context's operation.

Obtain the context's strategy.

Functions

Obtain the context struct.

A strategy hook is called with a Skitter.Strategy.context/0 as its first argument. This macro is used to obtain this struct.

Examples

iex> defstrategy FullContext do
...>   defhook read, do: context()
...> end
iex> FullContext.read(%Context{operation: SomeOperation})
%Context{operation: SomeOperation}
Link to this macro

defhook(clause, list)

View Source (macro)

Define a hook.

This macro defines a strategy hook. Hooks are functions called by the Skitter runtime system in response to predefined events (described in Skitter.Strategy.Operation), or by other hooks. Internally, hooks are plain Elixir functions which accept a Skitter.Strategy.context/0 as their first argument. This macro generates a plain Elixir function which accepts such a context.

Said otherwise, the following two definitions are equivalent:

def my_hook(context, arg1, arg2), do: ...
defhook my_hook(arg1, arg2), do: ...

Using the defhook/2 macro, however, ensures hooks can be inherited (as described in defstrategy/3), and offers access to the macros defined in Skitter.DSL.Strategy.Helpers, which provide the building blocks required to build a strategy. The context/0, operation/0, strategy/0 and deployment/0 macros can be used to obtain the information stored in the context the hook was called with.

Calling hooks

Since hooks are plain Elixir functions, they may be called like any other function. However, a Skitter.Strategy.context/0 must be provided:

iex> defstrategy Strategy do
...>   defhook hook, do: "hello"
...> end
iex> Strategy.hook(%Context{})
"hello"

When a hook calls another hook (defined in another strategy or in the same strategy), it must use the context/0 macro to pass the current context.

iex> defstrategy S1 do
...>   defhook example, do: "world!"
...> end
iex> defstrategy S2 do
...>   defhook example, do: "Hello, " <> S1.example(context())
...> end
iex> S2.example(%Context{})
"Hello, world!"

iex> defstrategy Local do
...>   defhook left, do: "Hello, "
...>   defhook right, do: "world!"
...>   defhook example, do: left(context()) <> right(context())
...> end
iex> Local.example(%Context{})
"Hello, world!"

Since the context contains the current strategy, it is possible to dynamically call the current strategy. However, the syntax for this is rather unwieldy:

iex> defstrategy AbstractAnimal do
...>   defhook example, do: "Animal says: " <> strategy().say(context())
...> end
iex> defstrategy Dog, extends: AbstractAnimal do
...>   defhook say, do: "woof!"
...> end
iex> Dog.example(%Context{strategy: Dog})
"Animal says: woof!"
iex> defstrategy Cat, extends: AbstractAnimal do
...>   defhook say, do: "meow!"
...> end
iex> Cat.example(%Context{strategy: Cat})
"Animal says: meow!"

Note that we have to explicitly pass the strategy as a part of the context in the examples above. In a real application, the Skitter Runtime calls hooks, ensuring the appropriate context is passed.

Link to this macro

defstrategy(name, opts \\ [], list)

View Source (macro)

Define a strategy.

This macro is used to define a strategy. A Skitter.Strategy is defined as a regular Elixir module which defines several hooks. As such, any code that is valid inside an Elixir module (such as function definitions or module attributes) is valid inside defstrategy/3. Additionally, the defhook/2 macro may be used to define a Skitter hook. Hooks are special Elixir functions which accept a Skitter.Strategy.context/0 argument.

Extending Strategies

Strategies can be created based on existing strategies. This is done by extending some strategy. When a strategy extends another strategy, it will inherit all the hooks defined by the strategy it extends:

iex> defstrategy Parent do
...>   defhook example, do: :example_hook
...> end
iex> defstrategy Child, extends: Parent do
...> end
iex> Child.example(%Context{})
:example_hook

Inherited hooks can be overridden:

iex> defstrategy Parent do
...>   defhook example, do: :parent
...> end
iex> defstrategy Child, extends: Parent do
...>   defhook example, do: :child
...> end
iex> Child.example(%Context{})
:child

Finally, a strategy can extend multiple parent strategies. When this is done, the hooks of earlier parent strategies take precedence over later hooks:

iex> defstrategy Parent1 do
...>   defhook example, do: :parent1
...> end
iex> defstrategy Parent2 do
...>   defhook example, do: :parent2
...>   defhook another, do: :parent2
...> end
iex> defstrategy Child, extends: [Parent1, Parent2] do
...> end
iex> Child.example(%Context{})
:parent1
iex> Child.another(%Context{})
:parent2

Obtain the context's deployment.

Examples

iex> defstrategy ReadDeployment do
...>   defhook read, do: deployment()
...> end
iex> ReadDeployment.read(%Context{deployment: :some_deployment_data})
:some_deployment_data

Obtain the context's operation.

Examples

iex> defstrategy ReadOperation do
...>   defhook read, do: operation()
...> end
iex> ReadOperation.read(%Context{operation: SomeOperation})
SomeOperation

Obtain the context's strategy.

Examples

iex> defstrategy ReadStrategy do
...>   defhook read, do: strategy()
...> end
iex> ReadStrategy.read(%Context{strategy: ReadStrategy})
Skitter.DSL.StrategyTest.ReadStrategy