Skip to content

Commit

Permalink
Feat: Route and Resource Loading Add --path to beacon.install generat…
Browse files Browse the repository at this point in the history
…or (#387)
  • Loading branch information
edborsa authored Jan 9, 2024
1 parent 609eb72 commit bbed1a5
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 7 deletions.
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, "/")
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

0 comments on commit bbed1a5

Please sign in to comment.