Skip to content

Instantly share code, notes, and snippets.

@cyhsutw
Last active August 29, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cyhsutw/73328c2f1f76eb1b7f8c to your computer and use it in GitHub Desktop.
Save cyhsutw/73328c2f1f76eb1b7f8c to your computer and use it in GitHub Desktop.
Ruby Notes

Usage of unless

Play a game unless you're tired. If you're tired, go to bed.

unless tired
 puts "Play games" 
else
  puts "Go to bed"
end

String formatting

my_name = 'Cheng-Yu'
# must use double quotes around the string to be formatted,
# using single quotes will not cause an evaluation just like in php
puts "My name is #{my_name}."

For more details: http://rors.org/2008/10/26/dont-escape-in-strings

Check if a string is in another string

# this will return a boolean
some_string.include? "string to match"

Substitute a string with another one

# gsub(pattern, rep) => a string with `pattern` replaced by `rep`
sub_string = some_string.gsub(/\s+/, ' ')

or using gsub!

# gsub!(pattern, rep) => a string with `pattern` replaced by `rep` and set the new string back
some_string.gsub!(/\s+/, ' ')

Range

Two different ways to define a range:

0..5  # [0, 1, 2, 3, 4, 5]
0...5 # [0, 1, 2, 3, 4]

for loops

You may either use .. or ... to enum some range of numbers

for num in 1...13
 puts num
end
1
2
3
4
5
6
7
8
9
10
11
12

or

for num in 1..13
 puts num
end
1
2
3
4
5
6
7
8
9
10
11
12
13

Iterators

# Infinite loop that prints `YOLO` for days.
loop { puts "YOLO" }
# loop until i <= 0
i = 20
loop do
  i -= 1
  puts i
  break if i <= 0
end

next

In Ruby, the continue keyword in a loop is replaced by next.

However, break remains the same.

for i in 1..20
 if i % 2 == 0
  next
 end
 puts i
end

The above code will print out the odd numbers in [1, 20].

each

each is a iterator that iterate through a iteratable.

array = [1,2,3,4,5]

# You may give arbitary name the element it iterate through
# in this case, x
array.each do |x|
  x += 10
  print "#{x}"
end

As seen in most for the languages, this iterator will not modify the original values.

(Review) forEach seen in Javascript.

arr = [1, 2, 3];
arr.forEach(function(ele){
 console.log(ele);
});

each Syntax Sugar

numbers = [1, 2, 3, 4, 5]

# one way to loop
numbers.each { |item| puts item }

# another way to loop
numbers.each do |item|
  puts item
end

.times

# do something for multiple times
3.times{
 puts "Ruby Sparks"
}

Hash a.k.a Map

my_map = Hash.new
my_another_map = {
 "key" => "value"
}

# giving a default value to an undefined key
map2 = Hash.new("default_val")

Sorting a Hash

colors = {"blue" => 3, "green" => 1, "red" => 2}
colors = colors.sort_by do |color, count|
  count
end
colors.reverse!

or if you don't want to reverse that

colors = {"blue" => 3, "green" => 1, "red" => 2}
colors = colors.sort_by do |color, count|
  # use some simple math 
  -count
end

Sorting an Array

arr = [4, 5, 3]
arr.sort!

or with custom comparison rules:

arr = ["yolo", "yoy", "yay"]
arr.sort! { |first, second|
	# sort by length, desc
	second.length <=> first.length
}

Splat Arguments

Just something like public void someMethod(String... args) in Java.

def func(*args)
 args.each{ |arg|
  puts arg
 }
end

Method Definition

? is legal for a name of a method.

def is_even? (num)
 return num % 2 == 0
end

Blocks

You can think of blocks as a way of creating methods that don't have a name. (These are similar to anonymous functions in JavaScript or lambdas in Python.)

3.times{
 puts "Blocks!"
}

Combined Comparison Operator

Can be thought as "Is the first operand greater than the second one?"

1 <=> 1 # Since 1 == 1, it should be 0
2 <=> 1 # Since 2 > 1, it should be 1
1 <=> 2 # Since 1 < 2, it should be -1

Method returns

By default, Ruby will return last line of a method.

def add(a, b)
  a + b
  # or use explicit return
  # return a + b
end

# c will be 4
c = add(1, 3)

Symbols

Ruby symbol as a sort of name.

It's important to remember that symbols aren't strings:

"string" == :string # false

Also, only one symbol may exist in a give time.

Try to access the object_id:

