searchable-by 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/searchable_by.rb +49 -28
- data/searchable-by.gemspec +1 -1
- data/spec/searchable_by_spec.rb +6 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c1c5cfdea465a6a9ff73159a062558c5475889d27e0119e81212bb8bb37bbc1
|
4
|
+
data.tar.gz: c77c158c890b37b67d1243c93b4d5c982731b55fb48d284ecc404de58142da26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ba79022ae7056fc1cd6f6ef7af109ed6e0a7228601241ac2bc8f501818b6f487a6fd929f77bc3ce0dba9956b879def3dc705159c3696246d4653e3197f3d71f
|
7
|
+
data.tar.gz: c023dff5eb1bb4685dcfe7d10b7164242a090a930189ccc1b336c6de3437ab05d1de0dc90d13f52412e8e15b16a0a1386d5dacb289b208e00eefca5eab0cc2f7
|
data/Gemfile.lock
CHANGED
data/lib/searchable_by.rb
CHANGED
@@ -3,24 +3,42 @@ require 'shellwords'
|
|
3
3
|
|
4
4
|
module ActiveRecord
|
5
5
|
module SearchableBy
|
6
|
-
class
|
6
|
+
class Column
|
7
|
+
attr_reader :attr, :type
|
8
|
+
attr_accessor :node
|
9
|
+
|
10
|
+
def initialize(attr, type: :string)
|
11
|
+
@attr = attr
|
12
|
+
@type = type.to_sym
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Config
|
17
|
+
attr_reader :columns, :scoping
|
18
|
+
attr_accessor :max_terms
|
19
|
+
|
7
20
|
def initialize
|
8
|
-
|
21
|
+
@columns = []
|
22
|
+
@max_terms = 5
|
9
23
|
scope { all }
|
10
24
|
end
|
11
25
|
|
26
|
+
def initialize_copy(other)
|
27
|
+
@columns = other.columns.dup
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
12
31
|
def column(*attrs, &block)
|
13
32
|
opts = attrs.extract_options!
|
14
|
-
cols = self[:columns]
|
15
33
|
attrs.each do |attr|
|
16
|
-
|
34
|
+
columns.push Column.new(attr, opts)
|
17
35
|
end
|
18
|
-
|
19
|
-
|
36
|
+
columns.push Column.new(block, opts) if block
|
37
|
+
columns
|
20
38
|
end
|
21
39
|
|
22
40
|
def scope(&block)
|
23
|
-
|
41
|
+
@scoping = block
|
24
42
|
end
|
25
43
|
end
|
26
44
|
|
@@ -32,27 +50,30 @@ module ActiveRecord
|
|
32
50
|
values
|
33
51
|
end
|
34
52
|
|
35
|
-
def self.build_clauses(
|
53
|
+
def self.build_clauses(columns, values)
|
36
54
|
clauses = values.map do |value|
|
37
55
|
negate = value[0] == '-'
|
38
56
|
value.slice!(0) if negate || value[0] == '+'
|
39
57
|
|
40
|
-
|
41
|
-
build_condition(
|
42
|
-
end
|
43
|
-
|
58
|
+
grouping = columns.map do |column|
|
59
|
+
build_condition(column, value)
|
60
|
+
end
|
61
|
+
grouping.compact!
|
62
|
+
next if grouping.empty?
|
44
63
|
|
45
|
-
|
64
|
+
clause = grouping.inject(&:or)
|
65
|
+
clause = clause.not if negate
|
66
|
+
clause
|
46
67
|
end
|
47
68
|
clauses.compact!
|
48
69
|
clauses
|
49
70
|
end
|
50
71
|
|
51
|
-
def self.build_condition(
|
52
|
-
case
|
72
|
+
def self.build_condition(column, value)
|
73
|
+
case column.type
|
53
74
|
when :int, :integer
|
54
75
|
begin
|
55
|
-
|
76
|
+
column.node.eq(Integer(value))
|
56
77
|
rescue ArgumentError
|
57
78
|
nil
|
58
79
|
end
|
@@ -60,7 +81,7 @@ module ActiveRecord
|
|
60
81
|
value = value.dup
|
61
82
|
value.gsub!('%', '\%')
|
62
83
|
value.gsub!('_', '\_')
|
63
|
-
|
84
|
+
column.node.matches("%#{value}%")
|
64
85
|
end
|
65
86
|
end
|
66
87
|
|
@@ -72,35 +93,35 @@ module ActiveRecord
|
|
72
93
|
end
|
73
94
|
|
74
95
|
def inherited(base) # :nodoc:
|
75
|
-
base._searchable_by_config = _searchable_by_config.
|
96
|
+
base._searchable_by_config = _searchable_by_config.dup
|
76
97
|
super
|
77
98
|
end
|
78
99
|
|
79
100
|
def searchable_by(max_terms: 5, &block)
|
80
101
|
_searchable_by_config.instance_eval(&block)
|
81
|
-
_searchable_by_config
|
102
|
+
_searchable_by_config.max_terms = max_terms if max_terms
|
82
103
|
end
|
83
104
|
|
84
105
|
# @param [String] query the search query
|
85
106
|
# @return [ActiveRecord::Relation] the scoped relation
|
86
107
|
def search_by(query)
|
87
|
-
columns = _searchable_by_config
|
108
|
+
columns = _searchable_by_config.columns
|
88
109
|
return all if columns.empty?
|
89
110
|
|
90
|
-
values = SearchableBy.norm_values(query).first(_searchable_by_config
|
111
|
+
values = SearchableBy.norm_values(query).first(_searchable_by_config.max_terms)
|
91
112
|
return all if values.empty?
|
92
113
|
|
93
|
-
|
94
|
-
|
95
|
-
opts.merge(rel: rel)
|
114
|
+
columns.each do |col|
|
115
|
+
col.node ||= col.attr.is_a?(Proc) ? col.attr.call : arel_table[col.attr]
|
96
116
|
end
|
97
|
-
clauses = SearchableBy.build_clauses(
|
117
|
+
clauses = SearchableBy.build_clauses(columns, values)
|
98
118
|
return all if clauses.empty?
|
99
119
|
|
100
|
-
scope = instance_exec(&_searchable_by_config
|
101
|
-
clauses.
|
102
|
-
|
120
|
+
scope = instance_exec(&_searchable_by_config.scoping)
|
121
|
+
clauses.each do |clause|
|
122
|
+
scope = scope.where(clause)
|
103
123
|
end
|
124
|
+
scope
|
104
125
|
end
|
105
126
|
end
|
106
127
|
end
|
data/searchable-by.gemspec
CHANGED
data/spec/searchable_by_spec.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe ActiveRecord::SearchableBy do
|
4
|
+
|
4
5
|
it 'should ignore bad inputs' do
|
5
6
|
expect(Post.search_by(nil).count).to eq(4)
|
6
7
|
expect(Post.search_by('').count).to eq(4)
|
7
8
|
end
|
8
9
|
|
10
|
+
it 'should configure correctly' do
|
11
|
+
expect(AbstractModel._searchable_by_config.columns.size).to eq(1)
|
12
|
+
expect(Post._searchable_by_config.columns.size).to eq(4)
|
13
|
+
end
|
14
|
+
|
9
15
|
it 'should generate SQL' do
|
10
16
|
sql = Post.search_by('123').to_sql
|
11
17
|
expect(sql).to include(%("posts"."id" = 123))
|