Class: Sequel::Dataset
- Includes:
- Enumerable, Convenience, SQL, Sequelizer
- Defined in:
- lib/sequel/dataset.rb,
lib/sequel/dataset/sql.rb,
lib/sequel/dataset/sequelizer.rb,
lib/sequel/dataset/convenience.rb
Overview
A Dataset represents a view of a the data in a database, constrained by specific parameters such as filtering conditions, order, etc. Datasets can be used to create, retrieve, update and delete records.
Query results are always retrieved on demand, so a dataset can be kept around and reused indefinitely:
my_posts = DB[:posts].filter(:author => 'david') # no records are retrieved
p my_posts.all # records are now retrieved
...
p my_posts.all # records are retrieved again
In order to provide this functionality, dataset methods such as where, select, order, etc. return modified copies of the dataset, so you can use different datasets to access data:
posts = DB[:posts]
davids_posts = posts.filter(:author => 'david')
old_posts = posts.filter('stamp < ?', 1.week.ago)
Datasets are Enumerable objects, so they can be manipulated using any of the Enumerable methods, such as map, inject, etc.
The Dataset Adapter Interface
Each adapter should define its own dataset class as a descendant of Sequel::Dataset. The following methods should be overriden by the adapter Dataset class (each method with the stock implementation):
# Iterate over the results of the SQL query and call the supplied
# block with each record (as a hash).
def fetch_rows(sql, &block)
@db.synchronize do
r = @db.execute(sql)
r.each(&block)
end
end
# Insert records.
def insert(*values)
@db.synchronize do
@db.execute(insert_sql(*values)).last_insert_id
end
end
# Update records.
def update(*args, &block)
@db.synchronize do
@db.execute(update_sql(*args, &block)).affected_rows
end
end
# Delete records.
def delete(opts = nil)
@db.synchronize do
@db.execute(delete_sql(opts)).affected_rows
end
end
Direct Known Subclasses
ADO::Dataset, Adapter::Dataset, Sequel::DB2::Dataset, Sequel::DBI::Dataset, Informix::Dataset, JDBC::Dataset, MySQL::Dataset, ODBC::Dataset, OpenBase::Dataset, Oracle::Dataset, Postgres::Dataset, SQLite::Dataset
Defined Under Namespace
Modules: Convenience, SQL, Sequelizer
Constant Summary collapse
- NOTIMPL_MSG =
"This method must be overriden in Sequel adapters".freeze
- STOCK_TRANSFORMS =
{ :marshal => [proc {|v| Marshal.load(v)}, proc {|v| Marshal.dump(v)}], :yaml => [proc {|v| YAML.load v if v}, proc {|v| v.to_yaml}] }
- @@dataset_classes =
[]
Constants included from Convenience
Convenience::COMMA_SEPARATOR, Convenience::MAGIC_METHODS, Convenience::MUTATION_RE, Convenience::NAKED_HASH
Constants included from SQL
SQL::ALIASED_REGEXP, SQL::AND_SEPARATOR, SQL::COMMA_SEPARATOR, SQL::DATE_FORMAT, SQL::FALSE, SQL::JOIN_TYPES, SQL::NULL, SQL::QUALIFIED_REGEXP, SQL::QUESTION_MARK, SQL::STOCK_COUNT_OPTS, SQL::TIMESTAMP_FORMAT, SQL::TRUE, SQL::WILDCARD
Constants included from Sequelizer
Sequelizer::JOIN_AND, Sequelizer::JOIN_COMMA
Instance Attribute Summary collapse
-
#db ⇒ Object
readonly
Returns the value of attribute db.
-
#opts ⇒ Object
Returns the value of attribute opts.
Attributes included from Convenience
#current_page, #page_count, #page_size, #pagination_record_count
Class Method Summary collapse
-
.dataset_classes ⇒ Object
:nodoc:.
-
.inherited(c) ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#<<(*args) ⇒ Object
Inserts the supplied values into the associated table.
-
#clone_merge(opts) ⇒ Object
Returns a new instance of the dataset with with the give options merged.
-
#columns ⇒ Object
Returns the columns in the result set in their true order.
-
#delete(opts = nil) ⇒ Object
Deletes the records in the dataset.
-
#each(opts = nil, &block) ⇒ Object
Iterates over the records in the dataset.
-
#extend_with_destroy ⇒ Object
Extends the dataset with a destroy method, that calls destroy for each record in the dataset.
-
#fetch_rows(sql, &block) ⇒ Object
Executes a select query and fetches records, passing each record to the supplied block.
-
#initialize(db, opts = nil) ⇒ Dataset
constructor
Constructs a new instance of a dataset with a database instance, initial options and an optional record class.
-
#insert(*values) ⇒ Object
Inserts values into the associated table.
-
#model_classes ⇒ Object
Returns the the model classes associated with the dataset as a hash.
-
#naked ⇒ Object
Returns a naked dataset clone - i.e.
-
#polymorphic_key ⇒ Object
Returns the column name for the polymorphic key.
-
#remove_row_proc ⇒ Object
Removes the row making proc.
-
#set(*args, &block) ⇒ Object
Updates the dataset with the given values.
-
#set_model(key, *args) ⇒ Object
Associates or disassociates the dataset with a model.
-
#set_options(opts) ⇒ Object
:nodoc:.
-
#set_row_proc(&filter) ⇒ Object
Overrides the each method to pass the values through a filter.
-
#transform(t) ⇒ Object
Sets a value transform which is used to convert values loaded and saved to/from the database.
-
#transform_load(r) ⇒ Object
Applies the value transform for data loaded from the database.
-
#transform_save(r) ⇒ Object
Applies the value transform for data saved to the database.
-
#update(values, opts = nil) ⇒ Object
Updates values for the dataset.
-
#update_each_method ⇒ Object
Updates the each method according to whether @row_proc and @transform are set or not.
Methods included from Convenience
#[], #[]=, #avg, #create_or_replace_view, #create_view, #current_page_record_count, #current_page_record_range, #each_hash, #empty?, #first, #group_and_count, #interval, #last, #magic_method_missing, #map, #max, #method_missing, #min, #multi_insert, #next_page, #page_range, #paginate, #prev_page, #print, #query, #range, #set_pagination_info, #single_record, #single_value, #sum, #to_csv, #to_hash
Methods included from SQL
#and, #column_list, #count, #delete_sql, #except, #exclude, #exists, #expression_list, #filter, #from, #full_outer_join, #group, #having, #inner_join, #insert_multiple, #insert_sql, #intersect, #invert_order, #join_expr, #join_table, #left_outer_join, #limit, #literal, #or, #order, #qualified_column_name, #quote_column_ref, #reverse_order, #right_outer_join, #select, #select_sql, #source_list, #to_table_reference, #union, #uniq, #update_sql, #where
Methods included from Sequelizer
#call_expr, #compare_expr, #eval_expr, #ext_expr, #fcall_expr, #iter_expr, #match_expr, #proc_to_sql, #pt_expr, #replace_dvars, #unfold_each_expr, #value_to_parse_tree, #vcall_expr
Methods included from Enumerable
Constructor Details
#initialize(db, opts = nil) ⇒ Dataset
Constructs a new instance of a dataset with a database instance, initial options and an optional record class. Datasets are usually constructed by invoking Database methods:
DB[:posts]
Or:
DB.dataset # the returned dataset is blank
Sequel::Dataset is an abstract class that is not useful by itself. Each database adaptor should provide a descendant class of Sequel::Dataset.
87 88 89 90 91 92 |
# File 'lib/sequel/dataset.rb', line 87 def initialize(db, opts = nil) @db = db @opts = opts || {} @row_proc = nil @transform = nil end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class Sequel::Dataset::Convenience
Instance Attribute Details
#db ⇒ Object (readonly)
Returns the value of attribute db.
72 73 74 |
# File 'lib/sequel/dataset.rb', line 72 def db @db end |
#opts ⇒ Object
Returns the value of attribute opts.
73 74 75 |
# File 'lib/sequel/dataset.rb', line 73 def opts @opts end |
Class Method Details
.dataset_classes ⇒ Object
:nodoc:
400 401 402 |
# File 'lib/sequel/dataset.rb', line 400 def self.dataset_classes #:nodoc: @@dataset_classes end |
.inherited(c) ⇒ Object
:nodoc:
404 405 406 |
# File 'lib/sequel/dataset.rb', line 404 def self.inherited(c) #:nodoc: @@dataset_classes << c end |
Instance Method Details
#<<(*args) ⇒ Object
Inserts the supplied values into the associated table.
153 154 155 |
# File 'lib/sequel/dataset.rb', line 153 def <<(*args) insert(*args) end |
#clone_merge(opts) ⇒ Object
Returns a new instance of the dataset with with the give options merged.
95 96 97 98 99 |
# File 'lib/sequel/dataset.rb', line 95 def clone_merge(opts) new_dataset = clone new_dataset.(@opts.merge(opts)) new_dataset end |
#columns ⇒ Object
Returns the columns in the result set in their true order. The stock implementation returns the content of @columns. If @columns is nil, a query is performed. Adapters are expected to fill @columns with the column information when a query is performed.
147 148 149 150 |
# File 'lib/sequel/dataset.rb', line 147 def columns first unless @columns @columns || [] end |
#delete(opts = nil) ⇒ Object
Deletes the records in the dataset. Adapters should override this method.
136 137 138 139 140 141 |
# File 'lib/sequel/dataset.rb', line 136 def delete(opts = nil) # @db.synchronize do # @db.execute(delete_sql(opts)).affected_rows # end raise NotImplementedError, NOTIMPL_MSG end |
#each(opts = nil, &block) ⇒ Object
Iterates over the records in the dataset
163 164 165 166 |
# File 'lib/sequel/dataset.rb', line 163 def each(opts = nil, &block) fetch_rows(select_sql(opts), &block) self end |
#extend_with_destroy ⇒ Object
Extends the dataset with a destroy method, that calls destroy for each record in the dataset.
385 386 387 388 389 390 391 392 393 394 395 396 |
# File 'lib/sequel/dataset.rb', line 385 def extend_with_destroy unless respond_to?(:destroy) (:destroy) do unless @opts[:models] raise Error, "No model associated with this dataset" end count = 0 @db.transaction {each {|r| count += 1; r.destroy}} count end end end |
#fetch_rows(sql, &block) ⇒ Object
Executes a select query and fetches records, passing each record to the supplied block. Adapters should override this method.
110 111 112 113 114 115 116 |
# File 'lib/sequel/dataset.rb', line 110 def fetch_rows(sql, &block) # @db.synchronize do # r = @db.execute(sql) # r.each(&block) # end raise NotImplementedError, NOTIMPL_MSG end |
#insert(*values) ⇒ Object
Inserts values into the associated table. Adapters should override this method.
120 121 122 123 124 125 |
# File 'lib/sequel/dataset.rb', line 120 def insert(*values) # @db.synchronize do # @db.execute(insert_sql(*values)).last_insert_id # end raise NotImplementedError, NOTIMPL_MSG end |
#model_classes ⇒ Object
Returns the the model classes associated with the dataset as a hash.
169 170 171 |
# File 'lib/sequel/dataset.rb', line 169 def model_classes @opts[:models] end |
#naked ⇒ Object
Returns a naked dataset clone - i.e. a dataset that returns records as hashes rather than model objects.
180 181 182 183 184 |
# File 'lib/sequel/dataset.rb', line 180 def naked d = clone_merge(:naked => true, :models => nil, :polymorphic_key => nil) d.set_model(nil) d end |
#polymorphic_key ⇒ Object
Returns the column name for the polymorphic key.
174 175 176 |
# File 'lib/sequel/dataset.rb', line 174 def polymorphic_key @opts[:polymorphic_key] end |
#remove_row_proc ⇒ Object
Removes the row making proc.
270 271 272 273 |
# File 'lib/sequel/dataset.rb', line 270 def remove_row_proc @row_proc = nil update_each_method end |
#set(*args, &block) ⇒ Object
Updates the dataset with the given values.
158 159 160 |
# File 'lib/sequel/dataset.rb', line 158 def set(*args, &block) update(*args, &block) end |
#set_model(key, *args) ⇒ Object
Associates or disassociates the dataset with a model. If no argument or nil is specified, the dataset is turned into a naked dataset and returns records as hashes. If a model class specified, the dataset is modified to return records as instances of the model class, e.g:
class MyModel
def initialize(values)
@values = values
...
end
end
dataset.set_model(MyModel)
You can also provide additional arguments to be passed to the model’s initialize method:
class MyModel
def initialize(values, )
@values = values
...
end
end
dataset.set_model(MyModel, :allow_delete => false)
The dataset can be made polymorphic by specifying a column name as the polymorphic key and a hash mapping column values to model classes.
dataset.set_model(:kind, {1 => Person, 2 => Business})
You can also set a default model class to fall back on by specifying a class corresponding to nil:
dataset.set_model(:kind, {nil => DefaultClass, 1 => Person, 2 => Business})
To disassociate a model from the dataset, you can call the #set_model and specify nil as the class:
dataset.set_model(nil)
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/sequel/dataset.rb', line 227 def set_model(key, *args) # pattern matching case key when nil # set_model(nil) => no # no argument provided, so the dataset is denuded @opts.merge!(:naked => true, :models => nil, :polymorphic_key => nil) remove_row_proc # extend_with_stock_each when Class # isomorphic model @opts.merge!(:naked => nil, :models => {nil => key}, :polymorphic_key => nil) set_row_proc {|h| key.new(h, *args)} extend_with_destroy when Symbol # polymorphic model hash = args.shift || raise(ArgumentError, "No class hash supplied for polymorphic model") @opts.merge!(:naked => true, :models => hash, :polymorphic_key => key) set_row_proc do |h| c = hash[h[key]] || hash[nil] || \ raise(Error, "No matching model class for record (#{polymorphic_key} => #{h[polymorphic_key].inspect})") c.new(h, *args) end extend_with_destroy else raise ArgumentError, "Invalid model specified" end self end |
#set_options(opts) ⇒ Object
:nodoc:
101 102 103 104 |
# File 'lib/sequel/dataset.rb', line 101 def (opts) #:nodoc: @opts = opts @columns = nil end |
#set_row_proc(&filter) ⇒ Object
Overrides the each method to pass the values through a filter. The filter receives as argument a hash containing the column values for the current record. The filter should return a value which is then passed to the iterating block. In order to elucidate, here’s a contrived example:
dataset.set_row_proc {|h| h.merge(:xxx => 'yyy')}
dataset.first[:xxx] #=> "yyy" # always!
264 265 266 267 |
# File 'lib/sequel/dataset.rb', line 264 def set_row_proc(&filter) @row_proc = filter update_each_method end |
#transform(t) ⇒ Object
Sets a value transform which is used to convert values loaded and saved to/from the database. The transform should be supplied as a hash. Each value in the hash should be an array containing two proc objects - one for transforming loaded values, and one for transforming saved values. The following example demonstrates how to store Ruby objects in a dataset using Marshal serialization:
dataset.transform(:obj => [
proc {|v| Marshal.load(v)},
proc {|v| Marshal.dump(v)}
])
dataset.insert_sql(:obj => 1234) #=>
"INSERT INTO items (obj) VALUES ('\004\bi\002\322\004')"
Another form of using transform is by specifying stock transforms:
dataset.transform(:obj => :marshal)
The currently supported stock transforms are :marshal and :yaml.
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
# File 'lib/sequel/dataset.rb', line 300 def transform(t) @transform = t t.each do |k, v| case v when Array if (v.size != 2) || !v.first.is_a?(Proc) && !v.last.is_a?(Proc) raise Error::InvalidTransform, "Invalid transform specified" end else unless v = STOCK_TRANSFORMS[v] raise Error::InvalidTransform, "Invalid transform specified" else t[k] = v end end end update_each_method self end |
#transform_load(r) ⇒ Object
Applies the value transform for data loaded from the database.
321 322 323 324 325 326 327 328 |
# File 'lib/sequel/dataset.rb', line 321 def transform_load(r) @transform.each do |k, tt| if r.has_key?(k) r[k] = tt[0][r[k]] end end r end |
#transform_save(r) ⇒ Object
Applies the value transform for data saved to the database.
331 332 333 334 335 336 337 338 |
# File 'lib/sequel/dataset.rb', line 331 def transform_save(r) @transform.each do |k, tt| if r.has_key?(k) r[k] = tt[1][r[k]] end end r end |
#update(values, opts = nil) ⇒ Object
Updates values for the dataset. Adapters should override this method.
128 129 130 131 132 133 |
# File 'lib/sequel/dataset.rb', line 128 def update(values, opts = nil) # @db.synchronize do # @db.execute(update_sql(values, opts)).affected_rows # end raise NotImplementedError, NOTIMPL_MSG end |
#update_each_method ⇒ Object
Updates the each method according to whether @row_proc and @transform are set or not.
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/sequel/dataset.rb', line 342 def update_each_method # warning: ugly code generation ahead if @row_proc && @transform class << self def each(opts = nil, &block) if opts && opts[:naked] fetch_rows(select_sql(opts)) {|r| block[transform_load(r)]} else fetch_rows(select_sql(opts)) {|r| block[@row_proc[transform_load(r)]]} end self end end elsif @row_proc class << self def each(opts = nil, &block) if opts && opts[:naked] fetch_rows(select_sql(opts), &block) else fetch_rows(select_sql(opts)) {|r| block[@row_proc[r]]} end self end end elsif @transform class << self def each(opts = nil, &block) fetch_rows(select_sql(opts)) {|r| block[transform_load(r)]} self end end else class << self def each(opts = nil, &block) fetch_rows(select_sql(opts), &block) self end end end end |