sequel_core 1.4.0 → 1.5.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.
- 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
|
|