Ruby lean Note

Hello World

puts "Hello World!"

Comments

# single line comment
=begin
Multi-lines
comment
=end

Variables

my_var = 42
My_constant_var = 31337

Shorthand assignment

a += b # a = a + b
a -= b # a = a - b
a *= b # a = a * b
a /= b # a = a / b
a %= b # a = a % b
a **= b # a = a**b

Parallel assignment

a, b, c = 11, 22, 33

Input

input = gets

Collections

#Array
# Basics
peoples = ["Alice", "Bob", "Eve"]
=> ["Alice", "Bob", "Eve"]
peoples[0]
=> "Alice"
peoples[1]
=> "Bob"
peoples[-1]
=> "Eve"
# Adding elements
foo = [42, "Cindy", 0.02, true]
=> [42, "Cindy", 0.02, true]
foo << 'd'
=> [42, "Cindy", 0.02, true, "d"]
foo.push(1337)
=> [42, "Cindy", 0.02, true, "d", 1337]
foo.insert(2, 0.05)
=> [42, "Cindy", 0.05, 0.02, true, "d", 1337]
# Removing elements
foo.pop
=> 1337
foo
=> [42, "Cindy", 0.05, 0.02, true, "d"]
foo.delete_at(2)
=> 0.05
foo
=> [42, "Cindy", 0.02, true, "d"]
# Display
puts foo
42
Cindy
0.02
true
d
=> nil
print foo
[42, "Cindy", 0.02, true, "d"]=> nil
# Array Ranges
alph = ['a', 'c', 'z', 'e']
=> ["a", "c", "z", "e"]
alph[1..3]
=> ["c", "z", "e"]
# Combining arrays
x = [41, 53]
=> [41, 53]
y = [72, 16, 133]
=> [72, 16, 133]
res = x + y
=> [41, 53, 72, 16, 133]
res = y + x
=> [72, 16, 133, 41, 53]
z = [16, 41, 55]
=> [16, 41, 55]
res - z
=> [72, 133, 53]
# Boolean operations
x
=> [41, 53]
z
=> [16, 41, 55]
x & z
=> [41]
x | z
=> [41, 53, 16, 55]
# Moving elements
z.reverse
=> [55, 41, 16]
z
=> [16, 41, 55]
z.reverse!
=> [55, 41, 16]
z
=> [55, 41, 16]
# Array methods
z.length
=> 3
z.size
=> 3
z.sort
=> [16, 41, 55]
c = [42, 42, 75, 42]
=> [42, 42, 75, 42]
c.uniq
=> [42, 75]
z.freeze # prevent from being modified, there is no unfreeze method
=> [55, 41, 16]
z.pop
RuntimeError: can't modify frozen Array
from (irb):96:in 'pop'
from (irb):96
from /usr/bin/irb:11:in `<main>'
z.frozen?
=> true
z.include?(55)
=> true
z.include?(56)
=> false
z.min
=> 16
z.max
=> 55

Hashes and symbols

# String as key
grades = {"Paul" => 17, "Virginia" => 18, "Seb" => 15}
=> {"Paul"=>17, "Virginia"=>18, "Seb"=>15}
grades["Seb"]
=> 15
# Symbols as key
identity = {:name => "Alex", :age => 22, :sex => "male"}
=> {:name=>"Alex", :age=>22, :sex=>"male"}
identity[:name]
=> "Alex"
# Symbols as key (shorter syntax)
rock = {name: "ruby", color: "red"}
=> {:name=>"ruby", :color=>"red"}
rock[:name]
=> "ruby"
# Some methods
identity.delete(:age)
=> 22
grades.key(18)
=> "Virginia"
grades.invert
=> {17=>"Paul", 18=>"Virginia", 15=>"Seb"}
no = {a: 42, b: 42, c: 42}
=> {:a=>42, :b=>42, :c=>42}
no.invert
=> {42=>:c} # Don't forget the key needs to be unique
rock.keys
=> [:name, :color]
rock.values
=> ["ruby", "red"]
rock.length
=> 2

Nested arrays and hashes

# Nested arrays
nest = [[10,11,12], ['a','b','c']]
=> [[10, 11, 12], ["a", "b", "c"]]
nest[1]
=> ["a", "b", "c"]
nest[0][2]
=> 12
# Nested hashes
albums = {:"2010" => {:artist => "The Glitch Mob", :title => "Drink The Sea"}, :"2011" => {:artist => "Cryptex", :title => "Isolated Incidents"}}
=> {:"2010"=>{:artist=>"The Glitch Mob", :title=>"Drink The Sea"}, :"2011"=>{:artist=>"Cryptex", :title=>"Isolated Incidents"}}
albums[:"2011"][:artist]
=> "Cryptex"

Iterators

# Range iterator
(1..26).each do |i|
puts i
end
# Array iterator
letters = ('a'..'z').to_a
alphabet = String.new
letters.each do |c|
alphabet += c
end
# Hash iterator
num = {one: 1, two: 2, three: 3}
num.each do |k,v|
puts "#{k}: #{v}"
end
num.each {|k,v| puts "#{k}: #{v}"}
num.each do |k,v| puts "#{k}: #{v}" end
# times iterator
7.times do
print "7"
end
# Iterator with index
arr = ["red", "blue", "green"]
arr.each_with_index do |color, index| puts "#{index}: #{color}" end

Equality

42 == 42.0 # equal values
=> true
42.eql?(42.0) # equal values and same type
=> false

Subsumption(包容)

42 === (10..100)
=> false
42 === (50..100)
=> false
(10..100) === 42(因为10..100之间存在42因此true)
=> true
(50..100) === 42
=> false
Integer === 42
=> true
Integer === 'string'
=> false
42 === Integer
=> false
/at/ === 'Hi mate'
=> true
/at/ === 'Hello'
=> false
'Hi mate' === /at/
=> false

if/elsif/else statement

val = 1337
if val == 42
puts "That's always true"
elsif val == 1000
puts "Nearly right"
elsif val == 1300
puts "Even closer"
else
puts "Nah!!"
end
# ouput: Nah!

unless statement(unless executes a block of code only if the condition is false.)

val = 1337
unless val == 1337
puts "Your're the elite!"
else
puts "Master!"
end
# output: Master!

Logical operators

&& => and
|| => or
!  => not

case statement

a = 10
case a
when 1
puts 'One'
when 2
puts 'Two'
when 5
puts 'Five'
when 10
puts 'Ten'
when 42, 1337, 31337
puts 'Master!'
else
puts 'Nah!'
end
# output: Ten

while loop

a = 0
while a <= 5
puts a
a += 1
end

until loop

a = 0
until a > 5
puts a
a += 1
end

Ranges

(41..43).to_a
=> [41, 42, 43]
(41...43).to_a
=> [41, 42]
('m'..'o').to_a
=> ["m", "n", "o"]
('y'..'b').to_a
=> []

for loop

for i in (1..10)
puts "i: #{i}"
end

break

for i in (1..10)
break if i > 5
puts "i: #{i}"
end

next if

for i in (1..10)
next if i % 2 == 0 #(if is false execute)
puts "i: #{i}"
end

next unless

numbers = [1, 2, 3, 4, 5]
numbers.each do |num|
next unless num.odd?
puts num
end

loop do

a = 0
loop do
puts a
a += 1
break if a > 10
end

Files

Creating and opening files

# Create a file
file = File.new("newfile.txt", "w+")
file.close
# File modes
# **r**, read-only, starts at the beginning
# **r+**, read-write, starts at the beginning
# **w**, write-only, override existing files or creates a new file
# **w+**, read-write, override existing files or creates a new file
# **a**, write-only, appends to the end of existing file or creates a new file
# **a+**, read-write, appends or reads to the end of existing file or creates a new file
# Open an existing file
file = File.open('filename.txt', 'w+')
file.close

Reading and writing files

# Write into a file
file = File.new("newfile.txt", "w+")
file.puts("some text") # add line break \n
file.write("some other") # no line break
file.close
# File block
File.open("test.txt", "w+") do |file|
file.puts("test")
end # auto-close
# Read the entire content of a file
puts File.read("file.txt")
# Read a file line by line
File.readlines("file.txt").each do |line|
puts "> #{line}"
end

Deleting files

# Delete a file permanently
File.delete("file.txt")
# Check file existence
File.open("test.txt") if File.file?("test.txt")

File information

# Size of a file
f1 = File.new("test1.txt", "w")
f1.puts("this is a test")
f1.size # return 15, need open stream
f1.close
# Check if empty
File.zero?("test1.txt") # return false
# Check permissions
File.readable?("test1.txt") # return: true
File.writable?("test1.txt") # return: true
File.executable?("test1.txt") # return: false

Methods

Defining and calling

def hi
puts "Hi!!"
end
hi
=> Hi!!

Method parameters

def hi(name)
puts "Hi #{name}"
end
hi("Tim")
=> Hi Tim

Default and optional parameters

# Default parameters
def hi(name="")
puts "Hi #{name}"
end
hi
=> Hi
hi("Roger")
=> Hi Roger
# Without parentheses
def sum x,y
puts x+y
end
sum 3,9
=> 12
# Optional parameters
def method(*arg)
puts arg
end
method(1)
=> 1
method(1,"toto")
=> 1
=> toto

Return values from methods

def min(arr)
return arr.min
end
min([1,5,2,6])
=> 1
# Default return
def append(text)
text = "#{text}!!!"
end
append("magic")
=> "magic!!!"
# Return array
def sqr(x,y,z)
return x**2,y**2,z**2
end
sqr(2,7,9)
=> [4, 49, 81]
# Chaining methods
[8,5,9,4.5,7].min.**(2).ceil
=> 21
# Methods as argument
def reverse(text)
text.reverse
end
def print_reverse(text)
"Reverse: #{text}"
end
print_reverse(reverse("some text"))
=> "Reverse: txet emos"

Variable scope

# Local variables
def add(x,y)
x + y
end
puts y
# output: undefined local variable or method `y' for main:Object (NameError)
# Global variables
$y = 12
def add(x)
x + $y
end
puts add(7)
# output: 19

