def column(opts = {}, &block)
      options = {
        allow_multiple_selection:    ConfigurationProvider.value_for(:ALLOW_MULTIPLE_SELECTION),
        assoc:                       nil,
        attribute:                   nil,
        auto_reload:                 ConfigurationProvider.value_for(:AUTO_RELOAD),
        boolean_filter_false_label:  NlMessage['boolean_filter_false_label'],
        boolean_filter_true_label:   NlMessage['boolean_filter_true_label'],
        class:                       nil,
        custom_filter:               nil,
        detach_with_id:              nil,
        filter:                      true,
        filter_all_label:            ConfigurationProvider.value_for(:CUSTOM_FILTER_ALL_LABEL),
        filter_type:                 nil,
        html:                        {},
        in_csv:                      true,
        in_html:                     true,
        model:                       nil, # will throw an exception with instructions
        name:                        '',
        negation:                    ConfigurationProvider.value_for(:NEGATION_IN_STRING_FILTERS),
        ordering:                    true,
        table_alias:                 nil
      }

      opts.assert_valid_keys(options.keys)
      options.merge!(opts)

      assocs = nil

      if options[:model]
        raise WiceGridArgumentError.new('Instead of specifying a model of a joined table please use assoc: :name_of_association')
      end

      unless options[:assoc].nil?

        unless options[:assoc].is_a?(Symbol) ||
              (options[:assoc].is_a?(Array) && ! options[:assoc].empty? && options[:assoc].all?{ |assoc| assoc.is_a?(Symbol)})

          raise WiceGridArgumentError.new('Option :assoc can only be a symbol or an array of symbols')
        end

        assocs = options[:assoc].is_a?(Symbol) ? [options[:assoc]] : options[:assoc]

        options[:model] = get_model_from_associations(@grid.klass, assocs)
      end

      if options[:attribute].nil? && options[:model]
        raise WiceGridArgumentError.new('Option :assoc is only used together with :attribute')
      end

      if options[:attribute] && options[:attribute].index('.')
        raise WiceGridArgumentError.new("Invalid attribute name #{options[:attribute]}. An attribute name must not contain a table name!")
      end


      if options[:class]
        options[:html] ||= {}
        Wice::WgHash.add_or_append_class_value!(options[:html], options[:class])
        options.delete(:class)
      end

      if block.nil?
        if !options[:attribute].blank?
          if assocs.nil?
            block = ->(obj) { obj.send(options[:attribute]) }
          else
            messages = assocs + [ options[:attribute] ]
            block = ->(obj) { obj.deep_send(*messages) }
          end
        else
          raise WiceGridArgumentError.new(
            'Missing column block without attribute defined. You can only omit the block if attribute is present.')
        end
      end

      klass = Columns::ViewColumn
      if options[:attribute] &&
        col_type_and_table_name = @grid.declare_column(
          column_name:          options[:attribute],
          model:                options[:model],
          custom_filter_active: options[:custom_filter],
          table_alias:          options[:table_alias],
          filter_type:          options[:filter_type],
          assocs:               assocs
        )

        # [ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::Column, String, Boolean]
        db_column, table_name, main_table = col_type_and_table_name
        col_type = db_column.type

        if options[:custom_filter]

          custom_filter = if options[:custom_filter] == :auto
            -> { @grid.distinct_values_for_column(db_column) } # Thank God Ruby has higher order functions!!!

          elsif options[:custom_filter].class == Symbol
            -> { @grid.distinct_values_for_column_in_resultset([options[:custom_filter]]) }

          elsif options[:custom_filter].class == Hash
            options[:custom_filter].keys

            options[:custom_filter].to_a

          elsif options[:custom_filter].class == Array
            if options[:custom_filter].empty?
              []
            elsif Wice::WgEnumerable.all_items_are_of_class(options[:custom_filter], Symbol)
              -> { @grid.distinct_values_for_column_in_resultset(options[:custom_filter]) }

            elsif Wice::WgEnumerable.all_items_are_of_class(options[:custom_filter], String) || WgEnumerable.all_items_are_of_class(options[:custom_filter], Numeric)
              options[:custom_filter].map { |i| [i, i] }

            elsif Wice::WgEnumerable.all_items_are_of_class(options[:custom_filter], Array)
              options[:custom_filter]
            else
              raise WiceGridArgumentError.new(
                ':custom_filter can equal :auto, an array of string and/or numbers (direct values for the dropdown), ' \
                'a homogeneous array of symbols (a sequence of methods to send to AR objects in the result set to ' \
                'retrieve unique values for the dropdown), a Symbol (a shortcut for a one member array of symbols), ' \
                'a hash where keys are labels and values are values for the dropdown option, or an array of two-item arrays, ' \
                'each of which contains the label (first element) and the value (second element) for a dropdown option'
                )
            end
          end

          klass = Columns.get_view_column_processor(:custom)
        elsif options[:filter_type]
          klass = Columns.get_view_column_processor(options[:filter_type])
        else

          col_type = case col_type
          when :date
            Wice::Defaults::DEFAULT_FILTER_FOR_DATE
          when :datetime
            Wice::Defaults::DEFAULT_FILTER_FOR_DATETIME
          when :timestamp
            Wice::Defaults::DEFAULT_FILTER_FOR_DATETIME
          else
            col_type
          end

          klass = Columns.get_view_column_processor(col_type)
        end # custom_filter

      end # attribute

      vc = klass.new(block, options, @grid, table_name, main_table, custom_filter, @view)

      vc.negation = options[:negation] if vc.respond_to? :negation=

      vc.filter_all_label = options[:filter_all_label] if vc.is_a?(Columns.get_view_column_processor(:custom))
      if vc.is_a?(Columns.get_view_column_processor(:boolean))
        vc.boolean_filter_true_label = options[:boolean_filter_true_label]
        vc.boolean_filter_false_label = options[:boolean_filter_false_label]
      end
      add_column(vc)
    end
