searchlight 1.2.3 → 1.2.4
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 +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
|