collate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 46caae4fc83f2556a1c2835b951dfb89dd57d557
4
+ data.tar.gz: 99278ff861ac8cfb50ba7cdcb1b6ca2940c311ea
5
+ SHA512:
6
+ metadata.gz: 070f0611c0adea44f057b62f37d49d93fff18c04ef5681c7638b0491c178908dac17200f5b5966e9f1c10f1971291b7c59b37e4958d35acaa52e42d127531554
7
+ data.tar.gz: 5db48b737d9c51c0be11283d74e5e7108f3e1546012c54d8dc969e00e6ff899e60510f3f21e305a8c6ef7d0178d2211b7798c002856fc2f04683f10f3ecf0f5c
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /log/
11
+ /coverage/
12
+ /*.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Nicholas Page
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,267 @@
1
+ # Collate
2
+
3
+ [![CircleCI](https://img.shields.io/circleci/project/github/trackingboard/collate.svg)](https://circleci.com/gh/trackingboard/collate)
4
+ [![Coveralls](https://img.shields.io/coveralls/trackingboard/collate.svg)](https://coveralls.io/github/trackingboard/collate?branch=master)
5
+ [![Gem](https://img.shields.io/gem/v/collate.svg)]()
6
+ [![Gem](https://img.shields.io/gem/dt/collate.svg)]()
7
+
8
+ ## Installation
9
+
10
+ ```gem install collate```
11
+
12
+ or with bundler in your Gemfile:
13
+
14
+ ```gem 'collate'```
15
+
16
+ ## Usage
17
+
18
+ This gem currently only supports PostgreSQL.
19
+
20
+ To use collate in a model, include several collation definitions. The first argument is the name of the database column to use in the query. The simplest example looks like this:
21
+
22
+ ```
23
+ collate_on :name
24
+ ```
25
+
26
+ This will add a filter to the model that will grab all records where the ```name``` column equals the parameter that is passed in.
27
+
28
+ ### Operators
29
+
30
+ You can currently collate using multiple types of operators. To specify an operator to collate on, you can pass in the keyword argument ```operator```, like this:
31
+
32
+ ```
33
+ collate_on :name, operator: :ilike
34
+ ```
35
+
36
+ Translates to:
37
+
38
+ ```
39
+ WHERE name ILIKE ?
40
+ ```
41
+
42
+ Here are the currently available operators:
43
+
44
+ | Operator | Behavior |
45
+ |:------------------|:--------------------|
46
+ | ```:eq``` | ```field = ?``` |
47
+ | ```:ilike``` | ```field ILIKE ?``` |
48
+ | ```:in``` | ```field IN (?)``` |
49
+ | ```:le``` | ```field <= ?``` |
50
+ | ```:ge``` | ```field >= ?``` |
51
+ | ```:null``` | ```field IS NULL``` |
52
+ | ```:contains``` | ```field @> ?``` |
53
+ | ```:present?``` | ```field = ?``` |
54
+ | ```:&``` | ```field & ?``` |
55
+
56
+ ### Field Transformations
57
+
58
+ Field transformations are database functions applied to a field before the operator is used to compare it with the value. Field transformations are passed in as an array of tuples, where the first element in the tuple is the symbol for the transfomation, and the second element is the first argument to the database function.
59
+
60
+ For example:
61
+
62
+ ```
63
+ collate_on :name, field_transformations: [[:split, ' ']]
64
+ ```
65
+
66
+ This would translate to this PostgreSQL query:
67
+
68
+ ```
69
+ WHERE string_to_array(name, ' ') = ?
70
+ ```
71
+
72
+ Here are the available field transformations:
73
+
74
+ | Transformation | Behavior |
75
+ |:-------------------------|:--------------------|
76
+ | ```:date_difference``` | ```date_difference(arg1, field)``` |
77
+ | ```:date_part``` | ```date_part(arg1, field)``` |
78
+ | ```:array_agg``` | ```array_agg(field)``` |
79
+ | ```:downcase``` | ```lower(field)``` |
80
+ | ```:split``` | ```string_to_array(field, arg1)``` |
81
+ | ```:array_length``` | ```array_length(field, arg1)``` |
82
+
83
+ These transformations can also be chained together on the same filter. They are applied in the order they appear in the array that is passed in.
84
+
85
+ For example:
86
+
87
+ ```
88
+ collate_on :name, field_transformations: [[:split, ' '], [:array_length, 1]]
89
+ ```
90
+
91
+ Translates to this PostgreSQL query:
92
+
93
+ ```
94
+ WHERE array_length(string_to_array(name, ' '), 1) = ?
95
+ ```
96
+
97
+ ### Value Transformations
98
+
99
+ Value transformations are functions applied to the user-supplied value before it is passed to the database query. They are passed in the same way as the field transmorations, as an array of tuples.
100
+
101
+ For example:
102
+
103
+ ```
104
+ collate_on :name, value_transformations: [[:join, ', ']]
105
+ ```
106
+
107
+ Translates to the following code:
108
+
109
+ ```
110
+ value = value.join(', ')
111
+ ar_rel = ar_rel.where("name = ?", value)
112
+ ```
113
+
114
+ Here are the available value transformations:
115
+
116
+ | Transformation | Behavior |
117
+ |:-------------------------|:--------------------|
118
+ | ```:join``` | ```value = value.join(arg1)``` |
119
+ | ```:downcase``` | ```value = value.downcase``` |
120
+ | ```:string_part``` | ```value = "%#{value}%"``` |
121
+
122
+ ### Additional Arguments
123
+
124
+ There are many other additional arguments you can initialize a filter with. Here is a list of all of them:
125
+
126
+ #### Label
127
+ --------------
128
+ ```
129
+ collate_on :name, label: 'Character Name'
130
+ ```
131
+
132
+ This argument will overwrite the default label for the filter, which is ```field.to_s.titleize```
133
+
134
+ #### Not
135
+ --------------
136
+ ```
137
+ collate_on :name, not: true
138
+ ```
139
+
140
+ This argument causes the entire query to be surrounded by a NOT(). The above, for example, translates to this PostgreSQL query:
141
+
142
+ ```
143
+ WHERE NOT(name = ?)
144
+ ```
145
+
146
+ #### Having
147
+ --------------
148
+ ```
149
+ collate_on :name, having: true
150
+ ```
151
+
152
+ This argument tells the gem to use ```having``` instead of ```where``` in the ActiveRecord query. The above example then becomes:
153
+
154
+ ```
155
+ HAVING name = ?
156
+ ```
157
+
158
+ #### Joins
159
+ --------------
160
+ ```
161
+ collate_on 'genres.id', operator: :in, joins: [:genres, :movies => [:people]]
162
+ ```
163
+
164
+ This argument tells the gem to use the ActiveRecord ```joins``` method with the value passed in. You can pass in an array of values, and it will evaluate each one in succession. The above code would then run this before any query is evaluated:
165
+
166
+ ```
167
+ ar_rel = ar_rel.joins(:genres)
168
+ ar_rel = ar_rel.joins(:movies => [:people])
169
+ ```
170
+
171
+ #### Joins_prefix
172
+ --------------
173
+ ```
174
+ collate_on 'select_genres.id', operator: :in, joins: [:genres], joins_prefix: 'select_'
175
+ ```
176
+
177
+ This argument will tell the gem to join in the relations specified in the ```joins``` argument, but to prefix all table names with the prefix specified. The above code would then translate to the following PostgreSQL query:
178
+
179
+ ```
180
+ INNER JOIN genres AS select_genres ON ...
181
+ ```
182
+
183
+ #### Component
184
+ --------------
185
+ ```
186
+ collate_on 'genres.id', operator: :in, component: {load_records: true}
187
+ ```
188
+
189
+ This argument is used for rendering in the views. Currently the gem does not have helper methods for the views, but when those are added, that is how you would set the various options.
190
+
191
+ ### The View
192
+
193
+ To know the ```params``` key to use for each filter, you need to look at the filter's ```param_key``` method value. The filters are organized in a hierarchal scheme in the class variable ```collate_filters``` for the model you included the DSL on.
194
+
195
+ Here is a small example of a few filters:
196
+
197
+ ```
198
+ class Person < ActiveRecord::Base
199
+ collate_on :name, operator: :ilike
200
+ collate_on :birthday, operator: :le, label: 'Birthday Before'
201
+ end
202
+ ```
203
+
204
+ And here is how ```Person.collate_filters``` would look like:
205
+
206
+ ```
207
+ {
208
+ :main=> {
209
+ :label=>"Main",
210
+ :filters=> [
211
+ #<Collate::Filter
212
+ @base_model_table_name="people",
213
+ @component={:type=>"string"},
214
+ @field="people.name",
215
+ @field_transformations={},
216
+ @grouping=nil,
217
+ @html_id="1people_name",
218
+ @joins=nil,
219
+ @label="Name",
220
+ @operator=:ilike,
221
+ @value_transformations={}>
222
+ ,
223
+ #<Collate::Filter
224
+ @base_model_table_name="people",
225
+ @component={:type=>"string"},
226
+ @field="people.birthday",
227
+ @field_transformations={},
228
+ @grouping=nil,
229
+ @html_id="1people_name",
230
+ @joins=nil,
231
+ @label="Birthday Before",
232
+ @operator=:le,
233
+ @value_transformations={}>
234
+ ]
235
+ }
236
+ }
237
+ ```
238
+
239
+ In order to use this in a view, you could have some HAML like this:
240
+
241
+ ```
242
+ = form_tag '', :method => :get do
243
+ - Person.collate_filters.each do |group_key, group|
244
+ - filters = group[:filters]
245
+ - filters.each do |filter|
246
+ - case filter.component[:type]
247
+ - when "string"
248
+ = filter.label
249
+ %br
250
+ = text_field_tag filter.param_key, params[filter.param_key], id: "#{filter.html_id}", style:'width:100%'
251
+
252
+ ```
253
+
254
+ This will ensure that the keys that the inputs are submitted with match the parameter key that the gem is expecting for that specific filter.
255
+
256
+ ## Contributing
257
+
258
+ Bug reports and pull requests are welcome on GitHub at https://github.com/trackingboard/collate.
259
+
260
+ 1. Fork.
261
+ 2. Branch.
262
+ 3. Pull Request your feature branch or fix.
263
+ 4. 🍕
264
+
265
+ ## License
266
+
267
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
data/circle.yml ADDED
@@ -0,0 +1,25 @@
1
+ dependencies:
2
+ override:
3
+ - rvm --default use ruby-2.3.3
4
+ - gem install bundler
5
+ - bundle
6
+ cache_directories:
7
+ - "/opt/circleci/.rvm/gems/ruby-2.3.3"
8
+
9
+ database:
10
+ override:
11
+ - psql -c 'create database collate_test;' -U postgres
12
+
13
+ test:
14
+ override:
15
+ - RAILS_ENV=test bundle exec rake test
16
+
17
+ deployment:
18
+ release:
19
+ tag: /^([0-9]+\.{0,1}){1,3}(\-([a-z0-9]+\.{0,1})+){0,1}(\+(build\.{0,1}){0,1}([a-z0-9]+\.{0,1}){0,}){0,1}$/
20
+ commands:
21
+ - gem build collate.gemspec
22
+ - chmod +x deploy.sh
23
+ - sh deploy.sh
24
+ - chmod 0600 ~/.gem/credentials
25
+ - gem push collate-${CIRCLE_TAG}.gem
data/collate.gemspec ADDED
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'collate/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "collate"
8
+ spec.version = Collate::VERSION
9
+ spec.authors = ["Nicholas Page", "Colleen McGuckin"]
10
+ spec.email = ["npage85@gmail.com", "colleenmcguckin@gmail.com"]
11
+
12
+ spec.summary = %q{Facilitates the filtering of ActiveRecord models using simplified DSL.}
13
+ spec.description = %q{Add some DSL to your model, then run a single method in your controller, and your model now accepts filtering through the parameters.}
14
+ spec.homepage = "https://github.com/trackingboard/collate"
15
+ spec.license = "MIT"
16
+
17
+ if spec.respond_to?(:metadata)
18
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
19
+ else
20
+ raise "RubyGems 2.0 or newer is required to protect against " \
21
+ "public gem pushes."
22
+ end
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
25
+ f.match(%r{^(test|spec|features|app|coverage|config|log)/})
26
+ end
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_development_dependency "bundler", "~> 1.14"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "rails", "~> 4.2", ">= 4.2.6"
34
+ spec.add_development_dependency "pg", "~> 0.18.4"
35
+ spec.add_development_dependency "minitest", "~> 5.0"
36
+ spec.add_development_dependency "pry-rescue", "~> 1.4", ">= 1.4.2"
37
+ spec.add_development_dependency "simplecov", "~> 0.12.0"
38
+ spec.add_development_dependency "coveralls", "~> 0.8.15"
39
+ spec.add_development_dependency "haml", "~> 4.0", ">= 4.0.7"
40
+ end
data/deploy.sh ADDED
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+
3
+ cat <<EOF > ~/.gem/credentials
4
+ ---
5
+ :rubygems_api_key: $RUBYGEMS_API_KEY
6
+ EOF
data/lib/collate.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "collate/version"
2
+ require "collate/engine"
3
+
4
+ module Collate
5
+
6
+ end
@@ -0,0 +1,175 @@
1
+ require_relative 'filter'
2
+
3
+ module Collate
4
+ module ActiveRecordExtension
5
+
6
+ def collate_on field, opts={}
7
+ initialize_collate
8
+
9
+ self.collate_filters[self.default_group] ||= {filters: []}.merge(self.group_options)
10
+
11
+ self.collate_filters[self.default_group][:filters] << Collate::Filter.new(field, opts.merge({base_model_table_name: self.table_name}))
12
+ end
13
+
14
+ def collate_group name, **opts, &blk
15
+ initialize_collate
16
+
17
+ opts[:label] ||= name.to_s.titleize
18
+ self.group_options = opts
19
+ self.default_group = name
20
+ blk.call
21
+ end
22
+
23
+ def collate params
24
+ ar_rel = self.all
25
+
26
+ self.collate_filters.each do |group_key, group|
27
+ group[:filters].each do |filter|
28
+ if params[filter.param_key].present? || params["#{filter.param_key}[]"].present?
29
+ ar_rel = apply_filter(ar_rel, filter, params[filter.param_key] || params["#{filter.param_key}[]"])
30
+ end
31
+ end
32
+ end
33
+
34
+ ar_rel
35
+ end
36
+
37
+ private
38
+
39
+ def initialize_collate
40
+ if !self.method_defined? :collate_filters
41
+ class << self
42
+ attr_accessor :collate_filters, :default_group, :group_options
43
+ end
44
+ end
45
+ self.collate_filters ||= {}
46
+ self.default_group ||= :main
47
+ self.group_options ||= {}
48
+ end
49
+
50
+ def apply_filter ar_rel, filter, filter_value
51
+ if filter.joins
52
+ filter.joins.each do |join|
53
+ ar_rel = if filter.joins_prefix
54
+ prefix_index = 0
55
+ original_query = ar_rel.model.unscoped.joins(join).to_sql
56
+
57
+ previous_replacements = {}
58
+ new_query = original_query.split('INNER JOIN').drop(1).map do |chunk|
59
+ table_name = /([\"'])(?:\\\1|.)*?\1/.match(chunk)[0].gsub('"','')
60
+
61
+ if previous_replacements.has_key?("\"#{table_name}\"")
62
+ previous_replacements.delete("\"#{table_name}\"")
63
+ prefix_index += 1
64
+
65
+ check_alias_match = /(?<="#{table_name}" ")[^"]*(?=")/.match(chunk)
66
+ if check_alias_match
67
+ chunk = chunk.partition(check_alias_match[0]).drop(1).join('').prepend('"').gsub(check_alias_match[0], "#{table_name}")
68
+ end
69
+ end
70
+
71
+ previous_replacements.each do |the_match, replacement|
72
+ chunk = chunk.gsub(the_match, replacement)
73
+ end
74
+
75
+ replaced = chunk.gsub("\"#{table_name}\"", "\"#{filter.joins_prefix[prefix_index]}#{table_name}\"")
76
+
77
+ previous_replacements["\"#{table_name}\""] = "\"#{filter.joins_prefix[prefix_index]}#{table_name}\""
78
+
79
+ "\"#{table_name}\" AS #{replaced}"
80
+ end.join(' INNER JOIN ').prepend('INNER JOIN ')
81
+
82
+ ar_rel.joins(new_query)
83
+ else
84
+ ar_rel.joins(join)
85
+ end
86
+ end
87
+ end
88
+
89
+ ar_rel = ar_rel.group(filter.grouping) if filter.grouping
90
+
91
+ field_query = filter.field
92
+
93
+ filter.field_transformations.each do |ft|
94
+ transformation = ft
95
+ transformation = ft[0] if !transformation.is_a? Symbol
96
+ field_query = case transformation
97
+ when :date_difference
98
+ "age(#{ft[1]}, #{field_query})"
99
+ when :date_part
100
+ "date_part('#{ft[1]}', #{field_query})"
101
+ when :array_agg
102
+ "array_agg(#{field_query})"
103
+ when :downcase
104
+ "lower(#{field_query})"
105
+ when :split
106
+ "string_to_array(#{field_query}, '#{ft[1]}')"
107
+ when :array_length
108
+ "array_length(#{field_query}, '#{ft[1]}')"
109
+ else
110
+ field_query
111
+ end
112
+ end
113
+
114
+ if filter.component[:load_records]
115
+ results = filter.component[:load_record_model].constantize.where("#{filter.component[:load_record_field]} IN (?)", filter_value)
116
+
117
+ filter.component[:values] = results.map{ |r| {id: r.id, text: r.name} }
118
+ end
119
+
120
+ if filter.component[:tags]
121
+ filter.component[:values] = filter_value.map { |v| {id: v, text: v} }
122
+ end
123
+
124
+ filter.value_transformations.each do |vt|
125
+ transformation = vt
126
+ transformation = vt[0] if !transformation.is_a? Symbol
127
+
128
+ filter_value = case transformation
129
+ when :join
130
+ "{#{filter_value.join(', ')}}"
131
+ when :downcase
132
+ filter_value.downcase
133
+ when :string_part
134
+ "%#{filter_value}%"
135
+ else
136
+ filter_value
137
+ end
138
+ end
139
+
140
+ ar_method = filter.having ? "having" : "where"
141
+
142
+ query_string = case filter.operator
143
+ when :eq
144
+ "#{field_query} = ?"
145
+ when :ilike
146
+ "#{field_query} ILIKE ?"
147
+ when :in
148
+ "#{field_query} IN (?)"
149
+ when :le
150
+ "#{field_query} <= ?"
151
+ when :ge
152
+ "#{field_query} >= ?"
153
+ when :null
154
+ "#{field_query} IS NULL"
155
+ when :contains
156
+ "#{field_query} @> ?"
157
+ when :present?
158
+ "#{field_query} = true"
159
+ when :&
160
+ "#{field_query} && ?"
161
+ else
162
+ ""
163
+ end
164
+
165
+ query_string = "NOT(#{query_string})" if filter.not
166
+
167
+ ar_rel = if query_string.include?('?')
168
+ ar_rel.send(ar_method, query_string, filter_value)
169
+ else
170
+ ar_rel.send(ar_method, query_string)
171
+ end
172
+ end
173
+
174
+ end
175
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'active_record_extension'
2
+ require 'rails'
3
+
4
+ module Collate
5
+ class Engine < ::Rails::Engine
6
+
7
+ isolate_namespace Collate
8
+
9
+ ActiveSupport.on_load :active_record do
10
+ extend Collate::ActiveRecordExtension
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,118 @@
1
+ module Collate
2
+ class Filter
3
+ OPERATORS = [:eq, :ilike, :in, :le, :ge, :null, :contains, :present?, :&]
4
+
5
+ FIELD_TRANSFORMATIONS = [:date_difference, :date_part, :array_agg, :downcase, :split, :array_length]
6
+ AGGREGATE_TRANSFORMATIONS = [:array_agg]
7
+ VALUE_TRANSFORMATIONS = [:join, :downcase, :string_part]
8
+
9
+ attr_accessor :field, :operator, :base_model_table_name, :field_transformations, :label,
10
+ :component, :joins, :value_transformations, :grouping, :html_id, :having,
11
+ :joins_prefix, :not
12
+
13
+ def initialize(field, opt={})
14
+ opt.each do |field, value|
15
+ self.send("#{field}=", value)
16
+ end
17
+
18
+ self.component ||= {}
19
+
20
+ self.field = field
21
+ self.label ||= field.to_s.titleize
22
+ self.operator ||= if field.to_s.last(3) == '.id' || field.to_s.last(3) == '_id'
23
+ :in
24
+ elsif self.component[:type] == 'checkboxgroup'
25
+ :in
26
+ else
27
+ :eq
28
+ end
29
+ self.field = "#{base_model_table_name}.#{field}" if field.is_a? Symbol
30
+ self.field_transformations ||= {}
31
+ self.value_transformations ||= {}
32
+
33
+ self.html_id ||= param_key.gsub('{','').gsub('}','').gsub('.','_')
34
+
35
+ field_parts = self.field.to_s.partition('.')
36
+ table_name = field_parts[0]
37
+ field_selector = field_parts[2]
38
+
39
+ self.component = if self.operator == :in
40
+ self.component.reverse_merge({type: 'select', multiple: true, values: []})
41
+ elsif self.operator == :null || self.operator == :present?
42
+ self.component.reverse_merge({type: 'checkbox'})
43
+ elsif self.component[:tags]
44
+ self.component.reverse_merge({type: 'select', multiple: true})
45
+ else
46
+ self.component.reverse_merge({type: 'string'})
47
+ end
48
+
49
+ if self.component[:load_records]
50
+ model_name = if field_selector.last(3) == '_id'
51
+ field_selector.chomp(field_selector.last(3))
52
+ elsif self.field.to_s.last(3) == '.id'
53
+ table_name.singularize
54
+ else
55
+ table_name.singularize
56
+ end
57
+
58
+ self.component[:load_record_model] ||= model_name.titleize
59
+ self.component[:load_record_field] ||= "id"
60
+ self.component[:load_record_route] ||= "/#{model_name.pluralize}.json"
61
+ end
62
+
63
+ self.joins ||= if table_name != base_model_table_name
64
+ join_name = table_name.to_sym
65
+ base_model_table_name.singularize.titleize.constantize.reflect_on_all_associations.each do |assoc|
66
+
67
+ join_name = assoc.name if assoc.plural_name == table_name
68
+ end
69
+
70
+ [join_name.to_sym]
71
+ end
72
+ self.joins_prefix = [self.joins_prefix] if self.joins_prefix.is_a? String
73
+
74
+ if !opt.has_key?(:having) && (field_transformations.to_a.flatten & AGGREGATE_TRANSFORMATIONS).any?
75
+ self.having = true
76
+ end
77
+
78
+ if self.value_transformations.empty? && self.operator == :ilike
79
+ self.value_transformations = [:string_part]
80
+ end
81
+
82
+ self.grouping ||= if self.having
83
+ "#{base_model_table_name}.id"
84
+ end
85
+
86
+ if self.component[:values]
87
+ self.component[:values] = if self.component[:values].is_a?(Array) && self.component[:values].all? { |item| item.is_a? String }
88
+ self.component[:values].map { |s| {id: s, text: s.titleize} }
89
+ elsif self.component[:values].is_a?(Array) && self.component[:values].all? { |item| item.is_a? Symbol }
90
+ self.component[:values].map { |s| {id: s, text: s.to_s.titleize} }
91
+ elsif self.component[:values].respond_to?(:<) && self.component[:values] < ActiveRecord::Base
92
+ self.component[:values].table_exists? ? self.component[:values].all.map { |m| {id: m.id, text: m.name} } : []
93
+ else
94
+ self.component[:values]
95
+ end
96
+ elsif component[:tags]
97
+ self.component[:values] = []
98
+ end
99
+ end
100
+
101
+ def param_key
102
+ key = ""
103
+ field_transformations.each do |ft|
104
+ transformation = ft
105
+ transformation = ft[0] if !transformation.is_a? Symbol
106
+ key += FIELD_TRANSFORMATIONS.index(transformation).to_s
107
+ end
108
+ key += OPERATORS.index(operator).to_s
109
+ value_transformations.each do |vt|
110
+ transformation = vt
111
+ transformation = vt[0] if !transformation.is_a? Symbol
112
+ key += VALUE_TRANSFORMATIONS.index(transformation).to_s
113
+ end
114
+
115
+ "{#{key}}#{field}"
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,3 @@
1
+ module Collate
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,205 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: collate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nicholas Page
8
+ - Colleen McGuckin
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2017-02-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.14'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.14'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rails
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '4.2'
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 4.2.6
52
+ type: :development
53
+ prerelease: false
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - "~>"
57
+ - !ruby/object:Gem::Version
58
+ version: '4.2'
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 4.2.6
62
+ - !ruby/object:Gem::Dependency
63
+ name: pg
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.18.4
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.18.4
76
+ - !ruby/object:Gem::Dependency
77
+ name: minitest
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.0'
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '5.0'
90
+ - !ruby/object:Gem::Dependency
91
+ name: pry-rescue
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.4'
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: 1.4.2
100
+ type: :development
101
+ prerelease: false
102
+ version_requirements: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: '1.4'
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 1.4.2
110
+ - !ruby/object:Gem::Dependency
111
+ name: simplecov
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: 0.12.0
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: 0.12.0
124
+ - !ruby/object:Gem::Dependency
125
+ name: coveralls
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: 0.8.15
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: 0.8.15
138
+ - !ruby/object:Gem::Dependency
139
+ name: haml
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '4.0'
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: 4.0.7
148
+ type: :development
149
+ prerelease: false
150
+ version_requirements: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - "~>"
153
+ - !ruby/object:Gem::Version
154
+ version: '4.0'
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: 4.0.7
158
+ description: Add some DSL to your model, then run a single method in your controller,
159
+ and your model now accepts filtering through the parameters.
160
+ email:
161
+ - npage85@gmail.com
162
+ - colleenmcguckin@gmail.com
163
+ executables: []
164
+ extensions: []
165
+ extra_rdoc_files: []
166
+ files:
167
+ - ".gitignore"
168
+ - Gemfile
169
+ - LICENSE.txt
170
+ - README.md
171
+ - Rakefile
172
+ - circle.yml
173
+ - collate.gemspec
174
+ - deploy.sh
175
+ - lib/collate.rb
176
+ - lib/collate/active_record_extension.rb
177
+ - lib/collate/engine.rb
178
+ - lib/collate/filter.rb
179
+ - lib/collate/version.rb
180
+ homepage: https://github.com/trackingboard/collate
181
+ licenses:
182
+ - MIT
183
+ metadata:
184
+ allowed_push_host: https://rubygems.org
185
+ post_install_message:
186
+ rdoc_options: []
187
+ require_paths:
188
+ - lib
189
+ required_ruby_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ required_rubygems_version: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ version: '0'
199
+ requirements: []
200
+ rubyforge_project:
201
+ rubygems_version: 2.5.2
202
+ signing_key:
203
+ specification_version: 4
204
+ summary: Facilitates the filtering of ActiveRecord models using simplified DSL.
205
+ test_files: []