ruport 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. data/LICENSE +55 -9
  2. data/Rakefile +3 -5
  3. data/examples/line_plotter.rb +3 -0
  4. data/examples/pdf_complex_report.rb +0 -1
  5. data/examples/pdf_report_with_common_base.rb +72 -0
  6. data/examples/pdf_styles.rb +16 -0
  7. data/examples/simple_pdf_lines.rb +0 -1
  8. data/lib/ruport.rb +5 -66
  9. data/lib/ruport/acts_as_reportable.rb +122 -51
  10. data/lib/ruport/data/grouping.rb +30 -13
  11. data/lib/ruport/data/record.rb +26 -25
  12. data/lib/ruport/data/table.rb +91 -34
  13. data/lib/ruport/formatter.rb +86 -11
  14. data/lib/ruport/formatter/csv.rb +29 -2
  15. data/lib/ruport/formatter/html.rb +23 -1
  16. data/lib/ruport/formatter/pdf.rb +123 -32
  17. data/lib/ruport/formatter/text.rb +62 -6
  18. data/lib/ruport/query.rb +75 -39
  19. data/lib/ruport/renderer.rb +250 -35
  20. data/lib/ruport/renderer/grouping.rb +2 -2
  21. data/test/html_formatter_test.rb +4 -1
  22. data/test/pdf_formatter_test.rb +30 -7
  23. data/test/query_test.rb +12 -0
  24. data/test/renderer_test.rb +33 -2
  25. data/test/table_test.rb +8 -2
  26. data/test/text_formatter_test.rb +11 -1
  27. metadata +53 -107
  28. data/bin/rope +0 -12
  29. data/examples/rope_examples/itunes/README +0 -12
  30. data/examples/rope_examples/itunes/Rakefile +0 -39
  31. data/examples/rope_examples/itunes/config/environment.rb +0 -4
  32. data/examples/rope_examples/itunes/data/mix.txt +0 -1
  33. data/examples/rope_examples/itunes/lib/helpers.rb +0 -0
  34. data/examples/rope_examples/itunes/lib/init.rb +0 -39
  35. data/examples/rope_examples/itunes/lib/reports.rb +0 -1
  36. data/examples/rope_examples/itunes/lib/reports/playlist.rb +0 -17
  37. data/examples/rope_examples/itunes/log/ruport.log +0 -1
  38. data/examples/rope_examples/itunes/test/test_playlist.rb +0 -8
  39. data/examples/rope_examples/itunes/util/build +0 -96
  40. data/examples/rope_examples/itunes/util/sql_exec +0 -5
  41. data/examples/rope_examples/sales_report/README +0 -4
  42. data/examples/rope_examples/sales_report/Rakefile +0 -39
  43. data/examples/rope_examples/sales_report/config/environment.rb +0 -4
  44. data/examples/rope_examples/sales_report/lib/helpers.rb +0 -0
  45. data/examples/rope_examples/sales_report/lib/init.rb +0 -39
  46. data/examples/rope_examples/sales_report/lib/reports.rb +0 -1
  47. data/examples/rope_examples/sales_report/lib/reports/sales.rb +0 -132
  48. data/examples/rope_examples/sales_report/log/ruport.log +0 -1
  49. data/examples/rope_examples/sales_report/output/books.pdf +0 -170
  50. data/examples/rope_examples/sales_report/output/books.txt +0 -11
  51. data/examples/rope_examples/sales_report/test/test_sales.rb +0 -8
  52. data/examples/rope_examples/sales_report/util/build +0 -96
  53. data/examples/rope_examples/sales_report/util/sql_exec +0 -5
  54. data/examples/sample.rb +0 -16
  55. data/lib/ruport/generator.rb +0 -294
  56. data/lib/ruport/report.rb +0 -262
  57. data/setup.rb +0 -1585
  58. data/test/report_test.rb +0 -218
  59. data/test/samples/foo.rtxt +0 -3
data/LICENSE CHANGED
@@ -1,13 +1,59 @@
1
- = License Terms
1
+ Ruport is copyrighted free software originally produced by Gregory Brown
2
+ <gregory.t.brown@gmail.com> which now contains a number of community
3
+ contributions and is actively developed by Michael Milner <mikem836@gmail.com>.
2
4
 
