Recently, I was playing around with a little OAuth consumer app that I had thrown together.  Everything was all fine and dandy until suddenly I began seeing those damned "Something went wrong" Rails error pages.  You know what I'm talking about...

Screen_shot_2010-04-21_at_1.15.52_AM.png

Well, as it turns out, the problem was the sketchy OAuth provider api.  It was down/unresponsive/whatever.  Shame on me for not writing robust enough code to blow up gracefully.

What's a girl to do?  I'm not sure about her, but what I did was come up with a dry little way to not see that stupid red apology again.

All of my OAuth crap was in the same controller, and whenever the oauth gem choked on the sketchy api I just wanted it to redirect back to the referrer.  So here's what I did...

def raises_exception?
  begin
    yield
  rescue => ex
    Rails.logger.info("Exception occurred (#{ex.class.to_s}): #{ex.message}")
    flash[:error] = "There was a problem completing your request.  Please try again later."
    redirect_to request.referrer
    return true
  end
  return false
end

And then anywhere in my controller that I was calling an oauth method that might spawn a request (and bomb), I did this...

return if raises_exception? do
  # call out to the sketchy api
end

So in the raises_exception? method, it yields to block of code inside a rescue block.  If an exception occurs, its rescued, logged, and then the request is redirected back to the referrer.

One thing to take note of -- you have to prepend the raises_exception? call with return if.  Otherwise, the code in that action will continue to execute, and I'm pretty sure you wouldn't want that.

There you have it, just a little sumthin-sumthin...

Comments (0)    rails ruby oauth

You can never have too many.

Much like every new version of OSX, there have been a plethora of "How to install BLANK on Snow Leopard" blog posts and walk-throughs detailing all the little tips and tricks around how to install some tool or piece of software.  Having a lot of options is awesome, but in the words of the great Biggie Smalls, "mo' blog posts, mo' problems".

That IS how it went, right?

Anyway, with all of these walk-throughs, how do you know which ones are good, and which one just suck.  Well, you don't really...

So here's a list of a few [confirmed] valid and useful dev setup walk-throughs:


First, this is actually a series of posts, as opposed to one single write-up.  Actually, its not even a series of posts.  Its just the search results for 'Snow Leopard' on the Hive Logic site.  So really, its only the first 3-5 posts that matter.

http://hivelogic.com/search/results/a7a831b978f2667fa301ea095d3d8fa7

This is the route that I personally followed after a fresh install of Snow Leopard, and I had everything up and running in no time.


Next, Robby On Rails did a thorough and entertaining post on Snow Leopard Rails dev env setup, or SLRDESU for short.  Acronyms make everything better (AMEB).  I don't know about you, but Robby's older post about getting setup with Passenger came in handy for me on more than one occasion.

http://www.robbyonrails.com/articles/2010/02/08/installing-ruby-on-rails-passenger-postgresql-mysql-oh-my-zsh-on-snow-leopard-fourth-edition

His latest post covers everything from start to finish, and he even included a few video to pass the time while waiting for binaries to build and whatnot.  I haven't personally used this walk-through, but based on my previous experience with Robby's posts, and the recommendation from coworkers, I'm sure it'll get you where you need to be.


Another noteworthy mention comes from the guys over at Thoughtbot, the makers of such wonderful tools as Shoulda, Paperclip, and Factory Girl.  Their robot-laden guide goes beyond just Rails/dev-related stuff, and covers the likes of several generally useful OSX tools.  Things like Quicksilver, Fluid, and Firefox/Firebug.

http://robots.thoughtbot.com/post/159805668/2009-rubyists-guide-to-a-mac-os-x-development

This is another one that I haven't personally used, but I think we can trust the guys over at Thoughtbot.  After all, their company reputation depends on it!


So there you have it.  Three different hand-holding recipes for getting you set up on Snow Leopard.  If you haven't upgraded yet, what're you waiting for?  Get to it!

