Skip to content

Instantly share code, notes, and snippets.

@ideadapt
Created November 19, 2013 17:47
Show Gist options
  • Save ideadapt/7549393 to your computer and use it in GitHub Desktop.
Save ideadapt/7549393 to your computer and use it in GitHub Desktop.
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