def column(opts = {}, &block)
      options = {
        allow_multiple_selection:    ConfigurationProvider.value_for(:ALLOW_MULTIPLE_SELECTION),
        assoc:                       nil,
        attribute:                   nil,
        auto_reload:                 ConfigurationProvider.value_for(:AUTO_RELOAD),
        boolean_filter_false_label:  NlMessage['boolean_filter_false_label'],
        boolean_filter_true_label:   NlMessage['boolean_filter_true_label'],
        class:                       nil,
        custom_filter:               nil,
        detach_with_id:              nil,
        filter:                      true,
        filter_all_label:            ConfigurationProvider.value_for(:CUSTOM_FILTER_ALL_LABEL),
        filter_type:                 nil,
        html:                        {},
        in_csv:                      true,
        in_html:                     true,
        model:                       nil, # will throw an exception with instructions
        name:                        '',
        negation:                    ConfigurationProvider.value_for(:NEGATION_IN_STRING_FILTERS),
        ordering:                    true,
        table_alias:                 nil
      }

      opts.assert_valid_keys(options.keys)
      options.merge!(opts)

      assocs = nil

      if options[:model]
        raise WiceGridArgumentError.new('Instead of specifying a model of a joined table please use assoc: :name_of_association')
      end

      unless options[:assoc].nil?

        unless options[:assoc].is_a?(Symbol) ||
              (options[:assoc].is_a?(Array) && ! options[:assoc].empty? && options[:assoc].all?{ |assoc| assoc.is_a?(Symbol)})

          raise WiceGridArgumentError.new('Option :assoc can only be a symbol or an array of symbols')
        end

        assocs = options[:assoc].is_a?(Symbol) ? [options[:assoc]] : options[:assoc]

        options[:model] = get_model_from_associations(@grid.klass, assocs)
      end

      if options[:attribute].nil? && options[:model]
        raise WiceGridArgumentError.new('Option :assoc is only used together with :attribute')
      end

      if options[:attribute] && options[:attribute].index('.')
        raise WiceGridArgumentError.new("Invalid attribute name #{options[:attribute]}. An attribute name must not contain a table name!")
      end


      if options[:class]
        options[:html] ||= {}
        Wice::WgHash.add_or_append_class_value!(options[:html], options[:class])
        options.delete(:class)
      end

      if block.nil?
        if !options[:attribute].blank?
          if assocs.nil?
            block = ->(obj) { obj.send(options[:attribute]) }
          else
            messages = assocs + [ options[:attribute] ]
            block = ->(obj) { obj.deep_send(*messages) }
          end
        else
          raise WiceGridArgumentError.new(
            'Missing column block without attribute defined. You can only omit the block if attribute is present.')
        end
      end

      klass = Columns::ViewColumn
      if options[:attribute] &&
        col_type_and_table_name = @grid.declare_column(
          column_name:          options[:attribute],
          model:                options[:model],
          custom_filter_active: options[:custom_filter],
          table_alias:          options[:table_alias],
          filter_type:          options[:filter_type],
          assocs:               assocs
        )

        # [ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::Column, String, Boolean]
        db_column, table_name, main_table = col_type_and_table_name
        col_type = db_column.type

        if options[:custom_filter]

          custom_filter = if options[:custom_filter] == :auto
            -> { @grid.distinct_values_for_column(db_column) } # Thank God Ruby has higher order functions!!!

          elsif options[:custom_filter].class == Symbol
            -> { @grid.distinct_values_for_column_in_resultset([options[:custom_filter]]) }

          elsif options[:custom_filter].class == Hash
            options[:custom_filter].keys

            options[:custom_filter].to_a

          elsif options[:custom_filter].class == Array
            if options[:custom_filter].empty?
              []
            elsif Wice::WgEnumerable.all_items_are_of_class(options[:custom_filter], Symbol)
              -> { @grid.distinct_values_for_column_in_resultset(options[:custom_filter]) }

            elsif Wice::WgEnumerable.all_items_are_of_class(options[:custom_filter], String) || WgEnumerable.all_items_are_of_class(options[:custom_filter], Numeric)
              options[:custom_filter].map { |i| [i, i] }

            elsif Wice::WgEnumerable.all_items_are_of_class(options[:custom_filter], Array)
              options[:custom_filter]
            else
              raise WiceGridArgumentError.new(
                ':custom_filter can equal :auto, an array of string and/or numbers (direct values for the dropdown), ' \
                'a homogeneous array of symbols (a sequence of methods to send to AR objects in the result set to ' \
                'retrieve unique values for the dropdown), a Symbol (a shortcut for a one member array of symbols), ' \
                'a hash where keys are labels and values are values for the dropdown option, or an array of two-item arrays, ' \
                'each of which contains the label (first element) and the value (second element) for a dropdown option'
                )
            end
          end

          klass = Columns.get_view_column_processor(:custom)
        elsif options[:filter_type]
          klass = Columns.get_view_column_processor(options[:filter_type])
        else

          col_type = case col_type
          when :date
            Wice::Defaults::DEFAULT_FILTER_FOR_DATE
          when :datetime
            Wice::Defaults::DEFAULT_FILTER_FOR_DATETIME
          when :timestamp
            Wice::Defaults::DEFAULT_FILTER_FOR_DATETIME
          else
            col_type
          end

          klass = Columns.get_view_column_processor(col_type)
        end # custom_filter

      end # attribute

      vc = klass.new(block, options, @grid, table_name, main_table, custom_filter, @view)

      vc.negation = options[:negation] if vc.respond_to? :negation=

      vc.filter_all_label = options[:filter_all_label] if vc.is_a?(Columns.get_view_column_processor(:custom))
      if vc.is_a?(Columns.get_view_column_processor(:boolean))
        vc.boolean_filter_true_label = options[:boolean_filter_true_label]
        vc.boolean_filter_false_label = options[:boolean_filter_false_label]
      end
      add_column(vc)
    end
