Want to see the full-length video right now for free?
On this week's episode, Chris is again joined by Melanie Gilman to discuss the amazing array of extensions added to Ruby by Rails' Active Support component.
Active Support is the collection of extensions to Ruby that Rails
provides. It adds additional utility methods to core classes like Date
and
String
to bring even more user-friendliness to Ruby's already very friendly
landscape.
In this video we'll provide an overview of some of our favorite methods from Active Support, but be sure to check out the outline in the Active Support Guide which provides a much more thorough summary of the provided methods.
Some methods are so useful that Active Support includes them on Object, making them available on every object instance.
"".present?
#=> false
"hello".present?
#=> true
[].present?
#=> false
[1, 2, 3].present?
#=> true
{}.present?
#=> false
{ a: 1, b: 2 }.present?
#=> true
"".blank?
#=> true
"hello".blank?
#=> false
[].blank?
#=> true
[1, 2, 3].blank?
#=> false
{}.blank?
#=> true
{ a: 1, b: 2 }.blank?
#=> false
presence
allows you to check for the presence of a value and use it if
present, otherwise it will return nil
. This allows you to collapse the
common object.present? && object
expressions.
def keywords(words)
words.presence || Topic.all.pluck(:name).join(", ")
end
# Equivalent method without using presence
def keywords(words)
if words.present?
words
else
Topic.all.pluck(:name).join(", ")
end
end
try
allows you to call a method on an object only if the object responds
to the method, otherwise it returns nil
. This can be useful in cases where
an object may be present, or may be nil
, as nil.try(:any_method)
will just
return nil
again.
def status
statuses_by_id[@trail.id].try(:first) || Unstarted.new
end
Note - While it is possible to chain multiple try
calls, e.g.
video.try(:trail).try(:name).try(:titleize)
, this is likely something you
want to avoid. try
violates ["Tell Don't Ask"][] as well as the ["Law of
Demeter"][], and chaining multiple together compounds this quickly.
["Tell Don't Ask"]: https://upcase.com/videos/tell-don-t-ask ["Law of Demeter"]: https://upcase.com/videos/law-of-demeter
Active Support provides a number of methods that transform a string in some way. Many of these are used in Rails' convention-based mappings between controllers, models, table names, etc.
"snake_case_string".camelize
#=> "SnakeCaseString"
"camelCaseString".underscore
#=> "camel_case_string"
"a_long_variable_name".humanize
#=> "A long variable name"
"a string for a url".parameterize
#=> "a-string-for-a-url"
"a lower case string".titleize
#=> "A Lower Case String"
"post".pluralize
#=> "posts"
"posts".singularize
#=> "post"
" \n weird\n\r \t whitespace \n".squish
#=> "weird whitespace"
fruits = ["apples", "oranges", "bananas"]
fruits.to_sentence.capitalize
#=> "Apples, oranges, and bananas"
# The `in?` method is the `include?` method with caller and receiver flipped
"apples".in? fruits
#=> true
fruits.include? "apples"
#=> true
Ruby provides the .first
method to access the first element of an array.
Active Support provides similar methods for additional elements in an array,
e.g. second
and third
. While these only go up to fifth
, oddly enough
there is an outlier forty_two
method which exists for fun reasons. Quoting
[the docs][]:
Thanks to social wisdom and positive constructiveness all around, forty_two is also available.
[the docs]: http://guides.rubyonrails.org/active_support_core_extensions.html#accessing
Dates and Times are notoriously difficult to work with, but with Active Support we have a whole slew of methods that make working with dates and even doing Date and Timezone math easy and very readable.
Date.today
#=> Tue, 08 Jul 2014
Date.today.next_month
#=> Fri, 08 Aug 2014
Date.today + 3.days
#=> Fri, 11 Jul 2014
Date.today.beginning_of_week
#=> Mon, 07 Jul 2014
Date.today.next_week(:friday)
#=> Fri, 18 Jul 2014
Date.today.tuesday?
#=> true
Date.today.all_week
#=> Mon, 07 Jul 2014..Sun, 13 Jul 2014
3.days.ago
#=> Sat, 05 July 2014
Rails includes all of Active Support by default, but luckily it exists as a standalone gem and can be included in non-Rails apps if desired.
You can load all of Active Support with:
require "active_support/all"
or load just the extensions for a specific class like String
with:
require "active_support"
require "active_support/core_ext/string"
Monkey patching has gone somewhat out of favor of late, especially when you consider the recent work to introduce [Refinements][] to Ruby. With this in mind it is worth thinking about whether we are comfortable [monkey patching][] nearly every object in our system.
In general the community seems to support the majority of Active Support, and certain examples such as the Date & Time math are absolutely critical pieces of many web applications.
It's possible to make a comparison to [Prototype.js][] as both Active Support and Prototype provide much of their functionality by extending and modifying core classes. While similar in functionality to [Prototype.js][], Active Support has the benefit of running in a controlled server process. One of the major concerns with Prototype was that it modified and extended core objects in the JavaScript environment that is shared by all code running on the page. With Rails, our code runs on our servers and we can control all of it, making these sorts of collisions less likely and more manageable.
tl;dr While Active Record is somewhat extreme in the extensions it makes, the community has embraced these changes and in many cases they provide drastically simpler and more expressive ways to write your code. Solid understanding of what is in Active Support is definitely worth your time.
[Refinements]: http://ruby-doc.org/core-2.1.1/doc/syntax/refinements_rdoc.html [Prototype.js]: http://prototypejs.org/ [monkey patching]: http://stackoverflow.com/questions/394144/what-does-monkey-patching-exactly-mean-in-ruby