activerecord-trilogis-adapter 8.1.1 → 8.1.3

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: 30266da053b3f001b74a6f0fcbcfffe26b11a0205f1e1a2164584f53b995bc21
4
- data.tar.gz: ea5ca2afb3df8e11d9697cecb5e1ef5e06209158d1db3178c111c1a3f3aafb9d
3
+ metadata.gz: cff233bec60dde0ec31a359eb0c3725de326e2bff707a9e8e20e7a59e96d2711
4
+ data.tar.gz: 32778c148a38f3a15bb6e32f64d6e89f734584fb2c9aea9756e213dea1c6ae68
5
5
  SHA512:
6
- metadata.gz: 3cd09501502ce9bd8dd42ddbee0e27c88f4285a6c6d9df63cef047e31f1bfa8e6e89aee123c58190b20e089c26a0c5122ff841258d1a6d4b35fec58363bfb523
7
- data.tar.gz: ef2a9ed811ce28e493d41a5fcd5bebb8ae7b9a4f34400b80ac80e02cf1f6149d288de532e2133a84a93d21aa757704752f0d5a9cfe125624e399f3d617a38db7
6
+ metadata.gz: 9382cfbc137be0434e049c40a95d1dce30de00d8008e2f7eff81c18e0d2367bac4531b017f79e53127545abaa3667f298c55df5f40e115f9999dc88442dec0ec
7
+ data.tar.gz: 8504eaf6bb77294fc427f77556b2bf1291d2a81373c95238e6b8a14974614c329d51d9c4bed49e92fa436d283ce8873b03bc8bbea16b213e2f1c3de9c2c8907b
@@ -48,48 +48,36 @@ module Arel
48
48
  end
49
49
  end
50
50
 
51
- def visit_spatial_value(node, collector)
52
- case node
53
- when RGeo::Feature::Instance
54
- visit_RGeo_Feature_Instance(node, collector)
55
- when String
56
- if node.match?(/^[A-Z]/) # WKT string
57
- visit_wkt_string(node, collector)
58
- else
59
- super
60
- end
61
- else
62
- super
63
- end
64
- end
65
-
66
51
  def visit_RGeo_Feature_Instance(node, collector)
67
52
  srid = node.srid || 0
68
53
  wkt = node.as_text
69
54
 
70
- # MySQL ST_GeomFromText supports axis-order option for geographic SRIDs
71
- # This ensures longitude-latitude order for SRID 4326 (WGS84)
72
- if srid == 4326
73
- collector << "ST_GeomFromText('#{wkt}', #{srid}, #{ActiveRecord::ConnectionAdapters::TrilogisAdapter::AXIS_ORDER_LONG_LAT})"
55
+ append_geometry(wkt, srid, collector)
56
+ end
57
+
58
+ def visit_wkt_string(wkt, collector)
59
+ if (match = /\ASRID=(\d+);(.*)\z/im.match(wkt))
60
+ append_geometry(match[2], match[1].to_i, collector)
74
61
  else
75
- collector << "ST_GeomFromText('#{wkt}', #{srid})"
62
+ append_geometry(wkt, 0, collector)
76
63
  end
77
64
  end
78
65
 
79
- def visit_wkt_string(wkt, collector)
80
- # Extract SRID if present in EWKT format
81
- if wkt =~ /^SRID=(\d+);(.+)$/i
82
- srid = Regexp.last_match(1).to_i
83
- clean_wkt = Regexp.last_match(2)
84
- # Use axis-order for geographic SRID
85
- if srid == 4326
86
- collector << "ST_GeomFromText('#{clean_wkt}', #{srid}, #{ActiveRecord::ConnectionAdapters::TrilogisAdapter::AXIS_ORDER_LONG_LAT})"
66
+ def append_geometry(wkt, srid, collector)
67
+ collector << "ST_GeomFromText(#{quote(wkt)}, #{srid}, " \
68
+ "#{ActiveRecord::ConnectionAdapters::TrilogisAdapter::AXIS_ORDER_LONG_LAT})"
69
+ end
70
+
71
+ def visit_RGeo_ActiveRecord_SpatialNamedFunction(node, collector)
72
+ node = node.dup
73
+ node.expressions = node.expressions.each_with_index.map do |argument, index|
74
+ if node.spatial_argument?(index) || argument.is_a?(RGeo::ActiveRecord::SpatialConstantNode)
75
+ argument
87
76
  else
88
- collector << "ST_GeomFromText('#{clean_wkt}', #{srid})"
77
+ Arel::Nodes.build_quoted(argument)
89
78
  end
90
- else
91
- collector << "ST_GeomFromText('#{wkt}', 0)"
92
79
  end
80
+ super
93
81
  end
94
82
 
95
83
  # Handle spatial function calls
@@ -140,15 +128,6 @@ module Arel
140
128
  collector << quote(value)
141
129
  end
142
130
  end
143
-
144
- # Support for spatial predicates in WHERE clauses
145
- def visit_spatial_predicate(predicate_name, left, right, collector)
146
- collector << "#{SPATIAL_FUNCTIONS[predicate_name]}("
147
- visit(left, collector)
148
- collector << ", "
149
- visit(right, collector)
150
- collector << ")"
151
- end
152
131
  end
153
132
  end
154
133
  end
@@ -7,18 +7,7 @@ module ActiveRecord
7
7
  private
8
8
 
9
9
  def add_column_options!(sql, options)
10
- # Add SRID option for spatial columns in MySQL 8.0+
11
- # Format: /*!80003 SRID #{srid} */
12
- if options[:srid]
13
- sql_result = "#{sql} /*!80003 SRID #{options[:srid]} */"
14
- sql.replace(sql_result)
15
-
16
- # MySQL does not support DEFAULT values for spatial columns
17
- # Remove :default option before calling super to prevent SQL errors
18
- options = options.dup
19
- options.delete(:default)
20
- end
21
-
10
+ sql << " SRID #{Integer(options[:srid])}" if options[:srid]
22
11
  super
23
12
  end
24
13
  end
@@ -5,215 +5,59 @@ module ActiveRecord
5
5
  module Trilogis
6
6
  module SchemaStatements
7
7
  def type_to_sql(type, limit: nil, precision: nil, scale: nil, **)
8
- if spatial_type?(type.to_s)
9
- # If limit[:type] is specified, use it as the geometry type (e.g., "point")
10
- # Otherwise use the base type (e.g., "geometry")
11
- base_type = if limit.is_a?(Hash) && limit[:type]
12
- limit[:type]
13
- else
14
- type
15
- end
16
- sql_type = spatial_sql_type(base_type, nil)
17
- sql_type = "#{sql_type} SRID #{limit[:srid]}" if limit.is_a?(Hash) && limit[:srid]
18
- sql_type
19
- else
20
- super
21
- end
8
+ return super unless (spatial_type = TrilogisAdapter.spatial_type(type))
9
+
10
+ spatial_type = TrilogisAdapter.spatial_type(limit[:type]) if limit.is_a?(Hash) && limit[:type]
11
+ sql = spatial_type.upcase
12
+ sql += " SRID #{Integer(limit[:srid])}" if limit.is_a?(Hash) && limit[:srid]
13
+ sql
22
14
  end
