Skip to content

Commit

Permalink
ActiveRecord 4.2 support
Browse files Browse the repository at this point in the history
  • Loading branch information
spilin committed Jan 5, 2015
1 parent 7d58a54 commit 27aab98
Show file tree
Hide file tree
Showing 10 changed files with 40 additions and 22 deletions.
8 changes: 4 additions & 4 deletions lib/mincer/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ def sql
# Allows enumerable methods to be called directly on object
def each(&block)
@collection ||= if @relation.is_a?(ActiveRecord::Relation)
@relation.to_a
else
@relation.all
end
@relation.to_a
else
@relation.all
end
@collection.each(&block)
end

Expand Down
1 change: 1 addition & 0 deletions lib/mincer/processors/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Processors
module Helpers

def join_expressions(expressions, join_with)
return expressions.first if expressions.size < 2
case join_with
when :and then Arel::Nodes::And.new(expressions)
when :or then expressions.inject { |accumulator, expression| Arel::Nodes::Or.new(accumulator, expression) }
Expand Down
20 changes: 16 additions & 4 deletions lib/mincer/processors/pg_search/sanitizer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ def sanitize_column

def sanitize_string(options = {})
if sanitizers.empty?
return options[:quote] ? Mincer.connection.quote(@term) : @term
if defined?(Arel::Nodes::Quoted)
return self.class.quote(@term)
elsif options[:quote]
return Mincer.connection.quote(@term)
end
end
@sanitized_string ||= sanitize(@term)
end
Expand All @@ -40,21 +44,29 @@ def self.sanitize_string_quoted(term, *sanitizers)
end

def self.ignore_case(term)
Arel::Nodes::NamedFunction.new('lower', [term])
Arel::Nodes::NamedFunction.new('lower', [quote(term)])
end

def self.ignore_accent(term)
Arel::Nodes::NamedFunction.new('unaccent', [term])
Arel::Nodes::NamedFunction.new('unaccent', [quote(term)])
end

def self.coalesce(term, val = '')
if Mincer.pg_extension_installed?(:unaccent)
Arel::Nodes::NamedFunction.new('coalesce', [term, val])
Arel::Nodes::NamedFunction.new('coalesce', [quote(term), quote(val)])
else
term
end
end

def self.quote(string)
if defined?(Arel::Nodes::Quoted) && !string.is_a?(Arel::Nodes::Quoted) && !string.is_a?(Arel::Nodes::NamedFunction)
Arel::Nodes::Quoted.new(string)
else
string
end
end

end
end
end
Expand Down
5 changes: 5 additions & 0 deletions lib/mincer/processors/pg_search/search_engines/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def initialize(args, search_statements)
def arel_group(sql_string = nil)
sql_string = yield if block_given?
arel_query = sql_string.is_a?(String) ? Arel.sql(sql_string) : sql_string
return arel_query if arel_query.is_a?(Arel::Nodes::Grouping)
Arel::Nodes::Grouping.new(arel_query)
end

Expand Down Expand Up @@ -50,6 +51,10 @@ def rank
nil
end

def quote(string)
Mincer::Processors::PgSearch::Sanitizer.quote(string)
end

end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/mincer/processors/pg_search/search_engines/fulltext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ def ts_vectors_for(search_statement)
# sanitizers += [:coalesce] if (search_statement.columns.size > 1)
documents = search_statement.columns.map do |search_column|
sanitized_term = sanitize_column(search_column, sanitizers)
ts_vector = Arel::Nodes::NamedFunction.new('to_tsvector', [search_statement.dictionary, sanitized_term])
ts_vector = Arel::Nodes::NamedFunction.new('to_tsvector', [quote(search_statement.dictionary), sanitized_term])
end
end

def ts_query_for(search_statement)
terms_delimiter = search_statement.options[:any_word] ? '|' : '&'
tsquery_sql = Arel.sql(search_statement.terms.map { |term| sanitize_string_quoted(term, search_statement.sanitizers(:query)).to_sql }.join(" || ' #{terms_delimiter} ' || "))
Arel::Nodes::NamedFunction.new('to_tsquery', [search_statement.dictionary, tsquery_sql])
Arel::Nodes::NamedFunction.new('to_tsquery', [quote(search_statement.dictionary), tsquery_sql])
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/mincer/version.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Mincer

def self.version
Gem::Version.new '0.2.3'
Gem::Version.new '0.2.4'
end

module VERSION #:nodoc:
Expand Down
2 changes: 1 addition & 1 deletion mincer.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']

spec.add_dependency 'activerecord', '>= 4.0'
spec.add_dependency 'activerecord', '= 4.2'

