Microsoft's Internet Explorer browser has no built-in vector graphics machinery required for "loss-free" gradient background themes.

Please upgrade to a better browser such as Firefox, Opera, Safari or others with built-in vector graphics machinery and much more. (Learn more or post questions or comments at the Slide Show (S9) project site. Thanks!)

Ruby

Introduzione allo scripting Ruby

Master "Tecnologie OpenSource"

Obiettivi

  • Conoscere le origini e le influenze di Ruby
  • Conoscere i paradigmi di programmazione supportati
  • Conoscere le regole di sintassi base
  • Saper utilizzare l’interprete interattivo
  • Saper scrivere ed eseguire un semplice script
  • Conoscere e Saper utilizzare i primi tipi di dati
  • Conoscere e Saper utilizzare le strutture di controllo
  • Conoscere la programmazione OOP in Ruby ed il Duck Typing
  • Conoscere il significato della Metaprogrammazione
  • Saper dichiarare ed utilizzare una Classe
  • Conoscere il significato delle Closure
  • Saper utilizzare gli iterator e le closure

Le Origini

Ruby è un linguaggio OOP General Purpose creato da Yukihiro “Matz” Matsumoto a metà degli anni 90.

L’obiettivo era quello di creare un liguaggio che fosse più ad oggetti di Python e con una sintassi per lo scripting più fluida di quella di Perl.

Le Influenze

Le influenze di Ruby vanno ricercate in: Perl , Python , Smalltalk e Lisp .

http://en.wikipedia.org/wiki/Ruby_programming_language

Le Implementazioni

Non esiste una specifica ufficiale del linguaggio, ma l’implementazione in C viene considerato lo standard de facto.

Altre implementazioni sono YARV , JRuby , Rubinius , IronRuby , e MacRuby .

Ruby 1.9 e Ruby 2.0 sono basate su YARV

Le Caratteristiche

  • Object-oriented, “everything is an object”
  • Six levels of variable scope: global, class, class instance, instance, local, and block
  • Exception handling
  • Iterators and closures (based on passing blocks of code)
  • Native, Perl-like regular expressions at the language level
  • Operator overloading
  • Automatic garbage collecting
  • Highly portable
  • Cooperative multi-threading on all platforms using green threads
  • DLL/Shared library dynamic loading on most platforms
  • Introspection, reflection and metaprogramming
  • Large standard library
  • Supports dependency injection
  • Supports object runtime alteration
  • Continuations and generators

From Wikipedia

I Paradigmi

Ruby consente la convivenza di diversi paradigmi di programmazione:

  • Shell Scripting
  • Procedurale
  • Orientato agli oggetti
  • Funzionale (Closure, Funzioni Anonime)
  • Metaprogrammazione (Introspezione, Reflection e Open Classes)

Interactive Ruby

IRB è una Shell di comando Ruby completa di command line editing e command history.

La shell IRB è utile per sperimentazione, debugging e sviluppo iterativo.

rpl@ubik:~$ irb
irb(main):001:0> puts "Hello World"
Hello World
=> nil
irb(main):002:0> def sum(a1,a2)
irb(main):003:1> a1+a2
irb(main):004:1> end
=> nil
irb(main):005:0> sum(5,6)
=> 11
irb(main):006:0> 

Base Ruby

  • Regole di sintassi base
  • Tipi di dati
  • Strutture di controllo
  • OOP
  • Iterator e Closure

Regole di sintassi base

Regole Generali di sintassi

Regole di sintassi base

Parole chiave riservate

Ruby fa uso di un ristretto numero di parole riservate:

alias   and     BEGIN   begin   break   case    class   def     defined 
do      else    elsif   END     end     ensure  false   for     if 
in      module  next    nil     not     or      redo    rescue  retry 
return  self    super   then    true    undef   unless  until   when 
while   yield

From http://spec.ruby-doc.org/wiki/Quickref

Regole di sintassi base: Variabili

Le variabili sono identificatori che iniziano con una lettera o un underscore (i successivi caratteri possono contenere anche i numeri da 0 a 9).

Le variabili possono essere dotate di un prefisso ($, @ o @@) che ne determina lo scope (visibilità).

$ARGV
$1
$MY_GLOBAL
@my_instance_var
@filename
@@my_class_var
@@counter
@@doc

