sequel 2.11.0 → 2.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. data/CHANGELOG +168 -0
  2. data/README.rdoc +77 -95
  3. data/Rakefile +100 -80
  4. data/bin/sequel +2 -1
  5. data/doc/advanced_associations.rdoc +23 -32
  6. data/doc/cheat_sheet.rdoc +23 -40
  7. data/doc/dataset_filtering.rdoc +6 -6
  8. data/doc/prepared_statements.rdoc +22 -22
  9. data/doc/release_notes/2.12.0.txt +534 -0
  10. data/doc/schema.rdoc +3 -1
  11. data/doc/sharding.rdoc +8 -8
  12. data/doc/virtual_rows.rdoc +65 -0
  13. data/lib/sequel.rb +1 -1
  14. data/lib/{sequel_core → sequel}/adapters/ado.rb +3 -3
  15. data/lib/{sequel_core → sequel}/adapters/db2.rb +0 -0
  16. data/lib/{sequel_core → sequel}/adapters/dbi.rb +1 -1
  17. data/lib/{sequel_core → sequel}/adapters/do.rb +9 -5
  18. data/lib/{sequel_core → sequel}/adapters/do/mysql.rb +1 -1
  19. data/lib/{sequel_core → sequel}/adapters/do/postgres.rb +1 -1
  20. data/lib/{sequel_core → sequel}/adapters/do/sqlite.rb +1 -1
  21. data/lib/{sequel_core → sequel}/adapters/firebird.rb +84 -80
  22. data/lib/{sequel_core → sequel}/adapters/informix.rb +1 -1
  23. data/lib/{sequel_core → sequel}/adapters/jdbc.rb +21 -14
  24. data/lib/{sequel_core → sequel}/adapters/jdbc/h2.rb +14 -13
  25. data/lib/{sequel_core → sequel}/adapters/jdbc/mysql.rb +1 -1
  26. data/lib/{sequel_core → sequel}/adapters/jdbc/oracle.rb +1 -1
  27. data/lib/{sequel_core → sequel}/adapters/jdbc/postgresql.rb +1 -1
  28. data/lib/{sequel_core → sequel}/adapters/jdbc/sqlite.rb +1 -1
  29. data/lib/{sequel_core → sequel}/adapters/mysql.rb +60 -39
  30. data/lib/{sequel_core → sequel}/adapters/odbc.rb +8 -4
  31. data/lib/{sequel_core → sequel}/adapters/openbase.rb +0 -0
  32. data/lib/{sequel_core → sequel}/adapters/oracle.rb +38 -7
  33. data/lib/{sequel_core → sequel}/adapters/postgres.rb +24 -24
  34. data/lib/{sequel_core → sequel}/adapters/shared/mssql.rb +5 -5
  35. data/lib/{sequel_core → sequel}/adapters/shared/mysql.rb +126 -71
  36. data/lib/{sequel_core → sequel}/adapters/shared/oracle.rb +7 -10
  37. data/lib/{sequel_core → sequel}/adapters/shared/postgres.rb +159 -125
  38. data/lib/{sequel_core → sequel}/adapters/shared/progress.rb +1 -2
  39. data/lib/{sequel_core → sequel}/adapters/shared/sqlite.rb +72 -67
  40. data/lib/{sequel_core → sequel}/adapters/sqlite.rb +11 -7
  41. data/lib/{sequel_core → sequel}/adapters/utils/date_format.rb +0 -0
  42. data/lib/{sequel_core → sequel}/adapters/utils/stored_procedures.rb +0 -0
  43. data/lib/{sequel_core → sequel}/adapters/utils/unsupported.rb +19 -0
  44. data/lib/{sequel_core → sequel}/connection_pool.rb +7 -5
  45. data/lib/sequel/core.rb +221 -0
  46. data/lib/{sequel_core → sequel}/core_sql.rb +91 -49
  47. data/lib/{sequel_core → sequel}/database.rb +264 -149
  48. data/lib/{sequel_core/schema/generator.rb → sequel/database/schema_generator.rb} +6 -2
  49. data/lib/{sequel_core/database/schema.rb → sequel/database/schema_methods.rb} +12 -12
  50. data/lib/sequel/database/schema_sql.rb +224 -0
  51. data/lib/{sequel_core → sequel}/dataset.rb +78 -236
  52. data/lib/{sequel_core → sequel}/dataset/convenience.rb +99 -61
  53. data/lib/{sequel_core/object_graph.rb → sequel/dataset/graph.rb} +16 -14
  54. data/lib/{sequel_core → sequel}/dataset/prepared_statements.rb +1 -1
  55. data/lib/{sequel_core → sequel}/dataset/sql.rb +150 -99
  56. data/lib/sequel/deprecated.rb +593 -0
  57. data/lib/sequel/deprecated_migration.rb +91 -0
  58. data/lib/sequel/exceptions.rb +48 -0
  59. data/lib/sequel/extensions/blank.rb +42 -0
  60. data/lib/{sequel_model → sequel/extensions}/inflector.rb +8 -1
  61. data/lib/{sequel_core → sequel/extensions}/migration.rb +1 -1
  62. data/lib/{sequel_core/dataset → sequel/extensions}/pagination.rb +0 -0
  63. data/lib/{sequel_core → sequel/extensions}/pretty_table.rb +7 -0
  64. data/lib/{sequel_core/dataset → sequel/extensions}/query.rb +7 -0
  65. data/lib/sequel/extensions/string_date_time.rb +47 -0
  66. data/lib/sequel/metaprogramming.rb +43 -0
  67. data/lib/sequel/model.rb +110 -0
  68. data/lib/sequel/model/associations.rb +1300 -0
  69. data/lib/sequel/model/base.rb +937 -0
  70. data/lib/sequel/model/deprecated.rb +204 -0
  71. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  72. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  73. data/lib/sequel/model/deprecated_validations.rb +388 -0
  74. data/lib/sequel/model/errors.rb +39 -0
  75. data/lib/{sequel_model → sequel/model}/exceptions.rb +4 -4
  76. data/lib/sequel/model/inflections.rb +208 -0
  77. data/lib/sequel/model/plugins.rb +76 -0
  78. data/lib/sequel/plugins/caching.rb +122 -0
  79. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  80. data/lib/sequel/plugins/schema.rb +53 -0
  81. data/lib/sequel/plugins/serialization.rb +117 -0
  82. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  83. data/lib/sequel/plugins/validation_class_methods.rb +384 -0
  84. data/lib/sequel/plugins/validation_helpers.rb +150 -0
  85. data/lib/{sequel_core → sequel}/sql.rb +125 -190
  86. data/lib/{sequel_core → sequel}/version.rb +2 -1
  87. data/lib/sequel_core.rb +1 -172
  88. data/lib/sequel_model.rb +1 -91
  89. data/spec/adapters/firebird_spec.rb +5 -5
  90. data/spec/adapters/informix_spec.rb +1 -1
  91. data/spec/adapters/mysql_spec.rb +128 -42
  92. data/spec/adapters/oracle_spec.rb +47 -19
  93. data/spec/adapters/postgres_spec.rb +64 -52
  94. data/spec/adapters/spec_helper.rb +1 -1
  95. data/spec/adapters/sqlite_spec.rb +12 -17
  96. data/spec/{sequel_core → core}/connection_pool_spec.rb +10 -10
  97. data/spec/{sequel_core → core}/core_ext_spec.rb +19 -19
  98. data/spec/{sequel_core → core}/core_sql_spec.rb +68 -71
  99. data/spec/{sequel_core → core}/database_spec.rb +135 -99
  100. data/spec/{sequel_core → core}/dataset_spec.rb +398 -242
  101. data/spec/{sequel_core → core}/expression_filters_spec.rb +13 -13
  102. data/spec/core/migration_spec.rb +263 -0
  103. data/spec/{sequel_core → core}/object_graph_spec.rb +10 -10
  104. data/spec/{sequel_core → core}/pretty_table_spec.rb +2 -2
  105. data/spec/{sequel_core → core}/schema_generator_spec.rb +0 -0
  106. data/spec/{sequel_core → core}/schema_spec.rb +8 -10
  107. data/spec/{sequel_core → core}/spec_helper.rb +29 -2
  108. data/spec/{sequel_core → core}/version_spec.rb +0 -0
  109. data/spec/extensions/blank_spec.rb +67 -0
  110. data/spec/extensions/caching_spec.rb +201 -0
  111. data/spec/{sequel_model/hooks_spec.rb → extensions/hook_class_methods_spec.rb} +8 -23
  112. data/spec/{sequel_model → extensions}/inflector_spec.rb +3 -0
  113. data/spec/{sequel_core → extensions}/migration_spec.rb +4 -4
  114. data/spec/extensions/pagination_spec.rb +99 -0
  115. data/spec/extensions/pretty_table_spec.rb +91 -0
  116. data/spec/extensions/query_spec.rb +85 -0
  117. data/spec/{sequel_model → extensions}/schema_spec.rb +22 -1
  118. data/spec/extensions/serialization_spec.rb +109 -0
  119. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  120. data/spec/{sequel_model → extensions}/spec_helper.rb +13 -4
  121. data/spec/extensions/string_date_time_spec.rb +93 -0
  122. data/spec/{sequel_model/validations_spec.rb → extensions/validation_class_methods_spec.rb} +15 -103
  123. data/spec/extensions/validation_helpers_spec.rb +291 -0
  124. data/spec/integration/dataset_test.rb +31 -0
  125. data/spec/integration/eager_loader_test.rb +17 -30
  126. data/spec/integration/schema_test.rb +8 -5
  127. data/spec/integration/spec_helper.rb +17 -0
  128. data/spec/integration/transaction_test.rb +68 -0
  129. data/spec/{sequel_model → model}/association_reflection_spec.rb +0 -0
  130. data/spec/{sequel_model → model}/associations_spec.rb +23 -10
  131. data/spec/{sequel_model → model}/base_spec.rb +29 -20
  132. data/spec/{sequel_model → model}/caching_spec.rb +16 -14
  133. data/spec/{sequel_model → model}/dataset_methods_spec.rb +0 -0
  134. data/spec/{sequel_model → model}/eager_loading_spec.rb +8 -8
  135. data/spec/model/hooks_spec.rb +472 -0
  136. data/spec/model/inflector_spec.rb +126 -0
  137. data/spec/{sequel_model → model}/model_spec.rb +25 -20
  138. data/spec/model/plugins_spec.rb +142 -0
  139. data/spec/{sequel_model → model}/record_spec.rb +121 -62
  140. data/spec/model/schema_spec.rb +92 -0
  141. data/spec/model/spec_helper.rb +124 -0
  142. data/spec/model/validations_spec.rb +1080 -0
  143. metadata +136 -107
  144. data/lib/sequel_core/core_ext.rb +0 -217
  145. data/lib/sequel_core/dataset/callback.rb +0 -13
  146. data/lib/sequel_core/dataset/schema.rb +0 -15
  147. data/lib/sequel_core/deprecated.rb +0 -26
  148. data/lib/sequel_core/exceptions.rb +0 -44
  149. data/lib/sequel_core/schema.rb +0 -2
  150. data/lib/sequel_core/schema/sql.rb +0 -325
  151. data/lib/sequel_model/association_reflection.rb +0 -267
  152. data/lib/sequel_model/associations.rb +0 -499
  153. data/lib/sequel_model/base.rb +0 -539
  154. data/lib/sequel_model/caching.rb +0 -82
  155. data/lib/sequel_model/dataset_methods.rb +0 -26
  156. data/lib/sequel_model/eager_loading.rb +0 -370
  157. data/lib/sequel_model/hooks.rb +0 -101
  158. data/lib/sequel_model/plugins.rb +0 -62
  159. data/lib/sequel_model/record.rb +0 -568
  160. data/lib/sequel_model/schema.rb +0 -49
  161. data/lib/sequel_model/validations.rb +0 -429
  162. 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