Tag: ruby

  • Viaggiatreno Ruby gem: version 1.0.5

    On the background of all my activities, I continued the developing of viaggiatreno, a Ruby gem to parse Italian railway real-time system information of the status of the various trains: location, delay, expected and real arrival time. Yesterday I released the version 1.0.5 of this gem, which improves the overall code quality of this gem (0 rubocops violations!):

    viaggiatreno gem source code is hosted on GitHub.

    Simple usage of this gem has been included in the README.

    Another interesting fact that I dealt while writing continuous integration test (with Travis) in the development of this gem: as it fetches and parses information from Internet URLs, one way of writing RSpec tests is to “mock” the remote web part.

    Using vcr gem, I mocked the response of the remote web server in order to write static test-cases (since the informations on train change over time, subsequent web requests did not fetch the same results).

  • Ruby e OSX: problemi coi certificati SSL durante l’installazione delle gem

    Nella nuova versione dell’installer di RubyGems è presente un check di sicurezza sul certificato SSL del sito da cui si scaricano le gem che si stanno per installare.
    Questo può comportare un errore durante l’installazione di una qualsiasi gem:

    ERROR: Could not find a valid gem 'rails' (= 3.2.14), here is why: Unable to download data from https://rubygems.org/ - SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
    

    Sono disponibili due strade per risolvere il problema:

    • creare un file ~/.gemrc contentente :ssl_verify_mode: 0. Questa soluzione, oltre a non essere dipendente dall’utente con cui si installano le gems, è fortemente sconsigliata: infatti, disabilita il controllo del certificato SSL del sito da cui si prelevano le gems, con immaginabili problematiche di security. Usate questa soluzione solo in ambiente di test.
    • importare il certificato system-wide:
      	cert_file=$(ruby -ropenssl -e 'puts OpenSSL::X509::DEFAULT_CERT_FILE')
      security find-certificate -a -p /Library/Keychains/System.keychain > "$cert_file"
      security find-certificate -a -p /System/Library/Keychains/SystemRootCertificates.keychain >> "$cert_file"

      	
  • FizzBuzz reloaded: le differenze tra Java e Ruby

    Tempo fa vi ho parlato di FizzBuzz, un quiz spesso posto ai programmatori alle prime armi.

    Una variante è la seguente:

    Sommare tutti i numeri da 1 a 200 che non sono multipli di 4 e di 7

    La parte divertente sta nella differenza di espressività tra Java e Ruby per ottenere lo stesso risultato. In Java, infatti:

    public class FizzBuzzReloaded {
        public static void main(String[] args) {
            int sum = 0;
    
            for (int i = 1; i <= 200; i++) {
                if (i % 4 != 0 && i % 7 != 0) {
                                 sum += i;
                }
            }
            System.out.println(sum);
        }
    }
    

    In Ruby, invece, grazie alla funzionalità del linguaggio, possiamo semplicemente scrivere:

    (1..200).select {|n| n % 4 != 0 and n % 7 != 0}.inject(:+)
    
  • viaggiatreno-scraper: una libreria Ruby per accedere a viaggiatreno/trenitalia

    Un po’ per divertimento e un po’ perché volevo approfondire la mia conoscenza di Ruby e delle regex, oltre che delle espressioni XPath, ho deciso di rilasciare una libreria opensource Ruby che ho creato. Permette infatti di accedere ai dati di viaggiatreno che espone gli orari dei treni di Trenitalia in tempo reale.

    Come funziona?

    Un paio di anni fa avevo scritto un parser analogo in Python, ma molto meno robusto [proprio perché non utilizzavo le espressioni XPath e le regex]. Il nuovo scraper, scritto in Ruby con l’ausilio di nokogiri, è molto semplice da utilizzare; un esempio:

    irb(main):004:0> require './src/Train.rb'
    irb(main):005:0> train = Train.new(5033) # numero identificativo del treno (vedi Trenitalia.it)                               
    => 5033 (REG 5033): Il treno e' arrivato con 8 minuti di ritardo 
    (state: FINISHED, delay: 8, lastUpdate: )
    irb(main):007:0> train.status
    => "Il treno e' arrivato con 8 minuti di ritardo"
    irb(main):008:0> train.delay
    => 8
    irb(main):009:0> train.lastStop
    => "[X] BERGAMO = SCHEDULED: 07:46 ACTUAL: 07:53\n"
    irb(main):010:0> train.trainStops
    => [[X] LECCO = SCHEDULED: 07:03 ACTUAL: 07:04
    , [X] VERCURAGO S.GIROLAMO = SCHEDULED: 07:08 ACTUAL: 07:08
    , [X] CALOLZIOCORTE OLGINATE = SCHEDULED: 07:11 ACTUAL: 07:11
    , [X] CISANO CAPRINO BERGAMASCO = SCHEDULED: 07:22 ACTUAL: 07:21
    , [X] PONTIDA = SCHEDULED: 07:26 ACTUAL: 07:32
    , [X] AMBIVERE MAPELLO = SCHEDULED: 07:31 ACTUAL: 07:36
    , [X] PONTE S.PIETRO = SCHEDULED: 07:38 ACTUAL: 07:43
    , [X] BERGAMO = SCHEDULED: 07:46 ACTUAL: 07:53
    ]
    

    Di conseguenza, un programma alert per venire avvisati via sms (tramite tinsms) solo quando il treno è in ritardo per più di 10 minuti, o quando il treno non è ancora partito (scioperi, malfunzionamenti, etc.), diventa:

    require_relative 'src/Train.rb'
    require_relative '../sms-utils/sendsms.rb'
    
    RECIPIENT = 'num_tel'
    log = Logger.new('log/viaggiatreno-alert.log')
    log.level = Logger::DEBUG
    
    train = Train.new(ARGV[0])
    
    if train.delay.to_i >= 10 or train.status == "Il treno non e' ancora partito"
       send_sms(RECIPIENT, train)
       log.info(train)
    end
    

    Ho rilasciato i sorgenti su GitHub: viaggiatreno-scraper – il codice ha licenza GPL, e di conseguenza siete liberi di dire la vostra e soprattutto apportare migliorie!

    Un altro esempio di cosa si potrebbe fare?
    Ogni giorno alle 11:59PM, si potrebbe raccogliere la situazione di tutti i treni circolati sulla rete ferroviaria; in questo modo, si potrebbero estrarre delle curiose statistiche sul ritardo/anticipo medio, e tanto altro. Scrivere un task MapReduce/Hadoop che fa tutto questo non è per niente complicato…

  • Aggiornare tutte le Ruby gems installate

    Per aggiornare le gem installate suggerisco di:

    1. Aggiornare la versione di RubyGems installata:gem update --system
    2. Aggiornare tutte le gemme installate: gem update
    3. Rimuovere la versione “obsoleta” delle gemme installate: gem cleanup

    Può capitare che ci siano dei problemi di dipendenze durante la fase di cleanup:

    michele@delta:~ % gem cleanup [ 7:00PM]
    Cleaning up installed gems…
    Attempting to uninstall sprockets-2.1.2

    You have requested to uninstall the gem:
    sprockets-2.1.2
    actionpack-3.2.3 depends on [sprockets (~> 2.1.2)]
    If you remove this gems, one or more dependencies will not be met.
    Continue with Uninstall? [Yn]

    In questi casi vi sconsiglio di rimuovere la gem in questione in modo da non violare la dipendenza.

  • require_relative in ruby 1.9 e la retrocompatibilità con ruby 1.8

    A partire da ruby 1.9 è obbligatorio utilizzare l’istruzione:

    require_relative 'pkg'

    affinché venga importato il modulo ‘pkg’ presente nella stessa directory in cui è presente il modulo che stiamo scrivendo. Mi spiego meglio: dato un modulo ruby detto ‘pkg’, se vogliamo importarlo in un altro file (chiamiamolo ‘foo’ per semplicità), dobbiamo scrivere:

    • in ruby 1.8: require 'pkg'
    • in ruby 1.9: require_relative 'pkg'
    Questo accorgimento è utilizzato per aumentare la security di chi scrive e soprattutto di chi utilizza programmi: nel caso in cui siano definiti più moduli ‘pkg’ (uno in gems e uno locale), quale dovrebbe importare l’interprete?
    La soluzione è assolutamente valida, ma richiede un po’ di manutenzione per garantire la retrocompatibilità con ruby 1.8. Infatti, per permettere a ruby 1.8 di interpretare correttamente l’istruzione ‘require_relative’ dobbiamo procedere ad un workaround dichiarando l’implementazione di tale istruzione:
    unless Kernel.respond_to?(:require_relative)
      module Kernel
        def require_relative(path)
          require File.join(File.dirname(caller[0]), path.to_str)
        end
      end
    end
    

    A questo punto è sufficiente utilizzare require_relative anche in ruby 1.8 per non avere più problemi.