At some point, it’s almost guaranteed that you will want a forum for your website. Forums make a great way to allow your users to contact you and each other, and their persistence allows for archiving of the answers so you (hopefully) don’t have to keep answering the same questions over and over. Moreover, most users are familiar with the forum concept and interface, thereby reducing the barrier to communication.
If all you want is to get a forum up as fast as possible, there are several free/open-source/commercial solutions. They will get you up and running very quickly, provide all the functionality you need, plus tons of features you’ll probably never use. If that’s what you want, stop reading and go get one.
Assuming you’re still here, then you’re probably in the same boat as me. You’ve got a pre-existing Ruby on Rails application, and you want to add a forum. Moreover, you want to add an integrated forum that allows your users to utilize the same login credentials as they use for your current app.
Enter the Beast! Beast is a pure RoR forum application that has everything you might expect from a forum, plus offers the glimmer of hope that you’ll be able to integrate it with your current app. Still, once you dive in, the glimmer starts looking pretty dim. I approached it several times and backed off before I finally had the confidence to finish it. Luckily for others, I made a lot of mistakes and am happy to share them.
The Goal
For now, my only goal is to unify usernames and passwords, preventing the users from having to register twice. Very important to note is the fact that I am NOT talking about a single sign-on. I am comfortable (at this point) with forcing the user to sign in twice. I simply want to make sure that the username and password are the same both times. Remember: KISS and baby steps.
Before you start
Before I describe the actual approaches, I would like to give some general advice on how to proceed, regardless of your approach. These following tips could potentially save you a lot of time in the very real case that something goes horribly wrong.
Create a development branch
You are about to attempt a potentially nasty bit of integration. You might need to hack your code (and Beast’s code) in very ugly ways. You may need to compromise your whole design. Worst of all, you might fail! At the end, you may realize that it just is not worth it, and you’re destabilizing your codebase too much to justify the payoff. Wouldn’t it be nice to simply write off the time and get back to work?
From another angle, perhaps you’re in the middle of the integration when a user finds a nasty bug on your live site. You need to fix the bug right now, but your codebase is in a totally unstable state. You can’t fix the bug without checking in your Beast changes, and they will break everything. Sounds like it’s going to be a long night…
If you do your work on a development branch, then switching back to the trunk is very easy. Simply check in all your changes on the branch, then wipe out your working copy and check out a fresh one from the trunk. All the work you did on Beast is still there (in the branch), but you’re working on the trunk, totally clean. You can continue main development (such as fixing nasty bugs), and get back to Beast when time allows. Finally, when you’re satisfied that Beast is ready, a simple merge-to-trunk is all it takes to bring all your changes into the trunk.
Bottom line: Do all your Beast integration work on a branch.
Grab Beast as a vendor branch
Beast is still under active development, and they just hit 1.0 Do you plan to stay current with it as it matures over time? I definitely plan to. However, integrating it with your application will almost definitely require you to modify it in some way. Heck, just installing it by itself will probably require modifications in order to get the text to say what you want.
Starting from a vendor branch will allow you to (hopefully) easily upgrade to newer versions, while keeping all the changes you had to make in order to get it working with your app.
We have already covered how to do a vendor branch and how to upgrade using a vendor branch, so go browse those posts if you don’t know what I’m talking about.
Please, I’m begging you, trust me on this one. Do the vendor branch and save yourself a huge headache when Beast 1.1 comes out…
The Approaches
I attacked the integration problem from three different angles. Two worked and one was a total failure. I’ll describe each one and its pros and cons. If I get anything wrong, or left something out, please do not hesitate to correct me in the comments. I really want this to be a valuable resource for the Rails community.
One commonality to note between all the approaches was that I never tried to fully integrate the applications from the perspective of controllers and routing. They each retained their own directory structure, they each will have their own root domain, and they will each require their own mongrel server, unless I can figure out how to get a single mongrel to serve both.
1. Separate databases; Users pulled from my app’s database
With this approach, I tried to keep separate the two databases, except for the Users. The RDocs for ActiveRecord::Base give instructions on how to force Rails to connect to a different database for a specific model. This seemed like an excellent way to handle the Beast integration.
I added all the missing Beast user columns to my Users table, created the secondary database, and updated the Beast User model to connect to my database. At first it worked, and I was pretty excited. However, as I navigated around the forums, it crashed almost immediately. Because there are several relationships between the User and other models, the system was attempting to run some joins. Perhaps other RDBMS’s can do cross-database joins, but it seems that MySQL cannot, at least not how I have it set up.
Bottom line: Didn’t work because MySQL freaked out about the cross-database joins.
2. Same databases; Beast tables renamed with beast_
My second attempt involved adding all the Beast tables to my current database, and then renaming all the Beast tables with a “beast_” prefix. It wasn’t strictly necessary as there were no naming conflicts, but I thought it would help with readability.
I left the users table and sessions tables untouched, as I already had a users table and simply added the Beast columns to it. The sessions table seemed like something special, so I decided not to mess with it.
To accomplish the renaming, all I had to do (I thought) was edit the migrations a little, then use the set_table_name directive in each of the model classes. When this was done, things looked like they would work.
What I didn’t realize was that much of the Beast database querying is done via specifying :conditions on a find() call. I didn’t look too closely, but perhaps the Beast team found they could not be served adequately by the find_by_x_and_y finders. The end result of this was that there were several places where the table names were listed explicitly inside the query conditionals. I ran several search/replace runs for “posts”, “forums”, and so on for all the Beast tables. Of course, these strings got several hundred hits throughout the codebase, and the replacement was a real chore.
Eventually I got Beast working, but I did not have a lot of confidence in the changes. I was not sure if I had caught everything, although this could be reasonably tested using the unit and functional tests. Most troubling to me, however, was the prospect of trying to integrate a new release. I had the sneaking suspicion that every time there was a new release of Beast, I would be hunting down table names inside of conditionals and replacing them by hand. Besides the tediousness, this was bound to produce bugs that I wouldn’t catch and would escape into production.
Bottom line: Works, but you’re in for a world of hurt when you want to upgrade to a new release of Beast.
3. Same database; Beast tables unmodified
As I mentioned earlier, I had no table name conflicts with Beast, so this was a viable option for me. Just add all the new Beast tables, and modify my users table to add the necessary fields.
This was a no-brainer, and took almost no time at all to accomplish. Run the migrations, make a few modifications to the authentication system, and you’re up and running. After trying the other two methods, I was able to do this one in about 5 minutes.
This is ultimately the approach I would recommend to anyone attempting the integration. Of course, it works best if you have no table naming conflicts. However, assuming that you do, I recommend only renaming the Beast tables that actually conflict. This will minimize the amount of find/replace hunting that needs to be done, both now and for future upgrades. Remember: KISS.
Bottom line: Works, is easy to do, and represents the least amount of risk when upgrading to new releases of Beast. This is my recommended solution.
Modifications to support acts_as_authenticated
Handling the database will be the hardest part, but if you are using acts_as_authenticated, you’ll need to make a few updates to the authentication that goes on.
It actually turned out to be very easy, but remember, this does not get you single sign-on, just unified usernames and passwords.
Modify the Beast User.rb, and remove or comment out the self.authenticate method. We will be replacing this. I think there might be a way to re-alias it, but deleting works just as well.
Replace the one you removed with the following method:
def self.authenticate(login, password, activated=true)
u = find_by_login(login)
return nil unless u
crypted_pass = Digest::SHA1.hexdigest(“–#{u.salt}–#{password}–”)
return crypted_pass == u.crypted_password ? u : nil
end
As you can see, this simply finds the User by their login, and checks their password the same way that acts_as_authenticated does.
Other than that, I modified reset_login_key! to change “password_hash” to “cypted_password” To be honest, I have no idea what this does, but the two fields are basically equivalent in the two authentication schemes (I think). Anyways, it seems to work.
Next steps
So why is this titled part 1? Because I want single sign-on, of course! The eventual goal is to unify the sign in interface so that the user signs in at one place and can then navigate through the entire site, including the forums, and never have to re-authenticate.
Part 2 (if it ever gets written) will deal with unifying the sign-on. This has been done before (see the references at the end of the post), but I’m just not ready to attempt it yet.
Update 2007-12-17 Adam from the comments has posted a great tip on how to easily get single sign-on. He says:
Single sign on is very easy. ALl you have to do is set your session domain to be the same by typing (for rails 2.0)
config.action_controller.session = { :session_key => “_session_id”, :secret => “secret_session” , :session_domain => “.mydomain.com” }
So now forums.mydomain.com and www.mydomain.com will use same cookie/session and you can host beast on a subdomain as a separate app. I then added a before_filter to application.rb that ensured session[:user] was populated otherwise i redirected to www.mydomain.com/login
I have not tried this yet, but telling the two Rails apps to use the same session is probably the best way to go.
Additional References
Beast Forums – A great place to ask questions about Beast…go figure ![]()
Integrating Beast with another application – Someone else who did the same thing, but duplicated the User information across 2 databases
Call for participation
If I have gotten anything wrong, or you know a better way to do it, please post a note in the comments! This integration was fairly hard to do, and I made a lot of false starts. I want to make it smoother for those that follow.
July 4th, 2007 at 12:09 pm
Great post.
We’ve Beast integrated into our website for a while now, and what we ultimately did was integrated our app into Beast, because there wasn’t a way to create a smooth sign up experience without violating KISS.
I think finding the smoothest way to integrate Beast into an app is like the holy grail for a lot of RoR devs.
Good luck on with part 2, I look forward to seeing what you come up with.
– Calvin
July 10th, 2007 at 3:03 am
On the other hand, what I want to do in Beast is to have it support the “subdomain as account key” way. Is that possible?
July 10th, 2007 at 4:54 am
I’m not sure I understand. You want the subdomain to be the login?
I’m pretty sure this is possible, as I think you can pull the subdomain out of the request, then route this through the authentication system. I don’t know how you would handle gathering the password.
Have you asked about this on the Beast forum? They can be slow, but the developers usually have good answers.
July 10th, 2007 at 5:34 am
Could I hire you to do this?
July 11th, 2007 at 8:51 am
Kathleen,
Please contact us using the contact form. Thanks.
August 25th, 2007 at 8:38 am
Nice article.
I tried this out on PostgreSQL using schema_search_path, so that Beast tables were kept separate, falling back to my application’s schema for the users table only.
Looking forward to part 2!
September 24th, 2007 at 10:33 pm
I have integrated the forum with my application.But now i need to setup beast on the server i.e. with my existing application.Can anybody tell me how to do that.
September 25th, 2007 at 5:42 am
Arpit,
We have Beast running as a separate application, with its own Mongrel instance. So, in essence, Beast and our regular app are two separate applications running independently. The only thing they share is the database.
September 26th, 2007 at 8:00 pm
Thanks Micah.
You are right.I need to know whether there is a way to integrate beast in our application so that we need not to run two instances(i.e. my application and the beast)I don’t want to run these applications independently.
October 20th, 2007 at 6:43 am
Would it not be easier to rename your own objects / tables in the code that is under your control?
Could you simply add controllers etc. to a Beast install and name them to avoid conflicts?
P
October 21st, 2007 at 1:12 pm
Paulie,
You could definitely do that. To me, it seems sort of backwards. I view Beast as an add-on to my site, rather than my site being an add-on to Beast. Still, it’s mostly a point-of-view thing.
If you’re starting from scratch, or at least a very small codebase, I’ve heard that this approach is actually very popular and successful.
October 22nd, 2007 at 9:16 am
[...] read more here [...]
November 6th, 2007 at 3:10 pm
i’m eric. joining a couple boards and looking
forward to participating. hehe unless i get
too distracted!
eric
November 20th, 2007 at 5:22 am
Hey, I have done single sign on between a .NET and a rails app using a webservice. Basically all links to the rails app went to a page which would generate a one time token (only used once then it is deleted) which is tacked onto a url for the rails app. The rails app would use that token to call a webservice which would return the user info. No username or password is ever used again other than the initial logon. It only took me about an hour to write the web service and replace the rails logon code.
On the rails side when I got the userinfo from the web service I check to see if the user exists already , if they dont then I send them to a setup screen (in our app usually it was just to verify they want to use the data passed from the other system) otherwise they go right into the app. I am using this system with 2 apps on site and one externally hosted application , all use the same sign on, soon all our web apps will have one logon. (once I clean up the mess that is their database)
So maybe with the beast you could bypass the logon and call a webservice to create the useraccount from the info in your main app and logon them on in the background and leave everthing else the same. Basically run beast as it’s own app seems simpler that way to me.
Anyway good luck with whatever you chose.
Bill
December 17th, 2007 at 6:44 am
Single sign on is very easy. ALl you have to do is set your session domain to be the same by typing (for rails 2.0)
config.action_controller.session = { :session_key => “_session_id”, :secret => “secret_session” , :session_domain => “.mydomain.com” }
So now forums.mydomain.com and http://www.mydomain.com will use same cookie/session and you can host beast on a subdomain as a separate app. I then added a before_filter to application.rb that ensured session[:user] was populated otherwise i redirected to http://www.mydomain.com/login
December 17th, 2007 at 12:54 pm
Argh!! Adam, you just beat me to it. I was bumming around yesterday looking at sessions and such, and this exact idea occurred to me.
I’ve already added a ticket to our Trac to cover this. Now I just need the time to make the change.
Thanks!
January 4th, 2008 at 3:04 am
its very good understandable, but can you give me how to integrate it in my app i.e in controller where i have call this plugin where i have to write edit/view/delete of the forum in my application
January 22nd, 2008 at 6:37 pm
The single sign on idea worked perfect. Very nice.
January 22nd, 2008 at 6:37 pm
the single sign on idea worked perfect. Very nice.
January 27th, 2008 at 1:12 pm
This post is excelent it has introduced me to beast. Question. I already have act_as_authenticated plugin , I was wondering if anybody has tried integrating these two plugins yet
March 5th, 2008 at 5:30 am
Hi Bill,
I wanna ask one thing to you that how can i do single sign on between .NEt and ROR application.
Please give me some idea.
Thanks
Varun
March 26th, 2008 at 12:30 pm
Varun,
Basically what I did is when a user signed onto my .NET application I have a cookie that tells me who they are,(as most web applications do these days).
1. When the user wants to go to the ROR application I actually go to a .NET page first that calls a webservice (written in .net but doesnt have to be) and gets a token, basically a gui that is stored on a db.
2. I tack that gui onto the end of a url taking me to the ROR application, that is not a security risk as that token is only used once.
3. When the user arrives at the ROR application I use that token to call a webservice which returns information about the user (userid, name , etc..). That way I don’t have to have them log in.
4. If you would like some code email me at billtaichi at gmail dot com.
May 4th, 2008 at 3:50 am
Has anyone tried using ActiveResource to talk to Beast? Beast is a beacon of light when it comes to RESTful best practices and using ActiveResource to integrate would seem to make sence. But mind you that I have no experience with ActiveResource!
June 17th, 2008 at 6:10 am
Hello
Bye
June 18th, 2008 at 2:46 am
Hi all!
G’night
July 18th, 2008 at 3:43 pm
[...] forum in Rails with a scary name and … around 500 lines of code”. And then, the end of a discussion yielded this great advice: Single sign on is very easy. All you have to do is set your session [...]
July 30th, 2008 at 11:54 am
I just wanted to say that I’m working with savage-beast 2.0 and I’m in the middle of trying to rename one of the tables. The posts table conflicts with my blog. I think it would have made more sense for the developer of savage-beast and beast to use beast specific tables for a plugin. But, I do appreciate having this potentially rewarding plugin already written for me. If there are any other tips on renaming a table and all the methods that go with it for savage-beast 2.0, please let me know. Thanks for this blog post!
August 4th, 2008 at 10:49 pm
I am trying the single sign-on,
i tried this code
config.action_controller.session = { :session_key => “_session_id”, :secret => “secret_session” , :session_domain => “.mydomain.com” }
do i have to share the database, to get this working
December 10th, 2008 at 11:14 pm
Neat tutorial. Sometimes I wonder if integrating with something like phpbb or smf would be just as easy – maybe easier in the long run due to the maturity of the product.
December 19th, 2008 at 6:13 am
Yeah, I’m starting to think the same thing. If all you care about is linking the user accounts (like we did), then using phpbb is probably ok. On the down side, you’re probably cutting out any possibility for future integration.
July 15th, 2009 at 9:55 pm
hi all,
I have a rails application and a wiki which is also built in rails. Now i want the single -sign on feature to be added . That is a user logged in the rails app, on clicking a link should be able to navigate to the wiki without signing in again. Also i want to run the wiki as a separate application rather than using as a plugin.
Any help?
Thanks in advance,
Ak
July 16th, 2009 at 9:34 pm
Great posting…I love the template
June 29th, 2010 at 7:48 pm
[...] The Beast forum was set up by Shanti Braford as described in option #3 of this great article on integrating a beast forum into a Rails app. [...]