module RailsAdmin::Adapters::Mongoid

Constants

DISABLED_COLUMN_TYPES
ObjectId
STRING_TYPE_COLUMN_NAMES

Public Instance Methods

adapter_supports_joins?() click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 112
def adapter_supports_joins?
  false
end
all(options = {},scope=nil) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 37
def all(options = {},scope=nil)
  scope ||= self.scoped
  scope = scope.includes(*options[:include]) if options[:include]
  scope = scope.limit(options[:limit]) if options[:limit]
  scope = scope.any_in(:_id => options[:bulk_ids]) if options[:bulk_ids]
  scope = scope.where(query_conditions(options[:query])) if options[:query]
  scope = scope.where(filter_conditions(options[:filters])) if options[:filters]
  if options[:page] && options[:per]
    scope = scope.send(Kaminari.config.page_method_name, options[:page]).per(options[:per])
  end
  scope = sort_by(options, scope) if options[:sort]
  scope
end
associations() click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 63
def associations
  model.associations.values.map do |association|
    {
      :name => association.name.to_sym,
      :pretty_name => association.name.to_s.tr('_', ' ').capitalize,
      :type => association_type_lookup(association.macro),
      :model_proc => Proc.new { association_model_proc_lookup(association) },
      :primary_key_proc => Proc.new { association_primary_key_lookup(association) },
      :foreign_key => association_foreign_key_lookup(association),
      :foreign_type => association_foreign_type_lookup(association),
      :foreign_inverse_of => association_foreign_inverse_of_lookup(association),
      :as => association_as_lookup(association),
      :polymorphic => association_polymorphic_lookup(association),
      :inverse_of => association_inverse_of_lookup(association),
      :read_only => nil,
      :nested_form => association_nested_attributes_options_lookup(association)
    }
  end
end
count(options = {},scope=nil) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 51
def count(options = {},scope=nil)
  all(options.merge({:limit => false, :page => false}), scope).count
end
destroy(objects) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 55
def destroy(objects)
  Array.wrap(objects).each &:destroy
end
embedded?() click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 104
def embedded?
  @embedded ||= !!model.associations.values.find{|a| a.macro.to_sym == :embedded_in }
end
encoding() click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 100
def encoding
  'UTF-8'
end
first(options = {},scope=nil) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 33
def first(options = {},scope=nil)
  all(options, scope).first
end
get(id) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 16
def get(id)
  begin
    AbstractObject.new(model.find(id))
  rescue => e
    if ['BSON::InvalidObjectId', 'Mongoid::Errors::DocumentNotFound',
        'Mongoid::Errors::InvalidFind', 'Moped::Errors::InvalidObjectId'].include? e.class.to_s
      nil
    else
      raise e
    end
  end
end
new(params = {}) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 12
def new(params = {})
  AbstractObject.new(model.new)
end
object_id_from_string(str) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 108
def object_id_from_string(str)
  ObjectId.from_string(str)
end
primary_key() click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 59
def primary_key
  '_id'
end
properties() click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 83
def properties
  fields = model.fields.reject{|name, field| DISABLED_COLUMN_TYPES.include?(field.type.to_s) }
  fields.map do |name,field|
    {
      :name => field.name.to_sym,
      :length => nil,
      :pretty_name => field.name.to_s.gsub('_', ' ').strip.capitalize,
      :nullable? => true,
      :serial? => false,
    }.merge(type_lookup(name, field))
  end
end
scoped() click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 29
def scoped
  model.scoped
end
table_name() click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 96
def table_name
  model.collection_name.to_s
end

Private Instance Methods

association_as_lookup(association) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 328
def association_as_lookup(association)
  association.as.try :to_sym
end
association_foreign_inverse_of_lookup(association) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 310
def association_foreign_inverse_of_lookup(association)
  if association.polymorphic? && [:referenced_in, :belongs_to].include?(association.macro) && association.respond_to?(:inverse_of_field)
    association.inverse_of_field.try(:to_sym)
  end
end
association_foreign_key_lookup(association) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 344
def association_foreign_key_lookup(association)
  unless [:embeds_one, :embeds_many].include?(association.macro.to_sym)
    association.foreign_key.to_sym rescue nil
  end
end
association_foreign_type_lookup(association) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 304
def association_foreign_type_lookup(association)
  if association.polymorphic? && [:referenced_in, :belongs_to].include?(association.macro)
    association.inverse_type.try(:to_sym) || :"#{association.name}_type"
  end
end
association_inverse_of_lookup(association) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 340
def association_inverse_of_lookup(association)
  association.inverse_of.try :to_sym
end
association_model_proc_lookup(association) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 296
def association_model_proc_lookup(association)
  if association.polymorphic? && [:referenced_in, :belongs_to].include?(association.macro)
    RailsAdmin::AbstractModel.polymorphic_parents(:mongoid, self.model.model_name, association.name) || []
  else
    association.klass
  end
