filter_me 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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/LICENCE +20 -0
- data/README.md +11 -0
- data/Rakefile +12 -0
- data/config.ru +7 -0
- data/filter_me.gemspec +27 -0
- data/lib/filter_me/filter/arel_field_filter.rb +51 -0
- data/lib/filter_me/filter/dsl.rb +67 -0
- data/lib/filter_me/filter/field_validator.rb +32 -0
- data/lib/filter_me/filter.rb +73 -0
- data/lib/filter_me/filterable.rb +9 -0
- data/lib/filter_me/version.rb +3 -0
- data/lib/filter_me.rb +88 -0
- data/spec/acceptance/filters/accounts_filter_spec.rb +73 -0
- data/spec/acceptance/filters/users_filter_spec.rb +51 -0
- data/spec/internal/app/filters/accounts_filter.rb +5 -0
- data/spec/internal/app/filters/users_filter.rb +7 -0
- data/spec/internal/app/models/account.rb +3 -0
- data/spec/internal/app/models/user.rb +3 -0
- data/spec/internal/config/database.yml +3 -0
- data/spec/internal/db/fixtures/accounts.rb +17 -0
- data/spec/internal/db/fixtures/users.rb +26 -0
- data/spec/internal/db/schema.rb +13 -0
- data/spec/internal/log/.gitignore +1 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/unit/active_record_filter_spec.rb +28 -0
- data/spec/unit/arel_field_filter_spec.rb +103 -0
- data/spec/unit/filter_arel_dsl_spec.rb +187 -0
- data/spec/unit/filter_me_spec.rb +41 -0
- metadata +146 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 48be03e95db1c79700cb86c0e4d7d5fc3bb0105f
|
4
|
+
data.tar.gz: 1dadc42cd98cf3c249139deff59d5bb381e02f93
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0176700ff8445af02e848c65c8ad4e9018bce1c863ad8dab7932d349c1a5574a67c68190a0e21f28eacf320d82ef6b27c2d7a3e6414ebe7d4c656600b153d764
|
7
|
+
data.tar.gz: ef3971291473338f4d2728d9fa99d11e12be7f78b3bc962da835567b2f64bea9ea6e728ba404aa3027dd10458395d18d2fefd3c72a48165fe17414f5d7089d15
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENCE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Sam Clopton
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
[](https://codeclimate.com/github/Samsinite/filter_me)
|
2
|
+
filter_me
|
3
|
+
=========
|
4
|
+
|
5
|
+
A Rails/ActiveRecord filtering gem
|
6
|
+
|
7
|
+
### Coming Soon to a application near you!
|
8
|
+
|
9
|
+
|
10
|
+
## License
|
11
|
+
Copyright (c) 2014, Filter Me is developed and maintained by Sam Clopton, and is released under the open MIT Licence.
|
data/Rakefile
ADDED
data/config.ru
ADDED
data/filter_me.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'filter_me/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = 'filter_me'
|
9
|
+
s.version = FilterMe::VERSION
|
10
|
+
s.authors = ['Sam Clopton']
|
11
|
+
s.email = ['samsinite@gmail.com']
|
12
|
+
s.homepage = 'https://github.com/samsinite/filter_me'
|
13
|
+
s.summary = 'Filter Me'
|
14
|
+
s.description = 'This friendly library gives you ActiveRecord/Arel filtering in your Rails app.'
|
15
|
+
s.license = 'MIT'
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
19
|
+
s.require_paths = ['lib']
|
20
|
+
|
21
|
+
s.add_runtime_dependency 'activerecord', '>= 3.2.0'
|
22
|
+
s.add_runtime_dependency 'activesupport', '>= 3.2.0'
|
23
|
+
#s.add_runtime_dependency 'activemodel', '>= 3.2.0'
|
24
|
+
s.add_development_dependency 'combustion', '~> 0.5.1'
|
25
|
+
s.add_development_dependency 'rspec-rails', '~> 2.13'
|
26
|
+
s.add_development_dependency 'sqlite3', '~> 1.3.7'
|
27
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module FilterMe
|
2
|
+
class Filter
|
3
|
+
class ArelFieldFilter
|
4
|
+
attr_accessor :filters, :configuration
|
5
|
+
|
6
|
+
def initialize(filters, configuration)
|
7
|
+
@filters = filters
|
8
|
+
@configuration = configuration
|
9
|
+
|
10
|
+
unless validator.valid_filters?(filters)
|
11
|
+
raise FiltersNotWhiteListedError, "The filter types #{validator.invalid_filters(filters)} are not allowed."
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def filter(relation)
|
16
|
+
arel_filters.inject { |arel_relation, filter| filter.and(arel_relation) }
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def arel_filters
|
22
|
+
self.filters.map { |filter_array| build_filters_from_filter_array(filter_array) }.flatten
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_filters_from_filter_array(filter_array)
|
26
|
+
type = filter_array[0]
|
27
|
+
filter_array[1].map { |value| build_arel_filter(type, value) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_arel_filter(type, value)
|
31
|
+
arel_filter = arel_table[field].send(type, value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def arel_table
|
35
|
+
model_class.arel_table
|
36
|
+
end
|
37
|
+
|
38
|
+
def model_class
|
39
|
+
configuration[:model_class]
|
40
|
+
end
|
41
|
+
|
42
|
+
def validator
|
43
|
+
configuration[:validator]
|
44
|
+
end
|
45
|
+
|
46
|
+
def field
|
47
|
+
configuration[:field].to_sym
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module FilterMe
|
2
|
+
class Filter
|
3
|
+
|
4
|
+
###
|
5
|
+
# DSL Syntax (early version, highly subject to change):
|
6
|
+
#
|
7
|
+
# class UserFilter < FilterMe::ArelFilter
|
8
|
+
# model User
|
9
|
+
#
|
10
|
+
# association :account
|
11
|
+
# association :company, filter_class: SuperDuperCompanyFilter
|
12
|
+
#
|
13
|
+
# field :login, [:matches]
|
14
|
+
# field :created_at, [:gt, :gteq, :lt, :lteq, :eq]
|
15
|
+
# field :email, :all
|
16
|
+
# ...
|
17
|
+
# end
|
18
|
+
###
|
19
|
+
class ArelDSL
|
20
|
+
attr_reader :filter_class
|
21
|
+
|
22
|
+
extend Forwardable
|
23
|
+
|
24
|
+
def initialize(filter_class)
|
25
|
+
@filter_class = filter_class
|
26
|
+
end
|
27
|
+
|
28
|
+
def model(klass)
|
29
|
+
@filter_class._model = klass
|
30
|
+
end
|
31
|
+
|
32
|
+
def filter_for_name(name)
|
33
|
+
"#{name.to_s.pluralize.camelize}Filter".constantize
|
34
|
+
end
|
35
|
+
|
36
|
+
def field(name, filter_types)
|
37
|
+
field_validator = filter_types == :all ? AllValidator.new : FieldValidator.new(filter_types)
|
38
|
+
filter(name, ArelFieldFilter, { :field => name,
|
39
|
+
:validator => field_validator,
|
40
|
+
:model_class => self.filter_class._model })
|
41
|
+
end
|
42
|
+
|
43
|
+
def association(name, options={})
|
44
|
+
association_filter_class = options.fetch(:filter_class) { filter_for_name(name) }
|
45
|
+
configuration = options.fetch(:configuration, {})
|
46
|
+
filter_class._associations[name] = association_filter_class
|
47
|
+
|
48
|
+
filter(name, association_filter_class, configuration, filter_class._model)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def filter(name, klass, configuration, association = nil)
|
54
|
+
this = self
|
55
|
+
filter_class.send(:define_method, name) do |relation, filters|
|
56
|
+
filter = klass.new(filters, configuration)
|
57
|
+
if association
|
58
|
+
relation = relation.joins(name.to_sym)
|
59
|
+
filter.filter(relation)
|
60
|
+
else
|
61
|
+
relation.where(filter.filter(relation))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module FilterMe
|
2
|
+
class Filter
|
3
|
+
class AllValidator
|
4
|
+
def valid_filters?(filters)
|
5
|
+
true
|
6
|
+
end
|
7
|
+
|
8
|
+
def invalid_filters(filters)
|
9
|
+
[]
|
10
|
+
end
|
11
|
+
|
12
|
+
def whitelisted_filters
|
13
|
+
:all
|
14
|
+
end
|
15
|
+
end
|
16
|
+
class FieldValidator
|
17
|
+
attr_reader :whitelisted_filters
|
18
|
+
|
19
|
+
def initialize(whitelisted_filters)
|
20
|
+
@whitelisted_filters = whitelisted_filters
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid_filters?(filters)
|
24
|
+
filters.all? { |filter| whitelisted_filters.include? filter[0] }
|
25
|
+
end
|
26
|
+
|
27
|
+
def invalid_filters(filters)
|
28
|
+
filters.select { |filter| !(whitelisted_filters.include? filter[0]) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
require 'filter_me/filterable'
|
4
|
+
require 'filter_me/filter/field_validator'
|
5
|
+
require 'filter_me/filter/arel_field_filter'
|
6
|
+
require 'filter_me/filter/dsl'
|
7
|
+
|
8
|
+
module FilterMe
|
9
|
+
class ActiveRecordFilter
|
10
|
+
include Filterable
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def inherited(subclass)
|
14
|
+
subclass._associations = (_associations || {}).dup
|
15
|
+
end
|
16
|
+
|
17
|
+
def filter_for(relation)
|
18
|
+
"#{relation.klass.name.pluralize}Filter".constantize
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :_associations, :_model
|
22
|
+
|
23
|
+
extend Forwardable
|
24
|
+
|
25
|
+
def_delegators :dsl, :field, :association, :model
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def dsl
|
30
|
+
@dsl ||= Filter::ArelDSL.new(self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_accessor :configuration, :filters
|
35
|
+
|
36
|
+
# Filters are expected to be an array of arrays where the first value is
|
37
|
+
# the filter type and the remaining values are the values to filter by. Or if the
|
38
|
+
# filter type is a relation, the rest are sub filters (sub-array).
|
39
|
+
#
|
40
|
+
### Ex:
|
41
|
+
# class AccountsFilter < FilterMe::ActiveRecordFilter
|
42
|
+
# model Account
|
43
|
+
#
|
44
|
+
# field :type, [:matches, :eq, :not_eq]
|
45
|
+
# end
|
46
|
+
# class UsersFilter < FilterMe::ActiveRecordFilter
|
47
|
+
# model User
|
48
|
+
#
|
49
|
+
# association :account, :filter_class => AccountsFilter
|
50
|
+
# field :username, [:matches, :eq, :not_eq]
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# then the 'filters' param for an initialization of a user filter which would then
|
54
|
+
# filter users by accounts of types that match '%paid%' would look like:
|
55
|
+
# [
|
56
|
+
# # filters on the associaton account
|
57
|
+
# [:account, [
|
58
|
+
# [:type, [
|
59
|
+
# [:matches, ["%paid%"]]
|
60
|
+
# ]]
|
61
|
+
# ]],
|
62
|
+
# # filters on field username
|
63
|
+
# [:username, [
|
64
|
+
# [:not_eq, ["user1", "user2"]],
|
65
|
+
# [:matches, ["%user_%"]]
|
66
|
+
# ]]
|
67
|
+
# ]
|
68
|
+
def initialize(filters, configuration)
|
69
|
+
@filters = filters
|
70
|
+
@configuration = configuration
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/filter_me.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
require 'filter_me/filter'
|
4
|
+
require 'filter_me/version'
|
5
|
+
|
6
|
+
module FilterMe
|
7
|
+
class FiltersNotWhiteListedError < StandardError; end
|
8
|
+
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def normalize_param(param)
|
13
|
+
param.inject([]) do |filter, (k, v)|
|
14
|
+
case v
|
15
|
+
when Hash
|
16
|
+
filter.push([k, FilterMe.normalize_param(v)])
|
17
|
+
when Array
|
18
|
+
filter.push([k, v])
|
19
|
+
else
|
20
|
+
filter.push([k, [v]])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
included do
|
27
|
+
if respond_to?(:helper_method)
|
28
|
+
helper_method :filter_me
|
29
|
+
end
|
30
|
+
|
31
|
+
if respond_to?(:hide_action)
|
32
|
+
hide_action :filter_me
|
33
|
+
hide_action :filter_configuration
|
34
|
+
hide_action :build_filter
|
35
|
+
hide_action :filter_params
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def filter_me(relation, filter_class=nil)
|
40
|
+
klass = filter_class || ActiveRecordFilter.filter_for(relation)
|
41
|
+
filter = build_filter(klass)
|
42
|
+
|
43
|
+
filter.filter(relation)
|
44
|
+
end
|
45
|
+
|
46
|
+
def filter_configuration
|
47
|
+
{}
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_filter(filter_class)
|
51
|
+
filter_class.new(filter_params, filter_configuration)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Normalized Filter params Example:
|
55
|
+
# [
|
56
|
+
# # filters on the associaton account
|
57
|
+
# [:account, [
|
58
|
+
# [:type, [
|
59
|
+
# [:eq, ["admin"]]
|
60
|
+
# ]]
|
61
|
+
# ]],
|
62
|
+
# # filters on field username
|
63
|
+
# [:username, [
|
64
|
+
# [:not_in_any, ["sam", "samsinite"]],
|
65
|
+
# [:matches, ["sam%"]]
|
66
|
+
# ]],
|
67
|
+
# [:company, [
|
68
|
+
# [:job, [
|
69
|
+
# [:name, [
|
70
|
+
# [:matches, ["%software%"]]
|
71
|
+
# ]]
|
72
|
+
# ]]
|
73
|
+
# ]]
|
74
|
+
# ]
|
75
|
+
|
76
|
+
# FilterMe default params example (Seems straight forward for Rails to parse):
|
77
|
+
# {filters: {
|
78
|
+
# username: {matches: "sam%", not_in_any: ["sam", "samsinite"]},
|
79
|
+
# account: {type: {eq: "admin"}},
|
80
|
+
# company: {job: {name: {matches: "%software%"}}}
|
81
|
+
# }}
|
82
|
+
#
|
83
|
+
# Instead of Django Tastypie's style (original inspiration):
|
84
|
+
# [{username__matches: "%sam%"}, {account__type__eq: "admin"}]
|
85
|
+
def filter_params
|
86
|
+
FilterMe.normalize_param(params.fetch(:filters, {}))
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AccountsFilter do
|
4
|
+
before(:each) do
|
5
|
+
load_accounts
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:mock_controller_class) do
|
9
|
+
controller = Class.new do
|
10
|
+
include FilterMe
|
11
|
+
|
12
|
+
attr_accessor :params
|
13
|
+
|
14
|
+
def index
|
15
|
+
filter_me(Account.all)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:mock_controller) do
|
21
|
+
mock_controller_class.new
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns all accounts without any filtering" do
|
25
|
+
mock_controller.params = {}
|
26
|
+
|
27
|
+
expect(mock_controller.index.length).to eq(Account.all.length)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns accounts that cost less than 100000 and greater than 10000" do
|
31
|
+
mock_controller.params = {:filters => {
|
32
|
+
:cost => {:lt => 100000, :gt => 10000}
|
33
|
+
}}
|
34
|
+
|
35
|
+
mock_controller.index.each do |account|
|
36
|
+
expect(account.cost).to be > 10000
|
37
|
+
expect(account.cost).to be < 100000
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "returns accounts that cost less than or equal to 100000 and greater than 10000" do
|
42
|
+
mock_controller.params = {:filters => {
|
43
|
+
:cost => {:lteq => 100000, :gt => 10000}
|
44
|
+
}}
|
45
|
+
|
46
|
+
mock_controller.index.each do |account|
|
47
|
+
expect(account.cost).to be > 10000
|
48
|
+
expect(account.cost).to be <= 100000
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "returns accounts that cost less than 100000 and greater than or equal to 10000" do
|
53
|
+
mock_controller.params = {:filters => {
|
54
|
+
:cost => {:lt => 100000, :gteq => 10000}
|
55
|
+
}}
|
56
|
+
|
57
|
+
mock_controller.index.each do |account|
|
58
|
+
expect(account.cost).to be >= 10000
|
59
|
+
expect(account.cost).to be < 100000
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "returns accounts that cost less than or equal to 100000 and greater than or equal to 10000" do
|
64
|
+
mock_controller.params = {:filters => {
|
65
|
+
:cost => {:lteq => 100000, :gteq => 10000}
|
66
|
+
}}
|
67
|
+
|
68
|
+
mock_controller.index.each do |account|
|
69
|
+
expect(account.cost).to be >= 10000
|
70
|
+
expect(account.cost).to be <= 100000
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe UsersFilter do
|
4
|
+
before(:each) do
|
5
|
+
load_users
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:mock_controller_class) do
|
9
|
+
controller = Class.new do
|
10
|
+
include FilterMe
|
11
|
+
|
12
|
+
attr_accessor :params
|
13
|
+
|
14
|
+
def index
|
15
|
+
filter_me(User.all)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:mock_controller) do
|
21
|
+
mock_controller_class.new
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns all users without any filtering" do
|
25
|
+
mock_controller.params = {}
|
26
|
+
|
27
|
+
expect(mock_controller.index.length).to eq(User.all.length)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns users with e-mails from the domain test.com" do
|
31
|
+
mock_controller.params = {:filters => {
|
32
|
+
:email => {:matches => "%test.com"}
|
33
|
+
}}
|
34
|
+
|
35
|
+
mock_controller.index.each do |user|
|
36
|
+
expect(user.email).to end_with "test.com"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "returns users with e-mails from the domain test.com with accounts that cost less than 100000" do
|
41
|
+
mock_controller.params = {:filters => {
|
42
|
+
:email => {:matches => "%test.com"},
|
43
|
+
:account => {:cost => {:lt => 100000}}
|
44
|
+
}}
|
45
|
+
|
46
|
+
mock_controller.index.each do |user|
|
47
|
+
expect(user.email).to end_with "test.com"
|
48
|
+
expect(user.account.cost).to be < 100000
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Fixtures
|
2
|
+
module Accounts
|
3
|
+
def load_accounts
|
4
|
+
Account.create! do |account|
|
5
|
+
account.cost = 10000
|
6
|
+
end
|
7
|
+
|
8
|
+
Account.create! do |account|
|
9
|
+
account.cost = 50000
|
10
|
+
end
|
11
|
+
|
12
|
+
Account.create! do |account|
|
13
|
+
account.cost = 100000
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Fixtures
|
2
|
+
module Users
|
3
|
+
def load_users
|
4
|
+
User.create! do |user|
|
5
|
+
user.username = "test1"
|
6
|
+
user.email = "test2@test.com"
|
7
|
+
|
8
|
+
user.create_account!(:cost => 100000)
|
9
|
+
end
|
10
|
+
|
11
|
+
User.create! do |user|
|
12
|
+
user.username = "test2"
|
13
|
+
user.email = "test2@test.com"
|
14
|
+
|
15
|
+
user.create_account!(:cost => 50000)
|
16
|
+
end
|
17
|
+
|
18
|
+
User.create! do |user|
|
19
|
+
user.username = "test3"
|
20
|
+
user.email = "test3@spaz.com"
|
21
|
+
|
22
|
+
user.create_account!(:cost => 10000)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
create_table :users, :force => true do |t|
|
3
|
+
t.string :username
|
4
|
+
t.string :email
|
5
|
+
t.timestamps
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :accounts, :force => true do |t|
|
9
|
+
t.integer :user_id
|
10
|
+
t.integer :cost
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
*.log
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'combustion'
|
4
|
+
|
5
|
+
Bundler.require :default, :development
|
6
|
+
|
7
|
+
# Add rails modules here to have combustion load:
|
8
|
+
Combustion.initialize! :active_record
|
9
|
+
|
10
|
+
require 'rspec/rails'
|
11
|
+
|
12
|
+
# Load fixture helpers for testing
|
13
|
+
Dir[File.join(File.dirname(__FILE__), 'internal', 'db', "fixtures", "**", '*.rb')].each { |file| require file }
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.use_transactional_fixtures = true
|
17
|
+
|
18
|
+
config.include Fixtures::Accounts
|
19
|
+
config.include Fixtures::Users
|
20
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FilterMe::ActiveRecordFilter do
|
4
|
+
let(:filters) do
|
5
|
+
[
|
6
|
+
# filters on associaton account
|
7
|
+
[:account, [
|
8
|
+
[:type, [
|
9
|
+
[:matches, ["%paid%"]]
|
10
|
+
]]
|
11
|
+
]],
|
12
|
+
# filters on field username
|
13
|
+
[:username, [
|
14
|
+
[:not_eq, ["user1", "user2"]],
|
15
|
+
[:matches, ["%user_%"]]
|
16
|
+
]]
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "calls each filter method for every filter it is passed" do
|
21
|
+
relation_mock = double("relation")
|
22
|
+
model_filter = FilterMe::ActiveRecordFilter.new(filters, {})
|
23
|
+
|
24
|
+
expect(model_filter).to receive(:account).with(relation_mock, filters[0][1]) { relation_mock }
|
25
|
+
expect(model_filter).to receive(:username).with(relation_mock, filters[1][1]) { relation_mock }
|
26
|
+
model_filter.filter(relation_mock)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FilterMe::Filter::ArelFieldFilter do
|
4
|
+
context "with only greater than and less than allowed" do
|
5
|
+
let(:field) { :test }
|
6
|
+
let(:arel_table) do
|
7
|
+
Arel::Table.new("model")
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:filters) { [:lt, :gt] }
|
11
|
+
|
12
|
+
let(:model_class) do
|
13
|
+
model = double("model")
|
14
|
+
allow(model).to receive(:arel_table) { arel_table }
|
15
|
+
|
16
|
+
model
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:configuration) do
|
20
|
+
{
|
21
|
+
:field => field,
|
22
|
+
:model_class => model_class,
|
23
|
+
:validator => FilterMe::Filter::FieldValidator.new(filters)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can create an arel field filter with a less than filter" do
|
28
|
+
field_filter = FilterMe::Filter::ArelFieldFilter.new([[:lt, [10]]], configuration)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "can create an arel field filter with a greater than filter" do
|
32
|
+
field_filter = FilterMe::Filter::ArelFieldFilter.new([[:gt, [10]]], configuration)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "can create an arel field filter with a greater than and less than filter" do
|
36
|
+
field_filter = FilterMe::Filter::ArelFieldFilter.new([[:gt, [10]], [:lt, [20]]], configuration)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "cannot create an arel field filter with a equal filter" do
|
40
|
+
expect { FilterMe::Filter::ArelFieldFilter.new([[:eq, [10]]], configuration) }.to raise_error
|
41
|
+
end
|
42
|
+
end
|
43
|
+
context "with all types allowed" do
|
44
|
+
let(:field) { :test }
|
45
|
+
let(:arel_table) do
|
46
|
+
Arel::Table.new("model")
|
47
|
+
end
|
48
|
+
|
49
|
+
let(:model_class) do
|
50
|
+
model = double("model")
|
51
|
+
allow(model).to receive(:arel_table) { arel_table }
|
52
|
+
|
53
|
+
model
|
54
|
+
end
|
55
|
+
|
56
|
+
let(:configuration) do
|
57
|
+
{
|
58
|
+
:field => field,
|
59
|
+
:model_class => model_class,
|
60
|
+
:validator => FilterMe::Filter::AllValidator.new
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
it "can create a arel field filter" do
|
65
|
+
field_filter = FilterMe::Filter::ArelFieldFilter.new([[:lt, [10]], [:gt, [1]]], configuration)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "builds the correct arel filter with one filter type of one filter value" do
|
69
|
+
field_filter = FilterMe::Filter::ArelFieldFilter.new([[:lt, [10]]], configuration)
|
70
|
+
relation_mock = double("relation")
|
71
|
+
|
72
|
+
arel_filter = arel_table[field].lt(10)
|
73
|
+
expect(field_filter.filter(relation_mock)).to eq(arel_filter)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "builds the correct arel filter with one filter type of two filter values" do
|
77
|
+
field_filter = FilterMe::Filter::ArelFieldFilter.new([[:matches, ["%hi%", "%hey%"]]], configuration)
|
78
|
+
relation_mock = double("relation")
|
79
|
+
|
80
|
+
arel_filter = arel_table[field].matches("%hey%").and(arel_table[field].matches("%hi%"))
|
81
|
+
expect(field_filter.filter(relation_mock)).to eq(arel_filter)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "builds the correct arel filter with two filter types of one filter value each" do
|
85
|
+
field_filter = FilterMe::Filter::ArelFieldFilter.new([[:gt, [1]], [:lt, [10]]], configuration)
|
86
|
+
relation_mock = double("relation")
|
87
|
+
|
88
|
+
arel_filter = arel_table[field].lt(10).and(arel_table[field].gt(1))
|
89
|
+
expect(field_filter.filter(relation_mock)).to eq(arel_filter)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "builds the correct arel filter with two filter types of two filter value each" do
|
93
|
+
field_filter = FilterMe::Filter::ArelFieldFilter.new([[:matches, ["%hi%", "%hey%"]], [:lt, ["z", "Z"]]], configuration)
|
94
|
+
relation_mock = double("relation")
|
95
|
+
|
96
|
+
arel_filter = arel_table[field].lt("Z")
|
97
|
+
.and(arel_table[field].lt("z")
|
98
|
+
.and(arel_table[field].matches("%hey%")
|
99
|
+
.and(arel_table[field].matches("%hi%"))))
|
100
|
+
expect(field_filter.filter(relation_mock)).to eq(arel_filter)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FilterMe::Filter::ArelDSL do
|
4
|
+
it "creates a new arel field filter method named after the field" do
|
5
|
+
model = double("model")
|
6
|
+
filter_class = double("filter_class")
|
7
|
+
filter_class.stub(:_model).and_return(model)
|
8
|
+
filter_class.should_receive(:define_method) do |name, &block|
|
9
|
+
name.should eq(:test)
|
10
|
+
block.should_not be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
dsl = FilterMe::Filter::ArelDSL.new(filter_class)
|
14
|
+
dsl.field(:test, [:gt, :lt, :eq])
|
15
|
+
end
|
16
|
+
|
17
|
+
context "after defining a field filter the dynamically created filter method" do
|
18
|
+
it "initializes a ArelFieldFilter instance with the values to filter and the filter field configuration" do
|
19
|
+
filters = [:test1, :test2]
|
20
|
+
relation_mock = double("relation")
|
21
|
+
expect(relation_mock).to receive(:where) { |relation| relation }
|
22
|
+
|
23
|
+
model_mock = double("model")
|
24
|
+
|
25
|
+
FilterMe::Filter::FieldValidator.send(:define_method, "==") do |obj|
|
26
|
+
obj.whitelisted_filters == self.whitelisted_filters
|
27
|
+
end
|
28
|
+
|
29
|
+
field_filter_instance = double("field filter instance")
|
30
|
+
allow(field_filter_instance).to receive(:filter).once
|
31
|
+
|
32
|
+
field_filter_class = double("field filter class")
|
33
|
+
allow(field_filter_class).to receive(:new) do |filters, configuration|
|
34
|
+
expect(filters).to eq(filters)
|
35
|
+
expect(configuration).to eq({
|
36
|
+
:field => :test,
|
37
|
+
:validator => FilterMe::Filter::FieldValidator.new([:gt, :lt, :eq]),
|
38
|
+
:model_class => model_mock
|
39
|
+
})
|
40
|
+
|
41
|
+
field_filter_instance
|
42
|
+
end
|
43
|
+
|
44
|
+
stub_const("FilterMe::Filter::ArelFieldFilter", field_filter_class)
|
45
|
+
|
46
|
+
# We just need an object that supports and implements #define_method so the
|
47
|
+
# dynamic filter method can be created.
|
48
|
+
filter_class = Class.new do
|
49
|
+
end
|
50
|
+
|
51
|
+
allow(filter_class).to receive(:_model) { model_mock }
|
52
|
+
|
53
|
+
dsl = FilterMe::Filter::ArelDSL.new(filter_class)
|
54
|
+
dsl.field(:test, [:gt, :lt, :eq])
|
55
|
+
|
56
|
+
filter_instance = filter_class.new
|
57
|
+
filter_instance.test(relation_mock, filters)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "calls filter on the initialized ArelFieldFilter instance with the relation" do
|
61
|
+
relation_mock = double("relation")
|
62
|
+
expect(relation_mock).to receive(:where) { |relation| relation }
|
63
|
+
|
64
|
+
model_mock = double("model")
|
65
|
+
|
66
|
+
field_filter_instance = double("field filter instance")
|
67
|
+
expect(field_filter_instance).to receive(:filter) { |relation| relation }
|
68
|
+
|
69
|
+
field_filter_class = double("field filter class")
|
70
|
+
allow(field_filter_class).to receive(:new) { |filters, configuration| field_filter_instance }
|
71
|
+
|
72
|
+
stub_const("FilterMe::Filter::ArelFieldFilter", field_filter_class)
|
73
|
+
|
74
|
+
# We just need an object that supports and implements #define_method so the
|
75
|
+
# dynamic filter method can be created.
|
76
|
+
filter_class = Class.new do
|
77
|
+
end
|
78
|
+
|
79
|
+
allow(filter_class).to receive(:_model) { model_mock }
|
80
|
+
|
81
|
+
dsl = FilterMe::Filter::ArelDSL.new(filter_class)
|
82
|
+
dsl.field(:test, [:gt, :lt, :eq])
|
83
|
+
|
84
|
+
filter_instance = filter_class.new
|
85
|
+
|
86
|
+
expect(filter_instance.test(relation_mock, [:test1, :test2])).to eq(relation_mock)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
it "creates a new arel relation filter method named after the association" do
|
92
|
+
model = double("model")
|
93
|
+
filter_class = double("filter_class")
|
94
|
+
filter_class.stub(:_model).and_return(model)
|
95
|
+
filter_class.stub(:_associations).and_return({})
|
96
|
+
filter_class.should_receive(:define_method) do |name, &block|
|
97
|
+
name.should eq(:some_models)
|
98
|
+
block.should_not be_nil
|
99
|
+
end
|
100
|
+
|
101
|
+
dsl = FilterMe::Filter::ArelDSL.new(filter_class)
|
102
|
+
dsl.association(:some_models, {:filter_class => filter_class})
|
103
|
+
end
|
104
|
+
|
105
|
+
context "after defining an association filter the dynamically created filter method" do
|
106
|
+
it "initializes the association filter instance with the values to filter and the filter configuration" do
|
107
|
+
filters = [:test1, :test2]
|
108
|
+
relation_mock = double("relation")
|
109
|
+
allow(relation_mock).to receive(:joins) { relation_mock }
|
110
|
+
allow(relation_mock).to receive(:where) { relation_mock }
|
111
|
+
|
112
|
+
model = double("model")
|
113
|
+
allow(model).to receive(:name) { "Model" }
|
114
|
+
|
115
|
+
mock_association_filter = double("association filter")
|
116
|
+
allow(mock_association_filter).to receive(:filter) { relation_mock }
|
117
|
+
|
118
|
+
mock_association_filter_class = double("assocation filter class")
|
119
|
+
mock_association_filter_class.stub(:new).and_return(mock_association_filter)
|
120
|
+
|
121
|
+
expect(mock_association_filter_class).to receive(:new) do |filters, configuration|
|
122
|
+
expect(filters).to eq(filters)
|
123
|
+
expect(configuration).to eq({})
|
124
|
+
mock_association_filter
|
125
|
+
end
|
126
|
+
|
127
|
+
allow(mock_association_filter_class).to receive(:define_method) do |name, &block|
|
128
|
+
allow(mock_association_filter).to receive(name, &block)
|
129
|
+
end
|
130
|
+
|
131
|
+
mock_association_filter_class.stub(:_model).and_return(model)
|
132
|
+
|
133
|
+
# We just need an object that supports and implements #define_method so the
|
134
|
+
# dynamic filter method can be created.
|
135
|
+
filter_class = Class.new do
|
136
|
+
end
|
137
|
+
|
138
|
+
allow(filter_class).to receive(:_model) { model }
|
139
|
+
allow(filter_class).to receive(:_associations) { {} }
|
140
|
+
|
141
|
+
dsl = FilterMe::Filter::ArelDSL.new(filter_class)
|
142
|
+
dsl.association(:test, {:filter_class => mock_association_filter_class})
|
143
|
+
|
144
|
+
filter_instance = filter_class.new
|
145
|
+
filter_instance.test(relation_mock, filters)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "calls filter on the association filter instance with the relation" do
|
149
|
+
filters = [:test1, :test2]
|
150
|
+
relation_mock = double("relation")
|
151
|
+
allow(relation_mock).to receive(:joins) { relation_mock }
|
152
|
+
allow(relation_mock).to receive(:where) { relation_mock }
|
153
|
+
|
154
|
+
model = double("model")
|
155
|
+
allow(model).to receive(:name) { "Model" }
|
156
|
+
|
157
|
+
mock_association_filter = double("association filter")
|
158
|
+
expect(mock_association_filter).to receive(:filter) do |relation|
|
159
|
+
expect(relation).to eq(relation_mock)
|
160
|
+
|
161
|
+
relation
|
162
|
+
end
|
163
|
+
mock_association_filter_class = double("assocation filter class")
|
164
|
+
|
165
|
+
mock_association_filter_class.stub(:new).and_return(mock_association_filter)
|
166
|
+
|
167
|
+
mock_association_filter_class.stub(:_model).and_return(model)
|
168
|
+
allow(mock_association_filter_class).to receive(:define_method) do |name, &block|
|
169
|
+
allow(mock_association_filter).to receive(name, &block)
|
170
|
+
end
|
171
|
+
|
172
|
+
# We just need an object that supports and implements #define_method so the
|
173
|
+
# dynamic filter method can be created.
|
174
|
+
filter_class = Class.new do
|
175
|
+
end
|
176
|
+
|
177
|
+
allow(filter_class).to receive(:_model) { model }
|
178
|
+
allow(filter_class).to receive(:_associations) { {} }
|
179
|
+
|
180
|
+
dsl = FilterMe::Filter::ArelDSL.new(filter_class)
|
181
|
+
dsl.association(:test, {:filter_class => mock_association_filter_class})
|
182
|
+
|
183
|
+
filter_instance = filter_class.new
|
184
|
+
filter_instance.test(relation_mock, filters)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FilterMe do
|
4
|
+
let(:class_with_module) do
|
5
|
+
Class.new do
|
6
|
+
include FilterMe
|
7
|
+
|
8
|
+
attr_accessor params
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should normalize field params correctly" do
|
13
|
+
username_field_params = {:matches => "sam%", :not_in_any => ["sam", "samsinite"]}
|
14
|
+
username_params_normalized = [
|
15
|
+
[:matches, ["sam%"]],
|
16
|
+
[:not_in_any, ["sam", "samsinite"]]
|
17
|
+
]
|
18
|
+
|
19
|
+
expect(FilterMe.normalize_param(username_field_params)).to eq(username_params_normalized)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should normalize shallow association params correctly" do
|
23
|
+
account_association_params = {:type => {:eq => "admin"}}
|
24
|
+
account_params_normalized = [[:type, [
|
25
|
+
[:eq, ["admin"]]
|
26
|
+
]]]
|
27
|
+
|
28
|
+
expect(FilterMe.normalize_param(account_association_params)).to eq(account_params_normalized)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should normalize deeply nested association params correctly" do
|
32
|
+
company_association_with_nested_job_association_params = {:job => {:name => {:matches => "%software%"}}}
|
33
|
+
company_params_normalized = [[:job, [
|
34
|
+
[:name, [
|
35
|
+
[:matches, ["%software%"]]
|
36
|
+
]]
|
37
|
+
]]]
|
38
|
+
|
39
|
+
expect(FilterMe.normalize_param(company_association_with_nested_job_association_params)).to eq(company_params_normalized)
|
40
|
+
end
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: filter_me
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sam Clopton
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-16 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: 3.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.2.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.2.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: combustion
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.5.1
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.5.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.13'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.13'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sqlite3
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.3.7
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.3.7
|
83
|
+
description: This friendly library gives you ActiveRecord/Arel filtering in your Rails
|
84
|
+
app.
|
85
|
+
email:
|
86
|
+
- samsinite@gmail.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- .gitignore
|
92
|
+
- .travis.yml
|
93
|
+
- Gemfile
|
94
|
+
- LICENCE
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- config.ru
|
98
|
+
- filter_me.gemspec
|
99
|
+
- lib/filter_me.rb
|
100
|
+
- lib/filter_me/filter.rb
|
101
|
+
- lib/filter_me/filter/arel_field_filter.rb
|
102
|
+
- lib/filter_me/filter/dsl.rb
|
103
|
+
- lib/filter_me/filter/field_validator.rb
|
104
|
+
- lib/filter_me/filterable.rb
|
105
|
+
- lib/filter_me/version.rb
|
106
|
+
- spec/acceptance/filters/accounts_filter_spec.rb
|
107
|
+
- spec/acceptance/filters/users_filter_spec.rb
|
108
|
+
- spec/internal/app/filters/accounts_filter.rb
|
109
|
+
- spec/internal/app/filters/users_filter.rb
|
110
|
+
- spec/internal/app/models/account.rb
|
111
|
+
- spec/internal/app/models/user.rb
|
112
|
+
- spec/internal/config/database.yml
|
113
|
+
- spec/internal/db/fixtures/accounts.rb
|
114
|
+
- spec/internal/db/fixtures/users.rb
|
115
|
+
- spec/internal/db/schema.rb
|
116
|
+
- spec/internal/log/.gitignore
|
117
|
+
- spec/spec_helper.rb
|
118
|
+
- spec/unit/active_record_filter_spec.rb
|
119
|
+
- spec/unit/arel_field_filter_spec.rb
|
120
|
+
- spec/unit/filter_arel_dsl_spec.rb
|
121
|
+
- spec/unit/filter_me_spec.rb
|
122
|
+
homepage: https://github.com/samsinite/filter_me
|
123
|
+
licenses:
|
124
|
+
- MIT
|
125
|
+
metadata: {}
|
126
|
+
post_install_message:
|
127
|
+
rdoc_options: []
|
128
|
+
require_paths:
|
129
|
+
- lib
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - '>='
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - '>='
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
requirements: []
|
141
|
+
rubyforge_project:
|
142
|
+
rubygems_version: 2.0.3
|
143
|
+
signing_key:
|
144
|
+
specification_version: 4
|
145
|
+
summary: Filter Me
|
146
|
+
test_files: []
|