Comments (0)    git rails mysql snow leopard ruby

Earlier this afternoon, I was debugging an ajax call that consistently resulted in an error, but only in IE.  Checking the log file, I found this:

Processing ApplicationController#index (for 192.168.1.108 at 2009-10-27 14:37:03) [GET]
Session ID: ddde16cf83baca85a81e9fb0772c2844
Parameters: {"format"=>"js", "page"=>"1"}


ActionController::MethodNotAllowed (Only get, head, post, put, and delete requests are allowed.):
/vendor/rails/actionpack/lib/action_controller/routing/recognition_optimisation.rb:65:in `recognize_path'
/vendor/rails/actionpack/lib/action_controller/routing/route_set.rb:384:in `recognize'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:154:in `handle_request'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:107:in `dispatch'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:104:in `synchronize'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:104:in `dispatch'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:120:in `dispatch_cgi'
/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:35:in `dispatch'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/railz/request_handler.rb:50:in `process_request'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/abstract_request_handler.rb:207:in `main_loop'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/railz/application_spawner.rb:378:in `start_request_handler'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/railz/application_spawner.rb:336:in `handle_spawn_application'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/utils.rb:183:in `safe_fork'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/railz/application_spawner.rb:334:in `handle_spawn_application'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/abstract_server.rb:352:in `__send__'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/abstract_server.rb:352:in `main_loop'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/abstract_server.rb:196:in `start_synchronously'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/abstract_server.rb:163:in `start'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/railz/application_spawner.rb:213:in `start'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/spawn_manager.rb:262:in `spawn_rails_application'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/abstract_server_collection.rb:126:in `lookup_or_add'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/spawn_manager.rb:256:in `spawn_rails_application'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/abstract_server_collection.rb:80:in `synchronize'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/abstract_server_collection.rb:79:in `synchronize'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/spawn_manager.rb:255:in `spawn_rails_application'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/spawn_manager.rb:154:in `spawn_application'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/spawn_manager.rb:287:in `handle_spawn_application'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/abstract_server.rb:352:in `__send__'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/abstract_server.rb:352:in `main_loop'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/lib/phusion_passenger/abstract_server.rb:196:in `start_synchronously'
/Library/Ruby/Gems/1.8/gems/passenger-2.2.5/bin/passenger-spawn-server:61

Rendering /Users/brent/Intridea/project/vendor/rails/actionpack/lib/action_controller/templates/rescues/layout.erb (method_not_allowed)

WTF?  That told me a whole lotta nothing.

First of all, it was blowing up before it even got into the application code, which was was strange because it worked just fine in Firefox, Safari, etc.  Second, I checked the response body and it was returning html.  Html?  The format is clearly specified as "js" in the request parameters.  Double-checking the controller code, there was definitely a respond_to block with format.js, so why was it returning html?

I showed this to one of my coworkers and he asked if I had tried switching the format calls in the respond_to block.  There were two, one for html and one for javascript.  I switched them up, and put the format.js first.  I reloaded the page, and what do you know, it worked!  No error.  Again, wtf?  He told me that this same bug had kicked his ass on a previous project.

Aparently, IE7 isn't specific about what sort of response it expects in the accept header.  This causes Rails to merely return the first format that it comes to.  In my case, the html.

So if you're not seeing the format that you're expecting when testing with IE7, try reordering the format calls in the respond_to block.

Comments (2)    rails ie respond_to bug

Picture_6.png

Out of sheer boredom last night, I decided to whip up a mobile view for my blog.

It was extremely easy thanks to the mobile-fu plugin by my friend Brendan.  I also found a few tips and tricks from this post on the Engage Interactive blog such as how to handle orientation changes and how to specify an image for home screen bookmarks.

I did most of my testing via the iPhone Simulator from the SDK and also a Fluid browser which allows you to specify mobile safari as the user agent, giving you the proper look, and you can use Safari's built-in Firebug-like tools.

