Tuesday, April 15, 2014

Temporarily Force a No-arg Initializer in Ruby

How to temporarily bypass a Ruby class initializer, which you shouldn't do unless you really just need access to the instance and don't require any setup that happens in the initializer:

def force_class_instance(klass)
  result = nil
  if klass.instance_method(:initialize).parameters.any? {|ar|ar[0] == :req}
    klass.class_eval 'alias_method :orig_initialize, :initialize; def initialize; end'
    result = klass.new
    warn_level = $VERBOSE
    $VERBOSE = nil
    klass.class_eval 'remove_method :initialize; alias_method :initialize, :orig_initialize; remove_method :orig_initialize'
    $VERBOSE = warn_level
    result = klass.new

Now just use force_class_instance(YourClass) to get an instance.

The $VERBOSE setting is to avoid "(eval):1: warning: removing `initialize' may cause serious problems" showing up in the log.

Tuesday, March 18, 2014

Extract the Version of Bundler, Chef, Ruby, etc. for CI

RVM and rbenv can use the .ruby-version file to define the Ruby version to use for your application. What about extracting the versions for Bundler and Chef?

If your application doesn't need to know anything about Chef, but you want your CI build/tests for that application to use your Chef bootstrapping process to set up your VM, maybe in your Chef repo where you keep your recipes, etc., your Chef Gemfile might look like:

chef_version = File.read(

gem 'chef', chef_version

And, let's say that you have a Chef bootstrap script that needs to install Bundler prior to doing bundle install:

gem install bundler --version=`cat .bundler-version`
bundle install

Now put the Chef version in .chef-version and the Bundler version in .bundler-version, e.g. if a version is 1.2.3, then the file would only contain:


Your application's repo just has .ruby-version and the rest of the application, but after you clone it and change to that directory in CI, you could do the following to grab and use those new version files from the Chef git repo. More about git archive here:

rbenv install `cat .ruby-version` || true
rbenv local
git archive --format=tar \
  --remote=your_chef_git_repo_uri \
  HEAD: .chef-version .bundler-version \
  | tar -x
gem install bundler --version=`cat .bundler-version`
gem install chef --version=`cat .chef-version`

That installs ruby if it isn't there (need || true so won't fail if already installed), uses it, just gets .chef-version and .bundler-version from another git repo, and then installs bundler, then chef, and you are ready to start bootstrapping. If you are using RVM like I do in development (this is the long version- you might not need all of this):

if test -f ~/.rvm/scripts/rvm; then
    [ "$(type -t rvm)" = "function" ] \
       || source ~/.rvm/scripts/rvm
rvm get stable
rvm install `cat .ruby-version` || true
# if using gemset, specify that too, and --create
rvm use `cat .ruby-version`
git archive --format=tar \
  --remote=your_chef_git_repo_uri \
  HEAD: .chef-version .bundler-version \
  | tar -x
gem install bundler --version=`cat .bundler-version`
gem install chef --version=`cat .chef-version`

It is a bit dirty. Docker is probably a better choice for getting setup quickly, but it's nice to share.

Monday, March 17, 2014

Using Bullet with MiniTest to Identify N+1 Queries

