mongoid_geo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2011 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,123 @@
1
+ h1. Mongoid geo
2
+
3
+ A Geo extension for Mongoid.
4
+
5
+ "MongoDB Geospatial Indexing":http://www.mongodb.org/display/DOCS/Geospatial+Indexing
6
+
7
+ * Supports Mongoid 1.7 sphere distance calculations and
8
+ * Adds a set of geo related inflections
9
+ * Adds nearSphere inclusion method
10
+
11
+ h2. Status
12
+
13
+ This gem has so far just the initial design and has not been tested. Feel free to help in the effort. Thanks!
14
+
15
+ h2. Fields
16
+
17
+ When setting a geo-location array, the setter should try to convert the value to an array of floats
18
+
19
+ Old/Manual style:
20
+
21
+ <pre>
22
+ class Person
23
+ field :locations, :type => Array
24
+
25
+ def locations= args
26
+ @locations = args.kind_of?(String) ? args.split(",").map(&:to_f) : args
27
+ end
28
+ end
29
+ </pre>
30
+
31
+ With _mongoid-geo_, becomes:
32
+
33
+ <pre>
34
+ class Person
35
+ field :locations, :type => Array, :geo => true
36
+ end
37
+
38
+ p = Person.new
39
+ # set via String or Strings
40
+ p.locations = "45.1, -3.4"
41
+ p.locations = "45.1", "-3.4"
42
+
43
+ assert([45.1, -3.4], p.locations)
44
+ </pre>
45
+
46
+ h2. Extra Geo Inclusions
47
+
48
+ Find all addresses near a point using spherical distance calculation
49
+
50
+ h3. nearSphere
51
+
52
+ <pre>
53
+ base.where(:locations.nearSphere => [ 72, -44 ])
54
+ # => :locations => { "$nearSphere" : [ 72, -44 ] }
55
+ </pre>
56
+
57
+ h2. Extra Geo Inflections
58
+
59
+ h3. nearMax
60
+
61
+ Find all addresses near a point using spherical distance calculation
62
+
63
+ <pre>
64
+ base.where(:locations.nearMax => [[ 72, -44 ], 5])
65
+ # => { $near: [50, 40] , $maxDistance: 3 }
66
+
67
+ base.where(:locations.nearMax(:sphere) => [[ 72, -44 ], 5])
68
+ # => { $nearSphere: [50, 40] , $maxDistanceSphere: 3 }
69
+ </pre>
70
+
71
+ h3. withinBox
72
+
73
+ <pre>
74
+ box = [[50, 40], [30,55]]
75
+ base.where(:locations.withinBox => box)
76
+ # => locations: {"$within" : {"$box" : box}
77
+
78
+ base.where(:locations.withinBox(:sphere) => box)
79
+ # => locations: {"$within" : {"$boxSphere" : box}
80
+ </pre>
81
+
82
+ You can also use a Hash to define the box
83
+
84
+ <pre>
85
+ places.where(:location.withinBox => {:lower_left => [50, 40], :upper_right => [30,55]})
86
+ </pre>
87
+
88
+ Or use an Object (which must have the methods #lower_left and #upper_right that return the points of the bounding box)
89
+
90
+ <pre>
91
+ box = (Struct.new :lower_left, :upper_right).new
92
+ box.lower_left = [50, 40]
93
+ box.upper_right = [30,55]
94
+ places.where(:location.withinBox => box)
95
+ </pre>
96
+
97
+ h3. withinCenter
98
+
99
+ <pre>
100
+ center = [50, 40]
101
+ radius = 4
102
+
103
+ # places: {"$within" : {"$centerSphere" : [center, radius]}
104
+ places.where(:location.withinCenter(:sphere) => [center, radius])
105
+
106
+ # places: {"$within" : {"$center" : [center, radius]}
107
+ places.where(:location.withinCenter => [center, radius])
108
+ </pre>
109
+
110
+ You can also use a Hash to define the box
111
+
112
+ <pre>
113
+ places.where(:location.withinCenter => {:center => [50, 40], :radius => 4})
114
+ </pre>
115
+
116
+ Or use an Object (which must have the methods #lower_left and #upper_right that return the points of the bounding box)
117
+
118
+ <pre>
119
+ circle = (Struct.new :center, :radius).new
120
+ circle.center = [50, 40]
121
+ circle.radius = 4
122
+ places.where(:location.withinCenter => circle)
123
+ </pre>
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rake'
10
+ require 'rake/rdoctask'
11
+
12
+ require 'rspec/core'
13
+ require 'rspec/core/rake_task'
14
+
15
+ RSpec::Core::RakeTask.new(:spec)
16
+
17
+ task :default => :spec
18
+
19
+ Rake::RDocTask.new(:rdoc) do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'Mongoid Geo'
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.rdoc_files.include('README.rdoc')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
@@ -0,0 +1,6 @@
1
+ require 'mongoid/geo/criterion/complex'
2
+ require 'mongoid/geo/criterion/inclusion'
3
+ require 'mongoid/geo/criterion/outer_operator'
4
+ require 'mongoid/geo/criterion/twin_operators'
5
+ require 'mongoid/geo/inflections'
6
+ require 'mongoid/geo/criteria_helpers'
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Hash #:nodoc:
5
+ module CriteriaHelpers #:nodoc:
6
+ def expand_complex_criteria
7
+ hsh = {}
8
+ each_pair do |k,v|
9
+ case k
10
+ when Mongoid::Criterion::Complex
11
+ hsh[k.key] ||= {}
12
+ hsh[k.key].merge!(k.make_hash(v))
13
+ when Mongoid::Criterion::OuterOperator
14
+ hsh[k.key] ||= {}
15
+ hsh[k.key].merge!(k.make_hash(v))
16
+ when Mongoid::Criterion::TwinOperators
17
+ raise "TwinOperators expects an array with a value for each of the twin operators" if !v.kind_of?(Array) && !v.size == 2
18
+ hsh[k.key] ||= {}
19
+ hsh[k.key].merge!(k.make_hash(v))
20
+ else
21
+ hsh[k] = v
22
+ end
23
+ end
24
+ hsh
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+ # Complex criterion are used when performing operations on symbols to get
5
+ # get a shorthand syntax for where clauses.
6
+ #
7
+ # Example:
8
+ #
9
+ # <tt>{ :field => { "$lt" => "value" } }</tt>
10
+ # becomes:
11
+ # <tt> { :field.lt => "value }</tt>
12
+ class Complex
13
+
14
+ def make_hash v
15
+ {"$#{operator}" => v}
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+ module Inclusion
5
+ # Adds a criterion to the +Criteria+ that specifies values to do
6
+ # geospacial searches by. The field must be indexed with the "2d" option.
7
+ #
8
+ # @example Adding the criterion.
9
+ # criteria.near(:field1 => [30, -44])
10
+ #
11
+ # @param [ Hash ] attributes The fields with lat/long values.
12
+ #
13
+ # @return [ Criteria ] A new criteria with the added selector.
14
+ def nearSphere(attributes = {})
15
+ update_selector(attributes, "$nearSphere")
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+ # Complex criterion are used when performing operations on symbols to get
5
+ # get a shorthand syntax for where clauses.
6
+ #
7
+ # Example:
8
+
9
+ # {:outer_operator => 'within', :operator => 'center' }
10
+ # { :location => { "$within" => { "$center" => [ [ 50, -40 ], 1 ] } } }
11
+ class OuterOperator
12
+ attr_accessor :key, :outer_op, :operator
13
+
14
+ # Create the new complex criterion.
15
+ def initialize(opts = {})
16
+ @key = opts[:key]
17
+ @operator = opts[:operator]
18
+ @outer_op = opts[:outer_op]
19
+ end
20
+
21
+ def make_hash v
22
+ v = extract_box(v) if !v.kind_of?(Array) && operator =~ /box/
23
+ v = extract_circle(v) if !v.kind_of?(Array) && operator =~ /center/
24
+ {"$#{outer_op}" => {"$#{operator}" => v } }
25
+ end
26
+
27
+ def hash
28
+ [@outer_op, [@operator, @key]].hash
29
+ end
30
+
31
+ def eql?(other)
32
+ self == (other)
33
+ end
34
+
35
+ def ==(other)
36
+ return false unless other.is_a?(self.class)
37
+ self.outer_op == other.outer_op && self.key == other.key && self.operator == other.operator
38
+ end
39
+
40
+ protected
41
+
42
+ def extract_circle(v)
43
+ case v
44
+ when Hash
45
+ [v[:center], v[:radius]]
46
+ else
47
+ v.respond_to?(:center) ? [v.center, v.radius] : raise("Can't extract box from: #{v}, must have :center and :radius methods or hash keys")
48
+ end
49
+ end
50
+
51
+ def extract_box v
52
+ case v
53
+ when Hash
54
+ [v[:lower_left], v[:upper_right]]
55
+ else
56
+ v.respond_to?(:lower_left) ? [v.lower_left, v.upper_right] : raise("Can't extract box from: #{v}, must have :lower_left and :upper_right methods or hash keys")
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+ # Complex criterion are used when performing operations on symbols to get
5
+ # get a shorthand syntax for where clauses.
6
+ #
7
+ # Example:
8
+
9
+ # {:opA => 'near', :opB => 'maxDistance' }
10
+ # :location => { $near => [50,50], $maxDistance => 5 }
11
+ class TwinOperators
12
+ attr_accessor :key, :op_a, :op_b
13
+
14
+ # Create the new complex criterion.
15
+ def initialize(opts = {})
16
+ @key = opts[:key]
17
+ @op_a = opts[:op_a]
18
+ @op_b = opts[:op_b]
19
+ end
20
+
21
+ def make_hash v
22
+ {"$#{op_a}" => v.first, "$#{op_b}" => v.last }
23
+ end
24
+
25
+ def hash
26
+ [@op_a, @op_b, @key].hash
27
+ end
28
+
29
+ def eql?(other)
30
+ self == (other)
31
+ end
32
+
33
+ def ==(other)
34
+ return false unless other.is_a?(self.class)
35
+ self.op_a == other.op_a && self.op_b == other.op_b && self.key == other.key
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,23 @@
1
+ module Mongoid #:nodoc
2
+ # This module defines behaviour for fields.
3
+ module Fields
4
+ module ClassMethods #:nodoc
5
+ def create_accessors(name, meth, options = {})
6
+ generated_field_methods.module_eval do
7
+ define_method(meth) { read_attribute(name) }
8
+ options
9
+ define_method("#{meth}=") do |value|
10
+ value = if options[:type] == Array && options[:geo]
11
+ value.kind_of?(String) ? value.split(",") : value
12
+ end.map(&:to_f)
13
+ write_attribute(name, value)
14
+ end
15
+ define_method("#{meth}?") do
16
+ attr = read_attribute(name)
17
+ (options[:type] == Boolean) ? attr == true : attr.present?
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Symbol #:nodoc:
5
+ module Inflections #:nodoc:
6
+
7
+ # $nearSphere $centerSphere
8
+ # nearMax
9
+ # - { $near : [50,50] , $maxDistance : 5 }
10
+ # withinBox
11
+ # - {"$within" : {"$box" : box}
12
+ # withinCenter
13
+ # - {"$within" : {"$center" : [center, radius]}}})
14
+
15
+ def nearSphere
16
+ Criterion::Complex.new(:operator => 'nearSphere', :key => self)
17
+ end
18
+
19
+ def nearMax calc = :flat
20
+ Criterion::TwinOperators.new(:op_a => get_op(calc, 'near'), :op_b => get_op(calc, 'maxDistance'), :key => self)
21
+ end
22
+
23
+ def withinBox calc = :flat
24
+ Criterion::OuterOperator.new(:outer_op => 'within', :operator => get_op(calc, 'box'), :key => self)
25
+ end
26
+
27
+ def withinCenter calc = :flat
28
+ Criterion::OuterOperator.new(:outer_op => 'within', :operator => get_op(calc, 'center'), :key => self)
29
+ end
30
+
31
+ private
32
+
33
+ def get_op calc, operator
34
+ calc.to_s == 'sphere' ? "#{operator}Sphere" : operator
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,2 @@
1
+ require 'mongoid/geo/criteria'
2
+ require 'mongoid/geo/fields'
@@ -0,0 +1 @@
1
+ require 'mongoid/geo'
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid_geo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease: !!null
6
+ platform: ruby
7
+ authors: []
8
+ autorequire: !!null
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2011-02-05 00:00:00.000000000 +01:00
12
+ default_executable: !!null
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mongoid
16
+ requirement: &2159951560 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.0.0.rc.6
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2159951560
25
+ - !ruby/object:Gem::Dependency
26
+ name: bson_ext
27
+ requirement: &2159950420 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.1.6
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2159950420
36
+ description: Geo spatial extension on Mongoid 2, to add more geo-spatial capabilities
37
+ email: !!null
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - lib/mongoid/geo/criteria.rb
43
+ - lib/mongoid/geo/criteria_helpers.rb
44
+ - lib/mongoid/geo/criterion/complex.rb
45
+ - lib/mongoid/geo/criterion/inclusion.rb
46
+ - lib/mongoid/geo/criterion/outer_operator.rb
47
+ - lib/mongoid/geo/criterion/twin_operators.rb
48
+ - lib/mongoid/geo/fields.rb
49
+ - lib/mongoid/geo/inflections.rb
50
+ - lib/mongoid/geo.rb
51
+ - lib/mongoid_geo.rb
52
+ - MIT-LICENSE
53
+ - Rakefile
54
+ - README.textile
55
+ has_rdoc: true
56
+ homepage: https://github.com/kristianmandrup/mongoid-geo
57
+ licenses: []
58
+ post_install_message: !!null
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project: !!null
76
+ rubygems_version: 1.5.0
77
+ signing_key: !!null
78
+ specification_version: 3
79
+ summary: Adds extra convenience methods for geo-spatial operations etc.
80
+ test_files: []