search_cop 1.0.6 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +42 -0
  3. data/.rubocop.yml +128 -0
  4. data/CHANGELOG.md +36 -5
  5. data/CONTRIBUTING.md +18 -0
  6. data/Gemfile +4 -17
  7. data/README.md +143 -35
  8. data/Rakefile +0 -1
  9. data/docker-compose.yml +18 -0
  10. data/gemfiles/rails5.gemfile +13 -0
  11. data/gemfiles/rails6.gemfile +13 -0
  12. data/lib/search_cop.rb +15 -13
  13. data/lib/search_cop/grammar_parser.rb +3 -4
  14. data/lib/search_cop/hash_parser.rb +23 -17
  15. data/lib/search_cop/helpers.rb +15 -0
  16. data/lib/search_cop/query_builder.rb +2 -4
  17. data/lib/search_cop/query_info.rb +0 -2
  18. data/lib/search_cop/search_scope.rb +8 -5
  19. data/lib/search_cop/version.rb +1 -1
  20. data/lib/search_cop/visitors.rb +0 -2
  21. data/lib/search_cop/visitors/mysql.rb +9 -7
  22. data/lib/search_cop/visitors/postgres.rb +18 -8
  23. data/lib/search_cop/visitors/visitor.rb +13 -6
  24. data/lib/search_cop_grammar.rb +18 -9
  25. data/lib/search_cop_grammar.treetop +6 -4
  26. data/lib/search_cop_grammar/attributes.rb +77 -34
  27. data/lib/search_cop_grammar/nodes.rb +7 -2
  28. data/search_cop.gemspec +9 -10
  29. data/test/and_test.rb +6 -8
  30. data/test/boolean_test.rb +7 -9
  31. data/test/database.yml +4 -1
  32. data/test/date_test.rb +38 -12
  33. data/test/datetime_test.rb +45 -12
  34. data/test/default_operator_test.rb +51 -0
  35. data/test/error_test.rb +2 -4
  36. data/test/float_test.rb +16 -11
  37. data/test/fulltext_test.rb +8 -10
  38. data/test/hash_test.rb +39 -31
  39. data/test/integer_test.rb +9 -11
  40. data/test/not_test.rb +6 -8
  41. data/test/or_test.rb +8 -10
  42. data/test/scope_test.rb +11 -13
  43. data/test/search_cop_test.rb +36 -34
  44. data/test/string_test.rb +67 -19
  45. data/test/test_helper.rb +24 -18
  46. data/test/visitor_test.rb +15 -8
  47. metadata +41 -55
  48. data/.travis.yml +0 -34
  49. data/Appraisals +0 -20
  50. data/gemfiles/3.2.gemfile +0 -26
  51. data/gemfiles/4.0.gemfile +0 -26
  52. data/gemfiles/4.1.gemfile +0 -26
  53. data/gemfiles/4.2.gemfile +0 -26
data/test/test_helper.rb CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  require "search_cop"
3
2
 
4
3
  begin
@@ -13,12 +12,12 @@ end
13
12
 
14
13
  require "minitest/autorun"
15
14
  require "active_record"
16
- require "factory_girl"
15
+ require "factory_bot"
17
16
  require "yaml"
18
17
 
19
18
  DATABASE = ENV["DATABASE"] || "sqlite"
20
19
 
21
- ActiveRecord::Base.establish_connection YAML.load_file(File.expand_path("../database.yml", __FILE__))[DATABASE]
20
+ ActiveRecord::Base.establish_connection YAML.load_file(File.expand_path("database.yml", __dir__))[DATABASE]
22
21
 
23
22
  class User < ActiveRecord::Base; end
24
23
 
@@ -28,7 +27,7 @@ class Comment < ActiveRecord::Base
28
27
  belongs_to :user
29
28
 
30
29
  search_scope :search do
31
- attributes :user => "user.username"
30
+ attributes user: "user.username"
32
31
  attributes :title, :message
33
32
  end
34
33
  end
@@ -38,19 +37,23 @@ class Product < ActiveRecord::Base
38
37
 
39
38
  search_scope :search do