3
- Distributed by Gregory Brown under the user's choice* of the
4
- {GPL version 2}[http://www.gnu.org/copyleft/gpl.html] (see COPYING for details)
5
- or the {Ruby software license}[http://www.ruby-lang.org/en/LICENSE.txt].
5
+ See the AUTHORS file for a complete list of contributors.
6
6
 
7
- Please email Greg[mailto:gregory.t.brown_AT_gmail.com] with any questions.
7
+ Licensing terms follow (License of Ruby 1.8):
8
8
 
9
- *Note: This license refers specifically to GPLv2.
10
- Distributing under other versions of the GPL require explicit permission from
11
- Gregory Brown. Though we will most likely adopt the GPLv3 when the final draft
12
- is published, we want to be able to make that decision for ourselves.
9
+ You can redistribute Ruport and/or modify it under either the terms of the GPL
10
+ (see COPYING file), or the conditions below:
13
11
 
12
+ 1. You may make and give away verbatim copies of the source form of the
13
+ software without restriction, provided that you duplicate all of the
14
+ original copyright notices and associated disclaimers.
15
+
16
+ 2. You may modify your copy of the software in any way, provided that
17
+ you do at least ONE of the following:
18
+
19
+ a) place your modifications in the Public Domain or otherwise
20
+ make them Freely Available, such as by posting said
21
+ modifications to Usenet or an equivalent medium, or by allowing
22
+ the author to include your modifications in the software.
23
+
24
+ b) use the modified software only within your corporation or
25
+ organization.
26
+
27
+ c) rename any non-standard executables so the names do not conflict
28
+ with standard executables, which must also be provided.
29
+
30
+ d) make other distribution arrangements with the author.
31
+
32
+ 3. You may distribute the software in object code or executable
33
+ form, provided that you do at least ONE of the following:
34
+
35
+ a) distribute the executables and library files of the software,
36
+ together with instructions (in the manual page or equivalent)
37
+ on where to get the original distribution.
38
+
39
+ b) accompany the distribution with the machine-readable source of
40
+ the software.
41
+
42
+ c) give non-standard executables non-standard names, with
43
+ instructions on where to get the original software distribution.
44
+
45
+ d) make other distribution arrangements with the author.
46
+
47
+ 4. You may modify and include the part of the software into any other
48
+ software (possibly commercial).
49
+
50
+ 5. The scripts and library files supplied as input to or produced as
51
+ output from the software do not automatically fall under the
52
+ copyright of the software, but belong to whomever generated them,
53
+ and may be sold commercially, and may be aggregated with this
54
+ software.
55
+
56
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
57
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
58
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
59
+ PURPOSE.
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require "rake/testtask"
3
3
  require "rake/gempackagetask"
4
4
 
5
5
 
6
- RUPORT_VERSION = "0.10.0"
6
+ RUPORT_VERSION = "0.11.0"
7
7
 
8
8
  begin
9
9
  require "rubygems"
@@ -25,12 +25,10 @@ spec = Gem::Specification.new do |spec|
25
25
  spec.platform = Gem::Platform::RUBY
26
26
  spec.summary = "A generalized Ruby report generation and templating engine."
27
27
  spec.files = Dir.glob("{examples,lib,test,bin,util/bench}/**/**/*") +
28
- ["Rakefile", "setup.rb"]
28
+ ["Rakefile"]
29
29
  spec.require_path = "lib"
30
30
 
31
31
  spec.test_files = Dir[ "test/*_test.rb" ]
32
- spec.bindir = "bin"
33
- spec.executables = FileList["rope"]
34
32
  spec.has_rdoc = true
35
33
  spec.extra_rdoc_files = %w{README LICENSE AUTHORS}
36
34
  spec.rdoc_options << '--title' << 'Ruport Documentation' <<
@@ -41,7 +39,7 @@ spec = Gem::Specification.new do |spec|
41
39
  spec.author = "Gregory Brown"
42
40
  spec.email = " gregory.t.brown@gmail.com"
43
41
  spec.rubyforge_project = "ruport"
44
- spec.homepage = "http://code.rubyreports.org"
42
+ spec.homepage = "http://rubyreports.org"
45
43
  spec.description = <<END_DESC
46
44
  Ruby Reports is a software library that aims to make the task of reporting
