activerecord-auto_filter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 71bbfa7f6d59232176f41f78dda56840e44b1eac
4
+ data.tar.gz: 98ff34b5a5e4e5a692f348375c63e13e1ffb62e2
5
+ SHA512:
6
+ metadata.gz: 189141eeaeb400717550cfb6da57ea5dbe601506269c67dadb4c0c592122b94e9c9bb44906335a0bf3c0ff83975d66779bbcbf9cc1cf7025048b53693c91b88d
7
+ data.tar.gz: 756e51cfbdc7f272b4d1c81ebf041b9794050e3e39ba31472df7994cae4dacd6e83982ca49d3077061fecacfaeac014afc1c81862d1bc0b938af20312b14868d
@@ -0,0 +1,3 @@
1
+ pkg
2
+ *.lock
3
+ .idea/
data/Gemfile ADDED
@@ -0,0 +1,24 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in blah.gemspec
4
+ gemspec
5
+
6
+ # Database Adapters
7
+ platforms :ruby do
8
+ gem "mysql2"
9
+ end
10
+
11
+ platforms :jruby do
12
+ gem "jdbc-mysql"
13
+ gem "activerecord-jdbcmysql-adapter"
14
+ end
15
+
16
+
17
+
18
+ group :test do
19
+ gem 'test-unit'
20
+ gem "shoulda", '3.0.1'
21
+ gem "shoulda-context", '1.0.0'
22
+ gem "shoulda-matchers", '1.0.0'
23
+ end
24
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 kaushik
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,54 @@
1
+ # Activerecord::AutoFilter
2
+
3
+ Configuration based condition building and inclusion handling extension for ActiveRecord::Base
4
+
5
+ In case of eager loading of a model and its associations, it may be required to obtain query results based on filter criteria that span across multiple associations. To achieve this, we tend to construct dynamic where clauses based on the presence of certain filter criteria. Since this happens so frequently, it causes code duplication and hence violates the DRY principle. This gem tries to address that concern. Given request param hash and query specification hash, it is capable of emitting the required associations that need to be included, and where conditions that need to applied on a given model, to achieve the desired result or even apply them to the model directly.
6
+
7
+ **The primary goal of this gem is to build dynamic query conditions**. It is not limited to catering to the needs of
8
+ eager loading like above and can also be used to build where conditions on just a model too. All that is required is
9
+ - request-params
10
+ - query-specification
11
+ - proper association definitions(if needed).
12
+
13
+
14
+
15
+
16
+ **Notes:**
17
+
18
+ 1. See activerecord-auto_filter.gemspec for allowed ruby versions.
19
+
20
+ 2. Following issues will be addressed later
21
+
22
+ - Currently it is mysql specific as the condition extraction is based on splitting the Arel::Table query by the word 'WHERE'.
23
+ - Accept Procs for determining the condition for building where clause, instead of just param value presence. This gives better control for the programmer.
24
+
25
+
26
+ ## Installation
27
+
28
+ Add this line to your application's Gemfile:
29
+
30
+ gem 'activerecord-auto_filter'
31
+
32
+ And then execute:
33
+
34
+ $ bundle
35
+
36
+ Or install it yourself as:
37
+
38
+ $ gem install activerecord-auto_filter
39
+
40
+ ## Demo
41
+
42
+ Run demo.rb for usage demo. Steps are below.
43
+
44
+ 1. Checkout this repo
45
+ 2. Run bundle install
46
+ 3. Run ruby demo.rb
47
+
48
+ ## Contributing
49
+
50
+ 1. Fork it
51
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
52
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
53
+ 4. Push to the branch (`git push origin my-new-feature`)
54
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'activerecord-auto_filter/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "activerecord-auto_filter"
8
+ gem.version = Activerecord::AutoFilter::VERSION
9
+ gem.authors = ["kaushik"]
10
+ gem.email = ["kaushikd49@gmail.com"]
11
+ gem.description = %q{Configuration based condition building and inclusion handling extension for ActiveRecord::Base}
12
+ gem.summary = %q{Inclusions and where-condition building extension for ActiveRecord}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.required_ruby_version = ">= 1.9.3"
21
+
22
+ gem.add_runtime_dependency "activerecord", ">= 3.1.0"
23
+ gem.add_development_dependency "rake"
24
+ end
data/demo.rb ADDED
@@ -0,0 +1,81 @@
1
+ require 'bundler/setup'
2
+ require "logger"
3
+ require "./lib/activerecord-auto_filter"
4
+ require_relative "test/helpers/data_layer_setup"
5
+
6
+ include DataLayerSetup
7
+
8
+ QUERY_SPEC = {
9
+ :self =>
10
+ {
11
+ :product_id =>
12
+ {
13
+ :filter_operator => :eq, :column => :product_id
14
+ }
15
+ },
16
+ :order_items =>
17
+ {
18
+ :state =>
19
+ {
20
+ :filter_operator => :eq, :column => :state
21
+ }
22
+ },
23
+ :product =>
24
+ {
25
+ :vertical =>
26
+ {
27
+ :filter_operator => :eq, :column => :vertical
28
+ },
29
+ :price_from =>
30
+ {
31
+ :filter_operator => :gteq, :column => :selling_price
32
+ },
33
+ :price_to =>
34
+ {
35
+ :filter_operator => :lt, :column => :selling_price
36
+ }
37
+ },
38
+ :order_item_units =>
39
+ {
40
+ :size =>
41
+ {
42
+ :filter_operator => :eq, :column => :size
43
+ }
44
+ }
45
+ }
46
+
47
+ def demo
48
+ pretty_print("DB setups started")
49
+ setup_datalayer(true)
50
+ pretty_print("DB setups complete")
51
+ run_demo_with_joins
52
+ run_demo_with_no_joins
53
+ end
54
+
55
+ def run_demo_with_joins
56
+ pretty_print("Demo with joins")
57
+ params = {:vertical => :book, :price_from => 100, :price_to => 600, :size => 100, :state => :approved}
58
+ generate_demo(params)
59
+ end
60
+
61
+ def run_demo_with_no_joins
62
+ pretty_print("Demo without joins")
63
+ params = {:product_id => :f1}
64
+ generate_demo(params)
65
+ end
66
+
67
+ def generate_demo(params)
68
+ ar_with_query_builded = Order.apply_includes_and_where_clauses(params, QUERY_SPEC)
69
+ puts "Params for this demo are #{params}"
70
+ pretty_print("Final query generated is below")
71
+ ar_with_query_builded.inspect
72
+ puts "\n"
73
+ end
74
+
75
+ def pretty_print(string)
76
+ num_stars = (100-string.size)/2
77
+ puts "\n" + "*" * num_stars + " #{string} " + "*" * num_stars + "\n"
78
+ end
79
+
80
+
81
+ demo()
@@ -0,0 +1,134 @@
1
+ require 'active_record'
2
+ require "active_record/version"
3
+
4
+ class ActiveRecord::Base
5
+
6
+ class << self
7
+
8
+
9
+ ################################# Generic where clause and inclusion builder ###########################
10
+ # Query specification format
11
+ #
12
+ # Hash containing association specific query specification,
13
+ # each of which will map a param field to its param metadata
14
+ # needed to construct where condition. Param metadata involve
15
+ # information about the table, column the param maps to and
16
+ # the operator that need to applied in the query with the
17
+ # table column for the given param value. The operator passed
18
+ # is delegated to Arel::Table. So, all operators accepted
19
+ # by it are allowed (as mentioned in the example below)
20
+ #
21
+ # {
22
+ # :self =>
23
+ # {
24
+ # <param_field> =>
25
+ # {
26
+ # :column => <col_name_sym>
27
+ # :filter_operator => <:gt|:lt|:gteq|:lteq|:in|:not_in>,
28
+ # }
29
+ # }
30
+ # :association1 =>
31
+ # {
32
+ # <param_field> =>
33
+ # {
34
+ # :column => <col_name_sym>
35
+ # :filter_operator => <:gt|:lt|:gteq|:lteq|:in|:not_in>,
36
+ # },
37
+ # ...
38
+ # :join_filter =>
39
+ # {
40
+ # :table1_column => <column_name>, :table2_column => <column_name>,
41
+ # },
42
+ # :is_inclusion_mandatory => <true|false>
43
+ # },
44
+ # ...
45
+ # }
46
+
47
+
48
+ # Applies inclusion and where conditions/clauses for the model called upon.
49
+ # Where conditions are generated based on the presence of a param value.
50
+ def apply_includes_and_where_clauses(params, query_spec)
51
+ model = self
52
+ inclusions, where_clauses = get_inclusions_and_where_clauses(params, query_spec)
53
+
54
+ active_record = model.includes(inclusions)
55
+ where_clauses.each { |e| active_record = active_record.where(e) }
56
+ active_record
57
+ end
58
+
59
+ # Returns association inclusions and where conditions/clauses for the model called upon.
60
+ # Association inclusion is based on the presence of related where conditions.
61
+ # Where conditions are generated based on the presence of a param value.
62
+ def get_inclusions_and_where_clauses(params, query_spec)
63
+ result = emit_inclusion_and_filter_details(params, query_spec)
64
+ inclusions = result.keys - [:self]
65
+ where_clause_filters = result.values.flatten
66
+
67
+ [inclusions, where_clause_filters]
68
+ end
69
+
70
+ def emit_inclusion_and_filter_details(params, query_spec)
71
+ exclusions = [:join_filter, :is_inclusion_mandatory]
72
+ query_spec.each_with_object({}) do |(association, association_filter_spec), res|
73
+ join_spec, is_inclusion_mandatory = association_filter_spec.values_at(*exclusions)
74
+ new_association_filter_spec = association_filter_spec.reject { |e| exclusions.include?(e) }
75
+ association_model = get_association_model(association)
76
+ join_filter = get_association_join_filter(join_spec, self, association_model)
77
+
78
+ where_clause_filters =
79
+ new_association_filter_spec.each_with_object([]) do |(param_field, filter_spec), filter_res|
80
+ value = params[param_field]
81
+ if value.present?
82
+ filter_res << get_where_clause_filter(filter_spec, value, association_model)
83
+ end
84
+ end
85
+
86
+ if where_clause_filters.present?
87
+ res[association] = where_clause_filters
88
+ res[association] << join_filter if join_filter.present?
89
+ elsif is_inclusion_mandatory
90
+ res[association] = (join_filter.present?) ? [join_filter] : []
91
+ end
92
+ end
93
+ end
94
+
95
+ # Obtain ActiveRecord model from the association
96
+ def get_association_model(association)
97
+ (association == :self) ? self : self.reflect_on_association(association.to_sym).klass
98
+ end
99
+
100
+ private
101
+ def get_association_join_filter(join_filter, table1, table2)
102
+ return if join_filter.blank?
103
+ table1_col, table2_col = join_filter.values_at(:table1_column, :table2_column)
104
+ get_where_clause_sql(table2.arel_table[table2_col], table1, table1_col, :eq)
105
+ end
106
+
107
+ def get_where_clause_filter(filter_spec, value, table)
108
+ # Hash filters have good equality query abstractions.
109
+ # So choosing them over string filters for equality operator
110
+ if filter_spec[:filter_operator] == :eq
111
+ construct_hash_filter(value, filter_spec, table)
112
+ else
113
+ construct_str_filter(value, filter_spec, table)
114
+ end
115
+ end
116
+
117
+ def construct_str_filter(arg_value, filter_spec, table)
118
+ operator, column = filter_spec.values_at(:filter_operator, :column)
119
+ get_where_clause_sql(arg_value, table, column, operator)
120
+ end
121
+
122
+ def get_where_clause_sql(arg_value, table, column, operator)
123
+ table_arel = table.arel_table
124
+ sql = table_arel.where(table_arel[column].send(operator, arg_value)).to_sql
125
+ sql.split("WHERE").last
126
+ end
127
+
128
+ def construct_hash_filter(arg_value, filter_spec, table)
129
+ column = filter_spec[:column]
130
+ hash_filter = {column => arg_value}
131
+ (table != :self) ? {table.table_name.to_sym => hash_filter} : hash_filter
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,5 @@
1
+ module Activerecord
2
+ module AutoFilter
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,110 @@
1
+ require "rubygems"
2
+ require "shoulda"
3
+ require 'test/unit'
4
+ require 'active_record'
5
+ require_relative "helpers/data_layer_setup"
6
+ require_relative "../lib/activerecord-auto_filter"
7
+
8
+ class ActiveRecordAutoFilterTest < Test::Unit::TestCase
9
+ include DataLayerSetup # Arel has a dependency on db connection
10
+
11
+ def setup
12
+ setup_datalayer()
13
+ end
14
+
15
+ context "inclusions check" do
16
+ should "contain only expected associations for inclusions" do
17
+ params = {:f1 => :f1_val, :f2 => :f2_val, :f3 => 4, :f4 => 41}
18
+ assert_associations_are_included(params, :order_items, :product)
19
+
20
+ params = {:f1 => :f1_val, :f2 => :f2_val, :f3 => 4}
21
+ assert_associations_are_included(params, :order_items, :product)
22
+
23
+ params = {:f2 => :f2_val, :f3 => 4}
24
+ assert_associations_are_included(params, :order_items, :product)
25
+
26
+ params = {:f3 => 4}
27
+ assert_associations_are_included(params, :product)
28
+
29
+ params = {:f2 => :f2_val}
30
+ assert_associations_are_included(params, :order_items)
31
+ end
32
+ end
33
+
34
+ context "where clause filter structure check" do
35
+ should "contain appropriate kind filters - string or hash" do
36
+ table1_name = Order.table_name.to_sym
37
+ table2_name = OrderItem.table_name.to_sym
38
+ table3_name = Product.table_name.to_sym
39
+ params = {:f1 => :f1_val, :f2 => :f2_val, :f3 => 4}
40
+ query_details = Order.emit_inclusion_and_filter_details(params, get_sample_query_spec)
41
+
42
+ # Check hash filter exists and its structure
43
+ association_containing_hash_filter = query_details.delete(:order_items)
44
+ assert_equal([{table2_name => {:c2 => :f2_val}}], association_containing_hash_filter)
45
+
46
+ # Check string filter exists and its structure
47
+ assert(query_details.all? do |association,filter_details|
48
+ string_where_clause = filter_details.first
49
+ str_filter_format_regexps = [/#{table1_name}.*c1.*f1_val/, /#{table3_name}.*c3.*4/]
50
+ str_filter_format_regexps.any?{|r| string_where_clause.match(r)}
51
+ end)
52
+ end
53
+
54
+ should "contain multiple kind of filters for a given association" do
55
+ params = {:f1 => :f1_val, :f2 => :f2_val, :f3 => 4, :f4 => 5}
56
+ query_spec = get_sample_query_spec
57
+ query_spec[:order_items][:f4] = { :filter_operator => :eq, :column => :c4 }
58
+
59
+ result = Order.emit_inclusion_and_filter_details(params, query_spec)
60
+ assert_equal(2,result[:order_items].size)
61
+ end
62
+
63
+ should "contain join filters" do
64
+ table1_name = Order.table_name
65
+ table2_name = OrderItem.table_name
66
+ query_spec = get_sample_query_spec
67
+ params = {:f2 => :f2_val, :f3 => 4}
68
+
69
+ query_spec[:order_items][:join_filter] = { :table1_column => :col1, :table2_column => :col2 }
70
+ result = Order.emit_inclusion_and_filter_details(params, query_spec)
71
+
72
+ assert_equal(2,result[:order_items].size)
73
+ assert(result[:order_items].any? do |filter|
74
+ filter.class == String and filter.match(/#{table1_name}.*col1.*#{table2_name}.*col2.*/)
75
+ end)
76
+ end
77
+ end
78
+
79
+ def assert_associations_are_included(params, *expected_inclusions)
80
+ query_spec = get_sample_query_spec()
81
+ associations_actually_included, wh_clauses = Order.get_inclusions_and_where_clauses(params, query_spec)
82
+ assert_equal(expected_inclusions, associations_actually_included)
83
+ end
84
+
85
+ def get_sample_query_spec
86
+ {
87
+ :self =>
88
+ {
89
+ :f1 =>
90
+ {
91
+ :filter_operator => :lt, :column => :c1
92
+ }
93
+ },
94
+ :order_items =>
95
+ {
96
+ :f2 =>
97
+ {
98
+ :filter_operator => :eq, :column => :c2
99
+ }
100
+ },
101
+ :product =>
102
+ {
103
+ :f3 =>
104
+ {
105
+ :filter_operator => :gteq, :column => :c3
106
+ }
107
+ }
108
+ }
109
+ end
110
+ end
@@ -0,0 +1,13 @@
1
+ require_relative "./sample_model_definitions"
2
+ require_relative "./db_setup_helper"
3
+
4
+
5
+ module DataLayerSetup
6
+ include SampleModelDefinitions
7
+ include DbSetupHelper
8
+
9
+ def setup_datalayer(stdout_logging=false)
10
+ do_db_setups(stdout_logging)
11
+ create_tables()
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module DbSetupHelper
2
+ include SampleModelDefinitions
3
+
4
+ DATABASE = 'experiments'
5
+ CONNECTION_SPEC = {adapter: 'mysql2', username: 'root', password: '', host: 'localhost'}
6
+
7
+ def do_db_setups(stdout_logging=false)
8
+ establish_connection_with_no_db(CONNECTION_SPEC)
9
+ create_database(DATABASE)
10
+ establish_connection_with_db(DATABASE, CONNECTION_SPEC)
11
+ ActiveRecord::Base.logger = Logger.new(STDOUT) if stdout_logging
12
+ end
13
+
14
+ def create_database(database)
15
+ ActiveRecord::Base.connection.create_database(database) rescue nil
16
+ end
17
+
18
+ def establish_connection_with_no_db(connection_spec)
19
+ ActiveRecord::Base.establish_connection(connection_spec)
20
+ end
21
+
22
+ def establish_connection_with_db(database, connection_spec)
23
+ ActiveRecord::Base.establish_connection(connection_spec.merge(database: database))
24
+ end
25
+ end
@@ -0,0 +1,46 @@
1
+ module SampleModelDefinitions
2
+ class Order < ActiveRecord::Base
3
+ has_one :product
4
+ has_many :order_items
5
+ has_many :order_item_units, :through => :order_items
6
+ end
7
+
8
+ class OrderItem < ActiveRecord::Base
9
+ has_many :order_item_units
10
+ end
11
+
12
+ class OrderItemUnit < ActiveRecord::Base
13
+ end
14
+
15
+ class Product < ActiveRecord::Base
16
+ end
17
+
18
+
19
+ def create_tables
20
+ ActiveRecord::Schema.define(:version => 20140124132738) do
21
+ create_table :orders, :force => true do |t|
22
+ t.text :user
23
+ t.string :product_id
24
+ t.integer :quantity, :default => true
25
+ end
26
+
27
+ create_table :order_items, :force => true do |t|
28
+ t.string :state
29
+ t.integer :quantity
30
+ t.references :order
31
+ end
32
+
33
+ create_table :order_item_units, :force => true do |t|
34
+ t.integer :size, :default => true
35
+ t.references :order_item
36
+ end
37
+
38
+ create_table :products, :force => true do |t|
39
+ t.string :vertical
40
+ t.integer :selling_price
41
+ t.references :order
42
+ end
43
+ end
44
+ end
45
+
46
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-auto_filter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - kaushik
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-26 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.1.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.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Configuration based condition building and inclusion handling extension
42
+ for ActiveRecord::Base
43
+ email:
44
+ - kaushikd49@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - .gitignore
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - activerecord-auto_filter.gemspec
55
+ - demo.rb
56
+ - lib/activerecord-auto_filter.rb
57
+ - lib/activerecord-auto_filter/version.rb
58
+ - test/active_record_auto_filter_test.rb
59
+ - test/helpers/data_layer_setup.rb
60
+ - test/helpers/db_setup_helper.rb
61
+ - test/helpers/sample_model_definitions.rb
62
+ homepage: ''
63
+ licenses: []
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - '>='
72
+ - !ruby/object:Gem::Version
73
+ version: 1.9.3
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.1.11
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Inclusions and where-condition building extension for ActiveRecord
85
+ test_files:
86
+ - test/active_record_auto_filter_test.rb
87
+ - test/helpers/data_layer_setup.rb
88
+ - test/helpers/db_setup_helper.rb
89
+ - test/helpers/sample_model_definitions.rb