Tuteur Ruby / Ruby Tutorial
gilles.hunault@univ-angers.fr
Attention : ce tuteur utilise Ruby 1.9 et non pas Ruby 1.8 et comme il y a quelques incompatibilités entre ces versions, il faut être prudent(e) si vous faites des copier/coller des instructions ou des programmes. Comme mes autres tuteurs, ce texte est une introduction à un langage de programmation, pas à la programmation en général. On suppose donc ici que vous savez déjà programmer, mais dans un ou plusieurs autres langages.
Il est possible d'apprendre rapidement les rudiments de Ruby avec des sites comme tryruby (anglais), Ruby en vingt minutes (français) ou Apprenez Ruby (français). D'autres liens et des vidéos sont disponibles pour la communauté française ici.
Une fois Ruby installé, l'interpréteur interactif irb permet de tester du code, des expressions, des programmes... Il peut même compléter les commandes avec la touche Tab. Voir par exemple irb shell pour plus de détails sur irb et son utilisation. Vous pouvez aussi utiliser irb sur le web sans rien installer avec le site tryruby.
Enfin, sur le site zenspider, on trouvera une "carte de référence" détaillée pour Ruby nommée Ruby QuickRef et bien sûr l'ouvrage de référence par le créateur du langage est disponible en ligne : Programming Ruby.
Table des matières
1. Présentation de Ruby
Le créateur de Ruby, Yukihiro « Matz » Matsumoto reconnait avoir voulu créer son langage à partir de SmallTalk, Lisp et Perl. Il voulait un langage plus lisible, plus souple. La première version date de 1995 mais la version 1.9 n'a été disponible qu'en 2009. Ruby est surtout connu à cause de son fameux framework de développement nommé Rails ou Ror (Ruby On Rails) basé sur MVC, dont nous recommandons le tutoriel long et détaillé en français à l'adresse http://french.railstutorial.org/. Il y a bien sûr une rivalité entre Python, Ruby, Php et Perl et aussi entre C (ou variantes), Java et Ruby, rivalités déplacées parce des objectifs différents requièrent sans doute des langages différents.
En Ruby, tout est objet et tout est ouvert. Ainsi, l'opérateur + peut être redéfini pour une classe particulière d'objets, les crochets d'indexation aussi, - et / aussi, etc. Certaines méthodes sont chainables et les parenthèses facultatives, sauf en cas d'ambiguité. Par exemple a.sort.reverse.first 3 est légal et interprété comme a.sort().reverse().first(3). Certaines méthodes sont définies sans objet apparent parce qu'elles sont très souvent utilisées, comme par exemple p, print et puts pour afficher des valeurs, mais ce sont en fait des raccourcis qui font appel a l'objet Kernel. Ainsi puts a est interprété comme Kernel.puts(a), ce qui permet une lecture plus aisée des programmes Ruby. Les notions d'énumération et d'itération, couplées à celles de bloc, de fonction anonyme (lambdas) et de procédure en font un langage particulièrement expressif. Le traitement des expressions régulières "à la Perl " et l'utilisation de variables globales cryptiques le rendent aussi puissant que Perl y compris en mode one-liner, tout en conférant au langage une supériorité pour la conception objets.
Ruby est réflexif : on peut explorer le langage à travers ses propres méthodes. Par exemple 2.class est interprété comme 2.class() et renvoie Fixnum, 2.class.ancestors() fournit la classe de 2 et ses super-classes, Array.methods() donne la liste des 94 méthodes de la classe Array.
2. Syntaxe élémentaire
Pour accèder aux méthodes des objets en Ruby, la notation point est utilisée et les paramètres sont mis entre parenthèses. Ainsi, on peut suivre le modèle objet.methode(paramètres) pour écrire 2.+(3) mais la syntaxe traditionnelle 2 + 3, moins "choquante", est également disponible, ce qui autorise l'écriture procédurale traditionnelle et l'écriture fonctionnelle classique aussi. Les fonctions de conversion se nomment to_i, to_f, to_a, to_s... La méthode class fournit la classe de l'objet auquel on l'applique. Ainsi "oui".class() renvoie String. Une méthode qui se termine par le caractère ? indique un test. Par exemple a.nil? renvoie vrai ou faux suivant que a est nil ou pas. Une méthode qui se termine par ! indique qu'il faut faire attention à l'utilisation de la méthode, le plus souvent parce qu'elle modifie l'objet. Par exemple b = a.sort et b = a.sort! ne réalisent pas la même chose : dans le second cas, a est de plus trié in situ.
L'affectation se fait par = et peut être parallèle ou multiple, avec des subtilités liées à la possibilité d'affecter des structures. Ainsi a,b=b,a permute les variables a et b, mais a,(*b,c),d= (1..6).to_a ne fait certainement pas ce que vous croyez ! L'affectation multiple est surtout pratique pour les retours de fonction, comme par exemple x,y=f(...) quand on sait que f renvoie deux valeurs. En plus des constantes true et false, il y a nil. Seuls false et nil peuvent être évalués comme false (pas une expression correspondant à 0 ou à une chaine vide). Il y a des délimiteurs de bloc notés do end et { } mais ils n'ont pas la même priorité d'évaluation. Les grands entiers sont gérés en précision illimitée de façon transparente. Ainsi 2.class est Fixnum mais (2**150).class est Bignum.
L'égalité se teste par ==, === mais aussi par eql? et equal? et <==>. Un test utilise la syntaxe if elsif else end mais le mot then est possible, de même que unless alors que la structure de cas utilise case when else end. Une boucle explicite est définie par les mots-clés explicite while, loop, until, (avec do, redo, retry et next possibles) mais on préfére souvent les boucles implicites définies par les itérateurs each. Les entrées et sorties se font à l'aide des instructions gets, open, print, printf, putc, puts, readline, readlines et test mais en mode interactif on aime bien utiliser aussi p car plus court, et inspect car plus détaillé.
En plus des nombres, Ruby fournit des étendues et des plages de variation comme a..b et a...b (qui va de a à b-1) et des fonctions comme upto, downto et step. Les valeurs a et b dans les étendues peuvent être des nombres, des caractères et même des chaines. Ainsi "azz".next est "baa". On consultera la ruby doc de String pour plus de détails et aussi celle de range. Ruby implémente aussi la notion de symbole comme :a (qui n'est pas exactement "a") et de tranche (slice) de structure. Au passage, on notera que << permet d'ajouter des éléments à une structure :
$gh> irb irb(main):001:0> a = [1,8,3] => [1, 8, 3] irb(main):002:0> a << 5 => [1, 8, 3, 5] irb(main):003:0> a << 3 << 6 => [1, 8, 3, 5, 3, 6] irb(main):004:0> b = "oui " => "oui " irb(main):005:0> b << "ou non " => "oui ou non "Dans la hiérarchie standard de Ruby 1.9, il y a entre autres les tableaux (array) qui utilisent les crochets, les tables de hachages (hash) dénotés par des accolades, les ensembles (sets), les expressions régulières (regexp) délimitées par / et les symboles (symbols) préfixés par le symbole :. La définition et la gestion de structures (chaines, tableaux) est facilitée par des fonctions comme %Q, %q, %x, %W, %w et %r. A propos des expressions régulières en Ruby, on pourra consulter le site rubular qui permet de les tester en ligne.
La programmation fonctionnelle en Ruby va au-delà des simples map, filter, reduce traditionnels. Les fonctions anonymes (lambda) et le passage du controle de l'exécution à des blocs via yield en font un langage puissant mais pas toujours facile à maitriser.
Ainsi l'expression ligne.split(/,/).map() {|val| Integer(val) }; vient découper la variable ligne et renvoie les deux mots de cette ligne convertis en entiers à l'aide de map et Integer() :
$gh> irb irb(main):001:0> ligne =" 3 , 8 " => " 3 , 8 " irb(main):002:0> num1, num2 = ligne.split(/,/).map() {|val| Integer(val) } => [3, 8] irb(main):003:0> num1 => 3 irb(main):004:0> num2 => 8 irb(main):005:0> num3, num4 = ligne.split(/,/).map { |val| Integer(val) } # plus court => [3, 8] irb(main):006:0> num3, num4 = ligne.split(/,/).map(&:to_i) # encore plus court => [3, 8]Il faut connaitre et apprendre à utiliser les *ect que sont collect, detect, inject, reject, ainsi que partition pour profiter pleinement des méthodes disponibles pour les structures qui font partie de la classe Enumerable :
$gh) > irb irb(main):001:0> require "set" => true irb(main):002:0> a = Set.new => #<Set: {}> irb(main):003:0> b = Array.new => [] irb(main):004:0> c = Hash.new => {} irb(main):005:0> [a,b,c].each { |x| puts x.class.ancestors.to_s } [Set, Enumerable, Object, Kernel, BasicObject] [Array, Enumerable, Object, Kernel, BasicObject] [Hash, Enumerable, Object, Kernel, BasicObject] irb(main):006:0> d = [8,12,6,3,5,2,12,7] => [8, 12, 6, 3, 5, 2, 12, 7] irb(main):010:0> d.max => 12 irb(main):007:0> d.collect { |x| x*x } => [64, 144, 36, 9, 25, 4, 144, 49] irb(main):008:0> d.detect { |x| x%2==1 } => 3 irb(main):009:0> d.select { |x| x%2==1 } => [3, 5, 7] irb(main):010:0> d.inject { |s,x| s+x } => 55 irb(main):011:0> d.partition { |x| x<8 } => [[6, 3, 5, 2, 7], [8, 12, 12]] irb(main):012:0> d.reject { |x| x%2==1 } => [8, 12, 6, 2, 12]La taille d'un objet s'obtient souvent via length, size ou count qui sont sans doute des synonymes. D'ailleurs Ruby dispose d'une méthode alias qui se révèle à l'usage très pratique quand on utilise simultanément plusieurs langages de programmation. Il y a aussi alias_method.
Pour connaitre la classe des objets, on peut utiliser class, kind_of?, instance_of?, superclass et ancestors déjà utilisé dans le programme précédent. Pour connaitre les méthodes associées aux objets, il y a des fonctions comme methods, public_methods... comme dans le programme suivant nommé ar.rb qui affiche les méthodes pour la classe Array :
# (gH) -_- ar.rb ; TimeStamp (unix) : 24 Décembre 2012 vers 19:31 def souligne(x) puts x puts "-"*x.length end # fin de fonction souligne souligne("les ancetres de Array") puts Array.ancestors.to_a puts pum = Array.public_methods.sort souligne("les " + pum.length.to_s + " methodes \"public\" de Array") pum.each { |x| puts x } puts prim = Array.private_methods.sort souligne("les " + prim.length.to_s + " methodes \"private\" de Array") prim.each { |x| puts x } puts prom = Array.protected_methods.sort souligne("les " + prom.length.to_s + " methodes \"protected\" de Array") prom.each { |x| puts x } puts im = Array.instance_methods.sort souligne("les " + im.length.to_s + " \"instance\" methodes de Array") im.each { |x| puts x } puts puts("bye")Voici un extrait du résultat de son exécution, le fichier complet est ar_rb.txt :
les ancetres de Array --------------------- Array Enumerable Object Kernel BasicObject les 94 methodes "public" de Array --------------------------------- ! != !~ < <= <=> == === =~ > >= [] __id__ __send__ allocate ancestors autoload autoload? class class_eval class_exec class_variable_defined? class_variable_get class_variable_set class_variables clone const_defined? const_get const_missing const_set constants define_singleton_method display dup enum_for eql? equal? extend freeze frozen? [...] les 83 methodes "private" de Array ---------------------------------- Array Complex Float Integer Rational String __callee__ __method__ ` abort alias_method at_exit attr attr_accessor attr_reader attr_writer binding block_given? caller catch define_method eval exec exit exit! extended [...] les 0 methodes "protected" de Array ----------------------------------- les 152 "instance" methodes de Array ------------------------------------- ! != !~ & * + - << <=> == === =~ [] []= __id__ __send__ all? any? assoc at class clear clone collect collect! combination compact compact! concat count cycle define_singleton_method delete delete_at delete_if detect display drop drop_while dup each each_cons each_index each_slice each_with_index each_with_object empty? entries enum_for eql? equal? extend fetch fill find find_all find_index first flatten flatten! [...] byeLa programmation objets en Ruby utilise l'instruction class qui définit (ou complète) une classe. Il est possible de modifier toutes les classes de Ruby, y compris les classes de base. Le symbole @ désigne une instance d'objet alors que @@ désigne une instance de classe. On utilise self pour désigner l'objet courant. Signalons au passage que $ définit une variable globale et qu'une variable dont l'initiale est majuscule définit une constante. Il n'y a pas d'héritage multiple en Ruby mais le notion de module permet de réaliser des mixins.
La gestion des erreurs et exceptions est assurée par begin/end avec rescue raise ensure else. Il est possible de définir des auto-tests élémentaires (comme en Python) lorsqu'on exécute directement le fichier avec une construction basée sur __FILE__ et $0 ; le module test/unit permet de définir des test unitaires. Voici par exemple ce qu'on peut faire avec le fichier iota1.rb
# # (gH) -_- iota1.rb ; TimeStamp (unix) : 08 Janvier 2015 vers 11:18 def iota(n=10) return( (1..n).to_a ) end # fin de fonction iota # une version objets class Integer def iotan (1..self).to_a # pas de return (hum !) end # fin de fonction iotan end # fin de class # autotest et test unitaire if __FILE__ == $0 then puts("iota(n) renvoie un tableau des nombres de 1 a n ") puts(" exemple : iota(5) renvoie ") puts( iota(5) ) require 'test/unit' class TestIota < Test::Unit::TestCase def test_simple assert_equal([1,2,3],iota(3)) assert_equal([1,2,3],3.iotan) end # fin de fonction test_simple end # fin de classe TestIota end # fin de si fichier appelé directementiota(n) renvoie un tableau des nombres de 1 a n exemple : iota(5) renvoie 1 2 3 4 5 Run options: # Running tests: . Finished tests in 0.000198s, 5056.7366 tests/s, 10113.4732 assertions/s. 1 tests, 2 assertions, 0 failures, 0 errors, 0 skipsSi par contre on dit que iota(5) doit renvoyer [1,2,5] ce test échoue :
# # (gH) -_- iota2.rb ; TimeStamp (unix) : 08 Janvier 2015 vers 11:19 def iota(n=10) return( (1..n).to_a ) end # fin de fonction iota # une version objets class Integer def iotan (1..self).to_a # pas de return (hum !) end # fin de fonction iotan end # fin de class # autotest et test unitaire if __FILE__ == $0 then require 'test/unit' class TestIota < Test::Unit::TestCase def test_simple assert_equal([1,2,5],iota(5)) assert_equal([1,2,3],3.iotan) end # fin de fonction test_simple end # fin de classe TestIota end # fin de si fichier appelé directementRun options: # Running tests: F Finished tests in 0.000365s, 2739.2307 tests/s, 2739.2307 assertions/s. 1) Failure: test_simple(TestIota) [iota2.rb:24]: <[1, 2, 5]> expected but was <[1, 2, 3, 4, 5]>. 1 tests, 1 assertions, 1 failures, 0 errors, 0 skipsSignalons enfin que les modules benchmark et profile permettent respectivement d'obtenir des durées d'exécutions et des profils d'exécution afin de pouvoir optimiser le code Ruby, de même que le module pp permet de mieux afficher les structures (pp est mis pour pretty print). On trouvait sur le site RAA de nombreux modules pour Ruby (plus de 1800 début décembre 2012). Le site ayant fermé en 2013, on se rabattra désormais sur the Ruby Toolbox et sur RubyGems pour obtenir des modules, packages et "gemmes" (96318 gemmes en janvier 2015).
@ghchu~/public_html/tuteurs|(~gH) > irb irb(main):001:0> 2.class.ancestors => [Fixnum, Integer, Numeric, Comparable, Object, Kernel, BasicObject] irb(main):002:0> require 'pp' => true irb(main):003:0> pp 2.class.ancestors [Fixnum, Integer, Numeric, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject] => [Fixnum, Integer, Numeric, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]3. Nos exemples standards
Notre premier exemple demande un nom (ou un prénom) et le convertit en majuscules après avoir affiché la date et l'heure. Pour le formatage des dates et heures, on pourra consulter la page date-time-format-ruby. Nous avons utilisé gets.chomp() pour la lecture au clavier. On aurait pu écrire aussi gets.chomp ou STDIN.gets().chomp(). On notera aussi l'utilisation de if en modification, comme en Perl.
Fichier bonjour.rb :
require "date" print "Bonjour.\n Quel est ton nom ? " pren = gets.chomp() pren = "Bel(le) inconnu(e)" if pren.length()==0 puts "Le " + Time.now.strftime(" %d/%m/%Y vers %H h %M") + " au revoir, " + pren.upcase() + ". "Le deuxième exemple vient lire une valeur en ligne de commande. Tant que ce n'est pas un entier, on redemande une valeur. Quand c'est un entier, on affiche sa table de multiplication. Le test qui détermine si une chaine de caractères est un entier doit utiliser une fonction ou une méthode. Une première solution peut être :
Fichier tdm.rb :
# ----------------------------------------------------- def entier?(chaine) # retourne vrai si la chaine correspond a un entier return( chaine =~ /^\d+$/ ) end # fin de fonction entier? # ----------------------------------------------------- if ARGV.length==0 then puts " syntaxe : ruby tdm.rb NOMBRE_ENTIER " exit -1 end # fin de si nb = ARGV[0] while ! entier?(nb) puts " vous avez saisi " + nb + " qui n'est pas un entier. " print " redonner un entier : " nb = STDIN.gets.chomp # ne pas oublier STDIN ici end # fin de tant que puts "Table de " + nb nb = nb.to_i (1..10).each { |i| puts sprintf("%2d fois %d = ",i,nb) + sprintf("%5d",nb*i) } # find de eachIl est également très simple de rajouter la méthode entier? à la classe des objets chaines de caractères de façon à utiliser une approche plus objet :
Fichier tdm2.rb :
# ----------------------------------------------------- class String def entier? # retourne vrai si la chaine correspond a un entier return( self =~ /^\d+$/ ) end # fin de methode entier? end # fin de class String # ----------------------------------------------------- if ARGV.length==0 then puts " syntaxe : ruby tdm2.rb NOMBRE_ENTIER " exit -1 end # fin de si nb = ARGV[0] while ! nb.entier? puts " vous avez saisi " + nb + " qui n'est pas un entier. " print " redonner un entier : " nb = STDIN.gets.chomp # ne pas oublier STDIN ici end # fin de tant que puts "Table de " + nb nb = nb.to_i (1..10).each { |i| puts sprintf("%2d fois %d = ",i,nb) + sprintf("%5d",nb*i) } # find de eachVoici un exemple d'exécution :
$gh) > ruby tdm2.rb pomme vous avez saisi pomme qui n'est pas un entier. redonner un entier : oui vous avez saisi oui qui n'est pas un entier. redonner un entier : 6 Table de 6 1 fois 6 = 6 2 fois 6 = 12 3 fois 6 = 18 4 fois 6 = 24 5 fois 6 = 30 6 fois 6 = 36 7 fois 6 = 42 8 fois 6 = 48 9 fois 6 = 54 10 fois 6 = 60Pour le troisième exemple, il faut lire un fichier comme notes.txt où le nom est séparé des notes par le symbole étoile puis afficher les moyennes par ordre alphabétique puis par ordre de mérite. Voici une implémentation possible ; on teste bien sûr si le fichier existe avant de tenter de le lire et on ignore les lignes vides. La documention pour la classe des fichiers est ici.
# on teste s'il y a un argument en ligne de commande if ARGV.length==0 then puts " syntaxe : ruby notes.rb FICHIER_DES_NOMS_ET_NOTES " exit -1 end # fin de si # s'il y a un argument, on teste s'il correspond à un fichier qui existe fn = ARGV[0] if ! File.exists?(fn) then puts "Pas de chance, le fichier "+fn+" ne semble pas exister. Stop " end # fin de si # si c'est le cas, on parcourt le fichier pour stocker les noms, # et calculer les moyennes nblig = 0 # nombre de lignes nbelv = 0 # nombre d'élèves noms = {} # tableau associatif ; noms = Hash.new() est ok aussi File.readlines(fn).each do |ligne| ligne.strip! nblig += 1 next if ligne.empty? # pas la peine de continuer si la ligne est vide nbelv += 1 # arrivé ici, on est sur de ne pas avoir une ligne vide nom,notes = ligne.split(/\*/) # on sépare nom et notes nom.strip! # on enlève les espaces dans noms notes.strip! # on enlève les espaces dans notes lnotes = notes.split(/\s+/).map { | chen | Float(chen) } # on fait un tableau de notes moyenne = lnotes.reduce(:+).to_f / lnotes.length # on calcule la moyenne noms[nom.to_s] = moyenne # on remplit le tableau associatif end # fin de each puts "On a vu " + nbelv.to_s + " eleves sur " + nblig.to_s + " lignes. " # affichage trié par nom puts("Affichage alpha") noms.sort.each { |n,m| puts sprintf(" %-10s %5.2f",n,m) } # affichage trié par moyenne décroissante puts("Par ordre de merite") noms.sort_by { |k,v| v }.reverse.each { |n,m| puts sprintf(" %-10s %5.2f",n,m) }On notera que la construction de bloc avec File ferme automatiquement le fichier. Dans le tri par moyenne décroissante, on ne gère pas (volontairement) les ex-aequo.
4. Compléments pour les bases de données, le Web et XML
Pour accéder aux bases de données de MySql en Ruby, on peut par exemple utiliser le module nommé... mysql ! Pour une utilisation plus soutenue, sqlalchemy est une meilleure solution, notamment pour sa gestion ORM. Voici un mini-exemple d'utilisation qui montre au passage l'utilisation de begin rescue end.
# fichier sql.rb require "mysql" puts "Ruby, version ", RUBY_VERSION begin # connect to the MySQL server dbh = Mysql.real_connect("localhost", "anonymous", "anonymous", "statdata") # get server version string and display it puts "Server version: " + dbh.get_server_info rescue Mysql::Error => e puts "Error code: #{e.errno}" puts "Error message: #{e.error}" puts "Error SQLSTATE: #{e.sqlstate}" if e.respond_to?("sqlstate") # disconnect from server end res = dbh.query("SELECT iden,age FROM elf LIMIT 3 ") res.each do |row| printf "%s, %s\n", row[0], row[1] end dbh.close if dbh puts "ok"On produit le fichier suivant, résultat de l'exécution par la simple commande ruby sql.rb > sql_rb.txt.
Ruby, version 1.9.1 Server version: 5.1.66-0ubuntu0.10.04.3 M001, 62 M002, 60 M003, 31 okPour lire un fichier distant avec Ruby aucun souci, puisqu'il y a les modules net/http et open-uri :
# combien y a-t-il de jours en mai, via ndjpm ? # si tout est OK, ce programme affiche : # # [ndjpm] il y a 31 jours pour le mois numero 5 # require 'net/http' require 'open-uri' ndm = 5 url = "http://forge.info.univ-angers.fr/~gh/internet/ndjpm.php?m=#{ndm}" open(url) do |f| contenu = f.readlines /comporte (\d+?) jours/ =~ contenu.to_s nbj = $1 puts "[ndjpm] il y a " + nbj + " jours pour le mois numero #{ndm} " end # fin de openDe même, avec le module socket il est très simple de disposer d'un serveur Web élémentaire. On pourra également consulter la documentation des serveurs thin et webrick. Enfin, un détour par sinatra est incontournable pour comprendre la philosophie de Ruby.
Il existe plusieurs packages pour traiter du XML en Ruby mais dont l'installation n'est pas toujours facile. Voici un exemple nommé demo_xml.rb :
require "rexml/document" include REXML require "xml/xslt" # voir https://github.com/glejeune/ruby-xslt/blob/master/README.rdoc fxml = "test3.xml" fxsl = "vide.xsl" puts "Noms des personnes" xmldct = Document.new File.new(fxml) xmldct.elements.each("//personne/nom") { |n| puts n.text } puts puts "Les services" XPath.each(xmldct, "//service") { |e| puts e.text } puts "Noms des personnes" puts "La transformation vide" xslt = XML::XSLT.new xslt.xml = fxml xslt.xsl = fxsl resultat = xslt.serve puts resultatEt le résultat de son exécution, si les fichiers test3.xml et vide.xsl sont accessibles :
Noms des personnes Dupond Durand Dupuis Les services Achats Achats Courrier Noms des personnes La transformation vide <?xml version="1.0" encoding="ISO-8859-1"?> Dupond Achats Durand Achats Dupuis Courrier5. Forces et faiblesses du langage
Un des grands intérêts de Ruby est sa syntaxe qui permet de définir facilement des langages spécifiques (DSL). Voici un mini-exemple (ventes.rb) pour des ventes dans une kermesse où toutes les ventes de consommation sont à 1 euro :
# encoding: ISO-8859-1 ##################################################### # # ventes.rb # ##################################################### def debut $ventes = 0 end # fin de fonction debut def vente(conso) $ventes += 1 end # fin de fonction vente def recette puts "recette courante : #$ventes euros" end # fin de fonction recette def fin puts "Il y a eu #$ventes ventes, soit une recette finale de #$ventes euros." end # fin de fonction fin ##################################################### debut vente "jus d'orange" vente "bière" vente "bière" vente "café" recette vente "café" vente "café" fin ##################################################### # # à l'exécution, ce programme affiche : # ##################################################### # recette courante : 4 euros # Il y a eu 6 ventes, soit une recette finale de 6 euros.Les premières versions de Ruby étaient peu ou mal documentées et les modules disponibles aussi, ce qui a fait passer Ruby pour un langage peu évolué et mal conçu.
Si Ruby 1.9 est un successeur amélioré de Perl et de SmallTalk il reste lui aussi perfectible. Ainsi les sigils $, % @ de Perl sont évités mais Ruby implémente les sigils $, @ et @@. La notion de lambda et de bloc rend Ruby très puissant et presque complètement fonctionnel mais il n'est pas possible d'appliquer directement une fonction à une structure contrairement au langage R, par exemple où on peut écrire sapply(FUN=carre,X=1:10) pour calculer les carrés des nombres de 1 à 10 (il faut recourir à une méthode) :
$gh > irb irb(main):001:0> def auCarre(x) irb(main):002:1> return(x*x) irb(main):003:1> end # fin de fonction auCarre => nil irb(main):004:0> auCarre 5 => 25 irb(main):005:0> auCarre [3,8,2] TypeError: can't convert Array into Integer from (irb):2:in `*' from (irb):2:in `auCarre' from (irb):5 from /usr/bin/irb:12:in `<main>' irb(main):006:0> [3,8,2].map() { |i| auCarre(i) } => [9, 64, 4] irb(main):007:0> [3,8,2].map(&:auCarre) NoMethodError: private method `auCarre' called for 3:Fixnum from (irb):7:in `map' from (irb):7 from /usr/bin/irb:12:in `<main>' irb(main):008:0> class Fixnum irb(main):009:1> irb(main):010:1* def carre irb(main):011:2> return self*self irb(main):012:2> end # fin de fonction carre irb(main):013:1> irb(main):014:1* end # fin de classe Fixnum => nil irb(main):015:0> carre 5 NoMethodError: undefined method `carre' for main:Object from (irb):15 from /usr/bin/irb:12:in `<main>' irb(main):016:0> 5.carre => 25 irb(main):017:0> [3,8,2].map(&:carre) => [9, 64, 4] irb(main):018:0> def carre(x) # def globale irb(main):019:1> return(x*x) irb(main):020:1> end # fin de fonction carre => nil irb(main):021:0> carre 5 => 25Enfin, Ruby est souvent plus connu pour le développement web via le framework ROR (Ruby on Rails) que comme langage "indépendant". Le nombre de packages distincts (ou "gems") est actuellement sans doute un peu inférieur à celui de son «rival» Python, ce qui n'enlève pourtant rien comme à ses qualités de langage complètement objets, réflexif et ouvert... en attendant les améliorations qu'apportera Ruby 2.0.
Retour à la page principale de (gH)