Google


   


You are here: CodeIdol.com > Other > Ruby Cookbook > Databases And Persistence > Using Object Relational Mapping With Og

SAVE
Digg
Shown on del.icio.us del.icio.us
See Whos Talking About This on Technorati Technorati
I've Reddit reddit

Recipe 13.12. Using Object Relational Mapping with Og

Credit: Mauro Cicio

Problem

You want to store data in a database, without having to use SQL to create or access the database.

Solution

Use the Og (ObjectGraph) library, available as the og gem. Where ActiveRecord has a database-centric approach to object-relational mapping, Og is Ruby-centric. With ActiveRecord, you define the database schema ahead of time and have the library figure out what the Ruby objects should look like. With Og, you define the Ruby objects and let the library take care of creating the database schema.

The only restriction Og imposes on your class definitions is that you must use special versions of the decorator methods for adding attribute accessors. For instance, instead of calling attribute to define accessor methods, you call property.

Here we define a basic schema for a weblog program, like that defined in Recipe 13.11:

	require 'cookbook_dbconnect'
	require 'og'

	class BlogPost
	  property :title, :content, String
	end

	class Comment
	  property :author, :content, String
	  belongs_to : 
og_post,  
BlogPost
	end

	# Now that Comment's been defined, add a reference to it in BlogPost.
	class BlogPost
	  has_many :comments, Comment
	end

After defining the schema, we call the og_connect method defined in the chapter introduction. Og automatically creates any necessary database tables:

	og_connect
	# Og uses the Mysql store.
	# Created table 'ogcomment'.
	# Created table 'ogblogpost'.

Now we can create a blog post and some comments:

	post = BlogPost.new
	post.title = "First post"
	post.content = "Here are some pictures of our iguana."
	post.save!

	[["Alice", "That's one cute iguana!"],
	 ["Bob", "Thank you, Alice!"]].each do |author, content|
	  comment = Comment.new
	  comment.blog_post = post
	  comment.author = author
	  comment.content = content
	  comment.save!
	end

