Ruby para programadores PHP


Leia em 8 minutos

A maneira mais fácil de se aprender uma nova linguagem é comparando-a com uma linguagem que você já conhece. Pelo menos é assim que faço! Para os que são como eu, aqui vai uma série de comparações/equivalências entre as linguagens Ruby e PHP.

Estruturas de controle

Condicionais

# php
# setting some variables
$n1 = 1;
$n2 = 2;
$n3 = 3;

# if condition
if ($n1 > $n2)
  echo 'n1 > n2',"\n";
elseif ($n1 > $n3)
  echo 'n1 > n3', "\n";
else
  echo 'n1 =< n2; n1 =< n3', "\n";

# one-line if
echo n3 > n2? "n3 > n2\n" : "n3 <= n2\n";

# switch..case
switch($n1) {
    case 1:
        echo "n1 = 1", "\n";
        break;
    case 2:
        echo "n1 = 2", "\n";
        break;
    default:
        echo "n1 = {$n1}", "\n";
}
# ruby
# setting some variables
n1 = 1
n2 = 2
n3 = 3

# if condition
if n1 > n2
  puts 'n1 > n2'
elsif n1 > n3
  puts 'n1 > n3'
else
  puts 'n1 =< n2; n1 =< n3'
end

# one-line if
puts n3 > n2 ? 'n3 > n2' : 'n3 <= n2'

# unless condition
unless n3 > n2
  puts 'n3 > n2'
end

# different structure
puts 'n1 > n3' if n1 > n3
puts 'n1 =< n2' unless n1 > n2

# switch..case
case n1
  when 1
    puts 'n1 = 1'
  when 2
    puts 'n1 = 2'
  else
    puts 'n1 = %d' % n1
end

Estruturas de repetição

# php
# while
$i = 1;
while ($i < 5) {
  echo $i, "\n";
  $i += 1;
}

# do..while
$i = 1;
do {
    echo $i, "\n";
    $i += 1;
} while ($i < 5);

# for i++
for ($i = 1; $i <= 5; $i++) {
    echo $i, "\n";
}

# for i--
for ($i = 5; $i >= 1; $i--) {
    echo $i, "\n";
}

# each
foreach (range(1,5) as $i) {
    echo $i, "\n";
}

# break/continue
$i = 0;
while ($i < 10) {
    $i++;

    # break
    if ($i == 9) {
        break;
    }

    # continue
    if ($i % 2 == 0) {
        continue;
    }

    echo $i, "\n";
}
# ruby
# while
i = 1
while i < 5
  puts i
  i += 1
end

# until
i = 1
until i > 5
  puts i
  i += 1
end

# loop
i = 1
loop do
  break if i > 5
  puts i
  i += 1
end

# do..while
i = 1
begin
  puts i
  i += 1
end while i < 5

# do..until
i = 1
begin
  puts i
  i += 1
end until i > 5

# upto (for i++)
1.upto(5) do |i|
  puts i
end

# downto (for i--)
5.downto(1) do |i|
  puts i
end

# for..in (foreach)
for i in (1..5)
  puts i
end

# each
(1..5).each do |i|
  puts i
end

# break/next
i = 0
while i < 10 do
    i += 1

    break if i == 9     # break
    next if i % 2 == 0  # next (continue)

    puts i
end

Strings

Particularmente, penso que o PHP é muito mais completo para se lidar com strings quando o assunto é biblioteca padrão. Na maioria das vezes você possui alternativas — estender a classe, por exemplo — mas é um trabalho que alguém terá que fazer.

Manipulando strings

# php
# interpolate variables
$text = "php";
echo "i love $text", "\n";
echo "i love {$text}", "\n";
printf("i love %s\n", $text);

# basic functions
$text = 'i love php';
echo strtolower('I LOVE PHP'), "\n";    # lowercase text
echo strtoupper($text), "\n";           # uppercase text
echo ucfirst($text), "\n";              # capitalize text
echo strlen($text), "\n";               # string size
echo strrev($text), "\n";               # reversing text
echo str_repeat("php ", 3), "\n";       # repeat text
echo ord("a"), "\n";                    # get ASCII value
echo chr("97"), "\n";                   # get char from ASCII value

