SimpleForm relies on has_attribute? and type_for_attribute to infer attribute type so it may generate the correct input type according to its mapping.
I am currently trying to improve Ransack / SimpleForm integration at PR https://github.com/activerecord-hackery/ransack/pull/1487
It was a little hard to me to guess how this was done internally.
I wish I've had seen issue #1666 before.
This PR aims to improve the documentation regarding the use of SimpleForm with non-ActiveRecord objects, making it easier for others to implement and understand.
To ensure the documentation's correctness I am also providing a sample test of the example code.
# frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
gem "rails"
# If you want to test against edge Rails replace the previous line with this:
# gem "rails", github: "rails/rails", branch: "main"
gem "sqlite3"
gem "simple_form"
gem "byebug"
end
require "active_record"
require "action_view"
require "minitest/autorun"
require "logger"
require "byebug"
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :people, force: true do |t|
t.string :name
t.integer :age
t.boolean :registered
t.timestamps
end
end
class Person < ActiveRecord::Base
end
class NonArPerson < Struct.new(:id, :name, :age, :registered)
def to_model
self
end
def model_name
OpenStruct.new(param_key: "person")
end
def to_key
id
end
def persisted?
id.present?
end
def has_attribute?(attr_name)
%w(id name age registered).include?(attr_name.to_s)
end
def type_for_attribute(attr_name)
case attr_name.to_s
when "id" then OpenStruct.new(type: :integer)
when "name" then OpenStruct.new(type: :string)
when "age" then OpenStruct.new(type: :integer)
when "registered" then OpenStruct.new(type: :boolean)
end
end
end
class BugTest < ActionView::TestCase
def test_ar_on_simple_form
@person = Person.create!(name: "John", age: 30)
render inline: <<~ERB, locals: { person: @person }
<%= simple_form_for person, url: "/people" do |f| %>
<%= f.input :name %>
<%= f.input :age %>
<%= f.input :registered %>
<% end %>
ERB
# Each one with the correct type
assert_not_empty css_select(".string #person_name")
assert_not_empty css_select(".integer #person_age")
assert_not_empty css_select(".boolean #person_registered")
# Age and registered are not strings
assert_empty css_select(".string #person_age")
assert_empty css_select(".string #person_registered")
# Name and registered are not integers
assert_empty css_select(".integer #person_name")
assert_empty css_select(".integer #person_registered")
# Name and age are not booleans
assert_empty css_select(".boolean #person_name")
assert_empty css_select(".boolean #person_age")
end
def test_non_ar_on_simple_form
@person = NonArPerson.new
render inline: <<~ERB, locals: { person: @person }
<%= simple_form_for person, url: "/people" do |f| %>
<%= f.input :name %>
<%= f.input :age %>
<%= f.input :registered %>
<% end %>
ERB
# Each one with the correct type
assert_not_empty css_select(".string #person_name")
assert_not_empty css_select(".integer #person_age")
assert_not_empty css_select(".boolean #person_registered")
# Age and registered are not strings
assert_empty css_select(".string #person_age")
assert_empty css_select(".string #person_registered")
# Name and registered are not integers
assert_empty css_select(".integer #person_name")
assert_empty css_select(".integer #person_registered")
# Name and age are not booleans
assert_empty css_select(".boolean #person_name")
assert_empty css_select(".boolean #person_age")
end
end
SimpleForm relies on
has_attribute?
andtype_for_attribute
to infer attribute type so it may generate the correct input type according to its mapping.I am currently trying to improve Ransack / SimpleForm integration at PR https://github.com/activerecord-hackery/ransack/pull/1487 It was a little hard to me to guess how this was done internally. I wish I've had seen issue #1666 before.
This PR aims to improve the documentation regarding the use of SimpleForm with non-ActiveRecord objects, making it easier for others to implement and understand.
To ensure the documentation's correctness I am also providing a sample test of the example code.