# different object_ids
puts "string".object_id
puts "string".object_id

# same object_ids
puts :symbol.object_id
puts :symbol.object_id

Use symbols are good for performance:

  1. They're immutable, meaning they can't be changed once they're created
  2. Only one copy of any symbol exists at a given time, so they save memory
my_hash = {
  :first => "yep"
}

Converting symbols to strings (or strings to symbols)

:my_sym.to_s 	# "my_sym"
"my_sym".to_sym	# :my_sym
"my_sym".intern # :my_sym

Hash

For Ruby 1.9+, you may use the symbols in a hash more like the way in Javascript.

my_hash = {
	one: 2 # one is a symbol with : at the end
				# which is equal to :one => 2
}

Creating Hash from a 2D array.

Hash[[1, 2, 3].zip(["a", "b", "c"])]
=begin
{
	1: "a",
	2: "b",
	3: "c"
}
=end

More about zip: http://dfriedm.github.io/blog/2013/10/12/ruby-zip-method/

Filtering a Hash

movie_ratings = {
  memento: 1,
  primer: 3.5,
  the_matrix: 3
}

# good_movies = {:primer => 3.5}
good_movies = movie_ratings.select{ |k, v| 
	# return true if it is selected, false otherwise.
    v > 3
}

Filtering an array

Of course, you may filter an array using select.

(1..10).to_a.select{ |num|
	# return true if it is selected, false otherwise.
	num % 3 == 0
}
# [3, 6, 9]

Access Key Set or Value Set of a Hash

my_hash = {
	one: 1,
	two: 2
}

my_hash.each_key{ |k|
	puts k
}

my_hash.each_value{ |v|
	puts v
}

Delete a Key-Value Pair from a Hash

my_hash = {
	kingsman: 4.5
}
# if a key exist, it will return the value of the key
# and delete it from the hash
pop_val = my_hash.delete(:kingsman)

# otherwise, it will return a nil
nil_val = my_hash.delete(:gone_girl)

cycle

Enumarate elements in an iterable and repeat it forever.

arr = [1, 2, 3]
arr.cycle #<Enumerator: [1, 2, 3]:cycle>
# infinite loop that prints "1 2 3 1 2 3 1 2 3 ..."
arr.cycle.each{ |element|
	puts element
}

rotate

arr = [1, 2, 3]
arr.rotate!
arr.rotate!
arr.rotate!
[2, 3, 1]
[3, 1, 2]
[1, 2, 3]

One-line case

Must followed by a then after the when if you don't want to add a newline.

case color
when "Red" then puts "Apple"
when "Green" then puts "Watermelon"
else puts "Whatever..." 
end

Conditional Assignment

Assign to a variable only if it is nil.

my_book = nil
my_book ||= "What if"
my_book ||= "Zen"
puts my_book

Output:

"What if"

upto and downto

Iterate from a to b.

Works on numbers

# prints 3, 4, 5, 6, ..., 10
3.upto(10){ |num|
	puts num
}

And alphabets

# prints E, D, ..., A
'E'.downto('A'){ |char|
	puts char
}

respond_to?(:method)

Check if an object can respond to a certain method.

Same as [obj respondToSelector:@selector(someMethod:)]; in Objective-C

canPush = [1, 2, 3].respond_to?(:push)
canNotPush = {one: 1}.respond_to?(:push)

.next

.next will return the iterable.

# return 4
3.next
arr = [1, 2, 3]
ele = arr.to_enum
puts ele.next   #=> 1
puts ele.next   #=> 2
puts ele.next   #=> 3
puts ele.next   #raises StopIteration (an exception)

Concatenation Operator (Shovel)

arr = [1, 2, 3]
arr << 4
# => [1, 2, 3, 4]
# which is equivalent to arr.push(4)

name = "Cheng-Yu "
name << "Hsu"
# => "Cheng-Yu Hsu"
# which is equivalent to name += "Hsu"

# can do it mutiple times
coffee = "Latte"
"I love " << coffee << "!"

Check if an object is nil

isNil = my_obj.nil?

Prime numbers

# In Ruby 1.9+
require 'prime'

puts Prime.instance.first 5
# In Ruby 1.8-
require 'prime'

prime_arr ||= []
prime = Prime.new
5.times{ prime_arr < prime.next}

puts prime_arr

Suppress Warnings

In the previous example, if you try to run the second snippet on Ruby 1.9+ interpreter, you'll find that the interpreter complains about Prime.new is a deprecated method.

