Packages
espec
0.8.6
1.10.0
1.9.2
1.9.1
1.9.0
1.8.3
1.8.2
1.8.1
1.8.0
1.7.0
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.2
1.2.1
1.2.0
1.1.2
1.1.1
1.1.0
1.0.1
1.0.0
0.8.28
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.7
0.3.5
0.3.0
0.2.0
0.1.0
BDD testing framework for Elixir inspired by RSpec.
Current section
Files
Jump to
Current section
Files
lib/espec/let.ex
defmodule ESpec.Let do
@moduledoc """
Defines 'let', 'let!' and 'subject' macrsos.
'let' and 'let!' macros define named functions with cached return values.
The 'let' evaluate block in runtime when called first time.
The 'let!' evaluates as a before block just after all 'befores' for example.
The 'subject' macro is just an alias for let to define `subject`.
"""
@doc "Struct keeps the name of variable and random function name."
defstruct var: nil, module: nil, function: nil, keep_quoted: nil
@doc "The name of Agent to save state for lets and subject"
@agent_name :espec_let_agent
@doc """
The macro defines funtion with random name which returns block value.
That function will be called when example is run.
The function will place the block value to the Agent dict.
"""
defmacro let(var, keep_quoted \\ true, do: block) do
function = random_let_name
if keep_quoted, do: block = Macro.escape(block)
quote do
tail = @context
head = %ESpec.Let{var: unquote(var), module: __MODULE__, function: unquote(function), keep_quoted: unquote(keep_quoted)}
def unquote(function)(var!(shared), keep_quoted) do
var!(shared)
{unquote(block), keep_quoted, var!(shared)}
end
@context [head | tail]
unless ESpec.Let.agent_get({__MODULE__, "already_defined_#{unquote(var)}"}) do
def unquote(var)() do
{result, keep_quoted, assigns} = ESpec.Let.agent_get({self, __MODULE__, unquote(var)})
if keep_quoted do
#TODO This __ENV__ hack is annoying
functions = [{__MODULE__, __MODULE__.__info__(:functions)} | __ENV__.functions]
env = %{__ENV__ | functions: functions}
{result, _assigns} = Code.eval_quoted(result, [shared: assigns], env)
ESpec.Let.agent_put({self, __MODULE__, unquote(var)}, {result, false, assigns})
result
else
result
end
end
ESpec.Let.agent_put({__MODULE__, "already_defined_#{unquote(var)}"}, true)
end
end
end
@doc "let! evaluate block like `before`"
defmacro let!(var, do: block) do
quote do: let(unquote(var), false, do: unquote(block))
end
@doc "Defines 'subject'."
defmacro subject(do: block) do
quote do: let(:subject, do: unquote(block))
end
@doc "Defines 'subject'."
defmacro subject(var) do
quote do: let(:subject, do: unquote(var))
end
@doc "Defines 'subject!'."
defmacro subject!(do: block) do
quote do: let!(:subject, do: unquote(block))
end
@doc "Defines 'subject!'."
defmacro subject!(var) do
quote do: let!(:subject, do: unquote(var))
end
@doc """
Defines 'subject' with name.
It is just an alias for 'let'.
"""
defmacro subject(var, do: block) do
quote do: let(unquote(var), do: unquote(block))
end
@doc """
Defines 'subject!' with name.
It is just an alias for 'let!'.
"""
defmacro subject!(var, do: block) do
quote do: let!(unquote(var), do: unquote(block))
end
@doc "Starts Agent to save state of 'lets'."
def start_agent, do: Agent.start_link(fn -> HashDict.new end, name: @agent_name)
@doc "Stops Agent"
def stop_agent, do: Agent.stop(@agent_name)
@doc "Get stored value."
def agent_get(key) do
dict = Agent.get(@agent_name, &(&1))
Dict.get(dict, key)
end
@doc "Store value."
def agent_put(key, value), do: Agent.update(@agent_name, &(Dict.put(&1, key, value)))
defp random_let_name, do: String.to_atom("let_#{ESpec.Support.random_string}")
end