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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1d013c77ffd08bf7bb7d36d4f30c10bc763a4ea79c437d80604985de86fadd74
4
- data.tar.gz: a4a996e5b44c64af45806849f3744f06bf7f192d346416943739f172abf45180
3
+ metadata.gz: d5fd5304fedac4c3a0cddd986f91cbc7a6bbe03e31948a1abdbdeaf58b7e16bf
4
+ data.tar.gz: 55e50c5eb694771e367f9741d79ae5510adcf48c05fdd62252895a31157730d2
5
5
  SHA512:
6
- metadata.gz: 24e7c840762d376d5dd9353e3f9b4f167424914e1320573fbcc5cf6dcb691ab5a78f4465261fd2ca029784dd5fac25192c8def19b1f2280401dd3a1452baed7a
7
- data.tar.gz: b6be4607619b5dcae29c012fb5194b640e5a5bf5ad9de94f1cd80532cf9cfdb12d8d61f615297da0531b8fe2fa9504a589d7f3816da2192f3e6b7f603231cb58
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 blank input values. It can also
81
- order the results by one of its defined orders, in either ascending or
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`, and assigns it to the `@search` variable for later use
85
- in the view. The search results are also assigned to a variable for
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](http://www.rubydoc.info/gems/talent_scout/).
100
+ the [API documentation](https://www.rubydoc.info/gems/talent_scout/).
95
101
 
96
102
 
97
- ## Search Objects
103
+ ## Search Classes
98
104
 
99
- You can use the `talent_scout:search` generator to generate search
100
- object class definitions. For example,
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 objects inherit from `TalentScout::ModelSearch`. Their target
114
- model class is inferred from the search object's class name. For
115
- example, `PostSearch` will search for `Post` models by default. To
116
- override this behavior, use `ModelSearch::model_class=`:
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 # will search for Person models instead of `Employee`
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 as model scopes are.
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 is just like `:boolean`, except that the criteria will not be
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 be defined
335
- over multiple columns:
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, and instead fix a column in a static
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, by specifing `default: :asc` or `default: :desc`,
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 a variable for use in
511
- the view, as are the search results. The search results will be an
512
- `ActiveRecord::Relation`, so any additional scoping, such as pagination,
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`; this is **required**.
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 search
589
- object. Such behavior is suitable to generating links to multiple
590
- variants of a search, such as sort links in table column headers.
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, or to generate options for a
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 this line to your application's Gemfile:
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 install
730
+ $ bundle add talent_scout
725
731
  ```
726
732
 
727
- And finally, run the installation generator:
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 `rake test` to run the tests.
742
+ Run `bin/test` to run the tests.
737
743
 
738
744
 
739
745
  ## License
740
746
 
741
- [MIT License](https://opensource.org/licenses/MIT)
747
+ [MIT License](MIT-LICENSE)
data/Rakefile CHANGED
@@ -1,22 +1,3 @@
1
- begin
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-form of the controller name. The
8
- # model search class can also be set with {model_search_class=}.
9
- # If the model search class has not been set, and the default
10
- # class does not exist, a +NameError+ will be raised.
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 has not been set and the default
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 [Class<TalentScout::ModelSearch>]
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 value has not been set (via
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} using the current
50
- # request's query params. If that class does not exist, a
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 == -1 # block from Symbol#to_proc
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 name as the search class, minus the "Search"
11
- # suffix. The model class can also be set with {model_class=}.
12
- # If the model class has not been set, and the default class does
13
- # not exist, a +NameError+ will be raised.
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 has not been set and the default class does
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 [Class]
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 ActiveRecord's
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
- # ActiveRecord, multiple calls to this method will be merged
54
- # together.
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(&:published)
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 to incorporate into the search. Each criteria
82
- # corresponds to an attribute on the search object that can be used
83
- # when building a search form.
84
- #
85
- # Each attribute has a type, just as Active Model attributes do, and
86
- # values passed into the search object are typecasted before
87
- # criteria are evaluated. Supported types are the same as Active
88
- # Model (e.g. +:string+, +:boolean+, +:integer+, etc), with the
89
- # addition of a +:void+ type. A +:void+ type is just like a
90
- # +:boolean+ type, except that the criteria is not evaluated when
91
- # the type-casted value is falsey.
92
- #
93
- # Alternatively, instead of a type, an array or hash of +choices+
94
- # can be specified, and the criteria will be evaluated only if the
95
- # passed-in value matches one of the choices.
96
- #
97
- # Active Model +attribute_options+ can also be specified, most
98
- # notably +:default+ to provide the criteria a default value to
99
- # operate on.
100
- #
101
- # Each criteria can specify a block which recieves the corresponding
102
- # type-casted value as an argument. If the corresponding value is
103
- # not set on the search object (and no default value is defined),
104
- # the criteria will not be evaluated. Like an Active Record
105
- # +scope+ block, a criteria block is evaluated in the context of an
106
- # +ActiveRecord::Relation+ and should return an
107
- # +ActiveRecord::Relation+. A criteria block may also return nil,
108
- # in which case the criteria will be skipped. If no criteria block
109
- # is specified, the criteria will be evaluated as a +where+ clause
110
- # using the criteria name and type-casted value.
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 Implicit block
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 Explicit block
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 (array)
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 (hash)
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
- # @param names [String, Symbol, Array<String>, Array<Symbol>]
213
- # @param type [Symbol, ActiveModel::Type]
214
- # @param choices [Array<String>, Array<Symbol>, Hash<String, Object>, Hash<Symbol, Object>]
215
- # @param attribute_options [Hash]
216
- # @option attribute_options :default [Object]
217
- # @yieldreturn [ActiveRecord::Relation, nil]
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+ are specified and +type+ is other than +:string+
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 an order that the search can apply to its results. Only
254
- # one order can be applied at a time, but an order can be defined
255
- # over multiple columns. If no columns are specified, the order's
256
- # +name+ is taken as its column.
257
- #
258
- # Each order can be applied in an ascending or descending direction
259
- # by appending a corresponding suffix to the order value. By
260
- # default, these suffixes are +".asc"+ and +".desc"+, but they can
261
- # be overridden in the order definition using the +:asc_suffix+ and
262
- # +:desc_suffix+ options, respectively.
263
- #
264
- # Order direction affects all columns of an order defintion, unless
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 apply an order to the search results by default, use the
269
- # +:default+ option in the order definition. (Note that only one
270
- # order can be designated as the default order.)
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
- # See also {toggle_order}.
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
- # @param name [String, Symbol]
331
- # @param columns [Array<String>, Array<Symbol>, nil]
332
- # @param options [Hash]
333
- # @option options :default [Boolean, :asc, :desc] (false)
334
- # @option options :asc_suffix [String] (".asc")
335
- # @option options :desc_suffix [String] (".desc")
336
- # @return [void]
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 in +params+
350
- # to appropriate criteria attributes.
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<String, Object>, Hash<Symbol, Object>, ActionController::Parameters]
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 attribute values,
365
- # and the set or default {order} on top of the {default_scope}.
366
- # Returns an +ActiveRecord::Relation+, allowing further scopes, such
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 model search object with +criteria_values+ merged on
392
- # top of the subject search object's criteria values. Does not
393
- # modify the subject search object.
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<String, Object>, Hash<Symbol, Object>]
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 model search object with the subject search object's
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. Does not modify
418
- # the subject search object.
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 model search object with the specified order applied
445
- # on top of the subject search object's criteria values. If the
446
- # subject search object already has the specified order applied, the
447
- # order's direction will be toggled from +:asc+ to +:desc+ or from
448
- # +:desc+ to +:asc+. Otherwise, the specified order will be applied
449
- # with an +:asc+ direction, overriding any previously applied order.
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 used by the subject
483
- # search object. If no block is given, an +Enumerator+ will be
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 +nil+ if the
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 only one value in the Hash, at most, will be non-+nil+.
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
@@ -1,3 +1,3 @@
1
1
  module TalentScout
2
- VERSION = "1.0.0"
2
+ VERSION = "2.0.0"
3
3
  end
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: 1.0.0
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: 2019-03-17 00:00:00.000000000 Z
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: '5.2'
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: '5.2'
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.hefner@gmail.com
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
- post_install_message:
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: '0'
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: 3.0.1
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: []