Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > [ANN] Active Record 0.9.0: Thread safety, speed, naturalness

Reply
Thread Tools

[ANN] Active Record 0.9.0: Thread safety, speed, naturalness

 
 
David Heinemeier Hansson
Guest
Posts: n/a
 
      07-23-2004
What's new in Active Record 0.9.0?
==================================

Itís been a while, but the wait will be worth it. This massive update
includes thread safety, 400% speed increase (on a 100 objects loop),
better callback definitions, natural association assignment, hashes and
arrays in YAML storage, and much, much more.

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


* Active Record is now thread safe! (So you can use it with Cerise and
WEBrick
applications) [Implementation idea by Michael Neumann, debugging
assistance
by Jamis Buck]

* Improved performance by roughly 400% on a basic test case of pulling
100
records and querying one attribute. This brings the tax for using
Active
Record instead of "riding on the metal" (using MySQL-ruby C-driver
directly)
down to ~50%. Done by doing lazy type conversions and caching column
information on the class-level.

* Added callback objects and procs as options for implementing the
target for
callback macros.

* Added "counter_cache" option to belongs_to that automates the usage of
increment_counter and decrement_counter. Consider:

class Post < ActiveRecord::Base
has_many :comments
end

class Comment < ActiveRecord::Base
belongs_to ost
end

Iterating over 100 posts like this:

<% for post in @posts %>
<%= post.title %> has <%= post.comments_count %> comments
<% end %>

Will generate 100 SQL count queries -- one for each call to
post.comments_count. If you instead add a "comments_count" int column
to the posts table and rewrite the comments association macro with:

class Comment < ActiveRecord::Base
belongs_to ost, :counter_cache => true
end

Those 100 SQL count queries will be reduced to zero. Beware that
counter
caching is only appropriate for objects that begin life
with the object it's specified to belong with and is destroyed like
that as
well. Typically objects where you would also specify :dependent =>
true. If
your objects switch from one belonging to another (like a post that
can be
move from one category to another), you'll have to manage the counter
yourself.

* Added natural object-style assignment for has_one and belongs_to
associations. Consider the following model:

class Project < ActiveRecord::Base
has_one :manager
end

class Manager < ActiveRecord::Base
belongs_to roject
end

Earlier, assignments would work like following regardless of which
way the
assignment told the best story:

active_record.manager_id = david.id

Now you can do it either from the belonging side:

david.project = active_record

...or from the having side:

active_record.manager = david

If the assignment happens from the having side, the assigned object is
automatically saved. So in the example above, the project_id
attribute on
david would be set to the id of active_record, then david would be
saved.

* Added natural object-style assignment for has_many associations
[Florian
Weber]. Consider the following model:

class Project < ActiveRecord::Base
has_many :milestones
end

class Milestone < ActiveRecord::Base
belongs_to roject
end

Earlier, assignments would work like following regardless of which
way the
assignment told the best story:

deadline.project_id = active_record.id

Now you can do it either from the belonging side:

deadline.project = active_record

...or from the having side:

active_record.milestones << deadline

The milestone is automatically saved with the new foreign key.

* API CHANGE: Attributes for text (or blob or similar) columns will now
have
unknown classes stored using YAML instead of using to_s. (Known
classes that
won't be yamelized are: String, NilClass, TrueClass, FalseClass,
Fixnum,
Date, and Time). Likewise, data pulled out of text-based attributes
will be
attempted converged using Yaml if they have the "--- " header. This
was
primarily done to be enable the storage of hashes and arrays without
wrapping them in aggregations, so now you can do:

user = User.find(1)
user.preferences = { "background" => "black", "display" => large }
user.save

User.find(1).preferences # => { "background"=>"black",
"display"=>large }

Please note that this method should only be used when you don't care
about
representing the object in proper columns in the database. A money
object
consisting of an amount and a currency is still a much better fit for
a
value object done through aggregations than this new option.

* POSSIBLE CODE BREAKAGE: As a consequence of the lazy type
conversions, it's
a bad idea to reference the @attributes hash directly (it always was,
but
now it's paramount that you don't). If you do, you won't get the type
conversion. So to implement new accessors for existing attributes, use
read_attribute(attr_name) and write_attribute(attr_name, value)
instead.
Like this:

class Song < ActiveRecord::Base
# Uses an integer of seconds to hold the length of the song

def length=(minutes)
write_attribute("length", minutes * 60)
end

def length
read_attribute("length") / 60
end
end

