filter_me 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Code Climate](https://codeclimate.com/github/Samsinite/filter_me.png)](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: []
|