searchlight 1.2.3 → 1.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/TODO.md +1 -0
- data/lib/searchlight/adapters/active_record.rb +21 -4
- data/lib/searchlight/dsl.rb +2 -10
- data/lib/searchlight/search.rb +13 -5
- data/lib/searchlight/version.rb +1 -1
- data/spec/searchlight/adapters/active_record_spec.rb +36 -12
- data/spec/searchlight/search_spec.rb +59 -36
- data/spec/support/mock_model.rb +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebb954502f32317df1fe4fb490178e3c868a541c
|
4
|
+
data.tar.gz: b0d0283e388c84a917c1c0d627752ce0b625a10c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 001d3bfe0e77b84ae6d256892310d18c63cb54ea416c2d3b7b794d5374c228d17423e32d3c82abdb59da8db6886b3a449c591b863a4ac9d578e9d34431d7b8ea
|
7
|
+
data.tar.gz: 72c116a81eb742c6d7ecf97c982d8ec99731c1321aee96d0246491c11c3725d8ab747d8eb36ca44b93c14751e24a4b275a465a28c6b77e7f254c28864c714b8a
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
Searchlight does its best to use [semantic versioning](http://semver.org).
|
4
4
|
|
5
|
+
## v1.2.4
|
6
|
+
|
7
|
+
- `options` method only returns those that map to search methods (not `attr_accessor` type values)
|
8
|
+
- Previously, `searches :name` in a class with an `ActiveRecord` target would always trigger the `ActiveRecord` adapter to define `searches_name` as `search.where(name: name)`. Now it first checks whether `name` is a column on the model, and if not, defines the method to raise an error.
|
9
|
+
|
5
10
|
## v1.2.3
|
6
11
|
|
7
12
|
Fix bug introduced in v1.2: setting defaults in `initialize` did not add them to the options hash, which meant they weren't used in searches.
|
data/TODO.md
CHANGED
@@ -19,11 +19,23 @@ module Searchlight
|
|
19
19
|
end
|
20
20
|
|
21
21
|
eval_string = attribute_names.map { |attribute_name|
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
model_class = model_class_for(search_target)
|
23
|
+
if model_class_for(search_target).columns_hash.keys.include?(attribute_name.to_s)
|
24
|
+
|
25
|
+
<<-UNICORN_BILE
|
26
|
+
def search_#{attribute_name}
|
27
|
+
search.where('#{attribute_name}' => public_send("#{attribute_name}"))
|
28
|
+
end
|
29
|
+
UNICORN_BILE
|
30
|
+
else
|
31
|
+
<<-MERMAID_TEARS
|
32
|
+
def search_#{attribute_name}
|
33
|
+
raise Searchlight::Adapters::ActiveRecord::UndefinedColumn,
|
34
|
+
"Class `#{model_class}` has no column `#{attribute_name}`; please define `search_#{attribute_name}` on `\#{self.class}` to clarify what you intend to search for"
|
35
|
+
end
|
36
|
+
MERMAID_TEARS
|
25
37
|
end
|
26
|
-
|
38
|
+
|
27
39
|
}.join
|
28
40
|
|
29
41
|
@ar_searches_module.module_eval(eval_string, __FILE__, __LINE__)
|
@@ -49,10 +61,15 @@ module Searchlight
|
|
49
61
|
self.search_target = (active_record_version >= 4) ? search_target.all : search_target.scoped
|
50
62
|
end
|
51
63
|
|
64
|
+
def model_class_for(target)
|
65
|
+
is_active_record_class?(target) ? target : target.engine
|
66
|
+
end
|
67
|
+
|
52
68
|
def active_record_version
|
53
69
|
::ActiveRecord::VERSION::MAJOR.to_i
|
54
70
|
end
|
55
71
|
|
72
|
+
UndefinedColumn = Class.new(StandardError)
|
56
73
|
end
|
57
74
|
end
|
58
75
|
end
|
data/lib/searchlight/dsl.rb
CHANGED
@@ -19,17 +19,9 @@ module Searchlight
|
|
19
19
|
include @accessors_module
|
20
20
|
end
|
21
21
|
|
22
|
-
eval_string =
|
22
|
+
eval_string = "attr_accessor *#{attribute_names}\n"
|
23
|
+
eval_string << attribute_names.map { |attribute_name|
|
23
24
|
<<-LEPRECHAUN_JUICE
|
24
|
-
def #{attribute_name}=(val)
|
25
|
-
self.options ||= {}
|
26
|
-
self.options[:#{attribute_name}] = val
|
27
|
-
end
|
28
|
-
|
29
|
-
def #{attribute_name}
|
30
|
-
self.options[:#{attribute_name}]
|
31
|
-
end
|
32
|
-
|
33
25
|
def #{attribute_name}?
|
34
26
|
truthy?(public_send("#{attribute_name}"))
|
35
27
|
end
|
data/lib/searchlight/search.rb
CHANGED
@@ -2,8 +2,6 @@ module Searchlight
|
|
2
2
|
class Search
|
3
3
|
extend DSL
|
4
4
|
|
5
|
-
attr_accessor :options
|
6
|
-
|
7
5
|
def self.search_target
|
8
6
|
return @search_target if defined?(@search_target)
|
9
7
|
return superclass.search_target if superclass.respond_to?(:search_target) && superclass != Searchlight::Search
|
@@ -22,12 +20,23 @@ module Searchlight
|
|
22
20
|
@results ||= run
|
23
21
|
end
|
24
22
|
|
23
|
+
def options
|
24
|
+
search_attributes.reduce({}) { |hash, option_name|
|
25
|
+
option_val = send(option_name)
|
26
|
+
hash.tap { |hash| hash[option_name.to_sym] = option_val unless is_blank?(option_val) }
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
25
30
|
protected
|
26
31
|
|
27
32
|
attr_writer :search
|
28
33
|
|
29
34
|
private
|
30
35
|
|
36
|
+
def search_attributes
|
37
|
+
public_methods.map(&:to_s).select { |m| m.start_with?('search_') }.map { |m| m.sub(/\Asearch_/, '') }
|
38
|
+
end
|
39
|
+
|
31
40
|
def self.guess_search_class!
|
32
41
|
if self.name.end_with?('Search')
|
33
42
|
@search_target = name.sub(/Search\z/, '').split('::').inject(Kernel, &:const_get)
|
@@ -44,8 +53,7 @@ module Searchlight
|
|
44
53
|
end
|
45
54
|
|
46
55
|
def filter_and_mass_assign(provided_options = {})
|
47
|
-
|
48
|
-
self.options = provided_options.reject { |key, value| is_blank?(value) }
|
56
|
+
options = (provided_options || {}).reject { |key, value| is_blank?(value) }
|
49
57
|
begin
|
50
58
|
options.each { |key, value| public_send("#{key}=", value) } if options && options.any?
|
51
59
|
rescue NoMethodError => e
|
@@ -55,7 +63,7 @@ module Searchlight
|
|
55
63
|
|
56
64
|
def run
|
57
65
|
options.each do |option_name, value|
|
58
|
-
new_search = public_send("search_#{option_name}")
|
66
|
+
new_search = public_send("search_#{option_name}")
|
59
67
|
self.search = new_search unless new_search.nil?
|
60
68
|
end
|
61
69
|
search
|
data/lib/searchlight/version.rb
CHANGED
@@ -18,22 +18,46 @@ describe 'Searchlight::Adapters::ActiveRecord', adapter: true do
|
|
18
18
|
|
19
19
|
shared_examples "search classes with an ActiveRecord target" do
|
20
20
|
|
21
|
-
|
22
|
-
search_class.searches :elephants
|
23
|
-
end
|
21
|
+
context "when the base model has a column matching the search term" do
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
before :each do
|
24
|
+
MockActiveRecord.stub(:columns_hash).and_return({'elephants' => 'column info...'})
|
25
|
+
search_class.searches :elephants
|
26
|
+
end
|
27
|
+
|
28
|
+
it "adds search methods to the search class" do
|
29
|
+
expect(search_class.new).to respond_to(:search_elephants)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "defines search methods that call `where` on the search target" do
|
33
|
+
search_instance.results
|
34
|
+
expect(search_instance.search.called_methods).to include(:where)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "sets arguments properly in the defined method" do
|
38
|
+
search_instance.search.should_receive(:where).with('elephants' => 'yes, please')
|
39
|
+
search_instance.search_elephants
|
40
|
+
end
|
28
41
|
|
29
|
-
it "defines search methods that call where on the search target" do
|
30
|
-
search_instance.results
|
31
|
-
expect(search_instance.search.called_methods).to include(:where)
|
32
42
|
end
|
33
43
|
|
34
|
-
|
35
|
-
|
36
|
-
|
44
|
+
context "when the base model has no column matching the search term" do
|
45
|
+
|
46
|
+
before :each do
|
47
|
+
MockActiveRecord.stub(:columns_hash).and_return({})
|
48
|
+
search_class.searches :elephants
|
49
|
+
end
|
50
|
+
|
51
|
+
it "adds search methods to the search class" do
|
52
|
+
expect(search_class.new).to respond_to(:search_elephants)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "defines search methods to raise an exception" do
|
56
|
+
expect { search_instance.results }.to raise_error(
|
57
|
+
Searchlight::Adapters::ActiveRecord::UndefinedColumn
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
37
61
|
end
|
38
62
|
|
39
63
|
end
|
@@ -2,7 +2,11 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Searchlight::Search do
|
4
4
|
|
5
|
-
let(:search_class) { Named::Class.new('ExampleSearch', described_class).tap {
|
5
|
+
let(:search_class) { Named::Class.new('ExampleSearch', described_class).tap {|klass|
|
6
|
+
klass.searches *allowed_options
|
7
|
+
allowed_options.each { |name| klass.send(:define_method, "search_#{name}") {} }
|
8
|
+
}
|
9
|
+
}
|
6
10
|
let(:allowed_options) { Hash.new }
|
7
11
|
let(:provided_options) { Hash.new }
|
8
12
|
let(:search) { search_class.new(provided_options) }
|
@@ -57,55 +61,74 @@ describe Searchlight::Search do
|
|
57
61
|
|
58
62
|
end
|
59
63
|
|
64
|
+
context "when some options are do not map to search methods (eg, attr_accessor)" do
|
65
|
+
let(:search_class) {
|
66
|
+
Named::Class.new('ExampleSearch', described_class) do
|
67
|
+
attr_accessor :krazy_mode
|
68
|
+
def search_name; end
|
69
|
+
end.tap { |klass| klass.searches *allowed_options }
|
70
|
+
}
|
71
|
+
let(:provided_options) { {name: 'Reese Roper', krazy_mode: true} }
|
72
|
+
|
73
|
+
it "sets all the provided values" do
|
74
|
+
expect(search.name).to eq('Reese Roper')
|
75
|
+
expect(search.krazy_mode).to eq(true)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "only lists options for the values corresponding to search methods" do
|
79
|
+
expect(search.options).to eq({name: 'Reese Roper'})
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
60
84
|
end
|
61
85
|
|
62
86
|
end
|
63
87
|
|
64
88
|
context "when the search class has defaults" do
|
65
89
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
let(:search_class) {
|
70
|
-
Named::Class.new('ExampleSearch', described_class) do
|
90
|
+
let(:allowed_options) { [:name, :age] }
|
91
|
+
let(:search_class) {
|
92
|
+
Named::Class.new('ExampleSearch', described_class) do
|
71
93
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
94
|
+
def initialize(options)
|
95
|
+
super
|
96
|
+
self.name ||= 'Dennis'
|
97
|
+
self.age ||= 37
|
98
|
+
end
|
77
99
|
|
78
|
-
|
79
|
-
|
100
|
+
def search_name; end
|
101
|
+
def search_age; end
|
80
102
|
|
81
|
-
|
103
|
+
end.tap { |klass| klass.searches *allowed_options }
|
104
|
+
}
|
82
105
|
|
83
|
-
|
106
|
+
context "and there were no values given" do
|
84
107
|
|
85
|
-
|
86
|
-
expect(search.name).to eq('Dennis')
|
87
|
-
expect(search.age).to eq(37)
|
88
|
-
end
|
108
|
+
let(:provided_options) { Hash.new }
|
89
109
|
|
90
|
-
|
91
|
-
|
92
|
-
|
110
|
+
it "uses the defaults for its accessors" do
|
111
|
+
expect(search.name).to eq('Dennis')
|
112
|
+
expect(search.age).to eq(37)
|
113
|
+
end
|
93
114
|
|
115
|
+
it "uses the defaults for its options hash" do
|
116
|
+
expect(search.options).to eq({name: 'Dennis', age: 37})
|
94
117
|
end
|
95
118
|
|
96
|
-
|
119
|
+
end
|
97
120
|
|
98
|
-
|
121
|
+
context "and values are given" do
|
99
122
|
|
100
|
-
|
101
|
-
expect(search.name).to eq('Treebeard')
|
102
|
-
expect(search.age).to eq('A few thousand')
|
103
|
-
end
|
123
|
+
let(:provided_options) { {name: 'Treebeard', age: 'A few thousand'} }
|
104
124
|
|
105
|
-
|
106
|
-
|
107
|
-
|
125
|
+
it "uses the provided values" do
|
126
|
+
expect(search.name).to eq('Treebeard')
|
127
|
+
expect(search.age).to eq('A few thousand')
|
128
|
+
end
|
108
129
|
|
130
|
+
it "uses the provided values for its options hash" do
|
131
|
+
expect(search.options).to eq({name: 'Treebeard', age: 'A few thousand'})
|
109
132
|
end
|
110
133
|
|
111
134
|
end
|
@@ -345,13 +368,13 @@ describe Searchlight::Search do
|
|
345
368
|
end
|
346
369
|
}
|
347
370
|
|
348
|
-
let(:
|
371
|
+
let(:provided_options) { {bits: ' ', bats: nil, bots: false} }
|
349
372
|
|
350
373
|
it "only runs search methods that have real values to search on" do
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
374
|
+
search.should_not_receive(:search_bits)
|
375
|
+
search.should_not_receive(:search_bats)
|
376
|
+
search.should_receive(:search_bots)
|
377
|
+
search.send(:run)
|
355
378
|
end
|
356
379
|
|
357
380
|
end
|
data/spec/support/mock_model.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: searchlight
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Long
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
12
|
+
date: 2013-06-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: named
|