Recursion(递归)

def fact(n)
if n <= 1
1
else
n * fact(n - 1)
end
end
irb(main):008:0> fact(5)
=> 120
# infinite recursion
def fact(n)
n * fact(n - 1)
end
irb(main):012:0> fact(5)
SystemStackError: stack level too deep

Modules, mixins and standard classes

Modules and mixins

# Include vs extend
# an example from  John Nunemaker @ http://www.railstips.org/blog/archives/2009/05/15/include-vs-extend-in-ruby/
module Foo
def foo
puts 'heyyyyoooo!'
end
end
class Bar
include Foo
end
Bar.new.foo # heyyyyoooo!
Bar.foo # NoMethodError: undefined method ‘foo’ for Bar:Class
class Baz
extend Foo
end
Baz.foo # heyyyyoooo!
Baz.new.foo # NoMethodError: undefined method ‘foo’ for #<Baz:0x1e708>
# Module mixins example
module PiMath
def pisqr
Math::PI**2
end
end
class CosWrapper
def self.cos
# accessible only for classes which define number method
# number alone is ambiguous because it can refer to self.number or self.class.number
Math::cos(self.class.number)
end
end
class PiPowa < CosWrapper
extend PiMath
def self.number
self.pisqr
end
end
class EPowa < CosWrapper
def self.esqr
Math::E**2
end
def self.number
self.esqr
end
end
class Somenumber
attr_reader :mynumber
def initialize(x)
@mynumber = self.pisqr * x
end
protected
include PiMath
end
puts PiPowa.pisqr # output: 9.869604401089358
puts PiPowa.number # output: 9.869604401089358
puts PiPowa.cos # output: -0.9026853619330714
puts EPowa.esqr # output: 7.3890560989306495
puts EPowa.number # output: 7.3890560989306495
puts EPowa.cos # output: 0.44835624181873357
s = Somenumber.new(7)
puts s.mynumber # output: 69.0872308076255
puts CosWrapper.cos # output: NameError: undefined local variable or method `number' for CosWrapper:Class
# Auto extend when included
# an example from  John Nunemaker @ http://www.railstips.org/blog/archives/2009/05/15/include-vs-extend-in-ruby/
module Foo
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def bar
puts 'class method'
end
end
def foo
puts 'instance method'
end
end
class Baz
include Foo
end
Baz.bar # class method
Baz.new.foo # instance method
Baz.foo # NoMethodError: undefined method ‘foo’ for Baz:Class
Baz.new.bar # NoMethodError: undefined method ‘bar’ for #<Baz:0x1e3d4>
# Example of a mixin interacting with a class
class Character
attr_accessor :name
include Comparable
def initialize(name)
@name = name
end
def <=>(other)
self.name <=> other.name
end
end
c1 = Character.new("Toto")
c2 = Character.new("Tata")
puts c1 > c2 # ouput: true

