pg-searchable 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.editorconfig +5 -0
- data/.github/workflows/test.yml +32 -0
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +167 -0
- data/README.md +56 -0
- data/bin/local-docker-testing +3 -0
- data/config.ru +9 -0
- data/docker-compose.test.yml +10 -0
- data/lib/pg-searchable/model.rb +109 -0
- data/lib/pg-searchable/version.rb +5 -0
- data/lib/pg-searchable.rb +4 -0
- data/pg-searchable.gemspec +11 -0
- metadata +55 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: eaf2e17acef704a8fd936d20b9716dace4e9f4319a26f8d72e4f4a79fa515bde
|
|
4
|
+
data.tar.gz: 8153451549b6fd494227f9c01377b0021241026188509778b2c3c430b087fa23
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 70964f08dad60214f6d87f7f5f2497ce7118ad92d3ced7df0d79bcf31435bb45d9d269ff096e700d27c840df4b67383435b1ef96978e6d95429756d9d5ab6e99
|
|
7
|
+
data.tar.gz: 4904c0b55c7c847f15a1f2b2097a684a577c3ae28d1380bd9fed5f290db3fc899e5b2e8fde7f26212e5a13921a1fb05228213e272683589ccf15e05718d157a3
|
data/.editorconfig
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on: [push]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
rspec-test:
|
|
7
|
+
name: RSpec
|
|
8
|
+
runs-on: ubuntu-18.04
|
|
9
|
+
services:
|
|
10
|
+
postgres:
|
|
11
|
+
image: postgres:12
|
|
12
|
+
env:
|
|
13
|
+
POSTGRES_USER: postgres
|
|
14
|
+
POSTGRES_PASSWORD: postgres
|
|
15
|
+
POSTGRES_DB: postgres_test
|
|
16
|
+
ports:
|
|
17
|
+
- 5432:5432
|
|
18
|
+
# needed because the postgres container does not provide a healthcheck
|
|
19
|
+
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v1
|
|
22
|
+
- uses: actions/setup-ruby@v1
|
|
23
|
+
with:
|
|
24
|
+
ruby-version: 2.5
|
|
25
|
+
- name: Install postgres client
|
|
26
|
+
run: sudo apt-get install libpq-dev
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: |
|
|
29
|
+
gem install bundler
|
|
30
|
+
bundler install
|
|
31
|
+
- name: Run tests
|
|
32
|
+
run: bundle exec rspec
|
data/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
spec/log
|
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
GEM
|
|
2
|
+
remote: https://rubygems.org/
|
|
3
|
+
specs:
|
|
4
|
+
actioncable (6.0.2.2)
|
|
5
|
+
actionpack (= 6.0.2.2)
|
|
6
|
+
nio4r (~> 2.0)
|
|
7
|
+
websocket-driver (>= 0.6.1)
|
|
8
|
+
actionmailbox (6.0.2.2)
|
|
9
|
+
actionpack (= 6.0.2.2)
|
|
10
|
+
activejob (= 6.0.2.2)
|
|
11
|
+
activerecord (= 6.0.2.2)
|
|
12
|
+
activestorage (= 6.0.2.2)
|
|
13
|
+
activesupport (= 6.0.2.2)
|
|
14
|
+
mail (>= 2.7.1)
|
|
15
|
+
actionmailer (6.0.2.2)
|
|
16
|
+
actionpack (= 6.0.2.2)
|
|
17
|
+
actionview (= 6.0.2.2)
|
|
18
|
+
activejob (= 6.0.2.2)
|
|
19
|
+
mail (~> 2.5, >= 2.5.4)
|
|
20
|
+
rails-dom-testing (~> 2.0)
|
|
21
|
+
actionpack (6.0.2.2)
|
|
22
|
+
actionview (= 6.0.2.2)
|
|
23
|
+
activesupport (= 6.0.2.2)
|
|
24
|
+
rack (~> 2.0, >= 2.0.8)
|
|
25
|
+
rack-test (>= 0.6.3)
|
|
26
|
+
rails-dom-testing (~> 2.0)
|
|
27
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
28
|
+
actiontext (6.0.2.2)
|
|
29
|
+
actionpack (= 6.0.2.2)
|
|
30
|
+
activerecord (= 6.0.2.2)
|
|
31
|
+
activestorage (= 6.0.2.2)
|
|
32
|
+
activesupport (= 6.0.2.2)
|
|
33
|
+
nokogiri (>= 1.8.5)
|
|
34
|
+
actionview (6.0.2.2)
|
|
35
|
+
activesupport (= 6.0.2.2)
|
|
36
|
+
builder (~> 3.1)
|
|
37
|
+
erubi (~> 1.4)
|
|
38
|
+
rails-dom-testing (~> 2.0)
|
|
39
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
40
|
+
activejob (6.0.2.2)
|
|
41
|
+
activesupport (= 6.0.2.2)
|
|
42
|
+
globalid (>= 0.3.6)
|
|
43
|
+
activemodel (6.0.2.2)
|
|
44
|
+
activesupport (= 6.0.2.2)
|
|
45
|
+
activerecord (6.0.2.2)
|
|
46
|
+
activemodel (= 6.0.2.2)
|
|
47
|
+
activesupport (= 6.0.2.2)
|
|
48
|
+
activestorage (6.0.2.2)
|
|
49
|
+
actionpack (= 6.0.2.2)
|
|
50
|
+
activejob (= 6.0.2.2)
|
|
51
|
+
activerecord (= 6.0.2.2)
|
|
52
|
+
marcel (~> 0.3.1)
|
|
53
|
+
activesupport (6.0.2.2)
|
|
54
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
55
|
+
i18n (>= 0.7, < 2)
|
|
56
|
+
minitest (~> 5.1)
|
|
57
|
+
tzinfo (~> 1.1)
|
|
58
|
+
zeitwerk (~> 2.2)
|
|
59
|
+
builder (3.2.4)
|
|
60
|
+
combustion (1.1.2)
|
|
61
|
+
activesupport (>= 3.0.0)
|
|
62
|
+
railties (>= 3.0.0)
|
|
63
|
+
thor (>= 0.14.6)
|
|
64
|
+
concurrent-ruby (1.1.6)
|
|
65
|
+
crass (1.0.6)
|
|
66
|
+
diff-lcs (1.3)
|
|
67
|
+
erubi (1.9.0)
|
|
68
|
+
globalid (0.4.2)
|
|
69
|
+
activesupport (>= 4.2.0)
|
|
70
|
+
i18n (1.8.2)
|
|
71
|
+
concurrent-ruby (~> 1.0)
|
|
72
|
+
loofah (2.4.0)
|
|
73
|
+
crass (~> 1.0.2)
|
|
74
|
+
nokogiri (>= 1.5.9)
|
|
75
|
+
mail (2.7.1)
|
|
76
|
+
mini_mime (>= 0.1.1)
|
|
77
|
+
marcel (0.3.3)
|
|
78
|
+
mimemagic (~> 0.3.2)
|
|
79
|
+
method_source (1.0.0)
|
|
80
|
+
mimemagic (0.3.4)
|
|
81
|
+
mini_mime (1.0.2)
|
|
82
|
+
mini_portile2 (2.4.0)
|
|
83
|
+
minitest (5.14.0)
|
|
84
|
+
nio4r (2.5.2)
|
|
85
|
+
nokogiri (1.10.9)
|
|
86
|
+
mini_portile2 (~> 2.4.0)
|
|
87
|
+
pg (1.2.3)
|
|
88
|
+
rack (2.2.2)
|
|
89
|
+
rack-test (1.1.0)
|
|
90
|
+
rack (>= 1.0, < 3)
|
|
91
|
+
rails (6.0.2.2)
|
|
92
|
+
actioncable (= 6.0.2.2)
|
|
93
|
+
actionmailbox (= 6.0.2.2)
|
|
94
|
+
actionmailer (= 6.0.2.2)
|
|
95
|
+
actionpack (= 6.0.2.2)
|
|
96
|
+
actiontext (= 6.0.2.2)
|
|
97
|
+
actionview (= 6.0.2.2)
|
|
98
|
+
activejob (= 6.0.2.2)
|
|
99
|
+
activemodel (= 6.0.2.2)
|
|
100
|
+
activerecord (= 6.0.2.2)
|
|
101
|
+
activestorage (= 6.0.2.2)
|
|
102
|
+
activesupport (= 6.0.2.2)
|
|
103
|
+
bundler (>= 1.3.0)
|
|
104
|
+
railties (= 6.0.2.2)
|
|
105
|
+
sprockets-rails (>= 2.0.0)
|
|
106
|
+
rails-dom-testing (2.0.3)
|
|
107
|
+
activesupport (>= 4.2.0)
|
|
108
|
+
nokogiri (>= 1.6)
|
|
109
|
+
rails-html-sanitizer (1.3.0)
|
|
110
|
+
loofah (~> 2.3)
|
|
111
|
+
railties (6.0.2.2)
|
|
112
|
+
actionpack (= 6.0.2.2)
|
|
113
|
+
activesupport (= 6.0.2.2)
|
|
114
|
+
method_source
|
|
115
|
+
rake (>= 0.8.7)
|
|
116
|
+
thor (>= 0.20.3, < 2.0)
|
|
117
|
+
rake (13.0.1)
|
|
118
|
+
rspec (3.9.0)
|
|
119
|
+
rspec-core (~> 3.9.0)
|
|
120
|
+
rspec-expectations (~> 3.9.0)
|
|
121
|
+
rspec-mocks (~> 3.9.0)
|
|
122
|
+
rspec-core (3.9.1)
|
|
123
|
+
rspec-support (~> 3.9.1)
|
|
124
|
+
rspec-expectations (3.9.1)
|
|
125
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
126
|
+
rspec-support (~> 3.9.0)
|
|
127
|
+
rspec-mocks (3.9.1)
|
|
128
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
129
|
+
rspec-support (~> 3.9.0)
|
|
130
|
+
rspec-rails (3.9.1)
|
|
131
|
+
actionpack (>= 3.0)
|
|
132
|
+
activesupport (>= 3.0)
|
|
133
|
+
railties (>= 3.0)
|
|
134
|
+
rspec-core (~> 3.9.0)
|
|
135
|
+
rspec-expectations (~> 3.9.0)
|
|
136
|
+
rspec-mocks (~> 3.9.0)
|
|
137
|
+
rspec-support (~> 3.9.0)
|
|
138
|
+
rspec-support (3.9.2)
|
|
139
|
+
sprockets (4.0.0)
|
|
140
|
+
concurrent-ruby (~> 1.0)
|
|
141
|
+
rack (> 1, < 3)
|
|
142
|
+
sprockets-rails (3.2.1)
|
|
143
|
+
actionpack (>= 4.0)
|
|
144
|
+
activesupport (>= 4.0)
|
|
145
|
+
sprockets (>= 3.0.0)
|
|
146
|
+
thor (1.0.1)
|
|
147
|
+
thread_safe (0.3.6)
|
|
148
|
+
tzinfo (1.2.6)
|
|
149
|
+
thread_safe (~> 0.1)
|
|
150
|
+
websocket-driver (0.7.1)
|
|
151
|
+
websocket-extensions (>= 0.1.0)
|
|
152
|
+
websocket-extensions (0.1.4)
|
|
153
|
+
zeitwerk (2.3.0)
|
|
154
|
+
|
|
155
|
+
PLATFORMS
|
|
156
|
+
ruby
|
|
157
|
+
|
|
158
|
+
DEPENDENCIES
|
|
159
|
+
combustion (~> 1.1)
|
|
160
|
+
pg
|
|
161
|
+
rails
|
|
162
|
+
rake
|
|
163
|
+
rspec
|
|
164
|
+
rspec-rails
|
|
165
|
+
|
|
166
|
+
BUNDLED WITH
|
|
167
|
+
2.1.2
|
data/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
## pg-searchable
|
|
2
|
+
|
|
3
|
+
pg-searchable is a quick way to make Postgres columns full-text searchable in Rails models, with support for composing complex OR/AND queries.
|
|
4
|
+
|
|
5
|
+
If all you need is a straightforward search with trigram/multi-column support, [pg_search](https://github.com/Casecommons/pg_search) is probably a better fit.
|
|
6
|
+
|
|
7
|
+
### Install
|
|
8
|
+
|
|
9
|
+
`gem "pg-searchable"`
|
|
10
|
+
|
|
11
|
+
### Setup
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
class Product < ActiveRecord::Base
|
|
15
|
+
include PgSearchable::Model
|
|
16
|
+
searchable_column :name
|
|
17
|
+
end
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Usage
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
# Add test data
|
|
24
|
+
sour_bread = Product.create!(name: "Sour bread")
|
|
25
|
+
italian_pasta = Product.create!(name: "Italian pasta")
|
|
26
|
+
rye_bread = Product.create!(name: "Rye bread")
|
|
27
|
+
sushi = Product.create!(name: "Sour Sushi Itamae")
|
|
28
|
+
|
|
29
|
+
# Basic options
|
|
30
|
+
bread_condition = Product.search_name("bread")
|
|
31
|
+
expect(Product.where(bread_condition)).to match_array [sour_bread, rye_bread]
|
|
32
|
+
|
|
33
|
+
ita_condition = Product.search_name("ita", prefix: true)
|
|
34
|
+
expect(Product.where(ita_condition)).to match_array [italian_pasta, sushi]
|
|
35
|
+
|
|
36
|
+
nonsour_condition = Product.search_name("!sour")
|
|
37
|
+
expect(Product.where(nonsour_condition)).to match_array [italian_pasta, rye_bread]
|
|
38
|
+
|
|
39
|
+
# Composing searches
|
|
40
|
+
non_sour_breads = Product.where(bread_condition.and(nonsour_condition))
|
|
41
|
+
expect(non_sour_breads).to match_array [rye_bread]
|
|
42
|
+
|
|
43
|
+
nonsour_or_ita = Product.where(nonsour_condition.or(ita_condition))
|
|
44
|
+
expect(nonsour_or_ita).to match_array [rye_bread, italian_pasta, sushi]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Development
|
|
48
|
+
|
|
49
|
+
**Testing locally:**
|
|
50
|
+
|
|
51
|
+
1. Start local Postgres: `bin/local-docker-testing`
|
|
52
|
+
2. Run tests: `bundle exec rspec`
|
|
53
|
+
|
|
54
|
+
### Credits
|
|
55
|
+
|
|
56
|
+
pg_search provided much of the inspiration and basis
|
data/config.ru
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/concern'
|
|
4
|
+
|
|
5
|
+
module PgSearchable
|
|
6
|
+
module Model
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def searchable_column(column_name)
|
|
11
|
+
name = "search_#{column_name.to_s}"
|
|
12
|
+
define_singleton_method(name) do |*args|
|
|
13
|
+
Searcher.new(self).exec(column_name, *args)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Searcher
|
|
19
|
+
def initialize(model)
|
|
20
|
+
@model = model
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def dictionary
|
|
24
|
+
Arel::Nodes.build_quoted(:simple)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def exec(column_name, query, raw:false, prefix:false)
|
|
28
|
+
column_ref = "#{@model.quoted_table_name}.#{column_name}"
|
|
29
|
+
|
|
30
|
+
vector = Arel::Nodes::NamedFunction.new(
|
|
31
|
+
"to_tsvector",
|
|
32
|
+
[dictionary, Arel.sql(column_ref)]
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
query = Builder.tsquery(query, prefix: prefix, raw: raw)
|
|
36
|
+
|
|
37
|
+
condition = Arel::Nodes::Grouping.new(
|
|
38
|
+
Arel::Nodes::InfixOperation.new("@@", vector, query)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
condition
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module Builder
|
|
46
|
+
|
|
47
|
+
DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’]/.freeze
|
|
48
|
+
|
|
49
|
+
def self.dictionary
|
|
50
|
+
Arel::Nodes.build_quoted(:simple)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.normalize(x)
|
|
54
|
+
x # TODO
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.tsquery_for_terms(terms, prefix:)
|
|
58
|
+
terms = terms.map { |term| tsquery_term(term, prefix: prefix) }
|
|
59
|
+
terms.inject do |memo, term|
|
|
60
|
+
term_anded = Arel::Nodes::InfixOperation.new("||", Arel::Nodes.build_quoted(" & "), term)
|
|
61
|
+
Arel::Nodes::InfixOperation.new("||", memo, term_anded)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.tsquery_term(unsanitized_term, prefix:)
|
|
66
|
+
negated = false
|
|
67
|
+
|
|
68
|
+
if unsanitized_term.start_with?("!")
|
|
69
|
+
unsanitized_term[0] = ''
|
|
70
|
+
negated = true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
sanitized_term = unsanitized_term.gsub(DISALLOWED_TSQUERY_CHARACTERS, " ")
|
|
74
|
+
tsquery_expression(sanitized_term, negated: negated, prefix: prefix)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# After this, the SQL expression evaluates to a string containing the term surrounded by single-quotes.
|
|
78
|
+
# If :prefix is true, then the term will have :* appended to the end.
|
|
79
|
+
# If :negated is true, then the term will have ! prepended to the front.
|
|
80
|
+
def self.tsquery_expression(term, negated:, prefix:)
|
|
81
|
+
terms = [
|
|
82
|
+
(Arel::Nodes.build_quoted('!') if negated),
|
|
83
|
+
Arel::Nodes.build_quoted("' "),
|
|
84
|
+
Arel::Nodes.build_quoted(term),
|
|
85
|
+
Arel::Nodes.build_quoted(" '"),
|
|
86
|
+
(Arel::Nodes.build_quoted(":*") if prefix)
|
|
87
|
+
].compact
|
|
88
|
+
|
|
89
|
+
terms.inject do |memo, term|
|
|
90
|
+
Arel::Nodes::InfixOperation.new("||", memo, Arel::Nodes.build_quoted(term))
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.tsquery(query, prefix:, raw:)
|
|
95
|
+
query_terms = query.split(" ").compact
|
|
96
|
+
tsq =
|
|
97
|
+
if query.blank?
|
|
98
|
+
Arel::Nodes.build_quoted("")
|
|
99
|
+
elsif raw
|
|
100
|
+
Arel::Nodes.build_quoted(query)
|
|
101
|
+
else
|
|
102
|
+
tsquery_for_terms(query_terms, prefix: prefix)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
Arel::Nodes::NamedFunction.new("to_tsquery", [dictionary, tsq])
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require_relative "lib/pg-searchable/version"
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |gem|
|
|
4
|
+
gem.authors = ["wyozi"]
|
|
5
|
+
gem.name = "pg-searchable"
|
|
6
|
+
gem.summary = "Simple full-text searching for Rails"
|
|
7
|
+
|
|
8
|
+
gem.files = `git ls-files | grep -Ev '^(spec)'`.split("\n")
|
|
9
|
+
gem.version = PgSearchable::VERSION
|
|
10
|
+
gem.required_ruby_version = ">= 2.5.0"
|
|
11
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: pg-searchable
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- wyozi
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2020-03-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description:
|
|
14
|
+
email:
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- ".editorconfig"
|
|
20
|
+
- ".github/workflows/test.yml"
|
|
21
|
+
- ".gitignore"
|
|
22
|
+
- ".rspec"
|
|
23
|
+
- Gemfile
|
|
24
|
+
- Gemfile.lock
|
|
25
|
+
- README.md
|
|
26
|
+
- bin/local-docker-testing
|
|
27
|
+
- config.ru
|
|
28
|
+
- docker-compose.test.yml
|
|
29
|
+
- lib/pg-searchable.rb
|
|
30
|
+
- lib/pg-searchable/model.rb
|
|
31
|
+
- lib/pg-searchable/version.rb
|
|
32
|
+
- pg-searchable.gemspec
|
|
33
|
+
homepage:
|
|
34
|
+
licenses: []
|
|
35
|
+
metadata: {}
|
|
36
|
+
post_install_message:
|
|
37
|
+
rdoc_options: []
|
|
38
|
+
require_paths:
|
|
39
|
+
- lib
|
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
41
|
+
requirements:
|
|
42
|
+
- - ">="
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: 2.5.0
|
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '0'
|
|
50
|
+
requirements: []
|
|
51
|
+
rubygems_version: 3.1.2
|
|
52
|
+
signing_key:
|
|
53
|
+
specification_version: 4
|
|
54
|
+
summary: Simple full-text searching for Rails
|
|
55
|
+
test_files: []
|