activerecord-filter 5.0.0.beta1 → 5.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0ad1d6c4fde489d32e18e7a09fded1dc6f885733
4
- data.tar.gz: 90ad1edbedee643cb629ceb2d6a65181f535fc7e
3
+ metadata.gz: 70a1e7037c74baea87037f1a7e6bf7dc7a581754
4
+ data.tar.gz: 09391e5064f066076c345556c521e08016deebf2
5
5
  SHA512:
6
- metadata.gz: f5bda8ea3d34ad175787b16981fc9d9cd6048aa885e97b09e29714a1b3f1b20fe3d0786333e82f50eaeb9db901f48c13e8ea2ef4b0a16ea8cd5c83ba8fdaf5a7
7
- data.tar.gz: f36d565425a76172ab801cc3c84540daebaf579241c92e84bb8154deb6a3e6dcc192c68d369f4d8a9e059935097c6ece203ea009a78a12f140a72cfb5dd5a735
6
+ metadata.gz: 0e84af9fa5d6fbb5ed2e16cfa473af1f117d67c9d108c7200cd0a6331773bb6e9e331fc7079f257958c148678ef0034e3353c08acebca85f8a0514cb38453703
7
+ data.tar.gz: d66507821b1dc004c496f34c5565899377869725474cba87a83d5481e995734811e77f03e672dad3501ebab7b9d2877e8d491b310f8d85d2cadf0a699722b9f5
@@ -1,9 +1,323 @@
1
1
  require 'active_record'
2
+ require 'arel/extensions'
2
3
 
