Archive for January, 2007

Rails database guidelines

Posted in Rails on January 30, 2007 by Adam
These are some guidelines on building databases that you will use with your Ruby on Rails applications. I have seen many of these elsewhere in scattered places, though I’m not sure that I agree with all the other takes on these issues. My intention is to both list good practices for building new schemata and how to handle inheriting legacy structure which diverge from these practices. Anyway here goes:

Create a Primary Key Column

It is a reasonable restriction to insist that tables have single columns for primary keys. If you’ve inherited a poorly designed, then just create a view onto it with such a column. Unlike the claims of many anti-Rails rants, the column does not need to be called id Using

set_primary_key your-other-primary-key-column

works just fine. But if you’re creating a new database, why not call the column id?

Foreign Key Columns

If you’re designing a new database you should follow the convention of naming the foreign_key column as othertablename_id.

If your existing table doesn’t have this please use a view.

Join Tables

Join tables should be named with the convention of firstModelnamePlural_secondModelnamePlural. The columns in the join table should use the convention just mentioned of joining back to the model name tables with columns named otherTableName_id.

Has Many Through Tables

If you are going to build a :has_many :through relationship, the :through model probably has its own attributes. Otherwise you would have done :has_and_belongs_to_many. Remember to give that model table its own :id column.

Parent Reference Columns

If you are using a model with :acts_as_tree the column which refers to the parent entities. You can override this with the :foreign_key option on :acts_as_tree

Pluralized Table Names

Its better practice if you’re creating a new database schema to use pluralized names. But if you’ve inherited a database that doesn’t follow these practices, you can enter the following in your config/environment.rb file.

ActiveRecord::Base.pluralize_table_names = false

Overriding Table Names

More generally, if you just don’t want to use the same name or plural of the same name as your model for your table name. Or if you’re inheriting a database schema, and you want to use a different name for your model than is reflected in your database, use the set_table_name option in your ActiveRecord model definition.

class YourModel < ActiveRecord::Base
  set_table_name "some_table_name_different_than_model_name" 
end

Use Created_At and Updated_At Columns

If you create columns with these names, Rails will put the date and time of record creation in them for you. Be sure to leave the fields as allowing NULLs. For clarity you should avoid using other columns that have a similar purpose. For example, don’t use a column called “creation_time” that you put values in yourself.

There are also additional column names of created_on and updated_on that will be filled in with the dates only of record creation and modification times. I would tend to stick with created_at and modified_at flavors.

Avoid “Magic Columns” Unintentionally

Besides the columns mentioned there are several others that you could unwittingly use that will cause you problems. Don’t ever use these column names in your tables unless you know about and mean to use their special behavior.

created_at              
created_on             
updated_at
updated_on
lock_version
type
#{table_name}_count
lft
rgt
position
id                      
parent_id               
method
quote

Rails 1.2 scaffold_resource generator suggestions

Posted in Rails on January 23, 2007 by Adam
Rails 1.2 is out now of course. As most of you know the big feature in 1.2 is more support for “automatic CRUD style controllers”. The big feature here is the scaffold_resource generator. As documented elsewhere (but not that extensively) you invoke

ruby script/generate scaffold_resource <modelname> [attributename:attributetype]

where the optional attributename (or more than one of them) identifies an attribute of the model. The controller is generated for you with the same name.

The Good News

So, this is a great step forward. It promotes a REST style of design and the direction that Rails is moving. Its a very good thing that Rails is not agnostic about the right way to build apps. Just as Rails puts a stake in the ground and says that you essentially must build apps using MVC, using capabilities like this does the same to enforce and encourage REST.

More pragmatically, it saves you from writing those REST style controllers yourself, something I found myself doing in app after app. So I was very excited to see this feature and looking forward to saving some typing and being even DRYer.

Some Problems

But, alas, if you are starting from an existing table, you’ll immediately see the problem with this approach. If you don’t list your attributes explicitly (which those of you used to working with existing tables are not used to doing) you will get a surprise when you first invoke your controller actions(with URLs such as http://localhost:3000//view/1 or http://localhost:3000//new). When you view or add any particular record the forms will be empty. Of course, you can go look at your database table schema and just add several column name arguments to your call to the scaffold_resource generator. But that doesn’t seem very DRY, assuming that at some point you created that table (although its DRY enough if you don’t yet have the table).

Solutions

The best fix is probably that the forms introspect the model to find out its attributes and then display those attributes. This code can be placed once in the form.rhtml partial. Note that 1.2 doesn’t actually have that partial. But it appears to be in a fix put into Edge Rails, so it will probably be available in nearterm update to 1.2. Hopefully the form partial can include the dynamic model attribute display, at least as an option.

In the meantime, I may write a little scaffold_resource script generator “invocation generation” script that reads attributes to generate from the underlying table.

Handling Multiple Types of Clients in Your Rails Apps

Posted in mobile platforms, Rails on January 13, 2007 by Adam
If you are building a fullfledged application (versus just a web service), you may want to support both HTML and XForms clients (and possibly other clients such as WAP). This tip describes how to do this efficiently, in a way that is very much not the “standard Rails way” (but is much simpler).

Using A Common Client Checker Inside ApplicationController Class

Include the following code inside your parent application controller class (application.rb) which you use for all controllers in your application.

def checkClient
  if (@request.env['HTTP_ACCEPT']=="application/xml")
    @client="xf" 
    if @request.env['REQUEST_METHOD'] == "POST" 
      paramsFromXml(@request.env['RAW_POST_DATA'],'data')
    end 
  else
    @client="html" 
  end
end
def paramsFromXml(xml,parentname)
  doc=Document.new(xml)
  @params={}
  doc.root.elements.each do |x|
    @params[x.name]= x.text
  end
end

We will add this to the Rails application generator at some point. Note that the first method is the essential part: figuring out that the client is either “xf” (xforms) or “html”.

The second method handles the fact that Mobio’s XForms runner just populates XML in the POST data and doesn’t give the developer an @params hash to work with. paramsFromXml() populates the @params hash from the top level elements below the root XML element. This allows you to write a single controller action that handles data being available in an @params{} hash.

Note that if you wish to just manually parse the XML yourself then you may not need to use this second method. It is merely a “convenience” method for those of you that like to use a params hash in your controller actions

Hooking the Client Checker into Your Controller

Use the Rails filter capability in your controller to integrate the checkClient call to be called before each controller action. Specifically put the line “before filter :checkClient” at the top of each of your controllers. For example,

  class YourController &lt; ApplicationController
   before_filter :checkClient
   yourMethod
      ...your method code....
   end
  end

We will be modifying our controller generator to include this call.

Render with the Appropriate Template

Put the following code at the end of each method.

   yourMethod
     ... your code
     render :action =&gt; "#{action_name}.r" + @client
   end

What this will do is either execute the ”.rhtml” template or the ”.rxf” template based on what client is accessing it. Note that you will need to follow the convention of calling your XForms templates ”.rxf” files. Again we will modify the controller generator to stub in these render calls

Note that this whole methodology is quite different from the “respond_to” mechanism proposed by Rails creator David Heinemeier Hanson. The method he proposes pollutes each controller action with several lines of code (versus adding ”@client” to the end of the render call as shown above).