pg_search 2.3.5 → 2.3.7

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +80 -0
  3. data/.standard.yml +6 -0
  4. data/CHANGELOG.md +20 -0
  5. data/Gemfile +19 -6
  6. data/LICENSE +1 -1
  7. data/README.md +61 -39
  8. data/Rakefile +9 -6
  9. data/lib/pg_search/configuration/column.rb +6 -4
  10. data/lib/pg_search/configuration/foreign_column.rb +1 -1
  11. data/lib/pg_search/configuration.rb +13 -3
  12. data/lib/pg_search/document.rb +8 -8
  13. data/lib/pg_search/features/dmetaphone.rb +1 -1
  14. data/lib/pg_search/features/feature.rb +1 -1
  15. data/lib/pg_search/features/trigram.rb +4 -4
  16. data/lib/pg_search/features/tsearch.rb +15 -14
  17. data/lib/pg_search/migration/dmetaphone_generator.rb +2 -2
  18. data/lib/pg_search/migration/generator.rb +5 -5
  19. data/lib/pg_search/migration/multisearch_generator.rb +2 -2
  20. data/lib/pg_search/model.rb +6 -6
  21. data/lib/pg_search/multisearch.rb +16 -6
  22. data/lib/pg_search/multisearchable.rb +7 -7
  23. data/lib/pg_search/normalizer.rb +5 -5
  24. data/lib/pg_search/scope_options.rb +28 -10
  25. data/lib/pg_search/tasks.rb +2 -2
  26. data/lib/pg_search/version.rb +1 -1
  27. data/lib/pg_search.rb +5 -5
  28. data/pg_search.gemspec +17 -29
  29. data/spec/.rubocop.yml +20 -7
  30. data/spec/integration/.rubocop.yml +2 -2
  31. data/spec/integration/associations_spec.rb +106 -106
  32. data/spec/integration/deprecation_spec.rb +7 -8
  33. data/spec/integration/pg_search_spec.rb +339 -292
  34. data/spec/integration/single_table_inheritance_spec.rb +5 -5
  35. data/spec/lib/pg_search/configuration/association_spec.rb +15 -15
  36. data/spec/lib/pg_search/configuration/column_spec.rb +13 -1
  37. data/spec/lib/pg_search/configuration/foreign_column_spec.rb +4 -4
  38. data/spec/lib/pg_search/features/dmetaphone_spec.rb +4 -4
  39. data/spec/lib/pg_search/features/trigram_spec.rb +28 -28
  40. data/spec/lib/pg_search/features/tsearch_spec.rb +57 -39
  41. data/spec/lib/pg_search/multisearch/rebuilder_spec.rb +17 -17
  42. data/spec/lib/pg_search/multisearch_spec.rb +15 -6
  43. data/spec/lib/pg_search/multisearchable_spec.rb +26 -26
  44. data/spec/lib/pg_search/normalizer_spec.rb +7 -7
  45. data/spec/lib/pg_search_spec.rb +20 -20
  46. data/spec/spec_helper.rb +15 -9
  47. data/spec/support/database.rb +9 -7
  48. metadata +13 -188
  49. data/.autotest +0 -5
  50. data/.codeclimate.yml +0 -17
  51. data/.rubocop.yml +0 -134
  52. data/.travis.yml +0 -42
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/module/delegation"
4
- require 'active_support/deprecation'
4
+ require "active_support/deprecation"
5
5
 
6
6
  module PgSearch
7
7
  module Features
8
- class TSearch < Feature # rubocop:disable Metrics/ClassLength
8
+ class TSearch < Feature
9
9
  def self.valid_options
10
10
  super + %i[dictionary prefix negation any_word normalization tsvector_column highlight]
11
11
  end
@@ -36,12 +36,11 @@ module PgSearch
36
36
  end
37
37
 
38
38
  def ts_headline_options
39
- return '' unless options[:highlight].is_a?(Hash)
39
+ return "" unless options[:highlight].is_a?(Hash)
40
40
 
41
41
  headline_options
42
42
  .merge(deprecated_headline_options)
43
- .map { |key, value| "#{key} = #{value}" unless value.nil? }
44
- .compact
43
+ .filter_map { |key, value| "#{key} = #{value}" unless value.nil? }
45
44
  .join(", ")
46
45
  end
47
46
 
@@ -59,7 +58,7 @@ module PgSearch
59
58
  end
60
59
  end
61
60
 
62
- def deprecated_headline_options # rubocop:disable Metrics/MethodLength
61
+ def deprecated_headline_options
63
62
  indifferent_options = options.with_indifferent_access
