„Rubinul are un aspect simplu, dar este foarte complex în interior, la fel ca și corpul nostru uman”. – Matz, creatorul limbajului de programare Ruby

De ce să înveți Ruby?

Pentru mine, primul motiv este că este un limbaj frumos. Este firesc să codez și îmi exprimă întotdeauna gândurile.

Al doilea motiv – și principal – este Șine: același cadru pe care îl folosesc Twitter, Basecamp, Airbnb, Github și atât de multe companii.

Introducere / Istorie

Ruby este „Un limbaj de programare dinamic, open source, cu accent pe simplitate și productivitate. Are o sintaxă elegantă, naturală de citit și ușor de scris. ” – ruby-lang.org

Să începem cu câteva elemente de bază!

Variabile

Vă puteți gândi la o variabilă ca la un cuvânt care stochează o valoare. Simplu.

În Ruby este ușor să definiți o variabilă și să o setați. Imaginați-vă că doriți să stocați numărul 1 într-o variabilă numită una. S-o facem!

one = 1

Cât de simplu a fost asta? Tocmai ați atribuit valoarea 1 unei variabile numite una.

two = 2
some_number = 10000

Puteți atribui o valoare oricărei variabile dorite. În exemplul de mai sus, a Două variabila stochează un număr întreg de 2 și some_number magazine 10.000.

În afară de numere întregi, putem folosi și booleeni (adevărat / fals), șiruri, simboluri, flotant și alte tipuri de date.

# booleans
true_boolean = true
false_boolean = false

# string
my_name = "Leandro Tk"

# symbol
a_symbol = :my_symbol

# float
book_price = 15.80

Enunțuri condiționale: Flux de control

Afirmațiile condiționale evaluează adevăratul sau falsul. Dacă ceva este adevărat, acesta execută ceea ce se află în declarație. De exemplu:

if true
  puts "Hello Ruby If"
end

if 2 > 1
  puts "2 is greater than 1"
end

2 este mai mare decât 1, deci pune codul este executat.

Această instrucțiune else va fi executată atunci când expresia if este falsă:

if 1 > 2
  puts "1 is greater than 2"
else
  puts "1 is not greater than 2"
end

1 nu este mai mare decât 2, deci codul din instrucțiunea else va fi executat.

Există, de asemenea, declarația elsif. Îl puteți folosi astfel:

if 1 > 2
  puts "1 is greater than 2"
elsif 2 > 1
  puts "1 is not greater than 2"
else
  puts "1 is equal to 2"
end

Un mod în care îmi place foarte mult să scriu Ruby este să folosesc o instrucțiune if după codul de executat:

def hey_ho?
  true
end

puts "let’s go" if hey_ho?

Este atât de frumos și natural. Este Ruby idiomatic.

Buclă / Iterator

În Ruby putem itera în atât de multe forme diferite. Voi vorbi despre trei iteratori: în timp ce, pentru și fiecare.

În buclă: atâta timp cât instrucțiunea este adevărată, codul din interiorul blocului va fi executat. Deci, acest cod va imprima numărul de la 1 la 10:

num = 1

while num <= 10
  puts num
  num += 1
end

Pentru buclă: treceți variabila num către bloc și instrucțiunea for o va itera pentru dvs. Acest cod va imprima la fel ca codul while: de la 1 la 10:

for num in 1...10
  puts num
end

Fiecare iterator: îmi place foarte mult fiecare iterator. Pentru o serie de valori, va itera una câte una, trecând variabila la bloc:

[1, 2, 3, 4, 5].each do |num|
  puts num
end

S-ar putea să vă întrebați care este diferența dintre fiecare iterator și pentru buclă. Principala diferență este că fiecare iterator menține variabila numai în interiorul blocului de iterație, în timp ce pentru buclă permite variabilei să trăiască în afara blocului.

# for vs each

# for looping
for num in 1...5
  puts num
end

puts num # => 5

# each iterator
[1, 2, 3, 4, 5].each do |num|
  puts num
end

puts num # => undefined local variable or method `n' for main:Object (NameError)

Matrice: Colectare / Listă / Structură de date

Imaginați-vă că doriți să stocați numărul întreg 1 într-o variabilă. Dar poate că acum vrei să stochezi 2. Și 3, 4, 5 …

Am o modalitate de a stoca toate numerele întregi dorite, dar nu în milioane de variabile? Ruby are un răspuns!

Array este o colecție care poate fi utilizată pentru a stoca o listă de valori (cum ar fi aceste numere întregi). Deci, să-l folosim!

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

