ruport 0.4.11 → 0.4.13

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/CHANGELOG CHANGED
@@ -1,4 +1,32 @@
1
- The current version of Ruby Reports is 0.4.11
1
+ The current version of Ruby Reports is 0.4.13
2
+
3
+ changes since 0.4.11
4
+
5
+ - Added high level hook Report#text_processor which essentially works like
6
+ Format.register_filter
7
+
8
+ - Report can now run single or multiple reports and do things such as send them
9
+ via email or write/append them to files.
10
+
11
+ - Report has been overhauled to have a simple DSL
12
+
13
+ - Mailfactory is now a gem dependency. If you install via setup.rb or building
14
+ the gem yourself, you only need it if you plan to use Ruport::Mailer
15
+
16
+ - Report#query now has an 'as' method that allows direct translation of a query
17
+ to a particular format, e.g.
18
+
19
+ query "select * from ruport", :as => :pdf
20
+
21
+ - Lots of bug fixes and cleanup
22
+
23
+ - Ruport is now on Trac.
24
+ http://stonecode.svnrepository.com/ruport/
25
+
26
+ - I apparently never knew how to use inject.
27
+ Injects are all functional now.
28
+
29
+ - Fixed a bug in DataSet that made fields not get duped properly
2
30
 
3
31
  changes since 0.4.9
4
32
 
data/README CHANGED
@@ -1,8 +1,8 @@
1
1
  # ------------------------------------------------------------------------
2
2
  # -------------------------------------------------------------------------
3
- #
4
- # WARNING: THIS DOCUMENT IS FREQUENTLY OUT OF DATE.
5
3
  #
4
+ # NOTE:
5
+ #
6
6
  # The most up to date information can be found at:
7
7
  # http://reporting.stonecode.org
8
8
  #
@@ -24,9 +24,9 @@
24
24
  #
25
25
  # - Installation
26
26
  #
27
- # Optional Dependencies:
27
+ # Dependencies:
28
28
  #
29
- # Ruport has a number of dependencies:
29
+ # Ruport has a number of dependencies.
30
30
  #
31
31
  # Ruby/DBI and appropriate dbds: Makes Query useable
32
32
  # (must be installed manually)
@@ -37,9 +37,11 @@
37
37
  # RedCloth: Enables textile/markdown filtering
38
38
  # (available via rubygems)
39
39
  #
40
- # PDF::Writer: Enables printable documents via render_pdf (Experimental)
40
+ # PDF::Writer: Enables printable documents
41
41
  # (available via rubygems)
42
42
  #
43
+ # MailFactory: For email support.
44
+ #
43
45
  # Note that by installing any of the dependencies, either via gems or manually,
44
46
  # their functionality will automatically be enabled.
45
47
  #
@@ -64,7 +66,7 @@
64
66
  #
65
67
  # - Caveats
66
68
  #
67
- # Ruport is alpha software. It's not completely tested and the API is
69
+ # Ruport is experimental software. It's not completely tested and the API is
68
70
  # changing rapidly from version to version. Test suites are becoming
69
71
  # increasingly robust, but have not identified all possible edge cases. If
70
72
  # Ruport goes wild on you, it's because it hasn't been tamed yet.
@@ -105,9 +107,9 @@
105
107
  # http://rubyforge.org/projects/ruport
106
108
  #
107
109
  # - The latest stable API documentation is available at:
108
- # http://ruport.rubyforge.org/docs
110
+ # http://reporting.stonecode.org/docs
109
111
  #
110
- # There also will be some tutorials on stonecode.org
112
+ # There also will be some tutorials on ruport.infogami.com
111
113
  #
112
114
  # If you are interested in developing Ruport, please *never* study code in
113
115
  # official releases. As this software is in it's early stages, it's essential
@@ -121,7 +123,7 @@
121
123
  #
122
124
  # Those who would like to become regular contributors will be given write
123
125
  # access. Also, anyone interested in the internal design and project
124
- # management aspects of Ruport can request to be added to our basecamp
126
+ # management aspects of Ruport can request to be added to our Trac
125
127
  # account. This is primarily intended for people who are working on the
126
128
  # project actively, though.
127
129
  #
@@ -150,25 +152,13 @@
150
152
  # Format provides support for building filters and specialized formatting
151
153
  # systems.
152
154
  #
