Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Move Dataset code into appropriate files, add RDoc sections
Before, the Sequel::Dataset RDoc page was pretty messy, as just
listed the methods in alphabetical order without any sort of
grouping.

I just recently discovered RDoc sections, which allow you to
group related methods into different sections on the RDoc page,
which allows the user to easily focus on only the methods they
are probably interested in.

This commit adds sections to all of the Dataset code, separating
the methods the users probably don't care about with the methods
they probably do care about.  Unfortunately, I haven't yet
discovered how to to set the order of the sections, so that the
most important sections are first, but hopefully that can be
added later.  Even just this is a big improvement.

To make sure each method was in the correct section, I moved all
of the methods out of dataset.rb and dataset/convenience.rb and
placed them in the appropriate file.  I also added dataset/misc.rb
and dataset/mutation.rb to group the related methods together.
I discovered some more methods in the wrong section, and moved
those to the appropriate section.
  • Loading branch information
jeremyevans committed Apr 15, 2010
1 parent 70f58e6 commit c898e4d
Show file tree
Hide file tree
Showing 11 changed files with 799 additions and 749 deletions.
1 change: 0 additions & 1 deletion lib/sequel/core.rb
Expand Up @@ -282,7 +282,6 @@ def self.def_adapter_method(*adapters) # :nodoc:

require(%w"metaprogramming sql connection_pool exceptions dataset database timezones version")
require(%w"schema_generator schema_methods schema_sql", 'database')
require(%w"actions convenience features graph prepared_statements query sql", 'dataset')
require('core_sql') if !defined?(::SEQUEL_NO_CORE_EXTENSIONS) && !ENV.has_key?('SEQUEL_NO_CORE_EXTENSIONS')

# Add the database adapter class methods to Sequel via metaprogramming
Expand Down
192 changes: 2 additions & 190 deletions lib/sequel/dataset.rb
Expand Up @@ -24,195 +24,7 @@ class Dataset
extend Metaprogramming
include Metaprogramming
include Enumerable

# The dataset options that require the removal of cached columns
# if changed.
COLUMN_CHANGE_OPTS = [:select, :sql, :from, :join].freeze

# All methods that should have a ! method added that modifies
# the receiver.
MUTATION_METHODS = %w'add_graph_aliases and cross_join distinct except exclude
filter for_update from from_self full_join full_outer_join graph
group group_and_count group_by having inner_join intersect invert join join_table left_join
left_outer_join limit lock_style naked natural_full_join natural_join
natural_left_join natural_right_join or order order_by order_more paginate qualify query
reverse reverse_order right_join right_outer_join select select_all select_append select_more server
set_defaults set_graph_aliases set_overrides unfiltered ungraphed ungrouped union
unlimited unordered where with with_recursive with_sql'.collect{|x| x.to_sym}

# Which options don't affect the SQL generation. Used by simple_select_all?
# to determine if this is a simple SELECT * FROM table.
NON_SQL_OPTIONS = [:server, :defaults, :overrides, :graph, :eager_graph, :graph_aliases]

NOTIMPL_MSG = "This method must be overridden in Sequel adapters".freeze
WITH_SUPPORTED=:select_with_sql

# The database that corresponds to this dataset
attr_accessor :db

# Set the method to call on identifiers going into the database for this dataset
attr_accessor :identifier_input_method

# Set the method to call on identifiers coming the database for this dataset
attr_accessor :identifier_output_method

# The hash of options for this dataset, keys are symbols.
attr_accessor :opts

# Whether to quote identifiers for this dataset
attr_writer :quote_identifiers

# The row_proc for this database, should be a Proc that takes
# a single hash argument and returns the object you want
# each to return.
attr_accessor :row_proc

# Constructs a new Dataset instance with an associated database and
# options. Datasets are usually constructed by invoking the Database#[] method:
#
# DB[:posts]
#
# Sequel::Dataset is an abstract class that is not useful by itself. Each
# database adaptor should provide a subclass of Sequel::Dataset, and have
# the Database#dataset method return an instance of that class.
def initialize(db, opts = nil)
@db = db
@quote_identifiers = db.quote_identifiers? if db.respond_to?(:quote_identifiers?)
@identifier_input_method = db.identifier_input_method if db.respond_to?(:identifier_input_method)
@identifier_output_method = db.identifier_output_method if db.respond_to?(:identifier_output_method)
@opts = opts || {}
@row_proc = nil
end

### Class Methods ###

# Setup mutation (e.g. filter!) methods. These operate the same as the
# non-! methods, but replace the options of the current dataset with the
# options of the resulting dataset.
def self.def_mutation_method(*meths)
meths.each do |meth|
class_eval("def #{meth}!(*args, &block); mutation_method(:#{meth}, *args, &block) end", __FILE__, __LINE__)
end
end

### Instance Methods ###

# Return the dataset as an aliased expression with the given alias. You can
# use this as a FROM or JOIN dataset, or as a column if this dataset
# returns a single row and column.
def as(aliaz)
::Sequel::SQL::AliasedExpression.new(self, aliaz)
end

# Returns a new clone of the dataset with with the given options merged.
# If the options changed include options in COLUMN_CHANGE_OPTS, the cached
# columns are deleted.
def clone(opts = {})
c = super()
c.opts = @opts.merge(opts)
c.instance_variable_set(:@columns, nil) if opts.keys.any?{|o| COLUMN_CHANGE_OPTS.include?(o)}
c
end

# Add a mutation method to this dataset instance.
def def_mutation_method(*meths)
meths.each do |meth|
instance_eval("def #{meth}!(*args, &block); mutation_method(:#{meth}, *args, &block) end", __FILE__, __LINE__)
end
end

# Yield a dataset for each server in the connection pool that is tied to that server.
# Intended for use in sharded environments where all servers need to be modified
# with the same data:
#
# DB[:configs].where(:key=>'setting').each_server{|ds| ds.update(:value=>'new_value')}
def each_server
db.servers.each{|s| yield server(s)}
end

# Returns a string representation of the dataset including the class name
# and the corresponding SQL select statement.
def inspect
"#<#{self.class}: #{sql.inspect}>"
end

# Returns a naked dataset clone - i.e. a dataset that returns records as
# hashes instead of calling the row proc.
def naked
ds = clone
ds.row_proc = nil
ds
end

# Set the server for this dataset to use. Used to pick a specific database
# shard to run a query against, or to override the default (which is SELECT uses
# :read_only database and all other queries use the :default database).
def server(servr)
clone(:server=>servr)
end

# Set the default values for insert and update statements. The values hash passed
# to insert or update are merged into this hash.
def set_defaults(hash)
clone(:defaults=>(@opts[:defaults]||{}).merge(hash))
end

# Set values that override hash arguments given to insert and update statements.
# This hash is merged into the hash provided to insert or update.
def set_overrides(hash)
clone(:overrides=>hash.merge(@opts[:overrides]||{}))
end

# Add the mutation methods via metaprogramming
def_mutation_method(*MUTATION_METHODS)

protected

# Return true if the dataset has a non-nil value for any key in opts.
def options_overlap(opts)
!(@opts.collect{|k,v| k unless v.nil?}.compact & opts).empty?
end

# Whether this dataset is a simple SELECT * FROM table.
def simple_select_all?
o = @opts.reject{|k,v| v.nil? || NON_SQL_OPTIONS.include?(k)}
o.length == 1 && (f = o[:from]) && f.length == 1 && f.first.is_a?(Symbol)
end

private

# Set the server to use to :default unless it is already set in the passed opts
def default_server_opts(opts)
{:server=>@opts[:server] || :default}.merge(opts)
end

# Modify the identifier returned from the database based on the
# identifier_output_method.
def input_identifier(v)
(i = identifier_input_method) ? v.to_s.send(i) : v.to_s
end

# Modify the receiver with the results of sending the meth, args, and block
# to the receiver and merging the options of the resulting dataset into
# the receiver's options.
def mutation_method(meth, *args, &block)
copy = send(meth, *args, &block)
@opts.merge!(copy.opts)
self
end

# Modify the identifier returned from the database based on the
# identifier_output_method.
def output_identifier(v)
v = 'untitled' if v == ''
(i = identifier_output_method) ? v.to_s.send(i).to_sym : v.to_sym
end

# This is run inside .all, after all of the records have been loaded
# via .each, but before any block passed to all is called. It is called with
# a single argument, an array of all returned records. Does nothing by
# default, added to make the model eager loading code simpler.
def post_load(all_records)
end
end

require(%w"query actions features graph prepared_statements misc mutation sql", 'dataset')
end

0 comments on commit c898e4d

Please sign in to comment.