rgeo-activerecord 6.2.2 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0eaf7ecbe5d6d765688cd03ed0a6ce434f6b2eb7c3f8cf4f77e94cf87859e22c
4
- data.tar.gz: b10e77caf7d99659469548766255177627db551fc522fa76c26d4a0a2f775e74
3
+ metadata.gz: 9c1442b0141a1655c582e412962a3328b702e52da302e3a4874a3d22ee3f8fbc
4
+ data.tar.gz: 86933cf076e3c728ea34d3918bff61f56baef30c7e31dce99e56093a2049a5cd
5
5
  SHA512:
6
- metadata.gz: bbec5edddc4e64e3d9587619bb6316a71651f37806ffdc0ff73fbd595003232e5ec6f783c73eba931f148687b9d07dc245d5437403b5eb5c3583fe0ae7684fdd
7
- data.tar.gz: 1f487be6304b56fb804e6a97478093351d7a86586a14b94b6ca7c2ad1d2e9b6244767830e14f63a06dd4365c2b82b387759f1f311b939d907bf9c54a4ea1f7aa
6
+ metadata.gz: d4b10c1f4ab732aa52545bce0f920fd36511e68c4c560b5f29f3b5907243e82cc8723bb54abd7ce1277a1857d2783749e0d1bc8a452117d4b9b7f5f6da1262ea
7
+ data.tar.gz: 46b20c80597ca7dd822987fcf2592c56d68c0f413684eb5e87de012a5139f77a8b6ded059553001b5791c7f6be867a9897b01b4aca4ef5dda361d4259482d79b
data/History.md CHANGED
@@ -1,3 +1,8 @@
1
+ ### 7.0.0 / 2020-12-15
2
+
3
+ * Update visit_* methods and Arel interface to support RGeo features.
4
+ * Rework SpatialFactoryStore to support hierarchical matches and fallbacks.
5
+
1
6
  ### 6.2.2 / 2020-11-20
2
7
 