As with ActiveRecord, we can query the tables, relate blog posts to their comments, and relate comments back to their blog posts:

	post = BlogPost.first
	puts %{#{post.comments.size} comments for "#{post.title}"}
	# 2 comments for "First post"

	post.comments.each do |comment|
	  puts "Comment author: #{comment.author}"
	  puts "Comment: #{comment.content}"
	end
	# Comment author: Alice
	# Comment: That's one cute iguana!
	# Comment author: Bob
	# Comment: Thank you, Alice!

	puts %{The first comment was made on "#{Comment.first.blog_post.title}"}
	# The first comment was made on "First post"

Discussion

Like the ActiveRecord library, Og implements Martin Fowler's Active Record Pattern. While ActiveRecord does this by making all classes derive from the base class ActiveRecord::Base, Og does it by using custom attribute accessors instead of the traditional Ruby accessors. In this example, Comment and BlogPost are POR (Plain Old Ruby) classes, with accessor methods like author and author=, but those methods were defined with Og decorators instead of the standard Ruby decorators. This table shows the mapping between the two sets of decorators.

Table 13-2.

Standard Ruby accessors

Og accessors

attribute

roperty

attr_accessor

prop_accessor

attr_reader

prop_reader

attr_writer

prop_writer


Each of the Og decorator methods takes a Ruby class as its last argument: String, Integer, or the like. Og uses this to define the type of the corresponding database row. You can also specify Object as a field type, and Og will transparently store YAML representations of arbitrary Ruby objects in the corresponding database field.

ActiveRecord defines all kinds of conventions about how you're supposed to name your database tables and fields. Og doesn't care: it names database tables and fields that correspond to the names you use in your Ruby code.

Just as with ActiveRecord, relationships between Og tables are defined within Ruby code, using decorator methods. The API is almost exactly the same as ActiveRecord's. In the Solution section, we saw how to create a one-to-many relationship between blog posts and comments: by calling belongs_to in Comment and has_many in BlogPost. This relationship makes it possible to simply call BlogPost#comments and get an array of comments on a post.

Og defines two more decorator methods for describing relationships between tables. One of them is the has_one association, which is rarely used: if there's a one-to-one relationship between the rows in two tables, then you should probably just merge the tables.

The other decorator is many_to_many, which lets you to join two different tables with an intermediate join table. This lets you create many-to-many relationships, common in (to take one example) permissioning systems.

For an example of many_to_many, let's make our blog a collaborative effort. We'll add a User class that holds the posts' authors' names, and fix it so that each blog post can have multiple authors. Of course, each author can also contribute to multiple posts, so we've got a many-to-many relationship between users and blog posts. Og needs to know the class definition in order to create the necessary database tables, so the following code snippet should appear before the og_connect invocation in your program:

	class Person
	  property :name, String
	  many_to_many :posts, BlogPost
	end

The many_to_many decorator tells Og to create a table to store the people, and a join table to map authors to their blog posts. It also defines methods that navigate the join table, as we'll see in a moment.

Of course, the many-to-many relationship goes both ways: BlogPost has a many-to-many relationship to Person. So add a many_to_many call to the definition of BlogPost (this, too, must show up before your og_connect call):

	class BlogPost
	  many_to_many :authors, Person
	end

With these relationships in place, it's easy to find blog posts for an author, and authors for a blog post:

	og_connect

	# Retroactively make Bob and Carol the collaborative authors of our
	# first blog post.
	['Bob', 'Carol'].each do |name|
	  p = Person.new
	  p.name = name
	  p.save
	end
	Person.find_by_name('Bob').add_post(post)
	Person.find_by_name('Carol').add_post(post)

	author = Person.first
	puts "#{author.name} has made #{author.posts.size} blog post(s)."
	# Bob has made 1 blog post(s).

	puts %{The blog post "#{post.title}" has #{post.authors.size} author(s).}
	# The blog post "First post" has 2 author(s).

To add an anonymous BlogPost on the fly, use the add_post method as follows:

	author.add_post(BlogPost.create_with({
	    :title => 'Second post',
	    :content => 'We have some cats as well.'
	} ))

Since Person posts returns an array-like object, you can iterate over it to find all the blog posts to which a given user contributed:

	author.posts.each do |post|
	  puts %{#{author.name}'s blog post "#{post.title}" has #{post.comments.size}
	comments.}
	end

	# Bob's  
blog post "First post" has 2 comments.
	# Bob's  
blog post "Second post" has 0 comments.

If you want to delete an object from the database, you can use the delete method available to all Og database objects:

	BlogPost.first.delete

Deleting a blog post will automatically remove all the comments associated with that blog post. This automatic deletion (i.e., cascade deletion) is not always a good idea. For instance, we don't want the authors of a blog post to be deleted when the post itself is deleted! We can avoid the cascade deletion by passing false in as an argument to the delete method:

	BlogPost.first.delete(false)

If you want some associated objects (like comments) to get cascade-deleted, and other objects (like authors) to be left alone, the best strategy is to implement the cascade yourself, in post-delete hooks.

See Also

  • The Active Record pattern is described in Patterns of Enterprise Application Architecture by Martin Fowler (Addison-Wesley)


SAVE
Digg
Shown on del.icio.us del.icio.us
See Whos Talking About This on Technorati Technorati
I've Reddit reddit

You are here: CodeIdol.com > Other > Ruby Cookbook > Databases And Persistence > Using Object Relational Mapping With Og


ADBRITE ads links
   
Related tags







Popular Categories
Unix books and guides

AJAX popular information
C# language guides
Windows books and cookbooks

.......








Business Key Top Sites

be number one
rate your site




    С 2009 года мы стали переводить структура сайта на различные языки. Сайт теперь будет содержать книги не только на английском языке, но также и на других европейских языках, в том числе и на Русском языке.

    Русский Polski Francais Deutsch
    support sitemap terms

© CodeIdol Labs, 2007 - 2009