Defines everything related to a column in a grid - column name, filtering, rendering cells, etc.

 +column+ is only used inside the block of the +grid+ method. See documentation for the +grid+ method for more details.

 The only parameter is a hash of options. None of them is optional. If no options are supplied, the result will be a
 column with no name, no filtering and no sorting.

 * <tt>:name</tt> - Name of the column.
 * <tt>:html</tt> - a hash of HTML attributes to be included into the <tt>td</tt> tag.
 * <tt>:class</tt> - a shortcut for <tt>html: {class: 'css_class'}</tt>
 * <tt>:attribute</tt> - name of a database column (which normally correspond to a model attribute with the
   same name). By default the field is assumed to belong to the default table (see documentation for the
   +initialize_grid+ method). Parameter <tt>:assoc</tt> (association) allows to specify another joined table. Presence of
   this parameter
   * adds sorting capabilities by this field
   * automatically creates a filter based on the type of the field unless parameter <tt>:filter</tt> is set to false.
     The following filters exist for the following types:
     * <tt>string</tt> - a text field
     * <tt>integer</tt> and <tt>float</tt>  - two text fields to specify the range. Both limits or only one
       can be specified
     * <tt>boolean</tt> - a dropdown list with 'yes', 'no', or '-'. These labels can be changed in
       <tt>lib/wice_grid_config.rb</tt>.
     * <tt>date</tt> - two sets of standard date dropdown lists so specify the time range.
     * <tt>datetime</tt> - two sets of standard datetime dropdown lists so specify the time range. This filter
       is far from being user-friendly due to the number of dropdown lists.
 * <tt>:filter</tt> - Disables filters when set to false.
   This is needed if sorting is required while  filters are not.
 * <tt>:filter_type</tt> - Using a column filter different from the default filter chosen automatically based on the
   data type or the <tt>:custom_filter</tt> argument. See <tt>lib/columns/column_processor_index.rb</tt> for the
   list of available filters.
 * <tt>:ordering</tt> - Enable/disable ordering links in the column titles. The default is +true+
   (i.e. if <tt>:attribute</tt> is defined, ordering is enabled)
 * <tt>:assoc</tt> - Name of the model association. <tt>:attribute</tt> belongs to the table joined via this association.
 * <tt>:table_alias</tt> - In case there are two joined assocations both referring to the same table, ActiveRecord
   constructs a query where the second join provides an alias for the joined table. Setting <tt>:table_alias</tt>
   to this alias will enable WiceGrid to order and filter by columns belonging to different associatiations  but
   originating from the same table without confusion. See README for an example.
 * <tt>:custom_filter</tt> - Allows to construct a custom dropdown filter. Depending on the value of
   <tt>:custom_filter</tt> different modes are available:
   * array of strings and/or numbers - this is a direct  definition of possible values of the dropdown.
     Every item will be used both as the value of the select option and as its label.
   * Array of two-element arrays - Every first item of the two-element array is used for the label of the select option
     while the second element is the value of the select option
   * Hash - The keys of the hash become the labels of the generated dropdown list,
     while the values will be values of options of the dropdown list:
   * <tt>:auto</tt> - a powerful option which populates the dropdown list with all unique values of the field specified by
     <tt>:attribute</tt> and <tt>:model</tt>.
     <tt>:attribute</tt> throughout all pages. In other words, this runs an SQL query without +offset+ and +limit+
     clauses and  with <tt>distinct(table.field)</tt> instead of <tt>distinct(*)</tt>
   * any other symbol name (method name) - The dropdown list is populated by all unique value returned by the
     method with this name sent to <em>all</em> ActiveRecord objects throughout all pages. The main difference
     from <tt>:auto</tt> is that this method does not have to be a field in the result set, it is just some
     value computed in the method after the database call and ActiveRecord instantiation.

     But here lies the major drawback - this mode requires additional query without +offset+ and +limit+
     clauses to instantiate _all_ ActiveRecord objects, and performance-wise it brings all the advantages
     of pagination to nothing. Thus, memory- and performance-wise this can be really bad for some queries
     and tables and should be used with care.

     If the method returns a atomic value like a string or an integer, it is used for both the value and the
     label of the select option element. However, if the retuned value is a two element array, the first element
     is used for the option label and the second - for the value.
     Read more in README, section 'Custom dropdown filters'
   * An array of symbols (method names) - similar to the mode with a single symbol name. The first method name
     is sent to the ActiveRecord object if it responds to this method, the second method name is sent to the
     returned value unless it is +nil+, and so on. In other words, a single symbol mode is a
     case of an array of symbols where the array contains just one element. Thus the warning about the single method name
     mode applies here as well.

     If the last method returns a atomic value like a string or an integer, it is used for both the value and the
     label of the select option element.
     However, if the retuned value is a two element array, the first element is used for the option label and the
     second - for the value.
     Read more in README, section 'Custom dropdown filters'
 * <tt>:boolean_filter_true_label</tt> - overrides the label for <tt>true</tt> in the boolean filter (<tt>wice_grid.boolean_filter_true_label</tt> in <tt>wice_grid.yml</tt>).
 * <tt>:boolean_filter_false_label</tt> - overrides the label for <tt>false</tt> in the boolean filter (<tt>wice_grid.boolean_filter_false_label</tt> in <tt>wice_grid.yml</tt>).
 * <tt>:allow_multiple_selection</tt> - enables or disables switching between single and multiple selection modes for
   custom dropdown boxes. +true+ by default (see +ALLOW_MULTIPLE_SELECTION+ in the configuration file).
 * <tt>:filter_all_label</tt> - overrides the default value for <tt>BOOLEAN_FILTER_FALSE_LABEL</tt> ('<tt>--</tt>')
   in the config. Has effect in a column with a boolean filter _or_ a custom filter.
 * <tt>:detach_with_id</tt> - allows to detach the filter and render it after or before the grid with the
   +grid_filter+ helper. The value is an arbitrary unique identifier
   of the filter. Read section 'Detached Filters' in README for details.
   Has effect in a column with a boolean filter _or_ a custom filter.
 * <tt>:in_csv</tt> - When CSV export is enabled, all columns are included into the export. Setting <tt>:in_csv</tt>
   to false will prohibit the column from inclusion into the export.
 * <tt>:in_html</tt> - When CSV export is enabled and it is needed to use a column for CSV export only and ignore it
   in HTML, set this parameter to false.
 * <tt>:negation</tt> - turn on/off the negation checkbox in string filters
 * <tt>:auto_reload</tt> - a boolean value specifying if a change in a filter triggers reloading of the grid. Works with all
   filter types including the JS calendar, the only exception is the standard Rails date/datetime filters.
   The default is false. (see +AUTO_RELOAD+ in the configuration file).

 The block parameter is an ActiveRecord instance. This block is called for every ActiveRecord shown, and the return
 value of the block is a string which becomes the contents of one table cell, or an array of two elements where
 the first element is the cell contents and the second is a hash of HTML attributes to be added for the <tt><td></tt>
 tag of the current cell.

 In case of an array output, please note that if you need to define HTML attributes for all <tt><td></tt>'s in a
 column, use +html+. Also note that if the method returns a hash with a <tt>:class</tt> or <tt>'class'</tt>
 element, it will not overwrite the class defined in +html+, or classes added by the grid itself
 (+active-filter+ and +sorted+), instead they will be all concatenated:
 <tt><td class="sorted user_class_for_columns user_class_for_this_specific_cell"></tt>

 It is up to the developer to make sure that what in rendered in column cells
 corresponds to sorting and filtering specified by parameters <tt>:attribute</tt> and <tt>:model</tt>.