Monday, September 29, 2008

Actionview error when naming partials " _next.html.erb"

So, today I got this wonderfully cryptic error from the erb compiler:
Showing widget/profile_completeness/_next.html.erb where line #52 raised:

compile error
/[path_removed]/_next.html.erb:-3: syntax error, unexpected '=', expecting kEND
next = local_assigns[:next]
     ^
After debugging into action_view, I realized it was because I foolishly named a partial "_next.html.erb". "next" is apparently some pseudo-reserved word in action_view and if you use it, your template crashes in weird and interesting ways. I particularly like the negative line number in the error message which results from rails pre-pending code to the erb block when compiling the method used for rendering. Bug filed here. Update: Apparently, you'll run into this if you name your partial any reserved word in ruby (case, class, etc...)

Tuesday, September 23, 2008

Rails hack: Adding html comments to label partials

Ever want to figure out which partial a block of html is coming from without greping through your entire rails project? Render_partial_comments is a rails plugin that renders html comments to mark the begin and end of partial output. Give it a try:
ruby script/plugin install git://github.com/avvo/render_partial_comments.git
Then enable it by adding this global in config/environments/development.rb (or wherever)
$render_partial_comments = true
Then restart your server... Your rendered html will now include html comments wrapping the output from partials. Should look something like this:
<!-- render_begin 'user/view' -->
....
<!-- render_end 'user/view' -->
Update: This may have some adverse effects on autocomplete if you use it

Friday, September 19, 2008

belongs_to :dependent => :destroy with foreign key constraints

Moved here

I discovered what I believe to be a bug in activerecord (2.1.0) today. If you have something like the following:
class Person < ActiveRecord::Base
  belongs_to :person_address, :dependent => :destroy
end

class PersonAddress < ActiveRecord::Base
  has_one :person
end

...and you have for foreign key constraint on person_address_id on the person table to id on the person_address table (which would be a reasonable thing to do) you will get a foreign key constraint error when you try to destroy a person record (if the associated person_address record exists). I submitted a patch to rails for this, but in the meantime, I found this as a work around:
class Person < ActiveRecord::Base
  belongs_to :person_address # note: no :dependent => :destroy

  def destroy
    super() # first destroy ourselves before we destroy the association
    PersonAddress.destroy(self.person_address_id) if self.person_address_id
  end
end

Friday, September 12, 2008

Private Mixins - Including helpers in controllers

Despite being fully aware that controllers should include minimal logic, we still have some code that needs to be shared by multiple controllers (and even some views). The easiest way to share code in ruby is, of course, the mixin module. The trouble in rails is that any public methods on controllers are exposed as actions, even ones that come in via mixin. That's trouble. One solution was to define all methods in our helpers as private. Didn't really work in every scenario and kind of limits testing. What we really wanted was to be able to include modules privately. As far as I can tell, ruby doesn't support this off the shelf, so we decided we give it a shot to see where it took us.

class Module
 
  [:private, :protected].each do |type|
    eval %{
      def include_#{type}(*prms)
        prms.each do |mod|
          include(mod)
          mod.instance_methods.each do |meth|
            #{type}(meth)
          end
        end
      end
    }
  end
 
end

This creates two methods include_private and include_protected. These guys wrap the normal include method but then take all the instance methods for the included module and privatize or protected-ize them, respectively. Then we added this to ApplicationController:

class ApplicationController < ActionController::Base

  class << self
    alias_method :include_helper, :include_private
  end
 
end

Now we can include helpers in our controllers privately:

class MyController < ApplicationController
  include_helper MyHelper
end

Now, it seems to me that there must be a better way of doing this and we would love to see it because our googling came up empty and it seems odd that we had to resort to this, though it seems to fit our needs just fine.

Monday, September 8, 2008

DelSolr - Solr Facets Made Easy in Ruby

delsolr - lightweight solr wrapper for ruby Here's an example of solr faceting made simple via delsolr:
c = DelSolr::Client.new(:server => 'solr1', :port => 8983)

# faceting by field...
rsp = c.query('dismax', :query => 'mp3 player',
                        :facets => [{:field => 'brand', :limit => 5, :mincount => 1}]

# now let's output the facet values and counts
rsp.facet_field_values('brand').each do |brand|
  puts "#{brand} - #{rsp.facet_field_count('brand', brand)}"
end

# faceting by query... with filtering...
rsp = c.query('dismax', :query => 'mp3 player',
                        :filters => {:onsale => true},
                        :facets => [{:query => "instock:true", :name => 'instock'},
                                    {:query => {:price => (0..50), :description => 'cool'}, :name => 'cool_and_cheap'}]) 

# now let's output some query facets
puts "Instock: #{rsp.facet_query_count_by_name('instock')}"
puts "Sweet: #{rsp.facet_query_count_by_name('cool_and_cheap')}"

We've been using this code internally for a bit, so we'd figure we'd open it up as a gem to see if other people find it useful. There are a number of other gems/plugins that do something similar, but we weren't satisfied with the balance of power and simplicity in any of them, particularly when it comes to faceting. The full documentation is available on the delsolr page

Wednesday, September 3, 2008

Method of the Day: Array#in_groups_of

This one is great for building rows of tables.
<table>
<% @models.in_groups_of(3).each do |row| %>
  <tr>
    <% row.each do |model| %>
      <td><%= model.to_s %></td>
    <% end %>
  </tr>
<% end %>
</table>

This will render the array in a left-to-right, top-down order. If you want to render in top-down, left-to-right order, try this:
  <% num_cols = 3 %>
  <% @models.in_groups_of((@models.length/num_cols).ceil).transpose.each do |row| %>
  ...
  
That should give you the (more common) column-first layout.

Method of the Day: Array#to_sentence

An Oldy but a goody...
  <% a = ['hot dogs', 'hamburgers', 'pizza'] %>
  I like <%= a.to_sentence(:connector => 'and') %>.
  
renders...
  I like hot dogs, hamburgers, and pizza.