Skip to content

Instantly share code, notes, and snippets.

@pedroteixeira
Created August 10, 2012 18:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pedroteixeira/3316481 to your computer and use it in GitHub Desktop.
Save pedroteixeira/3316481 to your computer and use it in GitHub Desktop.
Dynamic class for Formtastic
class Form
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
# Subclass may provide a types hash. Any attributes not listed will
# default to string.
# self.types = {
# :description => :text,
# :state => {:type => :string, :limit => 2}
# :country => :string,
# :newsletter_opt_in => :boolean,
# }
class << self
attr_accessor :types
def add_accessors(obj, *attrs)
attrs.each do |attr|
obj.instance_eval "class << self; attr_accessor \"#{attr.to_s}\"; end;"
end
end
def split_date_time(obj, attr)
attr = attr.to_s
date_attr = attr + '_date'
time_attr = attr + '_time'
add_accessors obj, date_attr, time_attr
value = obj.send(attr)
if value.respond_to?(:strftime)
obj.send date_attr +'=', value.strftime('%d/%m/%Y')
obj.send time_attr +'=', value.strftime('%H:%M')
end
end
def build(*attrs)
form = Form.new
values = nil
if attrs.length == 1 && attrs.first.is_a?(Hash)
values = attrs.first
attrs = attrs.first.keys
elsif attrs.last.is_a?(Hash) || attrs.last.nil?
values = attrs.pop
end
form.instance_eval {@attrs = attrs}
attrs.each do |attr|
form.instance_eval "class << self; attr_accessor \"#{attr.to_s}\"; end;"
#form.class_eval "attr_accessor :#{attr}" #TODO: class_eval?
end
form.instance_eval{initialize(values)} if values
form
end
end
self.types = {}
# So the controller can say "@contact = Contact.new(params[:contact])"
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", clean(value))
end
end
# quack like a hash
def [](attr)
send(attr.to_s)
end
def []=(attr, value)
send("#{attr.to_s}=", clean(value))
end
def clean(value)
if value.is_a?(Array)
value.reject{|v| v.blank?}
else
value
end
end
def attributes
a = {}
@attrs.each {|k| a[k] = self[k] }
a
end
def attributes=(values)
initialize(values) if values.any?
end
# So form_for works correctly -- we only do "new" forms
def persisted? ; false ; end
# To provide the type information
def column_for_attribute(attr)
FauxColumnInfo.new(self.class.types[attr])
end
class FauxColumnInfo
attr_accessor :type, :limit
def initialize(type_info)
type_info ||= :string
case
when type_info.instance_of?(Hash), type_info.instance_of?(OpenStruct)
self.type = type_info[:type].to_sym
self.limit = type_info[:limit]
else
self.type = type_info.to_sym
self.limit = nil
end
end
end
end
@pedroteixeira
Copy link
Author

USAGE:

@Form = Form.build :name, :age

@pedroteixeira
Copy link
Author

@Form = Form.build :name, :age, params[:form]

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