The Bullet gem can be helpful when used on an on-demand basis to identify some n+1 query problems in code called by your integration tests. Although Tommy noticed that Bullet gives a false notification (see #148 for status) when using empty? on a relation that would trigger a query unless there is an includes, it identified some n+1 queries effectively.

In your test group in Gemfile:

# this example is just for testing,
# but it might be helpful in development as well
group :test do
  # ...
  gem 'bullet', '~> 4.8.0'

Then at command-line:

bundle install

In test_helper, we require a file that contains something similar to the following:

  Bullet.enable = true
  # it is set this way by default, but being explicit
  Bullet.raise = false

  require 'minitest/unit'
  module MiniTestUsesBullet
    def before_setup
      super if defined?(super)

    def after_teardown
      super if defined?(super)
      if passed?
        # format however you want
        raise "Bullet failed: #{Bullet.warnings.inspect}" if Bullet.warnings.present?

  class MiniTest::Unit::TestCase
    include MiniTestUsesBullet

Then prefix BULLET=1 in command line when running a test or all tests. Bullet docs note similar, but they use rspec and they leave it kind of up-in-the-air of what to use. We originally tried Bullet.raise=true, but Tommy noticed that hides valid ActiveRecord errors because the Bullet (4.8.0 at least) raises at the end of the Rack request handling. So, instead you need to only fail if the test passed and if there are warnings. As Tommy noted, since it is Rack based, you might only add this to your integration tests (not controller tests, etc.) vs. MiniTest::Unit::TestCase, and he said this works for better format:

raise warnings.values.flatten.map(&:full_notice).join('\n') unless warnings.values.empty?

Wednesday, March 5, 2014

Using GC vs. ObjectSpace for Counting Arrays, etc. Created in Ruby

One way to use ObjectSpace to diff the number of Ruby arrays created is:

def log_objectspace_object_count_delta(key)
  orig = ObjectSpace.count_objects[key]
  puts "#{ObjectSpace.count_objects[key] - orig} ObjectSpace objects created"

Which would let you specify the key, in this case :T_ARRAY, to find the delta in object count:

2.1.0p0 :006 > log_objectspace_object_count_delta :T_ARRAY do; nil; end
0 ObjectSpace objects created
 => nil 
2.1.0p0 :007 > log_objectspace_object_count_delta :T_ARRAY do; []; end
1 ObjectSpace objects created
 => nil 

GC has a nice little stat method that might also help, e.g.:

def log_gc_objects_allocated_delta
  orig = GC.stat(:total_allocated_object)
  puts "#{GC.stat(:total_allocated_object) - orig} objects allocated"

2.1.0p0 :006 > log_gc_objects_allocated_delta do; nil; end
0 objects allocated
 => nil 
2.1.0p0 :007 > log_gc_objects_allocated_delta do; []; end
1 objects allocated
 => nil 

We are intentionally not taking into account :total_freed_object there, because our goal is to just see how many objects were allocated.

And, please note that, according to the Ruby 2.1 API: "The contents of the hash are implementation specific and may be changed in the future. This method is only expected to work on C Ruby."

Friday, February 21, 2014

Puts'ing/Logging all ActiveRecord Callbacks in All Models in Rails

Sometimes it is helpful in test_helper or wherever to start puts'ing/logging all (or some) ActiveRecord callbacks.

The following loads all models in a Rails app (if it can) and adds callbacks to puts that they were called. Not perfect, but works enough for this example. Try putting this into an initializer, e.g. config/initializers/001_log_callbacks.rb:

# Log all callbacks
Dir[Rails.root.join('app/models/*.rb').to_s].each do |filename|
  name = File.basename(filename, '.rb')
    model = name.camelize.constantize
    ActiveRecord::Callbacks::CALLBACKS.each do |callback|
      if callback.to_s.start_with?('around_')
        model.class_eval "#{callback} do |*args, &prc|; puts \"#{model}(\#{id}).#{callback} start\"; prc.call(*args) if prc; puts \"#{model}(\#{id}).#{callback} end\" end"
        model.class_eval "#{callback} do; puts \"#{model}(\#{id}).#{callback}\" end"

Note that this is loading all models directly under app/models/ so you might not want to use this for more than temporary debugging.

Wednesday, February 19, 2014

Ruby's ObjectSpace and Diffy to Diagnose Unknown Object Changes

Ever diff'd ObjectSpace in Ruby/Rails?

As an example, I had a situation where I thought maybe I wasn't saving a record, and because it was a complex structure, the simplest thing seemed to be to try this:

unsaved = ObjectSpace.each_object(ActiveRecord::Base).to_a.select(&:changed?)
raise "expected all models in memory to be saved, but the following had changes:\n#{unsaved.join("\n")}" if unsaved.size > 0

...but that didn't show anything.

Since none of the model instances where changed- I thought it would be interesting to reload every ActiveModel::Base instance, then do a before and after diff of model instances in ObjectSpace. That way in scanning the diff, I might happen to see difference between model instances without having to deep dive through a bunch of associations, comparing objects.

Seems somewhat random, but it was interesting.

First add Diffy to your Gemfile and bundle install. Then after doing some queries/updates, try this:

# Note: this is a good way to waste memory and create
# some very long strings

# First, let's garbage collect. Then inspect all
# ActiveRecord::Base instances in ObjectSpace, sort
# those inspection strings, and then split them
# into lines for Diffy.
a = ObjectSpace.each_object(ActiveRecord::Base).to_a.map(&:inspect).sort.inspect.gsub(",", ",\n").gsub(">\",", ">\",\n")

# Now, let's reload all of them, just for kicks. :)
ObjectSpace.each_object(ActiveRecord::Base) {|m|m.reload}

# Do what we did before.
# part of ObjectSpace
b = ObjectSpace.each_object(ActiveRecord::Base).to_a.map(&:inspect).sort.inspect.gsub(",", ",\n").gsub(">\",", ">\",\n")

# Diff it!
if a != b
   puts "Changes: #{Diffy::Diff.new(a, b).to_s}"

Monday, February 17, 2014

Whether to Use where(...).first or find_by(...)

Just had a fun question of whether it is best to use find_by or where(...).first with Rails and ActiveRecord.

The answer is: "it depends". :) Basically find_by is less to write, but because it isn't automatically ordering by primary key in the query, which record you get back if there are two with that same field value may not be as definitive, if you are relying on the record with the lowest primary key to be returned if more than one are matched.

The ORDER BY that is automatically added by the where(...).first takes very slightly longer to process, so if it is not needed, and you don't need to do something else to the relation, find_by is probably a winner because it is shorter and faster. But to some developers, they may look at find_by and think find and that it will raise an exception if not found, which it won't, and that could cause bugs or problems diagnosing errors, so the few saved characters may not be worth it.

However, I'm starting to use find_by more than I used to.

Update: With the changes coming in Rails 4.2 (see tenderlove's post about AdequateRecord), find_by is an even better idea.