Warning: Trying to access array offset on value of type null in /data/websites/htmlit/web/app/themes/htmlit/src/ViewModel/Post/Templates/SingleGuide.php on line 113

Warning: Trying to access array offset on value of type null in /data/websites/htmlit/web/app/themes/htmlit/src/ViewModel/Post/Templates/SingleGuide.php on line 113

Warning: Trying to access array offset on value of type null in /data/websites/htmlit/web/app/themes/htmlit/src/ViewModel/Post/Templates/SingleGuide.php on line 113

Warning: Trying to access array offset on value of type null in /data/websites/htmlit/web/app/themes/htmlit/src/ViewModel/Post/Templates/SingleGuide.php on line 113

Warning: Trying to access array offset on value of type null in /data/websites/htmlit/web/app/themes/htmlit/src/ViewModel/Post/Templates/SingleGuide.php on line 113

Warning: Trying to access array offset on value of type null in /data/websites/htmlit/web/app/themes/htmlit/src/ViewModel/Post/Templates/SingleGuide.php on line 113

Warning: Trying to access array offset on value of type null in /data/websites/htmlit/web/app/themes/htmlit/src/ViewModel/Post/Templates/SingleGuide.php on line 113

Warning: Trying to access array offset on value of type null in /data/websites/htmlit/web/app/themes/htmlit/src/ViewModel/Post/Templates/SingleGuide.php on line 113
Report professionali per Ruby e Rails con Ruport | HTML.it
Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Report professionali per Ruby e Rails con Ruport

Creare report in PDF, ODF, CSV e altri formati
Creare report in PDF, ODF, CSV e altri formati
Link copiato negli appunti

Quando si ha a che fare con una banca dati, a prescindere dalla natura delle informazioni, che essa sia rappresentata da un database o da un grosso file, ci si deve confrontare con la necessità di confezionare delle visualizzazioni, dei report.

Questo per avere risultati utili e semplici da analizzare e manipolare, con tabelle, immagini e grafici, soprattutto se prodotti nei formati più usati come PDF o Excel (ma anche Word, CSV etc.).

In questo articolo ci occupiamo della creazione di report con Ruport, dividendo il discorso in due parti. Nella prima parleremo dei concetti di base della libreria:

  • strutture dati
  • manipolazione dati
  • controller
  • formatter
  • Nella seconda, invece, estenderemo alcuni concetti e approfondiremo temi quali la gestione dei template per la formattazione, la generazione di grafici, le estensioni per la generazione di file Excel e l'integrazione con Ruby on Rails.

    Inoltre ci serviremo di esempi che aiuteranno a capire ed apprezzare funzionalità e potenzialità di Ruport.

    Ruport: cosa è e cosa offre?

    Ruport arrivato alla versione 1.6.1, sviluppato da Gregory Brown, Dudley Flanders, James Healy, Dinko Mehinovic e Michael Milner, fornisce un insieme di tool che aiutano lo sviluppatore ad aggiungere capacità reportistiche alle proprie applicazioni.

    Offre una serie di facilitazioni per l'estrazione, il raggruppamento e la manipolazione di dati, oltre alla capacità di renderli disponibili in diversi formati di output grazie ad un sistema flessibile ed estensibile per la formattazione ed il rendering dei report.

    Prepariamo il campo di lavoro

    Prima di iniziare ad analizzare le potenzialità di Ruport, provvediamo all'installazione tramite l'ormai noto RubyGems.

    gem install ruport

    che provvederà ad installare automaticamente le dipendenze (fastercsv,
    pdf-writer
    ).

    Le strutture dati di Ruport

    Ruport usa quattro strutture dati di base: record, table, group e grouping, tutte contenute nel modulo Ruport::Data. Il nucleo delle strutture dati è la tabella. I record formano le tabelle, i group ed i grouping servono per raggruppare i dati in tabella.

    Iniziamo a crearne qualcuna. Partiamo dai record.

    I Record rappresentano la struttura dati più semplice. Corrispondono ad una riga di dati proveniente da database o da altre sorgenti di dati.

    Listato 1. Creare record

    %w[rubygems ruport].each {|l| require l}
    
      column_headers = ["artist", "album", "year", "tracks"]
    
      first_item  = Ruport::Data::Record.new(["King Crimson", "In the Court of Crimson King", "1969", "5"], :attributes => column_headers) => #<Ruport::Data::Record:0x2d1b864 @data={"artist"=>"King Crimson", "year"=>"1969", "tracks"=>"5", "album"=>"In the Court of Crimson King"}, @attributes=["artist", "album", "year", "tracks"]>
      second_item = Ruport::Data::Record.new(["King Crimson", "In the Court of Crimson King", "1969", "5"], :attributes => column_headers)
      third_item  = Ruport::Data::Record.new(["Genesis", "Nursey Crime", "1972", "7"], :attributes => column_headers)
      fourth_item = Ruport::Data::Record.new(["Frank Zappa","We're Only In It For The Money", "1968", "19"], :attributes => column_headers)
    

    L'accesso ai dati del record può avvenire usando una notazione array-like o hash-like, rispettivamente first_item[1] e first_item["album"].

    Le Tabelle sono collezioni di Record e presentano i metodi necessari per lavorare con i dati in esse conte

    Listato 2. Creare e visualizzare una tabella

    table = Ruport::Data::Table.new => #<Ruport::Data::Table:0x2d2fb48 @record_class="Ruport::Data::Record", @data=[], @column_names=[]>
    
    table.column_names = column_headers
    table << first_item << second_item << third_item << fourth_item
    
    puts table
    +---------------------------------------------------------------+
    |artist        | album                          | year | tracks |
    +---------------------------------------------------------------+
    | King Crimson | In the Court of Crimson King   | 1969 | 5      |
    | King Crimson | In the Court of Crimson King   | 1969 | 5      |
    | Genesis      | Nursey Crime                   | 1972 | 7      |
    | Frank Zappa  | We're Only In It For The Money | 1968 | 19     |
    +---------------------------------------------------------------+
    

    Come accennato precedentemente, i metodi disponibili sulla struttura dati Table sono molti. Vediamone alcuni:

    • column_names
    • remove_column("album") remove_columns("album", "year")
    • rename_column('old_name', 'new_name') rename_columns ('old_names', 'new_names')
    • swap_column('a', 'b')
    • replace_column('old_col', 'new_col')
    • add_column('c', :before => 'd')
    • sub_table(%w[album year])

    Per un elenco completo fare riferimento alla documentazione ufficiale.

    I dati possono essere raggruppati secondo criteri a nostra scelta. Ruport fornisce due strutture dati per questo:

    • I Group Ruport::Data::Group Ruport::Data::Table name
    • I Grouping Ruport::Data::Grouping.new Grouping

    Listato 3. Creazione di un Grouping

    artist_grouping = Grouping(table, :by => "artist")
    year_grouping = Ruport::Data::Grouping.new(table, :by => ["artist", "year"])
     
    puts artist_grouping
    puts year_grouping
    

    Manipolazione dei dati

    Con poche linee di codice, spesso ne basta solo una, è possibile eseguire manipolazioni dei dati presenti nelle strutture prima descritte. Questa funzionalità, insieme al fatto che i dati possono provenire da diverse sorgenti ed essere nativamente pronti per la visualizzazione in vari formati di output, rendono Ruport adatto alle esigenze di ogni sviluppatore.

    Prendiamo ora in considerazione operazioni quali l'ordinamento, la ricerca, le operazioni su colonna, il calcolo della somma e della media dei dati in colonna.

    Ordinare le tabelle

    puts table.sort_rows_by("year", :order => :ascending)

    Ordinare i Grouping

    Nel più semplice dei casi possiamo ordinare un Grouping dal nome del gruppo impostando l'opzione :order al valore del :name del gruppo.

    g = Grouping(table, :by => "tracks", :order => :name)
    puts g.to_a.map {|name,group| name}
    

    oppure ordinandolo in base ad uno specifico blocco:

    g = Grouping(table, :by => "artist", :order => lambda {|g| g.size })
    puts  g.to_a.map {|name,group| name }
    

    o ancora, grazie al metodo sort_grouping_by dopo che è stato creato:

    g = Grouping(table, :by => "year")
    puts g.sort_grouping_by {|g| g.size }
    

    Cercare righe in una tabella

    Ruport mette a disposizione del tipo Table il metodo rows_with, perfetto per le semplici operazioni di ricerca:

    table.rows_with("artist") { |a| 
      if a.size < 11
        puts a
      end
    }
    

    Se ad esempio volessimo conoscere il numero totale di tracce presenti nella nostra collezione di Cd e la media di tracce per cd, potremmo usare il metodo Table#sigma o il suo alias Table#sum

    # Totale
    # Media
    

    Filtrare e trasformare i dati

    Ruport::Data::Feeder fornisce un semplice proxy object che ci permette di filtrare i dati che vogliamo aggregare. È principalmente usato per creare una tabella wrapper con vincoli. Può essere usato anche con strutture dati astratte.

    # Esempio tratto dalla documentazione ufficiale
    # http://api.rubyreports.org/classes/Ruport/Data/Feeder.html
    

    Creazione dell'output

    Tutte le strutture dati di base di Ruport possono produrre nativamente output in diversi formati (PDF, Text, HTML, CSV). Per farlo basta semplicemente richiamare il metodo corrispondente:

    Listato 4. Formati di output per i tipi dati nativi di Ruport

    # TXT
    # CSV
    # HTML
    # PDF
    

    Per situazioni semplici o per i test, le capacità di output native delle strutture dati rappresentano davvero un aiuto.

    Il sistema di formattazione di Ruport ci consente di personalizzare la formattazione ed il rendering dei nostri dati. È costituito da due componenti:

    • Il Controller
    • Il Formatter

    In Ruport gli step definiti nel Controller prendono il nome di stage. Definendo gli stage, il Controller sa che dovrà fare riferimento alle rispettive implementazioni presenti nel Formatter a lui associato al fine di produrre l'output.

    Per la creazione dei metodi del Formatter corrispondenti agli stage del Controller, Ruport segue una convenzione interna. Fa uso del metodo di classe build con il nome dello stage e gli associa un block (il corpo del metodo). Nel caso non si volesse seguire la convenzione, nulla ci vieta di definire il metodo build_nomestage.

    Esempio finale

    In questo esempio mettiamo insieme i concetti finora esposti al fine di fornire un esempio di gestione e manipolazione dati, nonché di creazione di controller e formatter per i nostri report.

    Listato 5. Esempio finale

    # Definiamo il Controller
    # Formatter per l'output testuale
    # Formatter per l'output HTML
    # con template ERB
    

    Abbiamo accennato ai concetti ed alle funzionalità di base di Ruport. Nella seconda parte dell'articolo descriveremo le funzionalità permettono di creare grafici (SVG o su PDF), personalizzare il layout e interagire con RubyonRails.

    Nella prima parte dell'articolo ci siamo occupati degli aspetti di base della libreria, di come si creano e visualizzano tabelle, delle manipolazioni semplici dei dati e di come creare output personalizzato tramite la definizione di controller e formatter. Ora discuteremo di come:

    • integrare i report nelle nostra applicazioni Rails
    • creare dei grafici
    • personalizzare i template
    • esportare i report in formati OpenDocument

    ed infine accenneremo all'uso di rope.

    Aggiungiamo il necessario al campo di lavoro

    Con RubyGems provvediamo all'installazione delle gem necessarie (acts_as_reportable, ruport_util, documatic, pdf_writer_proxy).

    gem install ruport-util documatic acts_as_reportable
    gem install pdf_writer_proxy --source http://gems.rubyreport.org
    

    Ruport e RubyOnRails

    Per integrare dati di applicazioni RubyOnRails all'interno dei nostri report usiamo il modulo acts_as_reportable che usa i risultati di un ActiveRecord::Base.find() per preparare una tabella di Ruport (Ruport::Data::Table). Integrandosi con ActiveRecord è naturalmente utilizzabile in modo indipendente da Rails ed in tutte le applicazioni che usano ActiveRecord in modo standalone.

    Aggiungiamo require 'ruport' nel model ActiveRecord oppure direttamente nel file di Rails environment.rb che provvederà a caricare Ruport automaticamente.

    Il modo più semplice per iniziare ad usare i risultati di una ricerca in DB è aggiungere il metodo acts_as_reportable all'interno della definizione della model class di ActiveRecord. In questo modo si avrà già a disposizione il metodo report_table.

    Listato 6: acts_as_reportable

    class Album < ActiveRecord::Base 
          acts_as_reportable
          belongs_to :artist
      
      def artist_name
        artist.name
      end
    end
        
    class Artist < Active::Record::Base
          acts_as_reportable
          has_many :albums
    end
        
    puts Album.report_table(:all, :except => :artist_id)
    

    Riportiamo alcune opzioni disponibili con il metodo report_table:

    1. :only
    2. :except
    3. :methods
    4. :include
    5. :filters
    6. :transforms

    Poiché acts_as_reportable usa il metodo find di ActiveRecord, tutte le opzioni che non vengono automaticamente riconosciute sono passate al metodo find. Questo significa che è possibile usare tutte le opzioni riconosciute da ActiveRecord::Base.find.

    puts Album.report_table(
            :all, 
            :only => :title,
            :include => { :artist    => { :only => :name } }
            :filters => lambda {|r| r["id"] > 1 })
    

    Equivalente a:

    puts album.report_table(:all, :only => [:title], :methods => [:artist_name], :filters => lambda {|r| r["id"] > 1 })

    Il metodo di acts_as_reportable prende tutte le opzioni del metodo report_table, permettendo così di impostare le opzioni di default per i nostri report:

    acts_as_reportable :except => [:id, :artist_id]

    Analogamente al metodo ActiveRecord::Base.find_by_sql è presente il metodo report_table_by_sql.

    Un'altra modalità messa a disposizione da Ruport per collezionare i dati è quella di usare la classe Ruport::Query, che fornisce il supporto all'uso della libreria RubyDBI

    Listato 7. Query

    require 'rubygems'
    require 'ruport'
    require 'active_record'
    require 'ruport/acts_as_reportableì
        
    Ruport::Query.add_source(
        :default, 
        :user => "testuser",
        :passwrod => "testpwd"
        :dsn => "dbi:mysql:cdArchive_db")
            
    query = Ruport::Query.new("SELECT * FROM albums")
    puts query.result
    

    Inserire grafici ed immagini

    La creazione di grafici a partire dai dati presenti nelle strutture base di Ruport richiede l'installazione di ruport-util e non molto effort. Ruport fa da wrapper per alcune porzioni della libreria Gruff per i formati JPEG e PNG e di Scruffy per l'SVG. Risulta quindi necessario provvedere all'installazione di queste librerie, ricordandoci della dipendenza di Gruff da RMagick.

    È presente una struttura dati apposita per i grafici, nello specifico, Ruport::Data::Graph, figlia di Ruport::Data::Table, con l'aggiunta di metodi specifici per la preparazione di grafici, come series che permette di aggiungere linee ai grafici. A livello di Kernel è presente lo shurtcut Graph.

    Come detto in precedenza, porzioni delle librerie Gruff e Scruffy sono presenti direttamente in Ruport. Ad esempio usando Gruff è possibile creare soltanto grafici a linee. Questa non è affatto una limitazione. Sfruttando il concetto di classi aperte di Ruby, basta semplicemente aggiungere il tipo di grafico desiderato (pie, bar, etc.) a quello già presente. Vediamo come aggiungere il tipo grafico pie (torta) e bar.

    Listato 8. Estensione del modulo Graph

    %w[rubygems ruport ruport/util ruport/extensions].each {|lib| require lib}
    
    class Ruport::Formatter
    
      module Graph
        class Gruff < Ruport::Formatter 
          renders [:png,:jpg], :for => Ruport::Controller::Graph
          
          def build_graph
            height = "#{options.height || 600}"
            width = "#{options.width || 800}"
            dimensions = "#{width}x#{height}"
            type = options.graph_type || :line
            
            graph = case type
              when :line
                ::Gruff::Line.new(dimensions)
              
              # inizio aggiunta
    # fine aggiunta
    

    Listato 9. Applicare l'estensione al controller

    class ReportController < Ruport::Controller
      stage :long_report
      required_option :file
    
      ##
    # L'hook method setup viene richiamato dopo che le opzioni
    # sono processate. Questo significa che possiamo manipolare
    # in modo semplice gli attributi dei dati così come ogni
    # opzione passata al controller.
    ##
    # do someting
    # PDFReport
    # ReportController
    # Dummy Test
    

    Personalizzare i template

    Con la definizione di template propri è possibile specificare delle opzioni per la formattazione dei nostri report. La creazione di un nuovo template avviene usando il metodo Ruport::Formatter::Template.create mentre, per la scelta del template da usare in fase di rendering, si specifica l'opzione :template quando si crea l'output. È anche possibile estendere un template figlio a partire dal padre.

    Facciamo un esempio di quanto appena detto, definiamo un template che chiamiamo simple.

    Listato 10. Definire un template

    Ruport::Formatter::Template.create(:simple) do |format|
    
      format.page = { :layout => :landscape, :size => "LETTER" }      
      format.text = { :font_size => 12, :justification => :center}
    end
    

    Ora vediamo come estedere il template simple

    Listato 11. Estendere un template

    Ruport::Formatter::Template.create(:derived, :base => :simple) do |format|
      
      format.table = { :font_size => 10, :heading_font_size => 10, 
                       :maximum_width => 720, :width => 720 }      
      format.grouping = { :style => :separated }
      format.column = { :alignment => :right }
      format.heading = { :alignment => :right, :bold => true }
    end
    
    class Ruport::Formatter::Text
      def apply_template
        options.note = template.note
      end
    end
    
    ...
    puts AlbumController.render_text(:data => data, :template => :simple)
    puts AlbumController.render_text(:data => data, :template => :derived)
    

    Documatic - report in OpenOffice

    Si tratta di un progetto indipendente che fornisce un'utile estensione OpenDocument a Ruport. Come si legge dalla home page del progetto, la versione attuale (0.2.0) supporta i formati testo (odt) e foglioelettronico (ods).

    L'utilizzo di base è molto semplice. Basta creare un template con OpenOffice come quello in figura, preso dalla sezione tutorial del sito del progetto

    Figura 1. Esempio di template OpenDocument
    Esempio di template OpenDocument

    ed uno script ruby che si occupa di creare i dati, richiamare il template e passargli i dati.

    Listato 5. Popolare il template con i dati

    require 'rubygems'
    require 'documatic'
    
    # definizione della tabella
    # utilizziamo Documatic
    # per generare un OpenDocument in formato testo
    # ed un foglio elettronico
    

    Rope

    Rope è un tool che offre una serie di semplici utilities per la generazione di progetti di reportistica. Genera la struttura di base di una applicazione Ruport, fornendo un valido aiuto agli sviluppatori. Viene installato automaticamente con l'installazione di ruport-util.

    Per iniziare con un nuovo progetto basta lanciare il comando $rope testprj (pressoché l'equivalente di quanto fa il comando rails a livello di struttura del progetto).

    $ rope testprj
    
      creating directories..
          testprj/test
          testprj/config
          testprj/output
          testprj/data
          testprj/data/models
          testprj/lib
          testprj/lib/reports
          testprj/lib/controllers
          testprj/sql
          testprj/util
      creating files..
          testprj/lib/reports.rb
          testprj/lib/helpers.rb
          testprj/lib/controllers.rb
          testprj/lib/templates.rb
          testprj/lib/init.rb
          testprj/config/environment.rb
          testprj/util/build
          testprj/util/sql_exec
          testprj/Rakefile
          testprj/README
    
    • Utilities
      • build
      • sql_exec
      • Rakefile
    • Directories
      • test
      • config
      • reports
      • controllers
      • models
      • sql
      • util

    Il file config/environment.rb contiene le configurazioni per il progetto generato con rope (es. database, mailer).

    Generazione di un modello con rope

    $ util/build model album
    model file: data/models/album.rb
    class name: Album
        
    $ cat data/models/album.rb
    class Album < ActiveRecord::Base
      acts_as_reportable
    end
    

    Conclusioni

    L'utilizzo di Ruport e delle sue estensioni rende la generazione di report particolarmente semplice. In definitiva si tratta di un tool ben fatto, estendibile e ben documentato.

Ti consigliamo anche