search_object 1.2.1 → 1.2.5

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +18 -3
  3. data/.travis.yml +4 -2
  4. data/CHANGELOG.md +19 -2
  5. data/Gemfile +2 -0
  6. data/README.md +5 -5
  7. data/Rakefile +2 -0
  8. data/example/Gemfile +3 -1
  9. data/example/README.md +3 -3
  10. data/example/Rakefile +3 -1
  11. data/example/app/controllers/application_controller.rb +2 -0
  12. data/example/app/controllers/posts_controller.rb +2 -0
  13. data/example/app/models/post.rb +2 -0
  14. data/example/app/models/post_search.rb +6 -4
  15. data/example/app/models/user.rb +2 -0
  16. data/example/app/views/posts/index.html.slim +1 -1
  17. data/example/bin/bundle +3 -1
  18. data/example/bin/rails +3 -1
  19. data/example/bin/rake +2 -0
  20. data/example/config.ru +2 -0
  21. data/example/db/migrate/20131102130117_create_users.rb +1 -1
  22. data/example/db/migrate/20131102130413_create_posts.rb +1 -2
  23. data/example/db/schema.rb +19 -23
  24. data/example/spec/models/post_search_spec.rb +19 -19
  25. data/example/spec/spec_helper.rb +4 -2
  26. data/lib/search_object/base.rb +27 -7
  27. data/lib/search_object/errors.rb +2 -0
  28. data/lib/search_object/helper.rb +2 -4
  29. data/lib/search_object/plugin/enum.rb +6 -10
  30. data/lib/search_object/plugin/kaminari.rb +2 -0
  31. data/lib/search_object/plugin/model.rb +2 -0
  32. data/lib/search_object/plugin/paging.rb +8 -3
  33. data/lib/search_object/plugin/sorting.rb +2 -0
  34. data/lib/search_object/plugin/will_paginate.rb +2 -0
  35. data/lib/search_object/search.rb +14 -6
  36. data/lib/search_object/version.rb +3 -1
  37. data/lib/search_object.rb +2 -0
  38. data/search_object.gemspec +3 -3
  39. data/spec/search_object/base_spec.rb +58 -0
  40. data/spec/search_object/helper_spec.rb +2 -18
  41. data/spec/search_object/plugin/enum_spec.rb +57 -31
  42. data/spec/search_object/plugin/kaminari_spec.rb +2 -0
  43. data/spec/search_object/plugin/model_spec.rb +2 -0
  44. data/spec/search_object/plugin/paging_spec.rb +2 -0
  45. data/spec/search_object/plugin/sorting_spec.rb +2 -0
  46. data/spec/search_object/plugin/will_paginate_spec.rb +2 -0
  47. data/spec/search_object/search_spec.rb +40 -21
  48. data/spec/spec_helper.rb +2 -0
  49. data/spec/spec_helper_active_record.rb +2 -0
  50. data/spec/support/paging_shared_example.rb +2 -0
  51. metadata +14 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c19b1b27bb979fcee5671d55ef380f628fb8ba624c274166ad40aa8970d8ad2f
4
- data.tar.gz: 4dc53dd30f010996cea99732d647d44d6d2d3eb07b8a39920b590dabdad5caa1
3
+ metadata.gz: c01187c2852ea65c1c340bc37e62527c9d4133691ded6a9db4ac5dea78441b1d
4
+ data.tar.gz: 177bf4f66eccf02f9195a9aa2b8563f8d1de4465be812157f9eeec1ac90121a6
5
5
  SHA512:
6
- metadata.gz: 702e7451fa287d3e98c75dc00fe0977ed4bc901fd72b4bd26fd18089c6abc200f0a03b78be6312b3217a3ecbb193b9a0346cf14834000aecd517968d41b5384b
7
- data.tar.gz: 3b2cdb6312c1ac99afcbb1ea498339c6f732ab38bd45359af4ee84fc7543b5966bc6e5b797fb2525975c478fca4e10d5719487f8da9a87884de13cbcc9509bb5
6
+ metadata.gz: 3cd78261ad83342fec752d1e54714afe7971841ae5afa90270bd90a8a1713cbb2aa06fdbad3489be78127195666015918f9f356ec1f4c9809b201f5631fc0795
7
+ data.tar.gz: 575474f4d79ff650bccb1cb9e9d33cb540d4b85dfc18a23487e1239e6a6833bf4da7dc52193829d2c116a51c9089736720eb6ac5db5868b814558fe15675fc34
data/.rubocop.yml CHANGED
@@ -7,15 +7,15 @@ AllCops:
7
7
  - search_object.gemspec