If you've got an iPhone, give it a look...

Comments (0)    rails iphone mobile

Are those pants made of mirrors, because I can see myself in them...

No, I'm not going to tell you how to seduce the comic book superhero with an evening of dinner, cocktails, and smooth talk while demonstrating impeccable manners and etiquette.

What I am going to tell you is how to correctly use the handy Rails tool for passing objects between actions.  Now, this is no huge secret or stunningly clever trick.  In fact, you probably already know what I'm about to tell you.  It's just one of those things that I never think to do until after it's a problem.

In the typical Rails app, there a snippet/partial/whatever that displays anything from the flash hash with either :notice or :error keys.  If, in your controller action, you set flash[:notice] equal to some message and then redirect to another action, that message will persist through the redirect and get displayed on the subsequent view.

Here's the problem.  If, in your action, you just render a view template instead of redirecting, then the user will see that message like you intended but it will also still remain on the following request which may or may not be confusing.  Fortunately, there's an easy way to avoid this.

If you know you'll be redirecting, then there's nothing to worry about.  Business as usual.  But if you're not redirecting, just rendering a view, then you can use flash.now[:key].  The 'now' method only maintains the flash contents through the current request and is cleared before the next action.  Check it out.

def create
  @thing = Thing.new(params[:thing])
  if @thing.save
    flash[:notice] = "Oh snap!  You created a thing!"
    redirect_to @thing
  else
    flash.now[:error] = "Damn dog, you messed up"
    render :action => :new
  end
end

Notice how when the thing save without errors we use flash[] and redirect, but when there are errors we use flash.now[] and there's no redirect.  This will keep your app users from seeing any strance, out of place errors.

So that's it.  Like I said, it's nothing monumental.  As you were...

Comments (0)    rails flash

feat-shamwow.jpg

Remember my post from a few weeks back about temporarily disabling ActiveRecord callbacks?  Well, I decided to extend the functionality a bit and wrap it all up in a plugin.

You can now specify one or more callbacks to disable, or specify nothing and disable all callbacks by default.

You can check out the project page or jump over to the Github repo.

Comments (0)    git rails active_record callbacks plugin

Setting up polymorphic associations is ridiculously easy in Rails.  Setting up many-to-many associations in Rails is also ridiculously easy in Rails.  However, setting up polymorphic many-to-many associations in Rails is more difficult, but only slightly.

Recently, on an Intridea client project, I had one particular model that had many-to-many associations with several other models in the app.  The thought of multiple join tables in the database didn't sit well with me, so I decided to consolidate things a bit.

Now I can't use the actual models from the app without possibly giving away the yet-to-be-launched app.  So instead, I'll use an example that we're all familiar with, tags and taggings.

You know the drill.  You have your common model, Tag.

class Tag < ActiveRecord::Base
  has_many :taggings, :dependent => :destroy
  has_many :books, :through => :tagings, :source => :taggable, :source_type => "Book"
  has_many :movies, :through => :tagings, :source => :taggable, :source_type => "Movie"
end

Your join model, Tagging.

class Tagging < ActiveRecord::Base
  belongs_to :taggable, :polymorphic => true
  belongs_to :tag
end

And your taggable models, Book and Movie.

class Book < ActiveRecord::Base
  has_many :taggings, :as => :taggable
  has_many :tags, :through => :taggings
end

class Movie < ActiveRecord::Base
  has_many :taggings, :as => :taggable
  has_many :tags, :through => :taggings
end

Everything in Tagging, Movie, and Book is pretty much your standard setup.  This gives you all the normal associations.  You've got tag.taggings, book.tags, movie.tags, and tag.taggable.

It gets a little trickier when you want to find all movies (or books) associated with a tag.  There are a number of ways that this can be done.  You could use some crazy joined finder call on Movie, or find all Taggings with a particular taggable type (or a named scope) and collect the movies, or do it like up above.