3
- require File.expand_path(File.join(__FILE__, '../../../ext/arel/attributes/array'))
4
- require File.expand_path(File.join(__FILE__, '../../../ext/arel/nodes/contains'))
5
- require File.expand_path(File.join(__FILE__, '../../../ext/arel/nodes/overlaps'))
6
- require File.expand_path(File.join(__FILE__, '../../../ext/arel/visitors/postgresql'))
7
- require File.expand_path(File.join(__FILE__, '../../../ext/active_record/unkown_filter_error'))
8
- require File.expand_path(File.join(__FILE__, '../../../ext/active_record/base'))
9
- require File.expand_path(File.join(__FILE__, '../../../ext/active_record/query_methods'))
4
+ class ActiveRecord::UnkownFilterError < NoMethodError
5
+ attr_reader :klass, :filter
6
+
7
+ def initialize(klass, filter)
8
+ @klass = klass
9
+ @filter = filter.to_s
10
+ super("unkown filter #{filter.inspect} for #{klass}.")
11
+ end
12
+ end
13
+
14
+ module ActiveRecord::Filter
15
+
16
+ def inherited(subclass)
17
+ super
18
+ subclass.instance_variable_set('@filters', HashWithIndifferentAccess.new)
19
+ end
20
+
21
+ def filter_on(name, lambda)
22
+ @filters[name] = lambda
23
+ end
24
+
25
+ def filter(filters, options={})
26
+ resource = all
27
+ return resource unless filters
28
+
29
+ if filters.is_a?(Hash) || filters.class.name == "ActionController::Parameters".freeze
30
+ filters.each do |key, value|
31
+ if @filters[key]
32
+ #TODO add test for this... not sure how rails does this lambda call,
33
+ # do they run it in a context for merge?
34
+ resource = resource.merge( @filters[key].call(value) )
35
+ else
36
+ resource = resource.filter_for(key, value, options)
37
+ end
38
+ end
39
+ elsif filters.is_a?(Array) || filters.is_a?(Integer)
40
+ resource = resource.filter_for(:id, filters, options)
41
+ end
42
+
43
+ resource
44
+ end
45
+
46
+ def filter_for(key, value, options={})
47
+ column = columns_hash[key.to_s]
48
+ if column && column.array
49
+ all.filter_for_array(key, value, options)
50
+ elsif column
51
+ all.send("filter_for_#{column.type}", key, value, options)
52
+ else
53
+ if relation = reflect_on_association(key)
54
+ self.send("filter_for_#{relation.macro}", relation, value)
55
+ else
56
+ # Custome filter, try to guess based on value
57
+ # raise ActiveRecord::UnkownFilterError.new(self, key)
58
+ if value.is_a?(Hash) || value.class.name == "ActionController::Parameters".freeze
59
+ all.send("filter_for_#{value.values.first.class.to_s.downcase}", key, value, options)
60
+ else
61
+ all.send("filter_for_#{value.class.to_s.downcase}", key, value, options)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ {
68
+ filter_for_geometry: :itself,
69
+ filter_for_datetime: :to_datetime,
70
+ filter_for_integer: :to_i,
71
+ filter_for_text: :itself,
72
+ filter_for_boolean: :itself,
73
+ filter_for_string: :itself,
74
+ filter_for_uuid: :itself,
75
+ filter_for_decimal: :to_f,
76
+ filter_for_float: :to_f
77
+ }.each_pair do |method_name, send_method|
78
+ define_method(method_name) do |column, value, options={}|
79
+ table = options[:table_alias] ? arel_table.alias(options[:table_alias]) : arel_table
80
+
81
+ if value.is_a?(Hash) || value.class.name == "ActionController::Parameters".freeze
82
+ resource = all
83
+ value.each_pair do |key, value|
84
+ converted_value = if value.is_a?(Array)
85
+ value.map { |x| x.try(:send, send_method) }
86
+ else
87
+ value.try(:send, send_method)
88
+ end
89
+
90
+ resource = case key.to_sym
91
+ when :equal_to, :eq
92
+ resource.where(table[column].eq(converted_value))
93
+ when :greater_than, :gt
94
+ resource.where(table[column].gt(converted_value))
95
+ when :less_than, :lt
96
+ resource.where(table[column].lt(converted_value))
97
+ when :greater_than_or_equal_to, :gteq, :gte
98
+ resource.where(table[column].gteq(converted_value))
99
+ when :less_than_or_equal_to, :lteq, :lte
100
+ resource.where(table[column].lteq(converted_value))
101
+ when :in
102
+ resource.where(table[column].in(converted_value))
103
+ when :not
104
+ resource.where(table[column].not_eq(converted_value))
105
+ when :not_in
106
+ resource.where(table[column].not_in(converted_value).or(table[column].eq(nil)))
107
+ when :like, :ilike
108
+ resource.where(table[column].matches(converted_value))
109
+ when :ts_match
110
+ if converted_value.is_a?(Array)
111
+ resource.where(table[column].ts_query(*converted_value))
112
+ else
113
+ resource.where(table[column].ts_query(converted_value))
114
+ end
115
+ when :intersects
116
+ # geometry_value = if value.is_a?(Hash) # GeoJSON
117
+ # Arel::Nodes::NamedFunction.new('ST_GeomFromGeoJSON', [JSON.generate(value)])
118
+ # elsif # EWKB
119
+ # elsif # WKB
120
+ # elsif # EWKT
121
+ # elsif # WKT
122
+ # end
123
+
124
+ # TODO us above if to determin if SRID sent
125
+ geometry_value = if value.is_a?(Hash) || value.class.name == "ActionController::Parameters".freeze
126
+ Arel::Nodes::NamedFunction.new('ST_SetSRID', [Arel::Nodes::NamedFunction.new('ST_GeomFromGeoJSON', [Arel::Nodes.build_quoted(JSON.generate(value))]), 4326])
127
+ elsif value[0,1] == "\x00" || value[0,1] == "\x01" || value[0,4] =~ /[0-9a-fA-F]{4}/
128
+ Arel::Nodes::NamedFunction.new('ST_SetSRID', [Arel::Nodes::NamedFunction.new('ST_GeomFromEWKB', [Arel::Nodes.build_quoted(value)]), 4326])
129
+ else
130
+ Arel::Nodes::NamedFunction.new('ST_SetSRID', [Arel::Nodes::NamedFunction.new('ST_GeomFromText', [Arel::Nodes.build_quoted(value)]), 4326])
131
+ end
132
+
133
+ resource.where(Arel::Nodes::NamedFunction.new('ST_Intersects', [table[column], geometry_value]))
134
+ else
135
+ raise "Not Supported: #{key.to_sym}"
136
+ end
137
+ end
138
+ resource
139
+ elsif value.is_a?(Array)
140
+ where(table[column].in(value.map { |x| x.send(send_method) }))
141
+ elsif value == true || value == 'true'
142
+ case method_name # columns_hash[column.to_s].try(:type)
143
+ when :filter_for_boolean then where(table[column].eq(value.try(:send, send_method)))
144
+ else where(table[column].not_eq(nil))
145
+ end
146
+ elsif value == false || value == 'false'
147
+ case method_name # columns_hash[column.to_s].try(:type)
148
+ when :filter_for_boolean then where(table[column].eq(value.try(:send, send_method)))
149
+ else where(table[column].eq(nil))
150
+ end
151
+ # when ''
152
+ # # TODO support nil. Currently rails params encode nil as empty strings,
153
+ # # and we can't tell which is desired, so do both
154
+ # where(table[column].eq(value).or(table[column].eq(nil)))
155
+ else
156
+ where(table[column].eq(value.try(:send, send_method)))
157
+ end
158
+ end
159
+ end
160
+
161
+ def filter_for_jsonb(column, value, options = {})
162
+ table = options[:table_alias] ? arel_table.alias(options[:table_alias]) : arel_table
163
+ column = table[column]
164
+
165
+ drill_for_json(column, value, all)
166
+ end
167
+ alias :filter_for_json :filter_for_jsonb
168
+
169
+ def drill_for_json(column, drill, resource)
170
+ if cast = drill.delete(:cast)
171
+ column = column.cast_as(cast)
172
+ end
173
+
174
+ drill.each do |key, value|
175
+ if value.is_a?(Hash) || value.class.name == "ActionController::Parameters".freeze
176
+ resource = drill_for_json(column.key(key), value, resource)
177
+ else
178
+ resource = case key.to_sym
179
+ when :equal, :eq
180
+ resource.where(column.eq(value))
181
+ when :greater_than, :gt
182
+ resource.where(column.gt(value))
183
+ when :less_than, :lt
184
+ resource.where(column.lt(value))
185
+ when :greater_than_or_equal_to, :gteq, :gte
186
+ resource.where(column.gteq(value))
187
+ when :less_than_or_equal_to, :lteq, :lte
188
+ resource.where(column.lteq(value))
189
+ when :not
190
+ resource.where(column.not_eq(value))
191
+ when :has_key
192
+ resource.where(column.has_key(value))
193
+ when :not_in
194
+ resource.where(column.not_in(value).or(column.eq(nil)))
195
+ else
196
+ raise 'Not supported'
197
+ end
198
+ end
199
+ end
200
+ resource
201
+ end
202
+
203
+ def filter_for_array(column, value, options={})
204
+ table = options[:table_alias] ? arel_table.alias(options[:table_alias]) : arel_table
205
+
206
+ if value.is_a?(Hash) || value.class.name == "ActionController::Parameters".freeze
207
+ resource = all
208
+ value.each_pair do |key, value|
209
+ resource = case key.to_sym
210
+ when :contains
211
+ resource.where(table[column].contains(value))
212
+ when :overlaps
213
+ resource.where(table[column].overlaps(value))
214
+ when :excludes
215
+ resource.where.not(table[column].contains(value))
216
+ # when :not_overlaps
217
+ # resource.where.not(Arel::Nodes::Overlaps.new(table[column], Arel::Attributes::Array.new(Array(value))))
218
+ else
219
+ raise "Not Supported: #{key.to_sym}"
220
+ end
221
+ end
222
+ resource
223
+ else
224
+ where(table[column].contains(value))
225
+ end
226
+ end
227
+
228
+ def filter_for_has_and_belongs_to_many(relation, value)
229
+ resource = all
230
+
231
+ options = {}
232
+ if resource.klass == relation.klass
233
+ options[:table_alias] = "#{relation.name}_#{relation.klass.table_name}"
234
+ end
235
+
236
+ if value.is_a?(Hash) || value.class.name == "ActionController::Parameters".freeze
237
+ resource = resource.joins(relation.name) #if !resource.references?(relation.name)
238
+ resource = resource.merge(relation.klass.filter(value, options))
239
+ elsif value.is_a?(Integer)
240
+ resource = resource.joins(relation.name) #if !resource.references?(relation.name)
241
+ resource = resource.merge(relation.klass.filter(value, options))
242
+ elsif value.is_a?(Array)
243
+ resource = resource.joins(relation.name) #if !resource.references?(relation.name)
244
+ resource = resource.merge(relation.klass.filter(value, options))
245
+ else
246
+ raise 'Not supported'
247
+ end
248
+
249
+ resource
250
+ end
251
+
252
+ def filter_for_has_many(relation, value)
253
+ resource = all
254
+
255
+ if value.is_a?(Hash) || value.class.name == "ActionController::Parameters".freeze
256
+ if relation.options[:through] && !relation.options[:source_type]
257
+ resource = resource.joins(relation.options[:through] => relation.source_reflection_name)
258
+ else
259
+ resource = resource.joins(relation.name) # if !resource.joined?(relation.name)
260
+ end
261
+ resource = resource.merge(relation.klass.filter(value))
262
+ elsif value.is_a?(Array) || value.is_a?(Integer)
263
+ resource = filter_for_has_many(relation, {:id => value})
264
+ elsif value == true || value == 'true'
265
+ counter_cache_column_name = relation.counter_cache_column || "#{relation.plural_name}_count"
266
+ if resource.column_names.include?(counter_cache_column_name)
267
+ resource = resource.where(resource.arel_table[counter_cache_column_name.to_sym].gt(0))
268
+ else
269
+ raise 'Not supported'
270
+ end
271
+ elsif value == false || value == 'false'
272
+ # TODO if the has_many relationship has counter_cache true can just use counter_cache_column method
273
+ counter_cache_column_name = relation.counter_cache_column || "#{relation.plural_name}_count"
274
+ if resource.column_names.include?(counter_cache_column_name)
275
+ resource = resource.where(resource.arel_table[counter_cache_column_name.to_sym].eq(0))
276
+ else
277
+ raise 'Not supported'
278
+ end
279
+ else
280
+ raise 'Not supported'
281
+ end
282
+
283
+ resource
284
+ end
285
+ alias_method :filter_for_has_one, :filter_for_has_many
286
+
287
+ def filter_for_belongs_to(relation, value)
288
+ resource = all
289
+
290
+ if value.is_a?(Array) || value.is_a?(Integer) || value.is_a?(NilClass)
291
+ resource = resource.where(:"#{relation.foreign_key}" => value)
292
+ elsif value == true || value == 'true'
293
+ resource = resource.where(resource.arel_table[:"#{relation.foreign_key}"].not_eq(nil))
294
+ elsif value == false || value == 'false'
295
+ resource = resource.where(resource.arel_table[:"#{relation.foreign_key}"].eq(nil))
296
+ elsif value.is_a?(Hash) || value.class.name == "ActionController::Parameters".freeze
297
+ if relation.polymorphic?
298
+ raise 'no :as' if !value[:as]
299
+ v = value.dup
300
+ klass = v.delete(:as).classify.constantize
301
+ t1 = resource.arel_table
302
+ t2 = klass.arel_table
303
+ resource = resource.joins(t1.join(t2).on(
304
+ t2[:id].eq(t1["#{relation.name}_id"]).and(t1["#{relation.name}_type"].eq(klass.name))
305
+ ).join_sources.first)
306
+ resource = resource.merge(klass.filter(v))
307
+ else
308
+ resource = resource.joins(relation.name) # if !resource.references?(relation.name)
309
+ resource = resource.merge(relation.klass.filter(value))
310
+ end
311
+ else
312
+ if value.is_a?(String) && value =~ /\A\d+\Z/
313
+ resource = resource.where(:"#{relation.foreign_key}" => value.to_i)
314
+ else
315
+ raise 'Not supported'
316
+ end
317
+ end
318
+ resource
319
+ end
320
+
321
+ end
322
+
323
+ ActiveRecord::Base.extend(ActiveRecord::Filter)
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Filter
3
- VERSION = '5.0.0.beta1'
3
+ VERSION = '5.0.0.rc1'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,17 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-filter
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0.beta1
4
+ version: 5.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Bracy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-22 00:00:00.000000000 Z
11
+ date: 2016-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: pg
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.0.0.rc1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 5.0.0.rc1
27
+ - !ruby/object:Gem::Dependency
28
+ name: arel-extensions
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - ">="
@@ -25,19 +39,19 @@ dependencies:
25
39
  - !ruby/object:Gem::Version
