Wednesday, March 19, 2008

How to disable the image toolbar for images in Internet Explorer 6

By default, when viewing an image on a page with Internet Explorer 6, a little toolbar will appear in the upper left corner when you hold your mouse over the image for a few seconds. This toolbar really gets in the way if you want users viewing your page to do things with the image, like capture mouse clicks, drag the image, etc. Thanks to http://www.thesitewizard.com/webdesign/imagetoolbar.shtml, I found two ways to disable this. To disable the image for a whole page, add a meta tag to your header:
<meta http-equiv="imagetoolbar" content="no" />
Or to disable this on a per image basis, add:
galleryimg="no"
to the img tag.

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.

Thursday, February 7, 2008

Binding a Ruby TCPSocket to a local address and/or port

The Ruby TCPSocket class is a really easy to use class for making outbound connections and sending/receving data. But there's no way to bind to a local address or port for making the connection. The Socket class, which TCPSocket inherits from, has a bind call, but the constructor for TCPSocket immediately attempts to establish a connection using the default any port and any address. So it wouldn't do any good to bind on TCPSocket, since the connection is already made when you create it.

If you need to bind to a local address, you'll have to create a Socket object. The code below will bind to a port and address, and then attempt to connect. After that point, you can read from or write to the socket just like you can with TCPSocket. I'm not sure how to specify any address, or localhost, for in_addr. If anyone knows how to do that, please post here.

require 'socket'

# this is the address to bind to.  The first argument is the port, the second
# is the IP address (a string).  Pass 0 for the port to not bind to a specific
# port (the port will be auto assigned by the OS when a connection is made).
# The address MUST be an actual external address of the computer - 127.0.0.1 
# won't work.
in_addr = Socket.pack_sockaddr_in(12345, "10.10.1.3")

# for the outbound address, you must specify both a port and address
out_addr = Socket.pack_sockaddr_in(80, "www.google.com")

s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
s.bind(in_addr)
s.connect(out_addr)

# from this point you can read from and write to using the standard Socket
# and IO methods.

s.close

Sunday, January 20, 2008

Picture of mine used on Schmap.com

A picture that I took in Montreal in August 2007 and posted on Flickr has been selected to be used by the online guide Schamp.com for their Montreal city guide! Check it out at
http://www.schmap.com/montreal/sights_historic/. Click Bonsecours Market, and cycle through the pictures on the right to find mine.

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.