8
8
 
9
9
  # Disables "Line is too long"
10
- LineLength:
10
+ Layout/LineLength:
11
11
  Enabled: false
12
12
 
13
13
  # Disables Module has too many lines
14
- ModuleLength:
14
+ Metrics/ModuleLength:
15
15
  Enabled: false
16
16
 
17
17
  # Disables "Missing top-level class documentation comment"
18
- Documentation:
18
+ Style/Documentation:
19
19
  Enabled: false
20
20
 
21
21
  # Disables "Use each_with_object instead of inject"
@@ -37,3 +37,18 @@ RSpec/ExampleLength:
37
37
  # Disables "Too many expectations."
38
38
  RSpec/MultipleExpectations:
39
39
  Enabled: false
40
+
41
+ Lint/RaiseException:
42
+ Enabled: true
43
+
44
+ Lint/StructNewOverride:
45
+ Enabled: true
46
+
47
+ Style/HashEachMethods:
48
+ Enabled: true
49
+
50
+ Style/HashTransformKeys:
51
+ Enabled: true
52
+
53
+ Style/HashTransformValues:
54
+ Enabled: true
data/.travis.yml CHANGED
@@ -1,7 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2
4
- - 2.3
3
+ - 2.4
4
+ - 2.6
5
+ - 2.7
6
+ - 3.0
5
7
  script:
6
8
  - bundle exec rubocop
7
9
  - bundle exec rspec spec
data/CHANGELOG.md CHANGED
@@ -1,8 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## Version 1.2.5
4
+
5
+ * __[feature]__ Added `param?` method to `Search::Base` (@rstankov)
6
+
7
+ ## Version 1.2.4
8
+
9
+ * __[feature]__ Added `:with` and block support to enum (@hschne)
10
+
11
+ ## Version 1.2.3
12
+
13
+ * __[fix]__ convert enum values to strings (@Postmodum37)
14
+
15
+ ## Version 1.2.2
16
+
17
+ * __[feature]__ Added `SearchObject::Base#params=` method, to reset search results (@rstankov)
18
+ * __[change]__ `option :orderBy, enum: %(price date)`, now expects a method `apply_order_by_x`, instead of `apply_orderBy_with_` (__backward incompatible__) (@rstankov)
19
+
3
20
  ## Version 1.2.1
4
21
 
5
- * __[feature]__ Add `default:` option to `sort_by` plugin (@rstankov)
22
+ * __[feature]__ Added `default:` option to `sort_by` plugin (@rstankov)
6
23
 
7
24
  ```ruby
8
25
  class ProductSearch
@@ -16,7 +33,7 @@ end
16
33
 
17
34
  ## Version 1.2.0
18
35
 
19
- * __[feature]__ `enum` plugin added (@rstankov)
36
+ * __[feature]__ Added `enum` plugin (@rstankov)
20
37
 
21
38
  ```ruby
22
39
  class ProductSearch
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in search_object.gemspec
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
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
  require 'rubocop/rake_task'
data/example/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
- gem 'rails', '5.0.1'
5
+ gem 'rails', '6.0.2.1'
4
6
 
5
7
  gem 'bootstrap-sass'