end
association_nested_attributes_options_lookup(association) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 316
      def association_nested_attributes_options_lookup(association)
        nested = model.nested_attributes_options.try { |o| o[association.name.to_sym] }
        if !nested && [:embeds_one, :embeds_many].include?(association.macro.to_sym)
          raise <<-MSG.gsub(/^\s+/, '')
          Embbeded association without accepts_nested_attributes_for can't be handled by RailsAdmin,
          because embedded model doesn't have top-level access.
          Please add `accepts_nested_attributes_for :#{association.name}' line to `#{model.to_s}' model.
          MSG
        end
        nested
      end
association_polymorphic_lookup(association) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 332
def association_polymorphic_lookup(association)
  !!association.polymorphic? && [:referenced_in, :belongs_to].include?(association.macro)
end
association_primary_key_lookup(association) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 336
def association_primary_key_lookup(association)
  :_id # todo
end
association_type_lookup(macro) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 350
def association_type_lookup(macro)
  case macro.to_sym
  when :belongs_to, :referenced_in, :embedded_in
    :belongs_to
  when :has_one, :references_one, :embeds_one
    :has_one
  when :has_many, :references_many, :embeds_many
    :has_many
  when :has_and_belongs_to_many, :references_and_referenced_in_many
    :has_and_belongs_to_many
  else
    raise "Unknown association type: #{macro.inspect}"
  end
end
build_statement(column, type, value, operator) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 177
def build_statement(column, type, value, operator)
  # this operator/value has been discarded (but kept in the dom to override the one stored in the various links of the page)
  return if operator == '_discard' || value == '_discard'

  # filtering data with unary operator, not type dependent
  if operator == '_blank' || value == '_blank'
    return { column => {'$in' => [nil, '']} }
  elsif operator == '_present' || value == '_present'
    return { column => {'$nin' => [nil, '']} }
  elsif operator == '_null' || value == '_null'
    return { column => nil }
  elsif operator == '_not_null' || value == '_not_null'
    return { column => {'$ne' => nil} }
  elsif operator == '_empty' || value == '_empty'
    return { column => '' }
  elsif operator == '_not_empty' || value == '_not_empty'
    return { column => {'$ne' => ''} }
  end
  # now we go type specific
  case type
  when :boolean
    return { column => false } if ['false', 'f', '0'].include?(value)
    return { column => true } if ['true', 't', '1'].include?(value)
  when :integer
    case value
    when Array then
      val, range_begin, range_end = *value.map{|v| v.blank? ? nil : (v == v.to_i.to_s) ? v.to_i : nil}
      if range_begin && range_end
        { column => {'$gte' => range_begin, '$lte' => range_end} }
      elsif range_begin
        { column => {'$gte' => range_begin} }
      elsif range_end
        { column => {'$lte' => range_end} }
      elsif val
        { column => val }
      end
    else
      s = value.to_s
      return s =~ /^[\-]?\d+$/ ? { column => s.to_i } : nil
    end
  when :string, :text
    return if value.blank?
    value = case operator
    when 'default', 'like'
      Regexp.compile(Regexp.escape(value), Regexp::IGNORECASE)
    when 'starts_with'
      Regexp.compile("^#{Regexp.escape(value)}", Regexp::IGNORECASE)
    when 'ends_with'
      Regexp.compile("#{Regexp.escape(value)}$", Regexp::IGNORECASE)
    when 'is', '='
      value.to_s
    else
      return
    end
    { column => value }
  when :date
    start_date, end_date = get_filtering_duration(operator, value)

    if start_date && end_date
      { column => { '$gte' => start_date, '$lte' => end_date } }
    elsif start_date
      { column => { '$gte' => start_date } }
    elsif end_date
      { column => { '$lte' => end_date } }
    end
  when :datetime, :timestamp
    start_date, end_date = get_filtering_duration(operator, value)

    if start_date && end_date
      { column => { '$gte' => start_date.to_time.beginning_of_day, '$lte' => end_date.to_time.end_of_day } }
    elsif start_date
      { column => { '$gte' => start_date.to_time.beginning_of_day } }
    elsif end_date
      { column => { '$lte' => end_date.to_time.end_of_day } }
    end
  when :enum
    return if value.blank?
    { column => { "$in" => Array.wrap(value) } }
  when :belongs_to_association, :bson_object_id
    object_id = (object_id_from_string(value) rescue nil)
    { column => object_id } if object_id
  end
end
filter_conditions(filters, fields = config.list.fields.select(&:filterable?)) click to toggle source

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

# File lib/rails_admin/adapters/mongoid.rb, line 143
def filter_conditions(filters, fields = config.list.fields.select(&:filterable?))
  statements = []

  filters.each_pair do |field_name, filters_dump|
    filters_dump.each do |filter_index, filter_dump|
      conditions_per_collection = {}
      field = fields.find{|f| f.name.to_s == field_name}
      next unless field
      field.searchable_columns.each do |column_infos|
        collection_name, column_name = parse_collection_name(column_infos[:column])
        statement = build_statement(column_name, column_infos[:type], filter_dump[:v], (filter_dump[:o] || 'default'))
        if statement
          conditions_per_collection[collection_name] ||= []
          conditions_per_collection[collection_name] << statement
        end
      end
      if conditions_per_collection.any?
        field_statements = make_condition_for_current_collection(field, conditions_per_collection)
        if field_statements.length > 1
          statements << { '$or' => field_statements }
        else
          statements << field_statements.first
        end
      end
    end
  end

  if statements.any?
    { '$and' => statements }
  else
    {}
  end
