Skip to content

Instantly share code, notes, and snippets.

@scarfacedeb
Created March 12, 2014 10:01
Show Gist options
  • Save scarfacedeb/9504029 to your computer and use it in GitHub Desktop.
Save scarfacedeb/9504029 to your computer and use it in GitHub Desktop.
Polymorphic association for simple_form: 2 implementations
class AlbumFormBuilder < SimpleForm::FormBuilder
def polymorphic(association, options={})
options = options.dup
raise ArgumentError, "Association cannot be used in forms not associated with an object" unless @object
raise ArgumentError, "You need to pass types for select" unless options[:types]
reflection = find_association_reflection(association)
raise "Association #{association.inspect} not found" unless reflection
@association = association
# Convert symbols into constants
types = options.delete(:types).map {|type| type.to_s.classify.constantize }
types_options = {
as: :select,
collection: types.map {|klass| [klass.model_name.human, klass.to_s] },
input_html: { class: "js-polymorphic-type" }
}
# Get records for select tags
@attribute = (reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
collections ||= options.fetch(:collections) {
types.reduce({}) do |h, klass|
h[klass.to_s] = collection(klass)
h
end
}
collection_options = {
class: "select form-control",
include_blank: true
}
# Render tags
out = ""
out << input("#{association}_type", types_options)
out << template.content_tag(:div, class: "form-group form-polymorphic-select js-polymorphic-select") do
collections.map do |type, collection|
template.select_tag("#{type.downcase}_id", select_options(collection, type), collection_options.dup)
end.join.html_safe
end
out.html_safe
end
private
def collection(klass)
klass.pluck(:title, :id)
end
def select_options(collection, type)
template.options_for_select collection, selected?(type)
end
def selected?(type)
object.send("#{@association}_type") == type && object.send(@attribute)
end
end
class PolymorphicInput < SimpleForm::Inputs::Base
def input
raise ArgumentError, "You need to pass types for select" unless input_options[:types]
# Convert symbols into constants
types = input_options.delete(:types).map {|type| type.to_s.classify.constantize }
types_options = {
as: :select,
collection: types.map {|klass| [klass.model_name.human, klass.to_s] },
class: "js-polymorphic-type"
}
# Get records for select tags
collections ||= options.fetch(:collections) {
types.reduce({}) do |h, klass|
h[klass.to_s] = collection(klass)
h
end
}
collection_options = {
class: "select form-control",
include_blank: true
}
# Render tags
out = ""
out << @builder.input_field("#{attribute_name}_type", types_options)
out << template.content_tag(:div, class: "form-group form-polymorphic-select js-polymorphic-select") do
collections.map do |type, collection|
template.select_tag("#{type.downcase}_id", select_options(collection, type), collection_options.dup)
end.join.html_safe
end
out.html_safe
end
private
def collection(klass)
klass.pluck(:title, :id)
end
def select_options(collection, type)
template.options_for_select collection, selected?(type)
end
def selected?(type)
object.send("#{attribute_name}_type") == type && object.send("#{attribute_name}_id")
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment