Once again, a superb job Jeremy.

Thank you.

Luke

Jeremy Evans wrote:
> Sequel 3.5.0 has been released and should be available on the gem
> mirrors.  The 3.5.0 release adds numerous improvements:
>
> New Plugins
> -----------
>
> * A class_table_inheritance plugin has been added, supporting model
>    inheritance in the database using a table-per-model-class approach.
>    Each table stores only attributes unique to that model or subclass
>    hierarchy.
>
>    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 deleting, inserting, and updating
>    in the model to work with multiple tables, by handling each table
>    individually.
>
>    This plugin allows and encourages the use of a :key option to mark
>    a column holding the class name.  This allows methods on the
>    superclass to return instances of specific subclasses.
>
>      a = Employee.all # [<#Staff>,<#Manager>,<#Executive>]
>
>    This plugin requires the lazy_attributes plugin and uses it to
>    handle subclass specific attributes that would not be loaded
>    when calling superclass methods (since those wouldn't join
>    to the subclass tables).  For example:
>
>      a.first.values # {:id=>1, name=>'S', :kind=>'Staff'}
>      a.first.manager_id # Loads the manager_id attribute from the
>                         # database
>
>    The class_table_inheritance plugin requires JOIN USING and
>    therefore is not supported on H2 or Microsoft SQL Server, which do
>    not support that SQL-92 feature.
>
> * An associations_dependencies plugin was added for deleting,
>    destroying, or nullifying associated objects when destroying a
>    model object.  This just gives an easy way to add the necessary
>    before and after destroy hooks.  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.
>
>    The plugin call takes a hash of association symbol keys and
>    dependency action symbol values.  Alternatively, you can specify
>    additional dependencies later using add_association_dependencies:
>
>      Business.plugin :association_dependencies, :address=>:delete
>      # or:
>      Artist.plugin :association_dependencies
>      Artist.add_association_dependencies :albums=>:destroy,
>        :reviews=>:delete, :tags=>:nullify
>
> * A force_encoding plugin was added that forces the encoding of
>    strings used in model instances.  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
>
>    This plugin only works on ruby 1.9, since strings don't have
>    encodings in 1.8.
>
> * A typecast_on_load plugin was added, for fixing bad database
>    typecasting when loading model objects. Most of Sequel's database
>    adapters don't have complete control over typecasting, and may
>    return columns that aren't typecast correctly (with correct being
>    defined as how the model object would typecast the same column
>    values).
>
>    This plugin modifies Model.load to call the setter methods (which
>    typecast by default) for all columns given.  You can either specify
>    the columns to typecast on load in the plugin call itself, or
>    afterwards using add_typecast_on_load_columns:
>
>      Album.plugin :typecast_on_load, :release_date, :record_date
>      # or:
>      Album.plugin :typecast_on_load
>      Album.add_typecast_on_load_columns :release_date, :record_date
>
>    If the database returns release_date and record_date columns as
>    strings instead of dates, this will ensure that if you access those
>    columns through the model object, you'll get Date objects instead of
>    strings.
>
> * A touch plugin was added, which adds Model#touch for updating an
>    instance's timestamp, as well as touching associations when an
>    instance is updated or destroyed.
>
>    The Model#touch instance method 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.  Example:
>
>      class Album<  Sequel::Model
>        plugin :touch, :column=>:modified_on, :associations=>:artist
>      end
>
> * A subclasses plugin was added, for recording all of a models
>    subclasses and descendent classes.  Direct subclasses are available
>    via the subclasses method, and all descendent classes are available
>    via the descendents method:
>
>      c = Class.new(Sequel::Model)
>      c.plugin :subclasses
>      sc1 = Class.new(c)
>      sc2 = Class.new(c)
>      ssc1 = Class.new(sc1)
>      c.subclasses    # [sc1, sc2]
>      sc1.subclasses  # [ssc1]
>      sc2.subclasses  # []
>      ssc1.subclasses # []
>      c.descendents   # [sc1, ssc1, sc2]
>
>    The main use case for this is if you want to modify all models
>    after the model subclasses have been created.  Since mutable
>    options are copied when subclassing, modifying parent classes
>    does not affect current subclasses, only future ones.  The
>    subclasses plugin allows you get all subclasses so that you can
>    easily modify them.  The plugin only records subclasses
>    created after the plugin call, though.
>
> * An active_model plugin was added, giving Sequel::Model an
>    ActiveModel complaint API, in so much as it passes the
>    ActiveModel::Lint tests.
>
> New Extensions
> --------------
>
> * A named_timezones extension was added, allowing you to use named
>    timezones such as "America/Los_Angeles" (the default Sequel
>    timezone support only supports UTC or local time). This extension
>    requires TZInfo.  It also sets the Sequel.datetime_class to
>    DateTime, so database timestamps will be returned as DateTime
>    instances instead of Time instances.  This is because ruby's
>    Time class doesn't support timezones other than UTC and local time.
>
>    This plugin allows you to pass either 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 timestamps are stored in the database, they are
>    converted to New York time.  When timestamps are retrieved from the
>    database, they are converted to Los Angeles time.
>
> * A thread_local_timezones extension was added.  This 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.
>
> * An sql_expr extension was added that adds .sql_expr methods to
>    all objects, giving them easy access to Sequel's DSL:
>
>      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'
>
>    Proc#sql_expr uses a virtual row:
>
>      proc{[[a, b], [a, c]]}.sql_expr | :x
>      # (((a = b) AND (a = c)) OR x)
>
> * A looser_typecasting extension was added, for using to_f and to_i
>    instead of the more strict Kernel.Float and Kernel.Integer when
>    typecasting floats and integers. To use it, you should extend the
>    database with the Sequel::LooserTypecasting module after loading
>    the extension:
>
>      Sequel.extension :looser_typecasting
>      DB.extend(Sequel::LooserTypecasting)
>
>    This makes the behavior more like ActiveRecord:
>
>      a = Artist.new(:num_albums=>'a')
>      a.num_albums # =>  0
>
> Other New Features
> ------------------
>
> * Associations now support composite keys.  All of the :*key options
>    options now accept arrays of symbols instead of plain symbols.
>    Example:
>
>      Artist.primary_key # [:name, :city]
>      Album.many_to_one :artist, :key=>[:artist_name, :artist_city]
>      Artist.one_to_many :albums, :key=>[:artist_name, :artist_city]
>
>    All association types are supported, including the built-in
>    many_to_many association and the many_through_many plugin.  Both
>    methods of eager loading work with composite keys for all
>    association types. Setter and add/remove/remove_all methods
>    also now work with composite keys.
>
> * Associations now respect a :validate option, which can be set to
>    false to not validate when implicitly 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
>    the association methods implicitly save an 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.
>
> * Dataset#insert and #insert_sql were refactored and now support the
>    following API:
>
>    * No arguments - Treat as a single empty hash argument
>    * Single argument:
>      * Hash - Use keys as columns and values as values
>      * Array - Use as values, without specifying columns
>      * Dataset - Use a subselect, without specifying columns
>      * LiteralString - Use as the values
>    * 2 arguments:
>      * Array, Array - Use first array as keys, second as values
>      * Array, Dataset - Use a subselect, with the array as columns
>      * Array, LiteralString - Use LiteralString as the values, with
>        the array as the columns
>    * Anything else: Treat all given values an an array of values
>
> * Graphing now works with previously joined datasets.  The main use
>    case of this is when eagerly loading (via eager_graph) model
>    associations for models backed by joined datasets, such as those
>    created by the class_table_inheritance plugin.
>
> * Sequel.virtual_row was added allowing you to easily use the
>    VirtualRow support outside of select, order, and filter calls:
>
>      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)
>
> * When Sequel wraps exception in its own classes (to provide database
>    independence), it now keeps the wrapped exception available in
>    a wrapped_exception accessor.  This allows you to more easily
>    determine the wrapped exception class, without resorting to parsing
>    the exception message.
>
>      begin
>        DB.run('...')
>      rescue Sequel::DatabaseError =>  e
>        case e.wrapped_exception
>        when Mysql::Error
>          ...
>        when PGError
>          ...
>        end
>      end
>
> * The MySQL adapter now supports a Dataset#split_multiple_result_sets
>    method that yields arrays of rows (one per result set), instead of
>    rows.  This allows you to submit multiple statements at the same
>    time (or call a stored procedure that returns multiple result
>    sets), and know which rows are related to which result sets.
>
>    This violates a lot of Sequel's internal assumptions and should be
>    used with care.  Existing row_procs are modified to work correctly,
>    but graphing will not work on these datasets.
>
> * The ADO adapter now accepts a :conn_string option and uses that
>    as the full ADO connection string.  This can be used to connect to
>    any datasource ADO supports, such as Microsoft Excel.
>
> * The Microsoft SQL Server shared adapter now supports a
>    Database#server_version method.
>
> * The Microsoft SQL Server shared adapter now supports updating and
>    deleting from joined datasets.
>
> * The Microsoft SQL Server shared adapter now supports a
>    Dataset#output method that uses the OUTPUT clause.
>
> * Model#_save now calls either Model#_insert or Model#_update for
>    inserting/updating the row in the database.  This allows for easier
>    overriding when you want to allow creating and updating model
>    objects backed by a joined dataset.
>
> * Dataset#graph now takes a :from_self_alias option specifying the
>    alias to use for the subselect created if the receiver is a joined
>    but not yet graphed dataset.  It defaults to the first source table
>    in the receiver.
>
> Other Improvements
> ------------------
>
> * Typecasting model attributes is now done before checking existing
>    values, instead of after.  Before, the code for the model attribute
>    setters would compare the given value to the existing entry.  If it
>    didn't match, the value was typecasted and then assigned.  That led
>    to the following situation:
>
>      a = Album[1]
>      a.num_tracks # =>  10
>      params # =>  {'num_tracks'=>'10'}
>      a.set(params)
>      a.changed_columns # =>  [:num_tracks]
>
>    The new behavior typecasts the value first, and only sets it and
>    records the column as changed if it doesn't match the typecasted
>    value.
>
> * Model#modified? is now always true if the record is new.  modified?
>    indicates the instance's status relative to the database, and since
>    a new object is not yet in the database, and saving the object
>    would add it, the object is considered modified. A consequence of
>    this is that Model#save_changes now always saves if the object is
>    new.
>
>    If you want to check if there were changes to columns since the
>    object was first initialized, you should use
>    !changed_columns.empty?, which was the historical way to handle
>    the situation.
>
> * The DataObjects (do) adpater now supports DataObjects 0.10.
>
> * Dataset#select_more and Dataset#order_more no longer affect the
>    receiver.  They are supposed to just return a modified copy of the
>    receiver instead of modifying the receiver itself.  For a few
>    versions they have been broken in that they modified the receiver
>    in addition to returning a modified copy.
>
> * Performance was increased for execution of prepared statements
>    with multiple bound variables on MySQL.
>
> * On MySQL, database errors raised when preparing statements or
>    setting bound variable values are now caught and raised as
>    Sequel::DatabaseErrors.
>
> * On MySQL, more types of disconnection errors are detected.
>
> * When altering columns in MySQL, options such as :unsigned,
>    :elements, and :size that are given in the call are now respected.
>
> * MySQL enum defaults are now handled correctly in the schema dumper.
>
> * The schema dumper no longer attempts to use unparseable defaults
>    as literals on MySQL, since MySQL does not provide defaults as
>    valid literals.
>
> * The emulated offset support in the shared Microsoft SQL Server
>    adapter now works better with model classes (or any datasets with
>    row_procs).
>
> * Microsoft SQL Server now supports using the WITH clause in delete,
>    update, and insert calls.
>
> * Parsed indexes when connecting to Microsoft SQL Server via JDBC no
>    longer include primary key indexes.
>
> * Dataset#insert_select now returns nil if disable_insert_returning
>    is used in the shared PostgreSQL adapter.  This makes it work as
>    expected with model object creation.
>
> * Calling Model.set_primary_key with an array of symbols to set
>    a composite primary key is now supported.  You can also provide
>    multiple symbol arguments to do the same thing.  Before, specifying
>    an array of symbols broke the Model.[] optimization.
>
> * Literalization of timezones in timestamps now works correctly on
>    Oracle.
>
> * __FILE__ and __LINE__ are now used everywhere that eval is called
>    with a string, which makes for better backtraces.
>
> * The native MySQL adapter now correctly handles returning before
>    yielding all result sets.  Previously, this caused a commands out
>    of sync error.
>
> * Table names in common table expressions are now quoted.
>
> * The Oracle adapter's Dataset#except now accepts a hash, giving it
>    the same API as the default Dataset#except.
>
> * When connecting to Microsoft SQL Server via ADO, allow
>    Dataset#insert to take multiple arguments.
>
> * Fractional timestamps are no longer used on ODBC.
>
> * Schema parsing now works on MSSQL when the database is set to not
>    quote identifiers.
>
> * Timezone offsets are no longer used on Microsoft SQL Server, since
>    they only work for the datetimeoffset type.
>
> * Only 3 fractional digits in timestamps are used in Microsoft SQL
>    Server, since an error is raised if the use the datetime type
>    with more than that.
>
> * The integration test suite now has guards for expected failures
>    when run on known databases.  Expected failures are marked as
>    pending.
>
> Backwards Compatibility
> -----------------------
>
> * Graphing to an previously joined (but not graphed) dataset now
>    causes the receiver to be wrapped in a subselect, so if you
>    graph a dataset to a previously joined dataset, and then filter
>    the dataset referring to tables that were in the joined dataset
>    (other than the first table), the SQL produced will probably no
>    longer be valid.  You should either filter the dataset before
>    graphing or use the name of the first source of the joined
>    dataset (which is what the subselected is aliased to) if filtering
>    afterward.
>
>    In certain cases, this change can cause tables to be aliased
>    differently, so if you were graphing previously joined datasets
>    and then filtering using the automatically generated aliases, you
>    might need to modify your code.
>
> * The DataObjects (do) adpater no longer supports DataObjects 0.9.x.
>
> * The Dataset#virtual_row_block_call private instance method has
>    been removed.
>
> * Sequel's timezone support was significantly refactored, so if you
>    had any custom modifications to the timezone support, they might
>    need to be refactored as well.
>
> * The SQL generation code was significantly refactored, so if you
>    had any custom modifications in that area, you might need to
>    refactor as well.
>
> Thanks,
> Jeremy
> >
>    

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sequel-talk" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/sequel-talk?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to