activerecord-spatial 0.0.1 → 0.1.0
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.
- data/lib/activerecord-spatial/active_record/connection_adapters/postgresql/adapter_extensions.rb +24 -6
- data/lib/activerecord-spatial/associations.rb +5 -283
- data/lib/activerecord-spatial/associations/active_record.rb +146 -0
- data/lib/activerecord-spatial/associations/active_record_3.rb +123 -0
- data/lib/activerecord-spatial/associations/base.rb +182 -0
- data/lib/activerecord-spatial/spatial_columns.rb +1 -1
- data/lib/activerecord-spatial/spatial_function.rb +3 -2
- data/lib/activerecord-spatial/version.rb +1 -1
- data/test/associations_tests.rb +24 -4
- data/test/spatial_function_tests.rb +77 -0
- data/test/spatial_scopes_geographies_tests.rb +14 -14
- data/test/spatial_scopes_tests.rb +68 -68
- data/test/test_helper.rb +40 -31
- metadata +22 -11
- checksums.yaml +0 -15
@@ -0,0 +1,123 @@
|
|
1
|
+
|
2
|
+
module ActiveRecord
|
3
|
+
module Associations
|
4
|
+
class Builder::Spatial < Builder::HasMany #:nodoc:
|
5
|
+
self.macro = SPATIAL_MACRO
|
6
|
+
self.valid_options += VALID_SPATIAL_OPTIONS
|
7
|
+
self.valid_options -= INVALID_SPATIAL_OPTIONS
|
8
|
+
end
|
9
|
+
|
10
|
+
class Preloader #:nodoc:
|
11
|
+
class SpatialAssociation < HasMany #:nodoc:
|
12
|
+
def records_for(ids)
|
13
|
+
table_name = reflection.quoted_table_name
|
14
|
+
join_name = model.quoted_table_name
|
15
|
+
column = %{#{SPATIAL_JOIN_QUOTED_NAME}.#{model.quoted_primary_key}}
|
16
|
+
geom = {
|
17
|
+
:class => model,
|
18
|
+
:table_alias => SPATIAL_JOIN_NAME
|
19
|
+
}
|
20
|
+
|
21
|
+
if reflection.options[:geom].is_a?(Hash)
|
22
|
+
geom.merge!(reflection.options[:geom])
|
23
|
+
else
|
24
|
+
geom[:column] = reflection.options[:geom]
|
25
|
+
end
|
26
|
+
|
27
|
+
scoped.
|
28
|
+
select(%{array_to_string(array_agg(#{column}), ',') AS "#{SPATIAL_FIELD_ALIAS}"}).
|
29
|
+
joins(
|
30
|
+
"INNER JOIN #{join_name} AS #{SPATIAL_JOIN_QUOTED_NAME} ON (" <<
|
31
|
+
klass.send("st_#{reflection.options[:relationship]}",
|
32
|
+
geom,
|
33
|
+
(reflection.options[:scope_options] || {}).merge(
|
34
|
+
:column => reflection.options[:foreign_geom]
|
35
|
+
)
|
36
|
+
).where_values.join(' AND ') <<
|
37
|
+
")"
|
38
|
+
).
|
39
|
+
where(model.arel_table.alias(SPATIAL_JOIN_NAME)[model.primary_key].in(ids)).
|
40
|
+
group(table[klass.primary_key])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class AssociationScope #:nodoc:
|
46
|
+
def add_constraints_with_spatial(scope)
|
47
|
+
return add_constraints_without_spatial(scope) if !self.association.is_a?(SpatialAssociation)
|
48
|
+
|
49
|
+
tables = construct_tables
|
50
|
+
|
51
|
+
chain.each_with_index do |reflection, i|
|
52
|
+
table, foreign_table = tables.shift, tables.first
|
53
|
+
|
54
|
+
conditions = self.conditions[i]
|
55
|
+
geom_options = {
|
56
|
+
:class => self.association.klass
|
57
|
+
}
|
58
|
+
|
59
|
+
if self.association.geom.is_a?(Hash)
|
60
|
+
geom_options.merge!(
|
61
|
+
:value => owner[self.association.geom[:name]]
|
62
|
+
)
|
63
|
+
geom_options.merge!(self.association.geom)
|
64
|
+
else
|
65
|
+
geom_options.merge!(
|
66
|
+
:value => owner[self.association.geom],
|
67
|
+
:name => self.association.geom
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
if reflection == chain.last
|
72
|
+
scope = scope.send("st_#{self.association.relationship}", geom_options, self.association.scope_options)
|
73
|
+
|
74
|
+
if reflection.type
|
75
|
+
scope = scope.where(table[reflection.type].eq(owner.class.base_class.name))
|
76
|
+
end
|
77
|
+
|
78
|
+
conditions.each do |condition|
|
79
|
+
scope = scope.where(interpolate(condition))
|
80
|
+
end
|
81
|
+
else
|
82
|
+
constraint = scope.where(
|
83
|
+
scope.send(
|
84
|
+
"st_#{self.association.relationship}",
|
85
|
+
owner[self.association.foreign_geom],
|
86
|
+
self.association.scope_options
|
87
|
+
).where_values
|
88
|
+
).join(' AND ')
|
89
|
+
|
90
|
+
if reflection.type
|
91
|
+
type = chain[i + 1].klass.base_class.name
|
92
|
+
constraint = table[reflection.type].eq(type).and(constraint)
|
93
|
+
end
|
94
|
+
|
95
|
+
scope = scope.joins(join(foreign_table, constraint))
|
96
|
+
|
97
|
+
unless conditions.empty?
|
98
|
+
scope = scope.where(sanitize(conditions, table))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
scope
|
104
|
+
end
|
105
|
+
alias_method_chain :add_constraints, :spatial
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
module ActiveRecordSpatial::Associations
|
111
|
+
module ClassMethods #:nodoc:
|
112
|
+
def has_many_spatially(name, options = {}, &extension)
|
113
|
+
options = build_options(options)
|
114
|
+
|
115
|
+
if !ActiveRecordSpatial::SpatialScopeConstants::RELATIONSHIPS.include?(options[:relationship].to_s)
|
116
|
+
raise ArgumentError.new(%{Invalid spatial relationship "#{options[:relationship]}", expected one of #{ActiveRecordSpatial::SpatialScopeConstants::RELATIONSHIPS.inspect}})
|
117
|
+
end
|
118
|
+
|
119
|
+
ActiveRecord::Associations::Builder::Spatial.build(self, name, options, &extension)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
@@ -0,0 +1,182 @@
|
|
1
|
+
|
2
|
+
module ActiveRecord
|
3
|
+
module Associations #:nodoc:
|
4
|
+
class SpatialAssociation < HasManyAssociation
|
5
|
+
attr_reader :geom, :foreign_geom, :relationship, :scope_options
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
super
|
9
|
+
|
10
|
+
@geom = self.options[:geom]
|
11
|
+
@foreign_geom = self.options[:foreign_geom]
|
12
|
+
@relationship = self.options[:relationship].to_s
|
13
|
+
@scope_options = (self.options[:scope_options] || {}).merge({
|
14
|
+
:column => @foreign_geom
|
15
|
+
})
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Builder::Spatial < Builder::HasMany #:nodoc:
|
20
|
+
SPATIAL_MACRO = :has_many
|
21
|
+
|
22
|
+
VALID_SPATIAL_OPTIONS = [
|
23
|
+
:geom, :foreign_geom, :relationship, :scope_options
|
24
|
+
].freeze
|
25
|
+
|
26
|
+
INVALID_SPATIAL_OPTIONS = [
|
27
|
+
:through, :source, :source_type, :dependent, :finder_sql, :counter_sql,
|
28
|
+
:inverse_of
|
29
|
+
].freeze
|
30
|
+
|
31
|
+
private
|
32
|
+
def dependency_method_name
|
33
|
+
"spatially_#{self.relationship}_dependent_for_#{name}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Preloader #:nodoc:
|
38
|
+
class SpatialAssociation < HasMany #:nodoc:
|
39
|
+
SPATIAL_FIELD_ALIAS = '__spatial_ids__'
|
40
|
+
SPATIAL_JOIN_NAME = '__spatial_ids_join__'
|
41
|
+
SPATIAL_JOIN_QUOTED_NAME = %{"#{SPATIAL_JOIN_NAME}"}
|
42
|
+
|
43
|
+
private
|
44
|
+
def associated_records_by_owner
|
45
|
+
owners_map = owners_by_key
|
46
|
+
owner_keys = owners_map.keys.compact
|
47
|
+
|
48
|
+
if klass.nil? || owner_keys.empty?
|
49
|
+
records = []
|
50
|
+
else
|
51
|
+
sliced = owner_keys.each_slice(model.connection.in_clause_length || owner_keys.size)
|
52
|
+
records = sliced.map { |slice| records_for(slice) }.flatten
|
53
|
+
end
|
54
|
+
|
55
|
+
records_by_owner = Hash[owners.map { |owner| [owner, []] }]
|
56
|
+
|
57
|
+
records.each do |record|
|
58
|
+
record[SPATIAL_FIELD_ALIAS].split(',').each do |owner_key|
|
59
|
+
owners_map[owner_key].each do |owner|
|
60
|
+
records_by_owner[owner] << record
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
records_by_owner
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def preloader_for_with_spatial(reflection)
|
70
|
+
if reflection.options[:relationship]
|
71
|
+
SpatialAssociation
|
72
|
+
else
|
73
|
+
preloader_for_without_spatial(reflection)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
alias_method_chain :preloader_for, :spatial
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
module Reflection #:nodoc:
|
81
|
+
class AssociationReflection < MacroReflection #:nodoc:
|
82
|
+
def association_class_with_spatial
|
83
|
+
if self.options[:relationship]
|
84
|
+
Associations::SpatialAssociation
|
85
|
+
else
|
86
|
+
association_class_without_spatial
|
87
|
+
end
|
88
|
+
end
|
89
|
+
alias_method_chain :association_class, :spatial
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# ActiveRecord Spatial associations allow for +has_many+-style associations
|
95
|
+
# using spatial relationships.
|
96
|
+
#
|
97
|
+
# == Example
|
98
|
+
#
|
99
|
+
# class Neighbourhood < ActiveRecord::Base
|
100
|
+
# has_many_spatially :cities,
|
101
|
+
# :relationship => :contains
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# class City < ActiveRecord::Base
|
105
|
+
# has_many_spatially :neighbourhoods,
|
106
|
+
# :relationship => :within
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# Neighbourhood.first.cities
|
110
|
+
# #=> All cities that the neighbourhood is within
|
111
|
+
#
|
112
|
+
# City.first.neighbourhoods
|
113
|
+
# #=> All neighbourhoods contained by the city
|
114
|
+
#
|
115
|
+
# City.includes(:neighbourhoods).first.neighbourhoods
|
116
|
+
# #=> Eager loading works too
|
117
|
+
#
|
118
|
+
# Spatial associations can be set up using any of the relationships found in
|
119
|
+
# ActiveRecordSpatial::SpatialScopes::RELATIONSHIPS.
|
120
|
+
#
|
121
|
+
# == Options
|
122
|
+
#
|
123
|
+
# Many of the options available with standard +has_many+ associations will work
|
124
|
+
# with the exceptions of +:through+, +:source+, +:source_type+, +:dependent+,
|
125
|
+
# +:finder_sql+, +:counter_sql+, and +:inverse_of+.
|
126
|
+
#
|
127
|
+
# Polymorphic relationships can be used via the +:as+ option as in standard
|
128
|
+
# +:has_many+ relationships. Note that the default field for the geometry
|
129
|
+
# in these cases is "#{association_name}_geom" and can be overridden using
|
130
|
+
# the +:foreign_geom+ option.
|
131
|
+
#
|
132
|
+
# * +:relationship+ - sets the spatial relationship for the association.
|
133
|
+
# Valid options can be found in ActiveRecordSpatial::SpatialScopes::RELATIONSHIPS.
|
134
|
+
# The default value is +:intersects+.
|
135
|
+
# * +:geom+ - sets the geometry field for the association in the calling model.
|
136
|
+
# The default value is +:the_geom+ as is often seen in PostGIS documentation.
|
137
|
+
# * +:foreign_geom+ - sets the geometry field for the association's foreign
|
138
|
+
# table. The default here is again +:the_geom+.
|
139
|
+
# * +:scope_options+ - these are options passed directly to the SpatialScopes
|
140
|
+
# module and as such the options are the same as are available there. The
|
141
|
+
# default value here is <tt>{ :invert => true }</tt>, as we want our
|
142
|
+
# spatial relationships to say "Foo spatially contains many Bars" and
|
143
|
+
# therefore the relationship in SQL becomes
|
144
|
+
# <tt>ST_contains("foos"."the_geom", "bars"."the_geom")</tt>.
|
145
|
+
#
|
146
|
+
# Note that you can modify the default geometry column name for all of
|
147
|
+
# ActiveRecordSpatial by setting it via ActiveRecordSpatia.default_column_name.
|
148
|
+
#
|
149
|
+
# == Caveats
|
150
|
+
#
|
151
|
+
# * You should consider spatial associations to be essentially readonly. Since
|
152
|
+
# we're not dealing with unique IDs here but rather 2D and 3D geometries,
|
153
|
+
# the relationships between rows don't really map well to the traditional
|
154
|
+
# foreign key-style ActiveRecord associations.
|
155
|
+
module ActiveRecordSpatial::Associations
|
156
|
+
extend ActiveSupport::Concern
|
157
|
+
|
158
|
+
DEFAULT_OPTIONS = {
|
159
|
+
:relationship => :intersects,
|
160
|
+
:geom => ActiveRecordSpatial.default_column_name,
|
161
|
+
:foreign_geom => ActiveRecordSpatial.default_column_name,
|
162
|
+
:scope_options => {
|
163
|
+
:invert => true
|
164
|
+
}
|
165
|
+
}.freeze
|
166
|
+
|
167
|
+
module ClassMethods #:nodoc:
|
168
|
+
def build_options(options)
|
169
|
+
if !options[:foreign_geom] && options[:as]
|
170
|
+
options[:foreign_geom] = "#{options[:as]}_geom"
|
171
|
+
end
|
172
|
+
|
173
|
+
if options[:geom].is_a?(Hash)
|
174
|
+
options[:geom][:name] ||= ActiveRecordSpatial.default_column_name
|
175
|
+
end
|
176
|
+
|
177
|
+
DEFAULT_OPTIONS.deep_merge(options)
|
178
|
+
end
|
179
|
+
private :build_options
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
@@ -16,8 +16,9 @@ module ActiveRecordSpatial
|
|
16
16
|
|
17
17
|
def build_function_call(function, *args)
|
18
18
|
options = default_options(args.extract_options!)
|
19
|
-
|
20
|
-
|
19
|
+
|
20
|
+
geom = options.fetch(:geom_arg, args.first)
|
21
|
+
args = Array.wrap(options.fetch(:args, args.from(1)))
|
21
22
|
|
22
23
|
column_name = self.column_name(options[:column])
|
23
24
|
first_geom_arg = self.wrap_column_or_geometry(
|
data/test/associations_tests.rb
CHANGED
@@ -229,7 +229,7 @@ class PreloadTest < ActiveRecordSpatialTestCase
|
|
229
229
|
values = nil
|
230
230
|
assert_queries(4) do
|
231
231
|
assert_sql(/ST_intersects\('#{REGEXP_WKB_HEX}'::geometry, "bars"\."the_geom"/) do
|
232
|
-
values = Foo.all.collect do |foo|
|
232
|
+
values = Foo.all.to_a.collect do |foo|
|
233
233
|
foo.bars.length
|
234
234
|
end
|
235
235
|
end
|
@@ -242,7 +242,7 @@ class PreloadTest < ActiveRecordSpatialTestCase
|
|
242
242
|
values = nil
|
243
243
|
assert_queries(2) do
|
244
244
|
assert_sql(/SELECT "bars"\.\*, array_to_string\(array_agg\("__spatial_ids_join__"."id"\), ','\) AS "__spatial_ids__" FROM "bars" INNER JOIN "foos" AS "__spatial_ids_join__" ON \(ST_intersects\("__spatial_ids_join__"."the_geom", "bars"."the_geom"\)\) WHERE "__spatial_ids_join__"\."id" IN \(.+\) GROUP BY "bars"\."id"/) do
|
245
|
-
values = Foo.includes(:bars).
|
245
|
+
values = Foo.includes(:bars).to_a.collect do |foo|
|
246
246
|
foo.bars.length
|
247
247
|
end
|
248
248
|
end
|
@@ -267,7 +267,7 @@ class PreloadWithOtherGeomTest < ActiveRecordSpatialTestCase
|
|
267
267
|
values = nil
|
268
268
|
assert_queries(4) do
|
269
269
|
assert_sql(/ST_intersects\(ST_SetSRID\('#{REGEXP_WKB_HEX}'::geometry, #{ActiveRecordSpatial::UNKNOWN_SRID}\), "bars"\."the_geom"/) do
|
270
|
-
values = Foo.order('id').
|
270
|
+
values = Foo.order('id').to_a.collect do |foo|
|
271
271
|
foo.bars.length
|
272
272
|
end
|
273
273
|
end
|
@@ -280,7 +280,7 @@ class PreloadWithOtherGeomTest < ActiveRecordSpatialTestCase
|
|
280
280
|
values = nil
|
281
281
|
assert_queries(2) do
|
282
282
|
assert_sql(/SELECT "bars"\.\*, array_to_string\(array_agg\("__spatial_ids_join__"."id"\), ','\) AS "__spatial_ids__" FROM "bars" INNER JOIN "foos" AS "__spatial_ids_join__" ON \(ST_intersects\(ST_SetSRID\("__spatial_ids_join__"."the_other_geom", #{ActiveRecordSpatial::UNKNOWN_SRID}\), "bars"."the_geom"\)\) WHERE "__spatial_ids_join__"\."id" IN \(.+\) GROUP BY "bars"\."id"/) do
|
283
|
-
values = Foo.order('id').includes(:bars).
|
283
|
+
values = Foo.order('id').includes(:bars).to_a.collect do |foo|
|
284
284
|
foo.bars.length
|
285
285
|
end
|
286
286
|
end
|
@@ -460,6 +460,8 @@ class IncludeOptionTest < ActiveRecordSpatialTestCase
|
|
460
460
|
end
|
461
461
|
|
462
462
|
def test_includes
|
463
|
+
skip("Removed from AR 4") if ActiveRecord::VERSION::MAJOR >= 4
|
464
|
+
|
463
465
|
values = nil
|
464
466
|
assert_queries(3) do
|
465
467
|
assert_sql(/SELECT\s+"blorts"\.\*\s+FROM\s+"blorts"\s+WHERE\s+"blorts"\."foo_id"\s+IN\s+\(.+\)/) do
|
@@ -652,5 +654,23 @@ class BothGeomWrapperAndOptionsWithMixedSRIDsTest < ActiveRecordSpatialTestCase
|
|
652
654
|
|
653
655
|
assert_equal([ 1, 2, 3 ], values)
|
654
656
|
end
|
657
|
+
|
658
|
+
class ScopeArgumentTest < ActiveRecordSpatialTestCase
|
659
|
+
def setup
|
660
|
+
self.class.load_models(:foo, :bar, :blort)
|
661
|
+
end
|
662
|
+
|
663
|
+
def test_foo
|
664
|
+
Foo.class_eval do
|
665
|
+
has_many_spatially :bars, proc {
|
666
|
+
self.order(:id)
|
667
|
+
}
|
668
|
+
end
|
669
|
+
|
670
|
+
assert_sql(/ORDER BY "bars".id/) do
|
671
|
+
Foo.first.bars.to_a
|
672
|
+
end
|
673
|
+
end
|
674
|
+
end if ActiveRecord::VERSION::MAJOR >= 4
|
655
675
|
end
|
656
676
|
|
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
$: << File.dirname(__FILE__)
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class SpatialFunctionTests < ActiveRecordSpatialTestCase
|
6
|
+
def self.before_suite
|
7
|
+
load_models(:foo)
|
8
|
+
load_models(:blort)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_geom_arg_option
|
12
|
+
assert_equal(
|
13
|
+
%{ST_distance("foos"."the_geom", '010100000000000000000000000000000000000000'::geometry)},
|
14
|
+
Foo.spatial_function(:distance, :geom_arg => 'POINT(0 0)').to_sql
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_geom_as_argument
|
19
|
+
assert_equal(
|
20
|
+
%{ST_distance("foos"."the_geom", '010100000000000000000000000000000000000000'::geometry)},
|
21
|
+
Foo.spatial_function(:distance, 'POINT(0 0)').to_sql
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_column_option
|
26
|
+
assert_equal(
|
27
|
+
%{ST_distance("foos"."the_other_geom", ST_SetSRID('010100000000000000000000000000000000000000'::geometry, 4326))},
|
28
|
+
Foo.spatial_function(:distance, 'POINT(0 0)', :column => 'the_other_geom').to_sql
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_class_option
|
33
|
+
assert_equal(
|
34
|
+
%{ST_distance("foos"."the_other_geom", ST_SetSRID('010100000000000000000000000000000000000000'::geometry, 4326))},
|
35
|
+
Foo.spatial_function(:distance, {
|
36
|
+
:class => Blort,
|
37
|
+
:value => 'POINT(0 0)'
|
38
|
+
}, {
|
39
|
+
:column => 'the_other_geom'
|
40
|
+
}).to_sql
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_class_name_option
|
45
|
+
assert_equal(
|
46
|
+
%{ST_distance("foos"."the_other_geom", ST_SetSRID('010100000000000000000000000000000000000000'::geometry, 4326))},
|
47
|
+
Foo.spatial_function(:distance, {
|
48
|
+
:class => 'Blort',
|
49
|
+
:value => 'POINT(0 0)'
|
50
|
+
}, {
|
51
|
+
:column => 'the_other_geom'
|
52
|
+
}).to_sql
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_invert_option
|
57
|
+
assert_equal(
|
58
|
+
%{ST_distance('010100000000000000000000000000000000000000'::geometry, "foos"."the_geom")},
|
59
|
+
Foo.spatial_function(:distance, 'POINT(0 0)', :invert => true).to_sql
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_use_index_option
|
64
|
+
assert_equal(
|
65
|
+
%{_ST_distance("foos"."the_geom", '010100000000000000000000000000000000000000'::geometry)},
|
66
|
+
Foo.spatial_function(:distance, 'POINT(0 0)', :use_index => false).to_sql
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_allow_null_option
|
71
|
+
assert_equal(
|
72
|
+
%{(ST_distance("foos"."the_geom", '010100000000000000000000000000000000000000'::geometry) OR "foos"."the_geom" IS NULL)},
|
73
|
+
Foo.spatial_function(:distance, 'POINT(0 0)', :allow_null => true).to_sql
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|