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.
Define a strategy.
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}
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.
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