23
15
 
24
16
  def add_index(table_name, column_name, **options)
25
- index_type = options[:type]
26
-
27
- # Handle spatial indexes - MySQL uses SPATIAL keyword, not USING
28
- if index_type == :spatial
29
- options = options.dup
30
- options.delete(:using) # Remove any USING clause for spatial indexes
31
- options[:type] = :spatial
32
- end
33
-
17
+ options.delete(:using) if options[:type] == :spatial
34
18
  super
35
19
  end
36
20
 
37
21
  def indexes(table_name)
38
- indexes = super
39
-
40
- # MySQL doesn't support prefix lengths for spatial indexes
41
- indexes.each do |index|
42
- if index.using == :gist || index.comment&.include?("spatial") || index.type == :spatial
43
- index.instance_variable_set(:@lengths, {})
44
- end
22
+ super.each do |index|
23
+ # MySQL reports a prefix length for SPATIAL keys that cannot be reloaded.
24
+ index.instance_variable_set(:@lengths, {}) if index.type == :spatial
45
25
  end
46
-
47
- indexes
48
26
  end
49
27
 
50
- # Override columns to use parent's implementation but enhance spatial columns
51
- # DO NOT override - let parent class handle all column creation
28
+ def columns(table_name)
29
+ previous_metadata = @spatial_column_metadata
30
+ @spatial_column_metadata = nil
31
+ super
32
+ ensure
33
+ @spatial_column_metadata = previous_metadata
34
+ end
52
35
 
53
- # Override to properly handle spatial columns creation
54
36
  def new_column_from_field(table_name, field, definitions = nil)
55
- field_name = extract_field_value(field, :Field, :field)
56
- sql_type = extract_field_value(field, :Type, :type)
37
+ return super unless (spatial_type = TrilogisAdapter.spatial_type(field["Type"]))
57
38
 
58
- if spatial_type?(sql_type)
59
- build_spatial_column(table_name, field, field_name, sql_type)
60
- else
61
- super
62
- end
39
+ @spatial_column_metadata ||= SpatialColumnInfo.new(self, table_name).all
40
+ info = @spatial_column_metadata[field["Field"]]
41
+ cast_type = Type::Spatial.new(info ? "geometry(#{info[:type]},#{info[:srid] || 0})" : spatial_type).freeze
42
+ SpatialColumn.new(
43
+ field["Field"], cast_type,
44
+ nil, fetch_type_metadata(field["Type"]), field["Null"] == "YES", nil,
45
+ collation: field["Collation"], comment: field["Comment"].presence, spatial_info: info
46
+ )
63
47
  end
64
48
 
65
49
  def create_table_definition(name, **)
66
50
  Trilogis::TableDefinition.new(self, name, **)
67
51
  end
68
52
 
69
- def create_table(table_name, **options, &)
70
- # Clear spatial cache when creating table with force: true
71
- # This ensures we don't have stale cache from a previously dropped table
72
- clear_spatial_cache_for(table_name) if options[:force]
73
- super
74
- end
75
-
76
- def add_column(table_name, column_name, type, **options)
77
- if spatial_type?(type.to_s)
78
- # Build ALTER TABLE statement for spatial column
79
- sql_type = spatial_sql_type(type, options[:type])
80
- base_sql = "ALTER TABLE #{quote_table_name(table_name)} " \
81
- "ADD #{quote_column_name(column_name)} #{sql_type}"
82
- sql_parts = [base_sql]
83
-
84
- # Add SRID if specified
85
- sql_parts << " SRID #{options[:srid]}" if options[:srid] && options[:srid] != 0
86
-
87
- # Add NULL constraint
88
- sql_parts << " NOT NULL" if options[:null] == false
89
-
90
- # MySQL does not support DEFAULT values for spatial/geometry columns
91
- # Silently ignore :default option to prevent SQL syntax errors
92
- # Users should handle defaults at application level instead
93
-
94
- execute sql_parts.join
95
-
96
- # Clear memoized spatial column info for this table
97
- clear_spatial_cache_for(table_name)
98
- else
99
- super
53
+ def build_change_column_definition(table_name, column_name, type, **options)
54
+ column = column_for(table_name, column_name)
55
+ if column.spatial? && (type.nil? || TrilogisAdapter.spatial_type(type))
56
+ type ||= column.type
57
+ options[:srid] = column.srid_constraint unless options.key?(:srid) || options[:limit].is_a?(Hash)
100
58
  end
101
- end
102
-
103
- def drop_table(table_name, **options)
104
- # Clear memoized spatial column info for this table before dropping
105
- clear_spatial_cache_for(table_name)
106
59
  super
107
60
  end