Este foarte simplu. Am creat o matrice și am stocat-o în intregul meu.

S-ar putea să vă întrebați: „Cum pot obține o valoare din această matrice?” Marea întrebare. Tablourile au un concept numit index. Primul element obține indicele 0 (zero). Al doilea primește 1 și așa mai departe. Ai ideea!

Learning Ruby De la zero la erou

Folosind sintaxa Ruby, este ușor de înțeles:

my_integers = [5, 7, 1, 3, 4]
print my_integers[0] # 5
print my_integers[1] # 7
print my_integers[4] # 4

Imaginați-vă că doriți să stocați șiruri în loc de numere întregi, cum ar fi o listă cu numele rudelor dvs. Al meu ar fi ceva de genul acesta:

relatives_names = [
  "Toshiaki",
  "Juliana",
  "Yuji",
  "Bruno",
  "Kaio"
]

print relatives_names[4] # Kaio

Funcționează la fel ca numerele întregi. Grozav!

Tocmai am aflat cum funcționează indicii matrice. Acum să adăugăm elemente la structura datelor matrice (elemente în listă).

Cele mai comune metode pentru a adăuga o nouă valoare unui tablou sunt push și <<.

Push este super simplu! Trebuie doar să treceți elementul (The Effective Engineer) ca parametru push:

bookshelf = []
bookshelf.push("The Effective Engineer")
bookshelf.push("The 4 hours work week")
print bookshelf[0] # The Effective Engineer
print bookshelf[1] # The 4 hours work week

Metoda << este ușor diferită:

bookshelf = []
bookshelf << "Lean Startup"
bookshelf << "Zero to One"
print bookshelf[0] # Lean Startup
print bookshelf[1] # Zero to One

Ați putea întreba „Dar nu folosește notația punct ca la alte metode. Cum ar putea fi o metodă? ” Frumoasă întrebare! Scriind acest lucru:

bookshelf << "Hooked"

… este similar cu scrierea asta:

bookshelf.<<("Hooked")

Ruby este atât de grozav, nu?

Ei bine, destule matrice. Să vorbim despre o altă structură de date.

Hash: Structură de date cheie-valoare / Colecție de dicționare

Știm că matricile sunt indexate cu numere. Dar dacă nu vrem să folosim numerele ca indici? Unele structuri de date pot utiliza numerice, șiruri sau alte tipuri de indici. Structura de date hash este una dintre ele.

Hash este o colecție de perechi cheie-valoare. Arată așa:

hash_example = {
  "key1" => "value1",
  "key2" => "value2",
  "key3" => "value3"
}

Cheia este indicele care indică valoarea. Cum accesăm valoarea hash? Folosind cheia!

Iată un hash despre mine. Numele meu, porecla și naționalitatea sunt cheile hashului.

hash_tk = {
  "name" => "Leandro",
  "nickname" => "Tk",
  "nationality" => "Brazilian"
}

print "My name is #{hash_tk["name"]}" # My name is Leandro
print "But you can call me #{hash_tk["nickname"]}" # But you can call me Tk
print "And by the way I'm #{hash_tk["nationality"]}" # And by the way I'm Brazilian

În exemplul de mai sus am imprimat o frază despre mine folosind toate valorile stocate în hash.

Un alt lucru interesant despre hash este că putem folosi orice drept valoare. Voi adăuga cheia „vârstă” și vârsta mea întreagă reală (24).

hash_tk = {
  "name" => "Leandro",
  "nickname" => "Tk",
  "nationality" => "Brazilian",
  "age" => 24
}

print "My name is #{hash_tk["name"]}" # My name is Leandro
print "But you can call me #{hash_tk["nickname"]}" # But you can call me Tk
print "And by the way I'm #{hash_tk["age"]} and #{hash_tk["nationality"]}" # And by the way I'm 24 and Brazilian

Să învățăm cum să adăugăm elemente la un hash. Cheia care indică o valoare este o mare parte din ceea ce este hash – și același lucru este valabil atunci când dorim să adăugăm elemente la ea.

hash_tk = {
  "name" => "Leandro",
  "nickname" => "Tk",
  "nationality" => "Brazilian"  
}

hash_tk["age"] = 24
print hash_tk # { "name" => "Leandro", "nickname" => "Tk", "nationality" => "Brazilian", "age" => 24 }

Trebuie doar să atribuim o valoare unei chei hash. Nimic complicat aici, nu?

