logo club elec

Initiation au langage Ruby

logo ruby

By Joseph Caillet

Qui suis-je ?

Joseph Caillet

Joseph Caillet
  • Étudiant M1 à l'ISEN Brest

josephcaillet.fr - joseph.caillet@isen-bretagne.fr - GitHub

Disclaimer

  • Juste une introduction
  • Je ne suis pas un rubyste de longue date ;)

Introduction

松本行弘 - Yukihiro Matsumoto
aka Matz

Yukihiro Matsumoto
  • Créateur de Ruby
  • Prix du logiciel libre en 2011 par la FSF et RMS
  • Architecte en chef pour Ruby à Heroku (PAAS)

Qu'est-ce que ce langage de hipster ?

La première version à été publiée en 1995. Principales fonctionnalités (extrait wikipedia) :

  • Orienté objet
  • Ramasse-miettes
  • Gestion d'exceptions
  • Métaprogrammation
  • Les blocs
  • Héritage simple (mais avec modules)
  • Nombres entiers de taille illimitée
  • Portée des variables définie par leur forme
  • Threads natifs
  • Réflexion / introspection
  • Extensions en C

Qui utilise ruby...

...et pourquoi ?

  • Scripting et automatisation
  • Web (via RoR, ou pas)
  • Prototypage
  • etc

Bases de Ruby

Les bases de Ruby

  • Toutes les données sont des objets
  • Plein de sucres syntaxiques !
  • La syntaxe est simple
  • Langage interprété, utilisation des outils irb (ou pry), ri, gem et ruby

puts "Hello World !" # équivalent à : puts("Hello World !");

str = "omEleTtE dU FroMaGe."
puts str.downcase.split.each{ |word| word.capitalize! }.join ' '

puts "The answer is #{41.next}"
						

Conventions de nommage

Variables


ma_variable  #une variable classique
MA_CONSTANTE #une constante, modifiable, émet un warning
$ma_variable_globale #brr...
@ma_variable #une variable d'instance
@@ma_variable #une variable de classe (statique)
					

Méthodes


ma_methode  #une méthode
ma_methode? #une méthode, renvera un booleen
ma_methode! #une méthode qui modifie l'objet
					

Classes et modules


MaClasse
MomModule
					

Commentaires et variables

Ducktyping : si ça vole comme un canard, nage comme un canard, alors c'est un canard.


mon_nombre = 25
puts mon_nombre.class

mon_booleen = true
#puts mon_booleen.class

ma_string = "Ruby"
=begin
puts ma_string.class
=end

mon_objet = nil
__END__
puts mon_objet.class
					

Opérateurs


integer = 5 + 5
nb = entier - 2 * 4
float = nb / 2.1
big_int = 35 ** 2
reste_division = 10 % 8
puts "toto " * 3
#versions racourcies : +=, -=, *=, /=, **=
					

Attention, i++ et i-- inexistants !

Interpolation


puts "What is your name ?"
name = gets.chomp

puts "Hello #{name} ! :)"
print "Hello #{name} ! :)\n" #print ne rajoute pas d' '\n'
puts 'Hello #{name} ! :)\n' # pas d'interpolation si '' au lieu de ""
				    

Conversion de type


puts "nb1 :"
nb1 = gets.chomp.to_i # existe aussi to_s, to_f, to_a...
puts "nb2 :"
nb2 = Integer(gets.chomp)

puts "#{nb1} + #{nb2} = #{nb1 + nb2}"
				    

Array : Tableau


tab = [7, 0, 54, -9]
tab[0] = 8
tab.push 42
tab << 36
puts tab
tab.sort!
print tab
puts

tab2 = "Ruby", "is", "love", "<3"
puts tab2.join " "

tab3 = Array.new(4, "titi")
print tab3
puts
				    

Hash : Tableau associatif


langage1 = { "nom" => "Java", "niveau" => "haut niveau" }

#plus optimisé pour la machine :
langage2 = { :nom => "C", :niveau => "bas niveau" }

#plus optimisé pour la machine et le programmeur :
langage3 = { nom: "CSS", niveau: nil }


langage4 = {} # ou langage4 = Hash.new
langage4[:nom] = "Asm"
langage4[:niveau] = "très bas niveau"
puts langage4[:nom]
puts langage4.keys
				    

