sequel 2.2.0 → 2.3.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 (98) hide show
  1. data/CHANGELOG +1551 -4
  2. data/README +306 -19
  3. data/Rakefile +84 -56
  4. data/bin/sequel +106 -0
  5. data/doc/cheat_sheet.rdoc +225 -0
  6. data/doc/dataset_filtering.rdoc +182 -0
  7. data/lib/sequel_core.rb +136 -0
  8. data/lib/sequel_core/adapters/adapter_skeleton.rb +54 -0
  9. data/lib/sequel_core/adapters/ado.rb +80 -0
  10. data/lib/sequel_core/adapters/db2.rb +148 -0
  11. data/lib/sequel_core/adapters/dbi.rb +117 -0
  12. data/lib/sequel_core/adapters/informix.rb +78 -0
  13. data/lib/sequel_core/adapters/jdbc.rb +186 -0
  14. data/lib/sequel_core/adapters/jdbc/mysql.rb +55 -0
  15. data/lib/sequel_core/adapters/jdbc/postgresql.rb +66 -0
  16. data/lib/sequel_core/adapters/jdbc/sqlite.rb +47 -0
  17. data/lib/sequel_core/adapters/mysql.rb +231 -0
  18. data/lib/sequel_core/adapters/odbc.rb +155 -0
  19. data/lib/sequel_core/adapters/odbc_mssql.rb +106 -0
  20. data/lib/sequel_core/adapters/openbase.rb +64 -0
  21. data/lib/sequel_core/adapters/oracle.rb +170 -0
  22. data/lib/sequel_core/adapters/postgres.rb +199 -0
  23. data/lib/sequel_core/adapters/shared/mysql.rb +275 -0
  24. data/lib/sequel_core/adapters/shared/postgres.rb +351 -0
  25. data/lib/sequel_core/adapters/shared/sqlite.rb +146 -0
  26. data/lib/sequel_core/adapters/sqlite.rb +138 -0
  27. data/lib/sequel_core/connection_pool.rb +194 -0
  28. data/lib/sequel_core/core_ext.rb +203 -0
  29. data/lib/sequel_core/core_sql.rb +184 -0
  30. data/lib/sequel_core/database.rb +471 -0
  31. data/lib/sequel_core/database/schema.rb +156 -0
  32. data/lib/sequel_core/dataset.rb +457 -0
  33. data/lib/sequel_core/dataset/callback.rb +13 -0
  34. data/lib/sequel_core/dataset/convenience.rb +245 -0
  35. data/lib/sequel_core/dataset/pagination.rb +96 -0
  36. data/lib/sequel_core/dataset/query.rb +41 -0
  37. data/lib/sequel_core/dataset/schema.rb +15 -0
  38. data/lib/sequel_core/dataset/sql.rb +889 -0
  39. data/lib/sequel_core/deprecated.rb +26 -0
  40. data/lib/sequel_core/exceptions.rb +42 -0
  41. data/lib/sequel_core/migration.rb +187 -0
  42. data/lib/sequel_core/object_graph.rb +216 -0
  43. data/lib/sequel_core/pretty_table.rb +71 -0
  44. data/lib/sequel_core/schema.rb +2 -0
  45. data/lib/sequel_core/schema/generator.rb +239 -0
  46. data/lib/sequel_core/schema/sql.rb +325 -0
  47. data/lib/sequel_core/sql.rb +812 -0
  48. data/lib/sequel_model.rb +5 -1
  49. data/lib/sequel_model/association_reflection.rb +3 -8
  50. data/lib/sequel_model/base.rb +15 -10
  51. data/lib/sequel_model/inflector.rb +3 -5
  52. data/lib/sequel_model/plugins.rb +1 -1
  53. data/lib/sequel_model/record.rb +11 -3
  54. data/lib/sequel_model/schema.rb +4 -4
  55. data/lib/sequel_model/validations.rb +6 -1
  56. data/spec/adapters/ado_spec.rb +17 -0
  57. data/spec/adapters/informix_spec.rb +96 -0
  58. data/spec/adapters/mysql_spec.rb +764 -0
  59. data/spec/adapters/oracle_spec.rb +222 -0
  60. data/spec/adapters/postgres_spec.rb +441 -0
  61. data/spec/adapters/spec_helper.rb +7 -0
  62. data/spec/adapters/sqlite_spec.rb +400 -0
  63. data/spec/integration/dataset_test.rb +51 -0
  64. data/spec/integration/eager_loader_test.rb +702 -0
  65. data/spec/integration/schema_test.rb +102 -0
  66. data/spec/integration/spec_helper.rb +44 -0
  67. data/spec/integration/type_test.rb +43 -0
  68. data/spec/rcov.opts +2 -0
  69. data/spec/sequel_core/connection_pool_spec.rb +363 -0
  70. data/spec/sequel_core/core_ext_spec.rb +156 -0
  71. data/spec/sequel_core/core_sql_spec.rb +427 -0
  72. data/spec/sequel_core/database_spec.rb +964 -0
  73. data/spec/sequel_core/dataset_spec.rb +2977 -0
  74. data/spec/sequel_core/expression_filters_spec.rb +346 -0
  75. data/spec/sequel_core/migration_spec.rb +261 -0
  76. data/spec/sequel_core/object_graph_spec.rb +234 -0
  77. data/spec/sequel_core/pretty_table_spec.rb +58 -0
  78. data/spec/sequel_core/schema_generator_spec.rb +122 -0
  79. data/spec/sequel_core/schema_spec.rb +497 -0
  80. data/spec/sequel_core/spec_helper.rb +51 -0
  81. data/spec/{association_reflection_spec.rb → sequel_model/association_reflection_spec.rb} +6 -6
  82. data/spec/{associations_spec.rb → sequel_model/associations_spec.rb} +47 -18
  83. data/spec/{base_spec.rb → sequel_model/base_spec.rb} +2 -1
  84. data/spec/{caching_spec.rb → sequel_model/caching_spec.rb} +0 -0
  85. data/spec/{dataset_methods_spec.rb → sequel_model/dataset_methods_spec.rb} +13 -1
  86. data/spec/{eager_loading_spec.rb → sequel_model/eager_loading_spec.rb} +75 -14
  87. data/spec/{hooks_spec.rb → sequel_model/hooks_spec.rb} +4 -4
  88. data/spec/sequel_model/inflector_spec.rb +119 -0
  89. data/spec/{model_spec.rb → sequel_model/model_spec.rb} +30 -11
  90. data/spec/{plugins_spec.rb → sequel_model/plugins_spec.rb} +0 -0
  91. data/spec/{record_spec.rb → sequel_model/record_spec.rb} +47 -6
  92. data/spec/{schema_spec.rb → sequel_model/schema_spec.rb} +18 -4
  93. data/spec/{spec_helper.rb → sequel_model/spec_helper.rb} +3 -2
  94. data/spec/{validations_spec.rb → sequel_model/validations_spec.rb} +37 -17
  95. data/spec/spec_config.rb +9 -0
  96. data/spec/spec_config.rb.example +10 -0
  97. metadata +110 -37
  98. data/spec/inflector_spec.rb +0 -34
