Re-Graphing the Mental Model of the Rails Router

53f67e3899c7ca2ca340b795cf80d3c4?s=47 Vaidehi Joshi
April 19, 2018
820

Re-Graphing the Mental Model of the Rails Router

Rails is incredibly powerful because of its abstractions. For years, developers have been able to hit the ground running and be productive without having to know the in's and out's of the core computer science concepts that fuel this framework. But just because something is hidden away doesn't mean that it's not worth knowing! In this talk, we'll unpack the CS fundamentals that are used in the Rails router. Together, we'll demystify the graph data structure that lies under the hood of the router in order to understand how a simple structure allows for routing to actually work in a Rails app.

53f67e3899c7ca2ca340b795cf80d3c4?s=128

Vaidehi Joshi

April 19, 2018
Tweet

Transcript

  1. @vaidehijoshi Re-graphing The Mental Model of The Rails Router Vaidehi

    Joshi
  2. @vaidehijoshi hi! i’m vaidehi.

  3. @vaidehijoshi i work on

  4. @vaidehijoshi four years

  5. @vaidehijoshi vaidehi rails

  6. @vaidehijoshi off the happy path

  7. @vaidehijoshi abstractions

  8. @vaidehijoshi rails router

  9. @vaidehijoshi routing basics

  10. @vaidehijoshi router

  11. @vaidehijoshi post office post office router

  12. @vaidehijoshi

  13. @vaidehijoshi the router knows where to send you

  14. @vaidehijoshi $ rails routes

  15. @vaidehijoshi $ rails routes 
 Verb URI Pattern Controller#Action GET

    /articles(.:format) articles#index POST /articles(.:format) articles#create GET /articles/new(.:format) articles#new GET /articles/:id/edit(.:format) articles#edit GET /articles/:id(.:format) articles#show PATCH /articles/:id(.:format) articles#update
 PUT /articles/:id(.:format) articles#update
 DELETE /articles/:id(.:format) articles#destroy
  16. @vaidehijoshi routes.rb

  17. @vaidehijoshi Rails.application.routes.draw do root 'welcome#home' get 'recipes/:id', to: 'recipes#show' resources

    :articles end
  18. @vaidehijoshi

  19. @vaidehijoshi me: where u at bro?
 router:

  20. @vaidehijoshi $ rails middleware

  21. @vaidehijoshi $ rails middleware 
 use Rack::Sendfile use ActionDispatch::Static use

    ActionDispatch::Executor
 use ActiveSupport::Cache::Strategy::LocalCache::Middleware
 … use Rack::Head
 use Rack::ConditionalGet
 use Rack::ETag use Rack::TempfileReaper run ExampleApp::Application.routes
  22. @vaidehijoshi what’s middleware again?

  23. @vaidehijoshi what’s rack again?

  24. @vaidehijoshi responds to call (like a proc)

  25. @vaidehijoshi { [200, {}, ["Hello World"]] }

  26. @vaidehijoshi ProTip™: controller actions are just rack endpoints

  27. @vaidehijoshi Rails.application.routes.draw do root 'welcome#home' end root 'welcome#home' root to:

    proc { [200, {}, [“omg this works!”]] }
  28. @vaidehijoshi $ rails middleware 
 use Rack::Sendfile use ActionDispatch::Static use

    ActionDispatch::Executor
 use ActiveSupport::Cache::Strategy::LocalCache::Middleware
 … use Rack::Head
 use Rack::ConditionalGet
 use Rack::ETag use Rack::TempfileReaper run ExampleApp::Application.routes
  29. @vaidehijoshi ExampleApp::Application.routes

  30. @vaidehijoshi Application.routes

  31. @vaidehijoshi lib/rails/engine.rb

  32. @vaidehijoshi def routes #…
 @routes end

  33. @vaidehijoshi me: how u route bro? router:

  34. @vaidehijoshi get 'recipes/:id', to: ‘recipes#show’ post ‘articles(.:format)’, to: 'articles#create' post

    'articles/new', to: ‘articles#new’ get ‘articles/:id', to: ‘articles#new’
 # even more routes here…
  35. @vaidehijoshi if request_path =~ /^\articles$/ # go to articles#index
 end

  36. @vaidehijoshi if request_path =~ /^\articles$/ # go to articles#index
 elsif

    request_path =~ /^\recipes$/
 # go to recipes#index elsif
 # etc etc more looping ahhhhhh! else
 # route not found?
 end linear, O(n)
  37. @vaidehijoshi

  38. @vaidehijoshi

  39. @vaidehijoshi

  40. @vaidehijoshi

  41. None
  42. None
  43. None
  44. @vaidehijoshi where does journey fit in?

  45. @vaidehijoshi Application.routes

  46. @vaidehijoshi @routes ||=
 ActionDispatch::Routing::RouteSet.new

  47. @vaidehijoshi @routes ||=
 ActionDispatch::Routing::RouteSet.new

  48. @vaidehijoshi #action_dispatch/routing/route_set.rb
 @set ||= Journey.Routes.new

  49. @vaidehijoshi graph

  50. @vaidehijoshi node

  51. @vaidehijoshi nodes

  52. @vaidehijoshi nodes edges (links)

  53. @vaidehijoshi edges undirected

  54. @vaidehijoshi edges directed

  55. @vaidehijoshi

  56. @vaidehijoshi 12345 main street
 portland, oregon 97232-1337
 USA

  57. @vaidehijoshi

  58. @vaidehijoshi Rails.application.routes.draw do root 'welcome#home' resources :articles resources :recipes resources

    :comments end “/recipes/:id”
  59. @vaidehijoshi $ rails routes 
 Verb URI Pattern Controller#Action GET

    /articles(.:format) articles#index POST /articles(.:format) articles#create GET /articles/new(.:format) articles#new GET /articles/:id/edit(.:format) articles#edit GET /articles/:id(.:format) articles#show PATCH /articles/:id(.:format) articles#update
 PUT /articles/:id(.:format) articles#update
 DELETE /articles/:id(.:format) articles#destroy
  60. @vaidehijoshi Rails.application.routes.draw do root 'welcome#home' resources :articles resources :recipes resources

    :comments end “/recipes/:id”
  61. @vaidehijoshi

  62. @vaidehijoshi me: how u read bro? router:

  63. @vaidehijoshi The cow jumped over the moon.

  64. @vaidehijoshi The cow jumped over the moon.

  65. @vaidehijoshi “/recipes/:id”

  66. @vaidehijoshi tokenization

  67. @vaidehijoshi “/recipes/:id”

  68. @vaidehijoshi hi. i’m a scanner!

  69. @vaidehijoshi $ rails console
 > scanner = ActionDispatch::Journey::Scanner.new > scanner.scan_setup(“/recipes/:id”)

  70. @vaidehijoshi > scanner.next_token
 => [:SLASH, “/“] > scanner.next_token
 => [:LITERAL,

    "recipes"] 
 > scanner.next_token
 => [:SLASH, “/"]
 > scanner.next_token
 => [:SYMBOL, ":id"]
  71. @vaidehijoshi :SLASH, :LITERAL, :SYMBOL, :LPAREN, :RPAREN, :DOT, :OR,
 :STAR. Tokens

    I know:
  72. @vaidehijoshi half the battle

  73. @vaidehijoshi stars — they’re just like us! stars computers

  74. @vaidehijoshi hi. i’m a parser!

  75. @vaidehijoshi syntax tree

  76. @vaidehijoshi The cow jumped over the moon.

  77. @vaidehijoshi jumped The cow over the moon over the moon

  78. @vaidehijoshi jumped The cow over the moon over the moon

    noun phrase verb prepositional phrase
  79. @vaidehijoshi recipes :id / :format . / () “/recipes/:id(.:format)”

  80. @vaidehijoshi recipes :id / (.:format) / “/recipes/:id(.:format)”

  81. @vaidehijoshi recipes / :id(.:format) / “/recipes/:id(.:format)”

  82. @vaidehijoshi recipes /:id(.:format) / “/recipes/:id(.:format)”

  83. @vaidehijoshi recipes/:id(.:format) / “/recipes/:id(.:format)”

  84. @vaidehijoshi /recipes/:id(.:format) “/recipes/:id(.:format)”

  85. @vaidehijoshi $ rails console
 > parser = ActionDispatch::Journey::Parser.new > syntax_tree

    = parser.parse(“/recipes/:id(.:format)”)
  86. @vaidehijoshi intermediate step

  87. @vaidehijoshi + + = graph

  88. @vaidehijoshi + + = generalized
 transition graph (GTG)

  89. @vaidehijoshi + + = state
 machine

  90. @vaidehijoshi 0 0 0 0 0 0 1 2 3

    4
  91. @vaidehijoshi 0 0 0 0 0 0 1 2 4

    5 / articles / (?-mix:[^./?]+) 0 3 0 … . /articles.json
  92. @vaidehijoshi 0 0 0 0 0 0 1 2 4

    5 / articles / (?-mix:[^./?]+) 0 3 0 … . /articles.json
  93. @vaidehijoshi 0 0 0 0 0 0 1 2 4

    5 / articles / (?-mix:[^./?]+) 0 3 0 … . /articles.json
  94. @vaidehijoshi 0 0 0 0 0 0 1 2 4

    5 / articles / (?-mix:[^./?]+) 0 3 0 … . /articles.json
  95. @vaidehijoshi 0 0 0 0 0 0 1 2 4

    5 / articles / (?-mix:[^./?]+) 0 3 0 … . /articles.json
  96. @vaidehijoshi 0 0 0 0 0 0 1 2 4

    5 / articles / (?-mix:[^./?]+) 0 3 0 … . /articles.json
  97. @vaidehijoshi 0 0 0 0 0 0 1 2 4

    5 / articles / (?-mix:[^./?]+) 0 3 0 … . /articles.json ✅
  98. @vaidehijoshi get 'recipes/:id', to: ‘recipes#show’ get ‘articles(.:format)’, to: 'articles#index' post

    'articles/new', to: ‘articles#new’ get ‘articles/:id', to: ‘articles#new’
 # even more routes here…
  99. @vaidehijoshi 0 0 0 0 0 0 1 2 4

    5 / articles / (?-mix:[^./?]+) 0 3 0 … . /articles.json
  100. @vaidehijoshi 0 0 0 0 0 0 1 2 4

    5 / articles / (?-mix:[^./?]+) 0 3 0 … . /articles.json
  101. @vaidehijoshi 0 0 0 0 0 0 1 2 4

    5 / articles / (?-mix:[^./?]+) 0 3 0 … . /articles.json
  102. @vaidehijoshi 0 0 0 0 0 0 1 2 4

    5 / articles / (?-mix:[^./?]+) 0 3 0 … . /articles.json
  103. @vaidehijoshi 0 0 0 0 0 0 1 2 4

    5 / articles / (?-mix:[^./?]+) 0 3 0 … . /articles.json
  104. @vaidehijoshi nondeterministic finite automaton (NFA) just a fancy state machine!

  105. @vaidehijoshi two possible outcomes

  106. @vaidehijoshi we find a valid route

  107. @vaidehijoshi or we don’t

  108. @vaidehijoshi “accepted” state valid, acceptable route dispatch to corresponding controller

    action
  109. @vaidehijoshi

  110. @vaidehijoshi

  111. @vaidehijoshi “rejected” state invalid, unacceptable route cause an error

  112. @vaidehijoshi

  113. @vaidehijoshi

  114. @vaidehijoshi $ rails console
 > nfa = Rails.application.routes.router.visualizer

  115. @vaidehijoshi well this has been quite a ~journey~

  116. @vaidehijoshi but we learned so many things!

  117. @vaidehijoshi tokenization scanning trees & graphs automatons

  118. @vaidehijoshi you don’t need to think about abstractions every day.

  119. @vaidehijoshi but if you find yourself wanting or need to

    learn them, don’t be afraid!
  120. @vaidehijoshi

  121. @vaidehijoshi

  122. @vaidehijoshi basecs.org ✨

  123. @vaidehijoshi ✨ basecs.org thank you!