Map with index

# Map with index

%w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" }
# => ["0:foo", "1:bar", "2:baz"]

Enumerator#next and peek

# Enumerator#next and peek
e = [1,2,3].each   # returns an enumerator object.
e.next   # => 1
e.next   # => 2
e.peek   # => 3
e.next   # => 3
e.next   # raises StopIteration
e.rewind
e.peek   # => 1

Infinitely nested Hash

# Infinitely nested Hash
h = Hash.new
h[:this][:is][:really] = 'Amazing!'
# => NoMethodError: undefined method `[]' for nil:NilClass

h = Hash.new {|h, k| h[k] = Hash.new(&h.default_proc) }
h[:this][:is][:really] = 'Amazing!'
h
# => {:this=>{:is=>{:really=>"Amazing!"}}}

Source

gsub with hash

# gsub with hash
"The Force will be with you. Always.".gsub(/[eo]/, 'e' => 3, 'o' => '0')
# => "Th3 F0rc3 will b3 with y0u. Always."

gsub with blocks

# gsub with blocks
"The Force will be with you. Always.".gsub(/[aeiou]/) {|s| s.upcase}
# => "ThE FOrcE wIll bE wIth yOU. AlwAys."

"The Force will be with you. Always.".gsub(/[aeiou]/) {$&.upcase}
# => "ThE FOrcE wIll bE wIth yOU. AlwAys."

Custom setter

# Custom setter
class User
  attr_reader :name

  def name=(name)
    @name = name.capitalize
  end
end

user = User.new
user.name = 'john'
user # => #<User:0x0000000111638a00 @name="John">

Struct subclasses aren’t triple-equal to themselves

# Struct subclasses aren't triple-equal to themselves

klass = Struct.new(:foo)
klass === klass            #=> false
Class === klass            #=> true
Class === Struct           #=> true
Class === Struct.new(:baz) #=> true

each_slice

# each_slice

(1..10).each_slice(3) {|a| p a}
# => [1, 2, 3]
# => [4, 5, 6]
# => [7, 8, 9]
# => [10]

assoc and rassoc

# assoc and rassoc

a  = [ 
       [ "colors", "red", "blue", "green" ],
       [ "letters", "a", "b", "c" ],
       "foo"
     ]
a.assoc("letters")  #=> [ "letters", "a", "b", "c" ]
a.assoc("foo")      #=> nil

#-----------------------------------------------

a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ]
a.rassoc("two")    #=> [2, "two"]
a.rassoc("four")   #=> nil

Assoc doc@

Negative Array indices

# Negative Array indices
array = [:a, :b, :c, :d, :e]

array[-1]    # => :e
array[0..-3] # => [:a, :b, :c]
array[1..-1] # => [:b, :c, :d, :e]

Benchmark-ips

# Benchmark-ips
# gem install benchmark-ips
require 'benchmark/ips'

array = Array(1..100)

Benchmark.ips do |x|
  x.report("sample:")  { array.sample }
  x.report("shuffle.first:")  { array.shuffle.first }
end

# Warming up --------------------------------------
#              sample:   258.402k i/100ms
#       shuffle.first:    41.862k i/100ms
# Calculating -------------------------------------
#              sample:      8.141M (± 2.7%) i/s -     40.828M in   5.018541s
#       shuffle.first:    480.061k (± 2.8%) i/s -      2.428M in   5.061688s

benchmark-ips on Github

More about Benchmark on Fast-Ruby

Pathname

# Pathname
require 'pathname'

pn = Pathname.new("/usr/bin/ruby")
pn.size        # => 52016
pn.directory?  # => false
pn.dirname     # => Pathname:/usr/bin
pn.basename    # => Pathname:ruby
pn.to_s        # => "/usr/bin/ruby"

Pathname.new('foo.rb').extname # => '.rb'

Pathname.new("/usr/").children 
# => [#<Pathname:/usr/bin>,
 #<Pathname:/usr/standalone>,
 #... ]

