Ruby
Documentations > Langages
Posté le 15 novembre 2018 dans Langages par Julien.
Documentation sur le langage Ruby. Variables, boucles, conditions, etc đ
Description
Ci-dessous vous trouverez les bases du langage Ruby.
Ruby est un langage open-source dynamique qui met l’accent sur la simplicitĂ© et la productivitĂ©. Sa syntaxe Ă©lĂ©gante facilite la lecture et l’Ă©criture. C’est un langage non-compilĂ© qui est trĂšs utilisĂ© dans le monde du hacking pour la crĂ©ation de scripts / payloads / etc..
Fichiers
…
Executable Shebang
#!/usr/bin/env ruby
# Executable shebang
# La premiere ligne n'est pas un commentaire ;)
# Elle specifie que le fichier s'execute avec ruby
# (donc l'extention de fichier n'est plus obligatoire)
# L'excution du fichier peut se faire comme ceci:
#"./script" au lieu de "ruby script.rb"
puts "Hello World"
Conditions
# Operateurs logiques :
# != == && ||
# !!x => vaut vrai si x n'est pas nil
x=5
if (x == 5); puts "x vaut #{x} (if)"
elsif (x == 3); puts "x vaut #{x} (elsif)"
else; puts "x vaut #{x} (else)"
end
# Ces deux ecritures sont equivalentes
if (x == 4) then puts "ok" else puts "else" end
x == 4 ? puts("ok") : puts("else")
# ------------------------------------
case x
when 4; puts "x vaut #{x} (when 4)"
when 5; puts "x vaut #{x} (when 5)"
when 6; puts "x vaut #{x} (when 6)"
else; puts "x vaut #{x} (else)"
end
Boucles
x=5
tab = "argument1", "argument2", "argument3"
# for classique
for i in 0..5 do
puts "i vaut #{i}"
end
# for parcourir tableau
for valeur in tab do
puts "'valeur' pendant ce tour de boucle vaut : "+valeur
end
# for parcourir tableau 2
tab.each do |index|
puts index
end
# while
while x < 8 do puts "coucou" x+=1 end # do while begin puts "hello" x-=1 end while x > 5
# jusqu'a
until x >= 8 do
puts "hey"
x+=1
end
# do while jusqu'a
begin
puts "bonjour"
x-=1
end until x <= 5 # break for i in 0..5 if i > 2 then
break # Lorqu'atteint, ON SORT de la boucle la plus proche
end
puts "[break] La variable i vaut #{i}" # 1, 2, 3
end
# next
for i in 0..5
if i < 4 then
next # Lorqu'atteint, ON SAUTE jusqu'au tour de boucle suivant (on passe le reste)
end
puts "[next] La variable i vaut #{i}" # 4, 5
end
# redo Commente car genere une boucle infinie ;)
#for i in 0..5
# if i < 2 then
# puts "[redo] La variable i vaut #{i}"
# redo # Lorqu'atteint, ON RECOMMANCE la boucle la plus proche
# end
#end
# retry
begin
do_something # exception raised
rescue
# handles error
retry # restart from beginning
end
Modules
# Les modules permettent de définir des fonctions et variables dans un espace de nom
module MonModule
variable_scope = 2 # SANS @ la variable n'est dispo que dans le scope (a cote)
@variable_instance = 3 # AVEC @ la viable est dispo dans l'instance (a cote et "en dessous")
def self.maFonction
#puts variable_scope # Ne fonctionne PAS !
puts @variable_instance # Fonctionne :)
end
end
MonModule.maFonction # Appel d'une fonction du module
module MonDeuxiemeModule
module ModuleImbrique
def self.maFonction
puts "Le module imbrique fonctionne :)" # Fonctionne :)
end
end
end
MonDeuxiemeModule::ModuleImbrique.maFonction
Fonctions
# Les fonctions doivent etre definies avant d'etre appellees
variable = 3 # La variable n'est pas globale ;)
$variable_globale = 7 # Pour qu'elle soit globale on ajoute $
def affiche1
puts 1 # Affiche 1
end
def donneVariableGlobale
#return variable # variable n'est pas globale ;)
return $variable_globale
end
def additionne parametre_1, parametre_2
parametre_1+parametre_2 # le return n'est pas obligatoire
end
# regardez l'asterisk ;) On peut passer plusieurs parametres dans un tableau
def splatOperatorParameter *parameters
puts parameters.join(", ")
end
affiche1 # Appel de la fonction
puts donneVariableGlobale
puts additionne(5,5)
splatOperatorParameter(1,2,3,4,5,7,8,9,10,11,12,13)
Fonction lambda
# lambda permet de stocker du code dans une variable
# Une fonction lambda peut etre passee comme un block
mettre_au_carre = lambda{ |x| x*x }
resultat = mettre_au_carre.call(3)
puts resultat # 9
yield
# Le yield permet de passer du code en parametre
# On peut voir le yield d'une methode comme...
# stop ce que tu fais, execute le yield, et reprends
# La fonction qui recoit le bloc de code
def yielding
puts "DEBUT du code dans la methode"
yield
puts "FIN du code dans la methode"
end
# Syntaxe avec fonction { .. }
yielding { puts "code passe en parametre :D" }
# Syntaxe avec fonction do .. end
yielding do
puts "code passe en parametre :D"
end
Tableaux
# Initialisation
tableau = [5,6,7]
tableau_2 = Array.new(2)
tableau_3 = %w(creer un tableau de chaines de caracteres grace a %w :\) )
# Redefinition
tableau = [1,2,3,4]
tableau_2 = [5,6,7,8]
# Affichage tableau
# puts tableau # Affichage avec des retours a la ligne, pas pratique :/
# tableau.length retourne la longeur du tableau
puts tableau.length
# Methode tableau.join retourne une chaine des elements, avec un separateur ou non
puts tableau.join(', ')
# Methode tableau.map permet de faire un calcul sur tous les elements du tableau
# tableau.map { |n| n % 2 == 0 }}" #=> [false, true, false, true]
tableau.map{|x| x } # Inutile mais le principe est lĂ ;)
puts "tableau.map { |n| n*n } => #{ tableau.map { |n| n*n } }" #=> [1, 4, 9, 16]
puts "tableau.map { |n| n*2 } => #{ tableau.map { |n| n*2 } }" #=> [1, 2, 6, 8]
puts "tableau.map { |n| n+1 } => #{ tableau.map { |n| n+1 } }" #=> [2, 3, 4, 5]
# tableau.reject permet de retirer des elements (SUPPRIMER quand retourne TRUE)
tableau = tableau.reject { |n| n % 2 == 0 } # supprime les nombres pair
# tableau.shift recupere le PREMIER element, le supprime, et decale le tableau
tableau = [1,2,3,4]
tableau.shift
puts tableau.join(", ")
# tableau.pop recupere le DERNIER element, et le supprime
puts tableau.pop
puts tableau.join(", ")
# .max & .min
tableau = [1,9,7,3];
puts tableau.min
puts tableau.max
puts tableau.minmax
Hash
# Un hash est un objet compose de paires cle/valeur
# Il permet de stocker des structures complexes
hash = {
:cle => "Valeur",
:flags => %w(plein de flags)
}
puts hash[:cle]
puts hash[:flags].join(", ")
hash[:cle] << "-ajouter-a-la-suite-de-valeur" puts hash[:cle] hash[:nouvelle_cle] = "nouvelle_valeur" puts hash[:nouvelle_cle] # hash.merge(hash_2) & hash.merge!(hash_2) hash_2 = { :cle => "Valeur hash_2",
:cle_2_h2 => %w(blah blah blah)
}
hash_2.merge(hash) # On fusionne les cles IDENTIQUES seulement
puts hash_2
hash_2.merge!(hash) # On fusionne les cles DEFFERENTES seulement
puts hash_2
# Ecriture differente
tag = {type: "a", href: "http://juju-dev.fr/", link: "cliquez ici"}
puts tag
splat operator
# regardez l'asterisk ;) On peut passer plusieurs parametres dans un tableau
def splatOperatorParameter *parameters
puts parameters.join(", ")
end
# splat operator sur parametre de fonction
splatOperatorParameter 1,2,3,4,5,7,8,9,10,11,12,13
# splat operator split mode
pet1, pet2, pet3 = *["duck","dog","cat"] # pet1, pet2, pet3 prennent les valeurs ...
puts pet1+", "+pet2+", "+pet3
# splat operator collect mode
*zoo = pet1, pet2, pet3 # Le tableau zoo prend les valeurs ...
puts zoo.join(", ")
Point d’exclamation !
# Le point d'exclamation permet d'attribuer des valeurs de maniĂšre plus simple
s = "STRING"
puts s # => STRING
s.downcase! # modifier la variable en elle-meme !
puts s # => string
# L'exemple ci-dessus equivaut Ă ...
s_2 = "STRING"
puts s_2 # => STRING
s_2 = s_2.downcase
puts s_2 # => string
Arguments commande terminal
# A revoir
# A revoir
# A revoir
# A revoir
# A revoir
puts "" # retour a la ligne -----------------
# Exemple de commande pour cet exemple :
puts "-------------------"
puts "Exemple de commande :"
puts "ruby arguments_terminal_avance del champ1:val67 champ2:val23 champ3:val78"
puts "-------------------"
puts ""
puts "Voici les 3 premiers arguments reçus :"
p ARGV[0]
p ARGV[1]
p ARGV[2]
# Si pas d'arguments on ajoute l'argument --help
ARGV << '--help' if ARGV.empty?
# Les alias. Argument 'a' equivaut a 'add'
aliases = {
"a" => "add",
"m" => "mod",
"d" => "del",
}
# .shift = recuperer le champ [0], le supprimer, puis decaler le tab
commande = ARGV.shift
commande = aliases[commande] || commande
puts ""; puts "----------------------------";
case commande
when "add"
puts "Commande vaut "+commande # commande vaut add
# Ensuite on recupere le reste d'une commande telle que :
# ruby arguments_terminal_avance add champ0123:valeur0123
champ, valeur = ARGV.shift.split(':')
# Donc avec l'exemple on a 'champ' qui vaut 'champ0123' etc..
puts "champ vaut "+champ
puts "valeur vaut "+valeur
when "mod"
puts "Commande vaut "+commande # commande vaut mod
# Ici on va recuperer plusieurs pairs champ:valeur
# Pour une commande qui serait du genre :
# ruby arguments_terminal_avance mod champ1:val67 champ2:val23 champ3:val78
ARGV.each do |argument|
tab = argument.split(':')
champ = tab[0]
valeur = tab[1]
puts "champ vaut "+champ
puts "valeur vaut "+valeur
end
# Notez que l'on a pas utilise .shift, donc ARGV n'est pas vide
when "del" # commande vaut del
puts "Commande vaut "+commande # commande vaut del
# Cet exemple est le meme que le precedent mais plus court
# Pour une commande qui serait du genre :
# ruby arguments_terminal_avance del champ1:val67 champ2:val23 champ3:val78
ARGV.each do |argument|
champ, valeur = argument.split(':')
puts "champ vaut "+champ
puts "valeur vaut "+valeur
end
else
puts "souci chef'"
end
puts "----------------------------"; puts "";
Classes
class Chien
def nom # GET variable
@nom
end
def nom= nom # SET variable
@nom = nom
end
end
medor = Chien.new
medor.nom = "Medor"
puts medor.nom # Medor
# -----------
class Chat
attr_accessor :nom # Macro equivalent au code Chien (SET+GET)
attr_reader :race # Macro lecture seule
end
mimine = Chat.new
mimine.nom = "Mimine"
puts mimine.nom # Mimine
# puts medor2.race = "" # erreur
# -----------
class Voiture
attr_reader :nb_roues # Macro GET
attr_accessor :marque # Macro SET+GET
attr_accessor :a1, :a2, :a3 # On peut en mettre plusieurs
def initialize # Appele avec objet.new
@nb_roues = 4
@son_klaxon = "tut tut tut !!!" # private car aucun accesseur, et
# accessible dans toute la classe car @
end
def klaxon
son = @son_klaxon
puts son
# son est accessible uniquement dans ce scope (fonction klaxon)
# car declare SANS @
end
def testPartie1
@test ="ca marche" # Declaration @test
end
def testPartie2
puts @test # @test accessible dans toute la classe
end
end
audi_tt = Voiture.new
audi_tt.marque = "Audi"
puts audi_tt.marque # Audi
puts audi_tt.nb_roues # 4
audi_tt.klaxon # tut tut tut !!!
audi_tt.testPartie1
audi_tt.testPartie2 # ca marche
HĂ©ritage
class Vehicule
attr_accessor :marque
def initialize
puts "init vehicule"
end
def klaxon
puts "tut tut tut !!!"
end
end
class Voiture < Vehicule
attr_reader :nb_roues
def initialize
super # Appel classe parente
end
def roule
puts "je roule"
end
end
class Avion < Vehicule
attr_reader :largeur_ailes
def initialize
super # Appel classe parente
end
def klaxon # Redefinition de la methode parente
puts "bip bip !!"
end
def vole
puts "je vole"
end
end
voiture = Voiture.new
voiture.klaxon
voiture.roule
avion = Avion.new
avion.klaxon
avion.vole
SĂ©parer le code en plusieurs fichiers (require)
index.rb
# Fichier index.rb
# Les bonnes pratiques sont de creer 1 fichier par module et par classe
# Recuperer fichier courant
puts __FILE__ # index.rb
# Recuperer le chemin absolu du fichier
absolute_path = File.expand_path(__FILE__)
puts absolute_path # /Users/juliencuenot/Desktop/Perso divers/ruby/index.rb
# Retirer le nom du fichier, on ajoute ../
absolute_path = File.join(absolute_path, "../")
puts absolute_path # /Users/juliencuenot/Desktop/Perso divers/ruby/index.rb/../
# On charge les fichiers
require "#{absolute_path}un_module"
require "#{absolute_path}une_classe"
# On peut acceder au code des fichiers
MonModule.afficheUnTrucDuModule # Salut c'est moi le module
ma_classe = MaClasse.new # Salut c'est moi la classe
ma_classe.afficheUnTrucDeLaClasse
mon_module.rb
module MonModule
def self.afficheUnTrucDuModule # self est obligatoire avec les modules
puts "Salut c'est moi le module"
end
end
ma_classe.rb
class MaClasse
def afficheUnTrucDeLaClasse
puts "Salut c'est moi la classe"
end
end
Gems, installation macOS
gem install json
ou avec RubyVersionManager :
rvm gemset install json
verifier l’install :
irb
require 'json' # retourne true si c'est bon
utilisation :
require 'json'
hash={
:developpeur => "JuJu",
:tab => %w(blah blah blah)
}
str_json = hash.to_json
puts str_json # {"developpeur":"JuJu","tab":["blah","blah","blah"]}
hash_parse = JSON.parse(str_json)
puts hash_parse # {"developpeur"=>"JuJu", "tab"=>["blah", "blah", "blah"]}
# Attention les : ont disparu ;)
bundler : gerer les gems
index.rb
# On require le bundler qui va recuperer nos gems
require 'bundler'
Bundler.require
hash = {
:cle => "value"
}
# Utilisation du gem json (DL par bundler)
puts hash.to_json
Gemfile
source 'https://rubygems.org'
gem 'json' # Le bundler telechargera json
# gem 'nokogiri'
# gem 'rack', '~>1.1'
# gem 'rspec', :require => 'spec'
# Voir doc bundler pour les multiples possibilites
# Ce fichier a ete cree 'a la main' car erreur avec la commande 'bundle init'
Reflexion du langage
= Capacité de ruby à connaßtre son code
str="TEST 123"
# XXX.methods afficher les methodes de str
puts str.methods.join(", ") # Affiche les methodes de str
# XXX.send appeler une methode dynamiquement
puts str.send("downcase") # test 123 (downcase est une methode)
p str.send(:split, " ") # ["TEST", "123"]
# XXX.instance_of?(Classe) est-ce une instance de .. ?
puts str.instance_of?(String) # true
puts str.instance_of?(Object) # false
# XXX.is_a?(Classe) est-ce .. ?
puts str.is_a?(String) # true
puts str.is_a?(Object) # true
# XXX.respond_to?("methode") est-ce que ca repond, est-ce que ca existe ?
puts str.respond_to?(:upcase) # true
puts str.respond_to?("upcase") # true
puts str.respond_to?(:blah) # false
# XXX.class retourne la classe de l'objet
puts str.class # String
DSL
Domain specific languages = Jargon de definition
# A revoir
# A revoir
# A revoir
# A revoir
# A revoir
########### Classe ###############
class MaClasse
@property_1 = 0
@property_2 = 0
@property_3 = 0
def initialize &bloc_de_definition
#instance_eval &bloc_de_definition # ca fonctionnerait ;)
DSL.new &bloc_de_definition # Appel de la classe DSL
end
def affiche
puts "salut"
end
# ----- DSL = prise en charge du bloc de definition --------
class DSL
def initialize &bloc_de_definition
instance_eval &bloc_de_definition # execution du bloc dans la classe DSL
# affiche # Error : affiche n'est pas accessible
end
def test; puts "test"; end
end
# ----------------------------------------------------------
end
#################################
bloc_de_definition = lambda do |_|
test
end
ma_classe = MaClasse.new &bloc_de_definition # on passe le bloc en parametre
# one way of doing DSL with direct code block
#dsl.define(collector) do
# affiche
# affiche
# affiche
#end
# another way of doing DSL with lambda
#code = lambda do |_|
# add "4"
# add "5"
# add "6"
#end
#dsl.build(collector, &code)
#instance_eval changes evaluation context,
# so code passed for evaluation is treated as original code of the object.
# As a result, all private methods and fields are available:
RĂ©ouverture de classes & Monkey patching
= Ajouter des fonctionnalités aux classes / réécrire des methodes.
Le monkey patching est le fait de redefinir du code non dĂ©veloppĂ© soit mĂȘme. Il faut ĂȘtre prudent car on peut modifier le comportement des gems Ă©galement.
# Monkey patching de la class Object
# -> Implementation de try en ruby :p
j_existe_pas = nil
# puts j_existe_pas.upcase # -> Erreur
# on ajoute la fonction try a tous les objets ruby !
class Object
def try *args
if self.nil? # si l'objet est null
nil # On retourne nil
else
send *args # tableau decoupe
end
end
end
puts j_existe_pas.try(:upcase) # nil sans erreur
j_existe = "hey"
puts j_existe.try(:upcase) # hey
Gestion des erreurs
# A FAIRE
# A FAIRE
# A FAIRE
# A FAIRE
# A FAIRE