Get last output in console
# Get last output in console
irb(main):001:0> a = 1
=> 1
irb(main):002:0> b = _
=> 1
Private attr_reader
# Private attr_reader
require 'httparty'
class ExampleUserConnector
URL = "https://jsonplaceholder.typicode.com/users"
def initialize(adapter: HTTParty)
@adapter = adapter
end
def call
adapter.get(URL).body
end
private
attr_reader :adapter
end
Writing large Integers
# Writing large Integers
123_456_789
# => 123456789
Here documents
# Here documents
string = <<-FIN
a long string with
a lot of text
FIN
# => "a long string with\na lot of text\n"
strings = [<<END, "short", "strings"]
another long string
with a lot of text
END
# => ["another long string\nwith a lot of text \n", "short", "strings"]
Enumerable#find ifnone
# Enumerable#find ifnone
enum = [{a: 1, b:2}, {a:2, b:3}]
enum.find{ |e| e[:a] == 1 } # => {:a=>2, :b=>3}
enum.find{ |e| e[:a] == 3 } # => nil
# ifnone is called if no object matches:
# find(ifnone = nil) { |obj| block } → obj or nil
enum.find( proc{'default'} ) { |e| e[:a] == 1 } # => {:a=>2, :b=>3}
enum.find( proc{'default'} ) { |e| e[:a] == 3 } # => "default"
# and you can pass a proc or a lambda
enum.find( -> (){'default'} ) { |e| e[:a] == 3 } # => "default"
Enumerable#partition
# Enumerable#partition
(1..6).partition {|v| v.even? }
#=> [[2, 4, 6], [1, 3, 5]]
Unescaping text
# Unescaping text
"\"WTF is this \\n \\u2202 text \\u{1F9D0}\\u{1F914}\"".undump
# => "WTF is this \n ∂ text 🧐🤔"
String spliting
# String spliting
"This is amazing".split
# => ["This", "is", "amazing"]
"This is amazing".split(' ', 2)
# => ["This", "is amazing"]
# wrap the matcher in parentheses to include it in the output
"This is amazing".split(/(\s)/)
# => ["This", " ", "is", " ", "amazing"]
"This is amazing".partition(' ')
# => ["This", " ", "is amazing"]
"This is amazing".rpartition(' ')
# => ["This is", " ", "amazing"]
String#strip
# String#strip
user_input = " John "
user_input.strip # => "John"
user_input.lstrip # => "John "
user_input.rstrip # => " John"
user_input.strip! # => "John"
user_input = "John"
user_input.strip! # => nil
String#center
# String#center
'Foo'.center(20)
# => " Foo "
def puts_title(title, size:20, decoration:"#")
puts "".center(size, decoration)
puts " #{title} ".center(size, decoration)
puts "".center(size, decoration)
end
puts_title("Hello!")
####################
###### Hello! ######
####################
String#casecmp?
# String#casecmp?
"aBcDeF".casecmp?("abcde") #=> false
"aBcDeF".casecmp?("abcdef") #=> true
"aBcDeF".casecmp?("abcdefg") #=> false
"abcdef".casecmp?("ABCDEF") #=> true
"\u{e4 f6 fc}".casecmp?("\u{c4 d6 dc}") #=> true
Reduce
# Reduce
# Sum some numbers
(5..10).reduce(:+) #=> 45
# Same using a block and inject
(5..10).inject { |sum, n| sum + n } #=> 45
# Multiply some numbers
(5..10).reduce(1, :*) #=> 151200
# Same using a block
(5..10).inject(1) { |product, n| product * n } #=> 151200
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo, word|
memo.length > word.length ? memo : word
end
longest #=> "sheep"
The inject and reduce methods are aliases.
Example from ruby doc
OpenStruct
# OpenStruct
require 'ostruct'
address = OpenStruct.new(country: "France")
# => #<OpenStruct country="France">
address.city = "Paris"
# => #<OpenStruct country="France", city="Paris">
# OpenStruct VS Struct
Address = Struct.new(:country)
address2 = Address.new(country = "France")
# => #<struct Address country="France">
address2.city = "Paris"
# NoMethodError: undefined method `city=' for #<struct Address country="France">
Struct
# Struct
class User
Address = Struct.new(:city, :contry)
attr_accessor :name, :address
def initialize(name, address)
@name = name
@address = Address.new(address[:city], address[:country])
end
end
u = User.new("Rob Halford", {city: "Phoenix", country:"USA"})
u.address
# => #<struct User::Address city="Phoenix", contry="USA">
u.address.city = 'San Diego'
u.address
# => #<struct User::Address city="San Diego", contry="USA">
Time tips
# Time tips
t = Time.new(2018, 8, 28, 22, 40, 01, "+02:00")
# => 2018-08-27 22:40:01 +0200
t.monday?
# => true
t.wday # monday = 1, tuesday = 2,...
# => 1
t.yday # Returns the day of the year, 1..366.
# => 239
respond_to?
# respond_to?
class User
def name; end
private
def registered?; end
end
user = User.new
user.respond_to? :name # => true
user.respond_to? :registered? # => false
user.respond_to? :registered?, true # => true
itself and occurence count
# itself and occurence count
array = [1,2,3,4,5,1,2,2,3]
array.itself
# => [1, 2, 3, 4, 5, 1, 2, 2, 3]
# Real worl exemple: occurence count
array.each_with_object(Hash.new(0)) { |item, count| count[item] = count[item] + 1 }
# => {1=>2, 2=>3, 3=>2, 4=>1, 5=>1}
array.group_by{|e| e}
# => {1=>[1, 1], 2=>[2, 2, 2], 3=>[3, 3], 4=>[4], 5=>[5]}
array.group_by(&:itself)
# => {1=>[1, 1], 2=>[2, 2, 2], 3=>[3, 3], 4=>[4], 5=>[5]}
array.group_by(&:itself).transform_values(&:count)
# => {1=>2, 2=>3, 3=>2, 4=>1, 5=>1}
You should also look at this long thread about this feature
gsub: retrieve the matched string
# gsub: retrieve the matched string
"The Force will be with you. Always.".gsub(/[ae]/){"*#{$&}*"}
# => "Th*e* Forc*e* will b*e* with you. Alw*a*ys."
"The Force will be with you. Always.".gsub(/[ae]/, '*\0*')
# => "Th*e* Forc*e* will b*e* with you. Alw*a*ys."
Imagine we’re in the context of a rails app with the following model:
# Imagine we're in the context of a rails app with the following model:
class Restaurant < ApplicationRecord
scope :best, -> { where(stars: 3) }
end
# You suddenly feel the urge to understand how a `scope` gets defined by ActiveRecord.
# You know that `scope`, used as it is in the context of the `Restaurant` class,
# must be a class-level method.
Restaurant.method(:scope)
# => #<Method: Restaurant.scope>
Restaurant.method(:scope).source_location
# => ["/Users/nicolasfilzi/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/activerecord-5.2.1/lib/active_record/scoping/named.rb", 163]
# Now you know where to start your code spelunking session! Enjoy!
by @n_filzi
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!"}}}
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
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
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>,
#... ]
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]}
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
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
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