ruport-util 0.10.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -10,7 +10,7 @@ require "rake/gempackagetask"
10
10
 
11
11
  require 'spec/rake/spectask'
12
12
 
13
- task :default => [:wraptest]
13
+ task :default => [:test]
14
14
 
15
15
  desc "Run all tests"
16
16
  Spec::Rake::SpecTask.new('test') do |t|
@@ -33,7 +33,7 @@ end
33
33
 
34
34
  spec = Gem::Specification.new do |spec|
35
35
  spec.name = "ruport-util"
36
- spec.version = "0.10.0"
36
+ spec.version = "0.12.0"
37
37
  spec.platform = Gem::Platform::RUBY
38
38
  spec.summary = "A set of tools and helper libs for Ruby Reports"
39
39
  spec.files = Dir.glob("{example,lib,test,bin}/**/**/*") +
Binary file
data/lib/ruport/util.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Ruport
2
2
  module Util
3
- VERSION = "0.10.0"
3
+ VERSION = "0.12.0"
4
4
 
5
5
  file = __FILE__
6
6
  file = File.readlink(file) if File.symlink?(file)
@@ -18,4 +18,6 @@ require "ruport/util/mailer"
18
18
  require "ruport/util/bench"
19
19
  require "ruport/util/generator"
20
20
  require "ruport/util/pdf/form"
21
- require "ruport/util/ods"
21
+ require "ruport/util/ods"
22
+ require "ruport/util/query"
23
+ require "ruport/util/xls"
@@ -39,11 +39,11 @@ class Ruport::Formatter
39
39
 
40
40
  class PDF
41
41
  def draw_graph(graph, opts={})
42
- x = opts[:x]
43
- y = opts[:y]
44
- width = opts[:width]
45
- height = opts[:height]
46
- g = graph.as(:jpg)
42
+ x = opts.delete(:x)
43
+ y = opts.delete(:y)
44
+ width = opts.delete(:width)
45
+ height = opts.delete(:height)
46
+ g = graph.as(:jpg, opts)
47
47
  info = ::PDF::Writer::Graphics::ImageInfo.new(g)
48
48
 
49
49
  # reduce the size of the image until it fits into the requested box
@@ -4,8 +4,6 @@ module Ruport
4
4
 
5
5
  required_option :customer_info,:company_info,:order_info,:comments
6
6
 
7
- option :title
8
-
9
7
  stage :invoice_headers,:invoice_body,:invoice_footer
10
8
 
11
9
  finalize :invoice
