require 'kiss/form/field'; class Kiss class Form class Field dsl_accessor :name, :type, :form, :currency, :label, :no_label, :prompt, :value, :read_only, :ignore, :save, :options, :options_value_key, :options_display_key, :options_display_transform, :required, :unique, :cancel, :columns, :style, :hidden_join, :html, :other, :other_field, :object, :format, :display_format, :key, :match, :tip, :statement, :attach_errors, :factor, :digest, :min_value_size, :max_value_size, :choose_here alias_method :option_value_key, :options_value_key alias_method :option_display_key, :options_display_key alias_method :option_display_transform, :options_display_transform alias_method :min_value_length, :min_value_size alias_method :max_value_length, :max_value_size def method_missing(method, *args, &block) @_form.action.send method, *args, &block end def options_keys(value, display) @_options_value_key = value @_options_display_key = display end alias_method :option_keys, :options_keys def debug(*args) @_form.delegate.request.debug(args.first, Kernel.caller[0]) end def initialize(form, *args, &block) # defaults @_form = form @_save = true @_currency = nil @_attrs = args.to_attrs @_type = :text @_object = @_form.object @_save = true @_attach_errors = true _instance_variables_set_from_attrs(@_attrs) instance_eval(&block) if block_given? @_errors = [] @_format = Kiss::Format.lookup(@_format) @_display_format = Kiss::Format.lookup(@_display_format) raise 'field must have a name' unless @_name @_key ||= @_name @_label ||= @_name.titleize unless @_type == :submit # object's value overrides any form field value # form field value is intended as default in case object value is missing if @_object && (value = @_object[@_key.to_sym]) @_value = value end if @_currency.is_a?(Symbol) @_currency = case @_currency when :dollars '$' else '' end end if @_options if @_options[0].is_a?(Array) @_options_value_key ||= 0 (@_options_display_key ||= (@_options[0].size == 1) ? 0 : 1) elsif defined?(Kiss::Model) && @_options[0].is_a?(Kiss::Model) model_klass = @_options[0].class @_options_value_key ||= model_klass.value_column @_options_display_key ||= model_klass.display_column elsif @_options[0].is_a?(Hash) @_options_value_key ||= :id @_options_display_key ||= :name end end @_tip = ((legend = @_format.legend) ? "(#{legend})" : nil) unless defined? @_tip @_form.has_required_fields ||= @_required end def other_field_html return '' unless @_other other_checked = @_value && !option_pairs.any? {|v, d| v == @_value } (@_columns ? '
' : '  ') + [ input_tag_html( { :value => 'other', :html => { :id => @_name+'.other' } }, other_checked ? 'checked' : '' ), @_other[:label] || 'Other', ': ', @_currency.to_s, input_tag_html({ :type => :text, :name => @_name+'.other', :value => other_checked ? value_to_s(@_value) : nil, :html => { :onfocus => "document.getElementById('#{@_name}.other').checked = true" } }.merge(@_other)) ].join end def column_layout(elements_html) if elements_html.empty? '' elsif @_columns layout_columns = [@_columns, elements_html.size].min num_elements_per_column = ((elements_html.size + layout_columns - 1) / layout_columns).to_i layout_columns = ((elements_html.size + num_elements_per_column - 1) / num_elements_per_column).to_i style = "style=\"width: #{(100 / layout_columns).to_i - 1}%\"" '' + (0...layout_columns).map do |i| "" end.join + '
" + elements_html[i * num_elements_per_column, num_elements_per_column].join('
') + "
' else elements_html.map {|h| "#{h}"}.join('  ') end end def param @_param ||= @_form.params[@_name.to_s] end def value @_value end def value_string v = value v *= @_factor if v && @_factor param || value_to_s(v) end def add_error(message) (@_attach_errors ? @_errors : form.errors) << message @_form.has_field_errors = true nil end def value_to_s(value) value ? @_format.value_to_s(value, @_form.context) : '' end def display_to_s(value) value ? @_display_format.value_to_s(value).send(@_options_display_transform) : '' end def require_value(enter_verb = 'enter') if (param !~ /\S/) # value required add_error("Please #{enter_verb} #{@_label}") return end end def reset @_value = @_default_value if defined?(@_default_value) @_param = nil end def validate(enter_verb = 'enter') @_default_value ||= value require_value(enter_verb) if @_required begin p = @_format.validate(param, @_form.context) p *= @_factor if @_factor value = p rescue Kiss::Format::ValidateError => e return add_error("#{e.message.capitalize}") end if @_match match_field = @_form.fields[@_match.to_s] if value != match_field.value return add_error("#{match_field.label.pluralize} don't match.") end end if @_save && @_unique dataset = @_object.model.filter(@_name.to_sym => value) unless (@_object.new? ? dataset : dataset.exclude(@_object.pk_hash)).empty? return add_error("There is already another #{@_object.model.name.singularize.gsub('_', ' ')} with the same #{@_label.downcase.gsub('_', ' ')}.") end end @_value = value end def errors_html return nil unless @_errors.size > 0 if @_errors.size == 1 content = @_errors[0] else content = "" end %Q(#{content}
) end def element_html(attrs = {}) attrs.merge!( :value => value_string ) if width = @_format.input_width attrs[:style] ||= "width: #{width}px" end @_currency.to_s + input_tag_html( attrs ) + tip_html(attrs) end def table_row_html form.component_html(self) end def tip_html(attrs) (tip = attrs.has_key?(:tip) ? attrs[:tip] : @_tip) ? " #{tip}" : '' end def html(*args) errors = errors_html element_html(*args) + (errors ? (@_columns ? '' : '
') + %Q(#{errors}) : '') end def tag_start_html(tag_name, attrs = {}, extra_html = nil) attrs = attrs.clone attrs[:name] ||= @_name attrs[:size] ||= @_size if @_size attrs[:style] ||= @_style if @_style read_only = attrs.delete(:read_only) || @_read_only html_parts = ["<#{tag_name}"] html = attrs.delete(:html) || @_html attrs.each_pair {|k, v| html_parts.push %Q(#{k}="#{v}") } if html if html.is_a?(Hash) html.each_pair {|k, v| html_parts.push %Q(#{k}="#{v}") } else html_parts.push(@_html.to_s) end end html_parts.push(extra_html) if extra_html html_parts.join(' ') end def tag_html(tag_name, attrs = {}, extra_html = nil) return attrs[:value].html_escape.to_s.gsub(/\n/, '
') if attrs[:read_only] || @_read_only tag_start_html(tag_name, attrs, extra_html) + ' />' end def content_tag_html(tag_name, content, attrs = {}, extra_html = nil) return content.to_s.html_escape.gsub(/\n/, '
') if attrs[:read_only] || @_read_only tag_start_html(tag_name, attrs, extra_html) + ">#{content}" end def input_tag_html(attrs = {}, extra_html = nil) tag_html( 'input', {:type => @_type}.merge(attrs), extra_html ) end end end end require 'kiss/form/field_types'