Ransack Gem
Recently, I started using the ransack gem (https://github.com/ernie/ransack) for searching in one of my apps. I’m putting this here as an example, in case I ever need to make any more changes. The basics of ransack are pretty easy to do and work decently well. First step, add a search method to your controller. It should look something like this:
def search @q = Board.search(params[:q]) @boards = @q.result @count = @boards.count end
We have @count because we’d like to be able to show the number of results on the page.
Next we have a search view. The start of the form looks like this:
<%= search_form_for @q, :url => search_boards_path, :html => {:method => :post} do |f| %> <%= render 'status_fields' %>
In my example, we have a number of different fields that we are searching. With ransack, this is easy because they have a number of different built-in predicates that can be used. So here are two example searches that would be in the form above.
<%= f.label :temp_cycling_true, 'Temperature has been cycled' %>
<%= f.check_box :temp_cycling_true %> <%= f.label :r3v3_lteq, 'R3V3 <= '%> <%= f.text_field :r3v3_lteq, :size => 5 %>
In the first line, I have a checkbox that checks whether the field temp_cycling is set to true. The second line checks that the field r3v3 is less than or equal to the value entered in the box.
I have about 15 of these searches that I use in my search field. However, there was one more search that I wanted to be able to do that was giving me problems.
Status:Line 2 is a hidden field that specifies that this grouping should be or’d together. In line 3 I start a loop that goes through each of my possible values and generates two hidden tags. Line 4 sets the hash “c”=>{“0″=>{“a”=>{“0″=>{“name”=>”status”}} and line 5 adds “p”=>”eq”. Lines 6-10 are putting the check boxes with the options out. The if is necessary for the times when you haven’t yet submitted a search. In these cases, none of the boxes will be checked. After you have submitted a search, if a box was used previously, it should be set so you can see what you were searching for. So this bit, adds “v”=>{“0″=>{“value”=>”good”}}} to the query, if the good box was checked. For a final example, here is what my query looks like when I search for just the good or repaired boards.
<%= hidden_field_tag 'q[g][0][m]', 'or'%> <% ['good', 'repaired', 'untested', 'broken'].each_with_index do |s, i| %> <%= hidden_field_tag "q[g][0][c][#{i}][a][0][name]", 'status'%> <%= hidden_field_tag "q[g][0][c][#{i}][p]", 'eq' %> <% if params[:q].nil? %> <%= check_box_tag "q[g][0][c][#{i}][v][0][value]", "#{s}" %> <% else %> <%= check_box_tag "q[g][0][c][#{i}][v][0][value]", "#{s}", params[:q][:g][:"0"][:c][:"#{i}"][:v] ? true : false %> <% end %> <%= label_tag "q[g][0][c][#{i}][v][0][value]", "#{s}" %>
<% end %>
Parameters: {"utf8"=>"?", "authenticity_token"=>"66vdGQSBRD0Pr+8xbIlXim9mkkWkzMUukAvy20QEbFY=", "q"=>{"g"=>{"0"=>{"m"=>"or", "c"=>{ "0"=>{"a"=>{"0"=>{"name"=>"status"}}, "p"=>"eq", "v"=>{"0"=>{"value"=>"good"}}}, "1"=>{"a"=>{"0"=>{"name"=>"status"}}, "p"=>"eq", "v"=>{"0"=>{"value"=>"repaired"}}}, "2"=>{"a"=>{"0"=>{"name"=>"status"}}, "p"=>"eq"}, "3"=>{"a"=>{"0"=>{"name"=>"status"}}, "p"=>"eq"}}}}, "board_number_eq"=>"", "temp_cycling_true"=>"0", "temp_cycling_false"=>"0", "dc_current_without_firmware_gteq"=>"", "dc_current_without_firmware_lteq"=>"", "r3v3_gteq"=>"", "r3v3_lteq"=>"", "r2v5_gteq"=>"", "r2v5_lteq"=>"", "r1v2_gteq"=>"", "r1v2_lteq"=>"", "dc_current_with_firmware_gteq"=>"", "dc_current_with_firmware_lteq"=>"", "rod_slot_eq"=>"", "rod_crate_cont"=>"", "ftk_fiber_eq"=>"", "ftk_fiber_destination_cont"=>"", "daq_fiber_eq"=>"", "daq_fiber_destination_cont"=>"", "note_cont"=>""}, "commit"=>"Search"}I am 100% sure that there’s a better way to do this, but a better rails programmer will have to tell me what it is. I tried creating my own predicate. But whenever I used a checkbox, I could only get values = 1. I couldn’t find a way to make the value = ‘good’ or ‘repaired’.
I found the correct way to do this and I’ve written a blog post about it here.