Regole di sintassi base: Costanti

Le costanti sono identificatori che iniziano con una lettera maiuscola.

Le costanti vengono utilizzate per identificare valori che non dovrebbero cambiare una volta assegnati.

I nome delle Classi e i dei Moduli sono identificati da costanti.

NOTA: Ruby non ne impedisce la modifica ma la segnala con un warning solo se ad essere modificata è la costante stessa e non un attributo dell’oggetto referenziato.

Kernel
Object
MyClass
Array
Fixnum

Regole di sintassi base: Literals – Fixnum

I numeri interi con segno (incapsulati nella classe Fixnum ) si rappresentano con le seguenti sintassi:

0
1234
-576
+3
0xabcd # Hex
0xab_cd # Hex with embedded underscore
0xABCD # Caps are ok
-0xabcd # Negative hex
0177 # Octal
0_1_77 # Octal with embedded underscores
-0177 # Negative octal
0b10101 # Binary
-0b10101 # Negative binary
?a # character code for 'a' character

Regole di sintassi base: Literals – Float

I numeri in virgola mobile (64-bit IEEE floating point numbers) sono incapsulati nella classe Float e rispettano la seguente sintassi:

123.456
3.141_592_653_589_793_238_462
-4.5
6.5e3
-.4_5_6e10

Regole di sintassi base: Literals – Bignum

I Bignum rappresentano interi con segno di dimensione arbitraria (incapsulati nella classe Bignum) e rispettano la seguente sintassi:

112233445566778899112233445566778899
-999999999999999999999999999999999999
111_222_333_444_555_666_777_888_999_000
1_1_2_3_5_8_13_21_34_55_89_144_233_377
0xaaaaaaaaaaaaaaaaaaaaaaaaa
077777777777777777777777777777777777777777

Regole di sintassi base: Literals – String

Le stinghe rappresentano sequenze di caratteri (incapsule in classi String ) e rispettano le seguenti sintassi:

"Double quoted string"
'Single quoted string'
"Double quoted can embed Ruby code using #{do something} notation"
"Double quoted can even embed #{"double quoted strings"} inside the embedded code notation"
%q(This is a single-quoted string)
%Q(This is a double-quoted string)

Regole di sintassi base: Literals – String Block

x = <<END_OF_STRING
All this content will
be part of the string
with newlines intact.
HERE strings are
double quoted by default.
END_OF_STRING

y = <<-'SINGLE_QUOTED'
  Specify single-quoted HERE strings in this way.
  SINGLE_QUOTED

Regole di sintassi base: Literals – Symbol

I simboli ( Symbol ) sono rappresentati da sequenze di caratteri (per lo più alfanumerici ma con la possibilità di utilizzare alcuni caratteri speciali) preceduti dal carattere “:”

:this_is_a_symbol
:"and so is this"
:+ # some non-alphanumeric characters are allowed, 
   # primarily for the purpose of defining overloaded operators

A runtime l’interprete garantisce l’unicità dei simboli assicurandoci che due simboli composti dallo stessi caratteri siano lo stesso oggetto.

Regole di sintassi base: Literals – Range

I Range rappresentano gli estremi di una sequenza di numeri interi o stringhe e rispettano le seguenti sintassi:

1..5
'abb'..'abz'
(1..5)
('abb'..'abz')
1...11               # equivalente a 1..10

Regole di sintassi base: Literals – Regexp

I Regexp rappresentano una Regular Expression e rispettano le seguenti sintassi:

/^a.*b$/
%r{^a.*b$}
/[a-zA-Z].*/
/^[a-z][0-9].*/

NOTA : Per approfondire l’argomento Regular Expression consultare la relativa pagina di Wikipedia

Tipi di dati

Everything IS an Object

In Ruby tutto è un oggetto, quindi è lecito immaginare che tutti i tipi dati siano costituiti da classi che ereditano da Object.

Tipi di dati – Fixnum

Anche i numeri sono in realtà oggetti… e come tutti gli oggetti possono avere dei metodi:

10.class => "Fixnum"
-10.abs => 10         # in molti linguaggi sarebbe stato abs(-10)
10.next => 11
10.even? => true
10.odd? => false
10.+(3) = 13          # o più semplicemente 10 + 3 = 13
10.times { puts "Hello" }
10.to_s => "10"

