search_cop 1.0.6 → 1.2.0

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.
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 => "../"