search_object 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -2
- data/README.md +5 -5
- data/example/README.md +3 -3
- data/lib/search_object.rb +2 -0
- data/lib/search_object/base.rb +17 -7
- data/lib/search_object/errors.rb +2 -0
- data/lib/search_object/helper.rb +2 -4
- data/lib/search_object/plugin/enum.rb +3 -1
- data/lib/search_object/plugin/kaminari.rb +2 -0
- data/lib/search_object/plugin/model.rb +2 -0
- data/lib/search_object/plugin/paging.rb +2 -0
- data/lib/search_object/plugin/sorting.rb +2 -0
- data/lib/search_object/plugin/will_paginate.rb +2 -0
- data/lib/search_object/search.rb +14 -6
- data/lib/search_object/version.rb +3 -1
- data/spec/search_object/base_spec.rb +17 -0
- data/spec/search_object/helper_spec.rb +0 -18
- data/spec/search_object/plugin/enum_spec.rb +9 -0
- data/spec/search_object/search_spec.rb +38 -21
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f55c0e518e6eb83f63ca7670605b85c7caf3a3736b070759edd7e76688b85ec
|
4
|
+
data.tar.gz: 8435ace3621b80b7b146f9cb78f7586036fb6e849ce660844bbd0f49e4a8d3ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55a03c93e854a7de0aa2412a071a031219105131a036ca96c20f351ff5989566e278ee76b18d776adbed3d553f633ffd03158318667efef235a75748bfebb79c
|
7
|
+
data.tar.gz: 568572db3450627999c39fc0e5304f52dc06bb49cad958fe8f19e8506e17bfd6684b599a64027d91dcdc426f632454ccff89b4123510b624231b5434823fa04e
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
|
4
|
+
## Version 1.2.2
|
5
|
+
|
6
|
+
* __[feature]__ Added `SearchObject::Base#params=` method, to reset search results (@rstankov)
|
7
|
+
* __[change]__ `option :orderBy, enum: %(price date)`, now expects a method `apply_order_by_x`, instead of `apply_orderBy_with_` (__backward incompatible__) (@rstankov)
|
8
|
+
|
3
9
|
## Version 1.2.1
|
4
10
|
|
5
|
-
* __[feature]__
|
11
|
+
* __[feature]__ Added `default:` option to `sort_by` plugin (@rstankov)
|
6
12
|
|
7
13
|
```ruby
|
8
14
|
class ProductSearch
|
@@ -16,7 +22,7 @@ end
|
|
16
22
|
|
17
23
|
## Version 1.2.0
|
18
24
|
|
19
|
-
* __[feature]__ `enum` plugin
|
25
|
+
* __[feature]__ Added `enum` plugin (@rstankov)
|
20
26
|
|
21
27
|
```ruby
|
22
28
|
class ProductSearch
|
data/README.md
CHANGED
@@ -76,7 +76,7 @@ end
|
|
76
76
|
Then you can just search the given scope:
|
77
77
|
|
78
78
|
```ruby
|
79
|
-
search = PostSearch.new
|
79
|
+
search = PostSearch.new(filters: params[:filters])
|
80
80
|
|
81
81
|
# accessing search options
|
82
82
|
search.name # => name option
|
@@ -115,7 +115,7 @@ class ProductSearch
|
|
115
115
|
option :name
|
116
116
|
option :category_name
|
117
117
|
|
118
|
-
# per page defaults to
|
118
|
+
# per page defaults to 10
|
119
119
|
per_page 10
|
120
120
|
|
121
121
|
# range of values is also possible
|
@@ -123,7 +123,7 @@ class ProductSearch
|
|
123
123
|
max_per_page 100
|
124
124
|
end
|
125
125
|
|
126
|
-
search = ProductSearch.new
|
126
|
+
search = ProductSearch.new(filters: params[:filters], page: params[:page], per_page: params[:per_page])
|
127
127
|
|
128
128
|
search.page # => page number
|
129
129
|
search.per_page # => per page (10)
|
@@ -228,7 +228,7 @@ class ProductSearch
|
|
228
228
|
sort_by :name, :price
|
229
229
|
end
|
230
230
|
|
231
|
-
search = ProductSearch.new
|
231
|
+
search = ProductSearch.new(filters: {sort: 'price desc'})
|
232
232
|
|
233
233
|
search.results # => Product sorted my price DESC
|
234
234
|
search.sort_attribute # => 'price'
|
@@ -267,7 +267,7 @@ class ProductSearch
|
|
267
267
|
end
|
268
268
|
|
269
269
|
# first arguments is treated as scope (if no scope option is provided)
|
270
|
-
search = ProductSearch.new
|
270
|
+
search = ProductSearch.new(scope: Product.visible, filters: params[:f])
|
271
271
|
search.results # => includes only visible products
|
272
272
|
```
|
273
273
|
|
data/example/README.md
CHANGED
@@ -19,9 +19,9 @@ This is example application showing, one of the possible usages of ```SearchObje
|
|
19
19
|
```
|
20
20
|
gem install bundler
|
21
21
|
bundle install
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
rails db:create
|
23
|
+
rails db:migrate
|
24
|
+
rails db:seed
|
25
25
|
|
26
26
|
rails server
|
27
27
|
```
|
data/lib/search_object.rb
CHANGED
data/lib/search_object/base.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SearchObject
|
2
4
|
module Base
|
3
5
|
def self.included(base)
|
4
6
|
base.extend ClassMethods
|
5
7
|
base.instance_eval do
|
6
8
|
@config = {
|
7
|
-
defaults:
|
8
|
-
|
9
|
-
scope: nil
|
9
|
+
defaults: {},
|
10
|
+
options: {}
|
10
11
|
}
|
11
12
|
end
|
12
13
|
end
|
@@ -14,12 +15,15 @@ module SearchObject
|
|
14
15
|
def initialize(options = {})
|
15
16
|
config = self.class.config
|
16
17
|
scope = options[:scope] || (config[:scope] && instance_eval(&config[:scope]))
|
17
|
-
actions = config[:actions] || {}
|
18
|
-
params = Helper.normalize_params(config[:defaults], options[:filters], actions.keys)
|
19
18
|
|
20
19
|
raise MissingScopeError unless scope
|
21
20
|
|
22
|
-
@search = Search.new(
|
21
|
+
@search = Search.new(
|
22
|
+
scope: scope,
|
23
|
+
options: config[:options],
|
24
|
+
defaults: config[:defaults],
|
25
|
+
params: options[:filters]
|
26
|
+
)
|
23
27
|
end
|
24
28
|
|
25
29
|
def results
|
@@ -34,6 +38,12 @@ module SearchObject
|
|
34
38
|
@count ||= @search.count self
|
35
39
|
end
|
36
40
|
|
41
|
+
def params=(params)
|
42
|
+
@count = nil
|
43
|
+
@results = nil
|
44
|
+
@search.params = params
|
45
|
+
end
|
46
|
+
|
37
47
|
def params(additions = {})
|
38
48
|
if additions.empty?
|
39
49
|
@search.params
|
@@ -67,7 +77,7 @@ module SearchObject
|
|
67
77
|
handler = options[:with] || block
|
68
78
|
|
69
79
|
config[:defaults][name] = default unless default.nil?
|
70
|
-
config[:
|
80
|
+
config[:options][name] = Helper.normalize_search_handler(handler, name)
|
71
81
|
|
72
82
|
define_method(name) { @search.param name }
|
73
83
|
end
|
data/lib/search_object/errors.rb
CHANGED
data/lib/search_object/helper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SearchObject
|
2
4
|
# :api: private
|
3
5
|
module Helper
|
@@ -54,10 +56,6 @@ module SearchObject
|
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
57
|
-
def normalize_params(defaults, filters, keys)
|
58
|
-
(defaults || {}).merge(slice_keys(stringify_keys(filters || {}), keys || []))
|
59
|
-
end
|
60
|
-
|
61
59
|
def deep_copy(object) # rubocop:disable Metrics/MethodLength
|
62
60
|
case object
|
63
61
|
when Array
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SearchObject
|
2
4
|
module Plugin
|
3
5
|
module Enum
|
@@ -34,7 +36,7 @@ module SearchObject
|
|
34
36
|
return handle_invalid_value(object: object, option: option, enums: enums, scope: scope, value: value)
|
35
37
|
end
|
36
38
|
|
37
|
-
object.send("apply_#{option}_with_#{Helper.underscore(value)}", scope)
|
39
|
+
object.send("apply_#{Helper.underscore(option)}_with_#{Helper.underscore(value)}", scope)
|
38
40
|
end
|
39
41
|
|
40
42
|
def handle_invalid_value(object:, option:, enums:, scope:, value:)
|
data/lib/search_object/search.rb
CHANGED
@@ -1,21 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SearchObject
|
2
4
|
# :api: private
|
3
5
|
class Search
|
4
6
|
attr_reader :params
|
5
7
|
|
6
|
-
def initialize(scope,
|
7
|
-
@scope
|
8
|
-
@
|
9
|
-
@
|
8
|
+
def initialize(scope:, options: nil, defaults: nil, params: nil)
|
9
|
+
@scope = scope
|
10
|
+
@options = options || {}
|
11
|
+
@defaults = defaults || {}
|
12
|
+
|
13
|
+
self.params = params
|
14
|
+
end
|
15
|
+
|
16
|
+
def params=(params)
|
17
|
+
@params = @defaults.merge(Helper.slice_keys(Helper.stringify_keys(params || {}), @options.keys))
|
10
18
|
end
|
11
19
|
|
12
20
|
def param(name)
|
13
|
-
@params[name]
|
21
|
+
@params[name.to_s]
|
14
22
|
end
|
15
23
|
|
16
24
|
def query(context)
|
17
25
|
@params.inject(@scope) do |scope, (name, value)|
|
18
|
-
new_scope = context.instance_exec scope, value, &@
|
26
|
+
new_scope = context.instance_exec scope, value, &@options[name]
|
19
27
|
new_scope || scope
|
20
28
|
end
|
21
29
|
end
|
@@ -282,6 +282,23 @@ module SearchObject
|
|
282
282
|
end
|
283
283
|
end
|
284
284
|
|
285
|
+
describe '#params=' do
|
286
|
+
it 'resets search' do
|
287
|
+
search = new_search [1, 2, 3], value: 1 do
|
288
|
+
option :value do |scope, value|
|
289
|
+
scope.select { |v| v > value }
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
expect(search.results).to eq [2, 3]
|
294
|
+
expect(search.count).to eq 2
|
295
|
+
|
296
|
+
search.params = { value: 2 }
|
297
|
+
expect(search.results).to eq [3]
|
298
|
+
expect(search.count).to eq 1
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
285
302
|
describe '#params' do
|
286
303
|
it 'exports options as params' do
|
287
304
|
search = new_search [], value: 1
|
@@ -41,24 +41,6 @@ module SearchObject
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
describe '.normalize_filters' do
|
45
|
-
it 'combines defaults and filters' do
|
46
|
-
expect(described_class.normalize_params({ 'a' => 1, 'b' => 2 }, { a: 2 }, %w[a b])).to eq 'a' => 2, 'b' => 2
|
47
|
-
end
|
48
|
-
|
49
|
-
it 'excludes non specified keys' do
|
50
|
-
expect(described_class.normalize_params({ 'a' => 1 }, { b: 2 }, %w[a])).to eq 'a' => 1
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'handles missing defaults' do
|
54
|
-
expect(described_class.normalize_params(nil, { a: 1 }, %w[a])).to eq 'a' => 1
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'handles missing filters' do
|
58
|
-
expect(described_class.normalize_params(nil, nil, ['a'])).to eq({})
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
44
|
describe 'deep_copy' do
|
63
45
|
it 'returns a deep copy on the given object' do
|
64
46
|
original = {
|
@@ -10,6 +10,7 @@ module SearchObject
|
|
10
10
|
scope { [1, 2, 3, 4, 5] }
|
11
11
|
|
12
12
|
option :filter, enum: %w[odd even]
|
13
|
+
option :camelCase, enum: %w[someValue]
|
13
14
|
|
14
15
|
private
|
15
16
|
|
@@ -21,6 +22,10 @@ module SearchObject
|
|
21
22
|
scope.select(&:even?)
|
22
23
|
end
|
23
24
|
|
25
|
+
def apply_camel_case_with_some_value(_scope)
|
26
|
+
[1]
|
27
|
+
end
|
28
|
+
|
24
29
|
def handle_invalid_filter(_scope, value)
|
25
30
|
"invalid filter - #{value}"
|
26
31
|
end
|
@@ -40,6 +45,10 @@ module SearchObject
|
|
40
45
|
expect(TestSearch.results(filters: { filter: 'foo' })).to eq 'invalid filter - foo'
|
41
46
|
end
|
42
47
|
|
48
|
+
it 'underscores method and enum values' do
|
49
|
+
expect(TestSearch.results(filters: { camelCase: 'someValue' })).to eq [1]
|
50
|
+
end
|
51
|
+
|
43
52
|
it 'raises when block is passed with enum option' do
|
44
53
|
expect do
|
45
54
|
Class.new do
|
@@ -4,67 +4,84 @@ require 'ostruct'
|
|
4
4
|
module SearchObject
|
5
5
|
describe Search do
|
6
6
|
describe '.params' do
|
7
|
-
it '
|
8
|
-
search =
|
9
|
-
expect(search.params).to eq '
|
7
|
+
it 'stringify param keys' do
|
8
|
+
search = described_class.new(scope: 'scope', params: { name: 'value' }, options: { 'name' => nil })
|
9
|
+
expect(search.params).to eq 'name' => 'value'
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'filters invalid params' do
|
13
|
+
search = described_class.new(scope: 'scope', params: { name: 'value' })
|
14
|
+
expect(search.params).to eq({})
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'supports default values' do
|
18
|
+
search = described_class.new(scope: 'scope', params: {}, defaults: { 'name' => 'value' })
|
19
|
+
expect(search.params).to eq 'name' => 'value'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'can be updated' do
|
23
|
+
search = described_class.new(scope: 'scope', params: { name: 'value' }, options: { 'name' => nil })
|
24
|
+
search.params = { name: 'updated', fake: 'value' }
|
25
|
+
|
26
|
+
expect(search.params).to eq 'name' => 'updated'
|
10
27
|
end
|
11
28
|
end
|
12
29
|
|
13
30
|
describe '.param' do
|
14
31
|
it 'returns the param value' do
|
15
|
-
search =
|
32
|
+
search = described_class.new(scope: 'scope', params: { name: 'value' }, options: { 'name' => nil })
|
16
33
|
expect(search.param(:name)).to eq 'value'
|
17
34
|
end
|
18
35
|
end
|
19
36
|
|
20
37
|
describe '.query' do
|
21
38
|
it 'returns filtered result' do
|
22
|
-
|
23
|
-
min
|
39
|
+
options = {
|
40
|
+
'min' => ->(scope, min) { scope.select { |v| v > min } }
|
24
41
|
}
|
25
42
|
|
26
|
-
search =
|
43
|
+
search = described_class.new(scope: [1, 2, 3], params: { min: 2 }, options: options)
|
27
44
|
expect(search.query(Object.new)).to eq [3]
|
28
45
|
end
|
29
46
|
|
30
|
-
it 'applies
|
31
|
-
|
32
|
-
min
|
33
|
-
max
|
47
|
+
it 'applies options to params' do
|
48
|
+
options = {
|
49
|
+
'min' => ->(scope, min) { scope.select { |v| v > min } },
|
50
|
+
'max' => ->(scope, max) { scope.select { |v| v < max } }
|
34
51
|
}
|
35
52
|
|
36
|
-
search =
|
53
|
+
search = described_class.new(scope: [1, 2, 3, 4, 5], params: { min: 2, max: 5 }, options: options)
|
37
54
|
expect(search.query(Object.new)).to eq [3, 4]
|
38
55
|
end
|
39
56
|
|
40
57
|
it 'handles nil returned from action' do
|
41
|
-
|
42
|
-
odd
|
58
|
+
options = {
|
59
|
+
'odd' => ->(scope, odd) { scope.select(&:odd?) if odd }
|
43
60
|
}
|
44
61
|
|
45
|
-
search =
|
62
|
+
search = described_class.new(scope: [1, 2, 3, 4, 5], params: { odd: false }, options: options)
|
46
63
|
expect(search.query(Object.new)).to eq [1, 2, 3, 4, 5]
|
47
64
|
end
|
48
65
|
|
49
66
|
it 'executes action in the passed context' do
|
50
|
-
|
51
|
-
search
|
67
|
+
options = {
|
68
|
+
'search' => ->(scope, _) { scope.select { |v| v == target_value } }
|
52
69
|
}
|
53
70
|
|
54
71
|
context = OpenStruct.new target_value: 2
|
55
72
|
|
56
|
-
search =
|
73
|
+
search = described_class.new(scope: [1, 2, 3, 4, 5], params: { search: true }, options: options)
|
57
74
|
expect(search.query(context)).to eq [2]
|
58
75
|
end
|
59
76
|
end
|
60
77
|
|
61
78
|
describe '.count' do
|
62
79
|
it 'counts the results of the query' do
|
63
|
-
|
64
|
-
value
|
80
|
+
options = {
|
81
|
+
'value' => ->(scope, value) { scope.select { |v| v == value } }
|
65
82
|
}
|
66
83
|
|
67
|
-
search =
|
84
|
+
search = described_class.new(scope: [1, 2, 3], params: { value: 2 }, options: options)
|
68
85
|
expect(search.count(Object.new)).to eq 1
|
69
86
|
end
|
70
87
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: search_object
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Radoslav Stankov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-06-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -293,8 +293,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
293
293
|
- !ruby/object:Gem::Version
|
294
294
|
version: '0'
|
295
295
|
requirements: []
|
296
|
-
|
297
|
-
rubygems_version: 2.7.6
|
296
|
+
rubygems_version: 3.0.3
|
298
297
|
signing_key:
|
299
298
|
specification_version: 4
|
300
299
|
summary: Provides DSL for creating search objects
|