Permalink
Commits on Oct 1, 2009
  1. Bump version to 3.5.0

    committed Oct 1, 2009
Commits on Sep 29, 2009
  1. Merge CTI branch

    committed Sep 29, 2009
  2. Add class_table_inheritance plugin, supporting inheritance in the dat…

    …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).
    committed Sep 29, 2009
  3. Refactor Model#_save to call _insert and _update for easier overriding

    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).
    committed Sep 29, 2009
  4. Allow graphing to work on previously joined datasets

    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.
    committed Sep 29, 2009
Commits on Sep 28, 2009
  1. Update CHANGELOG

    committed Sep 28, 2009
Commits on Sep 27, 2009
  1. Align a documentation example with implementation

    There was an extra set of parentheses in the SQL produced by
    Sequel. Added it to the comment as well.
    masak committed Sep 27, 2009
Commits on Sep 23, 2009
Commits on Sep 22, 2009
  1. Support composite keys with set_primary_key when called with an array…

    … 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.
    committed Sep 22, 2009
  2. Fix select_more and order_more to not affect receiver

    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.
    committed Sep 22, 2009
  3. Initial support for composite keys in associations

    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.
    committed Sep 22, 2009
Commits on Sep 21, 2009
  1. Add the forced_encoding plugin for forcing encoding of strings for mo…

    …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
    committed Sep 21, 2009
  2. Support DataObjects 0.10 (previous DataObjects versions are now unsup…

    …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.
    committed Sep 21, 2009
Commits on Sep 17, 2009
Commits on Sep 16, 2009
  1. Add thread_local_timezones extension, and refactor timezone support i…

    …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.
    committed Sep 16, 2009
  2. Add named_timezones extension for using named timezones such as "Amer…

    …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.
    committed Sep 16, 2009
Commits on Sep 15, 2009
Commits on Sep 14, 2009
  1. Add Proc#sql_expr to the sql_expr extension

    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.
    committed Sep 14, 2009
  2. Replace Dataset#virtual_row_block_call with Sequel.virtual_row

    This removes the private virtual_row_block_call Dataset instance
    method, replacing with the Sequel.virtual_row module method.
    The API is slightly different, with virtual_row_block_call taking
    the block as a regular argument, and Sequel.virtual_row taking
    it as a block.  This allows the easier use of virtual rows
    outside of the select, order, and filter calls.  For example:
    
      net_benefit = Sequel.virtual_row{revenue > cost}
      good_employee = Sequel.virtual_row{num_commendations > 0}
      fire = ~net_benefit & ~good_employee
      demote = ~net_benefit & good_employee
      promote = net_benefit & good_employee
      DB[:employees].filter(fire).update(:employed=>false)
      DB[:employees].filter(demote).update(:rank=>:rank-1)
      DB[:employees].filter(promote).update(:rank=>:rank+1)
    
    There wasn't an easy way to do the above before, without
    creating the Sequel::SQL::VirtualRow instance manually.
    committed Sep 14, 2009
Commits on Sep 11, 2009
  1. added additional join example

    Michael Lang committed with Sep 11, 2009
  2. Add touch plugin, which adds Model#touch for updating an instance's t…

    …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.
    committed Sep 11, 2009
Commits on Sep 10, 2009
  1. Add sql_expr extension, which adds the sql_expr to all objects, givin…

    …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.
    committed Sep 10, 2009
  2. Be more explicit when checking types of ComplexExpression

    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.
    committed Sep 10, 2009
Commits on Sep 9, 2009
  1. Add active_model plugin, which gives Sequel::Model an ActiveModel com…

    …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.
    committed Sep 9, 2009
  2. Allow splitting of multiple result sets into separate arrays when usi…

    …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.
    committed Sep 9, 2009