@@ -6,12 +6,15 @@ defmodule Phoenix.Router do
6
6
defexception plug_status: 404 , message: "no route found" , conn: nil , router: nil
7
7
8
8
def exception ( opts ) do
9
- conn = Keyword . fetch! ( opts , :conn )
9
+ conn = Keyword . fetch! ( opts , :conn )
10
10
router = Keyword . fetch! ( opts , :router )
11
- path = "/" <> Enum . join ( conn . path_info , "/" )
11
+ path = "/" <> Enum . join ( conn . path_info , "/" )
12
12
13
- % NoRouteError { message: "no route found for #{ conn . method } #{ path } (#{ inspect router } )" ,
14
- conn: conn , router: router }
13
+ % NoRouteError {
14
+ message: "no route found for #{ conn . method } #{ path } (#{ inspect ( router ) } )" ,
15
+ conn: conn ,
16
+ router: router
17
+ }
15
18
end
16
19
end
17
20
@@ -100,16 +103,40 @@ defmodule Phoenix.Router do
100
103
GET /pages/hey/there/world
101
104
%{"page" => "y", "rest" => ["there" "world"]} = params
102
105
106
+ > #### Why the macros? {: .info}
107
+ >
108
+ > Phoenix does its best to keep the usage of macros low. You may have noticed,
109
+ > however, that the `Phoenix.Router` relies heavily on macros. Why is that?
110
+ >
111
+ > We use `get`, `post`, `put`, and `delete` to define your routes. We use macros
112
+ > for two purposes:
113
+ >
114
+ > * They define the routing engine, used on every request, to choose which
115
+ > controller to dispatch the request to. Thanks to macros, Phoenix compiles
116
+ > all of your routes to a single case-statement with pattern matching rules,
117
+ > which is heavily optimized by the Erlang VM
118
+ >
119
+ > * For each route you define, we also define metadata to implement `Phoenix.VerifiedRoutes`.
120
+ > As we will soon learn, verified routes allows to us to reference any route
121
+ > as if it is a plain looking string, except it is verified by the compiler
122
+ > to be valid (making it much harder to ship broken links, forms, mails, etc
123
+ > to production)
124
+ >
125
+ > In other words, the router relies on macros to build applications that are
126
+ > faster and safer. Also remember that macros in Elixir are compile-time only,
127
+ > which gives plenty of stability after the code is compiled. Phoenix also provides
128
+ > introspection for all defined routes via `mix phx.routes`.
129
+
103
130
## Generating routes
104
131
105
132
For generating routes inside your application, see the `Phoenix.VerifiedRoutes`
106
133
documentation for `~p` based route generation which is the preferred way to
107
134
generate route paths and URLs with compile-time verification.
108
135
109
136
Phoenix also supports generating function helpers, which was the default
110
- mechanism in Phoenix v1.6 and earlier. we will explore it next.
137
+ mechanism in Phoenix v1.6 and earlier. We will explore it next.
111
138
112
- ### Helpers
139
+ ### Helpers (deprecated)
113
140
114
141
Phoenix generates a module `Helpers` inside your router by default, which contains
115
142
named helpers to help developers generate and keep their routes up to date.
@@ -367,30 +394,52 @@ defmodule Phoenix.Router do
367
394
opts = resource . route
368
395
369
396
if resource . singleton do
370
- Enum . each resource . actions , fn
371
- :show -> get path , ctrl , :show , opts
372
- :new -> get path <> "/new" , ctrl , :new , opts
373
- :edit -> get path <> "/edit" , ctrl , :edit , opts
374
- :create -> post path , ctrl , :create , opts
375
- :delete -> delete path , ctrl , :delete , opts
376
- :update ->
397
+ Enum . each ( resource . actions , fn
398
+ :show ->
399
+ get path , ctrl , :show , opts
400
+
401
+ :new ->
402
+ get path <> "/new" , ctrl , :new , opts
403
+
404
+ :edit ->
405
+ get path <> "/edit" , ctrl , :edit , opts
406
+
407
+ :create ->
408
+ post path , ctrl , :create , opts
409
+
410
+ :delete ->
411
+ delete path , ctrl , :delete , opts
412
+
413
+ :update ->
377
414
patch path , ctrl , :update , opts
378
- put path , ctrl , :update , Keyword . put ( opts , :as , nil )
379
- end
415
+ put path , ctrl , :update , Keyword . put ( opts , :as , nil )
416
+ end )
380
417
else
381
418
param = resource . param
382
419
383
- Enum . each resource . actions , fn
384
- :index -> get path , ctrl , :index , opts
385
- :show -> get path <> "/:" <> param , ctrl , :show , opts
386
- :new -> get path <> "/new" , ctrl , :new , opts
387
- :edit -> get path <> "/:" <> param <> "/edit" , ctrl , :edit , opts
388
- :create -> post path , ctrl , :create , opts
389
- :delete -> delete path <> "/:" <> param , ctrl , :delete , opts
390
- :update ->
420
+ Enum . each ( resource . actions , fn
421
+ :index ->
422
+ get path , ctrl , :index , opts
423
+
424
+ :show ->
425
+ get path <> "/:" <> param , ctrl , :show , opts
426
+
427
+ :new ->
428
+ get path <> "/new" , ctrl , :new , opts
429
+
430
+ :edit ->
431
+ get path <> "/:" <> param <> "/edit" , ctrl , :edit , opts
432
+
433
+ :create ->
434
+ post path , ctrl , :create , opts
435
+
436
+ :delete ->
437
+ delete path <> "/:" <> param , ctrl , :delete , opts
438
+
439
+ :update ->
391
440
patch path <> "/:" <> param , ctrl , :update , opts
392
- put path <> "/:" <> param , ctrl , :update , Keyword . put ( opts , :as , nil )
393
- end
441
+ put path <> "/:" <> param , ctrl , :update , Keyword . put ( opts , :as , nil )
442
+ end )
394
443
end
395
444
end
396
445
end
@@ -399,7 +448,10 @@ defmodule Phoenix.Router do
399
448
@ doc false
400
449
def __call__ (
401
450
% { private: % { phoenix_router: router , phoenix_bypass: { router , pipes } } } = conn ,
402
- metadata , prepare , pipeline , _
451
+ metadata ,
452
+ prepare ,
453
+ pipeline ,
454
+ _
403
455
) do
404
456
conn = prepare . ( conn , metadata )
405
457
@@ -472,13 +524,13 @@ defmodule Phoenix.Router do
472
524
def call ( conn , _opts ) do
473
525
% { method: method , path_info: path_info , host: host } = conn = prepare ( conn )
474
526
527
+ # TODO: Remove try/catch on Elixir v1.13 as decode no longer raises
475
528
decoded =
476
- # TODO: Remove try/catch on Elixir v1.13 as decode no longer raises
477
529
try do
478
530
Enum . map ( path_info , & URI . decode / 1 )
479
531
rescue
480
532
ArgumentError ->
481
- raise MalformedURIError , "malformed URI path: #{ inspect conn . request_path } "
533
+ raise MalformedURIError , "malformed URI path: #{ inspect ( conn . request_path ) } "
482
534
end
483
535
484
536
case __match_route__ ( decoded , method , host ) do
@@ -490,7 +542,7 @@ defmodule Phoenix.Router do
490
542
end
491
543
end
492
544
493
- defoverridable [ init: 1 , call: 2 ]
545
+ defoverridable init: 1 , call: 2
494
546
end
495
547
end
496
548
@@ -616,9 +668,9 @@ defmodule Phoenix.Router do
616
668
quote line: route . line do
617
669
def __match_route__ ( unquote ( path ) , unquote ( verb_match ) , unquote ( host ) ) do
618
670
{ unquote ( build_metadata ( route , path_params ) ) ,
619
- fn var! ( conn , :conn ) , % { path_params: var! ( path_params , :conn ) } -> unquote ( prepare ) end ,
620
- & unquote ( Macro . var ( pipe_name , __MODULE__ ) ) / 1 ,
621
- unquote ( dispatch ) }
671
+ fn var! ( conn , :conn ) , % { path_params: var! ( path_params , :conn ) } ->
672
+ unquote ( prepare )
673
+ end , & ( unquote ( Macro . var ( pipe_name , __MODULE__ ) ) / 1 ) , unquote ( dispatch ) }
622
674
end
623
675
end
624
676
end
@@ -669,7 +721,7 @@ defmodule Phoenix.Router do
669
721
end
670
722
671
723
defp build_pipes ( name , pipe_through ) do
672
- plugs = pipe_through |> Enum . reverse |> Enum . map ( & { & 1 , [ ] , true } )
724
+ plugs = pipe_through |> Enum . reverse ( ) |> Enum . map ( & { & 1 , [ ] , true } )
673
725
opts = [ init_mode: Phoenix . plug_init_mode ( ) , log_on_halt: :debug ]
674
726
{ conn , body } = Plug.Builder . compile ( __ENV__ , plugs , opts )
675
727
@@ -733,15 +785,15 @@ defmodule Phoenix.Router do
733
785
defp add_route ( kind , verb , path , plug , plug_opts , options ) do
734
786
quote do
735
787
@ phoenix_routes Scope . route (
736
- __ENV__ . line ,
737
- __ENV__ . module ,
738
- unquote ( kind ) ,
739
- unquote ( verb ) ,
740
- unquote ( path ) ,
741
- unquote ( plug ) ,
742
- unquote ( plug_opts ) ,
743
- unquote ( options )
744
- )
788
+ __ENV__ . line ,
789
+ __ENV__ . module ,
790
+ unquote ( kind ) ,
791
+ unquote ( verb ) ,
792
+ unquote ( path ) ,
793
+ unquote ( plug ) ,
794
+ unquote ( plug_opts ) ,
795
+ unquote ( options )
796
+ )
745
797
end
746
798
end
747
799
@@ -786,8 +838,9 @@ defmodule Phoenix.Router do
786
838
compiler =
787
839
quote unquote: false do
788
840
Scope . pipeline ( __MODULE__ , plug )
789
- { conn , body } = Plug.Builder . compile ( __ENV__ , @ phoenix_pipeline ,
790
- init_mode: Phoenix . plug_init_mode ( ) )
841
+
842
+ { conn , body } =
843
+ Plug.Builder . compile ( __ENV__ , @ phoenix_pipeline , init_mode: Phoenix . plug_init_mode ( ) )
791
844
792
845
def unquote ( plug ) ( unquote ( conn ) , _ ) do
793
846
try do
@@ -800,6 +853,7 @@ defmodule Phoenix.Router do
800
853
Plug.Conn.WrapperError . reraise ( unquote ( conn ) , :error , reason , __STACKTRACE__ )
801
854
end
802
855
end
856
+
803
857
@ phoenix_pipeline nil
804
858
end
805
859
@@ -823,7 +877,7 @@ defmodule Phoenix.Router do
823
877
824
878
quote do
825
879
if pipeline = @ phoenix_pipeline do
826
- @ phoenix_pipeline [ { unquote ( plug ) , unquote ( opts ) , true } | pipeline ]
880
+ @ phoenix_pipeline [ { unquote ( plug ) , unquote ( opts ) , true } | pipeline ]
827
881
else
828
882
raise "cannot define plug at the router level, plug must be defined inside a pipeline"
829
883
end
@@ -961,32 +1015,32 @@ defmodule Phoenix.Router do
961
1015
962
1016
"""
963
1017
defmacro resources ( path , controller , opts , do: nested_context ) do
964
- add_resources path , controller , opts , do: nested_context
1018
+ add_resources ( path , controller , opts , do: nested_context )
965
1019
end
966
1020
967
1021
@ doc """
968
1022
See `resources/4`.
969
1023
"""
970
1024
defmacro resources ( path , controller , do: nested_context ) do
971
- add_resources path , controller , [ ] , do: nested_context
1025
+ add_resources ( path , controller , [ ] , do: nested_context )
972
1026
end
973
1027
974
1028
defmacro resources ( path , controller , opts ) do
975
- add_resources path , controller , opts , do: nil
1029
+ add_resources ( path , controller , opts , do: nil )
976
1030
end
977
1031
978
1032
@ doc """
979
1033
See `resources/4`.
980
1034
"""
981
1035
defmacro resources ( path , controller ) do
982
- add_resources path , controller , [ ] , do: nil
1036
+ add_resources ( path , controller , [ ] , do: nil )
983
1037
end
984
1038
985
1039
defp add_resources ( path , controller , options , do: context ) do
986
1040
scope =
987
1041
if context do
988
1042
quote do
989
- scope resource . member , do: unquote ( context )
1043
+ scope ( resource . member , do: unquote ( context ) )
990
1044
end
991
1045
end
992
1046
@@ -1098,18 +1152,20 @@ defmodule Phoenix.Router do
1098
1152
defmacro scope ( path , alias , options , do: context ) do
1099
1153
alias = expand_alias ( alias , __CALLER__ )
1100
1154
1101
- options = quote do
1102
- unquote ( options )
1103
- |> Keyword . put ( :path , unquote ( path ) )
1104
- |> Keyword . put ( :alias , unquote ( alias ) )
1105
- end
1155
+ options =
1156
+ quote do
1157
+ unquote ( options )
1158
+ |> Keyword . put ( :path , unquote ( path ) )
1159
+ |> Keyword . put ( :alias , unquote ( alias ) )
1160
+ end
1106
1161
1107
1162
do_scope ( options , context )
1108
1163
end
1109
1164
1110
1165
defp do_scope ( options , context ) do
1111
1166
quote do
1112
1167
Scope . push ( __MODULE__ , unquote ( options ) )
1168
+
1113
1169
try do
1114
1170
unquote ( context )
1115
1171
after
0 commit comments