# remove spaces
$text = '  i love php  ';
echo trim($text), "\n";     # both sides
echo ltrim($text), "\n";    # just left side
echo rtrim($text), "\n";    # just right side

# remove characters
$text = '__i love php__';
echo trim($text, '_'), "\n";     # both sides
echo ltrim($text, '_'), "\n";    # just left side
echo rtrim($text, '_'), "\n";    # just right side

# slicing strings
$text = 'i love php';
echo substr($text, 0, 6), "\n";
echo $text[0], "\n";

# search characters
echo strpos("php", "h"), "\n";  # find position
# ruby
# interpolate variables
text = "ruby"
puts "i love #{text}";
puts "i love %s" % text;
printf("i love %s\n", text);

# basic functions
text = 'i love ruby';
puts 'I LOVE RUBY'.downcase  # lowercase text
puts text.upcase            # uppercase text
puts text.capitalize        # capitalize text
puts text.size              # string size
puts text.reverse           # reversing text
puts "ruby " * 3            # repeat text
puts "a"[0]                 # get ASCII value
puts 97.chr                 # get char from ASCII value

# remove spaces
text = '  i love ruby  ';
puts text.strip     # both sides
puts text.lstrip    # just left side
puts text.rstrip    # just right side

# remove characters
# ruby has no easy way of doing that besides regular expressions
# so we're gonna extend String class
# check this link out: http://simplesideias.com.br/

text = '||__i love ruby__||'
puts text.trim('_|')  # both sides
puts text.ltrim('_|') # just left side
puts text.rtrim('_|') # just right side

# slicing strings
text = 'i love ruby';
puts text[0,6]
puts text[0,1]

# search characters
puts "ruby" =~ /y/  # find position

Gerando hashes com MD5 e SHA1

# php
echo md5("php"), "\n";
echo sha1("php");
# ruby
require 'digest/md5'
require 'digest/sha1'

puts Digest::MD5.hexdigest("ruby")
puts Digest::SHA1.hexdigest("ruby")

Expressões regulares

No PHP temos uma série de funções que lidam com expressões regulares como eregi, preg_match, preg_replace, dentre outras. O Ruby também tem métodos equivalentes:

preg_match vs match

Verificar se uma determinada string obedece um padrão

#php
if(preg_match("/^\d{2}\/\d{2}\/\d{4}$/", '01/01/2007')) {
    print 'valid format'
} else {
    print 'invalid format'
}
# ruby - method 1
if '01/01/2007' =~ /^\d{2}\/\d{2}\/\d{4}$/
  puts 'valid format'
else
  puts 'invalid format'
end

# ruby - method 2
if '01/01/2007'.match(/^\d{2}\/\d{2}\/\d{4}$/)
  puts 'valid format'
else
  puts 'invalid format'
end

Acessando os matches de uma expressão regular

# php
preg_match("/like/", "i like php", $matches);
print "match: " . $matches[0];
# ruby
matches = "i like ruby".match(/like/)
puts matches[0]

Atribuindo os matches de uma expressão regular à variáveis

# php
preg_match("/(\b\w+\b) (\b\w+\b)/", "nando vieira", $matches);
list($full_name, $first_name, $last_name) = $matches;

print $full_name . "\n";
print $first_name . "\n";
print $last_name;
# ruby
full_name, first_name, last_name = *"nando vieira".match(/(\b\w+\b) (\b\w+\b)/)

puts full_name
puts first_name
puts last_name

preg_replace vs gsub

Substituíndo padrões

# php
print preg_replace("/like/", "love", "i like php")
# ruby
puts "i like ruby".gsub(/like/, 'love')

Utilizando retrovisores (backreferences)

# php
print preg_replace("/(duck)/", "\$1 and one chicken", "one duck")
# ruby
puts "one duck".gsub(/(duck)/, "\1 and one chicken")

preg_match_all vs scan

# php
$text = "item 1\nitem 2\nitem 3";
preg_match_all("/item (\d+)/sim", $text, $matches);

print_r($matches[1]);
# ruby
text = "item 1\nitem 2\nitem 3"
matches = text.scan(/item (\d+)/)