153
- # Query currently provides a high level interface to DBI. Soon it will support
154
- # a mixin wrapper called Fetchable which will enable you to wrap whatever data
155
- # source you choose. If you would like to query a database or load a sql dump,
156
- # this class can help you do that. It can generate DataSets on the fly, or feed
157
- # you DBI:Rows, depending on what you need.
158
- #
159
- # There is also a Config class which allows you to set things such as data
160
- # sources, mailer information, and logging. Ruport#complain provides a robust
161
- # way to handle error logging and warnings.
162
- #
163
- # Finally, Ruport provides a powerful yet oh-so-scary parsing tool. It is
164
- # essentially James Edward Gray II's Parse::Input library within the Ruport
165
- # library. (And will soon be integrated nicely too)
166
- #
167
- # Read the source of ruport/parser.rb if you have some gnarly data you need to
168
- # munge.
155
+ # Query currently provides a high level interface to DBI.
156
+ # If you would like to query a database or load a sql dump,
157
+ # this class can help you do that. It can generate DataSets on the fly,
158
+ # or feed you DBI:Rows, depending on what you need.
169
159
  #
170
160
  # Finally, Please consult the API documentation and/or source code for more
171
- # information. (http://ruport.rubyforge.org/docs). Not all classes have been
161
+ # information. (http://reporting.stonecode.org/docs). Not all classes have been
172
162
  # documented but the ones that have may be easier to understand when their docs
173
163
  # have been read. Also, feel free to contribute documentation.
174
164
  #
data/Rakefile CHANGED
@@ -21,7 +21,7 @@ end
21
21
 
22
22
  spec = Gem::Specification.new do |spec|
23
23
  spec.name = LEAN ? "lean-ruport" : "ruport"
