…abase using a table-per-model-class approach
The class_table_inheritance plugin allows you to model inheritance
in the database using a table per model class in the hierarchy, with
only columns unique to that model class (or subclass hierarchy) being
stored in the related table. For example, with this hierarchy:
Employee
/ \
Staff Manager
|
Executive
the following database schema may be used (table - columns):
* employees - id, name, kind
* staff - id, manager_id
* managers - id, num_staff
* executives - id, num_managers
The class_table_inheritance plugin assumes that the main table
(e.g. employees) has a primary key field (usually autoincrementing),
and all other tables have a foreign key of the same name that points
to the same key in their superclass's table. For example:
* employees.id - primary key, autoincrementing
* staff.id - foreign key referencing employees(id)
* managers.id - foreign key referencing employees(id)
* executives.id - foreign key referencing managers(id)
When using the class_table_inheritance plugin, subclasses use joined
datasets:
Employee.dataset.sql # SELECT * FROM employees
Manager.dataset.sql # SELECT * FROM employees
# INNER JOIN managers USING (id)
Executive.dataset.sql # SELECT * FROM employees
# INNER JOIN managers USING (id)
# INNER JOIN executives USING (id)
This allows Executive.all to return instances with all attributes
loaded. The plugin overrides the deleting, inserting, and updating
in the model to work with multiple tables, by handling each table
individually.
This plugin allows the use of a :key option when loading to mark
a column holding a class name. This allows methods on the
superclass to return instances of specific subclasses.
This plugin also requires the lazy_attributes plugin and uses it to
return subclass specific attributes that would not be loaded
when calling superclass methods (since those wouldn't join
to the subclass tables). For example:
a = Employee.all # [<#Staff>, <#Manager>, <#Executive>]
a.first.values # {:id=>1, name=>'S', :kind=>'Staff'}
a.first.manager_id # Loads the manager_id attribute from the
# database
This plugin is not supported on H2 or MSSQL, because neither
support JOIN table USING (column). H2 support is possible in
the future using a NATURAL JOIN, and while MSSQL support is
theoretically possible using a JOIN ON, the resulting model
wouldn't be very usable because primary key lookups would
need to be qualified, which Sequel doesn't do.
Note that this plugin isn't fully functional on SQLite due to
a bug I discovered in their JOIN USING implementation (see
http://www.sqlite.org/src/tktview/3338b3fa19ac4abee6c475126a2e6d9d61f26ab1).

This is useful if the model is backed by a joined dataset, but you
want to allow creating and updating such datasets by just overriding
a couple small methods instead of copying and modifying a much
larger method (that is prone to breakage in later versions).

This changes Dataset#graph to recognize when you are graphing a
previously joined (but not graphed) dataset. If Dataset#graph
is called on a joined dataset that hasn't yet been graphed, it
wraps the dataset in a subselect so all columns the dataset
returns can be referred to with the same qualifier. The qualifier
will be the new :from_self_alias option if provided, or the
first source if not.
For this to work correctly, Dataset#from_self was changed to
not modify any of the existing graphing options.
This is mostly useful when eagerly graphing models that are backed
by joined datasets, so model associations for those models should
now work correctly.
This changes some of the SQL the spec produces, and has a chance
of breaking backwards compatibility for corner cases, such as
graphing an already joined dataset, and filtering later using
qualified identifiers that refer to tables that weren't the first
table but were joined before graphing.

… of multiple symbols
Before, this was only partially supported. It broke the Model.[]
optimization if called with an array of multiple symbols.
The specs allowed the set_primary_key to work with both multiple
symbol arguments as well as single array of symbols argument, so
this just fixes the optimization for that case.

This is my fault according to git blame, and it's been broken
for a while. Thanks to Tamas Denes for notifying me about this.
Amazingly, the many_to_many eager loading via .eager support
relied on this bug, so I fixed that as well.

This supports composite keys for association dataset methods as well
as the setter methods and the add_/remove_/remove_all methods.
Eager loading with composite keys is not yet supported, but that
will come next, as will documentation updates and later updates
to some association related plugins.

…dels (requires ruby 1.9)
The ForceEncoding plugin allows you force specific encodings for all
strings that are used by the model. When model instances are loaded
from the database, all values in the hash that are strings are
forced to the given encoding. Whenever you update a model column
attribute, the resulting value is forced to a given encoding if the
value is a string. There are two ways to specify the encoding. You
can either do so in the plugin call itself, or via the
forced_encoding class accessor:
class Album < Sequel::Model
plugin :force_encoding, 'UTF-8'
# or
plugin :force_encoding
self.forced_encoding = 'UTF-8'
end

…ported)
Anyone using the dataobjects adapter must upgrade to DataObjects 0.10
when upgrading to this commit. Previous versions of Sequel do not
support DataObjects 0.10, and with this commit, Sequel will no longer
support DataObjects versions before 0.10.