6
8
  gem 'jquery-rails'
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
- 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
  ```
data/example/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Add your own tasks in files placed in lib/tasks ending in .rake,
2
4
  # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
5
 
4
- require File.expand_path('../config/application', __FILE__)
6
+ require File.expand_path('config/application', __dir__)
5
7
 
6
8
  Example::Application.load_tasks
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ApplicationController < ActionController::Base
2
4
  protect_from_forgery with: :exception
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class PostsController < ApplicationController
2
4
  def index
3
5
  @search = PostSearch.new filters: params[:f], page: params[:page], per_page: params[:per_page]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Post < ActiveRecord::Base
2
4
  belongs_to :user
3
5
 
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class PostSearch
2
4
  include SearchObject.module(:model, :sorting, :will_paginate, :enum)
3
5
 
4
- scope { Post.all }
6
+ scope { Post.includes(:user).all }
5
7
 
6
8
  sort_by :id, :created_at, :views_count, :likes_count, :comments_count
7
9
 
@@ -18,11 +20,11 @@ class PostSearch
18
20
  option :rating, enum: %i[low high]
19
21
 
20
22
  option :title do |scope, value|
21
- scope.where 'title LIKE ?', escape_search_term(value)
23
+ scope.where 'title LIKE ?', escape_search_term(value) if value.present?
22
24
  end
23
25
 
24
26
  option :published do |scope, value|
25
- scope.where published: true if value.present?
27
+ scope.where published: true if value.present? && value != '0'
26
28
  end
27
29
 
28
30
  option :created_after do |scope, value|
@@ -38,7 +40,7 @@ class PostSearch
38
40
  private
39
41
 
40
42
  def apply_term(scope, value)
41
- scope.where 'title LIKE :term OR body LIKE :term', term: escape_search_term(value)
43
+ scope.where 'title LIKE :term OR body LIKE :term', term: escape_search_term(value) if value.present?
42
44
  end
43
45
 
44
46
  def apply_rating_with_low(scope)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class User < ActiveRecord::Base
2
4
  has_many :posts
3
5
 
@@ -32,7 +32,7 @@
32
32
  th
33
33
  th = form.text_field :title
34
34
  th = form.select :user_id, User.all.map { |u| [u.name, u.id] }, include_blank: true
35
- th = form.select :category_name, Post.pluck('DISTINCT category_name'), include_blank: true
35
+ th = form.select :category_name, Post.pluck(Arel.sql('DISTINCT category_name')), include_blank: true
36
36
  th
37
37
  th
38
38
  th
data/example/bin/bundle CHANGED
@@ -1,3 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
2
+ # frozen_string_literal: true
3
+
4
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3
5
  load Gem.bin_path('bundler', 'bundle')
data/example/bin/rails CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- APP_PATH = File.expand_path('../../config/application', __FILE__)
2
+ # frozen_string_literal: true
3
+
4
+ APP_PATH = File.expand_path('../config/application', __dir__)
3
5
  require_relative '../config/boot'
4
6
  require 'rails/commands'
data/example/bin/rake CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require_relative '../config/boot'
3
5
  require 'rake'
4
6
  Rake.application.run
data/example/config.ru CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is used by Rack-based servers to start the application.
2
4
 
3
5
  require ::File.expand_path('../config/environment', __FILE__)
@@ -1,4 +1,4 @@
1
- class CreateUsers < ActiveRecord::Migration
1
+ class CreateUsers < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  create_table :users do |t|
4
4
  t.string :name, null: false
@@ -1,4 +1,4 @@
1
- class CreatePosts < ActiveRecord::Migration
1
+ class CreatePosts < ActiveRecord::Migration[4.2]
2
2
  def change
3
3
  create_table :posts do |t|
4
4
  t.integer :user_id, null: false
@@ -9,7 +9,6 @@ class CreatePosts < ActiveRecord::Migration
9
9
  t.integer :likes_count, null: false, default: 0
10
10
  t.integer :comments_count, null: false, default: 0
11
11
  t.boolean :published, null: false, default: false
12
- t.datetime :published_at
13
12
  t.timestamps
14
13
  end
15
14
 
data/example/db/schema.rb CHANGED
@@ -1,40 +1,36 @@
1
- # encoding: UTF-8
2
1
  # This file is auto-generated from the current state of the database. Instead
3
2
  # of editing this file, please use the migrations feature of Active Record to
4
3
  # incrementally modify your database, and then regenerate this schema definition.
5
4
  #
6
- # Note that this schema.rb definition is the authoritative source for your
7
- # database schema. If you need to create the application database on another
8
- # system, you should be using db:schema:load, not running all the migrations
9
- # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
- # you'll amass, the slower it'll run and the greater likelihood for issues).
5
+ # This file is the source Rails uses to define your schema when running `rails
6
+ # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
7
+ # be faster and is potentially less error prone than running all of your
8
+ # migrations from scratch. Old migrations may fail to apply correctly if those
9
+ # migrations use external dependencies or application code.
11
10
  #
12
11
  # It's strongly recommended that you check this file into your version control system.
13
12
 
14
- ActiveRecord::Schema.define(version: 20131102130413) do
13
+ ActiveRecord::Schema.define(version: 2013_11_02_130413) do
15
14
 
16
- create_table "posts", force: true do |t|
17
- t.integer "user_id", null: false
18
- t.string "title", null: false
19
- t.string "body", null: false
20
- t.string "category_name", null: false
21
- t.integer "views_count", default: 0, null: false
22
- t.integer "likes_count", default: 0, null: false
23
- t.integer "comments_count", default: 0, null: false
24
- t.boolean "published", default: false, null: false
25
- t.datetime "published_at"
15
+ create_table "posts", force: :cascade do |t|
16
+ t.integer "user_id", null: false
17
+ t.string "title", null: false
18
+ t.string "body", null: false
19
+ t.string "category_name", null: false
20
+ t.integer "views_count", default: 0, null: false
21
+ t.integer "likes_count", default: 0, null: false
22
+ t.integer "comments_count", default: 0, null: false
23
+ t.boolean "published", default: false, null: false
26
24
  t.datetime "created_at"