47
45
  less tedious and painful. It provides tools for data acquisition,
@@ -31,6 +31,9 @@ class SVG < Ruport::Formatter
31
31
  def initialize
32
32
  require "builder"
33
33
  @builder = Builder::XmlMarkup.new(:indent => 2)
34
+ rescue LoadError
35
+ STDERR.puts "You need Builder to run this example:\ngem install builder"
36
+ exit
34
37
  end
35
38
 
36
39
  def render_plot
@@ -1,4 +1,3 @@
1
- require 'rubygems'
2
1
  require "ruport"
3
2
 
4
3
  module MyStuff
@@ -0,0 +1,72 @@
1
+ ########################################################
2
+ # This example shows how to build custom PDF output
3
+ # with Ruport that shares some common elements
4
+ # between reports. Basically, CompanyPDFBase implements
5
+ # some default rendering options, and derived classes
6
+ # such as ClientPDF would implement the stuff specific
7
+ # to a given report.
8
+ ########################################################
9
+
10
+ require "active_support"
11
+ require "ruport"
12
+
13
+ # This looks a little more messy than usual, but it addresses your
14
+ # concern of wanting to have a standard template.
15
+ #
16
+ class ClientRenderer < Ruport::Renderer
17
+ prepare :standard_report
18
+ stage :company_header, :client_header, :client_body, :client_footer
19
+ finalize :standard_report
20
+ option :example
21
+
22
+ def setup
23
+ # replace this with your header changing code
24
+ data.rename_columns { |c| c.to_s.titleize }
25
+ # this just lets us omit the options prefix in the formatter
26
+ formatter.class.opt_reader(:example)
27
+ end
28
+ end
29
+
30
+ # This defines your base PDF output, you'd do similar for other
31
+ # formats if needed It doesn't do much of anything except
32
+ # implement the common hooks
33
+ class CompanyPDFBase < Ruport::Formatter::PDF
34
+ def prepare_standard_report
35
+ options.paper_size = "A4"
36
+ end
37
+
38
+ def build_company_header
39
+ add_text "This would be my company header",
40
+ :justification => :center, :font_size => 14
41
+ end
42
+
43
+ def finalize_standard_report
44
+ render_pdf
45
+ end
46
+ end
47
+
48
+ # This is your report's formatter
49
+ #
50
+ # It implements the remaining hooks the standard formatter didn't
51
+ # Notice I left out a footer and it didn't complain.
52
+ class ClientPDF < CompanyPDFBase
53
+ renders :pdf, :for => ClientRenderer
54
+
55
+ def build_client_header
56
+ pad(10) do
57
+ add_text "Specific Report Header with option #{example}",
58
+ :justification => :center, :font_size => 12
59
+ end
60
+ end
61
+
62
+ def build_client_body
63
+ draw_table(data, :width => 300)
64
+ end
65
+
66
+ end
67
+
68
+
69
+ table = Table([:a,:b,:c]) << [1,2,3] << [4,5,6]
70
+ File.open("example.pdf","w") { |f|
71
+ f << ClientRenderer.render_pdf(:data => table,:example => "foo")
72
+ }
@@ -0,0 +1,16 @@
1
+ require "ruport"
2
+ require "rubygems"
3
+
4
+ class MyReport < Ruport::Report
5
+ renders_as_grouping
6
+
7
+ def generate
8
+ table = Table(%w[a b c]) << [1,2,3] << [4,5,6] << [1,7,9]
9
+ Grouping(table,:by => "a")
10
+ end
11
+ end
12
+
13
+ a = MyReport.new(:pdf)
14
+ [:justified, :separated, :inline, :offset].each do |style|
15
+ a.run(:style => style) { |r| r.write("#{style}.pdf") }
16
+ end
@@ -1,4 +1,3 @@
1
- require 'rubygems'
2
1
  require "ruport"
3
2
 
4
3
  class SimpleLines < Ruport::Renderer
data/lib/ruport.rb CHANGED
@@ -10,9 +10,12 @@
10
10
  # See LICENSE and COPYING for details
11
11
  #
12
12
 
13
- module Ruport
13
+ module Ruport #:nodoc:#
14
14
 