Namespacing

# Group classes
module Pets
class Dog
def bark
puts "Woof!"
end
end
class Cat
def meow
puts "Meow!"
end
end
end
d = Pets::Dog.new
c = Pets::Cat.new
d.bark
c.meow
# Group methods
module MoreMath
def self.recadd(n)
(n * (n + 1)) / 2
end
def self.factorial(n)
(1..n).reduce(:*) || 1
end
end
MoreMath.recadd(7) # output: 28

Structs

# Simple struct
Point = Struct.new(:x,:y)
a = Point.new(1,5)
b = Point.new(7,-2)
puts a.y # output: 5
a.x = 2
#OpenStruct (OStruct) is similar to Struct but doesn't require to define attributes.
#The drawback is that OpenStruct is slower than Struct.
require 'ostruct'
employee = OpenStruct.new
employee.name = "Antony"
employee.job = "Engineer"
employee.salary = 2100
puts employee.job # output: Engineer
# Initialize an OpenStruct with a hash
require 'ostruct'
employee = OpenStruct.new(name: "Antony", job: "Engineer", salary: 2100)
puts employee.job # output: Engineer

Math and Time

# square root
puts Math.sqrt(16)
# pi constant
puts Math::PI
# trigonometry
puts Math::tan(0)
# current time
t = Time.now
puts t
# year, month, day
puts t.year
puts t.month
puts t.day
# custom date
t = Time.new(1994,04,22)
# day of the week (0-6)
puts t.wday
# day of the year
puts t.yday