24
- spec.version = "0.4.11"
24
+ spec.version = "0.4.13"
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}/**/**/*") +
@@ -39,6 +39,7 @@ spec = Gem::Specification.new do |spec|
39
39
  spec.add_dependency('fastercsv', '>= 0.1.0')
40
40
  spec.add_dependency('RedCloth', '>= 3.0.0')
41
41
  spec.add_dependency('pdf-writer', '>= 1.1.3')
42
+ spec.add_dependency("mailfactory", ">= 1.2.2")
42
43
  end
43
44
  spec.author = "Gregory Brown"
44
45
  spec.email = " gregory.t.brown@gmail.com"
data/TODO CHANGED
@@ -4,6 +4,8 @@ Immediate Goals:
4
4
 
5
5
  Features:
6
6
 
7
+ - PDF document support + example
8
+
7
9
  - event system
8
10
 
9
11
  - Composite key selection
@@ -14,24 +16,8 @@ Immediate Goals:
14
16
 
15
17
  - Column based default values
16
18
 
17
- - Integrate Ruport::Parser into Report#parse and Format#parser
18
-
19
19
  - Document the inner classes of Format
20
20
 
21
21
  - make the Fetchable module to abstract data acquisition
22
22
 
23
23
  - Calculated fields
24
-
25
- Other High Priority Goals:
26
-
27
- - Offer charting support in Format
28
-
29
- Community Requests:
30
-
31
- - Integration with Rails (ActiveRecord)
32
-
33
- Other (Unordered) Goals:
34
-
35
- - Support KirbyBase
36
-
37
- - More Secure passwords for SQL
@@ -0,0 +1,35 @@
1
+ require "ruport"
2
+ require "fileutils"
3
+ class MyReport < Ruport::Report
4
+ prepare do
5
+ log_file "f.log"
6
+ log "preparing report", :status => :info
7
+ source :default,
8
+ :dsn => "dbi:mysql:foo",
9
+ :user => "root"
10
+ mailer :default,
11
+ :host => "mail.adelphia.net",
12
+ :address => "gregory.t.brown@gmail.com"
13
+ end
14
+
15
+ generate do
16
+ log "generated csv from query", :status => :info
17
+ query "select * from bar", :as => :csv
18
+ end
19
+
20
+ cleanup do
21
+ log "removing foo.csv", :status => :info
22
+ FileUtils.rm("foo.csv")
23
+ end
24
+ end
25
+
26
+ MyReport.run { |res|
27
+ res.write "foo.csv";
28
+ res.send_to("greg7224@gmail.com") do |mail|
29
+ mail.subject = "Sample report"
30
+ mail.attach "foo.csv"
31
+ mail.text = <<-EOS
32
+ this is a sample of sending an emailed report from within Ruport.
33
+ EOS
34
+ end
35
+ }
@@ -0,0 +1,15 @@
1
+ require "ruport"
2
+
3
+ TEMPLATE = <<-EOS
4
+
5
+ My HTML Table:
6
+ <%= query "select * from bar", :as => :html %>
7
+
8
+ EOS
9
+
10
+ class MyReport < Ruport::Report
11
+ prepare { source :default, :dsn => "dbi:mysql:foo", :user => "root" }
12
+ generate { eval_template TEMPLATE }
13
+ end
14
+
15
+ MyReport.run
@@ -0,0 +1,20 @@
1
+ require "ruport"
2
+
3
+
4
+ class MyReport < Ruport::Report
5
+ prepare {
6
+ self.results = "Foo Bar Baz"
7
+ text_processor(:replace_foo) { results.gsub!(/Foo/,"Ruport") }
8
+ text_processor(:replace_bar) { results.gsub!(/Bar/,"Is") }
9
+ text_processor(:replace_baz) { results.gsub!(/Baz/, "Cool!") }
10
+ }
11
+ generate {
12
+ process_text results, :filters => [:replace_foo,:replace_bar,:replace_baz]
13
+ }
14
+ end
15
+
16
+ MyReport.run { |e| puts e.results }
17
+
18
+
19
+
20
+
data/lib/ruport.rb CHANGED
@@ -14,7 +14,7 @@ module Ruport
14
14
 
15
15
  begin; require 'rubygems'; rescue LoadError; nil end
16
16
 
17
- VERSION = "Ruby Reports Version 0.4.11"
17
+ VERSION = "Ruby Reports Version 0.4.13"
18
18
 
19
19
  # Ruports logging and error interface.
20
20
  # Can generate warnings or raise fatal errors
@@ -39,24 +39,21 @@ module Ruport
39
39
  #
40
40
  # If you want to recover these messages to secondary output for debugging, you
41
41
  # can use Config::enable_paranoia
42
- def Ruport.complain(message,options={})
43
- options[:status] ||= :warn
44
- options[:output] ||= $stderr
42
+ def Ruport.log(message,options={})
43
+ options = {:status => :warn, :output => $stderr}.merge(options)
45
44
  options[:output].puts "[!!] #{message}" unless
46
45
  options[:level].eql?(:log_only) and not Ruport::Config.paranoid?
47
- case(options[:status])
48
- when :warn
49
- Ruport::Config::logger.warn(message) if Ruport::Config.logger
50
- when :fatal
51
- Ruport::Config::logger.fatal(message) if Ruport::Config.logger
52
- raise options[:exception] || RuntimeError, message
46
+ Ruport::Config::logger.send(options[:status],message) if Config.logger
47
+ if options[:status].eql? :fatal
48
+ raise(options[:exception] || RuntimeError, message)
53
49
  end
54
50
  end
55
-
51
+
52
+ def Ruport.complain(*args); Ruport.log(*args) end
53
+
56
54
  def Ruport.configure(&block)
57
55
  block.call(Ruport::Config)
58
56
  end
59
-
60
57
  end
61
58
 
62
59
 
data/lib/ruport/config.rb CHANGED
@@ -59,7 +59,7 @@ module Ruport
59
59
  #
60
60
  class Config
61
61
  include Singleton
62
-
62
+
63
63
  def Config.method_missing(method_id,*args)
64
64
  case(method_id)
65
65
  when :source
@@ -69,7 +69,7 @@ module Ruport
69
69
  when :mailer
70
70
  @@mailers[args.first] = OpenStruct.new(*args[1..-1])
71
71
  check_mailer(@@mailers[args.first],args.first)
72
- when :log_file
72
+ when :log_file,:log_file=
73
73
  @@logger = Logger.new(args.first)
74
74
  when :default_source
75
75
  @@sources[:default]
@@ -39,7 +39,7 @@ module Ruport
39
39
  # length, empty?, delete_at, first, last, pop
40
40
  #
41
41
  def initialize(fields=nil, options={})
42
- @fields = fields
42
+ @fields = fields.dup if fields
43
43
  @default = options[:default] || default
44
44
  @data = []
45
45
  options[:data].each { |r| self << r } if options[:data]
@@ -97,6 +97,8 @@ module Ruport
97
97
  #
98
98
  # data << [ 1, 2, 3 ]
99
99
  # data << { :some_field_name => 3, :other => 2, :another => 1 }
100
+ #
101
+ # FIXME: Appending a datarow is wonky.
100
102
  def << ( stuff, filler=@default )
101
103
  if stuff.kind_of?(DataRow)
102
104
  @data << stuff.clone
@@ -184,9 +186,10 @@ module Ruport
184
186
  lambda { |r| loaded_data << r }
185
187
  end
186
188
 
187
- loaded_data.fields = input[0] if options[:has_names]
188
- input = input[1..-1] if options[:has_names]
189
-
189
+ if options[:has_names]
190
+ loaded_data.fields = input[0] ; input = input[1..-1]
191
+ end
192
+
190
193
  loaded_data.default = options[:default]
191
194
  input.each { |row| action[row] }
192
195
  return loaded_data
@@ -199,7 +202,7 @@ module Ruport
199
202
  # Returns a new DataSet composed of the fields specified.
200
203
  def select_columns(*fields)
201
204
  fields = get_field_names(fields)
202
- rows = fields.inject([]) { |s,e| s << map { |row| row[e] } }.transpose
205
+ rows = fields.inject([]) { |s,e| s + [map { |row| row[e] }] }.transpose
203
206
  my_data = DataSet.new(fields, :data => rows)
204
207
  end
205
208
 
@@ -257,7 +260,7 @@ module Ruport
257
260
  # Only works with blocks resulting in numeric values.
258
261
  def sigma
259
262
  inject(0) do |s,r|
260
- s += yield(r) || 0
263
+ s + (yield(r) || 0)
261
264
  end
262
265
  end
263
266
 
@@ -281,7 +284,7 @@ module Ruport
281
284
 
282
285
  def get_field_names(f)
283
286
  f.all? { |e| e.kind_of? Integer } &&
284
- f.inject([]) { |s,e| s << @fields[e] } || f
287
+ f.inject([]) { |s,e| s + [@fields[e]] } || f
285
288
  end
286
289
 
287
290
  end
data/lib/ruport/format.rb CHANGED
@@ -59,41 +59,40 @@ module Ruport
59
59
  # This part of Ruport is under active development. Please do feel free to
60
60
  # submit feature requests or suggestions.
61
61
  class Format
62
-
63
- def Format.build_interface_for(engine,name)
64
- (class << self; self; end).send(:define_method, name,
65
- lambda { |options| simple_interface(engine, options) })
66
- (class << self; self; end).send(:define_method, "#{name}_object",
67
- lambda { |options|
68
- options[:auto_render] = false; simple_interface(engine,options) })
69
- end
70
-
71
- %w[open_node document engine plugin].each { |lib|
72
- require "ruport/format/#{lib}"
73
- }
74
-
75
- class << self
76
-
77
- def simple_interface(engine, options={})
78
- my_engine = engine.dup
79
-
80
- my_engine.send(:plugin=,options[:plugin])
81
- options = my_engine.active_plugin.rendering_options.merge(options)
82
-
83
- options[:auto_render] = true unless options.has_key? :auto_render
84
-
62
+
63
+ # Builds a simple interface to a formatting engine.
64
+ # Two of these interfaces are built into Ruport:
65
+ # Format.document and Format.table
66
+ #
67
+ # These interfaces pass a hash of keywords to the associative engine.
68
+ # Here is a simple example:
69
+ #
70
+ # Format.build_interface_for Format::Engine::Yable, "table"
71
+ #
72
+ # This will allow the following code to work:
73
+ #
74
+ # Format.table :data => [[1,2],[3,4]], :plugin => :csv
75
+ #
76
+ # So, if you want to create a standard interface to a
77
+ # custom built engine, you could simply do something like:
78
+ #
79
+ # Format.build_interface_for MyCustomEngine, "my_name"
80
+ #
81
+ # which would be accessible via
82
+ #
83
+ # Format.my_name ...
84
+ def Format.build_interface_for(engine,name)
85
+ (class << self; self; end).send(:define_method, name,
86
+ lambda { |options| simple_interface(engine, options) })
87
+ (class << self; self; end).send(:define_method, "#{name}_object",
88
+ lambda { |options|
89
+ options[:auto_render] = false; simple_interface(engine,options) })
90
+ end
85
91
 
86
- options[:data] = options[:data].dup
87
-
88
- options.each do |k,v|
89
- my_engine.send("#{k}=",v) if my_engine.respond_to? k
90
- end
91
-
92
- options[:auto_render] ? my_engine.render : my_engine.dup
93
- end
92
+ %w[open_node document engine plugin].each { |lib|
93
+ require "ruport/format/#{lib}"
94
+ }
94
95
 
95
- end
96
-
97
96
  @@filters = Hash.new
98
97
 
99
98
  # To hook up a Format object to your current class, you need to pass it a
@@ -101,7 +100,7 @@ module Ruport
101
100
  # evaluated in the context of the object they are being called from, rather
102
101
  # than within an instance of Format.
103
102
  #
104
- def initialize(klass_binding)
103
+ def initialize(klass_binding=binding)
105
104
  @binding = klass_binding
106
105
  end
107
106
 
@@ -143,9 +142,32 @@ module Ruport
143
142
  end
144
143
 
145
144
  def method_missing(m)
146
- @@filters[m] ? @@filters[m].call(@content) : super
145
+ @@filters[m] ? @@filters[m][@content] : super
147
146
  end
148
-
147
+
148
+
149
+ private
150
+
151
+ def Format.simple_interface(engine, options={})
152
+ my_engine = engine.dup
153
+
154
+ my_engine.send(:plugin=,options[:plugin])
155
+ options = my_engine.active_plugin.rendering_options.merge(options)
156
+
157
+ options[:auto_render] = true unless options.has_key? :auto_render
158
+
159
+
160
+ options[:data] = options[:data].dup
161
+
162
+ options.each do |k,v|
163
+ my_engine.send("#{k}=",v) if my_engine.respond_to? k
164
+ end
165
+
166
+ options[:auto_render] ? my_engine.render : my_engine.dup
167
+ end
168
+
169
+
170
+
149
171
  end
150
172
  end
151
173