Future on Rails

Using Rails

Posts Tagged ‘rails

Nested Forms & Webrat

leave a comment »

I love the new features of Rails 2.3 – one beeing the support for nested forms (forms which let you edit multiple models and associations). However, I ran into some problems with this new approach: 1) Making complex forms with AJAX work with the Rails 2.3 approach and 2) Testing those forms with Cucumber and Webrat.

1) Making complex forms with AJAX work with the Rails 2.3 approach

When I tried to use the approach from Ryan Bate’s railscast “Complex Forms Part 3”, ran into several issues with organizing my partials and getting the HTML and Javascript right. Fortunately, Ryan addressed this issue himself => see his github project.

2) Testing those forms with Cucumber and Webrat

Another problem I ran into was testing a complex AJAX form with the new nested forms. Imagine the default app for managing projects, where each project has multiple tasks. To add tasks to a project, I can simply click “Add Task” on the project’s edit page and add form fields dynamically via AJAX. However, how would you address those fields in a cucumber step using webrat?

Given I am on the project edit page
When I click “Add Task”
And I fill in ??? with “my new task”
And I click “Save”
Then I should see …

What to insert for the “???” ? Those fields all have the same label (“Task:”) and the name/id is very cryptic, containing a randomly generated id. I couldn’t find an elegant solution for this issue yet. Please tell me if you know of any solution!

Advertisements

Written by Matthias Orgler

September 30, 2009 at 1:21 pm

Posted in Uncategorized

Tagged with , , , , , ,

AOP on Rails

leave a comment »

AOP can make a software design very DRY and tidy. This post discusses a method to do aspect-oriented programming in RoR.

From my resarch at university as well as from real world projects I know what a fantastic tool aspect-oriented programming is. Yes, it’s a sharp knife, but if you handle it correctly, you can do great things without damaging your design ;). The downside is: Ruby is not an aspect-oriented language. Having said this, the Ruby language comes with some great constructs to allow an emulation of AOP.

First: Why the hell do we need AOP?? Let me give you a real world case that led me to an aspect-oriented design. The example comes from a social web app I was building for a client, so everyone should be able to relate to it.

Typical parts of a social web application are “Users”, “Friendships” or “Chats”. Basically a user can be friend with any other user in the network (m:n relationship). The same goes for chats: a user can chat with any other user. How would you model this in Rails? Well, the first thought might lead to something similar to Hartl/Prochazka’s solution in RailsSpace (pseudeo code): 

class User
  has_many :friendships
  has_many :friends, :through => :friendships
  def become_friend_with(other_user)
    self.friendships << Friendship.create(:inviter => self, :invitee => other_user)
  end 
  def friend_of?(other_user)
    Friendship.between?(self, other_user)
  end 
 end

This is a good approach. However, watch what happens, if we add “Chats” or other features to the “User” class:

class User
  has_many :friendships
  has_many :friends, :through => :friendships
  has_many :chats
  has_many :messages
  has_many :channels
  ...
  def become_friend_with(other_user)
  def subscribe_to_channel(channel_name)
  def active_chats 
  ...

end 
My point is: the “User” class shows the blob anti-pattern. Diverse features amass in the “User” class and make it more and more incoherent. The consequence is a class spanning several screens in code and various features that you need to find “somewhere” in that class (as opposed to finding a feature in a class with its name). Part of the problem with incoherence is already solved by using specialized classes for “Friendship”, “Message” and so on – the “User” class only acts as a facade and delegates method calls to the specialized classes. However, from a design point of view I would love to eliminate most of this delegation code from the “User” class, since it doesn’t really belong there and only clutters up the code. Furthermore I would like to be able to eliminate or add features with a simple line of code: one of my clients might need “Chats”, the other might not want them. It would be great, if I could simply delete a line with “feature Chat” from the “User” class an be done with a new version :). The notion of “features” comes from feature-oriented programming (FOP) and software product lines (SPL). Basically all these features are different aspects of a User class: the aspect of being a friend, the aspect of being a chat partner, … . This is why it’s called aspect-oriented programming. The goal is now, to put each aspect in its own code entity (class or module).

How to do this in RoR? Let me first show you what happens, once you go the AOP way. The User class become pretty sleak and looks like this:

class User
  include FriendshipUser, ChatUser, ChannelUser, ...
end

If I didn’t wanted a “User” to participate in “Friendships”, I can now simply remove the “FriendshipUser” from the include line and be done. If I wanted to add VoIP capabilities to the “User”, I could simply add the “VideoChatUser” module to the include statement. Did we save any code this way? No! But now all code belonging to the aspect of “Friendship” resides in its own module (called “FriendshipUser”):

module FriendshipUser
  def friend_of?(other_user)
    Friendship.between?(self, other_user)
  end
  ...
end 

This makes the classes/modules more coherent, maintainable and understandable. The “FriendshipUser” module is now an aspect and enhances the “User” class with functionality by adding methods. I would personally put this module definition in the same file as the “Friendship” class. What’s still missing are the declarations of the has_many-relationships and maybe validations, named scopes and so on. This requires a little meta programming, but it is still fairly simple:

module FriendshipUser
  def self.included(includer) 
    includer.class_eval do 
      has_many :friendships
      has_many :friends, :through => :friendships
    end 
  ...
  end
end 

An important feature of AOP is the possibility to enhance methods of the “User” class with new features – without the “User” class having to know about it. In plain old OOP, the only possibility to enhance functionality of a class later on is inheritance and overriding methodes (UserAsFriend < User). If we wanted to be able to have the new functionality in the base class already (“User” in this  case), we could fall back to the template method pattern. However, all of this would mean to plan in advance for all possible future features – this doesn’t sound very agile to me. The aspect-oriented solution is the notion of “join points” and “pointcuts”. A “join point” is basically any defined point in the static or dynamic structure of an application: methode calls, method executions, instantiations, … . “Pointcuts” are like search queries, that filter out specific join points of interest to the aspect. Let’s say, when a new “User” is created, we want it to automatically become friend with one of our support guys. Of course you already know, how this would be possible in Rails: 

module FriendshipUser
  def after_create
    self.become_friend_with(support_guy)
  end
end

We can use the lifecylce methods of Rails to hook into existing methods. Unfortunately this is a bit limited compared to freely definable pointcuts in an aspect-oriented language – but it works in many cases in Rails. before_filter and after_filter would be nice tools here, but they’re only made for controllers. Observers use a variation of the template method pattern and thus also won’t qualify for a pointcut solution. One hope I have are a few attempts of producing an AOP extension for the Ruby language – I will keep an eye on those (I know about Aquarium).

If you have any questions, thoughts or suggestions on this topic, please leave me a comment or send me a message. I’m eager to help, discuss and learn :).

Written by Matthias Orgler

April 28, 2009 at 1:20 pm

Posted in development techniques

Tagged with , , , , ,