NOTA : Nelle classi standard Ruby vendono utilizzati i caratteri “?” e “!” nei nomi di alcuni metodi secondo la seguente convenzione:

Tipi di dati – String

Analogamente ai numeri anche le stringhe in realtà sono oggetti dotati dei propri metodi di utilità:

"Hello".length => 5
"Hello".upcase => "HELLO"
"Hello".downcase => "hello"
"1,2,3".split(",") => ["1", "2", "3"]
"".empty? => true
"1,2,3".include?("4") => false
"1,2,3".+(",4")                        # o più semplicemente "1,2,3"+",4"
"10".to_i => 10
"Hello".to_i => 0
"10H".to_i => 10
"H10".to_i => 0

Tipi di dati – Range

I Range rappresentano degli intervalli in modo compatto e al tempo stesso orientato agli oggetti:

(1..5).to_a => [1, 2, 3, 4, 5]
(1..5).max => 5
(1...5).max => 4
(4..12).min => 4
(10..100).include?(50) => true
(10..100) === 5 => true
(10..100) === 150 => false
('a'..'z') === c => true

Ruby è in grado di gestire anche sequenze di oggetti non nativi, a patto che siano comparabili (che implementino l’operatore <=> ) e che rispondano al metodo succ ritornando l’elemento successivo della sequenza

MyClass.new(1)..MyClass.new(100)

Tipi di dati: RegExp

Le Regular Expression consentono di esprimere in maniera compatta regole di analisi del testo:

/[a-z]/ =~ "prova" => 0
/[a-z]/ =~ "1prova" => 1
/[a-z]/.match("prova") => #<MatchData "p">
/[a-z]/.match("1prova").to_s => 'p'
/[a-z]/.match("1prova").post_match => 'rova'
/[a-z]/.match("1prova").pre_match => '1'

def show_regexp(a, re)
  if a =~ re
    "#{$`}<<#{$&}>>#{$'}"
  else
    "no match"
  end
end

Tipi di dati – Array

Gli array sono collezioni di oggetti con indice intero, a loro volta sono costituiti da istanze della classe Array:

a = [0, 1, 5]
b = ['abc', 15, nil]
c = %w{ dog cat tiger } => [ 'dog', 'cat', 'tiger' ]
a[0] => 0
b[2] => nil
a[5] => nil            # ATT!!! indici fuori lunghezza ritornano nil
a => [0, 1 5]
[0, 1, 5].at(0) => 0
%w{ dog cat tiger }.member?('dog') => true
c.member?('panther') => false
c.sort! => ["cat", "dog", "tiger"]
c.slice(1..2) => ["dog", "tiger"]

Tipi di dati – Hash

Gli hash (o dizionari) sono collezioni di oggetti indicizzati da una chiave di tipo arbitrario (un qualsiasi oggetto):

dict1 = { 'key1' => 'value1', 'key2' => 'value2' }
dict2 = { :key1 => 'value1', :key2 => 'value2' }
dict1['key1'] => 'value1'
dict2[:key2] => 'value2'
dict1[:key1] => nil
dict1[:key1.to_s] => 'value1'
dict2["key1".to_sym] => 'value1'
dict1.size => 2
dict1.merge(dict2) => { :key1 => 'value1', :key2 => 'value2',
                        'key1' => 'value1', 'key2' => 'value2' }
dict1.keys => [ "key1", "key2" ]
dict1.each { |key, value| puts "#{key}: #{value}" }

Strutture di Controllo

Everything IS an Expression

In Ruby tutto è una espressione, cioè anche le strutture di controllo ( if , while , for , case ) ritornano un valore.

Strutture di Controllo – if (1/2)

res = if condition1 then
  block1
elsif condition2 then
  block2
else
  block3
end

action if condition1

mon, day, year = $1, $2, $3 if date =~ /(\d\d)-(\d\d)-(\d\d)/

Strutture di Controllo – if (2/2)

res = unless condition1 then
  block1
else
  block2
end

action unless condition1

print total unless total.zero?

value = current > 100 ? true : false

Strutture di Controllo – case (1/3)

str = case
       when year >= 2000:
         "maggiore/uguale di duemila"
       when year < 2000:
         "minore di duemila"
       end

