Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
292 changes: 292 additions & 0 deletions lib/mix/tasks/fun_with_flags.install.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
if Code.ensure_loaded?(Igniter) do
defmodule Mix.Tasks.FunWithFlags.Install do
@shortdoc "Installs and configures FunWithFlags"

@moduledoc """
Installs and configures FunWithFlags for your application.

## Usage

mix fun_with_flags.install [options]

## Options

* `--persistence` - The persistence adapter to use. Options: `ecto`, `redis`. Defaults to `ecto`.
* `--pubsub` - The pubsub adapter to use. Options: `phoenix`, `redis`. Defaults to `phoenix`.
* `--repo` - The Ecto repo to use (required when persistence is `ecto`).
* `--table-name` - The Ecto table name. Defaults to `fun_with_flags_toggles`.

## Examples

# Install with Ecto persistence and Phoenix PubSub
mix fun_with_flags.install --persistence ecto --repo MyApp.Repo

# Install with Redis persistence and Redis PubSub
mix fun_with_flags.install --persistence redis --pubsub redis

"""

use Igniter.Mix.Task

@impl Igniter.Mix.Task
def info(_argv, _composing_task) do
%Igniter.Mix.Task.Info{
group: :fun_with_flags,
adds_deps: [],
installs: [],
example: "mix fun_with_flags.install --persistence ecto --repo MyApp.Repo",
only: nil,
positional: [],
schema: [
persistence: :string,
pubsub: :string,
repo: :string,
table_name: :string
],
defaults: [
persistence: "ecto",
pubsub: "phoenix",
table_name: "fun_with_flags_toggles"
],
aliases: [],
required: []
}
end

@impl Igniter.Mix.Task
def igniter(igniter) do
opts = igniter.args.options

persistence = opts[:persistence]
pubsub = opts[:pubsub]
repo = opts[:repo]
table_name = opts[:table_name]

validate_and_configure(igniter, persistence, pubsub, repo, table_name)
end

defp validate_and_configure(igniter, persistence, pubsub, repo, table_name) do
case {persistence, repo} do
{"ecto", nil} ->
select_repo_and_configure(igniter, pubsub, table_name)

{"ecto", repo_string} ->
repo_module = Module.concat([repo_string])
configure_with_ecto(igniter, repo_module, pubsub, table_name)

{"redis", _} ->
configure_with_redis(igniter, pubsub)

{invalid, _} ->
Igniter.add_issue(
igniter,
"Invalid persistence adapter: #{inspect(invalid)}. Must be 'ecto' or 'redis'."
)
end
end

defp select_repo_and_configure(igniter, pubsub, table_name) do
case Igniter.Libs.Ecto.select_repo(igniter, label: "Which Ecto repo should FunWithFlags use?") do
{igniter, nil} ->
Igniter.add_issue(
igniter,
"No Ecto repo found. Please specify a repo with --repo or ensure your project has an Ecto repo configured."
)

{igniter, repo} ->
configure_with_ecto(igniter, repo, pubsub, table_name)
end
end

defp configure_with_ecto(igniter, repo, pubsub, table_name) do
persistence_config =
{:code,
Sourceror.parse_string!("""
[adapter: FunWithFlags.Store.Persistent.Ecto, repo: #{inspect(repo)}]
""")}

igniter
|> configure_persistence(persistence_config)
|> configure_cache()
|> configure_pubsub(pubsub)
|> generate_ecto_migration(repo, table_name)
|> add_ecto_notices(repo, table_name)
end

defp configure_with_redis(igniter, pubsub) do
persistence_config =
{:code,
Sourceror.parse_string!("""
[adapter: FunWithFlags.Store.Persistent.Redis]
""")}

igniter
|> configure_persistence(persistence_config)
|> configure_redis()
|> configure_cache()
|> configure_pubsub(pubsub)
|> add_redis_notices()
end

defp configure_persistence(igniter, persistence_config) do
Igniter.Project.Config.configure(
igniter,
"config.exs",
:fun_with_flags,
[:persistence],
persistence_config
)
end