40
39
  attributes :title, :description, :brand, :notice, :stock, :price, :created_at, :created_on, :available
41
- attributes :comment => ["comments.title", "comments.message"], :user => ["users.username", "users_products.username"]
42
- attributes :primary => [:title, :description]
40
+ attributes comment: ["comments.title", "comments.message"], user: ["users.username", "users_products.username"]
41
+ attributes primary: [:title, :description]
43
42
 
44
- aliases :users_products => :user
43
+ aliases users_products: :user
45
44
 
46
45
  if DATABASE != "sqlite"
47
- options :title, :type => :fulltext
48
- options :description, :type => :fulltext
49
- options :comment, :type => :fulltext
46
+ options :title, type: :fulltext, coalesce: true
47
+ options :description, type: :fulltext, coalesce: true
48
+ options :comment, type: :fulltext, coalesce: true
50
49
  end
51
50
 
52
51
  if DATABASE == "postgres"
53
- options :title, :dictionary => "english"
52
+ options :title, dictionary: "english"
53
+ end
54
+
55
+ generator :custom_eq do |column_name, raw_value|
56
+ "#{column_name} = #{quote raw_value}"
54
57
  end
55
58
  end
56
59
 
@@ -58,19 +61,23 @@ class Product < ActiveRecord::Base
58
61
  scope { joins "LEFT OUTER JOIN users users_products ON users_products.id = products.user_id" }
59
62
 
60
63
  attributes :title, :description
61
- attributes :user => "users_products.username"
64
+ attributes user: "users_products.username"
62
65
 
63
- options :title, :default => true
64
- aliases :users_products => User
66
+ options :title, default: true
67
+ aliases users_products: User
68
+ end
69
+
70
+ search_scope :search_multi_columns do
71
+ attributes all: [:title, :description]
65
72
  end
66
73
 
67
74
  has_many :comments
68
- has_many :users, :through => :comments
75
+ has_many :users, through: :comments
69
76
 
70
77
  belongs_to :user
71
78
  end
72
79
 
73
- FactoryGirl.define do
80
+ FactoryBot.define do
74
81
  factory :product do
75
82
  end
76
83
 
@@ -118,7 +125,7 @@ if DATABASE == "mysql"
118
125
  end
119
126
 
120
127
  class SearchCop::TestCase
121
- include FactoryGirl::Syntax::Methods
128
+ include FactoryBot::Syntax::Methods
122
129
 
123
130
  def teardown
124
131
  Product.delete_all
@@ -165,4 +172,3 @@ class SearchCop::TestCase
165
172
  ActiveRecord::Base.connection.quote object
166
173
  end
167
174
  end
168
-
data/test/visitor_test.rb CHANGED
@@ -1,5 +1,4 @@
1
-
2
- require File.expand_path("../test_helper", __FILE__)
1
+ require File.expand_path("test_helper", __dir__)
3
2
 
4
3
  class VisitorTest < SearchCop::TestCase
5
4
  def test_and
@@ -53,8 +52,8 @@ class VisitorTest < SearchCop::TestCase
53
52
  def test_matches
54
53
  node = SearchCopGrammar::Attributes::String.new(Product, "products", "notice").matches("Notice")
