Packages

Elixir library for feature flags integration with Consul

Current section

Files

Jump to
feature_flipper lib feature_flipper definition.ex
Raw

lib/feature_flipper/definition.ex

defmodule FeatureFlipper.Definition do
@moduledoc """
DSL for defining feature flippers.
This module provides macros for defining feature flippers with metadata
and automatically generates functions for each defined flipper.
## Usage
defmodule MyApp.FeatureFlippers do
use FeatureFlipper.Definition
define_flipper :use_warehouse_cache_tbs_2221?, %{
deadline: ~D[2025-06-30],
description: "Use warehouse cache for improved performance"
}
define_flipper :enable_warehouse_cache_mirroring_tbs_2395?, %{
deadline: ~D[2025-06-30],
force_disable: false,
description: "Enable cache mirroring for data consistency"
}
define_flipper :user_rate_tariff_manager_tbs_2873?, %{
deadline: ~D[2025-06-30],
key: "custom_tariff_key", # Optional custom Consul key
description: "Use new tariff manager for user rates"
}
end
This will automatically generate functions:
- `MyApp.FeatureFlippers.use_warehouse_cache_tbs_2221?()`
- `MyApp.FeatureFlippers.enable_warehouse_cache_mirroring_tbs_2395?()`
- `MyApp.FeatureFlippers.user_rate_tariff_manager_tbs_2873?()`
"""
@doc """
Use this module to enable the feature flipper DSL.
"""
defmacro __using__(_opts) do
quote do
import FeatureFlipper.Definition
Module.register_attribute(__MODULE__, :flippers, accumulate: true)
@before_compile FeatureFlipper.Definition
end
end
@doc """
Define a feature flipper with metadata.
## Parameters
* `name` - The name of the feature flipper (atom)
* `metadata` - A map containing flipper metadata
## Metadata Options
* `:deadline` - Date when the feature should be removed (Date struct)
* `:description` - Human-readable description of the feature
* `:force_disable` - Force disable in non-production environments (boolean, default: false)
* `:key` - Custom Consul key (string, optional)
## Examples
define_flipper :my_feature, %{
deadline: ~D[2025-06-30],
description: "My awesome feature",
force_disable: false
}
"""
defmacro define_flipper(name, metadata \\ %{}) do
quote do
@flippers {unquote(name), unquote(metadata)}
end
end
@doc false
defmacro __before_compile__(env) do
flippers = Module.get_attribute(env.module, :flippers)
# Store flippers in module attributes and register them
register_flippers_ast =
quote do
def __flippers__ do
unquote(Macro.escape(flippers))
end
# Register flippers immediately since we no longer have GenServer startup issues
FeatureFlipper.ConfigLoader.register_flippers(unquote(Macro.escape(flippers)))
end
# Generate functions for each flipper
flipper_functions =
Enum.map(flippers, fn {name, metadata} ->
generate_flipper_function(name, metadata)
end)
[register_flippers_ast | flipper_functions]
end
defp generate_flipper_function(name, metadata) do
consul_key = Map.get(metadata, :key, Atom.to_string(name))
description = Map.get(metadata, :description, "No description provided")
deadline = format_deadline(Map.get(metadata, :deadline, "No deadline set"))
quote do
@doc """
Check if the #{unquote(name)} feature flipper is enabled.
Description: #{unquote(description)}
Deadline: #{unquote(deadline)}
"""
def unquote(name)() do
FeatureFlipper.ConfigLoader.get_flipper_value(
unquote(name),
unquote(consul_key),
unquote(Macro.escape(metadata))
)
end
end
end
defp format_deadline(%Date{} = date) do
Date.to_string(date)
end
defp format_deadline(deadline) when is_binary(deadline) do
deadline
end
defp format_deadline(_) do
"No deadline set"
end
end