# 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: 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_control_options: {},
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,
sort_by: 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,
sort_by: options[:sort_by],
)
# [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 get_model_from_associations(model, assocs) # :nodoc:
if assocs.empty?
model
else
head = assocs[0]
tail = assocs[1..-1]
if reflection = model.reflect_on_association(head)
next_model = reflection.klass
get_model_from_associations(next_model, tail)
else
raise WiceGridArgumentError.new("Association #{head} not found in #{model}")
end
end
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.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:
!@columns.inject(false) { |a, b| a || b.filter_shown? }
end
def no_filter_needed_in_main_table? #:nodoc:
!@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.to_unsafe_h
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.to_unsafe_h
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('.') || column.main_table || column.table_alias_or_table_name.nil?
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.to_unsafe_h
cleaned_params.merge!(extra_parameters)
cleaned_params.delete(:controller)
cleaned_params.delete(:action)
query_params = Wice::WgHash.rec_merge(cleaned_params, query_params)
if Rails.version.to_i >= 5
'?' + query_params.to_h.to_query
else
'?' + query_params.to_query
end
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
| | |