# encoding: utf-8
module PopulateMe
module Mongo
module Crushyform
# This module adds to mutated models the ability
# to build forms using the settings of the schema
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def crushyform_types
@crushyform_types ||= {
:none => proc{''},
:string => proc do |m,c,o|
if o[:autocompleted]
values = o[:autocomplete_options] || m.class.collection.distinct(c)
js = <<-EOJS
EOJS
end
tag = "%s\n" % [o[:input_type]||'text', o[:input_name], o[:input_value], m.field_id_for(c), o[:input_class], o[:required]&&'required', o[:required]]
"#{tag}#{js}"
end,
:slug => proc do |m,c,o|
crushyform_types[:string].call(m,c,o)
end,
:price => proc do |m,c,o|
crushid = m.field_id_for(c)
price_main, price_cents = o[:input_value].to_i.divmod(100)
out = " • "
out << ""
out % [o[:input_name], crushid, o[:input_class], price_main, o[:input_name], crushid, o[:input_class], "%02d" % price_cents]
end,
:boolean => proc do |m,c,o|
crushid = m.field_id_for(c)
checked = 'checked' if o[:input_value]
out = "\n"
out += "\n"
out % [o[:input_name], crushid, o[:input_name], crushid, o[:input_class], checked]
end,
:text => proc do |m,c,o|
"%s\n" % [o[:input_name], m.field_id_for(c), o[:input_class], o[:required]&&'required', o[:input_value], o[:required]]
end,
:date => proc do |m,c,o|
o[:input_value] = o[:input_value].strftime("%Y-%m-%d") if o[:input_value].respond_to?(:strftime)
o[:required] = "%s Format: yyyy-mm-dd" % [o[:required]]
crushyform_types[:string].call(m,c,o)
end,
:time => proc do |m,c,o|
o[:input_value] = o[:input_value].strftime("%T") if o[:input_value].respond_to?(:strftime)
o[:required] = "%s Format: hh:mm:ss" % [o[:required]]
crushyform_types[:string].call(m,c,o)
end,
:datetime => proc do |m,c,o|
o[:input_value] = o[:input_value].strftime("%Y-%m-%d %T") if o[:input_value].respond_to?(:strftime)
o[:required] = "%s Format: yyyy-mm-dd hh:mm:ss" % [o[:required]]
crushyform_types[:string].call(m,c,o)
end,
:parent => proc do |m,c,o|
parent_class = o[:parent_class].nil? ? Kernel.const_get(c.sub(/^id_/, '')) : m.resolve_class(o[:parent_class])
option_list = parent_class.to_dropdown(o[:input_value])
"\n" % [o[:input_name], m.field_id_for(c), o[:input_class], option_list]
end,
:children => proc do |m,c,o|
children_class = o[:children_class].nil? ? Kernel.const_get(c.sub(/^ids_/, '')) : m.resolve_class(o[:children_class])
opts = o.update({
:multiple=>true,
:select_options=>children_class.dropdown_cache
})
@crushyform_types[:select].call(m,c,opts)
end,
:attachment => proc do |m,c,o|
deleter = " Delete this file
" unless m.doc[c].nil?
"%s%s%s\n" % [m.to_thumb(c), deleter, o[:input_name], m.field_id_for(c), o[:input_class], o[:required]]
end,
:select => proc do |m,c,o|
out = "%s\n" % [o[:required]]
end,
:string_list => proc do |m,c,o|
if o[:autocompleted]
values = o[:autocomplete_options] || m.class.collection.distinct(c)
js = <<-EOJS
EOJS
o[:autocompleted] = false # reset so that it does not autocomplete for :string type below
end
tag = @crushyform_types[:string].call(m,c,o.update({:input_value=>(o[:input_value]||[]).join(',')}))
"#{tag}#{js}"
end,
:permalink => proc do |instance, column_name, options|
values = "\n"
tag = @crushyform_types[:string].call(instance, column_name, options)
return tag if options[:permalink_classes].nil?
options[:permalink_classes].each do |sym|
c = Kernel.const_get sym
entries = c.find
unless entries.count==0
values << "\n"
end
end
"#{tag}
\n\n"
end
}
end
# What represents a required field
# Can be overriden
def crushyfield_required; " *"; end
# Stolen from ERB
def html_escape(s)
s.to_s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/, "<")
end
# Cache dropdown options for children classes to use
# Meant to be reseted each time an entry is created, updated or destroyed
# So it is only rebuild once required after the list has changed
# Maintaining an array and not rebuilding it all might be faster
# But it will not happen much so that it is fairly acceptable
def to_dropdown(selection=nil, nil_name='** UNDEFINED **')
dropdown_cache.inject("\n") do |out, row|
selected = 'selected' if row[1]==selection
"%s%s%s%s" % [out, row[2], selected, row[3]]
end
end
def dropdown_cache
@dropdown_cache ||= self.find({},:fields=>['_id',label_column]).inject([]) do |out,row|
out.push([row.to_label, row.id.to_s, "\n"])
end
end
def reset_dropdown_cache; @dropdown_cache = nil; end
end
# Instance Methods
def crushyform(columns=model.schema.keys, action=nil, meth='POST')
columns.delete('_id')
fields = columns.inject(""){|out,c|out+crushyfield(c)}
enctype = fields.match(/type='file'/) ? "enctype='multipart/form-data'" : ''
action.nil? ? fields : "
%s
\n%s