…n modules
The thread_local_timezones extension allows you to set a per-thread
timezone that will override the default global timezone while the
thread is executing. The main use case is for web applications that
execute each request in its own thread, and want to set the timezones
based on the request. The most common example is having the database
always store time in UTC, but have the application deal with the
timezone of the current user. That can be done with:
Sequel.database_timezone = :utc
# In each thread:
Sequel.thread_application_timezone = current_user.timezone
This extension is designed to work with the named_timezones
extension.
This extension adds the thread_application_timezone=,
thread_database_timezone=, and thread_typecast_timezone= methods to
the Sequel module. It overrides the application_timezone,
database_timezone, and typecast_timezone methods to check the related
thread local timezone first, and use it if present. If the related
thread local timezone is not present, it falls back to the default
global timezone.
There is one special case of note. If you have a default global
timezone and you want to have a nil thread local timezone, you have
to set the thread local value to :nil instead of nil:
Sequel.application_timezone = :utc
Sequel.thread_application_timezone = nil
Sequel.application_timezone # => :utc
Sequel.thread_application_timezone = :nil
Sequel.application_timezone # => nil
In order to implement this more easily, and just for generally better
design, the timezone support in the main Sequel module was separated
into the Sequel::Timezones module, which extends the main Sequel
module. The named_timezones extension now uses a NamedTimezones
module which also extends Sequel. And the new thread_local_timezones
extension adds a ThreadLocalTimezones which also extends Sequel.
A new private convert_timezone_setter_arg method was added to
ease implementation and allow the thread_local_timezones and
named_timezones extensions to work better together.

…ica/Los_Angeles" using TZInfo
By default, Sequel only supports UTC, local, or no timezone
conversion. This extension allows you use named timezones via
the TZInfo library (and requires tzinfo).
It forces the use of DateTime as Sequel's datetime_class, since
ruby's Time class doesn't support timezones other than local
and UTC.
This allows you to either pass strings or TZInfo::Timezone
instance to Sequel.database_timezone=, application_timezone=, and
typecast_timezone=. If a string is passed, it is converted to a
TZInfo::Timezone using TZInfo::Timezone.get.
Let's say you have the database server in New York and the
application server in Los Angeles. For historical reasons, data
is stored in local New York time, but the application server only
services clients in Los Angeles, so you want to use New York
time in the database and Los Angeles time in the application. This
is easily done via:
Sequel.database_timezone = 'America/New_York'
Sequel.application_timezone = 'America/Los_Angeles'
Then, before data is stored in the database, it is converted to New
York time. When data is retrieved from the database, it is
converted to Los Angeles time.
This adds some new private Sequel module methods, to make the
extension implementation simpler. The extension implementation
is fairly simple, but still much messier than should be necessary,
because TZInfo, even though it is a timezone library, doesn't respect
timezone offsets on DateTime objects, nor does it set the correct
timezone offset when converting DateTime objects to other timezones.
I'm not sure what the reason is behind it, but it makes TZInfo
much more difficult to use.

