ohm-geoindex 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a5b65c68b782f3ddb94c7699e53c798dc576bb4f
4
+ data.tar.gz: b04e62c93ac40539b1768db926ec2c123dac4942
5
+ SHA512:
6
+ metadata.gz: c604e68cd439e13922f828b0d4661c8daabb5ef331be92c4d73f7f635637d537f8e58937b5e926d5907dcd879daaf820ec657ac3cdcfe6162a2b60c8eeea8c16
7
+ data.tar.gz: 67d6b3dfeacec7a30d7a1c7314227b81a0e3ffde14b875d92c027063493e40b383247f4837abe571d613797b36cc55c1f6a1727e4e45acbeab70881de7b33fa4
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .DS_Store
2
+ .bundle
3
+ Gemfile.lock
4
+ test/redis-server
5
+ test/tmp
6
+ vendor/*
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ script: 'rake test'
3
+ env:
4
+ - REDIS_BRANCH=unstable
5
+ services:
6
+ - redis-server
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+ # gem 'rake'
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Eliot Shepard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ ohm-geoindex
2
+ =============
3
+
4
+ ### In early development. Requires Redis >= 3.2.
5
+
6
+ <!-- [![Build Status](https://travis-ci.org/slowernet/ohm-geoindex.png?branch=master)](https://travis-ci.org/slowernet/ohm-geoindex) -->
7
+
8
+ A plugin for [Ohm](https://github.com/soveran/ohm) models which enables radius queries via the Redis geospatial API.
9
+
10
+ Setup
11
+ -----
12
+
13
+ 1. Add the gem:
14
+
15
+ ```ruby
16
+ gem 'ohm-geoindex'
17
+ ````
18
+
19
+ 1. Include the module in your model:
20
+
21
+ ```ruby
22
+ include Ohm::Geoindex
23
+ ```
24
+
25
+ 1. Add a geospatial index to your model with the following:
26
+
27
+ ```ruby
28
+ geoindex [:longitude, :latitude] # :longitude, :latitude are attributes
29
+ ```
30
+
31
+ Note that `(lon,lat)` is the universal ordering convention for the Redis geospatial API.
32
+
33
+ Usage
34
+ -----
35
+
36
+ To perform a radius query, use the `within` class method.
37
+
38
+ ```ruby
39
+ @manly = Beach.create(latitude: -33.797948, longitude: 151.289414)
40
+ @bondi = Beach.create(latitude: -33.891472, longitude: 151.277243)
41
+ @coogee = Beach.create(latitude: -33.921017, longitude: 151.257566) # ~14km from manly
42
+
43
+ >> Beach.within(@coogee, '10 km', 'asc')
44
+ => [@coogee, @bondi]
45
+ >> Beach.within(@coogee, '10 mi', 'desc')
46
+ => [@manly, @bondi, @coogee]
47
+ >> Beach.within([151.257566, -33.921017], '10 mi', 'asc') # coords are @coogee's
48
+ => [@coogee, @bondi, @manly]
49
+ ```
50
+
51
+ See the Redis docs for [`GEORADIUS`](http://redis.io/commands/georadius) and [`GEOADD`](http://redis.io/commands/geoadd) for radius unit syntax.
52
+
53
+ Tests
54
+ --------------
55
+
56
+ `rake` will attempt to start a redis binary at ./test/redis-server at port 7771 for the duration of the test run.
57
+
58
+ Requirements
59
+ ------------
60
+
61
+ This plugin works with Ohm >= 2.2.0.
62
+
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ require "bundler/setup"
2
+ require "rake/testtask"
3
+
4
+ REDIS_PID = File.join(File.dirname(__FILE__), "test", "tmp", "redis.pid")
5
+ REDIS_LOG = File.join(File.dirname(__FILE__), "test", "tmp", "redis.log")
6
+ REDIS_PORT = 7771
7
+
8
+ task :default => [:setup, :_test, :teardown]
9
+
10
+ desc "Start the Redis server"
11
+ task :setup do
12
+ unless File.exists?(REDIS_PID)
13
+ system "#{File.join(File.dirname(__FILE__), 'test', 'redis-server')} --port #{REDIS_PORT} --pidfile #{REDIS_PID} --logfile #{REDIS_LOG} --daemonize yes"
14
+ end
15
+ end
16
+
17
+ desc "Stop the Redis server"
18
+ task :teardown do
19
+ if File.exists?(REDIS_PID)
20
+ system "kill #{File.read(REDIS_PID)}"
21
+ File.delete(REDIS_PID)
22
+ end
23
+ end
24
+
25
+ # wrap :test so that we can still teardown
26
+ task :_test do
27
+ begin
28
+ Rake::Task[:test].invoke
29
+ rescue Exception => e
30
+ puts e
31
+ end
32
+ end
33
+
34
+ Rake::TestTask.new do |t|
35
+ t.libs << "test"
36
+ t.test_files = FileList['test/*_test.rb']
37
+ # t.verbose = true
38
+ ENV["REDIS_URL"] = "redis://127.0.0.1:#{REDIS_PORT.to_s}/0"
39
+ end
@@ -0,0 +1,63 @@
1
+ require 'ohm'
2
+
3
+ module Ohm
4
+ module Geoindex
5
+ VERSION = "0.0.1"
6
+
7
+ def self.included(model)
8
+ begin
9
+ Ohm.redis.call!('GEOADD')
10
+ rescue RuntimeError => e
11
+ raise "This version of Redis (#{Ohm.redis.url}) does not support the geospatial API." if e.message =~ /unknown command/
12
+ end
13
+
14
+ model.extend(ClassMethods)
15
+ end
16
+
17
+ def save
18
+ super
19
+ redis.queue('MULTI')
20
+ redis.queue('ZREM', self.class.key[:geoindex], self.id)
21
+ redis.queue('GEOADD', self.class.key[:geoindex], self.longitude, self.latitude, self.id)
22
+ redis.queue('EXEC')
23
+ redis.commit
24
+ self
25
+ end
26
+
27
+ def delete
28
+ redis.call('ZREM', self.class.key[:geoindex], self.id)
29
+ super
30
+ end
31
+
32
+ module ClassMethods
33
+ def geoindex(coords)
34
+ @geoindex = coords
35
+ end
36
+
37
+ def within(center, radius, withdist: nil, sort: nil)
38
+ raise IndexNotFound unless @geoindex
39
+ args = center.is_a?(self.ancestors.first) ? ['GEORADIUSBYMEMBER', key[:geoindex], center.id] : ['GEORADIUS', key[:geoindex], *center]
40
+ args << parse_radius(radius)
41
+ args << sort if sort
42
+ args << 'withdist' if withdist
43
+ results = redis.call(*args.flatten)
44
+
45
+ # extract ids so we can fetch all at once
46
+ # can be [:id, :id, ...] or [[:id, :dist], [:id, :dist], ...]
47
+ ids = results.map { |r| [*r][0] }
48
+ models = self.ancestors.first.fetch(ids)
49
+
50
+ if withdist
51
+ results.each_with_index.map { |r,i| [models[i], r[1].to_f] }
52
+ else
53
+ models
54
+ end
55
+ end
56
+
57
+ protected
58
+ def parse_radius(r)
59
+ r.downcase.strip.scan(/(\d+)\s*(\S+)/).flatten
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path('../lib/ohm/geoindex', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'ohm-geoindex'
5
+ s.version = Ohm::Geoindex::VERSION
6
+ s.summary = %q{Geoindices for Ohm (on Redis 3.2+)}
7
+ s.author = "Eliot Shepard"
8
+ s.email = "eshepard@slower.net"
9
+ s.files = `git ls-files`.split("\n")
10
+ s.homepage = 'https://github.com/slowernet/ohm-geoindex'
11
+ s.license = 'MIT'
12
+
13
+ s.add_dependency "ohm", '>= 2.2.0'
14
+ s.add_development_dependency 'test-unit'
15
+ end
@@ -0,0 +1,81 @@
1
+ require 'test/unit'
2
+ require 'ohm'
3
+ require 'ohm/geoindex'
4
+
5
+ Ohm.redis = Redic.new(ENV["REDIS_URL"])
6
+
7
+ class Beach < Ohm::Model
8
+ include Ohm::Geoindex
9
+
10
+ attribute :longitude
11
+ attribute :latitude
12
+ geoindex [:longitude, :latitude]
13
+ end
14
+
15
+ class GeoindexTest < Test::Unit::TestCase
16
+ def setup
17
+ Ohm.flush
18
+ @manly = Beach.create(longitude: 151.289414, latitude: -33.797948)
19
+ @bondi = Beach.create(longitude: 151.277243, latitude: -33.891472)
20
+ @coogee = Beach.create(longitude: 151.257566, latitude: -33.921017) # ~14km from manly
21
+ end
22
+
23
+ def test_without_sort
24
+ a = Beach.within(@coogee, '10 km')
25
+ assert_equal(2, a.size)
26
+ end
27
+
28
+ def test_within_by_object
29
+ a = Beach.within(@coogee, '10 km', sort: 'asc')
30
+ assert_equal([@coogee, @bondi], a)
31
+ a = Beach.within(@coogee, '20 km', sort: 'asc')
32
+ assert_equal([@coogee, @bondi, @manly], a)
33
+ end
34
+
35
+ def test_within_by_coords
36
+ a = Beach.within([151.257566, -33.921017], '10 km', sort: 'desc') # coogee
37
+ assert_equal(2, a.size)
38
+ assert_equal(@coogee, a.last)
39
+ end
40
+
41
+ def test_within_with_distance
42
+ a = Beach.within(@coogee, '10 km', sort: 'asc', withdist: true)
43
+ assert_equal(2, a.size)
44
+ assert_equal([@bondi, 3.7550], a.last)
45
+ a = Beach.within(@coogee, '10 km', withdist: true)
46
+ assert_equal(2, a.size)
47
+ end
48
+
49
+ def test_index_update
50
+ a = Ohm.redis.call('ZSCORE', @bondi.class.key[:geoindex], @bondi.id)
51
+ @bondi.update(latitude: -33.921017, longitude: 151.257566) # bondi moves to coogee
52
+ b = Ohm.redis.call('ZSCORE', @bondi.class.key[:geoindex], @bondi.id)
53
+ assert_not_equal(a, b)
54
+ c = Ohm.redis.call('ZSCORE', @bondi.class.key[:geoindex], @coogee.id)
55
+ assert_equal(b, c)
56
+ end
57
+
58
+ def test_radius_parsing
59
+ a = Beach.within(@coogee, '10 km', sort: 'asc')
60
+ assert_equal([@coogee, @bondi], a)
61
+ a = Beach.within(@coogee, '10000m', sort: 'asc')
62
+ assert_equal([@coogee, @bondi], a)
63
+ assert_raise do
64
+ a = Beach.within(@coogee, 'one billion feet', 'asc')
65
+ end
66
+ end
67
+
68
+ def test_sort
69
+ a = Beach.within(@coogee, '10 km', sort: 'asc')
70
+ assert_equal([@coogee, @bondi], a)
71
+ a = Beach.within(@coogee, '10000m', sort: 'desc')
72
+ assert_equal([@bondi, @coogee], a)
73
+ end
74
+
75
+ def test_index_delete
76
+ [@bondi, @coogee, @manly].each_with_index do |b, i|
77
+ b.delete
78
+ assert_equal(3-(i+1), Ohm.redis.call('ZCARD', Beach.key[:geoindex]))
79
+ end
80
+ end
81
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ohm-geoindex
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Eliot Shepard
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ohm
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: test-unit
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description:
42
+ email: eshepard@slower.net
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - ".gitignore"
48
+ - ".travis.yml"
49
+ - Gemfile
50
+ - LICENSE
51
+ - README.md
52
+ - Rakefile
53
+ - lib/ohm/geoindex.rb
54
+ - ohm-geoindex.gemspec
55
+ - test/geoindex_test.rb
56
+ - test/tmp/.gitkeep
57
+ homepage: https://github.com/slowernet/ohm-geoindex
58
+ licenses:
59
+ - MIT
60
+ metadata: {}
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 2.4.5
78
+ signing_key:
79
+ specification_version: 4
80
+ summary: Geoindices for Ohm (on Redis 3.2+)
81
+ test_files: []