textacular 5.5.0 → 5.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 974b8a8209d44a134f421db20f9ae6338c03057d8bcf26ce1e0c40778a29d6bf
4
- data.tar.gz: 422c096f881919e53eaaab5f530e26e0e5aeffe33a4c8646bcb7f3b367dc9084
3
+ metadata.gz: 7c39daa8d9c982d9587abcff6d933d52e9a52d68ef03bda9f5eed512adb164e9
4
+ data.tar.gz: 934b8f16721f361531970c7c6f222f30ea10c971b271a9bc645f71b912dffa7b
5
5
  SHA512:
6
- metadata.gz: ab93dff1128025cc4615d29c819d104a9300bdbb0a88d9cc588940a1f630922a201b9f11449d610ba5cadc5ae85c2e761c0ad3ff5b8cb7a85f1fd3dd15acf744
7
- data.tar.gz: 2102ea9936a087f18ed4945ddb4213e93acb70dc4bc8d3281489664fda2968b635e024636f0f2112f0fa8fc7a13b5d2b7e728aa200ac7663b681ae287b7e6b5d
6
+ metadata.gz: 05f12605ab99f20772a326a45279c8f30eea56cb58687fdd7d0584cf756212374779432e674efbaa556888d32e37b691e738571ceb747276b888ffc56752400e
7
+ data.tar.gz: 4d3111ee2e70c83c5dda936b87761658599c7420e6c54bcc139410cf49da6dae65cb603e98805823c737e5d9d93393826065d64cd8406317b604f3bdf7073dc9
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 5.6.0
6
+
7
+ * ActiveRecord 7.1 compatibility
8
+
9
+ ## 5.5.1
10
+
11
+ * Revert breaking change to fuzzy_search introduced in 49f09b389d2f2b18cbe9de565b7ac5fcac14d7c6
12
+
5
13
  ## 5.5.0
6
14
 
7
15
  * ActiveRecord 7.0 compatibility
data/Gemfile CHANGED
@@ -2,8 +2,14 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- git 'git://github.com/rails/rails.git', branch: 'main' do
5
+ git_source(:github) do |repo_name|
6
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/')
7
+ "https://github.com/#{repo_name}.git"
8
+ end
9
+
10
+ github 'rails/rails', branch: 'main' do
6
11
  gem 'activerecord'
12
+ gem 'activesupport'
7
13
  end
8
14
 
9
15
  gem "pg", "~> 1.1"
data/README.md CHANGED
@@ -1,14 +1,8 @@
1
1
  # textacular