See pathname doc

Super

# Super
class Base
  def bar
    puts "Base#bar"
  end
end

class Foo < Base
  def bar(baz)
    # By default super sends all arguments from Foo#bar to Base#bar
    # Call super() to send no arguments to Base#bar and to avoid 
    # "ArgumentError: wrong number of arguments (given 1, expected 0)""
    super()
    puts "#{baz} Foo#bar"
  end
end

Foo.new.bar('Hello')
# => Base#bar
# => Hello Foo#bar

Tap method

# Tap method
class User
  def get_band_from_api
    @band = "Metallica" #example code
  end
end

user = User.new
user.get_band_from_api # => "Metallica"
user
# => #<User:0x000000010c366773 @band="Metallica">

user = User.new.tap(&:get_band_from_api)
# => #<User:0x000000010c366778 @band="Metallica">

Round to the nearest ten

# Round to the nearest ten
# instead of
15.7.round * 10 # => 160
# you can use
157.round(-1)   # => 160

# In Ruby 2.5 and above
155.round(-1, half: :up)     # => 160
155.round(-1, half: :down)   # => 150

The Spaceship Operator

# The Spaceship Operator
1 <=> 5 # => -1
5 <=> 5 # => 0
6 <=> 5 # => 1

numbers = Array(1..10)
numbers.group_by { |n| n <=> 5 }
# => {-1=>[1, 2, 3, 4], 0=>[5], 1=>[6, 7, 8, 9, 10]}

Comparable in ruby doc

How to Use The Spaceship Operator in Ruby

Symbol vs String

# Symbol vs String
'foo'.__id__ == 'foo'.__id__ # => false
:foo.__id__  == :foo.__id__  # => true

Two strings with the same contents are two different objects, but for any given name there is only one Symbol object More

clone vs dup

# clone vs dup
class Book
  attr_accessor :title, :read
  def initialize(title:)
    self.title = title
    self.read = false
  end
end

book = Book.new(title: 'The Hobbit')

def book.read!; read = true; end

book_clone = book.clone
book_dup = book.dup

book_clone.read! # => true
book_dup.read! # => NoMethodError: undefined method `read!'

book.title << ' by Tolkien'
book_clone.title # => "The Hobbit by Tolkien"
book_dup.title   # => "The Hobbit by Tolkien"

book.freeze.clone.frozen? # => true
book.freeze.dup.frozen? # => false

Frozen object and object id

# Frozen object and object id
class Book
  attr_accessor :title

  def initialize(title:)
    self.title = title
  end
end

book = Book.new(title: 'The Hobbit')

book.title.__id__ # => 70172975062580
book.title << ' by Tolkien'
book.title.__id__ # => 70172975062580 => ⚠️ the same
book.title = 'The Return of the King'
book.title.__id__ # => 70172979274460 => ⚠️ different

book.freeze
book.frozen?       # => true
book.title.frozen? # => false
book.title << ' by Tolkien'     # => "The Return of the King by Tolkien"
book.title = 'The Silmarillion' # => RuntimeError: can't modify frozen Book

Object equality test

# Object equality test
class Book
  attr_reader :author, :title

  def initialize(author:, title:)
    @author = author
    @title = title
  end

  def ==(other)
    self.class === other &&
      other.author == @author &&
      other.title == @title
  end
end

book1 = Book.new(title: 'The Hobbit', author: 'Tolkien')
book2 = Book.new(title: 'The Hobbit', author: 'Tolkien')

book1 == book2 # => true

Dig

# Dig
john = {
  name: "John Doe",
  address: {
    city: "Paris"
  }
}
jeanne = { name: "Jeanne"}

john.dig(:address, :city)   # => "Paris"

jeanne[:address][:city]  # => NoMethodError: undefined method `[]' for nil:NilClass
jeanne.dig(:address, :city) #=> nil

Hash#dig in ruby doc

Metaprogramming basics #1

