Reputation: 2331
I'm defining a constant in my main module this way:
@end_digits_adjusters [11, 12, 14, 21, 22, 23]
Here's how I'm trying to test for it:
defmodule PriceOptimizerTest do
use ExUnit.Case
doctest PriceOptimizer
test "get_random_adjuster() randomizer" do
adj_num = PriceOptimizer.get_random_adjuster()
is_in? = adj_num in @end_digits_adjusters
assert is_in? == true
end
end
That doesn't work. But when I explicitly specify the constant value in the test it does work. Like this...
is_in? = adj_num in [11, 12, 14, 21, 22, 23]
Am I missing a step somewhere to get Elixir to recognize the module constants in the tests?
Upvotes: 2
Views: 1302
Reputation: 4507
Shared constants, which are popular in other languages, is not a popular pattern in Elixir. I have personally found that most of the time I don't need to use the pattern. But there are some applications I've built relay heavily on them.
When I do need them, I have a constants module I use the following module:
defmodule Constants do
@moduledoc """
An alternative to use @constant_name value approach to defined reusable
constants in elixir.
This module offers an approach to define these in a
module that can be shared with other modules. They are implemented with
macros so they can be used in guards and matches
## Examples:
Create a module to define your shared constants
defmodule MyConstants do
use Constants
define something, 10
define another, 20
end
Use the constants
defmodule MyModule do
require MyConstants
alias MyConstants, as: Const
def myfunc(item) when item == Const.something, do: Const.something + 5
def myfunc(item) when item == Const.another, do: Const.another
end
"""
defmacro __using__(_opts) do
quote do
import Constants
end
end
@doc "Define a constant"
defmacro constant(name, value) do
quote do
defmacro unquote(name), do: unquote(value)
end
end
@doc "Define a constant. An alias for constant"
defmacro define(name, value) do
quote do
constant unquote(name), unquote(value)
end
end
@doc """
Import an hrl file.
Create constants for each -define(NAME, value).
"""
defmacro import_hrl(file_name) do
list = parse_file file_name
quote bind_quoted: [list: list] do
for {name, value} <- list do
defmacro unquote(name)(), do: unquote(value)
end
end
end
defp parse_file(file_name) do
for line <- File.stream!(file_name, [], :line) do
parse_line line
end
|> Enum.filter(&(not is_nil(&1)))
end
defp parse_line(line) do
case Regex.run ~r/-define\((.+),(.+)\)\./, line do
nil -> nil
[_, name, value] ->
{String.strip(name) |> String.downcase |> String.to_atom, String.strip(value) |> parse_value}
_ -> nil
end
end
defp parse_value(string) do
case Integer.parse string do
:error -> filter_string(string)
{num, _} -> num
end
end
defp filter_string(string), do: String.replace(string, "\"", "")
end
Couple notes:
require
statements. The define
is a macro.#define
statements is only a few key strokes when using sublime's multi cursor and vi key bindings :)Upvotes: 5
Reputation: 9639
I'm afraid, Elixir's module attributes are available only within the module where are defined. This is due fact that those attributes are online in code during compilation phase.
If you want to make them publicly accessible, you need to wrap it with function, eg:
defmodule MyMod do
@test "hello"
def test, do: @test
end
So:
MyMod.test # => "hello"
Hope that helps!
Upvotes: 1