To disable the warning, try to set the log level.

# make the interpreter less talkative
$VERBOSE = nil

For more details: http://devblog.avdi.org/2011/08/25/temporarily-disabling-warnings-in-ruby/

map and collect

map and collect are the same method that produce another array by manipulating each element of an array (results are of the same size of the original ones).

arr = [1, 2, 3]
doubled_arr = arr.map{ |num|
	num * 2
}
# prints out [2, 4, 6]
puts doubled_arr

or you can do it with collect

arr = [1, 2, 3]
doubled_arr = arr.collect{ |num|
	num * 2
}
# prints out [2, 4, 6]
puts doubled_arr

map, reduce, and inject: http://railspikes.com/2008/8/11/understanding-map-and-reduce

Accepting Blocks?

How to make your method accept a block? Actually, it's all about mixin (inject a code section into another code section).

To do this in a method, use the yield keyword.

def block_test
  puts "We're in the method!"
  puts "Yielding to the block..."
  yield
  puts "We're back in the method!"
end

block_test { puts ">>> We're in the block!" }

Output

We're in the method!
Yielding to the block...
>>> We're in the block!
We're back in the method!

yield with parameters

def show_my_name(name)
	puts "In the method!"
	puts "Begin to yield..."
	yield(name)
	puts "Yield ends, back to the method!"
end

show_my_name("Cheng-Yu"){ |name|
	puts "My name is " << name << "."
}

Above is the example of yielding a implicit block.

You can do it by explicit calling a block as a Proc

def show_my_name(name, &block)
  block.call(name)
end

show_my_name('cy') { |name| p name }  
  

Be aware, block.call is much more slower than yield for ~2.0x, so use yield.

Proc

