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

Sunday, January 22, 2012

Database migrations with deployed JRuby WAR files

In my previous post Compiling Rails project for distribution in a WAR file with JRuby, I explained how to build a  WAR file from a Rails project to distribute on to systems that have a Java app server like Tomcat or Glassfish.  If you're running this in production, you're probably going to want to run database migrations after the WAR file is deployed.  Unfortunately this is not as straightforward as you might expect.  But it's not too difficult.  To run database migrations, you must first create a file in your project, config/warbler.rb with the following contents:

Then, add a file named script/db_migrate with the following contents:

Now, on the production system, after the WAR file has been deployed, from the root directory of your web app, run the command:

jruby -S ./script/db_migrate

If you're running in 1.9 mode, add --1.9 before the -S. This assumes that you have a jruby executable in your path somewhere on the server. There should be a way to run the JRuby that is bundled in the WAR file, but I have not spent enough time looking in to it to figure out how. Has anyone had success with this?

Wednesday, January 4, 2012

Rails 3.1 "Could not find a JavaScript runtime" error

I just got my first Rails 3.1 on JRuby project created.  However I keep getting the error "Could not find a JavaScript runtime ..." when trying to do pretty much anything with it.  After digging in to this some, it turns out that with the addition of CoffeeScript in 3.1, Rails needs to run Javascript code natively.  A gem called execjs was added to Rails to allow this.  EXCEPT, execjs itself needs something else to actually evaluate the Javascript.    See https://github.com/sstephenson/execjs for a list.  If you're running in Mac or Windows, you're good, there are system libraries to do it.  But if you're in Linux, unless you have node.js installed, it won't work.

Let me repeat that.  Rails 3.1 apps by default will NOT work in Linux, unless you have node.js installed.  This is absolutely ridiculous... guess what Rails team, a lot of people are developing in Linux and not Mac.  I understand how stuff not working in Windows is released, but Linux??

So to get things to work you'll need to add one of the gems listed on the execjs page.  If you're running the MRI Ruby, just add:

gem 'therubyracer'

to your Gemfile.  I've read a lot of complaints about therubyracer.  But it appears to be the most popular.  If you're running JRuby like me, add:

gem 'therubyrhino'

to your Gemfile.  I've verified that this works correctly.

FOLLOW UP 1/13/2012:
See my comments.  I have made a fix to Rails to put the appropriate gem in the Gemfile if you are in Linux, and issued pull requests to the Rails core to incorporate this code.  They have made some comments on it, hopefully it will be pulled in to Rails soon.

Thursday, December 22, 2011

Fixing FBML Facebook apps that use Facebooker

If you've written an FBML Facebook app using the Facebooker (not Facebooker2) library for Ruby on Rails, I'm sure you've noticed by now that it's not working any more. In early to mid November, Facebook changed the way they send requests to all canvas apps (FBML and iFrame). Previously, Facebook would send a bunch of parameters called fb_sig_... where each parameter represented something, like the ID of the user. Then there was an fb_sig parameter which you could use with your secret key to validate that the request came from Facebook. The Facebooker plugin/gem is coded to use this.

In early/mid November they switched to using a single parameter, signed_request. This contains all of the info encoded as JSON, along with a validation signature. They no longer use a "session" to grant access, access is now granted with an OAuth2 key.

Unfortunately if you're still using the original Facebooker library like me, you've got some work on your hands to get your app back to a working state. I'll describe everything I did to get my app working again here.

Install Facebooker2 as a plugin
Development on the Facebooker plugin stopped long ago, the original maintainer started a new Facebooker2 plugin/gem. Unfortunately Facebooker2 doesn't have support for all of the FBML capabilities. There are some similarities between the two but overall Facebooker2 was designed to be MUCH simpler and easier to use, and it definitely is. To put Facebooker2 in your project:
  1. Download Facebooker2 to your vendor/plugins directory
  2. Install the mogli and ruby-hmac gems (how you do this depends on your Rails version, either install it on to the system, specify it as a required gem in your environment.rb file, etc.)
  3. Download the original Facebooker gem/plugin to your lib folder (or just copy it from your vendor folder if you put the code in your project previously).
  4. Edit your config/facebooker.yml file, and change secret_key to secret for your environments.
  5. Edit config/facebooker.yml, and add an app_id property to each environment for your App ID (which can be viewed from the Facebook app page for your app).
  6. Create a file config/initializers/facebooker2.rb and put a single line of code in it:


