Simple_form: SimpleForm appears to pull country_select gem into app unrequested

Created on 20 Apr 2016  路  2Comments  路  Source: heartcombo/simple_form

My app includes a State model that has a country field. Using SimpleForm to build the new and edit forms causes ActionView::Template::Error: undefined method `country_select'.

My Gemfile does not include the country_select gem. It appears that SimpleForm is including that gem into my project or at least behaving as though that gem is expected. I do not get this error when standard Rails form helpers are used.

Am not sure why country_select is included in any way in a gem that really has nothing to do with that specific use case. However, accepting that it is appropriate within SimpleForm, shouldn't it be possible for the end user of SimpleForm to optionally exclude the country_select gem and any built in support? Or at the very least shouldn't there be a warning in the README? I have not had the opportunity to poke around on the SimpleForm code to understand how country_select enters the logic, so cannot offer any suggestions on how to make it's use optional. However, if a warning in the README is more appropriate, I would be happy to generate a pull request.

I think my work around will be to use "pais" for my country field. Have not tried this but a quick read of the country_select gem code does not show any use of that term for "country".

From schema.rb

  create_table "states", force: :cascade do |t|
    t.string   "name"
    t.string   "abbreviation"
    t.string   "country"
    #...
    t.datetime "created_at",              null: false
    t.datetime "updated_at",              null: false
  end

The actions new and edit render _form:

<%= simple_form_for(@state) do |f| %>
  <%= render partial: 'shared/error_messages', locals: { model: @state } %>

  <div class="form-inputs">
    <%= f.input :name %>
    <%= f.input :abbreviation %>
    <%= f.input :country %>     # <== line 13 in the real form
    <% #... %>
  </div>

  <div class="form-actions">
    <%= f.button yield(:button_text) %>
  </div>
<% end %>

Testing or following the new/edit path for a state yields this error:

StatesControllerTest#test_should_get_edit:
ActionView::Template::Error: undefined method `country_select' for #<SimpleForm::FormBuilder:0x007f28a9a81cf8>
    app/views/states/_form.html.erb:13:in `block in _app_views_states__form_html_erb__1257389201885898929_69906310081500'
    app/views/states/_form.html.erb:1:in `_app_views_states__form_html_erb__1257389201885898929_69906310081500'
    app/views/states/edit.html.erb:7:in `_app_views_states_edit_html_erb__1169936360611348705_69906310063980'
    test/controllers/states_controller_test.rb:34:in `block in <class:StatesControllerTest>'

The test generated with rails g scaffold State ... :

require 'test_helper'

class StatesControllerTest < ActionController::TestCase
  setup do
    @state = states(:tx)
    sign_in users(:testadmin)
  end

  test "should get new" do
    get :new
    assert_response :success
  end

  test "should get edit" do
    get :edit, id: @state
    assert_response :success
  end
end

states.yml

tx:
  name: Texas
  abbreviation: TX
  country: USA

states_controller.rb:

class StatesController < ApplicationController
  before_action :set_state, only: [:show, :edit, :update, :destroy]

  def new
    @state = State.new
  end

  def edit
  end

  #...

  private
    def set_state
      @state = State.find(params[:id])
    end

    def state_params
      params.require(:state).permit(:name, :abbreviation, :country)
    end
end

state.rb

class State < ActiveRecord::Base
  has_many :addresses, inverse_of: :state

  before_destroy :detach_addresses

  scope :null,     -> { where(name: 'None') }
  scope :not_null, -> { where.not(name: 'None') }

  def self.null_id
    @null_id ||= self.null.first.id
  end

private
  def detach_addresses
    null_state_id = self.class.null_id
    addresses.update_all(state_id: null_state_id)
  end
end

Gemfile

source 'https://rubygems.org'

gem 'rails', '4.2.6'
gem 'pg', '~> 0.15'

gem 'bootstrap-sass'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.1.0'

gem 'jquery-rails'
gem 'turbolinks'
gem 'jbuilder', '~> 2.0'
gem 'therubyracer', :platform=>:ruby

gem 'simple_form'
gem 'ransack'
gem 'kaminari'

# Authorization & Authentication
gem 'devise'
gem 'pundit'
gem 'bcrypt', '~> 3.1.7'

# Static asset service
gem 'high_voltage'

group :development, :test do
  gem 'pry'
  gem 'pry-byebug'
end

group :development do
  gem 'web-console', '~> 2.0'
  gem 'spring'
  gem 'better_errors'
  gem 'quiet_assets'
  gem 'rails_layout'
end

group :test do
  gem 'capybara'
  gem 'minitest-reporters'
  gem 'mini_backtrace'
  gem 'guard'
  gem 'guard-minitest'
  gem 'test_after_commit'
end

group :production do
  gem 'thin'
end

Most helpful comment

The README actually _does_ have a section on country_select here.

Without setting up a full test to reproduce, I would assert you are hitting this due to using a very implicit input call in your view (just giving a field and essentially asking simple_form to sniff out the type, etc.)

You can try two things:
1) As the REAME states, you can disable the country input type being used by changing it to a basic string type.
(in your simple_form initializer)
config.input_mappings = { /country/ => :string }
2) you can explicitly set the type
<%= f.input :country, as: :text %>

All 2 comments

The README actually _does_ have a section on country_select here.

Without setting up a full test to reproduce, I would assert you are hitting this due to using a very implicit input call in your view (just giving a field and essentially asking simple_form to sniff out the type, etc.)

You can try two things:
1) As the REAME states, you can disable the country input type being used by changing it to a basic string type.
(in your simple_form initializer)
config.input_mappings = { /country/ => :string }
2) you can explicitly set the type
<%= f.input :country, as: :text %>

Thank you for the issue. @colinross' answer is correct. It doesn't require you to use country_select but if you have a field with the name :country it will try to use. If you don't want that behavior just do what the README say.

Was this page helpful?
0 / 5 - 0 ratings