gem install ruport게다가 여러분은 Ruport의 acts_as_reportable 모듈도 설치하고 싶을 텐데, acts_as_reportable 모듈을 이용하면 데이터를 수집하기 위해 액티브레코드(ActiveRecord)에 연결할 수 있기 때문이다.
gem install acts_as_reportable이렇게 하면 Ruport를 이용하는데 필요한 것이 모두 설치된다. Ruport의 핵심적인 부분 이외의 추가적인 기능을 제공하는 다른 패키지도 있지만, Ruport를 시작하는 데에는 필요하지 않을 것이다.
Rails::Initializer.run do |config| config.after_initialize do require "ruport" end end이제 Ruport가 로딩되면 레일즈 프로젝트에서 Ruport를 사용할 수 있을 것이다. 한 가지 알아둘 것은 우리가 acts_as_reportable 모듈을 사용하고자 하더라도 Ruport에서 자동적으로 acts_as_reportable 역시 로딩하려 할 것이므로 직접 모듈을 require할 필요가 없다는 점이다. 우리는 보고서 생성을 시작할 때 보고서의 코드를 담을 파일의 위치를 결정해야 할 것이다. 물론 레일즈가 레일즈에서 사용하는 모든 디렉터리 구조와 파일 위치에 대해 관례(convention)을 정하긴 하지만, Ruport에서 필요로 하는 파일에 대해서는 정하지 않는다. 결국 개인의 선호 문제이긴 하지만 Ruport 커뮤니티에서는 대개 app/reports 디렉터리를 사용하여 모든 보고서 코드를 담는다. 그러므로 여러분이 그러한 방식을 따를 것이라면 나중에 해당 디렉터리를 사용할 것이므로 지금 그 디렉터리를 생성해 두도록 한다.
config.load_paths += %W( #{RAILS_ROOT}/app/reports )Ruport를 설치하고 레일즈 프로젝트와 Ruport를 연동하는 것은 이게 끝이다. 다음으로는 Ruport에서 보고서에서 보여질 데이터를 어떻게 가져오는지 알아볼 것이다.
class CreateBooks < ActiveRecord::Migration def self.up create_table :books do |t| t.string :name t.string :description t.string :isbn t.string :status t.integer :author_id t.integer :pages t.integer :genre_id end end def self.down drop_table :books end end class CreateAuthors < ActiveRecord::Migration def self.up create_table :authors do |t| t.string :name t.timestamps end end def self.down drop_table :authors end end모델과 Ruport를 연결하기 위해서는 각 모델 정의에 acts_as_reportable 라인을 추가하기만 하면 된다.
class Book < ActiveRecord::Base acts_as_reportable belongs_to :author end class Author < ActiveRecord::Base acts_as_reportable has_many :books end이렇게 하면 모델로부터 Ruport Table을 직접 생성하는데 사용할 수 있는 report_table이라는 이름의 클래스 메서드가 각 모델에 제공될 것이다. 레일즈 콘솔에서 데이터를 조금 만들어 보면 앞서 설명한 내용이 어떻게 동작하는지 확인해 볼 수 있을 것이다.
>> Author.create(:name => "Umberto Eco") >> Author.create(:name => "William Gaddis") >> Author.create(:name => "Thomas Hardy") >> Author.create(:name => "Ben Okri")이제 몇 명의 저자가 만들어졌으므로 Ruport 테이블을 만드는 것이 얼마나 쉬운지 알 수 있을 것이다. 여러분이 단순히 report_table 메서드를 호출하기만 해도 테이블이 생성될 것이다. 결과로 나타나는 테이블의 구조를 확인해 보기 위해서는 Ruport에서 제공되는 기본 텍스트 출력결과를 사용할 것이다.
>> puts Author.report_table +----------------------------------------------------------------------------->> | name | updated_at | id | created_at >> +----------------------------------------------------------------------------->> | Umberto Eco | Mon Mar 31 23:05:39 -0400 2008 | 1 | Mon Mar 31 23:05:39 ->> | William Gaddis | Mon Mar 31 23:05:58 -0400 2008 | 2 | Mon Mar 31 23:05:58 ->> | Thomas Hardy | Mon Mar 31 23:06:07 -0400 2008 | 3 | Mon Mar 31 23:06:07 ->> | Ben Okri | Mon Mar 31 23:06:22 -0400 2008 | 4 | Mon Mar 31 23:06:22 ->> +----------------------------------------------------------------------------->>이렇게 하는 것은 매우 쉽지만 실제 프로젝트에서는 모든 컬럼을 보여주고 싶지는 않을 것이다. 사실 여러분은 저자 테이블에서 저자의 이름에만 관심이 있을 수도 있다. 여러분은 report_table에 다양한 옵션을 주어 출력결과를 직접 정의할 수가 있다. 여러분은 이것을 acts_as_reportable에서 사용되는 몇 가지 추가 옵션이 포함된 AactiveRecord.find 메서드로 생각하면 될 것이다. 옵션을 포함할 경우, 여러분은 첫 번째 매개변수로 :all이나 :first(매개변수를 지정하지 않으면 :all이 기본값이다)을 지정해야 한다.
>> puts Author.report_table(:all, :only => "name") +----------------+ | name | +----------------+ | Umberto Eco | | William Gaddis | | Thomas Hardy | | Ben Okri | +----------------+또 한 가지 알아둘 것은 만약 여러분이 :only 옵션에 컬럼명의 배열을 주게 되면 컬럼은 배열내의 순서에 따라 정렬될 것이라는 점이다.
>> puts Author.report_table(:all, :only => ["id","name"]) +---------------------+ | id | name | +---------------------+ | 1 | Umberto Eco | | 2 | William Gaddis | | 3 | Thomas Hardy | | 4 | Ben Okri | +---------------------+여러분은 일반 ActiveRecord.find에서 사용할 수 있는 것도 모두 사용할 수가 있다.
>> puts Author.report_table(:all, :only => ["id","name"], :order => "authors.name") +---------------------+ | id | name | +---------------------+ | 4 | Ben Okri | | 3 | Thomas Hardy | | 1 | Umberto Eco | | 2 | William Gaddis | +---------------------+만약 여러분이 여러 개의 연관된 모델로부터 출력결과를 결합하고자 한다면 :include 옵션을 사용할 수가 있다. 또한 여러분은 해시(hash)를 이용하여 포함된 모델에 옵션을 중첩할 수도 있는데, 이를 통해 테이블내의 모든 데이터를 직접 정의할 수가 있다. 먼저 우리는 책을 만들 필요가 있을 것이므로 다음과 같이 코드를 작성하여 관련 모델을 어떻게 한 테이블에 결합하는지 볼 수 있을 것이다.
>> Book.create(:name => "Baudolino", :author_id => 1, :pages => 521) >> Book.create(:name => "The Famished Road", :author_id => 4, :pages => 500) >> Book.create(:name => "The Recognitions", :author_id => 2, :pages => 956) >> Book.create(:name => "The Return of the Native", :author_id => 3, :pages => 418)가장 간단한 :include 옵션의 사용법은 단순히 포함될 모델의 이름을 지정하는 것이다.
>> puts Book.report_table(:all, :only => "name", :include => :author) +----------------------------------------------------------------------------->> | name | author.id | author.created_at | >> +----------------------------------------------------------------------------->> | Baudolino | 1 | Mon Mar 31 23:05:39 -0400 2008 | Mon >> | The Famished Road | 4 | Mon Mar 31 23:06:22 -0400 2008 | Mon >> | The Recognitions | 2 | Mon Mar 31 23:05:58 -0400 2008 | Mon >> | The Return of the Native | 3 | Mon Mar 31 23:06:07 -0400 2008 | Mon >> +----------------------------------------------------------------------------->>출력결과를 전부 직접 정의하기 위해서는 포함된 모델에도 옵션을 주어야 할 수도 있다. 한 가지 알아둘 점은 포함된 모델에서 반환되는 컬럼명에는 전체 경로가 지정된(qualified) 모델명이 포함된다는 것이다.
>> puts Book.report_table(:all, :only => "name", :include => { :author => { :only => "name" } }) +-------------------------------------------+ | name | author.name | +-------------------------------------------+ | Baudolino | Umberto Eco | | The Famished Road | Ben Okri | | The Recognitions | William Gaddis | | The Return of the Native | Thomas Hardy | +-------------------------------------------+이제 여러분은 액티브레코드 모델(또는 여러 모델)에서 Ruport 테이블을 생성하는 방법에 대해 알게 되었을 것이다. 그 밖에 acts_as_reportable이 이해하는 고급 명령도 있는데, 이는 여러분 스스로 찾아볼 수 있을 것이다. 하지만 지금은 여러분이 여태까지 수집한 데이터를 이용하여 형식이 지정된 보고서를 어떻게 만드는지에 관한 내용으로 넘어갈 것이다.
app/reports/book_report.rb class BookReport < Ruport::Controller stage :list def setup self.data = Book.report_table(:all, :include => { :author => { :only => ["name"] } }, :only => ["name", "author.name", "pages"], :order => "books.name") data.rename_columns("name" => "Title", "author.name" => "Author") end formatter :html do build :list do output << textile("h3. Book List") output << data.to_html end end endscript/console을 이용하여 실제로 보고서를 실행해볼 수 있다:
>> puts BookReport.render_htmlBook List
Title | Author | pages |
---|---|---|
Baudolino | Umberto Eco | 521 |
The Famished Road | Ben Okri | 500 |
The Recognitions | William Gaddis | 956 |
The Return of the Native | Thomas Hardy | 418 |
stage :list이 사실을 염두에 두면 형식자 코드가 아마도 좀 더 명확하게 느껴질 것이다:
formatter :html do build :list do output << textile("h3. Book List") output << data.to_html end endBookReport.render_html이 로딩될 때 HTML 형식자가 build 블럭 안의 코드를 실행하는 것이 확실하긴 하지만, output과 textile 메서드가 어디에서 오는지 알아내는 것이 다소 어려울 수도 있을 것이다.
class BookReport < Ruport::Controller stage :list def setup self.data = Book.report_table(:all, :include => { :author => { :only => ["name"] } }, :only => ["name", "author.name", "pages"], :order => "books.name") data.rename_columns("name" => "Title", "author.name" => "Author") end class HTML < Ruport::Formatter::HTML renders :html, :for => BookReport def build_list output << textile("h3. Book List") output << data.to_html end end end이같이 작성할 경우 Ruport의 Formatter 객체가 필요한 세부사항만을 공유하면서 실질적으로 컨트롤러의 엔터티를 분리한다는 것이 분명해 진다. 구체적으로 말하면 Controller와 Formatter 사이에서만 data와 options 속성이 공유된다는 것이다.
renders :html, :for => BookReport이 라인은 BookReport 컨트롤러로 하여금 Ruport의 HTML 형식자의 하위 클래스에서 HTML 출력결과를 처리하게 한다. 이는 컨트롤러에게 BookReport.render_html가 요청될 때 이 객체가 바로 해당 단계를 수행할 것이라는 것을 알려준다.
def build_list output << textile("h3. Book List") output << data.to_html end이 체인의 가장 아래에서 우리는 일종의 마법이 있다는 것을 알게 되므로 이 보고서의 컨텍스트에서 Ruport의 형식화 시스템이 동작하는 방식을 머릿속으로 잘 정리할 수가 있다.
formatter :pdf do build :list do pad(10) { add_text "Book List" } draw_table data end end formatter :csv do build :list do output << data.to_csv end end여러분도 알고 있듯이 CSV 포맷은 일반화된 형식이므로 Ruport 테이블에는 그러한 CSV에 대한 형식화 지원이 내장되어 있으며, CSV의 경우에는 단지 단순 데이터 뭉치만이 필요할 뿐이다.
>> puts BookReport.render_csv Title,Author,pages Baudolino,Umberto Eco,521 The Famished Road,Ben Okri,500 The Recognitions,William Gaddis,956 The Return of the Native,Thomas Hardy,418PDF 출력은 약간 더 재미있다. 첫 번째 라인에서는 위와 아래에 여백을 지정하면서 문서에 텍스트를 조금 추가한다.
pad(10) { add_text "Book List" }두 번째 라인을 보고 약간 놀라워했을지도 모르는데, 여러분이 output << data.to_pdf와 같은 것을 기대했을 수도 있기 때문이다. PDF는 HTML이나 텍스트, 또는 CSV와 같은 스트리밍 데이터 형식이 아니므로 PDF 캔버스에 테이블을 그리는 특수한 도우미가 필요하다.
draw_table data이것 말고는 코드는 특별한 게 없다. Ruport의 컨트롤러는 파일로 렌더링하는 것을 지원하므로 이렇게 하여 PDF를 받을 수가 있다.
>> BookReport.render_pdf(:file => "books.pdf")출력결과는 다음과 같다:
이전 글 : FOSS 초점: 마크 셔틀워스와의 인터뷰(2)
다음 글 : Ruport: 루비를 위한 업무 레포팅(2)
최신 콘텐츠