Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Route and Resource Loading Add --path to beacon.install generator #387

Merged
merged 6 commits into from
Jan 9, 2024
Merged
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
9 changes: 9 additions & 0 deletions lib/beacon/types/site.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ defmodule Beacon.Types.Site do
def valid?(site) when site in ["", nil, true, false], do: false

def valid?(site) when is_binary(site) do
Regex.match?(~r/^[a-zA-Z0-9_]+$/, site)
end

def valid?(site) when is_atom(site) do
Expand All @@ -44,6 +45,14 @@ defmodule Beacon.Types.Site do
not String.starts_with?(site, "beacon_")
end

def valid_path?(path) when is_atom(path) do
path |> Atom.to_string() |> valid_path?()
end

def valid_path?(path) when is_binary(path) do
String.starts_with?(path, "/")
edborsa marked this conversation as resolved.
Show resolved Hide resolved
end

@doc false
def safe_to_atom(site) when is_atom(site), do: site
def safe_to_atom(site) when is_binary(site), do: String.to_existing_atom(site)
Expand Down
35 changes: 29 additions & 6 deletions lib/mix/tasks/install.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ defmodule Mix.Tasks.Beacon.Install do
use Mix.Task

@switches [
site: :string
site: :string,
path: :string
]

def run(argv) do
Expand Down Expand Up @@ -274,7 +275,10 @@ defmodule Mix.Tasks.Beacon.Install do
end

defp build_context_bindings(options) do
options = validate_options!(options)
options =
options
|> add_default_options_if_missing()
|> validate_options!()

base_module = Mix.Phoenix.base()
web_module = Mix.Phoenix.web_module(base_module)
Expand All @@ -285,6 +289,7 @@ defmodule Mix.Tasks.Beacon.Install do
templates_path = Path.join([Application.app_dir(:beacon), "priv", "templates"])
root = root_path()
site = Keyword.get(options, :site)
path = Keyword.get(options, :path)

[
base_module: base_module,
Expand All @@ -293,6 +298,7 @@ defmodule Mix.Tasks.Beacon.Install do
ctx_app: ctx_app,
templates_path: templates_path,
site: site,
path: path,
beacon_data_source: %{
dest_path: Path.join([root, lib_path, "beacon_data_source.ex"]),
template_path: Path.join([templates_path, "install", "beacon_data_source.ex"]),
Expand Down Expand Up @@ -326,6 +332,8 @@ defmodule Mix.Tasks.Beacon.Install do
mix beacon.install expect a site name, for example:

mix beacon.install --site blog
or
mix beacon.install --site blog --path "/blog_path"
""")
end

Expand All @@ -334,12 +342,27 @@ defmodule Mix.Tasks.Beacon.Install do
end

defp validate_options!(options) do
site = String.to_atom(options[:site])

cond do
!Beacon.Types.Site.valid?(site) -> raise_with_help!("Invalid site name. It should not contain special characters.")
!Beacon.Types.Site.valid_name?(site) -> raise_with_help!("Invalid site name. The site name can't start with \"beacon_\".")
!Beacon.Types.Site.valid?(options[:site]) -> raise_with_help!("Invalid site name. It should not contain special characters.")
!Beacon.Types.Site.valid_name?(options[:site]) -> raise_with_help!("Invalid site name. The site name can't start with \"beacon_\".")
!Beacon.Types.Site.valid_path?(options[:path]) -> raise_with_help!("Invalid path value. The path value have to start with /.")
:default -> options
end
end

defp add_default_options_if_missing(options) do
defaults =
@switches
|> Keyword.keys()
|> Enum.reduce([], fn
:path, acc ->
site = Keyword.get(options, :site)
[{:path, "/#{site}"} | acc]

_key, acc ->
acc
end)

Keyword.merge(defaults, options)
end
end
2 changes: 1 addition & 1 deletion priv/templates/install/beacon_router_scope.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@

scope "/" do
pipe_through :browser
beacon_site "/<%= site %>", site: :<%= site %>
beacon_site "<%= path %>", site: :<%= site %>
end
10 changes: 10 additions & 0 deletions test/beacon/types/site_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,14 @@ defmodule Beacon.Types.SiteTest do
refute Site.valid_name?("beacon_some_name")
end
end

describe "valid_path?/1" do
test "SUCCESS: Return TRUE if it is a valid path" do
assert Site.valid_path?("/some_name")
end

test "SUCCESS: Return FALSE if it is an invalid name" do
refute Site.valid_path?("forgot_slash_at_beginning")
end
end
end
67 changes: 67 additions & 0 deletions test/mix/tasks/install_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -448,10 +448,77 @@ defmodule Mix.Tasks.Beacon.InstallTest do
Install.run(["--site", "beacon_"])
end

# Invalid path value
assert_raise Mix.Error, fn ->
Install.run(["--site", "blog", "--path", "forgot_slash_at_beginning"])
end

# Invalid option
assert_raise OptionParser.ParseError, ~r/1 error found!\n--invalid-argument : Unknown option/, fn ->
Install.run(["--invalid-argument", "invalid"])
end
end)
end

test "SUCCESS: New flag --path value updates beacon_path" do
Mix.Project.in_project(:my_app, ".", fn _module ->
Install.run(["--site", "my_site", "--path", "/some_other_path"])

# Injects beacon scope into router file
assert File.read!(@router_path) ==
"""
defmodule MyAppWeb.Router do
use MyAppWeb, :router

pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_live_flash
plug :put_root_layout, html: {MyAppWeb.Layouts, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
end

pipeline :api do
plug :accepts, ["json"]
end

scope "/", MyAppWeb do
pipe_through :browser

get "/", PageController, :home
end

# Other scopes may use custom stacks.
# scope "/api", MyAppWeb do
# pipe_through :api
# end

# Enable LiveDashboard and Swoosh mailbox preview in development
if Application.compile_env(:test_app, :dev_routes) do
# If you want to use the LiveDashboard in production, you should put
# it behind authentication and allow only admins to access it.
# If your application does not have an admins-only section yet,
# you can use Plug.BasicAuth to set up some basic authentication
# as long as you are also using SSL (which you should anyway).
import Phoenix.LiveDashboard.Router

scope "/dev" do
pipe_through :browser

live_dashboard "/dashboard", metrics: MyAppWeb.Telemetry
forward "/mailbox", Plug.Swoosh.MailboxPreview
end
end

use Beacon.Router

scope "/" do
pipe_through :browser
beacon_site "/some_other_path", site: :my_site
end
end
"""
end)
end
end
Loading