Après la conférence de Paris on Rails 2007, je penses que Ruby a de gros avantages, surtout en terme de temps de développement, alors je m’y mets une fois pour toutes. Cet article trace les grandes lignes des spécificités de Ruby, soit en terme de convention de nomage, soit en terme de fonctionnement.
Les variables
| locale | globale | d’instance | de classe | constante |
|---|---|---|---|---|
| myVar | $myVar | @myVar | @@myVar | MyVar |
Attention aux majuscules pour les constantes !
Strings
’<<’ concatène :
a="hello"
a<<" world"
ou redéfinit
a=<<END_STR
Hello World
END_STR
Arrays
digits=-1..9
digits.reject{ |i| i<5 } # enlève des éléments du tableau
(1..10) === 5 -> true
(1..10) === 3.14159 -> true
('a'..'j') === 'z' -> false
> Array.new<<"toto" => ["toto"]
Initialisation en chaîne
Attention à cette syntaxe :
> a=b=1;a=2;b
=> 1
> c=d=[];c<<"toto";d
=> ["toto"]
c et d sont les même objets.
Random
rand = rand (3) # retourne un entier entre 0 et 2
rand = rand(3.0)
Symboles
Une section spéciale pour les symboles, qui je l’avoue, m’ont posés problème pendant un moment. Vous trouverez cependant en anglais un topo très bien fait et très clair sur les symboles.
En résumé, les symboles sont un compromis entre la simplicité d’écriture et l’économie de mémoire. On instancie un symbole par le biais du caractère-clef ’ : ’ (remarquons que les String aussi utilise une syntaxe particulière pour être instanciée avec des guillements).
Un exemple parlant avec les Hash : les clefs sont des objets de la classe String. A chaque fois que l’on va faire référence aux clefs, on va instancier un objet de type String, ce qui est couteux en performance. Ainsi, pour optimiser, on va utiliser des symboles. On peut convertir les clefs String en symbole par le message "symbolize_keys !" :
>> params = { "id" => 1, "action" => "show" }
=> {"action"=>"show", "id"=>1}
>> params.symbolize_keys!
=> {:id=>1, :action=>"show"}
>> params[:id]
=> 1
Boucles
def mtdarry
10.times do |num|
square = num * num
return num, square if num > 5
end
end
a,b=mdtarry
On voit ici la facilité avec laquelle on peut manipuler des tableau courts.
Executer une commande système
Similaire aux scripts shell
puts `ls`
Les arguments au script Ruby peuvent être récupérés avec ARGV[0] ...
Les méthodes
def name=(nm)
@name = nm
end
String.methods.sort #méthodes de classe de la classe String
String.instance_methods.sort # méthodes d'instance
String.instance_methods(false).sort # méthodes d'instance de String seulement (sans les super classes)
d.respond_to?("talk")
puts (num.instance_of? Fixnum)
| notation | signification |
|---|---|
| Ticket#price | The instance method price in class Ticket |
| Ticket.most_expensive | The class method price |
| Ticket ::most_expensive | Same as Ticket.most_expensive |
In writing about Ruby, the pound notation (#) is sometimes used to indicate an instance method - for example, we say File.chmod to denote the class method chmod of class File, and File#chmod to denote the instance method that has the same name.
"method_missing"
C’est la méthode à définir pour traiter les appels à un message inconnu sur un objet :
class Dummy
def method_missing(m, *args)
puts "There's no method called #{m} here -- please try again."
end
end
Dummy.new.anything
Singleton
On "privatise" new pour empécher son utilisation, et on founit un create qui va se charger de conserver une instance unique (instance de classe ou "statique" en Java) avec "@@" :
class Logger
private_class_method :new
@@logger = nil
def Logger.create
@@logger = new unless @@logger
@@logger
end
end
Note : il semblerait que l’on puisse utilise la syntaxe :
@@logger ||= new
Note 2 : not thread safe ! (on preferera l’utilisation du Mixin "Singleton")
Blocks & Procs
un exemple de block :
def call_block
yield('hello', 99)
end
call_block {|str, num| puts str + ' ' + num.to_s}
Blocks are not objects, but they can be converted into objects of class Proc. This can be done by calling the lambda method of the module Kernel. A block created with lambda acts like a Ruby method. If you don’t specify the right number of arguments, you can’t call the block.
aBlock = lambda { |x| puts x }
aBlock.call 'Hello World!'
toast = lambda do
puts 'Cheers'
end
toast.call
Les Procs peuvent être passés en paramètre d’une méthode avec le caractère clef "&". On voit ici un exemple d’utilisation pour rendre silencieux un bloc spécifique de code lorsque l’on a, par exemple, lancé Ruby avec l’option "-w" (avec les warnings) :
def silently(&block)
warn_level = $VERBOSE
$VERBOSE = nil
result = block.call
$VERBOSE = warn_level
result
end
Utilisation :
silently { require 'net/https' }
Collecte
Prendre tous les noms de fleurs avec Rails :
Flowers.find.collect { |x| x.name }
Retourne un tableau constitué des noms de fleurs, en allant directement chercher dans la base.
Fichiers
File.open('data.txt', 'r') do |f1|
while line = f1.gets
puts line
end
end
$_
The gets routine has a side effect : as well as returning the line just read, it also stores it into the global variable $_. This variable is special, in that it is used as the default argument in many circumstances. If you call print with no argument, it prints the contents of $_. If you write an if or while statement with just a regular expression as the condition, that expression is matched against $_.
while gets # assigns line to $_
if /Ruby/ # matches against $_
print # prints $_
end
end
équivalent à
ARGF.each { |line| print line if line =~ /Ruby/ }
Include, require & load
puts $: # array of folders to search for load
$: << "c:/" # add a folder to the load path
include
Ce mot clef permet l’utilisation de Mixin. Une classe qui inclut un module va recevoir les même méthodes que ce module comme si elles avaient été définit dans cette classe :
class Library
include Enumerable
#how to iterate over the collection
def each
(...)
end
#compare 2 elements
def <=> (book)
(...)
end
c = Library.new()
c.find { |book| book.title =~ /world/}
Exceptions
par défaut, raise lance une exception "RuntimeError" :
def raise_and_rescue
begin
raise 'An error has occured.'
rescue
puts 'I am rescued.'
end
end
raise_and_rescue
Mais on peut lever des exception classiques :
def compute(x)
begin
raise ArgumentError, 'Argument is not numeric' unless x.is_a? Numeric
rescue ArgumentError => e
puts e.message
puts e.backtrace.inspect
rescue AnotherTypeOfException
puts "hello world"
else
puts "unknown error"
end
puts 'I am rescued.'
end
Le mot clef "ensure" qui permet de s’assurer qu’une action a été faite quelque soient les exceptions qui ont été lancées :
begin
file = open("/tmp/some_file", "w")
# do stuff here
ensure
file.close
end
Un mot sur le Duck Typing
Notion pas essentielle du tout, cela veut juste dire qu’il n’y a pas de vérification de type. Lorsque l’on fait un appel de méthode, si la méthode existe ça exécute la méthode, sinon ca lance une exception.
Freeze
La méthode de la classe Object freeze permet de rendre un objet non modifiable. On peut savoir si un objet est "glacé" ou non par la méthode "frozen ?"
str = 'A simple string. '
str.freeze
begin
str << 'An attempt to modify.'
rescue => err
puts "#{err.class} #{err}"
end
Sérialisation
Marshal only serializes data structures. It can’t serialize Ruby code (like Proc objects), or resources allocated by other processes (like file handles or database connections). Marshal just gives you an error when you try to serialize a file.
File.open('game', 'w+') do |f|
Marshal.dump(gc, f)
end
File.open('game') do |f|
@gc = Marshal.load(f)
end
Regex
//.class -> Regexp
m1 = /Ruby/.match("The future is Ruby")
puts m1.class # it returns MatchData
m2 = "The future is Ruby" =~ /Ruby/
puts m2 # it returns 14
Note : Les caractères qui ne sont pas des digits : "/\D/" (utilisation des majuscules).
%r {\.(gif|jpg|png)$}i
Eval
Doivent être utilisés avec précaution, évités si possible, ils permettent d’exécuter du texte en tant que code Ruby. Cf. Kernel#eval, Object#send, Module#class_eval, Module#module_eval et Object#instance_eval.
Sources :
Des exemples tirés de Programming Ruby et de Ruby Learning