15
- VERSION = "0.10.0"
15
+ VERSION = "0.11.0"
16
+
17
+ class FormatterError < RuntimeError #:nodoc:
18
+ end
16
19
 
17
20
  # SystemExtensions lovingly ganked from HighLine 1.2.1
18
21
  #
@@ -79,69 +82,6 @@ module Ruport
79
82
 
80
83
  end
81
84
 
82
- require 'timeout'
83
-
84
- class Attempt # :nodoc:
85
- VERSION = '0.1.0'
86
-
87
- # Number of attempts to make before failing. The default is 3.
88
- attr_accessor :tries
89
-
90
- # Number of seconds to wait between attempts. The default is 60.
91
- attr_accessor :interval
92
-
93
- # a level which ruport understands.
94
- attr_accessor :log_level
95
-
96
- # If set, this increments the interval with each failed attempt by that
97
- # number of seconds.
98
- attr_accessor :increment
99
-
100
- # If set, the code block is further wrapped in a timeout block.
101
- attr_accessor :timeout
102
-
103
- # Determines which exception level to check when looking for errors to
104
- # retry. The default is 'Exception' (i.e. all errors).
105
- attr_accessor :level
106
-
107
- # :call-seq:
108
- # Attempt.new{ |a| ... }
109
- #
110
- # Creates and returns a new +Attempt+ object. Use a block to set the
111
- # accessors.
112
- #
113
- def initialize
114
- @tries = 3 # Reasonable default
115
- @interval = 60 # Reasonable default
116
- @increment = nil # Should be an int, if provided
117
- @timeout = nil # Wrap the code in a timeout block if provided
118
- @level = Exception # Level of exception to be caught
119
-
120
- yield self if block_given?
121
- end
122
-
123
- def attempt
124
- count = 1
125
- begin
126
- if @timeout
127
- Timeout.timeout(@timeout){ yield }
128
- else
129
- yield
130
- end
131
- rescue @level => error
132
- @tries -= 1
133
- if @tries > 0
134
- msg = "Error on attempt # #{count}: #{error}; retrying"
135
- count += 1
136
- @interval += @increment if @increment
137
- sleep @interval
138
- retry
139
- end
140
- raise
141
- end
142
- end
143
- end
144
-
145
85
 
146
86
  end
147
87
 
@@ -161,7 +101,6 @@ end
161
101
  require "enumerator"
162
102
  require "ruport/renderer"
163
103
  require "ruport/data"
164
- require "ruport/report"
165
104
  require "ruport/formatter"
166
105
  require "ruport/query"
167
106
 
@@ -1,16 +1,49 @@
1
+ # Ruport : Extensible Reporting System
2
+ #
3
+ # acts_as_reportable.rb provides ActiveRecord integration for Ruport.
4
+ #
5
+ # Originally created by Dudley Flanders, 2006
6
+ # Revised and updated by Michael Milner, 2007
7
+ # Copyright (C) 2006-2007 Dudley Flanders / Michael Milner, All Rights Reserved.
8
+ #
9
+ # This is free software distributed under the same terms as Ruby 1.8
10
+ # See LICENSE and COPYING for details.
11
+ #
1
12
  require "ruport"
2
13
  quiet { require "active_record" }
3
14
 
4
15
  module Ruport
5
16
 
6
- # This module is designed to be mixed in with an ActiveRecord model
7
- # to add easy conversion to Ruport's data structures.
17
+ # === Overview
18
+ #
19
+ # This module is designed to allow an ActiveRecord model to be converted to
20
+ # Ruport's data structures. If ActiveRecord is available when Ruport is
21
+ # loaded, this module will be automatically mixed into ActiveRecord::Base.
22
+ #
23
+ # Add the acts_as_reportable call to the model class that you want to
24
+ # integrate with Ruport:
25
+ #
26
+ # class Book < ActiveRecord::Base
27
+ # acts_as_reportable
28
+ # belongs_to :author
29
+ # end
30
+ #
31
+ # Then you can use the <tt>report_table</tt> method to get data from the
32
+ # model using ActiveRecord.
33
+ #
34
+ # Book.report_table(:all, :include => :author)
35
+ #
8
36
  module Reportable
9
37
 
10
- def self.included(base) # :nodoc:
38
+ def self.included(base) #:nodoc:
11
39
  base.extend ClassMethods