Procs

Procs are object so they can be passed into methods.

# Simple proc
hello = Proc.new do |someone|
puts "Hi #{someone}"
end
hello.call "Tristan"
hello.call("David")
# Procs passed into methods
goodbye = Proc.new do |someone|
puts "Bye #{someone}"
end
def say(arr, proc)
arr.each {|person| proc.call person}
end
people = ["Tristan", "David", "Ophelia"]
say(people, hello)
say(people,goodbye)
# Proc example
def calc_time(proc, n)
start = Time.now
proc.call n
duration = Time.now - start
end
myProc = Proc.new do |n|
(1..n).reduce(:*) || 1
end
puts calc_time(myProc, 99999) # output: 3.382078767

Lambdas

# Lambda / anonymous function
l = lambda {puts "stuff"}
l2 = ->() {puts "stuff2"}
l.call
l2.call
# Lambda vs proc
# with proc, arguments are not mandatory(强制性的)
my_lambda = lambda {|arg| puts "Hi #{arg}"}
my_proc = Proc.new {|arg| puts "Hi #{arg}"}
my_lambda.call # output: ArgumentError: wrong number of arguments (given 0, expected 1)
my_proc.call # output: Hi
# Lambda vs proc
# When return is encountered, proc quite the enclosing method, lambda doesn't
def say(arr)
p = goodbye = Proc.new do |someone|
puts "Bye #{someone}"
return "proc return"
end
p.call arr.join(", ")
puts "not displayed"
end
people = ["Tristan", "David", "Ophelia"]
say(people)
# output: Bye Tristan, David, Ophelia
# => "proc return"
def say2(arr)
l = lambda do |someone|
puts "Bye #{someone}"
return "lambda return"
end
l_ret = l.call arr.join(", ")
puts "this is displayed: #{l_ret}"
end
say2(people)
# output: Bye Tristan, David, Ophelia
# output: this is displayed: lambda return
# => nil

