ruport 0.4.9 → 0.4.11

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 CHANGED
@@ -2,22 +2,20 @@ Developers:
2
2
  ---------------------------------------------------
3
3
 
4
4
  {Gregory Brown}[mailto:gregory.t.brown@gmail.com]
5
+ {Dudley Flanders}[mailto:dudley@misnomer.us]
5
6
 
6
- Contributors / People we've (legally) stole from:
7
+ Contributors / People we've (legally) stolen from:
7
8
  ---------------------------------------------------
8
9
 
9
10
  James Edward Gray II:
10
- Original inspiration via query.rb
11
- Parse::Input which is the base for Ruport::Parser
11
+ - Original inspiration via query.rb
12
+ - system_extensions.rb
12
13
 
13
14
  Francis Hwang:
14
- SQLSplit
15
+ - SQLSplit
15
16
 
16
17
  Simon Claret:
17
- PDF table support for Format::Builder
18
-
19
- Dudley Flanders:
20
- DataSet improvements
18
+ - PDF table support
21
19
 
22
20
  Dinko Mehinovic:
23
- util/release/raa.rb
21
+ - util/release/raa.rb
data/CHANGELOG CHANGED
@@ -1,4 +1,39 @@
1
- The current version of Ruby Reports is 0.4.9
1
+ The current version of Ruby Reports is 0.4.11
2
+
3
+ changes since 0.4.9
4
+
5
+ - DataSet#column_names and DataRow#column_names have been added as alias to
6
+ fields.
7
+
8
+ - Plugins are now safely copied when used via DataSet#as or the generated
9
+ Format::simple_interface(Format.table,Format.document,etc)
10
+
11
+ - Dropped insert_row / insert_column from Format::Engine::Table.
12
+ These are no longer needed.
13
+
14
+ - Reworked format engine unit tests to decouple from specific plugins.
15
+
16
+ - vendored SystemExtensions from HighLine to get terminal_width /
17
+ terminal_height functions
18
+
19
+ - DataSet#add_columns and DataSet#add_columns! has been added
20
+
21
+ - Added pre and post hooks for Plugins. Individual plugins choose if and
22
+ how to implement them.
23
+
24
+ - Parser is gone
25
+
26
+ - Format::Builder is gone
27
+
28
+ - fixed a bug in rails support. :columns now works properly.
29
+
30
+ - fieldnames can now be disabled in CSV loading
31
+
32
+ - DataSet.load now loads empty cells as nil instead "" by default
33
+
34
+ - fixed HTML table output in HTMLPlugin
35
+
36
+ - Select / Remove columns now accepts ordinal indexes
2
37
 
3
38
  changes since 0.4.5:
4
39
 
@@ -72,7 +107,6 @@ changes since 0.4.0:
72
107
 
73
108
  changes since 0.3.8:
74
109
 
75
-
76
110
  - added DataRow#to_h
77
111
 
