quickfilter 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bcced528170e4bb637585942b22c94881ba33c75
4
+ data.tar.gz: e6427b898d477e4552d6f25a9f5bd844d848be9f
5
+ SHA512:
6
+ metadata.gz: 732bd37aa82e8314379e5fdca616ea77e408a35f19465eb346fb748e510b60f891573e8a8babe4212717cafb17f73824887043ac4cfd5729433b7b17823f973c
7
+ data.tar.gz: cba03ac9dc7e2a10ed267a9762771f9e752ea27dabeea109dcc93ce4ce03a03802fed35d07497279db787dc2b1cedb4b9778238c9184bec94b9a4f4f76c1bbe2
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.2
5
+ before_install: gem install bundler -v 1.12.5
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in quickfilter.gemspec
4
+ gem 'activesupport'
5
+ gem 'activerecord'
6
+ gem 'sqlite3'
7
+
8
+ gemspec
@@ -0,0 +1,104 @@
1
+ # Quickfilter
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'quickfilter', git: 'https://github.com/rcpedro/quickfilter.git'
9
+ ```
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install quickfilter
18
+
19
+ ## Usage
20
+
21
+ Given the models:
22
+
23
+ ```ruby
24
+ class ApplicationRecord < ActiveRecord::Base
25
+ include Quickfilter
26
+ self.abstract_class = true
27
+ end
28
+
29
+ class User < ApplicationRecord
30
+ enum status: [:active, :inactive]
31
+
32
+ has_many :students
33
+ has_many :universities, through: :students
34
+
35
+ has_one :current_student, -> { where(status: 'active') }, class_name: 'Student'
36
+ has_one :current_university, through: :current_student, class_name: 'University'
37
+ end
38
+
39
+ class University < ApplicationRecord
40
+ has_many :students
41
+ has_many :users, through: :students
42
+ end
43
+
44
+ class Student < ApplicationRecord
45
+ belongs_to :university
46
+ belongs_to :user
47
+ end
48
+ ```
49
+
50
+ ### Basic Filter
51
+
52
+ Query using a set number of operators defined in `lib/quickfilter/handlers.rb`:
53
+
54
+ ```ruby
55
+ User.filter(
56
+ first_name: { likeic: 'jOhn' },
57
+ status: { in: [0, 1] },
58
+ created_at: {
59
+ gte: Date.today.beginning_of_month,
60
+ lte: Date.today
61
+ }
62
+ )
63
+ ```
64
+
65
+ If the parameter for a filter is null, it is ignored and removed (since this is the case for most search forms wherein almost all parameters are optional). For example, the following are equivalent:
66
+
67
+ ```ruby
68
+ User.filter(first_name: { likeic: nil })
69
+ User.filter({})
70
+ User.where(nil)
71
+ ```
72
+
73
+ A special `isnull` filter which accepts `true` or `false` can be used to filter nulls.
74
+
75
+ ### Filter with Joins
76
+
77
+ If another table is provided in the filter, the query builder will automatically do a join based on the table name:
78
+
79
+ ```ruby
80
+ # Automatically joins University with User
81
+ University.filter(name: { eq: 'SLU' },
82
+ users: { first_name: { likeic: 'john' }})
83
+ ```
84
+
85
+ However, if the parameter value is nil, the join is ommitted:
86
+
87
+ ```ruby
88
+ # Automatically joins University with User
89
+ query = University.filter(name: { eq: 'SLU' },
90
+ users: { first_name: { likeic: nil }})
91
+
92
+ query.to_sql # will output "SELECT \"universities\".* FROM \"universities\" WHERE (universities.name = 'SLU')"
93
+ ```
94
+
95
+ ## Development
96
+
97
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
98
+
99
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
100
+
101
+ ## Contributing
102
+
103
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/quickfilter.
104
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "quickfilter"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,26 @@
1
+ require 'active_support/concern'
2
+ require 'active_record'
3
+ require 'active_record/reflection'
4
+ require 'active_record/relation'
5
+
6
+ require 'quickfilter/version'
7
+ require 'quickfilter/query_builder'
8
+
9
+
10
+ module Quickfilter
11
+ extend ActiveSupport::Concern
12
+
13
+ class_methods do
14
+ # Parameters in the form of:
15
+ # { [table]: { [field]: { [operator]: [value] }}}
16
+ #
17
+ # e.g.:
18
+ # { courses: { name: { likeic: 'John' }}
19
+ # { sessions: { start: { gte: DateTime.now - 1.days, lte: DateTime.now }}}
20
+ #
21
+ # Wherein, table is optional and defaults to self.table_name
22
+ def filter(params)
23
+ return QueryBuilder.new(self).build(params).query
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ require 'quickfilter/handlers'
2
+
3
+ module Quickfilter
4
+ class Filter
5
+
6
+ attr_accessor :field, :operator, :value, :adapter
7
+
8
+ def initialize(table, field, operator, value, adapter)
9
+ self.field = "#{table}.#{field}"
10
+ self.operator = operator
11
+ self.value = value
12
+ self.adapter = adapter
13
+ end
14
+
15
+ def build
16
+ return nil if self.value.blank?
17
+ return Quickfilter::Handlers.get(self.operator, self.adapter).call(self.field, self.value)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,39 @@
1
+ module Quickfilter
2
+ module Handlers
3
+ SQL = {
4
+ eq: lambda { |field, value| ["#{field} = ?", value] },
5
+ eqic: lambda { |field, value| ["lower(#{field}) = ?", "#{value.downcase}"] },
6
+ likeic: lambda { |field, value| ["lower(#{field}) like ?", "#{value.downcase}"] },
7
+ startswith: lambda { |field, value| ["lower(#{field}) like ?", "#{value.downcase}%"] },
8
+ contains: lambda { |field, value| ["lower(#{field}) like ?", "%#{value.downcase}%"] },
9
+
10
+ gt: lambda { |field, value| ["#{field} > ?", value] },
11
+ gte: lambda { |field, value| ["#{field} >= ?", value] },
12
+ lt: lambda { |field, value| ["#{field} < ?", value] },
13
+ lte: lambda { |field, value| ["#{field} <= ?", value] },
14
+
15
+ in: lambda { |field, value| ["#{field} in (?)", value] },
16
+ isnull: lambda { |field, value| [field, (value.present? and value) ? 'null' : 'not null'] }
17
+ }
18
+
19
+ PG = {
20
+ sameday: lambda { |field, value| ["date_trunc('day', #{field}) = ?", value.to_date] },
21
+ sameweek: lambda { |field, value| ["date_trunc('week', #{field}) = ?", value.to_date.beginning_of_week] },
22
+ samemonth: lambda { |field, value| ["date_trunc('month', #{field}) = ?", value.to_date.beginning_of_month] }
23
+ }
24
+
25
+ class << self
26
+ def get(name, adapter)
27
+ result = SQL[name]
28
+ result ||= PG[name] if self.is_pg?(adapter)
29
+
30
+ raise "Operation #{name} not supported for adapter #{adapter}." if result.nil?
31
+ return result
32
+ end
33
+
34
+ def is_pg?(adapter)
35
+ return adapter == 'postgresql'
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,58 @@
1
+ require 'quickfilter/filter'
2
+
3
+ module Quickfilter
4
+ class QueryBuilder
5
+ attr_reader :klass, :query, :adapter
6
+
7
+ def initialize(klass, adapter=nil)
8
+ @klass = klass
9
+ @query = klass.where(nil)
10
+ @associations = klass.reflect_on_all_associations
11
+
12
+ @adapter = adapter
13
+ @adapter ||= ActiveRecord::Base.connection_config[:adapter]
14
+ end
15
+
16
+ def build(params)
17
+ params.each do |param|
18
+ self.with(param) do |tablename, fieldname, operator, value|
19
+ self.filter(tablename, fieldname, operator, value)
20
+ self.join(tablename) if tablename != @klass.table_name
21
+ end
22
+ end
23
+ return self
24
+ end
25
+
26
+ protected
27
+ def filter(tablename, fieldname, operator, value)
28
+ filter = Filter.new(tablename, fieldname, operator, value, @adapter)
29
+ @query = @query.where(filter.build)
30
+ return self
31
+ end
32
+
33
+ def join(tablename)
34
+ @query = @query.joins(self.association_for(tablename))
35
+ return self
36
+ end
37
+
38
+ def with(param)
39
+ param[1].each do |second|
40
+ if second[1].is_a?(Hash)
41
+ second[1].each do |operator, value|
42
+ next if value.nil?
43
+ yield(param[0], second[0], operator, value)
44
+ end
45
+ else
46
+ next if second[1].nil?
47
+ yield(@klass.table_name, param[0], second[0], second[1])
48
+ end
49
+ end
50
+ end
51
+
52
+ def association_for(table)
53
+ @associations.each do |assoc|
54
+ return assoc.name if table.to_s == assoc.table_name
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ module Quickfilter
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'quickfilter/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "quickfilter"
8
+ spec.version = Quickfilter::VERSION
9
+ spec.authors = ["Rodette Pedro"]
10
+ spec.email = ["rpedro@jway.com"]
11
+
12
+ spec.summary = "Build Filters Quickly"
13
+ spec.description = "Search active records using a standard set of SQL filters."
14
+ spec.homepage = "https://github.com/rcpedro/quickfilter"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
20
+ else
21
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
22
+ end
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.12"
30
+ spec.add_development_dependency "rake", "~> 10.0"
31
+ spec.add_development_dependency "rspec", "~> 3.0"
32
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: quickfilter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rodette Pedro
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-09-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: Search active records using a standard set of SQL filters.
56
+ email:
57
+ - rpedro@jway.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - README.md
67
+ - Rakefile
68
+ - bin/console
69
+ - bin/setup
70
+ - lib/quickfilter.rb
71
+ - lib/quickfilter/filter.rb
72
+ - lib/quickfilter/handlers.rb
73
+ - lib/quickfilter/query_builder.rb
74
+ - lib/quickfilter/version.rb
75
+ - quickfilter.gemspec
76
+ homepage: https://github.com/rcpedro/quickfilter
77
+ licenses: []
78
+ metadata:
79
+ allowed_push_host: https://rubygems.org
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.4.5
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: Build Filters Quickly
100
+ test_files: []