Modify your ApplicationController
Now that you've got Facebooker2 installed, you'll have to modify your code to use it instead of Facebooker. Modify your ApplicationController to include this code:

Now that you've got this in place, you'll have to edit the rest of your code.

Client and User
Instead of using facebook_session and facebook_session_secured, you'll want to call the methods current_facebook_user and current_facebook_client. current_facebook_user will return a Mogli::User object. Here is some code showing some of the common things you may want to do:


Facebooker Helpers
To include all of the FBML helpers that the Facebooker library includes, add the following code to your application_helper.rb:


You'll also need to make a few changes to the Facebooker helper file. Edit lib/facebooker.../lib/facebooker/rails/helpers.rb and add the following:


Views
If you have named your view files .fbml.erb, you'll need to rename them to just .erb. The .fbml type isn't registered with Facebooker2.

Publishing Stories
Facebooker had this whole publisher syntax to post stories to the users wall. None of this is necessary any more. If you wish to publish stories to a users wall, as long as the user has granted the offline_access permission to your app, do the following:


OK I think that's all there is to it. I have my Facebook app working with these changes.

The Future of FBML
You probably know that Facebook is completely abandoning FBML on June 1, 2012. Converting your app as I described here gives you some time to convert your app's views over to iframe instead of FBML. However, don't be surprised if stuff stops working before then. For instance, I can't get my app to display with https. I'm doing everything correctly but Facebook appears to not support this. Hopefully this blog post will give you some breathing time before June.

Wednesday, March 9, 2011

Going from Rails development to PHP development

A few months ago, I took a new job where I'm doing PHP development instead of Ruby on Rails, despite the fact that I have over 4 years of Rails experience and no PHP experience. You may be wondering the same thing that some of my former co-workers were wondering when I told them what I'll be doing at my new job - have I lost my mind? Why would I go back to developing with an old technology that Rails is obviously superior to?

As great as Rails is for doing web development, it's still just a tool that you use to produce results. If you're working on a project that's in maintenance mode with all the work being slight variations of what has already been done, not learning anything new, the fact that the project is in Rails doesn't magically make it satisfying and a lot of fun. Likewise, working on new projects in a business domain that you have a personal interest in, where you are a key contributer, is fun and satisfying regardless of whether Rails is being used or not.

I think many in the Rails community lose sight of this. They get so wrapped up in how elegant the code is that you can write in Ruby, how many different things Rails can do for you, and what the very latest way to do things is (even if that new way says that what was the new great way to do things a year ago is totally wrong), and forget that the usability and functionality of the web app that you produce, not the elegance of the code that you write, is what really matters.

I'll get off my soapbox now. I definitely still prefer Rails over PHP. I really miss it when doing things like making RESTful APIs, making changes to the database, and writing test code. But, at the end of the day, I'm finding myself almost as productive doing PHP development as I would be if I was using Rails.

I weighed many different factors when I chose to take this job compared to other potential jobs and staying at my old job - what development language/environment I would use, the company atmosphere, how interesting the projects are to me personally, how imporant/widely used the projects are, how much I can see myself contributing to the success of the company, what the team of people I'd be working with are like, salary/benefits, location, among others. I can now say after taking a chance at a non Rails job, that the language/enviroment is probably the least critical out of those factors that I listed for me personally.

My advice, don't overestimate how important a specific programming language/framework is when you're looking for a new job. In particular, don't think that Rails is so superior to everything else that you can't bring yourself to program in anything else.