@@ -0,0 +1,253 @@
1
+ # Ruport : Extensible Reporting System
2
+ #
3
+ # query.rb provides a basic wrapper around RubyDBI for SQL interaction
4
+ #
5
+ # Original work began by Gregory Brown based on ideas from James Edward Gray II
6
+ # in August, 2005.
7
+ #
8
+ # Copyright (C) 2005-2007, Gregory Brown
9
+ # All Rights Reserved.
10
+ #
11
+ # This is free software distributed under the same terms as Ruby 1.8
12
+ # See LICENSE and COPYING for details.
13
+ require "generator"
14
+
15
+ module Ruport
16
+
17
+ # === Overview
18
+ #
19
+ # Query offers a way to interact with databases via RubyDBI. It supports
20
+ # returning result sets in either Ruport's Data::Table, or in their
21
+ # raw form as DBI::Rows.
22
+ #
23
+ # Query allows you to treat your result sets as an Enumerable data structure
24
+ # that plays well with the rest of Ruport.
25
+ #
26
+ # If you are using ActiveRecord, you might prefer our acts_as_reportable
27
+ # extension.
28
+ #
29
+ class Query
30
+
31
+ include Enumerable
32
+
33
+ # Ruport::Query provides an interface for dealing with raw SQL queries.
34
+ # The SQL can be single or multistatement, but the resulting Data::Table
35
+ # will consist only of the result of the last statement.
36
+ #
37
+ # Available options:
38
+ #
39
+ # <b><tt>:source</tt></b>:: A source specified in
40
+ # Ruport::Query.sources, defaults to
41
+ # <tt>:default</tt>.
42
+ # <b><tt>:dsn</tt></b>:: If specifed, the Query object will
43
+ # manually override Ruport::Query.
44
+ # <b><tt>:user</tt></b>:: If a DSN is specified, the user can
45
+ # be set with this option.
46
+ # <b><tt>:password</tt></b>:: If a DSN is specified, the password
47
+ # can be set with this option.
48
+ # <b><tt>:row_type</tt></b>:: When set to :raw, DBI::Rows will be
49
+ # returned instead of a Data::Table
50
+ #
51
+ # Examples:
52
+ #
53
+ # # uses Ruport::Query's default source
54
+ # Ruport::Query.new("select * from fo")
55
+ #
56
+ # # uses the Ruport::Query's source labeled :my_source
57
+ # Ruport::Query.new("select * from fo", :source => :my_source)
58
+ #
59
+ # # uses a manually entered source
60
+ # Ruport::Query.new("select * from fo", :dsn => "dbi:mysql:my_db",
61
+ # :user => "greg", :password => "chunky_bacon" )
62
+ #
63
+ # # uses a SQL file stored on disk
64
+ # Ruport::Query.new("my_query.sql")
65
+ #
66
+ # # explicitly use a file, even if it doesn't end in .sql
67
+ # Ruport::Query.new(:file => "foo")
68
+ #
69
+ def initialize(sql, options={})
70
+ if sql.kind_of?(Hash)
71
+ options = { :source => :default }.merge(sql)
72
+ sql = options[:file] || options[:string]
73
+ else
74
+ options = { :source => :default, :string => sql }.merge(options)
75
+ options[:file] = sql if sql =~ /.sql$/
76
+ end
77
+ origin = options[:file] ? :file : :string
78
+
79
+ @statements = SqlSplit.new(get_query(origin,sql))
80
+ @sql = @statements.join
81
+
82
+ if options[:dsn]
83
+ Ruport::Query.add_source :temp, :dsn => options[:dsn],
84
+ :user => options[:user],
85
+ :password => options[:password]
86
+ options[:source] = :temp
87
+ end
88
+
89
+ select_source(options[:source])
90
+
91
+ @raw_data = options[:row_type].eql?(:raw)
92
+ @params = options[:params]
93
+ end
94
+
95
+ # Returns an OpenStruct with the configuration options for the default
96
+ # database source.
97
+ #
98
+ def self.default_source
99
+ sources[:default]
100
+ end
101
+
102
+ # Returns a hash of database sources, keyed by label.
103
+ def self.sources
104
+ @sources ||= {}
105
+ end
106
+
107
+ # Allows you to add a labeled DBI source configuration.
108
+ #
109
+ # Query objects will use the source labeled <tt>:default</tt>,
110
+ # unless another source is specified.
111
+ #
112
+ # Examples:
113
+ #
114
+ # # a connection to a MySQL database foo with user root, pass chunkybacon
115
+ # Query.add_source :default, :dsn => "dbi:mysql:foo",
116
+ # :user => "root",
117
+ # :password => "chunkybacon"
118
+ #
119
+ #
120
+ # # a second connection to a MySQL database bar
121
+ # Query.add_source :test, :dsn => "dbi:mysql:bar",
122
+ # :user => "tester",
123
+ # :password => "blinky"
124
+ #
125
+ #
126
+ def self.add_source(name,options={})
127
+ sources[name] = OpenStruct.new(options)
128
+ check_source(sources[name],name)
129
+ end
130
+
131
+ attr_accessor :raw_data
132
+
133
+ # The original SQL for the Query object
134
+ attr_reader :sql
135
+
136
+ # This will set the <tt>dsn</tt>, <tt>username</tt>, and <tt>password</tt>
137
+ # to one specified by a source in Ruport::Query.
138
+ #
139
+ def select_source(label)
140
+ @dsn = Ruport::Query.sources[label].dsn
141
+ @user = Ruport::Query.sources[label].user
142
+ @password = Ruport::Query.sources[label].password
143
+ end
144
+
145
+ # Yields result set by row.
146
+ def each(&action)
147
+ raise(LocalJumpError, "No block given!") unless action
148
+ fetch(&action)
149
+ self
150
+ end
151
+
152
+ # Runs the SQL query and returns the result set
153
+ def result; fetch; end
154
+
155
+ # Runs the query without returning its results.
156
+ def execute; fetch; nil; end
157
+
158
+ # Returns a Data::Table, even if in <tt>raw_data</tt> mode.
159
+ def to_table
160
+ data_flag, @raw_data = @raw_data, false
161
+ data = fetch; @raw_data = data_flag; return data
162
+ end
163
+
164
+ # Returns a csv dump of the query.
165
+ def to_csv
166
+ fetch.to_csv
167
+ end
168
+
169
+ # Returns a Generator object of the result set.
170
+ def generator
171
+ Generator.new(fetch)
172
+ end
173
+
174
+ private
175
+
176
+ def query_data(query_text, params=@params)
177
+
178
+ require "dbi"
179
+
180
+ data = @raw_data ? [] : Data::Table.new
181
+
182
+ DBI.connect(@dsn, @user, @password) do |dbh|
183
+ dbh.execute(query_text, *(params || [])) do |sth|
184
+ # Work-around for inconsistent DBD behavior w/ resultless queries
185
+ names = sth.column_names rescue []
186
+ if names.empty?
187
+ # Work-around for SQLite3 DBD bug
188
+ sth.cancel rescue nil
189
+ return nil
190
+ end
191
+
192
+ data.column_names = names unless @raw_data
193
+
194
+ sth.each do |row|
195
+ row = row.to_a
196
+ row = Data::Record.new(row, :attributes => names) unless @raw_data
197
+ yield row if block_given?
198
+ data << row if !block_given?
199
+ end
200
+ end
201
+ end
202
+ data
203
+ end
204
+
205
+ def get_query(type,query)
206
+ type.eql?(:file) ? load_file( query ) : query
207
+ end
208
+
209
+ def fetch(&block)
210
+ data = nil
211
+ final = @statements.size - 1
212
+ @statements.each_with_index do |query_text, index|
213
+ data = query_data(query_text, &(index == final ? block : nil))
214
+ end
215
+ return data
216
+ end
217
+
218
+ def load_file(query_file)
219
+ begin
220
+ File.read( query_file ).strip
221
+ rescue
222
+ raise LoadError, "Could not open #{query_file}"
223
+ end
224
+ end
225
+
226
+ def self.check_source(settings,label) # :nodoc:
227
+ raise ArgumentError unless settings.dsn
228
+ end
229
+
230
+ end
231
+
232
+ # Created by Francis Hwang, 2005.12.31
233
+ # Copyright (c) 2005, All Rights Reserved.
234
+ #++
235
+ class SqlSplit < Array #:nodoc:
236
+ def initialize( sql )
237
+ super()
238
+ next_sql = ''
239
+ sql.each do |line|
240
+ unless line =~ /^--/ or line =~ %r{^/\*.*\*/;} or line =~ /^\s*$/
241
+ next_sql << line
242
+ if line =~ /;$/
243
+ next_sql.gsub!( /;\s$/, '' )
244
+ self << next_sql
245
+ next_sql = ''
246
+ end
247
+ end
248
+ end
249
+ self << next_sql if next_sql != ''
250
+ end
251
+ end
252
+
253
+ end
@@ -0,0 +1,301 @@
1
+ require 'cgi'
2
+ require 'tmpdir'
3
+ require 'zip/zip'
4
+
5
+ module Ruport
6
+ # This class provides Excel output for Ruport's Table renderers.
7
+ # It can export to format :
8
+ # * Excel 2003 (use spreadsheet/excel gems)
9
+ # * Excel 2003 XML
10
+ # * Excel 2007 OpenXML
11
+ #
12
+ # === Rendering Options
13
+ # * worksheet_name : Name of the Worksheet
14
+ # * Renders
15
+ # * xls => Excel 2003 (If spreadsheet/excel no exist use xml format instead)
16
+ # * xlsx => Excel 2007 OpenXML
17
+ # * xlsxml => Excel 2003 XML
18
+ #
19
+ class Formatter::XLS < Formatter
20
+ renders :xls, :for => [ Renderer::Row, Renderer::Table]
21
+
22
+ def initialize
23
+ Ruport.quiet {
24
+ require 'spreadsheet/excel'
25
+ }
26
+ end
27
+
28
+
29
+ def prepare_table
30
+ @xls_row = 0
31
+ @tempfile = Tempfile.new('output.xls')
32
+ @workbook = Spreadsheet::Excel.new(@tempfile.path)
33
+ @worksheet = @workbook.add_worksheet(options.worksheet_name || 'Ruport')
34
+ @header_style = options.header_style || @workbook.add_format(:bold => 1, :size => 12)
35
+ end
36
+
37
+ def build_table_header
38
+ if options.show_table_headers
39
+ table_row { build_cells(data.column_names, @header_style) }
40
+ end
41
+ end
42
+
43
+ def build_table_body
44
+ data.each do |r|
45
+ table_row { build_cells(r) }
46
+ end
47
+ end
48
+
49
+ def build_row
50
+ table_row{ build_cells(data.to_a) }
51
+ end
52
+
53
+ def table_row
54
+ yield
55
+ @xls_row += 1
56
+ end
57
+
58
+ def build_cells(values, style = nil)
59
+ col = 0
60
+ values.each do |value|
61
+ if style
62
+ @worksheet.write(@xls_row, col, value, style)
63
+ else
64
+ @worksheet.write(@xls_row, col, value)
65
+ end
66
+ col += 1
67
+ end
68
+ end
69
+
70
+ def finalize_table
71
+ @workbook.close
72
+ options.io =
73
+ if options.tempfile
74
+ @tempfile
75
+ else
76
+ File.read(@tempfile.path)
77
+ end
78
+ end
79
+ end
80
+
81
+ # Excel 2007 OpenXML
82
+ class Formatter::XLSX < Formatter
83
+ BLANK_XLSX = File.join(Ruport::Util::BASEDIR, 'example', 'data', 'blank.xlsx')
84
+ renders :xlsx, :for => [ Renderer::Row, Renderer::Table]
85
+
86
+ def initialize
87
+ end
88
+
89
+
90
+
91
+ def prepare_table
92
+ @xls_row = 0
93
+ output << %{<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
94
+ <worksheet xml:space="preserve" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
95
+ <sheetPr codeName="#{options.worksheet_name || 'Ruport'}"/>
96
+
97
+ <sheetViews>
98
+ <sheetView tabSelected="1" workbookViewId="0">
99
+ <selection/>
100
+ </sheetView>
101
+ </sheetViews>
102
+ <sheetFormatPr defaultRowHeight="12.75"/>
103
+ <cols>
104
+ }
105
+ data.column_names.size.times {
106
+ output << %{ <col min="1" max="1" width="10" customWidth="true"/>}
107
+ }
108
+ output << %{</cols><sheetData>}
109
+ @strings = []
110
+ end
111
+
112
+ def build_table_header
113
+ if options.show_table_headers
114
+ table_row { build_cells(data.column_names, 'Heading') }
115
+ end
116
+ end
117
+
118
+ def build_table_body
119
+ data.each do |r|
120
+ table_row { build_cells(r) }
121
+ end
122
+ end
123
+
124
+ def build_row
125
+ table_row{ build_cells(data.to_a) }
126
+ end
127
+
128
+ def table_row
129
+ output << %{ <row r="#{@xls_row + 1}">\n}
130
+ yield
131
+ output << %{ </row>\n}
132
+ @xls_row += 1
133
+ end
134
+
135
+ def get_cell_name(row, col)
136
+ name = ((col % 26) + 65).chr + row.to_s
137
+ name = ((col / 26) + 65).chr + name if (col / 26 != 0)
138
+ name
139
+ end
140
+
141
+ def build_cells(values, style = '')
142
+ col = 0
143
+ values.each do |value|
144
+ value = CGI.escapeHTML(value.to_s)
145
+ id = @strings.length
146
+ @strings.push(value)
147
+ output << %{<c r="#{get_cell_name(@xls_row + 1, col)}" t="s">
148
+ <v>#{id}</v>
149
+ </c>}
150
+ col += 1
151
+ end
152
+ end
153
+
154
+ def build_strings_file
155
+ out = ''
156
+ out << %{<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" uniqueCount="#{@strings.length}">\n}
157
+ @strings.each {|val|
158
+ out << %{ <si><t>#{val}</t></si>\n}
159
+ }
160
+ out << %{</sst>\n}
161
+ out
162
+ end
163
+
164
+ def finalize_table
165
+ output << %{</sheetData>
166
+ <sheetProtection sheet="false" objects="false" scenarios="false" formatCells="false" formatColumns="false" formatRows="false" insertColumns="false" insertRows="false" insertHyperlinks="false" deleteColumns="false" deleteRows="false" selectLockedCells="false" sort="false" autoFilter="false" pivotTables="false" selectUnlockedCells="false"/>
167
+ <printOptions gridLines="false" gridLinesSet="true"/>
168
+ <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
169
+
170
+ <pageSetup paperSize="1" orientation="default"/>
171
+ <headerFooter differentOddEven="false" differentFirst="false" scaleWithDoc="true" alignWithMargins="true">
172
+ <oddHeader></oddHeader>
173
+ <oddFooter></oddFooter>
174
+ <evenHeader></evenHeader>
175
+ <evenFooter></evenFooter>
176
+ <firstHeader></firstHeader>
177
+ <firstFooter></firstFooter>
178
+ </headerFooter>
179
+
180
+ </worksheet>}
181
+
182
+ @tempfile = Tempfile.new('output.xlsx')
183
+
184
+ File.open(BLANK_XLSX) { |bo|
185
+ @tempfile.print(bo.read(1024)) until bo.eof?
186
+ }
187
+ @tempfile.close
188
+ zip = Zip::ZipFile.open(@tempfile.path)
189
+ zip.get_output_stream('xl/worksheets/sheet1.xml') do |cxml|
190
+ cxml.write(output)
191
+ end
192
+ zip.get_output_stream('xl/sharedStrings.xml') do |cxml|
193
+ cxml.write(build_strings_file)
194
+ end
195
+ workbook = %{<?xml version="1.0" encoding="UTF-8" standalone="yes"?><workbook xml:space="preserve" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
196
+ <fileVersion appName="xl" lastEdited="4" lowestEdited="4" rupBuild="4505"/>
197
+ <workbookPr codeName="ThisWorkbook"/>
198
+ <bookViews>
199
+ <workbookView activeTab="0" autoFilterDateGrouping="1" firstSheet="0" minimized="0" showHorizontalScroll="1" showSheetTabs="1" showVerticalScroll="1" tabRatio="600" visibility="visible"/>
200
+ </bookViews>
201
+ <sheets>
202
+ <sheet name="#{options.worksheet_name || 'Ruport'}" sheetId="1" r:id="rId4"/>
203
+ </sheets>
204
+ <definedNames/>
205
+ <calcPr calcId="124519" calcMode="auto" fullCalcOnLoad="1"/>
206
+ </workbook>}
207
+ zip.get_output_stream('xl/workbook.xml') do |cxml|
208
+ cxml.write(workbook)
209
+ end
210
+ zip.close
211
+ options.io =
212
+ if options.tempfile
213
+ @tempfile
214
+ else
215
+ File.read(@tempfile.path)
216
+ end
217
+ end
218
+ end
219
+
220
+ # Excel 2003 XML
221
+ class Formatter::XLSXML < Formatter
222
+ renders :xlsxml, :for => [ Renderer::Row, Renderer::Table]
223
+
224
+ def prepare_table
225
+ output << %{<?xml version="1.0" encoding="UTF-8"?><?mso-application progid="Excel.Sheet"?>
226
+ <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
227
+ xmlns:o="urn:schemas-microsoft-com:office:office"
228
+ xmlns:x="urn:schemas-microsoft-com:office:excel"
229
+ xmlns:html="http://www.w3.org/TR/REC-html40"
230
+ xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
231
+ <Styles>
232
+ <Style ss:ID="Default" ss:Name="Default"/>
233
+
234
+ <Style ss:ID="Heading" ss:Name="Heading">#{options.header_style || '
235
+ <Alignment ss:Horizontal="Center"/>
236
+ <Font ss:Bold="1" ss:Italic="1" ss:Size="12"/>'}
237
+ </Style>
238
+ <Style ss:ID="co1"/>
239
+ <Style ss:ID="ta1"/>
240
+ </Styles>
241
+ <ss:Worksheet ss:Name="#{options.worksheet_name || 'Ruport'}">
242
+ <Table ss:StyleID="ta1">
243
+ }
244
+ data.column_names.size.times {
245
+ output << %{<Column ss:AutoFitWidth="1"/>}
246
+ }
247
+ end
248
+
249
+ def build_table_header
250
+ if options.show_table_headers
251
+ table_row { build_cells(data.column_names, 'Heading') }
252
+ end
253
+ end
254
+
255
+ def build_table_body
256
+ data.each do |r|
257
+ table_row { build_cells(r) }
258
+ end
259
+ end
260
+
261
+ def build_row
262
+ table_row{ build_cells(data.to_a) }
263
+ end
264
+
265
+ def table_row
266
+ output << %{ <Row>\n}
267
+ yield
268
+ output << %{ </Row>\n}
269
+ end
270
+
271
+ def build_cells(values, style = '')
272
+ values.each do |value|
273
+ value = CGI.escapeHTML(value.to_s)
274
+ if style.length > 0
275
+ output << %{ <Cell>\n}
276
+ else
277
+ output << %{ <Cell ss:StyleID="#{style}">\n}
278
+ end
279
+ output << %{ <Data ss:Type="String">#{value}</Data>\n}
280
+ output << %{ </Cell>\n}
281
+ end
282
+ end
283
+
284
+ def finalize_table
285
+ output << %{ </Table>
286
+ </ss:Worksheet>
287
+ </Workbook>}
288
+
289
+ @tempfile = Tempfile.new('output.xls')
290
+ @tempfile.print(output)
291
+ @tempfile.close();
292
+ options.io =
293
+ if options.tempfile
294
+ @tempfile
295
+ else
296
+ File.read(@tempfile.path)
297
+ end
298
+ end
299
+
300
+ end
301
+ end
data/test/helper.rb CHANGED
@@ -1,7 +1,14 @@
1
1
  begin
2
2
  require "rubygems"
3
+ gem "ruport", "=1.2.3"
3
4
  rescue LoadError
4
5
  nil
6
+ end
7
+
8
+ class Array
9
+ def to_table(columns)
10
+ Table(columns) { |t| each { |r| t << r }}
11
+ end
5
12
  end
6
13
 
7
14
  require "spec"
@@ -0,0 +1,35 @@
1
+ require 'test/helper'
2
+ require 'rexml/document'
3
+ testcase_requires 'hpricot'
4
+ testcase_requires 'spreadsheet/excel'
5
+
6
+ describe 'XLS Formatter' do
7
+ before :all do
8
+ @csv = "first col,second col,third col\n" +
9
+ "first row,cell 1,cell 2\n" +
10
+ "second row,cell 3,cell 4\n" +
11
+ "third row,special >,special <\n" +
12
+ "fourth row,,after empty\n" +
13
+ "\n" +
14
+ "seventh row,nothing,more\n"
15
+ @table = Table(:string => @csv)
16
+ @xlsxml = Hpricot(@table.to_xlsxml())
17
+ @xls = @table.to_xls()
18
+ @xlsx = @table.to_xlsx(:tempfile => true)
19
+ zip = Zip::ZipFile.open(@xlsx.path)
20
+ @xlsx_content = Hpricot(zip.read('xl/worksheets/sheet1.xml'))
21
+ zip.close
22
+ @xlsx_rows = @xlsx_content.search('//row')
23
+ end
24
+
25
+ it 'should have content' do
26
+ @xlsxml.should_not be_nil
27
+ @xls.should_not be_nil
28
+ @xlsx.should_not be_nil
29
+ end
30
+
31
+ it 'should have all rows' do
32
+ #@xlsxml.search('//row').should have(7).rows
33
+ @xlsx_rows.should have(7).rows
34
+ end
35
+ end
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env ruby -w
2
+ require 'test/helper'
3
+ testcase_requires 'dbi'
4
+
5
+ $VERBOSE = nil
6
+
7
+ describe "A Query" do
8
+
9
+ before :each do
10
+ @sources = {
11
+ :default => {
12
+ :dsn => 'ruport:test', :user => 'greg', :password => 'apple' },
13
+ :alternative => {
14
+ :dsn => "ruport:test2", :user => "sandal", :password => "harmonix" },
15
+ }
16
+ Ruport::Query.add_source :default, @sources[:default]
17
+ Ruport::Query.add_source :alternative, @sources[:alternative]
18
+
19
+ @columns = %w(a b c)
20
+ @data = [ [[1,2,3],[4,5,6],[7,8,9]],
21
+ [[9,8,7],[6,5,4],[3,2,1]],
22
+ [[7,8,9],[4,5,6],[1,2,3]], ]
23
+ @datasets = @data.dup
24
+
25
+ @sql = [ "select * from foo", "create table foo ..." ]
26
+ @sql << @sql.values_at(0, 0).join(";\n")
27
+ @sql << @sql.values_at(1, 0).join(";\n")
28
+ @query = {
29
+ :plain => Ruport::Query.new(@sql[0]),
30
+ :sourced => Ruport::Query.new(@sql[0], :source => :alternative),
31
+ :paramed => Ruport::Query.new(@sql[0], :params => [ 42 ]),
32
+ :raw => Ruport::Query.new(@sql[0], :row_type => :raw),
33
+ :resultless => Ruport::Query.new(@sql[1]),
34
+ :multi => Ruport::Query.new(@sql[2]),
35
+ :mixed => Ruport::Query.new(@sql[3]),
36
+ }
37
+ end
38
+
39
+ if Object.const_defined? :DBI
40
+
41
+ it "should have a nil result on execute" do
42
+ query = @query[:plain]
43
+ setup_mock_dbi(1)
44
+
45
+ query.execute.should == nil
46
+ end
47
+
48
+ it "should allow execute to work with sources" do
49
+ query = @query[:sourced]
50
+ setup_mock_dbi(1, :source => :alternative)
51
+
52
+ query.execute.should == nil
53
+ end
54
+
55
+ it "should allow excute to accept parameters" do
56
+ query = @query[:paramed]
57
+ setup_mock_dbi(1, :params => [ 42 ])
58
+
59
+ query.execute.should == nil
60
+ end
61
+
62
+ it "should return nil for empty results" do
63
+ query = @query[:resultless]
64
+ setup_mock_dbi(1, :resultless => true, :sql => @sql[1])
65
+
66
+ query.result.should be_nil
67
+ end
68
+
69
+ it "should return last query result for multiple statements" do
70
+ query = @query[:multi]
71
+ setup_mock_dbi(2)
72
+
73
+ get_raw(query.result).should == @data[1]
74
+ end
75
+
76
+ it "should allow raw mode" do
77
+ query = @query[:raw]
78
+ setup_mock_dbi(1)
79
+
80
+ query.result.should == @data[0]
81
+ end
82
+
83
+ it "should allow reading from file with .sql extension" do
84
+ File.should_receive(:read).
85
+ with("query_test.sql").
86
+ and_return("select * from foo\n")
87
+
88
+ query = Ruport::Query.new "query_test.sql"
89
+ query.sql.should == "select * from foo"
90
+ end
91
+
92
+ it "should allow reading from file with explicit :file argument" do
93
+ File.should_receive(:read).
94
+ with("query_test").
95
+ and_return("select * from foo\n")
96
+
97
+ query = Ruport::Query.new(:file => "query_test")
98
+ query.sql.should == "select * from foo"
99
+
100
+ query = Ruport::Query.new(:string => "query_test")
101
+ query.sql.should == "query_test"
102
+ end
103
+
104
+ it "should raise a LoadError if the file is not found" do
105
+ File.should_receive(:read).
106
+ with("query_test.sql").
107
+ and_raise(Errno::ENOENT)
108
+
109
+ lambda { query = Ruport::Query.new "query_test.sql" }.
110
+ should raise_error(LoadError)
111
+ end
112
+
113
+ it "should support an each() iterator" do
114
+ query = @query[:plain]
115
+ setup_mock_dbi(2)
116
+
117
+ result = []; query.each { |r| result << r.to_a }
118
+ result.should == @data[0]
119
+
120
+ result = []; query.each { |r| result << r.to_a }
121
+ result.should == @data[1]
122
+ end
123
+
124
+ it "should iterate the last data set if multiple queries" do
125
+ query = @query[:multi]
126
+ setup_mock_dbi(2)
127
+
128
+ result = []; query.each { |r| result << r.to_a }
129
+ result.should == @data[1]
130
+ end
131
+
132
+ it "should raise a LocalJumpError if a block is not given" do
133
+ lambda { @query[:plain].each }.should raise_error(LocalJumpError)
134
+ end
135
+
136
+ it "should allow selecting sources" do
137
+ query = @query[:plain]
138
+ query.select_source :alternative
139
+ get_query_source(query).should == @sources[:alternative]
140
+
141
+ query.select_source :default
142
+ get_query_source(query).should == @sources[:default]
143
+ end
144
+
145
+ it "should initialize a temporary source" do
146
+ query = Ruport::Query.new "<unused>", @sources[:alternative]
147
+ get_query_source(query).should == @sources[:alternative]
148
+ end
149
+
150
+ it "should initialize multiple temporary source" do
151
+ query1 = Ruport::Query.new "<unused>", @sources[:default]
152
+ query2 = Ruport::Query.new "<unused>", @sources[:alternative]
153
+
154
+ get_query_source(query1).should == @sources[:default]
155
+ get_query_source(query2).should == @sources[:alternative]
156
+ end
157
+
158
+ it "should allow conversion to table" do
159
+ query = @query[:raw]
160
+ setup_mock_dbi(3, :returns => [@data[0]])
161
+
162
+ query.result.should == @data[0]
163
+ query.to_table.should == @data[0].to_table(@columns)
164
+ query.result.should == @data[0]
165
+ end
166
+
167
+ it "should support conversion to csv" do
168
+ query = @query[:plain]
169
+ setup_mock_dbi(1)
170
+
171
+ csv = @data[0].to_table(@columns).as(:csv)
172
+ query.to_csv.should == csv
173
+ end
174
+
175
+ it "should raise error when DSN is missing" do
176
+ lambda {
177
+ Ruport::Query.add_source :foo, :user => "root", :password => "fff"
178
+ }.should raise_error(ArgumentError)
179
+ end
180
+
181
+
182
+ it "should be able to set new defaults" do
183
+ Ruport::Query.add_source :default, :dsn => "dbi:mysql:test",
184
+ :user => "root",
185
+ :password => ""
186
+ Ruport::Query.default_source.dsn.should == "dbi:mysql:test"
187
+ Ruport::Query.default_source.user.should == "root"
188
+ Ruport::Query.default_source.password.should == ""
189
+ end
190
+
191
+ it "should allow setting multiple sources" do
192
+ Ruport::Query.add_source :foo, :dsn => "dbi:mysql:test"
193
+ Ruport::Query.add_source :bar, :dsn => "dbi:mysql:test2"
194
+ Ruport::Query.sources[:foo].dsn.should == "dbi:mysql:test"
195
+ Ruport::Query.sources[:bar].dsn.should == "dbi:mysql:test2"
196
+ end
197
+
198
+ end
199
+
200
+ private
201
+ def setup_mock_dbi(count, options={})
202
+ sql = options[:sql] || @sql[0]
203
+ source = options[:source] || :default
204
+ resultless = options[:resultless]
205
+ params = options[:params] || []
206
+
207
+ @dbh = mock("database_handle")
208
+ @sth = mock("statement_handle")
209
+ def @dbh.execute(*a, &b); execute__(*a, &b); ensure; sth__.finish if b; end
210
+ def @sth.each; data__.each { |x| yield(x.dup) }; end
211
+ def @sth.fetch_all; data__; end
212
+
213
+ DBI.should_receive(:connect).exactly(count).times.
214
+ with(*@sources[source].values_at(:dsn, :user, :password)).
215
+ and_yield(@dbh)
216
+ c = @dbh.should_receive(:execute__).exactly(count).times.with(sql, *params)
217
+ c.and_yield(@sth)
218
+ c.and_return(@sth)
219
+ @dbh.stub!(:sth__).and_return(@sth)
220
+ @sth.should_receive(:finish).with().exactly(count).times
221
+ unless resultless
222
+ @sth.stub!(:fetchable?).and_return(true)
223
+ @sth.stub!(:column_names).and_return(@columns)
224
+ if options[:returns]
225
+ if Array == options[:returns]
226
+ @sth.should_receive(:data__).any_number_of_times.and_return(*options[:returns])
227
+ else
228
+ @sth.should_receive(:data__).any_number_of_times.and_return(*Array(options[:returns]))
229
+ end
230
+ else
231
+ @sth.should_receive(:data__).any_number_of_times.and_return(*@datasets)
232
+ end
233
+ else
234
+ @sth.stub!(:fetchable?).and_return(false)
235
+ @sth.stub!(:column_names).and_return([])
236
+ @sth.stub!(:cancel)
237
+ @sth.should_receive(:data__).exactly(0).times
238
+ end
239
+ end
240
+
241
+ def get_query_source(query)
242
+ [ :dsn, :user, :password ].inject({}) do |memo, var|
243
+ memo.update var => query.instance_variable_get("@#{var}")
244
+ end
245
+ end
246
+
247
+ def get_raw(table)
248
+ table.map { |row| row.to_a }
249
+ end
250
+ end
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.4
2
+ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: ruport-util
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.10.0
7
- date: 2007-11-24 00:00:00 -05:00
6
+ version: 0.12.0
7
+ date: 2007-12-25 00:00:00 -05:00
8
8
  summary: A set of tools and helper libs for Ruby Reports
9
9
  require_paths:
10
10
  - lib
@@ -30,64 +30,71 @@ authors:
30
30
  - Gregory Brown
31
31
  files:
32
32
  - example/data
33
+ - example/data/amline_settings.xml
34
+ - example/data/blank.xlsx
35
+ - example/data/amline_graph.xml
36
+ - example/data/blank.ods
37
+ - example/invoice_report.rb
33
38
  - example/draw_graph.rb
34
39
  - example/form.rb
35
- - example/graph_report.rb
36
40
  - example/gruff_report.rb
37
- - example/invoice_report.rb
38
- - example/mailer.rb
39
- - example/managed_report.rb
40
41
  - example/ods.rb
41
- - example/data/amline_graph.xml
42
- - example/data/amline_settings.xml
43
- - example/data/blank.ods
44
- - lib/open_flash_chart.rb
42
+ - example/managed_report.rb
43
+ - example/mailer.rb
44
+ - example/graph_report.rb
45
45
  - lib/ruport
46
46
  - lib/ruport/util
47
- - lib/ruport/util.rb
48
- - lib/ruport/util/bench.rb
49
- - lib/ruport/util/generator.rb
50
47
  - lib/ruport/util/graph
51
- - lib/ruport/util/graph.rb
52
- - lib/ruport/util/invoice.rb
53
- - lib/ruport/util/mailer.rb
54
- - lib/ruport/util/ods.rb
55
- - lib/ruport/util/pdf
56
- - lib/ruport/util/report.rb
57
- - lib/ruport/util/report_manager.rb
48
+ - lib/ruport/util/graph/scruffy.rb
58
49
  - lib/ruport/util/graph/amline.rb
59
- - lib/ruport/util/graph/gruff.rb
60
50
  - lib/ruport/util/graph/o_f_c.rb
61
- - lib/ruport/util/graph/scruffy.rb
51
+ - lib/ruport/util/graph/gruff.rb
52
+ - lib/ruport/util/pdf
62
53
  - lib/ruport/util/pdf/form.rb
54
+ - lib/ruport/util/generator.rb
55
+ - lib/ruport/util/bench.rb
56
+ - lib/ruport/util/graph.rb
57
+ - lib/ruport/util/ods.rb
58
+ - lib/ruport/util/query.rb
59
+ - lib/ruport/util/report_manager.rb
60
+ - lib/ruport/util/xls.rb
61
+ - lib/ruport/util/mailer.rb
62
+ - lib/ruport/util/report.rb
63
+ - lib/ruport/util/invoice.rb
64
+ - lib/ruport/util.rb
65
+ - lib/open_flash_chart.rb
63
66
  - test/helper
64
- - test/helper.rb
65
- - test/samples
66
- - test/test_format_ods.rb
67
- - test/test_graph_ofc.rb
68
- - test/test_graph_renderer.rb
69
- - test/test_hpricot_traverser.rb
70
- - test/test_invoice.rb
71
- - test/test_mailer.rb
72
- - test/test_report.rb
73
- - test/test_report_manager.rb
74
67
  - test/helper/layout.rb
75
68
  - test/helper/wrap.rb
69
+ - test/samples
76
70
  - test/samples/data.csv
77
71
  - test/samples/foo.rtxt
72
+ - test/test_report_manager.rb
73
+ - test/test_format_xls.rb
74
+ - test/helper.rb
75
+ - test/test_query.rb
76
+ - test/test_hpricot_traverser.rb
77
+ - test/test_graph_renderer.rb
78
+ - test/test_graph_ofc.rb
79
+ - test/test_mailer.rb
80
+ - test/test_report.rb
81
+ - test/test_invoice.rb
82
+ - test/test_format_ods.rb
78
83
  - bin/csv2ods
79
84
  - bin/rope
80
85
  - Rakefile
81
86
  - INSTALL
82
87
  test_files:
83
- - test/test_format_ods.rb
84
- - test/test_graph_ofc.rb
85
- - test/test_graph_renderer.rb
88
+ - test/test_report_manager.rb
89
+ - test/test_format_xls.rb
90
+ - test/test_query.rb
86
91
  - test/test_hpricot_traverser.rb
87
- - test/test_invoice.rb
92
+ - test/test_graph_renderer.rb
93
+ - test/test_graph_ofc.rb
88
94
  - test/test_mailer.rb
89
95
  - test/test_report.rb
90
- - test/test_report_manager.rb
96
+ - test/test_invoice.rb
97
+ - test/test_format_ods.rb
91
98
  rdoc_options:
92
99
  - --title
93
100
  - ruport-util Documentation