Understand the underscore method provided by ActiveSupport in Ruby on Rails
The underscore method
This method is used to convert a string from CamelCase, also called PascalCase, to underscored snake_case.
The method is provided by ActiveSupport.
ActiveSupport
The ActiveSupport
module in Rails plays a fundamental role in enhancing the functionality and convenience of the framework. It serves as an extension to the Ruby standard library, providing a bunch of utility methods and enhancements that enrich the development experience.
Naming conventions
Additionally, ActiveSupport
actively contributes to the adherence of Rails' naming conventions by providing methods like underscore
, which enables the seamless conversion of ConstantNames to file_names and other cases.
The underscore method usage
Database Table Names
When defining database tables in Rails, the convention is to use pluralized, underscored names. For example, if you have a class named UserGroup
, calling underscore
on it would return 'user_groups', which can be used as the corresponding database table name.
File Naming
Rails follows a consistent file naming convention, where files are named using snake_case and classes follow the camel case pattern.
For instance, if you have a model class named ArticleCategory
, using underscore
will convert it to 'article_category.rb', which is the expected filename.
URL Routes
Route URLs
are typically defined in snake_case format. When generating URLs or working with routing, using underscore
helps ensure consistency with route naming.
For example, if you have a route for ArticleCategoryController
, calling underscore
on it will provide 'article_category', which can be used in routing definitions.
Implementation
# File activesupport/lib/active_support/inflector/methods.rb, line 92
def underscore(camel_cased_word)
return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze)
word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_'.freeze }#{$2.downcase}" }
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
word.tr!("-".freeze, "_".freeze)
word.downcase!
word
end
Scope resolution operator
The line return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
is going to check if the input contains any uppercase letters or the scope resolution operator ::.
If it doesn't, indicating that the word is already underscored or doesn't require conversion, the original word is returned as is.
If the word needs to be converted, the code proceeds with the conversion logic.
Name namespaces
The line word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze)
replaces the :: scope resolution operator with a forward slash /.
This step is done to handle namespaced class/module names and convert them into a path-like format.
Find acronyms
The next line word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_'.freeze }#{$2.downcase}" }
handles special cases for acronyms.
It uses a regular expression defined in the inflections module to find acronyms within the word and replaces them with an underscore followed by the lowercase version of the acronym. This ensures consistent formatting of acronyms within the underscored word.
Conversion
The two subsequent gsub!
lines handle the conversion of CamelCase to snake_case based on regular expressions.
Handle hyphens
The line word.tr!("-".freeze, "_".freeze)
replaces any hyphens (-) with underscores (_) to handle cases where hyphens are used instead of spaces or underscores.
Convert to lowercase
word.downcase!
converts the entire word to lowercase, ensuring consistency in the output format.
Return
The resulting word
, which was created at the beginning of the mehtod, is then returned as the underscored version of the camel_cased_word (the input).
Be happy
You got here. Way to go!
Let's connect
Final thoughts
I hope this article helped you. Let me know if you have any questions. Your thoughts, suggestions and corrections are more than welcome. By the way, feel free to drop your suggestions on new blog articles.
Hope to see you next time.