Iterare: Buclare prin structuri de date

Iterația matricei este foarte simplă. Dezvoltatorii Ruby folosesc de obicei fiecare iterator. S-o facem:

bookshelf = [
  "The Effective Engineer",
  "The 4 hours work week",
  "Zero to One",
  "Lean Startup",
  "Hooked"
]

bookshelf.each do |book|
  puts book
end

Fiecare iterator funcționează prin trecerea elementelor matricei ca parametri în bloc. În exemplul de mai sus, imprimăm fiecare element.

Pentru structura de date hash, putem utiliza, de asemenea, fiecare iterator, trecând doi parametri blocului: cheia și valoarea. Iată un exemplu:

hash = { "some_key" => "some_value" }
hash.each { |key, value| puts "#{key}: #{value}" } # some_key: some_value

Am numit cei doi parametri drept cheie și valoare, dar nu este necesar. Le putem numi orice:

hash_tk = {
  "name" => "Leandro",
  "nickname" => "Tk",
  "nationality" => "Brazilian",
  "age" => 24
}

hash_tk.each do |attribute, value|
  puts "#{attribute}: #{value}"
end

Puteți vedea că am folosit atributul ca parametru pentru cheia hash și funcționează corect. Grozav!

Clase și obiecte

Ca limbaj de programare orientat pe obiecte, Ruby folosește conceptele de clasă și obiect.

„Clasa” este o modalitate de a defini obiecte. În lumea reală există multe obiecte de același tip. La fel ca vehiculele, câinii, bicicletele. Fiecare vehicul are componente similare (roți, uși, motor).

„Obiectele” au două caracteristici principale: date și comportament. Vehiculele au date precum numărul de roți și numărul de uși. De asemenea, au un comportament cum ar fi accelerarea și oprirea.

În programarea orientată obiect, numim date „atribute” și comportament „metode”.

Date = Atribute

Comportament = Metode

Mod de programare orientat pe obiect rubin: Activat

Să înțelegem sintaxa Ruby pentru clase:

class Vehicle
end

Definim vehiculul cu declarația clasei și terminăm cu sfârșitul. Uşor!

Iar obiectele sunt instanțe ale unei clase. Creăm o instanță apelând metoda .new.

vehicle = Vehicle.new

Aici vehicul este un obiect (sau o instanță) din clasa Vehicle.

Clasa noastră de vehicule va avea 4 atribute: roți, tipul rezervorului, capacitatea de ședere și viteza maximă.

Să definim clasa noastră Vehicul pentru a primi date și a le instanția.

class Vehicle
  def initialize(number_of_wheels, type_of_tank, seating_capacity, maximum_velocity)
    @number_of_wheels = number_of_wheels
    @type_of_tank = type_of_tank
    @seating_capacity = seating_capacity
    @maximum_velocity = maximum_velocity
  end
end

Folosim metoda de inițializare. O numim o metodă constructor, astfel încât atunci când creăm obiectul vehiculului, putem defini atributele acestuia.

Imaginați-vă că vă place Tesla Model S și doriți să creați acest tip de obiect. Are 4 roti. Tipul său de rezervor este energie electrică. Are spațiu pentru 5 locuri și o viteză maximă este de 250 km / oră (155 mph). Să creăm obiectul tesla_model_s! 🙂

tesla_model_s = Vehicle.new(4, 'electric', 5, 250)

4 roți + rezervor electric + 5 locuri + 250km / oră viteză maximă = tesla_model_s.

tesla_model_s
# => <Vehicle:0x0055d516903a08 @number_of_wheels=4, @type_of_tank="electric", @seating_capacity=5, @maximum_velocity=250>

Am setat atributele Tesla. Dar cum le accesăm?

Trimitem un mesaj către obiect întrebând despre ele. O numim o metodă. Este comportamentul obiectului. Să-l implementăm!

class Vehicle
  def initialize(number_of_wheels, type_of_tank, seating_capacity, maximum_velocity)
    @number_of_wheels = number_of_wheels
    @type_of_tank = type_of_tank
    @seating_capacity = seating_capacity
    @maximum_velocity = maximum_velocity
  end

  def number_of_wheels
    @number_of_wheels
  end

  def set_number_of_wheels=(number)
    @number_of_wheels = number
  end
end

Aceasta este o implementare a două metode: number_of_wheels și set_number_of_wheels. Îi spunem „getter” și „setter”. Mai întâi obținem valoarea atributului și, în al doilea rând, stabilim o valoare pentru atribut.

