A collection of computer systems and programming tips that you may find useful.
Brought to you by Craic Computing LLC, a bioinformatics consulting company.

Wednesday, October 27, 2010

Searchlogic versus MetaSearch and Rails3

Searchlogic is a great Rails gem for basic searching of your tables. I've used it for quite a while now. Problem is that at the time of writing (October 2010) it does not work with Rails3.

You can install it just fine but starting up your server will produce and error like this:
/Users/jones/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.1/lib/active_support/core_ext/module/aliasing.rb:31:in `alias_method': undefined method `merge_joins' for class `Class' (NameError)

What to do? Well there is an alternative search gem called MetaSearch that has a very similar interface to Searchlogic and which does work with Rails3. In fact it may be a little better.

In my code I have search code in three places - the index action of my controllers, a search form at the top of my index view pages and column headers on my index view pages that will reorder the matching rows when I click on them.

So here are the three code blocks in Searchlogic and then MetaSearch (with a very simple model where I'm simple searching the name column). Note that I'm using will_paginate in both cases.

Controller Index Action

  def index
@search = Product.search(params[:search])
@search.order ||= :ascend_by_name
@products = @search.all.paginate :page => params[:page], :per_page => 20

  def index
@search = Product.search(params[:search])
@search.meta_sort ||= 'name.asc'
@products = @search.all.paginate :page => params[:page], :per_page => 20

View Index Page Search Form (omitting some of the html formatting)

<% form_for @search do |f| %>
<p><%= f.label :name_like, 'Name' %>
<%= f.text_field :title_like, :size => 15 %>
<%= f.submit "SEARCH" %>
<%= @search.count %>Matches</p>
<% end %>

MetaSearch - NO CHANGE!

View Index Page Column Headers (just showing a single column header)

<%= order @search, :by => 'name', :as => 'name'.humanize %>

<%= sort_link @search, 'name', 'name'.upcase %>

All in all, a pretty straightforward replacement.

But there is at least on additional bonus, over and about working with Rails3. 'attr_searchable' and 'assoc_searchable' allow you to specify in your models exactly which fields can and cannot be searched. Searching in either Searchlogic or MetaSearch uses GET requests which display the search parameters in the URL in the browser. That opens the door to people trying out other likely field names and searching otherwise private data. This mechanism provides a way to limit that problem.

I have no doubt that searchlogic will get updated soon, as so many people use it. But until then MetaSearch is the way to go.


Friday, October 22, 2010

Passenger 3, nginx and rvm on Mac OS X 10.6

I wanted to set up the web server nginx, an alternative to Apache, along with Phusion Passenger on my Mac. I've become a convert to RVM as a way to manage multiple versions of Ruby so I needed to setup Passenger (a Ruby gem) through RVM. Whenever you have a combination like that you can run into installation problems.

Here are the steps that I used (after a couple of false starts):

1. Make sure your rvm setup is working correctly. In particular I've found it best to ignore your system Ruby installation completely and in fact I install the system version (currently 1.8.7) separately under rvm and make that my rvm default. You have to reinstall your gems, a pain, but I think it the way to go - everything lives under rvm.

2. Do not install nginx! Passenger will do that for you. If you already have an installation then rename it or just ignore it.

3. Turn off Apache or Nginx if you have either of them running.

4. Put yourself into your default Ruby and install the Passenger gem
$ rvm default
$ gem install passenger

5. Run passenger-install-nginx-module with rvmsudo

Very important - don't use regular sudo - if you do it will complain about not finding the current gemset.
$ rvmsudo passenger-install-nginx-module
The script will ask you if you want a default installation or a custom/advanced on. I just did the default (option 1).

The script downloads and compiles nginx. It will ask you where you want it installed. I suggest /usr/local/nginx. Don't just say /usr/local as that will create conf, html and logs directory right in /usr/local. You really want these in a nginx specific directory.

All being well the compilation and installation will go smoothly and your fresh installation of nginx will be modified for use with passenger.

If I remember the installation correctly it actually fires up nginx and leaves it running. Check that with 'ps ax | grep nginx'

6. Go to your browser.
'http://localhost' should give a 'welcome to nginx' page. Look in /usr/local/nginx/logs/ for logging output.

7. Configuration

I set up a symlink in /usr/local/sbin to the nginx binary so that it can be found in my regular PATH
$ sudo ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/nginx

You start nginx with 'sudo nginx' and shut it down with 'sudo killall nginx'. It needs to be run via sudo.

Configuring nginx is done in /usr/local/nginx/conf/nginx.conf and if you are used to Apache config files this will be a breath of fresh air. The main changes I made from the default was to set myself as the 'user' and set the worker_processes to 2.

To set up a Rails application simply add a server block like this:
server {
listen 80;
server_name myapp.local;
root /Users/jones/Documents/myapp/public;
passenger_enabled on;
rails_env development;

Be sure to set the rails_env unless you are in production (the default) otherwise it will not work. Restart nginx and with any luck you'll be able to access your application.

I made a few missteps doing my installation - such as thinking I needed to install nginx myself - but overall this went very smoothly.

8. Extra credit

If you want to use rvm and passenger to run multiple apps with multiple versions of Ruby then you will want to look at this post by Phusion:

As you see it can get really complicated, but as a way to migrate a Rails app from 2.x to 3.x and/or Ruby 1.8.x to 1.9.x, this seems like the way to go.


Thursday, October 21, 2010

Setting Environment Variables for Sudo on Mac OS X

On Mac OS X you typically set up Unix environment variables in your ~/.bashrc or ~/.bash_profile files. To perform actions that need Root privileges you use 'sudo' instead of 'su'.

But when you 'sudo' a command your environment does not pick up your entire user environment and can cause problems in some cases.

To illustrate the issue, compare the output of these two commands in a Terminal (Unix shell):
$ printenv
$ sudo printenv

So how do you make a custom Environment variable visible to sudo? Rather than trying to modify system wide files like /etc/profile (which may not work anyway...) you want to modify the file /etc/sudoers.

Now, to modify this file you want to use the special command 'visudo', which as you can surmise, is a version of the editor 'vi'. Specifically it knows which file to edit and how to set the permissions on it. NOTE: If you don't know how to edit a file with 'vi' then learn the basics BEFORE you try the following steps!

$ sudo visudo
will bring the editor with the file opened. Look for this section:
# Defaults specification
Defaults env_reset
Defaults env_keep += "BLOCKSIZE"
Defaults env_keep += "COLORFGBG COLORTERM"

The env_keep lines indicate that the named environment variable should be made available to sudo. So you want to add your custom variable to this list by adding lines like this at the end of this section:
Defaults        env_keep += "APXS2"
And I would suggest adding a comment line (preceded by a # character) that makes it clear that this is a custom change. Save the file and look at the output of sudo printenv:
$ sudo printenv
Your custom environment variable should now be available.


Wednesday, October 20, 2010

Backtick operator in Jruby 1.5.3 not working

The backtick operator in Ruby (e.g. `date`) executes a system command.

In Jruby 1.5.3 this is not working and in my example caused some odd side effects. This is a known issue (http://jira.codehaus.org/browse/JRUBY-5133) that I'm sure will be fixed in the next release.

For now, use Jruby 1.4.1 as backticks seem to work fine here.


rvm on Mac OS X Snow Leopard

rvm, the Ruby Version Manager, is a great way to manage multiple Ruby versions. The documentation is extensive but I find it to be fragmented, such that if you do run into problems you have to look in multiple locations on the site to find relevant guidance.

On Mac OS X 10.6 (Snow Leopard) I had not problem installing rvm or ruby 1.9.2 but it blew up on my when I tried to list the gems under 1.9.2. The solution was to install zlib under the rvm tree:
$ ruby -v
ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin10.2.0]

$ rvm package install zlib
$ rvm install 1.9.2 -C --with-zlib-dir=$HOME/.rvm/usr

$ rvm use 1.9.2
$ gem list
$ gem install rails --pre

I had a similar problem when installing ruby 1.8.7 under rvm. This time it was the readline library that was the problem. Again, if you poke around in the docs you can find a fix:
$ rvm package install readline
$ rvm install 1.8.7 -C --with-readline-dir=$HOME/.rvm/usr

Although rvm makes it easy to switch back to your system ruby (with 'rvm system') I would strongly suggest that you do not use this. Instead, install the same release as your system ruby under rvm (1.8.7 in my case) and make that the default ('rvm --default 1.8.7').

You will have to reinstall gems under that ruby installation, which is a pain, but once you have all your rubies under rvm then you should have no issues with confusion over where specific libraries are located, which has been an issue for me more than once.

rvm also has a notion of gemsets, which I have not explored as yet, which allow you to isolate specific gem versions for specific applications. This could be extremely useful in migrating Rails applications to newer versions of Rails.

One area where I have not yet been successful is getting rvm and passenger to work. The rvm docs explain how to do this - but no luck for me as yet.


Tuesday, October 19, 2010

!! in Ruby

I noticed this odd looking construct in Rick Olsen's restful_authentication plugin a while back:

At face value it appears to be a 'double not' operation on the variable current_user. But that didn't make much sense to me at the time. I wondered if it was some special Ruby operator, but I could find no reference to it.

After a couple of experiments I see now that it is indeed a double not operator. It serves here as a very concise way to return true or false depending on whether the variable is nil or not. Here is how it works in an irb shell:
>> foo = nil
=>> nil
>> !foo
=>> true
>> !!foo
=>> false
>> foo = 1
=>> 1
>> !foo
=>> false
>>> !!foo
=>> true

Concise is good, but not at the cost of being unclear. I'm on the fence about this one.


Wednesday, October 13, 2010

Stripping non-ASCII characters from text in Ruby

I need to get rid of occasional non-ASCII characters in otherwise plain ASCII text, such as 'curly quotes' like “ and ”. I don't know the real encoding of my source text but I can tell that the characters are encoded as hexadecimal characters such as \x94

Here is the regular expression I use to remove them:
str.gsub!(/[\x80-\xff]/, '')

I'm sure this won't work in many cases but with my text it does the job just fine.


Monday, October 11, 2010

/etc/paths.d as a way to configure Paths in Mac OS X

Just found out about a simple way to manage paths to installed software in Mac OS X

As well as, or instead of, setting PATH environment variables in startup files, you can simply add a path as a file to the directory /etc/paths.d

For example, here is a command that sets up a path to a custom installation of MongoDB

$ sudo sh -c 'echo "/Users/jones/Documents/mypath/mongodb/bin" > /etc/paths.d/mongodb'

Start up a new shell and you will find that path added to your PATH environment variable.

Note that this is a system wide setting and so it gets set early in the process of shell creation. If you need to override PATH settings for specific users then you still need to set paths in .bashrc, etc. I don't know the order in which /etc/path.d files are sourced but I would assume alphabetical.

Also note that you need the 'sh -c ' construct in order to create the file as shown above. Simple 'sudo echo' will not work.


Archive of Tips