matryoshka_view 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/CHANGELOG +41 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +33 -0
- data/Rakefile +13 -0
- data/lib/matryoshka_view/record.rb +68 -0
- data/lib/matryoshka_view/version.rb +3 -0
- data/lib/matryoshka_view.rb +158 -0
- data/matryoshka_view.gemspec +32 -0
- data/spec/matryoshka_view_spec.rb +112 -0
- data/spec/spec_helper.rb +62 -0
- metadata +201 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 5fd3641abdb255955bfd8e0b78b13e84edadf7b2
|
|
4
|
+
data.tar.gz: 679ada03ad403621c94785d2104ae357ea52b789
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 93a02dad73d62071868513a23a7ab3178800a0bb10ec76ffdd4ce952f780a8b6b8ed7b94fc396a3b8459f4c462f6fe80d26b865f4f64ecb0a201043f5a4bf175
|
|
7
|
+
data.tar.gz: dbc9b7e721f1a6696a2f88a087514b942b9d2c2ae0febf6d409ddcec46cc424850c273777ff8ccaf05c7c8c552cadd4805be9a3e3f7ece46aa55fc58a06a73aa
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
2014-10-09 / 0.2.1
|
|
2
|
+
|
|
3
|
+
* Bug fixes
|
|
4
|
+
|
|
5
|
+
* Properly determine containers - before we were inadvertently drawing a box around container geoms, which resulted in false positives
|
|
6
|
+
|
|
7
|
+
2014-08-21 / 0.2.0
|
|
8
|
+
|
|
9
|
+
* Breaking changes
|
|
10
|
+
|
|
11
|
+
* Switch to CREATE TABLE ... LIKE instead of materialized views
|
|
12
|
+
|
|
13
|
+
2014-08-20 / 0.1.2
|
|
14
|
+
|
|
15
|
+
* Bug fixes
|
|
16
|
+
|
|
17
|
+
* Don't ignore custom name when you find an old view
|
|
18
|
+
|
|
19
|
+
* Enhancements
|
|
20
|
+
|
|
21
|
+
* Provide MatryoshkaView::Record.cleanup
|
|
22
|
+
|
|
23
|
+
2014-08-20 / 0.1.1
|
|
24
|
+
|
|
25
|
+
* Enhancements
|
|
26
|
+
|
|
27
|
+
* One weird trick to make ST_Contains slightly less strict (so that stuff on the border of a view counts as contained)
|
|
28
|
+
|
|
29
|
+
2014-08-20 / 0.1.0
|
|
30
|
+
|
|
31
|
+
* Breaking changes
|
|
32
|
+
|
|
33
|
+
* MatryoshkaView#lookup now takes geom_source: or the_geom_geojson: arguments
|
|
34
|
+
|
|
35
|
+
* Bug fixes
|
|
36
|
+
|
|
37
|
+
* Fix use of ST_Contains during view creation
|
|
38
|
+
|
|
39
|
+
0.0.1 / 2014-08-20
|
|
40
|
+
|
|
41
|
+
initial release
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2014 Seamus Abshere
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# MatryoshkaView
|
|
2
|
+
|
|
3
|
+
Maintains a list of inner (subset/nested) views and their geometic boundaries for a particular table. Helps you spawn new inner views and lookup the right one.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'matryoshka_view'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
$ bundle
|
|
16
|
+
|
|
17
|
+
Or install it yourself as:
|
|
18
|
+
|
|
19
|
+
$ gem install matryoshka_view
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
1. create a MatryoshkaView object with the base table name (`MatryoshkaView.new(base: Place)`)
|
|
24
|
+
2. maybe try to lookup a view that satisfies a boundary (`MatryoshkaView.new(base: Place).lookup(the_geom_geojson_of_massachusetts)`)
|
|
25
|
+
3. maybe create a new view within a boundary (`MatryoshkaView.new(base: Place).spawn(the_geom_geojson_of_boston)`)
|
|
26
|
+
|
|
27
|
+
## Contributing
|
|
28
|
+
|
|
29
|
+
1. Fork it ( https://github.com/[my-github-username]/matryoshka_view/fork )
|
|
30
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
31
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
32
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
33
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'the_geom_geojson'
|
|
2
|
+
require 'the_geom_geojson/active_record'
|
|
3
|
+
|
|
4
|
+
class MatryoshkaView
|
|
5
|
+
class Record < ActiveRecord::Base
|
|
6
|
+
class << self
|
|
7
|
+
def create_table
|
|
8
|
+
activity = false
|
|
9
|
+
connection_pool.with_connection do |c|
|
|
10
|
+
unless table_exists?
|
|
11
|
+
c.execute "CREATE TABLE #{quoted_table_name}(name text UNIQUE NOT NULL)"
|
|
12
|
+
activity = true
|
|
13
|
+
end
|
|
14
|
+
existing_columns = column_names
|
|
15
|
+
if (missing_columns = COLUMNS.keys - column_names).any?
|
|
16
|
+
activity = true
|
|
17
|
+
add_columns = missing_columns.map {|name| %{ADD COLUMN "#{name}" #{COLUMNS[name]}} }
|
|
18
|
+
c.execute "ALTER TABLE #{quoted_table_name} #{add_columns.join(',')}"
|
|
19
|
+
missing_columns.each do |name|
|
|
20
|
+
case name
|
|
21
|
+
when /the_geom/
|
|
22
|
+
c.execute "CREATE INDEX ON #{quoted_table_name} USING gist(#{name})"
|
|
23
|
+
else
|
|
24
|
+
c.execute "CREATE INDEX ON #{quoted_table_name} (#{name})"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
if activity
|
|
29
|
+
c.schema_cache.clear!
|
|
30
|
+
reset_column_information
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def cleanup
|
|
36
|
+
find_each do |record|
|
|
37
|
+
record.destroy unless record.exists?
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
include TheGeomGeoJSON::ActiveRecord
|
|
43
|
+
|
|
44
|
+
self.table_name = 'matryoshka_view_records'
|
|
45
|
+
self.primary_key = 'name'
|
|
46
|
+
|
|
47
|
+
COLUMNS = {
|
|
48
|
+
'base' => 'text',
|
|
49
|
+
'the_geom' => 'geometry(Geometry,4326)',
|
|
50
|
+
'the_geom_webmercator' => 'geometry(Geometry,3857)',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
def view
|
|
54
|
+
@view ||= begin
|
|
55
|
+
save!
|
|
56
|
+
unless exists?
|
|
57
|
+
raise "missing #{name} (#{inspect})"
|
|
58
|
+
end
|
|
59
|
+
MatryoshkaView.new name: name, base: base, the_geom_geojson: the_geom_geojson
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def exists?
|
|
64
|
+
MatryoshkaView.view_exists? name
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
require 'active_record'
|
|
2
|
+
require 'hash_digest'
|
|
3
|
+
|
|
4
|
+
require 'active_support'
|
|
5
|
+
require 'active_support/core_ext'
|
|
6
|
+
|
|
7
|
+
require 'matryoshka_view/version'
|
|
8
|
+
require 'matryoshka_view/record'
|
|
9
|
+
|
|
10
|
+
class MatryoshkaView
|
|
11
|
+
class << self
|
|
12
|
+
def setup
|
|
13
|
+
Record.create_table
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @private
|
|
17
|
+
def logger
|
|
18
|
+
ActiveRecord::Base.logger
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def view_exists?(*expected_names)
|
|
22
|
+
result = ActiveRecord::Base.connection_pool.with_connection do |conn|
|
|
23
|
+
conn.select_values <<-SQL
|
|
24
|
+
SELECT 1
|
|
25
|
+
FROM pg_catalog.pg_class mv
|
|
26
|
+
WHERE
|
|
27
|
+
mv.oid::regclass::text IN (#{expected_names.map { |name| conn.quote(name) }.join(',')})
|
|
28
|
+
AND mv.relkind = 'r'
|
|
29
|
+
SQL
|
|
30
|
+
end
|
|
31
|
+
result.length == expected_names.length
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
SCHEMA_NAME = 'matryoshka_view'
|
|
36
|
+
|
|
37
|
+
attr_reader :base
|
|
38
|
+
attr_reader :geom_source
|
|
39
|
+
|
|
40
|
+
def initialize(base:, geom_source: nil, the_geom_geojson: nil, name: nil)
|
|
41
|
+
@base = base
|
|
42
|
+
@the_geom_geojson = the_geom_geojson
|
|
43
|
+
@geom_source = geom_source
|
|
44
|
+
@name = name
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def lookup(geom_source: nil, the_geom_geojson: nil)
|
|
48
|
+
# FIXME move to Record class method
|
|
49
|
+
hit = if geom_source
|
|
50
|
+
Record.where("ST_Contains(ST_Buffer(the_geom, 0.00001), (SELECT the_geom FROM #{geom_source.class.quoted_table_name} WHERE id = #{quote(geom_source.id)}))").order("ST_Area(the_geom, false) ASC").first
|
|
51
|
+
elsif the_geom_geojson
|
|
52
|
+
Record.where("ST_Contains(ST_Buffer(the_geom, 0.00001), ST_SetSRID(ST_GeomFromGeoJSON(#{quote(the_geom_geojson)}), 4326))").order("ST_Area(the_geom, false) ASC").first
|
|
53
|
+
else
|
|
54
|
+
raise "expecting geom_source or the_geom_geojson"
|
|
55
|
+
end
|
|
56
|
+
hit.try(:view) || self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def spawn(attrs)
|
|
60
|
+
child = MatryoshkaView.new attrs.reverse_merge(base: base)
|
|
61
|
+
child.spawn!
|
|
62
|
+
child
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def spawn!
|
|
66
|
+
with_connection do |c|
|
|
67
|
+
c.execute "CREATE SCHEMA IF NOT EXISTS #{SCHEMA_NAME}"
|
|
68
|
+
c.execute <<-SQL
|
|
69
|
+
CREATE TABLE #{name} (LIKE #{quoted_base} INCLUDING ALL)
|
|
70
|
+
SQL
|
|
71
|
+
c.execute <<-SQL
|
|
72
|
+
INSERT INTO #{name}
|
|
73
|
+
SELECT *
|
|
74
|
+
FROM #{quoted_base}
|
|
75
|
+
WHERE ST_Contains(ST_SetSRID(ST_GeomFromGeoJSON(#{c.quote(the_geom_geojson)}), 4326), #{quoted_base}.the_geom)
|
|
76
|
+
SQL
|
|
77
|
+
record = Record.new
|
|
78
|
+
record.name = name
|
|
79
|
+
record.base = base
|
|
80
|
+
record.save!
|
|
81
|
+
record.the_geom_geojson = the_geom_geojson
|
|
82
|
+
record.save!
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def the_geom_geojson
|
|
87
|
+
@the_geom_geojson ||= geom_source.try :the_geom_geojson
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def quoted_base
|
|
91
|
+
@quoted_base ||= with_connection do |c|
|
|
92
|
+
base.split('.', 2).map { |part| c.quote_column_name(part) }.join('.')
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def name
|
|
97
|
+
@name ||= if inner?
|
|
98
|
+
"#{SCHEMA_NAME}.t#{HashDigest.digest3(uniq_attrs)}"
|
|
99
|
+
else
|
|
100
|
+
base
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def from_sql
|
|
105
|
+
name
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def name_without_schema
|
|
109
|
+
@name_without_schema ||= name.split('.', 2).last
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def eql?(other)
|
|
113
|
+
# puts "#{uniq_attrs} <-> #{other.uniq_attrs}"
|
|
114
|
+
other.is_a?(MatryoshkaView) and uniq_attrs == other.uniq_attrs
|
|
115
|
+
end
|
|
116
|
+
alias == eql?
|
|
117
|
+
|
|
118
|
+
def uniq_attrs
|
|
119
|
+
{
|
|
120
|
+
base_table_name: base,
|
|
121
|
+
the_geom_geojson: the_geom_geojson,
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
|
|
127
|
+
def inner?
|
|
128
|
+
!the_geom_geojson.nil?
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# db helper to make sure we immediately return connections to pool
|
|
132
|
+
# @private
|
|
133
|
+
[
|
|
134
|
+
# :execute,
|
|
135
|
+
:quote,
|
|
136
|
+
# :quote_column_name,
|
|
137
|
+
# :quote_table_name,
|
|
138
|
+
# :select_all,
|
|
139
|
+
# :select_rows,
|
|
140
|
+
# :select_value,
|
|
141
|
+
# :select_values,
|
|
142
|
+
].each do |method_id|
|
|
143
|
+
define_method method_id do |sql|
|
|
144
|
+
with_connection do |conn|
|
|
145
|
+
conn.send method_id, sql
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# db helper to make sure we immediately return connections to pool
|
|
151
|
+
# @private
|
|
152
|
+
def with_connection
|
|
153
|
+
ActiveRecord::Base.connection_pool.with_connection do |conn|
|
|
154
|
+
yield conn
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'matryoshka_view/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "matryoshka_view"
|
|
8
|
+
spec.version = MatryoshkaView::VERSION
|
|
9
|
+
spec.authors = ["Seamus Abshere"]
|
|
10
|
+
spec.email = ["seamus@abshere.net"]
|
|
11
|
+
spec.summary = %q{Maintains a list of inner (subset/nested) views and their geometic boundaries for a particular table.}
|
|
12
|
+
spec.description = %q{Helps you spawn new inner views and lookup the right one.}
|
|
13
|
+
spec.homepage = "https://github.com/faradayio/matryoshka_view"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_runtime_dependency 'activerecord'
|
|
22
|
+
spec.add_runtime_dependency 'activesupport'
|
|
23
|
+
spec.add_runtime_dependency 'hash_digest'
|
|
24
|
+
|
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
|
26
|
+
spec.add_development_dependency "database_cleaner"
|
|
27
|
+
spec.add_development_dependency "pg"
|
|
28
|
+
spec.add_development_dependency "pry"
|
|
29
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
30
|
+
spec.add_development_dependency "rspec"
|
|
31
|
+
spec.add_development_dependency "the_geom_geojson"
|
|
32
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe MatryoshkaView do
|
|
4
|
+
let(:world) { MatryoshkaView.new(base: Place.table_name) }
|
|
5
|
+
|
|
6
|
+
def geojson(name)
|
|
7
|
+
TheGeomGeoJSON::EXAMPLES.fetch name
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def place(name)
|
|
11
|
+
place = Place.new
|
|
12
|
+
place.save!
|
|
13
|
+
place.the_geom_geojson = geojson(name)
|
|
14
|
+
place.save!
|
|
15
|
+
place
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
shared_examples 'OK' do
|
|
19
|
+
it "just gives you back the base by default" do
|
|
20
|
+
expect(world.from_sql).to eq('places')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "doesn't auto-create anything" do
|
|
24
|
+
expect(world.lookup(the_geom_geojson: geojson(:burlington_point))).to eq(world)
|
|
25
|
+
expect(world.lookup(the_geom_geojson: geojson(:barre_point))).to eq(world)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "helps you spawn inner views given geojson" do
|
|
29
|
+
burlington # spawn it
|
|
30
|
+
expect(MatryoshkaView.view_exists?(burlington.from_sql)).to be_truthy
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "lets you specify an name name" do
|
|
34
|
+
world.spawn the_geom_geojson: geojson(:burlington), name: 'magic'
|
|
35
|
+
expect(MatryoshkaView.view_exists?('magic')).to be_truthy
|
|
36
|
+
ActiveRecord::Base.connection.execute 'DROP TABLE magic CASCADE'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "after spawning a matryoshka view" do
|
|
40
|
+
before do
|
|
41
|
+
burlington
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "tells you what view to use inside boundaries (inclusive)" do
|
|
45
|
+
expect(world.lookup(the_geom_geojson: geojson(:burlington_point))).to eq(burlington)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "falls back to original table outside boundaries" do
|
|
49
|
+
expect(world.lookup(the_geom_geojson: geojson(:montreal))).to eq(world)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
xit "doesn't confuse bases" do
|
|
53
|
+
# you can only use this with one base table
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe "non-overlapping matryoshka views" do
|
|
58
|
+
before do
|
|
59
|
+
south_burlington
|
|
60
|
+
downtown_burlington
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "chooses the right view" do
|
|
64
|
+
expect(world.lookup(the_geom_geojson: geojson(:south_burlington_point))).to eq(south_burlington)
|
|
65
|
+
expect(world.lookup(the_geom_geojson: geojson(:downtown_burlington_point))).to eq(downtown_burlington)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "falls back to original table outside boundaries" do
|
|
69
|
+
expect(world.lookup(the_geom_geojson: geojson(:montreal))).to eq(world)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe "overlapping matryoshka views" do
|
|
74
|
+
before do
|
|
75
|
+
burlington
|
|
76
|
+
south_burlington
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "chooses the smaller view" do
|
|
80
|
+
expect(world.lookup(the_geom_geojson: geojson(:south_burlington_point))).to eq(south_burlington)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe "contents of matryoshka views" do
|
|
85
|
+
it "has the same columns" do
|
|
86
|
+
expect(ActiveRecord::Base.connection.columns(burlington.from_sql).map(&:name)).to match_array(ActiveRecord::Base.connection.columns(world.from_sql).map(&:name))
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
describe "optimizing view creation" do
|
|
91
|
+
# even if you tell a view it's based on nationwide, it should see if there is a smaller view that it can base itself on
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
describe 'with the_geom_geojson' do
|
|
96
|
+
let(:burlington) { world.spawn the_geom_geojson: geojson(:burlington) }
|
|
97
|
+
let(:south_burlington) { world.spawn the_geom_geojson: geojson(:south_burlington) }
|
|
98
|
+
let(:downtown_burlington) { world.spawn the_geom_geojson: geojson(:downtown_burlington) }
|
|
99
|
+
|
|
100
|
+
it_behaves_like 'OK'
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
describe 'with geom_source' do
|
|
104
|
+
let(:burlington) { world.spawn geom_source: place(:burlington) }
|
|
105
|
+
let(:south_burlington) { world.spawn geom_source: place(:south_burlington) }
|
|
106
|
+
let(:downtown_burlington) { world.spawn geom_source: place(:downtown_burlington) }
|
|
107
|
+
|
|
108
|
+
it_behaves_like 'OK'
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
|
2
|
+
|
|
3
|
+
require 'pry'
|
|
4
|
+
|
|
5
|
+
require 'logger'
|
|
6
|
+
require 'fileutils'
|
|
7
|
+
|
|
8
|
+
log_dir = File.expand_path('../../log', __FILE__)
|
|
9
|
+
FileUtils.mkdir_p log_dir
|
|
10
|
+
|
|
11
|
+
logger = Logger.new(File.join(log_dir, 'test.log'))
|
|
12
|
+
logger.level = Logger::DEBUG
|
|
13
|
+
|
|
14
|
+
dbname = 'matryoshka_view_test'
|
|
15
|
+
unless ENV['FAST'] == 'true'
|
|
16
|
+
system 'dropdb', '--if-exists', dbname
|
|
17
|
+
system 'createdb', dbname
|
|
18
|
+
system 'psql', dbname, '--command', 'CREATE EXTENSION postgis'
|
|
19
|
+
system 'psql', dbname, '--command', 'CREATE TABLE places (id serial primary key, the_geom geometry(Geometry,4326), the_geom_webmercator geometry(Geometry,3857))'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
require 'active_record'
|
|
23
|
+
|
|
24
|
+
ActiveRecord::Base.logger = logger
|
|
25
|
+
|
|
26
|
+
ActiveRecord::Base.establish_connection "postgresql://127.0.0.1/#{dbname}"
|
|
27
|
+
|
|
28
|
+
# http://gray.fm/2013/09/17/unknown-oid-with-rails-and-postgresql/
|
|
29
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.tap do |klass|
|
|
30
|
+
klass::OID.register_type('geometry', klass::OID::Identity.new)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
require 'rspec'
|
|
34
|
+
require 'database_cleaner'
|
|
35
|
+
require 'the_geom_geojson/examples'
|
|
36
|
+
|
|
37
|
+
DatabaseCleaner.strategy = :deletion, {except: %w{ spatial_ref_sys }}
|
|
38
|
+
|
|
39
|
+
RSpec.configure do |config|
|
|
40
|
+
config.fail_fast = true
|
|
41
|
+
|
|
42
|
+
config.before(:suite) do
|
|
43
|
+
ActiveRecord::Base.connection.execute "DROP SCHEMA IF EXISTS #{MatryoshkaView::SCHEMA_NAME} CASCADE"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
config.before(:each) do
|
|
47
|
+
ActiveRecord::Base.connection.execute "DROP SCHEMA IF EXISTS #{MatryoshkaView::SCHEMA_NAME} CASCADE"
|
|
48
|
+
DatabaseCleaner.start
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
config.after(:each) do
|
|
52
|
+
DatabaseCleaner.clean
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
require 'matryoshka_view'
|
|
57
|
+
|
|
58
|
+
MatryoshkaView.setup
|
|
59
|
+
|
|
60
|
+
class Place < ActiveRecord::Base
|
|
61
|
+
include TheGeomGeoJSON::ActiveRecord
|
|
62
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: matryoshka_view
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Seamus Abshere
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-10-10 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activerecord
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: activesupport
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: hash_digest
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: bundler
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.6'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.6'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: database_cleaner
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: pg
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: pry
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: rake
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '10.0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '10.0'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: rspec
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: the_geom_geojson
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
type: :development
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - ">="
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '0'
|
|
153
|
+
description: Helps you spawn new inner views and lookup the right one.
|
|
154
|
+
email:
|
|
155
|
+
- seamus@abshere.net
|
|
156
|
+
executables: []
|
|
157
|
+
extensions: []
|
|
158
|
+
extra_rdoc_files: []
|
|
159
|
+
files:
|
|
160
|
+
- ".gitignore"
|
|
161
|
+
- ".rspec"
|
|
162
|
+
- ".travis.yml"
|
|
163
|
+
- CHANGELOG
|
|
164
|
+
- Gemfile
|
|
165
|
+
- LICENSE.txt
|
|
166
|
+
- README.md
|
|
167
|
+
- Rakefile
|
|
168
|
+
- lib/matryoshka_view.rb
|
|
169
|
+
- lib/matryoshka_view/record.rb
|
|
170
|
+
- lib/matryoshka_view/version.rb
|
|
171
|
+
- matryoshka_view.gemspec
|
|
172
|
+
- spec/matryoshka_view_spec.rb
|
|
173
|
+
- spec/spec_helper.rb
|
|
174
|
+
homepage: https://github.com/faradayio/matryoshka_view
|
|
175
|
+
licenses:
|
|
176
|
+
- MIT
|
|
177
|
+
metadata: {}
|
|
178
|
+
post_install_message:
|
|
179
|
+
rdoc_options: []
|
|
180
|
+
require_paths:
|
|
181
|
+
- lib
|
|
182
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
183
|
+
requirements:
|
|
184
|
+
- - ">="
|
|
185
|
+
- !ruby/object:Gem::Version
|
|
186
|
+
version: '0'
|
|
187
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
188
|
+
requirements:
|
|
189
|
+
- - ">="
|
|
190
|
+
- !ruby/object:Gem::Version
|
|
191
|
+
version: '0'
|
|
192
|
+
requirements: []
|
|
193
|
+
rubyforge_project:
|
|
194
|
+
rubygems_version: 2.2.2
|
|
195
|
+
signing_key:
|
|
196
|
+
specification_version: 4
|
|
197
|
+
summary: Maintains a list of inner (subset/nested) views and their geometic boundaries
|
|
198
|
+
for a particular table.
|
|
199
|
+
test_files:
|
|
200
|
+
- spec/matryoshka_view_spec.rb
|
|
201
|
+
- spec/spec_helper.rb
|