activerecord-postgis 0.2.0 → 0.3.1

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
  SHA256:
3
- metadata.gz: b452469d6861f42a259853b4f6b3f7e5a694d98c325141d5a43ac09eaf40e967
4
- data.tar.gz: ada960ed4d93542ae2f2f805412d8b39dc58ae52cfa1612b8007e53e2bf2952f
3
+ metadata.gz: 348c302fed46b25f5815a94a08f24882b3c9ce9a4f29907ba5e3fcd6570dbf27
4
+ data.tar.gz: dd1a7f7de5e9a71178a2e98981221861824336c20861fce51a2da6d6b6eeceac
5
5
  SHA512:
6
- metadata.gz: d3c0f7fa79a22ba014655f18198bc4c048f3287fa6ade5e5c1442e8f0099c910394bc7592939b3a8c911670d9c4a54b5bbb8d0d29b568dddaf0406cba310c453
7
- data.tar.gz: 14c0462864a39e967b1c2c684a0dda6a6e0d6b1d902c87561398dc214f4934238594f8847b26ba33e52df669f00d998828df0c2ca29305fc56f94209e45019bc
6
+ metadata.gz: '08ce6f86d903016208598a2c70e8da83d59fbf9b11afd8f7db0b4b1a8cb953599c43ce102ac3b84938a6b934e82af0791d2641ba453c2a17a6b75c15ad85ca13'
7
+ data.tar.gz: 6d529987e15c03978f48e346cb174f900d95493e6b1c1f2a1e417c71005ff2e7d75e1e3ee76099fcabd03b7705f28c60b04d983e609e6969929329f5d7d7a494
data/README.md CHANGED
@@ -93,22 +93,42 @@ parks_in_city = Park.where(
93
93
  )
94
94
  ```
95
95
 
96
- **With Arel Spatial Methods**:
96
+ **With Arel Spatial Methods** (Now with expanded arsenal!):
97
97
 
98
98
  ```ruby
99
- # Find locations within distance
99
+ # Basic spatial queries
100
100
  Location.where(
101
101
  Location.arel_table[:coordinates].st_distance(point).lt(1000)
102
102
  )
103
103
 