puts matches

Array e Hash

Utilizando arrays

# php
$companies = array('apple', 'microsoft', 'red hat', 'canonical');

echo count($companies), "\n";       # how many items?
echo $companies[0], "\n";           # first item
echo end($companies), "\n";         # last item
echo (int)empty($companies), "\n";  # size > 0?

# sorting array
sort($companies);

# removing duplicate items
$companies = array_unique($companies);

# applying a function
$companies = array_map('strtoupper', $companies);

# reversing
$companies = array_reverse($companies);

# joining
echo join(", ", $companies), "\n";

# slicing
echo join(", ", array_slice($companies, 1, 3)), "\n";

# removing last item
echo array_pop($companies), "\n";

# removing first item
echo array_shift($companies), "\n";

# adding item at the end
array_push($companies, 'ADOBE');

# adding item at the beginning
array_unshift($companies, 'NOVELL');
# ruby
companies = ['apple', 'microsoft', 'red hat', 'canonical']

puts companies.size    # how many items?
puts companies.first   # first item
puts companies.last    # last item
puts companies.empty?  # size > 0?

# sorting array
companies.sort!

# removing duplicate items
companies.uniq!

# applying a function
companies.map! { |company| company.upcase }

# reversing
companies.reverse!

# joining
puts companies.join(", ")

# slicing
puts companies[1..3].join(", ")
puts companies.slice(1, 3).join(", ")

# removing last item
puts companies.pop

# removing first item
puts companies.shift

# adding item at the end
companies.push 'ADOBE'

# adding item at the beginning
companies.unshift 'NOVELL'

Arrays associativos

No Ruby, os arrays associativos são definidos como Hash e tem sua sintaxe ligeiramente diferente. Vale lembrar que, ao iterar um hash, os valores apresentados geralmente não os mesmos da ordem de inserção.

# php
$companies = array(
    'apple' => 'mac os x',
    'microsoft' => 'windows vista',
    'canonical' => 'ubuntu'
);

echo $companies['apple'], "\n";

# getting the keys
echo join(", ", array_keys($companies)), "\n";

# iterating through array
foreach ($companies as $company => $so) {
    printf("%s: %s\n", $company, $so);
}

# removing a item
unset($companies['microsoft']);

# checking if array has a key
if (!array_key_exists('novell', $companies)) {
    echo "Novell not found\n";
}
# ruby
companies = {
    'apple' => 'mac os x',
    'microsoft' => 'windows vista',
    'canonical' => 'ubuntu'
}

puts companies['apple']

# getting the keys
puts companies.keys.join(", ")

# iterating through array
companies.each do |company, so|
    puts "%s: %s" % [company, so]
end

# removing a item
companies.delete('microsoft')

# checking if array has a key
puts 'Novell not found' if not companies.key?('novell')

Arquivos e diretórios

Arquivos

Criar novo novo arquivo e adicionar conteúdo

# php
$file = fopen('newfile.txt', 'w+');
fwrite($file, str_repeat("i love php\n", 10));
fclose($file);
# ruby
file = File.new('newfile.txt', 'w+')
file << "i love ruby\n" * 10
file.close

Lendo arquivos

# php
$file = fopen('newfile.txt', 'r');
echo fwrite($file, ('newfile.txt')), "\n";
fclose($file);
# ruby
file = File.new('newfile.txt')
puts file.read
file.close

Iterando nas linhas do arquivo

# php
$lines = file('newfile.txt');
foreach ($lines as $num => $line) {
    echo $num+1, ': ', $line;
}
# ruby
file = File.new('newfile.txt')
file.each_with_index do |line, num|
  puts "%d: %s" % [num+1, line]
end
file.close

Outras funções

# php
# check if file exists
echo (int)file_exists('newfile.txt'), "\n";

# print file size in bytes
echo filesize('newfile.txt'), "\n";

# write permission?
echo (int)is_writable('newfile.txt'), "\n";

# read permission?
echo (int)is_readable('newfile.txt'), "\n";

# is file?
echo (int)is_file('newfile.txt'), "\n";

