Created
November 19, 2013 17:47
-
-
Save ideadapt/7549393 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: lib/rails_admin/adapters/active_record.rb | |
IDEA additional info: | |
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
<+>UTF-8 | |
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP | |
<+>require 'active_record'\nrequire 'rails_admin/adapters/active_record/abstract_object'\n\nmodule RailsAdmin\n module Adapters\n module ActiveRecord\n DISABLED_COLUMN_TYPES = [:tsvector, :blob, :binary, :spatial, :hstore, :geometry]\n DISABLED_COLUMN_MATCHERS = [/_array$/]\n\n def new(params = {})\n AbstractObject.new(model.new(params))\n end\n\n def get(id)\n if object = model.where(primary_key => id).first\n AbstractObject.new object\n end\n end\n\n def scoped\n model.all\n end\n\n def first(options = {}, scope = nil)\n all(options, scope).first\n end\n\n def all(options = {}, scope = nil)\n scope ||= self.scoped\n scope = scope.includes(options[:include]) if options[:include]\n scope = scope.limit(options[:limit]) if options[:limit]\n scope = scope.where(primary_key => options[:bulk_ids]) if options[:bulk_ids]\n scope = query_scope(scope, options[:query]) if options[:query]\n scope = filter_scope(scope, options[:filters]) if options[:filters]\n if options[:page] && options[:per]\n scope = scope.send(Kaminari.config.page_method_name, options[:page]).per(options[:per])\n end\n scope = scope.reorder(\"#{options[:sort]} #{options[:sort_reverse] ? 'asc' : 'desc'}\") if options[:sort]\n scope\n end\n\n def count(options = {}, scope = nil)\n all(options.merge({:limit => false, :page => false}), scope).count\n end\n\n def destroy(objects)\n Array.wrap(objects).each &:destroy\n end\n\n def associations\n model.reflect_on_all_associations.map do |association|\n Association.new(association, model).to_options_hash\n end\n end\n\n def properties\n columns = model.columns.reject do |c|\n c.type.blank? ||\n DISABLED_COLUMN_TYPES.include?(c.type.to_sym) ||\n DISABLED_COLUMN_MATCHERS.any? {|matcher| matcher.match(c.type.to_s)}\n end\n columns.map do |property|\n {\n :name => property.name.to_sym,\n :pretty_name => property.name.to_s.tr('_', ' ').capitalize,\n :length => property.limit,\n :nullable? => property.null,\n :serial? => property.primary,\n }.merge(type_lookup(property))\n end\n end\n\n delegate :primary_key, :table_name, :to => :model, :prefix => false\n\n def encoding\n Rails.configuration.database_configuration[Rails.env]['encoding']\n end\n\n def embedded?\n false\n end\n\n def cyclic?\n false\n end\n\n def adapter_supports_joins?\n true\n end\n\n private\n\n def query_scope(scope, query, fields = config.list.fields.select(&:queryable?))\n statements = []\n values = []\n tables = []\n\n fields.each do |field|\n field.searchable_columns.flatten.each do |column_infos|\n statement, value1, value2 = build_statement(column_infos[:column], column_infos[:type], query, field.search_operator)\n statements << statement if statement\n values << value1 unless value1.nil?\n values << value2 unless value2.nil?\n table, column = column_infos[:column].split('.')\n tables.push(table) if column\n end\n end\n scope.where(statements.join(' OR '), *values).references(*(tables.uniq))\n end\n\n # filters example => {\"string_field\"=>{\"0055\"=>{\"o\"=>\"like\", \"v\"=>\"test_value\"}}, ...}\n # \"0055\" is the filter index, no use here. o is the operator, v the value\n def filter_scope(scope, filters, fields = config.list.fields.select(&:filterable?))\n filters.each_pair do |field_name, filters_dump|\n filters_dump.each do |filter_index, filter_dump|\n statements = []\n values = []\n tables = []\n fields.find{|f| f.name.to_s == field_name}.searchable_columns.each do |column_infos|\n statement, value1, value2 = build_statement(column_infos[:column], column_infos[:type], filter_dump[:v], (filter_dump[:o] || 'default'))\n statements << statement if statement.present?\n values << value1 unless value1.nil?\n values << value2 unless value2.nil?\n table, column = column_infos[:column].split('.')\n tables.push(table) if column\n end\n scope = scope.where(statements.join(' OR '), *values).references(*(tables.uniq))\n end\n end\n scope\n end\n\n def build_statement(column, type, value, operator)\n StatementBuilder.new(column, type, value, operator).to_statement\n end\n\n def type_lookup(property)\n if model.serialized_attributes[property.name.to_s]\n {:type => :serialized}\n else\n {:type => property.type}\n end\n end\n\n private\n class Association\n attr_reader :association, :model\n\n def initialize(association, model)\n @association = association\n @model = model\n end\n\n def to_options_hash\n {\n :name => name.to_sym,\n :pretty_name => display_name,\n :type => macro,\n :model_proc => Proc.new { model_lookup },\n :primary_key_proc => Proc.new { primary_key_lookup },\n :foreign_key => foreign_key.to_sym,\n :foreign_type => foreign_type_lookup,\n :as => as_lookup,\n :polymorphic => polymorphic_lookup,\n :inverse_of => inverse_of_lookup,\n :read_only => read_only_lookup,\n :nested_form => nested_attributes_options_lookup\n }\n end\n\n private\n def model_lookup\n if options[:polymorphic]\n polymorphic_parents(:active_record, model_name.to_s, name) || []\n else\n klass\n end\n end\n\n def foreign_type_lookup\n options[:foreign_type].try(:to_sym) || :\"#{name}_type\" if options[:polymorphic]\n end\n\n def nested_attributes_options_lookup\n model.nested_attributes_options.try { |o| o[name.to_sym] }\n end\n\n def as_lookup\n options[:as].try :to_sym\n end\n\n def polymorphic_lookup\n !!options[:polymorphic]\n end\n\n def primary_key_lookup\n options[:primary_key] || klass.primary_key\n end\n\n def inverse_of_lookup\n options[:inverse_of].try :to_sym\n end\n\n def read_only_lookup\n klass.all.instance_eval(&scope).readonly_value if scope.is_a? Proc\n end\n\n def display_name\n name.to_s.tr('_', ' ').capitalize\n end\n\n delegate :klass, :macro, :name, :options, :scope, :foreign_key,\n :to => :association, :prefix => false\n delegate :name, :to => :model, :prefix => true\n delegate :polymorphic_parents, :to => RailsAdmin::AbstractModel\n end\n\n class StatementBuilder < RailsAdmin::AbstractModel::StatementBuilder\n protected\n\n def unary_operators\n {\n '_blank' => [\"(#{column} IS NULL OR #{column} = '')\"],\n '_present' => [\"(#{column} IS NOT NULL AND #{column} != '')\"],\n '_null' => [\"(#{column} IS NULL)\"],\n '_not_null' => [\"(#{column} IS NOT NULL)\"],\n '_empty' => [\"(#{column} = '')\"],\n '_not_empty' => [\"(#{column} != '')\"]\n }\n end\n\n private\n\n def datetime_filter(start_date, end_date, datetime = false)\n if datetime\n start_date = start_date.to_time.beginning_of_day if start_date\n end_date = end_date.to_time.end_of_day if end_date\n end\n\n if start_date && end_date\n [\"(#{column} BETWEEN ? AND ?)\", start_date, end_date]\n elsif start_date\n [\"(#{column} >= ?)\", start_date]\n elsif end_date\n [\"(#{column} <= ?)\", end_date]\n end\n end\n\n def build_statement_for_type\n case type\n when :boolean\n return [\"(#{column} IS NULL OR #{column} = ?)\", false] if %w[false f 0].include?(@value)\n return [\"(#{column} = ?)\", true] if %w[true t 1].include?(@value)\n when :integer, :decimal, :float\n case @value\n when Array then\n val, range_begin, range_end = *@value.map do |v|\n type == :integer ? v.to_i : v.to_f if [v.to_i.to_s, v.to_f.to_s].include?(v)\n end\n case operator\n when 'between'\n datetime_filter(range_begin, range_end)\n else\n [\"(#{column} = ?)\", val] if val\n end\n else\n if @value.to_i.to_s == @value || @value.to_f.to_s == @value\n [\"(#{column} = ?)\", (type == :integer ? @value.to_i : @value.to_f)]\n end\n end\n when :belongs_to_association\n return if @value.blank?\n [\"(#{column} = ?)\", @value.to_i] if @value.to_i.to_s == @value\n when :string, :text\n return if @value.blank?\n @value = case operator\n when 'default', 'like'\n \"%#{@value.downcase}%\"\n when 'starts_with'\n \"#{@value.downcase}%\"\n when 'ends_with'\n \"%#{@value.downcase}\"\n when 'is', '='\n \"#{@value.downcase}\"\n else\n return\n end\n [\"(LOWER(#{column}) #{like_operator} ?)\", @value]\n when :date\n datetime_filter(*get_filtering_duration)\n when :datetime, :timestamp\n datetime_filter(*get_filtering_duration, true)\n when :enum\n return if @value.blank?\n [\"(#{column} IN (?))\", Array.wrap(@value)]\n end\n end\n\n def ar_adapter\n Rails.configuration.database_configuration[Rails.env]['adapter']\n end\n\n def like_operator\n ar_adapter == \"postgresql\" ? 'ILIKE' : 'LIKE'\n end\n end\n end\n end\nend\n | |
=================================================================== | |
--- lib/rails_admin/adapters/active_record.rb (revision 90e551d52748c06fd25c4d188c74596073181492) | |
+++ lib/rails_admin/adapters/active_record.rb (revision ) | |
@@ -90,46 +90,54 @@ | |
private | |
- def query_scope(scope, query, fields = config.list.fields.select(&:queryable?)) | |
- statements = [] | |
- values = [] | |
- tables = [] | |
+ class WhereBuilder | |
- fields.each do |field| | |
+ def initialize(scope) | |
+ @statements = [] | |
+ @values = [] | |
+ @tables = [] | |
+ @scope = scope | |
+ end | |
+ | |
+ def add(field, value, operator) | |
field.searchable_columns.flatten.each do |column_infos| | |
- statement, value1, value2 = build_statement(column_infos[:column], column_infos[:type], query, field.search_operator) | |
- statements << statement if statement | |
- values << value1 unless value1.nil? | |
- values << value2 unless value2.nil? | |
+ statement, value1, value2 = StatementBuilder.new(column_infos[:column], column_infos[:type], value, operator).to_statement | |
+ @statements << statement if statement.present? | |
+ @values << value1 unless value1.nil? | |
+ @values << value2 unless value2.nil? | |
table, column = column_infos[:column].split('.') | |
- tables.push(table) if column | |
+ @tables.push(table) if column | |
end | |
end | |
- scope.where(statements.join(' OR '), *values).references(*(tables.uniq)) | |
+ | |
+ def build | |
+ @scope.where(@statements.join(' OR '), *@values).references(*(@tables.uniq)) | |
- end | |
+ end | |
+ end | |
+ def query_scope(scope, query, fields = config.list.fields.select(&:queryable?)) | |
+ wb = WhereBuilder.new(scope) | |
+ fields.each do |field| | |
+ wb.add(field, query, field.search_operator) | |
+ end | |
+ wb.build | |
+ end | |
+ | |
# filters example => {"string_field"=>{"0055"=>{"o"=>"like", "v"=>"test_value"}}, ...} | |
# "0055" is the filter index, no use here. o is the operator, v the value | |
def filter_scope(scope, filters, fields = config.list.fields.select(&:filterable?)) | |
filters.each_pair do |field_name, filters_dump| | |
- filters_dump.each do |filter_index, filter_dump| | |
- statements = [] | |
- values = [] | |
- tables = [] | |
- fields.find{|f| f.name.to_s == field_name}.searchable_columns.each do |column_infos| | |
- statement, value1, value2 = build_statement(column_infos[:column], column_infos[:type], filter_dump[:v], (filter_dump[:o] || 'default')) | |
- statements << statement if statement.present? | |
- values << value1 unless value1.nil? | |
- values << value2 unless value2.nil? | |
- table, column = column_infos[:column].split('.') | |
- tables.push(table) if column | |
+ filters_dump.each do |_, filter_dump| | |
+ wb = WhereBuilder.new(scope) | |
+ operator = (filter_dump[:o] || 'default') | |
+ wb.add(fields.find{|f| f.name.to_s == field_name}, filter_dump[:v], operator) | |
+ scope = wb.build | |
- end | |
+ end | |
- scope = scope.where(statements.join(' OR '), *values).references(*(tables.uniq)) | |
- end | |
+ end | |
- end | |
scope | |
end | |
+ # <b>DEPRECATED:</b> Please use <tt>StatementBuilder.new(column, type, value, operator).to_statement</tt> instead. | |
def build_statement(column, type, value, operator) | |
StatementBuilder.new(column, type, value, operator).to_statement | |
end | |
@@ -142,7 +150,6 @@ | |
end | |
end | |
- private | |
class Association | |
attr_reader :association, :model | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment