search_object 1.2.1 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|