sequel 2.11.0 → 2.12.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 +168 -0
- data/README.rdoc +77 -95
- data/Rakefile +100 -80
- data/bin/sequel +2 -1
- data/doc/advanced_associations.rdoc +23 -32
- data/doc/cheat_sheet.rdoc +23 -40
- data/doc/dataset_filtering.rdoc +6 -6
- data/doc/prepared_statements.rdoc +22 -22
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/schema.rdoc +3 -1
- data/doc/sharding.rdoc +8 -8
- data/doc/virtual_rows.rdoc +65 -0
- data/lib/sequel.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/ado.rb +3 -3
- data/lib/{sequel_core → sequel}/adapters/db2.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/dbi.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/do.rb +9 -5
- data/lib/{sequel_core → sequel}/adapters/do/mysql.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/do/postgres.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/do/sqlite.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/firebird.rb +84 -80
- data/lib/{sequel_core → sequel}/adapters/informix.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc.rb +21 -14
- data/lib/{sequel_core → sequel}/adapters/jdbc/h2.rb +14 -13
- data/lib/{sequel_core → sequel}/adapters/jdbc/mysql.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc/oracle.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc/postgresql.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc/sqlite.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/mysql.rb +60 -39
- data/lib/{sequel_core → sequel}/adapters/odbc.rb +8 -4
- data/lib/{sequel_core → sequel}/adapters/openbase.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/oracle.rb +38 -7
- data/lib/{sequel_core → sequel}/adapters/postgres.rb +24 -24
- data/lib/{sequel_core → sequel}/adapters/shared/mssql.rb +5 -5
- data/lib/{sequel_core → sequel}/adapters/shared/mysql.rb +126 -71
- data/lib/{sequel_core → sequel}/adapters/shared/oracle.rb +7 -10
- data/lib/{sequel_core → sequel}/adapters/shared/postgres.rb +159 -125
- data/lib/{sequel_core → sequel}/adapters/shared/progress.rb +1 -2
- data/lib/{sequel_core → sequel}/adapters/shared/sqlite.rb +72 -67
- data/lib/{sequel_core → sequel}/adapters/sqlite.rb +11 -7
- data/lib/{sequel_core → sequel}/adapters/utils/date_format.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/utils/stored_procedures.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/utils/unsupported.rb +19 -0
- data/lib/{sequel_core → sequel}/connection_pool.rb +7 -5
- data/lib/sequel/core.rb +221 -0
- data/lib/{sequel_core → sequel}/core_sql.rb +91 -49
- data/lib/{sequel_core → sequel}/database.rb +264 -149
- data/lib/{sequel_core/schema/generator.rb → sequel/database/schema_generator.rb} +6 -2
- data/lib/{sequel_core/database/schema.rb → sequel/database/schema_methods.rb} +12 -12
- data/lib/sequel/database/schema_sql.rb +224 -0
- data/lib/{sequel_core → sequel}/dataset.rb +78 -236
- data/lib/{sequel_core → sequel}/dataset/convenience.rb +99 -61
- data/lib/{sequel_core/object_graph.rb → sequel/dataset/graph.rb} +16 -14
- data/lib/{sequel_core → sequel}/dataset/prepared_statements.rb +1 -1
- data/lib/{sequel_core → sequel}/dataset/sql.rb +150 -99
- data/lib/sequel/deprecated.rb +593 -0
- data/lib/sequel/deprecated_migration.rb +91 -0
- data/lib/sequel/exceptions.rb +48 -0
- data/lib/sequel/extensions/blank.rb +42 -0
- data/lib/{sequel_model → sequel/extensions}/inflector.rb +8 -1
- data/lib/{sequel_core → sequel/extensions}/migration.rb +1 -1
- data/lib/{sequel_core/dataset → sequel/extensions}/pagination.rb +0 -0
- data/lib/{sequel_core → sequel/extensions}/pretty_table.rb +7 -0
- data/lib/{sequel_core/dataset → sequel/extensions}/query.rb +7 -0
- data/lib/sequel/extensions/string_date_time.rb +47 -0
- data/lib/sequel/metaprogramming.rb +43 -0
- data/lib/sequel/model.rb +110 -0
- data/lib/sequel/model/associations.rb +1300 -0
- data/lib/sequel/model/base.rb +937 -0
- data/lib/sequel/model/deprecated.rb +204 -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 +388 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/{sequel_model → sequel/model}/exceptions.rb +4 -4
- data/lib/sequel/model/inflections.rb +208 -0
- data/lib/sequel/model/plugins.rb +76 -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/serialization.rb +117 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
- data/lib/sequel/plugins/validation_class_methods.rb +384 -0
- data/lib/sequel/plugins/validation_helpers.rb +150 -0
- data/lib/{sequel_core → sequel}/sql.rb +125 -190
- data/lib/{sequel_core → sequel}/version.rb +2 -1
- data/lib/sequel_core.rb +1 -172
- data/lib/sequel_model.rb +1 -91
- data/spec/adapters/firebird_spec.rb +5 -5
- data/spec/adapters/informix_spec.rb +1 -1
- data/spec/adapters/mysql_spec.rb +128 -42
- data/spec/adapters/oracle_spec.rb +47 -19
- data/spec/adapters/postgres_spec.rb +64 -52
- data/spec/adapters/spec_helper.rb +1 -1
- data/spec/adapters/sqlite_spec.rb +12 -17
- data/spec/{sequel_core → core}/connection_pool_spec.rb +10 -10
- data/spec/{sequel_core → core}/core_ext_spec.rb +19 -19
- data/spec/{sequel_core → core}/core_sql_spec.rb +68 -71
- data/spec/{sequel_core → core}/database_spec.rb +135 -99
- data/spec/{sequel_core → core}/dataset_spec.rb +398 -242
- data/spec/{sequel_core → core}/expression_filters_spec.rb +13 -13
- data/spec/core/migration_spec.rb +263 -0
- data/spec/{sequel_core → core}/object_graph_spec.rb +10 -10
- data/spec/{sequel_core → core}/pretty_table_spec.rb +2 -2
- data/spec/{sequel_core → core}/schema_generator_spec.rb +0 -0
- data/spec/{sequel_core → core}/schema_spec.rb +8 -10
- data/spec/{sequel_core → core}/spec_helper.rb +29 -2
- data/spec/{sequel_core → core}/version_spec.rb +0 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/caching_spec.rb +201 -0
- data/spec/{sequel_model/hooks_spec.rb → extensions/hook_class_methods_spec.rb} +8 -23
- data/spec/{sequel_model → extensions}/inflector_spec.rb +3 -0
- data/spec/{sequel_core → extensions}/migration_spec.rb +4 -4
- 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/{sequel_model → extensions}/schema_spec.rb +22 -1
- data/spec/extensions/serialization_spec.rb +109 -0
- data/spec/extensions/single_table_inheritance_spec.rb +53 -0
- data/spec/{sequel_model → extensions}/spec_helper.rb +13 -4
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/{sequel_model/validations_spec.rb → extensions/validation_class_methods_spec.rb} +15 -103
- data/spec/extensions/validation_helpers_spec.rb +291 -0
- data/spec/integration/dataset_test.rb +31 -0
- data/spec/integration/eager_loader_test.rb +17 -30
- data/spec/integration/schema_test.rb +8 -5
- data/spec/integration/spec_helper.rb +17 -0
- data/spec/integration/transaction_test.rb +68 -0
- data/spec/{sequel_model → model}/association_reflection_spec.rb +0 -0
- data/spec/{sequel_model → model}/associations_spec.rb +23 -10
- data/spec/{sequel_model → model}/base_spec.rb +29 -20
- data/spec/{sequel_model → model}/caching_spec.rb +16 -14
- data/spec/{sequel_model → model}/dataset_methods_spec.rb +0 -0
- data/spec/{sequel_model → model}/eager_loading_spec.rb +8 -8
- data/spec/model/hooks_spec.rb +472 -0
- data/spec/model/inflector_spec.rb +126 -0
- data/spec/{sequel_model → model}/model_spec.rb +25 -20
- data/spec/model/plugins_spec.rb +142 -0
- data/spec/{sequel_model → model}/record_spec.rb +121 -62
- data/spec/model/schema_spec.rb +92 -0
- data/spec/model/spec_helper.rb +124 -0
- data/spec/model/validations_spec.rb +1080 -0
- metadata +136 -107
- data/lib/sequel_core/core_ext.rb +0 -217
- data/lib/sequel_core/dataset/callback.rb +0 -13
- data/lib/sequel_core/dataset/schema.rb +0 -15
- data/lib/sequel_core/deprecated.rb +0 -26
- data/lib/sequel_core/exceptions.rb +0 -44
- data/lib/sequel_core/schema.rb +0 -2
- data/lib/sequel_core/schema/sql.rb +0 -325
- data/lib/sequel_model/association_reflection.rb +0 -267
- data/lib/sequel_model/associations.rb +0 -499
- data/lib/sequel_model/base.rb +0 -539
- data/lib/sequel_model/caching.rb +0 -82
- data/lib/sequel_model/dataset_methods.rb +0 -26
- data/lib/sequel_model/eager_loading.rb +0 -370
- data/lib/sequel_model/hooks.rb +0 -101
- data/lib/sequel_model/plugins.rb +0 -62
- data/lib/sequel_model/record.rb +0 -568
- data/lib/sequel_model/schema.rb +0 -49
- data/lib/sequel_model/validations.rb +0 -429
- data/spec/sequel_model/plugins_spec.rb +0 -80
|
@@ -0,0 +1,76 @@
|
|
|
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 is a module, load it directly. Otherwise,
|
|
20
|
+
# require the plugin from either sequel/plugins/#{plugin} or
|
|
21
|
+
# sequel_#{plugin}, and then attempt to load the module using a
|
|
22
|
+
# the camelized plugin name under Sequel::Plugins.
|
|
23
|
+
def self.plugin(plugin, *args)
|
|
24
|
+
arg = args.first
|
|
25
|
+
block = lambda{arg}
|
|
26
|
+
m = plugin.is_a?(Module) ? plugin : plugin_module(plugin)
|
|
27
|
+
if m.respond_to?(:apply)
|
|
28
|
+
m.apply(self, *args)
|
|
29
|
+
end
|
|
30
|
+
if m.const_defined?("InstanceMethods")
|
|
31
|
+
define_method(:"#{plugin}_opts", &block)
|
|
32
|
+
include(m::InstanceMethods)
|
|
33
|
+
end
|
|
34
|
+
if m.const_defined?("ClassMethods")
|
|
35
|
+
meta_def(:"#{plugin}_opts", &block)
|
|
36
|
+
extend(m::ClassMethods)
|
|
37
|
+
end
|
|
38
|
+
if m.const_defined?("DatasetMethods")
|
|
39
|
+
if @dataset
|
|
40
|
+
dataset.meta_def(:"#{plugin}_opts", &block)
|
|
41
|
+
dataset.extend(m::DatasetMethods)
|
|
42
|
+
end
|
|
43
|
+
dataset_method_modules << m::DatasetMethods
|
|
44
|
+
def_dataset_method(*m::DatasetMethods.public_instance_methods.reject{|x| NORMAL_METHOD_NAME_REGEXP !~ x.to_s})
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
module ClassMethods
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# Returns the new style location for the plugin name.
|
|
52
|
+
def plugin_gem_location(plugin)
|
|
53
|
+
"sequel/plugins/#{plugin}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Returns the old style location for the plugin name.
|
|
57
|
+
def plugin_gem_location_old(plugin)
|
|
58
|
+
"sequel_#{plugin}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Returns the module for the specified plugin. If the module is not
|
|
62
|
+
# defined, the corresponding plugin gem is automatically loaded.
|
|
63
|
+
def plugin_module(plugin)
|
|
64
|
+
module_name = plugin.to_s.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
|
|
65
|
+
if not Sequel::Plugins.const_defined?(module_name)
|
|
66
|
+
begin
|
|
67
|
+
require plugin_gem_location(plugin)
|
|
68
|
+
rescue LoadError
|
|
69
|
+
require plugin_gem_location_old(plugin)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
Sequel::Plugins.const_get(module_name)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
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
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
module Schema
|
|
4
|
+
module ClassMethods
|
|
5
|
+
# Creates table, using the column information from set_schema.
|
|
6
|
+
def create_table
|
|
7
|
+
db.create_table(table_name, :generator=>@schema)
|
|
8
|
+
@db_schema = get_db_schema(true)
|
|
9
|
+
columns
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Drops the table if it exists and then runs create_table. Should probably
|
|
13
|
+
# not be used except in testing.
|
|
14
|
+
def create_table!
|
|
15
|
+
drop_table rescue nil
|
|
16
|
+
create_table
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Drops table.
|
|
20
|
+
def drop_table
|
|
21
|
+
db.drop_table(table_name)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Returns table schema created with set_schema for direct descendant of Model.
|
|
25
|
+
# Does not retreive schema information from the database, see db_schema if you
|
|
26
|
+
# want that.
|
|
27
|
+
def schema
|
|
28
|
+
@schema || (superclass.schema unless superclass == Model)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Defines a table schema (see Schema::Generator for more information).
|
|
32
|
+
#
|
|
33
|
+
# This is only needed if you want to use the create_table/create_table! methods.
|
|
34
|
+
# Will also set the dataset if you provide a name, as well as setting
|
|
35
|
+
# the primary key if you defined one in the passed block.
|
|
36
|
+
#
|
|
37
|
+
# In general, it is a better idea to use migrations for production code, as
|
|
38
|
+
# migrations allow changes to existing schema. set_schema is mostly useful for
|
|
39
|
+
# test code or simple examples.
|
|
40
|
+
def set_schema(name = nil, &block)
|
|
41
|
+
set_dataset(db[name]) if name
|
|
42
|
+
@schema = Sequel::Schema::Generator.new(db, &block)
|
|
43
|
+
set_primary_key(@schema.primary_key_name) if @schema.primary_key_name
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Returns true if table exists, false otherwise.
|
|
47
|
+
def table_exists?
|
|
48
|
+
db.table_exists?(table_name)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# Sequel's built in Serialization plugin allows you to keep serialized
|
|
4
|
+
# ruby objects in the database, while giving you deserialized objects
|
|
5
|
+
# when you call an accessor.
|
|
6
|
+
#
|
|
7
|
+
# This plugin works by keeping the serialized value in the values, and
|
|
8
|
+
# adding a @deserialized_values hash. The reader method for serialized columns
|
|
9
|
+
# will check the @deserialized_values for the value, return it if present,
|
|
10
|
+
# or deserialized the entry in @values and return it. The writer method will
|
|
11
|
+
# set the @deserialized_values entry. This plugin adds a before_save hook
|
|
12
|
+
# that serializes all @deserialized_values to @values.
|
|
13
|
+
#
|
|
14
|
+
# You can use either marshal or yaml as the serialization format.
|
|
15
|
+
# If you use yaml, you should require yaml yourself.
|
|
16
|
+
#
|
|
17
|
+
# Because of how this plugin works, it must be used inside each model class
|
|
18
|
+
# that needs serialization, after any set_dataset method calls in that class.
|
|
19
|
+
# Otherwise, it is possible that the default column accessors will take
|
|
20
|
+
# precedence.
|
|
21
|
+
module Serialization
|
|
22
|
+
# Set up the column readers to do deserialization and the column writers
|
|
23
|
+
# to save the value in deserialized_values.
|
|
24
|
+
def self.apply(model, format, *columns)
|
|
25
|
+
raise(Error, "Unsupported serialization format (#{format}), should be :marshal or :yaml") unless [:marshal, :yaml].include?(format)
|
|
26
|
+
raise(Error, "No columns given. The serialization plugin requires you specify which columns to serialize") if columns.empty?
|
|
27
|
+
model.instance_eval do
|
|
28
|
+
@serialization_format = format
|
|
29
|
+
@serialized_columns = columns
|
|
30
|
+
InstanceMethods.module_eval do
|
|
31
|
+
columns.each do |column|
|
|
32
|
+
define_method(column) do
|
|
33
|
+
if deserialized_values.has_key?(column)
|
|
34
|
+
deserialized_values[column]
|
|
35
|
+
else
|
|
36
|
+
deserialized_values[column] = deserialize_value(@values[column])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
define_method("#{column}=") do |v|
|
|
40
|
+
changed_columns << column unless changed_columns.include?(column)
|
|
41
|
+
deserialized_values[column] = v
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
module ClassMethods
|
|
49
|
+
# The serialization format to use, should be :marshal or :yaml
|
|
50
|
+
attr_reader :serialization_format
|
|
51
|
+
|
|
52
|
+
# The columns to serialize
|
|
53
|
+
attr_reader :serialized_columns
|
|
54
|
+
|
|
55
|
+
# Copy the serialization format and columns to serialize into the subclass.
|
|
56
|
+
def inherited(subclass)
|
|
57
|
+
super
|
|
58
|
+
sf = serialization_format
|
|
59
|
+
sc = serialized_columns
|
|
60
|
+
subclass.instance_eval do
|
|
61
|
+
@serialization_format = sf
|
|
62
|
+
@serialized_columns = sc
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
module InstanceMethods
|
|
68
|
+
# Hash of deserialized values, used as a cache.
|
|
69
|
+
attr_reader :deserialized_values
|
|
70
|
+
|
|
71
|
+
# Set @deserialized_values to the empty hash
|
|
72
|
+
def initialize(*args, &block)
|
|
73
|
+
@deserialized_values = {}
|
|
74
|
+
super
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Serialize all deserialized values
|
|
78
|
+
def before_save
|
|
79
|
+
super
|
|
80
|
+
deserialized_values.each do |k,v|
|
|
81
|
+
@values[k] = serialize_value(v)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Empty the deserialized values when refreshing.
|
|
86
|
+
def refresh
|
|
87
|
+
@deserialized_values = {}
|
|
88
|
+
super
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
# Deserialize the column from either marshal or yaml format
|
|
94
|
+
def deserialize_value(v)
|
|
95
|
+
return v if v.nil?
|
|
96
|
+
case model.serialization_format
|
|
97
|
+
when :marshal
|
|
98
|
+
Marshal.load(v.unpack('m')[0]) rescue Marshal.load(v)
|
|
99
|
+
when :yaml
|
|
100
|
+
YAML.load v if v
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Serialize the column to either marshal or yaml format
|
|
105
|
+
def serialize_value(v)
|
|
106
|
+
return v if v.nil?
|
|
107
|
+
case model.serialization_format
|
|
108
|
+
when :marshal
|
|
109
|
+
[Marshal.dump(v)].pack('m')
|
|
110
|
+
when :yaml
|
|
111
|
+
v.to_yaml
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|