defp configure_redis(igniter) do
redis_config =
{:code,
Sourceror.parse_string!("""
[host: "localhost", port: 6379, database: 0]
""")}

Igniter.Project.Config.configure_new(
igniter,
"config.exs",
:fun_with_flags,
[:redis],
redis_config
)
end

defp configure_cache(igniter) do
cache_config =
{:code,
Sourceror.parse_string!("""
[enabled: true, ttl: 900]
""")}

Igniter.Project.Config.configure_new(
igniter,
"config.exs",
:fun_with_flags,
[:cache],
cache_config
)
end

defp configure_pubsub(igniter, "phoenix") do
pubsub_config =
{:code,
Sourceror.parse_string!("""
[adapter: FunWithFlags.Notifications.PhoenixPubSub, client: nil]
""")}

igniter
|> Igniter.Project.Config.configure(
"config.exs",
:fun_with_flags,
[:cache_bust_notifications],
pubsub_config
)
|> Igniter.add_notice("""
FunWithFlags is configured to use Phoenix.PubSub for cache notifications.

You need to set the :client option in your config to your PubSub module:

config :fun_with_flags, :cache_bust_notifications,
adapter: FunWithFlags.Notifications.PhoenixPubSub,
client: MyApp.PubSub
""")
end

defp configure_pubsub(igniter, "redis") do
pubsub_config =
{:code,
Sourceror.parse_string!("""
[adapter: FunWithFlags.Notifications.Redis]
""")}

Igniter.Project.Config.configure(
igniter,
"config.exs",
:fun_with_flags,
[:cache_bust_notifications],
pubsub_config
)
end

defp configure_pubsub(igniter, invalid) do
Igniter.add_issue(
igniter,
"Invalid pubsub adapter: #{inspect(invalid)}. Must be 'phoenix' or 'redis'."
)
end

defp generate_ecto_migration(igniter, repo, table_name) do
migration_body = """
def change do
create table(:#{table_name}) do
add :flag_name, :string, null: false
add :gate_type, :string, null: false
add :target, :string, null: false
add :enabled, :boolean, null: false
end

create index(:#{table_name}, [:flag_name])
create unique_index(:#{table_name}, [:flag_name, :gate_type, :target], name: "fwf_flag_name_gate_target_idx")
end
"""

Igniter.Libs.Ecto.gen_migration(
igniter,
repo,
"create_fun_with_flags_table",
body: migration_body,
on_exists: :skip
)
end

defp add_ecto_notices(igniter, repo, table_name) do
Igniter.add_notice(igniter, """
FunWithFlags has been configured with Ecto persistence.

Summary:
- Persistence: Ecto (#{inspect(repo)})
- Table name: #{table_name}

Next steps:
1. Run `mix deps.get` to fetch dependencies
2. Run `mix ecto.migrate` to create the flags table
3. Start using FunWithFlags in your application!

Basic usage:
FunWithFlags.enabled?(:my_feature)
FunWithFlags.enable(:my_feature)
FunWithFlags.disable(:my_feature)
""")
end

defp add_redis_notices(igniter) do
Igniter.add_notice(igniter, """
FunWithFlags has been configured with Redis persistence.

Summary:
- Persistence: Redis
- Default Redis config: localhost:6379, database 0

Next steps:
1. Run `mix deps.get` to fetch dependencies
2. Ensure you have `{:redix, "~> 1.0"}` in your dependencies
3. Configure your Redis connection if needed:

config :fun_with_flags, :redis,
host: "localhost",
port: 6379,
database: 0

4. Start using FunWithFlags in your application!

Basic usage:
FunWithFlags.enabled?(:my_feature)
FunWithFlags.enable(:my_feature)
FunWithFlags.disable(:my_feature)
""")
end
end
end
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ defmodule FunWithFlags.Mixfile do
{:myxql, "~> 0.2", optional: true, only: [:dev, :test]},
{:phoenix_pubsub, "~> 2.0", optional: true},
{:telemetry, "~> 1.3"},
{:igniter, "~> 0.6", optional: true},