Strutture di Controllo – case (2/3)

str = case year
       when 1..2000:
         "minore/uguale di duemila"
       when 2001..65536:
         "maggiore di duemila"
       end

NOTA: In questa forma Ruby confronterà la variabile year con gli argomenti di when mediante l’operatore ===

Strutture di Controllo – case (3/3)

case input_line
when "help"
  puts "help blah blah..."
when "cmd1"
  puts "elaborating cmd1..."
  puts "done"
when /^info\s+(\w+)/
  puts "info: #{$1}"
when "quit", "exit"
  exit
else
  puts "Unknown input: #{input_line}"
end

Strutture di Controllo – while (1/2)

while line = gets
  case line
  when /^help$/
    puts "help..."
  when /^info$/
    puts "info..."
  else
    puts "Unknown input: #{line}"
  end
end

Strutture di Controllo – while (1/2)

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

a = 1
a *= 2 while a < 100
a -= 10 until a < 100

NOTA : gets inserisce un \n alla fine di ogni stringa letta

Strutture di Controllo – for

for i in ['fee', 'fi', 'fo', 'fum']
  print i, " "
end

for i in 1..3
  print i, " "
end

for k,v in { :k1 => 10, :k2 => 20 }
  puts "#{k}: #{v}"
end

Strutture di Controllo – loop, break, next, retry

i=0
loop do
  i += 1
  next if i < 3
  print i
  break if i > 4
end

for i in 1..100
  print "Now at #{i}. Restart? "
  retry if gets =~ /^y/i
end

Hello, World! (1/3)

#!/bin/env ruby

puts "Hello, World!"

Hello, World! (2/3)

$ ruby hello.rb
Hello, World!
$ chmod a+x hello.rb
$ ./hello.rb
Hello, World!

Hello, World! (3/3)

def say_hello(word)
  puts "Hello, #{word}"
end

say_hello("World");
say_hello("Moon");

NOTA: il valore ritornato dall’ultima espressione contenuta in un metodo costituirà il valore di ritorno del metodo (il return esplicito è opzionale)

OOP – Definire una Classe (1/2)

class MyClass
  def initialize
    puts "init"
  end
end

OOP – Definire una Classe (2/2)

class MyClass
    def initialize
        ObjectSpace.define_finalizer(self,
                                     self.class.method(:finalize).to_proc)
    end
    def MyClass.finalize(id)
        puts "Object #{id} dying at #{Time.new}"
    end
end

# test code
3.times {
    MyClass.new
}
ObjectSpace.garbage_collect

OOP – attributi, variabili di classe e di istanza

class MyClass
  @@class_var = "class var value"

  attr_accessor :rw_instance_var
  attr_reader :ro_instance_var
  attr_writer :wo_instance_var

  def initialize
    @rw_instance_var = "test value"
  end
end

OOP – ereditarietà

class MyChildClass < MyClass
end

mcc = MyChildClass.new
mcc.rw_instance_var

OOP – Class Methods

class MyClass
  def MyClass.test_class_method1
    puts "test_class_method1"
  end

  def self.test_class_method2
    puts "test_class_method2"
  end

  class << self
    def test_class_method3
      puts "test_class_method3"
    end 
  end
end

MyClass.test_class_method1
MyClass.test_class_method2
MyClass.test_class_method3
...

OOP – Access Restriction

class A
   protected
   def protected_method
   end

   private
   def private_method
   end
end

class B < A
   public
   def test_protected
     myA = A.new
     myA.protected_method
   end
 end
 b = B.new.test_protected

Iteratori e Closure (1/5)

Cos’e’ una closure?

Il concetto di Closure (“chiusura”) deriva da Scheme e dalla programmazione funzionale in generale.

Consiste in una funzione (o blocco di codice) che porta con se le variabili raggiungibili dallo scope in cui è stata dichiarata oltre al codice che la compone.

La possibilità di definire Closure nei vari linguaggi di programmazione richiede che siano supportate le funzioni come First-class-object e viene spesso associato alla possibilità di definire funzioni anonime

From Wikipedia

Iteratori e Closure (2/5)

A cosa servono le closure?

L’uso delle Closure è molto simile a quello delle First class function in generale e vengono spesso utilizzate per:

