Wednesday, March 05, 2008

Integrate a forum with a Rails site

So you've got a fancy rails site where Users can log in and do stuff. And you'd like them to be able to host a community forum on the site, using your existing User model to authenticate users for posting, moderation, and administration. The Savage Beast 2.0 engine does just that. In typical Rails style, you just follow its conventions for determining the logged-in user and authenticating them, and you're good to go with a mini-Beast forum.

When I tried this out yesterday, I ran into tons of issues with the Engines plugin and the way that Rails development environment loads, unloads, and reloads classes. So here are a couple of things that I figured out:

  • First, I don't know thing one about Engines, other than they sound pretty useful. For some reason, after installing the engines plugin, I couldn't run my migrations. It looks like Engines tries to redefine the db:migrate task into a namespace that then migrates all of the plugins. But that didn't work with my version of Rake, so my workaround is just to migrate before installing Engines.

    *NOTE: I was using Rake 0.7.3, but when I tried using Rake 0.8.1, I didn't have the same error.
  • Lots of issues with modules being unloaded in development. So again the solution is to avoid the problem, and run in production mode.

    *NOTE: This is actually due to Rails Dependencies module and the way that Models and Controllers are loaded and unloaded in between requests, and it supposedly happens with lots of different plugins in development. Basically, if your plugin defines models or controllers, you have to call "unloadable" inside the class. Otherwise Rails doesn't unload the class at the end of the request, but does unload referenced modules in your main app. So when Savage Beast's ForumsController calls methods of AuthenticatedSystem included in ApplicationController, the AuthenticatedSystem module gets unloaded from ApplicationController but not ForumsController, and on the next request it won't be reloaded because it is still defined.
  • I also have the old 1.2.3 Rails on my work machine, since I'm maintaining old apps that I don't have time to migrate to Rails 2.0, so as you'll see, I'm explicitly freezing the 2.0.2 gems to vendor/rails.

Here's my install script. If you follow along (and none of the dependent plugin trunks have changed in a way that breaks things), you should be all set.


# steps to create a sample app for Savage Beast 2.0 using Restful Authentication as the User model
# requires: rails, svn
# note: better to run in Production environment than development, because development will cause
# Models and Controllers to unload in between requests, causing unexplained missing constants and methods
rails savage_beast_test
cd savage_beast_test
rake rails:freeze:edge TAG=rel_2-0-2
cd ..
ruby savage_beast_test/vendor/rails/railties/bin/rails savage_beast_test
# to overwrite all, type: a
cd savage_beast_test
ruby script/plugin install restful_authentication
ruby script/generate authenticated user sessions
cd vendor
cd plugins
svn checkout http://savage-beast-2.googlecode.com/svn/trunk/ savage_beast
cd ..
cd ..
cp vendor/plugins/savage_beast/db/migrate/001_create_savage_tables.rb db/migrate/002_create_savage_tables.rb
rake db:migrate
rake db:migrate RAILS_ENV=production
ruby script/plugin install white_list
ruby script/plugin install white_list_formatted_content
ruby script/plugin install acts_as_list
ruby script/plugin install svn://errtheblog.com/svn/plugins/gibberish
ruby script/plugin install svn://errtheblog.com/svn/plugins/will_paginate
ruby script/plugin install http://svn.rails-engines.org/engines/trunk



# *** app file modifications: ***

# config/environment.rb:
# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')
# Boot the Engines plugin
require File.join(File.dirname(__FILE__), '../vendor/plugins/engines/boot')
#...

# config/routes.rb:
ActionController::Routing::Routes.draw do |map|
map.from_plugin :savage_beast

map.resources :users

map.resource :session
#...

# app/models/user.rb:
include SavageBeast::UserInit
def display_name
login
end
def admin?
true
end
def currently_online
false
end
protected
#...



# app/controllers/application.rb:
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
include AuthenticatedSystem
def admin?
logged_in?
end
#...


# *** minor fix to Savage Beast application_helper.rb:
# (note: restful_authentication#current_user returns :false instead of nil)
# vendor/plugins/savage_beast/app/helpers/application_helper.rb:
def beast_user_name
#(current_user ? current_user.display_name : "Guest" )
(logged_in? ? current_user.display_name : "Guest" )
end


ruby script/server -e production

# get: http://localhost:3000/forums
# - first hit causes an error, lowpro.js missing. Second hit works.
# get: http://localhost:3000/forums/new
# - redirects to /sessions/new
# get: http://localhost:3000/users/new
# create a new user... logs in and redirects to /forums/new with "Thanks for signing up!" flash
# create a new forum... redirects to /forums/1 with no topics
# click New Topic link, gets /forums/1/topics/new
# create new topic/first post, redirects to /forums/1/topics/1

7 Comments:

At 3:34 AM, Blogger Unknown said...

The engines plugin doesn't redefine db:migrate, because its quite possible (and useful!) to have a task and a namespace with the same name. You can try this out yourself to verify it.

As for the other problems you're seeing, it's difficult to tell where they come from. I'm not sure how maintained the savage beast plugin is at the moment.

Please do feel free to post your experiences to the mailing list though :)

 
At 8:55 AM, Blogger Adam Payne said...

Thanks for clearing that up, James! I'll look into it further to see what's happening and post it to the list. The specific rake db:migrate error I've been getting is:
wrong number of arguments (3 for 1)
vendor/plugins/engines/tasks/engines.rake:45:in
`task'

 
At 9:24 AM, Blogger Unknown said...

What version of Rake are you using? It'd also be useful to know the revision/tag of the engines plugin, and of Rails.

 
At 9:47 AM, Blogger Adam Payne said...

I pulled down engines from trunk at 1:21 pm yesterday, and according to the svn log that's r669.

Rake is outdated, 0.7.3, so that might be the problem.

Rails is 2.0.2

 
At 2:28 PM, Blogger Adam Payne said...

Running rake -T with the latest version worked, so I'm guessing that migrating with it would work as well.

As for the module loading problems, I read up on "unloadable," and the lack of it in Savage Beast's controllers and models seemed to cause the issue. Once I added it everywhere, development mode worked.

 
At 7:34 PM, Blogger Bill said...

Hey Adam,

I've made a few updates to SB per your difficulties, including:
1) SB installation now includes tested versions of all the plugins it needs (reduces potential for plugin to get broken as time goes on due to plugin trunk versions changing)
2) Added fix for models getting unexpectedly unloaded when running in development
3) And various other simplifications of the install process (e.g., you don't have to visit the Engines page anymore).

Hopefully these changes make it easier for future implementers to get things rolling. Thanks for your feedback to point out some of the shortcomings.

 
At 10:06 PM, Blogger Sanjay Bahl said...

Hi Adam
Thanks a lot for the installation script.
It made my work so easy.
i'll be launching my site soon.

 

Post a Comment

<< Home