26
40
  version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
- name: activerecord
42
+ name: pg
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
- - - "~>"
45
+ - - ">="
32
46
  - !ruby/object:Gem::Version
33
- version: 5.0.0.beta1
34
- type: :runtime
47
+ version: '0'
48
+ type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
- - - "~>"
52
+ - - ">="
39
53
  - !ruby/object:Gem::Version
40
- version: 5.0.0.beta1
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -108,20 +122,34 @@ dependencies:
108
122
  - - ">="
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: railties
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 5.0.0.rc1
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 5.0.0.rc1
111
139
  - !ruby/object:Gem::Dependency
112
140
  name: factory_girl_rails
113
141
  requirement: !ruby/object:Gem::Requirement
114
142
  requirements:
115
- - - ">="
143
+ - - "~>"
116
144
  - !ruby/object:Gem::Version
117
- version: '0'
145
+ version: 4.6.0
118
146
  type: :development
119
147
  prerelease: false
120
148
  version_requirements: !ruby/object:Gem::Requirement
121
149
  requirements:
122
- - - ">="
150
+ - - "~>"
123
151
  - !ruby/object:Gem::Version
124
- version: '0'
152
+ version: 4.6.0
125
153
  - !ruby/object:Gem::Dependency
126
154
  name: faker
127
155
  requirement: !ruby/object:Gem::Requirement
