Monday, April 21, 2014

More About the :shipit: Squirrel Emoji

The Ship It squirrel emoji seemingly makes little sense. Chip it might have made sense if it were a chipmunk, or even a beaver. But, "Ship It Squirrel"? I have no idea how they came up with that.

So, what's the deal? According to the Quora answers on the subject, it is just a mascot that GitHub chose. GitHub also has provided a little more info on their ship it philosophy with mention of the squirrel in their blog. And of course, they have a dedicated website for it called shipitsquirrel.github.io with the page title "SHIP IT" and an image from National Geographic that was modified to indicate the squirrel is saying, "Ship it!" The original version of that photo was called "Squirrel Portrait, Banff" posted in photography.nationalgeographic.com, and apparently was a photo-bombing squirrel which became an "honourary citizen" of Banff.

But, that isn't the squirrel emoji used for :shipit: in GitHub. That emoji is small version of a meme I found on cheezeburger.com, called Inspector Squirrel, along with the largest unaltered version of it I could find.

If you have more info about this emoji or squirrel, let me know!

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
  else
    result = klass.new
  end
  result
end

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(
  File.join(
    File.dirname(__FILE__),
      '.chef-version')).strip

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:

1.2.3

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
fi
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'
end

Then at command-line:

bundle install

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

if ENV['BULLET']
  Bullet.enable = true
  # it is set this way by default, but being explicit
  Bullet.raise = false

  require 'minitest/unit'
  module MiniTestUsesBullet
    def before_setup
      Bullet.start_request
      super if defined?(super)
    end

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

  class MiniTest::Unit::TestCase
    include MiniTestUsesBullet
  end
end

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]
  yield
  puts "#{ObjectSpace.count_objects[key] - orig} ObjectSpace objects created"
end

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)
  yield
  puts "#{GC.stat(:total_allocated_object) - orig} objects allocated"
end

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')
  begin
    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"
      else
        model.class_eval "#{callback} do; puts \"#{model}(\#{id}).#{callback}\" end"
      end
    end
  rescue
  end
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:

GC.start
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.
GC.start
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}"
end