Tag Archive > rails

Checkout Hoptoad by thoughtbot, inc.

Dave » 15 June 2009 » In Technology, rails » 2 Comments

I’ve been using Hoptoad for about two months now, and I’m sold. Hoptoad is a product by thoughbot, inc. Previously, I’ve always used the excellent exception_notification plugin. The exception_notification plugin is easy to use, and it works great. However, there are a couple of problems with it.

  1. You could flood your inbox if there is a bad problem on a popular page.
  2. It’s difficult to collect all the emails and track errors.

Enter Hoptoad. Hoptoad solves both problems, and it is super simple to use and setup. It’s free for one project and two users. If that isn’t enough for you, the premium services are very reasonable.

How does it solve both problems? First, Hoptoad will still send you an email when there is an error. However, it won’t flood your inbox if there are hundreds of the same email. Second, Hoptoad provides a simple web-based system that groups your errors by exception class. That makes it easier to track down problems. Finally, you can mark errors resolved so you don’t have to wade through noise to solve the real problems.

Hoptoad installation:

REMOVE EXCEPTION_NOTIFIER

  1. In your ApplicationController, REMOVE this line:include ExceptionNotifiable
  2. In your config/environment* files, remove all references to ExceptionNotifier.
  3. Remove the vendor/plugins/exception_notifier directory.

INSTALL HOPTOAD_NOTIFIER

From your project’s RAILS_ROOT, run:

script/plugin install git://github.com/thoughtbot/hoptoad_notifier.git

CONFIGURATION

You should have something like this in config/initializers/hoptoad.rb.

HoptoadNotifier.configure do |config|
  config.api_key = '1234567890abcdef'  # You get your key when you sign up
end

(Please note that this configuration should be in a global configuration, and is not enrivonment-specific. Hoptoad is smart enough to know what errors are caused by what environments, so your staging errors don’t get mixed in with your production errors.)

Once you do the above, any Exception not caught by your controllers will be sent to Hoptoad where where they can be aggregated, filtered, sorted, analyzed, massaged, and searched.

Now, if you have read anything I’ve done before, you know I do a lot of asynchronous processing with Workling. By default, Hoptoad will not log errors from anything outside of your controllers. Fear not. Hoptoad provides a webservice API to send errors. Here is an example.

In workling/lib/workling/base.rb:

    # takes care of suppressing remote errors but raising Workling::WorklingNotFoundError
    # where appropriate. swallow workling exceptions so that everything behaves like remote code.
    # otherwise StarlingRunner and SpawnRunner would behave too differently to NotRemoteRunner.
    def dispatch_to_worker_method(method, options)
      begin
        self.send(method, options)
      rescue Exception => e
        raise e if e.kind_of? Workling::WorklingError
        logger.error "Workling Error: runner could not invoke #{ self.class }:#{ method } with #{ options.inspect }. error was: #{ e.inspect }\n #{ e.backtrace.join("\n") }"
        # DND: Let HopToad know of the issue
        params = options || {}
        HoptoadNotifier.notify(
          :error_class => "Workling Error - #{e.class.name}",
          :error_message => "Workling Error(#{e.class.name}): #{e.message}",
          :request => { :params => params.merge(:worker_class => self.class.name, :worker_method => method) })
        # DND: end of change
      end
    end

Now, any error not caught by your workers will be sent to Hoptoad for processing. You can use a similar method from Rake tasks or any scripts that run outside of controllers. Remember, Hoptoad will collect errors by :error_class, so you can use different classes to separate errors into bins based on where they came from.

Give Hoptoad a try. You will not be disappointed.

Continue reading...

Tags: , , ,

Gotcha with find_each and find_in_batches

Dave » 20 May 2009 » In Technology » 1 Comment

Rails 2.3 added a couple of nice new methods – find_each and find_in_batches. Both methods accomplish the same thing in a slightly different way. Unlike a normal finder these methods grab objects in batches instead of all at once. For instance, if you have 500,000 users, you don’t want to do the following:

User.find(:all).each { |user| user.some_method }

The reason is that you just loaded all 500,000 records into memory, and your server is not happy. Instead, you could do:

User.find_each { |user| user.some_method }

By default, the above will only load 1,000 User objects into memory, and your server will thank you. If 1,000 is too big/small for you, use the :batch_size option to change it. The find_in_batches method is similar except that it provides the array to the block instead of one object at a time. For example:

User.find_in_batches do |users|
  users.each { |user| user.some_method }
end

If you ever used the wonderful will_paginate gem, you are probably familiar with the concept from the paginated_each method that will_paginate provided.

So, what is the problem? The problem is that you have to aware that unlike paginate_each, find_each and find_in_batches work by setting up a with_scope block. Therefore, if you need to do any other finds on that same model, the scope will apply. Usually this only affects relationships, but it isn’t hard to forget. Here is an example:

# This is a purely made up example
class User < ActiveRecord::Base
  # There is a last_login_at attribute
  named_scope :recent_login,
              lambda { |*args|
              { :conditions => ["people.last_login_at >= ?", (args.first || 1.week.ago)] } }
  belongs_to :parent, :class_name => "User", :foreign_key => "parent_id"
end 
User.recent_login.find_each do |user|
  parent = user.parent # This will include the recent_login scope.
end 

It’s no different than other with_scope issues, but it isn’t as obvious. You can get around it by doing:

User.recent_login.find_each do |user|
  # Got use send because with_excusive_scope is protected.
  User.send(:with_exclusive_scope)
    parent = user.parent # This will include the recent_login scope
  end
end

Now, go and be kind to your server with find_each and find_in_batches – just remember the scope.

Continue reading...

Tags: , , ,