Symboles

Un symbole représente quelque chose de manière unique.


str1 = "chaine"
str2 = "chaine"

sym1 = :sym
sym2 = :sym

puts "str: #{str1.object_id} , #{str2.object_id} -> #{str1.object_id.equal?(str2.object_id)}"
puts "sym: #{sym1.object_id} , #{sym2.object_id} -> #{sym1.object_id.equal?(sym2.object_id)}"

#exemple d'utilité :
puts 43.respond_to?(:next)
puts 43.respond_to?(:gloubi_boulga)
				    

Structures de contrôle

Opérateurs de comparaison


puts "Hello" == "Hello"
puts 45 != 314
puts 1 >= 1.0
puts 2 > 5
puts 9 <= 2
puts "titi" > 9
puts 3 <=> 8
puts "Hello world" =~ /world/
				    

If elsif else


str = gets.chomp

if str == "titi"
	puts " et Grosminet"
elsif str == "beep beep"
	puts " et Coyote"
else
	puts "That's all folks !"
	puts "Pa pala pala papa pa papalapa, pa pa pa pa pa paaa !"
end
				    

Attention

0 est évalué à vrai, de même que les chaînes vides, les tableaux vides, etc.

Seul false et Nil sont évalué à false, tout le reste est évalué à true.

Conditions multiples


if 5 < 4 || 8.even?
	#do_some_magic
elsif 2.odd? && 8 == 9
	#do_some_magic
elsif 8 < 38 and 42 == 42
	#do_some_magic
eslif "toto" == "tutu" or "tata" == 8
	#do_some_magic
elsif	not 3 < 8
	#do_some_magic
end
			    

Unless et autres raccourcis


puts "It's true." if 25.36 < 78
puts "That's false" unless 25.36 > 78

unless false
	puts "It is false."
else
	puts "What did you expect?"
end
			    

Case


note = 12
case note
when 0
	puts "Tu sais pas jouer Jack, t'est mauvais !"
when (1..6) # Cette chose est un range, un intervalle de nombre.
	puts "Insuffisant"
when (7..12)
	puts "À encourager"
when (13..14)
	puts "Bien"
when 18, 19, 20
	puts "Très Bien"
else
	puts "Je vous soupçonne d'avoir triché, ou d'avoir entre 15 et 17."
end
			    

Case


salut = "Bonjour"
case salut
when "Bonjour"
	puts "Vous parlez français !"
when "Hola"
	puts "Habla Espagnol !"
when "こんにちは"
	puts "あなたは本当にこの長い文の意味を理解しようとしましたか?"
when /z/ #notez la regex
	puts "Votre salutation contient la lettre z..."
end
			    

Bloc

Un bloc est un bout de code, recevant éventuellement des paramètres.


#bloc sur plusieurs lignes
do |arg1, arg2, ...|
	do_something_here
	do_something_here
	do_something_here
end

#bloc sur une ligne
{ |arg1, arg2, ...| do_something_here }

#Si aucun argument, on omet le |...|
{ do_something_here }
			    

Loop

La plus simple des boucles.


loop do
	do_something_here
	break if condition #obligatoire sinon boucle infinie
end
			    

While

La boucle tant que.


i = 0
while i != 10
	puts i
	i += 1
end

i = 0
until i == 10
	puts i
	i += 1
end

i = 0
puts i += 1 while i != 10
			    

For in

La boucle pour.


for elm in (0..10) do
	puts elm
end

for elm in [1, 5, 7, 8] do
	puts elm
end
			    

Each

La boucle pour chaque.


(1..5).each { |nb| puts nb if nb.odd? }


hash = { a: 4, b: 8, c: nil, d: (45..78)}

hash.each do |key, value|
	puts "#{key} -> #{value}"
end
			    

Times, upto, downto


5.times { |nb| puts nb }
puts

7.upto(10) { |nb| puts nb }
puts

5.downto -2 do |nb|
	puts nb
end
			    

Fonctions / Méthodes

Définir une fonction :


def dire_boujour
	puts "Salutation, je suis Buzz l'éclair."
end

dire_boujour

def dire_boujour(prenom)
	puts "Bonjour #{prenom} ! :)"
end

dire_boujour "l'ISEN Brest"
dire_boujour("l'ISEN Brest")
			    

