From: David Heinemeier Hansson Date: 2004-06-03T00:09:23+09:00 Subject: [ANN] Active Record 0.8.0: Transaction that! What's new in Active Record 0.8.0? ================================== Transactions are here! This was the last major feature destined for inclusion before version 1.0 is released. So I was happy to see that it could be done in just a handful of lines. Transactions are guarded by blocks and work as such: Account.transaction do david.withdrawal(100) mary.deposit(100) end This example will only take money from David and give to Mary if neither withdrawal nor deposit raises an exception. Exceptions (and returning false in the last statement of the transaction block) will force a ROLLBACK that returns the database to the state before the transaction was begun. Be aware, though, that the objects will not have their instance data returned to their state before the transaction started. You�ll have to deal with that yourself (just as in the case of Validations). Also have in mind that exceptions thrown within a transaction block will be propagated (after triggering the ROLLBACK), so you should be ready to catch those in your application code. Also in 0.8.0: * Changed Base.find to also accept either a list (1, 5, 6) or an array of ids ([5, 7]) as parameter and then return an array of objects instead of just an object * Fixed method has_collection? for has_and_belongs_to_many macro to behave as a collection, not an association * Fixed SQLite adapter so empty or nil values in columns of datetime, date, or time type aren't treated as current time [Spotted by Gavin Sinclair] Get the release and read more at http://activerecord.rubyonrails.org/ Call for help! ============== Do you have working knowledge with and access to either Oracle, ODBC, Sybase, or DB2, I'd be really grateful if you would consider writing an adapter for Active Record. Adapters are usually just around 100 lines of code. You'll have three examples to look at, a well-specified interface[1], and almost 100 test cases to make it real easy. Luke Holden reports that he spent just a few hours getting SQLite and PostgreSQL adapters working. [1] http://ar.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/ AbstractAdapter.html Active Record -- Object-relation mapping put on rails ===================================================== Active Record connects business objects and database tables to create a persistable domain model where logic and data is presented in one wrapping. It's an implementation of the object-relational mapping (ORM) pattern by the same name as described by Martin Fowler: "An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data." Active Records main contribution to the pattern is to relieve the original of two stunting problems: lack of associations and inheritance. By adding a simple domain language-like set of macros to describe the former and integrating the Single Table Inheritance pattern for the latter, Active Record narrows the gap of functionality between the data mapper and active record approach. A short rundown of the major features: * Automated mapping between classes and tables, attributes and columns. class Product < ActiveRecord::Base; end ...is automatically mapped to the table named "products", such as: CREATE TABLE products ( id int(11) NOT NULL auto_increment, name varchar(255), PRIMARY KEY (`id`) ); ...which again gives Product#name and Product#name=(new_name) * Associations between objects controlled by simple meta-programming macros. class Firm < ActiveRecord::Base has_many :clients has_one :account belong_to :conglomorate end * Aggregations of value objects controlled by simple meta-programming macros. class Account < ActiveRecord::Base composed_of :balance, :class_name => "Money", :mapping => %w(balance amount) composed_of :address, :mapping => [%w(address_street street), %w(address_city city)] end * Validation rules that can differ for new or existing objects. class Post < ActiveRecord::Base def validate # validates on both creates and updates errors.add_on_empty "title" end def validate_on_update errors.add_on_empty "password" end end * Callbacks on the entire lifecycle (instantiation, saving, destroying, validating, etc). class Person < ActiveRecord::Base def before_destroy # is called just before Person#destroy CreditCard.find(credit_card_id).destroy end end * Observers for the entire lifecycle class CommentObserver < ActiveRecord::Observer def after_create(comment) # is called just after Comment#save NotificationService.send_email("david@loudthinking.com", comment) end end * Inheritance hierarchies class Company < ActiveRecord::Base; end class Firm < Company; end class Client < Company; end class PriorityClient < Client; end * Transaction support Account.transaction do david.withdrawal(100) mary.deposit(100) end * Direct manipulation (instead of service invocation) So instead of (Hibernate example): long pkId = 1234; DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) ); // something interesting involving a cat... sess.save(cat); sess.flush(); // force the SQL INSERT Active Record lets you: pkId = 1234 cat = Cat.find(pkId) # something even more interesting involving a the same cat... cat.save * Database abstraction for three different engines through simple adapters ActiveRecord::Base.mysql_connection(host, username, pass, database) ActiveRecord::Base.postgresql_connection(host, table, username, pass, database) ActiveRecord::Base.sqlite_connection(dbfile) * Logging support for Log4r and Logger == Philosophy Active Record attempts to provide a coherent wrapping for the inconvenience that is object-relational mapping. The prime directive for this mapping has been to minimize the amount of code needed to built a real-world domain model. This is made possible by relying on a number of conventions that make it easy for Active Record to infer complex relations and structures from a minimal amount of explicit direction. Convention over Configuration: * No XML-files! * Lots of reflection and run-time extension * Magic is not inherently a bad word Admit the Database: * Lets you drop down to SQL for odd cases and performance * Doesn't attempt to duplicate or replace data definitions