64
63
 
65
64
  %w[
@@ -71,9 +70,11 @@ module PgSearch
71
70
  unless value.nil?
72
71
  key = deprecated_key.camelize
73
72
 
74
- ActiveSupport::Deprecation.warn(
73
+ warn(
75
74
  "pg_search 3.0 will no longer accept :#{deprecated_key} as an argument to :ts_headline, " \
76
- "use :#{key} instead."
75
+ "use :#{key} instead.",
76
+ category: :deprecated,
77
+ uplevel: 1
77
78
  )
78
79
 
79
80
  hash[key] = ts_headline_option_value(value)
@@ -95,11 +96,11 @@ module PgSearch
95
96
  end
96
97
  end
97
98
 
98
- DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’]/.freeze
99
+ DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’ʻʼ]/
99
100
 
100
101
  def tsquery_for_term(unsanitized_term)
101
102
  if options[:negation] && unsanitized_term.start_with?("!")
102
- unsanitized_term[0] = ''
103
+ unsanitized_term[0] = ""
103
104
  negated = true
104
105
  end
105
106
 
@@ -117,7 +118,7 @@ module PgSearch
117
118
  # If :negated is true, then the term will have ! prepended to the front.
118
119
  def tsquery_expression(term_sql, negated:, prefix:)
119
120
  terms = [
120
- (Arel::Nodes.build_quoted('!') if negated),
121
+ (Arel::Nodes.build_quoted("!") if negated),
121
122
  Arel::Nodes.build_quoted("' "),
122
123
  term_sql,
123
124
  Arel::Nodes.build_quoted(" '"),
@@ -132,9 +133,9 @@ module PgSearch
132
133
  def tsquery
133
134
  return "''" if query.blank?
134
135
 
135
- query_terms = query.split(" ").compact
136
+ query_terms = query.split.compact
136
137
  tsquery_terms = query_terms.map { |term| tsquery_for_term(term) }
137
- tsquery_terms.join(options[:any_word] ? ' || ' : ' && ')
138
+ tsquery_terms.join(options[:any_word] ? " || " : " && ")
138
139
  end
139
140
 
140
141
  def tsdocument
@@ -152,7 +153,7 @@ module PgSearch
152
153
  end
153
154
  end
154
155
 
155
- tsdocument_terms.join(' || ')
156
+ tsdocument_terms.join(" || ")
156
157
  end
157
158
 
158
159
  # From http://www.postgresql.org/docs/8.3/static/textsearch-controls.html
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pg_search/migration/generator'
3
+ require "pg_search/migration/generator"
4
4
 
5
5
  module PgSearch
6
6
  module Migration
7
7
  class DmetaphoneGenerator < Generator
8
8
  def migration_name
9
- 'add_pg_search_dmetaphone_support_functions'
9
+ "add_pg_search_dmetaphone_support_functions"
10
10
  end
11
11
  end
12
12
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_record'
4
- require 'rails/generators/base'
3
+ require "active_record"
4
+ require "rails/generators/base"
5
5
 
6
6
  module PgSearch
7
7
  module Migration
@@ -10,19 +10,19 @@ module PgSearch
10
10
 
11
11
  def self.inherited(subclass)
12
12
  super
13
- subclass.source_root File.expand_path('templates', __dir__)
13
+ subclass.source_root File.expand_path("templates", __dir__)
14
14
  end
15
15
 
16
16
  def create_migration
17
17
  now = Time.now.utc
18
- filename = "#{now.strftime('%Y%m%d%H%M%S')}_#{migration_name}.rb"
18
+ filename = "#{now.strftime("%Y%m%d%H%M%S")}_#{migration_name}.rb"
19
19
  template "#{migration_name}.rb.erb", "db/migrate/#{filename}", migration_version
20
20
  end
21
21
 
22
22
  private
23
23
 
24
24
  def read_sql_file(filename)
25
- sql_directory = File.expand_path('../../../sql', __dir__)
25
+ sql_directory = File.expand_path("../../../sql", __dir__)
26
26
  source_path = File.join(sql_directory, "#{filename}.sql")
27
27
  File.read(source_path).strip
28
28
  end
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pg_search/migration/generator'
3
+ require "pg_search/migration/generator"
4
4
 
5
5
  module PgSearch
6
6
  module Migration
7
7
  class MultisearchGenerator < Generator
8
8
  def migration_name
9
- 'create_pg_search_documents'
9
+ "create_pg_search_documents"
10
10
  end
11
11
  end
12
12
  end
@@ -7,12 +7,12 @@ module PgSearch
7
7
  module ClassMethods
8
8
  def pg_search_scope(name, options)
9
9
  options_proc = if options.respond_to?(:call)
10
- options
11
- elsif options.respond_to?(:merge)
12
- ->(query) { { query: query }.merge(options) }
13
- else
14
- raise ArgumentError, 'pg_search_scope expects a Hash or Proc'
15
- end
10
+ options
11
+ elsif options.respond_to?(:merge)
12
+ ->(query) { {query: query}.merge(options) }
13
+ else
14
+ raise ArgumentError, "pg_search_scope expects a Hash or Proc"
15
+ end
16
16
 
17
17
  define_singleton_method(name) do |*args|
18
18
  config = Configuration.new(options_proc.call(*args), self)
@@ -5,20 +5,30 @@ require "pg_search/multisearch/rebuilder"
5
5
  module PgSearch
6
6
  module Multisearch
7
7
  class << self
8
- def rebuild(model, deprecated_clean_up = nil, clean_up: true)
8
+ def rebuild(model, deprecated_clean_up = nil, clean_up: true, transactional: true)
9
9
  unless deprecated_clean_up.nil?
10
- ActiveSupport::Deprecation.warn(
10
+ warn(
11
11
  "pg_search 3.0 will no longer accept a boolean second argument to PgSearchMultisearch.rebuild, " \
12
- "use keyword argument `clean_up:` instead."
12
+ "use keyword argument `clean_up:` instead.",
13
+ category: :deprecated,
14
+ uplevel: 1
13
15
  )
14
16
  clean_up = deprecated_clean_up
15
17
  end
16
18
 
17
- model.transaction do
18
- PgSearch::Document.where(searchable_type: model.base_class.name).delete_all if clean_up
19
- Rebuilder.new(model).rebuild
19
+ if transactional
20
+ model.transaction { execute(model, clean_up) }
21
+ else
22
+ execute(model, clean_up)
20
23
  end
21
24
  end
25
+
26
+ private
27
+
28
+ def execute(model, clean_up)
29
+ PgSearch::Document.where(searchable_type: model.base_class.name).delete_all if clean_up
30
+ Rebuilder.new(model).rebuild
31
+ end
22
32
  end
23
33
 
24
34
  class ModelNotMultisearchable < StandardError
@@ -7,12 +7,12 @@ module PgSearch
7
7
  def self.included(mod)
8
8
  mod.class_eval do
9
9
  has_one :pg_search_document,
10
- as: :searchable,
11
- class_name: "PgSearch::Document",
12
- dependent: :delete
10
+ as: :searchable,
11
+ class_name: "PgSearch::Document",
12
+ dependent: :delete
13
13
 
14
14
  after_save :update_pg_search_document,
15
- if: -> { PgSearch.multisearch_enabled? }
15
+ if: -> { PgSearch.multisearch_enabled? }
16
16
  end
17
17
  end
18
18
 
@@ -39,7 +39,7 @@ module PgSearch
39
39
  conditions.all? { |condition| condition.to_proc.call(self) }
40
40
  end
41
41
 
42
- def update_pg_search_document # rubocop:disable Metrics/AbcSize
42
+ def update_pg_search_document
43
43
  if_conditions = Array(pg_search_multisearchable_options[:if])
44
44
  unless_conditions = Array(pg_search_multisearchable_options[:unless])
45
45
 
@@ -50,7 +50,7 @@ module PgSearch
50
50
  if should_have_document
51
51
  create_or_update_pg_search_document
52
52
  else
53
- pg_search_document&.destroy
53
+ pg_search_document&.destroy # standard:disable Rails/SaveBang
54
54
  end
55
55
  end
56
56
 
@@ -58,7 +58,7 @@ module PgSearch
58
58
  if !pg_search_document
59
59
  create_pg_search_document(pg_search_document_attrs)
60
60
  elsif should_update_pg_search_document?
61
- pg_search_document.update(pg_search_document_attrs)
61
+ pg_search_document.update(pg_search_document_attrs) # standard:disable Rails/SaveBang
62
62
  end
63
63
  end
64
64
  end
@@ -10,11 +10,11 @@ module PgSearch
10
10
  return sql_expression unless config.ignore.include?(:accents)
11
11
 
12
12
  sql_node = case sql_expression
13
- when Arel::Nodes::Node
14
- sql_expression
15
- else
16
- Arel.sql(sql_expression)
17
- end
13
+ when Arel::Nodes::Node
14
+ sql_expression
15
+ else
16
+ Arel.sql(sql_expression)
17
+ end
18
18
 
19
19
  Arel::Nodes::NamedFunction.new(
20
20
  PgSearch.unaccent_function,
@@ -37,11 +37,8 @@ module PgSearch
37
37
 
38
38
  def with_pg_search_highlight
39
39
  scope = self
40
- scope.select(pg_search_highlight_field)
41
- end
42
-
43
- def pg_search_highlight_field
44
- "(#{highlight}) AS pg_search_highlight, #{table_name}.*"
40
+ scope = scope.select("#{table_name}.*") unless scope.select_values.any?
41
+ scope.select("(#{highlight}) AS pg_search_highlight")
45
42
  end
46
43
 
47
44
  def highlight
@@ -93,11 +90,32 @@ module PgSearch
93
90
  end
94
91
 
95
92
  def conditions
96
- config.features
97
- .reject { |_feature_name, feature_options| feature_options && feature_options[:sort_only] }
98
- .map { |feature_name, _feature_options| feature_for(feature_name).conditions }
99
- .inject { |accumulator, expression| Arel::Nodes::Or.new(accumulator, expression) }
93
+ expressions =
94
+ config.features
95
+ .reject { |_feature_name, feature_options| feature_options && feature_options[:sort_only] }
96
+ .map { |feature_name, _feature_options| feature_for(feature_name).conditions }
97
+
98
+ or_node(expressions)
99
+ end
100
+
101
+ # https://github.com/rails/rails/pull/51492
102
+ # :nocov:
103
+ # standard:disable Lint/DuplicateMethods
104
+ or_arity = Arel::Nodes::Or.instance_method(:initialize).arity
105
+ case or_arity
106
+ when 1
107
+ def or_node(expressions)
108
+ Arel::Nodes::Or.new(expressions)
109
+ end
110
+ when 2
111
+ def or_node(expressions)
112
+ expressions.inject { |accumulator, expression| Arel::Nodes::Or.new(accumulator, expression) }
113
+ end
114
+ else
115
+ raise "Unsupported arity #{or_arity} for Arel::Nodes::Or#initialize"
100
116
  end
117
+ # :nocov:
118
+ # standard:enable Lint/DuplicateMethods
101
119
 
102
120
  def order_within_rank
103
121
  config.order_within_rank || "#{primary_key} ASC"
@@ -111,7 +129,7 @@ module PgSearch
111
129
  if config.associations.any?
112
130
  config.associations.map do |association|
113
131
  association.join(primary_key)
114
- end.join(' ')
132
+ end.join(" ")
115
133
  end
116
134
  end
117
135
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rake'
4
- require 'pg_search'
3
+ require "rake"
4
+ require "pg_search"
5
5
 
6
6
  namespace :pg_search do
7
7
  namespace :multisearch do
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgSearch
4
- VERSION = '2.3.5'
4
+ VERSION = "2.3.7"
5
5
  end
data/lib/pg_search.rb CHANGED
@@ -18,7 +18,7 @@ module PgSearch
18
18
  autoload :Document, "pg_search/document"
19
19
 
20
20
  def self.included(base)
21
- ActiveSupport::Deprecation.warn <<~MESSAGE
21
+ warn(<<~MESSAGE, category: :deprecated, uplevel: 1)
22
22
  Directly including `PgSearch` into an Active Record model is deprecated and will be removed in pg_search 3.0.
23
23
 
24
24
  Please replace `include PgSearch` with `include PgSearch::Model`.
@@ -34,8 +34,8 @@ module PgSearch
34
34
  self.unaccent_function = "unaccent"
35
35
 
36
36
  class << self
37
- def multisearch(*args)
38
- PgSearch::Document.search(*args)
37
+ def multisearch(...)
38
+ PgSearch::Document.search(...)
39
39
  end
40
40
 
41
41
  def disable_multisearch
@@ -57,14 +57,14 @@ module PgSearch
57
57
  class PgSearchRankNotSelected < StandardError
58
58
  def message
59
59
  "You must chain .with_pg_search_rank after the pg_search_scope " \
60
- "to access the pg_search_rank attribute on returned records"
60
+ "to access the pg_search_rank attribute on returned records"
61
61
  end
62
62
  end
63
63
 
64
64
  class PgSearchHighlightNotSelected < StandardError
65
65
  def message
66
66
  "You must chain .with_pg_search_highlight after the pg_search_scope " \
67
- "to access the pg_search_highlight attribute on returned records"
67
+ "to access the pg_search_highlight attribute on returned records"
68
68
  end
69
69
  end
70
70
  end
data/pg_search.gemspec CHANGED
@@ -1,37 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- $LOAD_PATH.push File.expand_path('lib', __dir__)
4
- require 'pg_search/version'
3
+ $LOAD_PATH.push File.expand_path("lib", __dir__)
4
+ require "pg_search/version"
5
5
 
6
- Gem::Specification.new do |s| # rubocop:disable Metrics/BlockLength
7
- s.name = 'pg_search'
8
- s.version = PgSearch::VERSION
9
- s.platform = Gem::Platform::RUBY
10
- s.authors = ['Grant Hutchins', 'Case Commons, LLC']
11
- s.email = %w[gems@nertzy.com casecommons-dev@googlegroups.com]
12
- s.homepage = 'https://github.com/Casecommons/pg_search'
13
- s.summary = "PgSearch builds Active Record named scopes that take advantage of PostgreSQL's full text search"
6
+ Gem::Specification.new do |s|
7
+ s.name = "pg_search"
8
+ s.version = PgSearch::VERSION
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ["Grant Hutchins", "Case Commons, LLC"]
11
+ s.email = %w[gems@nertzy.com casecommons-dev@googlegroups.com]
12
+ s.homepage = "https://github.com/Casecommons/pg_search"
13
+ s.summary = "PgSearch builds Active Record named scopes that take advantage of PostgreSQL's full text search"
14
14
  s.description = "PgSearch builds Active Record named scopes that take advantage of PostgreSQL's full text search"
15
- s.licenses = ['MIT']
15
+ s.licenses = ["MIT"]
16
+ s.metadata["rubygems_mfa_required"] = "true"
16
17
 
17
- s.files = `git ls-files`.split("\n")
18
- s.test_files = `git ls-files -- spec/*`.split("\n")
19
- s.require_paths = ['lib']
18
+ s.files = `git ls-files -z`.split("\x0")
19
+ s.require_paths = ["lib"]
20
20
 
21
- s.add_dependency 'activerecord', '>= 5.2'
22
- s.add_dependency 'activesupport', '>= 5.2'
21
+ s.add_dependency "activerecord", ">= 6.1"
22
+ s.add_dependency "activesupport", ">= 6.1"
23
23
 
24
- s.add_development_dependency 'pry'
25
- s.add_development_dependency 'rake'
26
- s.add_development_dependency 'rspec'
27
- s.add_development_dependency 'rubocop'
28
- s.add_development_dependency 'rubocop-performance'
29
- s.add_development_dependency 'rubocop-rails'
30
- s.add_development_dependency 'rubocop-rake'
31
- s.add_development_dependency 'rubocop-rspec'
32
- s.add_development_dependency 'simplecov'
33
- s.add_development_dependency 'warning'
34
- s.add_development_dependency 'with_model'
35
-
36
- s.required_ruby_version = '>= 2.5'
24
+ s.required_ruby_version = ">= 3.0"
37
25
  end
data/spec/.rubocop.yml CHANGED
@@ -1,14 +1,27 @@
1
1
  inherit_from:
2
2
  - ../.rubocop.yml
3
3
 
4
- Layout/LineLength:
5
- Enabled: false
4
+ RSpec/ContextWording:
5
+ Prefixes:
6
+ - using
7
+ - via
8
+ - when
9
+ - with
10
+ - without
6
11
 
7
- Lint/SuppressedException:
8
- Enabled: false
12
+ RSpec/DescribedClass:
13
+ Enabled: true
9
14
 
10
- Lint/UselessAssignment:
11
- Enabled: false
15
+ RSpec/ExampleLength:
16
+ Max: 15
12
17
 
13
- Style/BlockDelimiters:
18
+ RSpec/ExpectInHook:
14
19
  Enabled: false
20
+
21
+ RSpec/FilePath:
22
+ CustomTransform:
23
+ TSearch: "tsearch"
24
+ DMetaphone: "dmetaphone"
25
+
26
+ RSpec/MultipleExpectations:
27
+ Max: 5
@@ -4,8 +4,8 @@ inherit_from:
4
4
  RSpec/DescribeClass:
5
5
  Enabled: false
6
6
 
7
- RSpec/MultipleExpectations:
7
+ RSpec/ExampleLength:
8
8
  Enabled: false
9
9
 
10
- RSpec/ExampleLength:
10
+ RSpec/MultipleExpectations:
11
11
  Enabled: false