# remove file
unlink('newfile.txt');
# ruby
# check if file exists
puts File.exists?('newfile.txt')

# print file size in bytes
puts File.size('newfile.txt')

# write permission?
puts File.writable?('newfile.txt')

# read permission?
puts File.readable?('newfile.txt')

# is file?
puts File.file?('newfile.txt')

# remove file
File.unlink('newfile.txt')

Diretórios

# php
# creating new directory
mkdir('newdir');

# listing files
$dir = opendir("newdir");

while (false !== ($entry = readdir($dir))) {
    echo $entry, "\n";
}
closedir($dir);

# directory?
echo (int)is_dir('newdir'), "\n";

# remove non-empty directory
# php have no such function
# check this link out for alternatives
# http://php.net/rmdir

# remove empty directory
rmdir('newdir');
# ruby
# creating new directory
dir = Dir.mkdir('newdir')
file = File.new('newdir/newfile.txt', 'w+')

# listing files
dir = Dir.new('newdir')
dir.entries.each do |entry|
  puts entry
end

# directory?
puts File.directory?('newdir')

# remove non-empty directory
require 'fileutils'
FileUtils.rm_rf('newdir')

# remove empty directory
Dir.mkdir('newdir')
Dir.rmdir('newdir')

Classes

# php
class Vehicle {
    # read/write attribute
    public $name;

    # private attribute
    private $color;

    # constructor
    function __construct($name)
    {
        $this->name = $name;
    }

    # setter
    function setColor($colorName)
    {
        $this->color = $colorName;
    }

    # getter
    function getColor()
    {
        return $this->color;
    }

    # static
    function getWheels()
    {
        return 0;
    }
}

class Car extends Vehicle {
    function __construct($name)
    {
        parent::__construct($name);
    }

    function getWheels()
    {
        return 4;
    }
}

$car = new Car('Stilo');
echo $car->name, "\n";      # name attribute
echo get_class($car), "\n"; # object class

$car->setColor('graffiti');
echo $car->getColor(), "\n";

echo Car::getWheels(), "\n"; # static method
# ruby
class Vehicle
  # read/write attribute
  attr_accessor :name

  # class variable
  @@wheels = 0

  # contructor
  def initialize(name)
    @name = name
  end

  # color getter
  def color
    @color
  end

  # color setter
  def color=(color_name)
    @color = color_name
  end

  def self.wheels
    @@wheels
  end
end

class Car < Vehicle
  @@wheels = 4

  def initialize(name)
    super(name)
  end
end

car = Car.new('Stilo')
puts car.name   # name attribute
puts car.class  # object class

car.color = 'yellow'
puts car.color

puts Car::wheels  # class method (static method on PHP)

Lendo páginas da web (scraping)

Processando XML

# php
$curl = curl_init('http://del.icio.us/rss/fnando');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($curl);

$xml = new SimpleXMLElement($response);

foreach ($xml->item as $item) {
    $title = $item->title;
    $link = $item->link;

    printf("%s\n%s\n\n", $title, $link);
}
# ruby
require 'net/http'
require 'uri'
require 'rexml/document'

url = URI.parse('http://del.icio.us/rss/fnando')
response = Net::HTTP.get(url)
xml = REXML::Document.new(response)

xml.root.elements.to_a('//item').each do |item|
  title = item.elements['title'].text
  link = item.elements['link'].text

  puts "%s\n%s\n\n" % [title, link]
end

Processando HTML

# php
$curl = curl_init('http://www.google.com/trends/music');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($curl);

preg_match_all("/<a.*?href=\".*?&res=artist.*?>(.*?)<\/a>/sim", $response, $matches);

echo join("\n", array_unique($matches[1])), "\n";
require 'net/http'
require 'uri'
require 'rexml/document'

url = URI.parse('http://www.google.com/trends/music')
response = Net::HTTP.get(url)
regex = /<a.*?href=".*?&res=artist.*?>(.*?)<\/a>/sim

puts response.scan(regex).uniq.join("\n")

Se você acha que eu deveria ter abordado algum outro conjunto de funções, avise-me e atualizarei o artigo. Divirta-se!

Update: Adicionei estruturas de controle