keep on that great job! joaquin
2009/10/1 Jeremy Evans <[email protected]> > > 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 > > > -- www.least-significant-bit.com --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---
