Have you ever wondered how does your UsersController just work fine when when you place it in the app/controllers/users_controller.rb file? It’s one of the first things in the Rails world that we say it works “automagically”. This magic is not too hard to understand if you know some Ruby. As Rails is an open-source framework, we all can be magicians!
We have a lot of helpers we use with Strings. I won’t be describing them all here one by one, you can find it nicely described here - it’s Rails' Inflector, a part ActiveSupport API. And that’s it! Thanks for reading! :-)
Just kidding! I know you’re more curious - let’s look deeper.
“The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without, and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept in inflections.rb.”
Your ActiveSupport gem contains inflector.rb in the lib/active_support directory, but wait… look at this file:
1 2 3 4 5 6
It looks strange, no methods, no classes, no modules, nothing. The reason this file exists starts to make sense when we want to require active_support/inflector without the rest of active_support. But we’re using it in Rails and still are looking for something else - our CamelCase to snake_case transformer. Let’s open the active_support/inflector/methods file (which is required by our inflector.rb) and you’ll see a part of the “magic”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
This two methods above make the job done. Just load the inflector into your IRB and check it out!
1 2 3 4 5 6
Great! We know how it works, but to be honest it’s not a big surprise that Rails uses gsub to change CamelCase to snake_case and vice versa. The question is: how the heck it figures out that I’m looking for a users_controller.rb file when typing “example.com/users” in the browser?! That’s much more interesting.
Class names are constants in Ruby
Launch your IRB and check that:
1 2 3 4 5 6 7 8 9
You see? Uninitialized constant. What Ruby performs here is a const_missing method call. This method is called every time Ruby fails to find a constant among all the classes and modules in the current scope. Try that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
We’re much closer to the answer now. The only thing that is missing here is:
“Checks for a constant with the given name in mod If inherit is set, the lookup will also search the ancestors (and Object if mod is a Module.) The value of the constant is returned if a definition is found, otherwise a NameError is raised.” [source].
What is going on here? Ruby const_get method ends up the explanation of Rails magic with all the CamelControllers to snake_files mapping. Just take a look on the active_support/inflector/methods constantize:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
You see const_get here? You can try this method by yourself in IRB:
1 2 3 4 5
For further reading you can try to find out how does Ruby resolve the constant names.