3
8
  * Removed `Arel::Visitor::DepthFirst` for ActiveRecord 6.1 compatibility (kamipo)
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  ## RGeo::ActiveRecord
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/rgeo-activerecord.svg)](http://badge.fury.io/rb/rgeo-activerecord)
4
- [![Build Status](https://travis-ci.org/rgeo/rgeo-activerecord.svg?branch=master)](https://travis-ci.org/rgeo/rgeo-activerecord)
3
+ [![Gem version](https://img.shields.io/gem/v/rgeo-activerecord)](https://rubygems.org/gems/rgeo-activerecord)
4
+ [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/rgeo/rgeo-activerecord/Tests)](https://github.com/rgeo/rgeo-activerecord/actions?query=workflow%3A%22Tests%22)
5
5
  [![Code Climate](https://codeclimate.com/github/rgeo/rgeo-activerecord.png)](https://codeclimate.com/github/rgeo/rgeo-activerecord)
6
6
 
7
- RGeo::ActiveRecord is an optional [RGeo](http://github.com/dazuma/rgeo) module
7
+ RGeo::ActiveRecord is an optional [RGeo](http://github.com/rgeo/rgeo) module
8
8
  providing spatial extensions for ActiveRecord, as well as a set of helpers for
9
9
  writing spatial ActiveRecord adapters based on RGeo.
10
10
 
@@ -28,6 +28,7 @@ Gemfile:
28
28
  ```ruby
29
29
  gem 'rgeo-activerecord'
30
30
  ```
31
+
31
32
  Version `6.1` supports ActiveRecord 5.x and 6.0 with `rgeo` 1.0+.
32
33
 
33
34
  Version `6.0` supports ActiveRecord 5.x with `rgeo` 1.x.
@@ -40,32 +41,28 @@ Version `1.1.0` supports ActiveRecord 4.0 and 4.1
40
41
 
41
42
  Version `0.6.0` supports earlier versions of ruby and ActiveRecord:
42
43
 
43
- * Ruby 1.8.7 or later
44
- * ActiveRecord 3.0.3 - 3.2.x
45
- * rgeo 0.3.20 or later
46
- * arel 2.0.6 or later
44
+ - Ruby 1.8.7 or later
45
+ - ActiveRecord 3.0.3 - 3.2.x
46
+ - rgeo 0.3.20 or later
47
+ - arel 2.0.6 or later
47
48
 
48
49
  ### Spatial Factories for Columns
49
50
 
50
- `rgeo_factory_generator` and related methods were removed in version 4.0, since column types
51
- are no longer tied to their database column in ActiveRecord 4.2.
51
+ **_This is an introduction. More details are available in the [wiki entry](https://github.com/rgeo/rgeo-activerecord/wiki/Spatial-Factory-Store)._**
52
52
 
53
- Register spatial factories in the `SpatialFactoryStore` singleton class. Each spatial type
54
- in your ActiveRecord models will use the `SpatialFactoryStore` to retrieve
55
- a factory matching the properties of its type. For example, you can set a different
56
- spatial factory for point types, or for types matching a specific SRID, or having
53
+ Register spatial factories in the `SpatialFactoryStore` singleton class to parse spatial data. Each spatial column
54
+ in your models will use the `SpatialFactoryStore` to parse the stored WKB into an RGeo Feature. The factory from the `SpatialFactoryStore` is chosen based on metadata from the spatial column and the attributes with which the factory was registered to the store. For example, you can set a factory for point types, for types matching a specific SRID, having
57
55
  a Z coordinate, or any combination of attributes.
58
56
 
59
- The supported keys when registering a spatial type are listed here with their default values
60
- and other allowed values:
57
+ The supported keys when registering a spatial type are listed here with their expected values:
61
58
 
62
59
  ```
63
- geo_type: "geometry", # point, polygon, line_string, geometry_collection,
64
- # multi_line_string, multi_point, multi_polygon
65
- has_m: false, # true
66
- has_z: false, # true
67
- sql_type: "geometry", # geography
68
- srid: 0, # (any valid SRID)
60
+ geo_type: string # geometry, point, polygon, line_string, geometry_collection,
61
+ # multi_line_string, multi_point, multi_polygon
62
+ has_m: boolean # true, false
63
+ has_z: boolean # true, false
64
+ sql_type: string # geometry, geography
65
+ srid: int # (any valid SRID)
69
66
  ```
70
67
 
71
68
  The default factories are `RGeo::Geographic.spherical_factory` for
@@ -73,7 +70,7 @@ geographic types, and `RGeo::Cartesian.preferred_factory` for geometric types.
73
70
 
74
71
  Here is an example setup:
75
72
 
76
- ```ruby
73
+ ```rb
77
74
  RGeo::ActiveRecord::SpatialFactoryStore.instance.tap do |config|
78
75
  # By default, use the GEOS implementation for spatial columns.
79
76
  config.default = RGeo::Geos.factory_generator
@@ -83,6 +80,33 @@ RGeo::ActiveRecord::SpatialFactoryStore.instance.tap do |config|
83
80
  end
84
81
  ```
85
82
 
83
+ _NOTE: `rgeo_factory_generator` and related methods were removed in version 4.0, since column types
84
+ are no longer tied to their database column in ActiveRecord 4.2._
85
+
86
+ ### Spatial Queries
87
+
88
+ RGeo-ActiveRecord provides an Arel interface to use functions commonly found in spatial databases. The interface also allows for the creation of your own spatial functions if they are not defined.
89
+
90
+ Here is an example using `st_contains`:
91
+
92
+ ```rb
93
+ point = RGeo::Geos.factory(srid: 0).point(1,1)
94
+
95
+ buildings = Building.arel_table
96
+ containing_buiildings = Building.where(buildings[:geom].st_contains(point))
97
+ ```
98
+
99
+ or using the `Arel.spatial` node:
100
+
101
+ ```rb
102
+ point = "SRID=0;POINT(1,1)"
103
+
104
+ buildings = Building.arel_table
105
+ containing_buiildings = Building.where(buildings[:geom].st_contains(Arel.spatial(point)))
106
+ ```
107
+
108
+ _Note: If you pass a WKT representation into an st_function, you should prepend the string with SRID=your_srid, otherwise the database will assume SRID=0 which may cause errors on certain operations._
109
+
86
110
  ### RGeo Dependency
87
111
 
88
112
  See the README for the [rgeo](https://github.com/rgeo/rgeo) gem, a dependency, for further
@@ -107,15 +131,17 @@ http://groups.google.com/group/rgeo-users
107
131
  ### Acknowledgments
108
132
 
109
133
  [Daniel Azuma](http://www.daniel-azuma.com) created RGeo.
110
- [Tee Parham](http://twitter.com/teeparham) is the current maintainer.
134
+ [Tee Parham](http://twitter.com/teeparham) is a former maintainer.
135
+ [Keith Doggett](http://www.github.com/keithdoggett) is a current maintainer.
136
+ [Ulysse Buonomo](http://www.github.com/BuonOmo) is a current maintainer.
111
137
 
112
138
  Development is supported by:
113
139
 
114
- * [Pirq](http://pirq.com)
115
- * [Neighborland](https://neighborland.com)
140
+ - [Klaxit](https://www.klaxit.com)
141
+ - Goldfish Ads
116
142
 
117
143
  ### License
118
144
 
119
- Copyright 2015 Daniel Azuma, Tee Parham
145
+ Copyright 2020 Daniel Azuma, Tee Parham
120
146
 
121
147
  https://github.com/rgeo/rgeo-activerecord/blob/master/LICENSE.txt
@@ -24,14 +24,13 @@ module RGeo
24
24
 
25
25
  def visit_RGeo_ActiveRecord_SpatialNamedFunction(node, collector)
26
26
  name = st_func(node.name)
27
- exprs = []
28
- node.expressions.each_with_index do |expr, index|
29
- exprs << (node.spatial_argument?(index) ? visit_in_spatial_context(expr, collector) : visit(expr, collector))
30
- end
31
27
  collector << name
32
28
  collector << "("
33
29
  collector << "DISTINCT " if node.distinct
34
- collector << exprs.join(", ")
30
+ node.expressions.each_with_index do |expr, index|
31
+ node.spatial_argument?(index) ? visit_in_spatial_context(expr, collector) : visit(expr, collector)
32
+ collector << ", " unless index == node.expressions.size - 1
33
+ end
35
34
  collector << ")"
36
35
  collector << " AS #{visit(node.alias, collector)}" if node.alias
37
36
  collector
@@ -41,13 +40,15 @@ module RGeo
41
40
  # The node must be a string (in which case it is treated as WKT),
42
41
  # an RGeo feature, or a spatial attribute.
43
42
  def visit_in_spatial_context(node, collector)
44
- case node
45
- when String
46
- collector << "#{st_func('ST_WKTToSQL')}(#{quote(node)})"
47
- when RGeo::Feature::Instance
48
- collector << visit_RGeo_Feature_Instance(node, collector)
49
- when RGeo::Cartesian::BoundingBox
50
- collector << visit_RGeo_Cartesian_BoundingBox(node, collector)
43
+ if node.is_a?(String)
44
+ collector << "#{st_func('ST_GeomFromText')}(#{quote(node)})"
45
+ elsif node.is_a?(RGeo::Feature::Instance)
46
+ srid = node.srid
47
+ collector << "#{st_func('ST_GeomFromText')}(#{quote(node.to_s)}, #{srid})"
48
+ elsif node.is_a?(RGeo::Cartesian::BoundingBox)
49
+ geom = node.to_geometry
50
+ srid = geom.srid
51
+ collector << "#{st_func('ST_GeomFromText')}(#{quote(geom.to_s)}, #{srid})"
51
52
  else
52
53
  visit(node, collector)
53
54
  end
@@ -86,11 +87,6 @@ module RGeo
86
87
  alias :visit_RGeo_Cartesian_BoundingBox :visit_String
87
88
  end
88
89
 
89
- Arel::Visitors::ToSql.class_eval do
90
- alias :visit_RGeo_Feature_Instance :visit_String
91
- alias :visit_RGeo_Cartesian_BoundingBox :visit_String
92
- end
93
-
94
90
  # A NamedFunction subclass that keeps track of the spatial-ness of
95
91
  # the arguments and return values, so that it can provide context to
96
92
  # visitors that want to interpret syntax differently when dealing with
@@ -4,16 +4,17 @@ module RGeo
4
4
  module ActiveRecord
5
5
  class SpatialFactoryStore
6
6
  include Singleton
7
+ Entry = Struct.new(:attrs, :factory)
7
8
 
8
9
  attr_accessor :registry
9
10
 
10
11
  def initialize
11
- @registry = {}
12
+ @registry = []
12
13
  @default = nil
13
14
  end
14
15
 
15
16
  def register(factory, attrs = {})
16
- registry[key(attrs)] = factory
17
+ registry.push(Entry.new(filter_attrs(attrs), factory))
17
18
  end
18
19
 
19
20
  def default(attrs = {})
@@ -25,11 +26,11 @@ module RGeo
25
26
  end
26
27
 
27
28
  def factory(attrs)
28
- registry[key(attrs)] || default(attrs)
29
+ closest_factory(attrs) || default(attrs)
29
30
  end
30
31
 
31
32
  def clear
32
- @registry = {}
33
+ @registry = []
33
34
  end
34
35
 
35
36
  private
@@ -50,14 +51,53 @@ module RGeo
50
51
  }
51
52
  end
52
53
 
53
- def key(attrs)
54
- {
55
- geo_type: "geometry",
56
- has_m: false,
57
- has_z: false,
58
- sql_type: "geometry",
59
- srid: 0,
60
- }.merge(attrs).hash
54
+ def filter_attrs(attrs)
55
+ attrs.slice(:geo_type, :has_m, :has_z, :sql_type, :srid)
56
+ end
57
+
58
+ ##
59
+ # Match attrs to the closest equal to or less specific factory
60
+ #
61
+ # That means that attrs can at most be matched to an Entry with the same
62
+ # number of keys as it. But could match with a factory with only 1 key
63
+ # in its attrs.
64
+ #
65
+ # Examples:
66
+ # attrs = {sql_type: "geometry" }, entry_attrs = {sql_type: "geometry", geo_type: "point"}
67
+ # is not a match because the entry is more specific than attrs
68
+ #
69
+ # attrs = {sql_type: "geometry", geo_type: "point"}, entry_attrs = {sql_type: "geometry"}
70
+ # is a match because the entry is less specific than attrs and would be the fallback for all "geometry" types
71
+ #
72
+ # attrs = {sql_type: "geometry", geo_type: "point"}, entry_attrs = {sql_type: "geometry", geo_type: "linestring"}
73
+ # is not a match because there are mismatched keys
74
+ #
75
+ # If there is no match, nil is returned
76
+ def closest_factory(attrs)
77
+ max_matches = 0
78
+ registry.reduce(nil) do |selected_fac, entry|
79
+ cmp = cmp_attrs(attrs, entry.attrs)
80
+ if cmp > max_matches
81
+ max_matches = cmp
82
+ entry.factory
83
+ else
84
+ selected_fac
85
+ end
86
+ end
87
+ end
88
+
89
+ ##
90
+ # Returns number of common key/values
91
+ # or -1 if oth is bigger than attrs, or they have a mismatched key/value pair
92
+ def cmp_attrs(attrs, oth)
93
+ return -1 if oth.size > attrs.size
94
+ matches = 0
95
+ attrs.each do |k, v|
96
+ next if oth[k].nil?
97
+ return -1 unless v == oth[k]
98
+ matches += 1
99
+ end
100
+ matches
61
101
  end
62
102
  end
63
103
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RGeo
4
4
  module ActiveRecord
5
- VERSION = "6.2.2"
5
+ VERSION = "7.0.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rgeo-activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.2.2
4
+ version: 7.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Azuma
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-12-11 00:00:00.000000000 Z
12
+ date: 2020-12-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -123,6 +123,20 @@ dependencies:
123
123
  - - ">="
124
124
  - !ruby/object:Gem::Version
125
125
  version: 1.0.0
126
+ - !ruby/object:Gem::Dependency
127
+ name: simplecov
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 0.20.0
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: 0.20.0
126
140
  description: RGeo is a geospatial data library for Ruby. RGeo::ActiveRecord is an
127
141
  optional RGeo module providing some spatial extensions to ActiveRecord, as well
128
142
  as common tools used by RGeo-based spatial adapters.
@@ -164,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
178
  - !ruby/object:Gem::Version
165
179
  version: '0'
166
180
  requirements: []
167
- rubygems_version: 3.0.3
181
+ rubygems_version: 3.0.8
168
182
  signing_key:
169
183
  specification_version: 4
170
184
  summary: An RGeo module providing spatial extensions to ActiveRecord.