Jun 18 2006

Acts As Commentable Plugin

I am happy to announce that I have released the acts_as_commentable plugin, my first Ruby on Rails plugin. The Acts As Commentable plugin allows for comments to be added to your Rails ActiveRecord classes.

To install the Acts As Commentable plugin run the following command:

script/plugin install http://juixe.com/svn/acts_as_commentable

The installation process will add several ruby scripts in the vendor/plugins directory. Create a new rails migration and cut and past the following self.up and self.down methods:

def self.up
  create_table :comments, :force => true do |t|
    t.column :title, :string, :limit => 50, :default => ""
    t.column :comment, :string, :default => ""
    t.column :created_at, :datetime, :null => false
    t.column :commentable_id, :integer, :default => 0, :null => false
    t.column :commentable_type, :string, :limit => 15,
      :default => "", :null => false
    t.column :user_id, :integer, :default => 0, :null => false
  end

  add_index :comments, ["user_id"], :name => "fk_comments_user"
end

def self.down
  drop_table :comments
end

Once you have installed the plugin you can start using it in your ActiveRecord class simply by calling the acts_as_commentable method.

class Post < ActiveRecord::Base
  acts_as_commentable
end

To add a comment to a post object you can do the following:

comment = Comment.new(:title => titleStr, :comment => commentStr)
logger << "COMMENT #{comment.comment}\n"
post.comments << comment

Or you could have use the add_comment method on post.

post.add_comment comment

You can also use the post’s comments property to read all comments for the given post. Once a comment has been added to a post you can always reference the post object using the comment’s commentable property.

comment.commentable # references the post

One note, the default implementation of Acts As Commentable requires you to use a user model to link all comments to a user. This requirement can easily be removed or enhanced in the Comment class. But if you have a user model you can retrieve all comments for a user by executing the following statement:

comments = Comment.find_comments_by_user(userInstance)

If you want to retrieve only the comments for a user for a particular model you can do something like:

postComments = Post.find_comments_by_user(userInstance)

If you have any comments, questions, and/or suggestions please don’t hesitate to drop me a line.


Jun 15 2006

Ruby Mixin Tutorial

In Java you just have classes (both abstract and concrete) and interfaces. The Ruby language provides classes, modules, and a mix of both. In this post I want to dive into mixins in Ruby.

In the Ruby language a mixin is a class that is mixed with a module. In other words the implementation of the class and module are joined, intertwined, combined, etc. A mixin is a different mechanism to the extend construct used to add concrete implementation to a class. With a mixin you can extend from a module instead of a class. Before we get started with the mixin examples let me first explain what a module is.

I think of a module as a degenerate abstract class. A module can’t be instantiated and no class can directly extend it but a module can fully implement methods. A class can leverage the implementation of a module by including the module’s methods. A module can define methods that can be shared in different and separate classes either at the class or instance level.

Let me define a module, albeit a trivial one, that would convert a numeric integer value to English.

# Convert a integer value to English.
module Stringify
  # Requires an instance variable @value
  def stringify
    if @value == 1
      "One"
    elsif @value == 2
      "Two"
    elsif @value == 3
      "Three"
    end
  end
end

Note that the Stringify module makes use of a @value instance variable. The class that will be mixed with this module needs to define and set a @value instance variable since the Stringify module uses it but does not define it. In addition to instance variables a module could invoke methods defined not in the module itself but in the class that it will be mixed with.

Now let me construct a self contained module that is not dependent on the implementation of any class that it can be mixed with.

# A Math module akin to Java Math class.
module Math
  # Could be called as a class, static, method
  def add(val_one, val_two)
    BigInteger.new(val_one + val_two)
  end
end

The methods in the Math module are intended to be invoked like class methods, also known as static methods. The add method in the Math module accepts two integer values and returns an instance of BigInteger. Let me now define the mixin BigInteger class.

# Base Number class
class Number
  def intValue
    @value
  end
end

# BigInteger extends Number
class BigInteger < Number


  # Add instance methods from Stringify
  include Stringify

  # Add class methods from Math
  extend Math

  # Add a constructor with one parameter
  def initialize(value)
    @value = value
  end
end

Continue reading


Jun 11 2006

Common Groovy Errors

At my work we are using Groovy extensively and often mix Java syntax in Groovy. I want to post some common Groovy language errors that I think everybody in my team has committed at one point when we forget that Groovy is not Java. The first error that I will mention is that use of array initializers. In Java you can construct an array using an initializer like this:

String[] array = new String[]{"one", "two", "three"}

In Groovy, when we attempted to use this construct we would get a nasty and hard to understand compiler exception. Groovy is an expressive language and you can construct an initialized list in the following fashion:

def list = ["one", "two", "three"]   // ArrayList

The list variable is an instance of ArrayList. If you require an array you need to cast the list such as:

def arr = (String[])list   // String array

Now, this leads me to the second compiler error that I have encountered in Groovy. In Groovy I keep writing for loops the Java way:

for(int i; i < list.size(); i++) {
   System.out.println(list.get(i));
}

From my understanding Groovy only supports the for/in loop construct. If you need to iterate over a list using an index you could do the following:

for(i in 0 .. list.size()-1) {
   println list.get(i)
}

If you don’t need the index you can loop over an list in by using the each method provided by the Groovy Development Kit (GDK):

list.each {
   println it
}

