defmodule Mflask do # Publicly expose the library version at compile time (read from mix.exs). # This provides a stable, compile-time value accessible as `Mflask.mflask_version/0` # or `Mflask.version/0`. @mflask_version Mix.Project.config()[:version] @moduledoc """ Mflask — a Flask-style web framework for Elixir. ## Minimal app defmodule MyApp do use Mflask get "/" do text(conn, "Hello!") end end Mflask.run(MyApp, port: 4000) ## Composed app (no blueprints needed) defmodule MyApp do use Mflask plug Mflask.Middleware.Logger plug Mflask.Middleware.BodyParser mount "/api", MyApp.API mount "/admin", MyApp.Admin mount "/", MyApp.Web end Sub-routers use `use Mflask.Router` and are completely self-contained. No app context, no circular imports, no registration. defmodule MyApp.API do use Mflask.Router plug MyApp.RequireAuth get "/users" do json(conn, Users.list()) end end ## Response helpers Available in all route blocks: | Helper | Description | |--------|-------------| | `text(conn, body, status \\\\ 200)` | Plain text | | `json(conn, data, status \\\\ 200)` | JSON (any Jason-encodable term) | | `html(conn, body, status \\\\ 200)` | HTML string | | `redirect(conn, to, status \\\\ 302)` | Redirect | | `send_file(conn, path)` | Send a file | | `render(conn, template, assigns)` | EEx template | | `halt(conn)` | Stop plug pipeline | ## Request helpers | Helper | Description | |--------|-------------| | `params["key"]` | Path or query param | | `body_param(conn, "key")` | Body param (POST/JSON) | | `query_param(conn, "key")` | Query string param | | `get_header(conn, "x-foo")` | Request header | | `remote_ip(conn)` | Client IP string | | `get_cookie(conn, "name")` | Cookie value | """ defmacro __using__(_opts) do quote do import Mflask.Router, except: [plug: 1, plug: 2] import Mflask.Response import Mflask.Request import Mflask.Template Module.register_attribute(__MODULE__, :mflask_routes, accumulate: true) Module.register_attribute(__MODULE__, :mflask_plugs, accumulate: true) @before_compile Mflask.Router end end @doc """ Start the Mflask HTTP server (blocking — runs under a supervisor). ## Options - `:port` — port number (default: `4000`) - `:ip` — bind address (default: `{0, 0, 0, 0}`) ## Example Mflask.run(MyApp, port: 4000) For production or umbrella apps, add it to your supervision tree instead: children = [ {Bandit, plug: MyApp, port: 4000} ] """ def run(app_module, opts \\ []) do port = Keyword.get(opts, :port, 4000) ip = Keyword.get(opts, :ip, {0, 0, 0, 0}) children = [{Bandit, plug: app_module, port: port, ip: ip}] IO.puts(""" \e[33m🔥 Mflask #{@mflask_version}\e[0m \e[32m→\e[0m http://localhost:#{port} Press Ctrl+C to stop. """) {:ok, _} = Supervisor.start_link(children, strategy: :one_for_one) Process.sleep(:infinity) end @doc "Return the Mflask version (as defined in mix.exs) at compile time." @spec mflask_version() :: String.t() | nil def mflask_version, do: @mflask_version @doc "Alias for `mflask_version/0`." @spec version() :: String.t() | nil def version, do: @mflask_version end