end
length_validation_lookup(name) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 365
def length_validation_lookup(name)
  shortest = model.validators.select do |validator|
    validator.respond_to?(:attributes) &&
      validator.attributes.include?(name.to_sym) &&
      validator.kind == :length &&
      validator.options[:maximum]
  end.min{|a, b| a.options[:maximum] <=> b.options[:maximum] }
  if shortest
    shortest.options[:maximum]
  else
    false
  end
end
make_condition_for_current_collection(target_field, conditions_per_collection) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 388
def make_condition_for_current_collection(target_field, conditions_per_collection)
  result =[]
  conditions_per_collection.each do |collection_name, conditions|
    if collection_name == table_name
      # conditions referring current model column are passed directly
      result.concat conditions
    else
      # otherwise, collect ids of documents that satisfy search condition
      result.concat perform_search_on_associated_collection(target_field.name, conditions)
    end
  end
  result
end
parse_collection_name(column) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 379
def parse_collection_name(column)
  collection_name, column_name = column.split('.')
  if [:embeds_one, :embeds_many].include?(model.associations[collection_name].try(:macro).try(:to_sym))
    [table_name, column]
  else
    [collection_name, column_name]
  end
end
perform_search_on_associated_collection(field_name, conditions) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 402
def perform_search_on_associated_collection(field_name, conditions)
  target_association = associations.find{|a| a[:name] == field_name }
  return [] unless target_association
  model = target_association[:model_proc].call
  case target_association[:type]
  when :belongs_to, :has_and_belongs_to_many
    [{ target_association[:foreign_key].to_s => { '$in' => model.where('$or' => conditions).all.map{|r| r.send(target_association[:primary_key_proc].call)} }}]
  when :has_many
    [{ target_association[:primary_key_proc].call.to_s => { '$in' => model.where('$or' => conditions).all.map{|r| r.send(target_association[:foreign_key])} }}]
  end
end
query_conditions(query, fields = config.list.fields.select(&:queryable?)) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 118
def query_conditions(query, fields = config.list.fields.select(&:queryable?))
  statements = []

  fields.each do |field|
    conditions_per_collection = {}
    field.searchable_columns.flatten.each do |column_infos|
      collection_name, column_name = parse_collection_name(column_infos[:column])
      statement = build_statement(column_name, column_infos[:type], query, field.search_operator)
      if statement
        conditions_per_collection[collection_name] ||= []
        conditions_per_collection[collection_name] << statement
      end
    end
    statements.concat make_condition_for_current_collection(field, conditions_per_collection)
  end

  if statements.any?
    { '$or' => statements }
  else
    {}
  end
end
sort_by(options, scope) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 414
def sort_by(options, scope)
  return scope unless options[:sort]

  field_name, collection_name = options[:sort].to_s.split('.').reverse
  if collection_name && collection_name != table_name
    # sorting by associated model column is not supported, so just ignore
    return scope
  end
  if options[:sort_reverse]
    scope.asc field_name
  else
    scope.desc field_name
  end
end
type_lookup(name, field) click to toggle source
# File lib/rails_admin/adapters/mongoid.rb, line 261
def type_lookup(name, field)
  {
    "Array"          => { :type => :serialized },
    "BigDecimal"     => { :type => :decimal },
    "Boolean"        => { :type => :boolean },
    "BSON::ObjectId" => { :type => :bson_object_id, :serial? => (name == primary_key) },
    "Moped::BSON::ObjectId" => { :type => :bson_object_id, :serial? => (name == primary_key) },
    "Date"           => { :type => :date },
    "DateTime"       => { :type => :datetime },
    "ActiveSupport::TimeWithZone" => { :type => :datetime },
    "Float"          => { :type => :float },
    "Hash"           => { :type => :serialized },
    "Money"          => { :type => :serialized },
    "Integer"        => { :type => :integer },
    "Object"         => (
      if associations.find{|a| a[:type] == :belongs_to && a[:foreign_key] == name.to_sym}
        { :type => :bson_object_id }
      else
        { :type => :string, :length => 255 }
      end
    ),
      "String"         => (
        if (length = length_validation_lookup(name)) && length < 256
          { :type => :string, :length => length }
        elsif STRING_TYPE_COLUMN_NAMES.include?(name.to_sym)
          { :type => :string, :length => 255 }
        else
          { :type => :text }
        end
    ),
      "Symbol"         => { :type => :string, :length => 255 },
      "Time"           => { :type => :datetime },
  }[field.type.to_s] or raise "Type #{field.type.to_s} for field :#{name} in #{model.inspect} not supported"
end