My favorite ActiveSupport features
… and check why 5600+ Rails engineers read also this
My favorite ActiveSupport features
This is short and not so comprehensie list of my favorite ActiveSupport features.
Array#second
%i(a b).second
# => :b
Useful in ad-hoc scripts when you get primitive data from file or API. Together
with map
they give you some_array.map(&:second)
to get what you want.
Array#extract_options
def options(*args)
args.extract_options!
end
options(1, 2) # => {}
options(1, 2, a: :b) # => {:a=>:b}
But with ruby 2.0 keyword arguments aka kwargs already present and ruby 1.9.3 support ending in February 2015 you should probably migrate to it:
def options(*args, **kwargs)
args
kwargs
end
options(1, 2) # => {}
options(1, 2, a: :b) # => {:a=>:b}
Array#in_groups_of
and Array#in_groups
%w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
# ["1", "2", "3", "4"]
# ["5", "6", "7", nil]
# ["8", "9", "10", nil]
%w(1 2 3 4 5 6 7 8 9 10).in_groups_of(3) {|group| p group}
# ["1", "2", "3"]
# ["4", "5", "6"]
# ["7", "8", "9"]
# ["10", nil, nil]
Remember that you can add false
as a second argument to avoid nil
s
in the arrays.
Array#wrap
Wraps its argument in an array unless it is already an array.
def method(one_or_many)
Array.wrap(one_or_many).each(&:do_something)
end
method(1)
method([3,4,5])
Nicely explained in the documentation why it is better then usual ruby idioms
to_formatted_s
on many types
BigDecimal.new("12.23").to_s
#=> "0.1223E2"
require 'active_support/all'
BigDecimal.new("12.23").to_s
#=> "12.23"
BigDecimal.new("12.23").to_formatted_s
#=> "12.23"
Time::DATE_FORMATS[:w3c] = "%Y-%m-%dT%H:%M:%S%:z"
Time.now.to_s(:w3c)
#=> "2015-02-25T17:51:53+00:00"
ActiveSupport overwrites to_s
on my types to use
its to_formatted_s
version instead (especially when
arguments provided)
#ago
, #from_now
Time.now.ago(3.months)
# => 2014-11-25 17:55:53 +0000
3.months.ago
# => 2014-11-25 17:56:00 +0000
3.months.ago(Time.now)
#=> 2014-11-25 17:56:03 +0000
beginning_of_...
& end_of_...
%i(
beginning_of_minute
beginning_of_hour
beginning_of_day
beginning_of_week
beginning_of_quarter
beginning_of_month
beginning_of_year
end_of_minute
end_of_hour
end_of_day
end_of_week
end_of_month
end_of_quarter
end_of_year
).each{|method| puts "#{method} - #{Time.now.public_send(method)}"}
# beginning_of_minute - 2015-02-25 18:03:00 +0000
# beginning_of_hour - 2015-02-25 18:00:00 +0000
# beginning_of_day - 2015-02-25 00:00:00 +0000
# beginning_of_week - 2015-02-23 00:00:00 +0000
# beginning_of_quarter - 2015-01-01 00:00:00 +0000
# beginning_of_month - 2015-02-01 00:00:00 +0000
# beginning_of_year - 2015-01-01 00:00:00 +0000
# end_of_minute - 2015-02-25 18:03:59 +0000
# end_of_hour - 2015-02-25 18:59:59 +0000
# end_of_day - 2015-02-25 23:59:59 +0000
# end_of_week - 2015-03-01 23:59:59 +0000
# end_of_month - 2015-02-28 23:59:59 +0000
# end_of_quarter - 2015-03-31 23:59:59 +0000
# end_of_year - 2015-12-31 23:59:59 +0000
ActiveSupport::Duration
The class behind the little trick:
3.seconds.class
# => ActiveSupport::Duration
ActiveSupport::TimeWithZone
I hate time zones, but I love ActiveSupport::TimeWithZone
. It is so easy to use.
Time.use_zone("Europe/Moscow"){ Time.zone.now }
# => Wed, 25 Feb 2015 21:09:12 MSK +03:00
Time.find_zone!("America/New_York").parse("2015-03-03 12:00:11")
# => Tue, 03 Mar 2015 12:00:11 EST -05:00
Time.find_zone!("America/New_York").parse("2015-03-03 12:00:11").utc
# => 2015-03-03 17:00:11 UTC
Time.utc("2015-03-03 12:00:11").zone
# => "UTC"
And I love that it can properly compare times from different timezones based on what moment of time they point to.
moment = Time.utc("2015-03-03 12:00:11")
#=> 2015-01-01 00:00:00 UTC
moment.in_time_zone("Europe/Warsaw") == moment.in_time_zone("America/Chicago")
=> true
Hash#except
Returns a hash that includes everything but the given keys.
hash = { a: true, b: false, c: nil}
hash.except(:c)
# => { a: true, b: false}
Except that I always think that this method is called #without
.
Hash#slice
Slice a hash to include only the given keys.
{ a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
# => {:a=>1, :b=>2}
If only I could remember that this method is not named #only
:)
Hash#reverse_merge
options.reverse_merge(size: 25, velocity: 10)
is equivalent to
{ size: 25, velocity: 10 }.merge(options)
This is particularly useful for default values.
Module#delegate
class Foo < ActiveRecord::Base
belongs_to :greeter
delegate :hello, to: :greeter
end
Reading this is way easier for me, compared
to Forwardable#def_delegator
.
With prefix
and allow_nil
options that you can use with it,
it probably solves 95% of my delegation cases.
Object#blank?
and Object#present?
and Object#presence
nil.blank?
# => true
" ".blank?
#=> true
[].blank?
=> true
title = commment[:title].presence || "Missing title"
Never check for nil
or empty string again.
Enumerable#sum
[2,3,5].sum
# => 10
ActiveSupport::Notifications
Too long for our short blogpost but check out instrumentation API
ActiveSupport::MessageVerifier
You can use it to generate and verify signed messages
@verifier = ActiveSupport::MessageVerifier.new('s3Krit', serializer: JSON)
@verifier.generate("private message")
#=> "InByaXZhdGUgbWVzc2FnZSI=--43fc83190b28daf8df04c0b86ff2976931a6dcd2"
@verifier.verify("InByaXZhdGUgbWVzc2FnZSI=--43fc83190b28daf8df04c0b86ff2976931a6dcd2")
#=> "private message"
@verifier.generate("a" => "private message")
#=> "eyJhIjoicHJpdmF0ZSBtZXNzYWdlIn0=--b253af3e77622f743cf6804c870f4a95cbbd6f00"
@verifier.verify("eyJhIjoicHJpdmF0ZSBtZXNzYWdlIn0=--b253af3e77622f743cf6804c870f4a95cbbd6f00")
=> {"a"=>"private message"}
Summary
That’s it. You can browse entire ActiveSupport codebase quickly and easily at github
If you liked it, you may also enjoy Hidden features of Ruby you may not know about