ruport 0.5.4 → 0.6.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.
- data/AUTHORS +13 -4
- data/CHANGELOG +15 -1
- data/Rakefile +1 -1
- data/bin/rope +5 -1
- data/examples/basic_grouping.rb +13 -3
- data/examples/latex_table.rb +17 -0
- data/examples/line_graph_report.rb +23 -0
- data/examples/line_graph_report.rb.rej +15 -0
- data/examples/line_graph_report.rb~ +23 -0
- data/examples/line_plotter.rb +1 -0
- data/examples/sql_erb.rb +20 -0
- data/examples/template.rb +1 -1
- data/examples/text_processors.rb +3 -9
- data/lib/ruport.rb +2 -3
- data/lib/ruport.rb~ +69 -0
- data/lib/ruport/attempt.rb +63 -0
- data/lib/ruport/data.rb +1 -1
- data/lib/ruport/data.rb.rej +5 -0
- data/lib/ruport/data.rb~ +1 -0
- data/lib/ruport/data/groupable.rb +20 -0
- data/lib/ruport/data/record.rb +18 -13
- data/lib/ruport/data/table.rb +92 -26
- data/lib/ruport/data/table.rb~ +329 -0
- data/lib/ruport/format.rb +7 -1
- data/lib/ruport/format/engine/table.rb +2 -1
- data/lib/ruport/format/plugin.rb +8 -8
- data/lib/ruport/format/plugin/csv_plugin.rb +5 -1
- data/lib/ruport/format/plugin/html_plugin.rb +12 -8
- data/lib/ruport/format/plugin/latex_plugin.rb +50 -0
- data/lib/ruport/format/plugin/text_plugin.rb +38 -33
- data/lib/ruport/meta_tools.rb +3 -3
- data/lib/ruport/query.rb +21 -9
- data/lib/ruport/report.rb +35 -20
- data/lib/ruport/report/graph.rb +14 -0
- data/lib/ruport/system_extensions.rb +9 -8
- data/test/_test_groupable.rb +0 -0
- data/test/samples/data.tsv +3 -0
- data/test/samples/erb_test.sql +1 -0
- data/test/samples/query_test.sql +1 -0
- data/test/test_collection.rb +1 -1
- data/test/test_format.rb +1 -1
- data/test/test_format_engine.rb +17 -0
- data/test/test_groupable.rb +41 -0
- data/test/test_invoice.rb +1 -1
- data/test/test_latex.rb +20 -0
- data/test/test_plugin.rb +59 -29
- data/test/test_query.rb +12 -6
- data/test/test_record.rb +23 -4
- data/test/test_record.rb.rej +46 -0
- data/test/test_report.rb +32 -7
- data/test/test_table.rb +152 -4
- data/test/ts_all.rb +21 -0
- data/test/unit.log +61 -154
- metadata +32 -12
- data/lib/ruport/rails.rb +0 -2
- data/lib/ruport/rails/reportable.rb +0 -58
data/AUTHORS
CHANGED
@@ -8,17 +8,26 @@
|
|
8
8
|
= Contributors / People we've (legally) stolen from:
|
9
9
|
|
10
10
|
Iain Broadfoot:
|
11
|
-
- RuportDay 2006 Participant
|
11
|
+
- RuportDay 2006 Participant
|
12
12
|
|
13
13
|
Eric Pugh:
|
14
|
-
- RuportDay 2006 Participant
|
14
|
+
- RuportDay 2006 Participant
|
15
15
|
|
16
16
|
James Edward Gray II:
|
17
17
|
- Original inspiration via query.rb
|
18
18
|
- system_extensions.rb
|
19
19
|
|
20
|
+
Mathijs Mohlmann:
|
21
|
+
- Performance Enhancement
|
22
|
+
TextPlugin r264-r265
|
23
|
+
HTMLPlugin r266
|
24
|
+
|
20
25
|
Francis Hwang:
|
21
|
-
- SQLSplit
|
26
|
+
- SQLSplit
|
22
27
|
|
23
28
|
Simon Claret:
|
24
|
-
- Initial PDF table support (now deprecated)
|
29
|
+
- Initial PDF table support (now deprecated)
|
30
|
+
|
31
|
+
Daniel Berger:
|
32
|
+
- we vendored and modified attempt.rb to support it directly in Report.
|
33
|
+
Original website: http://raa.ruby-lang.org/project/attempt/
|
data/CHANGELOG
CHANGED
@@ -1,4 +1,18 @@
|
|
1
|
-
|
1
|
+
The current version of Ruby Reports is 0.6.0
|
2
|
+
|
3
|
+
changes since 0.5.4
|
4
|
+
|
5
|
+
From Ruport News (http://www.stonecode.org/blog/?cat=14)
|
6
|
+
|
7
|
+
* added ERb support inside SQL queries
|
8
|
+
* a new LaTeX plugin for the table formatting engine
|
9
|
+
* support for summation in data tables
|
10
|
+
* new grouping functionality based on the tagging system
|
11
|
+
* very large performance enhancements in HTML and text rendering
|
12
|
+
* better integration with FasterCSV
|
13
|
+
* some simple interfaces for graphs and data tables
|
14
|
+
* syntactic sugar (shortcuts) for common tasks
|
15
|
+
* support for rerunning reports on timeouts and errors
|
2
16
|
|
3
17
|
changes since 0.5.3
|
4
18
|
|
data/Rakefile
CHANGED
@@ -23,7 +23,7 @@ end
|
|
23
23
|
|
24
24
|
spec = Gem::Specification.new do |spec|
|
25
25
|
spec.name = LEAN ? "lean-ruport" : "ruport"
|
26
|
-
spec.version = "0.
|
26
|
+
spec.version = "0.6.0"
|
27
27
|
spec.platform = Gem::Platform::RUBY
|
28
28
|
spec.summary = "A generalized Ruby report generation and templating engine."
|
29
29
|
spec.files = Dir.glob("{examples,lib,test,bin}/**/**/**/*") +
|
data/bin/rope
CHANGED
@@ -68,7 +68,11 @@ class #{class_name} < Ruport::Report
|
|
68
68
|
|
69
69
|
end
|
70
70
|
|
71
|
-
|
71
|
+
# uncomment the line below to let the report be run directly
|
72
|
+
#
|
73
|
+
# if __FILE__ == $0
|
74
|
+
# #{class_name}.run { |res| puts res.results }
|
75
|
+
# end
|
72
76
|
EOR
|
73
77
|
|
74
78
|
TEST = <<EOR
|
data/examples/basic_grouping.rb
CHANGED
@@ -1,9 +1,19 @@
|
|
1
1
|
require "ruport"
|
2
2
|
a = [ ['a',7],['b',5],['c',11],
|
3
|
-
['d',9],['a',3],['b',2] ].to_table(%w[letter num])
|
3
|
+
['d',9],['a',3],['b',2], ['e',4] ].to_table(%w[letter num])
|
4
4
|
puts "Initial Data:\n#{a}"
|
5
|
+
|
6
|
+
|
7
|
+
# group by column values
|
5
8
|
b = a.split(:group => "letter")
|
6
9
|
totals = [].to_table(%w[group sum])
|
7
|
-
b.
|
8
|
-
puts "After grouping:\n#{totals}"
|
10
|
+
b.each_group { |x| totals << [x,b[x].sum("num")] }
|
11
|
+
puts "After column grouping:\n#{totals}"
|
9
12
|
|
13
|
+
# group by tag name
|
14
|
+
a.create_tag_group(:num_even) { |r| (r.num % 2).zero? }
|
15
|
+
a.create_tag_group(:num_odd) { |r| (r.num % 2).nonzero? }
|
16
|
+
c = a.group_by_tag
|
17
|
+
totals.data.clear
|
18
|
+
c.each_group { |x| totals << [x,c[x].sum("num")] }
|
19
|
+
puts "After tag grouping:\n#{totals}"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + "/../lib/")
|
2
|
+
require "ruport"
|
3
|
+
|
4
|
+
# build up a Table - this could be built in a range of ways
|
5
|
+
# Check the API documentation and recipe book for more information
|
6
|
+
data = [[1,4,7,9], [6,2,3,0]].to_table(%w[a b c d])
|
7
|
+
|
8
|
+
# Build the report object
|
9
|
+
report = Ruport::Format.table_object(:plugin => :latex, :data => data)
|
10
|
+
|
11
|
+
# By default, the latex plugin will return plain text latex source
|
12
|
+
# changing the format option asks Ruport to attempt to render
|
13
|
+
# the source into a PDF using pdflatex
|
14
|
+
report.options = { :format => :pdf }
|
15
|
+
|
16
|
+
# save the resulting report to a file on the filesystem
|
17
|
+
File.open( "table.tex","w") { |f| f.puts report.render }
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "ruport"
|
2
|
+
class GraphSample < Ruport::Report
|
3
|
+
|
4
|
+
include Graph
|
5
|
+
|
6
|
+
prepare do
|
7
|
+
@data = [[5,7,9,12,14,16,18]].to_table(%w[jan feb mar apr may jun jul])
|
8
|
+
end
|
9
|
+
|
10
|
+
generate do
|
11
|
+
render_graph do |g|
|
12
|
+
g.data = @data
|
13
|
+
g.width = 700
|
14
|
+
g.height = 500
|
15
|
+
g.title = "A Simple Line Graph"
|
16
|
+
g.style = :line
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
GraphSample.run { |r| puts r.results }
|
22
|
+
|
23
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "ruport"
|
2
|
+
class GraphSample < Ruport::Report
|
3
|
+
|
4
|
+
include Graph
|
5
|
+
|
6
|
+
prepare do
|
7
|
+
@data = [[5,7,9,12,14,16,18]].to_table(%w[jan feb mar apr may jun jul])
|
8
|
+
end
|
9
|
+
|
10
|
+
generate do
|
11
|
+
render_graph do |g|
|
12
|
+
g.data = @data
|
13
|
+
g.width = 700
|
14
|
+
g.height = 500
|
15
|
+
g.title = "A Simple Line Graph"
|
16
|
+
g.style = :line
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
GraphSample.run { |r| puts r.results }
|
22
|
+
|
23
|
+
|
data/examples/line_plotter.rb
CHANGED
data/examples/sql_erb.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "ruport"
|
2
|
+
require "rubygems"
|
3
|
+
|
4
|
+
class Foo < Ruport::Report
|
5
|
+
|
6
|
+
SQL = "select * from <%= helper %>"
|
7
|
+
|
8
|
+
prepare do
|
9
|
+
source :default, :dsn => "dbi:mysql:foo", :user => "root"
|
10
|
+
end
|
11
|
+
|
12
|
+
generate do
|
13
|
+
query(SQL)
|
14
|
+
end
|
15
|
+
|
16
|
+
def helper; "bar" end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
Foo.run { |r| puts r.results }
|
data/examples/template.rb
CHANGED
data/examples/text_processors.rb
CHANGED
@@ -3,17 +3,11 @@ require "ruport"
|
|
3
3
|
class MyReport < Ruport::Report
|
4
4
|
prepare {
|
5
5
|
self.results = "Foo Bar Baz"
|
6
|
-
text_processor(:replace_foo) {
|
7
|
-
text_processor(:replace_bar) {
|
8
|
-
text_processor(:replace_baz) {
|
6
|
+
text_processor(:replace_foo) { |r| r.gsub(/Foo/,"Ruport") }
|
7
|
+
text_processor(:replace_bar) { |r| r.gsub(/Bar/,"Is") }
|
8
|
+
text_processor(:replace_baz) { |r| r.gsub(/Baz/, "Cool!") }
|
9
9
|
}
|
10
10
|
generate {
|
11
11
|
process_text results, :filters => [:replace_foo,:replace_bar,:replace_baz]
|
12
12
|
}
|
13
13
|
end
|
14
|
-
|
15
|
-
MyReport.run { |e| puts e.results }
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
data/lib/ruport.rb
CHANGED
@@ -9,10 +9,9 @@
|
|
9
9
|
#
|
10
10
|
# See LICENSE and COPYING for details
|
11
11
|
#
|
12
|
-
|
13
12
|
module Ruport
|
14
13
|
|
15
|
-
VERSION = "0.
|
14
|
+
VERSION = "0.6.0"
|
16
15
|
|
17
16
|
# Ruports logging and error interface.
|
18
17
|
# Can generate warnings or raise fatal errors
|
@@ -65,6 +64,6 @@ module Ruport
|
|
65
64
|
end
|
66
65
|
|
67
66
|
|
68
|
-
%w[config meta_tools report format query
|
67
|
+
%w[attempt config data meta_tools report format query mailer].each { |lib|
|
69
68
|
require "ruport/#{lib}"
|
70
69
|
}
|
data/lib/ruport.rb~
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# ruport.rb : Ruby Reports toplevel module
|
2
|
+
#
|
3
|
+
# Author: Gregory T. Brown (gregory.t.brown at gmail dot com)
|
4
|
+
#
|
5
|
+
# Copyright (c) 2006, All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. You may modify and redistribute this freely under
|
8
|
+
# your choice of the GNU General Public License or the Ruby License.
|
9
|
+
#
|
10
|
+
# See LICENSE and COPYING for details
|
11
|
+
#
|
12
|
+
module Ruport
|
13
|
+
|
14
|
+
VERSION = "0.6.0"
|
15
|
+
|
16
|
+
# Ruports logging and error interface.
|
17
|
+
# Can generate warnings or raise fatal errors
|
18
|
+
#
|
19
|
+
# Takes a message to display and a set of options.
|
20
|
+
# Will log to the file defined by Config::log_file
|
21
|
+
#
|
22
|
+
# Options:
|
23
|
+
# <tt>:status</tt>:: sets the severity level. defaults to <tt>:warn</tt>
|
24
|
+
# <tt>:output</tt>:: optional 2nd output, defaults to <tt>$stderr</tt>
|
25
|
+
# <tt>:level</tt>:: set to <tt>:log_only</tt> to disable secondary output
|
26
|
+
# <tt>:exception</tt>:: exception to throw on fail. Defaults to RunTimeError
|
27
|
+
#
|
28
|
+
# The status <tt>:warn</tt> will invoke Logger#warn. A status of
|
29
|
+
# <tt>:fatal</tt> will invoke Logger#fatal and raise an exception
|
30
|
+
#
|
31
|
+
# By default, <tt>log()</tt> will also print warnings to $stderr
|
32
|
+
# You can redirect this to any I/O object via <tt>:output</tt>
|
33
|
+
#
|
34
|
+
# You can prevent messages from appearing on the secondary output by setting
|
35
|
+
# <tt>:level</tt> to <tt>:log_only</tt>
|
36
|
+
#
|
37
|
+
# If you want to recover these messages to secondary output for debugging, you
|
38
|
+
# can use Config::enable_paranoia
|
39
|
+
def self.log(message,options={})
|
40
|
+
options = {:status => :warn, :output => $stderr}.merge(options)
|
41
|
+
options[:output].puts "[!!] #{message}" unless
|
42
|
+
options[:level].eql?(:log_only) and not Ruport::Config.paranoid?
|
43
|
+
Ruport::Config::logger.send(options[:status],message) if Config.logger
|
44
|
+
if options[:status].eql? :fatal
|
45
|
+
raise(options[:exception] || RuntimeError, message)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#Alias for Ruport.log
|
50
|
+
def self.complain(*args); Ruport.log(*args) end
|
51
|
+
|
52
|
+
# yields a Ruport::Config object, allowing you to specify configuration
|
53
|
+
# options.
|
54
|
+
#
|
55
|
+
# Example:
|
56
|
+
#
|
57
|
+
# Ruport.configure do |c|
|
58
|
+
# c.source :default, :dsn => "dbi:mysql:foo",
|
59
|
+
# :user => "clyde", :password => "pman"
|
60
|
+
# end
|
61
|
+
def self.configure(&block)
|
62
|
+
block.call(Ruport::Config)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
%w[attempt config meta_tools report format query data mailer].each { |lib|
|
68
|
+
require "ruport/#{lib}"
|
69
|
+
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
class Attempt
|
4
|
+
VERSION = '0.1.0'
|
5
|
+
|
6
|
+
# Number of attempts to make before failing. The default is 3.
|
7
|
+
attr_accessor :tries
|
8
|
+
|
9
|
+
# Number of seconds to wait between attempts. The default is 60.
|
10
|
+
attr_accessor :interval
|
11
|
+
|
12
|
+
# a level which ruport understands.
|
13
|
+
attr_accessor :log_level
|
14
|
+
|
15
|
+
# If set, this increments the interval with each failed attempt by that
|
16
|
+
# number of seconds.
|
17
|
+
attr_accessor :increment
|
18
|
+
|
19
|
+
# If set, the code block is further wrapped in a timeout block.
|
20
|
+
attr_accessor :timeout
|
21
|
+
|
22
|
+
# Determines which exception level to check when looking for errors to
|
23
|
+
# retry. The default is 'Exception' (i.e. all errors).
|
24
|
+
attr_accessor :level
|
25
|
+
|
26
|
+
# :call-seq:
|
27
|
+
# Attempt.new{ |a| ... }
|
28
|
+
#
|
29
|
+
# Creates and returns a new +Attempt+ object. Use a block to set the
|
30
|
+
# accessors.
|
31
|
+
#
|
32
|
+
def initialize
|
33
|
+
@tries = 3 # Reasonable default
|
34
|
+
@interval = 60 # Reasonable default
|
35
|
+
@increment = nil # Should be an int, if provided
|
36
|
+
@timeout = nil # Wrap the code in a timeout block if provided
|
37
|
+
@level = Exception # Level of exception to be caught
|
38
|
+
|
39
|
+
yield self if block_given?
|
40
|
+
end
|
41
|
+
|
42
|
+
def attempt
|
43
|
+
count = 1
|
44
|
+
begin
|
45
|
+
if @timeout
|
46
|
+
Timeout.timeout(@timeout){ yield }
|
47
|
+
else
|
48
|
+
yield
|
49
|
+
end
|
50
|
+
rescue @level => error
|
51
|
+
@tries -= 1
|
52
|
+
if @tries > 0
|
53
|
+
msg = "Error on attempt # #{count}: #{error}; retrying"
|
54
|
+
count += 1
|
55
|
+
Ruport.log(msg, :level => log_level)
|
56
|
+
@interval += @increment if @increment
|
57
|
+
sleep @interval
|
58
|
+
retry
|
59
|
+
end
|
60
|
+
raise
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/ruport/data.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
%w[taggable record collection table set].each { |l| require "ruport/data/#{l}" }
|
1
|
+
%w[groupable taggable record collection table set ].each { |l| require "ruport/data/#{l}" }
|
data/lib/ruport/data.rb~
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
%w[taggable record collection table set groupable].each { |l| require "ruport/data/#{l}" }
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Ruport::Data
|
2
|
+
module Groupable
|
3
|
+
|
4
|
+
def group_by_tag
|
5
|
+
r_tags = data.map {|row| row.tags}.flatten.uniq
|
6
|
+
d = r_tags.map do |t|
|
7
|
+
select {|row| row.tags.include? t }.to_table(column_names)
|
8
|
+
end
|
9
|
+
r = Record.new d, :attributes => r_tags
|
10
|
+
class << r
|
11
|
+
def each_group; attributes.each { |a| yield(a) }; end
|
12
|
+
end; r
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_tag_group(label,&block)
|
16
|
+
select(&block).each { |r| r.tag label }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
data/lib/ruport/data/record.rb
CHANGED
@@ -47,7 +47,11 @@ module Ruport::Data
|
|
47
47
|
end
|
48
48
|
else
|
49
49
|
@data = data.dup
|
50
|
-
@attributes = options[:attributes]
|
50
|
+
@attributes = if options[:attributes]
|
51
|
+
options[:attributes]
|
52
|
+
else
|
53
|
+
[]
|
54
|
+
end
|
51
55
|
end
|
52
56
|
end
|
53
57
|
|
@@ -68,8 +72,8 @@ module Ruport::Data
|
|
68
72
|
raise "Invalid index" unless index < @data.length
|
69
73
|
@data[index]
|
70
74
|
else
|
71
|
-
raise "Invalid index" unless
|
72
|
-
@data[
|
75
|
+
raise "Invalid index" unless attributes.index(index.to_s)
|
76
|
+
@data[attributes.index(index.to_s)]
|
73
77
|
end
|
74
78
|
end
|
75
79
|
|
@@ -86,8 +90,8 @@ module Ruport::Data
|
|
86
90
|
raise "Invalid index" unless index < @data.length
|
87
91
|
@data[index] = value
|
88
92
|
else
|
89
|
-
raise "Invalid index" unless
|
90
|
-
@data[attributes.index(index)] = value
|
93
|
+
raise "Invalid index" unless attributes.index(index.to_s)
|
94
|
+
@data[attributes.index(index.to_s)] = value
|
91
95
|
end
|
92
96
|
end
|
93
97
|
|
@@ -95,9 +99,9 @@ module Ruport::Data
|
|
95
99
|
# If attributes and data are equivalent, then == evaluates to true.
|
96
100
|
# Otherwise, == returns false
|
97
101
|
def ==(other)
|
98
|
-
return false if
|
99
|
-
return false if other.attributes &&
|
100
|
-
|
102
|
+
return false if attributes && !other.attributes
|
103
|
+
return false if other.attributes && !attributes
|
104
|
+
attributes == other.attributes && @data == other.data
|
101
105
|
end
|
102
106
|
|
103
107
|
alias_method :eql?, :==
|
@@ -113,19 +117,20 @@ module Ruport::Data
|
|
113
117
|
#
|
114
118
|
# a = Data::Record.new([1,2],:attributes => %w[a b])
|
115
119
|
# a.to_h #=> {"a" => 1, "b" => 2}
|
116
|
-
def to_h; Hash[
|
120
|
+
def to_h; Hash[*attributes.zip(data).flatten] end
|
117
121
|
|
118
122
|
# Returns a copy of the list of attribute names associated with this Record.
|
119
123
|
#
|
120
124
|
# a = Data::Record.new([1,2],:attributes => %w[a b])
|
121
125
|
# a.attributes #=> ["a","b"]
|
122
|
-
def attributes; @attributes
|
126
|
+
def attributes; @attributes.map { |a| a.to_s }; end
|
123
127
|
|
124
128
|
# Sets the attribute list for this Record
|
125
129
|
#
|
126
130
|
# my_record.attributes = %w[foo bar baz]
|
127
131
|
attr_writer :attributes
|
128
132
|
|
133
|
+
|
129
134
|
# Allows you to change the order of or reduce the number of columns in a
|
130
135
|
# Record. Example:
|
131
136
|
#
|
@@ -140,9 +145,9 @@ module Ruport::Data
|
|
140
145
|
# Same as Record#reorder but is destructive
|
141
146
|
def reorder!(*indices)
|
142
147
|
indices = reorder_data!(*indices)
|
143
|
-
if
|
148
|
+
if attributes
|
144
149
|
if indices.all? { |e| e.kind_of? Integer }
|
145
|
-
@attributes = indices.map { |i|
|
150
|
+
@attributes = indices.map { |i| attributes[i] }
|
146
151
|
else
|
147
152
|
@attributes = indices
|
148
153
|
end
|
@@ -181,7 +186,7 @@ module Ruport::Data
|
|
181
186
|
# my_record.foo #=> 2
|
182
187
|
def method_missing(id,*args)
|
183
188
|
id = id.to_s.gsub(/=$/,"")
|
184
|
-
if
|
189
|
+
if attributes.include?(id)
|
185
190
|
args.empty? ? self[id] : self[id] = args.first
|
186
191
|
else
|
187
192
|
super
|