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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c19b1b27bb979fcee5671d55ef380f628fb8ba624c274166ad40aa8970d8ad2f
4
- data.tar.gz: 4dc53dd30f010996cea99732d647d44d6d2d3eb07b8a39920b590dabdad5caa1
3
+ metadata.gz: 5f55c0e518e6eb83f63ca7670605b85c7caf3a3736b070759edd7e76688b85ec
4
+ data.tar.gz: 8435ace3621b80b7b146f9cb78f7586036fb6e849ce660844bbd0f49e4a8d3ec
5
5
  SHA512:
6
- metadata.gz: 702e7451fa287d3e98c75dc00fe0977ed4bc901fd72b4bd26fd18089c6abc200f0a03b78be6312b3217a3ecbb193b9a0346cf14834000aecd517968d41b5384b
7
- data.tar.gz: 3b2cdb6312c1ac99afcbb1ea498339c6f732ab38bd45359af4ee84fc7543b5966bc6e5b797fb2525975c478fca4e10d5719487f8da9a87884de13cbcc9509bb5
6
+ metadata.gz: 55a03c93e854a7de0aa2412a071a031219105131a036ca96c20f351ff5989566e278ee76b18d776adbed3d553f633ffd03158318667efef235a75748bfebb79c
7
+ data.tar.gz: 568572db3450627999c39fc0e5304f52dc06bb49cad958fe8f19e8506e17bfd6684b599a64027d91dcdc426f632454ccff89b4123510b624231b5434823fa04e
@@ -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]__ Add `default:` option to `sort_by` plugin (@rstankov)
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 added (@rstankov)
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 filters: params[:filters]
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 25
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 filters: params[:filters], page: params[:page], per_page: params[:per_page]
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 filters: {sort: 'price desc'}
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 scope: Product.visible, filters: params[:f]
270
+ search = ProductSearch.new(scope: Product.visible, filters: params[:f])
271
271
  search.results # => includes only visible products
272
272
  ```
273
273
 
@@ -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
- rake db:create
23
- rake db:migrate
24
- rake db:seed
22
+ rails db:create
23
+ rails db:migrate
24
+ rails db:seed
25
25
 
26
26
  rails server
27
27
  ```
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'search_object/version'
2
4
  require 'search_object/errors'
3
5
  require 'search_object/helper'
@@ -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
- actions: {},
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(scope, params, actions)
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[:actions][name] = Helper.normalize_search_handler(handler, name)
80
+ config[:options][name] = Helper.normalize_search_handler(handler, name)
71
81
 
72
82
  define_method(name) { @search.param name }
73
83
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SearchObject
2
4
  class MissingScopeError < ArgumentError
3
5
  def initialize(message = 'No scope provided. Scope can be defined on a class level or passed as an option.')
@@ -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:)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SearchObject
2
4
  module Plugin
3
5
  module Kaminari
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SearchObject
2
4
  module Plugin
3
5
  module Model
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SearchObject
2
4
  module Plugin
3
5
  module Paging
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SearchObject
2
4
  module Plugin
3
5
  module Sorting
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SearchObject
2
4
  module Plugin
3
5
  module WillPaginate
@@ -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, params, actions)
7
- @scope = scope
8
- @actions = actions
9
- @params = params
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, &@actions[name]
26
+ new_scope = context.instance_exec scope, value, &@options[name]
19
27
  new_scope || scope
20
28
  end
21
29
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SearchObject
2
- VERSION = '1.2.1'.freeze
4
+ VERSION = '1.2.2'.freeze
3
5
  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 'returns the passed params' do
8
- search = Search.new('scope', 'params', {})
9
- expect(search.params).to eq 'params'
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 = Search.new('scope', { name: 'value' }, {})
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
- actions = {
23
- min: ->(scope, min) { scope.select { |v| v > min } }
39
+ options = {
40
+ 'min' => ->(scope, min) { scope.select { |v| v > min } }
24
41
  }
25
42
 
26
- search = Search.new [1, 2, 3], { min: 2 }, actions
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 actions to params' do
31
- actions = {
32
- min: ->(scope, min) { scope.select { |v| v > min } },
33
- max: ->(scope, max) { scope.select { |v| v < 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 = Search.new [1, 2, 3, 4, 5], { min: 2, max: 5 }, actions
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
- actions = {
42
- odd: ->(scope, odd) { scope.select(&:odd?) if odd }
58
+ options = {
59
+ 'odd' => ->(scope, odd) { scope.select(&:odd?) if odd }
43
60
  }
44
61
 
45
- search = Search.new [1, 2, 3, 4, 5], { odd: false }, actions
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
- actions = {
51
- search: ->(scope, _) { scope.select { |v| v == target_value } }
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 = Search.new [1, 2, 3, 4, 5], { search: true }, actions
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
- actions = {
64
- value: ->(scope, value) { scope.select { |v| v == value } }
80
+ options = {
81
+ 'value' => ->(scope, value) { scope.select { |v| v == value } }
65
82
  }
66
83
 
67
- search = Search.new [1, 2, 3], { value: 2 }, actions
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.1
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: 2018-05-20 00:00:00.000000000 Z
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
- rubyforge_project:
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