The clever kid will notice that this opens a door to sidestep the
automated
type conversion by using @attributes directly. This is not
recommended as
read/write_attribute may be granted additional responsibilities in the
future, but if you think you know what you're doing and aren't afraid
of
future consequences, this is an option.

* Applied a few minor bug fixes reported by Daniel Von Fange.


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/Ac...ctionAdapters/
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",
assword => "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.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



 
Reply With Quote
 
 
 
 
Carl Youngblood
Guest
Posts: n/a
 
      07-27-2004
I'm still new to ActiveRecord, so forgive me if this is obvious, but
is it necessary in a relationship like the one below for each table to
refer to the other? In this relationship, it would be sufficient for
the projects table to have a manager_id and it would not be necessary
for the managers table to have a project_id, or vice versa. Does
ActiveRecord require both tables to refer to one another?

Thanks,
Carl

> * Added natural object-style assignment for has_one and belongs_to
> associations. Consider the following model:
>
> class Project < ActiveRecord::Base
> has_one :manager
> end
>
> class Manager < ActiveRecord::Base
> belongs_to roject
> end
>
> Earlier, assignments would work like following regardless of which
> way the
> assignment told the best story:
>
> active_record.manager_id = david.id
>
> Now you can do it either from the belonging side:
>
> david.project = active_record
>
> ...or from the having side:
>
> active_record.manager = david
>
> If the assignment happens from the having side, the assigned object is
> automatically saved. So in the example above, the project_id
> attribute on
> david would be set to the id of active_record, then david would be
> saved.



 
Reply With Quote
 
 
 
 
David Heinemeier Hansson
Guest
Posts: n/a
 
      07-27-2004
> I'm still new to ActiveRecord, so forgive me if this is obvious, but
> is it necessary in a relationship like the one below for each table to
> refer to the other? In this relationship, it would be sufficient for
> the projects table to have a manager_id and it would not be necessary
> for the managers table to have a project_id, or vice versa. Does
> ActiveRecord require both tables to refer to one another?


Ahhh, no, no. In the example, the only *table* that holds any
information about the relationship is "managers", which has a
project_id foreign_key. Both *classes* know about the relationship,
though. But that's just to get the association methods on both and to
communicate the design through code.

In Active Record, it's the default style that the "belonging" table is
the one that holds the foreign key. So has_many and has_one depends on
the other guy to hold a foreign key for them.


>> * Added natural object-style assignment for has_one and belongs_to
>> associations. Consider the following model:
>>
>> class Project < ActiveRecord::Base
>> has_one :manager
>> end
>>
>> class Manager < ActiveRecord::Base
>> belongs_to roject
>> end
>>
>> Earlier, assignments would work like following regardless of which
>> way the
>> assignment told the best story:
>>
>> active_record.manager_id = david.id
>>
>> Now you can do it either from the belonging side:
>>
>> david.project = active_record
>>
>> ...or from the having side:
>>
>> active_record.manager = david
>>
>> If the assignment happens from the having side, the assigned
>> object is
>> automatically saved. So in the example above, the project_id
>> attribute on
>> david would be set to the id of active_record, then david would be
>> saved.

>
>
> !DSPAM:410637d7638491553813545!
>

--
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



 
Reply With Quote
 
Gavin Sinclair
Guest
Posts: n/a
 
      07-27-2004
On Tuesday, July 27, 2004, 8:59:26 PM, Carl wrote:

> I'm still new to ActiveRecord, so forgive me if this is obvious, but
> is it necessary in a relationship like the one below for each table to
> refer to the other? In this relationship, it would be sufficient for
> the projects table to have a manager_id and it would not be necessary
> for the managers table to have a project_id, or vice versa. Does
> ActiveRecord require both tables to refer to one another?


This is "obvious" from a database design point of view. If two tables
have a 1-N relationship, then it's pretty obvious (if you have some
experience in the area) that the N table will refer to the 1 table.
In your case, let's say a project has a single manager, but a manager
can manage multiple projects. Then you'll have:

table project:
project_id
manager_id
description
...

table manager:
manager_id
...

It's not feasible for one entry in the manager table to refer to
multiple projects.

I'm pretty confident that David would ensure ActiveRecord works just
fine with the above scenario. What you have to type in, I'm not sure.
But something like this should do.

class Project < ActiveRecord::Base
has_one :manager
end

class Manager < ActiveRecord::Base
has_many roject
end

Cheers,
Gavin



 
Reply With Quote
 
David Heinemeier Hansson
Guest
Posts: n/a
 
      07-27-2004
