ruport 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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