ruport 0.4.11 → 0.4.13

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