2
2
  [![Gem Version](http://img.shields.io/gem/v/textacular.svg)][rubygems]
3
- [![Build Status](https://img.shields.io/travis/textacular/textacular/master.svg)][travis]
4
- [![Code Climate](https://img.shields.io/codeclimate/github/textacular/textacular.svg)][codeclimate]
3
+ [![Build Status](https://github.com/textacular/textacular/actions/workflows/main.yml/badge.svg)](https://github.com/textacular/textacular/actions/workflows/main.yml)
5
4
 
6
5
  [rubygems]: http://rubygems.org/gems/textacular
7
- [travis]: https://travis-ci.org/textacular/textacular
8
- [codeclimate]: https://codeclimate.com/github/textacular/textacular
9
-
10
- Further documentation available at http://textacular.github.com/textacular.
11
-
12
6
 
13
7
  ## DESCRIPTION:
14
8
 
@@ -19,6 +13,8 @@ extending ActiveRecord with scopes making search easy and fun!
19
13
  ## FEATURES/PROBLEMS:
20
14
 
21
15
  * Only works with PostgreSQL
16
+ * Anything that mucks with the `SELECT` statement (notably `pluck`), is likely
17
+ to [cause problems](https://github.com/textacular/textacular/issues/28).
22
18
 
23
19
 
24
20
  ## SYNOPSIS:
data/Rakefile CHANGED
@@ -6,8 +6,15 @@ require 'pry'
6
6
  require 'rspec/core/rake_task'
7
7
 
8
8
  RSpec::Core::RakeTask.new(:spec)
9
+ require "rake/testtask"
9
10
 
10
- task :default => :spec
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << "test"
13
+ t.libs << "lib"
14
+ t.test_files = FileList["test/**/*_test.rb"]
15
+ end
16
+
17
+ task :default => :test
11
18
 
12
19
  file 'spec/config.yml' do |t|
13
20
  sh 'erb spec/config.yml.example > spec/config.yml'
@@ -20,9 +27,9 @@ end
20
27
 
21
28
  namespace :db do
22
29
 
23
- task :connect => 'spec/config.yml' do |t|
30
+ task :connect => 'test/config.yml' do |t|
24
31
  ActiveRecord::Base.establish_connection \
25
- YAML.load_file 'spec/config.yml'
32
+ YAML.load_file 'test/config.yml'
26
33
  end
27
34
 
28
35
  task :disconnect do
@@ -44,17 +51,16 @@ namespace :db do
44
51
  desc 'Run the test database migrations'
45
52
  task :up => :'db:connect' do
46
53
  if ActiveRecord.version >= Gem::Version.new('6.0.0')
47
- context = ActiveRecord::Migration.new.migration_context
48
- migrations = context.migrations
49
- schema_migration = context.schema_migration
54
+ ActiveRecord::Migration.new.migration_context.migrate
50
55
  elsif ActiveRecord.version >= Gem::Version.new('5.2')
51
56
  migrations = ActiveRecord::Migration.new.migration_context.migrations
52
57
  schema_migration = nil
58
+ ActiveRecord::Migrator.new(:up, migrations, schema_migration).migrate
53
59
  else
54
60
  migrations = ActiveRecord::Migrator.migrations('db/migrate')
55
61
  schema_migration = nil
62
+ ActiveRecord::Migrator.new(:up, migrations, schema_migration).migrate
56
63
  end
57
- ActiveRecord::Migrator.new(:up, migrations, schema_migration).migrate
58
64
  end
59
65
 
60
66
  desc 'Reverse the test database migrations'
@@ -1,5 +1,5 @@
1
1
  module Textacular
2
- VERSION = '5.5.0'
2
+ VERSION = '5.6.0'
3
3
 
4
4
  def self.version
5
5
  VERSION
data/lib/textacular.rb CHANGED
@@ -124,11 +124,7 @@ module Textacular
124
124
  end
125
125
 
126
126
  def fuzzy_condition_string(table_name, column, search_term)
127
- # At this point, search_term is already quoted and query ready. Insert % between the quotes and the actual string.
128
- search_term = search_term.gsub(/^(['"])/, '\1%')
129
- search_term = search_term.gsub(/(['"])$/, '%\1')
130
-
131
- "(#{table_name}.#{column}::text ILIKE #{search_term})"
127
+ "(#{table_name}.#{column}::text % #{search_term})"
132
128
  end
133
129
 
134
130
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: textacular
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.0
4
+ version: 5.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Hamill
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2021-12-23 00:00:00.000000000 Z
14
+ date: 2023-10-30 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: pg
@@ -42,7 +42,35 @@ dependencies:
42
42
  - !ruby/object:Gem::Version
43
43
  version: '0'
44
44
  - !ruby/object:Gem::Dependency
45
- name: database_cleaner
45
+ name: activesupport
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ - !ruby/object:Gem::Dependency
59
+ name: minitest
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ - !ruby/object:Gem::Dependency
73
+ name: database_cleaner-active_record
46
74
  requirement: !ruby/object:Gem::Requirement
47
75
  requirements:
48
76
  - - ">="
@@ -120,7 +148,7 @@ dependencies:
120
148
  version: '5.0'
121
149
  - - "<"
122
150
  - !ruby/object:Gem::Version
123
- version: '7.1'
151
+ version: '7.2'
124
152
  type: :runtime
125
153
  prerelease: false
126
154
  version_requirements: !ruby/object:Gem::Requirement
@@ -130,7 +158,7 @@ dependencies:
130
158
  version: '5.0'
131
159
  - - "<"
132
160
  - !ruby/object:Gem::Version
133
- version: '7.1'
161
+ version: '7.2'
134
162
  description: |-
135
163
  Textacular exposes full text search capabilities from PostgreSQL, extending
136
164
  ActiveRecord with scopes making search easy and fun!
@@ -154,27 +182,6 @@ files:
154
182
  - lib/textacular/tasks.rb
155
183
  - lib/textacular/trigram_installer.rb
156
184
  - lib/textacular/version.rb
157
- - spec/config.travis.yml
158
- - spec/config.yml.example
159
- - spec/spec_helper.rb
160
- - spec/support/ar_stand_in.rb
161
- - spec/support/character.rb
162
- - spec/support/game.rb
163
- - spec/support/game_extended_with_textacular.rb
164
- - spec/support/game_extended_with_textacular_and_custom_language.rb
165
- - spec/support/game_fail.rb
166
- - spec/support/game_fail_extended_with_textacular.rb
167
- - spec/support/not_there.rb
168
- - spec/support/textacular_web_comic.rb
169
- - spec/support/web_comic.rb
170
- - spec/support/web_comic_with_searchable.rb
171
- - spec/support/web_comic_with_searchable_name.rb
172
- - spec/support/web_comic_with_searchable_name_and_author.rb
173
- - spec/textacular/full_text_indexer_spec.rb
174
- - spec/textacular/migration_generator_spec.rb
175
- - spec/textacular/searchable_spec.rb
176
- - spec/textacular/trigram_installer_spec.rb
177
- - spec/textacular_spec.rb
178
185
  homepage: http://textacular.github.com/textacular
179
186
  licenses:
180
187
  - MIT
@@ -194,29 +201,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
201
  - !ruby/object:Gem::Version
195
202
  version: '0'
196
203
  requirements: []
197
- rubygems_version: 3.1.4
204
+ rubygems_version: 3.3.7
198
205
  signing_key:
199
206
  specification_version: 4
200
207
  summary: Textacular exposes full text search capabilities from PostgreSQL
201
- test_files:
202
- - spec/config.yml.example
203
- - spec/config.travis.yml
204
- - spec/spec_helper.rb
205
- - spec/support/ar_stand_in.rb
206
- - spec/support/character.rb
207
- - spec/support/game.rb
208
- - spec/support/game_extended_with_textacular.rb
209
- - spec/support/game_extended_with_textacular_and_custom_language.rb
210
- - spec/support/game_fail.rb
211
- - spec/support/game_fail_extended_with_textacular.rb
212
- - spec/support/not_there.rb
213
- - spec/support/textacular_web_comic.rb
214
- - spec/support/web_comic.rb
215
- - spec/support/web_comic_with_searchable.rb
216
- - spec/support/web_comic_with_searchable_name.rb
217
- - spec/support/web_comic_with_searchable_name_and_author.rb
218
- - spec/textacular_spec.rb
219
- - spec/textacular/full_text_indexer_spec.rb
220
- - spec/textacular/migration_generator_spec.rb
221
- - spec/textacular/searchable_spec.rb
222
- - spec/textacular/trigram_installer_spec.rb
208
+ test_files: []
@@ -1,8 +0,0 @@
1
- pool: 5
2
- timeout: 5000
3
- host: localhost
4
- adapter: postgresql
5
- username: postgres
6
- password: password
7
- database: textacular_test
8
- min_messages: ERROR
@@ -1,5 +0,0 @@
1
- database: textacular
2
- username: <%= ENV[ 'USER' ] %>
3
- pool: 5
4
- timeout: 5000
5
- adapter: postgresql
data/spec/spec_helper.rb DELETED
@@ -1,104 +0,0 @@
1
- require 'pry'
2
- require 'textacular'
3
- require 'database_cleaner'
4
- require 'yaml'
5
-
6
- config = YAML.load_file File.expand_path(File.dirname(__FILE__) + '/config.yml')
7
- ActiveRecord::Base.establish_connection config.merge(:adapter => :postgresql)
8
-
9
- # This file was generated by the `rspec --init` command. Conventionally, all
10
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
11
- # The generated `.rspec` file contains `--require spec_helper` which will cause this
12
- # file to always be loaded, without a need to explicitly require it in any files.
13
- #
14
- # Given that it is always loaded, you are encouraged to keep this file as
15
- # light-weight as possible. Requiring heavyweight dependencies from this file
16
- # will add to the boot time of your test suite on EVERY test run, even for an
17
- # individual file that may not need all of that loaded. Instead, consider making
18
- # a separate helper file that requires the additional dependencies and performs
19
- # the additional setup, and require it from the spec files that actually need it.
20
- #
21
- # The `.rspec` file also contains a few flags that are not defaults but that
22
- # users commonly want.
23
- #
24
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
25
- RSpec.configure do |config|
26
- # rspec-expectations config goes here. You can use an alternate
27
- # assertion/expectation library such as wrong or the stdlib/minitest
28
- # assertions if you prefer.
29
- config.expect_with :rspec do |expectations|
30
- # This option will default to `true` in RSpec 4. It makes the `description`
31
- # and `failure_message` of custom matchers include text for helper methods
32
- # defined using `chain`, e.g.:
33
- # be_bigger_than(2).and_smaller_than(4).description
34
- # # => "be bigger than 2 and smaller than 4"
35
- # ...rather than:
36
- # # => "be bigger than 2"
37
- expectations.include_chain_clauses_in_custom_matcher_descriptions = true
38
- end
39
-
40
- # rspec-mocks config goes here. You can use an alternate test double
41
- # library (such as bogus or mocha) by changing the `mock_with` option here.
42
- config.mock_with :rspec do |mocks|
43
- # Prevents you from mocking or stubbing a method that does not exist on
44
- # a real object. This is generally recommended, and will default to
45
- # `true` in RSpec 4.
46
- mocks.verify_partial_doubles = true
47
- end
48
-
49
- # These two settings work together to allow you to limit a spec run
50
- # to individual examples or groups you care about by tagging them with
51
- # `:focus` metadata. When nothing is tagged with `:focus`, all examples
52
- # get run.
53
- config.filter_run :focus
54
- config.run_all_when_everything_filtered = true
55
-
56
- # Limits the available syntax to the non-monkey patched syntax that is recommended.
57
- # For more details, see:
58
- # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
59
- # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
60
- # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
61
- config.disable_monkey_patching!
62
-
63
- # This setting enables warnings. It's recommended, but in some cases may
64
- # be too noisy due to issues in dependencies.
65
- config.warnings = true
66
-
67
- # Many RSpec users commonly either run the entire suite or an individual
68
- # file, and it's useful to allow more verbose output when running an
69
- # individual spec file.
70
- # if config.files_to_run.one?
71
- # Use the documentation formatter for detailed output,
72
- # unless a formatter has already been configured
73
- # (e.g. via a command-line flag).
74
- # config.default_formatter = 'doc'
75
- # end
76
-
77
- # Print the 10 slowest examples and example groups at the
78
- # end of the spec run, to help surface which specs are running
79
- # particularly slow.
80
- # config.profile_examples = 10
81
-
82
- # Run specs in random order to surface order dependencies. If you find an
83
- # order dependency and want to debug it, you can fix the order by providing
84
- # the seed, which is printed after each run.
85
- # --seed 1234
86
- config.order = :random
87
-
88
- # Seed global randomization in this process using the `--seed` CLI option.
89
- # Setting this allows you to use `--seed` to deterministically reproduce
90
- # test failures related to randomization by passing the same `--seed` value
91
- # as the one that triggered the failure.
92
- Kernel.srand config.seed
93
-
94
- config.before(:suite) do
95
- DatabaseCleaner.strategy = :transaction
96
- DatabaseCleaner.clean_with(:truncation)
97
- end
98
-
99
- config.around(:each) do |example|
100
- DatabaseCleaner.cleaning do
101
- example.run
102
- end
103
- end
104
- end
@@ -1,4 +0,0 @@
1
- class ARStandIn < ActiveRecord::Base;
2
- self.abstract_class = true
3
- extend Textacular
4
- end
@@ -1,7 +0,0 @@
1
- class Character < ActiveRecord::Base
2
- # string :name
3
- # string :description
4
- # integer :web_comic_id
5
-
6
- belongs_to :web_comic
7
- end
data/spec/support/game.rb DELETED
@@ -1,5 +0,0 @@
1
- class Game < ActiveRecord::Base
2
- # string :system
3
- # string :title
4
- # text :description
5
- end
@@ -1,5 +0,0 @@
1
- require 'support/game'
2
-
3
- class GameExtendedWithTextacular < Game
4
- extend Textacular
5
- end
@@ -1,7 +0,0 @@
1
- require 'support/game_extended_with_textacular'
2
-
3
- class GameExtendedWithTextacularAndCustomLanguage < GameExtendedWithTextacular
4
- def searchable_language
5
- 'spanish'
6
- end
7
- end
@@ -1,3 +0,0 @@
1
- require 'support/game'
2
-
3
- class GameFail < Game; end
@@ -1,5 +0,0 @@
1
- require 'support/game_fail'
2
-
3
- class GameFailExtendedWithTextacular < GameFail
4
- extend Textacular
5
- end
@@ -1,3 +0,0 @@
1
- require 'support/ar_stand_in'
2
-
3
- class NotThere < ARStandIn; end
@@ -1,7 +0,0 @@
1
- require 'support/ar_stand_in'
2
- require 'support/character'
3
-
4
- class TextacularWebComic < ARStandIn;
5
- has_many :characters, :foreign_key => :web_comic_id
6
- self.table_name = :web_comics
7
- end
@@ -1,7 +0,0 @@
1
- class WebComic < ActiveRecord::Base
2
- # string :name
3
- # string :author
4
- # integer :id
5
-
6
- has_many :characters
7
- end
@@ -1,6 +0,0 @@
1
- require 'textacular/searchable'
2
- require 'support/web_comic'
3
-
4
- class WebComicWithSearchable < WebComic
5
- extend Searchable
6
- end
@@ -1,6 +0,0 @@
1
- require 'textacular/searchable'
2
- require 'support/web_comic'
3
-
4
- class WebComicWithSearchableName < WebComic
5
- extend Searchable(:name)
6
- end
@@ -1,6 +0,0 @@
1
- require 'textacular/searchable'
2
- require 'support/web_comic'
3
-
4
- class WebComicWithSearchableNameAndAuthor < WebComic
5
- extend Searchable(:name, :author)
6
- end
@@ -1,69 +0,0 @@
1
- require 'support/web_comic_with_searchable_name'
2
- require 'support/web_comic_with_searchable_name_and_author'
3
-
4
- RSpec.describe Textacular::FullTextIndexer do
5
- context "with one specific field in a Searchable call" do
6
- it "generates the right SQL" do
7
- file_name = "web_comic_with_searchable_name_full_text_search"
8
- content = <<-MIGRATION
9
- class WebComicWithSearchableNameFullTextSearch < ActiveRecord::Migration
10
- def self.up
11
- execute(<<-SQL.strip)
12
- DROP index IF EXISTS web_comics_name_fts_idx;
13
- CREATE index web_comics_name_fts_idx
14
- ON web_comics
15
- USING gin(to_tsvector('english', "web_comics"."name"::text));
16
- SQL
17
- end
18
-
19
- def self.down
20
- execute(<<-SQL.strip)
21
- DROP index IF EXISTS web_comics_name_fts_idx;
22
- SQL
23
- end
24
- end
25
- MIGRATION
26
-
27
- generator = double(:migration_generator)
28
- expect(Textacular::MigrationGenerator).to receive(:new).with(file_name, content).and_return(generator)
29
- expect(generator).to receive(:generate_migration)
30
-
31
- Textacular::FullTextIndexer.new.generate_migration('WebComicWithSearchableName')
32
- end
33
- end
34
-
35
- context "with two specific fields in a Searchable call" do
36
- it "generates the right SQL" do
37
- file_name = "web_comic_with_searchable_name_and_author_full_text_search"
38
- content = <<-MIGRATION
39
- class WebComicWithSearchableNameAndAuthorFullTextSearch < ActiveRecord::Migration
40
- def self.up
41
- execute(<<-SQL.strip)
42
- DROP index IF EXISTS web_comics_name_fts_idx;
43
- CREATE index web_comics_name_fts_idx
44
- ON web_comics
45
- USING gin(to_tsvector('english', "web_comics"."name"::text));
46
- DROP index IF EXISTS web_comics_author_fts_idx;
47
- CREATE index web_comics_author_fts_idx
48
- ON web_comics
49
- USING gin(to_tsvector('english', "web_comics"."author"::text));
50
- SQL
51
- end
52
-
53
- def self.down
54
- execute(<<-SQL.strip)
55
- DROP index IF EXISTS web_comics_name_fts_idx;
56
- DROP index IF EXISTS web_comics_author_fts_idx;
57
- SQL
58
- end
59
- end
60
- MIGRATION
61
-
62
- generator = double(:migration_generator)
63
- expect(Textacular::MigrationGenerator).to receive(:new).with(file_name, content).and_return(generator)
64
- expect(generator).to receive(:generate_migration)
65
-
66
- Textacular::FullTextIndexer.new.generate_migration('WebComicWithSearchableNameAndAuthor')
67
- end
68
- end
69
- end
@@ -1,67 +0,0 @@
1
- RSpec.describe Textacular::MigrationGenerator do
2
- describe ".stream_output" do
3
- context "when Rails is not defined" do
4
- subject do
5
- Textacular::MigrationGenerator.new('filename', 'content')
6
- end
7
-
8
- it "points to STDOUT" do
9
- output_stream = nil
10
-
11
- subject.stream_output do |io|
12
- output_stream = io
13
- end
14
-
15
- expect(output_stream).to eq(STDOUT)
16
- end
17
- end
18
-
19
- context "when Rails is defined" do
20
- before do
21
- module ::Rails
22
- # Stub this out, sort of.
23
- def self.root
24
- File.join('.', 'fake_rails')
25
- end
26
- end
27
- end
28
-
29
- after do
30
- Object.send(:remove_const, :Rails)
31
- FileUtils.rm_rf(File.join('.', 'fake_rails'))
32
- end
33
-
34
- let(:now) do
35
- Time.now
36
- end
37
-
38
- subject do
39
- Textacular::MigrationGenerator.new('file_name', 'content')
40
- end
41
-
42
- it "points to a properly names migration file" do
43
- expected_file_name = "./fake_rails/db/migrate/#{now.strftime('%Y%m%d%H%M%S')}_file_name.rb"
44
-
45
- output_stream = nil
46
-
47
- subject.stream_output(now) do |io|
48
- output_stream = io
49
- end
50
-
51
- expect(output_stream.path).to eq(expected_file_name)
52
- end
53
- end
54
-
55
- it "generates the right SQL" do
56
- content = "content\n" #newline automatically added
57
- output = StringIO.new
58
-
59
- generator = Textacular::MigrationGenerator.new('file_name', content)
60
- generator.instance_variable_set(:@output_stream, output)
61
-
62
- generator.generate_migration
63
-
64
- expect(output.string).to eq(content)
65
- end
66
- end
67
- end
@@ -1,244 +0,0 @@
1
- require 'support/web_comic_with_searchable'
2
- require 'support/web_comic_with_searchable_name'
3
- require 'support/web_comic_with_searchable_name_and_author'
4
- require 'support/character'
5
-
6
- RSpec.describe "Searchable" do
7
- context "when extending an ActiveRecord::Base subclass" do
8
- context "with no parameters" do
9
- let!(:questionable_content) do
10
- WebComicWithSearchable.create(
11
- name: 'Questionable Content',
12
- author: 'Jeph Jaques',
13
- )
14
- end
15
-
16
- let!(:johnny_wander) do
17
- WebComicWithSearchable.create(
18
- name: 'Johnny Wander',
19
- author: 'Ananth & Yuko',
20
- )
21
- end
22
-
23
- let!(:dominic_deegan) do
24
- WebComicWithSearchable.create(
25
- name: 'Dominic Deegan',
26
- author: 'Mookie',
27
- )
28
- end
29
-
30
- let!(:penny_arcade) do
31
- WebComicWithSearchable.create(
32
- name: 'Penny Arcade',
33
- author: 'Tycho & Gabe',
34
- )
35
- end
36
-
37
- let!(:null) do
38
- WebComicWithSearchable.create(
39
- author: 'Foo',
40
- )
41
- end
42
-
43
- it "searches across all columns" do
44
- expect(
45
- WebComicWithSearchable.advanced_search("Penny")
46
- ).to eq([penny_arcade])
47
- expect(
48
- WebComicWithSearchable.advanced_search("Dominic")
49
- ).to eq([dominic_deegan])
50
- end
51
-
52
- it "ranks results, egen with NULL columns" do
53
- comic = WebComicWithSearchable.basic_search('Foo').first
54
- rank = comic.attributes.find { |key, value| key.to_s =~ /\Arank\d+\z/ }.last
55
-
56
- expect(rank).to be_present
57
- end
58
- end
59
-
60
- context "with one column as a parameter" do
61
- let!(:questionable_content) do
62
- WebComicWithSearchableName.create(
63
- name: 'Questionable Content',
64
- author: nil,
65
- )
66
- end
67
-
68
- let!(:johnny_wander) do
69
- WebComicWithSearchableName.create(
70
- name: 'Johnny Wander',
71
- author: 'Ananth & Yuko',
72
- )
73
- end
74
-
75
- let!(:dominic_deegan) do
76
- WebComicWithSearchableName.create(
77
- name: 'Dominic Deegan',
78
- author: 'Mookie',
79
- )
80
- end
81
-
82
- let!(:penny_arcade) do
83
- WebComicWithSearchableName.create(
84
- name: 'Penny Arcade',
85
- author: 'Tycho & Gabe',
86
- )
87
- end
88
-
89
- it "only searches across the given column" do
90
- expect(WebComicWithSearchableName.advanced_search("Penny")).to eq([penny_arcade])
91
-
92
- expect(WebComicWithSearchableName.advanced_search("Tycho")).to be_empty
93
- end
94
-
95
- describe "basic search" do # Uses plainto_tsquery
96
- ["hello \\", "tebow!" , "food &"].each do |search_term|
97
- it "works with interesting term \"#{search_term}\"" do
98
- expect(WebComicWithSearchableName.basic_search(search_term)).to be_empty
99
- end
100
- end
101
- end
102
-
103
- describe "advanced_search" do # Uses to_tsquery
104
- ["hello \\", "tebow!" , "food &"].each do |search_term|
105
- it "fails with interesting term \"#{search_term}\"" do
106
- expect {
107
- WebComicWithSearchableName.advanced_search(search_term).first
108
- }.to raise_error(ActiveRecord::StatementInvalid)
109
- end
110
- end
111
- it "searches with negation" do
112
- expect(WebComicWithSearchableName.advanced_search('foo & ! bar')).to be_empty
113
- end
114
- end
115
-
116
- describe "web search" do # Uses websearch_to_tsquery
117
- ["hello \\", "tebow!" , "food &"].each do |search_term|
118
- it "works with interesting term \"#{search_term}\"" do
119
- expect(WebComicWithSearchableName.web_search(search_term)).to be_empty
120
- end
121
- end
122
- end
123
-
124
- it "does fuzzy searching" do
125
- expect(
126
- WebComicWithSearchableName.fuzzy_search('Questio')
127
- ).to eq([questionable_content])
128
- end
129
-
130
- it "return a valid rank when fuzzy searching on NULL columns" do
131
- qcont_with_author = questionable_content.becomes(WebComicWithSearchableNameAndAuthor)
132
- search_result = WebComicWithSearchableNameAndAuthor.fuzzy_search('Questio')
133
- expect([qcont_with_author]).to eq(search_result)
134
- expect(search_result.first.attributes.find { |k, _| k[0..3] == 'rank' }.last).to be_truthy
135
- end
136
-
137
- it "defines :searchable_columns as private" do
138
- expect { WebComicWithSearchableName.searchable_columns }.to raise_error(NoMethodError)
139
-
140
- begin
141
- WebComicWithSearchableName.searchable_columns
142
- rescue NoMethodError => error
143
- expect(error.message).to match(/private method/)
144
- end
145
- end
146
-
147
- it "defines #indexable_columns which returns a write-proof Enumerable" do
148
- expect(WebComicWithSearchableName.indexable_columns).to be_an(Enumerator)
149
-
150
- expect {
151
- WebComicWithSearchableName.indexable_columns[0] = 'foo'
152
- }.to raise_error(NoMethodError)
153
- end
154
- end
155
-
156
- context "with two columns as parameters" do
157
- let!(:questionable_content) do
158
- WebComicWithSearchableNameAndAuthor.create(
159
- name: 'Questionable Content',
160
- author: 'Jeph Jaques',
161
- )
162
- end
163
-
164
- let!(:johnny_wander) do
165
- WebComicWithSearchableNameAndAuthor.create(
166
- name: 'Johnny Wander',
167
- author: 'Ananth & Yuko',
168
- )
169
- end
170
-
171
- let!(:dominic_deegan) do
172
- WebComicWithSearchableNameAndAuthor.create(
173
- name: 'Dominic Deegan',
174
- author: 'Mookie',
175
- )
176
- end
177
-
178
- let!(:penny_arcade) do
179
- WebComicWithSearchableNameAndAuthor.create(
180
- name: 'Penny Arcade',
181
- author: 'Tycho & Gabe',
182
- )
183
- end
184
-
185
- it "only searches across the given columns" do
186
- expect(
187
- WebComicWithSearchableNameAndAuthor.advanced_search("Penny")
188
- ).to eq([penny_arcade])
189
-
190
- expect(
191
- WebComicWithSearchableNameAndAuthor.advanced_search("Tycho")
192
- ).to eq([penny_arcade])
193
-
194
- expect(
195
- WebComicWithSearchableNameAndAuthor.web_search("Penny")
196
- ).to eq([penny_arcade])
197
-
198
- expect(
199
- WebComicWithSearchableNameAndAuthor.web_search("Tycho")
200
- ).to eq([penny_arcade])
201
- end
202
-
203
- it "allows includes" do
204
- expect(
205
- WebComicWithSearchableNameAndAuthor.includes(:characters).advanced_search("Penny")
206
- ).to eq([penny_arcade])
207
- end
208
- end
209
-
210
- context 'custom rank' do
211
- let!(:questionable_content) do
212
- WebComicWithSearchableName.create(
213
- name: 'Questionable Content',
214
- author: nil,
215
- )
216
- end
217
-
218
- it "is selected for search" do
219
- search_result = WebComicWithSearchableNameAndAuthor.search('Questionable Content', true, 'my_rank')
220
- expect(search_result.first.attributes['my_rank']).to be_truthy
221
- end
222
-
223
- it "is selected for basic_search" do
224
- search_result = WebComicWithSearchableNameAndAuthor.basic_search('Questionable Content', true, 'my_rank')
225
- expect(search_result.first.attributes['my_rank']).to be_truthy
226
- end
227
-
228
- it "is selected for advanced_search" do
229
- search_result = WebComicWithSearchableNameAndAuthor.advanced_search('Questionable Content', true, 'my_rank')
230
- expect(search_result.first.attributes['my_rank']).to be_truthy
231
- end
232
-
233
- it "is selected for fuzzy_search" do
234
- search_result = WebComicWithSearchableNameAndAuthor.fuzzy_search('Questionable Content', true, 'my_rank')
235
- expect(search_result.first.attributes['my_rank']).to be_truthy
236
- end
237
-
238
- it "is selected for web_search" do
239
- search_result = WebComicWithSearchableNameAndAuthor.web_search('Questionable Content', true, 'my_rank')
240
- expect(search_result.first.attributes['my_rank']).to be_truthy
241
- end
242
- end
243
- end
244
- end
@@ -1,24 +0,0 @@
1
- RSpec.describe "Textacular::TrigramInstaller" do
2
- let(:content) do
3
- <<-MIGRATION
4
- class InstallTrigram < ActiveRecord::Migration[5.0]
5
- def self.up
6
- ActiveRecord::Base.connection.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm;")
7
- end
8
-
9
- def self.down
10
- ActiveRecord::Base.connection.execute("DROP EXTENSION pg_trgm;")
11
- end
12
- end
13
- MIGRATION
14
- end
15
-
16
- it "generates a migration" do
17
- generator = double(:migration_generator)
18
-
19
- expect(Textacular::MigrationGenerator).to receive(:new).with('install_trigram', content).and_return(generator)
20
- expect(generator).to receive(:generate_migration)
21
-
22
- Textacular::TrigramInstaller.new.generate_migration
23
- end
24
- end
@@ -1,300 +0,0 @@
1
- # coding: utf-8
2
- # Above for Ruby 1.9 tests
3
-
4
- require 'support/ar_stand_in'
5
- require 'support/not_there'
6
- require 'support/textacular_web_comic'
7
- require 'support/game_extended_with_textacular'
8
- require 'support/game_extended_with_textacular_and_custom_language'
9
- require 'support/game_fail_extended_with_textacular'
10
-
11
- RSpec.describe Textacular do
12
- context "after extending ActiveRecord::Base" do
13
- it "doesn't break #respond_to?" do
14
- expect{ ARStandIn.respond_to?(:abstract_class?) }.to_not raise_error
15
- end
16
-
17
- it "doesn't break #respond_to? for table-less classes" do
18
- expect(NotThere.table_exists?).to be_falsey
19
- expect { NotThere.respond_to? :system }.to_not raise_error
20
- end
21
-
22
- it "doesn't break #method_missing" do
23
- expect { ARStandIn.random }.to raise_error(NoMethodError)
24
-
25
- begin
26
- ARStandIn.random
27
- rescue NoMethodError => error
28
- expect(error.message).to match(/undefined method `random'/)
29
- end
30
- end
31
-
32
- it "doesn't break #method_missing for table-less classes" do
33
- expect(NotThere.table_exists?).to be_falsey
34
-
35
- expect { NotThere.random }.to raise_error(NoMethodError)
36
-
37
- begin
38
- NotThere.random
39
- rescue NoMethodError => error
40
- expect(error.message).to match(/undefined method `random'/)
41
- end
42
- end
43
-
44
- context "when finding models based on searching a related model" do
45
- let(:webcomics_with_tall_characters) do
46
- [johnny_wander]
47
- end
48
-
49
- let(:webcomics_with_angry_characters) do
50
- [johnny_wander, penny_arcade, questionable_content]
51
- end
52
-
53
- let(:webcomics_with_crude_characters) do
54
- [penny_arcade, questionable_content]
55
- end
56
-
57
- let!(:johnny_wander) do
58
- TextacularWebComic.create(:name => "Johnny Wander", :author => "Ananth & Yuko").tap do |comic|
59
- comic.characters.create :name => 'Ananth', :description => 'Stubble! What is under that hat?!?'
60
- comic.characters.create :name => 'Yuko', :description => 'So... small. Carl Sagan haircut.'
61
- comic.characters.create :name => 'John', :description => 'Tall. Anger issues?'
62
- comic.characters.create :name => 'Cricket', :description => 'Chirrup!'
63
- end
64
- end
65
-
66
- let!(:questionable_content) do
67
- TextacularWebComic.create(:name => "Questionable Content", :author => "Jeph Jaques").tap do |comic|
68
- comic.characters.create :name => 'Martin', :description => 'the insecure protagonist'
69
- comic.characters.create :name => 'Faye', :description => 'a sarcastic barrista with anger management issues'
70
- comic.characters.create :name => 'Pintsize', :description => 'a crude AnthroPC'
71
- end
72
- end
73
-
74
- let!(:penny_arcade) do
75
- TextacularWebComic.create(:name => "Penny Arcade", :author => "Tycho & Gabe").tap do |comic|
76
- comic.characters.create :name => 'Gabe', :description => 'the simple one'
77
- comic.characters.create :name => 'Tycho', :description => 'the wordy one'
78
- comic.characters.create :name => 'Div', :description => 'a crude divx player with anger management issues'
79
- end
80
- end
81
-
82
- it "looks in the related model with nested searching syntax" do
83
- expect(
84
- TextacularWebComic.joins(:characters).advanced_search(
85
- :characters => {:description => 'tall'}
86
- )
87
- ).to eq(webcomics_with_tall_characters)
88
-
89
- expect(
90
- TextacularWebComic.joins(:characters).advanced_search(
91
- :characters => {:description => 'anger'}
92
- ).sort
93
- ).to eq(webcomics_with_angry_characters.sort)
94
-
95
- expect(
96
- TextacularWebComic.joins(:characters).advanced_search(
97
- :characters => {:description => 'crude'}
98
- ).sort
99
- ).to eq(webcomics_with_crude_characters.sort)
100
- end
101
- end
102
- end
103
-
104
- context "after extending an ActiveRecord::Base subclass" do
105
- context "when the DB connection is unavailable" do
106
- before do
107
- GameFailExtendedWithTextacular.establish_connection({:adapter => :postgresql, :database =>'unavailable', :username=>'bad', :pool=>5, :timeout=>5000}) rescue nil
108
- end
109
-
110
- it "doesn't break respond_to?" do
111
- expect { GameFailExtendedWithTextacular.respond_to?(:advanced_search) }.to_not raise_error
112
- end
113
- end
114
-
115
- context "when the DB connection is available" do
116
- let!(:zelda) do
117
- GameExtendedWithTextacular.create(
118
- :system => "NES",
119
- :title => "Legend of Zelda",
120
- :description => "A Link to the Past."
121
- )
122
- end
123
-
124
- let!(:mario) do
125
- GameExtendedWithTextacular.create(
126
- :system => "NES",
127
- :title => "Super Mario Bros.",
128
- :description => "The original platformer."
129
- )
130
- end
131
-
132
- let!(:sonic) do
133
- GameExtendedWithTextacular.create(
134
- :system => "Genesis",
135
- :title => "Sonic the Hedgehog",
136
- :description => "Spiky."
137
- )
138
- end
139
-
140
- let!(:donkey_kong) do
141
- GameExtendedWithTextacular.create(
142
- :system => "SNES",
143
- :title => "Diddy's Kong Quest",
144
- :description => "Donkey Kong Country 2"
145
- )
146
- end
147
-
148
- let!(:mega_man) do
149
- GameExtendedWithTextacular.create(
150
- :system => nil,
151
- :title => "Mega Man",
152
- :description => "Beware Dr. Brain"
153
- )
154
- end
155
-
156
- let!(:sf_nes) do
157
- GameExtendedWithTextacular.create(
158
- :system => "SNES",
159
- :title => "Street Fighter 2",
160
- :description => "Yoga Flame!"
161
- )
162
- end
163
-
164
- let!(:sf_genesis) do
165
- GameExtendedWithTextacular.create(
166
- :system => "Genesis",
167
- :title => "Street Fighter 2",
168
- :description => "Yoga Flame!"
169
- )
170
- end
171
-
172
- let!(:takun) do
173
- GameExtendedWithTextacular.create(
174
- :system => "Saturn",
175
- :title => "Magical Tarurūto-kun",
176
- :description => "カッコイイ!"
177
- )
178
- end
179
-
180
- it "defines a #search method" do
181
- expect(GameExtendedWithTextacular).to respond_to(:search)
182
- end
183
-
184
- describe "#fuzzy_search" do
185
- it 'searches non-text columns' do
186
- expect(GameExtendedWithTextacular.fuzzy_search(id: mario.id)
187
- ).to eq([mario])
188
- end
189
- end
190
-
191
- describe "#advanced_search" do
192
- context "with a String argument" do
193
- it "searches across all :string columns (if not indexes have been specified)" do
194
- expect(
195
- GameExtendedWithTextacular.advanced_search("Mario")
196
- ).to eq([mario])
197
-
198
- expect(
199
- GameExtendedWithTextacular.advanced_search("NES").to_set
200
- ).to eq(Set.new([mario, zelda]))
201
- end
202
-
203
- it "works if a query has an apostrophe" do
204
- expect(GameExtendedWithTextacular.advanced_search("Diddy's")).to eq([donkey_kong])
205
- end
206
-
207
- it "works if the query contains whitespace" do
208
- expect(GameExtendedWithTextacular.advanced_search("Mega Man")).to eq([mega_man])
209
- end
210
-
211
- it "works if the query contains an accent" do
212
- expect(GameExtendedWithTextacular.advanced_search("Tarurūto-kun")).to eq([takun])
213
- end
214
-
215
- it "searches across records with NULL values" do
216
- expect(GameExtendedWithTextacular.advanced_search("Mega")).to eq([mega_man])
217
- end
218
-
219
- it "scopes consecutively" do
220
- expect(
221
- GameExtendedWithTextacular.advanced_search("Genesis").advanced_search("Street Fighter")
222
- ).to eq([sf_genesis])
223
- end
224
- end
225
-
226
- context "with a Hash argument" do
227
- it "searches across the given columns" do
228
- expect(
229
- GameExtendedWithTextacular.advanced_search(:title => 'NES')
230
- ).to be_empty
231
- expect(
232
- GameExtendedWithTextacular.advanced_search(:system => "Mario")
233
- ).to be_empty
234
- expect(
235
- GameExtendedWithTextacular.advanced_search(:system => "NES", :title => "Sonic")
236
- ).to be_empty
237
-
238
- expect(
239
- GameExtendedWithTextacular.advanced_search(:title => "Mario")
240
- ).to eq([mario])
241
-
242
- expect(
243
- GameExtendedWithTextacular.advanced_search(:system => "NES").size
244
- ).to eq(2)
245
-
246
- expect(
247
- GameExtendedWithTextacular.advanced_search(:system => "NES", :title => "Zelda")
248
- ).to eq([zelda])
249
- expect(
250
- GameExtendedWithTextacular.advanced_search(:title => "Mega")
251
- ).to eq([mega_man])
252
- end
253
-
254
- it "scopes consecutively" do
255
- expect(
256
- GameExtendedWithTextacular
257
- .advanced_search(:system => "Genesis")
258
- .advanced_search(:title => "Street Fighter")
259
- ).to eq([sf_genesis])
260
- end
261
-
262
- it "casts non-string columns as text" do
263
- expect(
264
- GameExtendedWithTextacular.advanced_search(:id => mario.id)
265
- ).to eq([mario])
266
- end
267
- end
268
-
269
- context "after selecting columns to return" do
270
- it "doesn't fetch extra columns" do
271
- expect {
272
- GameExtendedWithTextacular.select(:title).advanced_search("Mario").first.system
273
- }.to raise_error(ActiveModel::MissingAttributeError)
274
- end
275
- end
276
-
277
- context "after setting a custom language" do
278
- let!(:harry_potter_7) do
279
- GameExtendedWithTextacularAndCustomLanguage.create(
280
- :system => "PS3",
281
- :title => "Harry Potter & the Deathly Hallows"
282
- )
283
- end
284
-
285
- it "finds results" do
286
- expect(
287
- GameExtendedWithTextacularAndCustomLanguage.advanced_search(title: "harry")
288
- ).to eq([harry_potter_7])
289
- end
290
- end
291
- end
292
-
293
- describe "#fuzzy_search" do
294
- it "works if column contains multiple space delimited strings" do
295
- expect(GameExtendedWithTextacular.fuzzy_search(title: 'mar')).to eq([mario])
296
- end
297
- end
298
- end
299
- end
300
- end