Main

Ruby 1.9.1 is the bomb

May 12 by Andre

More on this soon. All I can say for now is:

Ruby 1.8.6:

Completed in 3368ms (View: 2256, DB: 8) | 200 OK [http://hotspotr.com/wifi/list/14-austin-tx]
Completed in 3668ms (View: 2420, DB: 8) | 200 OK [http://hotspotr.com/wifi/list/14-austin-tx]
Completed in 3312ms (View: 1964, DB: 12) | 200 OK [http://hotspotr.com/wifi/list/14-austin-tx]
Completed in 3296ms (View: 1896, DB: 8) | 200 OK [http://hotspotr.com/wifi/list/14-austin-tx]

Ruby 1.9.1

Completed in 989ms (View: 593, DB: 7) | 200 OK [http://hotspotr.com/wifi/list/14-austin-tx]
Completed in 1035ms (View: 591, DB: 6) | 200 OK [http://hotspotr.com/wifi/list/14-austin-tx]
Completed in 1055ms (View: 588, DB: 7) | 200 OK [http://hotspotr.com/wifi/list/14-austin-tx]
Completed in 985ms (View: 592, DB: 7) | 200 OK [http://hotspotr.com/wifi/list/14-austin-tx]

Continue reading "Ruby 1.9.1 is the bomb

Get your character encodings synched up

May 01 by Andre

With Ruby 1.9 out there and all the multibyte string goodness it brings, it's a good time to think about your character encodings. Here are a few pointers on getting everything synched up.

1. MySQL encoding.

How to check it: I use Sequel Pro ... just click on a table name for the metadata:

How to change it if it's wrong:

I had a bunch of UTF content living in a latin1 table (MySQL calls it ISO-5589-1 latin1). To fix this, export your database and re- import as UTF Details are here, but the gist of it is:

mysqldump -uUSER -pPASSWORD --default-character-set=latin1 DB_NAME | sed 's/latin1/utf8/' > temp.sql 
mysql -uUSER -pPASSWORD DBNAME  < temp.sql

Why not just run the conversions in-place (ALTER TABLE table_XXX CONVERT TO CHARACTER SET utf8 COLLATE utf8_ci;)? That's a different operation -- CONVERT TO CHARACTER SET is appropriate when your content and your DB's encoding already match, and you want to convert it to another encoding. If you have a mismatch in content and encoding, the export/import trick is just what you need. Discussion on additional techniques are here.

2. The charset defined in your HTML headers

How to check it: use curl

~ $ curl -I http://hotspotr.com
HTTP/1.1 302 Found
...
Content-Type: text/html; charset=utf-8

Rails uses utf-8 by default, so unless you've consciously changed it you should be good.

3. The charset specified in your HTML metatags.

How to check it: just view your source and look for something along the lines of <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />. Yes, this is different from the charset in the headers. Mine didn't match on one site. Fortunately, browsers pretty much ignore the metatags in favor of the value given in the header. Still, if you've got a mismatch, it's trivial to fix.

How to fix: open up your application.html.erb and make the change.

4. Your database.yml

Just make sure you have the line encoding: utf8 in your database configuration blocks in database.yml.

5. One more thing to look at...

if you're doing static html caching, then Apache (or whatever web server you're using) probably controls the charset when it serves up the cached page. Make sure it's setting the right charset. More details here

Continue reading "Get your character encodings synched up

Gem graphs, new in RubyGems

Apr 22 by Andre

Cool. The newly released RubyGems 1.3.2 has plugins, one of which is graphs. Here is my graph for my Ruby 1.8:

And here is my graph for Ruby 1.9:

Continue reading "Gem graphs, new in RubyGems

Development with Rails + Passenger (AKA mod_rails) on Mac

May 24 by Andre

Passenger, AKA mod_rails

There are a number of posts (one, two) out there on getting Phusion's Passenger up and running on OSX (Leopard). I decided to give it a go, and was pleased to discover several things:

  1. Despite reports to the contrary, Passenger installed just fine with Leopard's built-in Apache (I'm running Apache 2.2.8).
  2. Setup is very easy, as advertised.
  3. My default doc root(~/Sites) is works exactly as it did before. I do some HTML and PHP work there, so it was key that it continue to work properly.

Since the install process itself is quite easy, I wanted to offer a few tips for utilizing Passenger in a typical dev environment -- i.e., what you need after you get your first Passenger-powered Rails app up and running.

Your Brain on Passenger

As you know, script/server starts your Rails app on a specific port. If you bounce around between a number of applications at any one time, you're probably used to either starting them on different ports, or control-c'ing your current mongrel, cd'ing to another app's directory, and script/server'ing again. This familiar pattern changes when you're running passenger. All your apps are available at any one time, as long as you have your vhosts configured.

If you're like me, you usually hit your currently running Rails app on http://localhost:3000. That also changes when you're running Passenger. Instead, you'll hit a unique URL for each app, which you've configured in /etc/hosts to just go to 127.0.0.1

Setting up a new app

I set up a lot of Rails apps in my dev environment. With Passenger, in exchange for the on-demand convenience of accessing any of your apps any time, there are a few additional setup steps to take whenever you introduce a new app into your dev environment.

  1. create your rails project as usual
  2. add a new vhost. I configure mine in /private/etc/apache2/extra/http-vhosts.conf
  3. add the host in /etc/hosts
  4. restart apache: sudo apachectl restart

Here's a vhosts example with two apps I'm running locally. You can set up as many apps as you want this way:

  <VirtualHost *:80>
    DocumentRoot "/Users/andre/projects/rails/hotspotr/public"
    ServerName dev.hotspotr.com
    ErrorLog "/Users/andre/projects/rails/hotspotr/log/error.log"
  </VirtualHost>

  <VirtualHost *:80>
    DocumentRoot "/Users/andre/projects/rails/shapewiki/public"
    ServerName dev.shapewiki.com
    ErrorLog "/Users/andre/projects/rails/shapewiki/log/error.log"
  </VirtualHost>

Two things to note here:

  1. The ErrorLog line is optional. If you don't include it, the error output for this app will go to /private/var/log/apache2/error.log. Not that that's bad, but you're probably not used to looking for Rails logs there.
  2. I decided to go with the convention of dev.[PRODUCTION_URL].com. You can use anything here, as long as it matches up with an entry in /etc/hosts (see below)

And here's an example /etc/hosts addition to match the two virtual hosts above:

  127.0.0.1       dev.hotspotr.com
  127.0.0.1       dev.shapewiki.com

That's it! Go to (for example) http://dev.hotspotr.com, and you're hitting you local development app. There is nothing to start and stop. The first request for any app you hit will take a moment. Subsequent requests will feel quite snappy.

Let's Set up some Aliases to Make it all Flow

Here are the aliases I added to my .bashrc file to give me quick access to everything I needed for a new, Passenger-centric workflow in my development environment:

# Use this in any RAILS_ROOT dir. That restart.txt file tells mod_rails to restart this app.
# You'll want to do this when (for example) you install a new plugin.
alias restart_rails='touch tmp/restart.txt'

# By default, your app's error log now goes here. Unless you configure your apps otherwise, 
# it's helpful to have an alias to take you to your error log quickly.
alias apache_logs='cd /private/var/log/apache2/'

# You'll be adding to your vhosts configuration everytime you introduce a new Rails app. 
# Might as well make it a shortcut
alias vhosts='sudo vi /private/etc/apache2/extra/httpd-vhosts.conf'

# Dito with hosts
alias hosts='sudo vi /etc/hosts'

# You'll need to restart apache whenever you make a change to vhosts. 
# You can also click System Preference->Sharing->Web Sharing, but this is quicker.
alias apache_restart='sudo apachectl restart'

Continue reading "Development with Rails + Passenger (AKA mod_rails) on Mac

A Request for NetBeans: faster go-to file

May 22 by Andre

A small thing, but it impacts every moment spent with code: the speed of the as-you-type file lookup/open. On Netbeans, Control-Shift-O takes several seconds (up to 6 seconds in my experience) with large projects.

In contrast, Aptanta's, equivalent Apple-Shift-R is instantaneous -- it behaves like a direct link from thought to file. It also helps that the default keyboard shortcut rolls off nicely with one hand.

So here's my NetBeans enhancement request for faster go-to file.

Continue reading "A Request for NetBeans: faster go-to file

Server and Application Monitoring with Scout

Apr 15 by Andre

Highgroove's Scout is live! Scout is our server monitoring and reporting application. If you didn't have the opportunity to test-drive it during the beta period, you can now sign up for any of the plans. Why? Three reasons, based on my own Scout usage over the last few months:

  • Scout makes you look smart: if something goes wrong with a client's app, I generally know about it before they call me. So I get to be very proactive. In one case I was able to fix a crashed server before the client even realized anything was wrong.

  • Scout helps you build better apps: I had some memory-sucking CSV handling in one of my applications ... but it only happened under certain circumstances in production. With Scout, I was able to correlate the mongrel memory spikes with user activity and zero in on the problem. The result: mongrel instances that don't spike to 200MB, and a better experience for users.

  • Scout helps you sleep better: uncertainty is a stressor, and the more apps you have out there the more things you have to be uncertain about. Just knowing that I'll get an email notification if something goes astray helps my peace of mind a lot. Which means my mind is clear to ... design and build more apps!

Ready to learn more? Here's Derek's official launch post. Or cut to the chase and hit the Scout signup page!

Continue reading "Server and Application Monitoring with Scout

deprec2: Mongrel, nginx, Rails, mysql on Ubuntu 7.1

Feb 26 by Andre

Following up on my previous post on low-cost staging environments, I chose a host and set up my staging environment.

Note: these are the steps I took to make it work. They are probably not the optimal steps. Actually, I'll go one step further: I guarantee you these are not the optimal steps. But, I did get my staging environment up and running in less then three hours.

1) signed up for SilverRack.

Why? Dave from SilverRack offers $10 off if you're involved in a Ruby user group, which makes the total cost of a 256MB vps $10/mo. Hard to beat that from a pure cost perspective.

  • signup took two minutes including paying my first month on paypal
  • the VPS was provisioned about two minutes after that. I had originally chosen centos, but realized I needed Ubutu 7.1 for deprec 2 -- SilverRack has a simple web-based reinstall you can do yourself, and it took a minute. Note: the Rails stack on CENTOS (before I changed to Ubuntu) looked like it was Rails 1.2 based.

2) install new cap

(I hadn't upgraded to 2 yet)

sudo gem install capistrano -v2.1.0

3) install deprec 2 preview

... I had a hard time finding the gem; grab it from the wiki page like so: curl http://www.deprec.org/attachment/wiki/WikiStart/deprec-1.99.13.gem?format=raw > deprec-preview.gem

sudo gem install deprec-preview.gem

4) decided to create myself a little deprec play area, but I'm not sure it's necessary:

cd ~/projects mkdir deprec cd deprec mkdir config depify .

4.5) set up ssh keys

. . . I know deprec provides some tasks for this, but I just did it manually (copied my local ~/.ssh/id_rsa.pub) into my new vps's ~/.ssh/

5) let's setup a rails stack!

Used an IP because my dns hasn't resolved yet. If your dns has resovled ... good for you!

export HOSTS=xxx.xxx.xxx.xxx 

# here comes the big one . . .
cap deprec:rails:install_rails_stack

ok, it almost made it through ... ran for a few minutes and failed on sqlite3. Specifically, it failed here:

executing `deprec:rails:install'
executing `deprec:rails:install_deps'
executing "sudo -p 'sudo password: ' \n      
sh -c \"DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' 
DEBIAN_FRONTEND=noninteractive apt-get -qyu --force-yes  
install libmysqlclient15-dev sqlite3 libsqlite3-ruby libsqlite3-dev \""

.. with the error:

Package sqlite3 is not available, but is referred to by another package. This may mean that the package is missing, has been obsoleted, or is only available from another source. E: Package sqlite3 has no installation candidate

After ssh'ing on the server, fooling around with apt-get myself, googling sqlite3/ubuntu, etc, still no love. So I went ghetto on this one:

sudo vi /opt/local/lib/ruby/gems/1.8/gems/deprec-1.99.13/lib/deprec/recipes/rails.rb

.. and removed the two sqlite related libs from the recipe (sqlite3 libsqlite3-ruby), at around line 77. Bottom line is that all my apps use mysql, so I knew I wouldn't miss sqlite

6) NOW let's setup a rails stack!

