Latest Entries

How To Setup Twitter Style Following In Your User Model

Setting up a Twitter-like following scheme can be a little tricky at first glance. The reason being that your User model will need to refer to itself in order for a user to follow another user. Here’s how to look at the relationships:

user-follows

We can build this relationship using self-referential associations, and a model as a join table. We’ll call the joining model Follow.

db/migrate/create_follows.rb
class CreateFollows < ActiveRecord::Migration
  def self.up
    create_table :follows do |t|
      t.integer :follower_id
      t.integer :followed_id
      ...

Next, we can add the follower and followed associations to the our new Follow model.

app/models/follow.rb
class Follow < ActiveRecord::Base
  belongs_to :follower, :class_name => "User"
  belongs_to :followed, :class_name => "User"
end

Lastly, we can set up the self-referential associations in the User model.

app/models/user.rb
class User < ActiveRecord::Base
  has_many :follows, :foreign_key => "follower_id", :class_name => "Follow", :dependent => :destroy
  has_many :users_followed, :through => :follows, :source => :followed
 
  has_many :followings, :foreign_key => "followed_id", :class_name => "Follow", :dependent => :destroy
  has_many :users_following, :through => :followings, :source => :follower
end

This allows the User to be both followed and a follower. See this blog post for a good explanation of self-referential associations.

Your controller can look basically like this:

app/controllers/follows_controller.rb
class FollowsController < ApplicationController
 
  def create
    @follow = current_user.follows.build(:followed_id => params[:followed_id])
    if @follow.save
      flash[:notice] = "You are now following #{@follow.followed.name}"
      redirect_to user_path(@follow.followed)
    else
      flash[:error] = "Unable to follow."
      redirect_to user_path(@follow.followed)
    end
  end
 
  def destroy
    @follow = current_user.follows.find_by_followed_id(params[:followed_id])
    @follow.destroy
    flash[:notice] = "Removed follow."
    redirect_to user_path(params[:followed_id])
  end
 
end

Getting Cucumber, Authlogic, and Machinist to Play Together

Out of the box, Cucumber, Authlogic, and Machinist need a little bit of bridging to get them to work together. This post will cover setting up your Machinist blueprints for your User model, and Cucumber login stories and login steps. It also covers how to use Authlogic’s current_user method in your Cucumber stories.

This post assumes you already have Cucumber, Authlogic and Machinist setup and bootstrapped. If not, see these links first: Cucumber, Authlogic, Machinist.

When writing your Cucumber stories you will inevitably come across scenarios like the following:

features/user.feature
Scenario: User fills in profile
   Given I am a logged in user
   And I am on the My Profile page
   ...

How do you setup a logged in user? First, you need create a Machinist blueprint for your user.

spec/blueprint.rb
User.blueprint do
  username 'test_user'
  email 'test@user.com'
  password 'spacemonkey'
  password_confirmation 'spacemonkey'
end

Next you will need to create step definitions for your login stories. Here are a few handy functions that can make the step of creating a logged in user easier and more module:

features/step_definitions/user_steps.rb
1
2
3
4
5
6
7
8
9
10
11
def user
   @user ||= User.make
end
 
def login
  user
  visit '/login'
  fill_in("Email", :with => user.email)
  fill_in("Password", :with => user.password)
  click_button("Login")
end

Next we can create the login step:

features/step_definitions/user_steps.rb
12
13
14
Given /^I am a logged in user$/ do
   login
end

Now the Given I am a logged in user step should pass.

In your other Cucumber step definitions it will come in handy to be able to use Authlogic’s built in current_user method in your Cucumber steps. Since the current_user method apply to users, you can add it to your user_steps.rb file within a module:

features/step_definitions/user_steps.rb
1
2
3
4
5
6
7
8
9
10
11
12
module UserHelpers
  def current_user_session
    return @current_user_session if defined?(@current_user_session)
    @current_user_session = UserSession.find
  end
 
  def current_user
    return @current_user if defined?(@current_user)
    @current_user = current_user_session &amp;&amp; current_user_session.user
  end
end
World(UserHelpers)

Now you should be able to easily run tests involving the logged in session. For example, to make the And I am on the My Profile page step pass you will have to add the path to your path.rb file. In the path reference we can now make use of Authlogic’s current_user method:

features/support/paths.rb
1
2
3
4
5
6
7
8
9
10
module NavigationHelpers
 
  def path_to(page_name)
    case page_name
 
    when /the My Profile page/
      user_path(current_user)
    end
end
World(NavigationHelpers)

Another useful step to add to your user steps is one which creates multiple users. The following step can be used to make a specific amount of users:

features/step_definitions/user_steps.rb
15
16
17
18
19
20
Given /^there exists at least (\d+) other user$/ do |number|
   @users = []
   number.scan(/\d/).join.to_i.times do |i|
      @users[i] = User.make
   end
end

In the end your user_steps.rb file should look like:



Copyright © 2004–2009. All rights reserved.

RSS Feed. This blog is proudly powered by Wordpress and uses Modern Clix, a theme by Rodrigo Galindez.