Arguments facultatifs


 #argument avec valeur par default toujours en dernière position
def dire_boujour(prenom, smiley = ":)")
	puts "Bonjour #{prenom} ! #{smiley}"
end

dire_boujour "les rubystes"
dire_boujour "les rubystes", ":D"
					

Liste d'arguments


def dire_boujour(*prenom) #prenom est une liste d'arguments
	puts "Bonjour #{prenom.join ', '} !"
end

dire_boujour "les rubystes"
dire_boujour "Pierre", "Paul", "Jaques", "Jean"

__END__
#Prototypes possibles:
def dire_boujour(*prenom)
def dire_boujour(titi,*prenom)
def dire_boujour(*prenom, toto)
def dire_boujour(tutu, *prenom, tata)
					

Keywords arguments


def seconds(sec: 0, min: 0, hour: 0)
	return sec + min*60 + hour*60**2
end

puts seconds(min: 2)
puts seconds(min: 1, sec: 4)
puts seconds(hour: 1, sec: 41, min: 3)
					

Return implicite

La dernière valeur évaluée est retournée.


def seconds(sec: 0, min: 0, hour: 0)
	sec + min*60 + hour*60**2
end

puts seconds(min: 2)
puts seconds(min: 1, sec: 4)
puts seconds(hour: 1, sec: 41, min: 3)
					

Les classes

Késako ?

Une classe est un "plan", un "moule", pour fabriquer un objet, qui aura des propriétés et des méthodes.

Visibilité

Les attributs seront toujours privés.

Les méthodes sont publiques par défaut, peuvent être protected ou private (contrairement aux attributs).

Création d'une classe


class Animal
	def initialize(nom)
		@nom = nom
	end

	def dire_boujour
		puts "Je m'appele #{@nom} !"
	end
end

hamster = Animal.new "Pachmina"
hamster.dire_boujour
					

Getters et setters


class Animal
	def initialize(nom)
		@nom = nom
		@message = nil
	end

	def set_message(message)
		@message = ", " + message
	end

	def dire_boujour
		puts "Je m'appele #{@nom}#{@message} !"
	end
end

hamster = Animal.new "Chapeau"
hamster.dire_boujour
hamster.set_message "devinez ce que j'ai sur la tête"
hamster.dire_boujour
					

Getters et setters


class Animal
	def message=(message)
		@message = ", " + message
	end
end
    

class Animal
	def initialize(nom)
		@nom = nom
		@message = nil
	end

	def dire_boujour
		puts "Je m'appele #{@nom}#{@message} !"
	end
end

hamster = Animal.new "Chapeau"
hamster.dire_boujour
hamster.message = "devinez ce que j'ai sur la tête"
hamster.dire_boujour








class Animal
	def message=(message)
		@message = ", " + message
	end
end
					

Getters et setters


class Animal
	attr_reader :nom, :message #génération des getters
	attr_writer :nom, :message #génération des setters
	def initialize(nom)
		@nom = nom
		@message = nil
	end

	def dire_boujour
		puts "Je m'appele #{@nom}#{@message} !"
	end
end

hamster = Animal.new "Chapeau"
hamster.dire_boujour
hamster.message = "devinez ce que j'ai sur la tête"
hamster.dire_boujour
puts hamster.nom
					

Getters et setters


class Animal
	attr_accessor :nom, :message #génération des getters et des setters

	def initialize(nom)
		@nom = nom
		@message = nil
	end

	def dire_boujour
		puts "Je m'appele #{@nom}#{@message} !"
	end
end

hamster = Animal.new "Chapeau"
hamster.dire_boujour
hamster.message = "devinez ce que j'ai sur la tête"
hamster.dire_boujour
puts hamster.nom
					

Héritage


class Animal
	attr_accessor :nom

	def initialize(nom)
		@nom = nom
	end
end

class HeroDessinAnime < Animal
	def initialize(nom, generique)
		super(nom)
		@generique = generique
	end

	def chanter
		puts @generique
	end
end

hamster = HeroDessinAnime.new("Hamtaro", "Hamtaro, c'est moi le plus petit des grands héros...")
hamster.chanter
puts hamster.nom
					

Self

Référence à l'objet courant.


class Fixnum #Modification des classes existantes
	def double
		self*2
	end

	def equal_number? nb
		self.equal? nb
	end