spec.add_development_dependency 'bundler', '~> 1.3'
spec.add_development_dependency 'rake'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,38 +31,38 @@
it 'generates search condition with one column, one term and no options' do
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:array])
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1])
search_engine.conditions.to_sql.should == %{((("records"."text"::text[]) @> ARRAY['search']))}
search_engine.conditions.to_sql.should == %{(("records"."text"::text[]) @> ARRAY['search'])}
end

it 'generates search condition with two columns, one term and no options' do
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:array])
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1])
search_engine.conditions.to_sql.should == %{((("records"."text"::text[] || "records"."text2"::text[]) @> ARRAY['search']))}
search_engine.conditions.to_sql.should == %{(("records"."text"::text[] || "records"."text2"::text[]) @> ARRAY['search'])}
end

it 'generates search condition with two columns, two terms and no options' do
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:array])
search_engine = search_engine_class.new({ pattern: 'search word' }, [search_statement1])
search_engine.conditions.to_sql.should == %{((("records"."text"::text[] || "records"."text2"::text[]) @> ARRAY['search','word']))}
search_engine.conditions.to_sql.should == %{(("records"."text"::text[] || "records"."text2"::text[]) @> ARRAY['search','word'])}
end

it 'generates search condition with two columns, two terms and option "ignore_accent" set to true ' do
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:array], ignore_accent: true)
search_engine = search_engine_class.new({ pattern: 'search word' }, [search_statement1])
search_engine.conditions.to_sql.should == %{((("records"."text"::text[] || "records"."text2"::text[]) @> ARRAY[unaccent('search'),unaccent('word')]))}
search_engine.conditions.to_sql.should == %{(("records"."text"::text[] || "records"."text2"::text[]) @> ARRAY[unaccent('search'),unaccent('word')])}
end

#TODO: sanitizer can not be set on array columns since we ned to unpack an reconstruct those arrays. Find a solution
it 'generates search condition with two columns, two terms and option "any_word" set to true ' do
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:array], any_word: true)
search_engine = search_engine_class.new({ pattern: 'search word' }, [search_statement1])
search_engine.conditions.to_sql.should == %{((("records"."text"::text[] || "records"."text2"::text[]) && ARRAY['search','word']))}
search_engine.conditions.to_sql.should == %{(("records"."text"::text[] || "records"."text2"::text[]) && ARRAY['search','word'])}
end

it 'generates search condition with two columns, two terms and option "ignore_accent" and "ignore_case" set to true ' do
search_statement1 = search_statement_class.new(['"records"."text"', '"records"."text2"'], engines: [:array], ignore_accent: true, ignore_case: true)
search_engine = search_engine_class.new({ pattern: 'search word' }, [search_statement1])
search_engine.conditions.to_sql.should == %{((("records"."text"::text[] || "records"."text2"::text[]) @> ARRAY[unaccent(lower('search')),unaccent(lower('word'))]))}
search_engine.conditions.to_sql.should == %{(("records"."text"::text[] || "records"."text2"::text[]) @> ARRAY[unaccent(lower('search')),unaccent(lower('word'))])}
end

it 'generates search condition with one column, one term, two statements and no options' do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
it 'generates search condition with one column, one term and no options without columns wrapped with coalesce' do
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:fulltext])
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1])
search_engine.conditions.to_sql.should == %{((to_tsvector('simple', "records"."text") @@ to_tsquery('simple', 'search')))}
search_engine.conditions.to_sql.should == %{(to_tsvector('simple', "records"."text") @@ to_tsquery('simple', 'search'))}
end
end

Expand Down Expand Up @@ -86,7 +86,7 @@
it 'generates search condition with one column, one term and option "dictionary" set to :english' do
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:fulltext], dictionary: :english)
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1])
search_engine.conditions.to_sql.should == %{((to_tsvector('english', "records"."text") @@ to_tsquery('english', 'search')))}
search_engine.conditions.to_sql.should == %{(to_tsvector('english', "records"."text") @@ to_tsquery('english', 'search'))}
end

it 'generates search condition with two search statements one column, one term and no options' do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@
it 'generates search condition with one column, one term and no options' do
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:trigram])
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1])
search_engine.conditions.to_sql.should == %{(("records"."text" % 'search'))}
search_engine.conditions.to_sql.should == %{("records"."text" % 'search')}
end

it 'generates search condition with one column, one term and "threshold" option set to 0.5' do
search_statement1 = search_statement_class.new(['"records"."text"'], engines: [:trigram], threshold: 0.5)
search_engine = search_engine_class.new({ pattern: 'search' }, [search_statement1])
search_engine.conditions.to_sql.should == %{((similarity("records"."text", 'search') >= 0.5))}
search_engine.conditions.to_sql.should == %{(similarity("records"."text", 'search') >= 0.5)}
end

it 'generates search condition with two columns, one term and no options' do
Expand Down

0 comments on commit 27aab98

Please sign in to comment.