activerecord-filter 5.0.0.beta1 → 5.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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