55
54
 
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"
55
+ assert_equal("(#{quote_table_name "products"}.#{quote_column_name "notice"} IS NOT NULL AND #{quote_table_name "products"}.#{quote_column_name "notice"} LIKE #{quote "%Notice%"} ESCAPE #{quote "\\"})", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] != "postgres"
56
+ assert_equal("(#{quote_table_name "products"}.#{quote_column_name "notice"} IS NOT NULL AND #{quote_table_name "products"}.#{quote_column_name "notice"} ILIKE #{quote "%Notice%"} ESCAPE #{quote "\\"})", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "postgres"
58
57
  end
59
58
 
60
59
  def test_not
@@ -75,7 +74,7 @@ class VisitorTest < SearchCop::TestCase
75
74
  node = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").matches("Query").optimize!
76
75
 
77
76
  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"
77
+ assert_equal("to_tsvector('english', COALESCE(\"products\".\"title\", '')) @@ to_tsquery('english', '''Query''')", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "postgres"
79
78
  end
80
79
 
81
80
  def test_fulltext_and
@@ -85,7 +84,7 @@ class VisitorTest < SearchCop::TestCase
85
84
  node = query1.and(query2).optimize!
86
85
 
87
86
  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"
87
+ assert_equal("(to_tsvector('english', COALESCE(\"products\".\"title\", '')) @@ to_tsquery('english', '(''Query1'') & (''Query2'')'))", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "postgres"
89
88
  end
90
89
 
91
90
  def test_fulltext_or
@@ -95,7 +94,15 @@ class VisitorTest < SearchCop::TestCase
95
94
  node = query1.or(query2).optimize!
96
95
 
97
96
  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"
97
+ assert_equal("(to_tsvector('english', COALESCE(\"products\".\"title\", '')) @@ to_tsquery('english', '(''Query1'') | (''Query2'')'))", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "postgres"
99
98
  end
100
- end
101
99
 
100
+ def test_generator
101
+ generator = lambda do |column_name, value|
102
+ "#{column_name} = #{quote value}"
103
+ end
104
+ node = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").generator(generator, "value").optimize!
105
+
106
+ assert_equal "#{quote_table_name "products"}.#{quote_column_name "title"} = 'value'", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
107
+ end
108
+ end
metadata CHANGED
@@ -1,126 +1,111 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: search_cop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
5
- prerelease:
4
+ version: 1.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Benjamin Vetter
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2015-03-07 00:00:00.000000000 Z
11
+ date: 2021-05-13 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: treetop
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - ">="
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
- name: bundler
28
+ name: activerecord
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ~>
31
+ - - ">="
36
32
  - !ruby/object:Gem::Version
37
- version: '1.3'
33
+ version: 3.0.0
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ~>
38
+ - - ">="
44
39
  - !ruby/object:Gem::Version
45
- version: '1.3'
40
+ version: 3.0.0
46
41
  - !ruby/object:Gem::Dependency
47
- name: rake
42
+ name: bundler
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - ">="
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - ">="
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
- name: activerecord
56
+ name: factory_bot
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - ">="
68
60
  - !ruby/object:Gem::Version
69
- version: 3.0.0
61
+ version: '0'
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - ">="
76
67
  - !ruby/object:Gem::Version
77
- version: 3.0.0
68
+ version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
- name: factory_girl
70
+ name: minitest
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - ">="
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0'
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - ">="
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0'
94
83
  - !ruby/object:Gem::Dependency
95
- name: appraisal
84
+ name: rake
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ! '>='
87
+ - - ">="
100
88
  - !ruby/object:Gem::Version
101
89
  version: '0'
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ! '>='
94
+ - - ">="
108
95
  - !ruby/object:Gem::Version
109
96
  version: '0'
110
97
  - !ruby/object:Gem::Dependency
111
- name: minitest
98
+ name: rubocop
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
- - - ! '>='
101
+ - - ">="
116
102
  - !ruby/object:Gem::Version
117
103
  version: '0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
- - - ! '>='
108
+ - - ">="
124
109
  - !ruby/object:Gem::Version
125
110
  version: '0'
126
111
  description: Search engine like fulltext query support for ActiveRecord
@@ -130,22 +115,23 @@ executables: []
130
115
  extensions: []
131
116
  extra_rdoc_files: []
132
117
  files:
133
- - .gitignore
134
- - .travis.yml
135
- - Appraisals
118
+ - ".github/workflows/test.yml"
119
+ - ".gitignore"
120
+ - ".rubocop.yml"
136
121
  - CHANGELOG.md
122
+ - CONTRIBUTING.md
137
123
  - Gemfile
138
124
  - LICENSE.txt
139
125
  - MIGRATION.md
140
126
  - README.md
141
127
  - Rakefile
142
- - gemfiles/3.2.gemfile
143
- - gemfiles/4.0.gemfile
144
- - gemfiles/4.1.gemfile
145
- - gemfiles/4.2.gemfile
128
+ - docker-compose.yml
129
+ - gemfiles/rails5.gemfile
130
+ - gemfiles/rails6.gemfile
146
131
  - lib/search_cop.rb
147
132
  - lib/search_cop/grammar_parser.rb
148
133
  - lib/search_cop/hash_parser.rb
134
+ - lib/search_cop/helpers.rb
149
135
  - lib/search_cop/query_builder.rb
150
136
  - lib/search_cop/query_info.rb
151
137
  - lib/search_cop/search_scope.rb
@@ -164,6 +150,7 @@ files:
164
150
  - test/database.yml
165
151
  - test/date_test.rb
166
152
  - test/datetime_test.rb
153
+ - test/default_operator_test.rb
167
154
  - test/error_test.rb
168
155
  - test/float_test.rb
169
156
  - test/fulltext_test.rb
@@ -179,27 +166,25 @@ files:
179
166
  homepage: https://github.com/mrkamel/search_cop
180
167
  licenses:
181
168
  - MIT
169
+ metadata: {}
182
170
  post_install_message:
183
171
  rdoc_options: []
184
172
  require_paths:
185
173
  - lib
186
174
  required_ruby_version: !ruby/object:Gem::Requirement
187
- none: false
188
175
  requirements:
189
- - - ! '>='
176
+ - - ">="
190
177
  - !ruby/object:Gem::Version
191
178
  version: '0'
192
179
  required_rubygems_version: !ruby/object:Gem::Requirement
193
- none: false
194
180
  requirements:
195
- - - ! '>='
181
+ - - ">="
196
182
  - !ruby/object:Gem::Version
197
183
  version: '0'
198
184
  requirements: []
199
- rubyforge_project:
200
- rubygems_version: 1.8.23
185
+ rubygems_version: 3.0.3
201
186
  signing_key:
202
- specification_version: 3
187
+ specification_version: 4
203
188
  summary: Easily perform complex search engine like fulltext queries on your ActiveRecord
204
189
  models
205
190
  test_files:
@@ -208,6 +193,7 @@ test_files:
208
193
  - test/database.yml
209
194
  - test/date_test.rb
210
195
  - test/datetime_test.rb
196
+ - test/default_operator_test.rb
211
197
  - test/error_test.rb
212
198
  - test/float_test.rb
213
199
  - test/fulltext_test.rb
data/.travis.yml DELETED
@@ -1,34 +0,0 @@
1
-
2
- addons:
3
- postgresql: "9.3"
4
-
5
- before_script:
6
- - mysql -e 'create database search_cop;'
7
- - psql -c 'create database search_cop;' -U postgres
8
-
9
- rvm:
10
- - 1.9.3
11
- - 2.0.0
12
- - 2.1.1
13
- - rbx-2
14
- - jruby
15
- env:
16
- - DATABASE=sqlite
17
- - DATABASE=mysql
18
- - DATABASE=postgres
19
- matrix:
20
- allow_failures:
21
- - rvm: rbx-2
22
- - rvm: jruby
23
-
24
- gemfile:
25
- - gemfiles/3.2.gemfile
26
- - gemfiles/4.0.gemfile
27
- - gemfiles/4.1.gemfile
28
- - gemfiles/4.2.gemfile
29
-
30
- install:
31
- - "travis_retry bundle install"
32
-
33
- script: "bundle exec rake test"
34
-
data/Appraisals DELETED
@@ -1,20 +0,0 @@
1
- appraise "3.2" do
2
- gem "activerecord", "~> 3.2.18"
3
- gem "search_cop", :path => "../"
4
- end
5
-
6
- appraise "4.0" do
7
- gem "activerecord", "~> 4.0.0"
8
- gem "search_cop", :path => "../"
9
- end
10
-
11
- appraise "4.1" do
12
- gem "activerecord", "~> 4.1.0.beta"
13
- gem "search_cop", :path => "../"
14
- end
15
-
16
- appraise "4.2" do
17
- gem "activerecord", "~> 4.2.0.beta"
18
- gem "search_cop", :path => "../"
19
- end
20
-
data/gemfiles/3.2.gemfile DELETED
@@ -1,26 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "activerecord", "~> 3.2.18"
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 => "../"