Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve docs on Non Active Record #1839

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

abinoam
Copy link

@abinoam abinoam commented Apr 15, 2024

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 activerecord-hackery/ransack#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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant