activerecord-postgis 0.3.1 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 348c302fed46b25f5815a94a08f24882b3c9ce9a4f29907ba5e3fcd6570dbf27
4
- data.tar.gz: dd1a7f7de5e9a71178a2e98981221861824336c20861fce51a2da6d6b6eeceac
3
+ metadata.gz: b80f70c84d88643edef39e5d702779a4248b05c03ed692c09139bad00f7f5009
4
+ data.tar.gz: 9f923717999ac80231119e353356f8f9570c04e17ef560e84aa0656132ec0618
5
5
  SHA512:
6
- metadata.gz: '08ce6f86d903016208598a2c70e8da83d59fbf9b11afd8f7db0b4b1a8cb953599c43ce102ac3b84938a6b934e82af0791d2641ba453c2a17a6b75c15ad85ca13'
7
- data.tar.gz: 6d529987e15c03978f48e346cb174f900d95493e6b1c1f2a1e417c71005ff2e7d75e1e3ee76099fcabd03b7705f28c60b04d983e609e6969929329f5d7d7a494
6
+ metadata.gz: aff8a1b78d4b16f498c013652b5fd0d1b9b268fe9292197e7d799e12c8cfa562be6f6ba9a118fd3bab8cef19f028439db1befa6da02528e81c7aede7669560d6
7
+ data.tar.gz: 8cb2c7bfe70dfdab95a8c8d535f5b7397be7b03978808b01a3169979cc6c417eae1f4a9c8e24043b5196aa5bfb678bd50c1778393ed40429f10b58d82f3666fd
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.files = Dir.glob('{lib}/**/*') + [ gemspec, 'LICENSE.txt', 'README.md' ]
23
23
  spec.require_paths = [ 'lib' ]
24
24
 
25
- spec.add_dependency 'activerecord', '>= 8.0', '< 8.1'
25
+ spec.add_dependency 'activerecord', '>= 8.1.0', '< 8.2'
26
26
  spec.add_dependency 'pg'
27
27
  spec.add_dependency 'rgeo-activerecord', '>= 8.0'
28
28
  end
@@ -19,6 +19,7 @@ module ActiveRecord
19
19
 