Object oriented programming

Classes and objects

# Initialization
class Person
def initialize
puts "My name ise no one!"
end
end
irb(main):020:0> Person.new
My name ise no one!

Instance variables

# Instance variables
class Color
@color = "no color"
def initialize(color)
@color = color
end
end
y = Color.new("yellow")
g = Color.new("green")
``` Instance methods and accessors
###
```ruby
# Accessors
class Person
def initialize(name)
@name = name
end
# Getter
def get_name
@name
end
# Setter
def set_name(name)
@name = name
end
end
irb(main):039:0> p = Person.new("Nicolas")
=> #<Person:0x000056328b740568 @name="Nicolas">
irb(main):040:0> p.get_name
=> "Nicolas"
irb(main):055:0> p.set_name("Phil")
=> "Phil"
irb(main):056:0> p.get_name
=> "Phil"
# Setter helper
class Person
def set_name=(name)
@name = name
end
end
irb(main):063:0> p = Person.new("Alexia")
=> #<Person:0x000056328bbd4cc8 @name="Alexia">
irb(main):064:0> p.set_name = "Alexandra"
=> "Alexandra"

Accessor methods

class Person
attr_reader :name   # getter
attr_writer :age    # setter (with =)
attr_accessor :surname, :nick   # setter (with =) + getter
def initialize(name, age, surname)
@name = name
@age = age
@surname = @nick = surname
end
end
irb(main):075:0> p = Person.new("André", 22, "Dédé")
=> #<Person:0x000056328bba43e8 @name="André", @age=22, @nick="Dédé", @surname="Dédé">
irb(main):076:0> p.name
=> "André"
irb(main):078:0> p.age = 23
=> 23
irb(main):080:0> p.surname = "Dré"
=> "Dré"
irb(main):081:0> p.nick
=> "Dédé"
# Self keyword
class Person
attr_reader :age
attr_accessor :birth
def initialize(birth)
@birth = birth
@age = 2017 - birth.to_i
end
=begin attr_accessor :birth
def birth
@birth
end
def birth=(birth)
@birth = birth
end
=end
def modify=(birth)
# birth = birth? local argument = local argument or birth sugar setter with birth local argument?
self.birth = birth # self refer to the public instance method
@age = 2017 - birth.to_i
end
end
irb(main):032:0> p = Person.new(1985)
=> #<Person:0x000055ab32ed0d90 @birth=1985, @age=32>
irb(main):033:0> p.age
=> 32
irb(main):034:0> p.modify = 1990
=> 1990
irb(main):035:0> p.age
=> 27
# self ambiguity
class WhereDoesItComeFrom
def self.from
puts "class method"
end
def from
puts "instance method"
end
def which
from # instand method so it will call the *from* instance method
end
def self.which
from # class method so it will call the *from* class method
end
def unambiguous
self.from # call the instance method
self.class.from # call the class method
end
def self.unambiguous
# self always refer to an instance
# but `WhereDoesItComeFrom` is an instance of the class `Class`
self.from # output: class method
self.class.from # raise an error because `Class.from` doesn't exist
end
end
WhereDoesItComeFrom.from # output: class method
WhereDoesItComeFrom.new.from # output: instance method
WhereDoesItComeFrom.which # output: class method
WhereDoesItComeFrom.new.which # output: instance method
WhereDoesItComeFrom.unambiguous # output: 1st line: class method, 2nd line: NoMethodError: undefined method `from' for Class:Class
WhereDoesItComeFrom.new.unambiguous # output: 1st line: instance method, 2nd line: class method

Class methods and variables

