[ANN] Active Record 0.9.1: More naturalness, new license

What's new in Active Record 0.9.1?

···

==================================

This is a minor update that brings more natural object-style accessing to has_and_belongs_to_many and has_many. It also changed the license to MIT license.

Download from http://activerecord.rubyonrails.org, talk on #rubyonrails (FreeNet).

* Added natural object-style assignment for has_and_belongs_to_many associations.
   Consider the following model:

     class Event < ActiveRecord::Base
       has_one_and_belongs_to_many :sponsors
     end

     class Sponsor < ActiveRecord::Base
       has_one_and_belongs_to_many :sponsors
     end

   Earlier, you'd have to use synthetic methods for creating
   associations between two objects of the above class:

     roskilde_festival.add_to_sponsors(carlsberg)
     roskilde_festival.remove_from_sponsors(carlsberg)

     nike.add_to_events(world_cup)
     nike.remove_from_events(world_cup)

   Now you can use regular array-styled methods:

     roskilde_festival.sponsors << carlsberg
     roskilde_festival.sponsors.delete(carlsberg)

     nike.events << world_cup
     nike.events.delete(world_cup)

* Added delete method for has_many associations. Using this will
   nullify an association between the has_many and the belonging
   object by setting the foreign key to null. Consider this model:

     class Post < ActiveRecord::Base
       has_many :comments
     end

     class Comment < ActiveRecord::Base
       belongs_to :post
     end

   You could do something like:

     funny_comment.has_post? # => true
     announcement.comments.delete(funny_comment)
     funny_comment.has_post? # => false

What about Active Record 1.0.0?

Active Record will be moving to promised land of 1.0.0 within a
reasonably short time frame. So if you have any wishes, comments, or
complaints, you'll want to voice them sooner rather than later. 1.0.0
won't mean the end of developement, of course, but it would be nice to
have a really solid release. So do speak forth.

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 as methods or ques 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

     class Account < ActiveRecord::Base
       after_find :eager_load, 'self.class.announce(#{id})'
     end

    Learn more in link:classes/ActiveRecord/Callbacks.html

* 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 on both a database and object level. The latter
is implemented
    by using Transaction::Simple

      # Just database transaction
      Account.transaction do
        david.withdrawal(100)
        mary.deposit(100)
      end

      # Database and object transaction
      Account.transaction(david, mary) 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 through simple adapters (~100 lines) with a
shared connector

     ActiveRecord::Base.establish_connection(:adapter => "sqlite",
:dbfile => "dbfile")

     ActiveRecord::Base.establish_connection(
       :adapter => "mysql",
       :host => "localhost",
       :username => "me",
       :password => "secret",
       :database => "activerecord"
     )

* Logging support for Log4r and Logger

      ActiveRecord::Base.logger = Logger.new(STDOUT)
      ActiveRecord::Base.logger = Log4r::Logger.new("Application Log")

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

--
David Heinemeier Hansson,
http://www.rubyonrails.org/ -- Web-application framework for Ruby
http://www.instiki.org/ -- A No-Step-Three Wiki in Ruby
http://www.basecamphq.com/ -- Web-based Project Management
http://www.loudthinking.com/ -- Broadcasting Brain
http://www.nextangle.com/ -- Development & Consulting Services