Since deprec's installrailsstack had made it almost all the way though, I checked out the recipe to see what's left. Turned out it's just one task:

cap deprec:rails:install

That's it. At this point my server has Rails2, a bunch of gems including mongrel, nginx, mysql, and probably a whole bunch of other stuff I don't know about yet.

7) so . . . how do you deploy to staging?

I didn't want to figure out the "right" way to configure Capistano for a staging environment right now. Plus, my project already has a (non-depified) deploy.rb, which I don't want to mess with right now.

In the interest of expediency, I just checked out a separate copy of my app and:

rm config/deploy.rb
depify .
vi config/deploy.rb

... add set :user, 'root', updated :application, :repository, :domain, and the three roles (:app, :web, :db)

cap setup 

cap deprec:deploy

... and had to work through some problems with access to my SVN repo, which is probably reflective of the sorry state of said repository. As a takeaway, however, note that capistrano2 uses scmusername and scmpassword instead of svnusername/svnpassword.

8) create the database

First you have to create your database user. Again, I know deprec helps with this, but it wasn't immediately obvious to me how to do it. SSHing onto the server and issuing a few commands inside mysql was easy enough though:

mysql> create user general@localhost identified by 'yourpassword';
mysql> GRANT ALL ON *.* TO 'general'@'localhost';

And back on my local machine:

cap deprec:db:create

from here you could do cap deprec:db:migrate, but I wanted a fresh snapshot from production via mysqldump.

9) fire it up

deprec:nginx:restart
deprec:mongrel:restart

It works! Total time elapsed: about three hrs.

This is a good stopping point, since my staging environment is up and running. The next step is to replace Mongrel with Thin -- but that will have to wait for the next post.

Notes on what's where

  • Mongrel Cluster config is in `/etc/mongrelcluster/[appname].yml
  • ninx config is in /usr/local/nginx/vhosts/[app_name].conf

Continue reading "deprec2: Mongrel, nginx, Rails, mysql on Ubuntu 7.1

Cheap Rails VPS options

Feb 24 by Andre

I did some research today on low-cost VPS setups for Rails deployments. My immediate need is to set up some staging environments, but I'm also interested in being able to quickly spawn new production-grade Rails environments as easily (and cheaply) as possible. Note: there's a good discussion on VPS's here in the Rails Business group.

Here are my findings so far:

Who 256mb 512mb 1GB setup fee Rails Stack? Backups Notes
RimuHosting $30 $40 $85 $10-$20 Just ask for it -- Ruby1.8.6 Rails2 Apache2.2 mongrel mysql rmagick "RimuHosting run backups of each VPS once a week. We try to keep 2 copies of the backup" Rimu also gives you 300MB of space on an ftp server -- I use this for rotating mysqldump backups Rimu is the only one of these I've personally used. I have a dedicated box there and VPS's for clients
Linode $19* $29* $60 none None "Backups are your responsibility. You are protected against a hard drive failure on the host, as we utilize hardware RAID 1 mirroring across two drives. However, this is not a substitute for proper backups" *Memory is in odd sizes -- $19 is for 360MB, $29 is for 540MB
SliceHost $20 $38 $70 none They have good instructions for using deprec Cost extra. "At any given time, you have access to 3 images of your system: a daily (less than 24 hours), a weekly (less than 7 days) and a snapshot (variable). Monthly pricing is $5/$10/$15/$30 for 256/512/1024/2048 slices. You can also create a new slice from the backup of another slice (cloning)" *actually have availability right now (2/23/08) on all their slices.
SilverRack $20 $40 $70 none "We have Centos and Debian VPS templates that come with Ruby, Ruby on Rails, Apache 2.2, MySql 5, and ImageMagick installed and ready to go." "We perform nightly backups of all of our customer's VPSs. We keep these backups for 2 days" will give you $10/mo off if you're a member of a Ruby users group
VPSLink $25 $40 $75 none "All of VPSLink's Ruby on Rails templates come with Ruby 1.8.6, Lighttpd, FastCGI, mySQL, and RubyGems 0.9.5 pre-installed"   FastCGI? WTF

Rails stack setup / Application management

For the providers that don't have a Rails stack, there are some good options for setting up your own stack and/or deploying/managing your apps after you get your server running:

  • RailsMachine gem -- Stack setup + App management
  • RubyWorks -- Stack setup -- "RubyWorks implements a load-balanced cluster of Mongrel servers, which in most situations is the best way to deploy a Rails application today. It is packaged as a Yum repository (for RedHat/CentOS) or APT repository (for Debian/Ubuntu)" Works on a range of distros, including Centos 4,5. Uses HAProxy for balancing, doesn't include a web server.
  • Deprec -- Stack setup + App management -- "Deprec is a collection of automated recipes, written in Ruby, for setting up production ready Ruby on Rails servers. This includes everything from creating admin accounts and setting up your ssh keys to compiling and installing the packages required to get a Rails application running on a freshly installed Ubuntu server." Works on Ubuntu 7.10
  • Slingshot gem -- Stack setup + App management -- "a set of recipes, tasks, extensions and plugins that work with Capistrano to setup and configure a server for a Rails Application, quickly, and easily."
  • FiveRun's production stack -- Stack setup -- "provides a fast, easy way to get everything you need to develop and deploy Ruby on Rails applications. RubyStack includes Ruby, Subversion, MySQL, SQLite, ImageMagick and several Ruby Gems, and will optionally install Apache 2.2 with rewrite and proxy support"

Additions / recommendations / corrections? Leave a comment.

Continue reading "Cheap Rails VPS options

Trying Netbeans was worth it

Dec 02 by Andre

I just did the last round of updates on PlaceShout using Netbeans 6RC2's Ruby support. Based on my brief exposure, here are some things it offers which made it worth by time in a concrete way.

  1. really excellent svn integration
    • it shows you lines you've added/deleted/changed in your regular editing view, as you work -- it's a great visual cue on the extent of your changes without leaving your normal coding flow.
    • the source compare is outstanding, it highlights not just changed lines but parts of lines. Very smart and intuitive, and better than anything I've used before.
  2. control-click on any method to go to its definition. This is especially useful in .erb templates to go to a helper definition. If there are multiple definitions, Netbeans gives you a list.
  3. as-you-type "jump to class" in addition to "jump to file". Command-o brings it up, and it works on project classes, native ruby, and gem and plugin code. It's snappy enough to be useful, but could get a little faster.
  4. method completion on AR attributes/finders: netbeans gives you a nice as-you-type completion list on the dynamic, data-driven getters/setters Rails infers from the database columns. Nice! There's a lot of other method completion it tries to do, but this is the one that stood out to me.

Netbeans has got a ton of functionality and a rich plugin ecosystems, and I'm sure there are more gems in there. The points I've indicated here are just the ones that were useful, accessible, and intuitive enough to engage me right away.

Tor Norbye is publishing a regular "Ruby Screenshot of the Week", give it a read to see what else is possible.

My take is that if 1) method/paramater completion is refined a bit, 2) debug mode works seamlessly and effortlessly (couldn't get it to work on Leopard), and 3) performance improves a bit -- Netbeans will be a home run for Rails development.

Huge bonus points if the collaboration plugin delivers on its promises (and supports Ruby!)

Continue reading "Trying Netbeans was worth it

Rails Deployment Gotchas: Random Notes From the Whiteboard

Nov 07 by Andre

This is an odd list of things that tripped me up at one point or another during Rails app deployment. They've been living on the whiteboard next to my desk for a while; now it's time to set them free.

  • App URL in Apache vhosts config is missing the .com. The requests to this url go to the next available application configured in vhosts. Symptom: accessing your newly launched app actually gives you some other app you have hosted on the same machine.

  • Apache listener is configured on the same port as another Rails app. Symptom: don't remember exactly, but I think Apache fails to start.

  • gem names case-sensitive in deployment environment (Linux), not in development environment (Windows -- this is from the dark days when I was developing on Windows). Symptoms: 1) can't install the gems you need; 2) free-floating mac envy.

  • reverse slash in 'require' works in development but causes error in production -- another Windows/Linux gotcha.

  • Rake gotcha: one missing include breaks all other tasks and everything fails silently.

  • Failure to clear out existing sessions after major rework and deployment. Symptoms: things failing with no reasonable explanation; pointed pangs of stupidity when you realize that you refactored stuff you store in session. rake db:sessions:clear.

  • rake not defaulting to production environment on production systems. This was a while ago, but it tripped me up a few times.

  • gems you installed in dev, but forgot to install in production.

There, now I can reclaim that space on my whiteboard . . .

Continue reading "Rails Deployment Gotchas: Random Notes From the Whiteboard

Ruby, you're still the one for me

Sep 24 by Andre

Ruby and Rails seems to be taking a beating on blogs the last few days. Lots of assertions, rants, and general discontent. And, a few valid points made on both sides of the spectrum.

At the end of the day, nothing I've read will change what I'm doing tomorrow: having fun creating great products with a language and framework that is a joy to use. Working in a space that is dominated by innovative thinking. Engaging a vibrant community of smart, creative technologists.

Everyone knows that Ruby and Rails have limitations. They are not perfect for every project. But, they are perfect for my projects right now!

Continue reading "Ruby, you're still the one for me

Rails & OpenID with Caboo.se Sample App

Jun 06 by Andre

Integrating OpenID authentication into the Caboo.se sample Rails app turns out to be pretty straightforward. My approach was to start with a clean install of the Caboose sample app (version 3), and add code as necessary from the code generated by Eastmedia's Restful OpenId Authentication Plugin (http://svn.eastmedia.com/svn/bantay/plugins/trunk/restful_open_id_authentication).

Assuming that you are starting with the Sample app, here are the files you need to change:

Models

  • authenticated_system.rb -- no changes
  • User.rb (model) -- the code looks different because the sample app uses authenticated_base mixin (a more encapsulated approach), but I it ends up being nearly identical to the User model in the OpenId plugin. Some quick changes:
    • add open_id_url to the list of attr_accessable, otherwise the session_controller won't be able to set the open_id_url, the password_required? method will always return true, and you'll life will generally be difficult.
    • authenticated_base.rb -- change password_required? method to include: open_id_url.nil? -- see the openId password_required? method.
  • application.rb - no changes

users_controller.rb

  • no changes. The OpenId UserController includes AuthenticatedSystem, but sampleapp includes that in application.rb. The upshot: you can use the sampleApps more complete users_controller directly

session_controller.rb

  • add line: open_id_consumer :required => [:email, :nickname]
  • copy begin and complete methods from the OpenId session_controller.rb

routes.rb

  • add map.resource :session, :collection => { :begin => :post, :complete => :get }

migrations

  • add AddOpenIdTables migration from the OpenId plugin, including the open_id_url column to users

views

  • session/new.rhtml -- replace with the OpenId equivalent
  • user/new.rhtml -- this file is an easy merge, just look for additions in the OpenId new.rhtml

Continue reading "Rails & OpenID with Caboo.se Sample App

Business of Rails Google Group

May 21 by Andre

I had the pleasure of being on the The Business of Rails panel at RailsConf this last weekend. Based on the response we got from the audience and the followup communications, co-panelist Robby Russell has created a Google Group to continue the discussion. If you are working independently with Rails, or want to chat with others who are making their living working with this wonderful technology, join us in the group! See Robby's announcement here.

Continue reading "Business of Rails Google Group

The Rails Community is Making a Difference

May 21 by Andre

Chad Fowler challenged all of us at RailsConf 2007 to share our success in a meaningful way though charitable donations to Livestrong, Earthday, Habitat for humanity, and others. By the end of the conference, the donations were approaching $30,000! This is a community that I am proud to be a part of.

Thanks to Chad for his dedication and stewardship.

Continue reading "The Rails Community is Making a Difference

12 Takeaways from RailsConf 2007 Sessions

May 21 by Andre
  1. Streamlined's use of separate UI classes is a great way to describe how the scaffolding it creates should behave. This is the best solution to the problem I've seen so far.

  2. Play with helpers in script/console: helper.methods.grep /tag/

  3. I plan on making a small suite of highest-level tests for all my apps with Selenium. Selenium tests should encompass relatively long sequences of user actions. Pro: can test the full stack, including Ajax. Con: slow and relatively brittle. Rule off thumb: convert to integration tests whenever you can.

  4. Orthedoxy means teaching right; othopraxy means doing right. Dig it.

  5. Take advantage of what Rails' REST implementation gives you, but don't be dogmatic about REST principles. I will continue to add verbs to my controllers when it is pragmatic to do so, use cookies for authentication, and use server-side sessions for maintenance of state.

  6. A valuable notion: URL's can be considered part of your user interface. Don't let Rails dictate how your URL's should look if it's not best for our users.

    • In a conversation afterwards, Richard White brought up a great counterpoint: sometimes we should choose a UI according to what's best for the community of users as a whole, rather than what is best for individual users.
  7. Adam Key's Rails Spelunking Recipe: 1) start with routes. look at your url, use routes to figure out where you are; 2) check out the controller or skip directly to the view; 4) grok any controller filters; 5) grok the controller action; 6) grok any methods the action calls; 7) check the template and any helpers it might use

  8. Remember that we're simply running code as we're defining a class . . . you can define a method and call the method all within the definition of the class. Thanks to James Adams for a great presentation on developing plugins.

  9. The !! idiom is generally unnecessary. Not to mention alarmist looking.

  10. Go check out Hampton Caitlin's make_resourceful plugin. Can't wait to try it out, it will 1) make your RESTful controllers shorter and DRYer, and your xml/JSON/whatever representations easier to produce.

  11. Simile is an Javascript widget for time-based data. Think Google Maps for timelines.

  12. Solr rocks for full-text search. You can add multiple facets to your search (think: searching on EBay, and getting counts for each of the categories in which hits occur), get highlight, spell checking, and more. Loot at solr-ruby, acts_as_solr, and Flare as well. Thanks Erik Hatcher for an extremely useful presentation.

Continue reading "12 Takeaways from RailsConf 2007 Sessions

GeekSessions next week

May 15 by Andre

My friend Christian Perry (of SF Beta fame) is co-producing the first GeekSessions event next week. The topic is Scaling Ruby on Rails, and he's got an impressive speaker lineup from Twitter, Joyent, Pivital Labs, and Sun Microsystems. I'll be there, fresh from my Railsconf talk this weekend, to see if I can trip up the speakers with some hard questions ;-)

Continue reading "GeekSessions next week

In Portland, looking forward to Railsconf

May 14 by Andre

I came up to Portland a little early this week to hang out with family before Railsconf starts on Thursday. I am excited about the conference, it's going to be a blast.

Now that the schedules are finalized, the Business of Rails panel panel I'm doing is on Saturday at 11:45-12:35.

Continue reading "In Portland, looking forward to Railsconf

ShapWiki gets GeoRSS export

May 09 by Andre

ShapeWiki just got a small update today with GeoRSS feed support. You can get GeoRSS feeds for:

Check it out by going to maps.google.com and pasting http://shapewiki.com/tags/state.rss into the map search box.

Continue reading "ShapWiki gets GeoRSS export

ShapeWiki: a collaborative shape repository

Apr 21 by Andre

http://shapewiki.com is a an open repository for shapes you can use on Google Maps. It includes a point-and-click editor for creating new shapes, and is backed by a tagged, searchable database so you can find shapes made by others.

There are three export formats:

  • JSON-- essentially an array of latitude/longitude pairs with some additional metadata
  • JavaScript -- the same JSON array, but with some associated JS to easily allow you to add a GPolygon to your map
  • XML

Potential future enhancements include: export as encoded polylines, and import from a variety of formats.

When you try out creating a shape, notice the reference image section on the right-hand side. Enter a URL there to pin an image to the map so you can trace neighborhoods, etc. Here's an image of the neighborhoods in San Francisco: http://real-estate-us.com/images/SanFranciscoNeighbourhoodsCA.gif

Continue reading "ShapeWiki: a collaborative shape repository

Using GeoKit? Tell us about it

Mar 13 by Andre

If you are using GeoKit, drop me an email. I am going to put up a "Who's using GeoKit?" page at geokit.rubyforge.org, and I'd love to hear:

  • how you're using GeoKit
  • what site or service you're using it on, with a link that I can put up on Rubyforge
  • how GeoKit made your life (or at least your project!) easier

Send me an email at andre {at} earthcode {dot} com. Thanks!

Continue reading "Using GeoKit? Tell us about it

Local Q&A coming soon

Mar 04 by Andre

TwoBlocksDown is a local Q&A service, and is a collaboration between me and Derek Haynes. Although we still have a lot of work to do, I'm very excited about this service! Of course we're doing it in Ruby on Rails, and it will have a Google maps component. Stay tuned for more details, or drop us your email to be the first with the 411!

Continue reading "Local Q&A coming soon

Four mongrels, digg, and lifehacker

Feb 27 by Andre

Hotspotr had a record-breaking day yesterday, with a total of 61,000 pageviews according to Google Analytics. The peak hour was 5pm, shortly after it went onto the digg homepage, with just under 15,000 pageviews in one hour.

Since a frequently-expressed concern with Rails is it's ability to handle traffic, I'm happy to say the site survived without a hicup. If you are building a Rails app and wondering about scalability, here are some points of reference:

  • the backend is Apache 2.2 with mod_balancer going to a cluster of four mongrels.
  • the server is a Rimu hosting dedicated server, with a few other Rails apps running alongside it (none very highly trafficked yesterday though).
  • I don't have quantitative measurements on its responsiveness during the peak load times, but qualitatively it seemed pretty snappy -- I hit the site myself repeatedly during the peak time to see how it was holding up.
  • the hotspotr homepage has four database queries. The main map page (for any given city) has five queries.
  • I've done very little to optimize hotspotr. The basics are there: no iterative retrievals, appropriate indexes on tables, and sessions in the database.
  • beyond that, I haven't optimized much at all -- there's no caching of either ActiveRecord objects or HTML fragments.

You can see there is still quite a bit of room for improvement. The fact that the site survived a 15K hour certainly makes me more confident about deploying Rails in a high-traffic environment.

Thanks to Josh at Webware Rick at lifehacker for writing up hotspotr and sparking all the traffic yesterday!

Continue reading "Four mongrels, digg, and lifehacker

GeoKit: all about the :origin argument

Feb 15 by Andre

The whole point of the acts_as_mappable mixin is to let you find things by location:

class Store < ActiveRecord::Base
    acts_as_mappable
    ...
end

...

Store.find(:all, :origin=>[37.792,-122.393])

But, what all can be supplied in the :origin argument? There are three possibilities.

1. Pass in a two-element array with latitudes/longitude values

Store.find(:all, :origin=>[37.792,-122.393])

Whenever :origin is an array, the code assumes in an array of two numbers representing latitude and longitude. If you pass in an array of anything else, you'll get an error.

2. Pass in a geocodeable string

Store.find(:all, :origin=>'100 Spear st, San Francisco, CA')

Again, the detection is based on the type of thing passed in under origin. So, if you expect GeoKit to geocode it, it better be in the format of a string. Zip codes are fine, but it better be a string. So, this will work:

Store.find(:all, :origin=>'94111') # it's a string, so it gets geocoded

But this will not:

# WON'T WORK -- it's not a string, so it doesn't get geocoded
Store.find(:all, :origin=>94111)

3. pass an object

Specifically, an object that has lat and lng methods, or latitude and longitude methods, or whatever methods you have specified for lng_column_name and lat_column_name.

Store.find(:all, :origin=>an_object_with_the_right_methods)

So, what sort of objects have lat and lng methods? Well, our own GeoKit::LatLng objects for starters. So, this will certainly work:

ll=LatLng.new(37.792,-122.393])
Store.find(:all, :origin=>ll)

GeoKit::GeoLoc also has lat and lng methods, so it will work too. By the way, GeoLoc is what gets returned when you make a geocoding call:

loc = GeoKit::Geocoders.GoogleGeocoder.geocode('94117')
Store.find(:all, :origin=>loc)

So far so good. What else has the appropriate methods? Well, anything that mixes in actsasmappable needs to have some sort of latitude/longitude methods. Say you already have an instance of Store -- you can pass the instance in as the origin:

starbucks=Store.find_by_name('Starbucks')
Store.find(:all, :origin=>starbucks)

In Summary . . .

There are three things you can pass into the :origin argument: 1) a two-element array containing latitude and longitude; 2) a geocodable string; 3) an object which will respond to lat/lng or latitude/longitude calls. Typically, you'll find yourself passing in LatLng or GeoLoc objects, or possibly other instances of whatever model you've mixed ActsAsMappable into.

Continue reading "GeoKit: all about the :origin argument

"The Business of Rails" panel at RailsConf

Feb 12 by Andre

I'm pleased to be a part of the "Business of Rails" panel at RailsConf this year. I'll be speaking alongside some major players in Rails consulting -- Justin Gehtland, Geoffrey Grosenbach, Joe O'Brien, Robby Russell, and Nathaniel Talbott. The panel is meant to answer your questions about the challenges, rewards, and dynamics of consulting with Rails.

As a one-man shop, I represent the small end of spectrum. I hope my contribution will be useful to others who are contemplating taking a similar approach, i.e, splitting your time between paid work and your own speculative projects. And, importantly, making a living doing what you enjoy with people you enjoy working with.

Again, I'm pleased to be on a panel with an esteemed group like this, and thanks to Nathaniel for pulling it together!

Continue reading ""The Business of Rails" panel at RailsConf

In-memory and in-database distance calculations

Feb 11 by Andre

Following up on our recent release of GeoKit, Bill and I are working on some posts highlighting some of GeoKit's cool features. This post is on distance calculations. GeoKit can calculate distance in memory, and it can calculate distance as part of a database query.

In-memory distance calculation

The basic syntax for in-memory distance calculations is: d = first_loc.distance_to(second_loc)

What class is first_loc and second_loc? They should be either LatLng (Geokit::LatLng), or GeoLoc. If you open up the mappable.rb file, you're see that GeoLoc inherits from LatLng. If you're looking for the distance_to method, you'll find it in the Mappable module (the code for which is also in mappable.rb). The Mappable module is mixed in to LatLng, and imbues it with the distance_to method.

Distance in miles and kilometers

By default, distance_to yields a result in miles. If you want a distance in kilometers instead, just pass the optional units argument:

>>d = first_loc.distance_to(second_loc, :units=>:kilos)

A full example

Here we are just instantiating two LatLng objects using raw latitudes and longitudes (which correspond to San Francisco, CA, and Irving, TX).
>ruby script/console
Loading development environment. >> include GeoKit >> first_loc=LatLng.new(37.775,-122.418) >> second_loc=LatLng.new(32.813,-96.948) >> first_loc.distance_to(second_loc) => 1473.29189341352

. . . and with geocoding

Here we are starting with just the addresses, geocoding them through Google's geocoding service, and then calculating the distance. Calls to any of the Geocoders in GeoKit return the GeoLoc object. Recall that GeoLoc inherits from LatLng though, so it has all of LatLngs methods, including distance_to.

>>ruby script/console Loading development environment. 
>> include GeoKit::Geocoders 
>> sf=GoogleGeocoder.geocode('San Francisco,CA') 
>> puts sf.ll 
37.775,-122.418333 
>> irving=GoogleGeocoder.geocode('Irving,TX') 
>> puts irving.ll 
32.813889,-96.948611 
>> sf.distance_to(irving) 
=> 1473.25508451219 
>> sf.distance_to(irving, :units=>:kilometers)
=> 2370.46743098012

Flat vs. Spherical distances

If it turns out the earth is actually flat, GeoLoc still has us covered:

sf.distance_to(irving, :formula=>:flat)
=> 1206.13692230874

In case you're wondering, the spherical formula uses the Haversine method, and flat formula just uses the pythagorian therorum. You can find both implementations in the Mappable module in mappable.rb.

So there you have in-memory distance calculations provided by Geokit. Next, let's look at distance calculations in your database through ActiveRecord finder extensions provided by ActsAsMappable.

Database Distance Calculations

I have a model named Shop, which mixes in the ActsAsMappable module:

class Shop < ActiveRecord::Base 
acts_as_mappable 
... end 
Let's find the 10 closest shops to the center of the '94117' zipcode:
>> loc=GoogleGeocoder.geocode('94117')
>> puts loc.full_address
"San Francisco, CA 94117, USA" >> shops=Shop.find :all, :origin=>loc, :order=>'distance asc', :limit=>10

Now, each of the shops that has a bonus attribute, 'distance'. This represents the distance from the origin provided in the query.

>> puts shops[0].distance
0.22292201411219
>> puts shops[9].distance
0.61426029124168
>> shops.each {|shop| puts "#{shop.name} -- #{shop.distance} miles from #{loc.full_address}"}
Coffee to the People -- 0.22292201411219 miles from San Francisco, CA 94117, USA
Central Coffee, Tea & Spice -- 0.24258501247455 miles from San Francisco, CA 94117, USA
Bean Bag Cafe -- 0.33106086315379 miles from San Francisco, CA 94117, USA
Cafe Abir -- 0.42131954288221 miles from San Francisco, CA 94117, USA
Cole Valley Cafe -- 0.53454381452665 miles from San Francisco, CA 94117, USA
Jumping Java -- 0.5419605766772 miles from San Francisco, CA 94117, USA
Cafe Reverie -- 0.59076205971555 miles from San Francisco, CA 94117, USA
Cafe Du Soleil -- 0.60029433169774 miles from San Francisco, CA 94117, USA
Tully's (Cole Valley) -- 0.61426029124168 miles from San Francisco, CA 94117, USA

Since distance is being calculated in the database, you can use it in the conditions like any other column:

>> shops=Shop.find :all, :origin=>loc, :order=>'distance asc', 
                         :conditions=>'distance>20 AND distance <25'

Database and memory distance calculations together

Since my Shop class mixes in the Mappable module (through the acts_as_mappable call), I can also calculate its distance from any arbitrary location:

shops=Shop.find :all, :origin=>loc, :order=>'distance asc',
                         :conditions=>'distance>20 AND distance <25'
>> other_loc=GoogleGeocoder.geocode('100 Spear St, San Francisco, CA')
>> puts shops[0].distance_to(other_loc) # distance to an arbitrary location
=> 19.6377479942219
>> puts shops[0].distance # distance to the origin given in the query
22.581680094216

In case you're wondering, the math happening behind the scenes is essentially the same as outlined in sorting your queries by geographic distance -- they math is now nicely hidden, thanks to Bill Eisenhaurer's work in abstracting it away.

Summary

GeoKit supports both in-memory and in-database distance calculations. You can use units of miles or kilometers, and a flat or spherical formula.

Coming up next, I’m going to talk more about the find’s :origin argument.

Bill is also putting up some more GeoKit documentation and examples -- check out his live example of IP-based geocoding. It’s very cool stuff which allows you to determine roughly where your visitors are based on their IP address. GeoKit makes it easy for any Rails controller to employ the functionality.

Continue reading "In-memory and in-database distance calculations

GeoKit: a plugin for location-based Rails apps

Feb 09 by Andre

I am happy to release GeoKit over at RubyForge. GeoKit has been a collaboration between myself and Bill Eisenhauer

What is GeoKit?

Geokit is a Rails plugin for building location-based apps. It provides geocoding, location finders, and distance calculation in one cohesive package. If you have any tables with latitude/longitude oolumns in your database, or if you every wanted to easily query for "all the stores within a 50 mile radius," then GeoKit is for you.

What can GeoKit do for you?

  • Distance calculations between two points on the earth. Calculate the distance in miles or KM, with all the trigonometry abstracted away by GeoKit.
  • ActiveRecord distance-based finders. For example, you can find all the points in your database within a 50-mile radius.
  • Geocoding from multiple providers. It currently supports Google, Yahoo, Geocoder.us, and Geocoder.ca geocoders, and it provides a uniform response structure from all of them. It also provides a fail-over mechanism, in case your input fails to geocode in one service.
  • IP-based location lookup utilizing hostip.info. Provide an IP address, and get city name and latitude/longitude in return.
  • A before_filter helper to geocode the user's location based on IP address, and retain the location in a cookie.

Examples?

Find near latitude and longitude:

Store.find(:all, :origin =>  [37.792, -122.393] :conditions=>'distance<10')

Find near an address:

Store.find(:all, :origin=>'100 Spear st, San Francisco, CA', :conditions=>'distance<10')

Geocode an address:

res=GeoKit::Geocoders::GoogleGeocoder.geocode('100 Spear st, San Francisco, CA') 
puts res.lat 

Find distance:

distance=first_location.distance_from(second_location, :units=>:miles)

Where can you get it?

Head over to GeoKit at RubyForge for the plugin source and API docs.

Or, skip right to installing it as a plugin:

ruby script/plugin install svn://rubyforge.org/var/svn/geokit/trunk

Continue reading "GeoKit: a plugin for location-based Rails apps

REST presentation at Silicon Valley RoR

Jan 18 by Andre

I presented on RESTful routing last night at the Silicon Valley Rails group. I stepped through some code showing how to use scaffold_resource, set up nested resources using REST, use the new named routes, and use responds_to to return information in different formats (HTML/XML/JS/JSON). I also talked about the motivations for embracing RESTful design.

The slides to this and other presentations I've given are here.

Thanks to everyone who came and listened and participated in the REST discussion!

Continue reading "REST presentation at Silicon Valley RoR

to_param still works with RESTful routes!

Jan 16 by Andre
Just a friendly reminder, the to_param trick still works with RESTful routes. This will still give your Post class a nice "ID + title" URL:
class Post < ActiveRecord::Base
  has_many :comments
 
  def to_param
    "#{id}-#{title.gsub(/[^a-z0-9]+/i, '-')}"
  end
end

Continue reading "to_param still works with RESTful routes!

Nested CRUD resources in Rails 1.2

Jan 10 by Andre
The CRUD methodology in Rails 1.2 is a great way to simplify your application structure. One of the first things you will find yourself doing is nesting one resource inside another. This is just the RESTful way of working with has_many relationships. The first couple times I did this, I forgot some of the steps (particularly changing the url paths in the controllers). So, I'm going to iterate the steps here -- hopefully this will help someone going through the learning process on Rails 1.2 CRUD.

Continue reading "Nested CRUD resources in Rails 1.2

Four choices to Jumpstart your Rails App

Jan 06 by Andre

AutoAdmin

No code generation; goal is to provide a Django-like dynamic admin interface with minimal effort. Takes a "either it works for you or it doesn't" approach, because there's no code to edit or customize if you don't like what AutoAdmin gives you. http://code.trebex.net/auto-admin/

Streamlined

Code generation backed by dynamically generated interfaces. Goal is "to bring the declarative goodness of ActiveRecord to the view layer. It manages the presentation, creation and editing of instances of your models, with full-featured scaffolds that include relationship management."

Unfortunately, the streamlined site hasn't been updated in a while. http://streamlined.relevancellc.com/

Hobo

The newest project, and probably the most ambitious one. Hobo has its own templating language, a library of custom tags, theme support, and user login/access control baked in. http://hobocentral.net/blog/

Ajax Scaffold

"A Ruby on Rails plugin that creates an AJAX'ified scaffold for controllers. Sister project of the Ajax Scaffold Generator. Extends the work of the base RoR scaffolds with AJAX and full CSS styling." http://www.ajaxscaffold.com

Continue reading "Four choices to Jumpstart your Rails App

Latitude and Longitude Columns in Rails Migrations

Dec 29 by Andre
So you're building a map application in Rails? Awesome. What datatypes are you going to use to store latitude and longitude in your database? There are three options:

1. Just use Floats.

This may be the first thing you try. The migration looks like this:
class CreatePlaces < ActiveRecord::Migration
  def self.up
    create_table :places do |t|
      t.column :lat, :float
      t.column :lng, :float
    end
  end

  def self.down
    drop_table :places
  end
end


Floats work, but their precision is limited. For example, if you geocode 101 Market st, San Francisco, you'll get latitude=-122.395899, longitude=37.793621. Store those values in the MySql table created by the above migration, and you'll get -122.396 and 37.7936 back out, thereby losing three decimal places of precision on your latitude. Still, they work fine if you're experimenting, and I use floats for some examples in my upcoming Rails/Gmaps book.

By the way, try this link if you want to see Google's XML output for geocoding this address. Yes, you can even use my personal Google API key embedded in this link -- I don't mind:
http://maps.google.com/maps/geo?q=101+market+st,94105.........

2. Alter a column using post-hoc SQL in your migration

Use this approach if you are running Rails 1.1.6, and need better precision than what Float provides. The migration looks like this:
  def self.up
    create_table :places do |t|
      t.column :lat, :float
      t.column :lng, :float
    end
    execute("ALTER TABLE places MODIFY lat numeric(15,10);")
    execute("ALTER TABLE places MODIFY lng numeric(15,10);")
  end
This gives you full-on decimal precision, but now your migration is MySQL-specific and not very DRY. That's the breaks if you're on 1.1.6. You should really upgrade to Rails 1.2RCx and . . .

3. Use proper decimal-type migrations in Rails 1.2

  def self.up
    create_table :places do |t|
      t.column "lat", :decimal, :precision => 15, :scale => 10
      t.column "lng", :decimal, :precision => 15, :scale => 10
    end
  end

Much nicer! This doesn't work in 1.1.6 but was fixed in 1.2. The columns it generates is identical to the post-hoc ALTER TABLE we did earlier, but without the DB-specific ugliness or redundancy. By the way, see here  if you want to learn about scale and precision in the DB.

In summary

use Rails 1.2 and :decimal, :precision => 15, :scale => 10 migrations if possible. Use floats if you're on 1.1.6 and just messing around. Use ALTER TABLE if you're stuck on Rails 1.1.6, and can stomach it.

Continue reading "Latitude and Longitude Columns in Rails Migrations

Speaking at SDForum next week

Dec 01 by Andre
I will be presenting "Ruby and Google Maps" at this month's SDForum Ruby SIG, which is next Thursday (Dec 7th). If you're in the area, I'd love to see you there!

I plan on making this a pretty interactive session. I've got three broad topics, and we'll spend time on whatever you think is most interesting. The topics are:
  • ImageMagick and geographic data: creating custom Google Map overlays with RMagick
  • Google Maps controls: creating a better (or at least different) zoom control
  • Demystifying the geocoder: you, too, can create a geocoder from scratch
Some of the material will be from my upcoming book with Apress, Beginning Google Maps Applications with Rails and Ajax

Hope to see you there!

Continue reading "Speaking at SDForum next week

Hey, want to sort your query by geographic distance?

Nov 14 by Andre
Sure you do. Here's a sample Rails action which finds the ten closest cities to the latitude/longitude you provide. Obviously, this presumes your City model has the fields latitude and longitude. These examples are for MySQL 5.
def nearby_cities
  lat,lng=params[:ll].split(',').collect{|e|e.to_f}
 
  #convert to radians
  lat_radians=(lat/180) * Math::PI
  lng_radians=(lng/180) * Math::PI

  # 3963 is the earth's radius, more or less, in miles.
  # Which means that the distances you get out of this are in miles.
  # Want a different measure? Kilometers=6378. Nautical miles=3444. Furlongs=29544
  earths_radius = 3963
  table_name=City.table_name
  distance_sql=<<-SQL_END  (acos(cos(#{lat_radians})*cos(#{lng_radians})*
cos(radians(#{table_name}.latitude))*cos(radians(#{table_name}.longitude)) +
cos(#{lat_radians})*sin(#{lng_radians})*cos(radians(#{table_name}.latitude))*
sin(radians(#{table_name}.longitude)) +
sin(#{lat_radians})*sin(radians(#{table_name}.latitude))) * #{earths_radius})
  SQL_END

  cities = City.find(:all,
        :select=>"*, #{distance_sql} as distance",
        :order => 'distance asc',
        :limit => 10)
       
  render :text=>cities.collect{|c|c.attributes}.to_json
end
The key thing is the distance_sql, which does all the heavy lifting. You can take this basic formula and use it verbatim for any model with latitude/longitude columns -- just make sure you change the table_name to match your model.

Find within Radius

So, what if you want to constrain the results to a given radius? No problem. This finds all cities within a 25 mile radius:
  cities = City.find(:all,
        :select=>"*, #{distance_sql} as distance",
:conditions=>"#{distance_sql} <= 25",
        :order => 'distance asc')
Note that when you use distance in :conditions, you have to repeat distance_sql, whereas if you use distance in your order clause, you can simply reference the alias ("distance") which you gave the computed column in the select clause.

Utilizing that "distance" column

In the :select clause, you tacked an extra column -- distance -- on there. You can access this field just as you would any other (say from your .rhtml template): <%=city.name%> is <%=city.distance%> miles away.. Note that any of these add-on columns are treated by default as strings. If you want to format it as an actual number, you'll need to do something like <%=city.distance.to_f.ceil%> miles

Continue reading "Hey, want to sort your query by geographic distance?

Tools and Resources for Mapping on Rails

Sep 05 by Andre
So you're up and running with some Google Map action on Rails? You went through the basic Google Maps API tutorial, and checked out my own Google Maps/Geocoding with Rails post. Now, you're probably wondering what other Rails-based resources are out there for this kind of work.

I've come across two projects which aim to simplify the map creation process for us Rails users: Cartographer and YM4R. Cartographer has been around longer (according to Rubyforge), but YM4R is more mature and full-featured. YM4R is also under more active use and development; it's currently ranked #264 in Rubyforge.

Cartographer

Cartographer gives you helpers to easily create Google Maps and add markers to it. The project recently went through a major revision for compatability with the Google Maps API version 2, and unfortunately the documentation hasn't been updated in step. The project includes code for geocoding via both Geocoder.us and Ontok (a geocoder which I haven't encountered before). Something which looks good in the code is an abstraction layer for the Google Maps key -- it will let you specify different GMaps keys for different combinations of hosts/controllers/actions.

YM4R

Next up is YM4R. YM4R packs a lot of functionality for both Google Maps and Yahoo maps (via the Mapstraction library). The project is broken into four parts:

  1. The YM4R/GM plugin for Rails provides helpers for interacting with the main Google Maps API. It is engineered to facilitate updates to the map via RJS. The plugin also includes some functionality not directly available through the GMaps API: Clusterer, GeoRSS, WMS layer. Read up in the documentation do see if these extensions are useful for your project.
  2. The YM4R/Mapstraction plugin for Rails provides rails helpers to interact with the Mapstraction API. As with YM4R, it facilitates updates to the maps via RJS.
  3. The YM4R gem provides ruby helpers for the Google Maps geocoding API and the Yahoo! Maps Building Block API’s: Geocoding, Traffic, Map Image and Local Search v3. The gem plugs into both Yahoo and Google geocoders, but not any 3rd party geocoders like geocoder.us.
  4. The Tools package provides command-line tools to generate tiles in order to create custom map types for use in Google Maps.

The YM4R author has been posting regular updates and tutorials on using the packages, so there's a lot of information available. He has the RDocs for all the packages online as well.

The Tools package for custom tiles is interesting. Read through the docs at http://thepochisuperstarmegashow.com/ProjectsDoc/ym4r_tools-doc/ and check out an example of the result at http://open.atlas.free.fr/GMapsTransparenciesImgOver.php

Helpers or no Helpers?

Cartographer and YM4R definitely provide some food for thought. If you're doing serious map-based development, you have a choice to make: do you want to program directly in JavaScript (the native tongue of all the web-based mapping APIs), or do you want an a Ruby-based intermediary package (as these two projects provide). There are pros and cons to both approaches, and I think the answer will depend on the kind of project you're doing. I'll write more on this in a future post.


Links/References

Continue reading "Tools and Resources for Mapping on Rails

San Francisco Wireless Cafes

Jul 11 by Andre
Wireless Cafes in San Francisco

Hotspotr (hotstpor.com) is a community-driven directory of cafes with wireless internet access here in San Francisco. This is a community effort, and the more listings it has the better -- so if you're in the San Francisco area, go ahead and add your favorite cafe.

Hotspotr is built in Ruby on Rails, and has served as a testing ground for my Rails/Google Maps development over the last couple months. I hope it will become a useful resource for others who enjoy getting out of the office and doing work in cafes.

Some things to note as you look around the site:
  • Open listings: anyone can add or update cafe listings
  • Google maps integration: the map view lets you see cafes citywide, or filtered by a specific San Francisco neighborhood
  • GMaps "Zoom" control: if you're using Firefox, there's a nifty "zoom" control on the main map view, which lets you outline a region on the map to center and zoom. Safari users -- this feature is coming soon
  • Ratings & collective opinion: there's a nice AJAXy rating system to aggregate collective opinion on important things like the availability of power outlets
The link again: hotstpor.com/

Continue reading "San Francisco Wireless Cafes

Use models to instantiate your Rails fixtures

Jun 27 by Andre
Following is a handy Rake script to import your fixtures (or a subset of your fixtures) through the associated model's constructor.

Say what? You import your fixtures with rake db:fixtures:load FIXTURES=neighborhoods,shops right? Yes, but there's a crucial difference:
  • The standard rake fixtures:load does a straight data load into the database -- your Rails Models are not involved. It's as if you did a series of INSERT INTOs through your MySQL command line.
  • The instantiation script takes the fixture data, and invokes the associated model's constructor for each item.
Which is better? It depends on what you need. I often have logic in my models that is kicked off on instantiation -- in this case, the the instantiation script is crucial, because the model will be forced to to whatever the model is meant to do upon instantiation.

Continue reading "Use models to instantiate your Rails fixtures

Rails Day '06 here we come

Jun 06 by Andre
So I'm all signed up for Rails Day 2006. The goal: with a small team (<=3 peeps), develop the coolest possible Rails app in 24 hrs. Yep 24 hrs. If Jack Bauer coded Rails, this would be his kind of thing. Come to think if it, it would be great to have Chloe O'Brian on our team.

My co-conspiritor for this effort is Josh Susser from has_many :through. We're not ready to talk about details of the project, but definitely expect something cool and useful!

update
: Chris Wanstrath is a part of our team as well. I'm really looking forward to this!

Beyond RD'06, I've had a lot going on over the last few weeks. Last month I sold some commercial development/consulting on Rails -- a hosted lead management system for small business. Also, I'm getting ready to launch an experiment in Web 2.0-style collaboration (codename Thoth) -- it will go up in the next few days.

Continue reading "Rails Day '06 here we come

RadRails

May 03 by Andre

Things I love about RadRails

  • Integrated SVN support
  • Easy tailing of development logs
  • Development Rails servers right there in the IDE
  • Control-shift-R (it's the little things!)

Things I wish RadRails had

  • Integrated FTP ala Dreamweaver (come on, you don't use Capistrano for EVERY little update). Note: may be coming in v0.7
  • . . . integrated Capistrano support (may be coming in v0.63)
  • a debugger! Why do they tease us with a debugging view :-(

Continue reading "RadRails

Selective Fixture Loading in Rails

Apr 27 by Andre
Selective Fixture loading is exactly what it sounds like: load in a subset of your fixtures rather than the whole enchilada. I have found this to be absolutely essential. The syntax is:  rake db:fixtures:load FIXTURES=neighborhoods,stores

While I'm on fixtures - dump your existing data to a fixture or a file: http://nubyonrails.com/articles/2005/12/27/dump-or-slurp-yaml-reference-data

Continue reading "Selective Fixture Loading in Rails

Rails, Geocoding, and Google Maps

Apr 18 by Andre
Update April '07: GeoKit, my Rails Geocoding plugin, abstracts away all the geocoding logic for multiple providers (Google, Yahoo Geocoder.us, Geocoder.ca -- including failover!), distance-based finders for ActiveRecord, and much more!
This is a simple example to demonstrate how to display a Google Map using Ruby on Rails, including utilizing a geocoding web service to translate addresses to geocodes.

Why the lookup web service? The Google Maps API doesn't just let you map an address. You need to supply the Maps API with a longitude and latitude to place a marker on a map (or even to center a map on a city). The geocoding web service translates a valid address into longitude and latitude; you can pass the result to the Maps API.

Continue reading "Rails, Geocoding, and Google Maps

Lots of Ruby power in a small package

Apr 14 by Andre
I came across these Ruby one-liners by David P. Thomas. Very cool -- I respect the brevity and utility. Finding things like this make me like Ruby (with or without the rails part) even more.

Continue reading "Lots of Ruby power in a small package

Automated backup using Amazon S3?

Apr 09 by Andre
A really nice application for Amazon's S3 would be a scheduled backup service. As long as I'm wishing someone would write this, here's my feature wish-list:
  • Select multiple folders to backup
  • Only backup changed files (since you pay for bandwidth)
  • Specify conditionals and/or regex's for the files you want backed up
  • Runs in the background; only alerts me if it wasn't able to complete a scheduled backup
  • Local encryption before sending it to the S3 servers
There are some interesting problems to solve here -- for example, since the backup takes time to complete, how can you ensure consistency of the backup set? Maybe you could take a local snapshot, and transmit to the S3 web service from the local cache.

On a related note, I came across an effort by Elliot Smith to build a Rails front-end to S3. Still in the proof-of-concept stage, but something to keep an eye on.

Continue reading "Automated backup using Amazon S3?

Scaling Ruby on Rails

Mar 28 by Andre
poocs.net continues their series on scaling Rails:
While a couple of high-traffic sites are being powered by Rails and while the Rails book has a handful of instructions to scale your application, it was apparent for us that you’re on your own at a certain point. This series of articles is meant to serve more as a case study as opposed to a generic “How To Scale Your Rails Application” piece of writing.
The third article (in a series of four) is here: http://poocs.net/articles/2006/03/27/the-adventures-of-scaling-stage-3

Continue reading "Scaling Ruby on Rails

Ruby on Rails 1.1 is out!

Mar 28 by Andre
I can't wait to try it out . . .

Continue reading "Ruby on Rails 1.1 is out!

Client does not support authentication protocol requested by server

Mar 24 by Andre
So I upgraded my local MySql server installation from 4.1 to 5.0. When I tried to generate scaffolds with Rails, I got a Client does not support authentication protocol requested by server; consider upgrading MySQL client.

If you run into this the answer is http://dev.mysql.com/doc/refman/5.0/en/old-client.html

In a nutshell, you have to run SET PASSWORD FOR 'some_user'@'some_host' = OLD_PASSWORD('newpwd');

Happy upgrading, MySQL users.

Continue reading "Client does not support authentication protocol requested by server