Ruby
Table of Contents
- Good to Know
- Strings
- Number Methods
- Misc
- Comparison Operators
- Boolean Operators
- Operators
- Combined Comparison Operator
- Arrays
- Loops
- One Line Ifs & Ternary Conditional Expressions & Case Statements
- Methods that Take Blocks
- Times Iterator
- Loop Iterator
- Each Iterator
- Map
- Select
.upto
,.downto
- Hashes
- Methods
- Splat Arguments
- Procs
- Lamdas
- Scope Prefixes
- Classes
- Modules
- Getting all but the first element from Ruby array
- Returning a fixed number of items from the tail of a list
- Basic Object
- Currying in Ruby
- Inject vs each_with_object
- Using pry and ncurses together
- Parallel assignment
- Regex Literals and URLs
- Spliting a string into a maximum number of segments
- The
DATA
constant - References
TODO
Good to Know
Everything is an object
- Uniform access principle
Classes
Blocks (closures or anonymous functions)
Hashes
Strings
.length
.reverse
.upcase
.downcase
.capitalize # capitalizes the first letter
.include? "x" # tests if string contains "x"
.match # works like .include?
.start_with?("x")
.end_with?("x")
“a”.next => “b”
"#{}" # string interpolation - "My name is #{name}"
Returns a copy of str with the all occurrences of pattern substituted. Pattern is typically a regex.
.gsub(pattern, replacement)
Iterates through the string, returns an array of all results matching the pattern
.scan(pattern)
.insert(index, “something”)
Number Methods
.abs # absolute value
.ceil # rounds float up
.floor # rounds float down
.eql?(n) # is the object equal to n?
Misc
gets # raw input from user (get string)
gets.chomp # raw input from user w/o extra line
.is_a? Integer # tests whether == given data type
rand # generates random float between 0 and 1
rand(n) # generates random integer between 0 and n, exclusive
.sort # sorts numerically or alphabetically.
# For a reverse sort, include the following block as a parameter:
array.sort! { |first, second| second <=> first }
# or
array.sort.reverse!
.to_s # converts a variable to a string
.to_i # converts to integer
.to_f # converts to float
.to_sym # or .intern converts a string to a symbol
object.replace(object.method) replaces object with the output of method (liked .method!)
Comparison Operators
>
<
>=
==
!=
Boolean Operators
&& # and
|| # or
! # not
Operators
+=
-=
*=
/=
||= # conditional assignment operator - only assigns a value to a variable if the current value of the variable is nil.
Combined Comparison Operator
<=>
Returns 0
if the first operand (item to be compared) equals the second, 1
if first operand is greater than the second, and -1
if the first operand is less than the second.
Arrays
array = [1, 2, 3]
array[0] # fetch item at given index
array[0][0] # fetch item at given index (2D array or beyond)
array[0] = 1 # set item at given index
array << 23 # add item to end of array ("concatenation operation")
array.push(element) << is a shortcut for this
array.pop(element)
array.unshift(element) # like push but it adds to beginning of array
array.shift(element) # like pop but it removes from beginning of array
array.first # returns first element
array.length # returns # of elements
array.empty?
array.include?(x)
array.index(x) # What index is the given element at?
array.join(x) # returns elements as string separated by x
string.split(x) # turns a string into an array with x as separator
get where two arrays match in ruby: use Intersection, or &
x = [1, 2, 4]
y = [5, 2, 4]
x & y # => [2, 4]
Loops
while
until # opposite of while (stops when false)
for x in 1...10 # Up to 10, exclusive (9 times), e.g.: for num in 1...10 puts num end
for x in 1..10 # Up to 10, inclusive (10 times)
One Line Ifs
puts "It's true" if true
Ternary Conditional Expressions
puts 0 < 1 ? "0 is less than one" : "0 is greater than 1"
Case Statements
case which_is_greater
when x > y then puts "x is greater."
when x < y then puts "y is greater."
when x == y then puts "x and y are equal."
else puts "I am confused."
end
Or
case which_is_greater
when x > y
puts "x is greater."
when x < y
puts "y is greater."
when x == y
puts "x and y are equal."
else
puts "I am confused."
end
Methods that Take Blocks
Yield
You can make your own methods that take blocks with yield.
def method(n)
yield n # Passes "n" to the block. yield can also be standalone.
end
def double(num)
yield num
end
double(3) { |x| x*2 }
Times Iterator
Technically a method using the block ({}) as an argument.
30.times {
print "Ruby!"
}
You can keep track of the index if you like.
30.times { |index|
print index
}
Loop Iterator
n = 1
loop {
n += 1
print "Ruby!"
break if n > 30
}
Each Iterator
Technically a method that uses the block ({}) as an argument.
array = [1, 2, 3]
array.each { |x|
puts x
}
hash.each { |key, value| puts "#{key} is #{value}"}
hash.each_key { |key| #do something}
hash.each_value { |value| #do something}
Map
A lot like .each, except .map returns an array composed of the block results while each returns the original array. .map is a good choice if you actually want to change the elements in your array: array = array.map(some_block)
doubled_array = array.map { |num| num*2 }
array.map(&:to_s) #Converts all elements to a string. .to_s is passed like a block.
array.map(&:reverse)
Select
Returns values that matches the conditions defined in the block.
array.select { |x| x > 3}
.upto
, .downto
1.upto(5) { |number| puts number} 1 2 3 4 5
"L".upto("O") { |letter| puts letter} L M N O
5.downto(1) { |number| puts number} 5 4 3 2 1
array.reduce(:+) # Returns sum of values in array. + is passed like a block.
array.inject(:+) # Works similarly.
Hashes
Literal Notation
my_hash = {
"name" => "Eric",
"age" => 26,
"hungry?" => true
}
It's often faster to use symbols (:symbol) as keys. In Ruby 1.9 onward you can use this notation:
my_hash = {
name: "Eric",
age: 26,
hungry: true
}
.new
notation
my_hash = Hash.new
my_hash["name"] = "Eric" # Adds (or changes) key-value pair.
my_hash = Hash.new("derp") # creates a hash with "derp" as the default nil (that is, the value of an undefined key)
my_hash.select { |k, v| v > 3} # returns values greater than 3 (an example).
Some Hash Methods
.keys # returns list of keys as an array
.values # returns list of keys as an array
.has_key?(key)
.has_value?(value)
.merge(another_hash) # combines 2 hashes, values of another_hash trump the other if there are matching keys
Methods
def say_hello(parameter)
puts "Hello there, #{parameter}!"
end
Splat Arguments
A "splat argument" – can accept an indefinite # of arguments, stored as an an array.
def what_up(greeting, *bros)
bros.each { |bro| puts "#{greeting}, #{bro}!" }
end
Procs
A closure or a "saved block”. Kind of like a method, but it can be used like a block and passed to methods that take blocks as parameters. & is used to convert a proc into a block.
cube = Proc.new { |x| x ** 3 }
[1, 2, 3].collect!(&cube) # & is used to convert a proc into a block.
cube.call(2) # .call calls procs directly.
Methods can be treated like blocks, too!:
strings_array = numbers_array.collect(&:to_s)
Lamdas
A closure. More or less identical to PROCS, with a key difference: when a lambda returns, it passes control back to the calling method; when a proc returns, it does so immediately, without finishing the calling method.
cube = lambda { |x| x ** 3 }
[1, 2, 3].collect!(&cube)
cube.call(2)
Scope Prefixes
Prefixed to variables to specify their scope.
- Global Variable: available everywhere
- Local Variable: available only within a method
- Class Variable: available to a common class (all instances share it)
- Instance Variable: available only to a particular instance of a class
$ # global variable (variables are global by default unless inside a method or class, use this prefix if you want to make an exception for a variable within a method or class).
@ # instance variable (specific to a single instance of a class).
@@ # class variable
Classes
class NameOfClass
def initialize(x)
@x = x
end
end
class Class < SuperClass
#this is the syntax for class inheritance.
end
class Name
#public methods here
private
#private methods here
end
attr_reader :variable # allows you to read "variable" without an extra method
attr_writer :variable # allows you to edit "variable"
attr_accessor :variable # allows you to both read and edit "variable"
calling a parent method in ruby:
From: StackOverflow
If the method is the same name, i.e. you're overriding a method you can simply use super. Otherwise you can use an alias_method or a binding.
class Parent
def method
end
end
class Child < Parent
alias_method :parent_method, :method
def method
super
end
def other_method
parent_method
#OR
Parent.instance_method(:method).bind(self).call
end
end
Modules
"Toolkits" for the interpreter; like a class but it doesn't have instances.
module Circle
#capitalized variables are "constants" and are not supposed to change.
PI = 3.141592653589793
def Circle.area(radius) #the Module must be called explicitly in each method.
PI * radius**2
end
def Circle.circumference(radius)
2 * PI * radius
end
end
puts Circle::PI # scope resolution operator ("get this from the module")
require 'circle' # imports a given module into the environment
include Circle # when placed in a class, this allows an instance of the class to use the module's methods as if they were its own. This is called a "mixin," and allows Ruby to mimic multiple class inheritance, which it can't normally do – it CAN "inherit" multiple modules.
require_relative ‘something.rb’ # allows you to access the data found in another file in the same directory.
Getting all but the first element from Ruby array
When getting all but the first element from an array in Ruby, we have a few options. Given the following list:
list = [1,2,3,4]
Shift
We could use shift
, however it mutates the array. It also doesn't return the list so we need two lines to write this code:
list.shift # => 1
rest_of_list = list # => [2,3,4]
Slice
In the past I've used slice
, but specifying indices or passing the length of an array is ugly and low-level:
rest_of_list = list[1..-1] # => [2,3,4]
rest_of_list = list.slice(1, list.length) # => [2,3,4]
list # is not modified # => [1,2,3,4]
Drop
My new favorite is drop
which does not mutate and does not require ugly, low-level code:
list.drop(1) # => [2,3,4]
list # is not modified # => [1,2,3,4]
An added benefit ofdrop
is that it will always return an array, while slice
will sometimes return nil
[].slice(1..-1) # => nil
[].drop(1) # => []
Returning a fixed number of items from the tail of a list
Array#last
takes an argument to limit the number of items to return
from the tail of a list.
> [1,2,3,4,5].last(2) # => [4, 5]
Basic Object
Ruby has a BasicObject
class which doesn't include Kernel methods and acts as the parent for all Object
s. You can see the ancestry in practice via:
Object.ancestors # => [Object, Kernel, BasicObject]
BasicObject.ancestors # => [BasicObject]
BasicObject
thus has very few instance methods which would be inherited by a
subclass:
BasicObject.instance_methods # => [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]
Object.instance_methods.length # => 55
This is useful for delegators which would otherwise not delegate methods such as #to_s.
Example:
class ListDelegator
def initialize(*list)
@list = list
end
def method_missing(method, *args, &block)
@list.map do |item|
item.__send__(method, *args, &block)
end
end
end
class ListDelegatorBasic < BasicObject
def initialize(*list)
@list = list
end
def method_missing(method, *args, &block)
@list.map do |item|
item.__send__(method, *args, &block)
end
end
end
class Out
def to_s
"Out"
end
end
ListDelegator.new(Out.new).to_s # => "#<ListDelegator2:0x007fcab400d148>"
ListDelegatorBasic.new(Out.new).to_s # => ["Out"]
Currying in Ruby
Ruby defines curry
for Method
and Proc
, allowing procs to return partially applied procs when they get called with fewer than the required number of arguments. For example:
multiply = -> x,y { x * y }.curry # => #<Proc:0x007fed33851510 (lambda)>
multiply[2,3] # => 6
double = multiply[2] # => #<Proc:0x007fed35892888 (lambda)>
double[3] # => 6
Note: While Proc#curry
has been around since Ruby 1.9, Method#curry
was only added in Ruby 2.2.0. For versions before 2.2.0, you will first need to convert your method object to a proc via Method#to_proc
.
Check out the ruby docs for more details.
Inject vs each_with_object
Inject
Inject takes the value of the block and passes it along. This causes a lot of errors like.
inject_array = ['A', 'B', 'C', 'D']
inject_array.inject({}) do |accumulator, value|
accumulator[value] = value.downcase
end
# 'IndexError: string not matched'
The above code causes IndexError: string not matched error
.
What you really wanted.
inject_array = ['A', 'B', 'C', 'D']
inject_array.inject({}) do |accumulator, value|
accumulator[value] = value.downcase
accumulator
end
# Output: => {"A"=>"a", "B"=>"b", "C"=>"c", "D"=>"d"}
each_with_object
each_with_object
ignores the return value of the block and passes the initial object along.
array = ['A', 'B', 'C', 'D']
array.each_with_object({}) do |value, accumulator|
accumulator[value] = value.downcase
end
# Output: => {"A"=>"a", "B"=>"b", "C"=>"c", "D"=>"d"}
One more thing which you can notice is the order of arguments to the block for each of the functions.
Inject takes the result/accumulator and then the iteratable value, whereas each_with_object
takes the value followed by the result/accumulator.
Using pry and ncurses together
Because of ncurse's visual mode you will not be able to use pry normally. The easiest way to get pry working is to do the following:
close_screen
binding.pry
refresh
close_screen
restores the non-visual mode which allows you to see the output of pry correctly. When you exit
from pry, refresh
will resume ncurse's visual mode.
Parallel assignment
Ruby allows us to assign multiple variables on the same line. This is also called parallel assignment.
a, b = 1, 2 # => [1, 2]
a # => 1
b # => 2
We can also use parallel assignment and the splat operator in combination to split the array into the head and tail. This is in particular useful if we want to get all but the first element from an array.
list = [1,2,3,4] # => [1,2,3,4]
head, *tail = list # => [1,2,3,4]
head # => 1
tail # => [2,3,4]
list # is not modified # => [1,2,3,4]
Empty arrays
When using parallel assignment on an empty array, the tail will always return an array while the head will be nil
.
head, *tail = [] # => []
head # => nil
tail # => []
Regex Literals and URLs
Regular expressions in Ruby are typically delineated by the /
character. However, this forces you to escape /
in your expression.
/http:\/\/sub\.domain\.com\/path/
Use the %r{...}
notation to avoid escaping all those slashes.
%r{http://sub\.domain\.com/path}
Spliting a string into a maximum number of segments
String#split
takes an argument to limit the number of returned matches. Once the limit is reached, the rest of the string is returned as one match even if it contains more delimiters.
For example, say we are parsing a string whose first two lines are always headers and the rest is the body:
text = <<-TEXT
header1 = value
header2 = value
the body
keeps going
on over
multiple lines
TEXT
A simple split
on newlines would create an array with each line as an individual element:
text.split("\n")
# => ["header1 = value",
# "header2 = value",
# "the body",
# "keeps going",
# "on over",
# "multiple lines"]
Since we know there are two headers and a body, we can tell split
to stop splitting once we have three segments:
text.split("\n", 3)
# => ["header1 = value",
# "header2 = value",
# "the body\nkeeps going\non over\nmultiple lines"]
The DATA
constant
The DATA
constant in Ruby allows us to access the text at the end of our file listed after the __END__
block. This can be surprisingly useful, for instance if we need to extract information from a rather large data blob.
Imagine a scenario where we need to extract the user names from a SQL statement.
puts DATA.read.gsub("ALTER USER ", "").gsub(/IDENTIFIED BY.+/, "")
__END__
ALTER USER Annette IDENTIFIED BY 'Annette';
ALTER USER Warren IDENTIFIED BY 'Warren';
ALTER USER Anthony IDENTIFIED BY 'Anthony ';
ALTER USER Preston IDENTIFIED BY 'Preston';
ALTER USER Kelly IDENTIFIED BY 'Kelly ';
ALTER USER Taylor IDENTIFIED BY 'Taylor';
ALTER USER Stiller IDENTIFIED BY 'Stiller';
ALTER USER Dennis IDENTIFIED BY 'Dennis';
ALTER USER Schwart IDENTIFIED BY 'Schwart';
Executing the file will print this:
Annette
Warren
Anthony
Preston
Kelly
Taylor
Stiller
Dennis
Schwart
Since DATA
is simply just a virtual File
object within our file, we can also pass the DATA
variable (without #read
) to methods that accept a File
.
require "json"
puts JSON.load(DATA)
__END__
{
"records": [
{
"artist":"Iggy Pop",
"title":"Lust for Life"
},
{
"artist":"Television",
"title":"Marquee Moon"
},
{
"artist":"Talking Heads",
"title":"Talking Heads: 77"
}
]
}
Dir
Change directory
Dir.chdr(dir)
Elements in directory
def elements_in_dir(dir)
Dir.chdir(dir)
Dir.glob('*')
end
Directories in a directory
def dirs_in_dir(dir)
original_dir = Dir.pwd
Dir.chdir(dir)
dirs = Dir.glob('*').select {|f| File.directory? f}
Dir.chdir(original_dir)
dirs
end
File
Rename file/directory
File.rename "original_path", "new_path"
Favorite Gems
RuboCop
Install
gem install rubocop
Autocorrect
rubocop --auto-correct <file>