DRY (Don't Repeat Yourself) is a design policy in Ruby.

To define a repeatable block, we need to assign it to a reference, otherwise, it will be anonymous and disappear after it's allocated.

Proc serves as this purpose, you may understand it as a repeatable block.

even_numbers = Proc.new { |n|
  n % 2 == 0
}
(1..100).to_a.select(&even_numbers)

Invoke a Proc

Without parameters:

my_proc = Proc.new { 
	puts "My number is 100!"
}

my_proc.call

With parameters

my_proc = Proc.new { |num|
	puts "My number is #{num}!"
}

my_proc.call(19)

Proc with built-in methods

You may call build-in methods as a Proc.

strings = ["1", "2", "3"]
nums = strings.map(&:to_i)
# [1, 2, 3]

Passing a method as a variable

my_arr = [1, 2, 3]
sum = my_arr.reduce(:+)
# sum = 1 + 2 + 3 = 6

lambda

lambda looks like to a proc.

my_lambda = lambda { 
	puts "I'm the lambda!" 
}
my_lambda.call
# I'm the lambda

Short syntax of lambda

triple = -> (x) { x * 3 }
[1, 2, 3].map(&triple)

However, there're some differences between them.

One is the way they handle the parameters:

  1. Proc does NOT check the number of parameters.
  2. lambda does check the number of parameters

Example: Proc

sum = proc{ |a, b|
	a + b
}

puts sum.call(1, 3)
puts sum.call(1)

Result:

4
TypeError: nil can't be coerced into Fixnum

Example: lambda

sum = lambda { |a, b|
	a + b
}

puts sum.call(1, 3)
puts sum.call(1)

Result

4
ArgumentError: wrong number of arguments (1 for 2)

Another difference is that Proc and lambda treat return differently:

  1. The return in Proc actually jumps out the Proc block, which will cause the Proc and the caller return immediately (more like mixin, which injects some code into other codes).

    Note that if a proc (block) is passed as an argument, a explicit return will casue an exception. http://ruby-doc.org/core-2.1.5/LocalJumpError.html

  2. And the return in lambda will return the value to the caller (also the control).

Example: Proc

def test_return
	my_proc = Proc.new { return "This is a string!" }
	my_proc.call
	# will not be called, 
	# since the proc returns the method
	"This is a line of DEAD code!"
end

puts test_return

Result

This is a string!

Example: lambda

def test_return
	my_lambda = lambda{
		return "This is a string!"
	}
	my_lambda.call
	# by default, Ruby will return the last line 
	# of code it executes, that is,
	# the following one.
	"This is a line of alive code!"
end

puts test_return

Result

This is a line of alive code!

For more details: http://stackoverflow.com/questions/1740046/whats-the-difference-between-a-proc-and-a-lambda-in-ruby

Class

  1. Use class to define a class.

  2. Use initialize to override the constructor.

  3. Variables:

    • @name: an instance variable named name
    • @@objectType: a class variable named objectType
    • $constant: a global variable named GLOB, it's dangerous, be aware.
  4. Use normal method definition to define instance methods:

    def myMethod
    	# do something
    end
  5. Use self.method_name or ClassName.method_name to define class methods:

    class MyClass
    	def self.classMethod
    		# do something
    	end
    	
    	def  MyClass.classMethod2
    		# do something
    	end
    	
    end
    
    # calling it
    MyClass.classMethod
    MyClass.classMethod2

    Reference: http://www.railstips.org/blog/archives/2009/05/11/class-and-instance-methods-in-ruby/

  6. Subclass

    # a subclass of Fixnum named MyCoolNum
    class MyCoolNum < Fixnum
    end

Exception / Error

Raise an error.

def myFunc(num)
	raise ArgumentError unless num.is_a? Integer
end

Mixin (1)

Inject a code snippet into another file (at instance level).

Use include to do it!

# calc.rb
module Calc
	def add(a, b)
		a + b
	end
	def sub(a, b)
		a - b
	end
end

Inject into this

# my_num_set.rb
# include other files
require_relative './calc.rb'

class MyNumSet
	include Calc
end

And others may call

my_num_set = MyNumSet.new
my_num_set.add(1, 2)

Mixin (2)

Inject a code snippet into another file (at class level).

Use extend to do it!

module ThePresent
  def now
    puts "Now?"
  end
end

class TheHereAnd
  extend ThePresent
end

TheHereAnd.now

Output

Now?

Public, Private Methods

To use the access control modifiers in Ruby OOP, put the keyword before the definition of the method.

class MyClass
	public
	def pubMethod
		puts "This is a public method!"
	end
	
	private
	def priMethod
		puts "This is a private method!"
	end
end

MyClass.new.priMethod

Output

NoMethodError: private method `priMethod' called for #<MyClass:0x007ff8518521f8>

More details: http://blog.eddie.com.tw/2011/07/26/public-protected-and-private-method-in-ruby/

Setters and Getters

class Person

  # read only
  attr_reader :name

  # write only
  attr_writer :name

  def initialize(name)
    @name = name
  end
end

generates the following code

def name
  @name
end

# This is an awesome setter,
# a function with name:
# 						obj.name=
def name=(value)
  @name = value
end

And can be used like

me = Person.new("CY")
me.name = "Cheng-Yu"
puts me.name

Output

Cheng-Yu

Read-write instance variables

class Person

  # read/write
  attr_accessor :name, :whatever

  def initialize(name)
    @name = name
  end
  
end

Module

Module can be understood as a library serves for certain purposes. (My contains methods, constants)

module Circle
	M_PI = 3.14
	
	def self.area(r)
		r * r * M_PI
	end
end

Namespace

Double columns serves for namespace, which is like fully qualified name in Java.

It's used to resolve conflict method names or variable names. Put the module/class name before double columns to specify the namespace.

puts Circle::M_PI
puts Math::PI

require

To import some module, use require (just like the way we do in Node.js).

require 'date'

puts Date.today

Psuedo Multiple Inheritances

You may use mixin to achieve psuedo multiple inheritances.

module MartialArts
    def swordsman
        puts "I'm a swordsman."
    end
end

# Both Ninja and Samurai are "sort of" SwordsMan now!
# Like an attribute, not actually inherit from SwordsMan
class Ninja
  include MartialArts
  def initialize(clan)
    @clan = clan
  end
end

class Samurai
  include MartialArts
  def initialize(shogun)
    @shogun = shogun
  end
end

Binary / Hex Conversion

Integer to binary / hex.

5.to_s(2) 	# 101
11.to_s(16)	# b

String to binary / hex.

"5".ord 				# 53
"5".unpack("B*")		# ["00110101"]
"5".unpack("H*")		# ["35"]

More: http://ruby-doc.org/core-2.2.1/String.html#method-i-unpack

Invoke Methods using send

def print_args(*args)
	print args
end
  1. Invoke with argument [1, 2, 3] (an array):

    send(:print_args, [1, 2, 3])
    # output: [[1, 2, 3]]
  2. Invoke with 3 arguments:

    send(:print_args, *[1, 2, 3])
    # output: [1, 2, 3]

Note: * can used for converting splat arguments to array and vice versa (just like & for proc).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment