activerecord-cockroachdb-adapter 0.2.3 → 5.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.gitmodules +0 -3
- data/CONTRIBUTING.md +25 -53
- data/Gemfile +58 -6
- data/README.md +293 -2
- data/Rakefile +17 -5
- data/activerecord-cockroachdb-adapter.gemspec +3 -6
- data/build/Dockerfile +1 -1
- data/build/teamcity-test.sh +17 -37
- data/docker.sh +1 -1
- data/lib/active_record/connection_adapters/cockroachdb/arel_tosql.rb +27 -0
- data/lib/active_record/connection_adapters/cockroachdb/attribute_methods.rb +28 -0
- data/lib/active_record/connection_adapters/cockroachdb/column.rb +94 -0
- data/lib/active_record/connection_adapters/cockroachdb/column_methods.rb +53 -0
- data/lib/active_record/connection_adapters/cockroachdb/database_statements.rb +102 -0
- data/lib/active_record/connection_adapters/cockroachdb/oid/spatial.rb +121 -0
- data/lib/active_record/connection_adapters/cockroachdb/quoting.rb +37 -0
- data/lib/active_record/connection_adapters/cockroachdb/referential_integrity.rb +23 -38
- data/lib/active_record/connection_adapters/cockroachdb/schema_statements.rb +123 -40
- data/lib/active_record/connection_adapters/cockroachdb/setup.rb +19 -0
- data/lib/active_record/connection_adapters/cockroachdb/spatial_column_info.rb +44 -0
- data/lib/active_record/connection_adapters/cockroachdb/table_definition.rb +56 -0
- data/lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb +14 -16
- data/lib/active_record/connection_adapters/cockroachdb/type.rb +14 -0
- data/lib/active_record/connection_adapters/cockroachdb_adapter.rb +218 -123
- metadata +18 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ef8a3ba83448f71d7a2d0e5a4d21f065531f17a862aa8a33e7ed4e9470927f0a
|
4
|
+
data.tar.gz: ae23824599ad3bc37773f99e08a143b7242e81094098674d22499df7153c5856
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4100c35b51262901f1f087be1d0a5b4864f45a89585878e5d00aa12919863cdc66cd8ffddfcd0f3b16037bd6b21539845ddbe18c7901fd2f48f7d7e807f61f0b
|
7
|
+
data.tar.gz: accca94dcb97b512e261a676d031657c8cc34d22590866744ffd6dbaf757a67386d9c642638d77aa4354d5281437e7a5e8c5c5dc141335f441422e20a3605aee
|
data/.gitignore
CHANGED
data/.gitmodules
CHANGED
data/CONTRIBUTING.md
CHANGED
@@ -11,15 +11,19 @@ CockroachDBAdapter to initialize ActiveRecord for their projects.
|
|
11
11
|
This adapter extends the PostgreSQL ActiveRecord adapter in order to
|
12
12
|
override and monkey-patch functionality.
|
13
13
|
|
14
|
-
The other repository is a fork of [Rails]. The tests have been modified
|
15
|
-
for the purposes of testing our CockroachDB adapter.
|
16
|
-
|
17
14
|
[activerecord-cockroachdb-adapter]: https://github.com/cockroachdb/activerecord-cockroachdb-adapter/
|
18
|
-
[Rails]: https://github.com/lego/ruby-on-rails
|
19
|
-
|
20
15
|
|
21
16
|
## Setup and running tests
|
22
17
|
|
18
|
+
In CockroachDB, create two databases to be used by the ActiveRecord test suite:
|
19
|
+
activerecord_unittest and activerecord_unittest2.
|
20
|
+
|
21
|
+
```sql
|
22
|
+
CREATE DATABASE activerecord_unittest;
|
23
|
+
|
24
|
+
CREATE DATABASE activerecord_unittest2;
|
25
|
+
```
|
26
|
+
|
23
27
|
It is best to have a Ruby environment manager installed, such as
|
24
28
|
[rvm](https://rvm.io/), as Rails has varying Ruby version requirements.
|
25
29
|
If you are using rvm, you then install and use the required Ruby
|
@@ -37,65 +41,42 @@ rvm use 2.2.5
|
|
37
41
|
```
|
38
42
|
|
39
43
|
Using [bundler](http://bundler.io/), install the dependancies of Rails.
|
40
|
-
Additionally, make sure the Rails git submodule is loaded.
|
41
44
|
|
42
45
|
```bash
|
43
|
-
|
44
|
-
git submodule update
|
45
|
-
# Install rails dependancies.
|
46
|
-
(cd rails && bundle install)
|
46
|
+
bundle install
|
47
47
|
```
|
48
48
|
|
49
|
-
Then, to run the test with an active CockroachDB instance:
|
49
|
+
Then, to run the full test suite with an active CockroachDB instance:
|
50
50
|
|
51
51
|
```bash
|
52
|
-
|
53
|
-
(cd rails/activerecord && BUNDLE_GEMFILE=../Gemfile bundle exec rake db:cockroachdb:rebuild)
|
54
|
-
(cd rails/activerecord && BUNDLE_GEMFILE=../Gemfile bundle exec rake test:cockroachdb)
|
52
|
+
bundle exec rake test
|
55
53
|
```
|
56
54
|
|
57
|
-
|
55
|
+
To run specific ActiveRecord tests, set environemnt variable `TEST_FILES_AR`. For example, to run ActiveRecord tests `test/cases/associations_test.rb` and `test/cases/ar_schema_test.rb.rb`
|
58
56
|
|
59
57
|
```bash
|
60
|
-
|
58
|
+
TEST_FILES_AR="test/cases/associations_test.rb,test/cases/ar_schema_test.rb" bundle exec rake test
|
61
59
|
```
|
62
60
|
|
63
|
-
|
64
|
-
This configuration specifies:
|
65
|
-
|
66
|
-
- CockroachDB port and host the test suite uses.
|
67
|
-
- Database names used for the different test connections. (ActiveRecord
|
68
|
-
uses two separate connections for some tests.)
|
61
|
+
To run specific CockroachDB Adapter tests, set environemnt variable `TEST_FILES`. For example, to run CockroachDB Adpater tests `test/cases/adapter_test.rb` and `test/cases/associations/left_outer_join_association_test.rb`
|
69
62
|
|
70
|
-
```
|
71
|
-
|
63
|
+
```bash
|
64
|
+
TEST_FILES="test/cases/adapter_test.rb,test/cases/associations/left_outer_join_association_test.rb" bundle exec rake test
|
72
65
|
```
|
73
66
|
|
74
|
-
|
75
|
-
re-creates all of the databases needed.
|
67
|
+
To run a specific test case, use minitest's `-n` option to run tests that match a given pattern. All minitest options are set via the `TESTOPTS` environemnt variable. For example, to run `test_indexes` from CockroachDB's `test/cases/adapter_test.rb` file
|
76
68
|
|
77
|
-
|
78
|
-
|
79
|
-
tasks) such as executing tests.
|
80
|
-
- `BUNDLE_GEMFILE=../Gemfile` tells `bundle` to use the dependancies for
|
81
|
-
Rails that were previously installed.
|
82
|
-
- `bundle exec rake` uses `bundle` to execute the Ruby package `rake`.
|
83
|
-
- `rake db:cockroachdb:rebuild` runs the specified Rake task. All tasks
|
84
|
-
can be found in `rails/activerecord/Rakefile`.
|
85
|
-
|
86
|
-
|
87
|
-
```
|
88
|
-
(cd rails/activerecord && BUNDLE_GEMFILE=../Gemfile bundle exec rake test:cockroachdb)
|
69
|
+
```bash
|
70
|
+
TEST_FILES="test/cases/adapter_test.rb" TESTOPTS=`-n=/test_indexes/` bundle exec rake test
|
89
71
|
```
|
90
72
|
|
91
|
-
|
73
|
+
By default, tests will be run from the bundled version of Rails. To run against a local copy, set environemnt variable `RAILS_SOURCE`. Running against a local copy of Rails can be helpful when try to debug issues.
|
92
74
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
`TESTFILES=test/cases/attribute_methods.rb` to the command. Globs are
|
97
|
-
used. Multiple individual files cannot be specified.
|
75
|
+
```bash
|
76
|
+
RAILS_SOURCE="path/to/local_copy" bundle exec rake test
|
77
|
+
```
|
98
78
|
|
79
|
+
`test/config.yml` assumes CockroachDB will be running at localhost:26257 with a root user. Make changes to `test/config.yml` as needed.
|
99
80
|
|
100
81
|
# Improvements
|
101
82
|
|
@@ -143,15 +124,6 @@ A possible way to approach this would be to add a shim to cause any
|
|
143
124
|
tests that use it to fail, and grep the tests that pass and then skip
|
144
125
|
them.
|
145
126
|
|
146
|
-
# Cleanup
|
147
|
-
|
148
|
-
One of the earlier commits to the Rails repo did a big grep of
|
149
|
-
`PostgreSQLAdapter` -> `CockroachDBAdapter`. In order to better support
|
150
|
-
changes upstream, this modification should be changed to instead only
|
151
|
-
add `CockroachDBAdapter` alongside any `PostgreSQLAdapter`. The later
|
152
|
-
test cleanup commit will conflict on any further changes (like adding
|
153
|
-
back PostgreSQL, or removing CockroachDB for PostgreSQL).
|
154
|
-
|
155
127
|
## Publishing to Rubygems
|
156
128
|
|
157
129
|
TODO: Expand on this. Jordan is likely the only person with publishing
|
data/Gemfile
CHANGED
@@ -1,11 +1,63 @@
|
|
1
|
+
require 'openssl'
|
1
2
|
source 'https://rubygems.org'
|
3
|
+
gemspec
|
2
4
|
|
3
|
-
|
5
|
+
if ENV['RAILS_SOURCE']
|
6
|
+
gemspec path: ENV['RAILS_SOURCE']
|
7
|
+
else
|
8
|
+
def get_version_from_gemspec
|
9
|
+
gemspec = eval(File.read('activerecord-cockroachdb-adapter.gemspec'))
|
4
10
|
|
5
|
-
|
11
|
+
gem_version = gemspec.dependencies.
|
12
|
+
find { |dep| dep.name == 'activerecord' }.
|
13
|
+
requirement.
|
14
|
+
requirements.
|
15
|
+
first.
|
16
|
+
last
|
6
17
|
|
7
|
-
|
8
|
-
|
18
|
+
major, minor, tiny, pre = gem_version.segments
|
19
|
+
|
20
|
+
if pre
|
21
|
+
gem_version.to_s
|
22
|
+
else
|
23
|
+
find_latest_matching_version(major, minor)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_latest_matching_version(gemspec_major, gemspec_minor)
|
28
|
+
all_activerecord_versions.
|
29
|
+
reject { |version| version["prerelease"] }.
|
30
|
+
map { |version| version["number"].split(".").map(&:to_i) }.
|
31
|
+
find { |major, minor|
|
32
|
+
major == gemspec_major && (minor == gemspec_minor || gemspec_minor.nil?)
|
33
|
+
}.join(".")
|
34
|
+
end
|
35
|
+
|
36
|
+
def all_activerecord_versions
|
37
|
+
require 'net/http'
|
38
|
+
require 'yaml'
|
39
|
+
|
40
|
+
uri = URI.parse "https://rubygems.org/api/v1/versions/activerecord.yaml"
|
41
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
42
|
+
http.use_ssl = true
|
43
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
44
|
+
|
45
|
+
YAML.load(
|
46
|
+
http.request(Net::HTTP::Get.new(uri.request_uri)).body
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Get Rails from source beacause the gem doesn't include tests
|
51
|
+
version = ENV['RAILS_VERSION'] || get_version_from_gemspec
|
52
|
+
gem 'rails', git: "https://github.com/rails/rails.git", tag: "v#{version}"
|
53
|
+
end
|
54
|
+
|
55
|
+
group :development do
|
56
|
+
gem "byebug"
|
57
|
+
gem "minitest-excludes"
|
9
58
|
|
10
|
-
#
|
11
|
-
gem "
|
59
|
+
# Gems used by the ActiveRecord test suite
|
60
|
+
gem "bcrypt"
|
61
|
+
gem "mocha"
|
62
|
+
gem "sqlite3"
|
63
|
+
end
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ CockroachDB adapter for ActiveRecord 4 and 5. This is a lightweight extension of
|
|
7
7
|
Add this line to your project's Gemfile:
|
8
8
|
|
9
9
|
```ruby
|
10
|
-
gem 'activerecord-cockroachdb-adapter', '~>
|
10
|
+
gem 'activerecord-cockroachdb-adapter', '~> 5.2.0'
|
11
11
|
```
|
12
12
|
|
13
13
|
If you're using Rails 4.x, use the `0.1.x` versions of this gem.
|
@@ -22,8 +22,299 @@ development:
|
|
22
22
|
user: <username>
|
23
23
|
```
|
24
24
|
|
25
|
+
## Working with Spatial Data
|
26
|
+
|
27
|
+
The adapter uses [RGeo](https://github.com/rgeo/rgeo) and [RGeo-ActiveRecord](https://github.com/rgeo/rgeo-activerecord) to represent geometric and geographic data as Ruby objects and easily interface them with the adapter. The following is a brief introduction to RGeo and tips to help setup your spatial application. More documentation about RGeo can be found in the [YARD Docs](https://rubydoc.info/github/rgeo/rgeo) and [wiki](https://github.com/rgeo/rgeo/wiki).
|
28
|
+
|
29
|
+
### Installing RGeo
|
30
|
+
|
31
|
+
RGeo can be installed with the following command:
|
32
|
+
|
33
|
+
```sh
|
34
|
+
gem install rgeo
|
35
|
+
```
|
36
|
+
|
37
|
+
The best way to use RGeo is with GEOS support. If you have a version of libgeos installed, you can check that it was properly linked with RGeo by running the following commands:
|
38
|
+
|
39
|
+
```rb
|
40
|
+
require 'rgeo'
|
41
|
+
|
42
|
+
RGeo::Geos.supported?
|
43
|
+
#=> true
|
44
|
+
```
|
45
|
+
|
46
|
+
If this is `false`, you may need to specify the GEOS directory while installing. Here's an example linking it to the CockroachDB GEOS binary.
|
47
|
+
|
48
|
+
```sh
|
49
|
+
gem install rgeo -- --with-geos-dir=/path/to/cockroach/lib/
|
50
|
+
```
|
51
|
+
|
52
|
+
### Working with RGeo
|
53
|
+
|
54
|
+
RGeo uses [factories](https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)) to create geometry objects and define their properties. Different factories define their own implementations for standard methods. For instance, the `RGeo::Geographic.spherical_factory` accepts latitudes and longitues as its coordinates and does computations on a spherical surface, while `RGeo::Cartesian.factory` implements geometry objects on a plane.
|
55
|
+
|
56
|
+
The factory (or factories) you choose to use will depend on the requirements of your application and what you need to do with the geometries they produce. For example, if you are working with points or other simple geometries across long distances and need precise results, the spherical factory is a good choice. If you're working with polygons or multipolygons and analyzing complex relationships between them (`intersects?`, `difference`, etc.), then using a cartesian factory backed by GEOS is a much better option.
|
57
|
+
|
58
|
+
Once you've selected a factory, you need to create objects. RGeo supports geometry creation through standard constructors (`point`, `line_string`, `polygon`, etc.) or by WKT and WKB.
|
59
|
+
|
60
|
+
```rb
|
61
|
+
require 'rgeo'
|
62
|
+
factory = RGeo::Cartesian.factory(srid: 3857)
|
63
|
+
|
64
|
+
# Create a line_string from points
|
65
|
+
pt1 = factory.point(0,0)
|
66
|
+
pt2 = factory.point(1,1)
|
67
|
+
pt3 = factory.point(2,2)
|
68
|
+
line_string = factory.line_string([pt1,pt2,pt3])
|
69
|
+
|
70
|
+
p line_string.length
|
71
|
+
#=> 2.8284271247461903
|
72
|
+
|
73
|
+
# check line_string equality
|
74
|
+
line_string2 = factory.parse_wkt("LINESTRING (0 0, 1 1, 2 2)")
|
75
|
+
p line_string == line_string2
|
76
|
+
#=> true
|
77
|
+
|
78
|
+
# create polygon and test intersection with line_string
|
79
|
+
pt4 = factory.point(0,2)
|
80
|
+
outer_ring = factory.linear_ring([pt1,pt2,pt3,pt4,pt1])
|
81
|
+
poly = factory.polygon(outer_ring)
|
82
|
+
|
83
|
+
p line_string.intersects? poly
|
84
|
+
#=> true
|
85
|
+
```
|
86
|
+
### Creating Spatial Tables
|
87
|
+
|
88
|
+
To store spatial data, you must create a column with a spatial type. PostGIS
|
89
|
+
provides a variety of spatial types, including point, linestring, polygon, and
|
90
|
+
different kinds of collections. These types are defined in a standard produced
|
91
|
+
by the Open Geospatial Consortium. You can specify options indicating the coordinate system and number of coordinates for the values you are storing.
|
92
|
+
|
93
|
+
The adapter extends ActiveRecord's migration syntax to
|
94
|
+
support these spatial types. The following example creates five spatial
|
95
|
+
columns in a table:
|
96
|
+
|
97
|
+
```rb
|
98
|
+
create_table :my_spatial_table do |t|
|
99
|
+
t.column :shape1, :geometry
|
100
|
+
t.geometry :shape2
|
101
|
+
t.line_string :path, srid: 3857
|
102
|
+
t.st_point :lonlat, geographic: true
|
103
|
+
t.st_point :lonlatheight, geographic: true, has_z: true
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
The first column, "shape1", is created with type "geometry". This is a general
|
108
|
+
"base class" for spatial types; the column declares that it can contain values
|
109
|
+
of _any_ spatial type.
|
110
|
+
|
111
|
+
The second column, "shape2", uses a shorthand syntax for the same type as the shape1 column.
|
112
|
+
You can create a column either by invoking `column` or invoking the name of the type directly.
|
113
|
+
|
114
|
+
The third column, "path", has a specific geometric type, `line_string`. It
|
115
|
+
also specifies an SRID (spatial reference ID) that indicates which coordinate
|
116
|
+
system it expects the data to be in. The column now has a "constraint" on it;
|
117
|
+
it will accept only LineString data, and only data whose SRID is 3857.
|
118
|
+
|
119
|
+
The fourth column, "lonlat", has the `st_point` type, and accepts only Point
|
120
|
+
data. Furthermore, it declares the column as "geographic", which means it
|
121
|
+
accepts longitude/latitude data, and performs calculations such as distances
|
122
|
+
using a spheroidal domain.
|
123
|
+
|
124
|
+
The fifth column, "lonlatheight", is a geographic (longitude/latitude) point
|
125
|
+
that also includes a third "z" coordinate that can be used to store height
|
126
|
+
information.
|
127
|
+
|
128
|
+
The following are the data types understood by PostGIS and exposed by
|
129
|
+
the adapter:
|
130
|
+
|
131
|
+
- `:geometry` -- Any geometric type
|
132
|
+
- `:st_point` -- Point data
|
133
|
+
- `:line_string` -- LineString data
|
134
|
+
- `:st_polygon` -- Polygon data
|
135
|
+
- `:geometry_collection` -- Any collection type
|
136
|
+
- `:multi_point` -- A collection of Points
|
137
|
+
- `:multi_line_string` -- A collection of LineStrings
|
138
|
+
- `:multi_polygon` -- A collection of Polygons
|
139
|
+
|
140
|
+
Following are the options understood by the adapter:
|
141
|
+
|
142
|
+
- `:geographic` -- If set to true, create a PostGIS geography column for
|
143
|
+
longitude/latitude data over a spheroidal domain; otherwise create a
|
144
|
+
geometry column in a flat coordinate system. Default is false. Also
|
145
|
+
implies :srid set to 4326.
|
146
|
+
- `:srid` -- Set a SRID constraint for the column. Default is 4326 for a
|
147
|
+
geography column, or 0 for a geometry column. Note that PostGIS currently
|
148
|
+
(as of version 2.0) requires geography columns to have SRID 4326, so this
|
149
|
+
constraint is of limited use for geography columns.
|
150
|
+
- `:has_z` -- Specify that objects in this column include a Z coordinate.
|
151
|
+
Default is false.
|
152
|
+
- `:has_m` -- Specify that objects in this column include an M coordinate.
|
153
|
+
Default is false.
|
154
|
+
|
155
|
+
To create a PostGIS spatial index, add `using: :gist` to your index:
|
156
|
+
|
157
|
+
```rb
|
158
|
+
add_index :my_table, :lonlat, using: :gist
|
159
|
+
|
160
|
+
# or
|
161
|
+
|
162
|
+
change_table :my_table do |t|
|
163
|
+
t.index :lonlat, using: :gist
|
164
|
+
end
|
165
|
+
```
|
166
|
+
### Configuring ActiveRecord
|
167
|
+
|
168
|
+
ActiveRecord's usefulness stems from the way it automatically configures
|
169
|
+
classes based on the database structure and schema. If a column in the
|
170
|
+
database has an integer type, ActiveRecord automatically casts the data to a
|
171
|
+
Ruby Integer. In the same way, the adapter automatically
|
172
|
+
casts spatial data to a corresponding RGeo data type.
|
173
|
+
|
174
|
+
RGeo offers more flexibility in its type system than can be
|
175
|
+
interpreted solely from analyzing the database column. For example, you can
|
176
|
+
configure RGeo objects to exhibit certain behaviors related to their
|
177
|
+
serialization, validation, coordinate system, or computation. These settings
|
178
|
+
are embodied in the RGeo factory associated with the object.
|
179
|
+
|
180
|
+
You can configure the adapter to use a particular factory (i.e. a
|
181
|
+
particular combination of settings) for data associated with each type in
|
182
|
+
the database.
|
183
|
+
|
184
|
+
Here's an example using a Geos default factory:
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
RGeo::ActiveRecord::SpatialFactoryStore.instance.tap do |config|
|
188
|
+
# By default, use the GEOS implementation for spatial columns.
|
189
|
+
config.default = RGeo::Geos.factory_generator
|
190
|
+
|
191
|
+
# But use a geographic implementation for point columns.
|
192
|
+
config.register(RGeo::Geographic.spherical_factory(srid: 4326), geo_type: "point")
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
The default spatial factory for geographic columns is `RGeo::Geographic.spherical_factory`.
|
197
|
+
The default spatial factory for cartesian columns is `RGeo::Cartesian.preferred_factory`.
|
198
|
+
You do not need to configure the `SpatialFactoryStore` if these defaults are ok.
|
199
|
+
|
200
|
+
More information about configuration options for the `SpatialFactoryStore` can be found in the [rgeo-activerecord](https://github.com/rgeo/rgeo-activerecord#spatial-factories-for-columns) docs.
|
201
|
+
|
202
|
+
### Reading and Writing Spatial Columns
|
203
|
+
|
204
|
+
When you access a spatial attribute on your ActiveRecord model, it is given to
|
205
|
+
you as an RGeo geometry object (or nil, for attributes that allow null
|
206
|
+
values). You can then call the RGeo api on the object. For example, consider
|
207
|
+
the MySpatialTable class we worked with above:
|
208
|
+
|
209
|
+
```rb
|
210
|
+
record = MySpatialTable.find(1)
|
211
|
+
point = record.lonlat # Returns an RGeo::Feature::Point
|
212
|
+
p point.x # displays the x coordinate
|
213
|
+
p point.geometry_type.type_name # displays "Point"
|
214
|
+
```
|
215
|
+
|
216
|
+
The RGeo factory for the value is determined by how you configured the
|
217
|
+
ActiveRecord class, as described above. In this case, we explicitly set a
|
218
|
+
spherical factory for the `:lonlat` column:
|
219
|
+
|
220
|
+
```rb
|
221
|
+
factory = point.factory # returns a spherical factory
|
222
|
+
```
|
223
|
+
|
224
|
+
You can set a spatial attribute by providing an RGeo geometry object, or by
|
225
|
+
providing the WKT string representation of the geometry. If a string is
|
226
|
+
provided, the adapter will attempt to parse it as WKT and
|
227
|
+
set the value accordingly.
|
228
|
+
|
229
|
+
```rb
|
230
|
+
record.lonlat = 'POINT(-122 47)' # sets the value to the given point
|
231
|
+
```
|
232
|
+
|
233
|
+
If the WKT parsing fails, the value currently will be silently set to nil. In
|
234
|
+
the future, however, this will raise an exception.
|
235
|
+
|
236
|
+
```rb
|
237
|
+
record.lonlat = 'POINT(x)' # sets the value to nil
|
238
|
+
```
|
239
|
+
|
240
|
+
If you set the value to an RGeo object, the factory needs to match the factory
|
241
|
+
for the attribute. If the factories do not match, the adapter
|
242
|
+
will attempt to cast the value to the correct factory.
|
243
|
+
|
244
|
+
```rb
|
245
|
+
p2 = factory.point(-122, 47) # p2 is a point in a spherical factory
|
246
|
+
record.lonlat = p2 # sets the value to the given point
|
247
|
+
record.shape1 = p2 # shape1 uses a flat geos factory, so it
|
248
|
+
# will cast p2 into that coordinate system
|
249
|
+
# before setting the value
|
250
|
+
record.save
|
251
|
+
```
|
252
|
+
|
253
|
+
If you attempt to set the value to the wrong type, such as setting a linestring attribute to a point value, you will get an exception from the database when you attempt to save the record.
|
254
|
+
|
255
|
+
```rb
|
256
|
+
record.path = p2 # This will appear to work, but...
|
257
|
+
record.save # This will raise an exception from the database
|
258
|
+
```
|
259
|
+
|
260
|
+
### Spatial Queries
|
261
|
+
|
262
|
+
You can create simple queries based on representational equality in the same
|
263
|
+
way you would on a scalar column:
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
record2 = MySpatialTable.where(:lonlat => factory.point(-122, 47)).first
|
267
|
+
```
|
268
|
+
|
269
|
+
You can also use WKT:
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
record3 = MySpatialTable.where(:lonlat => 'POINT(-122 47)').first
|
273
|
+
```
|
274
|
+
|
275
|
+
Note that these queries use representational equality, meaning they return
|
276
|
+
records where the lonlat value matches the given value exactly. A 0.00001
|
277
|
+
degree difference would not match, nor would a different representation of the
|
278
|
+
same geometry (like a multi_point with a single element). Equality queries
|
279
|
+
aren't generally all that useful in real world applications. Typically, if you
|
280
|
+
want to perform a spatial query, you'll look for, say, all the points within a
|
281
|
+
given area. For those queries, you'll need to use the standard spatial SQL
|
282
|
+
functions provided by PostGIS.
|
283
|
+
|
284
|
+
To perform more advanced spatial queries, you can use the extended Arel interface included in the adapter. The functions accept WKT strings or RGeo features.
|
285
|
+
|
286
|
+
```rb
|
287
|
+
point = RGeo::Geos.factory(srid: 0).point(1,1)
|
288
|
+
|
289
|
+
# Example Building model where geom is a column of polygons.
|
290
|
+
buildings = Building.arel_table
|
291
|
+
containing_buiildings = Building.where(buildings[:geom].st_contains(point))
|
292
|
+
```
|
293
|
+
|
294
|
+
See the [rgeo-activerecord YARD Docs](https://rubydoc.info/github/rgeo/rgeo-activerecord/RGeo/ActiveRecord/SpatialExpressions) for a list of available PostGIS functions.
|
295
|
+
|
296
|
+
### Validation Issues
|
297
|
+
|
298
|
+
If you see an `RGeo::Error::InvalidGeometry (LinearRing failed ring test)` message while loading data or creating geometries, this means that the geometry you are trying to instantiate is not topologically valid. This is usually due to self-intersections in the geometry. The default behavior of RGeo factories is to raise this error when an invalid geometry is being instansiated, but this can be ignored by setting the `uses_lenient_assertions` flag to `true` when creating your factory.
|
299
|
+
|
300
|
+
```rb
|
301
|
+
regular_fac = RGeo::Geographic.spherical_factory
|
302
|
+
modified_fac = RGeo::Geographic.spherical_factory(uses_lenient_assertions: true)
|
303
|
+
|
304
|
+
wkt = "POLYGON (0 0, 1 1, 0 1, 1 0, 0 0)" # closed ring with self intersection
|
305
|
+
|
306
|
+
regular_fac.parse_wkt(wkt)
|
307
|
+
#=> RGeo::Error::InvalidGeometry (LinearRing failed ring test)
|
308
|
+
|
309
|
+
p modified_fac.parse_wkt(wkt)
|
310
|
+
#=> #<RGeo::Geographic::SphericalPolygonImpl>
|
311
|
+
```
|
312
|
+
|
313
|
+
Be careful when performing calculations on potentially invalid geometries, as the results might be nonsensical. For example, the area returned of an hourglass made of 2 equivalent triangles with a self-intersection in the middle is 0.
|
314
|
+
|
315
|
+
Note that when using the `spherical_factory`, there is a chance that valid geometries will be interpreted as invalid due to floating point issues with small geometries.
|
25
316
|
|
26
317
|
## Modifying the adapter?
|
27
318
|
|
28
319
|
See [CONTRIBUTING.md](/CONTRIBUTING.md) for more details on setting up
|
29
|
-
the environment and making modifications.
|
320
|
+
the environment and making modifications.
|