searchkon 0.1.1 → 1.0.3
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/Gemfile +2 -0
- data/README.md +143 -1
- data/lib/searchkon/regex_formatter.rb +1 -1
- data/searchkon.gemspec +10 -6
- data/spec/filterable_spec.rb +24 -0
- data/spec/models/coupon.rb +3 -0
- data/spec/models/payment.rb +3 -0
- data/spec/models/product.rb +17 -0
- data/spec/query_builder_spec.rb +102 -0
- data/spec/schema.rb +20 -0
- data/spec/spec_helper.rb +15 -0
- metadata +54 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8b03e8feb425ac126b0abc8d2e0684ba41f57a89e36373da2b7bb6e834b30841
|
|
4
|
+
data.tar.gz: 97bc80799ba817ba658ed2f259e8664c3ede624e419c8e5b317ecf195e8c6ece
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1fe13d61e92bf189d2aa96db3f09efa587c4657fe712e870f57dc5340a371f77a016c81d886785c3be67f0ed5810a3b5755a6dfe9d96279d9ab8bc887a217ff5
|
|
7
|
+
data.tar.gz: b7eeb6ba941a093e1ec77826efe721eab29664147d006dca01a0f7abf877d9047e4e3401d8d72cb37ea88086433a1144db77e4f8e69d26b250b135de3ba56c39
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1 +1,143 @@
|
|
|
1
|
-
|
|
1
|
+
# Searchkon
|
|
2
|
+
|
|
3
|
+
Searchkon is Advanced active record search(filter) command that makes easy to search throw models and their relationships.
|
|
4
|
+
|
|
5
|
+
## Introduction
|
|
6
|
+
|
|
7
|
+
Lets say we want to return a list of products filtered by multiple parameters. our request contain below parameters:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
{
|
|
11
|
+
title: 'foobar',
|
|
12
|
+
id: [1, 2, 3, 4],
|
|
13
|
+
created_at: '(2012-12-21..2019-12-21)'
|
|
14
|
+
categories.name: 'mobile'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Filter above parameters with Searchkon gem:
|
|
20
|
+
|
|
21
|
+
```rb
|
|
22
|
+
Searchkon::QueryBuilder.filter('Product', filters)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Getting Start
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
Add Searchkon to your Gemfile:
|
|
29
|
+
```sh
|
|
30
|
+
gem 'searchkon'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Searchable columns
|
|
34
|
+
|
|
35
|
+
at the first we should determine witch columns of model can be filter in Searchkon
|
|
36
|
+
|
|
37
|
+
```rb
|
|
38
|
+
class Product < ActiveRecord::Base
|
|
39
|
+
|
|
40
|
+
has_many :coupons
|
|
41
|
+
has_many :payments
|
|
42
|
+
|
|
43
|
+
def self.searchable_columns
|
|
44
|
+
{
|
|
45
|
+
like: ['title', 'coupons.code'],
|
|
46
|
+
exact: [
|
|
47
|
+
'created_at',
|
|
48
|
+
'coupons.title', ## relational filter on coupons table
|
|
49
|
+
'payments.id', ## relational filter on payments table
|
|
50
|
+
'id'
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
<b> like: </b> if you add specific column in your like scope, your query will be like below sql.
|
|
60
|
+
```sql
|
|
61
|
+
select * from products where title like %foo%
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
<b>exact:</b> Searchkon create query using equal operation.
|
|
65
|
+
|
|
66
|
+
```sql
|
|
67
|
+
select * from products where created_at = foo
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Simple where query
|
|
71
|
+
|
|
72
|
+
<b>Important: </b> Searchkon just accept filters key, you can add your filterable columns in filter key.
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
```rb
|
|
76
|
+
params = {
|
|
77
|
+
id: 1,
|
|
78
|
+
title: 'foobar'
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```rb
|
|
83
|
+
Searchkon::QueryBuilder.filter('Product', params)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
sql result:
|
|
87
|
+
```sql
|
|
88
|
+
SELECT "products".* FROM "products" WHERE (products.title like '%foobar%') AND "products"."id" = 1
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Search Range
|
|
92
|
+
|
|
93
|
+
```rb
|
|
94
|
+
params = {
|
|
95
|
+
id: '(1..10)',
|
|
96
|
+
created_at: '(2012-12-21..2019-12-21)'
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
sql result:
|
|
101
|
+
|
|
102
|
+
```sql
|
|
103
|
+
SELECT "products".* FROM "products" WHERE (products.title like '%foobar%') AND "products"."id" = 1
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
### Search in relational table
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
```rb
|
|
111
|
+
params = {
|
|
112
|
+
'coupons.id': [1,4,8]
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
sql result:
|
|
117
|
+
|
|
118
|
+
```sql
|
|
119
|
+
SELECT "products".* FROM "products" INNER JOIN "coupons" ON "coupons"."product_id" = "products"."id" WHERE "coupons"."id" IN (1, 4, 8)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
### Invalid Column in query
|
|
124
|
+
|
|
125
|
+
if your filter parameters contain invalid column name, Searchkon skip it and create query without that column.
|
|
126
|
+
|
|
127
|
+
```rb
|
|
128
|
+
invalid_mock_params = {
|
|
129
|
+
id: 1,
|
|
130
|
+
foo: 'foobar'
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```rb
|
|
135
|
+
Searchkon::QueryBuilder.filter('Product', invalid_mock_params)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
sql result:
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
```sql
|
|
142
|
+
SELECT "products".* FROM "products" WHERE "products"."id" = 1
|
|
143
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Searchkon
|
|
2
2
|
module RegexFormatter
|
|
3
|
-
|
|
3
|
+
SIMPLE_RANGE_FORMAT_REGEX = /\((.*)\.\.(.*)\)/
|
|
4
4
|
RELATIONAL_FORMAT_REGEX = /(.*)\.(.*)/
|
|
5
5
|
DIGIT_RANGE_FORMAT_REGEX = /\((\d*)\.\.(\d*)\)/
|
|
6
6
|
DATE_RANGE_FORMAT_REGEX = /\((\d{4}-\d{1,2}-\d{1,2})\.\.(\d{4}-\d{1,2}-\d{1,2})\)/
|
data/searchkon.gemspec
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
2
|
s.name = 'searchkon'
|
|
3
|
-
s.version = '0.
|
|
3
|
+
s.version = '1.0.3'
|
|
4
4
|
s.date = '2020-05-24'
|
|
5
|
-
s.summary = "
|
|
6
|
-
s.description = "
|
|
7
|
-
s.authors = ["Majid Imanzade"]
|
|
5
|
+
s.summary = "Advanced activerecord search/filter command"
|
|
6
|
+
s.description = "Advanced activerecord search/filter command"
|
|
7
|
+
s.authors = ["Majid Imanzade", 'Amin Samadzade']
|
|
8
8
|
s.email = 'majidimanzade1@gmail.com'
|
|
9
|
-
s.homepage = 'https://rubygems.org/gems/
|
|
10
|
-
s.homepage = "http://github.com/majidimanzade/
|
|
9
|
+
s.homepage = 'https://rubygems.org/gems/searchkon'
|
|
10
|
+
s.homepage = "http://github.com/majidimanzade/searchkon"
|
|
11
11
|
s.license = 'MIT'
|
|
12
12
|
|
|
13
13
|
s.files = `git ls-files`.split("\n")
|
|
@@ -15,4 +15,8 @@ Gem::Specification.new do |s|
|
|
|
15
15
|
s.add_development_dependency("bundler")
|
|
16
16
|
s.add_development_dependency("rake")
|
|
17
17
|
s.add_development_dependency("rspec")
|
|
18
|
+
s.add_development_dependency "sqlite3"
|
|
19
|
+
|
|
20
|
+
s.add_dependency "activesupport"
|
|
21
|
+
s.add_dependency "activerecord"
|
|
18
22
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
|
+
|
|
3
|
+
class DummyClass
|
|
4
|
+
include Searchkon::RegexFormatter
|
|
5
|
+
include Searchkon::Filterable
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
RSpec.describe Searchkon::Filterable do
|
|
9
|
+
describe 'filterable test' do
|
|
10
|
+
it 'should return correct filter with scope' do
|
|
11
|
+
filrtetables = {
|
|
12
|
+
like: ['name', 'cities.name'],
|
|
13
|
+
exact: ['cities.id']
|
|
14
|
+
}
|
|
15
|
+
filters = {'cities.name,name': 'Don', 'cities.id': 1}
|
|
16
|
+
result = [{:value=>1, :key => "cities.id", :scope=>:exact},
|
|
17
|
+
{:value=>"Don", :key=> "cities.name,name", :scope=>:fulltext}]
|
|
18
|
+
|
|
19
|
+
dc = DummyClass.new
|
|
20
|
+
|
|
21
|
+
expect(dc.validate_params(filters, filrtetables)).to eq result
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
|
+
|
|
3
|
+
RSpec.describe Searchkon::QueryBuilder do
|
|
4
|
+
describe 'query builder for filter' do
|
|
5
|
+
let (:simple_where_params) do
|
|
6
|
+
{
|
|
7
|
+
id: 1,
|
|
8
|
+
title: 'foobar'
|
|
9
|
+
}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
let (:simple_range_params) do
|
|
13
|
+
{
|
|
14
|
+
id: '(1..10)',
|
|
15
|
+
created_at: '(2012-12-21..2019-12-21)'
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let (:range_and_array_where_params) do
|
|
20
|
+
{
|
|
21
|
+
id: [1,2,3,4,5],
|
|
22
|
+
created_at: '(2012-12-21..2019-12-21)'
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'should return simple where query' do
|
|
27
|
+
query = "SELECT \"products\".* FROM \"products\" WHERE (products.title like '%foobar%') AND \"products\".\"id\" = 1"
|
|
28
|
+
expect(Searchkon::QueryBuilder.filter('Product', simple_where_params).to_sql).to eq query
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'should return simple range query' do
|
|
32
|
+
query = "SELECT \"products\".* FROM \"products\" WHERE (products.created_at between '2012-12-21' and '2019-12-21') AND (products.id between '1' and '10')"
|
|
33
|
+
|
|
34
|
+
expect(Searchkon::QueryBuilder.filter('Product', simple_range_params).to_sql).to eq query
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'should return simple where relational query' do
|
|
38
|
+
relational_params = { 'coupons.id': 1 }
|
|
39
|
+
query = "SELECT \"products\".* FROM \"products\" INNER JOIN \"coupons\" ON \"coupons\".\"product_id\" = \"products\".\"id\" WHERE \"coupons\".\"id\" = 1"
|
|
40
|
+
|
|
41
|
+
expect(Searchkon::QueryBuilder.filter('Product', relational_params).to_sql).to eq query
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'should return range relational query' do
|
|
45
|
+
relational_params = { 'coupons.id': [1,2,3] }
|
|
46
|
+
query = "SELECT \"products\".* FROM \"products\" INNER JOIN \"coupons\" ON \"coupons\".\"product_id\" = \"products\".\"id\" WHERE \"coupons\".\"id\" IN (1, 2, 3)"
|
|
47
|
+
|
|
48
|
+
expect(Searchkon::QueryBuilder.filter('Product', relational_params).to_sql).to eq query
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'should return simple where relational query with two relations' do
|
|
52
|
+
relational_params = { 'coupons.id': 1, 'payments.id': 1 }
|
|
53
|
+
query = "SELECT \"products\".* FROM \"products\" INNER JOIN \"coupons\" ON \"coupons\".\"product_id\" = \"products\".\"id\" INNER JOIN \"payments\" ON \"payments\".\"product_id\" = \"products\".\"id\" WHERE \"coupons\".\"id\" = 1 AND \"payments\".\"id\" = 1"
|
|
54
|
+
expect(Searchkon::QueryBuilder.filter('Product', relational_params).to_sql).to eq query
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'should return range query and array where' do
|
|
58
|
+
query = "SELECT \"products\".* FROM \"products\" WHERE (products.created_at between '2012-12-21' and '2019-12-21') AND \"products\".\"id\" IN (1, 2, 3, 4, 5)"
|
|
59
|
+
|
|
60
|
+
expect(Searchkon::QueryBuilder.filter('Product', range_and_array_where_params).to_sql).to eq query
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'should return all if params empty' do
|
|
64
|
+
query = "SELECT \"products\".* FROM \"products\""
|
|
65
|
+
|
|
66
|
+
expect(Searchkon::QueryBuilder.filter('Product', {}).to_sql).to eq query
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'should raise error if model not exist' do
|
|
70
|
+
expect { Searchkon::QueryBuilder.filter('foobar', simple_where_params) }.to raise_error NameError
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'should return all if all filter column not exist' do
|
|
74
|
+
query = "SELECT \"products\".* FROM \"products\""
|
|
75
|
+
invalid_mock_params = {
|
|
76
|
+
blah: 1,
|
|
77
|
+
foo: 'foobar'
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
expect(Searchkon::QueryBuilder.filter('Product', invalid_mock_params).to_sql).to eq query
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'should return query if one filter column not exist' do
|
|
84
|
+
query = "SELECT \"products\".* FROM \"products\" WHERE \"products\".\"id\" = 1"
|
|
85
|
+
some_invalid_mock_params = {
|
|
86
|
+
id: 1,
|
|
87
|
+
foo: 'foobar'
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
expect(Searchkon::QueryBuilder.filter('Product', some_invalid_mock_params).to_sql).to eq query
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'should return fulltext query if params has fulltext key' do
|
|
94
|
+
query = "SELECT \"products\".* FROM \"products\" INNER JOIN \"coupons\" ON \"coupons\".\"product_id\" = \"products\".\"id\" WHERE (products.title like '%foobar%' or coupons.code like '%foobar%')"
|
|
95
|
+
params = {
|
|
96
|
+
'title,coupons.code': 'foobar'
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
expect(Searchkon::QueryBuilder.filter('Product', params).to_sql).to eq query
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
data/spec/schema.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
ActiveRecord::Schema.define do
|
|
2
|
+
self.verbose = false
|
|
3
|
+
|
|
4
|
+
create_table :products, :force => true do |t|
|
|
5
|
+
t.string :key
|
|
6
|
+
t.string :title
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
create_table :coupons, :force => true do |t|
|
|
11
|
+
t.string :title
|
|
12
|
+
t.timestamps
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
create_table :payments, :force => true do |t|
|
|
16
|
+
t.string :title
|
|
17
|
+
t.timestamps
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
3
|
+
|
|
4
|
+
require 'searchkon'
|
|
5
|
+
require 'active_record'
|
|
6
|
+
require 'active_support'
|
|
7
|
+
require 'active_support/core_ext'
|
|
8
|
+
require 'rspec'
|
|
9
|
+
|
|
10
|
+
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
|
11
|
+
|
|
12
|
+
load File.dirname(__FILE__) + '/schema.rb'
|
|
13
|
+
require File.dirname(__FILE__) + '/models/coupon.rb'
|
|
14
|
+
require File.dirname(__FILE__) + '/models/payment.rb'
|
|
15
|
+
require File.dirname(__FILE__) + '/models/product.rb'
|
metadata
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: searchkon
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Majid Imanzade
|
|
8
|
+
- Amin Samadzade
|
|
8
9
|
autorequire:
|
|
9
10
|
bindir: bin
|
|
10
11
|
cert_chain: []
|
|
@@ -52,7 +53,49 @@ dependencies:
|
|
|
52
53
|
- - ">="
|
|
53
54
|
- !ruby/object:Gem::Version
|
|
54
55
|
version: '0'
|
|
55
|
-
|
|
56
|
+
- !ruby/object:Gem::Dependency
|
|
57
|
+
name: sqlite3
|
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '0'
|
|
63
|
+
type: :development
|
|
64
|
+
prerelease: false
|
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0'
|
|
70
|
+
- !ruby/object:Gem::Dependency
|
|
71
|
+
name: activesupport
|
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
|
73
|
+
requirements:
|
|
74
|
+
- - ">="
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '0'
|
|
77
|
+
type: :runtime
|
|
78
|
+
prerelease: false
|
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '0'
|
|
84
|
+
- !ruby/object:Gem::Dependency
|
|
85
|
+
name: activerecord
|
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0'
|
|
91
|
+
type: :runtime
|
|
92
|
+
prerelease: false
|
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
94
|
+
requirements:
|
|
95
|
+
- - ">="
|
|
96
|
+
- !ruby/object:Gem::Version
|
|
97
|
+
version: '0'
|
|
98
|
+
description: Advanced activerecord search/filter command
|
|
56
99
|
email: majidimanzade1@gmail.com
|
|
57
100
|
executables: []
|
|
58
101
|
extensions: []
|
|
@@ -62,14 +105,20 @@ files:
|
|
|
62
105
|
- Gemfile
|
|
63
106
|
- LICENSE
|
|
64
107
|
- README.md
|
|
65
|
-
- elliot-0.0.1.gem
|
|
66
108
|
- lib/searchkon.rb
|
|
67
109
|
- lib/searchkon/filterable.rb
|
|
68
110
|
- lib/searchkon/query_builder.rb
|
|
69
111
|
- lib/searchkon/regex_formatter.rb
|
|
70
112
|
- lib/searchkon/searchables.rb
|
|
71
113
|
- searchkon.gemspec
|
|
72
|
-
|
|
114
|
+
- spec/filterable_spec.rb
|
|
115
|
+
- spec/models/coupon.rb
|
|
116
|
+
- spec/models/payment.rb
|
|
117
|
+
- spec/models/product.rb
|
|
118
|
+
- spec/query_builder_spec.rb
|
|
119
|
+
- spec/schema.rb
|
|
120
|
+
- spec/spec_helper.rb
|
|
121
|
+
homepage: http://github.com/majidimanzade/searchkon
|
|
73
122
|
licenses:
|
|
74
123
|
- MIT
|
|
75
124
|
metadata: {}
|
|
@@ -91,5 +140,5 @@ requirements: []
|
|
|
91
140
|
rubygems_version: 3.1.2
|
|
92
141
|
signing_key:
|
|
93
142
|
specification_version: 4
|
|
94
|
-
summary:
|
|
143
|
+
summary: Advanced activerecord search/filter command
|
|
95
144
|
test_files: []
|