108
-
109
- def rename_table(table_name, new_name)
110
- # Clear cache for both old and new table names
111
- clear_spatial_cache_for(table_name)
112
- clear_spatial_cache_for(new_name)
113
- super
114
- end
115
-
116
- # Clear all spatial column caches (useful for tests)
117
- def clear_spatial_cache!
118
- @spatial_column_info = {}
119
- end
120
-
121
- def spatial_sql_type(base_type, subtype = nil)
122
- sql_type = base_type.to_s.delete("_").upcase
123
- subtype_sql = subtype.to_s
124
- if subtype_sql.empty?
125
- sql_type
126
- else
127
- "#{sql_type}(#{subtype_sql.delete('_').upcase})"
128
- end
129
- end
130
-
131
- private
132
-
133
- def spatial_type?(type)
134
- TrilogisAdapter::SPATIAL_COLUMN_TYPES.include?(type.to_s.downcase)
135
- end
136
-
137
- def schema_creation
138
- SchemaCreation.new(self)
139
- end
140
-
141
- # Memoized spatial column info per table
142
- def spatial_column_info(table_name)
143
- @spatial_column_info ||= {}
144
- @spatial_column_info[table_name.to_sym] ||= SpatialColumnInfo.new(self, table_name.to_s)
145
- end
146
-
147
- # Clear spatial cache for a specific table
148
- def clear_spatial_cache_for(table_name)
149
- @spatial_column_info&.delete(table_name.to_sym)
150
- @spatial_column_info&.delete(table_name.to_s)
151
- end
152
-
153
- # Extract field value with case-insensitive key lookup
154
- def extract_field_value(field, *keys)
155
- keys.each do |key|
156
- return field[key] if field.key?(key)
157
- return field[key.to_s] if field.key?(key.to_s)
158
- end
159
- nil
160
- end
161
-
162
- # Build a spatial column from field metadata
163
- def build_spatial_column(table_name, field, field_name, sql_type)
164
- spatial_info = spatial_column_info(table_name).get(field_name, sql_type)
165
-
166
- # Keep original sql_type in metadata (point, linestring, etc.)
167
- # This preserves the actual geometric type for @geo_type_name
168
- type_metadata = fetch_type_metadata(sql_type)
169
-
170
- # Lookup cast type as "geometry" for schema dumper compatibility
171
- # Schema dumper validates cast_type, and only "geometry" is registered as table method
172
- # Actual type is preserved in sql_type_metadata and limit hash
173
- cast_type = lookup_cast_type("geometry")
174
-
175
- SpatialColumn.new(
176
- field_name,
177
- cast_type,
178
- nil, # MySQL spatial columns cannot have DEFAULT values
179
- type_metadata,
180
- extract_field_value(field, :Null, :null) == "YES",
181
- nil, # MySQL spatial columns cannot have DEFAULT functions
182
- collation: extract_field_value(field, :Collation, :collation),
183
- comment: extract_field_value(field, :Comment, :comment).presence,
184
- spatial_info: spatial_info
185
- )
186
- end
187
- end
188
-
189
- class SchemaCreation < ActiveRecord::ConnectionAdapters::MySQL::SchemaCreation
190
- private
191
-
192
- def visit_ColumnDefinition(o)
193
- if spatial_column?(o)
194
- sql_type = spatial_sql_type(o.sql_type, o.options[:type])
195
- column_sql_parts = ["#{quote_column_name(o.name)} #{sql_type}"]
196
-
197
- # Add SRID if specified (MySQL 8.0+ syntax: COLUMN TYPE SRID value)
198
- column_sql_parts << " SRID #{o.options[:srid]}" if o.options[:srid] && o.options[:srid] != 0
199
-
200
- column_sql_parts << " NOT NULL" unless o.null
201
-
202
- # MySQL does not support DEFAULT values for spatial/geometry columns
203
- # Silently ignore default to prevent SQL syntax errors
204
-
205
- column_sql_parts.join
206
- else
207
- super
208
- end
209
- end
210
-
211
- def spatial_column?(column)
212
- # Check both with and without underscores
213
- sql_type = column.sql_type.to_s.downcase
214
- TrilogisAdapter::SPATIAL_COLUMN_TYPES.include?(sql_type) ||
215
- TrilogisAdapter::SPATIAL_COLUMN_TYPES.include?(sql_type.delete("_"))
216
- end
217
61
  end
218
62
  end
219
63
  end
@@ -13,7 +13,7 @@ module ActiveRecord
13
13
 
14
14
  module Trilogis
15
15
  class SpatialColumn < ActiveRecord::ConnectionAdapters::MySQL::Column
16
- attr_reader :geometric_type, :srid, :geo_type_name
16
+ attr_reader :geometric_type, :srid, :geo_type_name, :srid_constraint
17
17
 
18
18
  # Map SQL type strings to RGeo type classes
