Search code examples
ruby-on-railsrubyruby-on-rails-5ransack

Boolean value not saving in display


I have a Class Company with a boolean attribute of female_founder. The company either has a female_founder (true) or not (false).

I have created a search filter using the ransack gem to query whether a company is female-founded or not. When I query 'true' searches, the enum value saves and displays fine, but when I search for the 'false' value, the display resets to the prompt value I have given it.

Why is this? And how can I fix it? I want the display to show whichever value I have selected.

Schema

create_table "companies", force: :cascade do |t|
 ...
 t.boolean  "female_founder"
 ...
end 

Company.rb

class Company < ApplicationRecord
 enum female_founders: {
  'Yes' => 1,
  'No' => 0
 }
end

Search.html.erb

<%= f.label :female_founder_eq, 'Female Founder' %>
<%= f.select :female_founder_eq, Company.female_founders.to_a, {prompt: "Any"} %>

Search 'Yes' for female_founder returns correct display

Search 'Yes' for female_founder

Search 'No' for female_founder resets search display to prompt value. The search queries correctly, it is just the display that is incorrect. Search 'No' for female_founder


Solution

  • Here's where Rails determines whether to add the prompt, in rails/actionview/lib/action_view/helpers/tags/base.rb:

          def add_options(option_tags, options, value = nil)
            if options[:include_blank]
              option_tags = tag_builder.content_tag_string("option", options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, value: "") + "\n" + option_tags
            end
            if value.blank? && options[:prompt]
              option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), value: "") + "\n" + option_tags
            end
            option_tags
          end
    

    When Rails is building its select tag, it's determining whether to include a prompt by using blank?, which can't distinguish whether it's nil or false; because @company.female_founder is false, and false is blank?, it will assume no value was given and add the prompt. (This may or may not be intentional on their part.)

    Now, the value here is the tag's selected value, which can be overriden if the :selected option is defined, so if :selected is manually set to your No value of 0, then it should suppress the prompt as expected (because 0 is not blank?). Here's a kludge that appears to work for me for a regular form:

    # 1 if true - show 'Yes', 0 if false - show 'No', nil if nil - show 'Any'
    <%= f.select :female_founder, Company.female_founders.to_a, { prompt: "Any", selected: { true => 1, false => 0 }[@company.female_founder] } %>
    

    For ransack it'll need to look something like this:

    <%= f.select :female_founder_eq, Company.female_founders.to_a, {prompt: "Any", selected: { true => 1, false => 0 }[@q.base[:female_founder_eq].try(:value)] } %>