Since I mention how to construct an initialized list in Groovy, let me also cover maps. In Groovy you can construct a map by using the following construct:

def map = [name:'Juixe', date:new Date()]

Since map is an instance of HashMap you can retrieve values using the get method but Groovy provides a different mechanism. In Groovy you can access a value from a map like this:

println map['name']   // returns Juixe

In the examples above I have mentioned how to create lists and maps with data. If you need to create a list or map without data you can use one of the following statements:

def emptyList = []
def emptyMap = [:]

A third error that I have commited is that I keep thinking of the each closures as for loops. I have received compilation erros because I try to continue or break out of a closure loop. Closures are code block so you can break out of them using the return keyword. I continue to the next element in a list do something like the following:

[1, 2, 3, 4, 5].each {
  if(it == 3)
	return
  println it
}

Technorati Tags: , , ,


Jun 10 2006

Ruby On Rails Controller Path

With Ruby on Rails, you can redirect to or link to an action in different controller. For example, to redirect to the login action in the account controller you can use the following from withing a controller in you rails application:

redirect_to :controller => 'account', :action => 'login'

When this redirect statement is executed you application will redirect to a URL like the following:

http://localhost:3000/myapp/account/login

The controller value is like a fragment of URL and can refer to a controller relative to your current controller location or can be absolute to you rails application. Because you can place controllers in their own directories under the app/controllers you can easily imagine having an admin/user_controller. In this case, from the user controller you can refer to another controller that is at the root level by the following:

redirect_to :controller => '../account', :action => 'login'

The controller portion of the redirect to, the ‘../account’ part, is relative to your current controller. To refer to a controller with an absolute path you can do the following:

redirect_to :controller => '/account', :action => 'login'

The controller portion of a redirect is really just a path which can be relative to your current controller or absolute to your rails application and the rails routing will do the right thing for you. This same logic applies to the link_to function.

Technorati Tags: , , ,


Jun 7 2006

Belated Spring Cleaning

I was reorganizing and cleaning my technical bookshelf earlier today. I have a whole bunch of books that I don’t use and are just taking space. I want to donate some of these books to my local library, maybe up and coming techies will get some use out of them. Some of these books are a little dated but others are just not useful in day to day software development. Now that I am into Ruby, Python, and Groovy I just don’t have much room in my bookshelf for Perl books. I have a whole pile of Perl books that I am going to toss out, books like Perl Cookbook, Programming Perl, and Advanced Perl Programming. The only Perl book that I am thinking of keeping is Learning Perl. I also don’t have space for antiquated web frameworks so out goes Programming Jakarta Struts, Professional Struts Applications, Enterprise JavaBeans Component Architecture, and Enterprise JavaBeans.

I think these books will be replaced with up coming Groovy titles and maybe the second edition of Agile Web Development with Rails.

Technorati Tags: , , , ,


Jun 4 2006

Query Managed Auto Increment

Different database vendors allow you to auto increment an integer primary key in different ways. I thought that I could manage to increment the primary key from within the same insert statement. Given the following table:

CREATE TABLE temps (
   id INT NOT NULL,
   name VARCHAR(10) NOT NULL,
   PRIMARY KEY (id)
);

I thought could create a single insert statement that also managed to auto increment the id primary key. I thought I could do something like:

INSERT INTO temps VALUES(
   (SELECT max(t.id)+1 FROM
      (SELECT id FROM temps UNION SELECT 0)
   t),
'juixe');

When I run the above insert statement using MySQL 4.1.15 I get an error that reads, “You can’t specify target table ‘temps’ for update in FROM clause.”

Using Microsoft SQL Server 2005 Express I get an error that reads, “Subqueries are not allowed in this context. Only scalar expressions are allowed.” This is most unfortunate because subqueries are really powerful.

I had better luck on Oracle Database 10g Express Edition where I could run what I call Query Managed Auto Increment statement. On Oracle 10g EX I was able to run the following Query Managed Auto Increment statement with a minor modification:

INSERT INTO temps VALUES(
   (SELECT max(t.id)+1 FROM
      (SELECT id FROM temps UNION SELECT 0 from user_tables)
   t),
'techknow');

What doesn’t work well in Oracle is that you can’t run a select statement without a table name. On Oracle you can’t run the following statement: select 0. Query Managed Auto Increment works in Oracle but you would need default to a table that has at least one record, such as the user_tables table.

Just for completeness I tried PostgreSQL 8.1.4. PostgreSQL supports static select statements, select statements without the from clause and return a constant value. Because PostgreSQL supports static select statements I am guaranteed that the following will return at least one row of ids even if the table is empty:

SELECT id FROM temps UNION SELECT 0;

Adding one to the max id returns the next primary key id, which I use in the insert statement. Once again here is the working SQL (in PostgreSQL) that manages the primary key and automatically increments the id to the next available integer value.

INSERT INTO temps VALUES(
   (SELECT max(t.id)+1 FROM
      (SELECT id FROM temps UNION SELECT 0)
   t),
'zenze');

So in conclusion, Query Managed Auto Increment doesn’t exactly work in MySQL because you can’t use a subquery that refers the same table as the insert. SQL Server has the limitation that you can’t even execute a subquery in an insert. And as we have seen Query Managed Auto Increment works on Oracle and PostgreSQL.

Technorati Tags: , , , , , ,