# Class methods
class Car
def self.info
puts "A car"
end
end
# When there is no needs to instantiate an object.
irb(main):041:0> Car.info
A car
=> nil
# Class variables
class Animals
@@number = 0
def initialize
@@number += 1
end
def self.number
@@number
end
end
irb(main):066:0> dog = Animals.new
=> #<Animals:0x000055ab32f47260>
irb(main):067:0> cat = Animals.new
=> #<Animals:0x000055ab32f3a9e8>
irb(main):068:0> snake = Animals.new
=> #<Animals:0x000055ab32c0b358>
irb(main):069:0> Animals.number
=> 3
# Class constants
class Universe
ANSWER = 42
end
irb(main):073:0> Universe::ANSWER
=> 42

to_s method

def Aclass
end
puts Aclass # to_s is automatically called
puts Aclass.to_s

Inheritance

class Language # superclass / base class
def initialize(style)
@style = style
end
def example
puts 'printf ("%s", "Hello world!")'
end
end
class Ruby < Language # subclass / derived class
def example
puts 'puts "Hello world!"'
end
end
r = Ruby.new("OOB")
r.example
# output : puts "Hello world!"

super method

class Human
def speak
print "I am a human"
end
end
class Avatar < Human
def speak
super
puts " and I'm blue"
end
end
a = Avatar.new
a.speak
# output : I am a human and I'm blue
class Vehicle
def initialize(name)
@name = name
end
end
class Car < Vehicle
def initialize(name,wheels)
super(name)
@wheels = wheels
end
def to_s
"My #{@name} has #{@wheels} wheels."
end
end
c = Car.new("Twingo", 4)
puts c
# output : My Twingo has 4.

Operator overloading

class Coordinates
attr_accessor :x, :y
def initialize(x, y)
self.x = x
self.y = y
end
def +(obj)
Coordinates.new(self.x + obj.x, self.y + obj.y)
end
end
a = Coordinates.new(8,5)
b = Coordinates.new(-1,4)
c = a + b
puts c.x # output: 7
puts c.y # output: 9

Access modifiers

# private methods
class My_time
def initialize(hours)
@hours = hours
end
def convert
# can call private minutes method because we are in the class
# but not self.minutes because minutes is not public
puts "#{@hours} hours(s) = #{minutes} minutes = #{seconds} seconds"
end
private
def minutes
@hours * 60
end
def seconds
@hours * 3600
end
end
t = My_time.new(24)
t.convert # output: 24 hours(s) = 1440 minutes = 86400 seconds
puts t.minutes # output: NoMethodError: private method `minutes' called for #<My_time:0x000055b311f9df20 @hours=24>
# protected methods
class Thing
def initialize
@id = rand(9999)
end
def ==(obj)
# can call protected (private-like) method from the code but are not public
self.id == obj.id
end
protected
def id
@id
end
end
t1 = Thing.new
t2 = Thing.new
puts (t1 == t2)
# output: false

Abstract classes and methods

Ruby doesn’t have a built-in notion of an abstract method or class. Note: it’s advised not to use abstract class without proper error management. An abstract class is a class that should never be instantiated directly.

# Abstract class and method
class A
# abstract class
def initialize
raise NotImplementedError
end
# abstract method
def something
# accessible only for classes which define number method
puts "number: #{number}"
end
end
class B < A
def initialize
# just need to override parent class
end
def number
4
end
end
b = B.new
puts b.number # output: 4
puts b.something # output: number: 4
# A.new will raise an error
# if `A` didn't have `raise` in `initialize` method we should have been able to call the
# `something` method but it will have raise an error as the `number` method doesn't exist
# for the `A` class.

use httpx

require "httpx"
File.readlines("wordlist.txt").each do |user|
File.readlines("wordlist1.txt").each do |pass|
xml_data = "<user><username>#{user.strip}</username><password>#{pass.strip}</password></user>"
headers = { "Content-Type" => "text/xml" }
response = HTTPX.post("http://eci-2zeatljaj2gspazb58z0.cloudeci1.ichunqiu.com/doLogin.php", body: xml_data, headers: headers)
puts "#{user.strip},#{pass.strip}"
break if response.to_s.include?("<code>3</code>")
end
end