> I'm pretty confident that David would ensure ActiveRecord works just
> fine with the above scenario. What you have to type in, I'm not sure.
> But something like this should do.
>
> class Project < ActiveRecord::Base
> has_one :manager
> end
>
> class Manager < ActiveRecord::Base
> has_many roject
> end


Actually, that's the primary difference between belongs_to and has_one.
With belongs_to, you accept responsibility for the foreign key. With
has_one, you expect the other side to hold it. So in your example, it
would be:

class Project < ActiveRecord::Base
belongs_to :manager
end

class Manager < ActiveRecord::Base
has_many rojects
end

P.S.: Note that "has_many" expects the symbol as a plural if it is to
do automated mapping.
--
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



 
Reply With Quote
 
Carl Youngblood
Guest
Posts: n/a
 
      07-27-2004
This stuff should probably be documented better, since it is not very
obvious. Thanks for the help. So, just to sum up, which associations
rely on other tables and which associations are responsible for
holding their own keys?

Thanks,

Carl

On Tue, 27 Jul 2004 21:34:44 +0900, David Heinemeier Hansson
<(E-Mail Removed)> wrote:
> > I'm pretty confident that David would ensure ActiveRecord works just
> > fine with the above scenario. What you have to type in, I'm not sure.
> > But something like this should do.
> >
> > class Project < ActiveRecord::Base
> > has_one :manager
> > end
> >
> > class Manager < ActiveRecord::Base
> > has_many roject
> > end

>
> Actually, that's the primary difference between belongs_to and has_one.
> With belongs_to, you accept responsibility for the foreign key. With
> has_one, you expect the other side to hold it. So in your example, it
> would be:
>
> class Project < ActiveRecord::Base
> belongs_to :manager
> end
>
> class Manager < ActiveRecord::Base
> has_many rojects
> end
>
> P.S.: Note that "has_many" expects the symbol as a plural if it is to
> do automated mapping.
>
>
> --
> 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
>
>



 
Reply With Quote
 
David Morton
Guest
Posts: n/a
 
      07-27-2004
Carl Youngblood wrote:
> This stuff should probably be documented better, since it is not very


Jump over to the wiki and write some up!




 
Reply With Quote
 
Carl Youngblood
Guest
Posts: n/a
 
      07-27-2004
You're right. Sorry for being a non-contributing leech. As soon as I
understand it I well enough, I will.

Carl

On Wed, 28 Jul 2004 08:28:16 +0900, David Morton <(E-Mail Removed)> wrote:
> Carl Youngblood wrote:
> > This stuff should probably be documented better, since it is not very

>
> Jump over to the wiki and write some up!
>
>
>
>



 
Reply With Quote
 
David Morton
Guest
Posts: n/a
 
      07-27-2004
The reason I say this is that it's the best way to document what users
are running into. That's why I started a tutorial... not that I'm any
good at it, but I wanted to pass along a few of the things I learned the
hard way.


Carl Youngblood wrote:
> You're right. Sorry for being a non-contributing leech. As soon as I
> understand it I well enough, I will.
>
> Carl
>
> On Wed, 28 Jul 2004 08:28:16 +0900, David Morton <(E-Mail Removed)> wrote:
>
>>Carl Youngblood wrote:
>>
>>>This stuff should probably be documented better, since it is not very

>>
>>Jump over to the wiki and write some up!
>>
>>
>>
>>

>
>




 
Reply With Quote
 
David Heinemeier Hansson
Guest
Posts: n/a
 
      07-27-2004
> The reason I say this is that it's the best way to document what users
> are running into. That's why I started a tutorial... not that I'm any
> good at it, but I wanted to pass along a few of the things I learned
> the hard way.


Couldn't agree more. Once you're a proficient user of something, your
ability to see the rough spots diminishes rapidly. So I didn't catch
the snag with permissions on log files properly because of it.

But bringing rough spots to the attention of others is a good first
step, though. So thanks, Carl!
--
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



 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
IP Address, MX Record, A Record Question K.J. 44 Cisco 2 09-06-2006 05:14 PM
You cannot add or change a record because a related record is required in table 'lok" Andrť ASP .Net 0 06-25-2006 01:30 PM
Retrieving Record Key while creating the record. =?Utf-8?B?SnVzdGlu?= ASP .Net 4 10-05-2004 08:11 PM
[ANN] Active Record 0.9.1: More naturalness, new license David Heinemeier Hansson Ruby 0 07-29-2004 12:11 AM
" Invalid Disk Table in Boot Record - Boot Record could not be repaired " reply@newsgroup.please Computer Support 2 12-01-2003 05:37 AM



Advertisments