From 1cdcf487fa0c8eb8c159f4c9f0929ef152e06744 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 21 Nov 2021 16:19:54 -0600 Subject: [PATCH 01/55] Remove outdated autotest config --- .autotest | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .autotest diff --git a/.autotest b/.autotest deleted file mode 100644 index 0bb1f593..00000000 --- a/.autotest +++ /dev/null @@ -1,5 +0,0 @@ -Autotest.add_hook :initialize do |at| - at.add_mapping(%r%^lib/.*\.rb$%, true) { |filename, _| - at.files_matching %r%^spec/.*_spec.rb$% - } -end From e890b7fa14044dc8b3bf2aaed043a5b95e73aa59 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Wed, 24 Nov 2021 15:18:14 -0600 Subject: [PATCH 02/55] Address Rubocop issues --- pg_search.gemspec | 1 + spec/lib/pg_search/features/trigram_spec.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pg_search.gemspec b/pg_search.gemspec index a199777e..f08c2275 100644 --- a/pg_search.gemspec +++ b/pg_search.gemspec @@ -13,6 +13,7 @@ Gem::Specification.new do |s| # rubocop:disable Metrics/BlockLength s.summary = "PgSearch builds Active Record named scopes that take advantage of PostgreSQL's full text search" s.description = "PgSearch builds Active Record named scopes that take advantage of PostgreSQL's full text search" s.licenses = ['MIT'] + s.metadata["rubygems_mfa_required"] = "true" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- spec/*`.split("\n") diff --git a/spec/lib/pg_search/features/trigram_spec.rb b/spec/lib/pg_search/features/trigram_spec.rb index b06256f2..995c5dae 100644 --- a/spec/lib/pg_search/features/trigram_spec.rb +++ b/spec/lib/pg_search/features/trigram_spec.rb @@ -16,7 +16,7 @@ ] } let(:normalizer) { PgSearch::Normalizer.new(config) } - let(:config) { OpenStruct.new(ignore: []) } + let(:config) { OpenStruct.new(ignore: []) } # rubocop:disable Style/OpenStructUse let(:coalesced_columns) do <<~SQL.squish From 849e667d24c8d5415cce2512d130fc48aefb793b Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Thu, 9 Dec 2021 11:06:53 -0500 Subject: [PATCH 03/55] Optionally disable transaction when rebuilding documents --- README.md | 7 +++++++ lib/pg_search/multisearch.rb | 16 ++++++++++++---- spec/lib/pg_search/multisearch_spec.rb | 9 +++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 52b6feed..6af2ada1 100644 --- a/README.md +++ b/README.md @@ -335,6 +335,13 @@ like so: PgSearch::Multisearch.rebuild(Product, clean_up: false) ``` +```rebuild``` runs inside a single transaction. To run outside of a transaction, +you can pass ```transactional: false``` like so: + +```ruby +PgSearch::Multisearch.rebuild(Product, transactional: false) +``` + Rebuild is also available as a Rake task, for convenience. $ rake pg_search:multisearch:rebuild[BlogPost] diff --git a/lib/pg_search/multisearch.rb b/lib/pg_search/multisearch.rb index 27ac544f..271d4582 100644 --- a/lib/pg_search/multisearch.rb +++ b/lib/pg_search/multisearch.rb @@ -5,7 +5,7 @@ module PgSearch module Multisearch class << self - def rebuild(model, deprecated_clean_up = nil, clean_up: true) + def rebuild(model, deprecated_clean_up = nil, clean_up: true, transactional: true) unless deprecated_clean_up.nil? ActiveSupport::Deprecation.warn( "pg_search 3.0 will no longer accept a boolean second argument to PgSearchMultisearch.rebuild, " \ @@ -14,11 +14,19 @@ def rebuild(model, deprecated_clean_up = nil, clean_up: true) clean_up = deprecated_clean_up end - model.transaction do - PgSearch::Document.where(searchable_type: model.base_class.name).delete_all if clean_up - Rebuilder.new(model).rebuild + if transactional + model.transaction { execute(model, clean_up) } + else + execute(model, clean_up) end end + + private + + def execute(model, clean_up) + PgSearch::Document.where(searchable_type: model.base_class.name).delete_all if clean_up + Rebuilder.new(model).rebuild + end end class ModelNotMultisearchable < StandardError diff --git a/spec/lib/pg_search/multisearch_spec.rb b/spec/lib/pg_search/multisearch_spec.rb index 2ee8220a..8d7eab86 100644 --- a/spec/lib/pg_search/multisearch_spec.rb +++ b/spec/lib/pg_search/multisearch_spec.rb @@ -33,6 +33,15 @@ expect(model).to have_received(:transaction).once end + context "when transactional is false" do + it "does not operate inside a transaction" do + allow(model).to receive(:transaction) + + described_class.rebuild(model, transactional: false) + expect(model).not_to have_received(:transaction) + end + end + describe "cleaning up search documents for this model" do before do connection.execute <<~SQL.squish From 40335c5f4d886b263c0ac4c7742456e0fac2ea86 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 17 Dec 2021 11:42:08 -0500 Subject: [PATCH 04/55] Don't require against if tsvector_column is specified --- lib/pg_search/configuration.rb | 11 +++++++++-- spec/integration/pg_search_spec.rb | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/pg_search/configuration.rb b/lib/pg_search/configuration.rb index 2687b57b..e24e06b9 100644 --- a/lib/pg_search/configuration.rb +++ b/lib/pg_search/configuration.rb @@ -92,8 +92,8 @@ def default_options }.freeze def assert_valid_options(options) - unless options[:against] || options[:associated_against] - raise ArgumentError, "the search scope #{@name} must have :against or :associated_against in its options" + unless options[:against] || options[:associated_against] || using_tsvector_column?(options[:using]) + raise ArgumentError, "the search scope #{@name} must have :against or :associated_against in its options or specify a tsvector_column" end options.assert_valid_keys(VALID_KEYS) @@ -104,5 +104,12 @@ def assert_valid_options(options) end end end + + def using_tsvector_column?(options) + return unless options.is_a?(Hash) + + options.dig(:dmetaphone, :tsvector_column).present? || + options.dig(:tsearch, :tsvector_column).present? + end end end diff --git a/spec/integration/pg_search_spec.rb b/spec/integration/pg_search_spec.rb index 70cd84e1..6ab50bac 100644 --- a/spec/integration/pg_search_spec.rb +++ b/spec/integration/pg_search_spec.rb @@ -140,6 +140,22 @@ }.to raise_error(ArgumentError, /against/) end end + + context "when a tsvector column is specified" do + it "does not raise an exception when invoked" do + ModelWithPgSearch.pg_search_scope :with_unknown_ignoring, { + using: { + tsearch: { + tsvector_column: "tsv" + } + } + } + + expect { + ModelWithPgSearch.with_unknown_ignoring("foo") + }.not_to raise_error + end + end end end end From 70251e8003e10b55bf653645b3d1cd1c326b0cfd Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Wed, 29 Dec 2021 13:39:38 -0600 Subject: [PATCH 05/55] Simplify error message for when column is missing --- lib/pg_search/configuration.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pg_search/configuration.rb b/lib/pg_search/configuration.rb index e24e06b9..ce37edf9 100644 --- a/lib/pg_search/configuration.rb +++ b/lib/pg_search/configuration.rb @@ -93,7 +93,10 @@ def default_options def assert_valid_options(options) unless options[:against] || options[:associated_against] || using_tsvector_column?(options[:using]) - raise ArgumentError, "the search scope #{@name} must have :against or :associated_against in its options or specify a tsvector_column" + raise( + ArgumentError, + "the search scope #{@name} must have :against, :associated_against, or :tsvector_column in its options" + ) end options.assert_valid_keys(VALID_KEYS) From 916d0c5c39a3efbc83b9b3c01a314b530cc3ad77 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Wed, 29 Dec 2021 15:30:33 -0600 Subject: [PATCH 06/55] Remove workaround code for AR 6.0 reflection cache --- spec/lib/pg_search_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/lib/pg_search_spec.rb b/spec/lib/pg_search_spec.rb index 2823bc20..658d075d 100644 --- a/spec/lib/pg_search_spec.rb +++ b/spec/lib/pg_search_spec.rb @@ -2,9 +2,9 @@ require "spec_helper" -# For Active Record 5.x and 6.0, the association reflection's cache needs be cleared +# For Active Record 5.x, the association reflection's cache needs be cleared # because we're stubbing the related constants. -if ActiveRecord::VERSION::MAJOR == 5 || (ActiveRecord::VERSION::MAJOR == 6 && ActiveRecord::VERSION::MINOR == 0) +if ActiveRecord::VERSION::MAJOR == 5 def clear_searchable_cache PgSearch::Document.reflect_on_association(:searchable).clear_association_scope_cache end From 552b22e5434c9e8c17e906d7403d2f3f08ea89fe Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Wed, 29 Dec 2021 15:31:21 -0600 Subject: [PATCH 07/55] Run tests in GitHub Actions --- .github/workflows/ruby.yml | 83 ++++++++++++++++++++++++++++++++++++++ Rakefile | 1 + spec/support/database.rb | 10 +++-- 3 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/ruby.yml diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 00000000..7fc8a5a3 --- /dev/null +++ b/.github/workflows/ruby.yml @@ -0,0 +1,83 @@ +# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby + +name: Ruby + +on: + push: + branches: + - master + - github-actions + pull_request: + branches: + - master + +jobs: + test: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:latest + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + env: + CI: true + PGHOST: 127.0.0.1 + PGUSER: postgres + PGPASS: postgres + strategy: + fail-fast: false + matrix: + ruby-version: ['2.6', '2.7', '3.0', '3.1'] + active-record-version-env: + - ACTIVE_RECORD_VERSION="~> 5.2.0" + - ACTIVE_RECORD_VERSION="~> 6.0.0" + - ACTIVE_RECORD_VERSION="~> 6.1.0" + - ACTIVE_RECORD_VERSION="~> 7.0.0" + allow-failure: [false] + include: + - ruby-version: '3.1' + active-record-version-env: ACTIVE_RECORD_VERSION="~> 7.0.0" + allow-failure: true + - ruby-version: '3.1' + active-record-version-env: ACTIVE_RECORD_BRANCH="7-0-stable" + allow-failure: true + - ruby-version: '3.1' + active-record-version-env: ACTIVE_RECORD_BRANCH="6-1-stable" + allow-failure: true + exclude: + - ruby-version: '3.0' + active-record-version-env: ACTIVE_RECORD_VERSION="~> 5.2.0" + allow-failure: false + - ruby-version: '3.1' + active-record-version-env: ACTIVE_RECORD_VERSION="~> 5.2.0" + allow-failure: false + - ruby-version: '2.6' + active-record-version-env: ACTIVE_RECORD_VERSION="~> 7.0.0" + allow-failure: false + - ruby-version: '3.1' + active-record-version-env: ACTIVE_RECORD_VERSION="~> 7.0.0" + allow-failure: false + continue-on-error: ${{ matrix.allow-failure }} + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true + - name: Set up test database + env: + PGPASSWORD: postgres + run: createdb pg_search_test + - name: Update bundle + run: ${{ matrix.active-record-version-env }} bundle update + - name: Run tests + run: ${{ matrix.active-record-version-env }} bundle exec rake diff --git a/Rakefile b/Rakefile index 996ae9e6..fd5be07e 100644 --- a/Rakefile +++ b/Rakefile @@ -13,6 +13,7 @@ end desc "Check test coverage" task :undercover do + system("git fetch --unshallow") if ENV["CI"] exit(1) unless system("bin/undercover --compare origin/master") end diff --git a/spec/support/database.rb b/spec/support/database.rb index 4bd2a5ea..ea31b63f 100644 --- a/spec/support/database.rb +++ b/spec/support/database.rb @@ -10,10 +10,12 @@ end begin - ActiveRecord::Base.establish_connection(adapter: 'postgresql', - database: 'pg_search_test', - username: (ENV["TRAVIS"] ? "postgres" : ENV["USER"]), - min_messages: 'warning') + connection_options = { adapter: 'postgresql', database: 'pg_search_test', min_messages: 'warning' } + if ENV["CI"] + connection_options[:username] = 'postgres' + connection_options[:password] = 'postgres' + end + ActiveRecord::Base.establish_connection(connection_options) connection = ActiveRecord::Base.connection connection.execute("SELECT 1") rescue ERROR_CLASS, ActiveRecord::NoDatabaseError => e From cef3250eb5f98329375344734520f34660ad8e39 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Wed, 29 Dec 2021 15:40:28 -0600 Subject: [PATCH 08/55] Rename GitHub actions workflow --- .github/workflows/{ruby.yml => ci.yml} | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) rename .github/workflows/{ruby.yml => ci.yml} (95%) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ci.yml similarity index 95% rename from .github/workflows/ruby.yml rename to .github/workflows/ci.yml index 7fc8a5a3..c74f1146 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,4 @@ -# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby - -name: Ruby +name: build on: push: From d99727bda64664335c33ce14618b5f06a7a2c422 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Wed, 29 Dec 2021 15:43:37 -0600 Subject: [PATCH 09/55] Update status badge for tests in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6af2ada1..349ada70 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # [pg_search](http://github.com/Casecommons/pg_search/) [![Gem Version](https://img.shields.io/gem/v/pg_search.svg?style=flat)](https://rubygems.org/gems/pg_search) -[![Build Status](https://secure.travis-ci.org/Casecommons/pg_search.svg?branch=master)](https://travis-ci.org/Casecommons/pg_search) +[![Build Status](https://github.com/Casecommons/pg_search/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/Casecommons/pg_search/actions/workflows/ci.yml) [![Join the chat at https://gitter.im/Casecommons/pg_search](https://img.shields.io/badge/gitter-join%20chat-blue.svg)](https://gitter.im/Casecommons/pg_search?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ## DESCRIPTION From 7b3f856558242530de91781303473cdda0c4d6ea Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Thu, 6 Jan 2022 20:53:48 -0600 Subject: [PATCH 10/55] Test against Ruby 3.1 / Active Record 7.0 --- .github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c74f1146..238477c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,9 +41,6 @@ jobs: - ACTIVE_RECORD_VERSION="~> 7.0.0" allow-failure: [false] include: - - ruby-version: '3.1' - active-record-version-env: ACTIVE_RECORD_VERSION="~> 7.0.0" - allow-failure: true - ruby-version: '3.1' active-record-version-env: ACTIVE_RECORD_BRANCH="7-0-stable" allow-failure: true @@ -60,9 +57,6 @@ jobs: - ruby-version: '2.6' active-record-version-env: ACTIVE_RECORD_VERSION="~> 7.0.0" allow-failure: false - - ruby-version: '3.1' - active-record-version-env: ACTIVE_RECORD_VERSION="~> 7.0.0" - allow-failure: false continue-on-error: ${{ matrix.allow-failure }} steps: - uses: actions/checkout@v2 From 9d718b5d8afa3ac5ec0defc55632d7d2c71f217c Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Thu, 6 Jan 2022 20:59:50 -0600 Subject: [PATCH 11/55] VERSION 2.3.6 --- CHANGELOG.md | 9 +++++++++ lib/pg_search/version.rb | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca9e13cf..8d6a196c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # pg_search changelog +## 2.3.6 + +* Drop support for Ruby 2.5 +* Support Ruby 3.1 +* Support Active Record 7.0 +* Don't require `:against` if `:tsvector_column` is specified (Travis Hunter) +* Optionally disable transaction when rebuilding documents (Travis Hunter) +* Preserve columns when chaining ::with_pg_search_highlight (jcsanti) + ## 2.3.5 * Add table of contents to README (Barry Woolgar) diff --git a/lib/pg_search/version.rb b/lib/pg_search/version.rb index 60625f86..2a007da3 100644 --- a/lib/pg_search/version.rb +++ b/lib/pg_search/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module PgSearch - VERSION = '2.3.5' + VERSION = '2.3.6' end From 07ab919c1f8de65be0ee2b79112d9ade4bd546b1 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Thu, 6 Jan 2022 21:02:21 -0600 Subject: [PATCH 12/55] Test against Active Record master branch --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 238477c1..c19ee5c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,9 @@ jobs: - ACTIVE_RECORD_VERSION="~> 7.0.0" allow-failure: [false] include: + - ruby-version: '3.1' + active-record-version-env: ACTIVE_RECORD_BRANCH="master" + allow-failure: true - ruby-version: '3.1' active-record-version-env: ACTIVE_RECORD_BRANCH="7-0-stable" allow-failure: true From 5bb83f66bcab2da5f78a90b295ff34779b24eae9 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Thu, 6 Jan 2022 21:10:09 -0600 Subject: [PATCH 13/55] Run CI weekly --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c19ee5c4..33675293 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,8 @@ on: pull_request: branches: - master + schedule: + - cron: "0 18 * * 6" # Saturdays at 12pm CST jobs: test: From 60b1bcd3d8041f220767c52fd90f17d050be95c2 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Thu, 6 Jan 2022 21:11:03 -0600 Subject: [PATCH 14/55] Allow CI to be triggered manually --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33675293..fd0234a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,7 @@ on: - master schedule: - cron: "0 18 * * 6" # Saturdays at 12pm CST + workflow_dispatch: jobs: test: From b51d9bef74fb4bbf5448e75a88d68acb86f10df8 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Thu, 6 Jan 2022 21:16:36 -0600 Subject: [PATCH 15/55] Use correct upstream Active Record branch name --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd0234a6..74e1cd30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: allow-failure: [false] include: - ruby-version: '3.1' - active-record-version-env: ACTIVE_RECORD_BRANCH="master" + active-record-version-env: ACTIVE_RECORD_BRANCH="main" allow-failure: true - ruby-version: '3.1' active-record-version-env: ACTIVE_RECORD_BRANCH="7-0-stable" From 86ce88399f511bd8edc719d9c711e6d54c26b901 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Fri, 7 Jan 2022 18:39:58 -0600 Subject: [PATCH 16/55] Update copyright --- LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 1a6a48d1..9e0a02e9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010-2021 Casebook, PBC +Copyright (c) 2010–2022 Casebook, PBC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 349ada70..e7b7873d 100644 --- a/README.md +++ b/README.md @@ -1201,5 +1201,5 @@ for discussing pg_search and other Casebook PBC open source projects. ## LICENSE -Copyright © 2010–2021 [Casebook PBC](http://www.casebook.net). +Copyright © 2010–2022 [Casebook PBC](http://www.casebook.net). Licensed under the MIT license, see [LICENSE](/LICENSE) file. From c3376c4032931ff405df226e8822b846571c98ae Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Thu, 20 Jan 2022 21:30:17 -0600 Subject: [PATCH 17/55] Use safer commands for files in gemspec --- pg_search.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pg_search.gemspec b/pg_search.gemspec index f08c2275..9172d9c9 100644 --- a/pg_search.gemspec +++ b/pg_search.gemspec @@ -15,8 +15,8 @@ Gem::Specification.new do |s| # rubocop:disable Metrics/BlockLength s.licenses = ['MIT'] s.metadata["rubygems_mfa_required"] = "true" - s.files = `git ls-files`.split("\n") - s.test_files = `git ls-files -- spec/*`.split("\n") + s.files = `git ls-files -z`.split("\x0") + s.test_files = `git ls-files -z -- spec/*`.split("\x0") s.require_paths = ['lib'] s.add_dependency 'activerecord', '>= 5.2' From f707d6a5b6413611a59031cb4b508757232381dd Mon Sep 17 00:00:00 2001 From: Prima Aulia Gusta Date: Sat, 13 Mar 2021 12:53:32 +0800 Subject: [PATCH 18/55] Update README.md As mentioned here https://github.com/Casecommons/pg_search/issues/303 --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index e7b7873d..1ff63545 100644 --- a/README.md +++ b/README.md @@ -550,6 +550,21 @@ class Beer < ActiveRecord::Base end ``` +Here's an example if you pass multiple :using options with additional configurations. + +```ruby +class Beer < ActiveRecord::Base + include PgSearch::Model + pg_search_scope :search_name, + against: :name, + using: { + :trigram => {}, + :dmetaphone => {}, + :tsearch => { :prefix => true } + } +end +``` + The currently implemented features are * :tsearch - [Full text search](http://www.postgresql.org/docs/current/static/textsearch-intro.html), which is built-in to PostgreSQL From 38b314a6d1a4c012683a25fdece7e9244f762550 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 13 Mar 2022 17:35:44 -0500 Subject: [PATCH 19/55] Rubocop: RSpec/BeEq Authored-by: Grant Hutchins --- spec/integration/pg_search_spec.rb | 6 +++--- spec/lib/pg_search/features/tsearch_spec.rb | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/spec/integration/pg_search_spec.rb b/spec/integration/pg_search_spec.rb index 6ab50bac..d89bf66a 100644 --- a/spec/integration/pg_search_spec.rb +++ b/spec/integration/pg_search_spec.rb @@ -176,7 +176,7 @@ expect(results).to include(included) expect(results).not_to include(excluded) - expect(results.first.attributes.key?('content')).to eq false + expect(results.first.attributes.key?('content')).to be false expect(results.select { |record| record.title == "bar" }).to eq [included] expect(results.reject { |record| record.title == "bar" }).to be_empty @@ -193,7 +193,7 @@ expect(results).to include(included) expect(results).not_to include(excluded) - expect(results.first.attributes.key?('content')).to eq false + expect(results.first.attributes.key?('content')).to be false expect(results.select { |record| record.title == "bar" }).to eq [included] expect(results.reject { |record| record.title == "bar" }).to be_empty @@ -210,7 +210,7 @@ expect(results).to include(included) expect(results).not_to include(excluded) - expect(results.first.attributes.key?('content')).to eq false + expect(results.first.attributes.key?('content')).to be false expect(results.select { |record| record.title == "bar" }).to eq [included] expect(results.reject { |record| record.title == "bar" }).to be_empty diff --git a/spec/lib/pg_search/features/tsearch_spec.rb b/spec/lib/pg_search/features/tsearch_spec.rb index f493c56e..f77f9a79 100644 --- a/spec/lib/pg_search/features/tsearch_spec.rb +++ b/spec/lib/pg_search/features/tsearch_spec.rb @@ -27,6 +27,24 @@ %{(ts_rank((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 0))} ) end + + context "with a tsvector column and a custom normalization" do + it "works?" do + query = "query" + columns = [ + PgSearch::Configuration::Column.new(:name, nil, Model), + PgSearch::Configuration::Column.new(:content, nil, Model) + ] + options = { tsvector_column: :my_tsvector, normalization: 2 } + config = instance_double("PgSearch::Configuration", :config, ignore: []) + normalizer = PgSearch::Normalizer.new(config) + + feature = described_class.new(query, options, columns, Model, normalizer) + expect(feature.rank.to_sql).to eq( + %{(ts_rank((#{Model.quoted_table_name}."my_tsvector"), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 2))} + ) + end + end end describe "#conditions" do From 6564d0bcd7cb80cde8f718187b31c7f6080793a2 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 24 Apr 2022 13:06:40 -0500 Subject: [PATCH 20/55] Rubocop: RSpec/VerifiedDoubleReference --- Gemfile | 6 ++--- spec/integration/pg_search_spec.rb | 6 ++--- .../lib/pg_search/features/dmetaphone_spec.rb | 4 ++-- spec/lib/pg_search/features/tsearch_spec.rb | 22 +++++++++---------- spec/lib/pg_search/normalizer_spec.rb | 10 ++++----- spec/lib/pg_search_spec.rb | 4 ++-- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Gemfile b/Gemfile index bd213e8c..ea35689d 100644 --- a/Gemfile +++ b/Gemfile @@ -8,8 +8,8 @@ gem 'pg', '>= 0.21.0', platform: :ruby gem "activerecord-jdbcpostgresql-adapter", ">= 1.3.1", platform: :jruby if ENV['ACTIVE_RECORD_BRANCH'] - gem 'activerecord', git: 'https://github.com/rails/rails.git', branch: ENV['ACTIVE_RECORD_BRANCH'] - gem 'arel', git: 'https://github.com/rails/arel.git' if ENV['ACTIVE_RECORD_BRANCH'] == 'master' + gem 'activerecord', git: 'https://github.com/rails/rails.git', branch: ENV.fetch('ACTIVE_RECORD_BRANCH', nil) + gem 'arel', git: 'https://github.com/rails/arel.git' if ENV.fetch('ACTIVE_RECORD_BRANCH', nil) == 'master' end -gem 'activerecord', ENV['ACTIVE_RECORD_VERSION'] if ENV['ACTIVE_RECORD_VERSION'] +gem 'activerecord', ENV.fetch('ACTIVE_RECORD_VERSION', nil) if ENV['ACTIVE_RECORD_VERSION'] diff --git a/spec/integration/pg_search_spec.rb b/spec/integration/pg_search_spec.rb index d89bf66a..fcc56468 100644 --- a/spec/integration/pg_search_spec.rb +++ b/spec/integration/pg_search_spec.rb @@ -477,7 +477,7 @@ it "accepts non-string queries and calls #to_s on them" do foo = ModelWithPgSearch.create!(content: "foo") - not_a_string = instance_double("Object", to_s: "foo") + not_a_string = instance_double(Object, to_s: "foo") expect(ModelWithPgSearch.search_content(not_a_string)).to eq([foo]) end @@ -993,13 +993,13 @@ it "passes the custom configuration down to the specified feature" do tsearch_feature = instance_double( - "PgSearch::Features::TSearch", + PgSearch::Features::TSearch, conditions: Arel::Nodes::Grouping.new(Arel.sql("1 = 1")), rank: Arel::Nodes::Grouping.new(Arel.sql("1.0")) ) trigram_feature = instance_double( - "PgSearch::Features::Trigram", + PgSearch::Features::Trigram, conditions: Arel::Nodes::Grouping.new(Arel.sql("1 = 1")), rank: Arel::Nodes::Grouping.new(Arel.sql("1.0")) ) diff --git a/spec/lib/pg_search/features/dmetaphone_spec.rb b/spec/lib/pg_search/features/dmetaphone_spec.rb index 808a07c3..995f76b4 100644 --- a/spec/lib/pg_search/features/dmetaphone_spec.rb +++ b/spec/lib/pg_search/features/dmetaphone_spec.rb @@ -18,7 +18,7 @@ PgSearch::Configuration::Column.new(:content, nil, Model) ] options = {} - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) @@ -43,7 +43,7 @@ PgSearch::Configuration::Column.new(:content, nil, Model) ] options = {} - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) diff --git a/spec/lib/pg_search/features/tsearch_spec.rb b/spec/lib/pg_search/features/tsearch_spec.rb index f77f9a79..e5d586e4 100644 --- a/spec/lib/pg_search/features/tsearch_spec.rb +++ b/spec/lib/pg_search/features/tsearch_spec.rb @@ -19,7 +19,7 @@ PgSearch::Configuration::Column.new(:content, nil, Model) ] options = {} - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) @@ -36,7 +36,7 @@ PgSearch::Configuration::Column.new(:content, nil, Model) ] options = { tsvector_column: :my_tsvector, normalization: 2 } - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) @@ -62,7 +62,7 @@ PgSearch::Configuration::Column.new(:content, nil, Model) ] options = {} - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) @@ -79,7 +79,7 @@ PgSearch::Configuration::Column.new(:content, nil, Model) ] options = { negation: true } - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) @@ -97,7 +97,7 @@ PgSearch::Configuration::Column.new(:content, nil, Model) ] options = { negation: false } - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) @@ -115,7 +115,7 @@ PgSearch::Configuration::Column.new(:content, nil, Model) ] options = { tsvector_column: "my_tsvector" } - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) @@ -133,7 +133,7 @@ PgSearch::Configuration::Column.new(:content, nil, Model) ] options = { tsvector_column: ["tsvector1", "tsvector2"] } - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) @@ -159,7 +159,7 @@ ] options = {} - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) @@ -184,7 +184,7 @@ } } - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) @@ -216,7 +216,7 @@ } } - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) @@ -246,7 +246,7 @@ } } - config = instance_double("PgSearch::Configuration", :config, ignore: []) + config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) feature = described_class.new(query, options, columns, Model, normalizer) diff --git a/spec/lib/pg_search/normalizer_spec.rb b/spec/lib/pg_search/normalizer_spec.rb index 3b4db0a1..f267caf2 100644 --- a/spec/lib/pg_search/normalizer_spec.rb +++ b/spec/lib/pg_search/normalizer_spec.rb @@ -8,7 +8,7 @@ context "when config[:ignore] includes :accents" do context "when passed an Arel node" do it "wraps the expression in unaccent()" do - config = instance_double("PgSearch::Configuration", "config", ignore: [:accents]) + config = instance_double(PgSearch::Configuration, "config", ignore: [:accents]) node = Arel::Nodes::NamedFunction.new("foo", [Arel::Nodes.build_quoted("bar")]) normalizer = described_class.new(config) @@ -20,7 +20,7 @@ allow(PgSearch).to receive(:unaccent_function).and_return("my_unaccent") node = Arel::Nodes::NamedFunction.new("foo", [Arel::Nodes.build_quoted("bar")]) - config = instance_double("PgSearch::Configuration", "config", ignore: [:accents]) + config = instance_double(PgSearch::Configuration, "config", ignore: [:accents]) normalizer = described_class.new(config) expect(normalizer.add_normalization(node)).to eq("my_unaccent(foo('bar'))") @@ -30,7 +30,7 @@ context "when passed a String" do it "wraps the expression in unaccent()" do - config = instance_double("PgSearch::Configuration", "config", ignore: [:accents]) + config = instance_double(PgSearch::Configuration, "config", ignore: [:accents]) normalizer = described_class.new(config) expect(normalizer.add_normalization("foo")).to eq("unaccent(foo)") @@ -40,7 +40,7 @@ it "wraps the expression in that function" do allow(PgSearch).to receive(:unaccent_function).and_return("my_unaccent") - config = instance_double("PgSearch::Configuration", "config", ignore: [:accents]) + config = instance_double(PgSearch::Configuration, "config", ignore: [:accents]) normalizer = described_class.new(config) expect(normalizer.add_normalization("foo")).to eq("my_unaccent(foo)") @@ -51,7 +51,7 @@ context "when config[:ignore] does not include :accents" do it "passes the expression through" do - config = instance_double("PgSearch::Configuration", "config", ignore: []) + config = instance_double(PgSearch::Configuration, "config", ignore: []) normalizer = described_class.new(config) expect(normalizer.add_normalization("foo")).to eq("foo") diff --git a/spec/lib/pg_search_spec.rb b/spec/lib/pg_search_spec.rb index 658d075d..80cca230 100644 --- a/spec/lib/pg_search_spec.rb +++ b/spec/lib/pg_search_spec.rb @@ -21,8 +21,8 @@ def clear_searchable_cache describe "delegation to PgSearch::Document.search" do subject { described_class.multisearch(query) } - let(:query) { instance_double("String", "query") } - let(:relation) { instance_double("ActiveRecord::Relation", "relation") } + let(:query) { instance_double(String, "query") } + let(:relation) { instance_double(ActiveRecord::Relation, "relation") } before do allow(PgSearch::Document).to receive(:search).with(query).and_return(relation) From b73f9b5fcfd33cc4fc481d07f60ab36241c14d89 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 8 May 2022 18:09:18 -0500 Subject: [PATCH 21/55] Remove Travis CI configuration --- .travis.yml | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a71836d4..00000000 --- a/.travis.yml +++ /dev/null @@ -1,37 +0,0 @@ -language: ruby -bundler_args: --binstubs -cache: bundler - -rvm: - - 3.0.1 - - 2.7.3 - - 2.6.7 - -services: - - postgresql - -env: - jobs: - - ACTIVE_RECORD_BRANCH="main" - - ACTIVE_RECORD_BRANCH="6-1-stable" - - ACTIVE_RECORD_BRANCH="6-0-stable" - - ACTIVE_RECORD_VERSION="~> 6.1.0" - - ACTIVE_RECORD_VERSION="~> 6.0.0" - - ACTIVE_RECORD_VERSION="~> 5.2.0" - -jobs: - allow_failures: - - env: ACTIVE_RECORD_BRANCH="main" - - env: ACTIVE_RECORD_BRANCH="6-0-stable" - - env: ACTIVE_RECORD_BRANCH="6-1-stable" - exclude: - - rvm: 3.0.1 - env: ACTIVE_RECORD_VERSION="~> 5.2.0" - - rvm: 2.6.7 - env: ACTIVE_RECORD_BRANCH="main" - -before_script: - - psql --version - - psql -c 'create database pg_search_test;' -U postgres >/dev/null - -script: bin/rake From d6064dc7a6b63c82ff8cd8d5d3b2860925f1b8cd Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 8 May 2022 18:07:03 -0500 Subject: [PATCH 22/55] Drop support for Ruby 2.6 --- .github/workflows/ci.yml | 5 +---- .rubocop.yml | 2 +- README.md | 2 +- lib/pg_search/features/tsearch.rb | 3 +-- pg_search.gemspec | 2 +- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74e1cd30..a89774b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: false matrix: - ruby-version: ['2.6', '2.7', '3.0', '3.1'] + ruby-version: ['2.7', '3.0', '3.1'] active-record-version-env: - ACTIVE_RECORD_VERSION="~> 5.2.0" - ACTIVE_RECORD_VERSION="~> 6.0.0" @@ -60,9 +60,6 @@ jobs: - ruby-version: '3.1' active-record-version-env: ACTIVE_RECORD_VERSION="~> 5.2.0" allow-failure: false - - ruby-version: '2.6' - active-record-version-env: ACTIVE_RECORD_VERSION="~> 7.0.0" - allow-failure: false continue-on-error: ${{ matrix.allow-failure }} steps: - uses: actions/checkout@v2 diff --git a/.rubocop.yml b/.rubocop.yml index c0dbf99e..5bb6075e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,7 +5,7 @@ require: - rubocop-rspec AllCops: - TargetRubyVersion: 2.6 + TargetRubyVersion: 2.7 NewCops: enable Exclude: - bin/**/* diff --git a/README.md b/README.md index 1ff63545..4aaf0373 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Read the blog post introducing PgSearch at https://tanzu.vmware.com/content/blog ## REQUIREMENTS -* Ruby 2.6+ +* Ruby 2.7+ * ActiveRecord 5.2+ * PostgreSQL 9.2+ * [PostgreSQL extensions](https://github.com/Casecommons/pg_search/wiki/Installing-PostgreSQL-Extensions) for certain features diff --git a/lib/pg_search/features/tsearch.rb b/lib/pg_search/features/tsearch.rb index baaf2197..d4136ef3 100644 --- a/lib/pg_search/features/tsearch.rb +++ b/lib/pg_search/features/tsearch.rb @@ -40,8 +40,7 @@ def ts_headline_options headline_options .merge(deprecated_headline_options) - .map { |key, value| "#{key} = #{value}" unless value.nil? } - .compact + .filter_map { |key, value| "#{key} = #{value}" unless value.nil? } .join(", ") end diff --git a/pg_search.gemspec b/pg_search.gemspec index 9172d9c9..664b9848 100644 --- a/pg_search.gemspec +++ b/pg_search.gemspec @@ -36,5 +36,5 @@ Gem::Specification.new do |s| # rubocop:disable Metrics/BlockLength s.add_development_dependency 'warning' s.add_development_dependency 'with_model' - s.required_ruby_version = '>= 2.6' + s.required_ruby_version = '>= 2.7' end From 0116cb5b6fbc6aa52d82d1b6bdcfdde0de4a658c Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sat, 21 May 2022 18:08:44 -0500 Subject: [PATCH 23/55] Rubocop: Lint/RedundantCopDisableDirective --- pg_search.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pg_search.gemspec b/pg_search.gemspec index 664b9848..450d9c13 100644 --- a/pg_search.gemspec +++ b/pg_search.gemspec @@ -3,7 +3,7 @@ $LOAD_PATH.push File.expand_path('lib', __dir__) require 'pg_search/version' -Gem::Specification.new do |s| # rubocop:disable Metrics/BlockLength +Gem::Specification.new do |s| s.name = 'pg_search' s.version = PgSearch::VERSION s.platform = Gem::Platform::RUBY From 9cdf9795cd34a87823fb023cfa07b4cbb36a8a97 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 12 Jun 2022 11:56:14 -0500 Subject: [PATCH 24/55] Rubocop: Gemspec/DeprecatedAttributeAssignment --- pg_search.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/pg_search.gemspec b/pg_search.gemspec index 450d9c13..ada1908b 100644 --- a/pg_search.gemspec +++ b/pg_search.gemspec @@ -16,7 +16,6 @@ Gem::Specification.new do |s| s.metadata["rubygems_mfa_required"] = "true" s.files = `git ls-files -z`.split("\x0") - s.test_files = `git ls-files -z -- spec/*`.split("\x0") s.require_paths = ['lib'] s.add_dependency 'activerecord', '>= 5.2' From 02dd53aead726e5aa5bf5f51cfc216c8e044f873 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sat, 9 Jul 2022 18:43:52 -0500 Subject: [PATCH 25/55] Rubocop: Layout/LineContinuationSpacing --- spec/integration/pg_search_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/integration/pg_search_spec.rb b/spec/integration/pg_search_spec.rb index fcc56468..efbc687e 100644 --- a/spec/integration/pg_search_spec.rb +++ b/spec/integration/pg_search_spec.rb @@ -1091,7 +1091,7 @@ end it 'concats tsvector columns' do - expected = "#{ModelWithTsvector.quoted_table_name}.\"content_tsvector\" || "\ + expected = "#{ModelWithTsvector.quoted_table_name}.\"content_tsvector\" || " \ "#{ModelWithTsvector.quoted_table_name}.\"message_tsvector\"" expect(ModelWithTsvector.search_by_multiple_tsvector_columns("something").to_sql).to include(expected) From 5c87d39756565844e89b34dd11d805a266abf762 Mon Sep 17 00:00:00 2001 From: Vital Ryabchinskiy Date: Wed, 20 Jul 2022 08:39:42 +0000 Subject: [PATCH 26/55] Add U+02BB/U+02BC to disallow tsquery characters --- lib/pg_search/features/tsearch.rb | 2 +- spec/integration/pg_search_spec.rb | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/pg_search/features/tsearch.rb b/lib/pg_search/features/tsearch.rb index d4136ef3..365eddad 100644 --- a/lib/pg_search/features/tsearch.rb +++ b/lib/pg_search/features/tsearch.rb @@ -94,7 +94,7 @@ def ts_headline_option_value(value) end end - DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’]/.freeze + DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’ʻʼ]/.freeze def tsquery_for_term(unsanitized_term) if options[:negation] && unsanitized_term.start_with?("!") diff --git a/spec/integration/pg_search_spec.rb b/spec/integration/pg_search_spec.rb index efbc687e..7005316d 100644 --- a/spec/integration/pg_search_spec.rb +++ b/spec/integration/pg_search_spec.rb @@ -1174,11 +1174,16 @@ end context "when the query includes accents" do - it "does not create an erroneous tsquery expression" do - included = ModelWithPgSearch.create!(title: "Weird L‘Content") + let(:term) { "L#{%w[‘ ’ ʻ ʼ].sample}Content" } + let(:included) { ModelWithPgSearch.create!(title: "Weird #{term}") } + let(:results) { ModelWithPgSearch.search_title_without_accents(term) } - results = ModelWithPgSearch.search_title_without_accents("L‘Content") - expect(results).to eq([included]) + before do + ModelWithPgSearch.create!(title: 'FooBar') + end + + it "does not create an erroneous tsquery expression" do + expect(results).to contain_exactly(included) end end end From 225c06f4817ed49ca352a1a427f43bac9c77cd9d Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 31 Jul 2022 18:33:31 -0500 Subject: [PATCH 27/55] Update examples in README for accuracy The previous examples did not work properly with the current PostgreSQL English stemmer. https://github.com/Casecommons/pg_search/issues/489 --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4aaf0373..0c3d1457 100644 --- a/README.md +++ b/README.md @@ -699,12 +699,12 @@ class BoringTweet < ActiveRecord::Base } end -sleepy = BoringTweet.create! text: "I snoozed my alarm for fourteen hours today. I bet I can beat that tomorrow! #sleepy" +sleep = BoringTweet.create! text: "I snoozed my alarm for fourteen hours today. I bet I can beat that tomorrow! #sleep" sleeping = BoringTweet.create! text: "You know what I like? Sleeping. That's what. #enjoyment" -sleeper = BoringTweet.create! text: "Have you seen Woody Allen's movie entitled Sleeper? Me neither. #boycott" +sleeps = BoringTweet.create! text: "In the jungle, the mighty jungle, the lion sleeps #tonight" -BoringTweet.kinda_matching("sleeping") # => [sleepy, sleeping, sleeper] -BoringTweet.literally_matching("sleeping") # => [sleeping] +BoringTweet.kinda_matching("sleeping") # => [sleep, sleeping, sleeps] +BoringTweet.literally_matching("sleeps") # => [sleeps] ``` ##### :normalization From 24b95bbb1bd51af6ee721a5d347678066115fc23 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Wed, 21 Sep 2022 10:15:12 -0500 Subject: [PATCH 28/55] Rubocop: RSpec/NoExpectationExample Authored-by: Grant Hutchins --- spec/lib/pg_search/multisearch/rebuilder_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/lib/pg_search/multisearch/rebuilder_spec.rb b/spec/lib/pg_search/multisearch/rebuilder_spec.rb index 3ae2bd01..5a462644 100644 --- a/spec/lib/pg_search/multisearch/rebuilder_spec.rb +++ b/spec/lib/pg_search/multisearch/rebuilder_spec.rb @@ -151,10 +151,10 @@ def rebuild_pg_search_documents end end - it "creates search document without PG error" do + it "rebuilds without error" do time = Time.utc(2001, 1, 1, 0, 0, 0) rebuilder = described_class.new(Model, -> { time }) - rebuilder.rebuild + expect { rebuilder.rebuild }.not_to raise_error end end From 892eec7fb656ad9d78fb96380e34fe0686bf6439 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Tue, 25 Oct 2022 17:14:19 -0500 Subject: [PATCH 29/55] Rubocop: Style/RedundantStringEscape Authored-by: Grant Hutchins --- .../configuration/association_spec.rb | 20 +++++++++---------- spec/lib/pg_search/features/tsearch_spec.rb | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/spec/lib/pg_search/configuration/association_spec.rb b/spec/lib/pg_search/configuration/association_spec.rb index 660b890e..90a08f50 100644 --- a/spec/lib/pg_search/configuration/association_spec.rb +++ b/spec/lib/pg_search/configuration/association_spec.rb @@ -55,9 +55,9 @@ LEFT OUTER JOIN (SELECT model_id AS id, #{column_select} AS #{association.columns.first.alias} - FROM \"#{User.table_name}\" - INNER JOIN \"#{association.table_name}\" - ON \"#{association.table_name}\".\"user_id\" = \"#{User.table_name}\".\"id\") #{association.subselect_alias} + FROM "#{User.table_name}" + INNER JOIN "#{association.table_name}" + ON "#{association.table_name}"."user_id" = "#{User.table_name}"."id") #{association.subselect_alias} ON #{association.subselect_alias}.id = model_id SQL end @@ -86,9 +86,9 @@ LEFT OUTER JOIN (SELECT model_id AS id, #{column_select} AS #{association.columns.first.alias} - FROM \"#{User.table_name}\" - INNER JOIN \"#{association.table_name}\" - ON \"#{association.table_name}\".\"id\" = \"#{User.table_name}\".\"site_id\") #{association.subselect_alias} + FROM "#{User.table_name}" + INNER JOIN "#{association.table_name}" + ON "#{association.table_name}"."id" = "#{User.table_name}"."site_id") #{association.subselect_alias} ON #{association.subselect_alias}.id = model_id SQL end @@ -116,10 +116,10 @@ <<~SQL.squish LEFT OUTER JOIN (SELECT model_id AS id, - string_agg(\"#{association.table_name}\".\"name\"::text, ' ') AS #{association.columns.first.alias} - FROM \"#{Site.table_name}\" - INNER JOIN \"#{association.table_name}\" - ON \"#{association.table_name}\".\"site_id\" = \"#{Site.table_name}\".\"id\" + string_agg("#{association.table_name}"."name"::text, ' ') AS #{association.columns.first.alias} + FROM "#{Site.table_name}" + INNER JOIN "#{association.table_name}" + ON "#{association.table_name}"."site_id" = "#{Site.table_name}"."id" GROUP BY model_id) #{association.subselect_alias} ON #{association.subselect_alias}.id = model_id SQL diff --git a/spec/lib/pg_search/features/tsearch_spec.rb b/spec/lib/pg_search/features/tsearch_spec.rb index e5d586e4..929a4134 100644 --- a/spec/lib/pg_search/features/tsearch_spec.rb +++ b/spec/lib/pg_search/features/tsearch_spec.rb @@ -120,7 +120,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) expect(feature.conditions.to_sql).to eq( - %{((#{Model.quoted_table_name}.\"my_tsvector\") @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))} + %{((#{Model.quoted_table_name}."my_tsvector") @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))} ) end end @@ -138,7 +138,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) expect(feature.conditions.to_sql).to eq( - %{((#{Model.quoted_table_name}.\"tsvector1\" || #{Model.quoted_table_name}.\"tsvector2\") @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))} + %{((#{Model.quoted_table_name}."tsvector1" || #{Model.quoted_table_name}."tsvector2") @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))} ) end end From c66fb103045cdabf03fa6b61623698b95fa92235 Mon Sep 17 00:00:00 2001 From: Ross Baird <483090+raspygold@users.noreply.github.com> Date: Tue, 15 Nov 2022 17:32:51 +1300 Subject: [PATCH 30/55] Readme updates - Replace delete_all with delete_by in multisearchable guide - Tweak update_if option to suggest *_previously_changed? instead of *_changed? to better work with ActiveRecord callbacks --- README.md | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 0c3d1457..d30ec3e1 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ To add PgSearch to an Active Record model, simply include the PgSearch module. class Shape < ActiveRecord::Base include PgSearch::Model end -``` +``` ### Contents * [Multi-search vs. search scopes](#multi-search-vs-search-scopes) @@ -198,17 +198,22 @@ problematic_record.published? # => true PgSearch.multisearch("timestamp") # => Includes problematic_record ``` -#### More Options +#### More Options **Conditionally update pg_search_documents** -You can specify an `:update_if` parameter to conditionally update pg_search documents. For example: +You can also use the `:update_if` option to pass a Proc or method name to call +to determine whether or not a particular record should be updated. + +Note that the Proc or method name is called in an `after_save` hook, so if you +are relying on ActiveRecord dirty flags use `*_previously_changed?`. ```ruby -multisearchable( - against: [:body], - update_if: :body_changed? - ) +class Message < ActiveRecord::Base + include PgSearch::Model + multisearchable against: [:body], + update_if: :body_previously_changed? +end ``` **Specify additional attributes to be saved on the pg_search_documents table** @@ -241,7 +246,8 @@ This allows much faster searches without joins later on by doing something like: PgSearch.multisearch(params['search']).where(author_id: 2) ``` -*NOTE: You must currently manually call `record.update_pg_search_document` for the additional attribute to be included in the pg_search_documents table* +*NOTE: You must currently manually call `record.update_pg_search_document` for +the additional attribute to be included in the pg_search_documents table* #### Multi-search associations @@ -359,7 +365,7 @@ the pg_search_documents table all at once. However, if you call any dynamic methods in :against, the following strategy will be used: ```ruby -PgSearch::Document.delete_all(searchable_type: "Ingredient") +PgSearch::Document.delete_by(searchable_type: "Ingredient") Ingredient.find_each { |record| record.update_pg_search_document } ``` @@ -555,8 +561,8 @@ Here's an example if you pass multiple :using options with additional configurat ```ruby class Beer < ActiveRecord::Base include PgSearch::Model - pg_search_scope :search_name, - against: :name, + pg_search_scope :search_name, + against: :name, using: { :trigram => {}, :dmetaphone => {}, @@ -677,7 +683,7 @@ Animal.with_name_matching("fish !red !blue") # => [one_fish, two_fish] PostgreSQL full text search also support multiple dictionaries for stemming. You can learn more about how dictionaries work by reading the [PostgreSQL -documention](http://www.postgresql.org/docs/current/static/textsearch-dictionaries.html). +documention](http://www.postgresql.org/docs/current/static/textsearch-dictionaries.html). If you use one of the language dictionaries, such as "english", then variants of words (e.g. "jumping" and "jumped") will match each other. If you don't want stemming, you should pick the "simple" dictionary which does @@ -849,7 +855,7 @@ used for searching. Double Metaphone support is currently available as part of the [fuzzystrmatch extension](http://www.postgresql.org/docs/current/static/fuzzystrmatch.html) that must be installed before this feature can be used. In addition to the -extension, you must install a utility function into your database. To generate +extension, you must install a utility function into your database. To generate and run a migration for this, run: $ rails g pg_search:migration:dmetaphone @@ -884,7 +890,7 @@ Trigram search works by counting how many three-letter substrings (or Trigram search has some ability to work even with typos and misspellings in the query or text. -Trigram support is currently available as part of the +Trigram support is currently available as part of the [pg_trgm extension](http://www.postgresql.org/docs/current/static/pgtrgm.html) that must be installed before this feature can be used. @@ -948,7 +954,7 @@ Vegetable.strictly_spelled_like("collyflower") # => [] Allows you to match words in longer strings. By default, trigram searches use `%` or `similarity()` as a similarity value. Set `word_similarity` to `true` to opt for `<%` and `word_similarity` instead. -This causes the trigram search to use the similarity of the query term +This causes the trigram search to use the similarity of the query term and the word with greatest similarity. ```ruby @@ -974,12 +980,12 @@ Sentence.similarity_like("word") # => [] Sentence.word_similarity_like("word") # => [sentence] ``` -### Limiting Fields When Combining Features +### Limiting Fields When Combining Features -Sometimes when doing queries combining different features you +Sometimes when doing queries combining different features you might want to searching against only some of the fields with certain features. For example perhaps you want to only do a trigram search against the shorter fields -so that you don't need to reduce the threshold excessively. You can specify +so that you don't need to reduce the threshold excessively. You can specify which fields using the 'only' option: ```ruby @@ -998,7 +1004,7 @@ class Image < ActiveRecord::Base end ``` -Now you can succesfully retrieve an Image with a file_name: 'image_foo.jpg' +Now you can succesfully retrieve an Image with a file_name: 'image_foo.jpg' and long_description: 'This description is so long that it would make a trigram search fail any reasonable threshold limit' with: From 1c7ff92b59bd7539957551cc9a4f023c8067ea1e Mon Sep 17 00:00:00 2001 From: Ross Baird <483090+raspygold@users.noreply.github.com> Date: Mon, 12 Dec 2022 10:55:59 +1300 Subject: [PATCH 31/55] Remove incorrect strategy description for rebuild with :against --- README.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d30ec3e1..3b70c3b5 100644 --- a/README.md +++ b/README.md @@ -359,15 +359,11 @@ pg_search_documents tables. The following will set the schema search path to $ rake pg_search:multisearch:rebuild[BlogPost,my_schema] -For models that are multisearchable :against methods that directly map to +For models that are multisearchable `:against` methods that directly map to Active Record attributes, an efficient single SQL statement is run to update -the pg_search_documents table all at once. However, if you call any dynamic -methods in :against, the following strategy will be used: - -```ruby -PgSearch::Document.delete_by(searchable_type: "Ingredient") -Ingredient.find_each { |record| record.update_pg_search_document } -``` +the `pg_search_documents` table all at once. However, if you call any dynamic +methods in `:against` then `update_pg_search_document` will be called on the +individual records being indexed in batches. You can also provide a custom implementation for rebuilding the documents by adding a class method called `rebuild_pg_search_documents` to your model. From 7e876b333aa79dc121696dad40d7c0352c382f40 Mon Sep 17 00:00:00 2001 From: Andy Atkinson Date: Sat, 21 Jan 2023 21:09:16 -0600 Subject: [PATCH 32/55] Fix readme typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b70c3b5..4b141c65 100644 --- a/README.md +++ b/README.md @@ -979,7 +979,7 @@ Sentence.word_similarity_like("word") # => [sentence] ### Limiting Fields When Combining Features Sometimes when doing queries combining different features you -might want to searching against only some of the fields with certain features. +might want to search against only some of the fields with certain features. For example perhaps you want to only do a trigram search against the shorter fields so that you don't need to reduce the threshold excessively. You can specify which fields using the 'only' option: From 6710bedccbb2e51a560c7cf4cd622b9653a106cf Mon Sep 17 00:00:00 2001 From: Kyle Fazzari Date: Tue, 24 Jan 2023 13:30:13 -0800 Subject: [PATCH 33/55] column: add support for Arel::Nodes::SqlLiteral This allows for more more advanced columns, for example, extracting elements out of json arrays, or fields out of json objects. Resolve #252 Signed-off-by: Kyle Fazzari --- lib/pg_search/configuration/column.rb | 8 +++++--- spec/lib/pg_search/configuration/column_spec.rb | 14 +++++++++++++- spec/lib/pg_search/features/dmetaphone_spec.rb | 4 ++-- spec/lib/pg_search/features/trigram_spec.rb | 6 +++--- spec/lib/pg_search/features/tsearch_spec.rb | 16 ++++++++-------- 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/lib/pg_search/configuration/column.rb b/lib/pg_search/configuration/column.rb index ad1397bc..f2225264 100644 --- a/lib/pg_search/configuration/column.rb +++ b/lib/pg_search/configuration/column.rb @@ -9,18 +9,20 @@ class Column def initialize(column_name, weight, model) @name = column_name.to_s - @column_name = column_name.to_s + @column_name = column_name @weight = weight @model = model @connection = model.connection end def full_name + return @column_name if @column_name.is_a?(Arel::Nodes::SqlLiteral) + "#{table_name}.#{column_name}" end def to_sql - "coalesce(#{expression}::text, '')" + "coalesce((#{expression})::text, '')" end private @@ -30,7 +32,7 @@ def table_name end def column_name - @connection.quote_column_name(@column_name) + @connection.quote_column_name(@name) end def expression diff --git a/spec/lib/pg_search/configuration/column_spec.rb b/spec/lib/pg_search/configuration/column_spec.rb index 50f56b1e..9e933713 100644 --- a/spec/lib/pg_search/configuration/column_spec.rb +++ b/spec/lib/pg_search/configuration/column_spec.rb @@ -7,6 +7,7 @@ with_model :Model do table do |t| t.string :name + t.json :object end end @@ -14,18 +15,29 @@ column = described_class.new("name", nil, Model) expect(column.full_name).to eq(%(#{Model.quoted_table_name}."name")) end + + it "returns nested json attributes" do + column = described_class.new(Arel.sql("object->>'name'"), nil, Model) + expect(column.full_name).to eq(%(object->>'name')) + end end describe "#to_sql" do with_model :Model do table do |t| t.string :name + t.json :object end end it "returns an expression that casts the column to text and coalesces it with an empty string" do column = described_class.new("name", nil, Model) - expect(column.to_sql).to eq(%{coalesce(#{Model.quoted_table_name}."name"::text, '')}) + expect(column.to_sql).to eq(%{coalesce((#{Model.quoted_table_name}."name")::text, '')}) + end + + it "returns an expression that casts the nested json attribute to text and coalesces it with an empty string" do + column = described_class.new(Arel.sql("object->>'name'"), nil, Model) + expect(column.to_sql).to eq(%{coalesce((object->>'name')::text, '')}) end end end diff --git a/spec/lib/pg_search/features/dmetaphone_spec.rb b/spec/lib/pg_search/features/dmetaphone_spec.rb index 995f76b4..8327619d 100644 --- a/spec/lib/pg_search/features/dmetaphone_spec.rb +++ b/spec/lib/pg_search/features/dmetaphone_spec.rb @@ -23,7 +23,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) expect(feature.rank.to_sql).to eq( - %{(ts_rank((to_tsvector('simple', pg_search_dmetaphone(coalesce(#{Model.quoted_table_name}."name"::text, ''))) || to_tsvector('simple', pg_search_dmetaphone(coalesce(#{Model.quoted_table_name}."content"::text, '')))), (to_tsquery('simple', ''' ' || pg_search_dmetaphone('query') || ' ''')), 0))} + %{(ts_rank((to_tsvector('simple', pg_search_dmetaphone(coalesce((#{Model.quoted_table_name}."name")::text, ''))) || to_tsvector('simple', pg_search_dmetaphone(coalesce((#{Model.quoted_table_name}."content")::text, '')))), (to_tsquery('simple', ''' ' || pg_search_dmetaphone('query') || ' ''')), 0))} ) end end @@ -48,7 +48,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) expect(feature.conditions.to_sql).to eq( - %{((to_tsvector('simple', pg_search_dmetaphone(coalesce(#{Model.quoted_table_name}."name"::text, ''))) || to_tsvector('simple', pg_search_dmetaphone(coalesce(#{Model.quoted_table_name}."content"::text, '')))) @@ (to_tsquery('simple', ''' ' || pg_search_dmetaphone('query') || ' ''')))} + %{((to_tsvector('simple', pg_search_dmetaphone(coalesce((#{Model.quoted_table_name}."name")::text, ''))) || to_tsvector('simple', pg_search_dmetaphone(coalesce((#{Model.quoted_table_name}."content")::text, '')))) @@ (to_tsquery('simple', ''' ' || pg_search_dmetaphone('query') || ' ''')))} ) end end diff --git a/spec/lib/pg_search/features/trigram_spec.rb b/spec/lib/pg_search/features/trigram_spec.rb index 995c5dae..d0915bb2 100644 --- a/spec/lib/pg_search/features/trigram_spec.rb +++ b/spec/lib/pg_search/features/trigram_spec.rb @@ -20,9 +20,9 @@ let(:coalesced_columns) do <<~SQL.squish - coalesce(#{Model.quoted_table_name}."name"::text, '') + coalesce((#{Model.quoted_table_name}."name")::text, '') || ' ' - || coalesce(#{Model.quoted_table_name}."content"::text, '') + || coalesce((#{Model.quoted_table_name}."content")::text, '') SQL end @@ -88,7 +88,7 @@ let(:options) { { only: :name } } it 'only searches against the select column' do - coalesced_column = "coalesce(#{Model.quoted_table_name}.\"name\"::text, '')" + coalesced_column = "coalesce((#{Model.quoted_table_name}.\"name\")::text, '')" expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_column}))") end end diff --git a/spec/lib/pg_search/features/tsearch_spec.rb b/spec/lib/pg_search/features/tsearch_spec.rb index 929a4134..286b0b91 100644 --- a/spec/lib/pg_search/features/tsearch_spec.rb +++ b/spec/lib/pg_search/features/tsearch_spec.rb @@ -24,7 +24,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) expect(feature.rank.to_sql).to eq( - %{(ts_rank((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 0))} + %{(ts_rank((to_tsvector('simple', coalesce((#{Model.quoted_table_name}."name")::text, '')) || to_tsvector('simple', coalesce((#{Model.quoted_table_name}."content")::text, ''))), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 0))} ) end @@ -67,7 +67,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) expect(feature.conditions.to_sql).to eq( - %{((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))) @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))} + %{((to_tsvector('simple', coalesce((#{Model.quoted_table_name}."name")::text, '')) || to_tsvector('simple', coalesce((#{Model.quoted_table_name}."content")::text, ''))) @@ (to_tsquery('simple', ''' ' || 'query' || ' ''')))} ) end @@ -84,7 +84,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) expect(feature.conditions.to_sql).to eq( - %{((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))) @@ (to_tsquery('simple', '!' || ''' ' || 'query' || ' ''')))} + %{((to_tsvector('simple', coalesce((#{Model.quoted_table_name}."name")::text, '')) || to_tsvector('simple', coalesce((#{Model.quoted_table_name}."content")::text, ''))) @@ (to_tsquery('simple', '!' || ''' ' || 'query' || ' ''')))} ) end end @@ -102,7 +102,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) expect(feature.conditions.to_sql).to eq( - %{((to_tsvector('simple', coalesce(#{Model.quoted_table_name}."name"::text, '')) || to_tsvector('simple', coalesce(#{Model.quoted_table_name}."content"::text, ''))) @@ (to_tsquery('simple', ''' ' || '!query' || ' ''')))} + %{((to_tsvector('simple', coalesce((#{Model.quoted_table_name}."name")::text, '')) || to_tsvector('simple', coalesce((#{Model.quoted_table_name}."content")::text, ''))) @@ (to_tsquery('simple', ''' ' || '!query' || ' ''')))} ) end end @@ -164,7 +164,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) expect(feature.highlight.to_sql).to eq( - "(ts_headline('simple', (coalesce(#{Model.quoted_table_name}.\"name\"::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), ''))" + "(ts_headline('simple', (coalesce((#{Model.quoted_table_name}.\"name\")::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), ''))" ) end @@ -189,7 +189,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) - expected_sql = %{(ts_headline('spanish', (coalesce(#{Model.quoted_table_name}."name"::text, '') || ' ' || coalesce(#{Model.quoted_table_name}."content"::text, '')), (to_tsquery('spanish', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = ""'))} + expected_sql = %{(ts_headline('spanish', (coalesce((#{Model.quoted_table_name}."name")::text, '') || ' ' || coalesce((#{Model.quoted_table_name}."content")::text, '')), (to_tsquery('spanish', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = ""'))} expect(feature.highlight.to_sql).to eq(expected_sql) end @@ -221,7 +221,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) - expected_sql = %{(ts_headline('simple', (coalesce(#{Model.quoted_table_name}."name"::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = "", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = TRUE'))} + expected_sql = %{(ts_headline('simple', (coalesce((#{Model.quoted_table_name}."name")::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = "", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = TRUE'))} expect(feature.highlight.to_sql).to eq(expected_sql) end @@ -252,7 +252,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) highlight_sql = ActiveSupport::Deprecation.silence { feature.highlight.to_sql } - expected_sql = %{(ts_headline('simple', (coalesce(#{Model.quoted_table_name}."name"::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = "", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = FALSE'))} + expected_sql = %{(ts_headline('simple', (coalesce((#{Model.quoted_table_name}."name")::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = "", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = FALSE'))} expect(highlight_sql).to eq(expected_sql) end From 90882a8afd310db9239b1d7a5bc75b0269546e3f Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 5 Feb 2023 16:41:26 -0600 Subject: [PATCH 34/55] Move development dependencies to Gemfile --- Gemfile | 14 ++++++++++++++ pg_search.gemspec | 14 -------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Gemfile b/Gemfile index ea35689d..e77fae11 100644 --- a/Gemfile +++ b/Gemfile @@ -13,3 +13,17 @@ if ENV['ACTIVE_RECORD_BRANCH'] end gem 'activerecord', ENV.fetch('ACTIVE_RECORD_VERSION', nil) if ENV['ACTIVE_RECORD_VERSION'] + +gem 'pry' +gem 'rake' +gem 'rspec' +gem 'rubocop' +gem 'rubocop-performance' +gem 'rubocop-rails' +gem 'rubocop-rake' +gem 'rubocop-rspec' +gem 'simplecov' +gem 'simplecov-lcov' +gem 'undercover' +gem 'warning' +gem 'with_model' diff --git a/pg_search.gemspec b/pg_search.gemspec index ada1908b..7ece1e41 100644 --- a/pg_search.gemspec +++ b/pg_search.gemspec @@ -21,19 +21,5 @@ Gem::Specification.new do |s| s.add_dependency 'activerecord', '>= 5.2' s.add_dependency 'activesupport', '>= 5.2' - s.add_development_dependency 'pry' - s.add_development_dependency 'rake' - s.add_development_dependency 'rspec' - s.add_development_dependency 'rubocop' - s.add_development_dependency 'rubocop-performance' - s.add_development_dependency 'rubocop-rails' - s.add_development_dependency 'rubocop-rake' - s.add_development_dependency 'rubocop-rspec' - s.add_development_dependency 'simplecov' - s.add_development_dependency 'simplecov-lcov' - s.add_development_dependency 'undercover' - s.add_development_dependency 'warning' - s.add_development_dependency 'with_model' - s.required_ruby_version = '>= 2.7' end From ff96199db4ccae5d5eeadd9756c7eb93bfe9a3b3 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 5 Feb 2023 16:43:22 -0600 Subject: [PATCH 35/55] Test against Ruby 3.2 in GitHub Actions --- .github/workflows/ci.yml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a89774b1..bb37f3ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,28 +36,23 @@ jobs: strategy: fail-fast: false matrix: - ruby-version: ['2.7', '3.0', '3.1'] + ruby-version: ['2.7', '3.0', '3.1', '3.2'] active-record-version-env: - - ACTIVE_RECORD_VERSION="~> 5.2.0" - ACTIVE_RECORD_VERSION="~> 6.0.0" - ACTIVE_RECORD_VERSION="~> 6.1.0" - ACTIVE_RECORD_VERSION="~> 7.0.0" allow-failure: [false] include: - - ruby-version: '3.1' + - ruby-version: '3.2' active-record-version-env: ACTIVE_RECORD_BRANCH="main" allow-failure: true - - ruby-version: '3.1' + - ruby-version: '3.2' active-record-version-env: ACTIVE_RECORD_BRANCH="7-0-stable" allow-failure: true - - ruby-version: '3.1' + - ruby-version: '3.2' active-record-version-env: ACTIVE_RECORD_BRANCH="6-1-stable" allow-failure: true - exclude: - - ruby-version: '3.0' - active-record-version-env: ACTIVE_RECORD_VERSION="~> 5.2.0" - allow-failure: false - - ruby-version: '3.1' + - ruby-version: '2.7' active-record-version-env: ACTIVE_RECORD_VERSION="~> 5.2.0" allow-failure: false continue-on-error: ${{ matrix.allow-failure }} From a00c4d2d2c0de92fb03a3c92f2eb7faff43b56dd Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 5 Feb 2023 16:48:39 -0600 Subject: [PATCH 36/55] Drop support for Active Record <6.0 --- .github/workflows/ci.yml | 3 --- README.md | 2 +- pg_search.gemspec | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb37f3ed..0f881afc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,9 +52,6 @@ jobs: - ruby-version: '3.2' active-record-version-env: ACTIVE_RECORD_BRANCH="6-1-stable" allow-failure: true - - ruby-version: '2.7' - active-record-version-env: ACTIVE_RECORD_VERSION="~> 5.2.0" - allow-failure: false continue-on-error: ${{ matrix.allow-failure }} steps: - uses: actions/checkout@v2 diff --git a/README.md b/README.md index 3b70c3b5..8014ce5a 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Read the blog post introducing PgSearch at https://tanzu.vmware.com/content/blog ## REQUIREMENTS * Ruby 2.7+ -* ActiveRecord 5.2+ +* Active Record 6.0+ * PostgreSQL 9.2+ * [PostgreSQL extensions](https://github.com/Casecommons/pg_search/wiki/Installing-PostgreSQL-Extensions) for certain features diff --git a/pg_search.gemspec b/pg_search.gemspec index 7ece1e41..7a2d3685 100644 --- a/pg_search.gemspec +++ b/pg_search.gemspec @@ -18,8 +18,8 @@ Gem::Specification.new do |s| s.files = `git ls-files -z`.split("\x0") s.require_paths = ['lib'] - s.add_dependency 'activerecord', '>= 5.2' - s.add_dependency 'activesupport', '>= 5.2' + s.add_dependency 'activerecord', '>= 6.0' + s.add_dependency 'activesupport', '>= 6.0' s.required_ruby_version = '>= 2.7' end From 1c848953b0ad0a806f5548e0923d6e7e0b1156e2 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 5 Feb 2023 16:53:47 -0600 Subject: [PATCH 37/55] Upgrade GitHub Actions checkout to v3 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f881afc..d953736a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,7 +54,7 @@ jobs: allow-failure: true continue-on-error: ${{ matrix.allow-failure }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: From 23fe69d58698ec91d1560a1f91c4cccedcec1a4c Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 19 Feb 2023 18:53:25 -0600 Subject: [PATCH 38/55] Switch to standard gem's rubocop configuration --- .rubocop.yml | 125 +--- Gemfile | 39 +- Rakefile | 4 +- lib/pg_search/configuration.rb | 2 +- lib/pg_search/configuration/column.rb | 2 +- lib/pg_search/configuration/foreign_column.rb | 2 +- lib/pg_search/document.rb | 14 +- lib/pg_search/features/dmetaphone.rb | 2 +- lib/pg_search/features/trigram.rb | 8 +- lib/pg_search/features/tsearch.rb | 18 +- .../migration/dmetaphone_generator.rb | 4 +- lib/pg_search/migration/generator.rb | 10 +- .../migration/multisearch_generator.rb | 4 +- lib/pg_search/model.rb | 12 +- lib/pg_search/multisearchable.rb | 10 +- lib/pg_search/normalizer.rb | 10 +- lib/pg_search/scope_options.rb | 8 +- lib/pg_search/tasks.rb | 4 +- lib/pg_search/version.rb | 2 +- pg_search.gemspec | 30 +- spec/.rubocop.yml | 27 +- spec/integration/.rubocop.yml | 4 +- spec/integration/associations_spec.rb | 200 +++--- spec/integration/pg_search_spec.rb | 587 +++++++++--------- .../single_table_inheritance_spec.rb | 10 +- .../configuration/association_spec.rb | 6 +- .../configuration/foreign_column_spec.rb | 8 +- spec/lib/pg_search/features/trigram_spec.rb | 48 +- spec/lib/pg_search/features/tsearch_spec.rb | 24 +- .../pg_search/multisearch/rebuilder_spec.rb | 6 +- spec/lib/pg_search/multisearchable_spec.rb | 30 +- spec/lib/pg_search_spec.rb | 20 +- spec/spec_helper.rb | 14 +- spec/support/database.rb | 12 +- 34 files changed, 614 insertions(+), 692 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 5bb6075e..7d946471 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,137 +1,34 @@ require: + - standard - rubocop-performance - rubocop-rails - rubocop-rake - rubocop-rspec +inherit_gem: + standard: config/base.yml + AllCops: TargetRubyVersion: 2.7 - NewCops: enable + NewCops: disable Exclude: - bin/**/* - vendor/**/* -Style/StringLiterals: - Enabled: false - -Layout/LineLength: - Max: 120 - -Metrics/MethodLength: - Max: 15 - -Metrics/BlockLength: - Exclude: - - spec/**/* - -Layout/ParameterAlignment: - EnforcedStyle: with_fixed_indentation - -Style/NumericPredicate: - Enabled: false - -Style/PercentLiteralDelimiters: - PreferredDelimiters: - '%w': '[]' - '%W': '[]' - -Style/GuardClause: - Enabled: false - -Naming/VariableNumber: - EnforcedStyle: snake_case - -Bundler/OrderedGems: - Enabled: false - -Bundler/DuplicatedGem: - Enabled: false - -Style/EmptyMethod: - EnforcedStyle: expanded - -Layout/FirstArrayElementIndentation: - EnforcedStyle: consistent - -Style/Documentation: - Enabled: false - -Style/WordArray: - EnforcedStyle: percent - MinSize: 3 - -Style/HashEachMethods: +Lint/RedundantCopDisableDirective: Enabled: true -Style/HashTransformKeys: +Lint/RedundantCopEnableDirective: Enabled: true -Style/HashTransformValues: - Enabled: true - -Rails/ApplicationRecord: +Bundler/DuplicatedGem: Enabled: false -Rails/TimeZone: +Rails/ApplicationRecord: Enabled: false -RSpec/ContextWording: - Prefixes: - - using - - via - - when - - with - - without - -Lint/RaiseException: - Enabled: true - -Lint/StructNewOverride: - Enabled: true - -Layout/SpaceAroundMethodCallOperator: - Enabled: true - -Style/ExponentialNotation: - Enabled: true - -RSpec/DescribedClass: - Enabled: true - -RSpec/ExpectInHook: +Rails/RakeEnvironment: Enabled: false -RSpec/FilePath: - CustomTransform: - TSearch: "tsearch" - DMetaphone: "dmetaphone" - -Layout/EmptyLinesAroundAttributeAccessor: - Enabled: true - -Lint/DeprecatedOpenSSLConstant: - Enabled: true - -Style/SlicingWithRange: - Enabled: true - -Lint/MixedRegexpCaptureTypes: - Enabled: true - -Style/RedundantFetchBlock: - Enabled: true - -Style/RedundantRegexpCharacterClass: - Enabled: true - -Style/RedundantRegexpEscape: - Enabled: true - -RSpec/MultipleExpectations: - Max: 5 - -RSpec/ExampleLength: - Max: 15 - -Rails/RakeEnvironment: +Rails/TimeZone: Enabled: false diff --git a/Gemfile b/Gemfile index e77fae11..6d374066 100644 --- a/Gemfile +++ b/Gemfile @@ -1,29 +1,30 @@ # frozen_string_literal: true -source 'https://rubygems.org' +source "https://rubygems.org" gemspec -gem 'pg', '>= 0.21.0', platform: :ruby +gem "pg", ">= 0.21.0", platform: :ruby gem "activerecord-jdbcpostgresql-adapter", ">= 1.3.1", platform: :jruby -if ENV['ACTIVE_RECORD_BRANCH'] - gem 'activerecord', git: 'https://github.com/rails/rails.git', branch: ENV.fetch('ACTIVE_RECORD_BRANCH', nil) - gem 'arel', git: 'https://github.com/rails/arel.git' if ENV.fetch('ACTIVE_RECORD_BRANCH', nil) == 'master' +if ENV["ACTIVE_RECORD_BRANCH"] + gem "activerecord", git: "https://github.com/rails/rails.git", branch: ENV.fetch("ACTIVE_RECORD_BRANCH", nil) + gem "arel", git: "https://github.com/rails/arel.git" if ENV.fetch("ACTIVE_RECORD_BRANCH", nil) == "master" end -gem 'activerecord', ENV.fetch('ACTIVE_RECORD_VERSION', nil) if ENV['ACTIVE_RECORD_VERSION'] +gem "activerecord", ENV.fetch("ACTIVE_RECORD_VERSION", nil) if ENV["ACTIVE_RECORD_VERSION"] -gem 'pry' -gem 'rake' -gem 'rspec' -gem 'rubocop' -gem 'rubocop-performance' -gem 'rubocop-rails' -gem 'rubocop-rake' -gem 'rubocop-rspec' -gem 'simplecov' -gem 'simplecov-lcov' -gem 'undercover' -gem 'warning' -gem 'with_model' +gem "pry" +gem "rake" +gem "rspec" +gem "rubocop" +gem "rubocop-performance" +gem "rubocop-rails" +gem "rubocop-rake" +gem "rubocop-rspec" +gem "simplecov" +gem "simplecov-lcov" +gem "standard", ">= 1.23.0" +gem "undercover" +gem "warning" +gem "with_model" diff --git a/Rakefile b/Rakefile index fd5be07e..6167eb4c 100644 --- a/Rakefile +++ b/Rakefile @@ -1,9 +1,9 @@ # frozen_string_literal: true -require 'bundler' +require "bundler" Bundler::GemHelper.install_tasks -require 'rspec/core/rake_task' +require "rspec/core/rake_task" RSpec::Core::RakeTask.new(:spec) require "rubocop/rake_task" diff --git a/lib/pg_search/configuration.rb b/lib/pg_search/configuration.rb index ce37edf9..5dd88f4d 100644 --- a/lib/pg_search/configuration.rb +++ b/lib/pg_search/configuration.rb @@ -80,7 +80,7 @@ def order_within_rank attr_reader :options def default_options - { using: :tsearch } + {using: :tsearch} end VALID_KEYS = %w[ diff --git a/lib/pg_search/configuration/column.rb b/lib/pg_search/configuration/column.rb index ad1397bc..ae0313bf 100644 --- a/lib/pg_search/configuration/column.rb +++ b/lib/pg_search/configuration/column.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'digest' +require "digest" module PgSearch class Configuration diff --git a/lib/pg_search/configuration/foreign_column.rb b/lib/pg_search/configuration/foreign_column.rb index 3886c160..b4956b01 100644 --- a/lib/pg_search/configuration/foreign_column.rb +++ b/lib/pg_search/configuration/foreign_column.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'digest' +require "digest" module PgSearch class Configuration diff --git a/lib/pg_search/document.rb b/lib/pg_search/document.rb index e0f3d3ec..e41eaaa1 100644 --- a/lib/pg_search/document.rb +++ b/lib/pg_search/document.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -require 'logger' +require "logger" module PgSearch class Document < ActiveRecord::Base include PgSearch::Model - self.table_name = 'pg_search_documents' + self.table_name = "pg_search_documents" belongs_to :searchable, polymorphic: true # The logger might not have loaded yet. @@ -17,12 +17,12 @@ def self.logger pg_search_scope :search, lambda { |*args| options = if PgSearch.multisearch_options.respond_to?(:call) - PgSearch.multisearch_options.call(*args) - else - { query: args.first }.merge(PgSearch.multisearch_options) - end + PgSearch.multisearch_options.call(*args) + else + {query: args.first}.merge(PgSearch.multisearch_options) + end - { against: :content }.merge(options) + {against: :content}.merge(options) } end end diff --git a/lib/pg_search/features/dmetaphone.rb b/lib/pg_search/features/dmetaphone.rb index 295f79be..eb6852c6 100644 --- a/lib/pg_search/features/dmetaphone.rb +++ b/lib/pg_search/features/dmetaphone.rb @@ -7,7 +7,7 @@ module Features class DMetaphone def initialize(query, options, columns, model, normalizer) dmetaphone_normalizer = Normalizer.new(normalizer) - options = (options || {}).merge(dictionary: 'simple') + options = (options || {}).merge(dictionary: "simple") @tsearch = TSearch.new(query, options, columns, model, dmetaphone_normalizer) end diff --git a/lib/pg_search/features/trigram.rb b/lib/pg_search/features/trigram.rb index 47f3cc68..80bdfed9 100644 --- a/lib/pg_search/features/trigram.rb +++ b/lib/pg_search/features/trigram.rb @@ -35,17 +35,17 @@ def word_similarity? def similarity_function if word_similarity? - 'word_similarity' + "word_similarity" else - 'similarity' + "similarity" end end def infix_operator if word_similarity? - '<%' + "<%" else - '%' + "%" end end diff --git a/lib/pg_search/features/tsearch.rb b/lib/pg_search/features/tsearch.rb index 365eddad..a4136bde 100644 --- a/lib/pg_search/features/tsearch.rb +++ b/lib/pg_search/features/tsearch.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true require "active_support/core_ext/module/delegation" -require 'active_support/deprecation' +require "active_support/deprecation" module PgSearch module Features - class TSearch < Feature # rubocop:disable Metrics/ClassLength + class TSearch < Feature def self.valid_options super + %i[dictionary prefix negation any_word normalization tsvector_column highlight] end @@ -36,7 +36,7 @@ def ts_headline end def ts_headline_options - return '' unless options[:highlight].is_a?(Hash) + return "" unless options[:highlight].is_a?(Hash) headline_options .merge(deprecated_headline_options) @@ -58,7 +58,7 @@ def headline_options end end - def deprecated_headline_options # rubocop:disable Metrics/MethodLength + def deprecated_headline_options indifferent_options = options.with_indifferent_access %w[ @@ -94,11 +94,11 @@ def ts_headline_option_value(value) end end - DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’ʻʼ]/.freeze + DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’ʻʼ]/ def tsquery_for_term(unsanitized_term) if options[:negation] && unsanitized_term.start_with?("!") - unsanitized_term[0] = '' + unsanitized_term[0] = "" negated = true end @@ -116,7 +116,7 @@ def tsquery_for_term(unsanitized_term) # If :negated is true, then the term will have ! prepended to the front. def tsquery_expression(term_sql, negated:, prefix:) terms = [ - (Arel::Nodes.build_quoted('!') if negated), + (Arel::Nodes.build_quoted("!") if negated), Arel::Nodes.build_quoted("' "), term_sql, Arel::Nodes.build_quoted(" '"), @@ -133,7 +133,7 @@ def tsquery query_terms = query.split.compact tsquery_terms = query_terms.map { |term| tsquery_for_term(term) } - tsquery_terms.join(options[:any_word] ? ' || ' : ' && ') + tsquery_terms.join(options[:any_word] ? " || " : " && ") end def tsdocument @@ -151,7 +151,7 @@ def tsdocument end end - tsdocument_terms.join(' || ') + tsdocument_terms.join(" || ") end # From http://www.postgresql.org/docs/8.3/static/textsearch-controls.html diff --git a/lib/pg_search/migration/dmetaphone_generator.rb b/lib/pg_search/migration/dmetaphone_generator.rb index 46e7888c..556a22e6 100644 --- a/lib/pg_search/migration/dmetaphone_generator.rb +++ b/lib/pg_search/migration/dmetaphone_generator.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -require 'pg_search/migration/generator' +require "pg_search/migration/generator" module PgSearch module Migration class DmetaphoneGenerator < Generator def migration_name - 'add_pg_search_dmetaphone_support_functions' + "add_pg_search_dmetaphone_support_functions" end end end diff --git a/lib/pg_search/migration/generator.rb b/lib/pg_search/migration/generator.rb index b0a422ae..2cf06a46 100644 --- a/lib/pg_search/migration/generator.rb +++ b/lib/pg_search/migration/generator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'active_record' -require 'rails/generators/base' +require "active_record" +require "rails/generators/base" module PgSearch module Migration @@ -10,19 +10,19 @@ class Generator < Rails::Generators::Base def self.inherited(subclass) super - subclass.source_root File.expand_path('templates', __dir__) + subclass.source_root File.expand_path("templates", __dir__) end def create_migration now = Time.now.utc - filename = "#{now.strftime('%Y%m%d%H%M%S')}_#{migration_name}.rb" + filename = "#{now.strftime("%Y%m%d%H%M%S")}_#{migration_name}.rb" template "#{migration_name}.rb.erb", "db/migrate/#{filename}", migration_version end private def read_sql_file(filename) - sql_directory = File.expand_path('../../../sql', __dir__) + sql_directory = File.expand_path("../../../sql", __dir__) source_path = File.join(sql_directory, "#{filename}.sql") File.read(source_path).strip end diff --git a/lib/pg_search/migration/multisearch_generator.rb b/lib/pg_search/migration/multisearch_generator.rb index c97fed4b..8f8cd860 100644 --- a/lib/pg_search/migration/multisearch_generator.rb +++ b/lib/pg_search/migration/multisearch_generator.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -require 'pg_search/migration/generator' +require "pg_search/migration/generator" module PgSearch module Migration class MultisearchGenerator < Generator def migration_name - 'create_pg_search_documents' + "create_pg_search_documents" end end end diff --git a/lib/pg_search/model.rb b/lib/pg_search/model.rb index eaa5ddf6..ceb7a0f1 100644 --- a/lib/pg_search/model.rb +++ b/lib/pg_search/model.rb @@ -7,12 +7,12 @@ module Model module ClassMethods def pg_search_scope(name, options) options_proc = if options.respond_to?(:call) - options - elsif options.respond_to?(:merge) - ->(query) { { query: query }.merge(options) } - else - raise ArgumentError, 'pg_search_scope expects a Hash or Proc' - end + options + elsif options.respond_to?(:merge) + ->(query) { {query: query}.merge(options) } + else + raise ArgumentError, "pg_search_scope expects a Hash or Proc" + end define_singleton_method(name) do |*args| config = Configuration.new(options_proc.call(*args), self) diff --git a/lib/pg_search/multisearchable.rb b/lib/pg_search/multisearchable.rb index 10a142f6..5309c1c0 100644 --- a/lib/pg_search/multisearchable.rb +++ b/lib/pg_search/multisearchable.rb @@ -7,12 +7,12 @@ module Multisearchable def self.included(mod) mod.class_eval do has_one :pg_search_document, - as: :searchable, - class_name: "PgSearch::Document", - dependent: :delete + as: :searchable, + class_name: "PgSearch::Document", + dependent: :delete after_save :update_pg_search_document, - if: -> { PgSearch.multisearch_enabled? } + if: -> { PgSearch.multisearch_enabled? } end end @@ -39,7 +39,7 @@ def should_update_pg_search_document? conditions.all? { |condition| condition.to_proc.call(self) } end - def update_pg_search_document # rubocop:disable Metrics/AbcSize + def update_pg_search_document if_conditions = Array(pg_search_multisearchable_options[:if]) unless_conditions = Array(pg_search_multisearchable_options[:unless]) diff --git a/lib/pg_search/normalizer.rb b/lib/pg_search/normalizer.rb index aed3f721..370b6a83 100644 --- a/lib/pg_search/normalizer.rb +++ b/lib/pg_search/normalizer.rb @@ -10,11 +10,11 @@ def add_normalization(sql_expression) return sql_expression unless config.ignore.include?(:accents) sql_node = case sql_expression - when Arel::Nodes::Node - sql_expression - else - Arel.sql(sql_expression) - end + when Arel::Nodes::Node + sql_expression + else + Arel.sql(sql_expression) + end Arel::Nodes::NamedFunction.new( PgSearch.unaccent_function, diff --git a/lib/pg_search/scope_options.rb b/lib/pg_search/scope_options.rb index 369a603a..b03d13d0 100644 --- a/lib/pg_search/scope_options.rb +++ b/lib/pg_search/scope_options.rb @@ -91,9 +91,9 @@ def subquery def conditions config.features - .reject { |_feature_name, feature_options| feature_options && feature_options[:sort_only] } - .map { |feature_name, _feature_options| feature_for(feature_name).conditions } - .inject { |accumulator, expression| Arel::Nodes::Or.new(accumulator, expression) } + .reject { |_feature_name, feature_options| feature_options && feature_options[:sort_only] } + .map { |feature_name, _feature_options| feature_for(feature_name).conditions } + .inject { |accumulator, expression| Arel::Nodes::Or.new(accumulator, expression) } end def order_within_rank @@ -108,7 +108,7 @@ def subquery_join if config.associations.any? config.associations.map do |association| association.join(primary_key) - end.join(' ') + end.join(" ") end end diff --git a/lib/pg_search/tasks.rb b/lib/pg_search/tasks.rb index 04fec234..3b5740f4 100644 --- a/lib/pg_search/tasks.rb +++ b/lib/pg_search/tasks.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'rake' -require 'pg_search' +require "rake" +require "pg_search" namespace :pg_search do namespace :multisearch do diff --git a/lib/pg_search/version.rb b/lib/pg_search/version.rb index 2a007da3..afff11ae 100644 --- a/lib/pg_search/version.rb +++ b/lib/pg_search/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module PgSearch - VERSION = '2.3.6' + VERSION = "2.3.6" end diff --git a/pg_search.gemspec b/pg_search.gemspec index 7a2d3685..103a9a38 100644 --- a/pg_search.gemspec +++ b/pg_search.gemspec @@ -1,25 +1,25 @@ # frozen_string_literal: true -$LOAD_PATH.push File.expand_path('lib', __dir__) -require 'pg_search/version' +$LOAD_PATH.push File.expand_path("lib", __dir__) +require "pg_search/version" Gem::Specification.new do |s| - s.name = 'pg_search' - s.version = PgSearch::VERSION - s.platform = Gem::Platform::RUBY - s.authors = ['Grant Hutchins', 'Case Commons, LLC'] - s.email = %w[gems@nertzy.com casecommons-dev@googlegroups.com] - s.homepage = 'https://github.com/Casecommons/pg_search' - s.summary = "PgSearch builds Active Record named scopes that take advantage of PostgreSQL's full text search" + s.name = "pg_search" + s.version = PgSearch::VERSION + s.platform = Gem::Platform::RUBY + s.authors = ["Grant Hutchins", "Case Commons, LLC"] + s.email = %w[gems@nertzy.com casecommons-dev@googlegroups.com] + s.homepage = "https://github.com/Casecommons/pg_search" + s.summary = "PgSearch builds Active Record named scopes that take advantage of PostgreSQL's full text search" s.description = "PgSearch builds Active Record named scopes that take advantage of PostgreSQL's full text search" - s.licenses = ['MIT'] + s.licenses = ["MIT"] s.metadata["rubygems_mfa_required"] = "true" - s.files = `git ls-files -z`.split("\x0") - s.require_paths = ['lib'] + s.files = `git ls-files -z`.split("\x0") + s.require_paths = ["lib"] - s.add_dependency 'activerecord', '>= 6.0' - s.add_dependency 'activesupport', '>= 6.0' + s.add_dependency "activerecord", ">= 6.0" + s.add_dependency "activesupport", ">= 6.0" - s.required_ruby_version = '>= 2.7' + s.required_ruby_version = ">= 2.7" end diff --git a/spec/.rubocop.yml b/spec/.rubocop.yml index 6e3e3040..ee624fbe 100644 --- a/spec/.rubocop.yml +++ b/spec/.rubocop.yml @@ -1,14 +1,27 @@ inherit_from: - ../.rubocop.yml -Layout/LineLength: - Enabled: false +RSpec/ContextWording: + Prefixes: + - using + - via + - when + - with + - without -Lint/SuppressedException: - Enabled: false +RSpec/DescribedClass: + Enabled: true -Lint/UselessAssignment: - Enabled: false +RSpec/ExampleLength: + Max: 15 -Style/BlockDelimiters: +RSpec/ExpectInHook: Enabled: false + +RSpec/FilePath: + CustomTransform: + TSearch: "tsearch" + DMetaphone: "dmetaphone" + +RSpec/MultipleExpectations: + Max: 5 diff --git a/spec/integration/.rubocop.yml b/spec/integration/.rubocop.yml index 09ebbc02..b76a90e7 100644 --- a/spec/integration/.rubocop.yml +++ b/spec/integration/.rubocop.yml @@ -4,8 +4,8 @@ inherit_from: RSpec/DescribeClass: Enabled: false -RSpec/MultipleExpectations: +RSpec/ExampleLength: Enabled: false -RSpec/ExampleLength: +RSpec/MultipleExpectations: Enabled: false diff --git a/spec/integration/associations_spec.rb b/spec/integration/associations_spec.rb index a0801ed6..e929c2af 100644 --- a/spec/integration/associations_spec.rb +++ b/spec/integration/associations_spec.rb @@ -20,23 +20,23 @@ model do include PgSearch::Model - belongs_to :another_model, class_name: 'AssociatedModel' + belongs_to :another_model, class_name: "AssociatedModel" - pg_search_scope :with_another, associated_against: { another_model: :title } + pg_search_scope :with_another, associated_against: {another_model: :title} end end it "returns rows that match the query in the columns of the associated model only" do - associated = AssociatedModel.create!(title: 'abcdef') + associated = AssociatedModel.create!(title: "abcdef") included = [ - ModelWithoutAgainst.create!(title: 'abcdef', another_model: associated), - ModelWithoutAgainst.create!(title: 'ghijkl', another_model: associated) + ModelWithoutAgainst.create!(title: "abcdef", another_model: associated), + ModelWithoutAgainst.create!(title: "ghijkl", another_model: associated) ] excluded = [ - ModelWithoutAgainst.create!(title: 'abcdef') + ModelWithoutAgainst.create!(title: "abcdef") ] - results = ModelWithoutAgainst.with_another('abcdef') + results = ModelWithoutAgainst.with_another("abcdef") expect(results.map(&:title)).to match_array(included.map(&:title)) expect(results).not_to include(excluded) end @@ -45,34 +45,34 @@ context "via a belongs_to association" do with_model :AssociatedModel do table do |t| - t.string 'title' + t.string "title" end end with_model :ModelWithBelongsTo do table do |t| - t.string 'title' - t.belongs_to 'another_model', index: false + t.string "title" + t.belongs_to "another_model", index: false end model do include PgSearch::Model - belongs_to :another_model, class_name: 'AssociatedModel' + belongs_to :another_model, class_name: "AssociatedModel" - pg_search_scope :with_associated, against: :title, associated_against: { another_model: :title } + pg_search_scope :with_associated, against: :title, associated_against: {another_model: :title} end end it "returns rows that match the query in either its own columns or the columns of the associated model" do - associated = AssociatedModel.create!(title: 'abcdef') + associated = AssociatedModel.create!(title: "abcdef") included = [ - ModelWithBelongsTo.create!(title: 'ghijkl', another_model: associated), - ModelWithBelongsTo.create!(title: 'abcdef') + ModelWithBelongsTo.create!(title: "ghijkl", another_model: associated), + ModelWithBelongsTo.create!(title: "abcdef") ] - excluded = ModelWithBelongsTo.create!(title: 'mnopqr', - another_model: AssociatedModel.create!(title: 'stuvwx')) + excluded = ModelWithBelongsTo.create!(title: "mnopqr", + another_model: AssociatedModel.create!(title: "stuvwx")) - results = ModelWithBelongsTo.with_associated('abcdef') + results = ModelWithBelongsTo.with_associated("abcdef") expect(results.map(&:title)).to match_array(included.map(&:title)) expect(results).not_to include(excluded) end @@ -81,61 +81,61 @@ context "via a has_many association" do with_model :AssociatedModelWithHasMany do table do |t| - t.string 'title' - t.belongs_to 'ModelWithHasMany', index: false + t.string "title" + t.belongs_to "ModelWithHasMany", index: false end end with_model :ModelWithHasMany do table do |t| - t.string 'title' + t.string "title" end model do include PgSearch::Model - has_many :other_models, class_name: 'AssociatedModelWithHasMany', foreign_key: 'ModelWithHasMany_id' + has_many :other_models, class_name: "AssociatedModelWithHasMany", foreign_key: "ModelWithHasMany_id" - pg_search_scope :with_associated, against: [:title], associated_against: { other_models: :title } + pg_search_scope :with_associated, against: [:title], associated_against: {other_models: :title} end end it "returns rows that match the query in either its own columns or the columns of the associated model" do included = [ - ModelWithHasMany.create!(title: 'abcdef', other_models: [ - AssociatedModelWithHasMany.create!(title: 'foo'), - AssociatedModelWithHasMany.create!(title: 'bar') + ModelWithHasMany.create!(title: "abcdef", other_models: [ + AssociatedModelWithHasMany.create!(title: "foo"), + AssociatedModelWithHasMany.create!(title: "bar") ]), - ModelWithHasMany.create!(title: 'ghijkl', other_models: [ - AssociatedModelWithHasMany.create!(title: 'foo bar'), - AssociatedModelWithHasMany.create!(title: 'mnopqr') + ModelWithHasMany.create!(title: "ghijkl", other_models: [ + AssociatedModelWithHasMany.create!(title: "foo bar"), + AssociatedModelWithHasMany.create!(title: "mnopqr") ]), - ModelWithHasMany.create!(title: 'foo bar') + ModelWithHasMany.create!(title: "foo bar") ] - excluded = ModelWithHasMany.create!(title: 'stuvwx', other_models: [ - AssociatedModelWithHasMany.create!(title: 'abcdef') + excluded = ModelWithHasMany.create!(title: "stuvwx", other_models: [ + AssociatedModelWithHasMany.create!(title: "abcdef") ]) - results = ModelWithHasMany.with_associated('foo bar') + results = ModelWithHasMany.with_associated("foo bar") expect(results.map(&:title)).to match_array(included.map(&:title)) expect(results).not_to include(excluded) end it "uses an unscoped relation of the associated model" do - excluded = ModelWithHasMany.create!(title: 'abcdef', other_models: [ - AssociatedModelWithHasMany.create!(title: 'abcdef') + excluded = ModelWithHasMany.create!(title: "abcdef", other_models: [ + AssociatedModelWithHasMany.create!(title: "abcdef") ]) included = [ - ModelWithHasMany.create!(title: 'abcdef', other_models: [ - AssociatedModelWithHasMany.create!(title: 'foo'), - AssociatedModelWithHasMany.create!(title: 'bar') + ModelWithHasMany.create!(title: "abcdef", other_models: [ + AssociatedModelWithHasMany.create!(title: "foo"), + AssociatedModelWithHasMany.create!(title: "bar") ]) ] results = ModelWithHasMany - .limit(1) - .order(Arel.sql("#{ModelWithHasMany.quoted_table_name}.id ASC")) - .with_associated('foo bar') + .limit(1) + .order(Arel.sql("#{ModelWithHasMany.quoted_table_name}.id ASC")) + .with_associated("foo bar") expect(results.map(&:title)).to match_array(included.map(&:title)) expect(results).not_to include(excluded) @@ -146,36 +146,36 @@ context "when on different tables" do with_model :FirstAssociatedModel do table do |t| - t.string 'title' - t.belongs_to 'ModelWithManyAssociations', index: false + t.string "title" + t.belongs_to "ModelWithManyAssociations", index: false end end with_model :SecondAssociatedModel do table do |t| - t.string 'title' + t.string "title" end end with_model :ModelWithManyAssociations do table do |t| - t.string 'title' - t.belongs_to 'model_of_second_type', index: false + t.string "title" + t.belongs_to "model_of_second_type", index: false end model do include PgSearch::Model has_many :models_of_first_type, - class_name: 'FirstAssociatedModel', - foreign_key: 'ModelWithManyAssociations_id' + class_name: "FirstAssociatedModel", + foreign_key: "ModelWithManyAssociations_id" belongs_to :model_of_second_type, - class_name: 'SecondAssociatedModel' + class_name: "SecondAssociatedModel" pg_search_scope :with_associated, - against: :title, - associated_against: { models_of_first_type: :title, model_of_second_type: :title } + against: :title, + associated_against: {models_of_first_type: :title, model_of_second_type: :title} end end @@ -184,25 +184,25 @@ unmatching_second = SecondAssociatedModel.create!(title: "uiop") included = [ - ModelWithManyAssociations.create!(title: 'abcdef', models_of_first_type: [ - FirstAssociatedModel.create!(title: 'foo'), - FirstAssociatedModel.create!(title: 'bar') + ModelWithManyAssociations.create!(title: "abcdef", models_of_first_type: [ + FirstAssociatedModel.create!(title: "foo"), + FirstAssociatedModel.create!(title: "bar") ]), - ModelWithManyAssociations.create!(title: 'ghijkl', models_of_first_type: [ - FirstAssociatedModel.create!(title: 'foo bar'), - FirstAssociatedModel.create!(title: 'mnopqr') + ModelWithManyAssociations.create!(title: "ghijkl", models_of_first_type: [ + FirstAssociatedModel.create!(title: "foo bar"), + FirstAssociatedModel.create!(title: "mnopqr") ]), - ModelWithManyAssociations.create!(title: 'foo bar'), - ModelWithManyAssociations.create!(title: 'qwerty', model_of_second_type: matching_second) + ModelWithManyAssociations.create!(title: "foo bar"), + ModelWithManyAssociations.create!(title: "qwerty", model_of_second_type: matching_second) ] excluded = [ - ModelWithManyAssociations.create!(title: 'stuvwx', models_of_first_type: [ - FirstAssociatedModel.create!(title: 'abcdef') + ModelWithManyAssociations.create!(title: "stuvwx", models_of_first_type: [ + FirstAssociatedModel.create!(title: "abcdef") ]), - ModelWithManyAssociations.create!(title: 'qwerty', model_of_second_type: unmatching_second) + ModelWithManyAssociations.create!(title: "qwerty", model_of_second_type: unmatching_second) ] - results = ModelWithManyAssociations.with_associated('foo bar') + results = ModelWithManyAssociations.with_associated("foo bar") expect(results.map(&:title)).to match_array(included.map(&:title)) excluded.each { |object| expect(results).not_to include(object) } end @@ -211,58 +211,58 @@ context "when on the same table" do with_model :DoublyAssociatedModel do table do |t| - t.string 'title' - t.belongs_to 'ModelWithDoubleAssociation', index: false - t.belongs_to 'ModelWithDoubleAssociation_again', index: false + t.string "title" + t.belongs_to "ModelWithDoubleAssociation", index: false + t.belongs_to "ModelWithDoubleAssociation_again", index: false end end with_model :ModelWithDoubleAssociation do table do |t| - t.string 'title' + t.string "title" end model do include PgSearch::Model has_many :things, - class_name: 'DoublyAssociatedModel', - foreign_key: 'ModelWithDoubleAssociation_id' + class_name: "DoublyAssociatedModel", + foreign_key: "ModelWithDoubleAssociation_id" has_many :thingamabobs, - class_name: 'DoublyAssociatedModel', - foreign_key: 'ModelWithDoubleAssociation_again_id' + class_name: "DoublyAssociatedModel", + foreign_key: "ModelWithDoubleAssociation_again_id" pg_search_scope :with_associated, against: :title, - associated_against: { things: :title, thingamabobs: :title } + associated_against: {things: :title, thingamabobs: :title} end end it "returns rows that match the query in either its own columns or the columns of the associated model" do included = [ - ModelWithDoubleAssociation.create!(title: 'abcdef', things: [ - DoublyAssociatedModel.create!(title: 'foo'), - DoublyAssociatedModel.create!(title: 'bar') + ModelWithDoubleAssociation.create!(title: "abcdef", things: [ + DoublyAssociatedModel.create!(title: "foo"), + DoublyAssociatedModel.create!(title: "bar") ]), - ModelWithDoubleAssociation.create!(title: 'ghijkl', things: [ - DoublyAssociatedModel.create!(title: 'foo bar'), - DoublyAssociatedModel.create!(title: 'mnopqr') + ModelWithDoubleAssociation.create!(title: "ghijkl", things: [ + DoublyAssociatedModel.create!(title: "foo bar"), + DoublyAssociatedModel.create!(title: "mnopqr") ]), - ModelWithDoubleAssociation.create!(title: 'foo bar'), - ModelWithDoubleAssociation.create!(title: 'qwerty', thingamabobs: [ + ModelWithDoubleAssociation.create!(title: "foo bar"), + ModelWithDoubleAssociation.create!(title: "qwerty", thingamabobs: [ DoublyAssociatedModel.create!(title: "foo bar") ]) ] excluded = [ - ModelWithDoubleAssociation.create!(title: 'stuvwx', things: [ - DoublyAssociatedModel.create!(title: 'abcdef') + ModelWithDoubleAssociation.create!(title: "stuvwx", things: [ + DoublyAssociatedModel.create!(title: "abcdef") ]), - ModelWithDoubleAssociation.create!(title: 'qwerty', thingamabobs: [ + ModelWithDoubleAssociation.create!(title: "qwerty", thingamabobs: [ DoublyAssociatedModel.create!(title: "uiop") ]) ] - results = ModelWithDoubleAssociation.with_associated('foo bar') + results = ModelWithDoubleAssociation.with_associated("foo bar") expect(results.map(&:title)).to match_array(included.map(&:title)) excluded.each { |object| expect(results).not_to include(object) } end @@ -272,21 +272,21 @@ context "when against multiple attributes on one association" do with_model :AssociatedModel do table do |t| - t.string 'title' - t.text 'author' + t.string "title" + t.text "author" end end with_model :ModelWithAssociation do table do |t| - t.belongs_to 'another_model', index: false + t.belongs_to "another_model", index: false end model do include PgSearch::Model - belongs_to :another_model, class_name: 'AssociatedModel' + belongs_to :another_model, class_name: "AssociatedModel" - pg_search_scope :with_associated, associated_against: { another_model: %i[title author] } + pg_search_scope :with_associated, associated_against: {another_model: %i[title author]} end end @@ -314,7 +314,7 @@ ) ] - results = ModelWithAssociation.with_associated('foo bar') + results = ModelWithAssociation.with_associated("foo bar") expect(results.to_sql.scan("INNER JOIN #{AssociatedModel.quoted_table_name}").length).to eq(1) included.each { |object| expect(results).to include(object) } @@ -325,21 +325,21 @@ context "when against non-text columns" do with_model :AssociatedModel do table do |t| - t.integer 'number' + t.integer "number" end end with_model :Model do table do |t| - t.integer 'number' - t.belongs_to 'another_model', index: false + t.integer "number" + t.belongs_to "another_model", index: false end model do include PgSearch::Model - belongs_to :another_model, class_name: 'AssociatedModel' + belongs_to :another_model, class_name: "AssociatedModel" - pg_search_scope :with_associated, associated_against: { another_model: :number } + pg_search_scope :with_associated, associated_against: {another_model: :number} end end @@ -353,7 +353,7 @@ Model.create!(number: 123) ] - results = Model.with_associated('123') + results = Model.with_associated("123") expect(results.map(&:number)).to match_array(included.map(&:number)) expect(results).not_to include(excluded) end @@ -384,10 +384,10 @@ # https://github.com/Casecommons/pg_search/issues/14 it "supports queries with periods" do - included = Parent.create!(name: 'bar.foo') - excluded = Parent.create!(name: 'foo.bar') + included = Parent.create!(name: "bar.foo") + excluded = Parent.create!(name: "foo.bar") - results = Parent.search_name('bar.foo').includes(:children) + results = Parent.search_name("bar.foo").includes(:children) results.to_a expect(results).to include(included) @@ -477,7 +477,7 @@ Position.create!(company_id: company.id, title: "penn 1") ] - results = company.positions.search('teller 1') + results = company.positions.search("teller 1") expect(results).to include(*included) expect(results).not_to include(*excluded) diff --git a/spec/integration/pg_search_spec.rb b/spec/integration/pg_search_spec.rb index 7005316d..f5fd4728 100644 --- a/spec/integration/pg_search_spec.rb +++ b/spec/integration/pg_search_spec.rb @@ -6,10 +6,10 @@ describe "an Active Record model which includes PgSearch" do with_model :ModelWithPgSearch do table do |t| - t.string 'title' - t.text 'content' - t.integer 'parent_model_id' - t.integer 'importance' + t.string "title" + t.text "content" + t.integer "parent_model_id" + t.integer "importance" end model do @@ -39,26 +39,38 @@ context "when passed a lambda" do it "builds a dynamic scope" do ModelWithPgSearch.pg_search_scope :search_title_or_content, - lambda { |query, pick_content| - { - query: query.gsub("-remove-", ""), - against: pick_content ? :content : :title - } - } + lambda { |query, pick_content| + { + query: query.gsub("-remove-", ""), + against: pick_content ? :content : :title + } + } + + included = ModelWithPgSearch.create!(title: "foo", content: "bar") + ModelWithPgSearch.create!(title: "bar", content: "foo") - included = ModelWithPgSearch.create!(title: 'foo', content: 'bar') - excluded = ModelWithPgSearch.create!(title: 'bar', content: 'foo') + expect(ModelWithPgSearch.search_title_or_content("fo-remove-o", false)).to eq([included]) + expect(ModelWithPgSearch.search_title_or_content("b-remove-ar", true)).to eq([included]) + end + end - expect(ModelWithPgSearch.search_title_or_content('fo-remove-o', false)).to eq([included]) - expect(ModelWithPgSearch.search_title_or_content('b-remove-ar', true)).to eq([included]) + context "when passed an invalid argument" do + it "builds a dynamic scope" do + expect { + ModelWithPgSearch.pg_search_scope :search_title_or_content, :some_symbol + }.to( + raise_exception(ArgumentError).with_message( + "pg_search_scope expects a Hash or Proc" + ) + ) end end context "when an unknown option is passed in" do it "raises an exception when invoked" do ModelWithPgSearch.pg_search_scope :with_unknown_option, - against: :content, - foo: :bar + against: :content, + foo: :bar expect { ModelWithPgSearch.with_unknown_option("foo") @@ -68,7 +80,7 @@ context "with a lambda" do it "raises an exception when invoked" do ModelWithPgSearch.pg_search_scope :with_unknown_option, - ->(*) { { against: :content, foo: :bar } } + ->(*) { {against: :content, foo: :bar} } expect { ModelWithPgSearch.with_unknown_option("foo") @@ -80,8 +92,8 @@ context "when an unknown :using is passed" do it "raises an exception when invoked" do ModelWithPgSearch.pg_search_scope :with_unknown_using, - against: :content, - using: :foo + against: :content, + using: :foo expect { ModelWithPgSearch.with_unknown_using("foo") @@ -91,7 +103,7 @@ context "with a lambda" do it "raises an exception when invoked" do ModelWithPgSearch.pg_search_scope :with_unknown_using, - ->(*) { { against: :content, using: :foo } } + ->(*) { {against: :content, using: :foo} } expect { ModelWithPgSearch.with_unknown_using("foo") @@ -103,8 +115,8 @@ context "when an unknown :ignoring is passed" do it "raises an exception when invoked" do ModelWithPgSearch.pg_search_scope :with_unknown_ignoring, - against: :content, - ignoring: :foo + against: :content, + ignoring: :foo expect { ModelWithPgSearch.with_unknown_ignoring("foo") @@ -114,7 +126,7 @@ context "with a lambda" do it "raises an exception when invoked" do ModelWithPgSearch.pg_search_scope :with_unknown_ignoring, - ->(*) { { against: :content, ignoring: :foo } } + ->(*) { {against: :content, ignoring: :foo} } expect { ModelWithPgSearch.with_unknown_ignoring("foo") @@ -168,15 +180,15 @@ context "when chained after a select() scope" do it "honors the select" do - included = ModelWithPgSearch.create!(content: 'foo', title: 'bar') - excluded = ModelWithPgSearch.create!(content: 'bar', title: 'foo') + included = ModelWithPgSearch.create!(content: "foo", title: "bar") + excluded = ModelWithPgSearch.create!(content: "bar", title: "foo") - results = ModelWithPgSearch.select('id, title').search_content('foo') + results = ModelWithPgSearch.select("id, title").search_content("foo") expect(results).to include(included) expect(results).not_to include(excluded) - expect(results.first.attributes.key?('content')).to be false + expect(results.first.attributes.key?("content")).to be false expect(results.select { |record| record.title == "bar" }).to eq [included] expect(results.reject { |record| record.title == "bar" }).to be_empty @@ -185,15 +197,15 @@ context "when chained before a select() scope" do it "honors the select" do - included = ModelWithPgSearch.create!(content: 'foo', title: 'bar') - excluded = ModelWithPgSearch.create!(content: 'bar', title: 'foo') + included = ModelWithPgSearch.create!(content: "foo", title: "bar") + excluded = ModelWithPgSearch.create!(content: "bar", title: "foo") - results = ModelWithPgSearch.search_content('foo').select('id, title') + results = ModelWithPgSearch.search_content("foo").select("id, title") expect(results).to include(included) expect(results).not_to include(excluded) - expect(results.first.attributes.key?('content')).to be false + expect(results.first.attributes.key?("content")).to be false expect(results.select { |record| record.title == "bar" }).to eq [included] expect(results.reject { |record| record.title == "bar" }).to be_empty @@ -202,15 +214,15 @@ context "when surrouned by select() scopes" do it "honors the select" do - included = ModelWithPgSearch.create!(content: 'foo', title: 'bar') - excluded = ModelWithPgSearch.create!(content: 'bar', title: 'foo') + included = ModelWithPgSearch.create!(content: "foo", title: "bar") + excluded = ModelWithPgSearch.create!(content: "bar", title: "foo") - results = ModelWithPgSearch.select('id').search_content('foo').select('title') + results = ModelWithPgSearch.select("id").search_content("foo").select("title") expect(results).to include(included) expect(results).not_to include(excluded) - expect(results.first.attributes.key?('content')).to be false + expect(results.first.attributes.key?("content")).to be false expect(results.select { |record| record.title == "bar" }).to eq [included] expect(results.reject { |record| record.title == "bar" }).to be_empty @@ -241,7 +253,7 @@ has_many :houses pg_search_scope :named, against: [:name] scope :with_house_in_city, lambda { |city| - joins(:houses).where(House.table_name.to_sym => { city: city }) + joins(:houses).where(House.table_name.to_sym => {city: city}) } scope :house_search_city, lambda { |query| joins(:houses).merge(House.search_city(query)) @@ -285,7 +297,7 @@ context "when chaining merged scopes" do it "does not raise an exception" do - relation = Person.named('foo').house_search_city('bar') + relation = Person.named("foo").house_search_city("bar") expect { relation.to_a }.not_to raise_error end @@ -298,49 +310,49 @@ end it "does not raise an exception" do - relation = ModelWithPgSearch.search_content('foo').search_title('bar') + relation = ModelWithPgSearch.search_content("foo").search_title("bar") expect { relation.to_a }.not_to raise_error end end it "returns an empty array when a blank query is passed in" do - ModelWithPgSearch.create!(content: 'foo') + ModelWithPgSearch.create!(content: "foo") - results = ModelWithPgSearch.search_content('') + results = ModelWithPgSearch.search_content("") expect(results).to eq([]) end it "returns rows where the column contains the term in the query" do - included = ModelWithPgSearch.create!(content: 'foo') - excluded = ModelWithPgSearch.create!(content: 'bar') + included = ModelWithPgSearch.create!(content: "foo") + excluded = ModelWithPgSearch.create!(content: "bar") - results = ModelWithPgSearch.search_content('foo') + results = ModelWithPgSearch.search_content("foo") expect(results).to include(included) expect(results).not_to include(excluded) end it "returns the correct count" do - ModelWithPgSearch.create!(content: 'foo') - ModelWithPgSearch.create!(content: 'bar') + ModelWithPgSearch.create!(content: "foo") + ModelWithPgSearch.create!(content: "bar") - results = ModelWithPgSearch.search_content('foo') + results = ModelWithPgSearch.search_content("foo") expect(results.count).to eq 1 end it "returns the correct count(:all)" do - ModelWithPgSearch.create!(content: 'foo') - ModelWithPgSearch.create!(content: 'bar') + ModelWithPgSearch.create!(content: "foo") + ModelWithPgSearch.create!(content: "bar") - results = ModelWithPgSearch.search_content('foo') + results = ModelWithPgSearch.search_content("foo") expect(results.count(:all)).to eq 1 end it "supports #select" do - record = ModelWithPgSearch.create!(content: 'foo') - other_record = ModelWithPgSearch.create!(content: 'bar') + record = ModelWithPgSearch.create!(content: "foo") + ModelWithPgSearch.create!(content: "bar") - records_with_only_id = ModelWithPgSearch.search_content('foo').select('id') + records_with_only_id = ModelWithPgSearch.search_content("foo").select("id") expect(records_with_only_id.length).to eq 1 returned_record = records_with_only_id.first @@ -349,36 +361,36 @@ end it "supports #pluck" do - record = ModelWithPgSearch.create!(content: 'foo') - other_record = ModelWithPgSearch.create!(content: 'bar') + record = ModelWithPgSearch.create!(content: "foo") + ModelWithPgSearch.create!(content: "bar") - ids = ModelWithPgSearch.search_content('foo').pluck('id') + ids = ModelWithPgSearch.search_content("foo").pluck("id") expect(ids).to eq [record.id] end it "supports adding where clauses using the pg_search.rank" do - once = ModelWithPgSearch.create!(content: 'foo bar') - twice = ModelWithPgSearch.create!(content: 'foo foo') + ModelWithPgSearch.create!(content: "foo bar") + twice = ModelWithPgSearch.create!(content: "foo foo") - records = ModelWithPgSearch.search_content('foo') - .where("#{PgSearch::Configuration.alias(ModelWithPgSearch.table_name)}.rank > 0.07") + records = ModelWithPgSearch.search_content("foo") + .where("#{PgSearch::Configuration.alias(ModelWithPgSearch.table_name)}.rank > 0.07") expect(records).to eq [twice] end it "returns rows where the column contains all the terms in the query in any order" do - included = [ModelWithPgSearch.create!(content: 'foo bar'), - ModelWithPgSearch.create!(content: 'bar foo')] - excluded = ModelWithPgSearch.create!(content: 'foo') + included = [ModelWithPgSearch.create!(content: "foo bar"), + ModelWithPgSearch.create!(content: "bar foo")] + excluded = ModelWithPgSearch.create!(content: "foo") - results = ModelWithPgSearch.search_content('foo bar') + results = ModelWithPgSearch.search_content("foo bar") expect(results).to match_array(included) expect(results).not_to include(excluded) end it "returns rows that match the query but not its case" do included = [ModelWithPgSearch.create!(content: "foo"), - ModelWithPgSearch.create!(content: "FOO")] + ModelWithPgSearch.create!(content: "FOO")] results = ModelWithPgSearch.search_content("Foo") expect(results).to match_array(included) @@ -397,8 +409,8 @@ end it "returns rows that match the query but not rows that are prefixed by the query" do - included = ModelWithPgSearch.create!(content: 'pre') - excluded = ModelWithPgSearch.create!(content: 'prefix') + included = ModelWithPgSearch.create!(content: "pre") + excluded = ModelWithPgSearch.create!(content: "prefix") results = ModelWithPgSearch.search_content("pre") expect(results).to eq([included]) @@ -407,36 +419,36 @@ it "returns rows that match the query exactly and not those that match the query when stemmed by the default english dictionary" do included = ModelWithPgSearch.create!(content: "jumped") - excluded = [ModelWithPgSearch.create!(content: "jump"), - ModelWithPgSearch.create!(content: "jumping")] + ModelWithPgSearch.create!(content: "jump") + ModelWithPgSearch.create!(content: "jumping") results = ModelWithPgSearch.search_content("jumped") expect(results).to eq([included]) end it "returns rows that match sorted by rank" do - loser = ModelWithPgSearch.create!(content: 'foo') - winner = ModelWithPgSearch.create!(content: 'foo foo') + loser = ModelWithPgSearch.create!(content: "foo") + winner = ModelWithPgSearch.create!(content: "foo foo") results = ModelWithPgSearch.search_content("foo").with_pg_search_rank expect(results[0].pg_search_rank).to be > results[1].pg_search_rank expect(results).to eq([winner, loser]) end - it 'preserves column selection when with_pg_search_rank is chained after a select()' do - loser = ModelWithPgSearch.create!(title: 'foo', content: 'bar') + it "preserves column selection when with_pg_search_rank is chained after a select()" do + ModelWithPgSearch.create!(title: "foo", content: "bar") - results = ModelWithPgSearch.search_content('bar').select(:content).with_pg_search_rank + results = ModelWithPgSearch.search_content("bar").select(:content).with_pg_search_rank expect(results.length).to be 1 - expect(results.first.as_json.keys).to contain_exactly('id', 'content', 'pg_search_rank') + expect(results.first.as_json.keys).to contain_exactly("id", "content", "pg_search_rank") end - it 'allows pg_search_rank along with a join' do + it "allows pg_search_rank along with a join" do parent_1 = ParentModel.create!(id: 98) parent_2 = ParentModel.create!(id: 99) - loser = ModelWithPgSearch.create!(content: 'foo', parent_model: parent_2) - winner = ModelWithPgSearch.create!(content: 'foo foo', parent_model: parent_1) + loser = ModelWithPgSearch.create!(content: "foo", parent_model: parent_2) + winner = ModelWithPgSearch.create!(content: "foo foo", parent_model: parent_1) results = ModelWithPgSearch.joins(:parent_model).merge(ParentModel.active).search_content("foo").with_pg_search_rank expect(results.map(&:id)).to eq [winner.id, loser.id] @@ -445,8 +457,8 @@ end it "returns results that match sorted by primary key for records that rank the same" do - sorted_results = [ModelWithPgSearch.create!(content: 'foo'), - ModelWithPgSearch.create!(content: 'foo')].sort_by(&:id) + sorted_results = [ModelWithPgSearch.create!(content: "foo"), + ModelWithPgSearch.create!(content: "foo")].sort_by(&:id) results = ModelWithPgSearch.search_content("foo") expect(results).to eq(sorted_results) @@ -454,16 +466,16 @@ it "returns results that match a query with multiple space-separated search terms" do included = [ - ModelWithPgSearch.create!(content: 'foo bar'), - ModelWithPgSearch.create!(content: 'bar foo'), - ModelWithPgSearch.create!(content: 'bar foo baz') + ModelWithPgSearch.create!(content: "foo bar"), + ModelWithPgSearch.create!(content: "bar foo"), + ModelWithPgSearch.create!(content: "bar foo baz") ] excluded = [ - ModelWithPgSearch.create!(content: 'foo'), - ModelWithPgSearch.create!(content: 'foo baz') + ModelWithPgSearch.create!(content: "foo"), + ModelWithPgSearch.create!(content: "foo baz") ] - results = ModelWithPgSearch.search_content('foo bar') + results = ModelWithPgSearch.search_content("foo bar") expect(results).to match_array(included) expect(results).not_to include(excluded) end @@ -493,7 +505,7 @@ # WARNING: searching timestamps is not something PostgreSQL # full-text search is good at. Use at your own risk. pg_search_scope :search_timestamps, - against: %i[created_at updated_at] + against: %i[created_at updated_at] end end @@ -514,15 +526,15 @@ it "returns rows whose columns contain all of the terms in the query across columns" do included = [ - ModelWithPgSearch.create!(title: 'foo', content: 'bar'), - ModelWithPgSearch.create!(title: 'bar', content: 'foo') + ModelWithPgSearch.create!(title: "foo", content: "bar"), + ModelWithPgSearch.create!(title: "bar", content: "foo") ] excluded = [ - ModelWithPgSearch.create!(title: 'foo', content: 'foo'), - ModelWithPgSearch.create!(title: 'bar', content: 'bar') + ModelWithPgSearch.create!(title: "foo", content: "foo"), + ModelWithPgSearch.create!(title: "bar", content: "bar") ] - results = ModelWithPgSearch.search_title_and_content('foo bar') + results = ModelWithPgSearch.search_title_and_content("foo bar") expect(results).to match_array(included) excluded.each do |result| @@ -531,17 +543,17 @@ end it "returns rows where at one column contains all of the terms in the query and another does not" do - in_title = ModelWithPgSearch.create!(title: 'foo', content: 'bar') - in_content = ModelWithPgSearch.create!(title: 'bar', content: 'foo') + in_title = ModelWithPgSearch.create!(title: "foo", content: "bar") + in_content = ModelWithPgSearch.create!(title: "bar", content: "foo") - results = ModelWithPgSearch.search_title_and_content('foo') + results = ModelWithPgSearch.search_title_and_content("foo") expect(results).to match_array([in_title, in_content]) end # Searching with a NULL column will prevent any matches unless we coalesce it. it "returns rows where at one column contains all of the terms in the query and another is NULL" do - included = ModelWithPgSearch.create!(title: 'foo', content: nil) - results = ModelWithPgSearch.search_title_and_content('foo') + included = ModelWithPgSearch.create!(title: "foo", content: nil) + results = ModelWithPgSearch.search_title_and_content("foo") expect(results).to eq([included]) end end @@ -552,21 +564,21 @@ end it "returns rows where one searchable column and the query share enough trigrams" do - included = ModelWithPgSearch.create!(title: 'abcdefghijkl', content: nil) - results = ModelWithPgSearch.with_trigrams('cdefhijkl') + included = ModelWithPgSearch.create!(title: "abcdefghijkl", content: nil) + results = ModelWithPgSearch.with_trigrams("cdefhijkl") expect(results).to eq([included]) end it "returns rows where multiple searchable columns and the query share enough trigrams" do - included = ModelWithPgSearch.create!(title: 'abcdef', content: 'ghijkl') - results = ModelWithPgSearch.with_trigrams('cdefhijkl') + included = ModelWithPgSearch.create!(title: "abcdef", content: "ghijkl") + results = ModelWithPgSearch.with_trigrams("cdefhijkl") expect(results).to eq([included]) end context "when a threshold is specified" do before do - ModelWithPgSearch.pg_search_scope :with_strict_trigrams, against: %i[title content], using: { trigram: { threshold: 0.5 } } - ModelWithPgSearch.pg_search_scope :with_permissive_trigrams, against: %i[title content], using: { trigram: { threshold: 0.1 } } + ModelWithPgSearch.pg_search_scope :with_strict_trigrams, against: %i[title content], using: {trigram: {threshold: 0.5}} + ModelWithPgSearch.pg_search_scope :with_permissive_trigrams, against: %i[title content], using: {trigram: {threshold: 0.1}} end it "uses the threshold in the trigram expression" do @@ -591,16 +603,16 @@ context "when using tsearch" do before do ModelWithPgSearch.pg_search_scope :search_title_with_prefixes, - against: :title, - using: { - tsearch: { prefix: true } - } + against: :title, + using: { + tsearch: {prefix: true} + } end context "with prefix: true" do it "returns rows that match the query and that are prefixed by the query" do - included = ModelWithPgSearch.create!(title: 'prefix') - excluded = ModelWithPgSearch.create!(title: 'postfix') + included = ModelWithPgSearch.create!(title: "prefix") + excluded = ModelWithPgSearch.create!(title: "postfix") results = ModelWithPgSearch.search_title_with_prefixes("pre") expect(results).to eq([included]) @@ -608,8 +620,8 @@ end it "returns rows that match the query when the query has a hyphen" do - included = ModelWithPgSearch.create!(title: 'foo-bar') - excluded = ModelWithPgSearch.create!(title: 'foo bar') + included = ModelWithPgSearch.create!(title: "foo-bar") + excluded = ModelWithPgSearch.create!(title: "foo bar") results = ModelWithPgSearch.search_title_with_prefixes("foo-bar") expect(results).to include(included) @@ -620,16 +632,16 @@ context "with the english dictionary" do before do ModelWithPgSearch.pg_search_scope :search_content_with_english, - against: :content, - using: { - tsearch: { dictionary: :english } - } + against: :content, + using: { + tsearch: {dictionary: :english} + } end it "returns rows that match the query when stemmed by the english dictionary" do included = [ModelWithPgSearch.create!(content: "jump"), - ModelWithPgSearch.create!(content: "jumped"), - ModelWithPgSearch.create!(content: "jumping")] + ModelWithPgSearch.create!(content: "jumped"), + ModelWithPgSearch.create!(content: "jumping")] results = ModelWithPgSearch.search_content_with_english("jump") expect(results).to match_array(included) @@ -639,14 +651,14 @@ describe "highlighting" do before do ["Strip Down", "Down", "Down and Out", "Won't Let You Down"].each do |name| - ModelWithPgSearch.create! title: 'Just a title', content: name + ModelWithPgSearch.create! title: "Just a title", content: name end end context "with highlight turned on" do before do ModelWithPgSearch.pg_search_scope :search_content, - against: :content + against: :content end it "adds a #pg_search_highlight method to each returned model record" do @@ -661,31 +673,31 @@ expect(result.pg_search_highlight).to eq("Won't Let You Down") end - it 'preserves column selection when with_pg_search_highlight is chained after a select()' do + it "preserves column selection when with_pg_search_highlight is chained after a select()" do result = ModelWithPgSearch.search_content("Let").select(:content).with_pg_search_highlight.first - expect(result.as_json.keys).to contain_exactly('id', 'content', 'pg_search_highlight') + expect(result.as_json.keys).to contain_exactly("id", "content", "pg_search_highlight") end end context "with custom highlighting options" do before do - ModelWithPgSearch.create! content: "#{'text ' * 2}Let #{'text ' * 2}Let #{'text ' * 2}" + ModelWithPgSearch.create! content: "#{"text " * 2}Let #{"text " * 2}Let #{"text " * 2}" ModelWithPgSearch.pg_search_scope :search_content, - against: :content, - using: { - tsearch: { - highlight: { - StartSel: '', - StopSel: '', - FragmentDelimiter: '', - MaxFragments: 2, - MaxWords: 2, - MinWords: 1 - } - } - } + against: :content, + using: { + tsearch: { + highlight: { + StartSel: '', + StopSel: "", + FragmentDelimiter: '', + MaxFragments: 2, + MaxWords: 2, + MinWords: 1 + } + } + } end it "applies the options to the excerpts" do @@ -714,10 +726,10 @@ context "with a normalization specified" do before do ModelWithPgSearch.pg_search_scope :search_content_with_normalization, - against: :content, - using: { - tsearch: { normalization: 2 } - } + against: :content, + using: { + tsearch: {normalization: 2} + } end it "ranks the results for documents with less text higher" do @@ -731,8 +743,8 @@ context "with no normalization" do before do ModelWithPgSearch.pg_search_scope :search_content_without_normalization, - against: :content, - using: :tsearch + against: :content, + using: :tsearch end it "ranks the results equally" do @@ -747,14 +759,14 @@ context "when against columns ranked with arrays" do before do ModelWithPgSearch.pg_search_scope :search_weighted_by_array_of_arrays, - against: [[:content, 'B'], [:title, 'A']] + against: [[:content, "B"], [:title, "A"]] end it "returns results sorted by weighted rank" do - loser = ModelWithPgSearch.create!(title: 'bar', content: 'foo') - winner = ModelWithPgSearch.create!(title: 'foo', content: 'bar') + loser = ModelWithPgSearch.create!(title: "bar", content: "foo") + winner = ModelWithPgSearch.create!(title: "foo", content: "bar") - results = ModelWithPgSearch.search_weighted_by_array_of_arrays('foo').with_pg_search_rank + results = ModelWithPgSearch.search_weighted_by_array_of_arrays("foo").with_pg_search_rank expect(results[0].pg_search_rank).to be > results[1].pg_search_rank expect(results).to eq([winner, loser]) end @@ -763,14 +775,14 @@ context "when against columns ranked with a hash" do before do ModelWithPgSearch.pg_search_scope :search_weighted_by_hash, - against: { content: 'B', title: 'A' } + against: {content: "B", title: "A"} end it "returns results sorted by weighted rank" do - loser = ModelWithPgSearch.create!(title: 'bar', content: 'foo') - winner = ModelWithPgSearch.create!(title: 'foo', content: 'bar') + loser = ModelWithPgSearch.create!(title: "bar", content: "foo") + winner = ModelWithPgSearch.create!(title: "foo", content: "bar") - results = ModelWithPgSearch.search_weighted_by_hash('foo').with_pg_search_rank + results = ModelWithPgSearch.search_weighted_by_hash("foo").with_pg_search_rank expect(results[0].pg_search_rank).to be > results[1].pg_search_rank expect(results).to eq([winner, loser]) end @@ -779,14 +791,14 @@ context "when against columns of which only some are ranked" do before do ModelWithPgSearch.pg_search_scope :search_weighted, - against: [:content, [:title, 'A']] + against: [:content, [:title, "A"]] end it "returns results sorted by weighted rank using an implied low rank for unranked columns" do - loser = ModelWithPgSearch.create!(title: 'bar', content: 'foo') - winner = ModelWithPgSearch.create!(title: 'foo', content: 'bar') + loser = ModelWithPgSearch.create!(title: "bar", content: "foo") + winner = ModelWithPgSearch.create!(title: "foo", content: "bar") - results = ModelWithPgSearch.search_weighted('foo').with_pg_search_rank + results = ModelWithPgSearch.search_weighted("foo").with_pg_search_rank expect(results[0].pg_search_rank).to be > results[1].pg_search_rank expect(results).to eq([winner, loser]) end @@ -795,17 +807,17 @@ context "when searching any_word option" do before do ModelWithPgSearch.pg_search_scope :search_title_with_any_word, - against: :title, - using: { - tsearch: { any_word: true } - } + against: :title, + using: { + tsearch: {any_word: true} + } ModelWithPgSearch.pg_search_scope :search_title_with_all_words, - against: :title + against: :title end it "returns all results containing any word in their title" do - numbers = %w[one two three four].map { |number| ModelWithPgSearch.create!(title: number) } + %w[one two three four].map { |number| ModelWithPgSearch.create!(title: number) } results = ModelWithPgSearch.search_title_with_any_word("one two three four") @@ -820,10 +832,10 @@ context "with :negation" do before do ModelWithPgSearch.pg_search_scope :search_with_negation, - against: :title, - using: { - tsearch: { negation: true } - } + against: :title, + using: { + tsearch: {negation: true} + } end it "doesn't return results that contain terms prepended with '!'" do @@ -847,10 +859,10 @@ context "without :negation" do before do ModelWithPgSearch.pg_search_scope :search_without_negation, - against: :title, - using: { - tsearch: {} - } + against: :title, + using: { + tsearch: {} + } end it "return results that contain terms prepended with '!'" do @@ -873,38 +885,37 @@ context "when using dmetaphone" do before do ModelWithPgSearch.pg_search_scope :with_dmetaphones, - against: %i[title content], - using: :dmetaphone + against: %i[title content], + using: :dmetaphone end it "returns rows where one searchable column and the query share enough dmetaphones" do - included = ModelWithPgSearch.create!(title: 'Geoff', content: nil) - excluded = ModelWithPgSearch.create!(title: 'Bob', content: nil) - results = ModelWithPgSearch.with_dmetaphones('Jeff') + included = ModelWithPgSearch.create!(title: "Geoff", content: nil) + ModelWithPgSearch.create!(title: "Bob", content: nil) + results = ModelWithPgSearch.with_dmetaphones("Jeff") expect(results).to eq([included]) end it "returns rows where multiple searchable columns and the query share enough dmetaphones" do - included = ModelWithPgSearch.create!(title: 'Geoff', content: 'George') - excluded = ModelWithPgSearch.create!(title: 'Bob', content: 'Jones') - results = ModelWithPgSearch.with_dmetaphones('Jeff Jorge') + included = ModelWithPgSearch.create!(title: "Geoff", content: "George") + ModelWithPgSearch.create!(title: "Bob", content: "Jones") + results = ModelWithPgSearch.with_dmetaphones("Jeff Jorge") expect(results).to eq([included]) end it "returns rows that match dmetaphones that are English stopwords" do - included = ModelWithPgSearch.create!(title: 'White', content: nil) - excluded = ModelWithPgSearch.create!(title: 'Black', content: nil) - results = ModelWithPgSearch.with_dmetaphones('Wight') + included = ModelWithPgSearch.create!(title: "White", content: nil) + ModelWithPgSearch.create!(title: "Black", content: nil) + results = ModelWithPgSearch.with_dmetaphones("Wight") expect(results).to eq([included]) end it "can handle terms that do not have a dmetaphone equivalent" do - term_with_blank_metaphone = "w" - - included = ModelWithPgSearch.create!(title: 'White', content: nil) - excluded = ModelWithPgSearch.create!(title: 'Black', content: nil) + included = ModelWithPgSearch.create!(title: "White", content: nil) + ModelWithPgSearch.create!(title: "Black", content: nil) - results = ModelWithPgSearch.with_dmetaphones('Wight W') + # "W" does not have a dmetaphone equivalent + results = ModelWithPgSearch.with_dmetaphones("Wight W") expect(results).to eq([included]) end end @@ -912,35 +923,35 @@ context "when using multiple features" do before do ModelWithPgSearch.pg_search_scope :with_tsearch, - against: :title, - using: [ - [:tsearch, { dictionary: 'english' }] - ] + against: :title, + using: [ + [:tsearch, {dictionary: "english"}] + ] ModelWithPgSearch.pg_search_scope :with_trigram, - against: :title, - using: :trigram + against: :title, + using: :trigram ModelWithPgSearch.pg_search_scope :with_trigram_and_ignoring_accents, - against: :title, - ignoring: :accents, - using: :trigram + against: :title, + ignoring: :accents, + using: :trigram ModelWithPgSearch.pg_search_scope :with_tsearch_and_trigram, - against: :title, - using: [ - [:tsearch, { dictionary: 'english' }], - :trigram - ] + against: :title, + using: [ + [:tsearch, {dictionary: "english"}], + :trigram + ] ModelWithPgSearch.pg_search_scope :complex_search, - against: %i[content title], - ignoring: :accents, - using: { - tsearch: { dictionary: 'english' }, - dmetaphone: {}, - trigram: {} - } + against: %i[content title], + ignoring: :accents, + using: { + tsearch: {dictionary: "english"}, + dmetaphone: {}, + trigram: {} + } end it "returns rows that match using any of the features" do @@ -982,13 +993,13 @@ end context "with feature-specific configuration" do - let(:tsearch_config) { { dictionary: 'english' } } - let(:trigram_config) { { foo: 'bar' } } + let(:tsearch_config) { {dictionary: "english"} } + let(:trigram_config) { {foo: "bar"} } before do ModelWithPgSearch.pg_search_scope :with_tsearch_and_trigram_using_hash, - against: :title, - using: { tsearch: tsearch_config, trigram: trigram_config } + against: :title, + using: {tsearch: tsearch_config, trigram: trigram_config} end it "passes the custom configuration down to the specified feature" do @@ -1029,8 +1040,8 @@ with_model :Post do table do |t| - t.text 'content' - t.tsvector 'content_tsvector' + t.text "content" + t.tsvector "content_tsvector" end model do @@ -1039,8 +1050,8 @@ end end - let!(:expected) { Post.create!(content: 'phooey') } - let!(:unexpected) { Post.create!(content: 'longcat is looooooooong') } + let!(:expected) { Post.create!(content: "phooey") } + let!(:unexpected) { Post.create!(content: "longcat is looooooooong") } before do ActiveRecord::Base.connection.execute <<~SQL.squish @@ -1048,17 +1059,17 @@ SET content_tsvector = to_tsvector('english'::regconfig, #{Post.quoted_table_name}."content") SQL - expected.comments.create(body: 'commentone') - unexpected.comments.create(body: 'commentwo') + expected.comments.create(body: "commentone") + unexpected.comments.create(body: "commentwo") Post.pg_search_scope :search_by_content_with_tsvector, - associated_against: { comments: [:body] }, - using: { - tsearch: { - tsvector_column: 'content_tsvector', - dictionary: 'english' - } - } + associated_against: {comments: [:body]}, + using: { + tsearch: { + tsvector_column: "content_tsvector", + dictionary: "english" + } + } end it "finds by the tsvector column" do @@ -1069,28 +1080,28 @@ expect(Post.search_by_content_with_tsvector("commentone").map(&:id)).to eq([expected.id]) end - it 'finds by a combination of the two' do + it "finds by a combination of the two" do expect(Post.search_by_content_with_tsvector("phooey commentone").map(&:id)).to eq([expected.id]) end end - context 'when using multiple tsvector columns' do + context "when using multiple tsvector columns" do with_model :ModelWithTsvector do model do include PgSearch::Model pg_search_scope :search_by_multiple_tsvector_columns, - against: ['content', 'message'], - using: { - tsearch: { - tsvector_column: ['content_tsvector', 'message_tsvector'], - dictionary: 'english' - } - } + against: ["content", "message"], + using: { + tsearch: { + tsvector_column: ["content_tsvector", "message_tsvector"], + dictionary: "english" + } + } end end - it 'concats tsvector columns' do + it "concats tsvector columns" do expected = "#{ModelWithTsvector.quoted_table_name}.\"content_tsvector\" || " \ "#{ModelWithTsvector.quoted_table_name}.\"message_tsvector\"" @@ -1101,17 +1112,17 @@ context "when using a tsvector column with" do with_model :ModelWithTsvector do table do |t| - t.text 'content' - t.tsvector 'content_tsvector' + t.text "content" + t.tsvector "content_tsvector" end model { include PgSearch::Model } end - let!(:expected) { ModelWithTsvector.create!(content: 'tiling is grouty') } + let!(:expected) { ModelWithTsvector.create!(content: "tiling is grouty") } before do - ModelWithTsvector.create!(content: 'longcat is looooooooong') + ModelWithTsvector.create!(content: "longcat is looooooooong") ActiveRecord::Base.connection.execute <<~SQL.squish UPDATE #{ModelWithTsvector.quoted_table_name} @@ -1119,13 +1130,13 @@ SQL ModelWithTsvector.pg_search_scope :search_by_content_with_tsvector, - against: :content, - using: { - tsearch: { - tsvector_column: 'content_tsvector', - dictionary: 'english' - } - } + against: :content, + using: { + tsearch: { + tsvector_column: "content_tsvector", + dictionary: "english" + } + } end it "does not use to_tsvector in the query" do @@ -1159,8 +1170,8 @@ context "when ignoring accents" do before do ModelWithPgSearch.pg_search_scope :search_title_without_accents, - against: :title, - ignoring: :accents + against: :title, + ignoring: :accents end it "returns rows that match the query but not its accents" do @@ -1179,7 +1190,7 @@ let(:results) { ModelWithPgSearch.search_title_without_accents(term) } before do - ModelWithPgSearch.create!(title: 'FooBar') + ModelWithPgSearch.create!(title: "FooBar") end it "does not create an erroneous tsquery expression" do @@ -1191,25 +1202,25 @@ context "when passed a :ranked_by expression" do before do ModelWithPgSearch.pg_search_scope :search_content_with_default_rank, - against: :content + against: :content ModelWithPgSearch.pg_search_scope :search_content_with_importance_as_rank, - against: :content, - ranked_by: "importance" + against: :content, + ranked_by: "importance" ModelWithPgSearch.pg_search_scope :search_content_with_importance_as_rank_multiplier, - against: :content, - ranked_by: ":tsearch * importance" + against: :content, + ranked_by: ":tsearch * importance" end it "returns records with a rank attribute equal to the :ranked_by expression" do - ModelWithPgSearch.create!(content: 'foo', importance: 10) + ModelWithPgSearch.create!(content: "foo", importance: 10) results = ModelWithPgSearch.search_content_with_importance_as_rank("foo").with_pg_search_rank expect(results.first.pg_search_rank).to eq(10) end it "substitutes :tsearch with the tsearch rank expression in the :ranked_by expression" do - ModelWithPgSearch.create!(content: 'foo', importance: 10) + ModelWithPgSearch.create!(content: "foo", importance: 10) tsearch_result = ModelWithPgSearch.search_content_with_default_rank("foo").with_pg_search_rank.first @@ -1218,8 +1229,8 @@ multiplied_result = ModelWithPgSearch.search_content_with_importance_as_rank_multiplier("foo") - .with_pg_search_rank - .first + .with_pg_search_rank + .first multiplied_rank = multiplied_result.pg_search_rank @@ -1228,9 +1239,9 @@ it "returns results in descending order of the value of the rank expression" do records = [ - ModelWithPgSearch.create!(content: 'foo', importance: 1), - ModelWithPgSearch.create!(content: 'foo', importance: 3), - ModelWithPgSearch.create!(content: 'foo', importance: 2) + ModelWithPgSearch.create!(content: "foo", importance: 1), + ModelWithPgSearch.create!(content: "foo", importance: 3), + ModelWithPgSearch.create!(content: "foo", importance: 2) ] results = ModelWithPgSearch.search_content_with_importance_as_rank("foo") @@ -1243,32 +1254,32 @@ before do ModelWithPgSearch.pg_search_scope scope_name, - against: :content, - ranked_by: ":#{feature}" + against: :content, + ranked_by: ":#{feature}" - ModelWithPgSearch.create!(content: 'foo') + ModelWithPgSearch.create!(content: "foo") end context "when .with_pg_search_rank is chained after" do specify "its results respond to #pg_search_rank" do - result = ModelWithPgSearch.send(scope_name, 'foo').with_pg_search_rank.first + result = ModelWithPgSearch.send(scope_name, "foo").with_pg_search_rank.first expect(result).to respond_to(:pg_search_rank) end it "returns the rank when #pg_search_rank is called on a result" do - results = ModelWithPgSearch.send(scope_name, 'foo').with_pg_search_rank + results = ModelWithPgSearch.send(scope_name, "foo").with_pg_search_rank expect(results.first.pg_search_rank).to be_a Float end end context "when .with_pg_search_rank is not chained after" do specify "its results do not respond to #pg_search_rank" do - result = ModelWithPgSearch.send(scope_name, 'foo').first + result = ModelWithPgSearch.send(scope_name, "foo").first expect(result).not_to respond_to(:pg_search_rank) end it "raises PgSearch::PgSearchRankNotSelected when #pg_search_rank is called on a result" do - result = ModelWithPgSearch.send(scope_name, 'foo').first + result = ModelWithPgSearch.send(scope_name, "foo").first expect { result.pg_search_rank }.to raise_exception(PgSearch::PgSearchRankNotSelected) @@ -1280,14 +1291,14 @@ context "when using the tsearch ranking algorithm" do it "sorts results by the tsearch rank" do ModelWithPgSearch.pg_search_scope :search_content_ranked_by_tsearch, - using: :tsearch, - against: :content, - ranked_by: ":tsearch" + using: :tsearch, + against: :content, + ranked_by: ":tsearch" - once = ModelWithPgSearch.create!(content: 'foo bar') - twice = ModelWithPgSearch.create!(content: 'foo foo') + once = ModelWithPgSearch.create!(content: "foo bar") + twice = ModelWithPgSearch.create!(content: "foo foo") - results = ModelWithPgSearch.search_content_ranked_by_tsearch('foo') + results = ModelWithPgSearch.search_content_ranked_by_tsearch("foo") expect(results.find_index(twice)).to be < results.find_index(once) end end @@ -1295,14 +1306,14 @@ context "when using the trigram ranking algorithm" do it "sorts results by the trigram rank" do ModelWithPgSearch.pg_search_scope :search_content_ranked_by_trigram, - using: :trigram, - against: :content, - ranked_by: ":trigram" + using: :trigram, + against: :content, + ranked_by: ":trigram" - close = ModelWithPgSearch.create!(content: 'abcdef') - exact = ModelWithPgSearch.create!(content: 'abc') + close = ModelWithPgSearch.create!(content: "abcdef") + exact = ModelWithPgSearch.create!(content: "abc") - results = ModelWithPgSearch.search_content_ranked_by_trigram('abc') + results = ModelWithPgSearch.search_content_ranked_by_trigram("abc") expect(results.find_index(exact)).to be < results.find_index(close) end end @@ -1310,14 +1321,14 @@ context "when using the dmetaphone ranking algorithm" do it "sorts results by the dmetaphone rank" do ModelWithPgSearch.pg_search_scope :search_content_ranked_by_dmetaphone, - using: :dmetaphone, - against: :content, - ranked_by: ":dmetaphone" + using: :dmetaphone, + against: :content, + ranked_by: ":dmetaphone" - once = ModelWithPgSearch.create!(content: 'Phoo Bar') - twice = ModelWithPgSearch.create!(content: 'Phoo Fu') + once = ModelWithPgSearch.create!(content: "Phoo Bar") + twice = ModelWithPgSearch.create!(content: "Phoo Fu") - results = ModelWithPgSearch.search_content_ranked_by_dmetaphone('foo') + results = ModelWithPgSearch.search_content_ranked_by_dmetaphone("foo") expect(results.find_index(twice)).to be < results.find_index(once) end end @@ -1326,17 +1337,17 @@ context "when there is a sort only feature" do it "excludes that feature from the conditions, but uses it in the sorting" do ModelWithPgSearch.pg_search_scope :search_content_ranked_by_dmetaphone, - against: :content, - using: { - tsearch: { any_word: true, prefix: true }, - dmetaphone: { any_word: true, prefix: true, sort_only: true } - }, - ranked_by: ":tsearch + (0.5 * :dmetaphone)" + against: :content, + using: { + tsearch: {any_word: true, prefix: true}, + dmetaphone: {any_word: true, prefix: true, sort_only: true} + }, + ranked_by: ":tsearch + (0.5 * :dmetaphone)" exact = ModelWithPgSearch.create!(content: "ash hines") one_exact_one_close = ModelWithPgSearch.create!(content: "ash heinz") one_exact = ModelWithPgSearch.create!(content: "ash smith") - one_close = ModelWithPgSearch.create!(content: "leigh heinz") + ModelWithPgSearch.create!(content: "leigh heinz") results = ModelWithPgSearch.search_content_ranked_by_dmetaphone("ash hines") expect(results).to eq [exact, one_exact_one_close, one_exact] diff --git a/spec/integration/single_table_inheritance_spec.rb b/spec/integration/single_table_inheritance_spec.rb index 8a068152..e4cb8668 100644 --- a/spec/integration/single_table_inheritance_spec.rb +++ b/spec/integration/single_table_inheritance_spec.rb @@ -6,8 +6,8 @@ context "with the standard type column" do with_model :SuperclassModel do table do |t| - t.text 'content' - t.string 'type' + t.text "content" + t.string "type" end model do @@ -46,13 +46,13 @@ context "with a custom type column" do with_model :SuperclassModel do table do |t| - t.text 'content' - t.string 'custom_type' + t.text "content" + t.string "custom_type" end model do include PgSearch::Model - self.inheritance_column = 'custom_type' + self.inheritance_column = "custom_type" pg_search_scope :search_content, against: :content end end diff --git a/spec/lib/pg_search/configuration/association_spec.rb b/spec/lib/pg_search/configuration/association_spec.rb index 90a08f50..7750a885 100644 --- a/spec/lib/pg_search/configuration/association_spec.rb +++ b/spec/lib/pg_search/configuration/association_spec.rb @@ -22,8 +22,8 @@ has_one :avatar, class_name: "Avatar" belongs_to :site - pg_search_scope :with_avatar, associated_against: { avatar: :url } - pg_search_scope :with_site, associated_against: { site: :title } + pg_search_scope :with_avatar, associated_against: {avatar: :url} + pg_search_scope :with_site, associated_against: {site: :title} end end @@ -36,7 +36,7 @@ include PgSearch::Model has_many :users, class_name: "User" - pg_search_scope :with_users, associated_against: { users: :name } + pg_search_scope :with_users, associated_against: {users: :name} end end diff --git a/spec/lib/pg_search/configuration/foreign_column_spec.rb b/spec/lib/pg_search/configuration/foreign_column_spec.rb index 105b8eba..3537a203 100644 --- a/spec/lib/pg_search/configuration/foreign_column_spec.rb +++ b/spec/lib/pg_search/configuration/foreign_column_spec.rb @@ -18,16 +18,16 @@ model do include PgSearch::Model - belongs_to :another_model, class_name: 'AssociatedModel' + belongs_to :another_model, class_name: "AssociatedModel" - pg_search_scope :with_another, associated_against: { another_model: :title } + pg_search_scope :with_another, associated_against: {another_model: :title} end end it "returns a consistent string" do association = PgSearch::Configuration::Association.new(Model, - :another_model, - :title) + :another_model, + :title) foreign_column = described_class.new("title", nil, Model, association) column_alias = foreign_column.alias diff --git a/spec/lib/pg_search/features/trigram_spec.rb b/spec/lib/pg_search/features/trigram_spec.rb index 995c5dae..94a5c95a 100644 --- a/spec/lib/pg_search/features/trigram_spec.rb +++ b/spec/lib/pg_search/features/trigram_spec.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true -require 'spec_helper' -require 'ostruct' +require "spec_helper" +require "ostruct" # rubocop:disable RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups describe PgSearch::Features::Trigram do subject(:feature) { described_class.new(query, options, columns, Model, normalizer) } - let(:query) { 'lolwut' } + let(:query) { "lolwut" } let(:options) { {} } let(:columns) { [ @@ -16,7 +16,7 @@ ] } let(:normalizer) { PgSearch::Normalizer.new(config) } - let(:config) { OpenStruct.new(ignore: []) } # rubocop:disable Style/OpenStructUse + let(:config) { OpenStruct.new(ignore: []) } let(:coalesced_columns) do <<~SQL.squish @@ -33,15 +33,15 @@ end end - describe 'conditions' do - it 'escapes the search document and query' do + describe "conditions" do + it "escapes the search document and query" do config.ignore = [] expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_columns}))") end - context 'when searching by word_similarity' do + context "when searching by word_similarity" do let(:options) do - { word_similarity: true } + {word_similarity: true} end it 'uses the "<%" operator when searching by word_similarity' do @@ -50,17 +50,17 @@ end end - context 'when ignoring accents' do - it 'escapes the search document and query, but not the accent function' do + context "when ignoring accents" do + it "escapes the search document and query, but not the accent function" do config.ignore = [:accents] expect(feature.conditions.to_sql).to eq("(unaccent('#{query}') % (unaccent(#{coalesced_columns})))") end end - context 'when a threshold is specified' do - context 'when searching by similarity' do + context "when a threshold is specified" do + context "when searching by similarity" do let(:options) do - { threshold: 0.5 } + {threshold: 0.5} end it 'uses a minimum similarity expression instead of the "%" operator' do @@ -70,9 +70,9 @@ end end - context 'when searching by word_similarity' do + context "when searching by word_similarity" do let(:options) do - { threshold: 0.5, word_similarity: true } + {threshold: 0.5, word_similarity: true} end it 'uses a minimum similarity expression instead of the "<%" operator' do @@ -83,28 +83,28 @@ end end - context 'when only certain columns are selected' do - context 'with one column' do - let(:options) { { only: :name } } + context "when only certain columns are selected" do + context "with one column" do + let(:options) { {only: :name} } - it 'only searches against the select column' do + it "only searches against the select column" do coalesced_column = "coalesce(#{Model.quoted_table_name}.\"name\"::text, '')" expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_column}))") end end - context 'with multiple columns' do - let(:options) { { only: %i[name content] } } + context "with multiple columns" do + let(:options) { {only: %i[name content]} } - it 'concatenates when multiples columns are selected' do + it "concatenates when multiples columns are selected" do expect(feature.conditions.to_sql).to eq("('#{query}' % (#{coalesced_columns}))") end end end end - describe '#rank' do - it 'returns an expression using the similarity() function' do + describe "#rank" do + it "returns an expression using the similarity() function" do expect(feature.rank.to_sql).to eq("(similarity('#{query}', (#{coalesced_columns})))") end end diff --git a/spec/lib/pg_search/features/tsearch_spec.rb b/spec/lib/pg_search/features/tsearch_spec.rb index 929a4134..c51a55f1 100644 --- a/spec/lib/pg_search/features/tsearch_spec.rb +++ b/spec/lib/pg_search/features/tsearch_spec.rb @@ -35,7 +35,7 @@ PgSearch::Configuration::Column.new(:name, nil, Model), PgSearch::Configuration::Column.new(:content, nil, Model) ] - options = { tsvector_column: :my_tsvector, normalization: 2 } + options = {tsvector_column: :my_tsvector, normalization: 2} config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) @@ -78,7 +78,7 @@ PgSearch::Configuration::Column.new(:name, nil, Model), PgSearch::Configuration::Column.new(:content, nil, Model) ] - options = { negation: true } + options = {negation: true} config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) @@ -96,7 +96,7 @@ PgSearch::Configuration::Column.new(:name, nil, Model), PgSearch::Configuration::Column.new(:content, nil, Model) ] - options = { negation: false } + options = {negation: false} config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) @@ -108,13 +108,13 @@ end context "when options[:tsvector_column] is a string" do - it 'uses the tsvector column' do + it "uses the tsvector column" do query = "query" columns = [ PgSearch::Configuration::Column.new(:name, nil, Model), PgSearch::Configuration::Column.new(:content, nil, Model) ] - options = { tsvector_column: "my_tsvector" } + options = {tsvector_column: "my_tsvector"} config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) @@ -126,13 +126,13 @@ end context "when options[:tsvector_column] is an array of strings" do - it 'uses the tsvector column' do + it "uses the tsvector column" do query = "query" columns = [ PgSearch::Configuration::Column.new(:name, nil, Model), PgSearch::Configuration::Column.new(:content, nil, Model) ] - options = { tsvector_column: ["tsvector1", "tsvector2"] } + options = {tsvector_column: ["tsvector1", "tsvector2"]} config = instance_double(PgSearch::Configuration, :config, ignore: []) normalizer = PgSearch::Normalizer.new(config) @@ -170,7 +170,7 @@ context "when options[:dictionary] is passed" do # rubocop:disable RSpec/ExampleLength - it 'uses the provided dictionary' do + it "uses the provided dictionary" do query = "query" columns = [ PgSearch::Configuration::Column.new(:name, nil, Model), @@ -206,13 +206,13 @@ options = { highlight: { StartSel: '', - StopSel: '', + StopSel: "", MaxWords: 123, MinWords: 456, ShortWord: 4, HighlightAll: true, MaxFragments: 3, - FragmentDelimiter: '…' + FragmentDelimiter: "…" } } @@ -236,13 +236,13 @@ options = { highlight: { start_sel: '', - stop_sel: '', + stop_sel: "", max_words: 123, min_words: 456, short_word: 4, highlight_all: false, max_fragments: 3, - fragment_delimiter: '…' + fragment_delimiter: "…" } } diff --git a/spec/lib/pg_search/multisearch/rebuilder_spec.rb b/spec/lib/pg_search/multisearch/rebuilder_spec.rb index 5a462644..58a48c02 100644 --- a/spec/lib/pg_search/multisearch/rebuilder_spec.rb +++ b/spec/lib/pg_search/multisearch/rebuilder_spec.rb @@ -6,10 +6,10 @@ describe PgSearch::Multisearch::Rebuilder do with_table "pg_search_documents", &DOCUMENTS_SCHEMA - describe 'when initialized with a model that is not multisearchable' do + describe "when initialized with a model that is not multisearchable" do with_model :not_multisearchable - it 'raises an exception' do + it "raises an exception" do expect { described_class.new(NotMultisearchable) }.to raise_exception( @@ -253,7 +253,7 @@ def foo model do include PgSearch::Model multisearchable against: :name, - additional_attributes: ->(obj) { { additional_attribute_column: "#{obj.class}::#{obj.id}" } } + additional_attributes: ->(obj) { {additional_attribute_column: "#{obj.class}::#{obj.id}"} } end end diff --git a/spec/lib/pg_search/multisearchable_spec.rb b/spec/lib/pg_search/multisearchable_spec.rb index c0814e58..c155741b 100644 --- a/spec/lib/pg_search/multisearchable_spec.rb +++ b/spec/lib/pg_search/multisearchable_spec.rb @@ -149,7 +149,7 @@ end context "when searching against a single column" do - let(:multisearchable_options) { { against: :some_content } } + let(:multisearchable_options) { {against: :some_content} } let(:text) { "foo bar" } before do @@ -159,7 +159,7 @@ record.save end - describe '#content' do + describe "#content" do subject { super().pg_search_document.content } it { is_expected.to eq(text) } @@ -167,17 +167,17 @@ end context "when searching against multiple columns" do - let(:multisearchable_options) { { against: %i[attr_1 attr_2] } } + let(:multisearchable_options) { {against: %i[attr_1 attr_2]} } before do without_partial_double_verification do - allow(record).to receive(:attr_1).and_return('1') - allow(record).to receive(:attr_2).and_return('2') + allow(record).to receive(:attr_1).and_return("1") + allow(record).to receive(:attr_2).and_return("2") end record.save end - describe '#content' do + describe "#content" do subject { super().pg_search_document.content } it { is_expected.to eq("1 2") } @@ -195,7 +195,7 @@ end context "when searching against a single column" do - let(:multisearchable_options) { { against: :some_content } } + let(:multisearchable_options) { {against: :some_content} } let(:text) { "foo bar" } before do @@ -205,7 +205,7 @@ record.save end - describe '#content' do + describe "#content" do subject { super().pg_search_document.content } it { is_expected.to eq(text) } @@ -213,17 +213,17 @@ end context "when searching against multiple columns" do - let(:multisearchable_options) { { against: %i[attr_1 attr_2] } } + let(:multisearchable_options) { {against: %i[attr_1 attr_2]} } before do without_partial_double_verification do - allow(record).to receive(:attr_1).and_return('1') - allow(record).to receive(:attr_2).and_return('2') + allow(record).to receive(:attr_1).and_return("1") + allow(record).to receive(:attr_2).and_return("2") end record.save end - describe '#content' do + describe "#content" do subject { super().pg_search_document.content } it { is_expected.to eq("1 2") } @@ -234,7 +234,7 @@ let(:multisearchable_options) do { additional_attributes: lambda do |record| - { foo: record.bar } + {foo: record.bar} end } end @@ -247,7 +247,7 @@ record.save expect(record) .to have_received(:create_pg_search_document) - .with(content: '', foo: text) + .with(content: "", foo: text) end end end @@ -269,7 +269,7 @@ record.save expect(record) .to have_received(:create_pg_search_document) - .with(content: '') + .with(content: "") end end diff --git a/spec/lib/pg_search_spec.rb b/spec/lib/pg_search_spec.rb index 80cca230..6a178f2d 100644 --- a/spec/lib/pg_search_spec.rb +++ b/spec/lib/pg_search_spec.rb @@ -49,7 +49,7 @@ def clear_searchable_cache end end - let!(:soundalike_record) { MultisearchableModel.create!(title: 'foning') } + let!(:soundalike_record) { MultisearchableModel.create!(title: "foning") } let(:query) { "Phoning" } it { is_expected.to include(soundalike_record) } @@ -65,9 +65,9 @@ def clear_searchable_cache allow(described_class).to receive(:multisearch_options) do lambda do |query, soundalike| if soundalike - { using: :dmetaphone, query: query } + {using: :dmetaphone, query: query} else - { query: query } + {query: query} end end end @@ -83,7 +83,7 @@ def clear_searchable_cache end end - let!(:soundalike_record) { MultisearchableModel.create!(title: 'foning') } + let!(:soundalike_record) { MultisearchableModel.create!(title: "foning") } let(:query) { "Phoning" } context "with soundalike true" do @@ -103,8 +103,8 @@ def clear_searchable_cache context "with standard type column" do with_model :SuperclassModel do table do |t| - t.text 'content' - t.string 'type' + t.text "content" + t.string "type" end end @@ -191,12 +191,12 @@ def clear_searchable_cache context "with custom type column" do with_model :SuperclassModel do table do |t| - t.text 'content' - t.string 'inherit' + t.text "content" + t.string "inherit" end model do - self.inheritance_column = 'inherit' + self.inheritance_column = "inherit" end end @@ -254,7 +254,7 @@ def clear_searchable_cache multisearch_enabled_inside = described_class.multisearch_enabled? raise end - rescue StandardError + rescue end multisearch_enabled_after = described_class.multisearch_enabled? diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e25191aa..0498959b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,19 +1,19 @@ # frozen_string_literal: true -require 'warning' +require "warning" # Ignore Ruby 2.7 warnings from Active Record Warning.ignore :keyword_separation # https://github.com/grodowski/undercover#setting-up-required-lcov-reporting -require 'simplecov' -require 'simplecov-lcov' +require "simplecov" +require "simplecov-lcov" SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter SimpleCov.start do add_filter(%r{^/spec/}) enable_coverage(:branch) end -require 'undercover' +require "undercover" require "bundler/setup" require "pg_search" @@ -29,11 +29,11 @@ mocks.verify_partial_doubles = true end - config.example_status_persistence_file_path = 'tmp/examples.txt' + config.example_status_persistence_file_path = "tmp/examples.txt" end -require 'support/database' -require 'support/with_model' +require "support/database" +require "support/with_model" DOCUMENTS_SCHEMA = lambda do |t| t.belongs_to :searchable, polymorphic: true, index: true diff --git a/spec/support/database.rb b/spec/support/database.rb index ea31b63f..980acb39 100644 --- a/spec/support/database.rb +++ b/spec/support/database.rb @@ -10,10 +10,10 @@ end begin - connection_options = { adapter: 'postgresql', database: 'pg_search_test', min_messages: 'warning' } + connection_options = {adapter: "postgresql", database: "pg_search_test", min_messages: "warning"} if ENV["CI"] - connection_options[:username] = 'postgres' - connection_options[:password] = 'postgres' + connection_options[:username] = "postgres" + connection_options[:password] = "postgres" end ActiveRecord::Base.establish_connection(connection_options) connection = ActiveRecord::Base.connection @@ -40,7 +40,7 @@ def install_extension(name) return unless extension.none? connection.execute "CREATE EXTENSION #{name};" -rescue StandardError => e +rescue => e at_exit do puts "-" * 80 puts "Please install the #{name} extension" @@ -52,7 +52,7 @@ def install_extension(name) def install_extension_if_missing(name, query, expected_result) result = ActiveRecord::Base.connection.select_value(query) raise "Unexpected output for #{query}: #{result.inspect}" unless result.casecmp(expected_result).zero? -rescue StandardError +rescue install_extension(name) end @@ -62,7 +62,7 @@ def install_extension_if_missing(name, query, expected_result) def load_sql(filename) connection = ActiveRecord::Base.connection - file_contents = File.read(File.join(File.dirname(__FILE__), '..', '..', 'sql', filename)) + file_contents = File.read(File.join(File.dirname(__FILE__), "..", "..", "sql", filename)) connection.execute(file_contents) end From 17c0b3484fa559ceb9632c634a07307860e0c3e4 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Tue, 7 Mar 2023 21:19:21 -0600 Subject: [PATCH 39/55] Rubocop: RSpec/MatchArray Authored-by: Grant Hutchins --- spec/integration/pg_search_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/integration/pg_search_spec.rb b/spec/integration/pg_search_spec.rb index f5fd4728..056b267c 100644 --- a/spec/integration/pg_search_spec.rb +++ b/spec/integration/pg_search_spec.rb @@ -547,7 +547,7 @@ in_content = ModelWithPgSearch.create!(title: "bar", content: "foo") results = ModelWithPgSearch.search_title_and_content("foo") - expect(results).to match_array([in_title, in_content]) + expect(results).to contain_exactly(in_title, in_content) end # Searching with a NULL column will prevent any matches unless we coalesce it. From 154e45eebb401f9b4c356540782466c854ea474f Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Tue, 13 Jun 2023 11:09:58 -0500 Subject: [PATCH 40/55] Run tests against Ruby 3.3 preview Authored-by: Grant Hutchins --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d953736a..226586b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,9 @@ jobs: - ruby-version: '3.2' active-record-version-env: ACTIVE_RECORD_BRANCH="6-1-stable" allow-failure: true + - ruby-version: '3.3.0-preview1' + active-record-version-env: ACTIVE_RECORD_VERSION="~> 7.0.0" + allow-failure: true continue-on-error: ${{ matrix.allow-failure }} steps: - uses: actions/checkout@v3 From 953c960539de3ae681e506341377a2865fdd83b5 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Wed, 20 Dec 2023 10:57:10 -0600 Subject: [PATCH 41/55] Run tests against Ruby 3.3 release candidate --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 226586b9..5b4516e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,7 @@ jobs: - ruby-version: '3.2' active-record-version-env: ACTIVE_RECORD_BRANCH="6-1-stable" allow-failure: true - - ruby-version: '3.3.0-preview1' + - ruby-version: '3.3.0-rc1' active-record-version-env: ACTIVE_RECORD_VERSION="~> 7.0.0" allow-failure: true continue-on-error: ${{ matrix.allow-failure }} From e469ccddea207556bbc8917433174ea00d3db8b9 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Mon, 25 Dec 2023 12:43:09 -0600 Subject: [PATCH 42/55] Run tests against Ruby 3.3 --- .github/workflows/ci.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b4516e2..b7ef9f87 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,25 +36,22 @@ jobs: strategy: fail-fast: false matrix: - ruby-version: ['2.7', '3.0', '3.1', '3.2'] + ruby-version: ['2.7', '3.0', '3.1', '3.2', '3.3'] active-record-version-env: - ACTIVE_RECORD_VERSION="~> 6.0.0" - ACTIVE_RECORD_VERSION="~> 6.1.0" - ACTIVE_RECORD_VERSION="~> 7.0.0" allow-failure: [false] include: - - ruby-version: '3.2' + - ruby-version: '3.3' active-record-version-env: ACTIVE_RECORD_BRANCH="main" allow-failure: true - - ruby-version: '3.2' + - ruby-version: '3.3' active-record-version-env: ACTIVE_RECORD_BRANCH="7-0-stable" allow-failure: true - - ruby-version: '3.2' + - ruby-version: '3.3' active-record-version-env: ACTIVE_RECORD_BRANCH="6-1-stable" allow-failure: true - - ruby-version: '3.3.0-rc1' - active-record-version-env: ACTIVE_RECORD_VERSION="~> 7.0.0" - allow-failure: true continue-on-error: ${{ matrix.allow-failure }} steps: - uses: actions/checkout@v3 From bc03fff75f5c403dfd3c9840cca60969ba77df14 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Mon, 25 Dec 2023 13:23:36 -0600 Subject: [PATCH 43/55] Drop support for Ruby 2.7 Ruby 2.7 has been EOL since 2023-03-31 --- .github/workflows/ci.yml | 2 +- .rubocop.yml | 2 +- README.md | 2 +- pg_search.gemspec | 2 +- spec/spec_helper.rb | 2 -- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7ef9f87..2694bba2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: false matrix: - ruby-version: ['2.7', '3.0', '3.1', '3.2', '3.3'] + ruby-version: ['3.0', '3.1', '3.2', '3.3'] active-record-version-env: - ACTIVE_RECORD_VERSION="~> 6.0.0" - ACTIVE_RECORD_VERSION="~> 6.1.0" diff --git a/.rubocop.yml b/.rubocop.yml index 7d946471..d003e006 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,7 +9,7 @@ inherit_gem: standard: config/base.yml AllCops: - TargetRubyVersion: 2.7 + TargetRubyVersion: 3.0 NewCops: disable Exclude: - bin/**/* diff --git a/README.md b/README.md index b452b37e..42aa3c91 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Read the blog post introducing PgSearch at https://tanzu.vmware.com/content/blog ## REQUIREMENTS -* Ruby 2.7+ +* Ruby 3.0+ * Active Record 6.0+ * PostgreSQL 9.2+ * [PostgreSQL extensions](https://github.com/Casecommons/pg_search/wiki/Installing-PostgreSQL-Extensions) for certain features diff --git a/pg_search.gemspec b/pg_search.gemspec index 103a9a38..5b64f0c1 100644 --- a/pg_search.gemspec +++ b/pg_search.gemspec @@ -21,5 +21,5 @@ Gem::Specification.new do |s| s.add_dependency "activerecord", ">= 6.0" s.add_dependency "activesupport", ">= 6.0" - s.required_ruby_version = ">= 2.7" + s.required_ruby_version = ">= 3.0" end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0498959b..41490aea 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true require "warning" -# Ignore Ruby 2.7 warnings from Active Record -Warning.ignore :keyword_separation # https://github.com/grodowski/undercover#setting-up-required-lcov-reporting require "simplecov" From 6c3e736940589fedd60b1d06e7cf19ace81f3a32 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Mon, 25 Dec 2023 13:28:30 -0600 Subject: [PATCH 44/55] Test against Active Record 7.1 --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2694bba2..ae51dc2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,11 +41,15 @@ jobs: - ACTIVE_RECORD_VERSION="~> 6.0.0" - ACTIVE_RECORD_VERSION="~> 6.1.0" - ACTIVE_RECORD_VERSION="~> 7.0.0" + - ACTIVE_RECORD_VERSION="~> 7.1.0" allow-failure: [false] include: - ruby-version: '3.3' active-record-version-env: ACTIVE_RECORD_BRANCH="main" allow-failure: true + - ruby-version: '3.3' + active-record-version-env: ACTIVE_RECORD_BRANCH="7-1-stable" + allow-failure: true - ruby-version: '3.3' active-record-version-env: ACTIVE_RECORD_BRANCH="7-0-stable" allow-failure: true From 9514462814c59103cf5ea493ce2d1c23f35f568d Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Mon, 25 Dec 2023 13:31:10 -0600 Subject: [PATCH 45/55] Drop support for Active Record 6.0 Active Record 6.0 has been EOL since 2023-06-01 --- .github/workflows/ci.yml | 1 - README.md | 2 +- pg_search.gemspec | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae51dc2c..2f5b56f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,6 @@ jobs: matrix: ruby-version: ['3.0', '3.1', '3.2', '3.3'] active-record-version-env: - - ACTIVE_RECORD_VERSION="~> 6.0.0" - ACTIVE_RECORD_VERSION="~> 6.1.0" - ACTIVE_RECORD_VERSION="~> 7.0.0" - ACTIVE_RECORD_VERSION="~> 7.1.0" diff --git a/README.md b/README.md index 42aa3c91..4ccf1562 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Read the blog post introducing PgSearch at https://tanzu.vmware.com/content/blog ## REQUIREMENTS * Ruby 3.0+ -* Active Record 6.0+ +* Active Record 6.1+ * PostgreSQL 9.2+ * [PostgreSQL extensions](https://github.com/Casecommons/pg_search/wiki/Installing-PostgreSQL-Extensions) for certain features diff --git a/pg_search.gemspec b/pg_search.gemspec index 5b64f0c1..4a5fa0eb 100644 --- a/pg_search.gemspec +++ b/pg_search.gemspec @@ -18,8 +18,8 @@ Gem::Specification.new do |s| s.files = `git ls-files -z`.split("\x0") s.require_paths = ["lib"] - s.add_dependency "activerecord", ">= 6.0" - s.add_dependency "activesupport", ">= 6.0" + s.add_dependency "activerecord", ">= 6.1" + s.add_dependency "activesupport", ">= 6.1" s.required_ruby_version = ">= 3.0" end From 7e294dc5ac4db78d44936d16a961687d22eb51ae Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Mon, 25 Dec 2023 14:27:17 -0600 Subject: [PATCH 46/55] Prefer updated syntax for forwarding arguments --- lib/pg_search.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pg_search.rb b/lib/pg_search.rb index 9ad67ada..89b643fb 100644 --- a/lib/pg_search.rb +++ b/lib/pg_search.rb @@ -34,8 +34,8 @@ def self.included(base) self.unaccent_function = "unaccent" class << self - def multisearch(*args) - PgSearch::Document.search(*args) + def multisearch(...) + PgSearch::Document.search(...) end def disable_multisearch From 0ba65abce40e09ad718c300da813673c4f645e0c Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Mon, 25 Dec 2023 14:27:10 -0600 Subject: [PATCH 47/55] Prefer Kernel#warn for deprecation messages --- lib/pg_search.rb | 2 +- lib/pg_search/features/tsearch.rb | 6 ++++-- lib/pg_search/multisearch.rb | 6 ++++-- spec/integration/deprecation_spec.rb | 15 +++++++-------- spec/lib/pg_search/features/tsearch_spec.rb | 4 ++-- spec/lib/pg_search/multisearch_spec.rb | 6 +++--- 6 files changed, 21 insertions(+), 18 deletions(-) diff --git a/lib/pg_search.rb b/lib/pg_search.rb index 89b643fb..3206e1ff 100644 --- a/lib/pg_search.rb +++ b/lib/pg_search.rb @@ -18,7 +18,7 @@ module PgSearch autoload :Document, "pg_search/document" def self.included(base) - ActiveSupport::Deprecation.warn <<~MESSAGE + warn(<<~MESSAGE, category: :deprecated, uplevel: 1) Directly including `PgSearch` into an Active Record model is deprecated and will be removed in pg_search 3.0. Please replace `include PgSearch` with `include PgSearch::Model`. diff --git a/lib/pg_search/features/tsearch.rb b/lib/pg_search/features/tsearch.rb index a4136bde..c2a3cc9d 100644 --- a/lib/pg_search/features/tsearch.rb +++ b/lib/pg_search/features/tsearch.rb @@ -70,9 +70,11 @@ def deprecated_headline_options unless value.nil? key = deprecated_key.camelize - ActiveSupport::Deprecation.warn( + warn( "pg_search 3.0 will no longer accept :#{deprecated_key} as an argument to :ts_headline, " \ - "use :#{key} instead." + "use :#{key} instead.", + category: :deprecated, + uplevel: 1 ) hash[key] = ts_headline_option_value(value) diff --git a/lib/pg_search/multisearch.rb b/lib/pg_search/multisearch.rb index 271d4582..bbe150dc 100644 --- a/lib/pg_search/multisearch.rb +++ b/lib/pg_search/multisearch.rb @@ -7,9 +7,11 @@ module Multisearch class << self def rebuild(model, deprecated_clean_up = nil, clean_up: true, transactional: true) unless deprecated_clean_up.nil? - ActiveSupport::Deprecation.warn( + warn( "pg_search 3.0 will no longer accept a boolean second argument to PgSearchMultisearch.rebuild, " \ - "use keyword argument `clean_up:` instead." + "use keyword argument `clean_up:` instead.", + category: :deprecated, + uplevel: 1 ) clean_up = deprecated_clean_up end diff --git a/spec/integration/deprecation_spec.rb b/spec/integration/deprecation_spec.rb index 1f5ee149..919c1163 100644 --- a/spec/integration/deprecation_spec.rb +++ b/spec/integration/deprecation_spec.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true require "spec_helper" +require "active_support/core_ext/kernel/reporting" describe "Including the deprecated PgSearch module" do with_model :SomeModel do model do - ActiveSupport::Deprecation.silence do + silence_warnings do include PgSearch end end @@ -18,16 +19,14 @@ end it "prints a deprecation message" do - allow(ActiveSupport::Deprecation).to receive(:warn) + allow(PgSearch).to receive(:warn) AnotherModel.include(PgSearch) - expect(ActiveSupport::Deprecation).to have_received(:warn).with( - <<~MESSAGE - Directly including `PgSearch` into an Active Record model is deprecated and will be removed in pg_search 3.0. + expect(PgSearch).to have_received(:warn).with(<<~MESSAGE, category: :deprecated, uplevel: 1) + Directly including `PgSearch` into an Active Record model is deprecated and will be removed in pg_search 3.0. - Please replace `include PgSearch` with `include PgSearch::Model`. - MESSAGE - ) + Please replace `include PgSearch` with `include PgSearch::Model`. + MESSAGE end end diff --git a/spec/lib/pg_search/features/tsearch_spec.rb b/spec/lib/pg_search/features/tsearch_spec.rb index de639d1d..04be5a24 100644 --- a/spec/lib/pg_search/features/tsearch_spec.rb +++ b/spec/lib/pg_search/features/tsearch_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "spec_helper" -require "active_support/deprecation" +require "active_support/core_ext/kernel/reporting" describe PgSearch::Features::TSearch do describe "#rank" do @@ -251,7 +251,7 @@ feature = described_class.new(query, options, columns, Model, normalizer) - highlight_sql = ActiveSupport::Deprecation.silence { feature.highlight.to_sql } + highlight_sql = silence_warnings { feature.highlight.to_sql } expected_sql = %{(ts_headline('simple', (coalesce((#{Model.quoted_table_name}."name")::text, '')), (to_tsquery('simple', ''' ' || 'query' || ' ''')), 'StartSel = "", StopSel = "", MaxFragments = 3, MaxWords = 123, MinWords = 456, ShortWord = 4, FragmentDelimiter = "…", HighlightAll = FALSE'))} expect(highlight_sql).to eq(expected_sql) diff --git a/spec/lib/pg_search/multisearch_spec.rb b/spec/lib/pg_search/multisearch_spec.rb index 8d7eab86..3329a883 100644 --- a/spec/lib/pg_search/multisearch_spec.rb +++ b/spec/lib/pg_search/multisearch_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "spec_helper" -require "active_support/deprecation" +require "active_support/core_ext/kernel/reporting" # rubocop:disable RSpec/NestedGroups describe PgSearch::Multisearch do @@ -82,7 +82,7 @@ context "when deprecated_clean_up is true" do it "deletes the document for the model" do - ActiveSupport::Deprecation.silence { described_class.rebuild(model, true) } + silence_warnings { described_class.rebuild(model, true) } expect(PgSearch::Document.count).to eq(1) expect(PgSearch::Document.first.searchable_type).to eq("Bar") end @@ -90,7 +90,7 @@ context "when deprecated_clean_up is false" do it "does not delete the document for the model" do - ActiveSupport::Deprecation.silence { described_class.rebuild(model, false) } + silence_warnings { described_class.rebuild(model, false) } expect(PgSearch::Document.count).to eq(2) end end From f8a863922f99a080b709ec665766397cf4071546 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Mon, 25 Dec 2023 14:34:21 -0600 Subject: [PATCH 48/55] Adopt more standardrb rules --- .rubocop.yml | 8 +++++++- Gemfile | 4 +++- spec/lib/pg_search/multisearchable_spec.rb | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index d003e006..69fe47f8 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,12 +1,18 @@ require: - standard + - standard-performance + - standard-rails + - standard-rspec - rubocop-performance - - rubocop-rails - rubocop-rake + - rubocop-rails - rubocop-rspec inherit_gem: standard: config/base.yml + standard-performance: config/base.yml + standard-rails: config/base.yml + standard-rspec: config/base.yml AllCops: TargetRubyVersion: 3.0 diff --git a/Gemfile b/Gemfile index 6d374066..8f16c990 100644 --- a/Gemfile +++ b/Gemfile @@ -24,7 +24,9 @@ gem "rubocop-rake" gem "rubocop-rspec" gem "simplecov" gem "simplecov-lcov" -gem "standard", ">= 1.23.0" +gem "standard" +gem "standard-rails" +gem "standard-rspec" gem "undercover" gem "warning" gem "with_model" diff --git a/spec/lib/pg_search/multisearchable_spec.rb b/spec/lib/pg_search/multisearchable_spec.rb index c155741b..2973ec76 100644 --- a/spec/lib/pg_search/multisearchable_spec.rb +++ b/spec/lib/pg_search/multisearchable_spec.rb @@ -36,7 +36,7 @@ belongs_to :multisearchable_parent after_destroy do - multisearchable_parent.update_attribute(:secret, rand(1000).to_s) # rubocop:disable Rails/SkipsModelValidations + multisearchable_parent.update_attribute(:secret, rand(1000).to_s) end end end From d179680b75d995ba2763e243524551da051edc57 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Thu, 28 Dec 2023 10:47:58 -0600 Subject: [PATCH 49/55] Switch from rubocop to standard gem for linting --- .codeclimate.yml | 17 -------- .rubocop.yml | 40 ------------------- .standard.yml | 6 +++ Gemfile | 13 ++---- Rakefile | 7 +--- lib/pg_search/document.rb | 2 +- lib/pg_search/multisearchable.rb | 4 +- spec/integration/associations_spec.rb | 12 +++--- spec/integration/pg_search_spec.rb | 8 ++-- .../configuration/association_spec.rb | 4 +- spec/lib/pg_search/features/trigram_spec.rb | 4 +- spec/lib/pg_search/features/tsearch_spec.rb | 12 +++--- .../pg_search/multisearch/rebuilder_spec.rb | 24 +++++------ spec/lib/pg_search/multisearch_spec.rb | 6 +-- spec/lib/pg_search/multisearchable_spec.rb | 20 +++++----- spec/lib/pg_search/normalizer_spec.rb | 4 +- spec/lib/pg_search_spec.rb | 12 +++--- 17 files changed, 68 insertions(+), 127 deletions(-) delete mode 100644 .codeclimate.yml delete mode 100644 .rubocop.yml create mode 100644 .standard.yml diff --git a/.codeclimate.yml b/.codeclimate.yml deleted file mode 100644 index 2ce19b6e..00000000 --- a/.codeclimate.yml +++ /dev/null @@ -1,17 +0,0 @@ ---- -engines: - duplication: - enabled: true - config: - languages: - - ruby - fixme: - enabled: true - rubocop: - enabled: true - channel: rubocop-0-78 -ratings: - paths: - - "**.rb" -exclude_paths: - - "spec/" diff --git a/.rubocop.yml b/.rubocop.yml deleted file mode 100644 index 69fe47f8..00000000 --- a/.rubocop.yml +++ /dev/null @@ -1,40 +0,0 @@ -require: - - standard - - standard-performance - - standard-rails - - standard-rspec - - rubocop-performance - - rubocop-rake - - rubocop-rails - - rubocop-rspec - -inherit_gem: - standard: config/base.yml - standard-performance: config/base.yml - standard-rails: config/base.yml - standard-rspec: config/base.yml - -AllCops: - TargetRubyVersion: 3.0 - NewCops: disable - Exclude: - - bin/**/* - - vendor/**/* - -Lint/RedundantCopDisableDirective: - Enabled: true - -Lint/RedundantCopEnableDirective: - Enabled: true - -Bundler/DuplicatedGem: - Enabled: false - -Rails/ApplicationRecord: - Enabled: false - -Rails/RakeEnvironment: - Enabled: false - -Rails/TimeZone: - Enabled: false diff --git a/.standard.yml b/.standard.yml new file mode 100644 index 00000000..7c8e0f15 --- /dev/null +++ b/.standard.yml @@ -0,0 +1,6 @@ +ruby_version: 3.0 + +plugins: + - standard-performance + - standard-rails: + target_rails_version: 6.1 diff --git a/Gemfile b/Gemfile index 8f16c990..0d6a9984 100644 --- a/Gemfile +++ b/Gemfile @@ -12,21 +12,16 @@ if ENV["ACTIVE_RECORD_BRANCH"] gem "arel", git: "https://github.com/rails/arel.git" if ENV.fetch("ACTIVE_RECORD_BRANCH", nil) == "master" end -gem "activerecord", ENV.fetch("ACTIVE_RECORD_VERSION", nil) if ENV["ACTIVE_RECORD_VERSION"] +gem "activerecord", ENV.fetch("ACTIVE_RECORD_VERSION", nil) if ENV["ACTIVE_RECORD_VERSION"] # standard:disable Bundler/DuplicatedGem gem "pry" gem "rake" gem "rspec" -gem "rubocop" -gem "rubocop-performance" -gem "rubocop-rails" -gem "rubocop-rake" -gem "rubocop-rspec" gem "simplecov" gem "simplecov-lcov" -gem "standard" -gem "standard-rails" -gem "standard-rspec" +gem "standard", require: false +gem "standard-rails", require: false +gem "standard-rspec", require: false gem "undercover" gem "warning" gem "with_model" diff --git a/Rakefile b/Rakefile index 6167eb4c..90edfb4e 100644 --- a/Rakefile +++ b/Rakefile @@ -6,10 +6,7 @@ Bundler::GemHelper.install_tasks require "rspec/core/rake_task" RSpec::Core::RakeTask.new(:spec) -require "rubocop/rake_task" -RuboCop::RakeTask.new do |t| - t.options = %w[--display-cop-names] -end +require "standard/rake" desc "Check test coverage" task :undercover do @@ -17,4 +14,4 @@ task :undercover do exit(1) unless system("bin/undercover --compare origin/master") end -task default: %w[spec rubocop undercover] +task default: %w[spec standard undercover] diff --git a/lib/pg_search/document.rb b/lib/pg_search/document.rb index e41eaaa1..1865bfd3 100644 --- a/lib/pg_search/document.rb +++ b/lib/pg_search/document.rb @@ -3,7 +3,7 @@ require "logger" module PgSearch - class Document < ActiveRecord::Base + class Document < ActiveRecord::Base # standard:disable Rails/ApplicationRecord include PgSearch::Model self.table_name = "pg_search_documents" diff --git a/lib/pg_search/multisearchable.rb b/lib/pg_search/multisearchable.rb index 5309c1c0..2d24036d 100644 --- a/lib/pg_search/multisearchable.rb +++ b/lib/pg_search/multisearchable.rb @@ -50,7 +50,7 @@ def update_pg_search_document if should_have_document create_or_update_pg_search_document else - pg_search_document&.destroy + pg_search_document&.destroy # standard:disable Rails/SaveBang end end @@ -58,7 +58,7 @@ def create_or_update_pg_search_document if !pg_search_document create_pg_search_document(pg_search_document_attrs) elsif should_update_pg_search_document? - pg_search_document.update(pg_search_document_attrs) + pg_search_document.update(pg_search_document_attrs) # standard:disable Rails/SaveBang end end end diff --git a/spec/integration/associations_spec.rb b/spec/integration/associations_spec.rb index e929c2af..f0d2072a 100644 --- a/spec/integration/associations_spec.rb +++ b/spec/integration/associations_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -# rubocop:disable RSpec/NestedGroups +# standard:disable RSpec/NestedGroups describe "a pg_search_scope" do context "when joining to another table" do context "without an :against" do @@ -418,10 +418,10 @@ end it "finds records of the other model" do - included_associated_1 = AssociatedModel.create(content: "foo bar") - included_associated_2 = AssociatedModel.create(content: "foo baz") - excluded_associated_1 = AssociatedModel.create(content: "baz quux") - excluded_associated_2 = AssociatedModel.create(content: "baz bar") + included_associated_1 = AssociatedModel.create!(content: "foo bar") + included_associated_2 = AssociatedModel.create!(content: "foo baz") + excluded_associated_1 = AssociatedModel.create!(content: "baz quux") + excluded_associated_2 = AssociatedModel.create!(content: "baz bar") included = [ ModelWithAssociation.create(associated_models: [included_associated_1]), @@ -484,4 +484,4 @@ end end end -# rubocop:enable RSpec/NestedGroups +# standard:enable RSpec/NestedGroups diff --git a/spec/integration/pg_search_spec.rb b/spec/integration/pg_search_spec.rb index 056b267c..8c73e692 100644 --- a/spec/integration/pg_search_spec.rb +++ b/spec/integration/pg_search_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -# rubocop:disable RSpec/NestedGroups +# standard:disable RSpec/NestedGroups describe "an Active Record model which includes PgSearch" do with_model :ModelWithPgSearch do table do |t| @@ -1059,8 +1059,8 @@ SET content_tsvector = to_tsvector('english'::regconfig, #{Post.quoted_table_name}."content") SQL - expected.comments.create(body: "commentone") - unexpected.comments.create(body: "commentwo") + expected.comments.create!(body: "commentone") + unexpected.comments.create!(body: "commentwo") Post.pg_search_scope :search_by_content_with_tsvector, associated_against: {comments: [:body]}, @@ -1355,4 +1355,4 @@ end end end -# rubocop:enable RSpec/NestedGroups +# standard:enable RSpec/NestedGroups diff --git a/spec/lib/pg_search/configuration/association_spec.rb b/spec/lib/pg_search/configuration/association_spec.rb index 7750a885..2e326ad8 100644 --- a/spec/lib/pg_search/configuration/association_spec.rb +++ b/spec/lib/pg_search/configuration/association_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -# rubocop:disable RSpec/NestedGroups +# standard:disable RSpec/NestedGroups describe PgSearch::Configuration::Association do with_model :Avatar do table do |t| @@ -139,4 +139,4 @@ end end end -# rubocop:enable RSpec/NestedGroups +# standard:enable RSpec/NestedGroups diff --git a/spec/lib/pg_search/features/trigram_spec.rb b/spec/lib/pg_search/features/trigram_spec.rb index 5686043a..4dd44eb2 100644 --- a/spec/lib/pg_search/features/trigram_spec.rb +++ b/spec/lib/pg_search/features/trigram_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" require "ostruct" -# rubocop:disable RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups +# standard:disable RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups describe PgSearch::Features::Trigram do subject(:feature) { described_class.new(query, options, columns, Model, normalizer) } @@ -109,4 +109,4 @@ end end end -# rubocop:enable RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups +# standard:enable RSpec/MultipleMemoizedHelpers, RSpec/NestedGroups diff --git a/spec/lib/pg_search/features/tsearch_spec.rb b/spec/lib/pg_search/features/tsearch_spec.rb index 04be5a24..cd4b3ada 100644 --- a/spec/lib/pg_search/features/tsearch_spec.rb +++ b/spec/lib/pg_search/features/tsearch_spec.rb @@ -169,7 +169,7 @@ end context "when options[:dictionary] is passed" do - # rubocop:disable RSpec/ExampleLength + # standard:disable RSpec/ExampleLength it "uses the provided dictionary" do query = "query" columns = [ @@ -193,11 +193,11 @@ expect(feature.highlight.to_sql).to eq(expected_sql) end - # rubocop:enable RSpec/ExampleLength + # standard:enable RSpec/ExampleLength end context "when options[:highlight] has options set" do - # rubocop:disable RSpec/ExampleLength + # standard:disable RSpec/ExampleLength it "passes the options to ts_headline" do query = "query" columns = [ @@ -225,9 +225,9 @@ expect(feature.highlight.to_sql).to eq(expected_sql) end - # rubocop:enable RSpec/ExampleLength + # standard:enable RSpec/ExampleLength - # rubocop:disable RSpec/ExampleLength + # standard:disable RSpec/ExampleLength it "passes deprecated options to ts_headline" do query = "query" columns = [ @@ -256,7 +256,7 @@ expect(highlight_sql).to eq(expected_sql) end - # rubocop:enable RSpec/ExampleLength + # standard:enable RSpec/ExampleLength end end end diff --git a/spec/lib/pg_search/multisearch/rebuilder_spec.rb b/spec/lib/pg_search/multisearch/rebuilder_spec.rb index 58a48c02..5572227a 100644 --- a/spec/lib/pg_search/multisearch/rebuilder_spec.rb +++ b/spec/lib/pg_search/multisearch/rebuilder_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -# rubocop:disable RSpec/NestedGroups +# standard:disable RSpec/NestedGroups describe PgSearch::Multisearch::Rebuilder do with_table "pg_search_documents", &DOCUMENTS_SCHEMA @@ -108,7 +108,7 @@ def rebuild_pg_search_documents end end - # rubocop:disable RSpec/ExampleLength + # standard:disable RSpec/ExampleLength it "executes the default SQL" do time = Time.utc(2001, 1, 1, 0, 0, 0) rebuilder = described_class.new(Model, -> { time }) @@ -137,7 +137,7 @@ def rebuild_pg_search_documents expect(executed_sql.length).to eq(1) expect(executed_sql.first.strip).to eq(expected_sql.strip) end - # rubocop:enable RSpec/ExampleLength + # standard:enable RSpec/ExampleLength context "with a model with a camel case column" do with_model :ModelWithCamelCaseColumn do @@ -170,7 +170,7 @@ def rebuild_pg_search_documents end end - # rubocop:disable RSpec/ExampleLength + # standard:disable RSpec/ExampleLength it "generates SQL with the correct primary key" do time = Time.utc(2001, 1, 1, 0, 0, 0) rebuilder = described_class.new(ModelWithNonStandardPrimaryKey, -> { time }) @@ -199,7 +199,7 @@ def rebuild_pg_search_documents expect(executed_sql.length).to eq(1) expect(executed_sql.first.strip).to eq(expected_sql.strip) end - # rubocop:enable RSpec/ExampleLength + # standard:enable RSpec/ExampleLength end end @@ -215,7 +215,7 @@ def foo end end - # rubocop:disable RSpec/ExampleLength + # standard:disable RSpec/ExampleLength it "calls update_pg_search_document on each record" do record = Model.create! @@ -241,7 +241,7 @@ def foo expect(record.pg_search_document).to be_present end - # rubocop:enable RSpec/ExampleLength + # standard:enable RSpec/ExampleLength end context "when only additional_attributes is set" do @@ -285,7 +285,7 @@ def foo end end - # rubocop:disable RSpec/ExampleLength + # standard:disable RSpec/ExampleLength it "calls update_pg_search_document on each record" do record_1 = Model.create!(active: true) record_2 = Model.create!(active: false) @@ -311,7 +311,7 @@ def foo expect(record_1.pg_search_document).to be_present expect(record_2.pg_search_document).not_to be_present end - # rubocop:enable RSpec/ExampleLength + # standard:enable RSpec/ExampleLength end context "via :unless" do @@ -326,7 +326,7 @@ def foo end end - # rubocop:disable RSpec/ExampleLength + # standard:disable RSpec/ExampleLength it "calls update_pg_search_document on each record" do record_1 = Model.create!(inactive: true) record_2 = Model.create!(inactive: false) @@ -352,10 +352,10 @@ def foo expect(record_1.pg_search_document).not_to be_present expect(record_2.pg_search_document).to be_present end - # rubocop:enable RSpec/ExampleLength + # standard:enable RSpec/ExampleLength end end end end end -# rubocop:enable RSpec/NestedGroups +# standard:enable RSpec/NestedGroups diff --git a/spec/lib/pg_search/multisearch_spec.rb b/spec/lib/pg_search/multisearch_spec.rb index 3329a883..c5c1570e 100644 --- a/spec/lib/pg_search/multisearch_spec.rb +++ b/spec/lib/pg_search/multisearch_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" require "active_support/core_ext/kernel/reporting" -# rubocop:disable RSpec/NestedGroups +# standard:disable RSpec/NestedGroups describe PgSearch::Multisearch do with_table "pg_search_documents", &DOCUMENTS_SCHEMA @@ -135,7 +135,7 @@ def model.rebuild_pg_search_documents end describe "the generated SQL" do - let(:now) { Time.now } + let(:now) { Time.now } # standard:disable Rails/TimeZone before { allow(Time).to receive(:now).and_return(now) } @@ -195,4 +195,4 @@ def model.rebuild_pg_search_documents end end end -# rubocop:enable RSpec/NestedGroups +# standard:enable RSpec/NestedGroups diff --git a/spec/lib/pg_search/multisearchable_spec.rb b/spec/lib/pg_search/multisearchable_spec.rb index 2973ec76..b21d451b 100644 --- a/spec/lib/pg_search/multisearchable_spec.rb +++ b/spec/lib/pg_search/multisearchable_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -# rubocop:disable RSpec/NestedGroups +# standard:disable RSpec/NestedGroups describe PgSearch::Multisearchable do with_table "pg_search_documents", &DOCUMENTS_SCHEMA @@ -156,7 +156,7 @@ without_partial_double_verification do allow(record).to receive(:some_content) { text } end - record.save + record.save! end describe "#content" do @@ -174,7 +174,7 @@ allow(record).to receive(:attr_1).and_return("1") allow(record).to receive(:attr_2).and_return("2") end - record.save + record.save! end describe "#content" do @@ -202,7 +202,7 @@ without_partial_double_verification do allow(record).to receive(:some_content) { text } end - record.save + record.save! end describe "#content" do @@ -220,7 +220,7 @@ allow(record).to receive(:attr_1).and_return("1") allow(record).to receive(:attr_2).and_return("2") end - record.save + record.save! end describe "#content" do @@ -244,7 +244,7 @@ without_partial_double_verification do allow(record).to receive(:bar).and_return(text) allow(record).to receive(:create_pg_search_document) - record.save + record.save! expect(record) .to have_received(:create_pg_search_document) .with(content: "", foo: text) @@ -266,7 +266,7 @@ without_partial_double_verification do allow(record).to receive(:bar?).and_return(false) allow(record).to receive(:create_pg_search_document) - record.save + record.save! expect(record) .to have_received(:create_pg_search_document) .with(content: "") @@ -286,7 +286,7 @@ it "does not update the document" do without_partial_double_verification do allow(record.pg_search_document).to receive(:update) - record.save + record.save! expect(record.pg_search_document).not_to have_received(:update) end end @@ -301,7 +301,7 @@ it "updates the document" do allow(record.pg_search_document).to receive(:update) - record.save + record.save! expect(record.pg_search_document).to have_received(:update) end end @@ -854,4 +854,4 @@ end end end -# rubocop:enable RSpec/NestedGroups +# standard:enable RSpec/NestedGroups diff --git a/spec/lib/pg_search/normalizer_spec.rb b/spec/lib/pg_search/normalizer_spec.rb index f267caf2..96df04dc 100644 --- a/spec/lib/pg_search/normalizer_spec.rb +++ b/spec/lib/pg_search/normalizer_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -# rubocop:disable RSpec/NestedGroups +# standard:disable RSpec/NestedGroups describe PgSearch::Normalizer do describe "#add_normalization" do context "when config[:ignore] includes :accents" do @@ -59,4 +59,4 @@ end end end -# rubocop:enable RSpec/NestedGroups +# standard:enable RSpec/NestedGroups diff --git a/spec/lib/pg_search_spec.rb b/spec/lib/pg_search_spec.rb index 6a178f2d..bc53da7f 100644 --- a/spec/lib/pg_search_spec.rb +++ b/spec/lib/pg_search_spec.rb @@ -13,7 +13,7 @@ def clear_searchable_cache end end -# rubocop:disable RSpec/NestedGroups +# standard:disable RSpec/NestedGroups describe PgSearch do describe ".multisearch" do with_table "pg_search_documents", &DOCUMENTS_SCHEMA @@ -149,7 +149,7 @@ def clear_searchable_cache expect(results.size).to eq(SearchableSubclassModel.count) end - # rubocop:disable RSpec/MultipleExpectations + # standard:disable RSpec/MultipleExpectations specify "reindexing works" do NonSearchableSubclassModel.create!(content: "foo bar") NonSearchableSubclassModel.create!(content: "baz") @@ -171,7 +171,7 @@ def clear_searchable_cache expect(PgSearch::Document.first.searchable.class).to be SearchableSubclassModel expect(PgSearch::Document.first.searchable).to eq expected end - # rubocop:enable RSpec/MultipleExpectations + # standard:enable RSpec/MultipleExpectations it "reindexing searchable STI doesn't clobber other related STI models" do SearchableSubclassModel.create!(content: "baz") @@ -263,7 +263,7 @@ def clear_searchable_cache expect(multisearch_enabled_after).to be(true) end - # rubocop:disable RSpec/ExampleLength + # standard:disable RSpec/ExampleLength it "does not disable multisearch on other threads" do values = Queue.new sync = Queue.new @@ -288,7 +288,7 @@ def clear_searchable_cache expect(multisearch_enabled_inside).to be(true) expect(multisearch_enabled_after).to be(true) end - # rubocop:enable RSpec/ExampleLength + # standard:enable RSpec/ExampleLength end end -# rubocop:enable RSpec/NestedGroups +# standard:enable RSpec/NestedGroups From 2e702053e6b99b02f6985bb5cb24e1e1ad6f0eed Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Thu, 28 Dec 2023 11:18:50 -0600 Subject: [PATCH 50/55] Prefer debug/irb gems for debugging Signed-off-by: Grant Hutchins --- Gemfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 0d6a9984..3c7d6664 100644 --- a/Gemfile +++ b/Gemfile @@ -14,7 +14,8 @@ end gem "activerecord", ENV.fetch("ACTIVE_RECORD_VERSION", nil) if ENV["ACTIVE_RECORD_VERSION"] # standard:disable Bundler/DuplicatedGem -gem "pry" +gem "debug" +gem "irb" gem "rake" gem "rspec" gem "simplecov" From 9391e7e7bbdf3fe7d6e0f0dabc2af740cb432e89 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Tue, 2 Jul 2024 02:00:33 +0300 Subject: [PATCH 51/55] Fix compatibility with ActiveRecord 7.2 --- .github/workflows/ci.yml | 4 ++++ lib/pg_search/scope_options.rb | 15 +++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f5b56f7..18ebbee7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,11 +41,15 @@ jobs: - ACTIVE_RECORD_VERSION="~> 6.1.0" - ACTIVE_RECORD_VERSION="~> 7.0.0" - ACTIVE_RECORD_VERSION="~> 7.1.0" + - ACTIVE_RECORD_VERSION="~> 7.2.0.beta2" allow-failure: [false] include: - ruby-version: '3.3' active-record-version-env: ACTIVE_RECORD_BRANCH="main" allow-failure: true + - ruby-version: '3.3' + active-record-version-env: ACTIVE_RECORD_BRANCH="7-2-stable" + allow-failure: true - ruby-version: '3.3' active-record-version-env: ACTIVE_RECORD_BRANCH="7-1-stable" allow-failure: true diff --git a/lib/pg_search/scope_options.rb b/lib/pg_search/scope_options.rb index b03d13d0..d6893b68 100644 --- a/lib/pg_search/scope_options.rb +++ b/lib/pg_search/scope_options.rb @@ -90,10 +90,17 @@ def subquery end def conditions - config.features - .reject { |_feature_name, feature_options| feature_options && feature_options[:sort_only] } - .map { |feature_name, _feature_options| feature_for(feature_name).conditions } - .inject { |accumulator, expression| Arel::Nodes::Or.new(accumulator, expression) } + expressions = + config.features + .reject { |_feature_name, feature_options| feature_options && feature_options[:sort_only] } + .map { |feature_name, _feature_options| feature_for(feature_name).conditions } + + # https://github.com/rails/rails/pull/51492 + if ActiveRecord.version >= Gem::Version.new("7.2.0.beta1") + Arel::Nodes::Or.new(expressions) + else + expressions.inject { |accumulator, expression| Arel::Nodes::Or.new(accumulator, expression) } + end end def order_within_rank From cf258058af8badc6a1bbb8195ab92ef56fb97195 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 11 Aug 2024 15:32:36 -0500 Subject: [PATCH 52/55] Prefer feature detection over version comparison --- lib/pg_search/scope_options.rb | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/pg_search/scope_options.rb b/lib/pg_search/scope_options.rb index d6893b68..6fe515dd 100644 --- a/lib/pg_search/scope_options.rb +++ b/lib/pg_search/scope_options.rb @@ -95,13 +95,27 @@ def conditions .reject { |_feature_name, feature_options| feature_options && feature_options[:sort_only] } .map { |feature_name, _feature_options| feature_for(feature_name).conditions } - # https://github.com/rails/rails/pull/51492 - if ActiveRecord.version >= Gem::Version.new("7.2.0.beta1") + or_node(expressions) + end + + # https://github.com/rails/rails/pull/51492 + # :nocov: + # standard:disable Lint/DuplicateMethods + or_arity = Arel::Nodes::Or.instance_method(:initialize).arity + case or_arity + when 1 + def or_node(expressions) Arel::Nodes::Or.new(expressions) - else + end + when 2 + def or_node(expressions) expressions.inject { |accumulator, expression| Arel::Nodes::Or.new(accumulator, expression) } end + else + raise "Unsupported arity #{or_arity} for Arel::Nodes::Or#initialize" end + # :nocov: + # standard:enable Lint/DuplicateMethods def order_within_rank config.order_within_rank || "#{primary_key} ASC" From b3d334db2d1d92e1fa6ecf47a89f46530de818ea Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 11 Aug 2024 15:52:12 -0500 Subject: [PATCH 53/55] Update CI for Active Record 7.2 --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 18ebbee7..d1405f0e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,8 +41,11 @@ jobs: - ACTIVE_RECORD_VERSION="~> 6.1.0" - ACTIVE_RECORD_VERSION="~> 7.0.0" - ACTIVE_RECORD_VERSION="~> 7.1.0" - - ACTIVE_RECORD_VERSION="~> 7.2.0.beta2" + - ACTIVE_RECORD_VERSION="~> 7.2.0" allow-failure: [false] + exclude: + - ruby-version: '3.0' + active-record-version-env: ACTIVE_RECORD_VERSION="~> 7.2.0" include: - ruby-version: '3.3' active-record-version-env: ACTIVE_RECORD_BRANCH="main" From f55a87f076638e62eb1d1441bc225eab125b0bb4 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 11 Aug 2024 16:20:15 -0500 Subject: [PATCH 54/55] Update actions/checkout step in CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1405f0e..21839800 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: allow-failure: true continue-on-error: ${{ matrix.allow-failure }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: From 1f88614ca795c1b031bde5e3b0f1bb2dc78939d8 Mon Sep 17 00:00:00 2001 From: Grant Hutchins Date: Sun, 11 Aug 2024 16:29:05 -0500 Subject: [PATCH 55/55] VERSION 2.3.7 --- CHANGELOG.md | 11 +++++++++++ lib/pg_search/version.rb | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d6a196c..97ae6112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # pg_search changelog +## 2.3.7 + +* Drop support for Ruby 2.6 and 2.7 +* Drop support for Active Record 6.0 and earlier +* Support Ruby 3.2 and 3.3 +* Support Active Record 7.1 +* Support Active Record 7.2 (fatkodima) +* Add U+02BB/U+02BC to disallowed tsquery characters (Vital Ryabchinskiy) +* add support for Arel::Nodes::SqlLiteral columns (Kyle Fazzari) +* Improve documentation (Prima Aulia Gusta, Ross Baird, Andy Atkinson) + ## 2.3.6 * Drop support for Ruby 2.5 diff --git a/lib/pg_search/version.rb b/lib/pg_search/version.rb index afff11ae..a4378281 100644 --- a/lib/pg_search/version.rb +++ b/lib/pg_search/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module PgSearch - VERSION = "2.3.6" + VERSION = "2.3.7" end