@@ -0,0 +1,138 @@
1
+ require 'sqlite3'
2
+ require 'sequel_core/adapters/shared/sqlite'
3
+
4
+ module Sequel
5
+ module SQLite
6
+ class Database < Sequel::Database
7
+ include ::Sequel::SQLite::DatabaseMethods
8
+
9
+ set_adapter_scheme :sqlite
10
+
11
+ def self.uri_to_options(uri) # :nodoc:
12
+ { :database => (uri.host.nil? && uri.path == '/') ? nil : "#{uri.host}#{uri.path}" }
13
+ end
14
+
15
+ private_class_method :uri_to_options
16
+
17
+ def connect
18
+ @opts[:database] = ':memory:' if @opts[:database].blank?
19
+ db = ::SQLite3::Database.new(@opts[:database])
20
+ db.busy_timeout(@opts.fetch(:timeout, 5000))
21
+ db.type_translation = true
22
+ # fix for timestamp translation
23
+ db.translator.add_translator("timestamp") do |t, v|
24
+ v =~ /^\d+$/ ? Time.at(v.to_i) : Time.parse(v)
25
+ end
26
+ db
27
+ end
28
+
29
+ def dataset(opts = nil)
30
+ SQLite::Dataset.new(self, opts)
31
+ end
32
+
33
+ def disconnect
34
+ @pool.disconnect {|c| c.close}
35
+ end
36
+
37
+ def execute(sql)
38
+ begin
39
+ log_info(sql)
40
+ @pool.hold {|conn| conn.execute_batch(sql); conn.changes}
41
+ rescue SQLite3::Exception => e
42
+ raise Error::InvalidStatement, "#{sql}\r\n#{e.message}"
43
+ end
44
+ end
45
+
46
+ def execute_insert(sql)
47
+ begin
48
+ log_info(sql)
49
+ @pool.hold {|conn| conn.execute(sql); conn.last_insert_row_id}
50
+ rescue SQLite3::Exception => e
51
+ raise Error::InvalidStatement, "#{sql}\r\n#{e.message}"
52
+ end
53
+ end
54
+
55
+ def single_value(sql)
56
+ begin
57
+ log_info(sql)
58
+ @pool.hold {|conn| conn.get_first_value(sql)}
59
+ rescue SQLite3::Exception => e
60
+ raise Error::InvalidStatement, "#{sql}\r\n#{e.message}"
61
+ end
62
+ end
63
+
64
+ def execute_select(sql, &block)
65
+ begin
66
+ log_info(sql)
67
+ @pool.hold {|conn| conn.query(sql, &block)}
68
+ rescue SQLite3::Exception => e
69
+ raise Error::InvalidStatement, "#{sql}\r\n#{e.message}"
70
+ end
71
+ end
72
+
73
+ def transaction(&block)
74
+ @pool.hold do |conn|
75
+ if conn.transaction_active?
76
+ return yield(conn)
77
+ end
78
+ begin
79
+ result = nil
80
+ conn.transaction {result = yield(conn)}
81
+ result
82
+ rescue ::Exception => e
83
+ raise (SQLite3::Exception === e ? Error.new(e.message) : e) unless Error::Rollback === e
84
+ end
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def connection_pool_default_options
91
+ o = super.merge(:pool_convert_exceptions=>false)
92
+ # Default to only a single connection if a memory database is used,
93
+ # because otherwise each connection will get a separate database
94
+ o[:max_connections] = 1 if @opts[:database] == ':memory:' || @opts[:database].blank?
95
+ o
96
+ end
97
+ end
98
+
99
+ class Dataset < Sequel::Dataset
100
+ include ::Sequel::SQLite::DatasetMethods
101
+
102
+ EXPLAIN = 'EXPLAIN %s'.freeze
103
+
104
+ def explain
105
+ res = []
106
+ @db.result_set(EXPLAIN % select_sql(opts), nil) {|r| res << r}
107
+ res
108
+ end
109
+
110
+ def fetch_rows(sql)
111
+ @db.execute_select(sql) do |result|
112
+ @columns = result.columns.map {|c| c.to_sym}
113
+ column_count = @columns.size
114
+ result.each do |values|
115
+ row = {}
116
+ column_count.times {|i| row[@columns[i]] = values[i]}
117
+ yield row
118
+ end
119
+ end
120
+ end
121
+
122
+ def literal(v)
123
+ case v
124
+ when LiteralString
125
+ v
126
+ when String
127
+ "'#{::SQLite3::Database.quote(v)}'"
128
+ when Time
129
+ literal(v.iso8601)
130
+ when Date, DateTime
131
+ literal(v.to_s)
132
+ else
133
+ super
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,194 @@
1
+ # A ConnectionPool manages access to database connections by keeping
2
+ # multiple connections and giving threads exclusive access to each
3
+ # connection.
4
+ class Sequel::ConnectionPool
5
+ # A hash of connections currently being used, key is the Thread,
6
+ # value is the connection.
7
+ attr_reader :allocated
8
+
9
+ # An array of connections opened but not currently used
10
+ attr_reader :available_connections
11
+
12
+ # The proc used to create a new database connection.
13
+ attr_accessor :connection_proc
14
+
15
+ # The total number of connections opened, should
16
+ # be equal to available_connections.length +
17
+ # allocated.length
18
+ attr_reader :created_count
19
+ alias_method :size, :created_count
20
+
21
+ # The maximum number of connections.
22
+ attr_reader :max_size
23
+
24
+ # The mutex that protects access to the other internal vairables. You must use
25
+ # this if you want to manipulate the variables safely.
26
+ attr_reader :mutex
27
+
28
+
29
+ # Constructs a new pool with a maximum size. If a block is supplied, it
30
+ # is used to create new connections as they are needed.
31
+ #
32
+ # pool = ConnectionPool.new(:max_connections=>10) {MyConnection.new(opts)}
33
+ #
34
+ # The connection creation proc can be changed at any time by assigning a
35
+ # Proc to pool#connection_proc.
36
+ #
37
+ # pool = ConnectionPool.new(:max_connections=>10)
38
+ # pool.connection_proc = proc {MyConnection.new(opts)}
39
+ #
40
+ # The connection pool takes the following options:
41
+ #
42
+ # * :max_connections - The maximum number of connections the connection pool
43
+ # will open (default 4)
44
+ # * :pool_convert_exceptions - Whether to convert non-StandardError based exceptions
45
+ # to RuntimeError exceptions (default true)
46
+ # * :pool_sleep_time - The amount of time to sleep before attempting to acquire
47
+ # a connection again (default 0.001)
48
+ # * :pool_timeout - The amount of seconds to wait to acquire a connection
49
+ # before raising a PoolTimeoutError (default 5)
50
+ def initialize(opts = {}, &block)
51
+ @max_size = opts[:max_connections] || 4
52
+ @mutex = Mutex.new
53
+ @connection_proc = block
54
+
55
+ @available_connections = []
56
+ @allocated = {}
57
+ @created_count = 0
58
+ @timeout = opts[:pool_timeout] || 5
59
+ @sleep_time = opts[:pool_sleep_time] || 0.001
60
+ @convert_exceptions = opts.include?(:pool_convert_exceptions) ? opts[:pool_convert_exceptions] : true
61
+ end
62
+
63
+ # Chooses the first available connection, or if none are available,
64
+ # creates a new connection. Passes the connection to the supplied block:
65
+ #
66
+ # pool.hold {|conn| conn.execute('DROP TABLE posts')}
67
+ #
68
+ # Pool#hold is re-entrant, meaning it can be called recursively in
69
+ # the same thread without blocking.
70
+ #
71
+ # If no connection is immediately available and the pool is already using the maximum
72
+ # number of connections, Pool#hold will block until a connection
73
+ # is available or the timeout expires. If the timeout expires before a
74
+ # connection can be acquired, a Sequel::Error::PoolTimeoutError is
75
+ # raised.
76
+ def hold
77
+ begin
78
+ t = Thread.current
79
+ time = Time.new
80
+ timeout = time + @timeout
81
+ sleep_time = @sleep_time
82
+ if conn = owned_connection(t)
83
+ return yield(conn)
84
+ end
85
+ until conn = acquire(t)
86
+ raise(::Sequel::Error::PoolTimeoutError) if Time.new > timeout
87
+ sleep sleep_time
88
+ end
89
+ begin
90
+ yield conn
91
+ ensure
92
+ release(t, conn)
93
+ end
94
+ rescue Exception => e
95
+ raise(@convert_exceptions && !e.is_a?(StandardError) ? RuntimeError.new(e.message) : e)
96
+ end
97
+ end
98
+
99
+ # Removes all connection currently available, optionally yielding each
100
+ # connection to the given block. This method has the effect of
101
+ # disconnecting from the database. Once a connection is requested using
102
+ # #hold, the connection pool creates new connections to the database.
103
+ def disconnect(&block)
104
+ @mutex.synchronize do
105
+ @available_connections.each {|c| block[c]} if block
106
+ @available_connections = []
107
+ @created_count = @allocated.size
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+ # Returns the connection owned by the supplied thread, if any.
114
+ def owned_connection(thread)
115
+ @mutex.synchronize{@allocated[thread]}
116
+ end
117
+
118
+ # Assigns a connection to the supplied thread, if one is available.
119
+ def acquire(thread)
120
+ @mutex.synchronize do
121
+ if conn = available
122
+ @allocated[thread] = conn
123
+ end
124
+ end
125
+ end
126
+
127
+ # Returns an available connection. If no connection is available,
128
+ # tries to create a new connection.
129
+ def available
130
+ @available_connections.pop || make_new
131
+ end
132
+
133
+ # Creates a new connection if the size of the pool is less than the
134
+ # maximum size.
135
+ def make_new
136
+ if @created_count < @max_size
137
+ @created_count += 1
138
+ @connection_proc ? @connection_proc.call : \
139
+ (raise Error, "No connection proc specified")
140
+ end
141
+ end
142
+
143
+ # Releases the connection assigned to the supplied thread.
144
+ def release(thread, conn)
145
+ @mutex.synchronize do
146
+ @allocated.delete(thread)
147
+ @available_connections << conn
148
+ end
149
+ end
150
+ end
151
+
152
+ # A SingleThreadedPool acts as a replacement for a ConnectionPool for use
153
+ # in single-threaded applications. ConnectionPool imposes a substantial
154
+ # performance penalty, so SingleThreadedPool is used to gain some speed.
155
+ #
156
+ # Note that using a single threaded pool with some adapters can cause
157
+ # errors in certain cases, see Sequel.single_threaded=.
158
+ class Sequel::SingleThreadedPool
159
+ # The single database connection for the pool
160
+ attr_reader :conn
161
+
162
+ # The proc used to create a new database connection
163
+ attr_writer :connection_proc
164
+
165
+ # Initializes the instance with the supplied block as the connection_proc.
166
+ #
167
+ # The single threaded pool takes the following options:
168
+ #
169
+ # * :pool_convert_exceptions - Whether to convert non-StandardError based exceptions
170
+ # to RuntimeError exceptions (default true)
171
+ def initialize(opts={}, &block)
172
+ @connection_proc = block
173
+ @convert_exceptions = opts.include?(:pool_convert_exceptions) ? opts[:pool_convert_exceptions] : true
174
+ end
175
+
176
+ # Yields the connection to the supplied block. This method simulates the
177
+ # ConnectionPool#hold API.
178
+ def hold
179
+ begin
180
+ @conn ||= @connection_proc.call
181
+ yield @conn
182
+ rescue Exception => e
183
+ # if the error is not a StandardError it is converted into RuntimeError.
184
+ raise(@convert_exceptions && !e.is_a?(StandardError) ? RuntimeError.new(e.message) : e)
185
+ end
186
+ end
187
+
188
+ # Disconnects from the database. Once a connection is requested using
189
+ # #hold, the connection is reestablished.
190
+ def disconnect(&block)
191
+ block[@conn] if block && @conn
192
+ @conn = nil
193
+ end
194
+ end
@@ -0,0 +1,203 @@
1
+ if RUBY_VERSION < '1.9.0'
2
+ class Hash
3
+ alias key index
4
+ end
5
+ end
6
+
7
+ class Array
8
+ # True if the array is not empty and all of its elements are
9
+ # arrays of size 2. This is used to determine if the array
10
+ # could be a specifier of conditions, used similarly to a hash
11
+ # but allowing for duplicate keys.
12
+ #
13
+ # hash.to_a.all_two_pairs? # => true unless hash is empty
14
+ def all_two_pairs?
15
+ !empty? && all?{|i| (Array === i) && (i.length == 2)}
16
+ end
17
+
18
+ # Removes and returns the last member of the array if it is a hash. Otherwise,
19
+ # an empty hash is returned This method is useful when writing methods that
20
+ # take an options hash as the last parameter. For example:
21
+ #
22
+ # def validate_each(*args, &block)
23
+ # opts = args.extract_options!
24
+ # ...
25
+ # end
26
+ def extract_options!
27
+ last.is_a?(Hash) ? pop : {}
28
+ end
29
+ end
30
+
31
+ module Enumerable
32
+ # Invokes the specified method for each item, along with the supplied
33
+ # arguments.
34
+ def send_each(sym, *args)
35
+ each{|i| i.send(sym, *args)}
36
+ end
37
+ end
38
+
39
+ class FalseClass
40
+ # false is always blank
41
+ def blank?
42
+ true
43
+ end
44
+ end
45
+
46
+ # Add some metaprogramming methods to avoid class << self
47
+ class Module
48
+ # Defines an instance method within a class/module
49
+ def class_def(name, &block)
50
+ class_eval{define_method(name, &block)}
51
+ end
52
+
53
+ private
54
+
55
+ # Define instance method(s) that calls class method(s) of the
56
+ # same name. Replaces the construct:
57
+ #
58
+ # define_method(meth){self.class.send(meth)}
59
+ def class_attr_reader(*meths)
60
+ meths.each{|meth| define_method(meth){self.class.send(meth)}}
61
+ end
62
+
63
+ # Create an alias for a singleton/class method.
64
+ # Replaces the construct:
65
+ #
66
+ # class << self
67
+ # alias_method to, from
68
+ # end
69
+ def metaalias(to, from)
70
+ meta_eval{alias_method to, from}
71
+ end
72
+
73
+ # Make a singleton/class attribute accessor method(s).
74
+ # Replaces the construct:
75
+ #
76
+ # class << self
77
+ # attr_accessor *meths
78
+ # end
79
+ def metaattr_accessor(*meths)
80
+ meta_eval{attr_accessor(*meths)}
81
+ end
82
+
83
+ # Make a singleton/class method(s) private.
84
+ # Make a singleton/class attribute reader method(s).
85
+ # Replaces the construct:
86
+ #
87
+ # class << self
88
+ # attr_reader *meths
89
+ # end
90
+ def metaattr_reader(*meths)
91
+ meta_eval{attr_reader(*meths)}
92
+ end
93
+ end
94
+
95
+ # Helpers from Metaid and a bit more
96
+ class Object
97
+ # Objects are blank if they respond true to empty?
98
+ def blank?
99
+ respond_to?(:empty?) && empty?
100
+ end
101
+
102
+ # Returns true if the object is an instance of one of the classes
103
+ def is_one_of?(*classes)
104
+ !!classes.find{|c| is_a?(c)}
105
+ end
106
+
107
+ # Add methods to the object's metaclass
108
+ def meta_def(name, &block)
109
+ meta_eval{define_method(name, &block)}
110
+ end
111
+
112
+ # Evaluate the block in the context of the object's metaclass
113
+ def meta_eval(&block)
114
+ metaclass.instance_eval(&block)
115
+ end
116
+
117
+ # The hidden singleton lurks behind everyone
118
+ def metaclass
119
+ class << self
120
+ self
121
+ end
122
+ end
123
+ end
124
+
125
+ class NilClass
126
+ # nil is always blank
127
+ def blank?
128
+ true
129
+ end
130
+ end
131
+
132
+ class Numeric
133
+ # Numerics are never blank (not even 0)
134
+ def blank?
135
+ false
136
+ end
137
+ end
138
+
139
+ class Range
140
+ # Returns the interval between the beginning and end of the range.
141
+ #
142
+ # For exclusive ranges, is one less than the inclusive range:
143
+ #
144
+ # (0..10).interval # => 10
145
+ # (0...10).interval # => 9
146
+ #
147
+ # Only works for numeric ranges, for other ranges the result is undefined,
148
+ # and the method may raise an error.
149
+ def interval
150
+ last - first - (exclude_end? ? 1 : 0)
151
+ end
152
+ end
153
+
154
+ class String
155
+ # Strings are blank if they are empty or include only whitespace
156
+ def blank?
157
+ strip.empty?
158
+ end
159
+
160
+ # Converts a string into a Date object.
161
+ def to_date
162
+ begin
163
+ Date.parse(self)
164
+ rescue => e
165
+ raise Sequel::Error::InvalidValue, "Invalid date value '#{self}' (#{e.message})"
166
+ end
167
+ end
168
+
169
+ # Converts a string into a DateTime object.
170
+ def to_datetime
171
+ begin
172
+ DateTime.parse(self)
173
+ rescue => e
174
+ raise Sequel::Error::InvalidValue, "Invalid date value '#{self}' (#{e.message})"
175
+ end
176
+ end
177
+
178
+ # Converts a string into a Time or DateTime object, depending on the
179
+ # value of Sequel.datetime_class
180
+ def to_sequel_time
181
+ begin
182
+ Sequel.datetime_class.parse(self)
183
+ rescue => e
184
+ raise Sequel::Error::InvalidValue, "Invalid time value '#{self}' (#{e.message})"
185
+ end
186
+ end
187
+
188
+ # Converts a string into a Time object.
189
+ def to_time
190
+ begin
191
+ Time.parse(self)
192
+ rescue => e
193
+ raise Sequel::Error::InvalidValue, "Invalid time value '#{self}' (#{e.message})"
194
+ end
195
+ end
196
+ end
197
+
198
+ class TrueClass
199
+ # true is never blank
200
+ def blank?
201
+ false
202
+ end
203
+ end