78
112
  - Ruport::Format.register_filter now passes the content it will modify via a
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.9"
24
+ spec.version = "0.4.11"
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}/**/**/*") +
@@ -52,7 +52,20 @@ quickly and cleanly.
52
52
  END_DESC
53
53
  end
54
54
 
55
+ Rake::RDocTask.new do |rdoc|
56
+ rdoc.rdoc_files.include( "README",
57
+ "TODO", "CHANGELOG",
58
+ "AUTHORS", "COPYING",
59
+ "LICENSE", "lib/" )
60
+ rdoc.main = "README"
61
+ rdoc.rdoc_dir = "doc/html"
62
+ rdoc.title = "Ruport Documentation"
63
+ end
64
+
65
+
55
66
  Rake::GemPackageTask.new(spec) do |pkg|
56
67
  pkg.need_zip = true
57
68
  pkg.need_tar = true
58
69
  end
70
+
71
+
data/TODO CHANGED
@@ -2,13 +2,6 @@ TODO: (Wiped clean for a fresh start as of 2006.02.20)
2
2
 
3
3
  Immediate Goals:
4
4
 
5
- Bugs:
6
-
7
- - DataSet deep cloning is broken thanks to activerecord.
8
-
9
- - The format engine doesn't have a good way to give you cloned copies
10
- of the Plugins meaningfully
11
-
12
5
  Features:
13
6
 
14
7
  - event system
@@ -0,0 +1,2 @@
1
+
2
+
@@ -0,0 +1,19 @@
1
+ require "rubygems"
2
+ require "ruport"
3
+ Ruport.configure { |c|
4
+ c.source :default, :dsn => "dbi:mysql:ruport", :user => "root"
5
+ }
6
+
7
+ SQL = <<-EOS
8
+ drop table if exists people;
9
+ create table people(
10
+ id int not null auto_increment,
11
+ name text,
12
+ email varchar(50),
13
+ primary key (id)
14
+ );
15
+ insert into people VALUES(NULL, 'gregory', 'g@g.com')
16
+ EOS
17
+
18
+ Ruport::Query.new(SQL).execute
19
+ p Ruport::Query.new("select * from people").result
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.9"
17
+ VERSION = "Ruby Reports Version 0.4.11"
18
18
 
19
19
  # Ruports logging and error interface.
20
20
  # Can generate warnings or raise fatal errors
@@ -73,7 +73,8 @@ module Ruport
73
73
 
74
74
 
75
75
  attr_accessor :fields, :tags
76
-
76
+ alias_method :column_names, :fields
77
+
77
78
  # Returns a new DataRow
78
79
  def +(other)
79
80
  DataRow.new @fields + other.fields, :data => (@data + other.to_a)
@@ -47,6 +47,7 @@ module Ruport
47
47
 
48
48
  #an array which contains column names
49
49
  attr_accessor :fields
50
+ alias_method :column_names, :fields
50
51
 
51
52
  #the default value to fill empty cells with
52
53
  attr_accessor :default
@@ -167,7 +168,8 @@ module Ruport
167
168
  # my_data = Ruport::DataSet.load("foo.csv")
168
169
  # my_data = Ruport::DataSet.load("foo.yaml")
169
170
  # my_data = Ruport::DataSet.load("foo.yml")
170
- def self.load ( source, default="", &block)
171
+ def self.load ( source, options={}, &block)
172
+ options = {:has_names => true}.merge(options)
171
173
  case source
172
174
  when /\.(yaml|yml)/
173
175
  return YAML.load(File.open(source))
@@ -181,10 +183,12 @@ module Ruport
181
183
  else
182
184
  lambda { |r| loaded_data << r }
183
185
  end
186
+
187
+ loaded_data.fields = input[0] if options[:has_names]
188
+ input = input[1..-1] if options[:has_names]
184
189
 
185
- loaded_data.fields = input[0]
186
- loaded_data.default = default
187
- input[1..-1].each { |row| action[row] }
190
+ loaded_data.default = options[:default]
191
+ input.each { |row| action[row] }
188
192
  return loaded_data
189
193
  else
190
194
  raise "Invalid file type"
@@ -194,6 +198,7 @@ module Ruport
194
198
 
195
199
  # Returns a new DataSet composed of the fields specified.
196
200
  def select_columns(*fields)
201
+ fields = get_field_names(fields)
197
202
  rows = fields.inject([]) { |s,e| s << map { |row| row[e] } }.transpose
198
203
  my_data = DataSet.new(fields, :data => rows)
199
204
  end
@@ -204,16 +209,26 @@ module Ruport
204
209
  @fields = a.fields; @data = a.data
205
210
  end
206
211
 
212
+ #Creates a new dataset with additional columns appending to it
213
+ def add_columns(*fields)
214
+ select_columns *(@fields + fields)
215
+ end
216
+
217
+ def add_columns!(*fields)
218
+ select_columns! *(@fields + fields)
219
+ end
207
220
 
208
221
  # Returns a new DataSet with the specified fields removed
209
222
  def remove_columns(*fields)
223
+ fields = get_field_names(fields)
210
224
  select_columns(*(@fields-fields))
211
225
  end
212
226
 
213
227
  # removes the specified fields from this DataSet (DESTRUCTIVE!)
214
228
  def remove_columns!(*fields)
215
- @fields -= fields
216
- @data = select_columns(*(@fields)).to_a
229
+ d = remove_columns(*fields)
230
+ @data = d.data
231
+ @fields = d.fields
217
232
  end
218
233
 
219
234
  # uses Format::Builder to render DataSets in various ready to output
@@ -262,6 +277,13 @@ module Ruport
262
277
  # Readable string representation of the DataSet
263
278
  def to_s; as(:text) end
264
279
 
280
+ private
281
+
282
+ def get_field_names(f)
283
+ f.all? { |e| e.kind_of? Integer } &&
284
+ f.inject([]) { |s,e| s << @fields[e] } || f
285
+ end
286
+
265
287
  end
266
288
  end
267
289
 
data/lib/ruport/format.rb CHANGED
@@ -68,7 +68,7 @@ module Ruport
68
68
  options[:auto_render] = false; simple_interface(engine,options) })
69
69
  end
70
70
 
71
- %w[builder open_node document engine plugin].each { |lib|
71
+ %w[open_node document engine plugin].each { |lib|
72
72
  require "ruport/format/#{lib}"
73
73
  }
74
74
 
@@ -83,13 +83,13 @@ module Ruport
83
83
  options[:auto_render] = true unless options.has_key? :auto_render
84
84
 
85
85
 
86
- options[:data] &&= options[:data].dup
86
+ options[:data] = options[:data].dup
87
87
 
88
88
  options.each do |k,v|
89
89
  my_engine.send("#{k}=",v) if my_engine.respond_to? k
90
90
  end
91
91
 
92
- options[:auto_render] ? my_engine.render : my_engine
92
+ options[:auto_render] ? my_engine.render : my_engine.dup
93
93
  end
94
94
 
95
95
  end
@@ -2,7 +2,6 @@ module Ruport
2
2
  class Format::Engine
3
3
  require "forwardable"
4
4
 
5
-
6
5
  class << self
7
6
 
8
7
  include Enumerable
@@ -16,32 +15,36 @@ module Ruport
16
15
  end
17
16
 
18
17
  attr_accessor :engine_klasses
18
+ attr_reader :plugin
19
+ attr_reader :data
20
+ attr_accessor :klass_binding
19
21
 
20
22
  def alias_engine(klass,name)
21
23
  Format::Engine.engine_klasses ||= {}
22
24
  Format::Engine.engine_klasses[name] = klass
23
25
  end
24
26
 
25
- def format_plugins
26
- @format_plugins ||= {}
27
+ def data=(data)
28
+ @data = data
29
+ active_plugin.data = data.dup if active_plugin
27
30
  end
28
31
 
29
- def accept_format_plugin(klass)
30
- format_plugins[klass.format_name] = klass
32
+ def active_plugin
33
+ @format_plugins[:current]
34
+ #plugin && @format_plugins[plugin]
31
35
  end
32
36
 
33
- def plugin_names
34
- format_plugins.keys
37
+ def plugin=(p)
38
+ @plugin = p
39
+ @format_plugins[:current] = @format_plugins[p].dup
40
+ @format_plugins[:current].data = self.data.dup if self.data
35
41
  end
36
-
37
- def plugins
38
- format_plugins.values
39
- end
40
-
41
- def active_plugin
42
- plugin && @format_plugins[plugin]
42
+
43
+ def apply_erb
44
+ active_plugin.data =
45
+ ERB.new(active_plugin.data).result(klass_binding || binding)
43
46
  end
44
-
47
+
45
48
  def render
46
49
  raise "No plugin specified" unless plugin
47
50
  raise "No data provided" unless data
@@ -52,53 +55,47 @@ module Ruport
52
55
  self.data = nil
53
56
  end
54
57
 
55
- def apply_erb
56
- active_plugin.data =
57
- ERB.new(active_plugin.data).result(klass_binding || binding)
58
+ def accept_format_plugin(klass)
59
+ format_plugins[klass.format_name] = klass
58
60
  end
59
61
 
60
- attr_accessor :plugin
61
- attr_accessor :data
62
- attr_accessor :klass_binding
63
-
62
+ private
63
+
64
+ def format_plugins
65
+ @format_plugins ||= {}
66
+ end
67
+
68
+ def plugin_names
69
+ format_plugins.keys
70
+ end
71
+
72
+ def plugins
73
+ format_plugins.values
74
+ end
75
+
64
76
  end
65
77
  end
66
78
 
67
79
  class Format::Engine::Table < Format::Engine
68
80
 
69
81
  renderer do
70
- super
82
+ super
71
83
  active_plugin.rendered_field_names = ""
72
- build_field_names if (data.respond_to?(:fields) && show_field_names)
73
- active_plugin.render_table
84
+ build_field_names if (data.respond_to?(:fields) &&
85
+ data.fields && show_field_names)
86
+ a = active_plugin.render_table
74
87
  end
75
88
 
76
89
  class << self
77
- def insert_column(options)
78
- filler = options[:filler].to_s
79
- nr_action = if (index = options[:left_of])
80
- lambda { |e| e.to_a[0..index-1] + [filler] + e.to_a[index..-1] }
81
- elsif (index = options[:right_of])
82
- lambda { |e| e.to_a[0..index] + [filler] + e.to_a[index+1..-1] }
83
- end
84
- self.data = self.data.inject([]) { |a,r|
85
- a << nr_action[r]
86
- }
87
- end
88
-
89
- def insert_row(options)
90
- filler = options[:filler].to_s
91
- self.data = if (index = options[:above])
92
- data.to_a[0..index-1] + [filler] + data.to_a[index..-1]
93
- elsif (index = options[:below])
94
- data.to_a[0..index] + [filler] + data.to_a[index+1..-1]
95
- end
96
- end
97
90
 
98
91
  def rewrite_column(key,&block)
99
92
  data.each { |r| r[key] = block[r] }
100
93
  end
101
94
 
95
+ def num_cols
96
+ data[0].to_a.length
97
+ end
98
+
102
99
  attr_accessor :show_field_names
103
100
 
104
101
  private
@@ -134,7 +131,6 @@ module Ruport
134
131
  require "redcloth"
135
132
  active_plugin.data = RedCloth.new(active_plugin.data).to_html
136
133
  end
137
-
138
134
 
139
135
  end
140
136
 
@@ -41,6 +41,7 @@ module Ruport
41
41
  end
42
42
 
43
43
  attr_accessor :rendered_field_names
44
+ attr_accessor :pre, :post
44
45
  end
45
46
 
46
47
 
@@ -48,7 +49,7 @@ module Ruport
48
49
 
49
50
  format_field_names do
50
51
  require "fastercsv"
51
- FasterCSV.generate { |csv| csv << data.fields }
52
+ FasterCSV.generate { |csv| csv << data.fields }
52
53
  end
53
54
 
54
55
  renderer :table do
@@ -61,30 +62,75 @@ module Ruport
61
62
  end
62
63
 
63
64
  class TextPlugin < Format::Plugin
64
-
65
65
  rendering_options :erb_enabled => true, :red_cloth_enabled => false
66
66
 
67
67
  renderer :document
68
68
 
69
- renderer :table do
70
- indices = (0...data.length).to_a
71
- data.inject(rendered_field_names){ |s,r|
72
- s << "row#{indices.shift}: ( #{r.to_a.join(', ')} )\n"
69
+ renderer :table do
70
+ require "ruport/system_extensions"
71
+
72
+ th = "#{rendered_field_names}#{hr}"
73
+
74
+ data.each { |r|
75
+ r.each_with_index { |f,i|
76
+ r[i] = f.to_s.center(max_col_width(i))
77
+ }
73
78
  }
79
+
80
+ a = data.inject(th){ |s,r|
81
+ s << "| #{r.to_a.join(' | ')} |\n"
82
+ } << hr
83
+
84
+ width = self.right_margin || SystemExtensions.terminal_width
85
+
86
+ a.split("\n").each { |r|
87
+ r.gsub!(/\A.{#{width},}/) { |m| m[0,width-2] += ">>" }
88
+ }.join("\n") << "\n"
74
89
  end
75
90
  format_field_names do
76
- "fields: ( #{data.fields.join(', ')} )\n"
91
+ data.fields.each_with_index { |f,i|
92
+ data.fields[i] = f.to_s.center(max_col_width(i))
93
+ }
94
+ "#{hr}| #{data.fields.to_a.join(' | ')} |\n"
95
+ end
96
+
97
+ def self.max_col_width(index)
98
+ f = data.fields if data.respond_to? :fields
99
+ d = DataSet.new f, :data => data
100
+
101
+ cw = d.map { |r| r[index].to_s.length }.max
102
+
103
+ return cw unless d.fields
104
+
105
+ nw = (index.kind_of?(Integer) ? d.fields[index] : index ).to_s.length
106
+
107
+ [cw,nw].max
108
+ end
109
+
110
+ def self.table_width
111
+ f = data.fields if data.respond_to? :fields
112
+ d = DataSet.new f, :data => data
113
+
114
+ d[0].fields.inject(0) { |s,e| s+=max_col_width(e) }
77
115
  end
78
116
 
117
+ def self.hr
118
+ len = data[0].to_a.length * 3 + table_width + 1
119
+ "+" + "-"*(len-2) + "+\n"
120
+ end
121
+
122
+ class << self; attr_accessor :right_margin; end
123
+
79
124
  register_on :table_engine
80
125
  register_on :document_engine
81
126
  end
82
127
 
83
128
  class PDFPlugin < Format::Plugin
129
+
84
130
  renderer :table do
85
131
  require "pdf/writer"; require "pdf/simpletable";
86
- return unless defined? PDF::Writer
87
132
  pdf = PDF::Writer.new
133
+ pre[pdf] if pre
88
134
  PDF::SimpleTable.new do |table|
89
135
  table.maximum_width = 500
90
136
  table.orientation = :center
@@ -94,6 +140,7 @@ module Ruport
94
140
  table.column_order = self.rendered_field_names
95
141
  table.render_on(pdf)
96
142
  end
143
+ post[pdf] if post
97
144
  pdf.render
98
145
  end
99
146
 
@@ -110,7 +157,8 @@ module Ruport
110
157
 
111
158
  renderer :table do
112
159
  rc = data.inject(rendered_field_names) { |s,r|
113
- s << "|#{r.to_a.join('|')}|\n"
160
+ row = r.map { |e| e.to_s.empty? ? "&nbsp;" : e }
161
+ s << "|#{row.to_a.join('|')}|\n"
114
162
  }
115
163
  Format.document :data => rc, :plugin => :html
116
164
  end