move mongoid support to a separate gem

This commit is contained in:
Greg Molnar 2018-06-25 20:50:14 +02:00
parent 1d1c64b470
commit 24e0f1c974
30 changed files with 2 additions and 2280 deletions

View File

@ -40,14 +40,6 @@ else
end end
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 group :test do
# TestUnit was removed from Ruby 2.2 but still needed for testing Rails 3.x. # 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' gem 'test-unit', '~> 3.0' if RUBY_VERSION >= '2.2'

View File

@ -875,6 +875,7 @@ en:
## Mongoid ## 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 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 Mongoid, associations are not currently supported. Demo source code may be found
[here](https://github.com/Zhomart/ransack-mongodb-demo). A `result` method [here](https://github.com/Zhomart/ransack-mongodb-demo). A `result` method

View File

@ -11,17 +11,8 @@ RSpec::Core::RakeTask.new(:spec) do |rspec|
# rspec.rspec_opts = ['--backtrace'] # rspec.rspec_opts = ['--backtrace']
end end
RSpec::Core::RakeTask.new(:mongoid) do |rspec|
ENV['SPEC'] = 'spec/mongoid/**/*_spec.rb'
rspec.rspec_opts = ['--backtrace']
end
task :default do task :default do
if ENV['DB'] =~ /mongoid/ Rake::Task["spec"].invoke
Rake::Task["mongoid"].invoke
else
Rake::Task["spec"].invoke
end
end end
desc "Open an irb session with Ransack and the sample data used in specs" desc "Open an irb session with Ransack and the sample data used in specs"
@ -31,15 +22,3 @@ task :console do
ARGV.clear ARGV.clear
Pry.start Pry.start
end 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 '&#9660;'
expect(Ransack.options[:down_arrow]).to eq '&#9650;'
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,13 +0,0 @@
require 'mongoid_spec_helper'
module Ransack
module Nodes
describe Grouping do
before do
@g = 1
end
end
end
end

View File

@ -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

View File

@ -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

View File

@ -1,11 +0,0 @@
test:
clients:
default:
database: ransack_mongoid_test
hosts:
- localhost:27017
sessions:
default:
database: ransack_mongoid_test
hosts:
- localhost:27017

View File

@ -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

View File

@ -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

View File

@ -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