12
40
  end
13
41
 
42
+ # === Overview
43
+ #
44
+ # This module contains class methods that will automatically be available
45
+ # to ActiveRecord models.
46
+ #
14
47
  module ClassMethods
15
48
 
16
49
  # In the ActiveRecord model you wish to integrate with Ruport, add the
@@ -18,12 +51,24 @@ module Ruport
18
51
  #
19
52
  # acts_as_reportable
20
53
  #
21
- # This will automatically make all the methods in this module available
22
- # in the model.
54
+ # Available options:
23
55
  #
24
- # You may pass the acts_as_reportable method the :only, :except,
25
- # :methods, and :include options. See report_table for the format
26
- # of these options.
56
+ # <b><tt>:only</tt></b>:: an attribute name or array of attribute
57
+ # names to include in the results, other
58
+ # attributes will be excuded.
59
+ # <b><tt>:except</tt></b>:: an attribute name or array of attribute
60
+ # names to exclude from the results.
61
+ # <b><tt>:methods</tt></b>:: a method name or array of method names
62
+ # whose result(s) will be included in the
63
+ # table.
64
+ # <b><tt>:include</tt></b>:: an associated model or array of associated
65
+ # models to include in the results.
66
+ #
67
+ # Example:
68
+ #
69
+ # class Book < ActiveRecord::Base
70
+ # acts_as_reportable, :only => 'title', :include => :author
71
+ # end
27
72
  #
28
73
  def acts_as_reportable(options = {})
29
74
  cattr_accessor :aar_options, :aar_columns
@@ -35,51 +80,68 @@ module Ruport
35
80
  end
36
81
  end
37
82
 
83
+ # === Overview
84
+ #
85
+ # This module contains methods that will be made available as singleton
86
+ # class methods to any ActiveRecord model that calls
87
+ # <tt>acts_as_reportable</tt>.
88
+ #
38
89
  module SingletonMethods
39
90
 
40
91
  # Creates a Ruport::Data::Table from an ActiveRecord find. Takes
41
- # parameters just like a regular find. If you use the :include
42
- # option, it will return a table with all columns from the model and
43
- # the included associations. If you use the :only option, it will
44
- # return a table with only the specified columns. If you use the
45
- # :except option, it will return a table with all columns except
46
- # those specified.
47
- #
48
- # Options may be passed to the :include option in order to specify
49
- # the output for any associated models. In this case, the :include
50
- # option must be a hash, where the keys are the names of the
51
- # associations and the values are hashes of options.
92
+ # parameters just like a regular find.
52
93
  #
53
- # Use the :methods option to include a column with the same name as
54
- # the method and the value resulting from calling the method on the
55
- # model object.
94
+ # Additional options include:
95
+ #
96
+ # <b><tt>:only</tt></b>:: an attribute name or array of attribute
97
+ # names to include in the results, other
98
+ # attributes will be excuded.
99
+ # <b><tt>:except</tt></b>:: an attribute name or array of attribute
100
+ # names to exclude from the results.
101
+ # <b><tt>:methods</tt></b>:: a method name or array of method names
102
+ # whose result(s) will be included in the
103
+ # table.
104
+ # <b><tt>:include</tt></b>:: an associated model or array of associated
105
+ # models to include in the results.
106
+ #
107
+ # The same set of options may be passed to the :include option in order to
108
+ # specify the output for any associated models. In this case, the
109
+ # :include option must be a hash, where the keys are the names of the
110
+ # associations and the values are hashes of options.
56
111
  #
57
112
  # Any options passed to report_table will disable the options set by
58
113
  # the acts_as_reportable class method.
59
114
  #
60
115
  # Example:
61
116
  #
62
- # class Book < ActiveRecord::Base
63
- # belongs_to :author
64
- # acts_as_reportable
65
- # end
117
+ # class Book < ActiveRecord::Base
118
+ # belongs_to :author
119
+ # acts_as_reportable
120
+ # end
66
121
  #
67
- # Book.report_table(:all, :only => ['title'],
68
- # :include => { :author => { :only => 'name' } }).as(:html)
122
+ # Book.report_table(:all, :only => ['title'],
123
+ # :include => { :author => { :only => 'name' } }).as(:html)
69
124
  #