# Metaprogramming basics #1

class Foo
  ["hello", "bye"].each do |method|
    define_method "say_#{method}" do
      "I say " + method.to_s
    end
  end
end

Foo.new.say_hello # => I say hello"
Foo.new.say_bye   # => I say bye"

# Real world exemple:
class Post
  STATUS = %w(to_moderate published planned done hidden)

  def initialize(status:)
    @status = status
  end

  STATUS.each do |status|
    define_method("#{status}?") do
      @status == status
    end
  end
end

post = Post.new(status: "published")
post.published? # => true
post.done?      # => false

Return the name of the current called method

# Return the name of the current called method
class Foo
  def bar
    [__method__, __callee__]
  end

  alias_method :hello, :bar 
end

Foo.new.bar # => [:bar, :bar]
Foo.new.hello # => [:bar, :hello]

Private class methods

# Private class methods
class Example

  def self.foo; end

  private
  # private_foo will not be private as it will be a singleton method
  def self.private_foo; end

  class << self
    def bar; end

    private
    def private_bar; end
  end
end

Example.methods(false)          # => [:foo, :private_foo, :bar]
Example.private_methods(false)  # => [:private_bar, :initialize, :inherited]

More: Class Methods In Ruby: a Thorough Review & Why I Define Them Using class « self

Singleton Methods

# Singleton Methods

class Foo
  def bar; "bar!"; end
end

foo = Foo.new
foo.bar # => "bar!"

def foo.bar
  "This is the new bar"
end
foo.define_singleton_method(:hi) {'hi!'}

foo.singleton_methods # => [:bar, :hi]
foo.bar               # => "This is the new bar"
foo.hi                # => "hi!"

Safe navigation operator (&.)

# Safe navigation operator (&.)
class Foo
  attr_accessor :bar
  def initialize(bar: Bar.new)
    @bar = bar
  end
end

class Bar
  def hello
    "hello"
  end
end

Foo.new.bar.hello # "hello"
Foo.new(bar: nil).bar # nil
Foo.new(bar: nil).bar.hello # NoMethodError: undefined method `hello' for nil:NilClass
Foo.new(bar: nil).bar&.hello # nil
Foo.new(bar: false).bar&.hello # NoMethodError: undefined method `hello' for false:FalseClass
Foo.new(bar: false).bar && Foo.new(bar: false).bar.hello # false

Convert value to boolean

# Convert value to boolean
!!(true) # true
!!(1)    # true
!!(nil)  # false
# Usage:
def foo?
  !!foo
end

# Bonus: Cast to boolean with ActiveModel in Rails
ActiveModel::Type::Boolean.new.cast(0)        # false
ActiveModel::Type::Boolean.new.cast('false')  # false
ActiveModel::Type::Boolean.new.cast('true')   # true
ActiveModel::Type::Boolean.new.cast(1)        # true

General Delimited Input, %w and %W

# General Delimited Input, %w and %W

%w(hello world)
# ["hello", "world"]
%w-hello world-
# ["hello", "world"]
%w$hello world$
# ["hello", "world"]

hello = 'hi'
%w(#{hello} world)
# ["\#{hello}", "world"]
%W(#{hello} world)
# ["hi", "world"]

more info about Q, q, W, w, x, r, s

more info about w, x

The “method” method

# The "method" method
# Passing a method taking a block to another method or object
%w[Hello World].each &method(:puts)
# Hello
# World

Splat operator

# Splat operator
first, *others, last = ['a', 'b', 'c', 'd']
{first: first, others: others, last: last}
#=> {:first=>"a", :others=>["b", "c"], :last=>"d"}

def foo(*args, **opts)
  puts 'args: ' + args.to_s
  puts 'opts: ' + opts.to_s
  puts '"a" opts: ' + opts[:a].to_s
end

foo(1, 2, 3, a: 4, b: 5)
# args: [1, 2, 3]
# opts: {:a=>4, :b=>5}
# "a" opts: 4