search_cop 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.travis.yml CHANGED
@@ -25,6 +25,7 @@ gemfile:
25
25
  - gemfiles/3.2.gemfile
26
26
  - gemfiles/4.0.gemfile
27
27
  - gemfiles/4.1.gemfile
28
+ - gemfiles/4.2.gemfile
28
29
 
29
30
  install:
30
31
  - "travis_retry bundle install"
data/Appraisals CHANGED
@@ -12,3 +12,9 @@ appraise "4.1" do
12
12
  gem "activerecord", "~> 4.1.0.beta"
13
13
  gem "search_cop", :path => "../"
14
14
  end
15
+
16
+ appraise "4.2" do
17
+ gem "activerecord", "~> 4.2.0.beta"
18
+ gem "search_cop", :path => "../"
19
+ end
20
+
data/README.md CHANGED
@@ -97,7 +97,7 @@ end
97
97
 
98
98
  ## How does it work
99
99
 
100
- SearchCop parses the query and maps it to an SQL Query using Arel.
100
+ SearchCop parses the query and maps it to an SQL Query in a database agnostic way.
101
101
  Thus, SearchCop is not bound to a specific RDBMS.
102
102
 
103
103
  ```ruby
@@ -0,0 +1,26 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.2.0.beta"
6
+ gem "search_cop", :path => "../"
7
+
8
+ platforms :jruby do
9
+ gem "activerecord-jdbcmysql-adapter"
10
+ gem "activerecord-jdbcsqlite3-adapter"
11
+ gem "activerecord-jdbcpostgresql-adapter"
12
+ end
13
+
14
+ platforms :ruby do
15
+ gem "sqlite3"
16
+ gem "mysql2"
17
+ gem "pg"
18
+ end
19
+
20
+ platforms :rbx do
21
+ gem "racc"
22
+ gem "rubysl", "~> 2.0"
23
+ gem "psych"
24
+ end
25
+
26
+ gemspec :path => "../"
@@ -9,7 +9,7 @@ module SearchCop
9
9
 
10
10
  arel = SearchCop::Parser.parse(query, query_info).optimize!
11
11
 
12
- self.sql = model.connection.visitor.accept(arel)
12
+ self.sql = SearchCop::Visitors::Visitor.new(model.connection).visit(arel)
13
13
  end
14
14
 
15
15
  def associations
@@ -29,7 +29,7 @@ module SearchCop
29
29
  def attributes(*args)
30
30
  args.each do |arg|
31
31
  attributes_hash arg.is_a?(Hash) ? arg : { arg => arg }
32
- end
32
+ end
33
33
  end
34
34
 
35
35
  def options(key, options = {})
@@ -54,8 +54,8 @@ module SearchCop
54
54
  table, attribute = column.to_s =~ /\./ ? column.to_s.split(".") : [model.name.tableize, column]
55
55
 
56
56
  "#{table}.#{attribute}"
57
- end
58
- end
57
+ end
58
+ end
59
59
  end
60
60
  end
61
61
  end
@@ -1,3 +1,3 @@
1
1
  module SearchCop
2
- VERSION = "1.0.2"
2
+ VERSION = "1.0.3"
3
3
  end
@@ -0,0 +1,43 @@
1
+
2
+ module SearchCop
3
+ module Visitors
4
+ module Mysql
5
+ class FulltextQuery < Visitor
6
+ def visit_SearchCopGrammar_Nodes_MatchesFulltextNot(node)
7
+ node.right.split(/[\s+'"<>()~-]+/).collect { |word| "-#{word}" }.join(" ")
8
+ end
9
+
10
+ def visit_SearchCopGrammar_Nodes_MatchesFulltext(node)
11
+ words = node.right.split(/[\s+'"<>()~-]+/)
12
+
13
+ words.size > 1 ? "\"#{words.join " "}\"" : words.first
14
+ end
15
+
16
+ def visit_SearchCopGrammar_Nodes_And_Fulltext(node)
17
+ res = node.nodes.collect do |node|
18
+ if node.is_a?(SearchCopGrammar::Nodes::MatchesFulltextNot)
19
+ visit node
20
+ else
21
+ node.nodes.size > 1 ? "+(#{visit node})" : "+#{visit node}"
22
+ end
23
+ end
24
+
25
+ res.join " "
26
+ end
27
+
28
+ def visit_SearchCopGrammar_Nodes_Or_Fulltext(node)
29
+ node.nodes.collect { |node| "(#{visit node})" }.join(" ")
30
+ end
31
+ end
32
+
33
+ def visit_SearchCopGrammar_Attributes_Collection(node)
34
+ node.attributes.collect { |attribute| visit attribute }.join(", ")
35
+ end
36
+
37
+ def visit_SearchCopGrammar_Nodes_FulltextExpression(node)
38
+ "MATCH(#{visit node.collection}) AGAINST(#{visit FulltextQuery.new(connection).visit(node.node)} IN BOOLEAN MODE)"
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,39 @@
1
+
2
+ module SearchCop
3
+ module Visitors
4
+ module Postgres
5
+ class FulltextQuery < Visitor
6
+ def visit_SearchCopGrammar_Nodes_MatchesFulltextNot(node)
7
+ "!'#{node.right.gsub /[\s&|!:'"]+/, " "}'"
8
+ end
9
+
10
+ def visit_SearchCopGrammar_Nodes_MatchesFulltext(node)
11
+ "'#{node.right.gsub /[\s&|!:'"]+/, " "}'"
12
+ end
13
+
14
+ def visit_SearchCopGrammar_Nodes_And_Fulltext(node)
15
+ node.nodes.collect { |node| "(#{visit node})" }.join(" & ")
16
+ end
17
+
18
+ def visit_SearchCopGrammar_Nodes_Or_Fulltext(node)
19
+ node.nodes.collect { |node| "(#{visit node})" }.join(" | ")
20
+ end
21
+ end
22
+
23
+ def visit_SearchCopGrammar_Nodes_Matches(node)
24
+ "#{visit node.left} ILIKE #{visit node.right}"
25
+ end
26
+
27
+ def visit_SearchCopGrammar_Attributes_Collection(node)
28
+ node.attributes.collect { |attribute| visit attribute }.join(" || ' ' || ")
29
+ end
30
+
31
+ def visit_SearchCopGrammar_Nodes_FulltextExpression(node)
32
+ dictionary = node.collection.options[:dictionary] || "simple"
33
+
34
+ "to_tsvector(#{visit dictionary}, #{visit node.collection}) @@ to_tsquery(#{visit dictionary}, #{visit FulltextQuery.new(connection).visit(node.node)})"
35
+ end
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,96 @@
1
+
2
+ module SearchCop
3
+ module Visitors
4
+ class Visitor
5
+ attr_accessor :connection
6
+
7
+ def initialize(connection)
8
+ @connection = connection
9
+
10
+ extend(SearchCop::Visitors::Mysql) if @connection.class.name =~ /mysql/i
11
+ extend(SearchCop::Visitors::Postgres) if @connection.class.name =~ /postgres/i
12
+ end
13
+
14
+ def visit(visit_node = node)
15
+ send "visit_#{visit_node.class.name.gsub /::/, "_"}", visit_node
16
+ end
17
+
18
+ def visit_SearchCopGrammar_Nodes_And(node)
19
+ "(#{node.nodes.collect { |n| visit n }.join(" AND ")})"
20
+ end
21
+
22
+ def visit_SearchCopGrammar_Nodes_Or(node)
23
+ "(#{node.nodes.collect { |n| visit n }.join(" OR ")})"
24
+ end
25
+
26
+ def visit_SearchCopGrammar_Nodes_GreaterThan(node)
27
+ "#{visit node.left} > #{visit node.right}"
28
+ end
29
+
30
+ def visit_SearchCopGrammar_Nodes_GreaterThanOrEqual(node)
31
+ "#{visit node.left} >= #{visit node.right}"
32
+ end
33
+
34
+ def visit_SearchCopGrammar_Nodes_LessThan(node)
35
+ "#{visit node.left} < #{visit node.right}"
36
+ end
37
+
38
+ def visit_SearchCopGrammar_Nodes_LessThanOrEqual(node)
39
+ "#{visit node.left} <= #{visit node.right}"
40
+ end
41
+
42
+ def visit_SearchCopGrammar_Nodes_Equality(node)
43
+ "#{visit node.left} = #{visit node.right}"
44
+ end
45
+
46
+ def visit_SearchCopGrammar_Nodes_NotEqual(node)
47
+ "#{visit node.left} != #{visit node.right}"
48
+ end
49
+
50
+ def visit_SearchCopGrammar_Nodes_Matches(node)
51
+ "#{visit node.left} LIKE #{visit node.right}"
52
+ end
53
+
54
+ def visit_SearchCopGrammar_Nodes_Not(node)
55
+ "NOT (#{visit node.object})"
56
+ end
57
+
58
+ def quote_table_name(name)
59
+ connection.quote_table_name name
60
+ end
61
+
62
+ def quote_column_name(name)
63
+ connection.quote_column_name name
64
+ end
65
+
66
+ def visit_attribute(attribute)
67
+ "#{quote_table_name attribute.table_alias}.#{quote_column_name attribute.column_name}"
68
+ end
69
+
70
+ alias :visit_SearchCopGrammar_Attributes_String :visit_attribute
71
+ alias :visit_SearchCopGrammar_Attributes_Text :visit_attribute
72
+ alias :visit_SearchCopGrammar_Attributes_Float :visit_attribute
73
+ alias :visit_SearchCopGrammar_Attributes_Integer :visit_attribute
74
+ alias :visit_SearchCopGrammar_Attributes_Decimal :visit_attribute
75
+ alias :visit_SearchCopGrammar_Attributes_Datetime :visit_attribute
76
+ alias :visit_SearchCopGrammar_Attributes_Timestamp :visit_attribute
77
+ alias :visit_SearchCopGrammar_Attributes_Date :visit_attribute
78
+ alias :visit_SearchCopGrammar_Attributes_Time :visit_attribute
79
+ alias :visit_SearchCopGrammar_Attributes_Boolean :visit_attribute
80
+
81
+ def quote(value)
82
+ connection.quote value
83
+ end
84
+
85
+ alias :visit_TrueClass :quote
86
+ alias :visit_FalseClass :quote
87
+ alias :visit_String :quote
88
+ alias :visit_Time :quote
89
+ alias :visit_Date :quote
90
+ alias :visit_Float :quote
91
+ alias :visit_Fixnum :quote
92
+ alias :visit_Symbol :quote
93
+ end
94
+ end
95
+ end
96
+
@@ -0,0 +1,5 @@
1
+
2
+ require "search_cop/visitors/visitor"
3
+ require "search_cop/visitors/mysql"
4
+ require "search_cop/visitors/postgres"
5
+
data/lib/search_cop.rb CHANGED
@@ -1,11 +1,11 @@
1
1
 
2
2
  require "search_cop/version"
3
- require "search_cop/arel"
4
3
  require "search_cop/search_scope"
5
4
  require "search_cop/query_info"
6
5
  require "search_cop/query_builder"
7
6
  require "search_cop/grammar_parser"
8
7
  require "search_cop/hash_parser"
8
+ require "search_cop/visitors"
9
9
 
10
10
  module SearchCop
11
11
  class SpecificationError < StandardError; end
@@ -82,16 +82,18 @@ module SearchCopGrammar
82
82
 
83
83
  raise(SearchCop::UnknownAttribute, "Unknown attribute #{attribute_definition}") unless klass.columns_hash[column]
84
84
 
85
- Attributes.const_get(klass.columns_hash[column].type.to_s.classify).new(klass.arel_table.alias(alias_for(table))[column], klass, options)
85
+ Attributes.const_get(klass.columns_hash[column].type.to_s.classify).new(klass, alias_for(table), column, options)
86
86
  end
87
87
  end
88
88
 
89
89
  class Base
90
- attr_reader :attribute, :options
90
+ attr_reader :attribute, :table_alias, :column_name, :options
91
91
 
92
- def initialize(attribute, klass, options = {})
93
- @attribute = attribute
92
+ def initialize(klass, table_alias, column_name, options = {})
93
+ @attribute = klass.arel_table.alias(table_alias)[column_name]
94
94
  @klass = klass
95
+ @table_alias = table_alias
96
+ @column_name = column_name
95
97
  @options = (options || {})
96
98
  end
97
99
 
@@ -115,16 +117,16 @@ module SearchCopGrammar
115
117
  define_method method do |value|
116
118
  raise(SearchCop::IncompatibleDatatype, "Incompatible datatype for #{value}") unless compatible?(value)
117
119
 
118
- SearchCopGrammar::Nodes.const_get(class_name).new(@attribute, map(value))
120
+ SearchCopGrammar::Nodes.const_get(class_name).new(self, map(value))
119
121
  end
120
122
  end
121
123
 
122
124
  def method_missing(name, *args, &block)
123
- @attribute.send name, *args, &block
125
+ @attribute.send(name, *args, &block)
124
126
  end
125
127
 
126
128
  def respond_to?(*args)
127
- @attribute.respond_to? *args
129
+ super(*args) || @attribute.respond_to?(*args)
128
130
  end
129
131
  end
130
132
 
@@ -154,9 +156,18 @@ module SearchCopGrammar
154
156
 
155
157
  false
156
158
  end
159
+
160
+ def map(value)
161
+ value.to_f
162
+ end
163
+ end
164
+
165
+ class Integer < Float
166
+ def map(value)
167
+ value.to_i
168
+ end
157
169
  end
158
170
 
159
- class Integer < Float; end
160
171
  class Decimal < Float; end
161
172
 
162
173
  class Datetime < WithoutMatches
@@ -159,7 +159,7 @@ module SearchCopGrammar
159
159
  def initialize(collection, *nodes)
160
160
  @collection = collection
161
161
 
162
- super *nodes
162
+ super(*nodes)
163
163
  end
164
164
 
165
165
  def fulltext?
@@ -21,7 +21,7 @@ grammar SearchCopGrammar
21
21
  end
22
22
 
23
23
  rule parentheses_expression
24
- '(' complex_expression ')' <ParenthesesExpression>
24
+ '(' complex_expression ')' <ParenthesesExpression>
25
25
  end
26
26
 
27
27
  rule not_expression
data/test/test_helper.rb CHANGED
@@ -146,5 +146,17 @@ class SearchCop::TestCase
146
146
  def assert_not_nil(value)
147
147
  assert value
148
148
  end
149
+
150
+ def quote_table_name(name)
151
+ ActiveRecord::Base.connection.quote_table_name name
152
+ end
153
+
154
+ def quote_column_name(name)
155
+ ActiveRecord::Base.connection.quote_column_name name
156
+ end
157
+
158
+ def quote(object)
159
+ ActiveRecord::Base.connection.quote object
160
+ end
149
161
  end
150
162
 
@@ -0,0 +1,101 @@
1
+
2
+ require File.expand_path("../test_helper", __FILE__)
3
+
4
+ class VisitorTest < SearchCop::TestCase
5
+ def test_and
6
+ node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").gt(0).and(SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").lt(2))
7
+
8
+ assert_equal "(#{quote_table_name "products"}.#{quote_column_name "stock"} > 0 AND #{quote_table_name "products"}.#{quote_column_name "stock"} < 2)", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
9
+ end
10
+
11
+ def test_or
12
+ node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").gt(0).or(SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").lt(2))
13
+
14
+ assert_equal "(#{quote_table_name "products"}.#{quote_column_name "stock"} > 0 OR #{quote_table_name "products"}.#{quote_column_name "stock"} < 2)", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
15
+ end
16
+
17
+ def test_greater_than
18
+ node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").gt(1)
19
+
20
+ assert_equal "#{quote_table_name "products"}.#{quote_column_name "stock"} > 1", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
21
+ end
22
+
23
+ def test_greater_than_or_equal
24
+ node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").gteq(1)
25
+
26
+ assert_equal "#{quote_table_name "products"}.#{quote_column_name "stock"} >= 1", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
27
+ end
28
+
29
+ def test_less_than
30
+ node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").lt(1)
31
+
32
+ assert_equal "#{quote_table_name "products"}.#{quote_column_name "stock"} < 1", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
33
+ end
34
+
35
+ def test_less_than_or_equal
36
+ node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").lteq(1)
37
+
38
+ assert_equal "#{quote_table_name "products"}.#{quote_column_name "stock"} <= 1", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
39
+ end
40
+
41
+ def test_equality
42
+ node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").eq(1)
43
+
44
+ assert_equal "#{quote_table_name "products"}.#{quote_column_name "stock"} = 1", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
45
+ end
46
+
47
+ def test_not_equal
48
+ node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").not_eq(1)
49
+
50
+ assert_equal "#{quote_table_name "products"}.#{quote_column_name "stock"} != 1", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
51
+ end
52
+
53
+ def test_matches
54
+ node = SearchCopGrammar::Attributes::String.new(Product, "products", "notice").matches("Notice")
55
+
56
+ assert_equal("#{quote_table_name "products"}.#{quote_column_name "notice"} LIKE #{quote "%Notice%"}", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] != "postgres"
57
+ assert_equal("#{quote_table_name "products"}.#{quote_column_name "notice"} ILIKE #{quote "%Notice%"}", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "postgres"
58
+ end
59
+
60
+ def test_not
61
+ node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").eq(1).not
62
+
63
+ assert_equal "NOT (#{quote_table_name "products"}.#{quote_column_name "stock"} = 1)", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
64
+ end
65
+
66
+ def test_attribute
67
+ # Already tested
68
+ end
69
+
70
+ def test_quote
71
+ assert_equal quote("Test"), SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit("Test")
72
+ end
73
+
74
+ def test_fulltext
75
+ node = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").matches("Query").optimize!
76
+
77
+ assert_equal("MATCH(`products`.`title`) AGAINST('Query' IN BOOLEAN MODE)", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "mysql"
78
+ assert_equal("to_tsvector('english', \"products\".\"title\") @@ to_tsquery('english', '''Query''')", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "postgres"
79
+ end
80
+
81
+ def test_fulltext_and
82
+ query1 = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").matches("Query1")
83
+ query2 = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").matches("Query2")
84
+
85
+ node = query1.and(query2).optimize!
86
+
87
+ assert_equal("(MATCH(`products`.`title`) AGAINST('+Query1 +Query2' IN BOOLEAN MODE))", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "mysql"
88
+ assert_equal("(to_tsvector('english', \"products\".\"title\") @@ to_tsquery('english', '(''Query1'') & (''Query2'')'))", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "postgres"
89
+ end
90
+
91
+ def test_fulltext_or
92
+ query1 = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").matches("Query1")
93
+ query2 = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").matches("Query2")
94
+
95
+ node = query1.or(query2).optimize!
96
+
97
+ assert_equal("(MATCH(`products`.`title`) AGAINST('(Query1) (Query2)' IN BOOLEAN MODE))", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "mysql"
98
+ assert_equal("(to_tsvector('english', \"products\".\"title\") @@ to_tsquery('english', '(''Query1'') | (''Query2'')'))", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "postgres"
99
+ end
100
+ end
101
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: search_cop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-10-16 00:00:00.000000000 Z
12
+ date: 2014-12-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: treetop
@@ -142,15 +142,18 @@ files:
142
142
  - gemfiles/3.2.gemfile
143
143
  - gemfiles/4.0.gemfile
144
144
  - gemfiles/4.1.gemfile
145
+ - gemfiles/4.2.gemfile
145
146
  - lib/search_cop.rb
146
- - lib/search_cop/arel.rb
147
- - lib/search_cop/arel/visitors.rb
148
147
  - lib/search_cop/grammar_parser.rb
149
148
  - lib/search_cop/hash_parser.rb
150
149
  - lib/search_cop/query_builder.rb
151
150
  - lib/search_cop/query_info.rb
152
151
  - lib/search_cop/search_scope.rb
153
152
  - lib/search_cop/version.rb
153
+ - lib/search_cop/visitors.rb
154
+ - lib/search_cop/visitors/mysql.rb
155
+ - lib/search_cop/visitors/postgres.rb
156
+ - lib/search_cop/visitors/visitor.rb
154
157
  - lib/search_cop_grammar.rb
155
158
  - lib/search_cop_grammar.treetop
156
159
  - lib/search_cop_grammar/attributes.rb
@@ -172,6 +175,7 @@ files:
172
175
  - test/search_cop_test.rb
173
176
  - test/string_test.rb
174
177
  - test/test_helper.rb
178
+ - test/visitor_test.rb
175
179
  homepage: https://github.com/mrkamel/search_cop
176
180
  licenses:
177
181
  - MIT
@@ -215,3 +219,4 @@ test_files:
215
219
  - test/search_cop_test.rb
216
220
  - test/string_test.rb
217
221
  - test/test_helper.rb
222
+ - test/visitor_test.rb
@@ -1,223 +0,0 @@
1
-
2
- module SearchCop
3
- module Arel
4
- module Visitors
5
- module ToSql
6
- if ::Arel::VERSION >= "4.0.1"
7
- def visit_SearchCopGrammar_Nodes_And(o, a)
8
- visit ::Arel::Nodes::Grouping.new(o.nodes.inject { |res, cur| ::Arel::Nodes::And.new [res, cur] }), a
9
- end
10
-
11
- def visit_SearchCopGrammar_Nodes_Or(o, a)
12
- visit ::Arel::Nodes::Grouping.new(o.nodes.inject { |res, cur| ::Arel::Nodes::Or.new res, cur }), a
13
- end
14
-
15
- def visit_SearchCopGrammar_Nodes_Equality(o, a)
16
- visit ::Arel::Nodes::Equality.new(o.left, o.right), a
17
- end
18
-
19
- def visit_SearchCopGrammar_Nodes_NotEqual(o, a)
20
- visit ::Arel::Nodes::NotEqual.new(o.left, o.right), a
21
- end
22
-
23
- def visit_SearchCopGrammar_Nodes_LessThan(o, a)
24
- visit ::Arel::Nodes::LessThan.new(o.left, o.right), a
25
- end
26
-
27
- def visit_SearchCopGrammar_Nodes_LessThanOrEqual(o, a)
28
- visit ::Arel::Nodes::LessThanOrEqual.new(o.left, o.right), a
29
- end
30
-
31
- def visit_SearchCopGrammar_Nodes_GreaterThan(o, a)
32
- visit ::Arel::Nodes::GreaterThan.new(o.left, o.right), a
33
- end
34
-
35
- def visit_SearchCopGrammar_Nodes_GreaterThanOrEqual(o, a)
36
- visit ::Arel::Nodes::GreaterThanOrEqual.new(o.left, o.right), a
37
- end
38
-
39
- def visit_SearchCopGrammar_Nodes_Not(o, a)
40
- visit ::Arel::Nodes::Not.new(o.object), a
41
- end
42
-
43
- def visit_SearchCopGrammar_Nodes_Matches(o, a)
44
- visit ::Arel::Nodes::Matches.new(o.left, o.right), a
45
- end
46
- else
47
- def visit_SearchCopGrammar_Nodes_And(o)
48
- visit ::Arel::Nodes::Grouping.new(o.nodes.inject { |res, cur| ::Arel::Nodes::And.new [res, cur] })
49
- end
50
-
51
- def visit_SearchCopGrammar_Nodes_Or(o)
52
- visit ::Arel::Nodes::Grouping.new(o.nodes.inject { |res, cur| ::Arel::Nodes::Or.new res, cur })
53
- end
54
-
55
- def visit_SearchCopGrammar_Nodes_Equality(o)
56
- visit ::Arel::Nodes::Equality.new(o.left, o.right)
57
- end
58
-
59
- def visit_SearchCopGrammar_Nodes_NotEqual(o)
60
- visit ::Arel::Nodes::NotEqual.new(o.left, o.right)
61
- end
62
-
63
- def visit_SearchCopGrammar_Nodes_LessThan(o)
64
- visit ::Arel::Nodes::LessThan.new(o.left, o.right)
65
- end
66
-
67
- def visit_SearchCopGrammar_Nodes_LessThanOrEqual(o)
68
- visit ::Arel::Nodes::LessThanOrEqual.new(o.left, o.right)
69
- end
70
-
71
- def visit_SearchCopGrammar_Nodes_GreaterThan(o)
72
- visit ::Arel::Nodes::GreaterThan.new(o.left, o.right)
73
- end
74
-
75
- def visit_SearchCopGrammar_Nodes_GreaterThanOrEqual(o)
76
- visit ::Arel::Nodes::GreaterThanOrEqual.new(o.left, o.right)
77
- end
78
-
79
- def visit_SearchCopGrammar_Nodes_Not(o)
80
- visit ::Arel::Nodes::Not.new(o.object)
81
- end
82
-
83
- def visit_SearchCopGrammar_Nodes_Matches(o)
84
- visit ::Arel::Nodes::Matches.new(o.left, o.right)
85
- end
86
- end
87
- end
88
-
89
- module MySQL
90
- if ::Arel::VERSION >= "4.0.1"
91
- def visit_SearchCopGrammar_Attributes_Collection(o, a)
92
- o.attributes.collect { |attribute| visit attribute.attribute, a }.join(", ")
93
- end
94
-
95
- def visit_SearchCopGrammar_Nodes_FulltextExpression(o, a)
96
- "MATCH(#{visit o.collection, a}) AGAINST(#{visit visit(o.node, a), a} IN BOOLEAN MODE)"
97
- end
98
-
99
- def visit_SearchCopGrammar_Nodes_MatchesFulltextNot(o, a)
100
- o.right.split(/[\s+'"<>()~-]+/).collect { |word| "-#{word}" }.join(" ")
101
- end
102
-
103
- def visit_SearchCopGrammar_Nodes_MatchesFulltext(o, a)
104
- words = o.right.split(/[\s+'"<>()~-]+/)
105
-
106
- words.size > 1 ? "\"#{words.join " "}\"" : words.first
107
- end
108
-
109
- def visit_SearchCopGrammar_Nodes_And_Fulltext(o, a)
110
- res = o.nodes.collect do |node|
111
- if node.is_a?(SearchCopGrammar::Nodes::MatchesFulltextNot)
112
- visit node, a
113
- else
114
- node.nodes.size > 1 ? "+(#{visit node, a})" : "+#{visit node, a}"
115
- end
116
- end
117
-
118
- res.join " "
119
- end
120
-
121
- def visit_SearchCopGrammar_Nodes_Or_Fulltext(o, a)
122
- o.nodes.collect { |node| "(#{visit node, a})" }.join(" ")
123
- end
124
- else
125
- def visit_SearchCopGrammar_Attributes_Collection(o)
126
- o.attributes.collect { |attribute| visit attribute.attribute }.join(", ")
127
- end
128
-
129
- def visit_SearchCopGrammar_Nodes_FulltextExpression(o)
130
- "MATCH(#{visit o.collection}) AGAINST(#{visit visit(o.node)} IN BOOLEAN MODE)"
131
- end
132
-
133
- def visit_SearchCopGrammar_Nodes_MatchesFulltextNot(o)
134
- o.right.split(/[\s+'"<>()~-]+/).collect { |word| "-#{word}" }.join(" ")
135
- end
136
-
137
- def visit_SearchCopGrammar_Nodes_MatchesFulltext(o)
138
- words = o.right.split(/[\s+'"<>()~-]+/)
139
-
140
- words.size > 1 ? "\"#{words.join " "}\"" : words.first
141
- end
142
-
143
- def visit_SearchCopGrammar_Nodes_And_Fulltext(o)
144
- res = o.nodes.collect do |node|
145
- if node.is_a?(SearchCopGrammar::Nodes::MatchesFulltextNot)
146
- visit node
147
- else
148
- node.nodes.size > 1 ? "+(#{visit node})" : "+#{visit node}"
149
- end
150
- end
151
-
152
- res.join " "
153
- end
154
-
155
- def visit_SearchCopGrammar_Nodes_Or_Fulltext(o)
156
- o.nodes.collect { |node| "(#{visit node})" }.join(" ")
157
- end
158
- end
159
- end
160
-
161
- module PostgreSQL
162
- if ::Arel::VERSION >= "4.0.1"
163
- def visit_SearchCopGrammar_Attributes_Collection(o, a)
164
- o.attributes.collect { |attribute| visit attribute.attribute, a }.join(" || ' ' || ")
165
- end
166
-
167
- def visit_SearchCopGrammar_Nodes_FulltextExpression(o, a)
168
- dictionary = o.collection.options[:dictionary] || "simple"
169
-
170
- "to_tsvector(#{visit dictionary, a}, #{visit o.collection, a}) @@ to_tsquery(#{visit dictionary, a}, #{visit visit(o.node, a), a})"
171
- end
172
-
173
- def visit_SearchCopGrammar_Nodes_MatchesFulltextNot(o, a)
174
- "!'#{o.right}'"
175
- end
176
-
177
- def visit_SearchCopGrammar_Nodes_MatchesFulltext(o, a)
178
- "'#{o.right.gsub /[\s&|!:'"]+/, " "}'"
179
- end
180
-
181
- def visit_SearchCopGrammar_Nodes_And_Fulltext(o, a)
182
- o.nodes.collect { |node| "(#{visit node, a})" }.join(" & ")
183
- end
184
-
185
- def visit_SearchCopGrammar_Nodes_Or_Fulltext(o, a)
186
- o.nodes.collect { |node| "(#{visit node, a})" }.join(" | ")
187
- end
188
- else
189
- def visit_SearchCopGrammar_Attributes_Collection(o)
190
- o.attributes.collect { |attribute| visit attribute.attribute }.join(" || ' ' || ")
191
- end
192
-
193
- def visit_SearchCopGrammar_Nodes_FulltextExpression(o)
194
- dictionary = o.collection.options[:dictionary] || "simple"
195
-
196
- "to_tsvector(#{visit dictionary.to_sym}, #{visit o.collection}) @@ to_tsquery(#{visit dictionary.to_sym}, #{visit visit(o.node)})" # to_sym fixes a 3.2 + postgres bug
197
- end
198
-
199
- def visit_SearchCopGrammar_Nodes_MatchesFulltextNot(o)
200
- "!'#{o.right}'"
201
- end
202
-
203
- def visit_SearchCopGrammar_Nodes_MatchesFulltext(o)
204
- "'#{o.right.gsub /[\s&|!:'"]+/, " "}'"
205
- end
206
-
207
- def visit_SearchCopGrammar_Nodes_And_Fulltext(o)
208
- o.nodes.collect { |node| "(#{visit node})" }.join(" & ")
209
- end
210
-
211
- def visit_SearchCopGrammar_Nodes_Or_Fulltext(o)
212
- o.nodes.collect { |node| "(#{visit node})" }.join(" | ")
213
- end
214
- end
215
- end
216
- end
217
- end
218
- end
219
-
220
- Arel::Visitors::PostgreSQL.send :include, SearchCop::Arel::Visitors::PostgreSQL
221
- Arel::Visitors::MySQL.send :include, SearchCop::Arel::Visitors::MySQL
222
- Arel::Visitors::ToSql.send :include, SearchCop::Arel::Visitors::ToSql
223
-
@@ -1,4 +0,0 @@
1
-
2
- require "arel"
3
- require "search_cop/arel/visitors"
4
-