104
- # Find polygons that contain a point
105
- Boundary.where(
106
- Boundary.arel_table[:area].st_contains(Arel.spatial(point))
104
+ # NEW: K-Nearest Neighbor - Lightning fast "find nearest" queries
105
+ # Uses spatial index for incredible performance!
106
+ nearest_locations = Location
107
+ .order(Location.arel_table[:coordinates].distance_operator(my_position))
108
+ .limit(10)
109
+
110
+ # Advanced spatial predicates
111
+ # Find intersecting routes
112
+ Route.where(Route.arel_table[:path].st_intersects(restricted_zone))
113
+
114
+ # Find points within efficient distance (uses spatial index!)
115
+ Location.where(
116
+ Location.arel_table[:coordinates].st_dwithin(headquarters, 5000)
117
+ )
118
+
119
+ # Create buffer zones
120
+ safe_zones = DangerZone.select(
121
+ DangerZone.arel_table[:area].st_buffer(100).as('safety_perimeter')
122
+ )
123
+
124
+ # Transform between coordinate systems
125
+ global_coords = Location.select(
126
+ Location.arel_table[:local_position].st_transform(4326).as('wgs84_position')
107
127
  )
108
128
 
109
- # Calculate lengths and areas
110
- Route.select(
111
- Route.arel_table[:path].st_length.as('distance')
129
+ # Calculate areas
130
+ territories = Region.select(
131
+ Region.arel_table[:boundary].st_area.as('territory_size')
112
132
  )
113
133
  ```
114
134
 
@@ -139,6 +159,97 @@ puts location.coordinates.x # -5.923647
139
159
  puts location.coordinates.y # 35.790897
140
160
  ```
141
161
 
162
+ ## Testing
163
+
164
+ When testing spatial functionality in your Rails application, this gem provides helpful test utilities:
165
+
166
+ ```ruby
167
+ # In your test_helper.rb or rails_helper.rb
168
+ require 'activerecord-postgis/test_helper'
169
+
170
+ class ActiveSupport::TestCase
171
+ include ActiveRecordPostgis::TestHelper
172
+ end
173
+
174
+ # Or for RSpec
175
+ RSpec.configure do |config|
176
+ config.include ActiveRecordPostgis::TestHelper
177
+ end
178
+ ```
179
+
180
+ ### Test Helper Methods
181
+
182
+ ```ruby
183
+ class LocationTest < ActiveSupport::TestCase
184
+ def test_spatial_operations
185
+ # Create test geometries
186
+ point1 = create_point(-5.9, 35.8)
187
+ point2 = create_point(-5.91, 35.81)
188
+ polygon = create_test_polygon
189
+
190
+ location = Location.create!(coordinates: point1, boundary: polygon)
191
+
192
+ # Traditional assertions
193
+ assert_spatial_equal point1, location.coordinates
194
+ assert_within_distance point1, point2, 200 # meters
195
+ assert_contains polygon, point1
196
+
197
+ # New chainable syntax (recommended)
198
+ assert_spatial_column(location.coordinates)
199
+ .has_srid(4326)
200
+ .is_type(:point)
201
+ .is_geographic
202
+
203
+ assert_spatial_column(location.boundary)
204
+ .is_type(:polygon)
205
+ .has_srid(4326)
206
+ end
207
+
208
+ def test_3d_geometry
209
+ point_3d = create_point(1.0, 2.0, srid: 4326, z: 10.0)
210
+
211
+ assert_spatial_column(point_3d)
212
+ .has_z
213
+ .has_srid(4326)
214
+ .is_type(:point)
215
+ .is_cartesian
216
+ end
217
+ end
218
+ ```
219
+
220
+ **Available Test Helpers:**
221
+
222
+ **Traditional Assertions:**
223
+ - `assert_spatial_equal(expected, actual)` - Assert spatial objects are equal
224
+ - `assert_within_distance(point1, point2, distance)` - Assert points within distance
225
+ - `assert_contains(container, contained)` - Assert geometry contains another
226
+ - `assert_within(inner, outer)` - Assert geometry is within another
227
+ - `assert_intersects(geom1, geom2)` - Assert geometries intersect
228
+ - `assert_disjoint(geom1, geom2)` - Assert geometries don't intersect
229
+
230
+ **Chainable Spatial Column Assertions:**
231
+ - `assert_spatial_column(geometry).has_z` - Assert has Z dimension
232
+ - `assert_spatial_column(geometry).has_m` - Assert has M dimension
233
+ - `assert_spatial_column(geometry).has_srid(srid)` - Assert SRID value
234
+ - `assert_spatial_column(geometry).is_type(type)` - Assert geometry type
235
+ - `assert_spatial_column(geometry).is_geographic` - Assert geographic factory
236
+ - `assert_spatial_column(geometry).is_cartesian` - Assert cartesian factory
237
+
238
+ **Geometry Factories:**
239
+ - `create_point(x, y, srid: 4326)` - Create test points
240
+ - `create_test_polygon(srid: 4326)` - Create test polygons
241
+ - `create_test_linestring(srid: 4326)` - Create test linestrings
242
+ - `factory(srid: 4326, geographic: false)` - Get geometry factory
243
+ - `geographic_factory(srid: 4326)` - Get geographic factory
244
+ - `cartesian_factory(srid: 0)` - Get cartesian factory
245
+
246
+ ## Documentation
247
+
248
+ 📚 **Learn Like You're Defending the Galaxy**
249
+
250
+ - [🚀 Spatial Warfare Manual](docs/SPATIAL_WARFARE.md) - Advanced PostGIS arsenal explained through space combat
251
+ - [🍳 The PostGIS Cookbook](docs/COOKBOOK.md) - Real-world recipes from delivery fleets to geofencing
252
+
142
253
  ## Features
143
254
 
144
255
  🌍 **Complete PostGIS Type Support**
@@ -148,7 +259,14 @@ puts location.coordinates.y # 35.790897
148
259
  - Support for SRID, Z/M dimensions
149
260
 
150
261
  🔍 **Spatial Query Methods**
151
- - `st_distance`, `st_contains`, `st_within`, `st_length`
262
+ - Core methods: `st_distance`, `st_contains`, `st_within`, `st_length`
263
+ - **NEW:** Advanced spatial operations:
264
+ - `<->` (distance_operator) - K-Nearest Neighbor search (blazing fast!)
265
+ - `st_intersects` - Detect geometry intersections
266
+ - `st_dwithin` - Efficient proximity queries (index-optimized!)
267
+ - `st_buffer` - Create buffer zones around geometries
268
+ - `st_transform` - Convert between coordinate systems
269
+ - `st_area` - Calculate polygon areas
152
270
  - Custom Arel visitor for PostGIS SQL generation
153
271
  - Seamless integration with ActiveRecord queries
154
272
 
@@ -163,6 +281,7 @@ puts location.coordinates.y # 35.790897
163
281
  - Works with existing PostgreSQL tools
164
282
  - Clear error messages and debugging
165
283
  - Full RGeo integration
284
+ - Comprehensive test helpers for spatial assertions
166
285
 
167
286
  ## Acknowledgments
168
287
 
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostGIS
6
+ # Test helpers for spatial data assertions
7
+ module TestHelpers
8
+ # Assert that two spatial objects are equal
9
+ def assert_spatial_equal(expected, actual, msg = nil)
10
+ msg ||= "Expected spatial object #{expected.as_text} but got #{actual.as_text}"
11
+
12
+ if expected.respond_to?(:equals?) && actual.respond_to?(:equals?)
13
+ assert expected.equals?(actual), msg
14
+ else
15
+ assert_equal expected.to_s, actual.to_s, msg
16
+ end
17
+ end
18
+
19
+ # Assert that a point is within a specified distance of another point
20
+ def assert_within_distance(point1, point2, distance, msg = nil)
21
+ actual_distance = point1.distance(point2)
22
+ msg ||= "Distance #{actual_distance} exceeds maximum allowed distance of #{distance}"
23
+ assert actual_distance <= distance, msg
24
+ end
25
+
26
+ # Assert that a geometry contains another geometry
27
+ def assert_contains(container, contained, msg = nil)
28
+ msg ||= "Expected #{container.as_text} to contain #{contained.as_text}"
29
+ assert container.contains?(contained), msg
30
+ end
31
+
32
+ # Assert that a geometry is within another geometry
33
+ def assert_within(inner, outer, msg = nil)
34
+ msg ||= "Expected #{inner.as_text} to be within #{outer.as_text}"
35
+ assert inner.within?(outer), msg
36
+ end
37
+
38
+ # Assert that two geometries intersect
39
+ def assert_intersects(geom1, geom2, msg = nil)
40
+ msg ||= "Expected #{geom1.as_text} to intersect #{geom2.as_text}"
41
+ assert geom1.intersects?(geom2), msg
42
+ end
43
+
44
+ # Assert that two geometries do not intersect
45
+ def assert_disjoint(geom1, geom2, msg = nil)
46
+ msg ||= "Expected #{geom1.as_text} to be disjoint from #{geom2.as_text}"
47
+ assert geom1.disjoint?(geom2), msg
48
+ end
49
+
50
+ # Assert that a geometry has the expected SRID
51
+ def assert_srid(geometry, expected_srid, msg = nil)
52
+ actual_srid = geometry.srid
53
+ msg ||= "Expected SRID #{expected_srid} but got #{actual_srid}"
54
+ assert_equal expected_srid, actual_srid, msg
55
+ end
56
+
57
+ # Chainable spatial column assertion builder
58
+ def assert_spatial_column(geometry, msg_prefix = nil)
59
+ SpatialColumnAssertion.new(geometry, self, msg_prefix)
60
+ end
61
+
62
+ # Assert that a geometry is of the expected type
63
+ def assert_geometry_type(geometry, expected_type, msg = nil)
64
+ actual_type = geometry.geometry_type.type_name.downcase
65
+ expected_type = expected_type.to_s.downcase.gsub("_", "")
66
+ actual_type = actual_type.gsub("_", "")
67
+ msg ||= "Expected geometry type #{expected_type} but got #{actual_type}"
68
+ assert_equal expected_type, actual_type, msg
69
+ end
70
+
71
+ # Legacy methods for backward compatibility
72
+ def assert_has_z(geometry, msg = nil)
73
+ assert_spatial_column(geometry, msg).has_z
74
+ end
75
+
76
+ def assert_has_m(geometry, msg = nil)
77
+ assert_spatial_column(geometry, msg).has_m
78
+ end
79
+
80
+ # Create a point for testing
81
+ def create_point(x, y, srid: 4326, z: nil, m: nil)
82
+ if z || m
83
+ # Use cartesian factory for 3D/4D points
84
+ factory = RGeo::Cartesian.preferred_factory(srid: srid, has_z_coordinate: !!z, has_m_coordinate: !!m)
85
+ if z && m
86
+ factory.point(x, y, z, m)
87
+ elsif z
88
+ factory.point(x, y, z)
89
+ elsif m
90
+ factory.point(x, y, 0, m) # Default Z to 0 for M-only
91
+ else
92
+ factory.point(x, y)
93
+ end
94
+ else
95
+ factory = RGeo::Geographic.simple_mercator_factory(srid: srid)
96
+ factory.point(x, y)
97
+ end
98
+ end
99
+
100
+ # Create a test polygon for testing
101
+ def create_test_polygon(srid: 4326)
102
+ factory = RGeo::Geographic.simple_mercator_factory(srid: srid)
103
+ factory.polygon(
104
+ factory.linear_ring([
105
+ factory.point(0, 0),
106
+ factory.point(0, 1),
107
+ factory.point(1, 1),
108
+ factory.point(1, 0),
109
+ factory.point(0, 0)
110
+ ])
111
+ )
112
+ end
113
+
114
+ # Create a test linestring for testing
115
+ def create_test_linestring(srid: 4326)
116
+ factory = RGeo::Geographic.simple_mercator_factory(srid: srid)
117
+ factory.line_string([
118
+ factory.point(0, 0),
119
+ factory.point(1, 1),
120
+ factory.point(2, 0)
121
+ ])
122
+ end
123
+ end
124
+
125
+ # Chainable spatial column assertion class
126
+ class SpatialColumnAssertion
127
+ def initialize(geometry, test_case, msg_prefix = nil)
128
+ @geometry = geometry
129
+ @test_case = test_case
130
+ @msg_prefix = msg_prefix
131
+ end
132
+
133
+ def has_z
134
+ msg = build_message("to have Z dimension")
135
+ has_z = detect_has_z(@geometry)
136
+ @test_case.assert has_z, msg
137
+ self
138
+ end
139
+
140
+ def has_m
141
+ msg = build_message("to have M dimension")
142
+ has_m = detect_has_m(@geometry)
143
+ @test_case.assert has_m, msg
144
+ self
145
+ end
146
+
147
+ def has_srid(expected_srid)
148
+ msg = build_message("to have SRID #{expected_srid}")
149
+ actual_srid = @geometry.srid
150
+ @test_case.assert_equal expected_srid, actual_srid, msg
151
+ self
152
+ end
153
+
154
+ def is_type(expected_type)
155
+ msg = build_message("to be of type #{expected_type}")
156
+ actual_type = @geometry.geometry_type.type_name.downcase
157
+ expected_type = expected_type.to_s.downcase.gsub("_", "")
158
+ actual_type = actual_type.gsub("_", "")
159
+ @test_case.assert_equal expected_type, actual_type, msg
160
+ self
161
+ end
162
+
163
+ def is_geographic
164
+ msg = build_message("to be geographic")
165
+ # Check if factory is geographic
166
+ is_geo = @geometry.factory.respond_to?(:spherical?) && @geometry.factory.spherical?
167
+ @test_case.assert is_geo, msg
168
+ self
169
+ end
170
+
171
+ def is_cartesian
172
+ msg = build_message("to be cartesian")
173
+ # Check if factory is cartesian
174
+ is_cart = !(@geometry.factory.respond_to?(:spherical?) && @geometry.factory.spherical?)
175
+ @test_case.assert is_cart, msg
176
+ self
177
+ end
178
+
179
+ private
180
+
181
+ def build_message(expectation)
182
+ prefix = @msg_prefix ? "#{@msg_prefix}: " : ""
183
+ "#{prefix}Expected geometry #{expectation}"
184
+ end
185
+
186
+ def detect_has_z(geometry)
187
+ return geometry.has_z_coordinate? if geometry.respond_to?(:has_z_coordinate?)
188
+ return geometry.has_z? if geometry.respond_to?(:has_z?)
189
+ return !geometry.z.nil? if geometry.respond_to?(:z)
190
+ false
191
+ end
192
+
193
+ def detect_has_m(geometry)
194
+ return geometry.has_m_coordinate? if geometry.respond_to?(:has_m_coordinate?)
195
+ return geometry.has_m? if geometry.respond_to?(:has_m?)
196
+ return !geometry.m.nil? if geometry.respond_to?(:m)
197
+ false
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module PostGIS
6
- VERSION = "0.2.0"
6
+ VERSION = "0.3.1"
7
7
  end
8
8
  end
9
9
  end
@@ -142,6 +142,16 @@ module ActiveRecord
142
142
  private
143
143
 
144
144
  def create_spatial_type_from_sql(sql_type)
145
+ # Handle empty sql_type (common in joins) - this is an upstream Rails issue
146
+ # where sql_type comes back as empty string, making it impossible to determine
147
+ # the correct spatial type properties
148
+ if sql_type.nil? || sql_type.empty?
149
+ # Log warning about potential type mismatch due to upstream issue
150
+ # Users experiencing this should use explicit attribute registration as workaround
151
+ # See: https://github.com/rgeo/activerecord-postgis-adapter/pull/334
152
+ return Type::Geometry.new(srid: 0, has_z: false, has_m: false, geographic: false)
153
+ end
154
+
145
155
  # Extract SRID and dimensions from SQL type
146
156
  srid = extract_srid_from_sql(sql_type)
147
157
  # Check for dimension suffixes (e.g., PointZ, PointM, PointZM)
@@ -182,6 +192,7 @@ module ActiveRecord
182
192
  when /geometry/i
183
193
  Type::Geometry.new(srid: srid, has_z: has_z, has_m: has_m)
184
194
  else
195
+ # Fallback for unrecognized types
185
196
  Type::Geometry.new(srid: srid, has_z: has_z, has_m: has_m)
186
197
  end
187
198
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../active_record/connection_adapters/postgis/test_helpers"
4
+
5
+ module ActiveRecordPostgis
6
+ module TestHelper
7
+ include ActiveRecord::ConnectionAdapters::PostGIS::TestHelpers
8
+
9
+ # Additional convenience methods for PostGIS testing
10
+ def factory(srid: 4326, geographic: false)
11
+ if geographic
12
+ RGeo::Geographic.spherical_factory(srid: srid)
13
+ else
14
+ RGeo::Cartesian.preferred_factory(srid: srid)
15
+ end
16
+ end
17
+
18
+ def geographic_factory(srid: 4326)
19
+ RGeo::Geographic.spherical_factory(srid: srid)
20
+ end
21
+
22
+ def cartesian_factory(srid: 0)
23
+ RGeo::Cartesian.preferred_factory(srid: srid)
24
+ end
25
+
26
+ def spatial_factory_store
27
+ RGeo::ActiveRecord::SpatialFactoryStore.instance
28
+ end
29
+
30
+ def reset_spatial_store
31
+ spatial_factory_store.clear
32
+ spatial_factory_store.default = nil
33
+ end
34
+
35
+ # Create a test table with spatial columns
36
+ def create_spatial_table(table_name, connection = ActiveRecord::Base.connection)
37
+ connection.create_table table_name, force: true do |t|
38
+ t.st_point :coordinates, srid: 4326
39
+ t.st_point :location, srid: 4326, geographic: true
40
+ t.st_polygon :boundary, srid: 4326
41
+ t.st_line_string :path, srid: 4326
42
+ t.timestamps
43
+ end
44
+ end
45
+
46
+ # Clean up spatial tables after tests
47
+ def drop_spatial_table(table_name, connection = ActiveRecord::Base.connection)
48
+ connection.drop_table table_name if connection.table_exists?(table_name)
49
+ end
50
+ end
51
+ end
@@ -16,6 +16,33 @@ module Arel
16
16
  class SpatialLength < Unary; end
17
17
  class SpatialContains < SpatialNode; end
18
18
  class SpatialWithin < SpatialNode; end
19
+ class SpatialIntersects < SpatialNode; end
20
+ class SpatialDWithin < SpatialNode
21
+ attr_reader :distance
22
+
23
+ def initialize(left, right, distance)
24
+ super(left, right)
25
+ @distance = distance
26
+ end
27
+ end
28
+ class SpatialBuffer < SpatialNode
29
+ def st_area
30
+ SpatialArea.new(self)
31
+ end
32
+ end
33
+ class SpatialTransform < SpatialNode
34
+ def st_area
35
+ SpatialArea.new(self)
36
+ end
37
+ end
38
+ class SpatialArea < Unary; end
39
+
40
+ # K-Nearest Neighbor distance operator
41
+ class SpatialDistanceOperator < Binary
42
+ def initialize(left, right)
43
+ super
44
+ end
45
+ end
19
46
 
20
47
  # Wrapper for spatial values that need special handling
21
48
  class SpatialValue < Node
@@ -36,6 +63,32 @@ module Arel
36
63
  def st_within(other)
37
64
  SpatialWithin.new(self, other)
38
65
  end
66
+
67
+ def st_intersects(other)
68
+ SpatialIntersects.new(self, other)
69
+ end
70
+
71
+ def st_dwithin(other, distance)
72
+ SpatialDWithin.new(self, other, distance)
73
+ end
74
+
75
+ def st_buffer(distance)
76
+ SpatialBuffer.new(self, distance)
77
+ end
78
+
79
+ def st_transform(srid)
80
+ SpatialTransform.new(self, srid)
81
+ end
82
+
83
+ def st_area
84
+ SpatialArea.new(self)
85
+ end
86
+
87
+ def distance_operator(other)
88
+ SpatialDistanceOperator.new(self, other)
89
+ end
90
+
91
+ alias :'<->' :distance_operator
39
92
  end
40
93
  end
41
94
 
@@ -56,6 +109,32 @@ module Arel
56
109
  def st_within(other)
57
110
  Arel::Nodes::SpatialWithin.new(self, other)
58
111
  end
112
+
113
+ def st_intersects(other)
114
+ Arel::Nodes::SpatialIntersects.new(self, other)
115
+ end
116
+
117
+ def st_dwithin(other, distance)
118
+ Arel::Nodes::SpatialDWithin.new(self, other, distance)
119
+ end
120
+
121
+ def st_buffer(distance)
122
+ Arel::Nodes::SpatialBuffer.new(self, distance)
123
+ end
124
+
125
+ def st_transform(srid)
126
+ Arel::Nodes::SpatialTransform.new(self, srid)
127
+ end
128
+
129
+ def st_area
130
+ Arel::Nodes::SpatialArea.new(self)
131
+ end
132
+
133
+ def distance_operator(other)
134
+ Arel::Nodes::SpatialDistanceOperator.new(self, other)
135
+ end
136
+
137
+ alias :'<->' :distance_operator
59
138
  end
60
139
  end
61
140
 
@@ -112,6 +191,52 @@ module Arel
112
191
  visit_spatial_operand(node.value, collector)
113
192
  end
114
193
 
194
+ def visit_Arel_Nodes_SpatialIntersects(node, collector)
195
+ collector << "ST_Intersects("
196
+ visit(node.left, collector)
197
+ collector << ", "
198
+ visit_spatial_operand(node.right, collector)
199
+ collector << ")"
200
+ end
201
+
202
+ def visit_Arel_Nodes_SpatialDWithin(node, collector)
203
+ collector << "ST_DWithin("
204
+ visit(node.left, collector)
205
+ collector << ", "
206
+ visit_spatial_operand(node.right, collector)
207
+ collector << ", "
208
+ collector << node.distance.to_s
209
+ collector << ")"
210
+ end
211
+
212
+ def visit_Arel_Nodes_SpatialBuffer(node, collector)
213
+ collector << "ST_Buffer("
214
+ visit(node.left, collector)
215
+ collector << ", "
216
+ collector << node.right.to_s
217
+ collector << ")"
218
+ end
219
+
220
+ def visit_Arel_Nodes_SpatialTransform(node, collector)
221
+ collector << "ST_Transform("
222
+ visit(node.left, collector)
223
+ collector << ", "
224
+ collector << node.right.to_s
225
+ collector << ")"
226
+ end
227
+
228
+ def visit_Arel_Nodes_SpatialArea(node, collector)
229
+ collector << "ST_Area("
230
+ visit(node.expr, collector)
231
+ collector << ")"
232
+ end
233
+
234
+ def visit_Arel_Nodes_SpatialDistanceOperator(node, collector)
235
+ visit(node.left, collector)
236
+ collector << " <-> "
237
+ visit_spatial_operand(node.right, collector)
238
+ end
239
+
115
240
  private
116
241
 
117
242
  def visit_spatial_operand(operand, collector)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-postgis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -80,6 +80,7 @@ files:
80
80
  - lib/active_record/connection_adapters/postgis/spatial_column_methods.rb
81
81
  - lib/active_record/connection_adapters/postgis/spatial_column_type.rb
82
82
  - lib/active_record/connection_adapters/postgis/table_definition.rb
83
+ - lib/active_record/connection_adapters/postgis/test_helpers.rb
83
84
  - lib/active_record/connection_adapters/postgis/type/geography.rb
84
85
  - lib/active_record/connection_adapters/postgis/type/geometry.rb
85
86
  - lib/active_record/connection_adapters/postgis/type/geometry_collection.rb
@@ -92,6 +93,7 @@ files:
92
93
  - lib/active_record/connection_adapters/postgis/type/spatial.rb
93
94
  - lib/active_record/connection_adapters/postgis/version.rb
94
95
  - lib/activerecord-postgis.rb
96
+ - lib/activerecord-postgis/test_helper.rb
95
97
  - lib/arel/visitors/postgis.rb
96
98
  homepage: https://github.com/seuros/activerecord-postgis
97
99
  licenses:
@@ -114,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
116
  - !ruby/object:Gem::Version
115
117
  version: '0'
116
118
  requirements: []
117
- rubygems_version: 3.6.7
119
+ rubygems_version: 3.6.9
118
120
  specification_version: 4
119
121
  summary: PostGIS Type support for ActiveRecord
120
122
  test_files: []