27
25
  t.datetime "updated_at"
26
+ t.index ["user_id"], name: "index_posts_on_user_id"
28
27
  end
29
28
 
30
- add_index "posts", ["user_id"], name: "index_posts_on_user_id"
31
-
32
- create_table "users", force: true do |t|
33
- t.string "name", null: false
29
+ create_table "users", force: :cascade do |t|
30
+ t.string "name", limit: 255, null: false
34
31
  t.datetime "created_at"
35
32
  t.datetime "updated_at"
33
+ t.index ["name"], name: "index_users_on_name", unique: true
36
34
  end
37
35
 
38
- add_index "users", ["name"], name: "index_users_on_name", unique: true
39
-
40
36
  end
@@ -1,4 +1,4 @@
1
- # rubocop:disable Lint/UselessAssignment:%s
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
4
 
@@ -11,11 +11,11 @@ describe PostSearch do
11
11
 
12
12
  def create(attributes = {})
13
13
  Post.create! attributes.reverse_merge(
14
- user: user,
15
- title: 'Title',
16
- body: 'Body',
17
- category_name: 'Tech',
18
- published: true
14
+ user: user,
15
+ title: 'Title',
16
+ body: 'Body',
17
+ category_name: 'Tech',
18
+ published: true
19
19
  )
20
20
  end
21
21
 
@@ -24,29 +24,29 @@ describe PostSearch do
24
24
  end
25
25
 
26
26
  it 'can search by category name' do
27
- post = create category_name: 'Personal'
28
- other = create category_name: 'Other'
27
+ post = create category_name: 'Personal'
28
+ _other = create category_name: 'Other'
29
29
 
30
30
  expect_search(category_name: 'Personal').to eq [post]
31
31
  end
32
32
 
33
33
  it 'can search by user_id' do
34
- post = create user: create_user
35
- other = create user: create_user
34
+ post = create user: create_user
35
+ _other = create user: create_user
36
36
 
37
37
  expect_search(user_id: post.user_id).to eq [post]
38
38
  end
39
39
 
40
40
  it 'can search by title' do
41
- post = create title: 'Title'
42
- other = create title: 'Other'
41
+ post = create title: 'Title'
42
+ _other = create title: 'Other'
43
43
 
44
44
  expect_search(title: 'itl').to eq [post]
45
45
  end
46
46
 
47
47
  it 'can search by published' do
48
- post = create published: true
49
- other = create published: false
48
+ post = create published: true
49
+ _other = create published: false
50
50
 
51
51
  expect_search(published: true).to eq [post]
52
52
  end
@@ -54,21 +54,21 @@ describe PostSearch do
54
54
  it 'can search by term' do
55
55
  post_with_body = create body: 'pattern'
56
56
  post_with_title = create title: 'pattern'
57
- other = create
57
+ _other = create
58
58
 
59
59
  expect_search(term: 'pattern').to eq [post_with_title, post_with_body]
60
60
  end
61
61
 
62
62
  it 'can search by created after' do
63
- post = create created_at: 1.month.ago
64
- other = create created_at: 3.month.ago
63
+ post = create created_at: 1.month.ago
64
+ _other = create created_at: 3.month.ago
65
65
 
66
66
  expect_search(created_after: 2.month.ago.strftime('%Y-%m-%d')).to eq [post]
67
67
  end
68
68
 
69
69
  it 'can search by created before' do
70
- post = create created_at: 3.month.ago
71
- other = create created_at: 1.month.ago
70
+ post = create created_at: 3.month.ago
71
+ _other = create created_at: 1.month.ago
72
72
 
73
73
  expect_search(created_before: 2.month.ago.strftime('%Y-%m-%d')).to eq [post]
74
74
  end
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is copied to spec/ when you run 'rails generate rspec:install'
2
4
  ENV['RAILS_ENV'] ||= 'test'
3
5
 
4
- require File.expand_path('../../config/environment', __FILE__)
6
+ require File.expand_path('../config/environment', __dir__)
5
7
  require 'rspec/rails'
6
8
 
7
9
  # Requires supporting ruby files with custom matchers and macros, etc,
8
10
  # in spec/support/ and its subdirectories.
9
- Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
11
+ Dir[Rails.root.join('spec/support/**/*.rb')].sort.each { |f| require f }
10
12
 
11
13
  # Checks for pending migrations before tests are run.
12
14
  # If you are not using ActiveRecord, you can remove this line.
@@ -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
@@ -42,6 +52,16 @@ module SearchObject
42
52
  end
43
53
  end
