Отчетность является одним из наиболее важных требований в любом приложении, и, если мы имеем дело с бизнес-приложением, требования могут стать еще более важными. Однако предоставление отчетов на экране или красивых отчетов в формате HTML или PDF не всегда удовлетворяет пользователя. Иногда пользователям требуются отчеты Excel, чтобы они могли в дальнейшем работать с данными, чтобы получить именно те результаты, которые им нужны.
К счастью, Ruby предоставляет множество библиотек, связанных с Excel, но большинство из них используют программный способ создания Excel, который может быстро усложниться и утомить более сложные отчеты. Но у нас есть решение для этого: jXLS . Это популярная библиотека Java, используемая для генерации файлов Excel из шаблонных файлов Excel. Благодаря JRuby мы можем использовать эту превосходную библиотеку в Rails. Без лишних слов давайте начнем.
Создание базового приложения Rails
Мы создадим базовое приложение Rails с моделью Invoice и действиями CRUD. Приложение будет использовать JRuby, Rails 4.2 и SQLite для упрощения работы. Прежде всего, установите JRuby с помощью RVM или rbenv .
Переключитесь на JRuby и gem install rails
для gem install rails
чтобы получить последнюю версию Rails. Теперь создайте новое приложение Rails, вот так:
rails new jxls_rails -T
После того, как приложение сгенерировано, создайте скаффолд Invoice, который имеет модель и необходимые операции ресурса CRUD:
cd jxls_rails rails g scaffold Invoice invoice_number:string invoice_date:string customer:string total_value:decimal
Теперь перенесите базу данных:
rake db:migrate
Давайте проверим, как это работает:
rails s
Направьте ваш любимый браузер на счета http: // localhost: 3000 / и убедитесь, что все работает правильно. Создайте несколько записей, чтобы мы могли использовать их позже.
Создание отчета Excel
Сначала давайте создадим электронную таблицу Excel, которая будет служить шаблоном отчета. Я использую LibreOffice, так как я нахожусь на Ubuntu. Сохраните его как invoices.xls в каталоге app / reports . Добавьте поля и комментарии, как показано на снимке экрана ниже.
Мы определяем шаблон для отчета, добавляя заголовки, заполнители для данных и комментарии для метаданных, связанных с jXLS. Как видите, мы можем отформатировать ячейки так, как нам нравится в самом шаблоне, поэтому нет необходимости программно управлять этим.
Мы в основном установили заполнители для данных, которые выглядят как ${invoice.invoice_number}
и будут заменены фактическими данными. Первый комментарий, jx:area(lastCell="D2")
, задает рабочую область для отчета, определяя последнюю ячейку в области видимости отчета. Другой комментарий, jx:each(items="jdbc.query(query)" var="invoice" lastCell="D2")
, указывает тег итератора, который перемещается по всем записям и заполняет строки, заменяя заполнители. Здесь items
— набор строк для отчета. Мы разработали отчет для непосредственного выполнения предоставленного SQL-запроса и заполнения отчета с использованием jdbc.query(query)
качестве items
. Мы могли бы также передать коллекцию объектов.
Мы создали простую электронную таблицу Excel. Есть несколько расширенных опций, которые можно найти в документации по jXLS .
Интеграция jXLS в Rails
Сначала загрузите файлы jXLS со страницы jXLS SourceForge . Мы используем версию 2.2.9, поэтому скачайте jxls-2.2.9.zip и разархивируйте файл. Скопируйте все файлы из папки dist в lib / jxls нашего приложения.
Также загрузите все зависимости jXLS по этой ссылке . Разархивируйте и скопируйте все файлы из папки lib в lib / jxls в нашем приложении.
Наша окончательная структура каталогов выглядит следующим образом:
jxls_rails - app ... - lib - jxls - jxls-2.2.9.jar ...
Теперь у нас есть все необходимые файлы на месте. Давайте создадим файл с именем jxls.rb в каталоге lib со следующим кодом:
Dir.entries("#{Rails.root}/lib/jxls").each do |lib| require "jxls/#{lib}" if lib =~ /\.jar$/ end require 'java' java_import Java::org.jxls.common.Context java_import Java::org.jxls.util.JxlsHelper java_import Java::org.jxls.jdbc.JdbcHelper java_import Java::java.io.BufferedInputStream java_import Java::java.io.FileInputStream java_import Java::java.io.ByteArrayOutputStream java_import Java::org.jxls.util.TransformerFactory java_import Java::org.jxls.transform.poi.PoiTransformer class Jxls DIR = "#{Rails.root}/app/reports" def initialize(report, query) @filename = report @conn = ActiveRecord::Base.connection.jdbc_connection @query = query end def to_xls beans = {} report_source = BufferedInputStream.new(FileInputStream.new("#{DIR}/#{@filename}.xls")) raise ArgumentError, "#@filename does not exist." unless File.exist?("#{DIR}/#{@filename}.xls") bos = ByteArrayOutputStream.new context = Context.new jdbc_helper = JdbcHelper.new(@conn) context.put_var("jdbc", jdbc_helper) context.put_var("query", @query) JxlsHelper.getInstance().processTemplate(report_source, bos, context) bos.close bytes = bos.toByteArray return String.from_java_bytes(bytes) end end
Хорошо, давайте пройдемся по этому коду и выясним, что он на самом деле делает. Во-первых, нам нужны все файлы jXLS в наш класс, вот так:
Dir.entries("#{Rails.root}/lib/jxls").each do |lib| require "jxls/#{lib}" if lib =~ /\.jar$/ end
Добавьте объявления импорта Java для jXLS:
require 'java' java_import Java::org.jxls.common.Context java_import Java::org.jxls.util.JxlsHelper java_import Java::org.jxls.jdbc.JdbcHelper java_import Java::java.io.BufferedInputStream java_import Java::java.io.FileInputStream java_import Java::java.io.ByteArrayOutputStream java_import Java::org.jxls.util.TransformerFactory java_import Java::org.jxls.transform.poi.PoiTransformer
Определите место, где будут храниться все отчеты:
DIR = "#{Rails.root}/app/reports"
Добавьте код инициализации в конструктор класса:
def initialize(report, query) @filename = report @query = query @conn = ActiveRecord::Base.connection.jdbc_connection end
В качестве первого шага мы инициализировали все необходимые переменные:
- Имя файла
report
—report
- SQL-запрос отчета —
query
@conn
— это соединение JDBC из пула соединений ActiveRecord, поскольку jXLS потребуется соединение JDBC для выполнения запроса, указанного в отчете. Если мы собираемся передать коллекцию объектов, то это соединение может не понадобиться.
Наконец, мы добавили метод для заполнения и экспорта отчета в формат Excel:
def to_xls report_source = BufferedInputStream.new(FileInputStream.new("#{DIR}/#{@filename}.xls")) raise ArgumentError, "#@filename does not exist." unless File.exist?("#{DIR}/#{@filename}.xls") bos = ByteArrayOutputStream.new context = Context.new jdbc_helper = JdbcHelper.new(@conn) context.put_var("jdbc", jdbc_helper) context.put_var("query", @query) JxlsHelper.getInstance().processTemplate(report_source, bos, context) bos.close bytes = bos.toByteArray return String.from_java_bytes(bytes) end
Файл отчета ( invoices.xls ), созданный ранее, устанавливается как источник report_source
. Затем мы добавляем jdbc_helper
и @query
в объект context
для передачи в отчет. Наконец, мы вызываем JxlsHelper.getInstance().processTemplate
для заполнения и экспорта отчета в поток данных Excel, который возвращается с использованием String.from_java_bytes
.
Теперь у нас есть отчет в виде байтового потока Excel, но мы пока не можем отправить его пользователю. Итак, добавьте небольшой вспомогательный метод в application_controller.rb следующим образом:
def respond_to_report(name, query, filename, download = false) @report = Jxls.new(name, query) disposition = (download.nil? || download == false) ? 'inline' : 'attachment' send_data @report.to_pdf, :filename => filename, :type => :xls, :disposition => disposition end
Этот вспомогательный метод упрощает вызов отчета и отправляет ответ обратно пользователю. Также добавлены параметры для предоставления имени файла, наряду с возможностью disposition
чтобы открыть файл в браузере или загрузить его как вложение.
Еще одна вещь, которую нам нужно сделать, это добавить инициализатор для загрузки нашего класса JXLS
в Rails. Создайте файл с именем config / initializers / jxls.rb со следующим кодом —
require 'jxls'
Теперь мы добавим действие в InvoicesController
который вызывает отчет. Добавьте следующий код в app / controllers / invoices_controller.rb :
def report respond_to_report('invoices', 'select * from invoices', 'invoices.xls') end
Поскольку у нас нет параметров для передачи и мы хотим открыть отчет в браузере, мы исключили download
и report_params
из вызова метода.
Обновите route.rb, чтобы добавить новое действие:
resources :invoices do get :report, on: :collection end
Добавьте ссылку на отчет в представлении invoices / index.html.erb :
... <h1>Listing Invoices</h1> <%= link_to 'Download as Excel', report_invoices_path %> ...
Запустите сервер и перейдите по адресу http: // localhost: 3000 / invoices . Нажмите «Скачать как Excel». Теперь вы должны увидеть файл Excel со всеми счетами в списке.
Завершение
Сегодня мы получили краткий обзор того, как использовать jXLS с приложением Rails для быстрой генерации отчетов Excel. jXLS предоставляет более сложные опции для сложных отчетов, таких как формулы, графики, макросы и т. д., но это на другой день.
Ваши комментарии и идеи всегда приветствуются.