Tuesday, September 7, 2010

Upgrade to Rails 2.3.9 session no longer works

I just upgraded to the newly released Rails 2.3.9, and session data stopped getting saved. I could set session data and it was accessible within the same request, but on the next request, the session data is gone.

After digging a little deeper, I found that I was specifying the options for the session in the wrong place. Previously, the session options were specified in environment.rb. Now, Rails has moved this in to a different file, config/initializers/session_store.rb. Simply create this file with the following code:



Change the key to what you previously set as :session_key, and set secret to your previous :secret value. Be sure to uncomment the last line if you're using the database as the session store. Also, be sure to delete all session code from environment.rb after you do this.

However, this still didn't do the trick for the main Rails app that I work on. I'm using ActiveRecord to store session data for this app - data is too sensitive to store in a cookie. After adding the file session_store.rb to config/initializers, data still wasn't getting stored in the session. This appears to be a bug in Rails 2.3.9, as evidenced by ticket #5581. I tried the patch that Mislav posted in the comments of the ticket, but the session still didn't work for me. So, it's back to Rails 2.3.5 for my main app. The ticket is closed, so it appears as if this has been fixed in the Rails code, but I'm not sure if/when a 2.3.10 version of Rails will be released.

Thursday, August 26, 2010

Rails 2.3.8 automatically escaping HTML when you don't want it to

I was upgrading my application to Rails 2.3.8 from 2.3.5 and found a pretty annoying bug in Rails 2.3.8. This bug HAS BEEN FIXED in Rails 2.3.9, so simply install Rails 2.3.9 to get around this problem. However, there are a lot of other problems with Rails 2.3.9, read my posting at Upgrade to Rails 2.3.9 session no longer works for a killer bug for me. Other problems have been reported too. I'm just sticking with 2.3.5.

The bug is that when you concatenate HTML strings in helper methods, Rails will automatically HTML escape the string under certain conditions. There is NO way to tell Rails not to do this. Here is an example that reproduces the problem. Add these two methods to your application helper:


Then simply output the outer_helper method in one of your views:

  <%= outer_helper %>



This is the result:
about to call inner_helper method

inside p content tag

a space should be between the following words: hello&nbsp;worldmore <span style="font-weight:bold;">dirty HTML</span>
inside div content tag

outside of inner_helper method in p tag



This is obviously not what it should be producing. Rails 3 automatically escapes HTML rendered, but you can simply call .html_safe on the output to mark that you don't want it to escape, or call raw(string), from what I've read. But these don't exist in Rails 2.3.8. This bug has been fixed in this commit to the Rails code, which has been included in Rails 2.3.9.

The blog posting at http://breakthebit.org/post/647352254/rails-2-3-8-forced-html-escaping-of-concatenated shows some ways you can get around this, but in my opinion you shouldn't have to work around this. Just stick with 2.3.5, or if you're brave you can try 2.3.9.

Saturday, July 24, 2010

Getting Facebooker app to run in subdirectory

When I set up My Trips, my Facebook app that I used the Facebooker gem for the Facebook interface for, I intended to run it as a subdirectory of my domain name. This way I could set up multiple Facebook apps with the same domain name and IP address. However, no matter what I tried, I could not get everything to work correctly. I thought that it was issues with my web server, Nginx. However, after looking in to this more I found that it was an issue with Facebooker. You'll have to edit a Facebooker source code file. It's best to unpack the gem to your vendor directory, that way you can edit this file for the specific project.

Once you've unpacked it, edit the file vendor/gems/facebooker-1.0.xx/lib/facebooker/rails/facebook_url_rewriting.rb. Modify the relative_url_root method to the following:
class Base
class << self
alias :old_relative_url_root :relative_url_root
def relative_url_root
#Facebooker.path_prefix
'/subdirectory_name'
end
end
end


