# tag of the current cell.
#
# In case of an array output, please note that if you need to define HTML attributes for all 's in a
# column, use +html+. Also note that if the method returns a hash with a :class or 'class'
# 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:
#
#
# It is up to the developer to make sure that what in rendered in column cells
# corresponds to sorting and filtering specified by parameters :attribute and :model.
def column(opts = {}, &block)
options = {
allow_multiple_selection: Defaults::ALLOW_MULTIPLE_SELECTION,
ordering: true,
attribute: nil,
auto_reload: Defaults::AUTO_RELOAD,
boolean_filter_false_label: NlMessage['boolean_filter_false_label'],
boolean_filter_true_label: NlMessage['boolean_filter_true_label'],
class: nil,
name: '',
custom_filter: nil,
detach_with_id: nil,
filter_all_label: Defaults::CUSTOM_FILTER_ALL_LABEL,
helper_style: Defaults::HELPER_STYLE,
in_csv: true,
in_html: true,
model: nil,
negation: Defaults::NEGATION_IN_STRING_FILTERS,
filter: true,
filter_type: nil,
table_alias: nil,
html: {}
}
opts.assert_valid_keys(options.keys)
options.merge!(opts)
unless options[:model].nil?
options[:model] = options[:model].constantize if options[:model].is_a? String
raise WiceGridArgumentError.new("Option :model can be either a class or a string instance") unless options[:model].is_a? Class
end
if options[:attribute].nil? && options[:model]
raise WiceGridArgumentError.new("Option :model 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?
block = lambda{|obj| obj.send(options[:attribute])}
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(options[:attribute], options[:model],
options[:custom_filter], options[:table_alias], options[:filter_type])
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
lambda{ @grid.distinct_values_for_column(db_column) } # Thank God Ruby has higher order functions!!!
elsif options[:custom_filter].class == Symbol
lambda{ @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)
lambda{ @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
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.kind_of?(Columns.get_view_column_processor(:custom))
if vc.kind_of?(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
# Optional method inside the +grid+ block, to which every ActiveRecord instance is injected, just like +column+.
# Unlike +column+, it returns a hash which will be used as HTML attributes for the row with the given ActiveRecord instance.
#
# Note that if the method returns a hash with a :class or 'class' element, it will not overwrite
# classes +even+ and +odd+, instead they will be concatenated:
def row_attributes(&block)
@row_attributes_handler = block
end
# Can be used to add HTML code (another row, for example) right after each grid row.
# Nothing is added if the block return +false+ or +nil+.
def after_row(&block)
@after_row_handler = block
end
# Can be used to add HTML code (another row, for example) right before each grid row.
# Nothing is added if the block return +false+ or +nil+.
def before_row(&block)
@before_row_handler = block
end
# Can be used to replace the HTML code (for example to make a multi-column spanning row) of a row.
# Nothing is replaced if the block return +false+ or +nil+.
def replace_row(&block)
@replace_row_handler = block
end
# Can be used to add HTML code (calculation results, for example) after all rows.
# Nothing is added if the block return +false+ or +nil+.
def last_row(&block)
@last_row_handler = block
end
# The output of the block submitted to +blank_slate+ is rendered instead of the whole grid if no filters are active
# and there are no records to render.
# In addition to the block style two other variants are accepted:
# * g.blank_slate "some text to be rendered"
# * g.blank_slate partial: "partial_name"
def blank_slate(opts = nil, &block)
if (opts.is_a?(Hash) && opts.has_key?(:partial) && block.nil?) || (opts.is_a?(String) && block.nil?)
@blank_slate_handler = opts
elsif opts.nil? && block
@blank_slate_handler = block
else
raise WiceGridArgumentError.new("blank_slate accepts only a string, a block, or template: 'path_to_template' ")
end
end
def get_row_attributes(ar_object) #:nodoc:
if @row_attributes_handler
row_attributes = @row_attributes_handler.call(ar_object)
row_attributes = {} if row_attributes.blank?
unless row_attributes.is_a?(Hash)
raise WiceGridArgumentError.new("row_attributes block must return a hash containing HTML attributes. The returned value is #{row_attributes.inspect}")
end
row_attributes
else
{}
end
end
def no_filter_needed? #:nodoc:
not @columns.inject(false){|a,b| a || b.filter_shown? }
end
def no_filter_needed_in_main_table? #:nodoc:
not @columns.inject(false){|a,b| a || b.filter_shown_in_main_table? }
end
def base_link_for_filter(controller, extra_parameters = {}) #:nodoc:
new_params = Wice::WgHash.deep_clone controller.params
new_params.merge!(extra_parameters)
if new_params[@grid.name]
new_params[@grid.name].delete(:page) # we reset paging here
new_params[@grid.name].delete(:f) # no filter for the base url
new_params[@grid.name].delete(:foc) # nullify the focus
new_params[@grid.name].delete(:q) # and no request for the saved query
end
new_params[:only_path] = false
base_link_with_pp_info = omit_empty_query controller.url_for(new_params)
if new_params[@grid.name]
new_params[@grid.name].delete(:pp) # and reset back to pagination if show all mode is on
end
[base_link_with_pp_info, omit_empty_query(controller.url_for(new_params))]
end
def link_for_export(controller, format, extra_parameters = {}) #:nodoc:
new_params = Wice::WgHash.deep_clone controller.params
new_params.merge!(extra_parameters)
new_params[@grid.name] = {} unless new_params[@grid.name]
new_params[@grid.name][:export] = format
new_params[:only_path] = false
controller.url_for(new_params)
end
def column_link(column, direction, params, extra_parameters = {}) #:nodoc:
column_attribute_name = if column.attribute.index('.') or column.main_table
column.attribute
else
column.table_alias_or_table_name + '.' + column.attribute
end
query_params = {@grid.name => {
@@order_parameter_name => column_attribute_name,
@@order_direction_parameter_name => direction
}}
cleaned_params = Wice::WgHash.deep_clone params
cleaned_params.merge!(extra_parameters)
cleaned_params.delete(:controller)
cleaned_params.delete(:action)
query_params = Wice::WgHash.rec_merge(cleaned_params, query_params)
'?' + query_params.to_query
end
protected
def filter_columns(method_name = nil) #:nodoc:
method_name ? @columns.select(&method_name) : @columns
end
def omit_empty_query(url) #:nodoc:
# /foo? --> /foo
# /foo?grid= --> /foo
# /foo?grid=&page=1 --> /foo?page=1
# /foo?grid=some_value --> /foo?grid=some_value
empty_hash_rx = Regexp.new "([&?])#{Regexp.escape @grid.name}=([&?]|$)" # c.f., issue #140
url.gsub(empty_hash_rx, '\1').gsub /\?+$/, ''
end
end
end
| | |