44
54
 
55
+ def param?(*args)
56
+ if args.size == 1
57
+ params.key?(args[0].to_s)
58
+ elsif args.size == 2
59
+ params[args[0].to_s] == args[1]
60
+ else
61
+ raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1 or 2)"
62
+ end
63
+ end
64
+
45
65
  private
46
66
 
47
67
  def fetch_results
@@ -67,7 +87,7 @@ module SearchObject
67
87
  handler = options[:with] || block
68
88
 
69
89
  config[:defaults][name] = default unless default.nil?
70
- config[:actions][name] = Helper.normalize_search_handler(handler, name)
90
+ config[:options][name] = Helper.normalize_search_handler(handler, name)
71
91
 
72
92
  define_method(name) { @search.param name }
73
93
  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
@@ -9,10 +11,7 @@ module SearchObject
9
11
  def option(name, options = nil, &block)
10
12
  return super unless options.is_a?(Hash) && options[:enum]
11
13
 
12
- raise BlockIgnoredError if block
13
- raise WithIgnoredError if options[:with]
14
-
15
- handler = Handler.build(name, options[:enum])
14
+ handler = options[:with] || block || Handler.build(name, options[:enum])
16
15
 
17
16
  super(name, options, &handler)
18
17
  end
@@ -30,19 +29,16 @@ module SearchObject
30
29
  def apply_filter(object:, option:, enums:, scope:, value:)
31
30
  return if value.nil? || value == ''
32
31
 
33
- unless enums.include? value
34
- return handle_invalid_value(object: object, option: option, enums: enums, scope: scope, value: value)
35
- end
32
+ return handle_invalid_value(object: object, option: option, enums: enums, scope: scope, value: value) unless enums.include? value.to_s
36
33
 
37
- object.send("apply_#{option}_with_#{Helper.underscore(value)}", scope)
34
+ object.send("apply_#{Helper.underscore(option)}_with_#{Helper.underscore(value)}", scope)
38
35
  end
39
36
 
40
37
  def handle_invalid_value(object:, option:, enums:, scope:, value:)
41
38
  specific = "handle_invalid_#{option}"
42
39
  return object.send(specific, scope, value) if object.respond_to? specific, true
43
40
 
44
- catch_all = 'handle_invalid_enum'
45
- return object.send(catch_all, option, scope, value) if object.respond_to? catch_all, true
41
+ return object.handle_invalid_enum(option, scope, value) if object.respond_to? :handle_invalid_enum, true
46
42
 
47
43
  raise InvalidEnumValueError.new(option, enums, value)
48
44
  end
@@ -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
@@ -26,17 +28,20 @@ module SearchObject
26
28
 
27
29
  module ClassMethods
28
30
  def per_page(number)
29
- raise InvalidNumberError.new('Per page', number) unless number > 0
31
+ raise InvalidNumberError.new('Per page', number) unless number.positive?
32
+
30
33
  config[:per_page] = number
31
34
  end
32
35
 
33
36
  def min_per_page(number)
34
- raise InvalidNumberError.new('Min per page', number) unless number > 0
37
+ raise InvalidNumberError.new('Min per page', number) unless number.positive?
38
+
35
39
  config[:min_per_page] = number
36
40
  end
37
41
 
38
42
  def max_per_page(number)
39
- raise InvalidNumberError.new('Max per page', number) unless number > 0
43
+ raise InvalidNumberError.new('Max per page', number) unless number.positive?
44
+
40
45
  config[:max_per_page] = number
41
46
  end
42
47
 
@@ -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.5'
3
5
  end
data/lib/search_object.rb CHANGED
@@ -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'
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.13"
21
+ spec.add_development_dependency "bundler"
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency 'rspec', '~> 3.5'
24
24
  spec.add_development_dependency 'rspec-mocks', '~> 3.5'
@@ -29,6 +29,6 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency 'will_paginate'
30
30
  spec.add_development_dependency 'kaminari'
31
31
  spec.add_development_dependency 'kaminari-activerecord'
32
- spec.add_development_dependency 'rubocop', '0.51.0'
33
- spec.add_development_dependency 'rubocop-rspec', '1.20.1'
32
+ spec.add_development_dependency 'rubocop', '0.81.0'
33
+ spec.add_development_dependency 'rubocop-rspec', '1.38.1'
34
34
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'active_support/core_ext/object/blank'
3
5
 
@@ -282,6 +284,23 @@ module SearchObject
282
284
  end
283
285
  end
284
286
 