end

puts 5.double
puts 5.equal_number? 42
					

Bonus

Commandes systèmes

Vitesse du ventilo


`sensors`.lines.each { |l| puts l.split[1] if l.include? "cpu_fan" }
				

Méthodes et variables statiques


class Animal
	@@instance_number = 0

	def initialize
		@@instance_number += 1
	end

	def self.get_instance_number
		@@instance_number
	end
end

Animal.new
puts Animal.get_instance_number
Animal.new
puts Animal.get_instance_number
					

Opérateurs bonus


#affectation parallèle
a, b, c = 1, 5, 3
puts a, b, c

def methode
	return true, 5
end

a, b = methode
puts a, b

#ternaire
puts 8 == 8 ? "vrai" : "faux"

#range
(1..10) #nombre de 1 à 10
(1...10) #nombre de 1 à 9

#Je met ce truc parceque c'est drole
(1..20).each do |i|
  puts i if (i == 3)..(i == 15)
end

#résolution de porté
Macron::CE_QUE_JE_VEUX
#c'est que vous, partout, vous codiez en Ruby, car c'est notre projet!

#si non défini
toto ||= 42
					

Contrôles d'exécution supplémentaires


break #quitter un bloc, une boucle
next #passer à l'itération suivante
redo #recommencer l'itération courante
retry #reprendre toute l'exécution du bloc depuis le début
					

Exception


begin #implicite quand dans une méthode
	ouvrir_fichier
rescue PasLesDroitsExeption
	afficher_erreur_de_droit
rescue FichierInexistantExeption
	demander_nouveau_non_fichier
	retry
else #autres types d'exceptions
	afficher_erreur
ensure
	#toujours exécuté
end

#lever une exeption :
raise
raise "Message"
raise TypeException, "Message"
					

Visibilités public, protected, private


class A
	attr_accessor :nom

	public
	def call_public(a)
		a.pub
	end

	def call_protected(a)
		a.pro
	end

	def call_private(a)
		a.pri
	end

	public
	def pub
		puts "Je suis public, et je suis #{@nom}"
	end

	protected
	def pro
		puts "Je suis protected,  et je suis #{@nom}"
	end

	private
	def pri
		puts "Je suis private,  et je suis #{@nom}"
	end
end

a1 = A.new
a1.nom = "a1"

a2 = A.new
a2.nom = "a2"

#appel des methode sur a1
#a1.pub
#a1.pro
#a1.pri

#demande à a1 d'appeler methode sur a2
#a1.call_public a2
#a1.call_protected a2
#a1.call_private a2
					

Multi-fichiers


require 'mon_fichier.rb'
#ou
require 'mon_fichier'
					

Modules

  • Servent de namespaces
  • Permettent de réaliser des mixins

Modules

On peut utiliser les modules pour faire des énumérations, si les valeurs ont un sens. Sinon, on préférera les symboles.


module Volume
	HIGHT = 100
	MEDIUM = 66
	LOW = 33
	MUTE = 0
end
					

Modules

Mixin: permet de "faire de l'héritage multiple".


module Trigo
	PI = 3.14

	def cos
		puts "calcul de cosinus..."
	end
end

module Logarithme
	E = 0,577

	def ln
		puts "calcul logarithme népérien..."
	end
end

class Maths
	include Trigo #méthode de Trigo deviennent des méthodes de classe de Maths
	extend Logarithme #méthode de Logarithme deviennent des méthode d'instance de la classe Maths

	def sqrt
		puts "calcul racine carrée..."
	end
end

Maths.ln
puts Maths::PI

m = Maths.new
m.cos
m.sqrt
					

Métaprogrammation

  • Classes ouvertes
  • Réféxivité / Introspection
  • Création / destruction de méthodes à l'exécution
  • Possibilité de changer les comportements en cas d'erreur
  • Eigen class

class A
	def methode_normale
		puts "hello"
	end

	def method_missing(name)
		puts "La methode #{name} n'existe pas..."
	end
end

a1 = A.new
a2 = A.new

def a1.methode_eigen
	puts "eigen !"
end

a1.methode_normale
a2.methode_normale
a1.methode_eigen
a2.methode_eigen
					

Pour aller plus loin

Fin du cours

Merci de votre attention :)

Des questions ?

Des remarques ?