ruport 1.2.3 → 1.4.0

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.
@@ -1,33 +0,0 @@
1
- #--
2
- # sqlsplit.rb : A tool to properly split SQL input
3
- #
4
- # This is Free Software. You may freely redistribute or modify under the terms
5
- # of the GNU General Public License or the Ruby License. See LICENSE and
6
- # COPYING for details.
7
- #
8
- # Created by Francis Hwang, 2005.12.31
9
- # Copyright (c) 2005, All Rights Reserved.
10
- #++
11
- module Ruport
12
- class Query
13
- # This class properly splits up multi-statement SQL input for use with
14
- # Ruby/DBI
15
- class SqlSplit < Array #:nodoc:
16
- def initialize( sql )
17
- super()
18
- next_sql = ''
19
- sql.each do |line|
20
- unless line =~ /^--/ or line =~ %r{^/\*.*\*/;} or line =~ /^\s*$/
21
- next_sql << line
22
- if line =~ /;$/
23
- next_sql.gsub!( /;\s$/, '' )
24
- self << next_sql
25
- next_sql = ''
26
- end
27
- end
28
- end
29
- self << next_sql if next_sql != ''
30
- end
31
- end
32
- end
33
- end
data/lib/ruport/query.rb DELETED
@@ -1,232 +0,0 @@
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
- require "ruport/query/sql_split"
15
-
16
- module Ruport
17
-
18
- # === Overview
19
- #
20
- # Query offers a way to interact with databases via RubyDBI. It supports
21
- # returning result sets in either Ruport's Data::Table, or in their
22
- # raw form as DBI::Rows.
23
- #
24
- # Query allows you to treat your result sets as an Enumerable data structure
25
- # that plays well with the rest of Ruport.
26
- #
27
- # If you are using ActiveRecord, you might prefer our acts_as_reportable
28
- # extension.
29
- #
30
- class Query
31
-
32
- include Enumerable
33
-
34
- # Ruport::Query provides an interface for dealing with raw SQL queries.
35
- # The SQL can be single or multistatement, but the resulting Data::Table
36
- # will consist only of the result of the last statement.
37
- #
38
- # Available options:
39
- #
40
- # <b><tt>:source</tt></b>:: A source specified in
41
- # Ruport::Query.sources, defaults to
42
- # <tt>:default</tt>.
43
- # <b><tt>:dsn</tt></b>:: If specifed, the Query object will
44
- # manually override Ruport::Query.
45
- # <b><tt>:user</tt></b>:: If a DSN is specified, the user can
46
- # be set with this option.
47
- # <b><tt>:password</tt></b>:: If a DSN is specified, the password
48
- # can be set with this option.
49
- # <b><tt>:row_type</tt></b>:: When set to :raw, DBI::Rows will be
50
- # returned instead of a Data::Table
51
- #
52
- # Examples:
53
- #
54
- # # uses Ruport::Query's default source
55
- # Ruport::Query.new("select * from fo")
56
- #
57
- # # uses the Ruport::Query's source labeled :my_source
58
- # Ruport::Query.new("select * from fo", :source => :my_source)
59
- #
60
- # # uses a manually entered source
61
- # Ruport::Query.new("select * from fo", :dsn => "dbi:mysql:my_db",
62
- # :user => "greg", :password => "chunky_bacon" )
63
- #
64
- # # uses a SQL file stored on disk
65
- # Ruport::Query.new("my_query.sql")
66
- #
67
- # # explicitly use a file, even if it doesn't end in .sql
68
- # Ruport::Query.new(:file => "foo")
69
- #
70
- def initialize(sql, options={})
71
- if sql.kind_of?(Hash)
72
- options = { :source => :default }.merge(sql)
73
- sql = options[:file] || options[:string]
74
- else
75
- options = { :source => :default, :string => sql }.merge(options)
76
- options[:file] = sql if sql =~ /.sql$/
77
- end
78
- origin = options[:file] ? :file : :string
79
-
80
- @statements = SqlSplit.new(get_query(origin,sql))
81
- @sql = @statements.join
82
-
83
- if options[:dsn]
84
- Ruport::Query.add_source :temp, :dsn => options[:dsn],
85
- :user => options[:user],
86
- :password => options[:password]
87
- options[:source] = :temp
88
- end
89
-
90
- select_source(options[:source])
91
-
92
- @raw_data = options[:row_type].eql?(:raw)
93
- @params = options[:params]
94
- end
95
-
96
- # Returns an OpenStruct with the configuration options for the default
97
- # database source.
98
- #
99
- def self.default_source
100
- sources[:default]
101
- end
102
-
103
- # Returns a hash of database sources, keyed by label.
104
- def self.sources
105
- @sources ||= {}
106
- end
107
-
108
- # Allows you to add a labeled DBI source configuration.
109
- #
110
- # Query objects will use the source labeled <tt>:default</tt>,
111
- # unless another source is specified.
112
- #
113
- # Examples:
114
- #
115
- # # a connection to a MySQL database foo with user root, pass chunkybacon
116
- # Query.add_source :default, :dsn => "dbi:mysql:foo",
117
- # :user => "root",
118
- # :password => "chunkybacon"
119
- #
120
- #
121
- # # a second connection to a MySQL database bar
122
- # Query.add_source :test, :dsn => "dbi:mysql:bar",
123
- # :user => "tester",
124
- # :password => "blinky"
125
- #
126
- #
127
- def self.add_source(name,options={})
128
- sources[name] = OpenStruct.new(options)
129
- check_source(sources[name],name)
130
- end
131
-
132
- attr_accessor :raw_data
133
-
134
- # The original SQL for the Query object
135
- attr_reader :sql
136
-
137
- # This will set the <tt>dsn</tt>, <tt>username</tt>, and <tt>password</tt>
138
- # to one specified by a source in Ruport::Query.
139
- #
140
- def select_source(label)
141
- @dsn = Ruport::Query.sources[label].dsn
142
- @user = Ruport::Query.sources[label].user
143
- @password = Ruport::Query.sources[label].password
144
- end
145
-
146
- # Yields result set by row.
147
- def each(&action)
148
- raise(LocalJumpError, "No block given!") unless action
149
- fetch(&action)
150
- self
151
- end
152
-
153
- # Runs the SQL query and returns the result set
154
- def result; fetch; end
155
-
156
- # Runs the query without returning its results.
157
- def execute; fetch; nil; end
158
-
159
- # Returns a Data::Table, even if in <tt>raw_data</tt> mode.
160
- def to_table
161
- data_flag, @raw_data = @raw_data, false
162
- data = fetch; @raw_data = data_flag; return data
163
- end
164
-
165
- # Returns a csv dump of the query.
166
- def to_csv
167
- fetch.to_csv
168
- end
169
-
170
- # Returns a Generator object of the result set.
171
- def generator
172
- Generator.new(fetch)
173
- end
174
-
175
- private
176
-
177
- def query_data(query_text, params=@params)
178
-
179
- require "dbi"
180
-
181
- data = @raw_data ? [] : Data::Table.new
182
-
183
- DBI.connect(@dsn, @user, @password) do |dbh|
184
- dbh.execute(query_text, *(params || [])) do |sth|
185
- # Work-around for inconsistent DBD behavior w/ resultless queries
186
- names = sth.column_names rescue []
187
- if names.empty?
188
- # Work-around for SQLite3 DBD bug
189
- sth.cancel rescue nil
190
- return nil
191
- end
192
-
193
- data.column_names = names unless @raw_data
194
-
195
- sth.each do |row|
196
- row = row.to_a
197
- row = Data::Record.new(row, :attributes => names) unless @raw_data
198
- yield row if block_given?
199
- data << row if !block_given?
200
- end
201
- end
202
- end
203
- data
204
- end
205
-
206
- def get_query(type,query)
207
- type.eql?(:file) ? load_file( query ) : query
208
- end
209
-
210
- def fetch(&block)
211
- data = nil
212
- final = @statements.size - 1
213
- @statements.each_with_index do |query_text, index|
214
- data = query_data(query_text, &(index == final ? block : nil))
215
- end
216
- return data
217
- end
218
-
219
- def load_file(query_file)
220
- begin
221
- File.read( query_file ).strip
222
- rescue
223
- raise LoadError, "Could not open #{query_file}"
224
- end
225
- end
226
-
227
- def self.check_source(settings,label) # :nodoc:
228
- raise ArgumentError unless settings.dsn
229
- end
230
-
231
- end
232
- end
@@ -1,272 +0,0 @@
1
- #!/usr/bin/env ruby -w
2
- require File.join(File.expand_path(File.dirname(__FILE__)), "helpers")
3
-
4
- begin
5
- require "mocha"
6
- require "stubba"
7
- Ruport.quiet { require "active_record" }
8
- rescue LoadError
9
- nil
10
- end
11
-
12
- if Object.const_defined?(:ActiveRecord) && Object.const_defined?(:Mocha)
13
-
14
- require "ruport/acts_as_reportable"
15
-
16
- class Team < ActiveRecord::Base
17
- acts_as_reportable :except => 'id', :include => :players
18
- has_many :players
19
- end
20
-
21
- class Player < ActiveRecord::Base
22
- acts_as_reportable
23
- belongs_to :team
24
- belongs_to :personal_trainer
25
-
26
- def stats
27
- "#{name} stats"
28
- end
29
- end
30
-
31
- module SomeModule
32
- class PersonalTrainer < ActiveRecord::Base
33
- acts_as_reportable
34
- has_one :team
35
- has_many :players
36
- end
37
- end
38
-
39
- module ModelStubsSetup
40
- Column = ActiveRecord::ConnectionAdapters::Column
41
- PersonalTrainer = SomeModule::PersonalTrainer
42
-
43
- def setup
44
- setup_column_stubs
45
-
46
- @trainers = []
47
- @trainers << PersonalTrainer.new(:name => "Trainer 1")
48
- @trainers << PersonalTrainer.new(:name => "Trainer 2")
49
- @teams = []
50
- @teams << Team.new( :name => "Testers",
51
- :league => "My League")
52
- @teams << Team.new( :name => "Others",
53
- :league => "Other League")
54
- @players = []
55
- @players << Player.new( :team_id => 1,
56
- :name => "Player 1",
57
- :personal_trainer_id => 1)
58
- @players << Player.new( :team_id => 1,
59
- :name => "Player 2",
60
- :personal_trainer_id => 2)
61
-
62
- setup_find_stubs
63
- end
64
-
65
- private
66
-
67
- def setup_column_stubs
68
- PersonalTrainer.stubs(:columns).returns([
69
- Column.new("id", nil, "integer", false),
70
- Column.new("name", nil, "string", false)])
71
- Team.stubs(:columns).returns([Column.new("id", nil, "integer", false),
72
- Column.new("name", nil, "string", false),
73
- Column.new("league", nil, "string", true)])
74
- Player.stubs(:columns).returns([Column.new("id", nil, "integer", false),
75
- Column.new("team_id", nil, "integer", true),
76
- Column.new("name", nil, "string", false),
77
- Column.new("personal_trainer_id", nil, "integer", true)])
78
- end
79
-
80
- def setup_find_stubs
81
- PersonalTrainer.stubs(:find).returns(@trainers)
82
- @trainers[0].stubs(:players).returns([@players[0]])
83
- @trainers[1].stubs(:players).returns([@players[1]])
84
- Team.stubs(:find).returns(@teams)
85
- @teams[0].stubs(:players).returns(@players)
86
- @teams[1].stubs(:players).returns([])
87
- Player.stubs(:find).returns(@players)
88
- Player.stubs(:find_by_sql).returns(@players)
89
- @players[0].stubs(:team).returns(@teams[0])
90
- @players[1].stubs(:team).returns(@teams[0])
91
- @players[0].stubs(:personal_trainer).returns(@trainers[0])
92
- @players[1].stubs(:personal_trainer).returns(@trainers[1])
93
- end
94
- end
95
-
96
-
97
- class TestActsAsReportableClassMethods < Test::Unit::TestCase
98
-
99
- def test_aar_options_set
100
- assert_equal({:except => 'id', :include => :players}, Team.aar_options)
101
- end
102
- end
103
-
104
- class TestActsAsReportableSingletonMethods < Test::Unit::TestCase
105
- include ModelStubsSetup
106
-
107
- def test_basic_report_table
108
- actual = Player.report_table
109
- expected = [[1, "Player 1", 1],
110
- [1, "Player 2", 2]].to_table(%w[team_id name personal_trainer_id])
111
- assert_equal expected, actual
112
- end
113
-
114
- def test_report_table_by_sql
115
- actual = Player.report_table_by_sql("SELECT * FROM players")
116
- expected = [[1, "Player 1", 1],
117
- [1, "Player 2", 2]].to_table(%w[team_id name personal_trainer_id])
118
- assert_equal expected, actual
119
- end
120
-
121
- def test_only_option
122
- actual = Player.report_table(:all, :only => 'name')
123
- expected = [["Player 1"],["Player 2"]].to_table(%w[name])
124
- assert_equal expected, actual
125
- end
126
-
127
- def test_only_option_preserves_column_sort_order
128
- column_order = %w[name personal_trainer_id team_id]
129
- actual = Player.report_table(:all, :only => column_order)
130
- expected = [["Player 1", 1, 1],
131
- ["Player 2", 2, 1]].to_table(column_order)
132
- assert_equal expected, actual
133
- end
134
-
135
- def test_except_option
136
- actual = Player.report_table(:all, :except => 'personal_trainer_id')
137
- expected = [[1, "Player 1"],[1, "Player 2"]].to_table(%w[team_id name])
138
- assert_equal expected, actual
139
- end
140
-
141
- def test_methods_option
142
- actual = Player.report_table(:all, :only => 'name', :methods => :stats)
143
- expected = [["Player 1", "Player 1 stats"],
144
- ["Player 2", "Player 2 stats"]].to_table(%w[name stats])
145
- assert_equal expected, actual
146
- end
147
-
148
- def test_include_option
149
- actual = Player.report_table(:all, :only => 'name',
150
- :include => :personal_trainer)
151
- expected = [["Player 1", "Trainer 1"],
152
- ["Player 2", "Trainer 2"]].to_table(%w[name personal_trainer.name])
153
- assert_equal expected, actual
154
- end
155
-
156
- def test_column_sorting_works_with_include_option
157
- actual = Player.report_table(:all,
158
- :only => %w[name personal_trainer.name],
159
- :include => { :personal_trainer => { :only => %w[name] } })
160
- expected = [["Player 1", "Trainer 1"],
161
- ["Player 2", "Trainer 2"]].to_table(%w[name personal_trainer.name])
162
- assert_equal expected, actual
163
-
164
- actual = Player.report_table(:all,
165
- :only => %w[personal_trainer.name name],
166
- :include => { :personal_trainer => { :only => %w[name] } })
167
- expected = [["Trainer 1", "Player 1"],
168
- ["Trainer 2", "Player 2"]].to_table(%w[personal_trainer.name name])
169
- assert_equal expected, actual
170
- end
171
-
172
- def test_include_has_options
173
- actual = Team.report_table(:all, :only => 'name',
174
- :include => { :players => { :only => 'name' } })
175
- expected = [["Testers", "Player 1"],
176
- ["Testers", "Player 2"],
177
- ["Others", nil]].to_table(%w[name players.name])
178
- assert_equal expected, actual
179
- end
180
-
181
- class CustomRecord < Ruport::Data::Record; end
182
-
183
- def test_record_class_option
184
- actual = Player.report_table(:all, :record_class => CustomRecord)
185
- actual.each { |r| assert_equal CustomRecord, r.class }
186
-
187
- actual = Player.report_table_by_sql("SELECT * FROM players",
188
- :record_class => CustomRecord)
189
- actual.each { |r| assert_equal CustomRecord, r.class }
190
- end
191
-
192
- def test_get_include_for_find
193
- assert_equal :players, Team.send(:get_include_for_find, nil)
194
- assert_equal nil, Player.send(:get_include_for_find, nil)
195
- assert_equal :team, Player.send(:get_include_for_find, :team)
196
- expected = {:team => {}}
197
- assert_equal expected,
198
- Player.send(:get_include_for_find, {:team => {:except => 'id'}})
199
- expected = {:team => {:a => {}, :b => {}},
200
- :c => {:d => {:e => {}, :f => {}}},
201
- :g => {}}
202
- assert_equal expected,
203
- Player.send(:get_include_for_find, {:team => {:include => [:a,:b]},
204
- :c => {:include => {:d => {:include => [:e,:f]}}}, :g => {}})
205
- end
206
- end
207
-
208
- class TestActsAsReportableInstanceMethods < Test::Unit::TestCase
209
- include ModelStubsSetup
210
-
211
- def test_reportable_data
212
- actual = @players[0].reportable_data
213
- expected = [{ 'team_id' => 1,
214
- 'name' => "Player 1",
215
- 'personal_trainer_id' => 1 }]
216
- assert_equal expected, actual
217
-
218
- actual = @teams[0].reportable_data(:include =>
219
- { :players => { :only => 'name' } })
220
- expected = [{ 'name' => "Testers",
221
- 'league' => "My League",
222
- 'players.name' => "Player 1" },
223
- { 'name' => "Testers",
224
- 'league' => "My League",
225
- 'players.name' => "Player 2" }]
226
- assert_equal expected, actual
227
- end
228
-
229
- def test_add_includes
230
- actual = @players[0].send(:add_includes,
231
- [{ 'name' => "Player 1" }], :personal_trainer)
232
- expected = [{ 'name' => "Player 1",
233
- 'personal_trainer.name' => "Trainer 1" }]
234
- assert_equal expected, actual
235
- end
236
-
237
- def test_has_report_options
238
- assert @teams[0].send(:has_report_options?, { :only => 'name' })
239
- assert @teams[0].send(:has_report_options?, { :except => 'name' })
240
- assert @teams[0].send(:has_report_options?, { :methods => 'name' })
241
- assert @teams[0].send(:has_report_options?, { :include => 'name' })
242
- assert !@teams[0].send(:has_report_options?, { :foo => 'name' })
243
- end
244
-
245
- def test_get_attributes_with_options
246
- actual = @players[0].send(:get_attributes_with_options)
247
- expected = { 'team_id' => 1,
248
- 'name' => "Player 1",
249
- 'personal_trainer_id' => 1 }
250
- assert_equal expected, actual
251
-
252
- actual = @players[0].send(:get_attributes_with_options,
253
- { :only => 'name' })
254
- expected = { 'name' => "Player 1" }
255
- assert_equal expected, actual
256
-
257
- actual = @players[0].send(:get_attributes_with_options,
258
- { :except => 'personal_trainer_id' })
259
- expected = { 'team_id' => 1,
260
- 'name' => "Player 1" }
261
- assert_equal expected, actual
262
-
263
- actual = @players[0].send(:get_attributes_with_options,
264
- { :only => 'name', :qualify_attribute_names => :players })
265
- expected = { 'players.name' => "Player 1" }
266
- assert_equal expected, actual
267
- end
268
- end
269
-
270
- else
271
- $stderr.puts "Warning: Mocha and/or ActiveRecord not found -- skipping AAR tests"
272
- end