Where subdirectory_name is the name of the subdirectory that you want to run this app from. This is a bit of a hack, hard coding the directory that you want to run from, but it works. After you do this, you'll then have to configure your web server to run your app in a subdirectory. For Nginx with Passenger, simply add "passenger_base_url /subdirectory_name" to your nginx.conf in the server { } block. Now you can change your Canvas URL to http://www.yourdomainname.com/subdirectory_name.

Sunday, December 13, 2009

Signature verification does NOT work with Facebooker and Rails 2.3

I've been working on a Facebook application called My Trips (http://apps.facebook.com/my_trips/) for several months now in my free time. I am using the Facebooker gem for this project. If you are interesting in developing Facebook applications with Rails, I would highly recommend reading Developing Facebook Platform Applications With Rails, about developing Facebook applications with Facebooker.

At some point I'm going to do a comprehensive write up of my experiences developing a Facebook application with Rails and Facebooker. But until then, I figured I should share one important thing I found.

Facebook passes signatures to your application on every request. The checksum of all signature parameters, plus your "secret key" (a key assigned to your application when it's installed, and never sent in HTTP requests) must equal the signature parameter sent to you by Facebook in params. This ensures that the request actually came from Facebook and not from a third party spoofing Facebook. This is a great security feature. Facebooker automatically checks the signature on every request from Facebook, and throws an exception if the signature doesn't match before your controller code is hit. At least that's how it works with Rails 2.2 and earlier.

With Rails 2.3, Facebooker adds the Rack::Facebook rack extension that is supposed to do this signature verification one level above Facebooker, among other things. However, Rack::Facebook didn't actually verify signatures in my application. I made requests by spoofing Facebook, and just manually typing in URLs without even attempting to spoof, and my requests were processed. They should not have been.

I couldn't really figure out why Rack::Facebook wasn't doing this. Since the signature verification code is in Facebooker and works in Rails 2.2 and earlier, I decided to modify the Facebooker code to do this check explicitly. To do this, edit the facebooker/lib/facebooker/rails/controller.rb file, find the verify_signature method, and comment out the first 3 lines that does a check to see if Rack::Facebook is loaded. When you restart your application, Facebooker goes back to verifying the signature itself. After doing this, my spoofed and manual requests were correctly caught.

Wednesday, May 27, 2009

Exception notifier plugin causing internal server error

I am using the exception notifier plugin to send me emails on the details of all unhandled exceptions in my web app (read about this plugin on my earlier blog posting, Exception notification plugin).  I recently put my web app on a new server, and when an unhandled exception occurred, Internal Server Error was displayed to the user, and no email was sent.  After digging in to this, I found that the exception notification code itself was generating an exception.  The offending file is views/exception_notification/_environment.rhtml.  Turns out there is a syntax error in that file when using Ruby 1.8.6 patch level 110 or higher.  The plugin was fixed in November 2007, but I had downloaded the plugin before that time.  

The fix is described at http://dev.rubyonrails.org/ticket/9940.  Simply edit the plugsin/exception_notification/views/exception_notification/_environment.rhtml file, and change the third line to:
* <%= "%-*s: %s" % [max.length, key, filter_sensitive_post_data_from_env(key, @request.env[key].to_s.strip)] %>



The only difference is that the first *- is changed to -*.

Saturday, December 27, 2008

Status 500 when calling any action on a controller

The other day I added a new controller, and any time I browsed to anything in that controller, the web browser displayed a status 500 Internal Server Error.  There was no debug screen.  I looked in the development log file, and this showed up:

/!\ FAILSAFE /!\ Mon Dec 22 14:13:51 -0500 2008
Status: 500 Internal Server Error
wrong number of arguments (1 for 0)

with a full call stack. After digging around Rails code and looking around on the Internet, I finally figured out what the problem is. You can't have a method called send in a controller. After I renamed the send method to something else, everything worked.

Monday, November 24, 2008

Database Migration Can't Assign/Update Newly Created Columns

I wrote a database migration today that I ran in to some problems with.  The migration simply adds a column to a table, and then assigns a value to the new column.  All appeared to work when the migration ran.  But, when I checked the database afterwards, the column was added to the table but the value that I assigned was not set.  Here is my example:

class AddAndSetColumn < ActiveRecord::Migration

def self.up    
add_column :nodes, :new_column_1, :boolean, :null => false, :default => false

Node.find(:all).each do |n|
n.new_column_1 = true
n.save!
end

end

def self.down
remove_column :nodes, :new_column_1
end
end


After running the migration, and looking at the database, all nodes had new_column_1 set to false, even though I explicitly set it to true in the migration.

After searching around to see why this happens, I found a posting that described this problem at http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/573cbb96b84f3306/a0d6a3c7dddbbbcb?lnk=raot. The solution for this is to call reset_column_information on the model for the table that you added the column to before you assign values. So I changed my migration to:
class AddAndSetColumn < ActiveRecord::Migration

def self.up    
add_column :nodes, :new_column_1, :boolean, :null => false, :default => false
Node.reset_column_information    

Node.find(:all).each do |n|
n.new_column_1 = true
n.save!
end

end

def self.down
remove_column :nodes, :new_column_1
end
end


And it works!

Sunday, November 23, 2008

Using ICE with Ruby on Rails

For those of you who have never heard of it, ICE (Internet Communications Engine) is a really good object oriented middleware platform for communication between applications written in different languages running on different platforms. ZeroC is the company that makes ICE, you can find out more about it at www.zeroc.com.

I've used ICE on mulitple projects for communication of actions to take between the Ruby on Rails web server, and a C++ backend application that performs the actions. I would highly recommend it, if you have any backend application (C++, Java, C#) that you need your Ruby on Rails application to communicate with.

For the most part, setting up ICE to work with Ruby on Rails is pretty simple. If your server is Linux, download the appropriate RPM from ZeroC's site, or download the source code and compile it. For Windows, download and install the ICE for Visual C++ 6.0 file (Ruby support is not in the Visual Studio 2005 or 2008 installation, the C++ 6.0 package can be installed along side either of these).

After this, add the /ruby directory of the ICE install to your RUBYLIB environment variable. This is the directory that Ice.rb gets installed to. For a Linux install, a common location is /opt/Ice-3.3.0/ruby. Then add the /bin directory of the ICE install to your PATH environment variable. Example for Linux installs - /opt/Ice-3.3.0/bin.

After those are added to your environment variables, you should be able to add
require "Ice"
to your Ruby on Rails code to start working with ICE. I typically use slice2rb from the command line to produce a .rb file from my slice definition file before hand, but you can also call slice2rb directly from Ruby. If you do things the way I do, add a require for your slice2rb produced Ruby file.

You must initialize the Ice engine ONCE in your server process. Originally, I was initializing it and destroying it on every action that used Ice. This seemed to work fine for a while, but then I started having strange memory issues in my server related to Ice. I took out the initialize and destroy on every action, and only initialized once, and this cleared up the strange issues.

So what I would recommend is that you make a class variable to hold the Ice runtime in either the controller that calls Ice (do it in application controller if more than one controller calls Ice), or make a separate class to handle Ice. Here is an example of what should be placed at the top of this class (application controller used as an example:
require 'Ice'

class ApplicationController < ActionController::Base

@@ic = nil


Then, for EVERY controller action where you are calling an ICE method, you must do something similar to this
@@ic = Ice::initialize() unless @@ic
proxy = SliceModule::ServerObjectPrx::checkedCast(@@ic.stringToProxy("ServerObject:tcp -h #{server_name} -p #{server_port}"))
# any actions on the server object proxy

Monday, June 16, 2008

Applying individual timestamp migrations

I posted in my previous entry (Timestamp based migrations in Rails 2.1) about the new UTC timestamp based migrations in Rails 2.1. After working with them a little more, I found how to apply or remove individual migrations. Simply run rake db:migrate:up VERSION=YYYYMMDDHHMMSS to apply a single specific migration, or rake db:migrate:down VERSION=YYYYMMDDHHMMSS to remove a specific migration.

However, I found a bug with db:migrate:down as noted at http://rails.lighthouseapp.com/projects/8994/tickets/369-db-migrate-down-does-not-remove-migration-version-from-schema_migrations-table. When you run this, the self.down function of the migration is called and the database is changed. However, the entry in schema_migrations for this migration is not removed. So if you run rake db:migrate later, this migration will not be applied because Rails thinks that the migration has already been applied. My work around for this is to manually delete the row in schema_migrations after I run db:migrate:down.

Wednesday, June 11, 2008

Timestamp based migrations in Rails 2.1

One really cool feature added to Rails 2.1 is UTC timestamp based migrations. Now, when you generate a new migration, instead of the migration number being sequential, it's a UTC timestamp. That way if you're working on different branches of code, you don't need to coordinate between the braches who gets what version number.

But the best part of this is how the migrations are tracked. Instead of just keeping a current database schema number like before, Rails keeps track of each migration that has been run. Then when you run rake db:migrate, Rails only applies the migrations that haven't been run, they don't have to be in order.

As an example, say I added a migration two days ago called add_new_column with script/generate migration. 20080610012942_add_new_column.rb gets generated. My co-worker John is working off of another code branch, and yesterday he added a new migration called change_accounts. 20080610225643_change_accounts.rb gets generated. Then today I add a new migration called delete_state. 20080612020139_delete_state.rb gets generated. I have two migrations, and John only has his one. When I run rake db:migrate, my two migrations get applied, and when John runs it his one migration gets applied. When we sync our code branches, we get each others migration files. Even though John's migration was created AFTER the first one that I created, when he runs migrate, both of my migrations will get added, and his migration that was already added will not get run again. And when I run migrate, only John's migration will get added, even though it was generated before the last migration that I ran.

The way Rails does this is that the first time you run a migration with Rails 2.1, a new table gets created called schema_migrations. An entry is added to this table for each migration that gets run. The old schema_info table is deleted. That way any time you run a migration, it checks each migration file against the database to see if it's been applied to your database yet.

This feature is already proving very useful for me. I no longer have to have migrations completely syncronized between myself and the other developer working on my project. We each work on our own branch, and we merge our migrations together when we're ready to merge all of our code.

A good Railscast video showing this is up at http://railscasts.com/episodes/107.

Wednesday, March 12, 2008

Assigning custom data types to new columns during migration, part 2

In my previous article, Assigning custom data types to new columns during migrations, I showed how you can create columns with custom data types during a database migration. Simply specify :"data type", instead of :integer, :string, :binary, etc. An example would be :"smallint", since there is no direct way to create a MySQL smallint during a Rails migration.

However, I recently found that this doesn't always work. It works fine if you are creating a new table in your migration. But this does NOT work if you are adding or modifying a column in an already existing table, you get the error:
rake aborted!
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]

Has anyone else run in to this problem? It's annoying to have to construct SQL statements to add columns, after all that's what migrations are supposed to get rid of.

Friday, February 8, 2008

How to dynamically update the contents of one select list from selections in a different list

Rails support for making AJAX requests is great, it's very easy to make simple requests perform actions on the page in the background. A common scenario where you might want to use AJAX to make your pages more dynamic is to repopulate a select list in a form when the user modifies other elements on the form. The built in capabilities of Rails seem to have some problems dealing with select boxes where multiple items can be selected while doing these AJAX requests, however.

I will provide a simple case here where you have two select boxes - one is a list of vehicle types, and the other is a list of vehicles. Instead of having one huge list of all vehicles, you might want the list of vehicles to only show vehicles that are of the selected types. Using AJAX updaters form observers, you can dynamically update the vehicle list based on the type list being changed.

For this to happen, you need to place observe_field Rails helpers in your view for the page, to observe for changes to the field. Typically you can specify here what the URL of the action to call is, but observe_field has some trouble sending parameters for select boxes with multiple selections, so it will instead call a Javascript function. In the Javascript function (which isn't necessary if you aren't using a select multiple), you need to manually make create an Ajax.updater to update the "results" selection box items, creating the query string dynamically based on the selected types. Then you need an action in your controller to respond to the Ajax request, and a view for this action to fill in the correct values to the select box.

I created a simple example with a page where you can select what types of vehicles you want displayed (car, SUV, or truck), and whenever you change the type, the list of vehicles will be updated.

First, add two new actions to your controller. The first is the regular action to get the page for the first time, which I'll call show_list. Within this action, the list of types (from the constant TYPES) gets put into a variable for later loading. The second action, update_list, is what the AJAX request will call to update the vehicle list with selected types. Here is the code for the controller:
# these are just some constants.  Most likely you'll want to find things 
# in your database to return.
TYPES = {"car" => [["Honda Accord", "1"], ["Scion Tc", "2"],
["Ford Focus", "3"], ["BMW M6", "4"]],
"suv" => [["Jeep Wrangler", "5"], ["Ford Explorer", "6"],
["Toyota Highlander", "7"]],
"truck" => [["Ford F150", "8"], ["Dodge Ram", "9"]] }

# this is the action to get the page for the first time.  
def show_list
@types_all = [["Car", "car"], ["SUV", "suv"], ["Truck", "truck"]]
end

# this is the action that the AJAX request will call.  types[] will be
# specified in the parameters list, containing the different vehicle
# types that the user has selected.
def update_list
@vehicles_all = []
if params[:types]
params[:types].each do |t|
@vehicles_all = @vehicles_all.concat(TYPES[t])
end
end
# this part is really important, if you don't tell it not to render the 
# layout, the area where your selection box is will have the whole 
# controller's layout around it.
render :layout => false
end


Next, create a new rhtml file in your controller's view folder called show_list.rhtml. This is the initial page that gets loaded:
<% # this is a function I wrote in my application controller to load a
# Javascript file.  show_list.js must be loaded.
add_javascript_file("show_list", false) -%>

<% # I don't actually have anything to respond to this form, it's just to show
# select boxes.
form_tag({}, {:name => "show_list", :id => "show_list"}) do -%>

<p>Vehicle Types:
<%= select_tag "types[]", options_for_select(@types_all, []),
{:size => 3, :multiple => true, :id => "types[]"} %>
</p>
<% # the observe_field must call a function and not directly call a method
# on the controller.  Select form elements with multiple elements do not
# get correctly passed as parameters to the controller action, so we
# have to manually do it in a Javascript function.
# with any other type of field (including single selects), you can
# specify :url as a parameter and it will call that url.
# if :frequency is not present, a request will be made every time the
# selection is changed.  This isn't recommended, because the user may
# very quickly change selections.  0.5 ensures that a request is made
# at most every half second while the user is changing the selection.
-%>
<%= observe_field "types[]",
:function => "typeChanged('vehicle_list', $F('types[]'))",
:frequency => 0.5 %>

<% # the hourglass will show while the browser is waiting for a response
# from the server, to let the user know something is going on.
-%>
<div style="display:none; text-align:center;" id="hourglass">
<%= image_tag "ani-busy.gif", {:width => 32, :height => 32} %>
</div>

<p>Vehicles from selected types:
<% # the whole select tag must be in a div.  This is what will be updated.
# If the browser is Firefox, you can put the select tag ID as the area
# to update in the AJAX call, and return a list of options in your rhtml,
# but this will NOT work in Internet Explorer.
-%>
<div id="vehicle_list">
<%= select_tag "vehicles[]", "", {:size => 9, :multiple => true} %>
</div>
</p>

<% end -%>

Next, create another new rhtml file in your controller's view folder called update_list.rhtml. This is what gets sent to the specific div section that gets updated whenever the type selection list gets updated:
<% # render just the select tag here
-%>
<%= select_tag "vehicles[]", options_for_select(@vehicles_all, []),
{:size => 9, :multiple => true} %>

Finally, add a javascript function to a file that gets loaded with show_list.rhtml:
function typeChanged(areaToUpdate, typesToSend) {
// Manually create a request string with parameters
var queryStr = "/node/update_list";
var started = false;
if (typesToSend) {
for (var i = 0; i < typesToSend.length; i++) {
// the first type must have ? before it to start the parameters list.
// after that, it should be prepended with & to specify an additional
// parameter.
queryStr += (started ? "&" : "?") + "types[]=" + typesToSend[i];
started = true;
}
}

// before we send the request, show the hourglass.
$('hourglass').show();

// manually send an Ajax request to update the area passed in to the
// function.  Upon completion, hide the hourglass.  You can also specify
// functions to run for onError, onSuccess, and many others.
new Ajax.Updater(areaToUpdate, queryStr,
{onComplete: function() { $('hourglass').hide(); } });
}

This may seem like a lot to add for a simple page, but it's very easy to set all this up and work with much more complicated scenarios.

Tuesday, January 15, 2008

New version of Ruby In Steel without Visual Studio license

The folks at SapphireSteel Software have released a new version of Ruby In Steel, called Text Edition, that doesn't require you to already have a Visual Studio license, and it's only $49 at this time!

For those who don't know about it, Ruby In Steel is a full Ruby and Rails development platform for Windows with real time debugging (breakpoints, mousing over and typing variables to see their values), syntax error detection, color coding, indenting, and anything else that you'd want in a full featured IDE. Check my previous blog entry, Ruby In Steel, Ruby on Rails add on for Visual Studio, for a better description. Previously, Ruby In Steel was an add on for Microsoft Visual Studio, you already had to have a Visual Studio license for this to work.

This new version comes with a free version of Visual Studio 2008 that it uses behind the scenes, but you can only do Ruby and Rails development with it. They still sell the Visual Studio add on. The main differences are that the add on version has a faster debugger, and uses the Intellisense libraries that show possible values for objects and attempts to highlight incorrect syntax. If you already have Visual Studio, I'd still recommend the regular version, but if the price is too much the Text edition will still work great. The Intellisense in the full version still doesn't work too well with Rails code (but what can you expect since it's not strongly typed), but it does work pretty well for strict Ruby code. But if you don't have Visual Studio, the Text edition provides a very powerful full featured Ruby and Rails IDE for a great price, I would highly recommend it! You can get it at http://www.sapphiresteel.com/.

Wednesday, January 9, 2008

Exception_notification plugin for automated exception emailing

I found a really useful plugin called exception_notification that I've been using on my servers for several months now. It will send emails to addresses that you configure any time an exception is generated on your server, if the server is in production mode. The email contains the full details of the exception, including the call stack. It's almost as useful as the error page that gets displayed for exceptions in development mode.

The plugin has helped me discover and fix a whole bunch of obscure errors that I never would have discovered on my own or through users complaining.

This may not be appropriate for a server where you have a large volume of users. You probably wouldn't want to get thousands of emails of the exact same error a day especially if it's a bug that you already know about. But it works great for servers with a low volume of users, or for internal test servers. For a test server, your testers won't need to record the details of the error and send it to you separately, it will automatically be sent to you!

To get started, run
script/plugin install exception_notification

Then edit your config/environment.rb file to include the following line, specifying the email addresses to send exceptions to:
ExceptionNotifier.exception_recipients = %w(email_recipient_1@address.com email_recipient_2@address.com)

Another useful option you can add to environment.rb specifies who the sender of the exception should appear as:
ExceptionNotifier.sender_address = "\"App Error\" <apperror@domain.com>"

Then add this to your application controller if you want all actions in your application to generate exception emails.
class ApplicationController < ActionController::Base
include ExceptionNotifiable 

You can also include this only on the controllers that you want to generate exception emails.

The full source for this is at http://dev.rubyonrails.org/browser/plugins/exception_notification. Read the README file there for more detailed information on configuring it.