{:mock, "~> 0.3", only: :test},

Expand Down
13 changes: 13 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,31 @@
"ex_doc": {:hex, :ex_doc, "0.38.4", "ab48dff7a8af84226bf23baddcdda329f467255d924380a0cf0cee97bb9a9ede", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "f7b62346408a83911c2580154e35613eb314e0278aeea72ed7fedef9c1f165b2"},
"exqlite": {:hex, :exqlite, "0.33.0", "2cc96c4227fbb2d0864716def736dff18afb9949b1eaa74630822a0865b4b342", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "8a7c2792e567bbebb4dafe96f6397f1c527edd7039d74f508a603817fbad2844"},
"file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"},
"finch": {:hex, :finch, "0.20.0", "5330aefb6b010f424dcbbc4615d914e9e3deae40095e73ab0c1bb0968933cadf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2658131a74d051aabfcba936093c903b8e89da9a1b63e430bee62045fa9b2ee2"},
"glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"},
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
"igniter": {:hex, :igniter, "0.7.0", "6848714fa5afa14258c82924a57af9364745316241a409435cf39cbe11e3ae80", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "1e7254780dbf4b44c9eccd6d86d47aa961efc298d7f520c24acb0258c8e90ba9"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"},
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
"mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
"mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"},
"mock": {:hex, :mock, "0.3.9", "10e44ad1f5962480c5c9b9fa779c6c63de9bd31997c8e04a853ec990a9d841af", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "9e1b244c4ca2551bb17bb8415eed89e40ee1308e0fbaed0a4fdfe3ec8a4adbd3"},
"myxql": {:hex, :myxql, "0.8.0", "60c60e87c7320d2f5759416aa1758c8e7534efbae07b192861977f8455e35acd", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:geo, "~> 3.4 or ~> 4.0", [hex: :geo, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "1ec0ceb26fb3cd0f8756519cf4f0e4f9348177a020705223bdf4742a2c44d774"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"owl": {:hex, :owl, "0.13.0", "26010e066d5992774268f3163506972ddac0a7e77bfe57fa42a250f24d6b876e", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "59bf9d11ce37a4db98f57cb68fbfd61593bf419ec4ed302852b6683d3d2f7475"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
"postgrex": {:hex, :postgrex, "0.21.1", "2c5cc830ec11e7a0067dd4d623c049b3ef807e9507a424985b8dcf921224cd88", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "27d8d21c103c3cc68851b533ff99eef353e6a0ff98dc444ea751de43eb48bdac"},
"redix": {:hex, :redix, "1.5.2", "ab854435a663f01ce7b7847f42f5da067eea7a3a10c0a9d560fa52038fd7ab48", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:nimble_options, "~> 0.5.0 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "78538d184231a5d6912f20567d76a49d1be7d3fca0e1aaaa20f4df8e1142dcb8"},
"req": {:hex, :req, "0.5.17", "0096ddd5b0ed6f576a03dde4b158a0c727215b15d2795e59e0916c6971066ede", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0b8bc6ffdfebbc07968e59d3ff96d52f2202d0536f10fef4dc11dc02a2a43e39"},
"rewrite": {:hex, :rewrite, "1.2.0", "80220eb14010e175b67c939397e1a8cdaa2c32db6e2e0a9d5e23e45c0414ce21", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "a1cd702bbb9d51613ab21091f04a386d750fc6f4516b81900df082d78b2d8c50"},
"sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"},
"spitfire": {:hex, :spitfire, "0.2.1", "29e154873f05444669c7453d3d931820822cbca5170e88f0f8faa1de74a79b47", [:mix], [], "hexpm", "6eeed75054a38341b2e1814d41bb0a250564092358de2669fdb57ff88141d91b"},
"statistex": {:hex, :statistex, "1.1.0", "7fec1eb2f580a0d2c1a05ed27396a084ab064a40cfc84246dbfb0c72a5c761e5", [:mix], [], "hexpm", "f5950ea26ad43246ba2cce54324ac394a4e7408fdcf98b8e230f503a0cba9cf5"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"},
}