287
+ describe '#params=' do
288
+ it 'resets search' do
289
+ search = new_search [1, 2, 3], value: 1 do
290
+ option :value do |scope, value|
291
+ scope.select { |v| v > value }
292
+ end
293
+ end
294
+
295
+ expect(search.results).to eq [2, 3]
296
+ expect(search.count).to eq 2
297
+
298
+ search.params = { value: 2 }
299
+ expect(search.results).to eq [3]
300
+ expect(search.count).to eq 1
301
+ end
302
+ end
303
+
285
304
  describe '#params' do
286
305
  it 'exports options as params' do
287
306
  search = new_search [], value: 1
@@ -310,5 +329,44 @@ module SearchObject
310
329
  expect(search.params).to eq 'value' => 1
311
330
  end
312
331
  end
332
+
333
+ describe '#param?' do
334
+ context 'one argument' do
335
+ it 'returns true when params is set' do
336
+ search = new_search([], value: 2) do
337
+ option :value
338
+ end
339
+
340
+ expect(search.param?(:value)).to eq true
341
+ end
342
+
343
+ it 'returns false when params isnt set' do
344
+ search = new_search do
345
+ option :value
346
+ end
347
+
348
+ expect(search.param?(:value)).to eq false
349
+ end
350
+ end
351
+
352
+ context 'two arguments' do
353
+ it 'returns true when param matches value' do
354
+ search = new_search([], value: 2) do
355
+ option :value
356
+ end
357
+
358
+ expect(search.param?(:value, 2)).to eq true
359
+ end
360
+
361
+ it 'returns false when param matches value' do
362
+ search = new_search([], value: 2) do
363
+ option :value
364
+ end
365
+
366
+ expect(search.param?(:value, 3)).to eq false
367
+ expect(search.param?(:other, 3)).to eq false
368
+ end
369
+ end
370
+ end
313
371
  end
314
372
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'action_controller'
3
5
 
@@ -41,24 +43,6 @@ module SearchObject
41
43
  end
42
44
  end
43
45
 
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
46
  describe 'deep_copy' do
63
47
  it 'returns a deep copy on the given object' do