70
- # Returns: an html version of a report with two columns, title from
125
+ # Returns:
126
+ #
127
+ # an html version of the table with two columns, title from
71
128
  # the book, and name from the associated author.
72
129
  #
73
- # Calling Book.report_table(:all, :include => [:author]).as(:html) will
74
- # return a table with all columns from books and authors.
130
+ # Example:
131
+ #
132
+ # Book.report_table(:all, :include => :author).as(:html)
133
+ #
134
+ # Returns:
135
+ #
136
+ # an html version of the table with all columns from books and authors.
75
137
  #
76
138
  # Note: column names for attributes of included models will be qualified
77
- # with the model's underscored class name, e.g. 'author.name'
78
- # By default, this will not preserve the entire namespace, but you
79
- # can get the fully qualified namespace by using the
80
- # :preserve_namespace => true option to report_table. So if the
81
- # Author model was enclosed in a module called MyModule, you'd
82
- # get 'my_module/author.name' as the column name.
139
+ # with the model's underscored class name, e.g. 'author.name'
140
+ # By default, this will not preserve the entire namespace, but you
141
+ # can get the fully qualified namespace by using the
142
+ # :preserve_namespace => true option to report_table. So if the
143
+ # Author model was enclosed in a module called MyModule, you'd
144
+ # get 'my_module/author.name' as the column name.
83
145
  #
84
146
  def report_table(number = :all, options = {})
85
147
  only = options.delete(:only)
@@ -105,12 +167,12 @@ module Ruport
105
167
 
106
168
  private
107
169
 
108
- def get_include_for_find(report_option) #:nodoc:
170
+ def get_include_for_find(report_option)
109
171
  includes = report_option.blank? ? aar_options[:include] : report_option
110
172
  includes.is_a?(Hash) ? includes.keys : includes
111
173
  end
112
174
 
113
- def normalize_column_names(table) #:nodoc:
175
+ def normalize_column_names(table)
114
176
  renamed = table.column_names.inject({}) do |s,c|
115
177
  s.merge(c => c.sub(/.*\//,""))
116
178
  end
@@ -118,6 +180,11 @@ module Ruport
118
180
  end
119
181
  end
120
182
 
183
+ # === Overview
184
+ #
185
+ # This module contains methods that will be made available as instance
186
+ # methods to any ActiveRecord model that calls <tt>acts_as_reportable</tt>.
187
+ #
121
188
  module InstanceMethods
122
189
 
123
190
  # Grabs all of the object's attributes and the attributes of the
@@ -135,16 +202,18 @@ module Ruport
135
202
  #
136
203
  # Example:
137
204
  #
138
- # class Book < ActiveRecord::Base
139
- # belongs_to :author
140
- # acts_as_reportable
141
- # end
205
+ # class Book < ActiveRecord::Base
206
+ # belongs_to :author
207
+ # acts_as_reportable
208
+ # end
142
209
  #
143
- # abook.reportable_data(:only => ['title'], :include => [:author])
210
+ # abook.reportable_data(:only => ['title'], :include => [:author])
144
211
  #
145
- # Returns: [{'title' => 'book title',
146
- # 'author.id' => 'author id',
147
- # 'author.name' => 'author name' }]
212
+ # Returns:
213
+ #
214
+ # [{'title' => 'book title',
215
+ # 'author.id' => 'author id',
216
+ # 'author.name' => 'author name' }]
148
217
  #
149
218
  # NOTE: title will only be returned if the value exists in the table.
150
219
  # If the books table does not have a title column, it will not be
@@ -152,11 +221,13 @@ module Ruport
152
221
  #
153
222
  # Example:
154
223
  #
155
- # abook.reportable_data(:only => ['title'],
156
- # :include => { :author => { :only => ['name'] } })
224
+ # abook.reportable_data(:only => ['title'],
225
+ # :include => { :author => { :only => ['name'] } })
226
+ #
227
+ # Returns:
157
228
  #
158
- # Returns: [{'title' => 'book title',
159
- # 'author.name' => 'author name' }]
229
+ # [{'title' => 'book title',
230
+ # 'author.name' => 'author name' }]
160
231
  #
161
232
  def reportable_data(options = {})
162
233
  options = options.merge(self.class.aar_options) unless