19
19
  GEOMETRIC_TYPES = {
@@ -39,12 +39,13 @@ module ActiveRecord
39
39
  # Use spatial info from INFORMATION_SCHEMA if available
40
40
  @geo_type_name = spatial_info[:type].to_s.downcase
41
41
  @geometric_type = GEOMETRIC_TYPES[@geo_type_name] || RGeo::Feature::Geometry
42
- @srid = spatial_info[:srid] || 0
42
+ @srid_constraint = spatial_info[:srid]
43
+ @srid = @srid_constraint || 0
43
44
  @has_z = spatial_info[:has_z] || false
44
45
  @has_m = spatial_info[:has_m] || false
45
46
  else
46
47
  # Fallback to extracting from SQL type
47
- @geo_type_name = sql_type_metadata.sql_type.to_s.downcase
48
+ @geo_type_name = TrilogisAdapter.spatial_type(sql_type_metadata.sql_type)
48
49
  type_info = Type::Spatial.new(@geo_type_name)
49
50
  @geometric_type = GEOMETRIC_TYPES[@geo_type_name] || RGeo::Feature::Geometry
50
51
  @srid = type_info.srid || 0
@@ -57,7 +58,7 @@ module ActiveRecord
57
58
  # Guard against nil sql_type_metadata during type registration
58
59
  return false unless sql_type_metadata&.sql_type
59
60
 
60
- TrilogisAdapter::SPATIAL_COLUMN_TYPES.include?(sql_type_metadata.sql_type.downcase)
61
+ !TrilogisAdapter.spatial_type(sql_type_metadata.sql_type).nil?
61
62
  end
62
63
 
63
64
  def has_z?
@@ -86,7 +87,31 @@ module ActiveRecord
86
87
 
87
88
  # Only include SRID in limit
88
89
  # Type information is in the type() method
89
- { srid: @srid }.compact
90
+ { srid: @srid_constraint } unless @srid_constraint.nil?
91
+ end
92
+
93
+ def ==(other)
94
+ super && other.is_a?(self.class) &&
95
+ srid_constraint == other.srid_constraint && geo_type_name == other.geo_type_name
96
+ end
97
+ alias eql? ==
98
+
99
+ def hash
100
+ [super, srid_constraint, geo_type_name].hash
101
+ end
102
+
103
+ def encode_with(coder)
104
+ super
105
+ coder["srid_constraint"] = @srid_constraint
106
+ coder["geo_type_name"] = @geo_type_name
107
+ end
108
+
109
+ def init_with(coder)
110
+ super
111
+ @srid_constraint = coder["srid_constraint"]
112
+ @srid = @srid_constraint || 0
113
+ @geo_type_name = coder["geo_type_name"]
114
+ @geometric_type = GEOMETRIC_TYPES.fetch(@geo_type_name, RGeo::Feature::Geometry)
90
115
  end
91
116
 
92
117
  # Override default to always return nil for spatial columns
@@ -4,7 +4,6 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module Trilogis
6
6
  # Queries MySQL INFORMATION_SCHEMA for spatial column metadata
7
- # and caches the results for performance
8
7
  class SpatialColumnInfo
9
8
  def initialize(adapter, table_name)
10
9
  @adapter = adapter
@@ -12,49 +11,37 @@ module ActiveRecord
12
11
  end
13
12
 
14
13
  def all
14
+ scope = @adapter.send(:quoted_scope, @table_name)
15
15
  # Query MySQL's ST_GEOMETRY_COLUMNS view for SRID info
16
16
  # This view properly returns SRS_ID for spatial columns
17
17
  sql = <<~SQL.squish
18
18
  SELECT
19
19
  gc.COLUMN_NAME as column_name,
20
20
  gc.SRS_ID as srs_id,
21
- gc.GEOMETRY_TYPE_NAME as geometry_type,
22
21
  c.COLUMN_TYPE as column_type
23
22
  FROM INFORMATION_SCHEMA.ST_GEOMETRY_COLUMNS gc
24
23
  JOIN INFORMATION_SCHEMA.COLUMNS c
25
24
  ON gc.TABLE_SCHEMA = c.TABLE_SCHEMA
26
25
  AND gc.TABLE_NAME = c.TABLE_NAME
27
26
  AND gc.COLUMN_NAME = c.COLUMN_NAME
28
- WHERE gc.TABLE_SCHEMA = DATABASE()
29
- AND gc.TABLE_NAME = #{@adapter.quote(@table_name)}
27
+ WHERE gc.TABLE_SCHEMA = #{scope[:schema]}
28
+ AND gc.TABLE_NAME = #{scope[:name]}
30
29
  SQL
31
30
 
32
31
  result = {}
33
32
  @adapter.exec_query(sql, "SCHEMA").each do |row|
34
33
  column_name = row["column_name"] || row["COLUMN_NAME"]
35
34
  srs_id = row["srs_id"] || row["SRS_ID"]
36
- row["geometry_type"] || row["GEOMETRY_TYPE"]
37
35
  column_type = (row["column_type"] || row["COLUMN_TYPE"]).to_s.sub(/m$/, "")
38
36
 
39
37
  result[column_name] = {
40
38
  name: column_name,
41
- srid: srs_id.to_i,
42
- type: column_type
39
+ srid: srs_id&.to_i,
40
+ type: TrilogisAdapter.spatial_type(column_type)
43
41
  }
44
42
  end
45
43
  result
46
44
  end
47
-
48
- # Get spatial info for a specific column if it's spatial
49
- # Returns nil for non-spatial columns to avoid unnecessary queries
50
- def get(column_name, sql_type)
51
- # Only query for known spatial types
52
- return unless TrilogisAdapter::SPATIAL_COLUMN_TYPES.include?(sql_type.to_s.downcase)
53
-
54
- # Don't memoize - always query fresh data to avoid stale cache issues
55
- # when columns are added during tests
56
- all[column_name]
57
- end
58
45
  end
59
46
  end
60
47
  end
@@ -5,77 +5,17 @@ module RGeo
5
5
  module Trilogis
6
6
  # MySQL-specific spatial expressions
7
7
  module SpatialExpressions
8
+ # Keep Arel expressions intact; the upstream implementation coerces distance to Float.
9
+ def st_buffer(distance)
10
+ SpatialNamedFunction.new("ST_Buffer", [self, distance], [true, true, false])
11
+ end
12
+
8
13
  # MySQL ST_Distance_Sphere for geographic distance calculations
9
14
  def st_distance_sphere(rhs, units = nil)
10
15
  args = [self, rhs]
11
16
  args << units.to_s if units
12
17
  SpatialNamedFunction.new("ST_Distance_Sphere", args, [false, true, true, false])
13
18
  end
14
-
15
- # Additional MySQL spatial functions
16
- def st_buffer(distance)
17
- SpatialNamedFunction.new("ST_Buffer", [self, distance], [false, true, false])
18
- end
19
-
20
- def st_contains(rhs)
21
- SpatialNamedFunction.new("ST_Contains", [self, rhs], [false, true, true])
22
- end
23
-
24
- def st_within(rhs)
25
- SpatialNamedFunction.new("ST_Within", [self, rhs], [false, true, true])
26
- end
27
-
28
- def st_intersects(rhs)
29
- SpatialNamedFunction.new("ST_Intersects", [self, rhs], [false, true, true])
30
- end
31
-
32
- def st_crosses(rhs)
33
- SpatialNamedFunction.new("ST_Crosses", [self, rhs], [false, true, true])
34
- end
35
-
36
- def st_touches(rhs)
37
- SpatialNamedFunction.new("ST_Touches", [self, rhs], [false, true, true])
38
- end
39
-
40
- def st_overlaps(rhs)
41
- SpatialNamedFunction.new("ST_Overlaps", [self, rhs], [false, true, true])
42
- end
43
-
44
- def st_equals(rhs)
45
- SpatialNamedFunction.new("ST_Equals", [self, rhs], [false, true, true])
46
- end
47
-
48
- def st_disjoint(rhs)
49
- SpatialNamedFunction.new("ST_Disjoint", [self, rhs], [false, true, true])
50
- end
51
-
52
- def st_area
53
- SpatialNamedFunction.new("ST_Area", [self], [false, true])
54
- end
55
-
56
- def st_length
57
- SpatialNamedFunction.new("ST_Length", [self], [false, true])
58
- end
59
-
60
- def st_centroid
61
- SpatialNamedFunction.new("ST_Centroid", [self], [false, true])
62
- end
63
-
64
- def st_envelope
65
- SpatialNamedFunction.new("ST_Envelope", [self], [false, true])
66
- end
67
-
68
- def st_astext
69
- SpatialNamedFunction.new("ST_AsText", [self], [false, true])
70
- end
71
-
72
- def st_asbinary
73
- SpatialNamedFunction.new("ST_AsBinary", [self], [false, true])
74
- end
75
-
76
- def st_srid
77
- SpatialNamedFunction.new("ST_SRID", [self], [false, true])
78
- end
79
19
  end
80
20
  end
81
21
  end
@@ -53,36 +53,30 @@ module ActiveRecord
53
53
  class TableDefinition < ActiveRecord::ConnectionAdapters::MySQL::TableDefinition
54
54
  include ColumnMethods
55
55
 
56
- # Override column to handle spatial types
57
56
  def column(name, type, index: nil, **options)
58
- # Store spatial-specific options before processing
59
- spatial_options = {
60
- srid: options.delete(:srid),
61
- has_m: options.delete(:has_m),
62
- has_z: options.delete(:has_z),
63
- geographic: options.delete(:geographic)
64
- }.compact
65
-
66
- # Call super to create column definition
67
- result = super
68
-
69
- # Add spatial options back to the column definition if it's a spatial type
70
- if spatial_type?(type) && (col = @columns_hash[name.to_s])
71
- spatial_options.each do |key, value|
72
- col.options[key] = value
57
+ index = { type: :spatial } if TrilogisAdapter.spatial_type(type) && [true, :spatial].include?(index)
58
+ super
59
+ end
60
+
61
+ def new_column_definition(name, type, **options)
62
+ if (spatial_type = TrilogisAdapter.spatial_type(type))
63
+ type = spatial_type.to_sym
64
+ # Retain legacy no-op options for existing migrations.
65
+ options.except!(:has_z, :has_m, :geographic)
66
+ options.delete(:default)
67
+ if options[:limit].is_a?(Hash)
68
+ limit = options.delete(:limit)
69
+ type = TrilogisAdapter.spatial_type(limit[:type]).to_sym if limit[:type]
70
+ options[:srid] = limit[:srid] unless options.key?(:srid)
73
71
  end
74
72
  end
75
-
76
- # Add spatial index if requested
77
- @indexes << [name, { type: :spatial }] if index && spatial_type?(type) && [true, :spatial].include?(index)
78
-
79
- result
73
+ super
80
74
  end
81
75
 
82
76
  private
83
77
 
84
- def spatial_type?(type)
85
- TrilogisAdapter::SPATIAL_COLUMN_TYPES.include?(type.to_s)
78
+ def valid_column_definition_options
79
+ super + [:srid]
86
80
  end
87
81
  end
88
82
  end
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module Trilogis
6
- VERSION = "8.1.1"
6
+ VERSION = "8.1.3"
7
7
  end
8
8
  end
9
9
  end
@@ -15,48 +15,6 @@ require_relative "../type/spatial"
15
15
  require_relative "../tasks/trilogis_database_tasks"
16
16
 
17
17
  module ActiveRecord
18
- module ConnectionHandling
19
- # Establishes a connection to the database using the Trilogis adapter.
20
- # This adapter extends the built-in Trilogy adapter with spatial support.
21
- def trilogis_connection(config)
22
- configuration = config.dup
23
-
24
- # Ensure required configuration
25
- configuration[:prepared_statements] = true unless configuration.key?(:prepared_statements)
26
-
27
- # Build connection options for Trilogy
28
- connection_options = {
29
- host: configuration[:host],
30
- port: configuration[:port],
31
- database: configuration[:database],
32
- username: configuration[:username],
33
- password: configuration[:password],
34
- socket: configuration[:socket],
35
- encoding: configuration[:encoding],
36
- ssl_mode: configuration[:ssl_mode],
37
- connect_timeout: configuration[:connect_timeout],
38
- read_timeout: configuration[:read_timeout],
39
- write_timeout: configuration[:write_timeout]
40
- }.compact
41
-
42
- # Create the Trilogy client connection
43
- require "trilogy"
44
- client = Trilogy.new(connection_options)
45
-
46
- # Return our spatial-enabled adapter
47
- ConnectionAdapters::TrilogisAdapter.new(
48
- client,
49
- logger,
50
- nil,
51
- configuration
52
- )
53
- rescue Trilogy::Error => e
54
- raise ActiveRecord::NoDatabaseError if e.message.include?("Unknown database")
55
-
56
- raise
57
- end
58
- end
59
-
60
18
  module ConnectionAdapters
61
19
  class TrilogisAdapter < TrilogyAdapter
62
20
  ADAPTER_NAME = "Trilogis"
@@ -75,6 +33,12 @@ module ActiveRecord
75
33
  geometrycollection
76
34
  ].freeze