În Ruby, putem face asta fără metode care folosesc attr_reader, attr_writer și attr_accessor. Să o vedem cu cod!

  • attr_reader: implementează metoda getter
class Vehicle
  attr_reader :number_of_wheels

  def initialize(number_of_wheels, type_of_tank, seating_capacity, maximum_velocity)
    @number_of_wheels = number_of_wheels
    @type_of_tank = type_of_tank
    @seating_capacity = seating_capacity
    @maximum_velocity = maximum_velocity
  end
end

tesla_model_s = Vehicle.new(4, 'electric', 5, 250)
tesla_model_s.number_of_wheels # => 4
  • attr_writer: implementează metoda setter
class Vehicle
  attr_writer :number_of_wheels

  def initialize(number_of_wheels, type_of_tank, seating_capacity, maximum_velocity)
    @number_of_wheels = number_of_wheels
    @type_of_tank = type_of_tank
    @seating_capacity = seating_capacity
    @maximum_velocity = maximum_velocity
  end
end

# number_of_wheels equals 4
tesla_model_s = Vehicle.new(4, 'electric', 5, 250)
tesla_model_s # => <Vehicle:0x0055644f55b820 @number_of_wheels=4, @type_of_tank="electric", @seating_capacity=5, @maximum_velocity=250>

# number_of_wheels equals 3
tesla_model_s.number_of_wheels = 3
tesla_model_s # => <Vehicle:0x0055644f55b820 @number_of_wheels=3, @type_of_tank="electric", @seating_capacity=5, @maximum_velocity=250>
  • attr_accessor: implementează ambele metode
class Vehicle
  attr_accessor :number_of_wheels

  def initialize(number_of_wheels, type_of_tank, seating_capacity, maximum_velocity)
    @number_of_wheels = number_of_wheels
    @type_of_tank = type_of_tank
    @seating_capacity = seating_capacity
    @maximum_velocity = maximum_velocity
  end
end

# number_of_wheels equals 4
tesla_model_s = Vehicle.new(4, 'electric', 5, 250)
tesla_model_s.number_of_wheels # => 4

# number_of_wheels equals 3
tesla_model_s.number_of_wheels = 3
tesla_model_s.number_of_wheels # => 3

Așa că acum am învățat cum să obținem valorile atributelor, să implementăm metodele getter și setter și să folosim attr (cititor, scriitor și accesor).

De asemenea, putem folosi metode pentru a face alte lucruri – cum ar fi o metodă „make_noise”. Să vedem!

class Vehicle
  def initialize(number_of_wheels, type_of_tank, seating_capacity, maximum_velocity)
    @number_of_wheels = number_of_wheels
    @type_of_tank = type_of_tank
    @seating_capacity = seating_capacity
    @maximum_velocity = maximum_velocity
  end

  def make_noise
    "VRRRRUUUUM"
  end
end

Când numim această metodă, aceasta returnează doar un șir „VRRRRUUUUM”.

v = Vehicle.new(4, 'gasoline', 5, 180)
v.make_noise # => "VRRRRUUUUM"

Incapsulare: ascunderea informațiilor

Incapsularea este o modalitate de a restricționa accesul direct la datele și metodele obiectelor. În același timp, facilitează funcționarea pe acele date (metodele obiectelor).

Incapsularea poate fi utilizată pentru a ascunde membrii de date și funcția membrilor … Incapsularea înseamnă că reprezentarea internă a unui obiect este în general ascunsă vizualizării în afara definiției obiectului.
Wikipedia

Deci, toată reprezentarea internă a unui obiect este ascunsă din exterior, doar obiectul poate interacționa cu datele sale interne.

În Ruby folosim metode pentru a accesa direct datele. Să vedem un exemplu:

class Person
  def initialize(name, age)
    @name = name
    @age  = age
  end
end

Tocmai am implementat această clasă Persoană. Și, după cum am învățat, pentru a crea persoana obiect, folosim noua metodă și trecem parametrii.

tk = Person.new("Leandro Tk", 24)

Așa că m-am creat! 🙂 The tk obiect! Trecându-mi numele și vârsta. Dar cum pot accesa aceste informații? Prima mea încercare este să apelez la numele și metodele de vârstă.

