Merge remote-tracking branch 'upstream/master'
Conflicts: spec/ransack/adapters/active_record/base_spec.rb
This commit is contained in:
commit
2d5de7301c
25
.travis.yml
25
.travis.yml
|
@ -1,16 +1,21 @@
|
|||
rvm:
|
||||
- 1.8.7
|
||||
- 1.9.2
|
||||
- 1.9.3
|
||||
- 2.0.0
|
||||
|
||||
env:
|
||||
- RAILS=4-0-stable
|
||||
- RAILS=3-2-stable
|
||||
- RAILS=3-1-stable
|
||||
- RAILS=3-0-stable
|
||||
- RAILS=4-0-stable DB=sqlite
|
||||
- RAILS=4-0-stable DB=mysql
|
||||
- RAILS=4-0-stable DB=postgres
|
||||
- RAILS=3-2-stable DB=sqlite
|
||||
- RAILS=3-2-stable DB=mysql
|
||||
- RAILS=3-2-stable DB=postgres
|
||||
- RAILS=3-1-stable DB=sqlite
|
||||
- RAILS=3-1-stable DB=mysql
|
||||
- RAILS=3-1-stable DB=postgres
|
||||
- RAILS=3-0-stable DB=sqlite
|
||||
- RAILS=3-0-stable DB=mysql
|
||||
- RAILS=3-0-stable DB=postgres
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- rvm: 1.8.7
|
||||
env: RAILS=4-0-stable
|
||||
before_script:
|
||||
- mysql -e 'create database ransack;'
|
||||
- psql -c 'create database ransack;' -U postgres
|
|
@ -1,6 +1,6 @@
|
|||
# Ransack
|
||||
|
||||
[![Build Status](https://travis-ci.org/ernie/ransack.png?branch=master)](https://travis-ci.org/ernie/ransack)
|
||||
[![Build Status](https://travis-ci.org/activerecord-hackery/ransack)](https://travis-ci.org/ernie/ransack)
|
||||
|
||||
Ransack is a rewrite of [MetaSearch](https://github.com/ernie/meta_search). While it
|
||||
supports many of the same features as MetaSearch, its underlying implementation differs
|
||||
|
|
|
@ -19,7 +19,7 @@ end
|
|||
require 'ransack/translate'
|
||||
require 'ransack/search'
|
||||
require 'ransack/ransacker'
|
||||
require 'ransack/adapters/active_record'
|
||||
require 'ransack/adapters/active_record' if defined?(::ActiveRecord::Base)
|
||||
require 'ransack/helpers'
|
||||
require 'action_controller'
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
require 'active_record'
|
||||
require 'ransack/adapters/active_record/base'
|
||||
ActiveRecord::Base.extend Ransack::Adapters::ActiveRecord::Base
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ module Ransack
|
|||
module Base
|
||||
|
||||
def self.extended(base)
|
||||
alias :search :ransack unless base.method_defined? :search
|
||||
alias :search :ransack unless base.respond_to? :search
|
||||
base.class_eval do
|
||||
class_attribute :_ransackers
|
||||
self._ransackers ||= {}
|
||||
|
|
|
@ -23,10 +23,12 @@ module Ransack
|
|||
module_function
|
||||
# replace % \ to \% \\
|
||||
def escape_wildcards(unescaped)
|
||||
if ActiveRecord::VERSION::MAJOR == 3
|
||||
unescaped.to_s.gsub(/([\\|\%|.])/, '\\\\\\1')
|
||||
case ActiveRecord::Base.connection.adapter_name
|
||||
when "SQLite"
|
||||
unescaped
|
||||
else
|
||||
unescaped.to_s.gsub(/\\/){ "\\\\" }.gsub(/%/, "\\%")
|
||||
# Necessary for PostgreSQL and MySQL
|
||||
unescaped.to_s.gsub(/([\\|\%|.])/, '\\\\\\1')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
module Ransack
|
||||
VERSION = "1.0.0"
|
||||
VERSION = "1.1.0"
|
||||
end
|
||||
|
|
|
@ -11,6 +11,7 @@ Gem::Specification.new do |s|
|
|||
s.homepage = "https://github.com/ernie/ransack"
|
||||
s.summary = %q{Object-based searching for ActiveRecord (currently).}
|
||||
s.description = %q{Ransack is the successor to the MetaSearch gem. It improves and expands upon MetaSearch's functionality, but does not have a 100%-compatible API.}
|
||||
s.license = 'MIT'
|
||||
|
||||
s.rubyforge_project = "ransack"
|
||||
|
||||
|
@ -21,6 +22,9 @@ Gem::Specification.new do |s|
|
|||
s.add_development_dependency 'machinist', '~> 1.0.6'
|
||||
s.add_development_dependency 'faker', '~> 0.9.5'
|
||||
s.add_development_dependency 'sqlite3', '~> 1.3.3'
|
||||
s.add_development_dependency 'pg', '0.17.0'
|
||||
s.add_development_dependency 'mysql2', '0.3.13'
|
||||
s.add_development_dependency 'pry', '0.9.12.2'
|
||||
|
||||
s.files = `git ls-files`.split("\n")
|
||||
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
Person.blueprint do
|
||||
name
|
||||
email { "test@example.com" }
|
||||
salary
|
||||
end
|
|
@ -1,2 +1,9 @@
|
|||
module RansackHelper
|
||||
def quote_table_name(table)
|
||||
ActiveRecord::Base.connection.quote_table_name(table)
|
||||
end
|
||||
|
||||
def quote_column_name(column)
|
||||
ActiveRecord::Base.connection.quote_column_name(column)
|
||||
end
|
||||
end
|
|
@ -20,6 +20,15 @@ module Ransack
|
|||
end
|
||||
|
||||
describe '#ransacker' do
|
||||
# For infix tests
|
||||
def self.sane_adapter?
|
||||
case ::ActiveRecord::Base.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]
|
||||
|
@ -36,18 +45,36 @@ module Ransack
|
|||
|
||||
it 'can be accessed through associations' do
|
||||
s = Person.search(:children_reversed_name_eq => 'htimS cirA')
|
||||
s.result.to_sql.should match /"children_people"."name" = 'Aric Smith'/
|
||||
|
||||
s.result.to_sql.should match /#{quote_table_name("children_people")}.#{quote_column_name("name")} = 'Aric Smith'/
|
||||
end
|
||||
|
||||
it 'allows an "attribute" to be an InfixOperation' do
|
||||
s = Person.search(:doubled_name_eq => 'Aric SmithAric Smith')
|
||||
s.result.first.should eq Person.find_by_name('Aric Smith')
|
||||
end if defined?(Arel::Nodes::InfixOperation)
|
||||
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')
|
||||
s.result.count.should eq 1
|
||||
end if defined?(Arel::Nodes::InfixOperation)
|
||||
end if defined?(Arel::Nodes::InfixOperation) && sane_adapter?
|
||||
|
||||
it "should function correctly when using fields with dots in them" do
|
||||
s = Person.search(email_cont: "example.com")
|
||||
s.result.exists?.should 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%")
|
||||
s.result.exists?.should 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\\")
|
||||
s.result.exists?.should be_true
|
||||
end
|
||||
|
||||
it 'allows sort by "only_sort" field' do
|
||||
s = Person.search("s"=>{"0"=>{"dir"=>"asc", "name"=>"only_sort"}})
|
||||
|
|
|
@ -18,7 +18,7 @@ module Ransack
|
|||
result = subject.evaluate(search)
|
||||
|
||||
result.should be_an ::ActiveRecord::Relation
|
||||
result.to_sql.should match /"name" = 'Joe Blow'/
|
||||
result.to_sql.should match /#{quote_column_name("name")} = 'Joe Blow'/
|
||||
end
|
||||
|
||||
it 'SELECTs DISTINCT when :distinct => true' do
|
||||
|
|
|
@ -13,13 +13,7 @@ module Ransack
|
|||
expect { subject.result }.to_not raise_error
|
||||
end
|
||||
|
||||
it (
|
||||
if ActiveRecord::VERSION::MAJOR == 3
|
||||
"escapes '%', '.' and '\\\\' in value"
|
||||
else
|
||||
"escapes % and \\ in value"
|
||||
end
|
||||
) do
|
||||
it "escapes '%', '.' and '\\\\' in value" do
|
||||
subject.send(:"#{method}=", '%._\\')
|
||||
subject.result.to_sql.should match(regexp)
|
||||
end
|
||||
|
@ -28,12 +22,14 @@ module Ransack
|
|||
describe 'eq' do
|
||||
it 'generates an equality condition for boolean true' do
|
||||
@s.awesome_eq = true
|
||||
@s.result.to_sql.should match /"people"."awesome" = 't'/
|
||||
field = "#{quote_table_name("people")}.#{quote_column_name("awesome")}"
|
||||
@s.result.to_sql.should match /#{field} = #{ActiveRecord::Base.connection.quoted_true}/
|
||||
end
|
||||
|
||||
it 'generates an equality condition for boolean false' do
|
||||
@s.awesome_eq = false
|
||||
@s.result.to_sql.should match /"people"."awesome" = 'f'/
|
||||
field = "#{quote_table_name("people")}.#{quote_column_name("awesome")}"
|
||||
@s.result.to_sql.should match /#{field} = #{ActiveRecord::Base.connection.quoted_false}/
|
||||
end
|
||||
|
||||
it 'does not generate a condition for nil' do
|
||||
|
@ -44,48 +40,56 @@ module Ransack
|
|||
|
||||
describe 'cont' do
|
||||
|
||||
it_has_behavior 'wildcard escaping', :name_cont, (
|
||||
if ActiveRecord::VERSION::MAJOR == 3
|
||||
/"people"."name" LIKE '%\\%\\._\\\\%'/
|
||||
it_has_behavior 'wildcard escaping', :name_cont,
|
||||
(if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
||||
/"people"."name" ILIKE '%\\%\\._\\\\%'/
|
||||
elsif ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
||||
/`people`.`name` LIKE '%\\\\%\\\\._\\\\\\\\%'/
|
||||
else
|
||||
/"people"."name" LIKE '%\\%._\\\\%'/
|
||||
/"people"."name" LIKE '%%._\\%'/
|
||||
end) do
|
||||
subject { @s }
|
||||
end
|
||||
|
||||
it 'generates a LIKE query with value surrounded by %' do
|
||||
@s.name_cont = 'ric'
|
||||
@s.result.to_sql.should match /"people"."name" LIKE '%ric%'/
|
||||
field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
|
||||
@s.result.to_sql.should match /#{field} I?LIKE '%ric%'/
|
||||
end
|
||||
end
|
||||
|
||||
describe 'not_cont' do
|
||||
it_has_behavior 'wildcard escaping', :name_not_cont, (
|
||||
if ActiveRecord::VERSION::MAJOR == 3
|
||||
/"people"."name" NOT LIKE '%\\%\\._\\\\%'/
|
||||
it_has_behavior 'wildcard escaping', :name_not_cont,
|
||||
(if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
||||
/"people"."name" NOT ILIKE '%\\%\\._\\\\%'/
|
||||
elsif ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
||||
/`people`.`name` NOT LIKE '%\\\\%\\\\._\\\\\\\\%'/
|
||||
else
|
||||
/"people"."name" NOT LIKE '%\\%._\\\\%'/
|
||||
/"people"."name" NOT LIKE '%%._\\%'/
|
||||
end) do
|
||||
subject { @s }
|
||||
end
|
||||
|
||||
it 'generates a NOT LIKE query with value surrounded by %' do
|
||||
@s.name_not_cont = 'ric'
|
||||
@s.result.to_sql.should match /"people"."name" NOT LIKE '%ric%'/
|
||||
field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
|
||||
@s.result.to_sql.should match /#{field} NOT I?LIKE '%ric%'/
|
||||
end
|
||||
end
|
||||
|
||||
describe 'null' do
|
||||
it 'generates a value IS NULL query' do
|
||||
@s.name_null = true
|
||||
@s.result.to_sql.should match /"people"."name" IS NULL/
|
||||
field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
|
||||
@s.result.to_sql.should match /#{field} IS NULL/
|
||||
end
|
||||
end
|
||||
|
||||
describe 'not_null' do
|
||||
it 'generates a value IS NOT NULL query' do
|
||||
@s.name_not_null = true
|
||||
@s.result.to_sql.should match /"people"."name" IS NOT NULL/
|
||||
field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
|
||||
@s.result.to_sql.should match /#{field} IS NOT NULL/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -113,25 +113,27 @@ module Ransack
|
|||
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 conditions contextually' do
|
||||
search = Search.new(Person, :children_name_eq => 'Ernie')
|
||||
search.result.should be_an ActiveRecord::Relation
|
||||
where = search.result.where_values.first
|
||||
where.to_sql.should match /"children_people"\."name" = 'Ernie'/
|
||||
where.to_sql.should match /#{children_people_name_field} = 'Ernie'/
|
||||
end
|
||||
|
||||
it 'evaluates compound conditions contextually' do
|
||||
search = Search.new(Person, :children_name_or_name_eq => 'Ernie')
|
||||
search.result.should be_an ActiveRecord::Relation
|
||||
where = search.result.where_values.first
|
||||
where.to_sql.should match /"children_people"\."name" = 'Ernie' OR "people"\."name" = 'Ernie'/
|
||||
where.to_sql.should 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')
|
||||
search.result.should be_an ActiveRecord::Relation
|
||||
where = search.result.where_values.first
|
||||
where.to_sql.should match /"people"."name" = 'Ernie'/
|
||||
where.to_sql.should match /#{people_name_field} = 'Ernie'/
|
||||
end
|
||||
|
||||
it 'evaluates nested conditions' do
|
||||
|
@ -145,9 +147,9 @@ module Ransack
|
|||
)
|
||||
search.result.should be_an ActiveRecord::Relation
|
||||
where = search.result.where_values.first
|
||||
where.to_sql.should match /"children_people"."name" = 'Ernie'/
|
||||
where.to_sql.should match /"people"."name" = 'Ernie'/
|
||||
where.to_sql.should match /"children_people_2"."name" = 'Ernie'/
|
||||
where.to_sql.should match /#{children_people_name_field} = 'Ernie'/
|
||||
where.to_sql.should match /#{people_name_field} = 'Ernie'/
|
||||
where.to_sql.should match /#{quote_table_name("children_people_2")}.#{quote_column_name("name")} = 'Ernie'/
|
||||
end
|
||||
|
||||
it 'evaluates arrays of groupings' do
|
||||
|
@ -161,10 +163,10 @@ module Ransack
|
|||
where = search.result.where_values.first
|
||||
sql = where.to_sql
|
||||
first, second = sql.split(/ AND /)
|
||||
first.should match /"people"."name" = 'Ernie'/
|
||||
first.should match /"children_people"."name" = 'Ernie'/
|
||||
second.should match /"people"."name" = 'Bert'/
|
||||
second.should match /"children_people"."name" = 'Bert'/
|
||||
first.should match /#{people_name_field} = 'Ernie'/
|
||||
first.should match /#{children_people_name_field} = 'Ernie'/
|
||||
second.should match /#{people_name_field} = 'Bert'/
|
||||
second.should match /#{children_people_name_field} = 'Bert'/
|
||||
end
|
||||
|
||||
it 'returns distinct records when passed :distinct => true' do
|
||||
|
|
|
@ -24,7 +24,8 @@ RSpec.configure do |config|
|
|||
|
||||
config.before(:suite) do
|
||||
puts '=' * 80
|
||||
puts "Running specs against ActiveRecord #{ActiveRecord::VERSION::STRING} and ARel #{Arel::VERSION}..."
|
||||
connection_name = ActiveRecord::Base.connection.adapter_name
|
||||
puts "Running specs against #{connection_name}, ActiveRecord #{ActiveRecord::VERSION::STRING} and ARel #{Arel::VERSION}..."
|
||||
puts '=' * 80
|
||||
Schema.create
|
||||
end
|
||||
|
|
|
@ -1,9 +1,26 @@
|
|||
require 'active_record'
|
||||
|
||||
case ENV['DB']
|
||||
when "mysql"
|
||||
ActiveRecord::Base.establish_connection(
|
||||
:adapter => 'sqlite3',
|
||||
:database => ':memory:'
|
||||
adapter: 'mysql2',
|
||||
database: 'ransack',
|
||||
encoding: 'utf8'
|
||||
)
|
||||
when "postgres"
|
||||
ActiveRecord::Base.establish_connection(
|
||||
adapter: 'postgresql',
|
||||
database: 'ransack',
|
||||
username: 'postgres',
|
||||
min_messages: 'warning'
|
||||
)
|
||||
else
|
||||
# Assume SQLite3
|
||||
ActiveRecord::Base.establish_connection(
|
||||
adapter: 'sqlite3',
|
||||
database: ':memory:'
|
||||
)
|
||||
end
|
||||
|
||||
class Person < ActiveRecord::Base
|
||||
if ActiveRecord::VERSION::MAJOR == 3
|
||||
|
@ -86,6 +103,7 @@ module Schema
|
|||
create_table :people, :force => true do |t|
|
||||
t.integer :parent_id
|
||||
t.string :name
|
||||
t.string :email
|
||||
t.integer :salary
|
||||
t.boolean :awesome, :default => false
|
||||
t.timestamps
|
||||
|
|
Loading…
Reference in New Issue