Le Closure in Ruby sono utilizzate soprattutto per gli iteratori e sono alla base dell’implementazione in Ruby dei DSL (Domain Specific Language) interni come Ruby On Rails

Iteratori e Closure (3/5)

[1,2,3,4].each do |i|
  puts i
end

{:p1 => "v1", :p2 => "v2"}.each do |k,v|
  puts "#{k}: #{v}"
end

["a", "b", "c"].each { |i| puts i }

[1,2,3,4].select { |i| i % 2 == 0 }

[1,2,3,4].map { |i| i * 2 }

def mult_forge(n)
  lambda { |i| i*n }
end

double = mult_forge(2)
[1,2,3,4].map &double

Iteratori e Closure (4/5)

class Array
  def each_odd
    self.each do |elem|
      yield elem if elem.odd?
    end
  end
end

[1,2,3].each_odd { |i| puts i }

Iteratori e Closure (5/5)

class Array
  def each_odd(&blk)
    self.each do |elem|
      blk.call(elem) if elem.odd?
    end
  end
end

[1,2,3].each_odd { |i| puts i }

Ruby Advanced

  • Ruby Standard Library e require
  • Moduli e Mixin
  • Exception Handling
  • OOP – Classi Aperte e Metaprogrammazione

Ruby Standard Library e require

Ruby è dotato di una completissima libreria di classi standard:

Per includere una libreria si utilizza il metodo require

require 'fileutils'

FileUtils.mkdir("/tmp/test")
FileUtils.mkdir("/tmp/test/test1")

tmp = Dir.open("/tmp/test").each { |i| puts i }

Moduli e Mixin (1/2)

I Moduli in Ruby svolgono sia la funzione di Namespace che di Mixin per l’inclusione di blocchi di codice nel contesto dell’oggetto corrente.

module MyNamespace
  class MyClass
  end
end

MyClass # NameError: uninitialized constant Test

MyNamespace
MyNamespace::MyClass

Moduli e Mixin (2/2)

module MyMixin
  def method_mm1
  end
end

class MyClass
  include MyMixin
  def method1
  end
end

class OtherClass
  extend MyMixin
  def method1
  end
end

MyClass.instance_methods(false)
MyClass.instance_methods()
OtherClass.singleton_methods()
OtherClass.instance_methods(false)

Exception Handling

begin
  try_a_method
rescue NameError => e 
  puts e
else
  puts "A different exception"
ensure
  puts "ensure block"
end

def testmethod
  try_a_method
  raise "error"
rescue NameError => e
  puts "try_a_method don't exist"
end

testmethod

def try_a_method
end

testmethod

OOP – Classi Aperte (1/2)

In Ruby, a differenze di linguaggi ad oggetti diffusi come C++ e Java, le classi non sono chiuse dopo la loro prima dichiarazione ma possono essere riaperte a Runtime in qualsiasi momento allo scopo di aggiungere del nuovo codice.

Questa caratteristica di Ruby affonda le radici nel passato: Smalltalk.

class MyClass
  @@class_var = "test_val"

  def MyClass.class_var
    @@class_var
  end
end

class MyClass
  def self.class_var=(new_value)
    @@class_var = new_value
  end
end

OOP – Classi Aperte (2/2)

Anche le classi contenute nella Ruby Standard Library (String, Fixnum, Range etc.) possono essere riaperte a Runtime.

class String
  def open
    File.open(self,"r")
  end
end

puts "/etc/lsb-release".open.read

OOP – Duck Typing

Cos’è il Duck Tiping (anche detto Duck Test)?

If it walks like a duck and quacks like a duck, I would call it a duck.

In other words, don’t check whether it IS-a duck: check whether it QUACKS-like-a duck, WALKS-like-a duck, etc, etc, depending on exactly what subset of duck-like behaviour you need to play your language-games with.

Wikipedia

OOP – Metaprogrammazione

Cos’e’ la Metaprogrammazione?

Le tecniche di metaprogrammazione e le caratteristiche dei linguaggi che ne consentano l’implementazione sono diverse:

  • La reflection
  • Un linguaggio manipolabile come tipo dato nativo ( First class object )
  • La possibilità di valutare a runtime codice in forma di stringa ( runtime evaluate )
  • Le classi aperte

