searchlight 1.3.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -6
- data/CHANGELOG.md +30 -0
- data/Gemfile +0 -2
- data/README.md +15 -29
- data/lib/searchlight.rb +0 -2
- data/lib/searchlight/adapters/action_view.rb +1 -1
- data/lib/searchlight/version.rb +1 -1
- data/searchlight.gemspec +10 -3
- data/spec/spec_helper.rb +0 -2
- metadata +68 -38
- data/lib/searchlight/adapters/active_record.rb +0 -96
- data/lib/searchlight/adapters/mongoid.rb +0 -60
- data/spec/searchlight/adapters/active_record_spec.rb +0 -107
- data/spec/searchlight/adapters/mongoid_spec.rb +0 -86
- data/spec/support/mock_models/active_record.rb +0 -23
- data/spec/support/mock_models/mongoid.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d87d47fdacfae5b1d8d87d2c34b6443610fb4f88
|
4
|
+
data.tar.gz: 639cb63efe19379336e0fbcd65b8d66f0d265f2a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9785b92c697fb59b0ae3aebd88af3f1913c04df0ff99aed3b73923b13d2d313d9923f98b217a7646be742c163ab892d61ce3eb8cff77960da21a132979a1717d
|
7
|
+
data.tar.gz: 5438b35af95bcf804ff0898f8989f0d804f7bb1b46f9f74642e59d3a98c5a27ac70c681a988b8ffca545aa9253a9339b3f3efca9fe29e7beaca868b70b0d73df
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,36 @@
|
|
2
2
|
|
3
3
|
Searchlight does its best to use [semantic versioning](http://semver.org).
|
4
4
|
|
5
|
+
## v2.0.0
|
6
|
+
|
7
|
+
Now with fewer features! :D
|
8
|
+
|
9
|
+
### No more ORM adapters
|
10
|
+
|
11
|
+
ORM "adapters", which were never actually necessary, have been removed. They required lots of hackery, added test dependencies, and sometimes introduced [weird bugs](https://github.com/nathanl/searchlight/pull/15). All that just so that if you said your class `searches :first_name, :last_name`, we could save you from typing simple search methods like:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
def search_first_name
|
15
|
+
search.where(first_name: first_name)
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
You can easily save yourself this effort with something like:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
%w[name address pant_size].each do |attr|
|
23
|
+
define_method("search_#{attr}") do
|
24
|
+
search.where(:"#{attr}" => attr)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
...and you'll get much saner backtraces if anything goes wrong.
|
30
|
+
|
31
|
+
**ActiveRecord users**: note that with this change, you'll need to update your `search_on` calls to return an `ActiveRecord::Relation` so that, if no options are passed, you don't return the model class itself. Eg, instead of `search_on User`, do `search_on User.all` for Rails > 4 or `search_on User.scoped` for Rails < 4.
|
32
|
+
|
33
|
+
With this change, Searchlight no longer has any ties to any ORM, but can still work with any of them that use method chaining. Hooray!
|
34
|
+
|
5
35
|
## v1.3.1
|
6
36
|
|
7
37
|
Add license to gemspec, thanks to notice from Benjamin Fleischer - see [his blog post](http://www.benjaminfleischer.com/2013/07/12/make-the-world-a-better-place-put-a-license-in-your-gemspec/)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Searchlight
|
2
2
|
|
3
|
-
Searchlight
|
3
|
+
Searchlight is a low-magic way to build database searches using an ORM.
|
4
4
|
|
5
|
-
Searchlight can work with any ORM or object that can build a query using chained
|
5
|
+
Searchlight can work with **any** ORM or object that can build a query using **chained method calls** (eg, ActiveRecord's `.where(...).where(...).limit(...)`, or similar chains with [Sequel](https://rubygems.org/gems/sequel), [Mongoid](https://rubygems.org/gems/mongoid), etc).
|
6
6
|
|
7
7
|
[![Gem Version](https://badge.fury.io/rb/searchlight.png)](https://rubygems.org/gems/searchlight)
|
8
8
|
[![Code Climate](https://codeclimate.com/github/nathanl/searchlight.png)](https://codeclimate.com/github/nathanl/searchlight)
|
@@ -21,10 +21,12 @@ The basic idea of Searchlight is to build a search by chaining method calls that
|
|
21
21
|
For example, if you have a Searchlight search class called `YetiSearch`, and you instantiate it like this:
|
22
22
|
|
23
23
|
```ruby
|
24
|
-
search = YetiSearch.new(
|
24
|
+
search = YetiSearch.new(
|
25
|
+
active: true, name: 'Jimmy', location_in: %w[NY LA] # or params[:search]
|
26
|
+
)
|
25
27
|
```
|
26
28
|
|
27
|
-
... calling `results` on the instance will build a search by chaining calls to `search_active`, `search_name`, and `
|
29
|
+
... calling `results` on the instance will build a search by chaining calls to `search_active`, `search_name`, and `search_location_in`.
|
28
30
|
|
29
31
|
The `results` method will then return the return value of the last search method. If you're using ActiveRecord, this would be an `ActiveRecord::Relation`, and you can then call `each` to loop through the results, `to_sql` to get the generated query, etc.
|
30
32
|
|
@@ -38,7 +40,9 @@ A search class has three main parts: a target, options, and methods. For example
|
|
38
40
|
class PersonSearch < Searchlight::Search
|
39
41
|
|
40
42
|
# The search target; in this case, an ActiveRecord model.
|
41
|
-
|
43
|
+
# This is the starting point for any chaining we do, and it's what
|
44
|
+
# will be returned if no search options are passed.
|
45
|
+
search_on Person.all
|
42
46
|
|
43
47
|
# The options the search understands. Supply any combination of them to an instance.
|
44
48
|
searches :first_name, :last_name
|
@@ -120,7 +124,7 @@ search = MySearchClass.new(awesomeness: 'Xtreme')
|
|
120
124
|
|
121
125
|
... your search methods can use:
|
122
126
|
|
123
|
-
- `awesomeness` to
|
127
|
+
- `awesomeness` to retrieve the given value, `'Xtreme'`
|
124
128
|
- `awesomeness?` to get a boolean version: `true`
|
125
129
|
|
126
130
|
The boolean conversion is form-friendly, so that `0`, `'0'`, and `'false'` are considered `false`.
|
@@ -181,7 +185,7 @@ You can define defaults for boolean attributes if you treat them as "yes/no/eith
|
|
181
185
|
```ruby
|
182
186
|
class AnimalSearch < Searchlight::Search
|
183
187
|
|
184
|
-
search_on Animal
|
188
|
+
search_on Animal.all
|
185
189
|
|
186
190
|
searches :is_fictional
|
187
191
|
|
@@ -214,7 +218,7 @@ You can subclass an existing search class and support all the same options with
|
|
214
218
|
|
215
219
|
```ruby
|
216
220
|
class VillageSearch < CitySearch
|
217
|
-
search_on Village
|
221
|
+
search_on Village.all
|
218
222
|
end
|
219
223
|
```
|
220
224
|
|
@@ -286,27 +290,9 @@ class OrdersController < ApplicationController
|
|
286
290
|
end
|
287
291
|
end
|
288
292
|
```
|
289
|
-
##
|
293
|
+
## ActionView Adapter
|
290
294
|
|
291
|
-
|
292
|
-
|
293
|
-
### ActiveRecord and Mongoid
|
294
|
-
|
295
|
-
When you call `search_on` in your Searchlight class, Searchlight checks whether the search target comes from ActiveRecord or Mongoid, and, if so, mixes a module into your class.
|
296
|
-
|
297
|
-
For each of your search options, the module will have the simplest possible search method defined. For example, if your class `searches :name`, the ActiveRecord module will have this method:
|
298
|
-
|
299
|
-
```ruby
|
300
|
-
def search_name
|
301
|
-
search.where(name: name)
|
302
|
-
end
|
303
|
-
```
|
304
|
-
|
305
|
-
Since that method is in a parent module, you can easily override it by defining your own method. You can also call `super` in the method you define.
|
306
|
-
|
307
|
-
### ActionView
|
308
|
-
|
309
|
-
Similarly, Searchlight adds ActionView-friendly methods to your classes if it sees that `ActionView` is a defined constant. See the code for details, but the upshot is that you can use a search with `form_for`.
|
295
|
+
Searchlight's ActionView adapter adds ActionView-friendly methods to your classes if it sees that `ActionView` is a defined constant. See the code for details, but the upshot is that you can use a search with `form_for`.
|
310
296
|
|
311
297
|
## Compatibility
|
312
298
|
|
@@ -337,4 +323,4 @@ Or install it yourself as:
|
|
337
323
|
## Shout Outs
|
338
324
|
|
339
325
|
- The excellent [Mr. Adam Hunter](https://github.com/adamhunter), co-creator of Searchlight.
|
340
|
-
- [TMA](http://tma1.com) for supporting the development of Searchlight.
|
326
|
+
- [TMA](http://tma1.com) for supporting the initial development of Searchlight.
|
data/lib/searchlight.rb
CHANGED
@@ -7,6 +7,4 @@ end
|
|
7
7
|
|
8
8
|
require 'searchlight/dsl'
|
9
9
|
require 'searchlight/search'
|
10
|
-
require 'searchlight/adapters/active_record' if defined?(::ActiveRecord)
|
11
|
-
require 'searchlight/adapters/mongoid' if defined?(::Mongoid)
|
12
10
|
require 'searchlight/adapters/action_view' if defined?(::ActionView)
|
data/lib/searchlight/version.rb
CHANGED
data/searchlight.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.version = Searchlight::VERSION
|
10
10
|
spec.authors = ["Nathan Long", "Adam Hunter"]
|
11
11
|
spec.email = ["nathanmlong@gmail.com", "adamhunter@me.com"]
|
12
|
-
spec.
|
13
|
-
spec.
|
12
|
+
spec.summary = %q{Searchlight is a low-magic way to build database searches using an ORM.}
|
13
|
+
spec.description = %q{Searchlight is a low-magic way to build database searches using an ORM. It's compatible with ActiveRecord, Sequel, Mongoid, and any other ORM that can build queries by chaining method calls.}
|
14
14
|
spec.homepage = "https://github.com/nathanl/searchlight"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
@@ -21,9 +21,16 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.add_dependency "named", "~> 1.0"
|
23
23
|
|
24
|
-
spec.add_development_dependency "rspec", "~> 2.
|
24
|
+
spec.add_development_dependency "rspec", "~> 2.14"
|
25
25
|
spec.add_development_dependency "bundler", "~> 1.3"
|
26
26
|
spec.add_development_dependency "rake"
|
27
27
|
spec.add_development_dependency "capybara", "~> 2.0"
|
28
28
|
spec.add_development_dependency "simplecov", "~> 0.7"
|
29
|
+
|
30
|
+
# To test integration with actionview and activerecord
|
31
|
+
spec.add_development_dependency "actionview", "~> 4.1"
|
32
|
+
spec.add_development_dependency "activemodel", "~> 4.1"
|
33
|
+
|
34
|
+
# For coverage testing on Travis
|
35
|
+
spec.add_development_dependency "coveralls", "~> 0.7"
|
29
36
|
end
|
data/spec/spec_helper.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:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Long
|
@@ -9,96 +9,137 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-04-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: named
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - ~>
|
18
|
+
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '1.0'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - ~>
|
25
|
+
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '1.0'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rspec
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- - ~>
|
32
|
+
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '2.
|
34
|
+
version: '2.14'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- - ~>
|
39
|
+
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '2.
|
41
|
+
version: '2.14'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: bundler
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- - ~>
|
46
|
+
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: '1.3'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- - ~>
|
53
|
+
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '1.3'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: rake
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
|
-
- -
|
60
|
+
- - ">="
|
61
61
|
- !ruby/object:Gem::Version
|
62
62
|
version: '0'
|
63
63
|
type: :development
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
|
-
- -
|
67
|
+
- - ">="
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '0'
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: capybara
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- - ~>
|
74
|
+
- - "~>"
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: '2.0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
|
-
- - ~>
|
81
|
+
- - "~>"
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: '2.0'
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
85
|
name: simplecov
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
|
-
- - ~>
|
88
|
+
- - "~>"
|
89
89
|
- !ruby/object:Gem::Version
|
90
90
|
version: '0.7'
|
91
91
|
type: :development
|
92
92
|
prerelease: false
|
93
93
|
version_requirements: !ruby/object:Gem::Requirement
|
94
94
|
requirements:
|
95
|
-
- - ~>
|
95
|
+
- - "~>"
|
96
96
|
- !ruby/object:Gem::Version
|
97
97
|
version: '0.7'
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: actionview
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '4.1'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '4.1'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: activemodel
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - "~>"
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '4.1'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - "~>"
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '4.1'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: coveralls
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - "~>"
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0.7'
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - "~>"
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0.7'
|
140
|
+
description: Searchlight is a low-magic way to build database searches using an ORM.
|
141
|
+
It's compatible with ActiveRecord, Sequel, Mongoid, and any other ORM that can build
|
142
|
+
queries by chaining method calls.
|
102
143
|
email:
|
103
144
|
- nathanmlong@gmail.com
|
104
145
|
- adamhunter@me.com
|
@@ -106,9 +147,9 @@ executables: []
|
|
106
147
|
extensions: []
|
107
148
|
extra_rdoc_files: []
|
108
149
|
files:
|
109
|
-
- .gitignore
|
110
|
-
- .rspec
|
111
|
-
- .travis.yml
|
150
|
+
- ".gitignore"
|
151
|
+
- ".rspec"
|
152
|
+
- ".travis.yml"
|
112
153
|
- CHANGELOG.md
|
113
154
|
- Gemfile
|
114
155
|
- LICENSE.txt
|
@@ -119,22 +160,16 @@ files:
|
|
119
160
|
- gemfiles/Gemfile.rails-4.0.x
|
120
161
|
- lib/searchlight.rb
|
121
162
|
- lib/searchlight/adapters/action_view.rb
|
122
|
-
- lib/searchlight/adapters/active_record.rb
|
123
|
-
- lib/searchlight/adapters/mongoid.rb
|
124
163
|
- lib/searchlight/dsl.rb
|
125
164
|
- lib/searchlight/search.rb
|
126
165
|
- lib/searchlight/version.rb
|
127
166
|
- searchlight.gemspec
|
128
167
|
- spec/searchlight/adapters/action_view_spec.rb
|
129
|
-
- spec/searchlight/adapters/active_record_spec.rb
|
130
|
-
- spec/searchlight/adapters/mongoid_spec.rb
|
131
168
|
- spec/searchlight/search_spec.rb
|
132
169
|
- spec/searchlight_spec.rb
|
133
170
|
- spec/spec_helper.rb
|
134
171
|
- spec/support/account_search.rb
|
135
172
|
- spec/support/mock_model.rb
|
136
|
-
- spec/support/mock_models/active_record.rb
|
137
|
-
- spec/support/mock_models/mongoid.rb
|
138
173
|
- spec/support/spiffy_account_search.rb
|
139
174
|
homepage: https://github.com/nathanl/searchlight
|
140
175
|
licenses:
|
@@ -146,30 +181,25 @@ require_paths:
|
|
146
181
|
- lib
|
147
182
|
required_ruby_version: !ruby/object:Gem::Requirement
|
148
183
|
requirements:
|
149
|
-
- -
|
184
|
+
- - ">="
|
150
185
|
- !ruby/object:Gem::Version
|
151
186
|
version: '0'
|
152
187
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
188
|
requirements:
|
154
|
-
- -
|
189
|
+
- - ">="
|
155
190
|
- !ruby/object:Gem::Version
|
156
191
|
version: '0'
|
157
192
|
requirements: []
|
158
193
|
rubyforge_project:
|
159
|
-
rubygems_version: 2.0
|
194
|
+
rubygems_version: 2.2.0
|
160
195
|
signing_key:
|
161
196
|
specification_version: 4
|
162
|
-
summary: Searchlight
|
163
|
-
write.
|
197
|
+
summary: Searchlight is a low-magic way to build database searches using an ORM.
|
164
198
|
test_files:
|
165
199
|
- spec/searchlight/adapters/action_view_spec.rb
|
166
|
-
- spec/searchlight/adapters/active_record_spec.rb
|
167
|
-
- spec/searchlight/adapters/mongoid_spec.rb
|
168
200
|
- spec/searchlight/search_spec.rb
|
169
201
|
- spec/searchlight_spec.rb
|
170
202
|
- spec/spec_helper.rb
|
171
203
|
- spec/support/account_search.rb
|
172
204
|
- spec/support/mock_model.rb
|
173
|
-
- spec/support/mock_models/active_record.rb
|
174
|
-
- spec/support/mock_models/mongoid.rb
|
175
205
|
- spec/support/spiffy_account_search.rb
|
@@ -1,96 +0,0 @@
|
|
1
|
-
module Searchlight
|
2
|
-
module Adapters
|
3
|
-
module ActiveRecord
|
4
|
-
|
5
|
-
def search_on(target)
|
6
|
-
super
|
7
|
-
extend Search if is_active_record?(target)
|
8
|
-
convert_to_relation if is_active_record_class?(target)
|
9
|
-
end
|
10
|
-
|
11
|
-
module Search
|
12
|
-
def searches(*attribute_names)
|
13
|
-
super
|
14
|
-
|
15
|
-
# Ensure this class only adds one search module to the ancestors chain
|
16
|
-
if @ar_searches_module.nil?
|
17
|
-
@ar_searches_module = Named::Module.new("SearchlightActiveRecordSearches(#{self})")
|
18
|
-
include @ar_searches_module
|
19
|
-
end
|
20
|
-
|
21
|
-
eval_string = attribute_names.map { |attribute_name|
|
22
|
-
model_class = model_class_for(search_target)
|
23
|
-
if model_has_db_attribute?(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
|
37
|
-
end
|
38
|
-
|
39
|
-
}.join
|
40
|
-
|
41
|
-
@ar_searches_module.module_eval(eval_string, __FILE__, __LINE__)
|
42
|
-
end
|
43
|
-
|
44
|
-
# The idea here is to provide a means to allow users to bypass the check if it causes problems (e.g. during
|
45
|
-
# `rake assets:precompile` if the DB has yet to be created). To bypass this, a user could monkey patch as
|
46
|
-
# follows:
|
47
|
-
#
|
48
|
-
# module Searchlight::Adapters::ActiveRecord::Search
|
49
|
-
# def model_has_db_attribute?(attribute_name)
|
50
|
-
# model_class_for(search_target).columns_hash.keys.include?(attribute_name)
|
51
|
-
# rescue StandardError
|
52
|
-
# true
|
53
|
-
# end
|
54
|
-
# end
|
55
|
-
#
|
56
|
-
# Alternatively, they could monkey-patch Searchlight::Adapters::ActiveRecord::Search::model_has_db_attribute
|
57
|
-
# to simply always return true, though they would then not get the benefit of the improved error messaging.
|
58
|
-
#
|
59
|
-
def model_has_db_attribute?(attribute_name)
|
60
|
-
model_class_for(search_target).columns_hash.keys.include?(attribute_name)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
protected
|
65
|
-
|
66
|
-
def is_active_record?(target)
|
67
|
-
is_active_record_class?(target) || is_active_record_relation?(target)
|
68
|
-
end
|
69
|
-
|
70
|
-
def is_active_record_class?(target)
|
71
|
-
target.is_a?(Class) && target.ancestors.include?(::ActiveRecord::Base)
|
72
|
-
end
|
73
|
-
|
74
|
-
def is_active_record_relation?(target)
|
75
|
-
target.is_a?(::ActiveRecord::Relation)
|
76
|
-
end
|
77
|
-
|
78
|
-
# Ensure that searches without options still return enumerable results
|
79
|
-
def convert_to_relation
|
80
|
-
self.search_target = (active_record_version >= 4) ? search_target.all : search_target.scoped
|
81
|
-
end
|
82
|
-
|
83
|
-
def model_class_for(target)
|
84
|
-
is_active_record_class?(target) ? target : target.engine
|
85
|
-
end
|
86
|
-
|
87
|
-
def active_record_version
|
88
|
-
::ActiveRecord::VERSION::MAJOR.to_i
|
89
|
-
end
|
90
|
-
|
91
|
-
UndefinedColumn = Class.new(StandardError)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
Searchlight::Search.extend(Searchlight::Adapters::ActiveRecord)
|
@@ -1,60 +0,0 @@
|
|
1
|
-
module Searchlight
|
2
|
-
module Adapters
|
3
|
-
module Mongoid
|
4
|
-
|
5
|
-
def search_on(target)
|
6
|
-
super
|
7
|
-
extend Search if mongoid?(target)
|
8
|
-
end
|
9
|
-
|
10
|
-
module Search
|
11
|
-
|
12
|
-
def searches(*attributes_names)
|
13
|
-
super
|
14
|
-
|
15
|
-
attributes_names.map do |attribute_name|
|
16
|
-
method_name = "search_#{attribute_name}"
|
17
|
-
if field?(attribute_name)
|
18
|
-
define_method method_name do
|
19
|
-
search.where(attribute_name.to_s => public_send(attribute_name))
|
20
|
-
end
|
21
|
-
else
|
22
|
-
define_method method_name do
|
23
|
-
raise Searchlight::Adapters::Mongoid::UndefinedColumn,
|
24
|
-
"Class `#{self.class.model_class}` has no field `#{attribute_name}`; please define `search_#{attribute_name}` on `#{self.class}` to clarify what you intend to search for"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def field?(attributes_name)
|
31
|
-
model_class.fields.has_key? attributes_name.to_s
|
32
|
-
end
|
33
|
-
|
34
|
-
def model_class
|
35
|
-
search_target.is_a?(::Mongoid::Criteria) ? search_target.klass : search_target
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
protected
|
41
|
-
|
42
|
-
def mongoid?(target)
|
43
|
-
mongoid_document?(target) || mongoid_criteria?(target)
|
44
|
-
end
|
45
|
-
|
46
|
-
def mongoid_document?(target)
|
47
|
-
defined?(::Mongoid::Document) && target.include?(::Mongoid::Document)
|
48
|
-
end
|
49
|
-
|
50
|
-
def mongoid_criteria?(target)
|
51
|
-
defined?(::Mongoid::Criteria) && target.is_a?(::Mongoid::Criteria)
|
52
|
-
end
|
53
|
-
|
54
|
-
UndefinedColumn = Class.new(StandardError)
|
55
|
-
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
Searchlight::Search.extend(Searchlight::Adapters::Mongoid)
|
@@ -1,107 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe 'Searchlight::Adapters::ActiveRecord', adapter: true do
|
4
|
-
|
5
|
-
before :all do
|
6
|
-
require 'searchlight/adapters/active_record'
|
7
|
-
require 'active_record'
|
8
|
-
end
|
9
|
-
|
10
|
-
let(:search_class) {
|
11
|
-
Named::Class.new('SearchClass', Searchlight::Search).tap { |klass|
|
12
|
-
klass.instance_eval(&ar_version_faker)
|
13
|
-
klass.search_on target
|
14
|
-
}
|
15
|
-
}
|
16
|
-
let(:ar_version_faker) { lambda {|klass| nil } } # no-op
|
17
|
-
let(:search_instance) { search_class.new(elephants: 'yes, please') }
|
18
|
-
|
19
|
-
shared_examples "search classes with an ActiveRecord target" do
|
20
|
-
|
21
|
-
context "when the base model has a column matching the search term" do
|
22
|
-
|
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
|
41
|
-
|
42
|
-
end
|
43
|
-
|
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
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
context "when the search target is an ActiveRecord class" do
|
66
|
-
|
67
|
-
let(:target) { MockActiveRecord }
|
68
|
-
|
69
|
-
describe "converting to an ActiveRecord::Relation" do
|
70
|
-
|
71
|
-
context "for ActiveRecord <= 3" do
|
72
|
-
|
73
|
-
let(:ar_version_faker) { lambda { |klass| klass.stub(:active_record_version).and_return(3) } }
|
74
|
-
|
75
|
-
it "calls 'scoped'" do
|
76
|
-
target.should_receive(:scoped)
|
77
|
-
search_class
|
78
|
-
end
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
context "for ActiveRecord >= 4" do
|
83
|
-
|
84
|
-
let(:ar_version_faker) { lambda { |klass| klass.stub(:active_record_version).and_return(4) } }
|
85
|
-
|
86
|
-
it "calls 'all'" do
|
87
|
-
target.should_receive(:all)
|
88
|
-
search_class
|
89
|
-
end
|
90
|
-
|
91
|
-
end
|
92
|
-
|
93
|
-
end
|
94
|
-
|
95
|
-
it_behaves_like "search classes with an ActiveRecord target"
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
context "when the search target is an ActiveRecord relation" do
|
100
|
-
|
101
|
-
let(:target) { MockActiveRecord.joins(:dudes_named_milford).tap { |r| r.called_methods.clear } }
|
102
|
-
|
103
|
-
it_behaves_like "search classes with an ActiveRecord target"
|
104
|
-
|
105
|
-
end
|
106
|
-
|
107
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe 'Searchlight::Adapters::Mongoid', adapter: true do
|
4
|
-
|
5
|
-
before :all do
|
6
|
-
require 'mongoid'
|
7
|
-
require 'searchlight/adapters/mongoid'
|
8
|
-
end
|
9
|
-
|
10
|
-
after :all do
|
11
|
-
Object.send(:remove_const, :Mongoid)
|
12
|
-
end
|
13
|
-
|
14
|
-
let(:search_class) {
|
15
|
-
Named::Class.new('SearchClass', Searchlight::Search).tap { |klass| klass.search_on target }
|
16
|
-
}
|
17
|
-
|
18
|
-
let(:search_instance) { search_class.new(elephants: 'yes, please') }
|
19
|
-
|
20
|
-
shared_examples "search classes with an Mongoid target" do
|
21
|
-
|
22
|
-
context "when the base model has a field matching the search term" do
|
23
|
-
|
24
|
-
before do
|
25
|
-
MockMongoid.stub(:fields).and_return('elephants' => 'column info...')
|
26
|
-
search_class.searches :elephants
|
27
|
-
end
|
28
|
-
|
29
|
-
it "adds search methods to the search class" do
|
30
|
-
expect(search_class.new).to respond_to(:search_elephants)
|
31
|
-
end
|
32
|
-
|
33
|
-
it "defines search methods that call `where` on the search target" do
|
34
|
-
search_instance.results
|
35
|
-
expect(search_instance.search.called_methods).to include(:where)
|
36
|
-
end
|
37
|
-
|
38
|
-
it "sets arguments properly in the defined method" do
|
39
|
-
search_instance.search.should_receive(:where).with('elephants' => 'yes, please')
|
40
|
-
search_instance.search_elephants
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
context "when the base model has no field matching the search term" do
|
46
|
-
|
47
|
-
before do
|
48
|
-
MockMongoid.stub(fields: {})
|
49
|
-
search_class.searches :elephants
|
50
|
-
end
|
51
|
-
|
52
|
-
it "adds search methods to the search class" do
|
53
|
-
expect(search_class.new).to respond_to(:search_elephants)
|
54
|
-
end
|
55
|
-
|
56
|
-
it "defines search methods to raise an exception" do
|
57
|
-
expect { search_instance.results }.to raise_error(
|
58
|
-
Searchlight::Adapters::Mongoid::UndefinedColumn
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
context "when the search target is a class with Mongoid::Document module" do
|
67
|
-
|
68
|
-
let(:target) { MockMongoid }
|
69
|
-
|
70
|
-
it_behaves_like "search classes with an Mongoid target"
|
71
|
-
|
72
|
-
end
|
73
|
-
|
74
|
-
context "when the search target is Mongoid::Criteria class" do
|
75
|
-
|
76
|
-
let(:target) { MockMongoidCriteria.new([]) }
|
77
|
-
|
78
|
-
before do
|
79
|
-
target.stub(klass: MockMongoid)
|
80
|
-
end
|
81
|
-
|
82
|
-
it_behaves_like "search classes with an Mongoid target"
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
class MockActiveRecord < MockModel
|
2
|
-
|
3
|
-
def self.ancestors
|
4
|
-
super + [::ActiveRecord::Base]
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.is_a?(thing)
|
8
|
-
thing == ::ActiveRecord::Base ? true : super
|
9
|
-
end
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
class MockActiveRecordRelation < MockRelation
|
14
|
-
|
15
|
-
def is_a?(thing)
|
16
|
-
thing == ::ActiveRecord::Relation ? true : super
|
17
|
-
end
|
18
|
-
|
19
|
-
def engine
|
20
|
-
MockActiveRecord
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
class MockMongoid < MockModel
|
2
|
-
|
3
|
-
def self.include?(thing)
|
4
|
-
thing == ::Mongoid::Document ? true : super
|
5
|
-
end
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
|
-
class MockMongoidCriteria < MockRelation
|
10
|
-
|
11
|
-
def is_a?(thing)
|
12
|
-
thing == ::Mongoid::Criteria ? true : super
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.include?(thing)
|
16
|
-
thing == ::Mongoid::Document ? false : super
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|