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.
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