La Metaprogrammazione consiste nella possibilità di scrivere software che genera altro software, lo manipola come se fossero dati, o di eseguire a runtime task eseguiti di solito in compile time

Tecniche di metaprogrammazione avanzate erano presenti in linguaggi come Lisp/Scheme e Smalltalk .

From Wikipedia

OOP – Metaprogrammazione: Reflection 1/3

ObjectSpace.each_object { |o| puts o if o.class == Class }
ObjectSpace.each_object(Class) { |o| puts o }

10.methods
10.respond_to?(:to_s)
10.id
10.class => Fixnum
10.kind_of? Fixnum  => true
10.kind_of? Numeric => true
10.instance_of? Fixnum => true
10.instance_of? Numeric => false
Fixnum.class
Fixnum.superclass
Fixnum.ancestors
Fixnum.included_modules
Fixnum.include?(Comparable)

OOP – Metaprogrammazione: Reflection 2/3

class MyClass
  A_CONST = "const value"
  @@a_var = "class var value"

  private
    def method1; puts "private method"; end
  protected
    def method2; puts "protected method"; end
  public
    def method3
      @an_other_var = "instance var value"
      local1, local2 = 1, 2
      local_variables
    end
  def self.method4
  end
end

OOP – Metaprogrammazione: Reflection 3/3

MyClass.instance_methods
MyClass.instance_methods(false)
MyClass.public_instance_methods(false)
MyClass.protected_instance_methods(false)
MyClass.private_instance_methods(false)
MyClass.singleton_methods(false)
MyClass.class_variables
MyClass.constants
MyClass.method_defined?(:method3)
MyClass.respond_to?(:method3)
mc1 = MyClass.new
mc1.respond_to?(:method3)
mc1.instance_variables
mc1.method3
mc1.instance_variables
...

OOP – Metaprogrammazione: Dynamic method calling

10.send(:to_s)
"10,20".send("split",",")

split = "10,20".method("split")
split.call(",")
split.call("0")

def double(x); 2*x; end
dbl = method(:double)
[1,2,3].collect(&dbl)

def a_method
  local_val = "local value"
  binding
end
local_val = "different scope"
method_binding = a_method
eval("local_val", method_binding)
eval("local_val")

OOP – Metaprogrammazione: Code Injection e Code Generation (1/2)

# definizione di metodi di classe (espressione valutata nel contesto nell'oggetto singleton di tipo Class)
#    MyClass.instance_eval 

MyClass.instance_eval { def method1; end}
MyClass.instance_methods(false)
MyClass.singleton_methods

# definizione di metodi di istanza metodi di istanza (espressione valutata nel contesto della Classe)
#    MyClass.class_eval | MyClass.module_eval      

MyClass.class_eval { def method2; end}
MyClass.instance_methods(false)
MyClass.singleton_methods

class MyClass
  #metodi di istanza
  class << self
    #metodi di classe
  end
end

OOP – Metaprogrammazione: Code Injection e Code Generation (2/2)

# Definizione di una versione localizzata (e semplificata) di attr)
#

Module.class_eval do
  def attributo(name)
    define_method(name) do
      instance_variable_get('@'+name.to_s)
    end
    define_method(name.to_s+"=") do |value|
      instance_variable_set('@'+name.to_s,value)
    end
  end
end

class MyClass
  attributo :test
end

MyClass.instance_methods
...

OOP – Metaprogrammazione: Method Missing

class MyClass  
  def method_missing(m, *args)  
    puts "WARN: no method #{m}!!!"  
  end  
end  

MyClass.new.everything_you_want
...

Altri argomenti

TestUnit / RSpec

RubyDoc

RubyGems

Ruby Debugger e ruby-debugger

Bibliografia

Programming in Ruby 2nd Edition (The Pragmatic Programmers)

The Ruby Way (Sams Publishing)

The Ruby Programming Language (O’Reilly)

Linkografia

Copyright (C) 2008 - Alca Societa' Cooperativa

http://alca.le.it - info@alca.le.it

released under CreativeCommons 2.5 by-nc-sa

NOTA: le immagini dei software e dei device contenuti
nella presentazione sono proprieta' dei relativi detentori
del copyright e sono state riprodotte a scopo esclusivamente didattico.