seek_party 0.0.2 → 0.0.8
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/.travis.yml +19 -0
- data/Gemfile.lock +1 -1
- data/README.md +6 -4
- data/lib/seek_party.rb +3 -4
- data/lib/seek_party/models/sp_attribute.rb +20 -0
- data/lib/seek_party/models/sp_query.rb +31 -0
- data/lib/seek_party/seek_party_attribute.rb +14 -15
- data/lib/seek_party/seek_party_engine.rb +27 -7
- data/lib/seek_party/{seek_party_query.rb → seek_party_query_builder.rb} +23 -32
- data/lib/seek_party/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ca3c040d22ecaf44d42c4d94f63bc34795a691ebc3fcf24a201ba346611c560
|
4
|
+
data.tar.gz: 884466fbeb0b97fc741e82e21eca594d1611c78407eabb7f44f2606a3f5e8812
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65fba3d3ab15003a1a3c781f8101a405f308688c6fcf3cd0ef1349551c13a795146c67d5e5ccd054af6f1fc26f45321d9574dc5ef0eb22bac38481d61daad8f7
|
7
|
+
data.tar.gz: 2de6b896f03f992aafa9b6cdaf80165289eeaab1eaae0a79c7b736cd436accee7bd1fdcf31018476270c8348e29e92d51f476a3528f948c60fd1920d648e748a
|
data/.travis.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
env:
|
2
|
+
global:
|
3
|
+
- CC_TEST_REPORTER_ID=$CODECLIMATE_REPO_TOKEN
|
4
|
+
sudo: false
|
5
|
+
language: ruby
|
6
|
+
cache: bundler
|
7
|
+
rvm:
|
8
|
+
- 2.6.3
|
9
|
+
before_install:
|
10
|
+
- gem install bundler -v 2.0.2
|
11
|
+
before_script:
|
12
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
13
|
+
- chmod +x ./cc-test-reporter
|
14
|
+
- ./cc-test-reporter before-build
|
15
|
+
script:
|
16
|
+
- bundle
|
17
|
+
- bundle exec rspec
|
18
|
+
after_success:
|
19
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# SeekParty - Searches made easy
|
2
2
|
|
3
|
+
[](https://travis-ci.org/caws/seek_party)
|
4
|
+
[](https://codeclimate.com/github/caws/seek_party/maintainability)
|
5
|
+
[](https://codeclimate.com/github/caws/seek_party/test_coverage)
|
6
|
+
|
3
7
|
### What is this?
|
4
8
|
|
5
9
|
Have you ever had to write a search functionality for a web application and found yourself
|
@@ -33,8 +37,6 @@ Below is what's currently doable:
|
|
33
37
|
PS: The DEFAULT_BLACK_LIST contains the attributes :id, :created_at and :updated_at. You can pass your own
|
34
38
|
DEFAULT_BLACK_LIST when calling the #search method from your model, but I recommend using the WHITE_LIST,
|
35
39
|
as SeekParty will then only generate queries using the column names present there.
|
36
|
-
You should also keep in mind that when building the queries, seek_party tries to pluralize your table name in order to refer to the table
|
37
|
-
columns by their full name (ex: 'your_table.your_column_name').
|
38
40
|
|
39
41
|
## Getting started
|
40
42
|
|
@@ -43,7 +45,7 @@ PS: The DEFAULT_BLACK_LIST contains the attributes :id, :created_at and :updated
|
|
43
45
|
Add the following to your Gemfile
|
44
46
|
|
45
47
|
``` ruby
|
46
|
-
gem 'seek_party', '~> 0.0.
|
48
|
+
gem 'seek_party', '~> 0.0.7'
|
47
49
|
```
|
48
50
|
Then run:
|
49
51
|
|
@@ -134,7 +136,7 @@ User.search(params: params)
|
|
134
136
|
And this would result in the following query being produced:
|
135
137
|
|
136
138
|
``` sql
|
137
|
-
SELECT "users".* FROM "users" WHERE (LOWER(CAST(users.name AS TEXT)) LIKE '%
|
139
|
+
SELECT "users".* FROM "users" WHERE (LOWER(CAST(users.name AS TEXT)) LIKE '%bilbo@theshire.com%' AND LOWER(CAST(users.name AS TEXT)) = 'bilbo' OR LOWER(CAST(users.email AS TEXT)) LIKE '%bilbo@theshire.com%' AND LOWER(CAST(users.name AS TEXT)) = 'bilbo')
|
138
140
|
```
|
139
141
|
|
140
142
|
### TO DO
|
data/lib/seek_party.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'active_record' unless defined? ActiveRecord
|
2
|
+
require_relative 'seek_party/models/sp_attribute'
|
3
|
+
require_relative 'seek_party/models/sp_query'
|
2
4
|
require_relative 'seek_party/seek_party_attribute'
|
3
5
|
require_relative 'seek_party/seek_party_engine'
|
4
|
-
require_relative 'seek_party/
|
6
|
+
require_relative 'seek_party/seek_party_query_builder'
|
5
7
|
require_relative 'seek_party/version'
|
6
8
|
|
7
9
|
module SeekParty
|
@@ -12,9 +14,6 @@ module SeekParty
|
|
12
14
|
black_list: DEFAULT_BLACK_LIST,
|
13
15
|
white_list: nil,
|
14
16
|
scopes: [])
|
15
|
-
# If there are params to work with, use SeekParty.
|
16
|
-
# Otherwise, just return an empty instance of ActiveRecord_Relation.
|
17
|
-
return none if params.empty?
|
18
17
|
|
19
18
|
SeekPartyEngine
|
20
19
|
.new(self,
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class SPAttribute
|
2
|
+
attr_accessor :attributes, :table_name
|
3
|
+
|
4
|
+
def initialize(attributes: [], table_name: nil)
|
5
|
+
@attributes = attributes
|
6
|
+
@table_name = table_name
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_attribute(attribute)
|
10
|
+
@attributes << attribute
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_full_column_name(attribute)
|
14
|
+
if @attributes.include? attribute
|
15
|
+
"#{@table_name}.#{attribute}"
|
16
|
+
else
|
17
|
+
raise 'Attribute not present in the attribute list.'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class SPQuery
|
2
|
+
attr_accessor :queries, :params
|
3
|
+
|
4
|
+
def initialize(queries: {}, params: nil)
|
5
|
+
@queries = queries
|
6
|
+
@params = params
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_attribute_query(attribute, query)
|
10
|
+
@queries[attribute] << query
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_attribute_query(attribute, query)
|
14
|
+
@queries[attribute] = query
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_final_query
|
18
|
+
return if @queries.blank?
|
19
|
+
|
20
|
+
@queries.map { |key, value|
|
21
|
+
if key == @queries.keys.first
|
22
|
+
value
|
23
|
+
else
|
24
|
+
or_and(value)
|
25
|
+
end }.sum
|
26
|
+
end
|
27
|
+
|
28
|
+
def or_and(value)
|
29
|
+
@params[:search].present? ? " OR #{value}" : " AND #{value}"
|
30
|
+
end
|
31
|
+
end
|
@@ -5,17 +5,17 @@ module SeekParty
|
|
5
5
|
:black_list
|
6
6
|
|
7
7
|
def initialize(inspected_class, white_list, black_list)
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
@inspected_class = inspected_class
|
9
|
+
@white_list = white_list
|
10
|
+
@black_list = black_list
|
11
11
|
end
|
12
12
|
|
13
13
|
# Compare attributes to params passed
|
14
14
|
# If only search is present, query against all params
|
15
|
-
# If both search and other attributes match
|
15
|
+
# If both search and other attributes match against the
|
16
16
|
# params hash, query against them too.
|
17
17
|
def discover_attributes
|
18
|
-
check_attributes inspected_class.new
|
18
|
+
check_attributes @inspected_class.new
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
@@ -23,31 +23,30 @@ module SeekParty
|
|
23
23
|
def check_attributes(another_model)
|
24
24
|
return nil if another_model.nil?
|
25
25
|
|
26
|
-
|
26
|
+
sp_attribute = SPAttribute.new(table_name: get_table_name)
|
27
|
+
|
27
28
|
another_model.attributes.keys.each do |attribute|
|
28
29
|
next unless another_model.has_attribute? attribute
|
29
30
|
next if black_listed? attribute
|
30
31
|
|
31
|
-
if white_listed? attribute
|
32
|
-
attributes << "#{pluralize_and_snake_case_class_name}.#{attribute}"
|
33
|
-
end
|
32
|
+
sp_attribute.add_attribute(attribute) if white_listed? attribute
|
34
33
|
end
|
35
34
|
|
36
|
-
|
35
|
+
sp_attribute
|
37
36
|
end
|
38
37
|
|
39
38
|
def white_listed?(attribute_name)
|
40
|
-
return true if white_list.nil?
|
39
|
+
return true if @white_list.nil?
|
41
40
|
|
42
|
-
white_list.include? attribute_name
|
41
|
+
@white_list.include? attribute_name
|
43
42
|
end
|
44
43
|
|
45
44
|
def black_listed?(attribute_name)
|
46
|
-
black_list.include? attribute_name
|
45
|
+
@black_list.include? attribute_name
|
47
46
|
end
|
48
47
|
|
49
|
-
def
|
50
|
-
inspected_class.
|
48
|
+
def get_table_name
|
49
|
+
@inspected_class.table_name.underscore
|
51
50
|
end
|
52
51
|
end
|
53
52
|
end
|
@@ -4,16 +4,22 @@ module SeekParty
|
|
4
4
|
:seek_party_query,
|
5
5
|
:inspected_class
|
6
6
|
|
7
|
-
def initialize(inspected_class,
|
8
|
-
|
9
|
-
|
7
|
+
def initialize(inspected_class,
|
8
|
+
params: {},
|
9
|
+
white_list: nil,
|
10
|
+
black_list: nil,
|
11
|
+
scopes: {})
|
12
|
+
@seek_party_attribute = SeekPartyAttribute.new(inspected_class,
|
13
|
+
white_list,
|
14
|
+
black_list)
|
15
|
+
@seek_party_query = SeekPartyQueryBuilder.new(params)
|
10
16
|
@inspected_class = inspected_class
|
11
17
|
@scopes = scopes
|
12
18
|
end
|
13
19
|
|
14
20
|
def search
|
15
|
-
|
16
|
-
final_query = seek_party_query.build_query(
|
21
|
+
spa_attribute = seek_party_attribute.discover_attributes
|
22
|
+
final_query = seek_party_query.build_query(spa_attribute)
|
17
23
|
run_search(final_query)
|
18
24
|
end
|
19
25
|
|
@@ -33,8 +39,22 @@ module SeekParty
|
|
33
39
|
# else, as the returned object from run_search is an
|
34
40
|
# ActiveRecord_Relation object that would accept
|
35
41
|
# method chaining
|
36
|
-
@scopes.each do |
|
37
|
-
|
42
|
+
@scopes.each do |scope|
|
43
|
+
if scope.respond_to?(:keys)
|
44
|
+
scope.keys.each do |key|
|
45
|
+
scope_call(key, scope[key])
|
46
|
+
end
|
47
|
+
else
|
48
|
+
scope_call(scope, nil)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def scope_call(key, value)
|
54
|
+
if value.present?
|
55
|
+
@inspected_class = @inspected_class.method(key).call(*value)
|
56
|
+
else
|
57
|
+
@inspected_class = @inspected_class.method(key).call
|
38
58
|
end
|
39
59
|
end
|
40
60
|
end
|
@@ -1,44 +1,45 @@
|
|
1
1
|
module SeekParty
|
2
|
-
class
|
2
|
+
class SeekPartyQueryBuilder
|
3
3
|
|
4
4
|
attr_accessor :params,
|
5
|
-
:
|
5
|
+
:sp_query
|
6
6
|
|
7
7
|
def initialize(params)
|
8
|
-
|
9
|
-
|
8
|
+
@params = params
|
9
|
+
@sp_query = SPQuery.new(params: params)
|
10
10
|
end
|
11
11
|
|
12
|
-
def build_query(
|
12
|
+
def build_query(sp_attributes)
|
13
13
|
return unless params
|
14
14
|
|
15
|
-
build_base_queries(
|
16
|
-
|
15
|
+
build_base_queries(sp_attributes)
|
16
|
+
|
17
|
+
@sp_query.build_final_query
|
17
18
|
end
|
18
19
|
|
19
20
|
private
|
20
21
|
|
21
22
|
def build_subquery_string(attribute, attribute_deep)
|
22
23
|
if params[:search].present?
|
23
|
-
|
24
|
-
"'#{params[attribute_deep.to_sym].to_s.downcase}'"
|
24
|
+
@sp_query.add_attribute_query(attribute, " AND #{build_equals_query(attribute_deep)}")
|
25
25
|
else
|
26
|
-
|
27
|
-
"'#{params[attribute_deep.to_sym].to_s.downcase}'"
|
26
|
+
@sp_query.set_attribute_query(attribute_deep, build_equals_query(attribute_deep))
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
31
|
-
def build_base_queries(
|
32
|
-
attributes.each do |attribute|
|
30
|
+
def build_base_queries(spattribute)
|
31
|
+
spattribute.attributes.each do |attribute|
|
33
32
|
if params[:search].present?
|
34
|
-
|
35
|
-
|
33
|
+
full_column_name = spattribute.get_full_column_name(attribute)
|
34
|
+
cast_column_name = cast_according_to_adapter(full_column_name)
|
35
|
+
@sp_query.set_attribute_query(attribute, "#{cast_column_name} LIKE "\
|
36
|
+
"'%#{params[:search].downcase}%'")
|
36
37
|
end
|
37
38
|
|
38
39
|
# If there are other params being used other than :search
|
39
40
|
# it means a where clause is needed.
|
40
41
|
# TODO: Figure out a way to work with date intervals, too.
|
41
|
-
attributes.each do |attribute_deep|
|
42
|
+
spattribute.attributes.each do |attribute_deep|
|
42
43
|
next unless params[attribute_deep.to_sym]
|
43
44
|
|
44
45
|
build_subquery_string(attribute, attribute_deep)
|
@@ -46,31 +47,21 @@ module SeekParty
|
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
49
|
-
def build_final_query
|
50
|
-
final_query = ''
|
51
|
-
queries.each do |_key, value|
|
52
|
-
next unless value
|
53
|
-
|
54
|
-
final_query << if value == queries[queries.keys.first]
|
55
|
-
value
|
56
|
-
else
|
57
|
-
params[:search].present? ? " OR #{value}" : " AND #{value}"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
final_query
|
62
|
-
end
|
63
|
-
|
64
50
|
def cast_according_to_adapter(column_name)
|
65
51
|
if db_sqlite3?
|
66
52
|
"LOWER(CAST(#{column_name} AS TEXT))"
|
67
53
|
elsif db_postgresql?
|
68
54
|
"LOWER(#{column_name}::VARCHAR)"
|
69
55
|
else
|
70
|
-
raise "SeekParty does not support #{ActiveRecord::Base.connection.class}"
|
56
|
+
raise "SeekParty does not support #{ActiveRecord::Base.connection.class}."
|
71
57
|
end
|
72
58
|
end
|
73
59
|
|
60
|
+
def build_equals_query(attribute)
|
61
|
+
"#{cast_according_to_adapter(attribute)} = "\
|
62
|
+
"'#{@params[attribute.to_sym].to_s.downcase}'"
|
63
|
+
end
|
64
|
+
|
74
65
|
def db_sqlite3?
|
75
66
|
ActiveRecord::Base.connection.instance_of? ActiveRecord::ConnectionAdapters::SQLite3Adapter
|
76
67
|
rescue StandardError
|
data/lib/seek_party/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: seek_party
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charles Washington
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-05-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -63,6 +63,7 @@ files:
|
|
63
63
|
- ".gitignore"
|
64
64
|
- ".rakeTasks"
|
65
65
|
- ".rspec"
|
66
|
+
- ".travis.yml"
|
66
67
|
- CODE_OF_CONDUCT.md
|
67
68
|
- Gemfile
|
68
69
|
- Gemfile.lock
|
@@ -72,9 +73,11 @@ files:
|
|
72
73
|
- bin/console
|
73
74
|
- bin/setup
|
74
75
|
- lib/seek_party.rb
|
76
|
+
- lib/seek_party/models/sp_attribute.rb
|
77
|
+
- lib/seek_party/models/sp_query.rb
|
75
78
|
- lib/seek_party/seek_party_attribute.rb
|
76
79
|
- lib/seek_party/seek_party_engine.rb
|
77
|
-
- lib/seek_party/
|
80
|
+
- lib/seek_party/seek_party_query_builder.rb
|
78
81
|
- lib/seek_party/version.rb
|
79
82
|
- seek_party.gemspec
|
80
83
|
homepage: https://github.com/caws/seek_party
|