64
48
  original = {
@@ -1,63 +1,89 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'ostruct'
3
5
 
4
6
  module SearchObject
5
7
  module Plugin
6
8
  describe Enum do
7
- class TestSearch
8
- include SearchObject.module(:enum)
9
+ let(:test_search) do
10
+ Class.new do
11
+ include SearchObject.module(:enum)
9
12
 
10
- scope { [1, 2, 3, 4, 5] }
13
+ scope { [1, 2, 3, 4, 5] }
11
14
 
12
- option :filter, enum: %w[odd even]
15
+ option :filter, enum: %w[odd even]
16
+ option :camelCase, enum: %w[someValue]
13
17
 
14
- private
18
+ private
15
19
 
16
- def apply_filter_with_odd(scope)
17
- scope.select(&:odd?)
18
- end
20
+ def apply_filter_with_odd(scope)
21
+ scope.select(&:odd?)
22
+ end
19
23
 
20
- def apply_filter_with_even(scope)
21
- scope.select(&:even?)
22
- end
24
+ def apply_filter_with_even(scope)
25
+ scope.select(&:even?)
26
+ end
23
27
 
24
- def handle_invalid_filter(_scope, value)
25
- "invalid filter - #{value}"
28
+ def apply_camel_case_with_some_value(_scope)
29
+ [1]
30
+ end
31
+
32
+ def handle_invalid_filter(_scope, value)
33
+ "invalid filter - #{value}"
34
+ end
26
35
  end
27
36
  end
28
37
 
29
38
  it 'can filter by enum values' do
30
- expect(TestSearch.results(filters: { filter: 'odd' })).to eq [1, 3, 5]
31
- expect(TestSearch.results(filters: { filter: 'even' })).to eq [2, 4]
39
+ expect(test_search.results(filters: { filter: 'odd' })).to eq [1, 3, 5]
40
+ expect(test_search.results(filters: { filter: 'even' })).to eq [2, 4]
41
+ end
42
+
43
+ it 'converts input to string' do
44
+ expect(test_search.results(filters: { filter: :odd })).to eq [1, 3, 5]
45
+ expect(test_search.results(filters: { filter: :even })).to eq [2, 4]
32
46
  end
33
47
 
34
48
  it 'ignores blank values' do
35
- expect(TestSearch.results(filters: { filter: nil })).to eq [1, 2, 3, 4, 5]
36
- expect(TestSearch.results(filters: { filter: '' })).to eq [1, 2, 3, 4, 5]
49
+ expect(test_search.results(filters: { filter: nil })).to eq [1, 2, 3, 4, 5]
50
+ expect(test_search.results(filters: { filter: '' })).to eq [1, 2, 3, 4, 5]
37
51
  end
38
52
 
39
53
  it 'handles wrong enum values' do
40
- expect(TestSearch.results(filters: { filter: 'foo' })).to eq 'invalid filter - foo'
54
+ expect(test_search.results(filters: { filter: 'foo' })).to eq 'invalid filter - foo'
41
55
  end
42
56
 
43
- it 'raises when block is passed with enum option' do
44
- expect do
45
- Class.new do
46
- include SearchObject.module(:enum)
57
+ it 'underscores method and enum values' do
58
+ expect(test_search.results(filters: { camelCase: 'someValue' })).to eq [1]
59
+ end
47
60
 
48
- option(:filter, enum: %w[a b]) { |_scope, _value| nil }
49
- end
50
- end.to raise_error Enum::BlockIgnoredError
61
+ it 'can filter by passed block' do
62
+ block_search = Class.new do
63
+ include SearchObject.module(:enum)
64
+
65
+ scope { [1, 2, 3, 4, 5] }
66
+
67
+ option(:filter, enum: %w[odd even]) { |scope, value| scope.select(&:"#{value}?".to_sym) }
68
+ end
69
+ expect(block_search.results(filters: { filter: :odd })).to eq [1, 3, 5]
70
+ expect(block_search.results(filters: { filter: :even })).to eq [2, 4]
51
71
  end
52
72
 
53
- it 'raises when :with is passed with enum option' do
54
- expect do
55
- Class.new do
56
- include SearchObject.module(:enum)
73
+ it 'can filter by with option' do
74
+ with_search = Class.new do
75
+ include SearchObject.module(:enum)
76
+
77
+ scope { [1, 2, 3, 4, 5] }
57
78
 
58
- option :filter, enum: %w[a b], with: :method_name
79
+ option(:filter, enum: %w[odd even], with: :filter)
80
+
81
+ def filter(scope, value)
82
+ scope.select(&:"#{value}?".to_sym)
59
83
  end
60
- end.to raise_error Enum::WithIgnoredError
84
+ end
85
+ expect(with_search.results(filters: { filter: :odd })).to eq [1, 3, 5]
86
+ expect(with_search.results(filters: { filter: :even })).to eq [2, 4]
61
87
  end
62
88
  end
63
89
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper_active_record'
2
4
  require 'kaminari'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  require 'active_support/core_ext/object/blank'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper_active_record'
2
4
 
3
5
  module SearchObject
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper_active_record'
2
4
 
3
5
  module SearchObject
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper_active_record'
2
4
 
3
5
  require 'will_paginate'
@@ -1,70 +1,89 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'ostruct'
3
5
 
4
6
  module SearchObject
5
7
  describe Search do
6
8
  describe '.params' do
7
- it 'returns the passed params' do
8
- search = Search.new('scope', 'params', {})
9
- expect(search.params).to eq 'params'
9
+ it 'stringify param keys' do
10
+ search = described_class.new(scope: 'scope', params: { name: 'value' }, options: { 'name' => nil })
11
+ expect(search.params).to eq 'name' => 'value'
12
+ end
13
+
14
+ it 'filters invalid params' do
15
+ search = described_class.new(scope: 'scope', params: { name: 'value' })
16
+ expect(search.params).to eq({})
17
+ end
18
+
19
+ it 'supports default values' do
20
+ search = described_class.new(scope: 'scope', params: {}, defaults: { 'name' => 'value' })
21
+ expect(search.params).to eq 'name' => 'value'
22
+ end
23
+
24
+ it 'can be updated' do
25
+ search = described_class.new(scope: 'scope', params: { name: 'value' }, options: { 'name' => nil })
26
+ search.params = { name: 'updated', fake: 'value' }
27
+
28
+ expect(search.params).to eq 'name' => 'updated'
10
29
  end
11
30
  end
12
31
 
13
32
  describe '.param' do
14
33
  it 'returns the param value' do
15
- search = Search.new('scope', { name: 'value' }, {})
34
+ search = described_class.new(scope: 'scope', params: { name: 'value' }, options: { 'name' => nil })
16
35
  expect(search.param(:name)).to eq 'value'
17
36
  end
18
37
  end
19
38
 
20
39
  describe '.query' do
21
40
  it 'returns filtered result' do
22
- actions = {
23
- min: ->(scope, min) { scope.select { |v| v > min } }
41
+ options = {
42
+ 'min' => ->(scope, min) { scope.select { |v| v > min } }
24
43
  }
25
44
 
26
- search = Search.new [1, 2, 3], { min: 2 }, actions
45
+ search = described_class.new(scope: [1, 2, 3], params: { min: 2 }, options: options)
27
46
  expect(search.query(Object.new)).to eq [3]
28
47
  end
29
48
 
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 } }
49
+ it 'applies options to params' do
50
+ options = {
51
+ 'min' => ->(scope, min) { scope.select { |v| v > min } },
52
+ 'max' => ->(scope, max) { scope.select { |v| v < max } }
34
53
  }
