Showing posts with label rails. Show all posts
Showing posts with label rails. Show all posts

Sunday, August 15, 2010

Helper Testing in Rails 3

This post shows how to test both simple and complicated testers in Rails 3. The method outlined here should work in Rails 2.3 as well, but it might not be the best method.

Summary
The skeleton helper testcase class generated by Rails 3 inherits from ActionView::TestCase. Simple helpers can be tested by comparing their return value with a golden string. For more complex helpers, use render :text to fill the view state with the helper return value, then use assert_select. assert_tag doesn't work.

Simple Helpers
Helpers that produce a single tag, or a string, are probably best tested simply by comparing their output with an expected value. Helpers can be called directly from the test class, as inheriting from ActionView::TestCase will set up an appropriate test environment, which includes a mock controller if necessary.

The code listing below demonstrates this.


Complex Helpers
To me, canonical example of a complex helper is one that includes a call to form_tag or form_for. These methods produce a div inside the form tag, and the contents of the div has been very brittle during Rails 3 development, mostly due to the snowman debate. A test shouldn't depend on the div contents, if all that matters is that there's a form in the helper's output.

This post suggests that at some point, Rails had an assert_tag_in method, which works similarly to assert_select, but takes a string input instead of testing against the current view output. Unfortunately, the method isn't available in Rails 3.

However, ActionView::TestCase provides a render method, which can be used to create fake view output. assert_select checks for the fake view output, but assert_tag doesn't, so your tests will have to stick to the former method.

The code listing below demonstrates this approach.


Motivation
I wrote this post because I wanted to properly test a helper with a form tag. I spent a few hours sifting through blog posts, RDocs, LightHouse tickets and GitHub commits. I hope this post helps other developers avoid duplicating my effort.

Conclusion
Rails 3 makes testing helpers easy, as long as you know what methods to call. I hope you found my post useful, and I'm looking forward to your comments and suggestions!

Wednesday, April 21, 2010

Rails 3 Development Setup for Ubuntu

This post describes the steps needed to set up a fresh Ubuntu installation so you can start playing with Rails 3 and contribute to it

Method
This method isn't the most conventional way of developing on Ubuntu (I do replace the Ubuntu-supplied RubyGems binaries, so that I can get the latest version, and get gem binaries on my path). It also is not the most secure method (you will create install a ton of packages and create some random users on your database servers). It's quite fast and effective for development, though.

I have tested the steps below on Ubuntu 10.04 (Lucid Lynx), and I have good reasons to believe that they will work on Ubuntu 9.04 (Karmic Koala) as well.

Start off by installing some Ubuntu packages:
sudo apt-get update; sudo apt-get dist-upgrade
sudo apt-get install ruby rubygems ri ruby-dev libopenssl-ruby build-essential mysql-client mysql-server libmysqlclient-dev postgresql postgresql-client libpq-dev sqlite3 libsqlite3-dev curl git-core git-completion git-doc git-gui

Override Ubuntu's crippled RubyGems with the proper version:
sudo gem install rubygems-update
gem env | grep 'EXECUTABLE DIRECTORY'
sudo `gem env | grep 'EXECUTABLE DIRECTORY' | ruby -e "puts gets.split(': ', 2).last"`/update_rubygems

Create the rails user in MySQL:
echo "GRANT ALL ON *.* TO 'rails'@'localhost';" | mysql -uroot

Create a superuser in PostgreSQL:
sudo -u postgres createuser --superuser $USER
echo "\\password $USER" | sudo -u postgres psql

Install rvm:
sudo gem install rvm
rvm-install
echo "if [[ -s /home/$USER/.rvm/scripts/rvm ]] ; then source /home/$USER/.rvm/scripts/rvm ; fi" | cat - /home/$USER/.bashrc > /home/$USER/.bashrc2
rm /home/$USER/.bashrc; mv /home/$USER/.bashrc2 /home/$USER/bashrc


Install the Rails-preferred VM and create a clean gem set:
rvm install 1.8.7
rvm use 1.8.7
rvm gemset create rails3
rvm gemset use rails3
gem install bundler mocha

