activerecord-hackery--ransack/spec/ransack/search_spec.rb

695 lines
27 KiB
Ruby

require '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
context 'whitespace stripping' do
context 'when whitespace_strip option is true' do
before do
Ransack.configure { |c| c.strip_whitespace = true }
end
it 'strips leading & trailing whitespace before building' do
expect_any_instance_of(Search).to receive(:build)
.with({ 'name_eq' => 'foobar' })
Search.new(Person, name_eq: ' foobar ')
end
end
context 'when whitespace_strip option is false' do
before do
Ransack.configure { |c| c.strip_whitespace = false }
end
it 'doesn\'t strip leading & trailing whitespace before building' do
expect_any_instance_of(Search).to receive(:build)
.with({ 'name_eq' => ' foobar ' })
Search.new(Person, name_eq: ' foobar ')
end
end
it 'strips leading & trailing whitespace when strip_whitespace search parameter is true' do
expect_any_instance_of(Search).to receive(:build)
.with({ 'name_eq' => 'foobar' })
Search.new(Person, { name_eq: ' foobar ' }, { strip_whitespace: true })
end
it 'doesn\'t strip leading & trailing whitespace when strip_whitespace search parameter is false' do
expect_any_instance_of(Search).to receive(:build)
.with({ 'name_eq' => ' foobar ' })
Search.new(Person, { name_eq: ' foobar ' }, { strip_whitespace: false })
end
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
it 'accepts a context option' do
shared_context = Context.for(Person)
s1 = Search.new(Person, { name_eq: 'A' }, context: shared_context)
s2 = Search.new(Person, { name_eq: 'B' }, context: shared_context)
expect(s1.context).to be s2.context
end
end
describe '#build' do
it 'creates conditions for top-level attributes' do
s = Search.new(Person, name_eq: 'Ernie')
condition = s.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
it 'creates conditions for association attributes' do
s = Search.new(Person, children_name_eq: 'Ernie')
condition = s.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
s = Search.new(Note, notable_of_Person_type_name_eq: 'Ernie')
condition = s.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
s = Search.new(Note,
notable_of_Person_type_name_or_notable_of_Article_type_title_eq: 'Ernie')
condition = s.
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
it 'creates conditions for aliased attributes',
if: Ransack::SUPPORTS_ATTRIBUTE_ALIAS do
s = Search.new(Person, full_name_eq: 'Ernie')
condition = s.base[:full_name_eq]
expect(condition).to be_a Nodes::Condition
expect(condition.predicate.name).to eq 'eq'
expect(condition.attributes.first.name).to eq 'full_name'
expect(condition.value).to eq 'Ernie'
end
it 'preserves default scope and conditions for associations' do
s = Search.new(Person, published_articles_title_eq: 'Test')
expect(s.result.to_sql).to include 'default_scope'
expect(s.result.to_sql).to include 'published'
end
# The failure/oversight in Ransack::Nodes::Condition#arel_predicate or deeper is beyond my understanding of the structures
it 'preserves (inverts) default scope and conditions for negative subqueries' do
# the positive case (published_articles_title_eq) is
# SELECT "people".* FROM "people"
# LEFT OUTER JOIN "articles" ON "articles"."person_id" = "people"."id"
# AND "articles"."published" = 't'
# AND ('default_scope' = 'default_scope')
# WHERE "articles"."title" = 'Test' ORDER BY "people"."id" DESC
#
# negative case was
# SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
# SELECT "articles"."person_id" FROM "articles"
# WHERE "articles"."person_id" = "people"."id"
# AND NOT ("articles"."title" != 'Test')
# ) ORDER BY "people"."id" DESC
#
# Should have been like
# SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
# SELECT "articles"."person_id" FROM "articles"
# WHERE "articles"."person_id" = "people"."id"
# AND "articles"."title" = 'Test' AND "articles"."published" = 't' AND ('default_scope' = 'default_scope')
# ) ORDER BY "people"."id" DESC
#
# With tenanting (eg default_scope with column reference), NOT IN should be like
# SELECT "people".* FROM "people" WHERE "people"."tenant_id" = 'tenant_id' AND "people"."id" NOT IN (
# SELECT "articles"."person_id" FROM "articles"
# WHERE "articles"."person_id" = "people"."id"
# AND "articles"."tenant_id" = 'tenant_id'
# AND "articles"."title" = 'Test' AND "articles"."published" = 't' AND ('default_scope' = 'default_scope')
# ) ORDER BY "people"."id" DESC
pending("spec should pass, but I do not know how/where to fix lib code")
s = Search.new(Person, published_articles_title_not_eq: 'Test')
expect(s.result.to_sql).to include 'default_scope'
expect(s.result.to_sql).to include 'published'
end
it 'discards empty conditions' do
s = Search.new(Person, children_name_eq: '')
condition = s.base[:children_name_eq]
expect(condition).to be_nil
end
it 'accepts base grouping condition as an option' do
expect(Nodes::Grouping).to receive(:new).with(kind_of(Context), 'or')
Search.new(Person, {}, { grouping: 'or' })
end
it 'accepts arrays of groupings' do
s = Search.new(Person,
g: [
{ m: 'or', name_eq: 'Ernie', children_name_eq: 'Ernie' },
{ m: 'or', name_eq: 'Bert', children_name_eq: 'Bert' },
]
)
ors = s.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
s = 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 = s.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
s = Search.new(Person,
c: {
'0' => { a: ['name'], p: 'eq', v: ['Ernie'] },
'1' => {
a: ['children_name', 'parent_name'],
p: 'eq', v: ['Ernie'], m: 'or'
}
}
)
conditions = s.base.conditions
expect(conditions.size).to eq(2)
expect(conditions.map { |c| c.class })
.to eq [Nodes::Condition, Nodes::Condition]
end
it 'creates conditions for custom predicates that take arrays' do
Ransack.configure do |config|
config.add_predicate 'ary_pred', wants_array: true
end
s = Search.new(Person, name_ary_pred: ['Ernie', 'Bert'])
condition = s.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
it 'does not evaluate the query on #inspect' do
s = Search.new(Person, children_id_in: [1, 2, 3])
expect(s.inspect).not_to match /ActiveRecord/
end
context 'with an invalid condition' do
subject { Search.new(Person, unknown_attr_eq: 'Ernie') }
context 'when ignore_unknown_conditions configuration option 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 configuration option is true' do
before do
Ransack.configure { |c| c.ignore_unknown_conditions = true }
end
specify { expect { subject }.not_to raise_error }
end
subject(:with_ignore_unknown_conditions_false) {
Search.new(Person,
{ unknown_attr_eq: 'Ernie' },
{ ignore_unknown_conditions: false }
)
}
subject(:with_ignore_unknown_conditions_true) {
Search.new(Person,
{ unknown_attr_eq: 'Ernie' },
{ ignore_unknown_conditions: true }
)
}
context 'when ignore_unknown_conditions search parameter is absent' do
specify { expect { subject }.not_to raise_error }
end
context 'when ignore_unknown_conditions search parameter is false' do
specify { expect { with_ignore_unknown_conditions_false }.to raise_error ArgumentError }
end
context 'when ignore_unknown_conditions search parameter is true' do
specify { expect { with_ignore_unknown_conditions_true }.not_to raise_error }
end
end
it 'does not modify the parameters' do
params = { name_eq: '' }
expect { Search.new(Person, params) }.not_to change { params }
end
context "ransackable_scope" do
around(:each) do |example|
Person.define_singleton_method(:name_eq) do |name|
self.where(name: name)
end
begin
example.run
ensure
Person.singleton_class.undef_method :name_eq
end
end
it "is prioritized over base predicates" do
allow(Person).to receive(:ransackable_scopes)
.and_return(Person.ransackable_scopes + [:name_eq])
s = Search.new(Person, name_eq: "Johny")
expect(s.instance_variable_get(:@scope_args)["name_eq"]).to eq("Johny")
expect(s.base[:name_eq]).to be_nil
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")}"
}
let(:notable_type_field) {
"#{quote_table_name("notes")}.#{quote_column_name("notable_type")}"
}
it 'evaluates conditions contextually' do
s = Search.new(Person, children_name_eq: 'Ernie')
expect(s.result).to be_an ActiveRecord::Relation
expect(s.result.to_sql).to match /#{
children_people_name_field} = 'Ernie'/
end
it 'use appropriate table alias' do
s = Search.new(Person, {
name_eq: "person_name_query",
articles_title_eq: "person_article_title_query",
parent_name_eq: "parent_name_query",
parent_articles_title_eq: 'parents_article_title_query'
}).result
real_query = remove_quotes_and_backticks(s.to_sql)
expect(real_query)
.to match(%r{LEFT OUTER JOIN articles ON (\('default_scope' = 'default_scope'\) AND )?articles.person_id = people.id})
expect(real_query)
.to match(%r{LEFT OUTER JOIN articles articles_people ON (\('default_scope' = 'default_scope'\) AND )?articles_people.person_id = parents_people.id})
expect(real_query)
.to include "people.name = 'person_name_query'"
expect(real_query)
.to include "articles.title = 'person_article_title_query'"
expect(real_query)
.to include "parents_people.name = 'parent_name_query'"
expect(real_query)
.to include "articles_people.title = 'parents_article_title_query'"
end
it 'evaluates conditions for multiple `belongs_to` associations to the same table contextually' do
s = Search.new(
Recommendation,
person_name_eq: 'Ernie',
target_person_parent_name_eq: 'Test'
).result
expect(s).to be_an ActiveRecord::Relation
real_query = remove_quotes_and_backticks(s.to_sql)
expected_query = <<-SQL
SELECT recommendations.* FROM recommendations
LEFT OUTER JOIN people ON people.id = recommendations.person_id
LEFT OUTER JOIN people target_people_recommendations
ON target_people_recommendations.id = recommendations.target_person_id
LEFT OUTER JOIN people parents_people
ON parents_people.id = target_people_recommendations.parent_id
WHERE (people.name = 'Ernie' AND parents_people.name = 'Test')
SQL
.squish
expect(real_query).to eq expected_query
end
it 'evaluates compound conditions contextually' do
s = Search.new(Person, children_name_or_name_eq: 'Ernie').result
expect(s).to be_an ActiveRecord::Relation
expect(s.to_sql).to match /#{children_people_name_field
} = 'Ernie' OR #{people_name_field} = 'Ernie'/
end
it 'evaluates polymorphic belongs_to association conditions contextually' do
s = Search.new(Note, notable_of_Person_type_name_eq: 'Ernie').result
expect(s).to be_an ActiveRecord::Relation
expect(s.to_sql).to match /#{people_name_field} = 'Ernie'/
expect(s.to_sql).to match /#{notable_type_field} = 'Person'/
end
it 'evaluates nested conditions' do
s = Search.new(Person, children_name_eq: 'Ernie',
g: [
{ m: 'or', name_eq: 'Ernie', children_children_name_eq: 'Ernie' }
]
).result
expect(s).to be_an ActiveRecord::Relation
first, last = s.to_sql.split(/ AND /)
expect(first).to match /#{children_people_name_field} = 'Ernie'/
expect(last).to match /#{
people_name_field} = 'Ernie' OR #{
quote_table_name("children_people_2")}.#{
quote_column_name("name")} = 'Ernie'/
end
it 'evaluates arrays of groupings' do
s = Search.new(Person,
g: [
{ m: 'or', name_eq: 'Ernie', children_name_eq: 'Ernie' },
{ m: 'or', name_eq: 'Bert', children_name_eq: 'Bert' }
]
).result
expect(s).to be_an ActiveRecord::Relation
first, last = s.to_sql.split(/ AND /)
expect(first).to match /#{people_name_field} = 'Ernie' OR #{
children_people_name_field} = 'Ernie'/
expect(last).to match /#{people_name_field} = 'Bert' OR #{
children_people_name_field} = 'Bert'/
end
it 'returns distinct records when passed distinct: true' do
s = Search.new(Person,
g: [
{ m: 'or', comments_body_cont: 'e', articles_comments_body_cont: 'e' }
]
)
all_or_load, uniq_or_distinct = :load, :distinct
expect(s.result.send(all_or_load).size)
.to eq(9000)
expect(s.result(distinct: true).size)
.to eq(10)
expect(s.result.send(all_or_load).send(uniq_or_distinct))
.to eq s.result(distinct: true).send(all_or_load)
end
it 'evaluates joins with belongs_to join' do
s = Person.joins(:parent).ransack(parent_name_eq: 'Ernie').result(distinct: true)
expect(s).to be_an ActiveRecord::Relation
end
private
def remove_quotes_and_backticks(str)
str.gsub(/["`]/, '')
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'
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'
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'
end
it 'creates sorts based on a single alias/direction' do
@s.sorts = 'daddy desc'
expect(@s.sorts.size).to eq(1)
sort = @s.sorts.first
expect(sort).to be_a Nodes::Sort
expect(sort.name).to eq 'parent_name'
expect(sort.dir).to eq 'desc'
end
it 'creates sorts based on a single alias and uppercase direction' do
@s.sorts = 'daddy DESC'
expect(@s.sorts.size).to eq(1)
sort = @s.sorts.first
expect(sort).to be_a Nodes::Sort
expect(sort.name).to eq 'parent_name'
expect(sort.dir).to eq 'desc'
end
it 'creates sorts based on a single alias and without direction' do
@s.sorts = 'daddy'
expect(@s.sorts.size).to eq(1)
sort = @s.sorts.first
expect(sort).to be_a Nodes::Sort
expect(sort.name).to eq 'parent_name'
expect(sort.dir).to eq 'asc'
end
it 'creates sorts based on attributes, alias and directions in array format' do
@s.sorts = ['id desc', { name: 'daddy', 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 'parent_name'
expect(sort2.dir).to eq 'asc'
end
it 'creates sorts based on attributes, alias and uppercase directions in array format' do
@s.sorts = ['id DESC', { name: 'daddy', 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 'parent_name'
expect(sort2.dir).to eq 'asc'
end
it 'creates sorts based on attributes, alias and different directions
in array format' do
@s.sorts = ['id DESC', { name: 'daddy', 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 'parent_name'
expect(sort2.dir).to eq 'asc'
end
it 'creates sorts based on attributes, alias and directions in hash format' do
@s.sorts = {
'0' => { name: 'id', dir: 'desc' },
'1' => { name: 'daddy', 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' }
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
expect(id_sort.dir).to eq 'desc'
expect(daddy_sort.dir).to eq 'asc'
end
it 'creates sorts based on attributes, alias and uppercase directions
in hash format' do
@s.sorts = {
'0' => { name: 'id', dir: 'DESC' },
'1' => { name: 'daddy', 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' }
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
expect(id_sort.dir).to eq 'desc'
expect(daddy_sort.dir).to eq 'asc'
end
it 'creates sorts based on attributes, alias and different directions
in hash format' do
@s.sorts = {
'0' => { name: 'id', dir: 'DESC' },
'1' => { name: 'daddy', 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' }
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
expect(id_sort.dir).to eq 'desc'
expect(daddy_sort.dir).to eq 'asc'
end
it 'overrides existing sort' do
@s.sorts = 'id asc'
expect(@s.result.first.id).to eq 1
end
it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
default = Ransack.options.clone
s = Search.new(Person, s: 'name asc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC"
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
s = Search.new(Person, s: 'name asc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS FIRST"
s = Search.new(Person, s: 'name desc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS LAST"
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last }
s = Search.new(Person, s: 'name asc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS LAST"
s = Search.new(Person, s: 'name desc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS FIRST"
Ransack.options = default
end
it "PG's sort option with double name", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
default = Ransack.options.clone
s = Search.new(Person, s: 'doubled_name asc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC"
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
s = Search.new(Person, s: 'doubled_name asc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST"
s = Search.new(Person, s: 'doubled_name desc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST"
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last }
s = Search.new(Person, s: 'doubled_name asc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST"
s = Search.new(Person, s: 'doubled_name desc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_first }
s = Search.new(Person, s: 'doubled_name asc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST"
s = Search.new(Person, s: 'doubled_name desc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_last }
s = Search.new(Person, s: 'doubled_name asc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST"
s = Search.new(Person, s: 'doubled_name desc')
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST"
Ransack.options = default
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
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