has_many :movies, :through => :tagings, 
                  :source => :taggable, 
                  :source_type => "Movie"

This association allows us to access a movies with a particular tag directly from the tag object itself.  It's just your typical has_many association, but with a few more options.  The :through option says that movies can be accessed through our join model, tagging.  Rails normally would expect there to be a movies association on tagging, but the :source option tells Rails to examine the taggable association instead.  Similarly, the :source_type option specifies the class (or type) of the polymorphic association that we trying to retrieve.

So remember when you want bidirectional class-specific many-to-many polymorphic associations, :source and :source_type are your friend.

Comments (8)    rails active_record

google-apps-logo.jpgWant to use Gmail, or a Google Apps account, with your Rails app?  Well, it's gotten even easier than before.

The last time I set up and app to use Gmail's smtp, I had to use a plugin called ActionMailerTLS.  Well, not any more.  Ever since Rails 2.2.2, the tls functionality is built right in, but there's a catch.

While setting up notifications for this app, it took me a few minutes to realize what the problem was.  This app is running on Rails 2.2.2, but the emails were not being sent out.  WTF?  I went back through all of the write-ups I had and seen and finally caught it.

It only works if you're running Ruby version 1.8.7, not 1.8.6.

Fortunately my Dreamhost shared hosting already had 1.8.7 installed, so notifications are working perfectly.  So leave me a comment, and I'll be notified!

Comments (0)    rails gmail action_mailer_tls

So I was working on an Intridea project where I needed to send out a daily digest email.  The emails needed to be sent out on a scheduled interval, not as the result of some user action.  This meant that it would need to be handled outside of the normal request/response cycle.

Typically in the Rails world, when someone mentions long-running, or background tasks, you think either BackgrounDrb or Starling/Workling.  Those tools are fine and all -- actually, don't get me started on BackgrounDrb -- but I wanted something a bit more simple.

I didn't need queueing, or jobs, or anything like that.  I had only one task to manage.  I just want something that will say, "Once a day, do [task]", and that's all.

Also, I really didn't want to have to monkey with deployment tasks, be it with Vlad or Capistrano or whatever.  I wanted to just deploy as usual, and have it all just work, no extra processes to start or tasks to run.

This seemed like a tall order, but I began my search anyway.  Google led me to a number of pages about a number of tools, all of which included one or more of the things I was trying to avoid.  Of all places, I finally ended up on this page from the Ruby On Rails wiki.

Despite having only a one line mention near the bottom of the page, I decided to check out rufus-scheduler, and it turned out to be exactly what I was looking for.  There was no database table, queueing mechanism, or separate process to manage.  Just a simple scheduler to call out to your existing ruby code.

It couldn't get any simpler.  Here's how I set it up...

First, install the gem.

sudo gem install rufus-scheduler

I also froze it into vendor/gems by declaring the gem in the initializer block of environment.rb and then running rake gems:unpack.

Then I created a file called task_scheduler.rb in my config/initializers directory.   Inside this file is where the magic happens.  This is where it all goes down.  Are you ready?  Here it is...

scheduler = Rufus::Scheduler.start_new

scheduler.every("1m") do
  DailyDigest.send_digest!
end

Yeah, that's it. Seriously.

I was in disbelief myself until I started up the server and watched it send me an email.  In it's current state, it would send the digest every minute, which is not what I wanted.  Fortunately, the rufus-scheduler provides several ways to schedule your tasks.  Once I was ready, I changed it to more of a cron-style scheduler.

scheduler = Rufus::Scheduler.start_new

# Send the digest every day at noon
scheduler.cron("0 12 * * *") do
  DailyDigest.new.send_digest!
end

You could also use scheduler.in or scheduler.at for one time tasks set to run at some point in the future.

I know a guy who's from "the future", but that's another story.

Anyway, there you have it.  Dead simple task scheduling in Rails.  It really doesn't get any easier than that.

Comments (0)    rails task-scheduling