-
Notifications
You must be signed in to change notification settings - Fork 4
Description
According to the Ruby Version Policy, Ruby will release a new MINOR version every Christmas as a Christmas gift, and so we are expecting 2.3.0 this year. This release brings many new features and improvements and this post helps you catch up on the changes.
General
Did you mean gem is now bundled by default
Have you ever made a typo, this gem shows the possible corrections for errors like NoMethodError and NameError:
"".downcasr
NoMethodError: undefined method `downcasr' for "":String
Did you mean? downcase
downcase!
Safe operator (&.)
This new operator helps to avoid method invocation on nil.
Consider this:
user && user.nameCan now can be reduced to:
user&.nameWithout method name will raise SyntaxError:
obj&. {} # syntax errorOnly invoked when needed:
user&.address( telphone() ) # telphone() is conditionally evaluatedUseful for attribute assignment too:
user&.sign_in_count += 1
Benchmark
require "benchmark/ips"
def fast(user = nil)
user&.email
end
def slow(user = nil)
user && user.email
end
Benchmark.ips do |x|
x.report("&.") { fast }
x.report("check") { slow }
x.compare!
endCalculating -------------------------------------
&. 155.950k i/100ms
check 150.034k i/100ms
-------------------------------------------------
&. 7.750M (± 9.1%) i/s - 38.520M
check 7.556M (± 9.2%) i/s - 37.508M
Comparison:
&.: 7750478.7 i/s
check: 7555733.4 i/s - 1.03x slower
Trivia: Matz also calls this the "lonely operator" in his RubyConf 2015 keynote RubyConf 2015 (a person sitting alone and looking at a dot).
Further Reading:
Frozen String Literal Pragma
Add this Magic Comment to the top of a file, and all Strings in the file will then be frozen by default.
# frozen_string_literal: true
This gem can help to add magic comments to all your ruby files.
Alternatively, you can also:
- Compile Ruby with
--enable=frozen-string-literalor--disable=frozen-string-literal - Invoke Ruby with ENV variable like so
$ RUBYOPT="--enable=frozen-string-literal" ruby
Known Issue:
Related Issue:
Further Reading:
- Unraveling String Key Performance in Ruby 2.2 by Richard Schneems
- The let_it_go gem can help to identify hotspots that can easily accept frozen string in your application.
String
<<~ Indented heredoc
sql = <<~SQL
SELECT * FROM MICHELIN_RAMENS
WHERE STAR = 1
SQLsquish
Active Support's String#squish and String#squish! has been ported to Ruby 2.3.
Module
#deprecate_constant
class Deppbot
GITHUB_URL = "http://github.com/jollygoodcode/deppbot"
deprecate_constant :GITHUB_URL
GITHUB_URI = "https://github.com/jollygoodcode/deppbot"
endDeppbot::GITHUB_URL will throw a warning: warning: constant Deppbot::GITHUB_URL is deprecated.
Numeric
#positive?
42.positive? # => trueBenchmark
require "benchmark/ips"
class Numeric
def my_positive?
self > 0
end
end
Benchmark.ips do |x|
x.report("#positive?") { 1.positive? }
x.report("#my_positive?") { 1.my_positive? }
x.compare!
endCalculating -------------------------------------
#positive? 166.229k i/100ms
#my_positive? 167.341k i/100ms
-------------------------------------------------
#positive? 10.098M (± 9.7%) i/s - 50.035M
#my_positive? 10.094M (± 8.9%) i/s - 50.035M
Comparison:
#positive?: 10098105.6 i/s
#my_positive?: 10093845.8 i/s - 1.00x slower
#negative?
-13.negative? # => trueBenchmark
require "benchmark/ips"
class Numeric
def my_negative?
self < 0
end
end
Benchmark.ips do |x|
x.report("#negative?") { 1.negative? }
x.report("#my_negative?") { 1.my_negative? }
x.compare!
endCalculating -------------------------------------
#negative? 154.580k i/100ms
#my_negative? 155.471k i/100ms
-------------------------------------------------
#negative? 9.907M (±10.3%) i/s - 49.002M
#my_negative? 10.116M (±10.4%) i/s - 50.062M
Comparison:
#my_negative?: 10116334.8 i/s
#negative?: 9907112.3 i/s - 1.02x slower
Array
#dig
foo = [[[0, 1]]]
foo.dig(0, 0, 0) => 0Faster than foo[0] && foo[0][0] && foo[0][0][0]
Benchmark
require "benchmark/ips"
results = [[[1, 2, 3]]]
Benchmark.ips do |x|
x.report("Array#dig") { results.dig(0, 0, 0) }
x.report("&&") { results[0] && results[0][0] && results[0][0][0] }
x.compare!
endCalculating -------------------------------------
Array#dig 144.577k i/100ms
&& 142.233k i/100ms
-------------------------------------------------
Array#dig 8.263M (± 8.3%) i/s - 41.060M
&& 7.652M (± 9.3%) i/s - 37.976M
Comparison:
Array#dig: 8262509.7 i/s
&&: 7651601.9 i/s - 1.08x slower
#bsearch_index
While Array#bsearch returns a match in sorted array:
[10, 11, 12].bsearch { |x| x < 12 } # => 10
Array#bsearch_index returns the index instead:
[10, 11, 12].bsearch_index { |x| x < 12 } # => 0
Please note that #bsearch and #bsearch_index only works for sorted array.
Struct
#dig
klass = Struct.new(:a)
o = klass.new(klass.new({b: [1, 2, 3]}))
o.dig(:a, :a, :b, 0) #=> 1
o.dig(:b, 0) #=> nilOpenStruct
OpenStruct is now 3X-10X faster in Ruby 2.3 than it was in earlier versions of Ruby.
If you are using Hashie in any of your libraries (especially API wrappers), now is a good time to switch back to OpenStruct.
Hash
#dig
info = {
matz: {
address: {
street: "MINASWAN street"
}
}
}
info.dig(:matz, :address, :street) => 0Faster than info[:matz] && info[:matz][:address] && info[:matz][:address][:street]
Benchmark
require "benchmark/ips"
info = {
user: {
address: {
street1: "123 Main street"
}
}
}
Benchmark.ips do |x|
x.report("#dig") { info.dig(:user, :address, :street1) }
x.report("&&") { info[:user] && info[:user][:address] && info[:user][:address][:street1] }
x.compare!
endCalculating -------------------------------------
Hash#dig 150.463k i/100ms
&& 141.490k i/100ms
-------------------------------------------------
Hash#dig 7.219M (± 8.1%) i/s - 35.961M
&& 5.344M (± 7.6%) i/s - 26.600M
Comparison:
Hash#dig: 7219097.1 i/s
&&: 5344038.3 i/s - 1.35x slower
#>, #<, #>=, #<=
Check if a hash's size is larger / smaller than the other hash, or if a hash is a subset (or equals to) of the other hash.
Check this spec on ruby/rubyspec to learn more.
Further Reading:
Note that there isn't Hash#<=>.
#fetch_values
Extract many values from a Hash (fetch_values is similar to values_at):
jollygoodcoders = {
principal_engineer: "Winston",
jolly_good_coder: "Juanito",
}
> jollygoodcoders.values_at(:principal_engineer, :jolly_good_coder)
=> ["Winston", "Juanito"]
> jollygoodcoders.fetch_values(:principal_engineer, :jolly_good_coder)
=> ["Winston", "Juanito"]However, fetch_values will throwKeyError` when a given key is not found in the hash:
> jollygoodcoders.values_at(:principal_engineer, :jolly_good_coder, :project_manager)
=> ["Winston", "Juanito", nil]
> jollygoodcoders.fetch_values(:principal_engineer, :jolly_good_coder, :project_manager)
=> KeyError: key not found: :project_managerBenchmark
require "benchmark/ips"
jollygoodcoders = {
principal_engineer: "Winston",
jolly_good_coder: "Juanito",
}
Benchmark.ips do |x|
x.report("Hash#values_at") { jollygoodcoders.values_at(:principal_engineer, :jolly_good_coder) }
x.report("Hash#fetch_values") { jollygoodcoders.fetch_values(:principal_engineer, :jolly_good_coder) }
x.compare!
endCalculating -------------------------------------
Hash#values_at 133.600k i/100ms
Hash#fetch_values 123.666k i/100ms
-------------------------------------------------
Hash#values_at 5.869M (± 9.4%) i/s - 29.125M
Hash#fetch_values 5.583M (± 7.7%) i/s - 27.825M
Comparison:
Hash#values_at: 5868709.9 i/s
Hash#fetch_values: 5582932.0 i/s - 1.05x slower
#to_proc
hash = { a: 1, b: 2, c: 3 }
[:a, :b, :c].map &hash
=> [1, 2, 3]Enumerable
#grep_v
This method returns the inverse of Enumerable#grep. Do not confuse this with the $ grep -v command.
#chunk_while
The positive form of Enumerable#slice_when:
> pi = Math::PI.to_s.scan(/\d/).map(&:to_i)
=> [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3]
> pi.slice_when { |i, j| i.even? != j.even? }.to_a
=> [[3, 1], [4], [1, 5, 9], [2, 6], [5, 3, 5], [8], [9, 7, 9, 3]]
> pi.chunk_while { |i, j| i.even? == j.even? }.to_a
=> [[3, 1], [4], [1, 5, 9], [2, 6], [5, 3, 5], [8], [9, 7, 9, 3]]Additional Resources
- New features in ruby 2.3 - BlockScore Blog
- Ruby 2.3.0 changes and features
- Preview of New Features in Ruby 2.3.0
- Ruby 2.3.0 preview 1 by Mario Alberto Chavez
- What's new in Ruby 2.3?
- ruby/ruby NEWS at v2_3_0
- ruby/ruby ChangeLog at v2_3_0
Thanks for reading!
@JuanitoFatas ✏️ Jolly Good Code
About Jolly Good Code
We specialise in Agile practices and Ruby, and we love contributing to open source.
Speak to us about your next big idea, or check out our projects.
