bharat_filter_engine 0.1.1
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 +7 -0
- data/CHANGELOG.md +62 -0
- data/Gemfile +19 -0
- data/LICENSE +21 -0
- data/README.md +633 -0
- data/Rakefile +6 -0
- data/lib/bharat_filter_engine/association_resolver.rb +102 -0
- data/lib/bharat_filter_engine/dynamic_filter_values.rb +39 -0
- data/lib/bharat_filter_engine/engine.rb +443 -0
- data/lib/bharat_filter_engine/errors.rb +15 -0
- data/lib/bharat_filter_engine/rule_normalizer.rb +47 -0
- data/lib/bharat_filter_engine/search_builder.rb +146 -0
- data/lib/bharat_filter_engine/version.rb +5 -0
- data/lib/bharat_filter_engine.rb +34 -0
- data/spec/association_resolver_spec.rb +160 -0
- data/spec/dynamic_filter_values_spec.rb +130 -0
- data/spec/engine_spec.rb +894 -0
- data/spec/rule_normalizer_spec.rb +52 -0
- data/spec/search_builder_spec.rb +157 -0
- data/spec/spec_helper.rb +79 -0
- metadata +107 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
RSpec.describe BharatFilterEngine::RuleNormalizer do
|
|
4
|
+
|
|
5
|
+
it "keeps modern configuration unchanged" do
|
|
6
|
+
|
|
7
|
+
rule = {
|
|
8
|
+
type: :single,
|
|
9
|
+
filter_type: :string,
|
|
10
|
+
dbcolumn: :stage
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
expect(
|
|
14
|
+
described_class.call(rule)
|
|
15
|
+
).to eq(rule)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "supports legacy configuration" do
|
|
19
|
+
|
|
20
|
+
result =
|
|
21
|
+
described_class.call(
|
|
22
|
+
type: :string,
|
|
23
|
+
dbcolumn: :stage
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
expect(
|
|
27
|
+
result[:type]
|
|
28
|
+
).to eq(:single)
|
|
29
|
+
|
|
30
|
+
expect(
|
|
31
|
+
result[:filter_type]
|
|
32
|
+
).to eq(:string)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "returns nil for nil rule" do
|
|
36
|
+
expect(
|
|
37
|
+
described_class.call(nil)
|
|
38
|
+
).to be_nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "symbolizes rule keys" do
|
|
42
|
+
result = described_class.call(
|
|
43
|
+
"type" => :single,
|
|
44
|
+
"filter_type" => :string,
|
|
45
|
+
"dbcolumn" => :stage
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
expect(result[:type]).to eq(:single)
|
|
49
|
+
expect(result[:filter_type]).to eq(:string)
|
|
50
|
+
expect(result[:dbcolumn]).to eq(:stage)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
RSpec.describe BharatFilterEngine::SearchBuilder do
|
|
4
|
+
|
|
5
|
+
let!(:client) do
|
|
6
|
+
Client.create!(
|
|
7
|
+
name: "John Doe",
|
|
8
|
+
email: "john@example.com"
|
|
9
|
+
)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
let!(:lead) do
|
|
13
|
+
Lead.create!(
|
|
14
|
+
source: "google",
|
|
15
|
+
client: client
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let!(:sale) do
|
|
20
|
+
Sale.create!(
|
|
21
|
+
stage: "qualified",
|
|
22
|
+
lead: lead
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "supports simple search" do
|
|
27
|
+
|
|
28
|
+
result =
|
|
29
|
+
described_class.new(
|
|
30
|
+
scope: Sale.all,
|
|
31
|
+
allowed_columns: [
|
|
32
|
+
:stage,
|
|
33
|
+
:"lead__source"
|
|
34
|
+
]
|
|
35
|
+
).apply("google")
|
|
36
|
+
|
|
37
|
+
expect(result).to contain_exactly(sale)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "supports case insensitive search" do
|
|
41
|
+
result =
|
|
42
|
+
described_class.new(
|
|
43
|
+
scope: Sale.all,
|
|
44
|
+
allowed_columns: [:stage]
|
|
45
|
+
).apply("QUAL")
|
|
46
|
+
|
|
47
|
+
expect(result).to contain_exactly(sale)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "supports nested field search" do
|
|
51
|
+
|
|
52
|
+
result =
|
|
53
|
+
described_class.new(
|
|
54
|
+
scope: Sale.all,
|
|
55
|
+
allowed_columns: [
|
|
56
|
+
:"lead__client__name"
|
|
57
|
+
]
|
|
58
|
+
).apply(
|
|
59
|
+
"lead__client__name=John"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
expect(result).to contain_exactly(sale)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "supports multi-level association search" do
|
|
66
|
+
organization = Organization.create!(
|
|
67
|
+
name: "Acme"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
client_two = Client.create!(
|
|
71
|
+
name: "Jane",
|
|
72
|
+
organization: organization
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
lead_two = Lead.create!(
|
|
76
|
+
source: "referral",
|
|
77
|
+
client: client_two
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
sale_two = Sale.create!(
|
|
81
|
+
lead: lead_two
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
result =
|
|
85
|
+
described_class.new(
|
|
86
|
+
scope: Sale.all,
|
|
87
|
+
allowed_columns: [
|
|
88
|
+
:"lead__client__organization__name"
|
|
89
|
+
]
|
|
90
|
+
).apply(
|
|
91
|
+
"lead__client__organization__name=Acme"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
expect(result).to contain_exactly(sale_two)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "supports OR search" do
|
|
98
|
+
|
|
99
|
+
result =
|
|
100
|
+
described_class.new(
|
|
101
|
+
scope: Sale.all,
|
|
102
|
+
allowed_columns: [
|
|
103
|
+
:stage,
|
|
104
|
+
:"lead__source"
|
|
105
|
+
]
|
|
106
|
+
).apply(
|
|
107
|
+
"stage=qualified|lead__source=google"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
expect(result).to contain_exactly(sale)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "supports combined AND and OR conditions" do
|
|
114
|
+
sale_one = Sale.create!(
|
|
115
|
+
stage: "qualified",
|
|
116
|
+
approval_status: "approved"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
sale_two = Sale.create!(
|
|
120
|
+
stage: "qualified",
|
|
121
|
+
approval_status: "pending"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
Sale.create!(
|
|
125
|
+
stage: "new",
|
|
126
|
+
approval_status: "approved"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
result =
|
|
130
|
+
described_class.new(
|
|
131
|
+
scope: Sale.all,
|
|
132
|
+
allowed_columns: [
|
|
133
|
+
:stage,
|
|
134
|
+
:approval_status
|
|
135
|
+
]
|
|
136
|
+
).apply(
|
|
137
|
+
"stage=qualified&approval_status=approved|approval_status=pending"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
expect(result).to contain_exactly(
|
|
141
|
+
sale_one,
|
|
142
|
+
sale_two
|
|
143
|
+
)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "returns no records when the searched field is not configured" do
|
|
147
|
+
result =
|
|
148
|
+
described_class.new(
|
|
149
|
+
scope: Sale.all,
|
|
150
|
+
allowed_columns: [:stage]
|
|
151
|
+
).apply(
|
|
152
|
+
"unknown=value"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
expect(result).to be_empty
|
|
156
|
+
end
|
|
157
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "active_record"
|
|
5
|
+
require "database_cleaner/active_record"
|
|
6
|
+
require "bharat_filter_engine"
|
|
7
|
+
require "sqlite3"
|
|
8
|
+
|
|
9
|
+
ActiveRecord::Base.establish_connection(
|
|
10
|
+
adapter: "sqlite3",
|
|
11
|
+
database: ":memory:"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
ActiveRecord::Schema.define do
|
|
15
|
+
create_table :organizations do |t|
|
|
16
|
+
t.string :name
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
create_table :clients do |t|
|
|
20
|
+
t.string :name
|
|
21
|
+
t.string :email
|
|
22
|
+
t.references :organization
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
create_table :leads do |t|
|
|
26
|
+
t.string :source
|
|
27
|
+
t.string :status
|
|
28
|
+
t.references :client
|
|
29
|
+
t.datetime :created_at
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
create_table :sales do |t|
|
|
33
|
+
t.string :stage
|
|
34
|
+
t.string :approval_status
|
|
35
|
+
t.integer :project_id
|
|
36
|
+
t.boolean :active
|
|
37
|
+
t.integer :amount
|
|
38
|
+
t.float :conversion_rate
|
|
39
|
+
t.datetime :actual_sale_date
|
|
40
|
+
t.references :lead
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Organization < ActiveRecord::Base
|
|
46
|
+
has_many :clients
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Client < ActiveRecord::Base
|
|
51
|
+
belongs_to :organization
|
|
52
|
+
has_many :leads
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Lead < ActiveRecord::Base
|
|
57
|
+
belongs_to :client
|
|
58
|
+
has_many :sales
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class Sale < ActiveRecord::Base
|
|
63
|
+
belongs_to :lead
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
RSpec.configure do |config|
|
|
68
|
+
config.before(:suite) do
|
|
69
|
+
DatabaseCleaner.clean_with(:truncation)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
config.around(:each) do |example|
|
|
73
|
+
DatabaseCleaner.strategy = :truncation
|
|
74
|
+
|
|
75
|
+
DatabaseCleaner.cleaning do
|
|
76
|
+
example.run
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: bharat_filter_engine
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jayesh Rathore
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-08-30 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activerecord
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '6.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '6.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: activemodel
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '6.1'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '6.1'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: activesupport
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '6.1'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '6.1'
|
|
55
|
+
description: Configurable Rails ActiveRecord filtering engine supporting string, array,
|
|
56
|
+
boolean, numeric, date-range, nested association and AND/OR search filters.
|
|
57
|
+
email:
|
|
58
|
+
- jayeshrathore7024@gmail.com
|
|
59
|
+
executables: []
|
|
60
|
+
extensions: []
|
|
61
|
+
extra_rdoc_files: []
|
|
62
|
+
files:
|
|
63
|
+
- CHANGELOG.md
|
|
64
|
+
- Gemfile
|
|
65
|
+
- LICENSE
|
|
66
|
+
- README.md
|
|
67
|
+
- Rakefile
|
|
68
|
+
- lib/bharat_filter_engine.rb
|
|
69
|
+
- lib/bharat_filter_engine/association_resolver.rb
|
|
70
|
+
- lib/bharat_filter_engine/dynamic_filter_values.rb
|
|
71
|
+
- lib/bharat_filter_engine/engine.rb
|
|
72
|
+
- lib/bharat_filter_engine/errors.rb
|
|
73
|
+
- lib/bharat_filter_engine/rule_normalizer.rb
|
|
74
|
+
- lib/bharat_filter_engine/search_builder.rb
|
|
75
|
+
- lib/bharat_filter_engine/version.rb
|
|
76
|
+
- spec/association_resolver_spec.rb
|
|
77
|
+
- spec/dynamic_filter_values_spec.rb
|
|
78
|
+
- spec/engine_spec.rb
|
|
79
|
+
- spec/rule_normalizer_spec.rb
|
|
80
|
+
- spec/search_builder_spec.rb
|
|
81
|
+
- spec/spec_helper.rb
|
|
82
|
+
homepage: https://github.com/jayesh70240/bharat_filter_engine
|
|
83
|
+
licenses:
|
|
84
|
+
- MIT
|
|
85
|
+
metadata:
|
|
86
|
+
source_code_uri: https://github.com/jayesh70240/bharat_filter_engine
|
|
87
|
+
changelog_uri: https://github.com/jayesh70240/bharat_filter_engine/blob/main/CHANGELOG.md
|
|
88
|
+
post_install_message:
|
|
89
|
+
rdoc_options: []
|
|
90
|
+
require_paths:
|
|
91
|
+
- lib
|
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '3.0'
|
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - ">="
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '0'
|
|
102
|
+
requirements: []
|
|
103
|
+
rubygems_version: 3.5.3
|
|
104
|
+
signing_key:
|
|
105
|
+
specification_version: 4
|
|
106
|
+
summary: A generic filtering engine for Rails ActiveRecord
|
|
107
|
+
test_files: []
|