This allows even easier virtual row block calls:
net_benefit = proc{revenue > cost}.sql_expr
good_employee = proc{num_commendations > 0}.sql_expr
It's similar to using Sequel.virtual_row, but it also makes sure the
object returned is an SQL::Expression, so you can do:
proc{[[a, b], [a, c]]}.sql_expr | :x
# (((a = b) AND (a = c)) OR x)
If the object returned by the proc is a Hash or array of two element
arrays, it is converted to a BooleanExpression. Otherwise, the
object is wrapped in a SQL::GenericComplexExpression unless the
object is already an SQL::Expression.

…imestamp, as well as touching associations when an instance is updated or destroyed
The touch plugin adds a touch method to model instances, which saves
the object with a modified timestamp. By default, it uses the
:updated_at column, but you can set which column to use.
It also supports touching of associations, so that when the current
model object is updated or destroyed, the associated rows in the
database can have their modified timestamp updated to the current
timestamp.
Since the instance touch method works on model instances,
it uses Time.now for the timestamp. The association touching works
on datasets, so it updates all related rows in a single query, using
the SQL standard CURRENT_TIMESTAMP. Both of these can be overridden
easily if necessary.

…g them easy access to Sequel's DSL
The sql_expr extension adds the sql_expr method to every object, which
returns an object that works nicely with Sequel's DSL. This is
best shown by example:
1.sql_expr < :a # 1 < a
false.sql_expr & :a # FALSE AND a
true.sql_expr | :a # TRUE OR a
~nil.sql_expr # NOT NULL
"a".sql_expr + "b" # 'a' || 'b'
This isn't possible to do in Sequel by default. I generally refrain
from modifying the core classes unless necessary, which is why this
is an extension instead of being included in Sequel itself.
This extension also allows you to do:
o = Object.new
o.sql_expr < :a
Of course, for this to work, you'll need to add your own extension
which literalizes the object properly. You'll need to modify
Dataset#literal_other to recognize the object and literalize it
correctly.
I'm generally against parametized specs, but I did use them in a
couple instances here, since the specs are small and the behavior
is identical.

This is needed by an upcoming commit. It changes the check for
bad ComplexExpression subclasses to reference the subclasses
disallowed explicitly, instead of listing the allowed subclass and
saying all other subclasses are disallowed. This allows the
creation of a generic ComplexExpression subclass that will not
be disallowed. It also simplifies the code.
While here, add some RDoc documentation and reorgnize and
alphabetize some code.

…pliant API, passes the ActiveModel::Lint tests
Honestly, I'm not sure how complete the ActiveModel::Lint tests
are. This fully supports the most current version on rails/rails
at github. I remember Yehuda mentioning that ActiveModel naming
was necessary for compliance, but the Lint tests don't currently
include that.
The active_model plugin should be considered a work in progress
until the ActiveModel::Lint tests have been finalized.

…ng multiple statements in a single query in the native MySQL adapter
This allows you to use Dataset#split_multiple_result_sets when using
the native MySQL adapter to have each yield arrays of rows, one
per statement, instead of yielding hashes for all result sets.
This makes it possible to determine which statements yielded which
result sets.
Because of the way Sequel is architected, you cannot graph a
dataset that splits multiple statements. There are no plans to
rearchitect Sequel to accomodate that.
Having each yield arrays of hashes instead of hashes breaks
numerous internal assumptions held by Sequel. For example,
row_proc assigned to split_multiple_result_sets datasets have
to be recoded to accept arrays of hashes instead of plain hashes.
Note that when you split a multiple result set dataset, it will
modify an existing row_proc for you, so this particular issue
won't be a problem. However, you should be aware that having
each returning arrays instead of hash may cause unexpected breakage
elsewhere.
While here, fix another commands out of sync bug that occured when
multiple statements. Before, if you executed a multiple result set
query and returned before getting all of the results (say by using
Dataset#first), the next query would get a commands out of sync
message. Now, an ensure block inside of Database#execute will
get all remaining result sets so that should no longer happen.
This was found while writing tests for the split multiple
result set code.