1defmodule Phoenix.Router.Resource do 2 # This module defines the Resource struct that is used 3 # throughout Phoenix's router. This struct is private 4 # as it contains internal routing information. 5 @moduledoc false 6 7 alias Phoenix.Router.Resource 8 9 @default_param_key "id" 10 @actions [:index, :edit, :new, :show, :create, :update, :delete] 11 12 @doc """ 13 The `Phoenix.Router.Resource` struct. It stores: 14 15 * :path - the path as string (not normalized) 16 * :param - the param to be used in routes (not normalized) 17 * :controller - the controller as an atom 18 * :actions - a list of actions as atoms 19 * :route - the context for resource routes 20 * :member - the context for member routes 21 * :collection - the context for collection routes 22 23 """ 24 defstruct [:path, :actions, :param, :route, :controller, :route, :member, :collection, :singleton] 25 @type t :: %Resource{} 26 27 @doc """ 28 Builds a resource struct. 29 """ 30 def build(path, controller, options) when is_atom(controller) and is_list(options) do 31 path = Phoenix.Router.Scope.validate_path(path) 32 alias = Keyword.get(options, :alias) 33 param = Keyword.get(options, :param, @default_param_key) 34 name = Keyword.get(options, :name, Phoenix.Naming.resource_name(controller, "Controller")) 35 as = Keyword.get(options, :as, name) 36 private = Keyword.get(options, :private, %{}) 37 assigns = Keyword.get(options, :assigns, %{}) 38 39 singleton = Keyword.get(options, :singleton, false) 40 actions = extract_actions(options, singleton) 41 42 route = [as: as, private: private, assigns: assigns] 43 collection = [path: path, as: as, private: private, assigns: assigns] 44 member_path = if singleton, do: path, else: Path.join(path, ":#{name}_#{param}") 45 member = [path: member_path, as: as, alias: alias, private: private, assigns: assigns] 46 47 %Resource{path: path, actions: actions, param: param, route: route, 48 member: member, collection: collection, controller: controller, singleton: singleton} 49 end 50 51 defp extract_actions(opts, singleton) do 52 only = Keyword.get(opts, :only) 53 except = Keyword.get(opts, :except) 54 55 cond do 56 only -> 57 supported_actions = validate_actions(:only, singleton, only) 58 supported_actions -- (supported_actions -- only) 59 60 except -> 61 supported_actions = validate_actions(:except, singleton, except) 62 supported_actions -- except 63 64 true -> default_actions(singleton) 65 end 66 end 67 68 defp validate_actions(type, singleton, actions) do 69 supported_actions = default_actions(singleton) 70 71 unless actions -- supported_actions == [], do: raise ArgumentError, """ 72 invalid :#{type} action(s) passed to resources. 73 74 supported#{if singleton, do: " singleton", else: ""} actions: #{inspect(default_actions(singleton))} 75 76 got: #{inspect(actions)} 77 """ 78 79 supported_actions 80 end 81 82 defp default_actions(true = _singleton), do: @actions -- [:index] 83 defp default_actions(false = _singleton), do: @actions 84end 85