20
20
  PostgreSQL::Column.new(
21
21
  column_name,
22
+ type_metadata.type,
22
23
  default_value,
23
24
  type_metadata,
24
25
  field["is_nullable"] == "YES",
@@ -18,13 +18,15 @@ module ActiveRecord
18
18
  end
19
19
 
20
20
  def type_cast(value)
21
- if value.is_a?(RGeo::Feature::Instance)
22
- # Convert spatial objects to EWKT string for parameter binding
23
- if value.srid && value.srid != 0
24
- "SRID=#{value.srid};#{value.as_text}"
25
- else
26
- value.as_text
27
- end
21
+ if RGeo::Feature::Geometry.check_type(value)
22
+ # Use EWKB format to preserve SRID information
23
+ RGeo::WKRep::WKBGenerator.new(
24
+ hex_format: true,
25
+ type_format: :ewkb,
26
+ emit_ewkb_srid: true
27
+ ).generate(value)
28
+ elsif value.is_a?(RGeo::Cartesian::BoundingBox)
29
+ value.to_s
28
30
  else
29
31
  super
30
32
  end
@@ -91,6 +91,7 @@ module ActiveRecord
91
91
 
92
92
  PostgreSQL::Column.new(
93
93
  field["column_name"],
94
+ type_metadata.type,
94
95
  default_value,
95
96
  type_metadata,
96
97
  field["is_nullable"] == "YES",
@@ -120,10 +120,15 @@ module ActiveRecord
120
120
  return base_type if @type == "geography"
121
121
 
122
122
  type_with_dimensions = build_type_with_dimensions
123
- # Include SRID if specified and not the default for the type
124
- # Geography defaults to 4326, geometry defaults to 0
125
- should_include_srid = @srid &&
126
- ((@geography && @srid != 4326) || (!@geography && @srid != 0))
123
+ # Include SRID if specified and not the default for the column type
124
+ # Geography columns: only include SRID if it's not the default 4326
125
+ # Geometry columns: always include SRID when specified and not 0
126
+ if @geography
127
+ should_include_srid = @srid && @srid != 4326
128
+ else
129
+ # For geometry columns, always include SRID when explicitly specified and not 0
130
+ should_include_srid = @srid && @srid != 0
131
+ end
127
132
 
128
133
  if should_include_srid
129
134
  "#{base_type}(#{type_with_dimensions},#{@srid})"
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostGIS
6
+ module SpatialQueries
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ # Safe wrapper for ST_Distance queries
11
+ # Usage: Model.where_st_distance(:column, lon, lat, '<', distance)
12
+ # For geographic calculations (meters), cast to geography
13
+ def where_st_distance(column, lon, lat, operator, distance, srid: 4326, geographic: false)
14
+ if geographic
15
+ where("ST_Distance(#{column}::geography, ST_SetSRID(ST_MakePoint(?, ?), ?)::geography) #{operator} ?", lon, lat, srid, distance)
16
+ else
17
+ where("ST_Distance(#{column}, ST_SetSRID(ST_MakePoint(?, ?), ?)) #{operator} ?", lon, lat, srid, distance)
18
+ end
19
+ end
20
+
21
+ # Safe wrapper for ST_DWithin queries
22
+ # Usage: Model.where_st_dwithin(:column, lon, lat, distance)
23
+ # For geographic calculations (meters), cast to geography
24
+ def where_st_dwithin(column, lon, lat, distance, srid: 4326, geographic: false)
25
+ if geographic
26
+ where("ST_DWithin(#{column}::geography, ST_SetSRID(ST_MakePoint(?, ?), ?)::geography, ?)", lon, lat, srid, distance)
27
+ else
28
+ where("ST_DWithin(#{column}, ST_SetSRID(ST_MakePoint(?, ?), ?), ?)", lon, lat, srid, distance)
29
+ end
30
+ end
31
+
32
+ # Safe wrapper for ST_Contains with point
33
+ # Usage: Model.where_st_contains(:column, lon, lat)
34
+ def where_st_contains(column, lon, lat, srid: 4326)
35
+ where("ST_Contains(#{column}, ST_SetSRID(ST_MakePoint(?, ?), ?))", lon, lat, srid)
36
+ end
37
+
38
+ # Safe wrapper for ST_Within with point
39
+ # Usage: Model.where_st_within_point(:column, lon, lat)
40
+ def where_st_within_point(column, lon, lat, srid: 4326)
41
+ where("ST_Within(ST_SetSRID(ST_MakePoint(?, ?), ?), #{column})", lon, lat, srid)
42
+ end
43
+
44
+ # Safe wrapper for ST_Intersects with WKT geometry
45
+ # Usage: Model.where_st_intersects(:column, wkt_string)
46
+ def where_st_intersects(column, wkt, srid: 4326)
47
+ where("ST_Intersects(#{column}, ST_GeomFromText(?, ?))", wkt, srid)
48
+ end
49
+
50
+ # Generic safe wrapper for any PostGIS function with a point parameter
51
+ # Usage: Model.where_st_function('ST_Distance', :column, lon, lat, '<', value)
52
+ def where_st_function(function, column, lon, lat, operator = nil, value = nil, srid: 4326)
53
+ if operator && value
54
+ where("#{function}(#{column}, ST_SetSRID(ST_MakePoint(?, ?), ?)) #{operator} ?", lon, lat, srid, value)
55
+ else
56
+ where("#{function}(#{column}, ST_SetSRID(ST_MakePoint(?, ?), ?))", lon, lat, srid)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ # Module to include in models for instance methods
63
+ module SpatialScopes
64
+ extend ActiveSupport::Concern
65
+
66
+ included do
67
+ # Define commonly used spatial scopes
68
+ scope :within_distance, ->(column, lon, lat, distance, srid: 4326, geographic: false) {
69
+ where_st_distance(column, lon, lat, "<", distance, srid: srid, geographic: geographic)
70
+ }
71
+
72
+ scope :beyond_distance, ->(column, lon, lat, distance, srid: 4326, geographic: false) {
73
+ where_st_distance(column, lon, lat, ">", distance, srid: srid, geographic: geographic)
74
+ }
75
+
76
+ scope :near, ->(column, lon, lat, distance, srid: 4326, geographic: false) {
77
+ where_st_dwithin(column, lon, lat, distance, srid: srid, geographic: geographic)
78
+ }
79
+
80
+ scope :containing_point, ->(column, lon, lat, srid: 4326) {
81
+ where_st_contains(column, lon, lat, srid: srid)
82
+ }
83
+
84
+ scope :intersecting, ->(column, wkt, srid: 4326) {
85
+ where_st_intersects(column, wkt, srid: srid)
86
+ }
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ # Automatically include in ActiveRecord::Base
94
+ ActiveSupport.on_load(:active_record) do
95
+ ActiveRecord::Base.include(ActiveRecord::ConnectionAdapters::PostGIS::SpatialQueries)
96
+ end
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module PostGIS
6
- VERSION = "0.3.1"
6
+ VERSION = "0.5.0"
7
7
  end
8
8
  end
9
9
  end
@@ -19,6 +19,7 @@ require_relative "postgis/spatial_column_type"
19
19
  require_relative "postgis/adapter_extensions"
20
20
  require_relative "postgis/column_extensions"
21
21
  require_relative "postgis/quoting"
22
+ require_relative "postgis/spatial_queries"
22
23
 
23
24
  module ActiveRecord
24
25
  module ConnectionAdapters
@@ -19,7 +19,7 @@ module Arel
19
19
  class SpatialIntersects < SpatialNode; end
20
20
  class SpatialDWithin < SpatialNode
21
21
  attr_reader :distance
22
-
22
+
23
23
  def initialize(left, right, distance)
24
24
  super(left, right)
25
25
  @distance = distance
@@ -36,7 +36,7 @@ module Arel
36
36
  end
37
37
  end
38
38
  class SpatialArea < Unary; end
39
-
39
+
40
40
  # K-Nearest Neighbor distance operator
41
41
  class SpatialDistanceOperator < Binary
42
42
  def initialize(left, right)
@@ -83,11 +83,11 @@ module Arel
83
83
  def st_area
84
84
  SpatialArea.new(self)
85
85
  end
86
-
86
+
87
87
  def distance_operator(other)
88
88
  SpatialDistanceOperator.new(self, other)
89
89
  end
90
-
90
+
91
91
  alias :'<->' :distance_operator
92
92
  end
93
93
  end
@@ -129,11 +129,11 @@ module Arel
129
129
  def st_area
130
130
  Arel::Nodes::SpatialArea.new(self)
131
131
  end
132
-
132
+
133
133
  def distance_operator(other)
134
134
  Arel::Nodes::SpatialDistanceOperator.new(self, other)
135
135
  end
136
-
136
+
137
137
  alias :'<->' :distance_operator
138
138
  end
139
139
  end
@@ -143,6 +143,26 @@ module Arel
143
143
  Arel::Nodes::SpatialValue.new(value)
144
144
  end
145
145
 
146
+ # Add Arel.st_make_point() method that properly handles floats for Rails 8.1
147
+ def self.st_make_point(x, y, srid = nil)
148
+ # Wrap floats in SqlLiteral nodes to avoid Rails 8.1 Arel strictness
149
+ x_node = x.is_a?(Numeric) ? Arel::Nodes::SqlLiteral.new(x.to_s) : x
150
+ y_node = y.is_a?(Numeric) ? Arel::Nodes::SqlLiteral.new(y.to_s) : y
151
+
152
+ if srid
153
+ srid_node = srid.is_a?(Numeric) ? Arel::Nodes::SqlLiteral.new(srid.to_s) : srid
154
+ Arel::Nodes::NamedFunction.new(
155
+ "ST_SetSRID",
156
+ [
157
+ Arel::Nodes::NamedFunction.new("ST_MakePoint", [ x_node, y_node ]),
158
+ srid_node
159
+ ]
160
+ )
161
+ else
162
+ Arel::Nodes::NamedFunction.new("ST_MakePoint", [ x_node, y_node ])
163
+ end
164
+ end
165
+
146
166
  module Visitors
147
167
  class PostGIS < PostgreSQL
148
168
  include RGeo::ActiveRecord::SpatialToSql
@@ -230,7 +250,7 @@ module Arel
230
250
  visit(node.expr, collector)
231
251
  collector << ")"
232
252
  end
233
-
253
+
234
254
  def visit_Arel_Nodes_SpatialDistanceOperator(node, collector)
235
255
  visit(node.left, collector)
236
256
  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.3.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -15,20 +15,20 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '8.0'
18
+ version: 8.1.0
19
19
  - - "<"
20
20
  - !ruby/object:Gem::Version
21
- version: '8.1'
21
+ version: '8.2'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: '8.0'
28
+ version: 8.1.0
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
- version: '8.1'
31
+ version: '8.2'
32
32
  - !ruby/object:Gem::Dependency
33
33
  name: pg
34
34
  requirement: !ruby/object:Gem::Requirement
@@ -79,6 +79,7 @@ files:
79
79
  - lib/active_record/connection_adapters/postgis/schema_statements.rb
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
+ - lib/active_record/connection_adapters/postgis/spatial_queries.rb
82
83
  - lib/active_record/connection_adapters/postgis/table_definition.rb
83
84
  - lib/active_record/connection_adapters/postgis/test_helpers.rb
84
85
  - lib/active_record/connection_adapters/postgis/type/geography.rb