Unit test your private methods for great justice

The continuing dissent and confusion about unit testing of private class methods surprises me.

The access specifier is much like your choice of software license: it exists to limit consumers’ actions, not to limit yours. A method’s access specifier is completely irrelevant to testing, and only describes what you want the consumer to use; any code that takes inputs and produces outputs, private or not, should be tested.

The opponents of private-method testing tend to argue in quasi-religious terms: that private methods are mere hidden implementation details; that users of the class will only care about the public API; that testing of private methods breaks encapsulation. A typical unhelpful “solution”: private methods should be put into a different class and made public there.

To argue against granular testing of private methods is to mean well while being thoroughly unhelpful. The purpose of testing is more than just to guarantee the viability of your public interface – it is also to examine the inner machinery and support routines of your class to ensure that they themselves function correctly for a spectrum of inputs and edge cases. The private implementation will contain non-trivial complexities that are more readily and precisely tested directly than via the public API.
Continue reading

Advertisements

Beautiful logging for Ruby on Rails 4

In a previous post I showed you a simple way to get beautiful, easy-to-read logs in your Rails 3.2 application. Rails 4 changed the game again; for Rails 3.2 or earlier, refer to my earlier post; but for Rails 4 read on…

It’s really easy. Just make a new file in your ‘config/initializers’ directory called something like ‘log_formatting.rb’ and paste into it the following code. Restart your app, and voila: pretty logs again!

UPDATED. Konrad’s comment below was correct. I’ve altered this code to work with both the regular logger and the new tagged logger. Now you can configure your logger as

config.logger = ActiveSupport::Logger.new('your_app.log')

or

config.logger = ActiveSupport::TaggedLogging.new(Logger.new('your_app.log'))

… both will work. Here’s the updated monkey patch:

class ActiveSupport::Logger::SimpleFormatter
  SEVERITY_TO_TAG_MAP     = {'DEBUG'=>'meh', 'INFO'=>'fyi', 'WARN'=>'hmm', 'ERROR'=>'wtf', 'FATAL'=>'omg', 'UNKNOWN'=>'???'}
  SEVERITY_TO_COLOR_MAP   = {'DEBUG'=>'0;37', 'INFO'=>'32', 'WARN'=>'33', 'ERROR'=>'31', 'FATAL'=>'31', 'UNKNOWN'=>'37'}
  USE_HUMOROUS_SEVERITIES = true

  def call(severity, time, progname, msg)
    if USE_HUMOROUS_SEVERITIES
      formatted_severity = sprintf("%-3s",SEVERITY_TO_TAG_MAP[severity])
    else
      formatted_severity = sprintf("%-5s",severity)
    end

    formatted_time = time.strftime("%Y-%m-%d %H:%M:%S.") << time.usec.to_s[0..2].rjust(3)
    color = SEVERITY_TO_COLOR_MAP[severity]

    "\033[0;37m#{formatted_time}\033[0m [\033[#{color}m#{formatted_severity}\033[0m] #{msg.strip} (pid:#{$$})\n"
  end
end

Indexing rich documents with Rails, Sunspot, Solr, Sunspot Cell and Carrierwave (cookbook-style)

Solr / Sunspot installation and configuration is easy when you just need to index and search your model data. I won’t go into details about configuring basic Sunspot / Solr for Rails here. For a great primer on Sunspot and basic installation instructions, I recommend Ryan Bates’ Railscast.

But configuring Solr to index rich documents (e.g. PDFs, Word documents) via Sunspot Cell is really quite badly documented, and it doesn’t need to be. Once you configure things correctly, it really does work. Learn from my pain…

Read the rest of this story…

Beautiful Logging for Ruby on Rails 3.2

UPDATE: Go here for the Rails 4 version…

In my previous post I showed you a simple way to get beautiful, easy-to-read logs in your Rails application. That trick stopped working in Rails 3.2. So for Rails 3.1 or earlier, refer to my earlier post; but for 3.2 on, read on…

It’s really easy. Just make a new file in your ‘config/initializers’ directory called something like ‘log_formatting.rb’ and paste into it the following code. Restart your app, and voila: pretty logs again!

class ActiveSupport::BufferedLogger
  def formatter=(formatter)
    @log.formatter = formatter
  end
end

class Formatter
  SEVERITY_TO_TAG_MAP     = {'DEBUG'=>'meh', 'INFO'=>'fyi', 'WARN'=>'hmm', 'ERROR'=>'wtf', 'FATAL'=>'omg', 'UNKNOWN'=>'???'}
  SEVERITY_TO_COLOR_MAP   = {'DEBUG'=>'0;37', 'INFO'=>'32', 'WARN'=>'33', 'ERROR'=>'31', 'FATAL'=>'31', 'UNKNOWN'=>'37'}
  USE_HUMOROUS_SEVERITIES = true

  def call(severity, time, progname, msg)
    if USE_HUMOROUS_SEVERITIES
      formatted_severity = sprintf("%-3s","#{SEVERITY_TO_TAG_MAP[severity]}")
    else
      formatted_severity = sprintf("%-5s","#{severity}")
    end

    formatted_time = time.strftime("%Y-%m-%d %H:%M:%S.") << time.usec.to_s[0..2].rjust(3)
    color = SEVERITY_TO_COLOR_MAP[severity]

    "\033[0;37m#{formatted_time}\033[0m [\033[#{color}m#{formatted_severity}\033[0m] #{msg.strip} (pid:#{$$})\n"
  end

end

Rails.logger.formatter = Formatter.new

Credit for figuring out how to get log formatting working with Rails 3.2 goes to JRochkind. If you’d like a complete log formatting solution in the form of a gem, see his FormattedRailsLogger gem at Github.

Humorously informative Rails logging

I’ll just leave this here:

module ActiveSupport
  # Format the buffered logger with timestamp/severity info.
  class BufferedLogger
    NUMBER_TO_NAME_MAP  = {0=>'meh', 1=>'fyi', 2=>'hmm', 3=>'wtf', 4=>'omg', 5=>'???'}
    NUMBER_TO_COLOR_MAP = {0=>'1;30',  1=>'0;36', 2=>'0;33', 3=>'1;33',  4=>'1;31',  5=>'0;37'}

    def add(severity, message = nil, progname = nil, &block)
      return if @level > severity
      sevstring = NUMBER_TO_NAME_MAP[severity]
      color     = NUMBER_TO_COLOR_MAP[severity]

      message = (message || (block && block.call) || progname).to_s
      message = "\033[0;37m#{Time.now.to_s(:db)}\033[0m [\033[#{color}m" + sprintf("%-3s","#{sevstring}") + "\033[0m] #{message.strip} (pid:#{$$})\n" unless message[-1] == ?\n
      buffer << message
      auto_flush
      message
    end
  end
end

(To use, put this code in an initializer in config/initializers and restart. Provides highly-readable, timestamped, colorized logging for your rails app.)

Twitter, OAuth, and Ruby on Rails integrated cookbook-style in the console (updated for Twitter 1.0)

Update March 17, 2011: This is a rewrite of my October post of the same name, modified for the changes that were made in the Twitter gem from 1.0 onward. John Nunemaker removed native oauth capability from the gem at 1.0, necessitating a rewrite of my instructions below.

As of August 31, 2010, Twitter officially deprecated HTTP Auth from the Twitter API in favor of OAuth. OAuth has a number of benefits as far as user authorization is concerned, but it is a tad complicated for the developer, especially if you simply want your application to interact with Twitter. So even if you aren’t concerned at all with authorizing your application’s users — for example, if you want your application to post tweet updates to its own account — OAuth is your only choice at this point. Here is a quick and dirty, cookbook-style recipe to get your Ruby on Rails application to interact with Twitter.

Read the rest of this story…

An init script for managing delayed_job

If you run delayed_job as a daemon on a server, you’d probably like a way to start it when the system boots. Furthermore, you’re probably using RVM at this stage, which requires its own finicky management details within a script. This little init script will handle all those details for you and make starting and stopping the delayed_job daemon a straightforward process.

You’ll want to modify paths appropriate for your system. Also, although this init script is tuned for Gentoo, it can easily be adapted for Red Hat and other Linux variants.

Read the rest of this story…