Skip to content

Instantly share code, notes, and snippets.

@huacnlee
Created August 3, 2010 01:18
Show Gist options
  • Save huacnlee/505657 to your computer and use it in GitHub Desktop.
Save huacnlee/505657 to your computer and use it in GitHub Desktop.
# Rails wice_grid 5.0 插件实现跳过 default_scope 的问题
# vendor/plugins/wice_grid/lib/wice_grid.rb
# 修正 WiceGrid 与 default_scope 的形式冲突的问题,修改此插件的源代码加入了 skip_default_scope 参数(默认 true),用于让使用者可选择是否需要在 initialize_grid 方法调用的时候跳过 default_scope。因为之前有试过直接在 initialize_grid 外面加上Model.send(:with_exclusive_scope) { } 是无法跳过 default_scope 的。
# 使用方法:
# initialize_grid(Post,:per_page => 10, :skip_default_scope => false ) # 开启 default_scope
# initialize_grid(Post,:per_page => 10, :skip_default_scope => true ) # 跳过 Post 里面定义的 default_scope
# 代码:
require 'wice_grid_misc.rb'
begin
require 'will_paginate'
rescue MissingSourceFile => e
raise Wice::WiceGridException.new('will_paginate not found, WiceGrid cannot proceed. Please install gem mislav-will_paginate. ' +
'You might need to add github.com as the gem source before you install the gem: ' +
'gem sources -a http://gems.github.com')
end
require 'js_calendar_helpers.rb'
require 'wice_grid_core_ext.rb'
require 'grid_renderer.rb'
require 'table_column_matrix.rb'
require 'wice_grid_view_helpers.rb'
require 'view_columns.rb'
require 'grid_output_buffer.rb'
require 'controller.rb'
require 'wice_grid_spreadsheet.rb'
require 'wice_grid_serialized_queries_controller.rb'
module Wice
class WiceGrid
attr_reader :klass, :name, :resultset, :custom_order, :after, :query_store_model
attr_reader :ar_options, :status, :export_to_csv_enabled, :csv_file_name, :saved_query
attr_writer :renderer
attr_accessor :output_buffer, :view_helper_finished
# core workflow methods START
def initialize(klass, controller, opts = {}) #:nodoc:
@controller = controller
raise WiceGridArgumentError.new(":after must be either a Proc a Symbol object") unless [NilClass, Symbol, Proc].index opts[:after].class
raise WiceGridArgumentError.new("ActiveRecord model class (second argument) must be a Class derived from ActiveRecord::Base") unless klass.kind_of? Class and klass.ancestors.index(ActiveRecord::Base)
raise WiceGridException.new("Plugin will_paginate not found! wice_grid requires will_paginate.") unless klass.respond_to?(:paginate)
opts[:order_direction].downcase! if opts[:order_direction].kind_of?(String)
if opts[:order_direction] and not (opts[:order_direction] == 'asc' or opts[:order_direction] == :asc or
opts[:order_direction] == 'desc' or opts[:order_direction] == :desc)
raise WiceGridArgumentError.new(":order_direction must be either 'asc' or 'desc'.")
end
# options that are understood
@options = {
:per_page => Defaults::PER_PAGE,
:order_direction => Defaults::ORDER_DIRECTION,
:name => Defaults::GRID_NAME,
:enable_export_to_csv => Defaults::ENABLE_EXPORT_TO_CSV,
:csv_file_name => nil,
:columns => nil,
:order => nil,
:page => 1,
:joins => nil,
:include => nil,
:conditions => nil,
:custom_order => {},
:after => nil,
:saved_query => nil,
:skip_default_scope => true
}
if opts.has_key?(:erb_mode)
STDERR.puts "WiceGrid: Parameter erb_mode has been moved to the view helper and is therefore ignored"
end
@options.merge!(opts)
@export_to_csv_enabled = @options[:enable_export_to_csv]
@csv_file_name = @options[:csv_file_name]
@after = @options[:after]
case @name = @options[:name]
when String
when Symbol
@name = @name.to_s
else
raise WiceGridArgumentError.new("name of the grid should be a string or a symbol")
end
raise WiceGridArgumentError.new("name of the grid can only contain alphanumeruc characters") unless @name =~ /^[a-zA-Z\d_]*$/
@klass = klass
@table_column_matrix = TableColumnMatrix.new
@table_column_matrix.default_model_class = @klass
@ar_options = {}
@status = HashWithIndifferentAccess.new
if @options[:order]
@options[:order] = @options[:order].to_s
@options[:order_direction] = @options[:order_direction].to_s
@status[:order_direction] = @options[:order_direction]
@status[:order] = @options[:order]
end
@status[:per_page] = @options[:per_page]
@status[:page] = @options[:page]
@status[:conditions] = @options[:conditions]
@status[:f] = @options[:f]
process_loading_query
process_params
@ar_options_formed = false
end
def process_loading_query #:nodoc:
@saved_query = nil
if params[name] && params[name][:q]
@saved_query = load_query(params[name][:q])
params[name].delete(:q)
elsif @options[:saved_query]
if @options[:saved_query].is_a? ActiveRecord::Base
@saved_query = @options[:saved_query]
else
@saved_query = load_query(@options[:saved_query])
end
else
return
end
unless @saved_query.nil?
params[name] = HashWithIndifferentAccess.new if params[name].blank?
[:f, :order, :order_direction].each do |key|
if @saved_query.query[key].blank?
params[name].delete(key)
else
params[name][key] = @saved_query.query[key]
end
end
end
end
def process_params #:nodoc:
if this_grid_params
@status.merge!(this_grid_params)
@status.delete(:export) unless self.export_to_csv_enabled
end
end
def declare_column(column_name, model_class, custom_filter_active, table_alias) #:nodoc:
if model_class # this is an included table
column = @table_column_matrix.get_column_by_model_class_and_column_name(model_class, column_name)
raise WiceGridArgumentError.new("Сolumn '#{column_name}' is not found in table '#{model_class.table_name}'!") if column.nil?
main_table = false
table_name = model_class.table_name
else
column = @table_column_matrix.get_column_in_default_model_class_by_column_name(column_name)
raise WiceGridArgumentError.new("Сolumn '#{column_name}' is not found in table '#{@klass.table_name}'! If '#{column_name}' belongs to another table you should declare it in :include or :join when initialising the grid, and specify :model_class in column declaration.") if column.nil?
main_table = true
table_name = @table_column_matrix.default_model_class.table_name
end
if column
conditions, current_parameter_name = column.initialize_request_parameters(@status[:f], main_table, table_alias, custom_filter_active)
if @status[:f] && conditions.blank?
@status[:f].delete(current_parameter_name)
end
@table_column_matrix.add_condition(column, conditions)
[column, table_name , main_table]
else
nil
end
end
def form_ar_options(opts = {}) #:nodoc:
return if @ar_options_formed
@ar_options_formed = true unless opts[:forget_generated_options]
@ar_options[:conditions] = @status[:conditions]
if @table_column_matrix.generated_conditions.size == 0
@status.delete(:f)
end
@table_column_matrix.generated_conditions.each do |table_column, conditions|
@ar_options[:conditions] = Wice::unite_conditions(@ar_options[:conditions], conditions)
end
if (! opts[:skip_ordering]) && @status[:order]
@ar_options[:order] = add_custom_order_sql(complete_column_name(@status[:order]))
@ar_options[:order] += ' ' + @status[:order_direction]
end
if self.output_html?
@ar_options[:per_page] = @status[:pp] || @status[:per_page]
@ar_options[:page] = @status[:page]
end
@ar_options[:joins] = @options[:joins]
@ar_options[:include] = @options[:include]
end
def read #:nodoc:
form_ar_options
# Wice.log(@ar_options.to_yaml)
if @options[:skip_default_scope] == true
@klass.send(:with_exclusive_scope){ @resultset = self.output_csv? ? @klass.find(:all, @ar_options) : @klass.paginate(@ar_options) }
else
@resultset = self.output_csv? ? @klass.find(:all, @ar_options) : @klass.paginate(@ar_options)
end
end
# core workflow methods END
# Getters
def filter_params(view_column) #:nodoc:
column_name = view_column.attribute_name_fully_qualified_for_all_but_main_table_columns
if @status[:f] and @status[:f][column_name]
@status[:f][column_name]
else
{}
end
end
def resultset #:nodoc:
self.read unless @resultset # database querying is late!
@resultset
end
def each #:nodoc:
self.read unless @resultset # database querying is late!
@resultset.each do |r|
yield r
end
end
def ordered_by?(column) #:nodoc:
return nil if @status[:order].blank?
if column.main_table && ! offs = @status[:order].index('.')
@status[:order] == column.attribute_name
else
@status[:order] == column.table_alias_or_table_name + '.' + column.attribute_name
end
end
def ordered_by #:nodoc:
@status[:order]
end
def order_direction #:nodoc:
@status[:order_direction]
end
def filtering_on? #:nodoc:
not @status[:f].blank?
end
def filtered_by #:nodoc:
@status[:f].nil? ? [] : @status[:f].keys
end
def filtered_by?(view_column) #:nodoc:
@status[:f].nil? ? false : @status[:f].has_key?(view_column.attribute_name_fully_qualified_for_all_but_main_table_columns)
end
def get_state_as_parameter_value_pairs(including_saved_query_request = false) #:nodoc:
res = []
unless status[:f].blank?
status[:f].parameter_names_and_values([name, 'f']).collect do |param_name, value|
if value.is_a?(Array)
param_name_ar = param_name + '[]'
value.each do |v|
res << [param_name_ar, v]
end
else
res << [param_name, value]
end
end
end
if including_saved_query_request && @saved_query
res << ["#{name}[q]", @saved_query.id ]
end
[:order, :order_direction].select{|parameter|
status[parameter]
}.collect do |parameter|
res << ["#{name}[#{parameter}]", status[parameter] ]
end
res
end
def count #:nodoc:
form_ar_options(:skip_ordering => true, :forget_generated_options => true)
if @options[:skip_default_scope] == true
@klass.send(:with_exclusive_scope){
@klass.count(:conditions => @ar_options[:conditions], :joins => @ar_options[:joins], :include => @ar_options[:include])
}
else
@klass.count(:conditions => @ar_options[:conditions], :joins => @ar_options[:joins], :include => @ar_options[:include])
end
end
alias_method :size, :count
def empty? #:nodoc:
self.count == 0
end
# with this variant we get even those values which do not appear in the resultset
def distinct_values_for_column(column) #:nodoc:
res = column.model_klass.find(:all, :select => 'distinct ' + column.name).collect{|ar| ar[column.name] }.reject(&:blank?).map{|i|[i,i]}
end
def distinct_values_for_column_in_resultset(messages) #:nodoc:
uniq_vals = Set.new
resultset_without_paging_without_user_filters.each do |ar|
v = ar.deep_send(*messages)
uniq_vals << v unless v.nil?
end
return uniq_vals.to_a.map{|i|
if i.is_a?(Array) && i.size == 2
i
elsif i.is_a?(Hash) && i.size == 1
i.to_a.flatten
else
[i,i]
end
}
end
def output_csv? #:nodoc:
@status[:export] == 'csv'
end
def output_html? #:nodoc:
@status[:export].blank?
end
def all_record_mode? #:nodoc:
@status[:pp]
end
def dump_status #:nodoc:
" params: #{params[name].inspect}\n" +
" status: #{@status.inspect}\n" +
" ar_options #{@ar_options.inspect}\n"
end
# Returns a list of all records of the current selection throughout all pages.
# Can be called only after the view helper.
# See section "Integration With The Application" in the README.
def selected_records
raise WiceGridException.new("all_records can only be called only after the grid view helper") unless self.view_helper_finished
resultset_without_paging_with_user_filters
end
protected
def add_custom_order_sql(fully_qualified_column_name) #:nodoc:
custom_order = if @options[:custom_order].has_key?(fully_qualified_column_name)
@options[:custom_order][fully_qualified_column_name]
else
if view_column = @renderer[fully_qualified_column_name]
view_column.custom_order
else
nil
end
end
if custom_order.blank?
fully_qualified_column_name
else
if custom_order.is_a? String
custom_order.gsub(/\?/, fully_qualified_column_name)
elsif custom_order.is_a? Proc
custom_order.call(fully_qualified_column_name)
else
raise WiceGridArgumentError.new("invalid custom order #{custom_order.inspect}")
end
end
end
def complete_column_name(col_name) #:nodoc:
if col_name.index('.') # already has a table name
col_name
else # add the default table
"#{@klass.table_name}.#{col_name}"
end
end
def params #:nodoc:
@controller.params
end
def this_grid_params #:nodoc:
params[name]
end
def resultset_without_paging_without_user_filters #:nodoc:
form_ar_options
if @options[:skip_default_scope] == true
@klass.send(:with_exclusive_scope){
@klass.find(:all, :joins => @ar_options[:joins], :include => @ar_options[:include], :conditions => @options[:conditions])
}
else
@klass.find(:all, :joins => @ar_options[:joins], :include => @ar_options[:include], :conditions => @options[:conditions])
end
end
def resultset_without_paging_with_user_filters #:nodoc:
form_ar_options
if @options[:skip_default_scope] == true
@klass.send(:with_exclusive_scope){
@klass.find(:all, :joins => @ar_options[:joins],
:include => @ar_options[:include],
:conditions => @ar_options[:conditions],
:order => @ar_options[:order])
}
else
@klass.find(:all, :joins => @ar_options[:joins],
:include => @ar_options[:include],
:conditions => @ar_options[:conditions],
:order => @ar_options[:order])
end
end
def load_query(query_id) #:nodoc:
@query_store_model ||= Wice::get_query_store_model
query = @query_store_model.find_by_id_and_grid_name(query_id, self.name)
Wice::log("Query with id #{query_id} for grid '#{self.name}' not found!!!") if query.nil?
query
end
end
end
module ActiveRecord #:nodoc:
module ConnectionAdapters #:nodoc:
class Column #:nodoc:
# TO DO: Move into this module what can be moved not to pollute the namespace
module GridTools #:nodoc:
class << self
def special_value(str) #:nodoc:
str =~ /^\s*(not\s+)?null\s*$/i
end
end
end
attr_accessor :model_klass
def initialize_request_parameters(all_filter_params, main_table, table_alias, custom_filter_active) #:nodoc:
@request_params = nil
return if all_filter_params.nil?
# if the parameter does not specify the table name we only allow columns in the default table to use these parameters
if main_table && @request_params = all_filter_params[self.name]
current_parameter_name = self.name
elsif @request_params = all_filter_params[alias_or_table_na
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment