Permalink
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.
    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.
    jeremyevans 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.
    jeremyevans 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.
    jeremyevans 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
    jeremyevans 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.
    jeremyevans 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.
    jeremyevans 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.
    jeremyevans 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.
    jeremyevans 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.
    jeremyevans committed Sep 14, 2009
Commits on Sep 11, 2009
  1. added additional join example

    Michael Lang committed with jeremyevans 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.
    jeremyevans 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.
    jeremyevans 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.
    jeremyevans 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.
    jeremyevans 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.
    jeremyevans committed Sep 9, 2009
  3. Make Dataset#insert_select return nil on PostgreSQL if disable_insert…

    …_returning is used
    
    Model object creation uses the results of insert_select if it
    returns a non-nil value.  Before insert_select on PostgreSQL
    would use an INSERT RETURNING query even if
    disable_insert_returning was used.  This makes it return
    immediately without executing a query if
    disable_insert_returning was used, so model object creation
    will default to using a plain insert query.
    jeremyevans committed Sep 9, 2009
Commits on Sep 8, 2009
  1. Modify adapter and integration tests to use cspecify for checked tests

    This expands the use of cspecify to work on sqlite, mysql, and
    postgres on the native, do, and jdbc adapters.  It also works
    with h2 and amalgalite.  A future commit will handle cspecify
    changes for Microsoft SQL Server on odbc and jdbc.
    
    This expands the use of cspecify so it works in the adapter
    tests, and also expands the API in the integration tests so
    it accepts procs.
    
    With these changes, the specs will show expected failures as
    pending.  In most cases, these failures are due to limitations
    either in the database itself or in the underlying database
    driver that Sequel uses.
    jeremyevans committed Sep 8, 2009
  2. Add association_dependencies plugin, for deleting, destroying, or nul…

    …lifying associated objects when destroying a model object
    
    The AssociationDependencies plugin allows you do easily set up before
    and/or after destroy hooks for destroying, deleting, or nullifying
    associated model objects.  The following association types support the
    following dependency actions:
    
    * :many_to_many - :nullify (removes all related entries in join table)
    * :many_to_one - :delete, :destroy
    * :one_to_many - :delete, :destroy, :nullify (sets foreign key to
      NULL for all associated objects)
    
    This plugin works directly with the association datasets and does not
    use any cached association values.  The :delete action will delete all
    associated objects from the database in a single SQL call.
    The :destroy action will load each associated object from the database
    and call the destroy method on it.
    
    To set up an association dependency, you must provide a hash with
    association name symbols and dependency action values.  You can
    provide the hash to the plugin call itself or to the
    add_association_dependencies method:
    
      Business.plugin :association_dependencies, :address=>delete
      # or:
      Artist.plugin :association_dependencies
      Artist.add_association_dependencies :albums=>:destroy, :reviews=>:delete, :tags=>:nullif
    jeremyevans committed Sep 8, 2009
Commits on Sep 7, 2009
  1. Add :validate association option, set to false to not validate when i…

    …mplicitly saving associated objects
    
    There isn't a lot of implicit saving in Sequel's association methods,
    but this gives the user the control over validation when those
    methods implicitly save the object.
    
    In addition to the regular association methods, the nested_attributes
    plugin was also updated to respect the :validate_association option.
    It was also modified to not validate associated objects twice, once
    when the parent object was validated and again when the associated
    object was saved.  Additionally, if you pass :validate=>false to
    the save method when saving the parent object, it will not longer
    attempt to validate associated objects when saving them.
    jeremyevans committed Sep 7, 2009