epugh-sequel 0.0.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/README.rdoc +652 -0
- data/VERSION.yml +4 -0
- data/bin/sequel +104 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +85 -0
- data/lib/sequel/adapters/db2.rb +132 -0
- data/lib/sequel/adapters/dbi.rb +101 -0
- data/lib/sequel/adapters/do.rb +197 -0
- data/lib/sequel/adapters/do/mysql.rb +38 -0
- data/lib/sequel/adapters/do/postgres.rb +92 -0
- data/lib/sequel/adapters/do/sqlite.rb +31 -0
- data/lib/sequel/adapters/firebird.rb +307 -0
- data/lib/sequel/adapters/informix.rb +75 -0
- data/lib/sequel/adapters/jdbc.rb +485 -0
- data/lib/sequel/adapters/jdbc/h2.rb +62 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
- data/lib/sequel/adapters/mysql.rb +370 -0
- data/lib/sequel/adapters/odbc.rb +184 -0
- data/lib/sequel/adapters/openbase.rb +57 -0
- data/lib/sequel/adapters/oracle.rb +140 -0
- data/lib/sequel/adapters/postgres.rb +453 -0
- data/lib/sequel/adapters/shared/mssql.rb +93 -0
- data/lib/sequel/adapters/shared/mysql.rb +341 -0
- data/lib/sequel/adapters/shared/oracle.rb +62 -0
- data/lib/sequel/adapters/shared/postgres.rb +743 -0
- data/lib/sequel/adapters/shared/progress.rb +34 -0
- data/lib/sequel/adapters/shared/sqlite.rb +263 -0
- data/lib/sequel/adapters/sqlite.rb +243 -0
- data/lib/sequel/adapters/utils/date_format.rb +21 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
- data/lib/sequel/adapters/utils/unsupported.rb +62 -0
- data/lib/sequel/connection_pool.rb +258 -0
- data/lib/sequel/core.rb +204 -0
- data/lib/sequel/core_sql.rb +185 -0
- data/lib/sequel/database.rb +687 -0
- data/lib/sequel/database/schema_generator.rb +324 -0
- data/lib/sequel/database/schema_methods.rb +164 -0
- data/lib/sequel/database/schema_sql.rb +324 -0
- data/lib/sequel/dataset.rb +422 -0
- data/lib/sequel/dataset/convenience.rb +237 -0
- data/lib/sequel/dataset/prepared_statements.rb +220 -0
- data/lib/sequel/dataset/sql.rb +1105 -0
- data/lib/sequel/deprecated.rb +529 -0
- data/lib/sequel/exceptions.rb +44 -0
- data/lib/sequel/extensions/blank.rb +42 -0
- data/lib/sequel/extensions/inflector.rb +288 -0
- data/lib/sequel/extensions/pagination.rb +96 -0
- data/lib/sequel/extensions/pretty_table.rb +78 -0
- data/lib/sequel/extensions/query.rb +48 -0
- data/lib/sequel/extensions/string_date_time.rb +47 -0
- data/lib/sequel/metaprogramming.rb +44 -0
- data/lib/sequel/migration.rb +212 -0
- data/lib/sequel/model.rb +142 -0
- data/lib/sequel/model/association_reflection.rb +263 -0
- data/lib/sequel/model/associations.rb +1024 -0
- data/lib/sequel/model/base.rb +911 -0
- data/lib/sequel/model/deprecated.rb +188 -0
- data/lib/sequel/model/deprecated_hooks.rb +103 -0
- data/lib/sequel/model/deprecated_inflector.rb +335 -0
- data/lib/sequel/model/deprecated_validations.rb +384 -0
- data/lib/sequel/model/errors.rb +37 -0
- data/lib/sequel/model/exceptions.rb +7 -0
- data/lib/sequel/model/inflections.rb +230 -0
- data/lib/sequel/model/plugins.rb +74 -0
- data/lib/sequel/object_graph.rb +230 -0
- data/lib/sequel/plugins/caching.rb +122 -0
- data/lib/sequel/plugins/hook_class_methods.rb +122 -0
- data/lib/sequel/plugins/schema.rb +53 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
- data/lib/sequel/plugins/validation_class_methods.rb +373 -0
- data/lib/sequel/sql.rb +854 -0
- data/lib/sequel/version.rb +11 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/ado_spec.rb +46 -0
- data/spec/adapters/firebird_spec.rb +376 -0
- data/spec/adapters/informix_spec.rb +96 -0
- data/spec/adapters/mysql_spec.rb +875 -0
- data/spec/adapters/oracle_spec.rb +272 -0
- data/spec/adapters/postgres_spec.rb +692 -0
- data/spec/adapters/spec_helper.rb +10 -0
- data/spec/adapters/sqlite_spec.rb +550 -0
- data/spec/core/connection_pool_spec.rb +526 -0
- data/spec/core/core_ext_spec.rb +156 -0
- data/spec/core/core_sql_spec.rb +528 -0
- data/spec/core/database_spec.rb +1214 -0
- data/spec/core/dataset_spec.rb +3513 -0
- data/spec/core/expression_filters_spec.rb +363 -0
- data/spec/core/migration_spec.rb +261 -0
- data/spec/core/object_graph_spec.rb +280 -0
- data/spec/core/pretty_table_spec.rb +58 -0
- data/spec/core/schema_generator_spec.rb +167 -0
- data/spec/core/schema_spec.rb +778 -0
- data/spec/core/spec_helper.rb +82 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/caching_spec.rb +201 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/inflector_spec.rb +122 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/schema_spec.rb +111 -0
- data/spec/extensions/single_table_inheritance_spec.rb +53 -0
- data/spec/extensions/spec_helper.rb +90 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/validation_class_methods_spec.rb +1054 -0
- data/spec/integration/dataset_test.rb +160 -0
- data/spec/integration/eager_loader_test.rb +683 -0
- data/spec/integration/prepared_statement_test.rb +130 -0
- data/spec/integration/schema_test.rb +183 -0
- data/spec/integration/spec_helper.rb +75 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +93 -0
- data/spec/model/associations_spec.rb +1780 -0
- data/spec/model/base_spec.rb +494 -0
- data/spec/model/caching_spec.rb +217 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1165 -0
- data/spec/model/hooks_spec.rb +472 -0
- data/spec/model/inflector_spec.rb +126 -0
- data/spec/model/model_spec.rb +588 -0
- data/spec/model/plugins_spec.rb +142 -0
- data/spec/model/record_spec.rb +1243 -0
- data/spec/model/schema_spec.rb +92 -0
- data/spec/model/spec_helper.rb +124 -0
- data/spec/model/validations_spec.rb +1080 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec.opts +0 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +202 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
# Empty namespace that plugins should use to store themselves,
|
|
3
|
+
# so they can be loaded via Model.plugin.
|
|
4
|
+
#
|
|
5
|
+
# Plugins should be modules with one of the following conditions:
|
|
6
|
+
# * A singleton method named apply, which takes a model and
|
|
7
|
+
# additional arguments.
|
|
8
|
+
# * A module inside the plugin module named InstanceMethods,
|
|
9
|
+
# which will be included in the model class.
|
|
10
|
+
# * A module inside the plugin module named ClassMethods,
|
|
11
|
+
# which will extend the model class.
|
|
12
|
+
# * A module inside the plugin module named DatasetMethods,
|
|
13
|
+
# which will extend the model's dataset.
|
|
14
|
+
module Plugins
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class Model
|
|
18
|
+
# Loads a plugin for use with the model class, passing optional arguments
|
|
19
|
+
# to the plugin. If the plugin has a DatasetMethods module and the model
|
|
20
|
+
# doesn't have a dataset, raise an Error.
|
|
21
|
+
def self.plugin(plugin, *args)
|
|
22
|
+
arg = args.first
|
|
23
|
+
block = lambda{arg}
|
|
24
|
+
m = plugin.is_a?(Module) ? plugin : plugin_module(plugin)
|
|
25
|
+
if m.respond_to?(:apply)
|
|
26
|
+
m.apply(self, *args)
|
|
27
|
+
end
|
|
28
|
+
if m.const_defined?("InstanceMethods")
|
|
29
|
+
define_method(:"#{plugin}_opts", &block)
|
|
30
|
+
include(m::InstanceMethods)
|
|
31
|
+
end
|
|
32
|
+
if m.const_defined?("ClassMethods")
|
|
33
|
+
meta_def(:"#{plugin}_opts", &block)
|
|
34
|
+
extend(m::ClassMethods)
|
|
35
|
+
end
|
|
36
|
+
if m.const_defined?("DatasetMethods")
|
|
37
|
+
if @dataset
|
|
38
|
+
dataset.meta_def(:"#{plugin}_opts", &block)
|
|
39
|
+
dataset.extend(m::DatasetMethods)
|
|
40
|
+
end
|
|
41
|
+
dataset_method_modules << m::DatasetMethods
|
|
42
|
+
def_dataset_method(*m::DatasetMethods.public_instance_methods.reject{|x| DATASET_METHOD_RE !~ x.to_s})
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
module ClassMethods
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# Returns the new style location for the plugin name.
|
|
50
|
+
def plugin_gem_location(plugin)
|
|
51
|
+
"sequel/plugins/#{plugin}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns the old style location for the plugin name.
|
|
55
|
+
def plugin_gem_location_old(plugin)
|
|
56
|
+
"sequel_#{plugin}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns the module for the specified plugin. If the module is not
|
|
60
|
+
# defined, the corresponding plugin gem is automatically loaded.
|
|
61
|
+
def plugin_module(plugin)
|
|
62
|
+
module_name = plugin.to_s.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
|
|
63
|
+
if not Sequel::Plugins.const_defined?(module_name)
|
|
64
|
+
begin
|
|
65
|
+
require plugin_gem_location(plugin)
|
|
66
|
+
rescue LoadError
|
|
67
|
+
require plugin_gem_location_old(plugin)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
Sequel::Plugins.const_get(module_name)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,230 @@
|
|
|
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
|
+
# If you are graphing a table and all columns for that table are nil, this
|
|
24
|
+
# indicates that no matching rows existed in the table, so graph will return nil
|
|
25
|
+
# instead of a hash with all nil values:
|
|
26
|
+
#
|
|
27
|
+
# # If the artist doesn't have any albums
|
|
28
|
+
# DB[:artists].graph(:albums, :artist_id=>:id).first
|
|
29
|
+
# => {:artists=>{:id=>artists.id, :name=>artists.name}, :albums=>nil}
|
|
30
|
+
#
|
|
31
|
+
# Arguments:
|
|
32
|
+
# * dataset - Can be a symbol (specifying a table), another dataset,
|
|
33
|
+
# or an object that responds to .dataset and yields a symbol or a dataset
|
|
34
|
+
# * join_conditions - Any condition(s) allowed by join_table.
|
|
35
|
+
# * options - A hash of graph options. The following options are currently used:
|
|
36
|
+
# * :implicit_qualifier - The qualifier of implicit conditions, see #join_table.
|
|
37
|
+
# * :join_type - The type of join to use (passed to join_table). Defaults to
|
|
38
|
+
# :left_outer.
|
|
39
|
+
# * :select - An array of columns to select. When not used, selects
|
|
40
|
+
# all columns in the given dataset. When set to false, selects no
|
|
41
|
+
# columns and is like simply joining the tables, though graph keeps
|
|
42
|
+
# some metadata about join that makes it important to use graph instead
|
|
43
|
+
# of join.
|
|
44
|
+
# * :table_alias - The alias to use for the table. If not specified, doesn't
|
|
45
|
+
# alias the table. You will get an error if the the alias (or table) name is
|
|
46
|
+
# used more than once.
|
|
47
|
+
# * block - A block that is passed to join_table.
|
|
48
|
+
def graph(dataset, join_conditions = nil, options = {}, &block)
|
|
49
|
+
# Allow the use of a model, dataset, or symbol as the first argument
|
|
50
|
+
# Find the table name/dataset based on the argument
|
|
51
|
+
dataset = dataset.dataset if dataset.respond_to?(:dataset)
|
|
52
|
+
case dataset
|
|
53
|
+
when Symbol
|
|
54
|
+
table = dataset
|
|
55
|
+
dataset = @db[dataset]
|
|
56
|
+
when ::Sequel::Dataset
|
|
57
|
+
table = dataset.first_source
|
|
58
|
+
else
|
|
59
|
+
raise Error, "The dataset argument should be a symbol, dataset, or model"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Raise Sequel::Error with explanation that the table alias has been used
|
|
63
|
+
raise_alias_error = lambda do
|
|
64
|
+
raise(Error, "this #{options[:table_alias] ? 'alias' : 'table'} has already been been used, please specify " \
|
|
65
|
+
"#{options[:table_alias] ? 'a different alias' : 'an alias via the :table_alias option'}")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Only allow table aliases that haven't been used
|
|
69
|
+
table_alias = options[:table_alias] || table
|
|
70
|
+
raise_alias_error.call if @opts[:graph] && @opts[:graph][:table_aliases] && @opts[:graph][:table_aliases].include?(table_alias)
|
|
71
|
+
|
|
72
|
+
# Join the table early in order to avoid cloning the dataset twice
|
|
73
|
+
ds = join_table(options[:join_type] || :left_outer, table, join_conditions, :table_alias=>table_alias, :implicit_qualifier=>options[:implicit_qualifier], &block)
|
|
74
|
+
opts = ds.opts
|
|
75
|
+
|
|
76
|
+
# Whether to include the table in the result set
|
|
77
|
+
add_table = options[:select] == false ? false : true
|
|
78
|
+
# Whether to add the columns to the list of column aliases
|
|
79
|
+
add_columns = !ds.opts.include?(:graph_aliases)
|
|
80
|
+
|
|
81
|
+
# Setup the initial graph data structure if it doesn't exist
|
|
82
|
+
unless graph = opts[:graph]
|
|
83
|
+
master = ds.first_source
|
|
84
|
+
raise_alias_error.call if master == table_alias
|
|
85
|
+
# Master hash storing all .graph related information
|
|
86
|
+
graph = opts[:graph] = {}
|
|
87
|
+
# Associates column aliases back to tables and columns
|
|
88
|
+
column_aliases = graph[:column_aliases] = {}
|
|
89
|
+
# Associates table alias (the master is never aliased)
|
|
90
|
+
table_aliases = graph[:table_aliases] = {master=>self}
|
|
91
|
+
# Keep track of the alias numbers used
|
|
92
|
+
ca_num = graph[:column_alias_num] = Hash.new(0)
|
|
93
|
+
# All columns in the master table are never
|
|
94
|
+
# aliased, but are not included if set_graph_aliases
|
|
95
|
+
# has been used.
|
|
96
|
+
if add_columns
|
|
97
|
+
select = opts[:select] = []
|
|
98
|
+
columns.each do |column|
|
|
99
|
+
column_aliases[column] = [master, column]
|
|
100
|
+
select.push(column.qualify(master))
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Add the table alias to the list of aliases
|
|
106
|
+
# Even if it isn't been used in the result set,
|
|
107
|
+
# we add a key for it with a nil value so we can check if it
|
|
108
|
+
# is used more than once
|
|
109
|
+
table_aliases = graph[:table_aliases]
|
|
110
|
+
table_aliases[table_alias] = add_table ? dataset : nil
|
|
111
|
+
|
|
112
|
+
# Add the columns to the selection unless we are ignoring them
|
|
113
|
+
if add_table && add_columns
|
|
114
|
+
select = opts[:select]
|
|
115
|
+
column_aliases = graph[:column_aliases]
|
|
116
|
+
ca_num = graph[:column_alias_num]
|
|
117
|
+
# Which columns to add to the result set
|
|
118
|
+
cols = options[:select] || dataset.columns
|
|
119
|
+
# If the column hasn't been used yet, don't alias it.
|
|
120
|
+
# If it has been used, try table_column.
|
|
121
|
+
# If that has been used, try table_column_N
|
|
122
|
+
# using the next value of N that we know hasn't been
|
|
123
|
+
# used
|
|
124
|
+
cols.each do |column|
|
|
125
|
+
col_alias, identifier = if column_aliases[column]
|
|
126
|
+
column_alias = :"#{table_alias}_#{column}"
|
|
127
|
+
if column_aliases[column_alias]
|
|
128
|
+
column_alias_num = ca_num[column_alias]
|
|
129
|
+
column_alias = :"#{column_alias}_#{column_alias_num}"
|
|
130
|
+
ca_num[column_alias] += 1
|
|
131
|
+
end
|
|
132
|
+
[column_alias, column.qualify(table_alias).as(column_alias)]
|
|
133
|
+
else
|
|
134
|
+
[column, column.qualify(table_alias)]
|
|
135
|
+
end
|
|
136
|
+
column_aliases[col_alias] = [table_alias, column]
|
|
137
|
+
select.push(identifier)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
ds
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# This allows you to manually specify the graph aliases to use
|
|
144
|
+
# when using graph. You can use it to only select certain
|
|
145
|
+
# columns, and have those columns mapped to specific aliases
|
|
146
|
+
# in the result set. This is the equivalent of .select for a
|
|
147
|
+
# graphed dataset, and must be used instead of .select whenever
|
|
148
|
+
# graphing is used. Example:
|
|
149
|
+
#
|
|
150
|
+
# DB[:artists].graph(:albums, :artist_id=>:id).set_graph_aliases(:artist_name=>[:artists, :name], :album_name=>[:albums, :name]).first
|
|
151
|
+
# => {:artists=>{:name=>artists.name}, :albums=>{:name=>albums.name}}
|
|
152
|
+
#
|
|
153
|
+
# Arguments:
|
|
154
|
+
# * graph_aliases - Should be a hash with keys being symbols of
|
|
155
|
+
# column aliases, and values being arrays with two symbol elements.
|
|
156
|
+
# The first element of the array should be the table alias,
|
|
157
|
+
# and the second should be the actual column name.
|
|
158
|
+
def set_graph_aliases(graph_aliases)
|
|
159
|
+
ds = select(*graph_alias_columns(graph_aliases))
|
|
160
|
+
ds.opts[:graph_aliases] = graph_aliases
|
|
161
|
+
ds
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Adds the give graph aliases to the list of graph aliases to use,
|
|
165
|
+
# unlike #set_graph_aliases, which replaces the list. See
|
|
166
|
+
# #set_graph_aliases.
|
|
167
|
+
def add_graph_aliases(graph_aliases)
|
|
168
|
+
ds = select_more(*graph_alias_columns(graph_aliases))
|
|
169
|
+
ds.opts[:graph_aliases] = (ds.opts[:graph_aliases] || {}).merge(graph_aliases)
|
|
170
|
+
ds
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
private
|
|
174
|
+
|
|
175
|
+
# Transform the hash of graph aliases to an array of columns
|
|
176
|
+
def graph_alias_columns(graph_aliases)
|
|
177
|
+
graph_aliases.collect do |col_alias, tc|
|
|
178
|
+
identifier = tc[2] || tc[1].qualify(tc[0])
|
|
179
|
+
identifier = SQL::AliasedExpression.new(identifier, col_alias) if tc[2] or tc[1] != col_alias
|
|
180
|
+
identifier
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Fetch the rows, split them into component table parts,
|
|
185
|
+
# tranform and run the row_proc on each part (if applicable),
|
|
186
|
+
# and yield a hash of the parts.
|
|
187
|
+
def graph_each(opts=(defarg=true;nil), &block)
|
|
188
|
+
# Reject tables with nil datasets, as they are excluded from
|
|
189
|
+
# the result set
|
|
190
|
+
datasets = @opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?}
|
|
191
|
+
# Get just the list of table aliases into a local variable, for speed
|
|
192
|
+
table_aliases = datasets.collect{|ta,ds| ta}
|
|
193
|
+
# Get an array of arrays, one for each dataset, with
|
|
194
|
+
# the necessary information about each dataset, for speed
|
|
195
|
+
datasets = datasets.collect do |ta, ds|
|
|
196
|
+
[ta, ds, ds.instance_variable_get(:@transform), ds.row_proc]
|
|
197
|
+
end
|
|
198
|
+
# Use the manually set graph aliases, if any, otherwise
|
|
199
|
+
# use the ones automatically created by .graph
|
|
200
|
+
column_aliases = @opts[:graph_aliases] || @opts[:graph][:column_aliases]
|
|
201
|
+
fetch_rows(defarg ? select_sql : select_sql(opts)) do |r|
|
|
202
|
+
graph = {}
|
|
203
|
+
# Create the sub hashes, one per table
|
|
204
|
+
table_aliases.each{|ta| graph[ta]={}}
|
|
205
|
+
# Split the result set based on the column aliases
|
|
206
|
+
# If there are columns in the result set that are
|
|
207
|
+
# not in column_aliases, they are ignored
|
|
208
|
+
column_aliases.each do |col_alias, tc|
|
|
209
|
+
ta, column = tc
|
|
210
|
+
graph[ta][column] = r[col_alias]
|
|
211
|
+
end
|
|
212
|
+
# For each dataset, transform and run the row
|
|
213
|
+
# row_proc if applicable
|
|
214
|
+
datasets.each do |ta,ds,tr,rp|
|
|
215
|
+
g = graph[ta]
|
|
216
|
+
graph[ta] = if g.values.any?{|x| !x.nil?}
|
|
217
|
+
g = ds.transform_load(g) if tr
|
|
218
|
+
g = rp[g] if rp
|
|
219
|
+
g
|
|
220
|
+
else
|
|
221
|
+
nil
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
yield graph
|
|
226
|
+
end
|
|
227
|
+
self
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# Sequel's built-in caching plugin supports caching to any object that
|
|
4
|
+
# implements the Ruby-Memcache API. You can add caching for any model
|
|
5
|
+
# or for all models via:
|
|
6
|
+
#
|
|
7
|
+
# Model.plugin :caching, store # Cache all models
|
|
8
|
+
# MyModel.plugin :caching, store # Just cache MyModel
|
|
9
|
+
#
|
|
10
|
+
# The cache store should implement the Ruby-Memcache API:
|
|
11
|
+
#
|
|
12
|
+
# cache_store.set(key, obj, time) # Associate the obj with the given key
|
|
13
|
+
# # in the cache for the time (specified
|
|
14
|
+
# # in seconds)
|
|
15
|
+
# cache_store.get(key) => obj # Returns object set with same key
|
|
16
|
+
# cache_store.get(key2) => nil # nil returned if there isn't an object
|
|
17
|
+
# # currently in the cache with that key
|
|
18
|
+
module Caching
|
|
19
|
+
# Set the cache_store and cache_ttl attributes for the given model.
|
|
20
|
+
# If the :ttl option is not given, 3600 seconds is the default.
|
|
21
|
+
def self.apply(model, store, opts={})
|
|
22
|
+
model.instance_eval do
|
|
23
|
+
@cache_store = store
|
|
24
|
+
@cache_ttl = opts[:ttl] || 3600
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module ClassMethods
|
|
29
|
+
# The cache store object for the model, which should implement the
|
|
30
|
+
# Ruby-Memcache API
|
|
31
|
+
attr_reader :cache_store
|
|
32
|
+
|
|
33
|
+
# The time to live for the cache store, in seconds.
|
|
34
|
+
attr_reader :cache_ttl
|
|
35
|
+
|
|
36
|
+
# Check the cache before a database lookup unless a hash is supplied.
|
|
37
|
+
def [](*args)
|
|
38
|
+
args = args.first if (args.size == 1)
|
|
39
|
+
return super(args) if args.is_a?(Hash)
|
|
40
|
+
ck = cache_key(args)
|
|
41
|
+
if obj = @cache_store.get(ck)
|
|
42
|
+
return obj
|
|
43
|
+
end
|
|
44
|
+
if obj = super(args)
|
|
45
|
+
@cache_store.set(ck, obj, @cache_ttl)
|
|
46
|
+
end
|
|
47
|
+
obj
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Set the time to live for the cache store, in seconds (default is 3600, # so 1 hour).
|
|
51
|
+
def set_cache_ttl(ttl)
|
|
52
|
+
@cache_ttl = ttl
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Copy the cache_store and cache_ttl to the subclass.
|
|
56
|
+
def inherited(subclass)
|
|
57
|
+
super
|
|
58
|
+
store = @cache_store
|
|
59
|
+
ttl = @cache_ttl
|
|
60
|
+
subclass.instance_eval do
|
|
61
|
+
@cache_store = store
|
|
62
|
+
@cache_ttl = ttl
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
# Delete the entry with the matching key from the cache
|
|
69
|
+
def cache_delete(key)
|
|
70
|
+
@cache_store.delete(key)
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Return a key string for the pk
|
|
75
|
+
def cache_key(pk)
|
|
76
|
+
"#{self}:#{Array(pk).join(',')}"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
module InstanceMethods
|
|
81
|
+
# Remove the object from the cache when updating
|
|
82
|
+
def before_update
|
|
83
|
+
return false if super == false
|
|
84
|
+
cache_delete
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Return a key unique to the underlying record for caching, based on the
|
|
88
|
+
# primary key value(s) for the object. If the model does not have a primary
|
|
89
|
+
# key, raise an Error.
|
|
90
|
+
def cache_key
|
|
91
|
+
raise(Error, "No primary key is associated with this model") unless key = primary_key
|
|
92
|
+
pk = case key
|
|
93
|
+
when Array
|
|
94
|
+
key.collect{|k| @values[k]}
|
|
95
|
+
else
|
|
96
|
+
@values[key] || (raise Error, 'no primary key for this record')
|
|
97
|
+
end
|
|
98
|
+
model.send(:cache_key, pk)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Remove the object from the cache when deleting
|
|
102
|
+
def delete
|
|
103
|
+
cache_delete
|
|
104
|
+
super
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Remove the object from the cache when updating
|
|
108
|
+
def update_values(*args)
|
|
109
|
+
cache_delete
|
|
110
|
+
super
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
# Delete this object from the cache
|
|
116
|
+
def cache_delete
|
|
117
|
+
model.send(:cache_delete, cache_key)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# Sequel's built-in hook class methods plugin is designed for backwards
|
|
4
|
+
# compatibility. Its use is not encouraged, it is recommended to use
|
|
5
|
+
# instance methods and super instead of this plugin. What this plugin
|
|
6
|
+
# allows you to do is, for example:
|
|
7
|
+
#
|
|
8
|
+
# # Block only, can cause duplicate hooks if code is reloaded
|
|
9
|
+
# before_save{self.created_at = Time.now}
|
|
10
|
+
# # Block with tag, safe for reloading
|
|
11
|
+
# before_save(:set_created_at){self.created_at = Time.now}
|
|
12
|
+
# # Tag only, safe for reloading, calls instance method
|
|
13
|
+
# before_save(:set_created_at)
|
|
14
|
+
#
|
|
15
|
+
# Pretty much anything you can do with a hook class method, you can also
|
|
16
|
+
# do with an instance method instead:
|
|
17
|
+
#
|
|
18
|
+
# def before_save
|
|
19
|
+
# return false if super == false
|
|
20
|
+
# self.created_at = Time.now
|
|
21
|
+
# end
|
|
22
|
+
module HookClassMethods
|
|
23
|
+
# Set up the hooks instance variable in the model.
|
|
24
|
+
def self.apply(model)
|
|
25
|
+
hooks = model.instance_variable_set(:@hooks, {})
|
|
26
|
+
Model::HOOKS.each{|h| hooks[h] = []}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module ClassMethods
|
|
30
|
+
Model::HOOKS.each{|h| class_eval("def #{h}(method = nil, &block); add_hook(:#{h}, method, &block) end", __FILE__, __LINE__)}
|
|
31
|
+
|
|
32
|
+
# This adds a new hook type. It will define both a class
|
|
33
|
+
# method that you can use to add hooks, as well as an instance method
|
|
34
|
+
# that you can use to call all hooks of that type. The class method
|
|
35
|
+
# can be called with a symbol or a block or both. If a block is given and
|
|
36
|
+
# and symbol is not, it adds the hook block to the hook type. If a block
|
|
37
|
+
# and symbol are both given, it replaces the hook block associated with
|
|
38
|
+
# that symbol for a given hook type, or adds it if there is no hook block
|
|
39
|
+
# with that symbol for that hook type. If no block is given, it assumes
|
|
40
|
+
# the symbol specifies an instance method to call and adds it to the hook
|
|
41
|
+
# type.
|
|
42
|
+
#
|
|
43
|
+
# If any hook block returns false, the instance method will return false
|
|
44
|
+
# immediately without running the rest of the hooks of that type.
|
|
45
|
+
#
|
|
46
|
+
# It is recommended that you always provide a symbol to this method,
|
|
47
|
+
# for descriptive purposes. It's only necessary to do so when you
|
|
48
|
+
# are using a system that reloads code.
|
|
49
|
+
#
|
|
50
|
+
# Example of usage:
|
|
51
|
+
#
|
|
52
|
+
# class MyModel
|
|
53
|
+
# define_hook :before_move_to
|
|
54
|
+
# before_move_to(:check_move_allowed){|o| o.allow_move?}
|
|
55
|
+
# def move_to(there)
|
|
56
|
+
# return if before_move_to == false
|
|
57
|
+
# # move MyModel object to there
|
|
58
|
+
# end
|
|
59
|
+
# end
|
|
60
|
+
def add_hook_type(*hooks)
|
|
61
|
+
Model::HOOKS.concat(hooks)
|
|
62
|
+
hooks.each do |hook|
|
|
63
|
+
@hooks[hook] = []
|
|
64
|
+
instance_eval("def #{hook}(method = nil, &block); add_hook(:#{hook}, method, &block) end", __FILE__, __LINE__)
|
|
65
|
+
class_eval("def #{hook}; run_hooks(:#{hook}); end", __FILE__, __LINE__)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Returns true if there are any hook blocks for the given hook.
|
|
70
|
+
def has_hooks?(hook)
|
|
71
|
+
!@hooks[hook].empty?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Yield every block related to the given hook.
|
|
75
|
+
def hook_blocks(hook)
|
|
76
|
+
@hooks[hook].each{|k,v| yield v}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Make a copy of the current class's hooks for the subclass.
|
|
80
|
+
def inherited(subclass)
|
|
81
|
+
super
|
|
82
|
+
hooks = subclass.instance_variable_set(:@hooks, {})
|
|
83
|
+
instance_variable_get(:@hooks).each{|k,v| hooks[k] = v.dup}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
# Add a hook block to the list of hook methods.
|
|
89
|
+
# If a non-nil tag is given and it already is in the list of hooks,
|
|
90
|
+
# replace it with the new block.
|
|
91
|
+
def add_hook(hook, tag, &block)
|
|
92
|
+
unless block
|
|
93
|
+
(raise Error, 'No hook method specified') unless tag
|
|
94
|
+
block = proc {send tag}
|
|
95
|
+
end
|
|
96
|
+
h = @hooks[hook]
|
|
97
|
+
if tag && (old = h.find{|x| x[0] == tag})
|
|
98
|
+
old[1] = block
|
|
99
|
+
else
|
|
100
|
+
if hook.to_s =~ /^before/
|
|
101
|
+
h.unshift([tag,block])
|
|
102
|
+
else
|
|
103
|
+
h << [tag, block]
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
module InstanceMethods
|
|
110
|
+
Model::HOOKS.each{|h| class_eval("def #{h}; run_hooks(:#{h}); end", __FILE__, __LINE__)}
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
# Runs all hook blocks of given hook type on this object.
|
|
115
|
+
# Stops running hook blocks and returns false if any hook block returns false.
|
|
116
|
+
def run_hooks(hook)
|
|
117
|
+
model.hook_blocks(hook){|block| return false if instance_eval(&block) == false}
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|