77
35
 
36
+ def self.spatial_type(type)
37
+ name = type.to_s.downcase.delete("_")
38
+ name = "geometrycollection" if name == "geomcollection"
39
+ name if SPATIAL_COLUMN_TYPES.include?(name)
40
+ end
41
+
78
42
  # Default SRID for MySQL
79
43
  DEFAULT_SRID = 0
80
44
 
@@ -83,63 +47,22 @@ module ActiveRecord
83
47
  # in longitude-latitude order instead of MySQL's default latitude-longitude
84
48
  AXIS_ORDER_LONG_LAT = "'axis-order=long-lat'"
85
49
 
86
- # Common geographic SRIDs that use latitude-longitude by default in MySQL 8.0
87
- # These need axis-order parameter to work with standard GIS longitude-latitude format
88
- GEOGRAPHIC_SRIDS = [
89
- 4326, # WGS 84 (GPS)
90
- 4269, # NAD83
91
- 4267, # NAD27
92
- 4258, # ETRS89
93
- 4019 # Unknown datum based upon the GRS 1980 ellipsoid
94
- ].freeze
95
-
96
- # Class method to check if a type is spatial
97
- def self.spatial_column_options(type)
98
- SPATIAL_COLUMN_TYPES.include?(type.to_s.downcase)
99
- end
100
-
101
- def initialize(...)
102
- super
103
-
104
- # Override the visitor for spatial support
105
- @visitor = Arel::Visitors::Trilogis.new(self)
106
-
107
- # Configure RGeo factory generator for SRID-based factory selection
108
- configure_rgeo_factory_generator
109
- end
110
-
111
- def adapter_name
112
- ADAPTER_NAME
50
+ def self.valid_type?(type)
51
+ !!spatial_type(type) || super
113
52
  end
114
53
 
115
54
  def supports_spatial?
116
- # MySQL 5.7.6+ supports spatial indexes and functions
117
- # MariaDB has different spatial support, so we exclude it for now
118
- !mariadb? && database_version >= "5.7.6"
55
+ !mariadb? && database_version >= "8.0"
119
56
  end
120
57
 
121
58
  def default_srid
122
59
  DEFAULT_SRID
123
60
  end
124
61
 
125
- def spatial_column_options(_table_name)
126
- # Return empty hash as MySQL stores spatial metadata in information_schema
127
- {}
128
- end
129
-
130
- def with_connection
131
- yield self
132
- end
133
-
134
62
  def schema_creation
135
63
  Trilogis::SchemaCreation.new(self)
136
64
  end
137
65
 
138
- # Override valid_type? to include spatial types
139
- def valid_type?(type)
140
- SPATIAL_COLUMN_TYPES.include?(type.to_s) || super
141
- end
142
-
143
66
  def native_database_types
