Tuesday, August 11, 2009

acts_as_paranoid gotcha

When eager-loading paranoid associations, beware that acts_as_paranoid's default behavior is to load ALL records, deleted or not. For example,

class User < ActiveRecord::Base
  has_many :blogs
end

class Blog < ActiveRecord::Base
  has_many :posts
end

class Post < ActiveRecord::Base
  acts_as_paranoid
end

# this will include deleted posts too!
@user.blogs.all(:include => :posts)

To only load associations that are not deleted, specify in your association conditions:

class Blog < ActiveRecord::Base
  has_many :posts, :conditions => "deleted_at IS NULL"
end

Thursday, July 30, 2009

Multiple versions of ruby can cause confusing errors

Probably a big "well, duh" moment for those more *nix experienced than me, but perhaps it will help some anyway.

So we upgraded ruby and rubygems on a machine last night, and after doing so found that a Rails script called by cron could no longer find the mysql gem. No problem I thought, I'll just reinstall it... but then "gem install mysql" reports an error:

/usr/local/bin/ruby extconf.rb install mysql
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lm... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lz... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lsocket... no
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lnsl... no
checking for mysql_query() in -lmysqlclient... no
*** extconf.rb failed ***

Poo. Google quickly solves this issue:

gem install mysql -- --with-mysql-config=/usr/bin/mysql_config

mysql gem installs without a hitch, happy again! So let's run the script: KABOOM.

custom_require.rb:36:in `gem_original_require': no such file to load -- mysql (MissingSourceFile)

WTF? I just installed it?! Let's see, "gem list" shows it, irb can require it, ./script/console can retrieve records... what's going on?

Check the cron command again: hmm, we're calling /usr/bin/ruby.

ruby -v
reports 1.8.7, but
/usr/bin/ruby -v
reports 1.8.5.
which ruby
tells us /usr/local/bin/ruby. Let's try this:
mv /usr/bin/ruby /usr/bin/ruby-1.8.5
ln -s /usr/local/bin/ruby /usr/bin/ruby

Cross fingers, try our cron script again, this time it works!

Tuesday, July 28, 2009

Google Voice Comedy Gold!

Well, maybe comedy tin, or perhaps nickel. I've gotten a Google Voice account, and I picked a number which I thought would be "cool"... a number that is a pretty common progression on the number pad. It turns out this number is frequently dialed by... toddlers discovering the telephone! So now I'm growing a library of voicemails from three-year-olds saying "Hello? Hello? Daddy?" and the like. Took me about two calls to realize this and subsequently turn off the call forwarding to my real phone. I'll probably just keep the account for its novelty value for the time being.

Wednesday, April 22, 2009

Form hints

So I was perusing my delicious history the other day, and happened along this post: http://woork.blogspot.com/2008/04/improve-form-usability-with-auto.html I was bored, so I thought I'd hack together my own version, especially given that someone else has laid out the blueprints already! Wheel++ I kept in mind some of the comments from the original post, namely keeping the javascript unobtrusive. Here's a demo for download. On with the details: First we'll mark up a simple form:
<form action="" method="get" accept-charset="utf-8">
 
  <p>
    <div class='label-wrapper'><label for='email'>Email: </label></div>
    <div class='element-wrapper'>
        <input type='text' name='email' id='email' />
        <div class='form-hint' id='email-form-hint'>
          Enter your email address
        </div>
      </div>
  </p>
 
  <p>
    <div class='label-wrapper'><label for='foo'>Foo: </label></div>
    <div class='element-wrapper'>
        <input type='text' name='foo' id='foo' />
        <div class='form-hint' id='foo-form-hint'>
          Enter something here
        </div>
      </div>
  </p>
 
  <p>
    <div class='label-wrapper'><label for='bar'>Bar: </label></div>
    <div class='element-wrapper'>
        <input type='text' name='bar' id='bar' />
        <div class='form-hint' id='bar-form-hint'>
          Enter something here
        </div>
      </div>
  </p>
 
  <p><input type="submit" value="Continue →"></p>
 
</form>
Here we let you keep it simple, merely adding a div of class "form-hint" to add a hint to a field. Note also that the hint's ID must match the form field's ID, in the form of "[ID]-form-hint" Next we'll add some style:
<style type="text/css" media="screen">
  
  .form-hint-wrapper {
    background:#fff;
    padding:0;
    position:absolute;
    z-index:999;
  }
        
  .form-hint {
    background:#def;
    clear:both;
    padding:5px 10px;
    display:none;
  }
  
  /* thanks to http://www.csskarma.com/lab/css_arrows/ */
  .arrow-up {
    background:transparent none repeat scroll 0 0;
    left:10px;
    top:0;
    width:20px;
  }
  .arrow-up-1 {
    border-right:10px solid #def;
    border-top:10px solid #fff;
    float:left;
  }
  .arrow-up-2 {
    border-left:10px solid #def;
    border-top:10px solid #fff;
    float:left;
  }
  .arrow-up-1, .arrow-up-2 {
    height:0;
    overflow:hidden;
    width:0;
  }

</style>
Finally finish off with the javascript guts:
<script type="text/javascript" charset="utf-8" src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.3/prototype.js">    </script>
<script type="text/javascript" charset="utf-8" src="http://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.2/scriptaculous.js?load=effects,builder">    </script>

<script type="text/javascript" charset="utf-8">
  Event.observe(window, 'load', init_hints);
  
  function init_hints() {
    $$('.form-hint').each(function(hint){
      
      var hint_parent = hint.parentNode;
      hint_parent.makePositioned();
      
      var arrow_wrapper = Builder.node('div', {'class': 'arrow-up'},
        [
          Builder.node('div', {'class': 'arrow-up-1'}),
          Builder.node('div', {'class': 'arrow-up-2'})
        ]
      );
      
      hint_clone = hint.cloneNode(true);
      Element.setStyle(hint_clone, {'display': 'block'})
      
      var hint_wrapper = Builder.node('div', {'class': 'form-hint-wrapper', 'style': 'display:none'},
        [arrow_wrapper, hint_clone]
      );

      var el = $(hint.id.replace('-form-hint', ''));
      Event.observe(el, 'focus', element_focused);
      Event.observe(el, 'blur', element_blurred);

      hint.replace(hint_wrapper);
      
    });
  }
  
  function element_focused(el) {
    var wrapper = $(el.target.id + '-form-hint').parentNode;
    new Effect.Appear(wrapper, {'duration': 0.25})
  }
  
  function element_blurred(el) {
    var wrapper = $(el.target.id + '-form-hint').parentNode;
    new Effect.Fade(wrapper, {'duration': 0.25})
  }
  
</script>
The script will find all elements on the page of class "form-hint", then wrap them in another div that contains an arrow pointing to the form element, using only CSS. That's about it - feel free to hack the CSS to change the hint colors, etc... Download the demo.