Prepare to work on Rails (do this every time you start a shell):
rvm use 1.8.7
rvm gemset use rails3

Check out Rails and install its dependencies:
git clone git://github.com/rails/rails.git
cd rails
bundle install
rake mysql:build_databases
rake postgresql:build_databases

Run the Rails test suite. If it passes, your environment is all set up. 
rake  # This should be run in the rails directory.

If you want to create a test application, then you should install the gems from the edge Rails:
rake install  # This should be run in the rails directory.

Happy hacking!

Motivation
At one of Boston.rb's hackfests, Dan Pickett held a workshop on contributing to Rails 3. I think I was the only one in the room not using OS X, and I had to follow a slightly different sequence of steps to get my setup working. I figured it's nice to summarize all the steps for future reference.

Conclusion
Thank you for reading! I hope the post above has been helpful, and hope you'll have fun playing with Rails 3. I look forward to your comments and suggestions for improving the method outlined here.

Thursday, January 14, 2010

Hosting Ruby on Rails on scripts.mit.edu

This post describes a method for developing a Rails application and deploying it on scripts.mit.edu, a free hosting service for MIT students and staff. The information here is primarily intended for students in MIT's Web Programming Competition (6.470), but might be useful for other MIT students and staff, as well as for Rails developers deploying in constrained environments.

Summary
The first part of my post shows how to create a Rails application on scripts.mit.edu. By default, the application will use the Rails version that is installed on scripts, which will usually be behind the edge version. The second part of the post copies the application to the developer's computer, and modifies it to use the latest version of Rails. The third part of the post describes the steps needed to deploy a new version of the application back on the scripts.mit.edu servers.