tk.name
> NoMethodError: undefined method `name' for #<Person:0x0055a750f4c520 @name="Leandro Tk", @age=24>

Nu o putem face! Nu am implementat metoda numelui (și vârstei).

Vă amintiți când am spus „În Ruby folosim metode pentru a accesa direct datele?” Pentru a accesa numele și vârsta tk, trebuie să implementăm aceste metode în clasa noastră de persoană.

class Person
  def initialize(name, age)
    @name = name
    @age  = age
  end
  
  def name
    @name
  end
  
  def age
    @age
  end
end

Acum putem accesa direct aceste informații. Cu încapsularea ne putem asigura că obiectului (tk în acest caz) i se permite doar accesul la nume și vârstă. Reprezentarea internă a obiectului este ascunsă din exterior.

Moștenirea: comportamente și caracteristici

Anumite obiecte au ceva în comun. Comportament și caracteristici.

De exemplu, am moștenit de la tatăl meu câteva caracteristici și comportamente – cum ar fi ochii și părul lui. Și comportamente precum nerăbdarea și introversiunea.

În programarea orientată pe obiecte, clasele pot moșteni caracteristici comune (date) și comportament (metode) de la o altă clasă.

Să vedem un alt exemplu și să-l implementăm în Ruby.

Imaginați-vă o mașină. Numărul de roți, capacitatea de loc și viteza maximă sunt toate atributele unei mașini.

class Car
  attr_accessor :number_of_wheels, :seating_capacity, :maximum_velocity

  def initialize(number_of_wheels, seating_capacity, maximum_velocity)
    @number_of_wheels = number_of_wheels
    @seating_capacity = seating_capacity
    @maximum_velocity = maximum_velocity
  end
end

Clasa noastră de mașini a fost implementată! 🙂

my_car = Car.new(4, 5, 250)
my_car.number_of_wheels # 4
my_car.seating_capacity # 5
my_car.maximum_velocity # 250

Instanțiat, putem folosi toate metodele create! Grozav!

În Ruby, folosim operatorul

class ElectricCar < Car
end

Simplu! Nu este nevoie să implementăm metoda de inițializare și orice altă metodă, deoarece această clasă o are deja (moștenită din clasa Mașină). Să dovedim!

tesla_model_s = ElectricCar.new(4, 5, 250)
tesla_model_s.number_of_wheels # 4
tesla_model_s.seating_capacity # 5
tesla_model_s.maximum_velocity # 250

Frumos!

Modul: O cutie de instrumente

Ne putem gândi la un modul ca la o cutie de instrumente care conține un set de constante și metode.

Un exemplu de modul Ruby este Math. Putem accesa PI constantă:

Math::PI # > 3.141592653589793 

Și metoda .sqrt:

Math.sqrt(9) # 3.0

Și putem implementa propriul nostru modul și îl putem folosi în clase. Avem o clasă RunnerAthlete:

class RunnerAthlete
  def initialize(name)
    @name = name
  end
end

Și implementați un modul Skill pentru a avea metoda average_speed.

module Skill
  def average_speed
    puts "My average speed is 20mph"
  end
end

Cum adăugăm modulul la clasele noastre, astfel încât acesta să aibă acest comportament (metoda average_speed)? Doar îl includem!

class RunnerAthlete
  include Skill

  def initialize(name)
    @name = name
  end
end

Vedeți „includeți Abilitatea”! Și acum putem folosi această metodă în instanța noastră din clasa RunnerAthlete.

mohamed = RunnerAthlete.new("Mohamed Farah")
mohamed.average_speed # "My average speed is 20mph"

Ura! Pentru a termina modulele, trebuie să înțelegem următoarele:

  • Un modul nu poate avea instanțe.
  • Un modul nu poate avea subclase.
  • Un modul este definit de modul … sfârșit.

Încheiat!

Am învățat MULTE lucruri aici!

  • Cum funcționează variabilele Ruby
  • Cum funcționează afirmațiile condiționate Ruby
  • Cum funcționează buclele și iteratoarele Ruby
  • Matrice: Colecție | Listă
  • Hash: Colecție valoare-cheie
  • Cum putem itera prin aceste structuri de date
  • Obiecte și clase
  • Atribute ca date ale obiectelor
  • Metode ca comportament al obiectelor
  • Folosind jeturi și seturi Ruby
  • Incapsulare: ascunderea informațiilor
  • Moștenirea: comportamente și caracteristici
  • Module: o cutie de instrumente

Asta e

Felicitări! Ați completat acest conținut dens despre Ruby! Am învățat multe aici. Sper că ți-a plăcut.

Distrează-te, continuă să înveți și continuă să codezi mereu!

Ale mele Stare de nervozitate & Github. ☺