Ruby snippets you can understand in 30 seconds or less.
Inspired by 30-seconds-of-knowledge.
Always wondered how to see Ruby's class hierarchy? Simply call ancestors
on a class to see the ancestor chain.
The return array contains the following order:
- the calling class
- its included modules
- its parent class
- the included modules of its parents class
- the parent class of its parent class
Here is the ancestor chain of the String class:
irb> String.ancestors
=> [String, Comparable, Object, Kernel, BasicObject]
A Ruby interpreter is any program that parses the Ruby source code to imitate its behavioral execution. It converts the program line by line compared to a compiler which converts the whole program at once. Hence, a code change only requires an edit. Interpreters only throw an error if the program gets executed and reach the line that containes the error, whereas compilers will throw the error already before execution. There are multiple versions of Ruby interpreters to choose from, each with a different but very similar set of rules and options.
Common Ruby Interpreters:
- Ruby MRI
- JRuby
- YARV
- Rubinius
If you've ever wondering what methods are available for a specific object, just call .methods
on it:
irb> name = 'Bob'
=> "Bob"
irb> name.methods
=> [:include?, :%, :unicode_normalize, :*, :+, :to_c, ...]
This will return an array of symbols (names) of all publicly accessible methods of the object and its ancestors.
The BasicObject class is the top parent of all class. It is a blank instance of class class
with no superclass and contains a minimum of methods for object creation and comparison.
irb> class User
irb> end
=> nil
irb> User
=> User
irb> User.class
=> Class
irb> User.superclass
=> Object
irb> Object.ancestors
=> [Object, Kernel, BasicObject]
irb> BasicObject.class
=> Class
irb> BasicObject.superclass
=> nil
irb> BasicObject.ancestors
=> [BasicObject]
Ever wondered if there is a better way how you can traverse through multi-layer hashes or arrays to retrieve a specific value? Luckily Ruby 2.3 introduced a new method called dig
.
catalogue = {
drinks: {
apple_juice: {
stock: 8,
price_per_unit: 2.4
},
orange_juice: {
stock: 4,
price_per_unit: 2.2
}
}
}
Given the above hash, we would access the price per unit of the orange juice by the below line.
catalogue[:drinks][:carrot_juice][:price_per_unit]
=> NoMethodError: undefined method `[]' for nil:NilClass
To mitigate Rails to throw an error we need to check the existence of each key while navigating through the hash structure.
catalogue[:drinks] && catalogue[:drinks][:carrot_juice] && catalogue[:drinks][:carrot_juice][:price_per_unit]
By using dig
we follow DRY and retrieve the value of each key object by dealing smoothly with nil values.
catalogue.dig(:drinks, :carrot_juice, :price_per_unit)
=> nil
catalogue.dig(:drinks, :apple_juice, :price_per_unit)
=> 2.4
In Ruby there are two common naming conventions for methods. Either you end a method name with a ! or ?. Methods ended by a ! are called bang methods and often modifies the original object.
irb> book_title = "example book title"
=> "example book title"
irb> puts book_title.capitalize!
Example book title
=> nil
irb> puts book_title
Example book title
=> nil
irb>
A famous example is merge
vs merge!
when dealing with params, whereas the former method will return the params and the latest modifies the original params.
The question mark at the end of a method name indicates that we can expect a boolean as return value.
irb> array = []
=> []
irb> array.empty?
=> true
irb> user = { 'first_name' => 'Bob', 'last_name' => 'Carlton' }
irb> user.has_key?('first_name')
=> true
The Kernel module is included by class Object and provides methods that are globally available, hence every Ruby object has access to them. These methods are called without a receiver and only act on the passed arguments.
Famouse representative of the Kernel module are:
- Array
- Hash
- Integer
- Hash
- p, print and puts
Ever wondered how you can find out where exactly a method lives inside Ruby?
Ruby offers two helpful methods here. By passing the name of the method as a symbol to owner
will reveal where a method lives inside Ruby. If you curious about who receives s specific method, just pass the name again as a symbol to receiver
.
irb> method(:initialize).owner
=> BasicObject
irb> method(:initialize).receiver
=> main
The ruby
command is mostly known for running a ruby app or script. But it also has a number of interesting options to offer.
Execute a single line code snippet just by using the -e flag:
ruby -e '[1, 2, 3].each do |e| puts e end'
Calling digits
on an integer returns the array including the digits extracted by place-value notation with radix base of int.
irb> 123.digits
=> [3, 2, 1]
irb> 14.digits(7)
=> [0, 2]
Using values_at
to retrieve multiple non-sequential values of arrays and hashes.
For a given array it will return an array of the values associated with the index position.
irb> programming_languages = ['Ruby', 'Python', 'Golang', 'C++', 'C', 'D']
=> ["Ruby", "Python", "Golang", "C++", "C", "D"]
irb> programming_languages.values_at(0, 2, 4)
=> ["Ruby", "Golang", "C"]
For a given hash it will return an array of the values associated with the given keys.
irb> fruits = { 'orange' => '$2.00', 'apple' => '$3.00', 'grapes' => '$2.50' }
=> => {"orange"=>"$2.00", "apple"=>"$3.00", "grapes"=>"$2.50"}
irb> fruits.values_at('orange')
=> ["$2.00"]
to_s
is an explicit casting helper and is used to transform a value from one type to another. to_str
is an implicit casting helper
with the same purpose. The difference is that the former will always return a string whereas the later will result in an error or data loss
if the value it acts on does not act like the type we are casting. Ruby will only return a value when objects act like the type.
Ruby is very strict when using an implicit casting helper to ensure that the value acts like the type we want.
irb> [1, 2].to_s
=> "[1, 2]"
irb> [1, 2].to_str
NoMethodError: undefined method `to_str` for [1, 2]:Array
Did you mean? to_s
irb> :name.to_s
=> "name"
irb> :name.to_str
NoMethodError: undefined method `to_str` for :name:Symbol
Did you mean? to_s
to_i
is an explicit casting helper and is used to transform a value from one type to another. to_int
is an implicit casting helper
with the same purpose. The difference is that the former will always return a string whereas the later will result in an error or data loss
if the value it acts on does not act like the type we are casting. Ruby will only return a value when objects act like the type.
Ruby is very strict when using an implicit casting helper to ensure that the value acts like the type we want.
irb> 19.99.to_i
=> 19
irb> 19.99.to_int
=> 19
irb> '19.99'.to_i
=> 19
irb> '19.99'.to_int
NoMethodError: undefined method `to_int` for "String":String
Did you mean? to_i
to_str
to_a
is an explicit casting helper and is used to transform a value from one type to another. to_ary
is an implicit casting helper
with the same purpose. The difference is that the former will always return a string whereas the later will result in an error or data loss
if the value it acts on does not act like the type we are casting. Ruby will only return a value when objects act like the type.
Ruby is very strict when using an implicit casting helper to ensure that the value acts like the type we want.
irb > { key: :value }.to_a
=> [[:key, :value]]
irb > { key: :value }.to_ary
NoMethodError: undefined method `to_ary` for {:key=>:value}:Hash
Did you mean? to_a
Ruby's Enumerable#cycle
offers an easy way to either repeat a certain pattern n times or just to switch between two predefined states.
Repeating a certain pattern:
irb> array = [1, 2, 3]
=> [1, 2, 3]
irb> array.cycle(3).to_a
=> [1, 2, 3, 1, 2, 3, 1, 2, 3]
Switching between two states:
irb> button = ['on', 'off'].cycle
=> #<Enumerator: ["on", "off"]:cycle>
irb> button.next
=> "on"
irb> button.next
=> "off"
Ever wondered how much power Ruby's count
method has? Besides counting the elements of an array, it can do pretty awesome things. So lets' start with the most common use case counting the elements of an array.
irb> numbers = [1,2,5,3,6,2,5,3]
=> [1, 2, 5, 3, 6, 2, 5, 3]
irb> numbers.count
=> 8
Let's say you want to count how often the number 3 is represented in the above array. The thought would be to use a loop an iterate over the array and increment a counter every time you see the number 3. But there is a better way. Just pass the object you looking for to the count
method.
irb> numbers.count(3)
=> 2
Finally, you can also pass a block to do more complicated counts.
irb> numbers.count(&:even?)
=> 3
Both methods are used to produce a shallow copy of obj—the instance variables of obj are copied, but not the objects they reference. clone
copies the frozen and tainted state of obj whereas dup
copies the tainted state of obj.
In general, clone and dup may have different semantics in the descendant classes. While clone is used to duplicate an object, including its internal state, dup typically uses the class of the descendant object to create the new instance. When using dup, any modules that the object has been extended with will not be copied.
irb> array = ['a','b','c']
=> ["a", "b", "c"]
irb> array.freeze
=> ["a", "b", "c"]
irb> array_clone = array.clone
=> ["a", "b", "c"]
irb> a_clone << "d"
RuntimeError: cant modify frozen Array
irb> array_dup = array.dup
=> ["a", "b", "c"]
irb> a_dup << "d"
=> ["a", "b", "c", "d"]
Returns a value from the hash for the given key. If the key can't be found, there are several options:
- will raise a KeyError exception when no arguments given
- if a default is given, then that will be returned
- if the optional code block is specified, then that will be run and its result returned.
irb> user = {'first_name': 'Dummy', 'last_name': 'User'}
=> {:first_name=>"Dummy", :last_name=>"User"}
irb> user.fetch(:first_name)
=> "Dummy"
irb> user.fetch(:email)
KeyError: key not found: :email
from (irb):5:in `fetch'
irb> user.fetch(:email, 'no email added')
=> "no email added"
irb> user.fetch(:email){ |e| "no #{e} added"}
=> "no email added"
Converts any arguments to arrays, then merges elements of self with corresponding elements from each argument.
This generates a sequence of ary.size n-element arrays, where n is one more than the count of arguments.
If the size of any argument is less than the size of the initial array, nil values are supplied.
If a block is given, it is invoked for each output array, otherwise an array of arrays is returned.
irb> first_names = ['George', 'Marcus', 'Brian']
=> ["George", "Marcus", "Brian"]
irb> last_names = ['Massy', 'Windmil']
=> ["Massy", "Windmil"]
irb> first_names.zip(last_names)
=> [["George", "Massy"], ["Marcus", "Windmil"], ["Brian", nil]]
Let's say we wanna add a new element to an array but we want to make sure to keep each element uniq. One way to achive this would be:
irb> alphabet = ['a','b','c']
=> ["a", "b", "c"]
irb> alphabet << 'c'
=> ["a", "b", "c", "c"]
irb> alphabet.uniq
=> ["a", "b", "c"]
Luckily Ruby offers Bitwise OR operator:
x |= y
is shorthand for:
x = x | y
So our example from above would become:
irb> alphabet = ['a','b','c']
=> ["a", "b", "c"]
irb> alphabet | ['c']
=> ["a", "b", "c"]
We can also assign more then one element at the same time:
irb> alphabet = ['a','b','c']
=> ["a", "b", "c"]
irb> alphabet | ['c','d']
=> ["a", "b", "c", "d"]