35
54
 
36
- search = Search.new [1, 2, 3, 4, 5], { min: 2, max: 5 }, actions
55
+ search = described_class.new(scope: [1, 2, 3, 4, 5], params: { min: 2, max: 5 }, options: options)
37
56
  expect(search.query(Object.new)).to eq [3, 4]
38
57
  end
39
58
 
40
59
  it 'handles nil returned from action' do
41
- actions = {
42
- odd: ->(scope, odd) { scope.select(&:odd?) if odd }
60
+ options = {
61
+ 'odd' => ->(scope, odd) { scope.select(&:odd?) if odd }
43
62
  }
44
63
 
45
- search = Search.new [1, 2, 3, 4, 5], { odd: false }, actions
64
+ search = described_class.new(scope: [1, 2, 3, 4, 5], params: { odd: false }, options: options)
46
65
  expect(search.query(Object.new)).to eq [1, 2, 3, 4, 5]
47
66
  end
48
67
 
49
68
  it 'executes action in the passed context' do
50
- actions = {
51
- search: ->(scope, _) { scope.select { |v| v == target_value } }
69
+ options = {
70
+ 'search' => ->(scope, _) { scope.select { |v| v == target_value } }
52
71
  }
53
72
 
54
73
  context = OpenStruct.new target_value: 2
55
74
 
56
- search = Search.new [1, 2, 3, 4, 5], { search: true }, actions
75
+ search = described_class.new(scope: [1, 2, 3, 4, 5], params: { search: true }, options: options)
57
76
  expect(search.query(context)).to eq [2]
58
77
  end
59
78
  end
60
79
 
61
80
  describe '.count' do
62
81
  it 'counts the results of the query' do
63
- actions = {
64
- value: ->(scope, value) { scope.select { |v| v == value } }
82
+ options = {
83
+ 'value' => ->(scope, value) { scope.select { |v| v == value } }
65
84
  }
66
85
 
67
- search = Search.new [1, 2, 3], { value: 2 }, actions
86
+ search = described_class.new(scope: [1, 2, 3], params: { value: 2 }, options: options)
68
87
  expect(search.count(Object.new)).to eq 1
69
88
  end
70
89
  end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/setup'
2
4
 
3
5
  if ENV['TRAVIS']
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'spec_helper'
2
4
  require_relative 'support/paging_shared_example'
3
5
  require 'active_record'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  shared_examples_for 'a paging plugin' do
2
4
  after do
3
5
  Product.delete_all
metadata CHANGED
@@ -1,29 +1,29 @@
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.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Radoslav Stankov
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-20 00:00:00.000000000 Z
11
+ date: 2021-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.13'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.13'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -170,28 +170,28 @@ dependencies:
170
170
  requirements:
171
171
  - - '='
172
172
  - !ruby/object:Gem::Version
173
- version: 0.51.0
173
+ version: 0.81.0
174
174
  type: :development
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
178
  - - '='
179
179
  - !ruby/object:Gem::Version
180
- version: 0.51.0
180
+ version: 0.81.0
181
181
  - !ruby/object:Gem::Dependency
182
182
  name: rubocop-rspec
183
183
  requirement: !ruby/object:Gem::Requirement
184
184
  requirements:
185
185
  - - '='
186
186
  - !ruby/object:Gem::Version
187
- version: 1.20.1
187
+ version: 1.38.1
188
188
  type: :development
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - '='
193
193
  - !ruby/object:Gem::Version
194
- version: 1.20.1
194
+ version: 1.38.1
195
195
  description: Search object DSL
196
196
  email:
197
197
  - rstankov@gmail.com
@@ -278,7 +278,7 @@ homepage: https://github.com/RStankov/SearchObject
278
278
  licenses:
279
279
  - MIT
280
280
  metadata: {}
281
- post_install_message:
281
+ post_install_message:
282
282
  rdoc_options: []
283
283
  require_paths:
284
284
  - lib
@@ -293,9 +293,8 @@ 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
298
- signing_key:
296
+ rubygems_version: 3.1.4
297
+ signing_key:
299
298
  specification_version: 4
300
299
  summary: Provides DSL for creating search objects
301
300
  test_files: