talent_scout 1.0.0 → 2.0.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 +4 -4
- data/README.md +58 -52
- data/Rakefile +1 -20
- data/lib/talent_scout/controller.rb +10 -12
- data/lib/talent_scout/criteria.rb +2 -1
- data/lib/talent_scout/model_search.rb +134 -104
- data/lib/talent_scout/version.rb +1 -1
- metadata +10 -74
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d5fd5304fedac4c3a0cddd986f91cbc7a6bbe03e31948a1abdbdeaf58b7e16bf
|
|
4
|
+
data.tar.gz: 55e50c5eb694771e367f9741d79ae5510adcf48c05fdd62252895a31157730d2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4ce41f71c08cc80a4acaf3ed9b4b382b3132966f8656ee7b817751e71abdfcc5571234bc66b93f07e022545be4e939f0317b227b9d662732f5f8916a7a2a967
|
|
7
|
+
data.tar.gz: dcc4a74c840c6cb423865794008496788af7bbcdfa5fc8d4a3096bf85448efcba553af1c4e1045d62866a38b4a4ec63f8791a25f4e68c5879b43054893b68274
|
data/README.md
CHANGED
|
@@ -42,10 +42,16 @@ end
|
|
|
42
42
|
```html+erb
|
|
43
43
|
<!-- app/views/posts/index.html.erb -->
|
|
44
44
|
|
|
45
|
-
<%= form_with model: @search, method: :get do |form| %>
|
|
45
|
+
<%= form_with model: @search, local: true, method: :get do |form| %>
|
|
46
|
+
<%= form.label :title_includes %>
|
|
46
47
|
<%= form.text_field :title_includes %>
|
|
48
|
+
|
|
49
|
+
<%= form.label :within %>
|
|
47
50
|
<%= form.select :within, @search.each_choice(:within), include_blank: true %>
|
|
51
|
+
|
|
52
|
+
<%= form.label :only_published %>
|
|
48
53
|
<%= form.check_box :only_published %>
|
|
54
|
+
|
|
49
55
|
<%= form.submit %>
|
|
50
56
|
<% end %>
|
|
51
57
|
|
|
@@ -77,13 +83,13 @@ In the above example:
|
|
|
77
83
|
|
|
78
84
|
* The `PostSearch` class handles the responsibility of searching for
|
|
79
85
|
`Post` models. It can apply any combination of its defined criteria,
|
|
80
|
-
automatically ignoring missing or
|
|
81
|
-
order the results by one of its defined orders, in either
|
|
82
|
-
descending direction.
|
|
86
|
+
automatically ignoring missing, blank, or invalid input values. It
|
|
87
|
+
can also order the results by one of its defined orders, in either
|
|
88
|
+
ascending or descending direction.
|
|
83
89
|
* `PostsController#index` uses the `model_search` helper to construct a
|
|
84
|
-
`PostSearch
|
|
85
|
-
in the view. The search results are also assigned to a
|
|
86
|
-
use in the view.
|
|
90
|
+
`PostSearch` instance, and assigns it to the `@search` variable for
|
|
91
|
+
later use in the view. The search results are also assigned to a
|
|
92
|
+
variable for use in the view.
|
|
87
93
|
* The view uses Rails' stock form builder to build a search form with
|
|
88
94
|
the `@search` variable. The `link_to_search` helper is used to create
|
|
89
95
|
links in the table header which sort the results. Note that the
|
|
@@ -91,13 +97,13 @@ In the above example:
|
|
|
91
97
|
`@search` unmodified.
|
|
92
98
|
|
|
93
99
|
For a detailed explanation of the methods used in this example, see
|
|
94
|
-
the [API documentation](
|
|
100
|
+
the [API documentation](https://www.rubydoc.info/gems/talent_scout/).
|
|
95
101
|
|
|
96
102
|
|
|
97
|
-
## Search
|
|
103
|
+
## Search Classes
|
|
98
104
|
|
|
99
|
-
You can use the `talent_scout:search` generator to generate
|
|
100
|
-
|
|
105
|
+
You can use the `talent_scout:search` generator to generate a model
|
|
106
|
+
search class definition. For example,
|
|
101
107
|
|
|
102
108
|
```bash
|
|
103
109
|
$ rails generate talent_scout:search post
|
|
@@ -110,14 +116,14 @@ class PostSearch < TalentScout::ModelSearch
|
|
|
110
116
|
end
|
|
111
117
|
```
|
|
112
118
|
|
|
113
|
-
Search
|
|
114
|
-
model class is inferred from the search
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
Search classes inherit from `TalentScout::ModelSearch`. Their target
|
|
120
|
+
model class is inferred from the search class name. For example,
|
|
121
|
+
`PostSearch` will search for `Post` models by default. To override this
|
|
122
|
+
inference, use `ModelSearch::model_class=`:
|
|
117
123
|
|
|
118
124
|
```ruby
|
|
119
125
|
class EmployeeSearch < TalentScout::ModelSearch
|
|
120
|
-
self.model_class = Person #
|
|
126
|
+
self.model_class = Person # search for Person models instead of `Employee`
|
|
121
127
|
end
|
|
122
128
|
```
|
|
123
129
|
|
|
@@ -147,7 +153,8 @@ end
|
|
|
147
153
|
```
|
|
148
154
|
|
|
149
155
|
Note that explicit query blocks are evaluated in the context of the
|
|
150
|
-
model's `ActiveRecord::Relation`, just
|
|
156
|
+
model's `ActiveRecord::Relation`, just like Active Record `scope` blocks
|
|
157
|
+
are.
|
|
151
158
|
|
|
152
159
|
|
|
153
160
|
#### Criteria Type
|
|
@@ -180,7 +187,7 @@ Available criteria types are the same as for Active Model attributes:
|
|
|
180
187
|
`:integer`, `:string`, `:time`, plus any custom types you define.
|
|
181
188
|
|
|
182
189
|
An additional convenience type is also available: `:void`. The `:void`
|
|
183
|
-
type
|
|
190
|
+
type typecasts like `:boolean`, but prevents the criteria from being
|
|
184
191
|
applied when the typecasted value is falsey. For example:
|
|
185
192
|
|
|
186
193
|
```ruby
|
|
@@ -331,8 +338,8 @@ PostSearch.new(order: :title)
|
|
|
331
338
|
PostSearch.new(order: :category)
|
|
332
339
|
```
|
|
333
340
|
|
|
334
|
-
Only one order can be applied at a time, but an order can
|
|
335
|
-
|
|
341
|
+
Only one order can be applied at a time, but an order can comprise
|
|
342
|
+
multiple columns:
|
|
336
343
|
|
|
337
344
|
```ruby
|
|
338
345
|
class PostSearch < TalentScout::ModelSearch
|
|
@@ -385,7 +392,7 @@ end
|
|
|
385
392
|
PostSearch.new(order: :category).toggle_order(:category)
|
|
386
393
|
```
|
|
387
394
|
|
|
388
|
-
To circumvent this behavior
|
|
395
|
+
To circumvent this behavior and instead fix a column in a static
|
|
389
396
|
direction, append `" ASC"` or `" DESC"` to the column name:
|
|
390
397
|
|
|
391
398
|
```ruby
|
|
@@ -459,7 +466,7 @@ PostSearch.new(order: :title)
|
|
|
459
466
|
```
|
|
460
467
|
|
|
461
468
|
Note that the default order direction can be either ascending or
|
|
462
|
-
descending
|
|
469
|
+
descending by specifying `default: :asc` or `default: :desc`,
|
|
463
470
|
respectively. Also, just as only one order can be applied at a time,
|
|
464
471
|
only one order can be designated default.
|
|
465
472
|
|
|
@@ -507,10 +514,9 @@ class EmployeesController < ApplicationController
|
|
|
507
514
|
end
|
|
508
515
|
```
|
|
509
516
|
|
|
510
|
-
In these examples, the search object is stored in
|
|
511
|
-
the view
|
|
512
|
-
|
|
513
|
-
can be applied to `@search.results`.
|
|
517
|
+
In these examples, the search object is stored in `@search` for use in
|
|
518
|
+
the view. Note that `@search.results` returns an `ActiveRecord::Relation`,
|
|
519
|
+
so any additional scoping, such as pagination, can be applied.
|
|
514
520
|
|
|
515
521
|
|
|
516
522
|
## Search Forms
|
|
@@ -519,15 +525,21 @@ Search forms can be rendered using Rails' form builder and a search
|
|
|
519
525
|
object:
|
|
520
526
|
|
|
521
527
|
```html+erb
|
|
522
|
-
<%= form_with model: @search, method: :get do |form| %>
|
|
528
|
+
<%= form_with model: @search, local: true, method: :get do |form| %>
|
|
529
|
+
<%= form.label :title_includes %>
|
|
523
530
|
<%= form.text_field :title_includes %>
|
|
531
|
+
|
|
532
|
+
<%= form.label :created_on %>
|
|
524
533
|
<%= form.date_field :created_on %>
|
|
534
|
+
|
|
535
|
+
<%= form.label :only_published %>
|
|
525
536
|
<%= form.check_box :only_published %>
|
|
537
|
+
|
|
526
538
|
<%= form.submit %>
|
|
527
539
|
<% end %>
|
|
528
540
|
```
|
|
529
541
|
|
|
530
|
-
Notice the `method: :get` argument to `form_with
|
|
542
|
+
**Notice the `method: :get` argument to `form_with`. This is required.**
|
|
531
543
|
|
|
532
544
|
Form fields will be populated with the criteria input (or default)
|
|
533
545
|
values of the same name from `@search`. Type-appropriate form fields
|
|
@@ -550,7 +562,7 @@ method:
|
|
|
550
562
|
<%= link_to_search "Sort by title", @search.toggle_order(:title, :asc) %>
|
|
551
563
|
```
|
|
552
564
|
|
|
553
|
-
The link will automatically point to current controller and current
|
|
565
|
+
The link will automatically point to the current controller and current
|
|
554
566
|
action, with query parameters from the given search object. To link to
|
|
555
567
|
a different controller or action, pass an options Hash in place of the
|
|
556
568
|
search object:
|
|
@@ -585,9 +597,9 @@ rendering the view.
|
|
|
585
597
|
One such method is `ModelSearch#toggle_order`, which was shown in
|
|
586
598
|
[previous examples](#order-direction). Remember that `toggle_order` is
|
|
587
599
|
a builder-style method that does not modify the search object. Instead,
|
|
588
|
-
it duplicates the search object, and sets the order on the new
|
|
589
|
-
|
|
590
|
-
|
|
600
|
+
it duplicates the search object, and sets the order on the new object.
|
|
601
|
+
Such behavior is suitable to generating links to multiple variants of a
|
|
602
|
+
search, such as sort links in table column headers.
|
|
591
603
|
|
|
592
604
|
|
|
593
605
|
### `ModelSearch#with` and `ModelSearch#without`
|
|
@@ -629,8 +641,7 @@ PostSearch.new(title: "Maaaaath!", published: false).without(:published)
|
|
|
629
641
|
|
|
630
642
|
Another helpful method is `ModelSearch#each_choice`, which will iterate
|
|
631
643
|
over the defined choices for a given criteria. This can be used to
|
|
632
|
-
generate links to variants of a search
|
|
633
|
-
select box:
|
|
644
|
+
generate links to variants of a search:
|
|
634
645
|
|
|
635
646
|
```ruby
|
|
636
647
|
class PostSearch < TalentScout::ModelSearch
|
|
@@ -645,17 +656,19 @@ end
|
|
|
645
656
|
<% end %>
|
|
646
657
|
```
|
|
647
658
|
|
|
659
|
+
Notice that if the block passed to `each_choice` accepts two arguments,
|
|
660
|
+
the 2nd argument will indicate if the choice is currently chosen.
|
|
661
|
+
|
|
662
|
+
If no block is passed to `each_choice`, it will return an `Enumerator`.
|
|
663
|
+
This can be used to generate options for a select box:
|
|
664
|
+
|
|
648
665
|
```html+erb
|
|
649
|
-
<%= form_with model: @search, method: :get do |form| %>
|
|
666
|
+
<%= form_with model: @search, local: true, method: :get do |form| %>
|
|
650
667
|
<%= form.select :category, @search.each_choice(:category) %>
|
|
651
668
|
<%= form.submit %>
|
|
652
669
|
<% end %>
|
|
653
670
|
```
|
|
654
671
|
|
|
655
|
-
If the block passed to `each_choice` accepts two arguments, the 2nd
|
|
656
|
-
argument will indicate if the choice is currently chosen. If no block
|
|
657
|
-
is passed to `each_choice`, it will return an `Enumerator`.
|
|
658
|
-
|
|
659
672
|
The `each_choice` method can also be invoked with `:order`. Doing so
|
|
660
673
|
will iterate over each direction of each defined order, yielding the
|
|
661
674
|
appropriate labels including direction suffix:
|
|
@@ -668,7 +681,7 @@ end
|
|
|
668
681
|
```
|
|
669
682
|
|
|
670
683
|
```html+erb
|
|
671
|
-
<%= form_with model: @search, method: :get do |form| %>
|
|
684
|
+
<%= form_with model: @search, local: true, method: :get do |form| %>
|
|
672
685
|
<%= form.select :order, @search.each_choice(:order) %>
|
|
673
686
|
<%= form.submit %>
|
|
674
687
|
<% end %>
|
|
@@ -707,24 +720,17 @@ end
|
|
|
707
720
|
|
|
708
721
|
Remember that only one order can be applied at a time, so only one value
|
|
709
722
|
in the Hash, at most, will be non-`nil`.
|
|
710
|
-
>>>>>>> b0d201c... Update README
|
|
711
723
|
|
|
712
724
|
|
|
713
725
|
## Installation
|
|
714
726
|
|
|
715
|
-
Add
|
|
716
|
-
|
|
717
|
-
```ruby
|
|
718
|
-
gem "talent_scout"
|
|
719
|
-
```
|
|
720
|
-
|
|
721
|
-
Then execute:
|
|
727
|
+
Add the gem to your Gemfile:
|
|
722
728
|
|
|
723
729
|
```bash
|
|
724
|
-
$ bundle
|
|
730
|
+
$ bundle add talent_scout
|
|
725
731
|
```
|
|
726
732
|
|
|
727
|
-
And
|
|
733
|
+
And run the installation generator:
|
|
728
734
|
|
|
729
735
|
```bash
|
|
730
736
|
$ rails generate talent_scout:install
|
|
@@ -733,9 +739,9 @@ $ rails generate talent_scout:install
|
|
|
733
739
|
|
|
734
740
|
## Contributing
|
|
735
741
|
|
|
736
|
-
Run `
|
|
742
|
+
Run `bin/test` to run the tests.
|
|
737
743
|
|
|
738
744
|
|
|
739
745
|
## License
|
|
740
746
|
|
|
741
|
-
[MIT License](
|
|
747
|
+
[MIT License](MIT-LICENSE)
|
data/Rakefile
CHANGED
|
@@ -1,22 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
require 'bundler/setup'
|
|
3
|
-
rescue LoadError
|
|
4
|
-
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
require 'yard'
|
|
8
|
-
|
|
9
|
-
YARD::Rake::YardocTask.new(:doc) do |t|
|
|
10
|
-
end
|
|
1
|
+
require 'bundler/setup'
|
|
11
2
|
|
|
12
3
|
require 'bundler/gem_tasks'
|
|
13
|
-
|
|
14
|
-
require 'rake/testtask'
|
|
15
|
-
|
|
16
|
-
Rake::TestTask.new(:test) do |t|
|
|
17
|
-
t.libs << 'test'
|
|
18
|
-
t.test_files = FileList['test/**/*_test.rb'].exclude('test/tmp/**/*')
|
|
19
|
-
t.verbose = false
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
task default: :test
|
|
@@ -4,10 +4,10 @@ module TalentScout
|
|
|
4
4
|
|
|
5
5
|
module ClassMethods
|
|
6
6
|
# Returns the controller model search class. Defaults to a class
|
|
7
|
-
# corresponding to the singular
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
7
|
+
# corresponding to the singular form of the controller name. The
|
|
8
|
+
# class can be set with {model_search_class=}. If the class has
|
|
9
|
+
# not been set and the default class does not exist, a +NameError+
|
|
10
|
+
# will be raised.
|
|
11
11
|
#
|
|
12
12
|
# @example
|
|
13
13
|
# class PostsController < ApplicationController
|
|
@@ -17,8 +17,7 @@ module TalentScout
|
|
|
17
17
|
#
|
|
18
18
|
# @return [Class<TalentScout::ModelSearch>]
|
|
19
19
|
# @raise [NameError]
|
|
20
|
-
# if the model search class
|
|
21
|
-
# class does not exist
|
|
20
|
+
# if the model search class does not exist
|
|
22
21
|
def model_search_class
|
|
23
22
|
@model_search_class ||= "#{controller_path.classify}Search".constantize
|
|
24
23
|
end
|
|
@@ -26,14 +25,13 @@ module TalentScout
|
|
|
26
25
|
# Sets the controller model search class. See {model_search_class}.
|
|
27
26
|
#
|
|
28
27
|
# @param klass [Class<TalentScout::ModelSearch>]
|
|
29
|
-
# @return [
|
|
28
|
+
# @return [klass]
|
|
30
29
|
def model_search_class=(klass)
|
|
31
30
|
@model_search_class = klass
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
# Similar to {model_search_class}, but returns nil instead of
|
|
35
|
-
# raising an error when the
|
|
36
|
-
# {model_search_class=}) and the default class does not exist.
|
|
34
|
+
# raising an error when the model search class does not exist.
|
|
37
35
|
#
|
|
38
36
|
# @return [Class<TalentScout::ModelSearch>, nil]
|
|
39
37
|
def model_search_class?
|
|
@@ -46,9 +44,9 @@ module TalentScout
|
|
|
46
44
|
end
|
|
47
45
|
end
|
|
48
46
|
|
|
49
|
-
# Instantiates {ClassMethods#model_search_class}
|
|
50
|
-
# request's query params. If that class does not
|
|
51
|
-
# +NameError+ will be raised.
|
|
47
|
+
# Instantiates {ClassMethods#model_search_class ::model_search_class}
|
|
48
|
+
# using the current request's query params. If that class does not
|
|
49
|
+
# exist, a +NameError+ will be raised.
|
|
52
50
|
#
|
|
53
51
|
# @return [TalentScout::ModelSearch]
|
|
54
52
|
# @raise [NameError]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module TalentScout
|
|
2
2
|
# @!visibility private
|
|
3
3
|
class Criteria
|
|
4
|
+
SYMBOL_TO_PROC_ARITY = :to_s.to_proc.arity
|
|
4
5
|
|
|
5
6
|
attr_reader :names, :allow_nil, :block
|
|
6
7
|
|
|
@@ -14,7 +15,7 @@ module TalentScout
|
|
|
14
15
|
if applicable?(attribute_set)
|
|
15
16
|
if block
|
|
16
17
|
block_args = names.map{|name| attribute_set[name].value }
|
|
17
|
-
if block.arity ==
|
|
18
|
+
if block.arity == SYMBOL_TO_PROC_ARITY # assume block is from Symbol#to_proc
|
|
18
19
|
scope.instance_exec(scope, *block_args, &block)
|
|
19
20
|
else
|
|
20
21
|
scope.instance_exec(*block_args, &block)
|
|
@@ -7,10 +7,10 @@ module TalentScout
|
|
|
7
7
|
extend ActiveModel::Translation
|
|
8
8
|
|
|
9
9
|
# Returns the model class that the search targets. Defaults to a
|
|
10
|
-
# class with same name
|
|
11
|
-
# suffix. The
|
|
12
|
-
#
|
|
13
|
-
#
|
|
10
|
+
# class with same name as the search class, minus the "Search"
|
|
11
|
+
# suffix. The class can be set with {model_class=}. If the class
|
|
12
|
+
# has not been set and the default class does not exist, a
|
|
13
|
+
# +NameError+ will be raised.
|
|
14
14
|
#
|
|
15
15
|
# @example Default behavior
|
|
16
16
|
# class PostSearch < TalentScout::ModelSearch
|
|
@@ -27,8 +27,7 @@ module TalentScout
|
|
|
27
27
|
#
|
|
28
28
|
# @return [Class]
|
|
29
29
|
# @raise [NameError]
|
|
30
|
-
# if the model class
|
|
31
|
-
# not exist
|
|
30
|
+
# if the model class does not exist
|
|
32
31
|
def self.model_class
|
|
33
32
|
@model_class ||= self.superclass == ModelSearch ?
|
|
34
33
|
self.name.chomp("Search").constantize : self.superclass.model_class
|
|
@@ -37,7 +36,7 @@ module TalentScout
|
|
|
37
36
|
# Sets the model class that the search targets. See {model_class}.
|
|
38
37
|
#
|
|
39
38
|
# @param model_class [Class]
|
|
40
|
-
# @return [
|
|
39
|
+
# @return [model_class]
|
|
41
40
|
def self.model_class=(model_class)
|
|
42
41
|
@model_class = model_class
|
|
43
42
|
end
|
|
@@ -47,11 +46,11 @@ module TalentScout
|
|
|
47
46
|
@model_name ||= ModelName.new(self)
|
|
48
47
|
end
|
|
49
48
|
|
|
50
|
-
# Sets the default scope of the search. Like
|
|
49
|
+
# Sets the default scope of the search. Like Active Record's
|
|
51
50
|
# +default_scope+, the scope here is specified as a block which is
|
|
52
51
|
# evaluated in the context of the {model_class}. Also like
|
|
53
|
-
#
|
|
54
|
-
#
|
|
52
|
+
# Active Record, multiple calls of this method will append to the
|
|
53
|
+
# default scope.
|
|
55
54
|
#
|
|
56
55
|
# @example
|
|
57
56
|
# class PostSearch < TalentScout::ModelSearch
|
|
@@ -66,7 +65,7 @@ module TalentScout
|
|
|
66
65
|
# end
|
|
67
66
|
#
|
|
68
67
|
# class PostSearch < TalentScout::ModelSearch
|
|
69
|
-
# default_scope
|
|
68
|
+
# default_scope &:published
|
|
70
69
|
# end
|
|
71
70
|
#
|
|
72
71
|
# PostSearch.new.results # == Post.published
|
|
@@ -78,36 +77,36 @@ module TalentScout
|
|
|
78
77
|
criteria_list.insert(i, Criteria.new([], true, &block))
|
|
79
78
|
end
|
|
80
79
|
|
|
81
|
-
# Defines criteria
|
|
82
|
-
#
|
|
83
|
-
# when building a search form.
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
# +ActiveRecord::Relation
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
80
|
+
# Defines criteria that can be incorporated into a search. The
|
|
81
|
+
# criteria will have a corresponding attribute on the search object
|
|
82
|
+
# that can be used when building a search form. The type of the
|
|
83
|
+
# attribute can be specified, just as with Active Model attributes,
|
|
84
|
+
# and the attribute value will be typecasted before the criteria is
|
|
85
|
+
# incorporated. Supported types are the same as Active Model (e.g.
|
|
86
|
+
# +:string+, +:boolean+, +:integer+, etc). The default type is
|
|
87
|
+
# +:string+. An additional +:void+ type is also supported, which
|
|
88
|
+
# typecasts like the +:boolean+ type, but prevents the criteria
|
|
89
|
+
# from being incorporated if the typecasted value is falsey.
|
|
90
|
+
#
|
|
91
|
+
# Alternatively, instead of a type, an Array or Hash of +choices+
|
|
92
|
+
# can be specified, and the criteria will be incorporated only if
|
|
93
|
+
# the attribute value matches one of the choices.
|
|
94
|
+
#
|
|
95
|
+
# Active Model +attribute_options+ can also be specified. Most
|
|
96
|
+
# notably, the +:default+ option can specify a default value for the
|
|
97
|
+
# criteria to operate on. If a default value is not specified, the
|
|
98
|
+
# criteria will be incorporated only if the attribute value is set.
|
|
99
|
+
#
|
|
100
|
+
# If no block is given, the criteria will be incorporated as a
|
|
101
|
+
# +where+ clause using the criteria name and typecasted attribute
|
|
102
|
+
# value.
|
|
103
|
+
#
|
|
104
|
+
# If a block is given, it will be passed the typecasted attribute
|
|
105
|
+
# value, and it will be evaluated in the context of an
|
|
106
|
+
# +ActiveRecord::Relation+, like Active Record +scope+ blocks are.
|
|
107
|
+
# The block should return an +ActiveRecord::Relation+. The block
|
|
108
|
+
# may also return nil, in which case the criteria will not be
|
|
109
|
+
# incorporated.
|
|
111
110
|
#
|
|
112
111
|
# As a convenient shorthand, Active Record scopes which have been
|
|
113
112
|
# defined on the {model_class} can be used directly as criteria
|
|
@@ -115,7 +114,7 @@ module TalentScout
|
|
|
115
114
|
# the criteria block.
|
|
116
115
|
#
|
|
117
116
|
#
|
|
118
|
-
# @example
|
|
117
|
+
# @example Without a block
|
|
119
118
|
# class PostSearch < TalentScout::ModelSearch
|
|
120
119
|
# criteria :title
|
|
121
120
|
# end
|
|
@@ -123,7 +122,7 @@ module TalentScout
|
|
|
123
122
|
# PostSearch.new(title: "FOO").results # == Post.where(title: "FOO")
|
|
124
123
|
#
|
|
125
124
|
#
|
|
126
|
-
# @example
|
|
125
|
+
# @example With a block
|
|
127
126
|
# class PostSearch < TalentScout::ModelSearch
|
|
128
127
|
# criteria :title do |string|
|
|
129
128
|
# where("title LIKE ?", "%#{string}%")
|
|
@@ -171,7 +170,7 @@ module TalentScout
|
|
|
171
170
|
# PostSearch.new(only_edited: "1").results # == Post.where("modified_at > created_at")
|
|
172
171
|
#
|
|
173
172
|
#
|
|
174
|
-
# @example Specifying choices (
|
|
173
|
+
# @example Specifying choices (Array)
|
|
175
174
|
# class PostSearch < TalentScout::ModelSearch
|
|
176
175
|
# criteria :category, choices: %w[science tech engineering math]
|
|
177
176
|
# end
|
|
@@ -180,7 +179,7 @@ module TalentScout
|
|
|
180
179
|
# PostSearch.new(category: "BLAH").results # == Post.all
|
|
181
180
|
#
|
|
182
181
|
#
|
|
183
|
-
# @example Specifying choices (
|
|
182
|
+
# @example Specifying choices (Hash)
|
|
184
183
|
# class PostSearch < TalentScout::ModelSearch
|
|
185
184
|
# criteria :within, choices: {
|
|
186
185
|
# "Last 24 hours" => 24.hours,
|
|
@@ -209,15 +208,38 @@ module TalentScout
|
|
|
209
208
|
# PostSearch.new(within_days: 2).results # == Post.where("created_at >= ?", 2.days.ago)
|
|
210
209
|
#
|
|
211
210
|
#
|
|
212
|
-
# @
|
|
213
|
-
#
|
|
214
|
-
#
|
|
215
|
-
#
|
|
216
|
-
#
|
|
217
|
-
#
|
|
211
|
+
# @overload criteria(name, type = :string, **attribute_options)
|
|
212
|
+
# @param name [String, Symbol]
|
|
213
|
+
# @param type [Symbol, ActiveModel::Type]
|
|
214
|
+
# @param attribute_options [Hash]
|
|
215
|
+
# @option attribute_options :default [Object]
|
|
216
|
+
#
|
|
217
|
+
# @overload criteria(name, type = :string, **attribute_options, &block)
|
|
218
|
+
# @param name [String, Symbol]
|
|
219
|
+
# @param type [Symbol, ActiveModel::Type]
|
|
220
|
+
# @param attribute_options [Hash]
|
|
221
|
+
# @option attribute_options :default [Object]
|
|
222
|
+
# @yieldparam value [Object]
|
|
223
|
+
# @yieldreturn [ActiveRecord::Relation, nil]
|
|
224
|
+
#
|
|
225
|
+
# @overload criteria(name, choices:, **attribute_options)
|
|
226
|
+
# @param name [String, Symbol]
|
|
227
|
+
# @param choices [Array<String>, Array<Symbol>, Hash{String => Object}, Hash{Symbol => Object}]
|
|
228
|
+
# @param attribute_options [Hash]
|
|
229
|
+
# @option attribute_options :default [Object]
|
|
230
|
+
# @yieldreturn [ActiveRecord::Relation, nil]
|
|
231
|
+
#
|
|
232
|
+
# @overload criteria(name, choices:, **attribute_options, &block)
|
|
233
|
+
# @param name [String, Symbol]
|
|
234
|
+
# @param choices [Array<String>, Array<Symbol>, Hash{String => Object}, Hash{Symbol => Object}]
|
|
235
|
+
# @param attribute_options [Hash]
|
|
236
|
+
# @option attribute_options :default [Object]
|
|
237
|
+
# @yieldparam value [Object]
|
|
238
|
+
# @yieldreturn [ActiveRecord::Relation, nil]
|
|
239
|
+
#
|
|
218
240
|
# @return [void]
|
|
219
241
|
# @raise [ArgumentError]
|
|
220
|
-
# if +choices+
|
|
242
|
+
# if +choices+ is specified and +type+ is not +:string+
|
|
221
243
|
def self.criteria(names, type = :string, choices: nil, **attribute_options, &block)
|
|
222
244
|
if choices
|
|
223
245
|
if type != :string
|
|
@@ -237,7 +259,7 @@ module TalentScout
|
|
|
237
259
|
criteria_list << crit
|
|
238
260
|
|
|
239
261
|
crit.names.each do |name|
|
|
240
|
-
attribute name, type, attribute_options
|
|
262
|
+
attribute name, type, **attribute_options
|
|
241
263
|
|
|
242
264
|
# HACK FormBuilder#select uses normal attribute readers instead
|
|
243
265
|
# of `*_before_type_cast` attribute readers. This breaks value
|
|
@@ -250,26 +272,26 @@ module TalentScout
|
|
|
250
272
|
end
|
|
251
273
|
end
|
|
252
274
|
|
|
253
|
-
# Defines
|
|
254
|
-
# one order can be
|
|
255
|
-
#
|
|
256
|
-
# +name+ is taken as its column.
|
|
257
|
-
#
|
|
258
|
-
#
|
|
259
|
-
# by appending
|
|
260
|
-
# default, these suffixes are +".asc"+ and +".desc"+, but they
|
|
261
|
-
# be overridden in the order definition using the +:asc_suffix+
|
|
262
|
-
# +:desc_suffix+ options, respectively.
|
|
263
|
-
#
|
|
264
|
-
#
|
|
275
|
+
# Defines a result order that can be incorporated into a search.
|
|
276
|
+
# Only one order can be incorporated at a time, but an order can
|
|
277
|
+
# comprise multiple columns. If no columns are specified, the
|
|
278
|
+
# order's +name+ is taken as its column.
|
|
279
|
+
#
|
|
280
|
+
# An order can be incorporated in either ascending or descending
|
|
281
|
+
# direction by appending an appropriate suffix to the order value.
|
|
282
|
+
# By default, these suffixes are +".asc"+ and +".desc"+, but they
|
|
283
|
+
# can be overridden in the order definition using the +:asc_suffix+
|
|
284
|
+
# and +:desc_suffix+ options, respectively.
|
|
285
|
+
#
|
|
286
|
+
# The order direction will affect all columns of the order, unless
|
|
265
287
|
# a column explicitly specifies +"ASC"+ or +"DESC"+, in which case
|
|
266
288
|
# that column will stay fixed in its specified direction.
|
|
267
289
|
#
|
|
268
|
-
# To
|
|
269
|
-
# +:default+ option
|
|
270
|
-
#
|
|
290
|
+
# To designate the order as the default result order, use the
|
|
291
|
+
# +:default+ option. (Note that only one order can be designated as
|
|
292
|
+
# the default order.)
|
|
271
293
|
#
|
|
272
|
-
#
|
|
294
|
+
# @see toggle_order
|
|
273
295
|
#
|
|
274
296
|
#
|
|
275
297
|
# @example Single-column order
|
|
@@ -327,44 +349,50 @@ module TalentScout
|
|
|
327
349
|
# PostSearch.new(order: :title).results # == Post.order("title")
|
|
328
350
|
#
|
|
329
351
|
#
|
|
330
|
-
# @
|
|
331
|
-
#
|
|
332
|
-
#
|
|
333
|
-
#
|
|
334
|
-
#
|
|
335
|
-
#
|
|
336
|
-
#
|
|
352
|
+
# @overload order(name, columns = [name], **options)
|
|
353
|
+
# @param name [String, Symbol]
|
|
354
|
+
# @param columns [Array<String>, Array<Symbol>]
|
|
355
|
+
# @param options [Hash]
|
|
356
|
+
# @option options :default [Boolean, :asc, :desc] (false)
|
|
357
|
+
# @option options :asc_suffix [String] (".asc")
|
|
358
|
+
# @option options :desc_suffix [String] (".desc")
|
|
359
|
+
# @return [void]
|
|
337
360
|
def self.order(name, columns = nil, default: false, **options)
|
|
338
|
-
definition = OrderDefinition.new(name, columns, options)
|
|
361
|
+
definition = OrderDefinition.new(name, columns, **options)
|
|
339
362
|
|
|
340
363
|
if !attribute_types.fetch("order", nil).equal?(order_type) || default
|
|
341
364
|
criteria_options = default ? { default: definition.choice_for_direction(default) } : {}
|
|
342
365
|
criteria_list.reject!{|crit| crit.names == ["order"] }
|
|
343
|
-
criteria "order", order_type, criteria_options, &:order
|
|
366
|
+
criteria "order", order_type, **criteria_options, &:order
|
|
344
367
|
end
|
|
345
368
|
|
|
346
369
|
order_type.add_definition(definition)
|
|
347
370
|
end
|
|
348
371
|
|
|
349
|
-
# Initializes a +ModelSearch+ instance. Assigns values
|
|
350
|
-
# to
|
|
372
|
+
# Initializes a +ModelSearch+ instance. Assigns values from
|
|
373
|
+
# +params+ to corresponding criteria attributes.
|
|
351
374
|
#
|
|
352
375
|
# If +params+ is a +ActionController::Parameters+, blank values are
|
|
353
376
|
# ignored. This behavior prevents empty search form fields from
|
|
354
377
|
# affecting search results.
|
|
355
378
|
#
|
|
356
|
-
# @param params [Hash
|
|
379
|
+
# @param params [Hash{String => Object}, Hash{Symbol => Object}, ActionController::Parameters]
|
|
380
|
+
# @raise [ActiveModel::UnknownAttributeError]
|
|
381
|
+
# if +params+ is a Hash, and it contains an unrecognized key
|
|
357
382
|
def initialize(params = {})
|
|
383
|
+
# HACK initialize ActiveRecord state required by ActiveRecord::AttributeMethods::BeforeTypeCast
|
|
384
|
+
@transaction_state ||= nil
|
|
385
|
+
|
|
358
386
|
if params.is_a?(ActionController::Parameters)
|
|
359
387
|
params = params.permit(self.class.attribute_types.keys).reject!{|key, value| value.blank? }
|
|
360
388
|
end
|
|
361
389
|
super(params)
|
|
362
390
|
end
|
|
363
391
|
|
|
364
|
-
# Applies search {criteria} with set or default
|
|
365
|
-
# and the set or default {order}
|
|
366
|
-
# Returns an +ActiveRecord::Relation+, allowing
|
|
367
|
-
# as pagination, to be applied post-hoc.
|
|
392
|
+
# Applies the {default_scope}, search {criteria} with set or default
|
|
393
|
+
# attribute values, and the set or default {order} to the
|
|
394
|
+
# {model_class}. Returns an +ActiveRecord::Relation+, allowing
|
|
395
|
+
# further scopes, such as pagination, to be applied post-hoc.
|
|
368
396
|
#
|
|
369
397
|
# @example
|
|
370
398
|
# class PostSearch < TalentScout::ModelSearch
|
|
@@ -383,14 +411,15 @@ module TalentScout
|
|
|
383
411
|
#
|
|
384
412
|
# @return [ActiveRecord::Relation]
|
|
385
413
|
def results
|
|
386
|
-
self.class.criteria_list.reduce(self.class.model_class) do |scope, crit|
|
|
414
|
+
self.class.criteria_list.reduce(self.class.model_class.all) do |scope, crit|
|
|
387
415
|
crit.apply(scope, attribute_set)
|
|
388
416
|
end
|
|
389
417
|
end
|
|
390
418
|
|
|
391
|
-
# Builds a new
|
|
392
|
-
#
|
|
393
|
-
#
|
|
419
|
+
# Builds a new search object with +criteria_values+ merged on top of
|
|
420
|
+
# the subject search object's criteria values.
|
|
421
|
+
#
|
|
422
|
+
# Does not modify the subject search object.
|
|
394
423
|
#
|
|
395
424
|
# @example
|
|
396
425
|
# class PostSearch < TalentScout::ModelSearch
|
|
@@ -404,7 +433,7 @@ module TalentScout
|
|
|
404
433
|
# search.with(category: "tech").results # == Post.where(category: "tech")
|
|
405
434
|
# search.results # == Post.where(category: "math")
|
|
406
435
|
#
|
|
407
|
-
# @param criteria_values [Hash
|
|
436
|
+
# @param criteria_values [Hash{String => Object}, Hash{Symbol => Object}]
|
|
408
437
|
# @return [TalentScout::ModelSearch]
|
|
409
438
|
# @raise [ActiveModel::UnknownAttributeError]
|
|
410
439
|
# if one or more +criteria_values+ keys are invalid
|
|
@@ -412,10 +441,11 @@ module TalentScout
|
|
|
412
441
|
self.class.new(attributes.merge!(criteria_values.stringify_keys))
|
|
413
442
|
end
|
|
414
443
|
|
|
415
|
-
# Builds a new
|
|
444
|
+
# Builds a new search object with the subject search object's
|
|
416
445
|
# criteria values, excluding values specified by +criteria_names+.
|
|
417
|
-
# Default criteria values will still be applied.
|
|
418
|
-
#
|
|
446
|
+
# Default criteria values will still be applied.
|
|
447
|
+
#
|
|
448
|
+
# Does not modify the subject search object.
|
|
419
449
|
#
|
|
420
450
|
# @example
|
|
421
451
|
# class PostSearch < TalentScout::ModelSearch
|
|
@@ -441,12 +471,12 @@ module TalentScout
|
|
|
441
471
|
self.class.new(attributes.except!(*criteria_names))
|
|
442
472
|
end
|
|
443
473
|
|
|
444
|
-
# Builds a new
|
|
445
|
-
#
|
|
446
|
-
#
|
|
447
|
-
#
|
|
448
|
-
#
|
|
449
|
-
#
|
|
474
|
+
# Builds a new search object with the specified order applied on top
|
|
475
|
+
# of the subject search object's criteria values. If the subject
|
|
476
|
+
# search object already has the specified order applied, the order's
|
|
477
|
+
# direction will be toggled from +:asc+ to +:desc+ or from +:desc+
|
|
478
|
+
# to +:asc+. Otherwise, the specified order will be applied with an
|
|
479
|
+
# +:asc+ direction, overriding any previously applied order.
|
|
450
480
|
#
|
|
451
481
|
# If +direction+ is explicitly specified, that direction will be
|
|
452
482
|
# applied regardless of previously applied direction.
|
|
@@ -479,10 +509,10 @@ module TalentScout
|
|
|
479
509
|
|
|
480
510
|
# Iterates over a specified {criteria}'s defined choices. If the
|
|
481
511
|
# given block accepts a 2nd argument, a boolean will be passed
|
|
482
|
-
# indicating whether that choice is currently
|
|
483
|
-
#
|
|
484
|
-
# returned.
|
|
512
|
+
# indicating whether that choice is currently assigned to the
|
|
513
|
+
# specified criteria attribute.
|
|
485
514
|
#
|
|
515
|
+
# If no block is given, an Enumerator is returned instead.
|
|
486
516
|
#
|
|
487
517
|
# @example With block
|
|
488
518
|
# class PostSearch < TalentScout::ModelSearch
|
|
@@ -547,9 +577,9 @@ module TalentScout
|
|
|
547
577
|
|
|
548
578
|
# Returns a +HashWithIndifferentAccess+ with a key for each defined
|
|
549
579
|
# {order}. Each key's associated value indicates that order's
|
|
550
|
-
# currently applied direction -- +:asc+, +:desc+, or
|
|
580
|
+
# currently applied direction -- +:asc+, +:desc+, or nil if the
|
|
551
581
|
# order is not applied. Note that only one order can be applied at
|
|
552
|
-
# a time, so
|
|
582
|
+
# a time, so, at most, one value in the Hash will be non-nil.
|
|
553
583
|
#
|
|
554
584
|
# @example
|
|
555
585
|
# class PostSearch < TalentScout::ModelSearch
|
data/lib/talent_scout/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: talent_scout
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jonathan Hefner
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rails
|
|
@@ -16,79 +15,16 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
18
|
+
version: '8.1'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: sqlite3
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - ">="
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0'
|
|
34
|
-
type: :development
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - ">="
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: capybara
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '2.15'
|
|
48
|
-
- - "<"
|
|
49
|
-
- !ruby/object:Gem::Version
|
|
50
|
-
version: '4.0'
|
|
51
|
-
type: :development
|
|
52
|
-
prerelease: false
|
|
53
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
54
|
-
requirements:
|
|
55
|
-
- - ">="
|
|
56
|
-
- !ruby/object:Gem::Version
|
|
57
|
-
version: '2.15'
|
|
58
|
-
- - "<"
|
|
59
|
-
- !ruby/object:Gem::Version
|
|
60
|
-
version: '4.0'
|
|
61
|
-
- !ruby/object:Gem::Dependency
|
|
62
|
-
name: yard
|
|
63
|
-
requirement: !ruby/object:Gem::Requirement
|
|
64
|
-
requirements:
|
|
65
|
-
- - "~>"
|
|
66
|
-
- !ruby/object:Gem::Version
|
|
67
|
-
version: '0.9'
|
|
68
|
-
type: :development
|
|
69
|
-
prerelease: false
|
|
70
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
71
|
-
requirements:
|
|
72
|
-
- - "~>"
|
|
73
|
-
- !ruby/object:Gem::Version
|
|
74
|
-
version: '0.9'
|
|
75
|
-
- !ruby/object:Gem::Dependency
|
|
76
|
-
name: simple_form
|
|
77
|
-
requirement: !ruby/object:Gem::Requirement
|
|
78
|
-
requirements:
|
|
79
|
-
- - "~>"
|
|
80
|
-
- !ruby/object:Gem::Version
|
|
81
|
-
version: '4.0'
|
|
82
|
-
type: :development
|
|
83
|
-
prerelease: false
|
|
84
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
85
|
-
requirements:
|
|
86
|
-
- - "~>"
|
|
87
|
-
- !ruby/object:Gem::Version
|
|
88
|
-
version: '4.0'
|
|
89
|
-
description:
|
|
25
|
+
version: '8.1'
|
|
90
26
|
email:
|
|
91
|
-
- jonathan
|
|
27
|
+
- jonathan@hefner.pro
|
|
92
28
|
executables: []
|
|
93
29
|
extensions: []
|
|
94
30
|
extra_rdoc_files: []
|
|
@@ -117,8 +53,9 @@ files:
|
|
|
117
53
|
homepage: https://github.com/jonathanhefner/talent_scout
|
|
118
54
|
licenses:
|
|
119
55
|
- MIT
|
|
120
|
-
metadata:
|
|
121
|
-
|
|
56
|
+
metadata:
|
|
57
|
+
source_code_uri: https://github.com/jonathanhefner/talent_scout
|
|
58
|
+
changelog_uri: https://github.com/jonathanhefner/talent_scout/blob/master/CHANGELOG.md
|
|
122
59
|
rdoc_options: []
|
|
123
60
|
require_paths:
|
|
124
61
|
- lib
|
|
@@ -126,15 +63,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
126
63
|
requirements:
|
|
127
64
|
- - ">="
|
|
128
65
|
- !ruby/object:Gem::Version
|
|
129
|
-
version: '
|
|
66
|
+
version: '3.4'
|
|
130
67
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
68
|
requirements:
|
|
132
69
|
- - ">="
|
|
133
70
|
- !ruby/object:Gem::Version
|
|
134
71
|
version: '0'
|
|
135
72
|
requirements: []
|
|
136
|
-
rubygems_version:
|
|
137
|
-
signing_key:
|
|
73
|
+
rubygems_version: 4.0.10
|
|
138
74
|
specification_version: 4
|
|
139
75
|
summary: Model-backed searches in Rails
|
|
140
76
|
test_files: []
|