144
67
  super.merge(
145
68
  geometry: { name: "geometry" },
@@ -163,26 +86,8 @@ module ActiveRecord
163
86
  if value.is_a?(RGeo::Feature::Instance)
164
87
  srid = value.srid || DEFAULT_SRID
165
88
 
166
- # For geographic SRIDs, use axis-order parameter to ensure longitude-latitude order
167
- # MySQL 8.0 defaults to latitude-longitude for geographic SRS, but GIS tools use long-lat
168
- # ST_GeomFromWKB DOES support axis-order parameter in MySQL 8.0+
169
89
  wkb_hex = RGeo::WKRep::WKBGenerator.new(hex_format: true, little_endian: true).generate(value)
170
- if geographic_srid?(srid)
171
- "ST_GeomFromWKB(0x#{wkb_hex}, #{srid}, #{AXIS_ORDER_LONG_LAT})"
172
- else
173
- # For projected SRIDs (like 3857), no axis-order needed - uses cartesian X,Y
174
- "ST_GeomFromWKB(0x#{wkb_hex}, #{srid})"
175
- end
176
- else
177
- super
178
- end
179
- end
180
-
181
- def type_cast(value, column = nil)
182
- if column&.type == :geometry && value.is_a?(String)
183
- # Extract SRID from spatial column if available
184
- srid = column.respond_to?(:srid) ? column.srid : DEFAULT_SRID
185
- parse_spatial_value(value, srid)
90
+ "ST_GeomFromWKB(0x#{wkb_hex}, #{srid}, #{AXIS_ORDER_LONG_LAT})"
186
91
  else
187
92
  super
188
93
  end
@@ -190,52 +95,17 @@ module ActiveRecord
190
95
 
191
96
  private
192
97
 
193
- def configure_rgeo_factory_generator
194
- # Register Geographic factories for geographic SRIDs in RGeo::ActiveRecord::SpatialFactoryStore
195
- # This ensures the correct factory (Geographic vs Cartesian) is used based on SRID
196
- factory_store = RGeo::ActiveRecord::SpatialFactoryStore.instance
197
-
198
- # Register Geographic spherical factory for each geographic SRID
199
- # The registry will match based on SRID and return the appropriate factory
200
- GEOGRAPHIC_SRIDS.each do |srid|
201
- factory_store.register(
202
- RGeo::Geographic.spherical_factory(srid: srid),
203
- srid: srid
204
- )
205
- end
206
- end
207
-
208
- def parse_spatial_value(value, srid = DEFAULT_SRID)
209
- return nil if value.nil?
210
-
211
- # Ensure SRID is an integer (defensive programming for external callers)
212
- srid = srid.to_i
213
-
214
- # Parse WKB hex string using SpatialFactoryStore for correct factory selection
215
- if value.is_a?(String) && value.match?(/\A[0-9a-fA-F]+\z/)
216
- factory = RGeo::ActiveRecord::SpatialFactoryStore.instance.factory(
217
- geo_type: "geometry",
218
- sql_type: "geometry",
219
- srid: srid
220
- )
221
- RGeo::WKRep::WKBParser.new(factory, support_ewkb: true, default_srid: srid).parse([value].pack("H*"))
222
- else
223
- value
224
- end
225
- end
226
-
227
- # Check if a SRID is geographic (uses latitude-longitude coordinate system)
228
- def geographic_srid?(srid)
229
- GEOGRAPHIC_SRIDS.include?(srid)
98
+ def arel_visitor
99
+ Arel::Visitors::Trilogis.new(self)
230
100
  end
231
101
 
232
102
  # Override type_map to include spatial types
233
103
  def type_map
234
104
  @type_map ||= begin
235
- map = super.dup
105
+ map = Type::TypeMap.new(super)
236
106
 
237
107
  # Add spatial types
238
- SPATIAL_COLUMN_TYPES.each do |geo_type|
108
+ (SPATIAL_COLUMN_TYPES + ["geomcollection"]).each do |geo_type|
239
109
  map.register_type(geo_type) do |sql_type|
240
110
  Type::Spatial.new(sql_type)
241
111
  end
@@ -244,14 +114,6 @@ module ActiveRecord
244
114
  map
245
115
  end
246
116
  end
247
-
248
- def translate_exception(exception, message:, sql:, binds:)
249
- if exception.is_a?(::Trilogy::SSLError)
250
- return ActiveRecord::ConnectionFailed.new(message, connection_pool: @pool)
251
- end
252
-
253
- super
254
- end
255
117
  end
256
118
  end
257
119
  end
@@ -6,7 +6,6 @@ module ActiveRecord
6
6
  attr_reader :geo_type, :srid
7
7
 
8
8
  def initialize(sql_type = "geometry")
9
- @sql_type = sql_type
10
9
  @geo_type, @srid = self.class.parse_sql_type(sql_type)
11
10
  end
12
11
 
@@ -87,32 +86,6 @@ module ActiveRecord
87
86
 
88
87
  private
89
88
 
90
- def parse_sql_type(sql_type)
91
- original_sql_type = sql_type.to_s
92
- sql_type = sql_type.to_s.downcase
93
-
94
- # Only parse known spatial types
95
- # For non-spatial types, return them unchanged
96
- spatial_types = %w[geometry point linestring polygon multipoint multilinestring multipolygon geometrycollection]
97
-
98
- # Check if it's a spatial type
99
- base_type = sql_type.split("(").first
100
- return [original_sql_type, 0] unless spatial_types.include?(base_type)
101
-
102
- # Extract geometry type and SRID from SQL type
103
- # Examples: "geometry", "point", "linestring", "geometry(Point,4326)"
104
- if sql_type =~ /(\w+)(?:\((\w+)(?:,(\d+))?\))?/
105
- geo_type = Regexp.last_match(1)
106
- sub_type = Regexp.last_match(2)
107
- srid = Regexp.last_match(3).to_i
108
-
109
- geo_type = sub_type.downcase if sub_type
110
- [geo_type, srid]
111
- else
112
- [sql_type, 0]
113
- end
114
- end
115
-
116
89
  def parse_string(string)
117
90
  return nil if string.blank?
118
91
 
@@ -121,12 +94,7 @@ module ActiveRecord
121
94
  srid = Regexp.last_match(1).to_i
122
95
  wkt = Regexp.last_match(2)
123
96
 
124
- # Use SpatialFactoryStore to get the appropriate factory
125
- geo_factory = RGeo::ActiveRecord::SpatialFactoryStore.instance.factory(
126
- geo_type: @geo_type,
127
- sql_type: @sql_type,
128
- srid: srid
129
- )
97
+ geo_factory = spatial_factory(srid)
130
98
  begin
131
99
  return RGeo::WKRep::WKTParser.new(geo_factory, support_ewkt: true, default_srid: srid).parse(wkt)
132
100
  rescue RGeo::Error::ParseError
@@ -134,12 +102,11 @@ module ActiveRecord
134
102
  end
135
103
  end
136
104
 
137
- # Try to parse as WKT
138
- if string.match?(/^[A-Z]/i)
139
- parse_wkt(string)
140
- # Try to parse as WKB hex
141
- elsif string.match?(/^[0-9a-fA-F]+$/)
105
+ # Hex can start with a letter, including MySQL's SRID 4326 prefix (e6100000).
106
+ if string.match?(/\A[0-9a-fA-F]+\z/)
142
107
  parse_wkb_hex(string)
108
+ elsif string.match?(/^[A-Z]/i)
109
+ parse_wkt(string)
143
110
  end
144
111
  end
145
112
 
@@ -152,84 +119,27 @@ module ActiveRecord
152
119
  end
153
120
 
154
121
  def parse_wkb_hex(hex_string)
155
- return nil if hex_string.nil? || hex_string.empty?
156
-
157
- binary = convert_hex_to_binary(hex_string)
158
- parse_binary_with_srid(binary)
159
- rescue RGeo::Error::ParseError, ArgumentError
160
- nil
161
- end
122
+ return nil if hex_string.length.odd?
162
123
 
163
- def convert_hex_to_binary(hex_string)
164
- # MySQL returns WKB as hex string
165
- [hex_string].pack("H*")
166
- end
167
-
168
- def parse_binary_with_srid(binary)
169
- # MySQL internal format: first 4 bytes are SRID (little-endian), then WKB
170
- if binary.length >= 5
171
- srid = extract_srid(binary)
172
- wkb_data = binary[4..]
173
- parse_wkb_with_factory(wkb_data, srid)
174
- else
175
- parse_standard_wkb(binary)
176
- end
177
- end
178
-
179
- def extract_srid(binary)
180
- # V = unsigned 32-bit little-endian
181
- binary[0..3].unpack1("V")
182
- end
183
-
184
- def parse_wkb_with_factory(wkb_data, srid)
185
- geo_factory = create_factory_for_srid(srid)
186
- RGeo::WKRep::WKBParser.new(geo_factory, support_ewkb: true, default_srid: srid).parse(wkb_data)
187
- end
188
-
189
- def create_factory_for_srid(srid)
190
- # Geographic SRIDs (4326, 4269, 4267, 4258, 4019) need spherical factory
191
- if geographic_srid?(srid)
192
- RGeo::Geographic.spherical_factory(srid: srid)
193
- else
194
- RGeo::Cartesian.preferred_factory(srid: srid)
195
- end
196
- end
197
-
198
- def parse_standard_wkb(binary)
199
- # Fall back to standard WKB parsing with EWKB support
200
- RGeo::WKRep::WKBParser.new(spatial_factory, support_ewkb: true, default_srid: @srid).parse(binary)
201
- end
202
-
203
- # Check if SRID represents a geographic coordinate system
204
- def geographic_srid?(srid)
205
- # Common geographic SRIDs that use latitude-longitude
206
- [4326, 4269, 4267, 4258, 4019].include?(srid)
124
+ parse_wkb_binary([hex_string].pack("H*"))
207
125
  end
208
126
 
209
127
  def parse_wkb_binary(binary_string)
210
128
  return nil if binary_string.nil? || binary_string.empty?
211
129
 
212
- # MySQL internal format: first 4 bytes are SRID (little-endian), then WKB
213
- if binary_string.length >= 5
214
- srid = binary_string[0..3].unpack1("V") # V = unsigned 32-bit little-endian
215
- wkb_data = binary_string[4..]
216
-
217
- # Create appropriate factory based on SRID
218
- # Geographic SRIDs (4326, 4269, 4267, 4258, 4019) need spherical factory
219
- geo_factory = if geographic_srid?(srid)
220
- RGeo::Geographic.spherical_factory(srid: srid)
221
- else
222
- RGeo::Cartesian.preferred_factory(srid: srid)
223
- end
224
-
225
- # Support EWKB format
226
- RGeo::WKRep::WKBParser.new(geo_factory, support_ewkb: true, default_srid: srid).parse(wkb_data)
227
- else
228
- # Fall back to standard WKB parsing with EWKB support
229
- RGeo::WKRep::WKBParser.new(spatial_factory, support_ewkb: true, default_srid: @srid).parse(binary_string)
130
+ # MySQL binary values contain a four-byte SRID prefix followed by WKB.
131
+ if binary_string.bytesize >= 5
132
+ srid = binary_string.unpack1("V")
133
+ begin
134
+ parser = RGeo::WKRep::WKBParser.new(spatial_factory(srid), support_ewkb: true, default_srid: srid)
135
+ return parser.parse(binary_string.byteslice(4..))
136
+ rescue RGeo::Error::ParseError, ArgumentError
137
+ # Standard WKB has no MySQL prefix; parse the complete input below.
138
+ end
230
139
  end
140
+
141
+ RGeo::WKRep::WKBParser.new(spatial_factory, support_ewkb: true, default_srid: @srid).parse(binary_string)
231
142
  rescue RGeo::Error::ParseError, ArgumentError
232
- # Failed to parse, return nil
233
143
  nil
234
144
  end
235
145
 
@@ -240,27 +150,22 @@ module ActiveRecord
240
150
  normalized_hash = hash.transform_keys(&:to_s)
241
151
  return unless normalized_hash["type"] && normalized_hash["coordinates"]
242
152
 
243
- RGeo::GeoJSON.decode(normalized_hash.to_json, geo_factory: spatial_factory)
153
+ unless defined?(RGeo::GeoJSON)
154
+ raise ArgumentError, 'GeoJSON input requires the rgeo-geojson gem and require "rgeo/geo_json"'
155
+ end
156
+
157
+ RGeo::GeoJSON.decode(normalized_hash, geo_factory: spatial_factory)
244
158
  end
245
159
 
246
- def spatial_factory
247
- # Rails 8.1 freezes type objects for Ractor compatibility
248
- # If frozen, create factory without caching to avoid FrozenError
249
- if frozen?
250
- return RGeo::ActiveRecord::SpatialFactoryStore.instance.factory(
251
- geo_type: @geo_type,
252
- sql_type: @sql_type,
253
- srid: @srid
254
- )
160
+ def spatial_factory(srid = @srid)
161
+ store = RGeo::ActiveRecord::SpatialFactoryStore.instance
162
+ attrs = { geo_type: @geo_type, sql_type: @geo_type, srid: srid }
163
+ registered = store.registry.any? do |entry|
164
+ entry.attrs.any? && entry.attrs.all? { |key, value| attrs[key] == value }
255
165
  end
256
-
257
- # For non-frozen objects, use instance variable caching
258
- @spatial_factories ||= {}
259
- @spatial_factories[@srid] ||= RGeo::ActiveRecord::SpatialFactoryStore.instance.factory(
260
- geo_type: @geo_type,
261
- sql_type: @sql_type,
262
- srid: @srid
263
- )
166
+ # Preserve the existing geographic defaults without mutating the global registry.
167
+ attrs[:sql_type] = "geography" if !registered && [4326, 4269, 4267, 4258, 4019].include?(srid)
168
+ store.factory(attrs)
264
169
  end
265
170
  end
266
171
  end
@@ -2,16 +2,9 @@
2
2
 
3
3
  require "active_record"
4
4
 
5
- # Load ActiveRecord dependencies explicitly to ensure correct loading order
6
- # This approach works across all Ruby versions and avoids autoload issues
7
- require_relative "active_record/dependency_loader"
8
-
9
5
  require "active_record/connection_adapters/trilogy_adapter"
10
6
  require "rgeo"
11
7
  require "rgeo/active_record"
12
8
 
13
9
  # Load the adapter
14
10
  require_relative "active_record/connection_adapters/trilogis_adapter"
15
-
16
- # Load railtie if Rails is defined
17
- require_relative "active_record/connection_adapters/trilogis/railtie" if defined?(Rails)
metadata CHANGED
@@ -1,28 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-trilogis-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.1
4
+ version: 8.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ether Moon
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-11-13 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: activerecord
14
15
  requirement: !ruby/object:Gem::Requirement
15
16
  requirements:
16
- - - "~>"
17
+ - - ">="
17
18
  - !ruby/object:Gem::Version
18
- version: '8.1'
19
+ version: 8.1.4.1
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '8.2'
19
23
  type: :runtime
20
24
  prerelease: false
21
25
  version_requirements: !ruby/object:Gem::Requirement
22
26
  requirements:
23
- - - "~>"
27
+ - - ">="
24
28
  - !ruby/object:Gem::Version
25
- version: '8.1'
29
+ version: 8.1.4.1
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '8.2'
26
33
  - !ruby/object:Gem::Dependency
27
34
  name: rgeo
28
35
  requirement: !ruby/object:Gem::Requirement
@@ -51,6 +58,26 @@ dependencies:
51
58
  - - "~>"
52
59
  - !ruby/object:Gem::Version
53
60
  version: '8.1'
61
+ - !ruby/object:Gem::Dependency
62
+ name: trilogy
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 2.13.3
68
+ - - "<"
69
+ - !ruby/object:Gem::Version
70
+ version: '3'
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 2.13.3
78
+ - - "<"
79
+ - !ruby/object:Gem::Version
80
+ version: '3'
54
81
  - !ruby/object:Gem::Dependency
55
82
  name: minitest
56
83
  requirement: !ruby/object:Gem::Requirement
@@ -163,23 +190,9 @@ dependencies:
163
190
  - - "~>"
164
191
  - !ruby/object:Gem::Version
165
192
  version: '0.6'
166
- - !ruby/object:Gem::Dependency
167
- name: trilogy
168
- requirement: !ruby/object:Gem::Requirement
169
- requirements:
170
- - - "~>"
171
- - !ruby/object:Gem::Version
172
- version: '2.9'
173
- type: :development
174
- prerelease: false
175
- version_requirements: !ruby/object:Gem::Requirement
176
- requirements:
177
- - - "~>"
178
- - !ruby/object:Gem::Version
179
- version: '2.9'
180
193
  description: ActiveRecord connection adapter for MySQL. It extends the Rails built-in
181
194
  Trilogy adapter and adds spatial extensions support via RGeo. Compatible with Rails
182
- 8.1+ native Trilogy adapter. Requires Ruby 3.2+ and Rails 8.1+.
195
+ 8.1 native Trilogy adapter. Requires Ruby 3.3+ and Rails 8.1.4.1+.
183
196
  email: chipseru@gmail.com
184
197
  executables: []
185
198
  extensions: []
@@ -187,7 +200,6 @@ extra_rdoc_files: []
187
200
  files:
188
201
  - LICENSE
189
202
  - lib/active_record/connection_adapters/trilogis/arel_tosql.rb
190
- - lib/active_record/connection_adapters/trilogis/railtie.rb
191
203
  - lib/active_record/connection_adapters/trilogis/schema_creation.rb
192
204
  - lib/active_record/connection_adapters/trilogis/schema_statements.rb
193
205
  - lib/active_record/connection_adapters/trilogis/spatial_column.rb
@@ -196,7 +208,6 @@ files:
196
208
  - lib/active_record/connection_adapters/trilogis/spatial_table_definition.rb
197
209
  - lib/active_record/connection_adapters/trilogis/version.rb
198
210
  - lib/active_record/connection_adapters/trilogis_adapter.rb
199
- - lib/active_record/dependency_loader.rb
200
211
  - lib/active_record/tasks/trilogis_database_tasks.rb
201
212
  - lib/active_record/type/spatial.rb
202
213
  - lib/activerecord-trilogis-adapter.rb
@@ -205,6 +216,7 @@ licenses:
205
216
  - MIT
206
217
  metadata:
207
218
  rubygems_mfa_required: 'true'
219
+ post_install_message:
208
220
  rdoc_options: []
209
221
  require_paths:
210
222
  - lib
@@ -212,17 +224,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
212
224
  requirements:
213
225
  - - ">="
214
226
  - !ruby/object:Gem::Version
215
- version: 3.2.0
227
+ version: 3.3.0
216
228
  - - "<"
217
229
  - !ruby/object:Gem::Version
218
- version: '3.5'
230
+ version: '5.0'
219
231
  required_rubygems_version: !ruby/object:Gem::Requirement
220
232
  requirements:
221
233
  - - ">="
222
234
  - !ruby/object:Gem::Version
223
235
  version: '0'
224
236
  requirements: []
225
- rubygems_version: 3.6.9
237
+ rubygems_version: 3.5.22
238
+ signing_key:
226
239
  specification_version: 4
227
240
  summary: ActiveRecord adapter for MySQL with spatial extensions, built on Trilogy.
228
241
  test_files: []
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveRecord
4
- module ConnectionAdapters
5
- module Trilogis
6
- class Railtie < Rails::Railtie
7
- initializer "trilogis.initialize" do
8
- ActiveSupport.on_load(:active_record) do
9
- require "active_record/connection_adapters/trilogis_adapter"
10
- end
11
- end
12
-
13
- # Register database tasks for spatial databases
14
- rake_tasks do
15
- namespace :db do
16
- namespace :trilogis do
17
- desc "Create spatial extensions if needed"
18
- task setup: :environment do
19
- ActiveRecord::Base.connection.execute(
20
- "SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() LIMIT 1"
21
- )
22
- puts "Trilogis adapter is ready. MySQL spatial support enabled."
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end
30
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # ActiveRecord dependency loader for all Ruby versions
4
- #
5
- # This file explicitly pre-loads all required ActiveRecord modules in the correct
6
- # dependency order. This approach:
7
- #
8
- # - Ensures consistent loading behavior across all Ruby versions (3.2+)
9
- # - Avoids reliance on Ruby's autoload mechanism, which changed in Ruby 3.4
10
- # - Prevents "uninitialized constant" errors during module inclusion
11
- # - Works with ActiveRecord 8.0+ internal structure
12
- #
13
- # While this was initially created to solve Ruby 3.4 compatibility issues,
14
- # using explicit requires for all versions provides better stability and consistency.
15
-
16
- # Layer 0: ConnectionAdapters module setup (defines .resolve method)
17
- require "active_record/connection_adapters"
18
-
19
- # Layer 1: Base modules with no dependencies
20
- require "active_record/connection_adapters/deduplicable"
21
-
22
- # Layer 2: Abstract adapter modules
23
- require "active_record/connection_adapters/abstract/quoting"
24
- require "active_record/connection_adapters/abstract/database_statements"
25
- require "active_record/connection_adapters/abstract/schema_statements"
26
- require "active_record/connection_adapters/abstract/database_limits"
27
- require "active_record/connection_adapters/abstract/query_cache"
28
- require "active_record/connection_adapters/abstract/savepoints"
29
-
30
- # Layer 3: Column and schema definitions
31
- require "active_record/connection_adapters/column"
32
- require "active_record/connection_adapters/abstract/schema_definitions"
33
-
34
- # Layer 4: Connection management (needed by ActiveRecord::Base)
35
- require "active_record/connection_adapters/abstract/connection_handler"
36
-
37
- # Layer 5: Abstract MySQL adapter (loads MySQL-specific modules)
38
- require "active_record/connection_adapters/abstract_mysql_adapter"