@@ -146,13 +174,6 @@ extra_rdoc_files:
146
174
  - README.md
147
175
  files:
148
176
  - README.md
149
- - ext/active_record/base.rb
150
- - ext/active_record/query_methods.rb
151
- - ext/active_record/unkown_filter_error.rb
152
- - ext/arel/attributes/array.rb
153
- - ext/arel/nodes/contains.rb
154
- - ext/arel/nodes/overlaps.rb
155
- - ext/arel/visitors/postgresql.rb
156
177
  - lib/active_record/filter.rb
157
178
  - lib/active_record/filter/version.rb
158
179
  homepage: https://github.com/malomalo/activerecord-filter
@@ -1,268 +0,0 @@
1
- class ActiveRecord::Base
2
- class << self
3
-
4
- def inherited_with_filter(subclass)
5
- inherited_without_filter(subclass)
6
- subclass.instance_variable_set('@filters', HashWithIndifferentAccess.new)
7
- end
8
- alias_method_chain :inherited, :filter
9
-
10
- def filter_on(name, lambda)
11
- @filters[name] = lambda
12
- end
13
-
14
- def filter(filters, options={})
15
- resource = all
16
- return resource unless filters
17
-
18
- if filters.is_a?(Hash) || filters.is_a?(ActionController::Parameters)
19
- filters.each do |key, value|
20
- if @filters[key]
21
- #TODO add test for this... not sure how rails does this lambda call,
22
- # do they run it in a context for merge?
23
- resource = resource.merge( @filters[key].call(value) )
24
- else
25
- resource = resource.filter_for(key, value, options)
26
- end
27
- end
28
- elsif filters.is_a?(Array) || filters.is_a?(Integer)
29
- resource = resource.filter_for(:id, filters, options)
30
- end
31
-
32
- resource
33
- end
34
-
35
- def filter_for(key, value, options={})
36
- column = columns_hash[key.to_s]
37
- if column && column.array
38
- all.filter_for_array(key, value, options)
39
- elsif column
40
- all.send("filter_for_#{column.type}", key, value, options)
41
- else
42
- if relation = reflect_on_association(key)
43
- self.send("filter_for_#{relation.macro}", relation, value)
44
- else
45
- raise ActiveRecord::UnkownFilterError.new(self, key)
46
- end
47
- end
48
- end
49
-
50
- {
51
- filter_for_geometry: :itself,
52
- filter_for_datetime: :to_datetime,
53
- filter_for_integer: :to_i,
54
- filter_for_text: :itself,
55
- filter_for_boolean: :itself,
56
- filter_for_string: :itself,
57
- filter_for_uuid: :itself,
58
- filter_for_decimal: :to_f,
59
- filter_for_float: :to_f
60
- }.each_pair do |method_name, send_method|
61
- define_method(method_name) do |column, value, options={}|
62
- table = options[:table_alias] ? arel_table.alias(options[:table_alias]) : arel_table
63
-
64
- case value
65
- when Hash, ActionController::Parameters
66
- resource = all
67
- value.each_pair do |key, value|
68
- converted_value = value.try(:send, send_method)
69
- resource = case key.to_sym
70
- when :greater_than, :gt
71
- resource.where(table[column].gt(converted_value))
72
- when :less_than, :lt
73
- resource.where(table[column].lt(converted_value))
74
- when :greater_than_or_equal_to, :gteq, :gte
75
- resource.where(table[column].gteq(converted_value))
76
- when :less_than_or_equal_to, :lteq, :lte
77
- resource.where(table[column].lteq(converted_value))
78
- when :not
79
- resource.where(table[column].not_eq(converted_value))
80
- when :not_in
81
- resource.where(table[column].not_in(value).or(table[column].eq(nil)))
82
- when :intersects
83
- # geometry_value = if value.is_a?(Hash) # GeoJSON
84
- # Arel::Nodes::NamedFunction.new('ST_GeomFromGeoJSON', [JSON.generate(value)])
85
- # elsif # EWKB
86
- # elsif # WKB
87
- # elsif # EWKT
88
- # elsif # WKT
89
- # end
90
-
91
- # TODO us above if to determin if SRID sent
92
- geometry_value = if value.is_a?(Hash) || value.is_a?(ActionController::Parameters)
93
- Arel::Nodes::NamedFunction.new('ST_SetSRID', [Arel::Nodes::NamedFunction.new('ST_GeomFromGeoJSON', [Arel::Nodes.build_quoted(JSON.generate(value))]), 4326])
94
- elsif value[0,1] == "\x00" || value[0,1] == "\x01" || value[0,4] =~ /[0-9a-fA-F]{4}/
95
- Arel::Nodes::NamedFunction.new('ST_SetSRID', [Arel::Nodes::NamedFunction.new('ST_GeomFromEWKB', [Arel::Nodes.build_quoted(value)]), 4326])
96
- else
97
- Arel::Nodes::NamedFunction.new('ST_SetSRID', [Arel::Nodes::NamedFunction.new('ST_GeomFromText', [Arel::Nodes.build_quoted(value)]), 4326])
98
- end
99
-
100
- resource.where(Arel::Nodes::NamedFunction.new('ST_Intersects', [table[column], geometry_value]))
101
- else
102
- raise "Not Supported: #{key.to_sym}"
103
- end
104
- end
105
- resource
106
- when Array
107
- where(table[column].in(value.map { |x| x.send(send_method) }))
108
- when true, 'true'
109
- case method_name # columns_hash[column.to_s].try(:type)
110
- when :filter_for_boolean then where(table[column].eq(value.try(:send, send_method)))
111
- else where(table[column].not_eq(nil))
112
- end
113
- when false, 'false'
114
- case method_name # columns_hash[column.to_s].try(:type)
115
- when :filter_for_boolean then where(table[column].eq(value.try(:send, send_method)))
116
- else where(table[column].eq(nil))
117
- end
118
- # when ''
119
- # # TODO support nil. Currently rails params encode nil as empty strings,
120
- # # and we can't tell which is desired, so do both
121
- # where(table[column].eq(value).or(table[column].eq(nil)))
122
- else
123
- where(table[column].eq(value.try(:send, send_method)))
124
- end
125
- end
126
- end
127
-
128
- def filter_for_jsonb(column, value, options = {})
129
- table = options[:table_alias] ? arel_table.alias(options[:table_alias]) : arel_table
130
-
131
- case value
132
- when true, 'true'
133
- where(table[column].not_eq(nil))
134
- when false, 'false'
135
- where(table[column].eq(nil))
136
- when nil
137
- where(table[column].eq(nil))
138
- else
139
- raise 'Not supported'
140
- end
141
- end
142
-
143
- def filter_for_array(column, value, options={})
144
- table = options[:table_alias] ? arel_table.alias(options[:table_alias]) : arel_table
145
-
146
- case value
147
- when Hash, ActionController::Parameters
148
- resource = all
149
- value.each_pair do |key, value|
150
- resource = case key.to_sym
151
- when :contains
152
- resource.where(Arel::Nodes::Contains.new(table[column], Arel::Attributes::Array.new(Array(value))))
153
- when :overlaps
154
- resource.where(Arel::Nodes::Overlaps.new(table[column], Arel::Attributes::Array.new(Array(value))))
155
- when :not_overlaps
156
- resource.where.not(Arel::Nodes::Overlaps.new(table[column], Arel::Attributes::Array.new(Array(value))))
157
- when :not_contains
158
- resource.where.not(Arel::Nodes::Contains.new(table[column], Arel::Attributes::Array.new(Array(value))))
159
- else
160
- raise "Not Supported: #{key.to_sym}"
161
- end
162
- end
163
- resource
164
- when Array
165
- where(Arel::Nodes::Contains.new(table[column], Arel::Attributes::Array.new(value)))
166
- else
167
- where(Arel::Nodes::Contains.new(table[column], Arel::Attributes::Array.new([value])))
168
- end
169
- end
170
-
171
- def filter_for_has_and_belongs_to_many(relation, value)
172
- resource = all
173
-
174
- options = {}
175
- if resource.klass == relation.klass
176
- options[:table_alias] = "#{relation.name}_#{relation.klass.table_name}"
177
- end
178
-
179
- case value
180
- when Hash, ActionController::Parameters
181
- resource = resource.joins(relation.name) if !resource.references?(relation.name)
182
- resource = resource.merge(relation.klass.filter(value, options))
183
- when Integer
184
- resource = resource.joins(relation.name) if !resource.references?(relation.name)
185
- resource = resource.merge(relation.klass.filter(value, options))
186
- when Array
187
- resource = resource.joins(relation.name) if !resource.references?(relation.name)
188
- resource = resource.merge(relation.klass.filter(value, options))
189
- else
190
- raise 'Not supported'
191
- end
192
-
193
- resource
194
- end
195
-
196
- def filter_for_has_many(relation, value)
197
- resource = all
198
-
199
- case value
200
- when Hash, ActionController::Parameters
201
- if relation.options[:through]
202
- resource = resource.joins(relation.options[:through] => relation.source_reflection_name)
203
- else
204
- resource = resource.joins(relation.name) # if !resource.joined?(relation.name)
205
- end
206
- resource = resource.merge(relation.klass.filter(value))
207
- when Array, Integer
208
- resource = filter_for_has_many(relation, {:id => value})
209
- when true, 'true'
210
- counter_cache_column_name = relation.counter_cache_column || "#{relation.plural_name}_count"
211
- if resource.column_names.include?(counter_cache_column_name)
212
- resource = resource.where(resource.arel_table[counter_cache_column_name.to_sym].gt(0))
213
- else
214
- raise 'Not supported'
215
- end
216
- when false, 'false'
217
- # TODO if the has_many relationship has counter_cache true can just use counter_cache_column method
218
- counter_cache_column_name = relation.counter_cache_column || "#{relation.plural_name}_count"
219
- if resource.column_names.include?(counter_cache_column_name)
220
- resource = resource.where(resource.arel_table[counter_cache_column_name.to_sym].eq(0))
221
- else
222
- raise 'Not supported'
223
- end
224
- else
225
- raise 'Not supported'
226
- end
227
-
228
- resource
229
- end
230
- alias_method :filter_for_has_one, :filter_for_has_many
231
-
232
- def filter_for_belongs_to(relation, value)
233
- resource = all
234
-
235
- case value
236
- when Array, Integer, NilClass
237
- resource = resource.where(:"#{relation.foreign_key}" => value)
238
- when true, 'true'
239
- resource = resource.where(resource.arel_table[:"#{relation.foreign_key}"].not_eq(nil))
240
- when false, 'false'
241
- resource = resource.where(resource.arel_table[:"#{relation.foreign_key}"].eq(nil))
242
- when Hash, ActionController::Parameters
243
- if relation.polymorphic?
244
- raise 'no :as' if !value[:as]
245
- v = value.dup
246
- klass = v.delete(:as).classify.constantize
247
- t1 = resource.arel_table
248
- t2 = klass.arel_table
249
- resource = resource.joins(t1.join(t2).on(
250
- t2[:id].eq(t1["#{relation.name}_id"]).and(t1["#{relation.name}_type"].eq(klass.name))
251
- ).join_sources.first)
252
- resource = resource.merge(klass.filter(v))
253
- else
254
- resource = resource.joins(relation.name) # if !resource.references?(relation.name)
255
- resource = resource.merge(relation.klass.filter(value))
256
- end
257
- else
258
- if value.is_a?(String) && value =~ /\A\d+\Z/
259
- resource = resource.where(:"#{relation.foreign_key}" => value.to_i)
260
- else
261
- raise 'Not supported'
262
- end
263
- end
264
- resource
265
- end
266
-
267
- end
268
- end
@@ -1,27 +0,0 @@
1
- # module ActiveRecord::QueryMethods
2
- #
3
- # # # TODO: testme and rename to joins?
4
- # # def joined?(assoc)
5
- # # joined_assocs = joins_values.map{ |i| i.is_a?(Hash) ? i.keys : i.to_sym }.flatten
6
- # # joined_assocs.include?(assoc)
7
- # # end
8
- # #
9
- # # # TODO: testme and rename to includes?
10
- # # def included?(assoc)
11
- # # included_assocs = includes_values.map{ |i| i.is_a?(Hash) ? i.keys : i.to_sym }.flatten
12
- # # included_assocs.include?(assoc)
13
- # # end
14
- #
15
- # # TODO: testme and rename to
16
- # def references?(assoc)
17
- # references_assocs = references_values.map{ |i| i.is_a?(Hash) ? i.keys : i.to_sym }.flatten
18
- # references_assocs.include?(assoc)
19
- # end
20
- #
21
- # end
22
-
23
- module ActiveRecord::Querying
24
- # delegate :joined?, :to => :all
25
- # delegate :included?, :to => :all
26
- delegate :references?, :to => :all
27
- end
@@ -1,9 +0,0 @@
1
- class ActiveRecord::UnkownFilterError < NoMethodError
2
- attr_reader :klass, :filter
3
-
4
- def initialize(klass, filter)
5
- @klass = klass
6
- @filter = filter.to_s
7
- super("unkown filter #{filter.inspect} for #{klass}.")
8
- end
9
- end
@@ -1,5 +0,0 @@
1
- module Arel
2
- module Attributes
3
- class Array < Attribute; end
4
- end
5
- end
@@ -1,6 +0,0 @@
1
- module Arel
2
- module Nodes
3
- class Contains < Binary
4
- end
5
- end
6
- end
@@ -1,6 +0,0 @@
1
- module Arel
2
- module Nodes
3
- class Overlaps < Binary
4
- end
5
- end
6
- end
@@ -1,37 +0,0 @@
1
- module Arel
2
- module Visitors
3
- class PostgreSQL
4
- private
5
-
6
- def visit_Arel_Nodes_Contains o, collector
7
- visit o.left, collector
8
- collector << ' @> '
9
- visit o.right, collector
10
- end
11
-
12
- def visit_Arel_Nodes_Overlaps o, collector
13
- visit o.left, collector
14
- collector << ' && '
15
- visit o.right, collector
16
- end
17
-
18
- def visit_Arel_Attributes_Array o, collector
19
- type = if !o.relation[0]
20
- ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.new(nil)
21
- else
22
- # TODO: temp fix, need to figure out how to look up ruby type ->
23
- # postgres type or use the column we're quering on...
24
- dt = if o.relation[0].class == Fixnum
25
- dt = "Integer"
26
- else
27
- o.relation[0].class
28
- end
29
- ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.new("ActiveRecord::Type::#{dt}".constantize.new)
30
- end
31
-
32
- collector << quote(type.serialize(o.relation))
33
- end
34
-
35
- end
36
- end
37
- end