Close and Go BackBack to Viget

RubyInline in Shared Rails Environments

David Eisinger
David Eisinger, Senior Developer, May 23, 2008

As an end-to-end web production company, we have a vested interest in making Rails applications easier to deploy for both development and production purposes. We’ve developed Tyrant, a Rails app for running Rails apps, and we’re eagerly watching as new solutions are created and refined.

But it’s a new market, and current solutions are not without their share of obstacles. In working with both Tyrant and Phusion Passenger, we’ve encountered difficulties running applications that use RubyInline to embed C into Ruby code (e.g., ImageScience, my image processing library of choice). Try to start up an app that uses RubyInline code in a shared environment, and you might encounter the following error:

/Library/Ruby/Gems/1.8/gems/RubyInline-3.6.7/lib/inline.rb:325:in `mkdir':
Permission denied - /home/users/www-data/.ruby_inline (Errno::EACCES)

RubyInline uses the home directory of the user who started the server to compile the inline code; problems occur when the current process is owned by a different user. “Simple,” you think. “I’ll just open that directory up to everybody.” Not so fast, hotshot. Try to start the app again, and you get the following:

/home/users/www-data/.ruby_inline is insecure (40777). It may not be group or world writable. Exiting.

Curses! Fortunately, VigetExtend is here to help. Drop this into your environment-specific config file:

temp = Tempfile.new('ruby_inline', '/tmp')
dir = temp.path
temp.delete
Dir.mkdir(dir, 0755)
ENV['INLINEDIR'] = dir

We use the Tempfile library to generate a guaranteed-unique filename in the /tmp directory, prepended with “ruby_inline.” After storing the filename, we delete the tempfile and create a directory with the proper permissions in its place. We then store the directory path in the INLINEDIR environment variable, so that RubyInline knows to use it to compile.

Make Apple’s Addressbook Pull from Exchange

Tony Pitale
Tony Pitale, Former Staffer, May 23, 2008

If you've ever wished you knew who you worked with but were to afraid to ask...entourage.

Continue reading "Make Apple’s Addressbook Pull from Exchange"

Announcing spect, a helping library for Test::Unit

Clinton R. Nixon
Clinton R. Nixon, Former Staffer, May 22, 2008

We've tried out a lot of test frameworks for Ruby here at the Lab. I have been a big fan of test/spec in the past, and like shoulda's nested contexts. test/spec's wrapped assertions - like user.should.not.be.nil instead of assert_not_nil user - are a particular feature I dig.

I wanted to use these with shoulda, and started to extend shoulda to have its own set of wrapped assertions, but when I looked in the test/spec code, I saw this beauty:

module Test::Spec
  class Should
    ...
    def add_assertion
      $TEST_SPEC_TESTCASE && $TEST_SPEC_TESTCASE.__send__(:add_assertion)
    end
    ...
  end

  class TestCase
  ...
    module InstanceMethods
      def setup                 # :nodoc:
        $TEST_SPEC_TESTCASE = self
        super
        call_methods_including_parents(:setups)
      end
    ...
    end
  ...
  end
end

I don't want to put down a piece of code I've used a lot and enjoyed, and I think this is a practical way for test/spec to handle increasing the assertion count for its wrapped assertions, since they all start with a method on Object. I, however, couldn't bring myself to use a global variable to handle this task. I thought for a while, and after being inspired by JDave, I decided to start with a method on Test::Unit::TestCase.

Announcing spect

I released my test library today as a Ruby gem called spect. It's very simple to use. Once you require spect, you can use the method expect in Test::Unit::TestCase or any derived classes, like a shoulda should block or a test/spec specify block. expect sets up expectations like the following:

expect(user).responds_to admin?
expect(user).is.an.admin
expect(user.name).to.not.match /mr_jenkins/
expect(user).to.be.equal User.find(:first)
expect(user).is.not.not.not.not user
expect(ZeroDivisionError).raised_by do
  user.id / 0
end

Most of the Test::Unit assertions are wrapped, with the following mappings:

  • assert_equal is equal
  • assert_in_delta is close_to
  • assert_match is match
  • assert_nil is nil
  • assert_respond_to is respond_to
  • assert_same is same
  • assert_raise is raised_by
  • assert_throws is thrown_by

In addition, all predicates on an object can be tested for truth by calling them per normal (admin?) or by dropping the question mark (admin.)

There are several methods used solely for grammatical purposes. to, an, and a are no-ops and just return self. is and be are no-ops unless they are passed an argument, in which case they assert equality between the original object and this argument. not reverses assertions, but also can be passed an argument to assert inequality.

I am sure there are a lot of improvements to make. I thought of one writing this blog post (equal_to should be an alias for equal.) I invite you to check out the code at our GitHub repository and submit patches.

Ruby Hack Night

Matt Swasey
Matt Swasey, Former Staffer, May 21, 2008

A couple of co-workers and I have been tossing around the idea of having a Ruby Hack Night in the DC metropolitan area for the past few weeks.  We are going to start out small, and have a very informal gathering at Murky Coffee in downtown Arlington on the night of June 18th, the third Wednesday of the month.  If you would like to join us, we will be there starting at 7pm.  Murky Coffee does not take space reservations, so we don’t have a set aside meeting location.  I am going to try and get the back room which holds about ten people comfortably.  If we have a larger crowed that than, we can migrate to the upstairs.  So, we’d love to see you there if you are a Ruby hacker wanting to get together with other DC Ruby hackers and work on various projects (of your choice).

We will ask everyone there what they are going to work on, and what they would like help with if anyone is interested.  So, if you have a project, bring that, if you don’t have a project, but would like to help someone else with theirs, that would be awesome.

Hope to see you there!

Problems With Attachment_fu and a Dedicated Image Model

Ben Scofield
Ben Scofield, Former Staffer, May 19, 2008

I recently ran across a real head-scratcher with an application I’ve been working on. I’ve got a polymorphic relationship from several different models to an Image model, which is implemented with attachment_fu. Each of the polymorphic models can have one and only one image, but those images each have many thumbnails (which are handled via attachment_fu).

To start, then, my polymorphic models each include the Imageable module, which in part looks like this:

module Imageable
  def self.included(base)
    base.class_eval do
      has_one :image, :dependent => :destroy, :as => :record
      # ...
    end
  end
end

The problem that we saw was that sometimes - for some records, on some pages, but repeatably - the public_filename coming back for a given thumbnail had two size modifiers attached. For instance, what should have looked like /uploads/0000/0123/test_large.png came back /uploads/0000/0123/test_large_large.png. The database records were all correct, and all calls to public_filename directly returned the correct result. After a couple of hackish attempts to work around this, though, I realized that the underlying problem was that, when I eagerly loaded the image record, one of the thumbnails was being returned instead of the parent image.

The solution ended up being simple:

module Imageable
  def self.included(base)
    base.class_eval do
      has_one :image, :dependent => :destroy, :as => :record, :conditions => 'parent_id IS NULL'
      # ...
    end
  end
end

By adding the parent_id IS NULL condition, the thumbnails are all excluded from any call to record.image, so the only row returned will always be the parent image.

Just something to keep in mind for next time!

We're the Developers

at Viget Labs. We write about web development trends, tips, best practices, industry events, and our projects — all with an emphasis on Ruby on Rails.

Contact Us

Have any questions, comments, ideas, or secrets to share? Let us know.


How many days in a non-leap year?

Sorry, you need to have Javascript enabled to use this form. (Don't blame us, blame the spammers!) If you'd like to contact us, please visit our Contact page.