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