Creating the Application
In the steps below, replace costan with your Athena username, and substitute sixprep with the name of your application.
  1. SSH into Athena: ssh costan@linux.mit.edu
  2. Sign up for scripts.mit.edu and MySQL: add scripts; signup-web; signup-sql
  3. Generate the Rails application: add scripts; scripts-rails app_name
    1. When prompted to enter 1 or 2, enter 1 (you're a user, not a group).
    2. Add the application name to the Desired address: field. For example: http://costan.scripts.mit.edu/app_name
    3. When asked to press Enter to continue, check your application's directory. It should be something along the lines of ~/web_scripts/app_name
  4. Verify that the application works by pointing Firefox to http://costan.scripts.mit.edu/app_name/
Move to the Latest Rails Release
The scripts-rails tool uses the rails command on the server,
  1. Open a command shell in your Eclipse workspace: cd ~/workspace
  2. Copy the application: scp -r costan@linux.mit.edu:~/web_scripts/app_name .
  3. Go into the application: cd app_name
  4. Hack around a configuration bug: ln -s . public/app_name
  5. Install the latest version of Rails: sudo gem install rails
  6. See what the latest version of Rails is (this example assumes 2.3.5): rails -v
  7. Edit config/environment.rb to say RAILS_GEM_VERSION='2.3.5'
  8. Update the configuration files: rake rails:update
  9. Add a requirement (the line below) for the Rack gem to your app's config/environment.rb:
      config.gem 'rack', :version => '1.0.1'
  10. Vendor Rails: rake rails:freeze:gems VERSION=2.3.5
  11. Vendor any other gems that your application uses: rake gems:unpack:dependencies
  12. Follow the steps below to deploy the application back to scripts.mit.edu, and verify that it still works.
Deploy Back on Scripts
The command sequence below will update your application in an almost safe way.
  1. Go into the application directory: cd ~/workspace/app_name
  2. Copy the application to scripts: scp -r . costan@linux.mit.edu:~/web_scripts/app_name
    NOTE: I'm using scp for simplicity. You'll probably want to put sixprep under version control, then remove web_scripts/app_name and check it out from the repository.
  3. SSH into a scripts server:
    ssh costan@linux.mit.edu
    ssh -k scripts

  4. Go into the application directory: cd app_name
  5. Build any new native gems you might have added: rake gems:build
  6. Apply database migrations: RAILS_ENV=production rake db:migrate
NOTE: earlier versions of this post included an extra step for killing FastCGI workers that are caching stale code. The scripts.mit.edu software improved, and this step is not necessary anymore.

The upgrade process is almost safe. The following things can go wrong:
  • a FastCGI worker is spawned with new code, works on the old database schema, and trashes the data (window of opportunity: HTTP requests between steps 2 and 6, assuming the request makes FastCGI spawn a new worker process)
  • a FastCGI worker caching the old code works on the new database schema, and trashes the data (window of opportunity: HTTP request between steps 6 and 7, assuming the requests hits a live FastCGI worker)
It shouldn't be too hard to figure out a solution to these problems, but I decided the windows of opportunity are small enough that I don't care. If you do find a nice and easy solution, please let me know in the comments.

Discussion
This is my method for hosting Rails applications on scripts.mit.edu. It is simple, and it allows me to use the latest version of Rails. There are other strategies that work well.

For example, you could use the version of Rails on scripts.mit.edu (rails -v to find the version on scripts, then gem install rails --version 2.3.2 to install a specific version of Rails). The scripts.mit.edu team back-ported the fixes for security vulnerabilities since 2.3.2 came out, and intends to do so in the future. This might be a better solution, if you don't plan to be proactive about testing and updating your application whenever a new Rails release comes out.

One more thing - in case you're wondering, you will not be able to use scripts.mit.edu with its current software with Rails 3. The reason is that the scripts server use Ruby 1.8.6, and Rails 3 requires Ruby 1.8.7 or above. Unfortunately, Ruby cannot be vendored :)

I hope you found this post useful, and I'm looking forward to your questions and suggestions in the comments.

Acknowledgements
Alice Li and Christopher Luna helped me iron out some bugs in this post by following earlier versions of it.

Saturday, January 9, 2010

Ruby on Rails Development Setup

This post documents my development setup for Ruby on Rails on Linux, MacOS X, and Windows. The Linux-specific instructions are geared towards Ubuntu, but should be easy to translate to other distributions.

Platform-Dependent Steps
The first part of the setup is installing the Ruby programming language, the Rubygems package management library, and the MySQL and SQLite database software. Due to the binary nature of the packages, the instructions are different for each OS.

Keep in mind that Windows and MacOS X differ in various ways from Linux, which will likely be your production environment. For best results, I strongly recommend installing Ubuntu as a dual-boot, or using Virtual Box to get an Ubuntu VM.

Ubuntu Linux
The steps below have been tested on a fresh installation of Ubuntu 9.10 Desktop.
  1. Update your package repository: sudo apt-get update
  2. Install Ruby: sudo apt-get install build-essential ruby irb rubygems libopenssl-ruby
  3. Install MySQL: sudo apt-get install mysql-client mysql-server libmysqlclient-dev
  4. When prompted, leave the MySQL root password blank. This will make it easy to share a database.yml file with the rest of your team.
  5. Install SQLite:  sudo apt-get install sqlite3 libsqlite3-dev
  6. Find out the system-wide RubyGems binary path (pick the one that is not in your home directory): gem env gempath
  7. Add the path to the system-wide binary path: sudo echo "PATH=/var/lib/gems/1.8/bin:$PATH" > /etc/profile.d/rubygems1.8.sh
  8. Reboot the system so the path change goes into effect.

MacOS X
The steps below have been tested on a fresh installation of MacOS X 10.6: Snow Leopard.
  1. Install Xcode from Apple's Developer site, or from the MacOS installation DVD.
  2. Go to the MySQL download site, and download the Mac OS X (package format) for your OS (Leopard: 10.5 x86, Snow Leopard: 10.5 x86_64). 
    1. You don't have to register, there is a No thanks, just take me to the downloads! link on the mirror page.
    2. Install the mysql- pkg file.
    3. Install MySQLStartupItem.pkg.
    4. Install (by double-clicking) MySQL.prefPane, and choose to Install for all users. Start the MySQL server, and check the Automatically Start MySQL Server on Startup box.
  3. In the Platform-Independent Steps, substitute gem install mysql with the following command, taken from the Riding Rails blog: sudo env ARCHFLAGS="-arch x86_64" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
Windows
The steps below have been tested on a fresh Windows XP SP3 32-bit installation.
  1. Install Firefox: www.getfirefox.com
  2. Install the latest stable release of 7-Zip: http://www.7-zip.org/
  3. Launch the 7-Zip File Manager (in Start > All Programs > 7-Zip). From the menu, select Tools > Options, and check .7z under Associate 7-Zip with. Click Ok to dismiss the dialog, and exit the file manager.
  4. Go to the OneClick Ruby download site.
    1. From the latest RubyInstaller, download the rubyinstaller-1.8.6 executable. Install in the default path c:\Ruby and "add Ruby executable to your path".
    2. From the latest Development Kit, download the devkit- archive. Extract its contents in c:\Ruby
  5. Go to the MySQL download site, and download the Windows MSI Installer (x86). 
    1. You don't have to register, there is a No thanks, just take me to the downloads! link on the mirror page.
    2. After downloading, do a Complete Install.
    3. At the end of installation, check Configure the MySQL server now, and uncheck Register the MySQL server now.
    4. When configuring the server, select Detailed Configuration, then Developer Machine, then Multifunctional Database. Accept the defaults, until the Install as Windows Service option shows up, then select both Install as Windows Service and Include Bin Directory in Windows PATH. For ease of development, don't change the root password (so it stays blank).
  6. Go to the SQLite download site, under the Precompiled Binaries For Windows section. Download the sqlite- and the sqlitedll- zip files, and extract them into c:\windows\system32.
  7. Restart your computer so the PATH changes go into effect.
  8. You can go into a Ruby-enabled command prompt via Start > All Programs > Ruby 1.8.6 > Start Command Prompt with Ruby.
  9. In the Platform-Independent Steps section, substitute gem install mysql with the following command. gem install mysql --source http://gems.rubyinstaller.org
Platform-Independent Steps
The second part of the development environment setup is identical in all OSes, with one small exception. The gem command should be executed with superuser privileges on Unix systems (Linux, MacOS X). To achieve that, all gem commands should be prefixed by sudo. For example, gem update would become sudo gem update.

Issue the following commands, and you're done setting up!
gem update --system
gem update
gem install rdoc 
gem install rails
gem install mysql
gem install sqlite3-ruby
gem install json mongrel ruby-debug-ide

Motivation
My post is intended to help the students in MIT's Web Programming Competition (6.470) who choose Rails as their back-end platform.

I hope you have found this post to be useful. Please leave a comment if you have trouble using the instructions above, or if you have a suggestion for improving these steps.

Saturday, November 14, 2009

Quick Way to See Your Gems' Documentation

Summary
The fastest way to see the RDoc for your installed gems is to type the following command into a terminal, and point your Web browser to the address it returns (usually http://localhost:8808)
gem server

Details
The Web server created by the gem server command contains the RDocs for all the gems you have installed, unless you disabled RDoc generation when you installed your gems.


Motivation
I was used to either searching via Google, or going to a gem's RubyForge page to see the RDoc for the gem.

Initially, it seemed that the bit of effort is worth not having to learn some way to generate and bring up the RDocs myself. To my surprise, the procedure is very simple, and it's totally faster than browsing to some Web site that has the RDocs.

That aside, using a local server has the advantage that you'll see the RDocs corresponding to the exact versions of your gems. And, last but not least, RubyForge seems to be slowly falling into oblivion, and newer gems don't seem to bother publishing their RDocs.

I hope this post saves you some time.

Saturday, July 25, 2009

Rebuild Your Ruby Gems If You Update To Snow Leopard

This post contains a command that you must absolutely issue if you are a Ruby developer upgrading to Snow Leopard.

The Commands
Update: Type the following command in terminal:
sudo gem update --system; sudo gem pristine --all
rubygems will produce some errors which are safe to ignore

For historical reasons, here's my initial solution to re-compiling all the native gems.
Fire up irb, and type the following command:
`gem list`.each_line {|line| system "sudo gem install #{line.split.first}"}

Motivation
Snow Leopard introduces a disrupting change: everything runs in 64-bit mode by default. Most importantly to me, ruby is now 64-bit. This is a problem when upgrading to Snow Leopard, as opposed to doing a fresh install, because the Ruby extensions in your old gems are probably 32-bit.

A quick solution to this problem is getting all the extensions rebuilt, which is done by reinstalling all the gems.

In case you're wondering, gem update won't do the trick, because it will not rebuild all your gems.

Symptoms
If you're lucky, you'll get a library loading error when trying to use some gem with an extension (example: json), and you'll figure out rather quickly that you need to reinstall the gem.

A subtle symptom of the same bug is experiencing slowdowns running a Rails development server. In my case, webrick was really slow - start-up took about 30 seconds. For this reason, it's better to re-compile all the gems, as oppose to fire-fighting load error messages.

I hope this post saves you some time.

Wednesday, April 15, 2009

App Engine supports Ruby! Sort-of.

This post is a follow-up to my Great Time To Be a Web Programmer post, where I assert that HTML / CSS / JavaScript are the technology to learn in 2009, if you don't know them already. In that post, I said that Google's App Engine only supports Python, and that has changed. I am writing this quick update so my blog's readers are aware of the change in the Web application hosting landscape.

Java leads the way to Ruby
As of early April, Google's App Engine supports Java. The really good news here, if you don't care for low-productivity languages, is that the Java 6 VM provided by the App Engine has near-native performance, and most high-level languages have interpreters written in Java.

This opens the route to my favorite language, Ruby, being available on Google's App Engine. appengine-jruby is an experimental open-source project aimed at making Jruby available for the App Engine, and at implementing Ruby-esque abstractions over Google's APIs. At the same time, Ola Bini from ThoughtWorks took the time to get Rails to run on the App Engine, and wrote a blog post documenting his method.

There is still a devil in the details, however. According to Ola Bini, performance is nothing to write home about, and developers still have to zip up their source code to work around App Engine's 1,000 file limit.

Why this matters 
I think Google's App Engine is an important cloud-hosting platform because of its generous free tier. It is the best solution that I know for hosting hobby projects, or for a project's incubating phase

Conclusion
Sooner or later, Rails applications will run seamlessly on Google's App Engine. I believe it will happen sooner rather than later. Once Rails 3 shows up in the horizon and delivers on its promise of modularity, developers will be in a good position to rewrite the right parts for the App Engine.

In the bigger picture, the reality is shifting towards my guess that the cloud hosting platforms will soon support all the high-level programming languages. So the last programming language that you will have to learn for Web development is JavaScript, because the browser is still tied to it.

I hope that you have found this post useful, and look forward to your comments.

Sunday, January 11, 2009

Date, time, and datetime validation in Rails 2.x

This is a very short post pointing to a good plug-in for validating time stuff in Rails. One-line summary: use validates_timeliness.

I wanted to validate the format of a datetime field in one of my models, so I tried validates_date_time and saw that Rails doesn't have it installed. I googled validates_date_time, and stumbled upon the plug-in with the same name by Jonathan Viney.

I installed the plugin, fixed the unit tests to run on Rails 2.2 with sqlite3, and then proceeded to write my validation, complete with unit tests. All the code is included, for your pleasure. After some booring debugging, I came to the conclusion the plugin can't handle invalid formats on new models, because of how it's using _before_type_cast to determine whether its parser failed or succeeded.

I googled for datetime validation in rails 2.2, hoping that someone fixed the bug. I didn't find a fix, but instead stumbled over the validates_timeliness plugin, written by Adam Meehan. In case you're seeing this in plain text, the link to the plug-in is http://github.com/adzap/validates_timeliness/

I hope Google indexes my post, and that it helps you avoid going through the same trouble that I have.

The code I used for this post:

Sunday, November 23, 2008

Some Tricks for Background Processing in Rails

This post describes a bunch of tricks that I'm using to do background processing in my Rails applications, Movie Nights and MIT's 6.006 Course Site.

If you want the whole picture, read ahead. If you just want to see the tricks, skip the next section. There's also code at the end, for your copy-pasting pleasure. And it all works with the newly released Rails 2.2.

Big Picture
I'm doing my background processing in one or more long-lived processes. I keep their code in script/background, and use simple-daemon to make them... daemons. I use Starling to pass messages between my Rails front-end processes and the background processors. I don't like starting anything by hand, so I use daemonz to start up Starling and my daemons.

Trick 1: ActiveRecord + Long-Running Process
When using ActiveRecord in long-lived processes, you'll see their connections drop. You know you're experiencing this if you see the following in your logs
Error processing task - ActiveRecord::StatementInvalid: Mysql::Error: MySQL server has gone away:

I fixed this problem by having ActiveRecord re-check its connections every time a Starling request is processed:

ActiveRecord::Base.verify_active_connections!


If your background tasks are really long, and you're working with your database (for instance, updating some status when a task completes) you might need to run the line above several times during a task.

Trick 2: ActiveRecord + Fork
I know I'm not supposed to fork. But I need to. My course website runs student-submitted programs, and then processes their stdouts. So my background processor daemon needs to fork/exec to be able to run those programs.

The problem is, forking copies the parent's memory. So ActiveRecord's connection pools will get copied. And when the child exits, ActiveRecord will close the database connections, and screw over the parent process. spawn used to handle this for me, then it stopped working in Rails 2.2. So, after some experimentation, the following seems to be the most concise fix:

ActiveRecord::Base.connection_handler.instance_variable_set :@connection_pools, {}


Keep in mind that my forked children run non-rails code, or exec something else right away. I haven't tried using ActiveRecord in them, and I suspect it would break.

Code
My daemonz configuration (daemonz.yml) for Starling and the background processor is below. I have 4 task processors, because the production server has 4 cores and therefore can run 4 student submissions in parallel.



My background processor boilerplate code is below. It uses two queues, pulls and pushes, and tasks in pulls have priority over tasks in pushes. It also works with daemonz.yml to get multiple instances of the same daemon, which is non-trivial when using simple-daemon.



The code above calls into OfflineTasks, which I define in lib/offline_tasks.rb. The code for my course site is below. Each background task has a method that pushes it into Starling, and a case branch that executes it when it's popped from the Starling queue.



And finally, a snippet of code that I use to fork:



Thanks for reading! I hope you found this useful.

Monday, September 22, 2008

Daemons in Ruby on Rails applications

Summary
I have created a Rails plug-in, daemonz, that automatically starts and stops the daemons associated with an application, without too much configuration. It works in both production and developer mode. Read below if you care about the motivation behind not using an existing tool.

The Story
In the life of every Ruby on Rails app, there comes a time when the boundaries of a single process are simply too narrow. It might be the need for speed-ups (memcached), full-text indexing (ferret), or background work (simple-daemon), or something entirely different. One thing is certain - daemons will start accompanying your applications.

My application, Movie Nights, desperately needed daemons for all the above reasons. I've been putting that off for as much as possible, because I didn't want to make my developers learn how to start all these processes, and I didn't want the hassle of having to remember all that. When I really had to do this, I knew there is only one right answer: automate.

So I looked into monit and god. They're awesome for the production environment, which didn't really help - I already have a deployment tool, which can handle daemons. I wanted something that would work under the development environment, and would automatically start/stop everything, and I accepted that I'll have to build my own.

Sadly, I don't have time to do this the right way (an integrated system that manages all the dependencies of a Rails app), so I settled for the quick way. Enter daemonz, a bandaid (not really a full solution) for the problem I've described above. Once it is configured, it knows how to start and stop the daemons in most non-trivial circumstances, e.g. having a cluster of app servers, or having the Rails framework brought up by tools. It's not a nice and elegant solution, but it works, and it took a few hours to code.

Enjoy!
(If you're not a Rails programmer, hopefully you can at least enjoy the speed that Movie Nights has gained by performing all its Facebook API calls in a background process.)