Merge pull request #928 from gregmolnar/extract_mongoid_support
move mongoid support to a separate gem
This commit is contained in:
commit
85fc41b329
8
Gemfile
8
Gemfile
|
@ -40,14 +40,6 @@ else
|
|||
end
|
||||
end
|
||||
|
||||
if ENV['DB'] =~ /mongoid4/
|
||||
gem 'mongoid', '~> 4.0.0', require: false
|
||||
end
|
||||
|
||||
if ENV['DB'] =~ /mongoid5/
|
||||
gem 'mongoid', '~> 5.0.0', require: false
|
||||
end
|
||||
|
||||
group :test do
|
||||
# TestUnit was removed from Ruby 2.2 but still needed for testing Rails 3.x.
|
||||
gem 'test-unit', '~> 3.0' if RUBY_VERSION >= '2.2'
|
||||
|
|
|
@ -875,6 +875,7 @@ en:
|
|||
|
||||
## Mongoid
|
||||
|
||||
Mongoid support has been moved to its own gem at [ransack-mongoid](github.com/activerecord-hackery/ransack-mongoid).
|
||||
Ransack works with Mongoid in the same way as Active Record, except that with
|
||||
Mongoid, associations are not currently supported. Demo source code may be found
|
||||
[here](https://github.com/Zhomart/ransack-mongodb-demo). A `result` method
|
||||
|
|
23
Rakefile
23
Rakefile
|
@ -11,17 +11,8 @@ RSpec::Core::RakeTask.new(:spec) do |rspec|
|
|||
# rspec.rspec_opts = ['--backtrace']
|
||||
end
|
||||
|
||||
RSpec::Core::RakeTask.new(:mongoid) do |rspec|
|
||||
ENV['SPEC'] = 'spec/mongoid/**/*_spec.rb'
|
||||
rspec.rspec_opts = ['--backtrace']
|
||||
end
|
||||
|
||||
task :default do
|
||||
if ENV['DB'] =~ /mongoid/
|
||||
Rake::Task["mongoid"].invoke
|
||||
else
|
||||
Rake::Task["spec"].invoke
|
||||
end
|
||||
Rake::Task["spec"].invoke
|
||||
end
|
||||
|
||||
desc "Open an irb session with Ransack and the sample data used in specs"
|
||||
|
@ -31,15 +22,3 @@ task :console do
|
|||
ARGV.clear
|
||||
Pry.start
|
||||
end
|
||||
|
||||
desc "Open an irb session with Ransack, Mongoid and the sample data used in specs"
|
||||
task :mongoid_console do
|
||||
require 'irb'
|
||||
require 'irb/completion'
|
||||
require 'pry'
|
||||
require 'mongoid'
|
||||
require File.expand_path('../lib/ransack.rb', __FILE__)
|
||||
require File.expand_path('../spec/mongoid/support/schema.rb', __FILE__)
|
||||
ARGV.clear
|
||||
Pry.start
|
||||
end
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
require 'ransack/adapters/mongoid/base'
|
||||
::Mongoid::Document.send :include, Ransack::Adapters::Mongoid::Base
|
||||
|
||||
require 'ransack/adapters/mongoid/attributes/attribute'
|
||||
require 'ransack/adapters/mongoid/table'
|
||||
require 'ransack/adapters/mongoid/inquiry_hash'
|
||||
|
||||
case ::Mongoid::VERSION
|
||||
when /^3\.2\./
|
||||
require 'ransack/adapters/mongoid/3.2/context'
|
||||
else
|
||||
require 'ransack/adapters/mongoid/context'
|
||||
end
|
||||
|
||||
Ransack::SUPPORTS_ATTRIBUTE_ALIAS = false
|
|
@ -1,37 +0,0 @@
|
|||
require 'ransack/adapters/mongoid/attributes/predications'
|
||||
require 'ransack/adapters/mongoid/attributes/order_predications'
|
||||
|
||||
module Ransack
|
||||
module Adapters
|
||||
module Mongoid
|
||||
module Attributes
|
||||
class Attribute < Struct.new :relation, :name
|
||||
# include Arel::Expressions
|
||||
# include Arel::Predications
|
||||
# include Arel::AliasPredication
|
||||
# include Arel::OrderPredications
|
||||
# include Arel::Math
|
||||
|
||||
include ::Ransack::Adapters::Mongoid::Attributes::Predications
|
||||
include ::Ransack::Adapters::Mongoid::Attributes::OrderPredications
|
||||
|
||||
###
|
||||
# Create a node for lowering this attribute
|
||||
def lower
|
||||
relation.lower self
|
||||
end
|
||||
end
|
||||
|
||||
class String < Attribute; end
|
||||
class Time < Attribute; end
|
||||
class Boolean < Attribute; end
|
||||
class Decimal < Attribute; end
|
||||
class Float < Attribute; end
|
||||
class Integer < Attribute; end
|
||||
class Undefined < Attribute; end
|
||||
end
|
||||
|
||||
Attribute = Attributes::Attribute
|
||||
end # Attributes
|
||||
end
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
module Ransack
|
||||
module Adapters
|
||||
module Mongoid
|
||||
module Attributes
|
||||
module OrderPredications
|
||||
def asc
|
||||
{ name => :asc }
|
||||
end
|
||||
|
||||
def desc
|
||||
{ name => :desc }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,141 +0,0 @@
|
|||
module Ransack
|
||||
module Adapters
|
||||
module Mongoid
|
||||
module Attributes
|
||||
module Predications
|
||||
def not_eq(other)
|
||||
{ name => { '$ne' => other } }.to_inquiry
|
||||
end
|
||||
|
||||
def not_eq_any(others)
|
||||
grouping_any :not_eq, others
|
||||
end
|
||||
|
||||
def not_eq_all(others)
|
||||
grouping_all :not_eq, others
|
||||
end
|
||||
|
||||
def eq(other)
|
||||
{ name => other }.to_inquiry
|
||||
end
|
||||
|
||||
def eq_any(others)
|
||||
grouping_any :eq, others
|
||||
end
|
||||
|
||||
def eq_all(others)
|
||||
grouping_all :eq, others
|
||||
end
|
||||
|
||||
def in(other)
|
||||
{ name => { "$in" => other } }.to_inquiry
|
||||
end
|
||||
|
||||
def in_any(others)
|
||||
grouping_any :in, others
|
||||
end
|
||||
|
||||
def in_all(others)
|
||||
grouping_all :in, others
|
||||
end
|
||||
|
||||
def not_in(other)
|
||||
{ "$not" => { name => { "$in" => other } } }.to_inquiry
|
||||
end
|
||||
|
||||
def not_in_any(others)
|
||||
grouping_any :not_in, others
|
||||
end
|
||||
|
||||
def not_in_all(others)
|
||||
grouping_all :not_in, others
|
||||
end
|
||||
|
||||
def matches(other)
|
||||
{ name => /#{other}/i }.to_inquiry
|
||||
end
|
||||
|
||||
def matches_any(others)
|
||||
grouping_any :matches, others
|
||||
end
|
||||
|
||||
def matches_all(others)
|
||||
grouping_all :matches, others
|
||||
end
|
||||
|
||||
def does_not_match(other)
|
||||
{ "$not" => { name => /#{other}/i } }.to_inquiry
|
||||
end
|
||||
|
||||
def does_not_match_any(others)
|
||||
grouping_any :does_not_match, others
|
||||
end
|
||||
|
||||
def does_not_match_all(others)
|
||||
grouping_all :does_not_match, others
|
||||
end
|
||||
|
||||
def gteq(right)
|
||||
{ name => { '$gte' => right } }.to_inquiry
|
||||
end
|
||||
|
||||
def gteq_any(others)
|
||||
grouping_any :gteq, others
|
||||
end
|
||||
|
||||
def gteq_all(others)
|
||||
grouping_all :gteq, others
|
||||
end
|
||||
|
||||
def gt(right)
|
||||
{ name => { '$gt' => right } }.to_inquiry
|
||||
end
|
||||
|
||||
def gt_any(others)
|
||||
grouping_any :gt, others
|
||||
end
|
||||
|
||||
def gt_all(others)
|
||||
grouping_all :gt, others
|
||||
end
|
||||
|
||||
def lt(right)
|
||||
{ name => { '$lt' => right } }.to_inquiry
|
||||
end
|
||||
|
||||
def lt_any(others)
|
||||
grouping_any :lt, others
|
||||
end
|
||||
|
||||
def lt_all(others)
|
||||
grouping_all :lt, others
|
||||
end
|
||||
|
||||
def lteq(right)
|
||||
{ name => { '$lte' => right } }.to_inquiry
|
||||
end
|
||||
|
||||
def lteq_any(others)
|
||||
grouping_any :lteq, others
|
||||
end
|
||||
|
||||
def lteq_all(others)
|
||||
grouping_all :lteq, others
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def grouping_any(method_id, others)
|
||||
nodes = others.map { |e| send(method_id, e) }
|
||||
{ "$or" => nodes }.to_inquiry
|
||||
end
|
||||
|
||||
def grouping_all(method_id, others)
|
||||
nodes = others.map { |e| send(method_id, e) }
|
||||
{ "$and" => nodes }.to_inquiry
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,134 +0,0 @@
|
|||
require 'delegate'
|
||||
|
||||
module Ransack
|
||||
module Adapters
|
||||
module Mongoid
|
||||
module Base
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
class_attribute :_ransackers
|
||||
class_attribute :_ransack_aliases
|
||||
self._ransackers ||= {}
|
||||
self._ransack_aliases ||= {}
|
||||
end
|
||||
|
||||
class ColumnWrapper < SimpleDelegator
|
||||
def type
|
||||
_super = super
|
||||
case _super
|
||||
when BSON::ObjectId, Object
|
||||
:string
|
||||
else
|
||||
_super.name.underscore.to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Connection
|
||||
def initialize model
|
||||
@model = model
|
||||
end
|
||||
|
||||
def quote_column_name name
|
||||
name
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def ransack(params = {}, options = {})
|
||||
params = params.presence || {}
|
||||
Search.new(self, params ? params.delete_if {
|
||||
|k, v| v.blank? && v != false } : params, options)
|
||||
end
|
||||
|
||||
alias_method :search, :ransack
|
||||
|
||||
def ransack_alias(new_name, old_name)
|
||||
self._ransack_aliases = _ransack_aliases.merge new_name.to_s =>
|
||||
old_name.to_s
|
||||
end
|
||||
|
||||
def ransacker(name, opts = {}, &block)
|
||||
self._ransackers = _ransackers.merge name.to_s => Ransacker
|
||||
.new(self, name, opts, &block)
|
||||
end
|
||||
|
||||
def all_ransackable_attributes
|
||||
['id'] + column_names.select { |c| c != '_id' } + _ransackers.keys +
|
||||
_ransack_aliases.keys
|
||||
end
|
||||
|
||||
def ransackable_attributes(auth_object = nil)
|
||||
all_ransackable_attributes
|
||||
end
|
||||
|
||||
def ransortable_attributes(auth_object = nil)
|
||||
# Here so users can overwrite the attributes
|
||||
# that show up in the sort_select
|
||||
ransackable_attributes(auth_object)
|
||||
end
|
||||
|
||||
def ransackable_associations(auth_object = nil)
|
||||
reflect_on_all_associations_all.map { |a| a.name.to_s }
|
||||
end
|
||||
|
||||
def reflect_on_all_associations_all
|
||||
reflect_on_all_associations(
|
||||
:belongs_to, :has_one, :has_many, :embeds_many, :embedded_in
|
||||
)
|
||||
end
|
||||
|
||||
# For overriding with a whitelist of symbols
|
||||
def ransackable_scopes(auth_object = nil)
|
||||
[]
|
||||
end
|
||||
|
||||
# imitating active record
|
||||
|
||||
def joins_values *args
|
||||
[]
|
||||
end
|
||||
|
||||
def custom_join_ast *args
|
||||
[]
|
||||
end
|
||||
|
||||
def first(*args)
|
||||
if args.size == 0
|
||||
super
|
||||
else
|
||||
self.criteria.limit(args.first)
|
||||
end
|
||||
end
|
||||
|
||||
# def group_by *args, &block
|
||||
# criteria
|
||||
# end
|
||||
|
||||
def columns
|
||||
@columns ||= fields.map(&:second).map{ |c| ColumnWrapper.new(c) }
|
||||
end
|
||||
|
||||
def column_names
|
||||
@column_names ||= fields.map(&:first)
|
||||
end
|
||||
|
||||
def columns_hash
|
||||
columns.index_by(&:name)
|
||||
end
|
||||
|
||||
def table
|
||||
name = ::Ransack::Adapters::Mongoid::Attributes::Attribute.new(
|
||||
self.criteria, :name
|
||||
)
|
||||
{ :name => name }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end # Base
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,212 +0,0 @@
|
|||
require 'ransack/context'
|
||||
require 'polyamorous'
|
||||
|
||||
module Ransack
|
||||
module Adapters
|
||||
module Mongoid
|
||||
class Context < ::Ransack::Context
|
||||
|
||||
def initialize(object, options = {})
|
||||
super
|
||||
# @arel_visitor = @engine.connection.visitor
|
||||
end
|
||||
|
||||
def relation_for(object)
|
||||
object.all
|
||||
end
|
||||
|
||||
def type_for(attr)
|
||||
return nil unless attr && attr.valid?
|
||||
name = attr.arel_attribute.name.to_s.split('.').last
|
||||
# table = attr.arel_attribute.relation.table_name
|
||||
|
||||
# schema_cache = @engine.connection.schema_cache
|
||||
# raise "No table named #{table} exists" unless schema_cache.table_exists?(table)
|
||||
# schema_cache.columns_hash(table)[name].type
|
||||
|
||||
# when :date
|
||||
# when :datetime, :timestamp, :time
|
||||
# when :boolean
|
||||
# when :integer
|
||||
# when :float
|
||||
# when :decimal
|
||||
# else # :string
|
||||
|
||||
name = '_id' if name == 'id'
|
||||
|
||||
t = object.klass.fields[name].try(:type) || bind_pair_for(attr.name).first.fields[name].type
|
||||
|
||||
t.to_s.demodulize.underscore.to_sym
|
||||
end
|
||||
|
||||
def evaluate(search, opts = {})
|
||||
viz = Visitor.new
|
||||
relation = @object.where(viz.accept(search.base))
|
||||
if search.sorts.any?
|
||||
ary_sorting = viz.accept(search.sorts)
|
||||
sorting = {}
|
||||
ary_sorting.each do |s|
|
||||
sorting.merge! Hash[s.map { |k, d| [k.to_s == 'id' ? '_id' : k, d] }]
|
||||
end
|
||||
relation = relation.order_by(sorting)
|
||||
# relation = relation.except(:order)
|
||||
# .reorder(viz.accept(search.sorts))
|
||||
end
|
||||
# -- mongoid has different distinct method
|
||||
# opts[:distinct] ? relation.distinct : relation
|
||||
relation
|
||||
end
|
||||
|
||||
def attribute_method?(str, klass = @klass)
|
||||
exists = false
|
||||
if ransackable_attribute?(str, klass)
|
||||
exists = true
|
||||
elsif (segments = str.split(Constants::UNDERSCORE)).size > 1
|
||||
remainder = []
|
||||
found_assoc = nil
|
||||
while !found_assoc && remainder.unshift(
|
||||
segments.pop) && segments.size > 0 do
|
||||
assoc, poly_class = unpolymorphize_association(
|
||||
segments.join('_')
|
||||
)
|
||||
if found_assoc = get_association(assoc, klass)
|
||||
exists = attribute_method?(remainder.join('_'),
|
||||
poly_class || found_assoc.klass
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
exists
|
||||
end
|
||||
|
||||
def table_for(parent)
|
||||
# parent.table
|
||||
Ransack::Adapters::Mongoid::Table.new(parent)
|
||||
end
|
||||
|
||||
def klassify(obj)
|
||||
if Class === obj && obj.ancestors.include?(::Mongoid::Document)
|
||||
obj
|
||||
elsif obj.respond_to? :klass
|
||||
obj.klass
|
||||
elsif obj.respond_to? :base_klass
|
||||
obj.base_klass
|
||||
else
|
||||
raise ArgumentError, "Don't know how to klassify #{obj}"
|
||||
end
|
||||
end
|
||||
|
||||
def lock_association(association)
|
||||
warn "lock_association is not implemented for Ransack mongoid adapter" if $DEBUG
|
||||
end
|
||||
|
||||
def remove_association(association)
|
||||
warn "remove_association is not implemented for Ransack mongoid adapter" if $DEBUG
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_parent_and_attribute_name(str, parent = @base)
|
||||
attr_name = nil
|
||||
|
||||
if ransackable_attribute?(str, klassify(parent))
|
||||
attr_name = str
|
||||
elsif (segments = str.split(Constants::UNDERSCORE)).size > 1
|
||||
remainder = []
|
||||
found_assoc = nil
|
||||
while remainder.unshift(
|
||||
segments.pop) && segments.size > 0 && !found_assoc do
|
||||
assoc, klass = unpolymorphize_association(segments.join('_'))
|
||||
if found_assoc = get_association(assoc, parent)
|
||||
parent, attr_name = get_parent_and_attribute_name(
|
||||
remainder.join('_'), found_assoc.klass
|
||||
)
|
||||
attr_name = "#{segments.join('_')}.#{attr_name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[parent, attr_name]
|
||||
end
|
||||
|
||||
def get_association(str, parent = @base)
|
||||
klass = klassify parent
|
||||
ransackable_association?(str, klass) &&
|
||||
klass.reflect_on_all_associations_all.detect { |a| a.name.to_s == str }
|
||||
end
|
||||
|
||||
def join_dependency(relation)
|
||||
if relation.respond_to?(:join_dependency) # Polyamorous enables this
|
||||
relation.join_dependency
|
||||
else
|
||||
build_join_dependency(relation)
|
||||
end
|
||||
end
|
||||
|
||||
# Checkout active_record/relation/query_methods.rb +build_joins+ for
|
||||
# reference. Lots of duplicated code maybe we can avoid it
|
||||
def build_join_dependency(relation)
|
||||
buckets = relation.joins_values.group_by do |join|
|
||||
case join
|
||||
when String
|
||||
Constants::STRING_JOIN
|
||||
when Hash, Symbol, Array
|
||||
Constants::ASSOCIATION_JOIN
|
||||
when JoinDependency, JoinDependency::JoinAssociation
|
||||
Constants::STASHED_JOIN
|
||||
when Arel::Nodes::Join
|
||||
Constants::JOIN_NODE
|
||||
else
|
||||
raise 'unknown class: %s' % join.class.name
|
||||
end
|
||||
end
|
||||
|
||||
association_joins = buckets[Constants::ASSOCIATION_JOIN] || []
|
||||
|
||||
stashed_association_joins = buckets[Constants::STASHED_JOIN] || []
|
||||
|
||||
join_nodes = buckets[Constants::JOIN_NODE] || []
|
||||
|
||||
string_joins = (buckets[Constants::STRING_JOIN] || [])
|
||||
.map { |x| x.strip }
|
||||
.uniq
|
||||
|
||||
join_list = relation.send :custom_join_ast,
|
||||
relation.table.from(relation.table), string_joins
|
||||
|
||||
join_dependency = JoinDependency.new(
|
||||
relation.klass, association_joins, join_list
|
||||
)
|
||||
|
||||
join_nodes.each do |join|
|
||||
join_dependency.alias_tracker.aliases[join.left.name.downcase] = 1
|
||||
end
|
||||
|
||||
join_dependency # ActiveRecord::Associations::JoinDependency
|
||||
end
|
||||
|
||||
# ActiveRecord method
|
||||
def build_or_find_association(name, parent = @base, klass = nil)
|
||||
found_association = @join_dependency.join_associations
|
||||
.detect do |assoc|
|
||||
assoc.reflection.name == name &&
|
||||
assoc.parent == parent &&
|
||||
(!klass || assoc.reflection.klass == klass)
|
||||
end
|
||||
unless found_association
|
||||
@join_dependency.send(
|
||||
:build,
|
||||
Polyamorous::Join.new(name, @join_type, klass),
|
||||
parent
|
||||
)
|
||||
found_association = @join_dependency.join_associations.last
|
||||
# Leverage the stashed association functionality in AR
|
||||
@object = @object.joins(found_association)
|
||||
end
|
||||
found_association
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,23 +0,0 @@
|
|||
module Ransack
|
||||
module Adapters
|
||||
module Mongoid
|
||||
class InquiryHash < Hash
|
||||
|
||||
def or(other)
|
||||
{ '$or' => [ self, other] }.to_inquiry
|
||||
end
|
||||
|
||||
def and(other)
|
||||
{ '$and' => [ self, other] }.to_inquiry
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Hash
|
||||
def to_inquiry
|
||||
::Ransack::Adapters::Mongoid::InquiryHash[self]
|
||||
end
|
||||
end
|
|
@ -1,88 +0,0 @@
|
|||
module Ransack
|
||||
module Constants
|
||||
DERIVED_PREDICATES = [
|
||||
[CONT, {
|
||||
:arel_predicate => 'matches',
|
||||
:formatter => proc { |v| "#{escape_regex(v)}" }
|
||||
}
|
||||
],
|
||||
['not_cont', {
|
||||
:arel_predicate => 'does_not_match',
|
||||
:formatter => proc { |v| "#{escape_regex(v)}" }
|
||||
}
|
||||
],
|
||||
['start', {
|
||||
:arel_predicate => 'matches',
|
||||
:formatter => proc { |v| "\\A#{escape_regex(v)}" }
|
||||
}
|
||||
],
|
||||
['not_start', {
|
||||
:arel_predicate => 'does_not_match',
|
||||
:formatter => proc { |v| "\\A#{escape_regex(v)}" }
|
||||
}
|
||||
],
|
||||
['end', {
|
||||
:arel_predicate => 'matches',
|
||||
:formatter => proc { |v| "#{escape_regex(v)}\\Z" }
|
||||
}
|
||||
],
|
||||
['not_end', {
|
||||
:arel_predicate => 'does_not_match',
|
||||
:formatter => proc { |v| "#{escape_regex(v)}\\Z" }
|
||||
}
|
||||
],
|
||||
['true', {
|
||||
:arel_predicate => 'eq',
|
||||
:compounds => false,
|
||||
:type => :boolean,
|
||||
:validator => proc { |v| TRUE_VALUES.include?(v) }
|
||||
}
|
||||
],
|
||||
['false', {
|
||||
:arel_predicate => 'eq',
|
||||
:compounds => false,
|
||||
:type => :boolean,
|
||||
:validator => proc { |v| TRUE_VALUES.include?(v) },
|
||||
:formatter => proc { |v| !v }
|
||||
}
|
||||
],
|
||||
['present', {
|
||||
:arel_predicate => proc { |v| v ? 'not_eq_all' : 'eq_any' },
|
||||
:compounds => false,
|
||||
:type => :boolean,
|
||||
:validator => proc { |v| BOOLEAN_VALUES.include?(v) },
|
||||
:formatter => proc { |v| [nil, ''] }
|
||||
}
|
||||
],
|
||||
['blank', {
|
||||
:arel_predicate => proc { |v| v ? 'eq_any' : 'not_eq_all' },
|
||||
:compounds => false,
|
||||
:type => :boolean,
|
||||
:validator => proc { |v| BOOLEAN_VALUES.include?(v) },
|
||||
:formatter => proc { |v| [nil, ''] }
|
||||
}
|
||||
],
|
||||
['null', {
|
||||
:arel_predicate => proc { |v| v ? 'eq' : 'not_eq' },
|
||||
:compounds => false,
|
||||
:type => :boolean,
|
||||
:validator => proc { |v| BOOLEAN_VALUES.include?(v)},
|
||||
:formatter => proc { |v| nil }
|
||||
}
|
||||
],
|
||||
['not_null', {
|
||||
:arel_predicate => proc { |v| v ? 'not_eq' : 'eq' },
|
||||
:compounds => false,
|
||||
:type => :boolean,
|
||||
:validator => proc { |v| BOOLEAN_VALUES.include?(v) },
|
||||
:formatter => proc { |v| nil } }
|
||||
]
|
||||
]
|
||||
|
||||
module_function
|
||||
# does nothing
|
||||
def escape_regex(unescaped)
|
||||
Regexp.escape(unescaped)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,59 +0,0 @@
|
|||
require 'ransack/visitor'
|
||||
|
||||
module Ransack
|
||||
class Context
|
||||
# attr_reader :arel_visitor
|
||||
|
||||
class << self
|
||||
|
||||
def for_class(klass, options = {})
|
||||
if klass.ancestors.include?(::Mongoid::Document)
|
||||
Adapters::Mongoid::Context.new(klass, options)
|
||||
end
|
||||
end
|
||||
|
||||
def for_object(object, options = {})
|
||||
case object
|
||||
when ActiveRecord::Relation
|
||||
Adapters::ActiveRecord::Context.new(object.klass, options)
|
||||
end
|
||||
end
|
||||
|
||||
end # << self
|
||||
|
||||
def initialize(object, options = {})
|
||||
@object = relation_for(object)
|
||||
@klass = @object.klass
|
||||
# @join_dependency = join_dependency(@object)
|
||||
# @join_type = options[:join_type] || Arel::OuterJoin
|
||||
@search_key = options[:search_key] || Ransack.options[:search_key]
|
||||
|
||||
@base = @object.klass
|
||||
# @engine = @base.arel_engine
|
||||
end
|
||||
|
||||
def bind_pair_for(key)
|
||||
@bind_pairs ||= {}
|
||||
|
||||
@bind_pairs[key] ||= begin
|
||||
parent, attr_name = get_parent_and_attribute_name(key.to_s)
|
||||
[parent, attr_name] if parent && attr_name
|
||||
end
|
||||
end
|
||||
|
||||
def klassify(obj)
|
||||
if Class === obj && ::ActiveRecord::Base > obj
|
||||
obj
|
||||
elsif obj.respond_to? :klass
|
||||
obj.klass
|
||||
elsif obj.respond_to? :active_record # Rails 3
|
||||
obj.active_record
|
||||
elsif obj.respond_to? :base_klass # Rails 4
|
||||
obj.base_klass
|
||||
else
|
||||
raise ArgumentError, "Don't know how to klassify #{obj.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,22 +0,0 @@
|
|||
module Ransack
|
||||
module Nodes
|
||||
class Condition
|
||||
|
||||
def arel_predicate
|
||||
predicates = attributes.map do |attr|
|
||||
attr.attr.send(
|
||||
arel_predicate_for_attribute(attr),
|
||||
formatted_values_for_attribute(attr)
|
||||
)
|
||||
end
|
||||
|
||||
if predicates.size > 1 && combinator == 'and'
|
||||
Arel::Nodes::Grouping.new(Arel::Nodes::And.new(predicates))
|
||||
else
|
||||
predicates.inject(&:or)
|
||||
end
|
||||
end
|
||||
|
||||
end # Condition
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
module Ransack
|
||||
module Translate
|
||||
|
||||
def self.i18n_key(klass)
|
||||
# if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
|
||||
# klass.model_name.i18n_key.to_s.tr('.', '/')
|
||||
# else
|
||||
# klass.model_name.i18n_key.to_s
|
||||
# end
|
||||
klass.model_name.i18n_key.to_s
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,18 +0,0 @@
|
|||
module Ransack
|
||||
class Visitor
|
||||
def visit_and(object)
|
||||
nodes = object.values.map { |o| accept(o) }.compact
|
||||
nodes.inject(&:and)
|
||||
end
|
||||
|
||||
def quoted?(object)
|
||||
case object
|
||||
when Arel::Nodes::SqlLiteral, Bignum, Fixnum
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
module Ransack
|
||||
module Adapters
|
||||
module Mongoid
|
||||
class Table
|
||||
attr_accessor :name
|
||||
|
||||
alias :table_name :name
|
||||
|
||||
def initialize(object, engine = nil)
|
||||
@object = object
|
||||
@name = object.collection.name
|
||||
@engine = engine
|
||||
@columns = nil
|
||||
@aliases = []
|
||||
@table_alias = nil
|
||||
@primary_key = nil
|
||||
|
||||
if Hash === engine
|
||||
# @engine = engine[:engine] || Table.engine
|
||||
|
||||
# Sometime AR sends an :as parameter to table, to let the table know
|
||||
# that it is an Alias. We may want to override new, and return a
|
||||
# TableAlias node?
|
||||
# @table_alias = engine[:as] unless engine[:as].to_s == @name
|
||||
end
|
||||
end
|
||||
|
||||
def [](name)
|
||||
Ransack::Adapters::Mongoid::Attribute.new self, name
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,314 +0,0 @@
|
|||
require 'mongoid_spec_helper'
|
||||
|
||||
module Ransack
|
||||
module Adapters
|
||||
module Mongoid
|
||||
describe Base do
|
||||
|
||||
subject { Person }
|
||||
|
||||
it { should respond_to :ransack }
|
||||
it { should respond_to :search }
|
||||
|
||||
describe '#search' do
|
||||
subject { Person.search }
|
||||
|
||||
it { should be_a Search }
|
||||
it 'has a Mongoid::Criteria as its object' do
|
||||
expect(subject.object).to be_an ::Mongoid::Criteria
|
||||
end
|
||||
|
||||
context 'with scopes' do
|
||||
before do
|
||||
allow(Person)
|
||||
.to receive(:ransackable_scopes)
|
||||
.and_return([:active, :over_age])
|
||||
end
|
||||
|
||||
it "applies true scopes" do
|
||||
search = Person.search('active' => true)
|
||||
expect(search.result.selector).to eq({ 'active' => 1 })
|
||||
end
|
||||
|
||||
it "ignores unlisted scopes" do
|
||||
search = Person.search('restricted' => true)
|
||||
expect(search.result.selector).to_not eq({ 'restricted' => 1})
|
||||
end
|
||||
|
||||
it "ignores false scopes" do
|
||||
search = Person.search('active' => false)
|
||||
expect(search.result.selector).to_not eq({ 'active' => 1 })
|
||||
end
|
||||
|
||||
it "passes values to scopes" do
|
||||
search = Person.search('over_age' => 18)
|
||||
expect(search.result.selector).to eq({ 'age' => { '$gt' => 18 } })
|
||||
end
|
||||
|
||||
it "chains scopes" do
|
||||
search = Person.search('over_age' => 18, 'active' => true)
|
||||
expect(search.result.selector).to eq({ 'age' => { '$gt' => 18 }, 'active' => 1 })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ransack_alias' do
|
||||
it 'translates an alias to the correct attributes' do
|
||||
p = Person.create!(name: 'Meatloaf', email: 'babies@example.com')
|
||||
|
||||
s = Person.ransack(term_cont: 'atlo')
|
||||
expect(s.result.to_a).to eq [p]
|
||||
|
||||
s = Person.ransack(term_cont: 'babi')
|
||||
expect(s.result.to_a).to eq [p]
|
||||
|
||||
s = Person.ransack(term_cont: 'nomatch')
|
||||
expect(s.result.to_a).to eq []
|
||||
end
|
||||
|
||||
it 'makes aliases available to subclasses' do
|
||||
yngwie = Musician.create!(name: 'Yngwie Malmsteen')
|
||||
|
||||
musicians = Musician.ransack(term_cont: 'ngw').result
|
||||
expect(musicians).to eq([yngwie])
|
||||
end
|
||||
|
||||
it 'handles naming collisions gracefully' do
|
||||
frank = Person.create!(name: 'Frank Stallone')
|
||||
|
||||
people = Person.ransack(term_cont: 'allon').result
|
||||
expect(people).to eq([frank])
|
||||
|
||||
Class.new(Article) do
|
||||
ransack_alias :term, :title
|
||||
end
|
||||
|
||||
people = Person.ransack(term_cont: 'allon').result
|
||||
expect(people).to eq([frank])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ransacker' do
|
||||
# For infix tests
|
||||
def self.sane_adapter?
|
||||
case ::Mongoid::Document.connection.adapter_name
|
||||
when "SQLite3", "PostgreSQL"
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
# # in schema.rb, class Person:
|
||||
# ransacker :reversed_name, formatter: proc { |v| v.reverse } do |parent|
|
||||
# parent.table[:name]
|
||||
# end
|
||||
|
||||
# ransacker :doubled_name do |parent|
|
||||
# Arel::Nodes::InfixOperation.new(
|
||||
# '||', parent.table[:name], parent.table[:name]
|
||||
# )
|
||||
# end
|
||||
|
||||
it 'creates ransack attributes' do
|
||||
s = Person.search(:reversed_name_eq => 'htimS cirA')
|
||||
expect(s.result.size).to eq(Person.where(name: 'Aric Smith').count)
|
||||
|
||||
expect(s.result.first).to eq Person.where(name: 'Aric Smith').first
|
||||
end
|
||||
|
||||
context 'with joins' do
|
||||
before { pending 'not implemented for mongoid' }
|
||||
|
||||
it 'can be accessed through associations' do
|
||||
s = Person.search(:children_reversed_name_eq => 'htimS cirA')
|
||||
expect(s.result.to_sql).to match(
|
||||
/#{quote_table_name("children_people")}.#{
|
||||
quote_column_name("name")} = 'Aric Smith'/
|
||||
)
|
||||
end
|
||||
|
||||
it "should keep proper key value pairs in the params hash" do
|
||||
s = Person.search(:children_reversed_name_eq => 'Testing')
|
||||
expect(s.result.to_sql).to match /LEFT OUTER JOIN/
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it 'allows an "attribute" to be an InfixOperation' do
|
||||
s = Person.search(:doubled_name_eq => 'Aric SmithAric Smith')
|
||||
expect(s.result.first).to eq Person.where(name: 'Aric Smith').first
|
||||
end if defined?(Arel::Nodes::InfixOperation) && sane_adapter?
|
||||
|
||||
it "doesn't break #count if using InfixOperations" do
|
||||
s = Person.search(:doubled_name_eq => 'Aric SmithAric Smith')
|
||||
expect(s.result.count).to eq 1
|
||||
end if defined?(Arel::Nodes::InfixOperation) && sane_adapter?
|
||||
|
||||
it "should remove empty key value pairs from the params hash" do
|
||||
s = Person.search(:reversed_name_eq => '')
|
||||
expect(s.result.selector).to eq({})
|
||||
end
|
||||
|
||||
it "should function correctly when nil is passed in" do
|
||||
s = Person.search(nil)
|
||||
end
|
||||
|
||||
it "should function correctly when a blank string is passed in" do
|
||||
s = Person.search('')
|
||||
end
|
||||
|
||||
it "should function correctly when using fields with dots in them" do
|
||||
s = Person.search(:email_cont => "example.com")
|
||||
expect(s.result.exists?).to be true
|
||||
|
||||
s = Person.search(:email_cont => "example.co.")
|
||||
expect(s.result.exists?).not_to be true
|
||||
end
|
||||
|
||||
it "should function correctly when using fields with % in them" do
|
||||
Person.create!(:name => "110%-er")
|
||||
s = Person.search(:name_cont => "10%")
|
||||
expect(s.result.exists?).to be true
|
||||
end
|
||||
|
||||
it "should function correctly when using fields with backslashes in them" do
|
||||
Person.create!(:name => "\\WINNER\\")
|
||||
s = Person.search(:name_cont => "\\WINNER\\")
|
||||
expect(s.result.exists?).to be true
|
||||
end
|
||||
|
||||
it 'allows sort by "only_sort" field' do
|
||||
pending "it doesn't work :("
|
||||
s = Person.search(
|
||||
"s" => { "0" => { "dir" => "desc", "name" => "only_sort" } }
|
||||
)
|
||||
expect(s.result.to_sql).to match(
|
||||
/ORDER BY #{quote_table_name("people")}.#{
|
||||
quote_column_name("only_sort")} ASC/
|
||||
)
|
||||
end
|
||||
|
||||
it "doesn't sort by 'only_search' field" do
|
||||
pending "it doesn't work :("
|
||||
s = Person.search(
|
||||
"s" => { "0" => { "dir" => "asc", "name" => "only_search" } }
|
||||
)
|
||||
expect(s.result.to_sql).not_to match(
|
||||
/ORDER BY #{quote_table_name("people")}.#{
|
||||
quote_column_name("only_search")} ASC/
|
||||
)
|
||||
end
|
||||
|
||||
it 'allows search by "only_search" field' do
|
||||
s = Person.search(:only_search_eq => 'htimS cirA')
|
||||
expect(s.result.selector).to eq({'only_search' => 'htimS cirA'})
|
||||
end
|
||||
|
||||
it "can't be searched by 'only_sort'" do
|
||||
s = Person.search(:only_sort_eq => 'htimS cirA')
|
||||
expect(s.result.selector).not_to eq({'only_sort' => 'htimS cirA'})
|
||||
end
|
||||
|
||||
it 'allows sort by "only_admin" field, if auth_object: :admin' do
|
||||
s = Person.search(
|
||||
{ "s" => { "0" => { "dir" => "asc", "name" => "only_admin" } } },
|
||||
{ auth_object: :admin }
|
||||
)
|
||||
expect(s.result.options).to eq({ sort: { '_id' => -1, 'only_admin' => 1 } })
|
||||
end
|
||||
|
||||
it "doesn't sort by 'only_admin' field, if auth_object: nil" do
|
||||
s = Person.search(
|
||||
"s" => { "0" => { "dir" => "asc", "name" => "only_admin" } }
|
||||
)
|
||||
expect(s.result.options).to eq({ sort: {'_id' => -1}})
|
||||
end
|
||||
|
||||
it 'allows search by "only_admin" field, if auth_object: :admin' do
|
||||
s = Person.search(
|
||||
{ :only_admin_eq => 'htimS cirA' },
|
||||
{ :auth_object => :admin }
|
||||
)
|
||||
expect(s.result.selector).to eq({ 'only_admin' => 'htimS cirA' })
|
||||
end
|
||||
|
||||
it "can't be searched by 'only_admin', if auth_object: nil" do
|
||||
s = Person.search(:only_admin_eq => 'htimS cirA')
|
||||
expect(s.result.selector).to eq({})
|
||||
end
|
||||
|
||||
it 'searches by id' do
|
||||
ids = ['some_bson_id', 'another_bson_id']
|
||||
s = Person.search(:id_in => ids)
|
||||
expect(s.result.selector).to eq({ '_id' => { '$in' => ids } })
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ransackable_attributes' do
|
||||
context 'when auth_object is nil' do
|
||||
subject { Person.ransackable_attributes }
|
||||
|
||||
it { should include 'name' }
|
||||
it { should include 'reversed_name' }
|
||||
it { should include 'doubled_name' }
|
||||
it { should include 'term' }
|
||||
it { should include 'only_search' }
|
||||
it { should_not include 'only_sort' }
|
||||
it { should_not include 'only_admin' }
|
||||
end
|
||||
|
||||
context 'with auth_object :admin' do
|
||||
subject { Person.ransackable_attributes(:admin) }
|
||||
|
||||
it { should include 'name' }
|
||||
it { should include 'reversed_name' }
|
||||
it { should include 'doubled_name' }
|
||||
it { should include 'term' }
|
||||
it { should include 'only_search' }
|
||||
it { should_not include 'only_sort' }
|
||||
it { should include 'only_admin' }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ransortable_attributes' do
|
||||
context 'when auth_object is nil' do
|
||||
subject { Person.ransortable_attributes }
|
||||
|
||||
it { should include 'name' }
|
||||
it { should include 'reversed_name' }
|
||||
it { should include 'doubled_name' }
|
||||
it { should include 'only_sort' }
|
||||
it { should_not include 'only_search' }
|
||||
it { should_not include 'only_admin' }
|
||||
end
|
||||
|
||||
context 'with auth_object :admin' do
|
||||
subject { Person.ransortable_attributes(:admin) }
|
||||
|
||||
it { should include 'name' }
|
||||
it { should include 'reversed_name' }
|
||||
it { should include 'doubled_name' }
|
||||
it { should include 'only_sort' }
|
||||
it { should_not include 'only_search' }
|
||||
it { should include 'only_admin' }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ransackable_associations' do
|
||||
subject { Person.ransackable_associations }
|
||||
|
||||
it { should include 'parent' }
|
||||
it { should include 'children' }
|
||||
it { should include 'articles' }
|
||||
end
|
||||
|
||||
describe '#ransackable_scopes' do
|
||||
subject { Person.ransackable_scopes }
|
||||
|
||||
it { should eq [] }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,56 +0,0 @@
|
|||
require 'mongoid_spec_helper'
|
||||
|
||||
module Ransack
|
||||
module Adapters
|
||||
module Mongoid
|
||||
describe Context do
|
||||
subject { Context.new(Person) }
|
||||
|
||||
describe '#relation_for' do
|
||||
before { pending "not implemented for mongoid" }
|
||||
it 'returns relation for given object' do
|
||||
expect(subject.object).to be_an ::ActiveRecord::Relation
|
||||
end
|
||||
end
|
||||
|
||||
describe '#evaluate' do
|
||||
it 'evaluates search objects' do
|
||||
search = Search.new(Person, :name_eq => 'Joe Blow')
|
||||
result = subject.evaluate(search)
|
||||
|
||||
expect(result).to be_an ::Mongoid::Criteria
|
||||
expect(result.selector).to eq({ 'name' => 'Joe Blow' })
|
||||
end
|
||||
|
||||
it 'SELECTs DISTINCT when distinct: true' do
|
||||
pending "distinct doesn't work"
|
||||
|
||||
search = Search.new(Person, :name_eq => 'Joe Blow')
|
||||
result = subject.evaluate(search, :distinct => true)
|
||||
|
||||
expect(result).to be_an ::ActiveRecord::Relation
|
||||
expect(result.to_sql).to match /SELECT DISTINCT/
|
||||
end
|
||||
end
|
||||
|
||||
it 'contextualizes strings to attributes' do
|
||||
attribute = subject.contextualize 'name'
|
||||
expect(attribute).to be_a ::Ransack::Adapters::Mongoid::Attributes::Attribute
|
||||
expect(attribute.name.to_s).to eq 'name'
|
||||
# expect(attribute.relation.table_alias).to eq 'parents_people'
|
||||
end
|
||||
|
||||
it 'builds new associations if not yet built' do
|
||||
pending "not implemented for mongoid"
|
||||
|
||||
attribute = subject.contextualize 'children_articles_title'
|
||||
expect(attribute).to be_a Arel::Attributes::Attribute
|
||||
expect(attribute.name.to_s).to eq 'title'
|
||||
expect(attribute.relation.name).to eq 'articles'
|
||||
expect(attribute.relation.table_alias).to be_nil
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,162 +0,0 @@
|
|||
require 'mongoid_spec_helper'
|
||||
|
||||
module Ransack
|
||||
describe Configuration do
|
||||
it 'yields Ransack on configure' do
|
||||
Ransack.configure { |config| expect(config).to eq Ransack }
|
||||
end
|
||||
|
||||
it 'adds predicates' do
|
||||
Ransack.configure do |config|
|
||||
config.add_predicate :test_predicate
|
||||
end
|
||||
|
||||
expect(Ransack.predicates).to have_key 'test_predicate'
|
||||
expect(Ransack.predicates).to have_key 'test_predicate_any'
|
||||
expect(Ransack.predicates).to have_key 'test_predicate_all'
|
||||
end
|
||||
|
||||
it 'avoids creating compound predicates if compounds: false' do
|
||||
Ransack.configure do |config|
|
||||
config.add_predicate(
|
||||
:test_predicate_without_compound,
|
||||
:compounds => false
|
||||
)
|
||||
end
|
||||
expect(Ransack.predicates)
|
||||
.to have_key 'test_predicate_without_compound'
|
||||
expect(Ransack.predicates)
|
||||
.not_to have_key 'test_predicate_without_compound_any'
|
||||
expect(Ransack.predicates)
|
||||
.not_to have_key 'test_predicate_without_compound_all'
|
||||
end
|
||||
|
||||
it 'should have default value for search key' do
|
||||
expect(Ransack.options[:search_key]).to eq :q
|
||||
end
|
||||
|
||||
it 'changes default search key parameter' do
|
||||
default = Ransack.options.clone
|
||||
|
||||
Ransack.configure { |c| c.search_key = :query }
|
||||
|
||||
expect(Ransack.options[:search_key]).to eq :query
|
||||
|
||||
Ransack.options = default
|
||||
end
|
||||
|
||||
it 'should have default values for arrows' do
|
||||
expect(Ransack.options[:up_arrow]).to eq '▼'
|
||||
expect(Ransack.options[:down_arrow]).to eq '▲'
|
||||
end
|
||||
|
||||
it 'changes the default value for the up arrow only' do
|
||||
default, new_up_arrow = Ransack.options.clone, 'U+02191'
|
||||
|
||||
Ransack.configure { |c| c.custom_arrows = { up_arrow: new_up_arrow } }
|
||||
|
||||
expect(Ransack.options[:down_arrow]).to eq default[:down_arrow]
|
||||
expect(Ransack.options[:up_arrow]).to eq new_up_arrow
|
||||
|
||||
Ransack.options = default
|
||||
end
|
||||
|
||||
it 'changes the default value for the down arrow only' do
|
||||
default, new_down_arrow = Ransack.options.clone, '<i class="down"></i>'
|
||||
|
||||
Ransack.configure { |c| c.custom_arrows = { down_arrow: new_down_arrow } }
|
||||
|
||||
expect(Ransack.options[:up_arrow]).to eq default[:up_arrow]
|
||||
expect(Ransack.options[:down_arrow]).to eq new_down_arrow
|
||||
|
||||
Ransack.options = default
|
||||
end
|
||||
|
||||
it 'changes the default value for both arrows' do
|
||||
default = Ransack.options.clone
|
||||
new_up_arrow = '<i class="fa fa-long-arrow-up"></i>'
|
||||
new_down_arrow = 'U+02193'
|
||||
|
||||
Ransack.configure do |c|
|
||||
c.custom_arrows = { up_arrow: new_up_arrow, down_arrow: new_down_arrow }
|
||||
end
|
||||
|
||||
expect(Ransack.options[:up_arrow]).to eq new_up_arrow
|
||||
expect(Ransack.options[:down_arrow]).to eq new_down_arrow
|
||||
|
||||
Ransack.options = default
|
||||
end
|
||||
|
||||
it 'consecutive arrow customizations respect previous customizations' do
|
||||
default = Ransack.options.clone
|
||||
|
||||
Ransack.configure { |c| c.custom_arrows = { up_arrow: 'up' } }
|
||||
expect(Ransack.options[:down_arrow]).to eq default[:down_arrow]
|
||||
|
||||
Ransack.configure { |c| c.custom_arrows = { down_arrow: 'DOWN' } }
|
||||
expect(Ransack.options[:up_arrow]).to eq 'up'
|
||||
|
||||
Ransack.configure { |c| c.custom_arrows = { up_arrow: '<i>U-Arrow</i>' } }
|
||||
expect(Ransack.options[:down_arrow]).to eq 'DOWN'
|
||||
|
||||
Ransack.configure { |c| c.custom_arrows = { down_arrow: 'down arrow-2' } }
|
||||
expect(Ransack.options[:up_arrow]).to eq '<i>U-Arrow</i>'
|
||||
|
||||
Ransack.options = default
|
||||
end
|
||||
|
||||
it 'adds predicates that take arrays, overriding compounds' do
|
||||
Ransack.configure do |config|
|
||||
config.add_predicate(
|
||||
:test_array_predicate,
|
||||
:wants_array => true,
|
||||
:compounds => true
|
||||
)
|
||||
end
|
||||
|
||||
expect(Ransack.predicates['test_array_predicate'].wants_array).to eq true
|
||||
expect(Ransack.predicates).not_to have_key 'test_array_predicate_any'
|
||||
expect(Ransack.predicates).not_to have_key 'test_array_predicate_all'
|
||||
end
|
||||
|
||||
describe '`wants_array` option takes precedence over Arel predicate' do
|
||||
it 'implicitly wants an array for in/not in predicates' do
|
||||
Ransack.configure do |config|
|
||||
config.add_predicate(
|
||||
:test_in_predicate,
|
||||
:arel_predicate => 'in'
|
||||
)
|
||||
config.add_predicate(
|
||||
:test_not_in_predicate,
|
||||
:arel_predicate => 'not_in'
|
||||
)
|
||||
end
|
||||
|
||||
expect(Ransack.predicates['test_in_predicate'].wants_array)
|
||||
.to eq true
|
||||
expect(Ransack.predicates['test_not_in_predicate'].wants_array)
|
||||
.to eq true
|
||||
end
|
||||
|
||||
it 'explicitly does not want array for in/not_in predicates' do
|
||||
Ransack.configure do |config|
|
||||
config.add_predicate(
|
||||
:test_in_predicate_no_array,
|
||||
:arel_predicate => 'in',
|
||||
:wants_array => false
|
||||
)
|
||||
config.add_predicate(
|
||||
:test_not_in_predicate_no_array,
|
||||
:arel_predicate => 'not_in',
|
||||
:wants_array => false
|
||||
)
|
||||
end
|
||||
|
||||
expect(Ransack.predicates['test_in_predicate_no_array'].wants_array)
|
||||
.to eq false
|
||||
expect(Ransack.predicates['test_not_in_predicate_no_array'].wants_array)
|
||||
.to eq false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,8 +0,0 @@
|
|||
unless ::ActiveSupport::VERSION::STRING >= '4'
|
||||
describe 'Ransack' do
|
||||
it 'can be required without errors' do
|
||||
output = `bundle exec ruby -e "require 'ransack'" 2>&1`
|
||||
expect(output).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
module RansackHelper
|
||||
def quote_table_name(table)
|
||||
# ActiveRecord::Base.connection.quote_table_name(table)
|
||||
table
|
||||
end
|
||||
|
||||
def quote_column_name(column)
|
||||
# ActiveRecord::Base.connection.quote_column_name(column)
|
||||
column
|
||||
end
|
||||
end
|
|
@ -1,49 +0,0 @@
|
|||
require 'mongoid_spec_helper'
|
||||
|
||||
module Ransack
|
||||
module Nodes
|
||||
describe Condition do
|
||||
|
||||
context 'with an alias' do
|
||||
subject {
|
||||
Condition.extract(
|
||||
Context.for(Person), 'term_start', Person.first(2).map(&:name)
|
||||
)
|
||||
}
|
||||
|
||||
specify { expect(subject.combinator).to eq 'or' }
|
||||
specify { expect(subject.predicate.name).to eq 'start' }
|
||||
|
||||
it 'converts the alias to the correct attributes' do
|
||||
expect(subject.attributes.map(&:name)).to eq(['name', 'email'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with multiple values and an _any predicate' do
|
||||
subject { Condition.extract(Context.for(Person), 'name_eq_any', Person.first(2).map(&:name)) }
|
||||
|
||||
specify { expect(subject.values.size).to eq(2) }
|
||||
end
|
||||
|
||||
context 'with an invalid predicate' do
|
||||
subject { Condition.extract(Context.for(Person), 'name_invalid', Person.first.name) }
|
||||
|
||||
context "when ignore_unknown_conditions is false" do
|
||||
before do
|
||||
Ransack.configure { |config| config.ignore_unknown_conditions = false }
|
||||
end
|
||||
|
||||
specify { expect { subject }.to raise_error ArgumentError }
|
||||
end
|
||||
|
||||
context "when ignore_unknown_conditions is true" do
|
||||
before do
|
||||
Ransack.configure { |config| config.ignore_unknown_conditions = true }
|
||||
end
|
||||
|
||||
specify { expect(subject).to be_nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
require 'mongoid_spec_helper'
|
||||
|
||||
module Ransack
|
||||
module Nodes
|
||||
describe Grouping do
|
||||
before do
|
||||
@g = 1
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,155 +0,0 @@
|
|||
require 'mongoid_spec_helper'
|
||||
|
||||
module Ransack
|
||||
describe Predicate do
|
||||
|
||||
before do
|
||||
@s = Search.new(Person)
|
||||
end
|
||||
|
||||
shared_examples 'wildcard escaping' do |method, value|
|
||||
it 'automatically converts integers to strings' do
|
||||
subject.parent_id_cont = 1
|
||||
expect { subject.result }.to_not raise_error
|
||||
end
|
||||
|
||||
it "escapes '%', '.' and '\\\\' in value" do
|
||||
subject.send(:"#{method}=", '%._\\')
|
||||
expect(subject.result.selector).to eq(value)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'eq' do
|
||||
it 'generates an equality condition for boolean true' do
|
||||
@s.awesome_eq = true
|
||||
expect(@s.result.selector).to eq({ "awesome" => true })
|
||||
end
|
||||
|
||||
it 'generates an equality condition for boolean false' do
|
||||
@s.awesome_eq = false
|
||||
expect(@s.result.selector).to eq({ "awesome" => false })
|
||||
end
|
||||
|
||||
it 'does not generate a condition for nil' do
|
||||
@s.awesome_eq = nil
|
||||
expect(@s.result.selector).to eq({ })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'cont' do
|
||||
it_has_behavior 'wildcard escaping', :name_cont, { 'name' => /%\._\\/i } do
|
||||
subject { @s }
|
||||
end
|
||||
|
||||
it 'generates a regex query' do
|
||||
@s.name_cont = 'ric'
|
||||
expect(@s.result.selector).to eq({ 'name' => /ric/i })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'not_cont' do
|
||||
it_has_behavior 'wildcard escaping', :name_not_cont, { "$not" => { 'name' => /%\._\\/i } } do
|
||||
subject { @s }
|
||||
end
|
||||
|
||||
it 'generates a regex query' do
|
||||
@s.name_not_cont = 'ric'
|
||||
expect(@s.result.selector).to eq({ "$not" => { 'name' => /ric/i } })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'null' do
|
||||
it 'generates a value IS NULL query' do
|
||||
@s.name_null = true
|
||||
expect(@s.result.selector).to eq({ 'name' => nil })
|
||||
end
|
||||
|
||||
it 'generates a value IS NOT NULL query when assigned false' do
|
||||
@s.name_null = false
|
||||
expect(@s.result.selector).to eq( { 'name' => { '$ne' => nil } })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'not_null' do
|
||||
it 'generates a value IS NOT NULL query' do
|
||||
@s.name_not_null = true
|
||||
expect(@s.result.selector).to eq({ 'name' => { '$ne' => nil } })
|
||||
end
|
||||
|
||||
it 'generates a value IS NULL query when assigned false' do
|
||||
@s.name_not_null = false
|
||||
expect(@s.result.selector).to eq({ 'name' => nil })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'present' do
|
||||
it %q[generates a value IS NOT NULL AND value != '' query] do
|
||||
@s.name_present = true
|
||||
expect(@s.result.selector).to eq({ '$and' => [ { 'name' => { '$ne' => nil } }, { 'name' => { '$ne' => '' } } ] })
|
||||
end
|
||||
|
||||
it %q[generates a value IS NULL OR value = '' query when assigned false] do
|
||||
@s.name_present = false
|
||||
expect(@s.result.selector).to eq({ '$or' => [ { 'name' => nil }, { 'name' => '' } ] })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'blank' do
|
||||
it %q[generates a value IS NULL OR value = '' query] do
|
||||
@s.name_blank = true
|
||||
expect(@s.result.selector).to eq({ '$or' => [ { 'name' => nil}, { 'name' => '' } ] })
|
||||
end
|
||||
|
||||
it %q[generates a value IS NOT NULL AND value != '' query when assigned false] do
|
||||
@s.name_blank = false
|
||||
expect(@s.result.selector).to eq({ '$and' => [ { 'name' => { '$ne' => nil}}, { 'name' => { '$ne' => '' }} ] })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'gt' do
|
||||
it 'generates an greater than for time' do
|
||||
time = Time.now
|
||||
@s.created_at_gt = time
|
||||
expect(@s.result.selector).to eq({ "created_at" => { '$gt' => time } })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'lt' do
|
||||
it 'generates an greater than for time' do
|
||||
time = Time.now
|
||||
@s.created_at_lt = time
|
||||
expect(@s.result.selector).to eq({ "created_at" => { '$lt' => time } })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'gteq' do
|
||||
it 'generates an greater than for time' do
|
||||
time = Time.now
|
||||
@s.created_at_gteq = time
|
||||
expect(@s.result.selector).to eq({ "created_at" => { '$gte' => time } })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'lteq' do
|
||||
it 'generates an greater than for time' do
|
||||
time = Time.now
|
||||
@s.created_at_lteq = time
|
||||
expect(@s.result.selector).to eq({ "created_at" => { '$lte' => time } })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'starts_with' do
|
||||
it 'generates an starts_with' do
|
||||
@s.name_start = 'ric'
|
||||
expect(@s.result.selector).to eq({ "name" => /\Aric/i })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'ends_with' do
|
||||
it 'generates an ends_with' do
|
||||
@s.name_end = 'ric'
|
||||
expect(@s.result.selector).to eq({ "name" => /ric\Z/i })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,445 +0,0 @@
|
|||
require 'mongoid_spec_helper'
|
||||
|
||||
module Ransack
|
||||
describe Search do
|
||||
describe '#initialize' do
|
||||
it "removes empty conditions before building" do
|
||||
expect_any_instance_of(Search).to receive(:build).with({})
|
||||
Search.new(Person, :name_eq => '')
|
||||
end
|
||||
|
||||
it "keeps conditions with a false value before building" do
|
||||
expect_any_instance_of(Search).to receive(:build).with({"name_eq" => false})
|
||||
Search.new(Person, :name_eq => false)
|
||||
end
|
||||
|
||||
it "keeps conditions with a value before building" do
|
||||
expect_any_instance_of(Search).to receive(:build).with({"name_eq" => 'foobar'})
|
||||
Search.new(Person, :name_eq => 'foobar')
|
||||
end
|
||||
|
||||
it "removes empty suffixed conditions before building" do
|
||||
expect_any_instance_of(Search).to receive(:build).with({})
|
||||
Search.new(Person, :name_eq_any => [''])
|
||||
end
|
||||
|
||||
it "keeps suffixed conditions with a false value before building" do
|
||||
expect_any_instance_of(Search).to receive(:build).with({"name_eq_any" => [false]})
|
||||
Search.new(Person, :name_eq_any => [false])
|
||||
end
|
||||
|
||||
it "keeps suffixed conditions with a value before building" do
|
||||
expect_any_instance_of(Search).to receive(:build).with({"name_eq_any" => ['foobar']})
|
||||
Search.new(Person, :name_eq_any => ['foobar'])
|
||||
end
|
||||
|
||||
it 'does not raise exception for string :params argument' do
|
||||
expect { Search.new(Person, '') }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe '#build' do
|
||||
it 'creates conditions for top-level attributes' do
|
||||
search = Search.new(Person, :name_eq => 'Ernie')
|
||||
condition = search.base[:name_eq]
|
||||
expect(condition).to be_a Nodes::Condition
|
||||
expect(condition.predicate.name).to eq 'eq'
|
||||
expect(condition.attributes.first.name).to eq 'name'
|
||||
expect(condition.value).to eq 'Ernie'
|
||||
end
|
||||
|
||||
context 'joins' do
|
||||
before { pending 'not implemented for mongoid' }
|
||||
|
||||
it 'creates conditions for association attributes' do
|
||||
search = Search.new(Person, :children_name_eq => 'Ernie')
|
||||
condition = search.base[:children_name_eq]
|
||||
expect(condition).to be_a Nodes::Condition
|
||||
expect(condition.predicate.name).to eq 'eq'
|
||||
expect(condition.attributes.first.name).to eq 'children_name'
|
||||
expect(condition.value).to eq 'Ernie'
|
||||
end
|
||||
|
||||
it 'creates conditions for polymorphic belongs_to association attributes' do
|
||||
search = Search.new(Note, :notable_of_Person_type_name_eq => 'Ernie')
|
||||
condition = search.base[:notable_of_Person_type_name_eq]
|
||||
expect(condition).to be_a Nodes::Condition
|
||||
expect(condition.predicate.name).to eq 'eq'
|
||||
expect(condition.attributes.first.name).to eq 'notable_of_Person_type_name'
|
||||
expect(condition.value).to eq 'Ernie'
|
||||
end
|
||||
|
||||
it 'creates conditions for multiple polymorphic belongs_to association attributes' do
|
||||
search = Search.new(Note,
|
||||
:notable_of_Person_type_name_or_notable_of_Article_type_title_eq => 'Ernie')
|
||||
condition = search.
|
||||
base[:notable_of_Person_type_name_or_notable_of_Article_type_title_eq]
|
||||
expect(condition).to be_a Nodes::Condition
|
||||
expect(condition.predicate.name).to eq 'eq'
|
||||
expect(condition.attributes.first.name).to eq 'notable_of_Person_type_name'
|
||||
expect(condition.attributes.last.name).to eq 'notable_of_Article_type_title'
|
||||
expect(condition.value).to eq 'Ernie'
|
||||
end
|
||||
before { skip }
|
||||
it 'accepts arrays of groupings with joins' do
|
||||
search = Search.new(Person,
|
||||
g: [
|
||||
{ :m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie' },
|
||||
{ :m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert' },
|
||||
]
|
||||
)
|
||||
ors = search.groupings
|
||||
expect(ors.size).to eq(2)
|
||||
or1, or2 = ors
|
||||
expect(or1).to be_a Nodes::Grouping
|
||||
expect(or1.combinator).to eq 'or'
|
||||
expect(or2).to be_a Nodes::Grouping
|
||||
expect(or2.combinator).to eq 'or'
|
||||
end
|
||||
|
||||
it 'accepts "attributes" hashes for groupings' do
|
||||
search = Search.new(Person,
|
||||
g: {
|
||||
'0' => { m: 'or', name_eq: 'Ernie', children_name_eq: 'Ernie' },
|
||||
'1' => { m: 'or', name_eq: 'Bert', children_name_eq: 'Bert' },
|
||||
}
|
||||
)
|
||||
ors = search.groupings
|
||||
expect(ors.size).to eq(2)
|
||||
or1, or2 = ors
|
||||
expect(or1).to be_a Nodes::Grouping
|
||||
expect(or1.combinator).to eq 'or'
|
||||
expect(or2).to be_a Nodes::Grouping
|
||||
expect(or2.combinator).to eq 'or'
|
||||
end
|
||||
|
||||
it 'accepts "attributes" hashes for conditions' do
|
||||
search = Search.new(Person,
|
||||
:c => {
|
||||
'0' => { :a => ['name'], :p => 'eq', :v => ['Ernie'] },
|
||||
'1' => { :a => ['children_name', 'parent_name'],
|
||||
:p => 'eq', :v => ['Ernie'], :m => 'or' }
|
||||
}
|
||||
)
|
||||
conditions = search.base.conditions
|
||||
expect(conditions.size).to eq(2)
|
||||
expect(conditions.map { |c| c.class })
|
||||
.to eq [Nodes::Condition, Nodes::Condition]
|
||||
end
|
||||
|
||||
before { skip }
|
||||
it 'does not evaluate the query on #inspect' do
|
||||
search = Search.new(Person, :children_id_in => [1, 2, 3])
|
||||
expect(search.inspect).not_to match /ActiveRecord/
|
||||
end
|
||||
end
|
||||
|
||||
it 'discards empty conditions' do
|
||||
search = Search.new(Person, :children_name_eq => '')
|
||||
condition = search.base[:children_name_eq]
|
||||
expect(condition).to be_nil
|
||||
end
|
||||
|
||||
it 'accepts arrays of groupings' do
|
||||
search = Search.new(Person,
|
||||
g: [
|
||||
{ :m => 'or', :name_eq => 'Ernie', :email_eq => 'ernie@example.org' },
|
||||
{ :m => 'or', :name_eq => 'Bert', :email_eq => 'bert@example.org' },
|
||||
]
|
||||
)
|
||||
ors = search.groupings
|
||||
expect(ors.size).to eq(2)
|
||||
or1, or2 = ors
|
||||
expect(or1).to be_a Nodes::Grouping
|
||||
expect(or1.combinator).to eq 'or'
|
||||
expect(or2).to be_a Nodes::Grouping
|
||||
expect(or2.combinator).to eq 'or'
|
||||
end
|
||||
|
||||
it 'creates conditions for custom predicates that take arrays' do
|
||||
Ransack.configure do |config|
|
||||
config.add_predicate 'ary_pred', :wants_array => true
|
||||
end
|
||||
|
||||
search = Search.new(Person, :name_ary_pred => ['Ernie', 'Bert'])
|
||||
condition = search.base[:name_ary_pred]
|
||||
expect(condition).to be_a Nodes::Condition
|
||||
expect(condition.predicate.name).to eq 'ary_pred'
|
||||
expect(condition.attributes.first.name).to eq 'name'
|
||||
expect(condition.value).to eq ['Ernie', 'Bert']
|
||||
end
|
||||
|
||||
context 'with an invalid condition' do
|
||||
subject { Search.new(Person, :unknown_attr_eq => 'Ernie') }
|
||||
|
||||
context "when ignore_unknown_conditions is false" do
|
||||
before do
|
||||
Ransack.configure { |c| c.ignore_unknown_conditions = false }
|
||||
end
|
||||
|
||||
specify { expect { subject }.to raise_error ArgumentError }
|
||||
end
|
||||
|
||||
context "when ignore_unknown_conditions is true" do
|
||||
before do
|
||||
Ransack.configure { |c| c.ignore_unknown_conditions = true }
|
||||
end
|
||||
|
||||
specify { expect { subject }.not_to raise_error }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#result' do
|
||||
let(:people_name_field) {
|
||||
"#{quote_table_name("people")}.#{quote_column_name("name")}"
|
||||
}
|
||||
# let(:children_people_name_field) {
|
||||
# "#{quote_table_name("children_people")}.#{quote_column_name("name")}"
|
||||
# }
|
||||
|
||||
it 'evaluates arrays of groupings' do
|
||||
search = Search.new(Person,
|
||||
:g => [
|
||||
{ :m => 'or', :name_eq => 'Ernie', :email_eq => 'ernie@example.org' },
|
||||
{ :m => 'or', :name_eq => 'Bert', :email_eq => 'bert@example.org' }
|
||||
]
|
||||
)
|
||||
expect(search.result).to be_an Mongoid::Criteria
|
||||
selector = search.result.selector
|
||||
expect(selector.keys).to eq ['$and']
|
||||
first, second = selector.values.first
|
||||
expect(first).to eq({ '$or' => [ { 'name' => 'Ernie' }, { 'email' => 'ernie@example.org' } ] })
|
||||
expect(second).to eq({ '$or' => [ { 'name' => 'Bert' }, { 'email' => 'bert@example.org' } ] })
|
||||
end
|
||||
|
||||
context 'with joins' do
|
||||
before { pending 'not implemented for mongoid' }
|
||||
|
||||
it 'evaluates conditions contextually' do
|
||||
search = Search.new(Person, :children_name_eq => 'Ernie')
|
||||
expect(search.result).to be_an ActiveRecord::Relation
|
||||
where = search.result.where_values.first
|
||||
expect(where.to_sql).to match /#{children_people_name_field} = 'Ernie'/
|
||||
end
|
||||
|
||||
it 'evaluates compound conditions contextually' do
|
||||
search = Search.new(Person, :children_name_or_name_eq => 'Ernie')
|
||||
expect(search.result).to be_an ActiveRecord::Relation
|
||||
where = search.result.where_values.first
|
||||
expect(where.to_sql).to match /#{children_people_name_field
|
||||
} = 'Ernie' OR #{people_name_field} = 'Ernie'/
|
||||
end
|
||||
|
||||
it 'evaluates polymorphic belongs_to association conditions contextually' do
|
||||
search = Search.new(Note, :notable_of_Person_type_name_eq => 'Ernie')
|
||||
expect(search.result).to be_an ActiveRecord::Relation
|
||||
where = search.result.where_values.first
|
||||
expect(where.to_sql).to match /#{people_name_field} = 'Ernie'/
|
||||
end
|
||||
|
||||
it 'evaluates nested conditions' do
|
||||
search = Search.new(Person, :children_name_eq => 'Ernie',
|
||||
:g => [
|
||||
{ :m => 'or',
|
||||
:name_eq => 'Ernie',
|
||||
:children_children_name_eq => 'Ernie'
|
||||
}
|
||||
]
|
||||
)
|
||||
expect(search.result).to be_an ActiveRecord::Relation
|
||||
where = search.result.where_values.first
|
||||
expect(where.to_sql).to match /#{children_people_name_field} = 'Ernie'/
|
||||
expect(where.to_sql).to match /#{people_name_field} = 'Ernie'/
|
||||
expect(where.to_sql).to match /#{quote_table_name("children_people_2")
|
||||
}.#{quote_column_name("name")} = 'Ernie'/
|
||||
end
|
||||
|
||||
it 'evaluates arrays of groupings' do
|
||||
search = Search.new(Person,
|
||||
:g => [
|
||||
{ :m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie' },
|
||||
{ :m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert' }
|
||||
]
|
||||
)
|
||||
expect(search.result).to be_an ActiveRecord::Relation
|
||||
where = search.result.where_values.first
|
||||
sql = where.to_sql
|
||||
first, second = sql.split(/ AND /)
|
||||
expect(first).to match /#{people_name_field} = 'Ernie'/
|
||||
expect(first).to match /#{children_people_name_field} = 'Ernie'/
|
||||
expect(second).to match /#{people_name_field} = 'Bert'/
|
||||
expect(second).to match /#{children_people_name_field} = 'Bert'/
|
||||
end
|
||||
|
||||
it 'returns distinct records when passed :distinct => true' do
|
||||
search = Search.new(
|
||||
Person, :g => [
|
||||
{ :m => 'or',
|
||||
:comments_body_cont => 'e',
|
||||
:articles_comments_body_cont => 'e'
|
||||
}
|
||||
]
|
||||
)
|
||||
if ActiveRecord::VERSION::MAJOR == 3
|
||||
all_or_load, uniq_or_distinct = :all, :uniq
|
||||
else
|
||||
all_or_load, uniq_or_distinct = :load, :distinct
|
||||
end
|
||||
expect(search.result.send(all_or_load).size).
|
||||
to eq(9000)
|
||||
expect(search.result(:distinct => true).size).
|
||||
to eq(10)
|
||||
expect(search.result.send(all_or_load).send(uniq_or_distinct)).
|
||||
to eq search.result(:distinct => true).send(all_or_load)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#sorts=' do
|
||||
before do
|
||||
@s = Search.new(Person)
|
||||
end
|
||||
|
||||
it 'creates sorts based on a single attribute/direction' do
|
||||
@s.sorts = 'id desc'
|
||||
expect(@s.sorts.size).to eq(1)
|
||||
sort = @s.sorts.first
|
||||
expect(sort).to be_a Nodes::Sort
|
||||
expect(sort.name).to eq 'id'
|
||||
expect(sort.dir).to eq 'desc'
|
||||
expect(@s.result.options).to eq({ :sort => { '_id' => -1 } })
|
||||
end
|
||||
|
||||
it 'creates sorts based on a single attribute and uppercase direction' do
|
||||
@s.sorts = 'id DESC'
|
||||
expect(@s.sorts.size).to eq(1)
|
||||
sort = @s.sorts.first
|
||||
expect(sort).to be_a Nodes::Sort
|
||||
expect(sort.name).to eq 'id'
|
||||
expect(sort.dir).to eq 'desc'
|
||||
expect(@s.result.options).to eq({ :sort => { '_id' => -1 } })
|
||||
end
|
||||
|
||||
it 'creates sorts based on a single attribute and without direction' do
|
||||
@s.sorts = 'id'
|
||||
expect(@s.sorts.size).to eq(1)
|
||||
sort = @s.sorts.first
|
||||
expect(sort).to be_a Nodes::Sort
|
||||
expect(sort.name).to eq 'id'
|
||||
expect(sort.dir).to eq 'asc'
|
||||
expect(@s.result.options).to eq({ :sort => { '_id' => 1 } })
|
||||
end
|
||||
|
||||
it 'creates sorts based on multiple attributes/directions in array format' do
|
||||
@s.sorts = ['id desc', { :name => 'name', :dir => 'asc' }]
|
||||
expect(@s.sorts.size).to eq(2)
|
||||
sort1, sort2 = @s.sorts
|
||||
expect(sort1).to be_a Nodes::Sort
|
||||
expect(sort1.name).to eq 'id'
|
||||
expect(sort1.dir).to eq 'desc'
|
||||
expect(sort2).to be_a Nodes::Sort
|
||||
expect(sort2.name).to eq 'name'
|
||||
expect(sort2.dir).to eq 'asc'
|
||||
expect(@s.result.options).to eq({ :sort=>{"_id"=>-1, "name"=>1} })
|
||||
end
|
||||
|
||||
it 'creates sorts based on multiple attributes and uppercase directions in array format' do
|
||||
@s.sorts = ['id DESC', { :name => 'name', :dir => 'ASC' }]
|
||||
expect(@s.sorts.size).to eq(2)
|
||||
sort1, sort2 = @s.sorts
|
||||
expect(sort1).to be_a Nodes::Sort
|
||||
expect(sort1.name).to eq 'id'
|
||||
expect(sort1.dir).to eq 'desc'
|
||||
expect(sort2).to be_a Nodes::Sort
|
||||
expect(sort2.name).to eq 'name'
|
||||
expect(sort2.dir).to eq 'asc'
|
||||
expect(@s.result.options).to eq({ :sort=>{"_id"=>-1, "name"=>1} })
|
||||
end
|
||||
|
||||
it 'creates sorts based on multiple attributes and different directions in array format' do
|
||||
@s.sorts = ['id DESC', { name: 'name', dir: nil }]
|
||||
expect(@s.sorts.size).to eq(2)
|
||||
sort1, sort2 = @s.sorts
|
||||
expect(sort1).to be_a Nodes::Sort
|
||||
expect(sort1.name).to eq 'id'
|
||||
expect(sort1.dir).to eq 'desc'
|
||||
expect(sort2).to be_a Nodes::Sort
|
||||
expect(sort2.name).to eq 'name'
|
||||
expect(sort2.dir).to eq 'asc'
|
||||
expect(@s.result.options).to eq({ :sort=>{"_id"=>-1, "name"=>1} })
|
||||
end
|
||||
|
||||
it 'creates sorts based on multiple attributes/directions in hash format' do
|
||||
@s.sorts = {
|
||||
'0' => { :name => 'id', :dir => 'desc' },
|
||||
'1' => { :name => 'name', :dir => 'asc' }
|
||||
}
|
||||
expect(@s.sorts.size).to eq(2)
|
||||
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
||||
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
||||
name_sort = @s.sorts.detect { |s| s.name == 'name' }
|
||||
expect(id_sort.dir).to eq 'desc'
|
||||
expect(name_sort.dir).to eq 'asc'
|
||||
expect(@s.result.options).to eq({ :sort=>{"_id"=>-1, "name"=>1} })
|
||||
end
|
||||
|
||||
it 'creates sorts based on multiple attributes and uppercase directions in hash format' do
|
||||
@s.sorts = {
|
||||
'0' => { :name => 'id', :dir => 'DESC' },
|
||||
'1' => { :name => 'name', :dir => 'ASC' }
|
||||
}
|
||||
expect(@s.sorts.size).to eq(2)
|
||||
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
||||
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
||||
name_sort = @s.sorts.detect { |s| s.name == 'name' }
|
||||
expect(id_sort.dir).to eq 'desc'
|
||||
expect(name_sort.dir).to eq 'asc'
|
||||
expect(@s.result.options).to eq({ :sort=>{"_id"=>-1, "name"=>1} })
|
||||
end
|
||||
|
||||
it 'creates sorts based on multiple attributes and different directions in hash format' do
|
||||
@s.sorts = {
|
||||
'0' => { :name => 'id', :dir => 'DESC' },
|
||||
'1' => { :name => 'name', :dir => nil }
|
||||
}
|
||||
expect(@s.sorts.size).to eq(2)
|
||||
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
||||
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
||||
name_sort = @s.sorts.detect { |s| s.name == 'name' }
|
||||
expect(id_sort.dir).to eq 'desc'
|
||||
expect(name_sort.dir).to eq 'asc'
|
||||
expect(@s.result.options).to eq({ :sort=>{"_id"=>-1, "name"=>1} })
|
||||
end
|
||||
|
||||
it 'overrides existing sort' do
|
||||
@s.sorts = 'id asc'
|
||||
expect(@s.result.first.id.to_s).to eq Person.min(:id).to_s
|
||||
end
|
||||
end
|
||||
|
||||
describe '#method_missing' do
|
||||
before do
|
||||
@s = Search.new(Person)
|
||||
end
|
||||
|
||||
it 'raises NoMethodError when sent an invalid attribute' do
|
||||
expect { @s.blah }.to raise_error NoMethodError
|
||||
end
|
||||
|
||||
it 'sets condition attributes when sent valid attributes' do
|
||||
@s.name_eq = 'Ernie'
|
||||
expect(@s.name_eq).to eq 'Ernie'
|
||||
end
|
||||
|
||||
context 'with joins' do
|
||||
it 'allows chaining to access nested conditions' do
|
||||
@s.groupings = [
|
||||
{ :m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie' }
|
||||
]
|
||||
expect(@s.groupings.first.children_name_eq).to eq 'Ernie'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
test:
|
||||
clients:
|
||||
default:
|
||||
database: ransack_mongoid_test
|
||||
hosts:
|
||||
- localhost:27017
|
||||
sessions:
|
||||
default:
|
||||
database: ransack_mongoid_test
|
||||
hosts:
|
||||
- localhost:27017
|
|
@ -1,135 +0,0 @@
|
|||
require 'mongoid'
|
||||
|
||||
Mongoid.load!(File.expand_path("../mongoid.yml", __FILE__), :test)
|
||||
Mongo::Logger.logger.level = Logger::WARN if defined?(Mongo)
|
||||
Mongoid.purge!
|
||||
|
||||
class Person
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
|
||||
field :name, type: String
|
||||
field :email, type: String
|
||||
field :only_search, type: String
|
||||
field :only_sort, type: String
|
||||
field :only_admin, type: String
|
||||
field :salary, type: Integer
|
||||
field :awesome, type: Boolean, default: false
|
||||
|
||||
belongs_to :parent, :class_name => 'Person', inverse_of: :children
|
||||
has_many :children, :class_name => 'Person', inverse_of: :parent
|
||||
|
||||
has_many :articles
|
||||
has_many :comments
|
||||
|
||||
ransack_alias :term, :name_or_email
|
||||
|
||||
# has_many :authored_article_comments, :through => :articles,
|
||||
# :source => :comments, :foreign_key => :person_id
|
||||
|
||||
has_many :notes, :as => :notable
|
||||
|
||||
default_scope -> { order(id: :desc) }
|
||||
|
||||
scope :restricted, lambda { where(restricted: 1) }
|
||||
scope :active, lambda { where(active: 1) }
|
||||
scope :over_age, lambda { |y| where(:age.gt => y) }
|
||||
|
||||
ransacker :reversed_name, :formatter => proc { |v| v.reverse } do |parent|
|
||||
parent.table[:name]
|
||||
end
|
||||
|
||||
ransacker :doubled_name do |parent|
|
||||
# Arel::Nodes::InfixOperation.new(
|
||||
# '||', parent.table[:name], parent.table[:name]
|
||||
# )
|
||||
parent.table[:name]
|
||||
end
|
||||
|
||||
def self.ransackable_attributes(auth_object = nil)
|
||||
if auth_object == :admin
|
||||
all_ransackable_attributes - ['only_sort']
|
||||
else
|
||||
all_ransackable_attributes - ['only_sort', 'only_admin']
|
||||
end
|
||||
end
|
||||
|
||||
def self.ransortable_attributes(auth_object = nil)
|
||||
if auth_object == :admin
|
||||
all_ransackable_attributes - ['only_search']
|
||||
else
|
||||
all_ransackable_attributes - ['only_search', 'only_admin']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Musician < Person
|
||||
end
|
||||
|
||||
class Article
|
||||
include Mongoid::Document
|
||||
|
||||
field :title, type: String
|
||||
field :body, type: String
|
||||
|
||||
belongs_to :person
|
||||
has_many :comments
|
||||
# has_and_belongs_to_many :tags
|
||||
has_many :notes, :as => :notable
|
||||
end
|
||||
|
||||
module Namespace
|
||||
class Article < ::Article
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
class Comment
|
||||
include Mongoid::Document
|
||||
|
||||
field :body, type: String
|
||||
|
||||
|
||||
belongs_to :article
|
||||
belongs_to :person
|
||||
end
|
||||
|
||||
class Tag
|
||||
include Mongoid::Document
|
||||
|
||||
field :name, type: String
|
||||
|
||||
# has_and_belongs_to_many :articles
|
||||
end
|
||||
|
||||
class Note
|
||||
include Mongoid::Document
|
||||
|
||||
field :note, type: String
|
||||
|
||||
belongs_to :notable, :polymorphic => true
|
||||
end
|
||||
|
||||
module Schema
|
||||
def self.create
|
||||
10.times do
|
||||
person = Person.make.save!
|
||||
Note.make.save!(:notable => person)
|
||||
3.times do
|
||||
article = Article.create!(:person => person)
|
||||
3.times do
|
||||
# article.tags = [Tag.make.save!, Tag.make.save!, Tag.make.save!]
|
||||
end
|
||||
Note.create.save!(:notable => article)
|
||||
10.times do
|
||||
Comment.create.save!(:article => article, :person => person)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Comment.create!(
|
||||
:body => 'First post!',
|
||||
:article => Article.create!(:title => 'Hello, world!')
|
||||
)
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
require 'mongoid_spec_helper'
|
||||
|
||||
module Ransack
|
||||
describe Translate do
|
||||
|
||||
describe '.attribute' do
|
||||
it 'translate namespaced attribute like AR does' do
|
||||
ar_translation = ::Namespace::Article.human_attribute_name(:title)
|
||||
ransack_translation = Ransack::Translate.attribute(:title, :context => ::Namespace::Article.search.context)
|
||||
expect(ransack_translation).to eq ar_translation
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,63 +0,0 @@
|
|||
require 'machinist/object'
|
||||
require 'sham'
|
||||
require 'faker'
|
||||
require 'pry'
|
||||
require 'mongoid'
|
||||
require 'ransack'
|
||||
|
||||
I18n.enforce_available_locales = false
|
||||
Time.zone = 'Eastern Time (US & Canada)'
|
||||
I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'support', '*.yml')]
|
||||
|
||||
Dir[File.expand_path('../{mongoid/helpers,mongoid/support,blueprints}/*.rb',
|
||||
__FILE__)]
|
||||
.each { |f| require f }
|
||||
|
||||
Sham.define do
|
||||
name { Faker::Name.name }
|
||||
title { Faker::Lorem.sentence }
|
||||
body { Faker::Lorem.paragraph }
|
||||
salary { |index| 30000 + (index * 1000) }
|
||||
tag_name { Faker::Lorem.words(3).join(' ') }
|
||||
note { Faker::Lorem.words(7).join(' ') }
|
||||
only_admin { Faker::Lorem.words(3).join(' ') }
|
||||
only_search { Faker::Lorem.words(3).join(' ') }
|
||||
only_sort { Faker::Lorem.words(3).join(' ') }
|
||||
notable_id { |id| id }
|
||||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.alias_it_should_behave_like_to :it_has_behavior, 'has behavior'
|
||||
|
||||
config.before(:suite) do
|
||||
if ENV['DB'] == 'mongoid4'
|
||||
message = "Running Ransack specs with #{Mongoid.default_session.inspect
|
||||
}, Mongoid #{Mongoid::VERSION}, Moped #{Moped::VERSION
|
||||
}, Origin #{Origin::VERSION} and Ruby #{RUBY_VERSION}"
|
||||
else
|
||||
message = "Running Ransack specs with #{Mongoid.default_client.inspect
|
||||
}, Mongoid #{Mongoid::VERSION}, Mongo driver #{Mongo::VERSION}"
|
||||
end
|
||||
line = '=' * message.length
|
||||
puts line, message, line
|
||||
Schema.create
|
||||
end
|
||||
|
||||
config.before(:all) { Sham.reset(:before_all) }
|
||||
config.before(:each) { Sham.reset(:before_each) }
|
||||
|
||||
config.include RansackHelper
|
||||
end
|
||||
|
||||
RSpec::Matchers.define :be_like do |expected|
|
||||
match do |actual|
|
||||
actual.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip ==
|
||||
expected.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip
|
||||
end
|
||||
end
|
||||
|
||||
RSpec::Matchers.define :have_attribute_method do |expected|
|
||||
match do |actual|
|
||||
actual.attribute_method?(expected)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue