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 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