sequel_core 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +74 -0
- data/COPYING +1 -0
- data/README +17 -6
- data/Rakefile +16 -21
- data/lib/sequel_core.rb +18 -28
- data/lib/sequel_core/adapters/ado.rb +3 -15
- data/lib/sequel_core/adapters/dbi.rb +1 -14
- data/lib/sequel_core/adapters/informix.rb +3 -3
- data/lib/sequel_core/adapters/jdbc.rb +2 -2
- data/lib/sequel_core/adapters/mysql.rb +39 -59
- data/lib/sequel_core/adapters/odbc.rb +18 -38
- data/lib/sequel_core/adapters/openbase.rb +1 -17
- data/lib/sequel_core/adapters/oracle.rb +1 -19
- data/lib/sequel_core/adapters/postgres.rb +20 -60
- data/lib/sequel_core/adapters/sqlite.rb +4 -8
- data/lib/sequel_core/connection_pool.rb +150 -0
- data/lib/sequel_core/core_ext.rb +41 -0
- data/lib/sequel_core/core_sql.rb +35 -38
- data/lib/sequel_core/database.rb +20 -17
- data/lib/sequel_core/dataset.rb +49 -80
- data/lib/sequel_core/dataset/callback.rb +11 -13
- data/lib/sequel_core/dataset/convenience.rb +18 -136
- data/lib/sequel_core/dataset/pagination.rb +81 -0
- data/lib/sequel_core/dataset/sequelizer.rb +5 -4
- data/lib/sequel_core/dataset/sql.rb +43 -33
- data/lib/sequel_core/deprecated.rb +200 -0
- data/lib/sequel_core/exceptions.rb +0 -14
- data/lib/sequel_core/object_graph.rb +199 -0
- data/lib/sequel_core/pretty_table.rb +27 -24
- data/lib/sequel_core/schema/generator.rb +16 -4
- data/lib/sequel_core/schema/sql.rb +5 -3
- data/lib/sequel_core/worker.rb +1 -1
- data/spec/adapters/informix_spec.rb +1 -47
- data/spec/adapters/mysql_spec.rb +85 -54
- data/spec/adapters/oracle_spec.rb +1 -57
- data/spec/adapters/postgres_spec.rb +66 -49
- data/spec/adapters/sqlite_spec.rb +4 -29
- data/spec/connection_pool_spec.rb +358 -0
- data/spec/core_sql_spec.rb +24 -19
- data/spec/database_spec.rb +13 -9
- data/spec/dataset_spec.rb +59 -78
- data/spec/object_graph_spec.rb +202 -0
- data/spec/pretty_table_spec.rb +1 -9
- data/spec/schema_generator_spec.rb +7 -1
- data/spec/schema_spec.rb +27 -0
- data/spec/sequelizer_spec.rb +2 -2
- data/spec/spec_helper.rb +4 -2
- metadata +16 -57
- data/lib/sequel_core/array_keys.rb +0 -322
- data/lib/sequel_core/model.rb +0 -8
- data/spec/array_keys_spec.rb +0 -682
@@ -0,0 +1,200 @@
|
|
1
|
+
module Sequel
|
2
|
+
# This module makes it easy to add deprecation functionality to other classes.
|
3
|
+
module Deprecation
|
4
|
+
# This sets the output stream for the deprecation messages. Set it to an IO
|
5
|
+
# (or any object that responds to puts) and it will call puts on that
|
6
|
+
# object with the deprecation message. Set to nil to ignore deprecation messages.
|
7
|
+
def self.deprecation_message_stream=(file)
|
8
|
+
@dms = file
|
9
|
+
end
|
10
|
+
|
11
|
+
# Set this to true to print tracebacks with every deprecation message,
|
12
|
+
# so you can see exactly where in your code the deprecated methods are
|
13
|
+
# being called.
|
14
|
+
def self.print_tracebacks=(pt)
|
15
|
+
@pt = pt
|
16
|
+
end
|
17
|
+
|
18
|
+
# Puts the messages unaltered to the deprecation message stream
|
19
|
+
def self.deprecate(message)
|
20
|
+
if @dms
|
21
|
+
@dms.puts(message)
|
22
|
+
caller.each{|c| @dms.puts(c)} if @pt
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Formats the message with a message that it will be removed in Sequel 2.0.
|
27
|
+
# This is the method that is added to the classes that include Sequel::Deprecation.
|
28
|
+
def deprecate(meth, message = nil)
|
29
|
+
::Sequel::Deprecation.deprecate("#{meth} is deprecated, and will be removed in Sequel 2.0.#{" #{message}." if message}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
include Sequel::Deprecation
|
35
|
+
def method_missing(m, *args) #:nodoc:
|
36
|
+
deprecate("Sequel.method_missing", "You should define Sequel.#{m} for the adapter.")
|
37
|
+
c = Database.adapter_class(m)
|
38
|
+
begin
|
39
|
+
# three ways to invoke this:
|
40
|
+
# 0 arguments: Sequel.dbi
|
41
|
+
# 1 argument: Sequel.dbi(db_name)
|
42
|
+
# more args: Sequel.dbi(db_name, opts)
|
43
|
+
case args.size
|
44
|
+
when 0
|
45
|
+
opts = {}
|
46
|
+
when 1
|
47
|
+
opts = args[0].is_a?(Hash) ? args[0] : {:database => args[0]}
|
48
|
+
else
|
49
|
+
opts = args[1].merge(:database => args[0])
|
50
|
+
end
|
51
|
+
rescue
|
52
|
+
raise Error::AdapterNotFound, "Unknown adapter (#{m})"
|
53
|
+
end
|
54
|
+
c.new(opts)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Dataset
|
59
|
+
include Deprecation
|
60
|
+
|
61
|
+
MUTATION_RE = /^(.+)!$/.freeze
|
62
|
+
|
63
|
+
def clone_merge(opts = {}) #:nodoc:
|
64
|
+
deprecate("Sequel::Dataset#clone", "Use clone")
|
65
|
+
clone(opts)
|
66
|
+
end
|
67
|
+
|
68
|
+
def set_options(opts) #:nodoc:
|
69
|
+
deprecate("Sequel::Dataset#set_options")
|
70
|
+
@opts = opts
|
71
|
+
@columns = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def set_row_proc(&filter) #:nodoc:
|
75
|
+
deprecate("Sequel::Dataset#set_row_proc", "Use row_proc=")
|
76
|
+
@row_proc = filter
|
77
|
+
end
|
78
|
+
|
79
|
+
def remove_row_proc #:nodoc:
|
80
|
+
deprecate("Sequel::Dataset#remove_row_proc", "Use row_proc=nil")
|
81
|
+
@row_proc = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
# Provides support for mutation methods (filter!, order!, etc.) and magic
|
85
|
+
# methods.
|
86
|
+
def method_missing(m, *args, &block) #:nodoc:
|
87
|
+
if m.to_s =~ MUTATION_RE
|
88
|
+
meth = $1.to_sym
|
89
|
+
super unless respond_to?(meth)
|
90
|
+
copy = send(meth, *args, &block)
|
91
|
+
super if copy.class != self.class
|
92
|
+
deprecate("Sequel::Dataset#method_missing", "Define Sequel::Dataset##{m}, or use Sequel::Dataset.def_mutation_method(:#{meth})")
|
93
|
+
@opts.merge!(copy.opts)
|
94
|
+
self
|
95
|
+
elsif magic_method_missing(m)
|
96
|
+
send(m, *args)
|
97
|
+
else
|
98
|
+
super
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
MAGIC_METHODS = {
|
103
|
+
/^order_by_(.+)$/ => proc {|c| proc {deprecate("Sequel::Dataset#method_missing", "Use order(#{c.inspect}) or define order_by_#{c}"); order(c)}},
|
104
|
+
/^first_by_(.+)$/ => proc {|c| proc {deprecate("Sequel::Dataset#method_missing", "Use order(#{c.inspect}).first or define first_by_#{c}"); order(c).first}},
|
105
|
+
/^last_by_(.+)$/ => proc {|c| proc {deprecate("Sequel::Dataset#method_missing", "Use order(#{c.inspect}).last or define last_by_#{c}"); order(c).last}},
|
106
|
+
/^filter_by_(.+)$/ => proc {|c| proc {|v| deprecate("Sequel::Dataset#method_missing", "Use filter(#{c.inspect}=>#{v.inspect}) or define filter_by_#{c}"); filter(c => v)}},
|
107
|
+
/^all_by_(.+)$/ => proc {|c| proc {|v| deprecate("Sequel::Dataset#method_missing", "Use filter(#{c.inspect}=>#{v.inspect}).all or define all_by_#{c}"); filter(c => v).all}},
|
108
|
+
/^find_by_(.+)$/ => proc {|c| proc {|v| deprecate("Sequel::Dataset#method_missing", "Use filter(#{c.inspect}=>#{v.inspect}).first or define find_by_#{c}"); filter(c => v).first}},
|
109
|
+
/^group_by_(.+)$/ => proc {|c| proc {deprecate("Sequel::Dataset#method_missing", "Use group(#{c.inspect}) or define group_by_#{c}"); group(c)}},
|
110
|
+
/^count_by_(.+)$/ => proc {|c| proc {deprecate("Sequel::Dataset#method_missing", "Use group_and_count(#{c.inspect}) or define count_by_#{c})"); group_and_count(c)}}
|
111
|
+
}
|
112
|
+
|
113
|
+
# Checks if the given method name represents a magic method and
|
114
|
+
# defines it. Otherwise, nil is returned.
|
115
|
+
def magic_method_missing(m) #:nodoc:
|
116
|
+
method_name = m.to_s
|
117
|
+
MAGIC_METHODS.each_pair do |r, p|
|
118
|
+
if method_name =~ r
|
119
|
+
impl = p[$1.to_sym]
|
120
|
+
return Dataset.class_def(m, &impl)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
module SQL
|
128
|
+
module DeprecatedColumnMethods #:nodoc:
|
129
|
+
AS = 'AS'.freeze
|
130
|
+
DESC = 'DESC'.freeze
|
131
|
+
ASC = 'ASC'.freeze
|
132
|
+
|
133
|
+
def as(a) #:nodoc:
|
134
|
+
Sequel::Deprecation.deprecate("Object#as is deprecated and will be removed in Sequel 2.0. Use Symbol#as or String#as.")
|
135
|
+
ColumnExpr.new(self, AS, a)
|
136
|
+
end
|
137
|
+
def AS(a) #:nodoc:
|
138
|
+
Sequel::Deprecation.deprecate("Object#AS is deprecated and will be removed in Sequel 2.0. Use Symbol#as or String#as.")
|
139
|
+
ColumnExpr.new(self, AS, a)
|
140
|
+
end
|
141
|
+
def desc #:nodoc:
|
142
|
+
Sequel::Deprecation.deprecate("Object#desc is deprecated and will be removed in Sequel 2.0. Use Symbol#desc or String#desc.")
|
143
|
+
ColumnExpr.new(self, DESC)
|
144
|
+
end
|
145
|
+
def DESC #:nodoc:
|
146
|
+
Sequel::Deprecation.deprecate("Object#DESC is deprecated and will be removed in Sequel 2.0. Use Symbol#desc or String#desc.")
|
147
|
+
ColumnExpr.new(self, DESC)
|
148
|
+
end
|
149
|
+
def asc #:nodoc:
|
150
|
+
Sequel::Deprecation.deprecate("Object#asc is deprecated and will be removed in Sequel 2.0. Use Symbol#asc or String#asc.")
|
151
|
+
ColumnExpr.new(self, ASC)
|
152
|
+
end
|
153
|
+
def ASC #:nodoc:
|
154
|
+
Sequel::Deprecation.deprecate("Object#ASC is deprecated and will be removed in Sequel 2.0. Use Symbol#asc or String#asc.")
|
155
|
+
ColumnExpr.new(self, ASC)
|
156
|
+
end
|
157
|
+
def all #:nodoc:
|
158
|
+
Sequel::Deprecation.deprecate("Object#all is deprecated and will be removed in Sequel 2.0. Use :#{self}.* or '#{self}.*'.lit.")
|
159
|
+
Sequel::SQL::ColumnAll.new(self)
|
160
|
+
end
|
161
|
+
def ALL #:nodoc:
|
162
|
+
Sequel::Deprecation.deprecate("Object#ALL is deprecated and will be removed in Sequel 2.0. Use :#{self}.* or '#{self}.*'.lit.")
|
163
|
+
Sequel::SQL::ColumnAll.new(self)
|
164
|
+
end
|
165
|
+
|
166
|
+
def cast_as(t) #:nodoc:
|
167
|
+
Sequel::Deprecation.deprecate("Object#cast_as is deprecated and will be removed in Sequel 2.0. Use Symbol#cast_as or String#cast_as.")
|
168
|
+
if t.is_a?(Symbol)
|
169
|
+
t = t.to_s.lit
|
170
|
+
end
|
171
|
+
Sequel::SQL::Function.new(:cast, self.as(t))
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class Object
|
178
|
+
include Sequel::SQL::DeprecatedColumnMethods
|
179
|
+
def Sequel(*args) #:nodoc:
|
180
|
+
Sequel::Deprecation.deprecate("Object#Sequel is deprecated and will be removed in Sequel 2.0. Use Sequel.connect.")
|
181
|
+
Sequel.connect(*args)
|
182
|
+
end
|
183
|
+
def rollback! #:nodoc:
|
184
|
+
Sequel::Deprecation.deprecate("Object#rollback! is deprecated and will be removed in Sequel 2.0. Use raise Sequel::Error::Rollback.")
|
185
|
+
raise Sequel::Error::Rollback
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
class Symbol
|
190
|
+
# Converts missing method calls into functions on columns, if the
|
191
|
+
# method name is made of all upper case letters.
|
192
|
+
def method_missing(sym, *args) #:nodoc:
|
193
|
+
if ((s = sym.to_s) =~ /^([A-Z]+)$/)
|
194
|
+
Sequel::Deprecation.deprecate("Symbol#method_missing is deprecated and will be removed in Sequel 2.0. Use :#{sym}[:#{self}].")
|
195
|
+
Sequel::SQL::Function.new(s.downcase, self)
|
196
|
+
else
|
197
|
+
super
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -35,17 +35,3 @@ module Sequel
|
|
35
35
|
class AdapterNotFound < Error ; end
|
36
36
|
end
|
37
37
|
end
|
38
|
-
|
39
|
-
# Object extensions
|
40
|
-
class Object
|
41
|
-
# Cancels the current transaction without an error:
|
42
|
-
#
|
43
|
-
# DB.tranaction do
|
44
|
-
# ...
|
45
|
-
# rollback! if failed_to_contact_client
|
46
|
-
# ...
|
47
|
-
# end
|
48
|
-
def rollback!
|
49
|
-
raise Sequel::Error::Rollback
|
50
|
-
end
|
51
|
-
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
module Sequel
|
2
|
+
class Dataset
|
3
|
+
# Allows you to join multiple datasets/tables and have the result set
|
4
|
+
# split into component tables.
|
5
|
+
#
|
6
|
+
# This differs from the usual usage of join, which returns the result set
|
7
|
+
# as a single hash. For example:
|
8
|
+
#
|
9
|
+
# # CREATE TABLE artists (id INTEGER, name TEXT);
|
10
|
+
# # CREATE TABLE albums (id INTEGER, name TEXT, artist_id INTEGER);
|
11
|
+
# DB[:artists].left_outer_join(:albums, :artist_id=>:id).first
|
12
|
+
# => {:id=>(albums.id||artists.id), :name=>(albums.name||artist.names), :artist_id=>albums.artist_id}
|
13
|
+
# DB[:artists].graph(:albums, :artist_id=>:id).first
|
14
|
+
# => {:artists=>{:id=>artists.id, :name=>artists.name}, :albums=>{:id=>albums.id, :name=>albums.name, :artist_id=>albums.artist_id}}
|
15
|
+
#
|
16
|
+
# Using a join such as left_outer_join, the attribute names that are shared between
|
17
|
+
# the tables are combined in the single return hash. You can get around that by
|
18
|
+
# using .select with correct aliases for all of the columns, but it is simpler to
|
19
|
+
# use graph and have the result set split for you. In addition, graph respects
|
20
|
+
# any row_proc or transform attributes of the current dataset and the datasets
|
21
|
+
# you use with graph.
|
22
|
+
#
|
23
|
+
# Arguments:
|
24
|
+
# * dataset - Can be a symbol (specifying a table), another dataset,
|
25
|
+
# or an object that responds to .dataset and yields a symbol or a dataset
|
26
|
+
# * join_conditions - A conditions hash that is passed to the join_table method
|
27
|
+
# * options - A hash of graph options. The following options are currently used:
|
28
|
+
# * :table_alias - The alias to use for the table. If not specified, doesn't
|
29
|
+
# alias the table. You will get an error if the the alias (or table) name is
|
30
|
+
# used more than once.
|
31
|
+
# * :join_type - The type of join to use (passed to join_table). Defaults to
|
32
|
+
# :left_outer.
|
33
|
+
# * :select - Whether to select the columns from the you are joining, and
|
34
|
+
# include them as a separate hash in the output. With this set to false,
|
35
|
+
# it is like simply joining the tables. This is designed to be used for
|
36
|
+
# many_to_many join tables, where the columns are just foreign keys to primary
|
37
|
+
# keys in other tables.
|
38
|
+
def graph(dataset, join_conditions, options = {})
|
39
|
+
# Allow the use of a model, dataset, or symbol as the first argument
|
40
|
+
# Find the table name/dataset based on the argument
|
41
|
+
dataset = dataset.dataset if dataset.respond_to?(:dataset)
|
42
|
+
case dataset
|
43
|
+
when Symbol
|
44
|
+
table = dataset
|
45
|
+
dataset = @db[dataset]
|
46
|
+
when ::Sequel::Dataset
|
47
|
+
table = dataset.first_source
|
48
|
+
else
|
49
|
+
raise Error, "The dataset argument should be a symbol, dataset, or model"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Raise Sequel::Error with explanation that the table alias has been used
|
53
|
+
raise_alias_error = lambda do
|
54
|
+
raise(Error, "this #{options[:table_alias] ? 'alias' : 'table'} has already been been used, please specify " \
|
55
|
+
"#{options[:table_alias] ? 'a different alias' : 'an alias via the :table_alias option'}")
|
56
|
+
end
|
57
|
+
|
58
|
+
# Only allow table aliases that haven't been used
|
59
|
+
table_alias = options[:table_alias] || table
|
60
|
+
raise_alias_error.call if @opts[:graph] && @opts[:graph][:table_aliases] && @opts[:graph][:table_aliases].include?(table_alias)
|
61
|
+
|
62
|
+
# Join the table early in order to avoid cloning the dataset twice
|
63
|
+
ds = join_table(options[:join_type] || :left_outer, table == table_alias ? table : "#{table} #{table_alias}", join_conditions)
|
64
|
+
opts = ds.opts
|
65
|
+
|
66
|
+
# Whether to include the table in the result set
|
67
|
+
add_table = options[:select] == false ? false : true
|
68
|
+
# Whether to add the columns to the list of column aliases
|
69
|
+
add_columns = !ds.opts.include?(:graph_aliases)
|
70
|
+
|
71
|
+
# Setup the initial graph data structure if it doesn't exist
|
72
|
+
unless graph = opts[:graph]
|
73
|
+
master = ds.first_source
|
74
|
+
raise_alias_error.call if master == table_alias
|
75
|
+
# Master hash storing all .graph related information
|
76
|
+
graph = opts[:graph] = {}
|
77
|
+
# Associates column aliases back to tables and columns
|
78
|
+
column_aliases = graph[:column_aliases] = {}
|
79
|
+
# Associates table alias (the master is never aliased)
|
80
|
+
table_aliases = graph[:table_aliases] = {master=>self}
|
81
|
+
# Keep track of the alias numbers used
|
82
|
+
ca_num = graph[:column_alias_num] = {}
|
83
|
+
# All columns in the master table are never
|
84
|
+
# aliased, but are not included if set_graph_aliases
|
85
|
+
# has been used.
|
86
|
+
if add_columns
|
87
|
+
select = (opts[:select] ||= [])
|
88
|
+
columns.each do |column|
|
89
|
+
column_aliases[column] = [master, column]
|
90
|
+
select.push(:"#{master}__#{column}")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Add the table alias to the list of aliases
|
96
|
+
# Even if it isn't been used in the result set,
|
97
|
+
# we add a key for it with a nil value so we can check if it
|
98
|
+
# is used more than once
|
99
|
+
table_aliases = graph[:table_aliases]
|
100
|
+
table_aliases[table_alias] = add_table ? dataset : nil
|
101
|
+
|
102
|
+
# Add the columns to the selection unless we are ignoring them
|
103
|
+
if add_table && add_columns
|
104
|
+
select = opts[:select]
|
105
|
+
column_aliases = graph[:column_aliases]
|
106
|
+
ca_num = graph[:column_alias_num]
|
107
|
+
# If the column hasn't been used yet, don't alias it.
|
108
|
+
# If it has been used, try table_column.
|
109
|
+
# If that has been used, try table_column_N
|
110
|
+
# using the next value of N that we know hasn't been
|
111
|
+
# used
|
112
|
+
dataset.columns.each do |column|
|
113
|
+
col_alias, c = if column_aliases[column]
|
114
|
+
tc = :"#{table_alias}_#{column}"
|
115
|
+
if column_aliases[tc]
|
116
|
+
if can = ca_num[tc]
|
117
|
+
ca_num[tc] += 1
|
118
|
+
tc = :"#{tc}_#{can}"
|
119
|
+
else
|
120
|
+
ca_num[tc] = 1
|
121
|
+
tc = :"#{tc}_0"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
[tc, :"#{table_alias}__#{column}___#{tc}"]
|
125
|
+
else
|
126
|
+
[column, :"#{table_alias}__#{column}"]
|
127
|
+
end
|
128
|
+
column_aliases[col_alias] = [table_alias, column]
|
129
|
+
select.push(c)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
ds
|
133
|
+
end
|
134
|
+
|
135
|
+
# This allows you to manually specify the graph aliases to use
|
136
|
+
# when using graph. You can use it to only select certain
|
137
|
+
# columns, and have those columns mapped to specific aliases
|
138
|
+
# in the result set. This is the equivalent of .select for a
|
139
|
+
# graphed dataset, and must be used instead of .select whenever
|
140
|
+
# graphing is used. Example:
|
141
|
+
#
|
142
|
+
# DB[:artists].graph(:albums, :artist_id=>:id).set_graph_aliases(:artist_name=>[:artists, :name], :album_name=>[:albums, :name]).first
|
143
|
+
# => {:artists=>{:name=>artists.name}, :albums=>{:name=>albums.name}}
|
144
|
+
#
|
145
|
+
# Arguments:
|
146
|
+
# * graph_aliases - Should be a hash with keys being symbols of
|
147
|
+
# column aliases, and values being arrays with two symbol elements.
|
148
|
+
# The first element of the array should be the table alias,
|
149
|
+
# and the second should be the actual column name.
|
150
|
+
def set_graph_aliases(graph_aliases)
|
151
|
+
ds = select(*graph_aliases.collect{|col_alias, tc| :"#{tc[0]}__#{tc[1]}#{"___#{col_alias}" unless tc[1] == col_alias}"})
|
152
|
+
ds.opts[:graph_aliases]=graph_aliases
|
153
|
+
ds
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
# Fetch the rows, split them into component table parts,
|
158
|
+
# tranform and run the row_proc on each part (if applicable),
|
159
|
+
# and yield a hash of the parts.
|
160
|
+
def graph_each(opts, &block)
|
161
|
+
# Reject tables with nil datasets, as they are excluded from
|
162
|
+
# the result set
|
163
|
+
datasets = @opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?}
|
164
|
+
# Get just the list of table aliases into a local variable, for speed
|
165
|
+
table_aliases = datasets.collect{|ta,ds| ta}
|
166
|
+
# Get an array of arrays, one for each dataset, with
|
167
|
+
# the necessary information about each dataset, for speed
|
168
|
+
datasets = datasets.collect do |ta, ds|
|
169
|
+
[ta, ds, ds.instance_variable_get(:@transform), ds.row_proc]
|
170
|
+
end
|
171
|
+
# Use the manually set graph aliases, if any, otherwise
|
172
|
+
# use the ones automatically created by .graph
|
173
|
+
column_aliases = @opts[:graph_aliases] || @opts[:graph][:column_aliases]
|
174
|
+
fetch_rows(select_sql(opts)) do |r|
|
175
|
+
graph = {}
|
176
|
+
# Create the sub hashes, one per table
|
177
|
+
table_aliases.each{|ta| graph[ta]={}}
|
178
|
+
# Split the result set based on the column aliases
|
179
|
+
# If there are columns in the result set that are
|
180
|
+
# not in column_aliases, they are ignored
|
181
|
+
column_aliases.each do |col_alias, tc|
|
182
|
+
ta, column = tc
|
183
|
+
graph[ta][column] = r[col_alias]
|
184
|
+
end
|
185
|
+
# For each dataset, transform and run the row
|
186
|
+
# row_proc if applicable
|
187
|
+
datasets.each do |ta,ds,tr,rp|
|
188
|
+
g = graph[ta]
|
189
|
+
g = ds.transform_load(g) if tr
|
190
|
+
g = rp[g] if rp
|
191
|
+
graph[ta] = g
|
192
|
+
end
|
193
|
+
|
194
|
+
yield graph
|
195
|
+
end
|
196
|
+
self
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -1,13 +1,27 @@
|
|
1
1
|
module Sequel
|
2
|
-
# Prints nice-looking plain-text tables
|
3
|
-
# +--+-------+
|
4
|
-
# |id|name |
|
5
|
-
# |--+-------|
|
6
|
-
# |1 |fasdfas|
|
7
|
-
# |2 |test |
|
8
|
-
# +--+-------+
|
9
2
|
module PrettyTable
|
10
|
-
|
3
|
+
# Prints nice-looking plain-text tables
|
4
|
+
#
|
5
|
+
# +--+-------+
|
6
|
+
# |id|name |
|
7
|
+
# |--+-------|
|
8
|
+
# |1 |fasdfas|
|
9
|
+
# |2 |test |
|
10
|
+
# +--+-------+
|
11
|
+
def self.print(records, columns = nil) # records is an array of hashes
|
12
|
+
columns ||= records_columns(records)
|
13
|
+
sizes = column_sizes(records, columns)
|
14
|
+
|
15
|
+
puts separator_line(columns, sizes)
|
16
|
+
puts header_line(columns, sizes)
|
17
|
+
puts separator_line(columns, sizes)
|
18
|
+
records.each {|r| puts data_line(columns, sizes, r)}
|
19
|
+
puts separator_line(columns, sizes)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
class << PrettyTable
|
23
|
+
private
|
24
|
+
def records_columns(records)
|
11
25
|
columns = []
|
12
26
|
records.each do |r|
|
13
27
|
if Array === r && (k = r.keys)
|
@@ -19,7 +33,7 @@ module Sequel
|
|
19
33
|
columns
|
20
34
|
end
|
21
35
|
|
22
|
-
def
|
36
|
+
def column_sizes(records, columns)
|
23
37
|
sizes = Hash.new {0}
|
24
38
|
columns.each do |c|
|
25
39
|
s = c.to_s.size
|
@@ -34,12 +48,12 @@ module Sequel
|
|
34
48
|
sizes
|
35
49
|
end
|
36
50
|
|
37
|
-
def
|
51
|
+
def separator_line(columns, sizes)
|
38
52
|
l = ''
|
39
53
|
'+' + columns.map {|c| '-' * sizes[c]}.join('+') + '+'
|
40
54
|
end
|
41
55
|
|
42
|
-
def
|
56
|
+
def format_cell(size, v)
|
43
57
|
case v
|
44
58
|
when Bignum, Fixnum
|
45
59
|
"%#{size}d" % v
|
@@ -50,24 +64,13 @@ module Sequel
|
|
50
64
|
end
|
51
65
|
end
|
52
66
|
|
53
|
-
def
|
67
|
+
def data_line(columns, sizes, record)
|
54
68
|
'|' + columns.map {|c| format_cell(sizes[c], record[c])}.join('|') + '|'
|
55
69
|
end
|
56
70
|
|
57
|
-
def
|
71
|
+
def header_line(columns, sizes)
|
58
72
|
'|' + columns.map {|c| "%-#{sizes[c]}s" % c.to_s}.join('|') + '|'
|
59
73
|
end
|
60
|
-
|
61
|
-
def self.print(records, columns = nil) # records is an array of hashes
|
62
|
-
columns ||= records_columns(records)
|
63
|
-
sizes = column_sizes(records, columns)
|
64
|
-
|
65
|
-
puts separator_line(columns, sizes)
|
66
|
-
puts header_line(columns, sizes)
|
67
|
-
puts separator_line(columns, sizes)
|
68
|
-
records.each {|r| puts data_line(columns, sizes, r)}
|
69
|
-
puts separator_line(columns, sizes)
|
70
|
-
end
|
71
74
|
end
|
72
75
|
end
|
73
76
|
|