ruport 0.4.9 → 0.4.11

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