module Dao class Form # for html generation # include Tagz.globally class << Form include Tagz.globally end # class methods # class << Form def for(*args, &block) new(*args, &block) end end # instance methods # attr_accessor :map def initialize(*args, &block) @map = args.first.is_a?(Map) ? args.shift : Map.new end def errors @map.errors end def path @map.path end # html generation methods # def form(*args, &block) options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {}) keys = args.flatten action = options.delete(:action) || './' method = options.delete(:method) || 'post' id = options.delete(:id) || id_for(keys) klass = class_for(keys, options.delete(:class)) error = error_for(keys, options.delete(:error)) content = if block.nil? and !options.has_key?(:content) '' else block ? block.call(form=self) : options.delete(:content) end form_(options_for(options, :action => action, :method => method, :class => klass, :id => id, :data_error => error)){ content } end def label(*args, &block) options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {}) keys = args.flatten id = options.delete(:id) || id_for(keys) klass = class_for(keys, options.delete(:class)) error = error_for(keys, options.delete(:error)) target = options.delete(:for) || id_for(keys) content = if block.nil? and !options.has_key?(:content) humanize(keys.last) else block ? block.call() : options.delete(:content) end label_(options_for(options, :for => target, :class => klass, :id => id, :data_error => error)){ content } end def input(*args, &block) options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {}) keys = args.flatten type = options.delete(:type) || :text name = options.delete(:name) || name_for(keys) id = options.delete(:id) || id_for(keys) klass = class_for(keys, options.delete(:class)) error = error_for(keys, options.delete(:error)) value = if block.nil? and !options.has_key?(:value) value_for(@map, keys) else block ? block.call(@map.get(keys)) : options.delete(:value) end input_(options_for(options, :type => type, :name => name, :value => value, :class => klass, :id => id, :data_error => error)){} end def submit(*args, &block) options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {}) content = block ? block.call : (args.first || 'Submit') options[:name] ||= :submit options[:type] ||= :submit options[:value] ||= content input_(options_for(options)){} end def button(*args) options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {}) keys = args.flatten type = options.delete(:type) || :button name = options.delete(:name) || name_for(keys) id = options.delete(:id) || id_for(keys) klass = class_for(keys, options.delete(:class)) error = error_for(keys, options.delete(:error)) value = if block.nil? and !options.has_key?(:value) value_for(@map, keys) else block ? block.call(@map.get(keys)) : options.delete(:value) end button_(options_for(options, :type => type, :name => name, :value => value, :class => klass, :id => id, :data_error => error)){} end def reset(*args) options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {}) options[:type] = :reset args.push(options) button(*args) end def textarea(*args, &block) options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {}) keys = args.flatten name = options.delete(:name) || name_for(keys) id = options.delete(:id) || id_for(keys) klass = class_for(keys, options.delete(:class)) error = error_for(keys, options.delete(:error)) value = if block.nil? and !options.has_key?(:value) value_for(@map, keys) else block ? block.call(@map.get(keys)) : options.delete(:value) end textarea_(options_for(options, :name => name, :class => klass, :id => id, :data_error => error)){ value.to_s } end def select(*args, &block) options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {}) keys = args.flatten name = options.delete(:name) || name_for(keys) from = options.delete(:from) || options.delete(:options) blank = options.delete(:blank) selected = if options.has_key?(:selected) options.delete(:selected) else value_for(@map, keys) end id = options.delete(:id) || id_for(keys) klass = class_for(keys, options.delete(:class)) error = error_for(keys, options.delete(:error)) block ||= lambda{|pair| pair = Array(pair).flatten.compact; [pair.first, pair.last, selected=nil]} if from.nil? key = keys.map{|key| "#{ key }"} key.last << "_options" from = @map.get(*key) if @map.has?(*key) end list = Array(from) case list.first when Hash, Array nil else list.flatten! list.compact! list.map!{|element| [element, element]} end case blank when nil, false nil when true list.push(nil) else list.unshift(blank) end selected_value = case selected when Array selected.first when Hash key = [:id, 'id', :value, 'value'].detect{|k| selected.has_key?(k)} key ? selected[key] : selected else selected end select_(options_for(options, :name => name, :class => klass, :id => id, :data_error => error)){ list.each do |pair| returned = block.call(pair) case returned when Array value, content, selected, *ignored = returned when Hash value = returned[:value] content = returned[:content] || value selected = returned[:selected] else value = returned content = returned selected = nil end if selected.nil? selected = value.to_s==selected_value.to_s end opts = {:value => value} opts[:selected] = !!selected if selected option_(opts){ content } end } end # html generation support methods # def id_for(keys) id = [path, keys.join('-')].compact.join('_') slug_for(id) end def class_for(keys, klass = nil) klass = if errors.on?(keys) [klass, 'dao', 'errors'].compact.join(' ') else [klass, 'dao'].compact.join(' ') end klass end def error_for(keys, klass = nil) errors.get(keys) if errors.on?(keys) end def value_for(map, keys) return nil unless map.has?(keys) value = Tagz.escapeHTML(map.get(keys)) end def Form.name_for(path, *keys) path = Path.new(path) unless path.is_a?(Path) "#{ path }(#{ Array(keys).flatten.compact.join(',') })" end def Form.name_re_for(path) path = Path.new(path) unless path.is_a?(Path) Regexp.new(/^#{ Regexp.escape(path) }/) end def Form.encoded?(path, params) name_re = Form.name_re_for(path) params.keys.any?{|key| name_re =~ key.to_s} end def name_for(keys) Form.name_for(path, keys) end def options_for(*hashes) map = Map.new hashes.flatten.each do |h| h.each{|k,v| map[attr_for(k)] = v unless v.nil?} end map end def slug_for(string) string = string.to_s words = string.to_s.scan(%r/\w+/) words.map!{|word| word.gsub(%r/[^0-9a-zA-Z_:-]/, '')} words.delete_if{|word| word.nil? or word.strip.empty?} words.join('-').downcase.sub(/_+$/, '') end def attr_for(string) slug_for(string).gsub(/_/, '-') end def humanize(string) string = string.to_s string = string.humanize if string.respond_to?(:humanize) string end end end