En el artículo anterior vimos como generar texto en formato CSV en ruby 1.9.2 y como utilizar una sencilla clase dentro de una aplicación rails 3 que generaba texto en dicho formato. En este artículo vamos a ver como incorporar lo aprendido para que nuestra aplicación responda a peticiones de formato CSV de una manera muy sencilla.
Partimos del siguiente controlador que simplemente crea un objecto ActiveRecord::Relation (nuestra aplicación no tiene muchos usuarios no nos preocupamos por recuperar todos los registros)
class Admin::UsersController < Admin::AdminController
def index
@users = User.scoped
end
end
Ahora simplemente debemos declarar que nuestro controlador y/o acción responde a peticiones csv
class Admin::UsersController < Admin:AdminController
respond_to :html, :csv
def index
@users = User.scoped
respond_with(@users)
end
end
Sin embago si abrimos nuestro navegador y vamos a “/admin/users.csv” nos encontramos con:
Template is missing Missing template admin/users/index with {:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml], :formats=>[:csv], :locale=>[:es, :es]}
Al utilizar el respond_with y gracias al respond_to :csv hemos indicado a la aplicación que responde a peticiones de formato :csv, pero rails primero comprobará si hemos añadido un bloque para dicho formato o si tenemos una plantilla para hacer el render (index.csv.erb), sino ejecutará un responder por defecto. Y este responder por defecto producirá el error mostrado anteriormente ya que intenta ejecutar igualmente render(:csv => @users) pero no tenemos ninguna plantilla definida.
Pero en rails 3 podemos definir que nuestra aplicación responda a xml y json y funcionariá sin definir ninguna plantilla. Y esto es porque rails 3 define “renderers” tanto para xml y json
class Admin::UsersController < Admin:AdminController
respond_to :html, :csv, :json, :xml
def index
@users = User.scoped
respond_with(@users)
end
end
module ActionController
module Renderers
...
add :json do |json, options|
json = json.to_json(options) unless json.respond_to?(:to_str)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
self.content_type ||= Mime::JSON
self.response_body = json
end
add :xml do |xml, options|
self.content_type ||= Mime::XML
self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
end
...
end
end
Entonces simplemente lo que necesitamos es añadir un nuevo “renderer” para el formato csv. Nos creamos el initializer csv_renderer.rb
ActiveSupport.on_load :action_controller do
ActionController.add_renderer :csv do |relation, options|
csv_instance = CsvDelegator.new(relation)
options = {:type => "text/csv; charset=utf8", :filename => csv_instance.filename}.merge(options)
send_data csv_instance.to_csv, options.slice(:type, :filename)
end
end
Y no necesitamos añadir nada más, bueno si definir nuestra clase CsvDelegator, pero eso no tiene mucha historia y os lo dejo a vosotros
Y bueno unos links que pueden resultar de interes: - Rails 3 upgrade - Render Options in Rails 3 - Creating your own renderer - Three reasons love responder
NOTA: ¿Por qué en los diferentes ejemplos utilizan ActionController::Renderers.add en lugar de ActionController.add_renderer? ¡Qué alguien me cuente!