rgeo-activerecord 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.rdoc CHANGED
@@ -1,3 +1,15 @@
1
+ === 0.3.0 / 2011-01-26
2
+
3
+ * Experimental support for complex spatial queries. (Requires Arel 2.1, which is expected to be released with Rails 3.1.) Currently, only a low-level Arel-based interface is supported.
4
+ * Better support for geography types in PostGIS.
5
+ * Adapters can now define additional column constructors.
6
+ * Support for spatial column constructors on change_table.
7
+ * Fixed column type inference for some cases where the column included Z and/or M.
8
+ * IS NULL predicates now work properly with spatial types.
9
+ * Preferred attribute type is now :spatial rather than :geometry.
10
+ * The gem version is now accessible via an api.
11
+ * Some code reorganization.
12
+
1
13
  === 0.2.4 / 2011-01-13
2
14
 
3
15
  * Fixed a problem that caused a hang during rake db:rollback, as well as probably certain other functions that use ActiveRecord::Base directly rather than a subclass. (Reported by Alexander Graefe.)
data/README.rdoc CHANGED
@@ -31,8 +31,8 @@ Generally, \ActiveRecord adapters which depend on this module should be
31
31
  installed as gems, and they will install this module automatically as
32
32
  a dependency. However, you can also install it manually as a gem:
33
33
 
34
- gem install rgeo
35
- gem install rgeo-activerecord
34
+ gem install rgeo
35
+ gem install rgeo-activerecord
36
36
 
37
37
  See the README for the "rgeo" gem, a required dependency, for further
38
38
  installation information.
data/Version CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.3.0
@@ -60,7 +60,23 @@ module RGeo
60
60
  end
61
61
 
62
62
 
63
+ # The rgeo-activerecord gem installs several patches to Arel to provide
64
+ # support for spatial queries.
65
+
66
+ module Arel
67
+ end
68
+
69
+
70
+ # The rgeo-activerecord gem installs several patches to ActiveRecord
71
+ # to support services needed by spatial adapters.
72
+
73
+ module ActiveRecord
74
+ end
75
+
76
+
63
77
  # Implementation files
64
- require 'rgeo/active_record/common.rb'
65
- require 'rgeo/active_record/arel_modifications'
66
- require 'rgeo/active_record/ar_modifications'
78
+ require 'rgeo/active_record/version.rb'
79
+ require 'rgeo/active_record/spatial_expressions.rb'
80
+ require 'rgeo/active_record/arel_spatial_queries'
81
+ require 'rgeo/active_record/common_adapter_elements.rb'
82
+ require 'rgeo/active_record/ar_factory_settings'
@@ -37,10 +37,6 @@
37
37
  require 'active_record'
38
38
 
39
39
 
40
- # RGeo extensions to ActiveRecord are installed when one of the spatial
41
- # connection adapters is needed. These modifications require ActiveRecord
42
- # 3.0.3 or later.
43
-
44
40
  module ActiveRecord
45
41
 
46
42
 
@@ -64,9 +60,6 @@ module ActiveRecord
64
60
  class Base
65
61
 
66
62
 
67
- self.attribute_types_cached_by_default << :geometry
68
-
69
-
70
63
  class_attribute :rgeo_factory_generator, :instance_writer => false
71
64
  self.rgeo_factory_generator = nil
72
65
 
@@ -110,37 +103,6 @@ module ActiveRecord
110
103
  end
111
104
 
112
105
 
113
- # :stopdoc:
114
- alias_method :columns_without_rgeo_modification, :columns
115
- def columns
116
- unless defined?(@columns) && @columns
117
- columns_without_rgeo_modification.each do |column_|
118
- column_.set_ar_class(self) if column_.respond_to?(:set_ar_class)
119
- end
120
- end
121
- @columns
122
- end
123
- # :startdoc:
124
-
125
- end
126
-
127
- end
128
-
129
-
130
- module ConnectionAdapters # :nodoc:
131
-
132
- class TableDefinition # :nodoc:
133
-
134
- ::RGeo::ActiveRecord::GEOMETRY_TYPES.each do |type_|
135
- method_ = <<-END_METHOD
136
- def #{type_}(*args_)
137
- opts_ = args_.extract_options!
138
- args_.each{ |name_| column(name_, '#{type_}', opts_) }
139
- end
140
- END_METHOD
141
- class_eval(method_, __FILE__, __LINE__-5)
142
- end
143
-
144
106
  end
145
107
 
146
108
  end
@@ -0,0 +1,250 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Mysqlgeo adapter for ActiveRecord
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module RGeo
38
+
39
+ module ActiveRecord
40
+
41
+
42
+ # A set of common Arel visitor hacks for spatial ToSql visitors.
43
+ # Generally, a spatial ActiveRecord adapter should provide a custom
44
+ # ToSql Arel visitor that includes and customizes this module.
45
+ # See the existing spatial adapters (i.e. postgis, spatialite,
46
+ # mysqlspatial, and mysql2spatial) for usage examples.
47
+
48
+ module SpatialToSql
49
+
50
+
51
+ # Map a standard OGC SQL function name to the actual name used by
52
+ # a particular database. This method should take a name and
53
+ # return either the changed name or the original name.
54
+
55
+ def st_func(standard_name_)
56
+ standard_name_
57
+ end
58
+
59
+
60
+ # Visit the SpatialNamedFunction node. This operates similarly to
61
+ # the standard NamedFunction node, but it performs function name
62
+ # mapping for the database, and it also uses the type information
63
+ # in the node to determine when to cast string arguments to WKT,
64
+
65
+ def visit_RGeo_ActiveRecord_SpatialNamedFunction(node_)
66
+ name_ = st_func(node_.name)
67
+ exprs_ = []
68
+ node_.expressions.each_with_index do |expr_, index_|
69
+ exprs_ << (node_.spatial_argument?(index_) ? visit_in_spatial_context(expr_) : visit(expr_))
70
+ end
71
+ "#{name_}(#{node_.distinct ? 'DISTINCT ' : ''}#{exprs_.join(', ')})#{node_.alias ? " AS #{visit node_.alias}" : ''}"
72
+ end
73
+
74
+
75
+ # Returns a true value if the given node is of spatial type-- that
76
+ # is, if it is a spatial literal or a reference to a spatial
77
+ # attribute.
78
+
79
+ def node_has_spatial_type?(node_)
80
+ case node_
81
+ when ::Arel::Attribute
82
+ @connection.instance_variable_set(:@_getting_columns, true)
83
+ begin
84
+ col_ = @engine.columns_hash[node_.name.to_s] unless @engine == ::ActiveRecord::Base
85
+ col_ && col_.respond_to?(:spatial?) && col_.spatial? ? true : false
86
+ ensure
87
+ @connection.instance_variable_set(:@_getting_columns, false)
88
+ end
89
+ when ::RGeo::ActiveRecord::SpatialNamedFunction
90
+ node_.spatial_result?
91
+ when ::RGeo::ActiveRecord::SpatialConstantNode, ::RGeo::Feature::Instance
92
+ true
93
+ else
94
+ false
95
+ end
96
+ end
97
+
98
+
99
+ # Generates SQL for a spatial node.
100
+ # The node must be a string (in which case it is treated as WKT),
101
+ # an RGeo feature, or a spatial attribute.
102
+
103
+ def visit_in_spatial_context(node_)
104
+ case node_
105
+ when ::String
106
+ "#{st_func('ST_WKTToSQL')}(#{visit_String(node_)})"
107
+ when ::RGeo::Feature::Instance
108
+ visit_RGeo_Feature_Instance(node_)
109
+ else
110
+ visit(node_)
111
+ end
112
+ end
113
+
114
+
115
+ def _check_equality_for_rgeo(node_, negate_) # :nodoc:
116
+ left_ = node_.left
117
+ right_ = node_.right
118
+ if !@connection.instance_variable_get(:@_getting_columns) && !right_.nil? && (node_has_spatial_type?(left_) || node_has_spatial_type?(right_))
119
+ "#{negate_ ? 'NOT ' : ''}#{st_func('ST_Equals')}(#{visit_in_spatial_context(left_)}, #{visit_in_spatial_context(right_)})"
120
+ else
121
+ false
122
+ end
123
+ end
124
+
125
+
126
+ # Override equality nodes to use the ST_Equals function if at least
127
+ # one of the operands is a spatial node.
128
+
129
+ def visit_Arel_Nodes_Equality(node_)
130
+ _check_equality_for_rgeo(node_, false) || super
131
+ end
132
+
133
+
134
+ # Override equality nodes to use the ST_Equals function if at least
135
+ # one of the operands is a spatial node.
136
+
137
+ def visit_Arel_Nodes_NotEqual(node_)
138
+ _check_equality_for_rgeo(node_, true) || super
139
+ end
140
+
141
+ end
142
+
143
+
144
+ end
145
+
146
+ end
147
+
148
+
149
+ # :stopdoc:
150
+
151
+
152
+ # This node wraps an RGeo feature and gives it spatial expression
153
+ # constructors.
154
+
155
+ module RGeo
156
+ module ActiveRecord
157
+
158
+ class SpatialConstantNode
159
+
160
+ include ::RGeo::ActiveRecord::SpatialExpressions
161
+
162
+ def initialize(delegate_)
163
+ @delegate = delegate_
164
+ end
165
+
166
+ attr_reader :delegate
167
+
168
+ end
169
+
170
+ end
171
+ end
172
+
173
+
174
+ # Make sure the standard Arel visitors can handle RGeo feature objects
175
+ # by default.
176
+
177
+ module Arel
178
+ module Visitors
179
+ class Visitor
180
+ def visit_RGeo_ActiveRecord_SpatialConstantNode(node_)
181
+ if respond_to?(:visit_in_spatial_context)
182
+ visit_in_spatial_context(node_.delegate)
183
+ else
184
+ visit(node_.delegate)
185
+ end
186
+ end
187
+ end
188
+ class Dot
189
+ alias :visit_RGeo_Feature_Instance :visit_String
190
+ end
191
+ class DepthFirst
192
+ alias :visit_RGeo_Feature_Instance :terminal
193
+ end
194
+ class ToSql
195
+ alias :visit_RGeo_Feature_Instance :visit_String
196
+ end
197
+ end
198
+ end
199
+
200
+
201
+ # Add tools to build spatial structures in the AST.
202
+ # This stuff requires Arel 2.1 or later.
203
+
204
+ if defined?(::Arel::Nodes::NamedFunction)
205
+
206
+ # Allow chaining of predications from named functions
207
+ # (Hack because Arel doesn't do this but should.)
208
+ ::Arel::Nodes::NamedFunction.class_eval do
209
+ include ::Arel::Predications unless include?(::Arel::Predications)
210
+ end
211
+
212
+ # Allow chaining of spatial expressions from attributes
213
+ ::Arel::Attribute.class_eval do
214
+ include ::RGeo::ActiveRecord::SpatialExpressions
215
+ end
216
+
217
+
218
+ # A NamedFunction subclass that keeps track of the spatial-ness of
219
+ # the arguments and return values, so that it can provide context to
220
+ # visitors that want to interpret syntax differently when dealing with
221
+ # spatial elements.
222
+
223
+ class ::RGeo::ActiveRecord::SpatialNamedFunction < ::Arel::Nodes::NamedFunction
224
+
225
+ include ::RGeo::ActiveRecord::SpatialExpressions
226
+
227
+ def initialize(name_, expr_, spatial_flags_=[], aliaz_=nil)
228
+ super(name_, expr_, aliaz_)
229
+ @spatial_flags = spatial_flags_
230
+ end
231
+
232
+ def spatial_result?
233
+ @spatial_flags.first
234
+ end
235
+
236
+ def spatial_argument?(index_)
237
+ @spatial_flags[index_+1]
238
+ end
239
+
240
+ end
241
+
242
+ else
243
+
244
+ # A dummy SpatialNamedFunction for pre-2.1 versions of Arel.
245
+ class ::RGeo::ActiveRecord::SpatialNamedFunction; end
246
+
247
+ end
248
+
249
+
250
+ # :startdoc:
@@ -0,0 +1,226 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Common tools for spatial adapters for ActiveRecord
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module RGeo
38
+
39
+ module ActiveRecord
40
+
41
+
42
+ # Some default column constructors specifications for most spatial
43
+ # databases. Individual adapters may add to or override this list.
44
+
45
+ DEFAULT_SPATIAL_COLUMN_CONSTRUCTORS = {
46
+ :spatial => {:type => 'geometry'},
47
+ :geometry => {},
48
+ :point => {},
49
+ :line_string => {},
50
+ :polygon => {},
51
+ :geometry_collection => {},
52
+ :multi_line_string => {},
53
+ :multi_point => {},
54
+ :multi_polygon => {},
55
+ }.freeze
56
+
57
+
58
+ # The default factory generator for ActiveRecord::Base.
59
+
60
+ DEFAULT_FACTORY_GENERATOR = ::Proc.new do |config_|
61
+ if config_.delete(:geographic)
62
+ ::RGeo::Geographic.spherical_factory(config_)
63
+ else
64
+ ::RGeo::Cartesian.preferred_factory(config_)
65
+ end
66
+ end
67
+
68
+
69
+ # Index definition struct with a spatial flag field.
70
+
71
+ class SpatialIndexDefinition < ::Struct.new(:table, :name, :unique, :columns, :lengths, :spatial)
72
+ end
73
+
74
+
75
+ # Returns a feature type module given a string type.
76
+
77
+ def self.geometric_type_from_name(name_)
78
+ case name_.to_s
79
+ when /^geometry/i then ::RGeo::Feature::Geometry
80
+ when /^point/i then ::RGeo::Feature::Point
81
+ when /^linestring/i then ::RGeo::Feature::LineString
82
+ when /^polygon/i then ::RGeo::Feature::Polygon
83
+ when /^geometrycollection/i then ::RGeo::Feature::GeometryCollection
84
+ when /^multipoint/i then ::RGeo::Feature::MultiPoint
85
+ when /^multilinestring/i then ::RGeo::Feature::MultiLineString
86
+ when /^multipolygon/i then ::RGeo::Feature::MultiPolygon
87
+ else nil
88
+ end
89
+ end
90
+
91
+
92
+ end
93
+
94
+ end
95
+
96
+
97
+ # :stopdoc:
98
+
99
+
100
+ # Make sure a few things are autoloaded before we modify them.
101
+ ::Arel::Attributes
102
+ ::ActiveRecord::ConnectionAdapters::AbstractAdapter
103
+ ::ActiveRecord::ConnectionAdapters::TableDefinition
104
+ ::ActiveRecord::ConnectionAdapters::Table
105
+ ::ActiveRecord::Base
106
+ ::ActiveRecord::SchemaDumper
107
+
108
+
109
+ # Hack Arel Attributes dispatcher to recognize geometry columns.
110
+ # This is deprecated but necessary to support legacy Arel versions.
111
+
112
+ module Arel
113
+ module Attributes
114
+ class << self
115
+ if method_defined?(:for)
116
+ alias_method :for_without_rgeo_modification, :for
117
+ def for(column_)
118
+ column_.type == :spatial ? Attribute : for_without_rgeo_modification(column_)
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+
126
+ # Provide methods for each geometric subtype during table definitions.
127
+
128
+ module ActiveRecord
129
+ module ConnectionAdapters
130
+ class TableDefinition
131
+
132
+ alias_method :method_missing_without_rgeo_modification, :method_missing
133
+ def method_missing(method_name_, *args_, &block_)
134
+ if @base.respond_to?(:spatial_column_constructor) && (info_ = @base.spatial_column_constructor(method_name_))
135
+ type_ = (info_.delete(:type) || method_name_).to_s
136
+ opts_ = args_.extract_options!.merge(info_)
137
+ args_.each do |name_|
138
+ column(name_, type_, opts_)
139
+ end
140
+ else
141
+ method_missing_without_rgeo_modification(method_name_, *args_, &block_)
142
+ end
143
+ end
144
+
145
+ end
146
+ end
147
+ end
148
+
149
+
150
+ # Provide methods for each geometric subtype during table changes.
151
+
152
+ module ActiveRecord
153
+ module ConnectionAdapters
154
+ class Table
155
+
156
+ alias_method :method_missing_without_rgeo_modification, :method_missing
157
+ def method_missing(method_name_, *args_, &block_)
158
+ if @base.respond_to?(:spatial_column_constructor) && (info_ = @base.spatial_column_constructor(method_name_))
159
+ type_ = (info_.delete(:type) || method_name_).to_s
160
+ opts_ = args_.extract_options!.merge(info_)
161
+ args_.each do |name_|
162
+ @base.add_column(@table_name, name_, type_, opts_)
163
+ end
164
+ else
165
+ method_missing_without_rgeo_modification(method_name_, *args_, &block_)
166
+ end
167
+ end
168
+
169
+ end
170
+ end
171
+ end
172
+
173
+
174
+ # When creating column objects, cause the enclosing ActiveRecord class
175
+ # to be set on any column that recognizes it. This is commonly used by
176
+ # spatial column subclasses.
177
+
178
+ module ActiveRecord
179
+ class Base
180
+ class << self
181
+ alias_method :columns_without_rgeo_modification, :columns
182
+ def columns
183
+ unless defined?(@columns) && @columns
184
+ columns_without_rgeo_modification.each do |column_|
185
+ column_.set_ar_class(self) if column_.respond_to?(:set_ar_class)
186
+ end
187
+ end
188
+ @columns
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+
195
+ # Hack schema dumper to output spatial index flag
196
+
197
+ module ActiveRecord
198
+ class SchemaDumper
199
+ private
200
+ def indexes(table_, stream_)
201
+ if (indexes_ = @connection.indexes(table_)).any?
202
+ add_index_statements_ = indexes_.map do |index_|
203
+ statement_parts_ = [ ('add_index ' + index_.table.inspect) ]
204
+ statement_parts_ << index_.columns.inspect
205
+ statement_parts_ << (':name => ' + index_.name.inspect)
206
+ statement_parts_ << ':unique => true' if index_.unique
207
+ statement_parts_ << ':spatial => true' if index_.respond_to?(:spatial) && index_.spatial
208
+ index_lengths_ = index_.lengths.compact if index_.lengths.is_a?(::Array)
209
+ statement_parts_ << (':length => ' + ::Hash[*index_.columns.zip(index_.lengths).flatten].inspect) if index_lengths_.present?
210
+ ' ' + statement_parts_.join(', ')
211
+ end
212
+ stream_.puts add_index_statements_.sort.join("\n")
213
+ stream_.puts
214
+ end
215
+ end
216
+ end
217
+ end
218
+
219
+
220
+ # Tell ActiveRecord to cache spatial attribute values so they don't get
221
+ # re-parsed on every access.
222
+
223
+ ::ActiveRecord::Base.attribute_types_cached_by_default << :spatial
224
+
225
+
226
+ # :startdoc:
@@ -0,0 +1,314 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Common tools for spatial adapters for ActiveRecord
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ module RGeo
38
+
39
+ module ActiveRecord
40
+
41
+
42
+ # Returns true if spatial expressions (i.e. the methods in the
43
+ # SpatialExpressions module) are supported. Generally, this is true
44
+ # if Arel is at version 2.1 or later.
45
+
46
+ def self.spatial_expressions_supported?
47
+ defined?(::Arel::Nodes::NamedFunction)
48
+ end
49
+
50
+
51
+ # A set of spatial expression builders.
52
+ # These methods can be chained off other spatial expressions to form
53
+ # complex expressions.
54
+ #
55
+ # These functions require Arel 2.1 or later.
56
+
57
+ module SpatialExpressions
58
+
59
+
60
+ #--
61
+ # Generic functions
62
+ #++
63
+
64
+ def st_function(function_, *args_)
65
+ spatial_info_ = args_.last.is_a?(::Array) ? args_.pop : []
66
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new(function_, [self] + args_, spatial_info_)
67
+ end
68
+
69
+
70
+ #--
71
+ # Geometry functions
72
+ #++
73
+
74
+ def st_dimension
75
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Dimension', [self], [false, true])
76
+ end
77
+
78
+ def st_geometrytype
79
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_GeometryType', [self], [false, true])
80
+ end
81
+
82
+ def st_astext
83
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_AsText', [self], [false, true])
84
+ end
85
+
86
+ def st_asbinary
87
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_AsBinary', [self], [false, true])
88
+ end
89
+
90
+ def st_srid
91
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_SRID', [self], [false, true])
92
+ end
93
+
94
+ def st_isempty
95
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_IsEmpty', [self], [false, true])
96
+ end
97
+
98
+ def st_issimple
99
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_IsSimple', [self], [false, true])
100
+ end
101
+
102
+ def st_boundary
103
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Boundary', [self], [true, true])
104
+ end
105
+
106
+ def st_envelope
107
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Envelope', [self], [true, true])
108
+ end
109
+
110
+ def st_equals(rhs_)
111
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Equals', [self, rhs_], [false, true, true])
112
+ end
113
+
114
+ def st_disjoint(rhs_)
115
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Disjoint', [self, rhs_], [false, true, true])
116
+ end
117
+
118
+ def st_intersects(rhs_)
119
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Intersects', [self, rhs_], [false, true, true])
120
+ end
121
+
122
+ def st_touches(rhs_)
123
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Touches', [self, rhs_], [false, true, true])
124
+ end
125
+
126
+ def st_crosses(rhs_)
127
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Crosses', [self, rhs_], [false, true, true])
128
+ end
129
+
130
+ def st_within(rhs_)
131
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Within', [self, rhs_], [false, true, true])
132
+ end
133
+
134
+ def st_contains(rhs_)
135
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Contains', [self, rhs_], [false, true, true])
136
+ end
137
+
138
+ def st_overlaps(rhs_)
139
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Overlaps', [self, rhs_], [false, true, true])
140
+ end
141
+
142
+ def st_relate(rhs_, matrix_=nil)
143
+ args_ = [self, rhs_]
144
+ args_ << matrix.to_s if matrix_
145
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Relate', args_, [false, true, true, false])
146
+ end
147
+
148
+ def st_distance(rhs_, units_=nil)
149
+ args_ = [self, rhs_]
150
+ args_ << units.to_s if units_
151
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Distance', args_, [false, true, true, false])
152
+ end
153
+
154
+ def st_intersection(rhs_)
155
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Intersection', [self, rhs_], [true, true, true])
156
+ end
157
+
158
+ def st_difference(rhs_)
159
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Difference', [self, rhs_], [true, true, true])
160
+ end
161
+
162
+ def st_union(rhs_)
163
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Union', [self, rhs_], [true, true, true])
164
+ end
165
+
166
+ def st_symdifference(rhs_)
167
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_SymDifference', [self, rhs_], [true, true, true])
168
+ end
169
+
170
+ def st_buffer(distance_, units_=nil)
171
+ args_ = [self, distance_.to_f]
172
+ args_ << units.to_s if units_
173
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Buffer', args_, [true, true, false])
174
+ end
175
+
176
+ def st_convexhull
177
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_ConvexHull', [self], [true, true])
178
+ end
179
+
180
+
181
+ #--
182
+ # Point functions
183
+ #++
184
+
185
+ def st_x
186
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_X', [self], [false, true])
187
+ end
188
+
189
+ def st_y
190
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Y', [self], [false, true])
191
+ end
192
+
193
+ def st_z
194
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Z', [self], [false, true])
195
+ end
196
+
197
+ def st_m
198
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_M', [self], [false, true])
199
+ end
200
+
201
+
202
+ #--
203
+ # Curve functions
204
+ #++
205
+
206
+ def st_startpoint
207
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_StartPoint', [self], [true, true])
208
+ end
209
+
210
+ def st_endpoint
211
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_EndPoint', [self], [true, true])
212
+ end
213
+
214
+ def st_isclosed
215
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_IsClosed', [self], [false, true])
216
+ end
217
+
218
+ def st_isring
219
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_IsRing', [self], [false, true])
220
+ end
221
+
222
+ def st_length(units_=nil)
223
+ args_ = [self]
224
+ args_ << units.to_s if units_
225
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Length', args_, [false, true, false])
226
+ end
227
+
228
+
229
+ #--
230
+ # LineString functions
231
+ #++
232
+
233
+ def st_numpoints
234
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_NumPoints', [self], [false, true])
235
+ end
236
+
237
+ def st_pointn(n_)
238
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_PointN', [self, n_.to_i], [true, true, false])
239
+ end
240
+
241
+
242
+ #--
243
+ # Surface functions
244
+ #++
245
+
246
+ def st_area(units_=nil)
247
+ args_ = [self]
248
+ args_ << units.to_s if units_
249
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_StartPoint', args_, [false, true, false])
250
+ end
251
+
252
+ def st_centroid
253
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_Centroid', [self], [true, true])
254
+ end
255
+
256
+ def st_pointonsurface
257
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_PointOnSurface', [self], [true, true])
258
+ end
259
+
260
+
261
+ #--
262
+ # Polygon functions
263
+ #++
264
+
265
+ def st_exteriorring
266
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_ExteriorRing', [self], [true, true])
267
+ end
268
+
269
+ def st_numinteriorrings
270
+ # Note: the name difference is intentional. The standard
271
+ # names this function incorrectly.
272
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_NumInteriorRing', [self], [false, true])
273
+ end
274
+
275
+ def st_interiorringn(n_)
276
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_InteriorRingN', [self, n_.to_i], [true, true, false])
277
+ end
278
+
279
+
280
+ #--
281
+ # GeometryCollection functions
282
+ #++
283
+
284
+ def st_numgeometries
285
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_NumGeometries', [self], [false, true])
286
+ end
287
+
288
+ def st_geometryn(n_)
289
+ ::RGeo::ActiveRecord::SpatialNamedFunction.new('ST_GeometryN', [self, n_.to_i], [true, true, false])
290
+ end
291
+
292
+
293
+ end
294
+
295
+
296
+ end
297
+
298
+ end
299
+
300
+
301
+ module Arel
302
+
303
+ # Create a spatial constant node.
304
+ # This node wraps a spatial value (such as an RGeo feature or a text
305
+ # string in WKT format). It supports chaining with the functions
306
+ # defined by RGeo::ActiveRecord::SpatialExpressions.
307
+ #
308
+ # Requires Arel 2.1 or later.
309
+
310
+ def self.spatial(arg_)
311
+ ::RGeo::ActiveRecord::SpatialConstantNode.new(arg_)
312
+ end
313
+
314
+ end
@@ -53,10 +53,15 @@ module RGeo
53
53
  end
54
54
 
55
55
  def call(task_)
56
- config_ = ::ActiveRecord::Base.configurations[@env || ::Rails.env || 'development']
57
- if @pattern === config_['adapter']
58
- task_.actions.delete_if{ |a_| a_ != self }
59
- @proc.call(config_)
56
+ env_ = @env || ::Rails.env || 'development'
57
+ config_ = ::ActiveRecord::Base.configurations[env_]
58
+ if config_
59
+ if @pattern === config_['adapter']
60
+ task_.actions.delete_if{ |a_| a_ != self }
61
+ @proc.call(config_)
62
+ end
63
+ else
64
+ puts "WARNING: Could not find environment #{env_.inspect} in your database.yml"
60
65
  end
61
66
  end
62
67
 
@@ -1,6 +1,6 @@
1
1
  # -----------------------------------------------------------------------------
2
2
  #
3
- # Mysqlgeo adapter for ActiveRecord
3
+ # Version of rgeo-activerecord
4
4
  #
5
5
  # -----------------------------------------------------------------------------
6
6
  # Copyright 2010 Daniel Azuma
@@ -34,44 +34,22 @@
34
34
  ;
35
35
 
36
36
 
37
- require 'arel'
37
+ begin
38
+ require 'versionomy'
39
+ rescue ::LoadError
40
+ end
38
41
 
39
42
 
40
- # The rgeo-activerecord gem installs several minor hacks into Arel to
41
- # support geometry values in the AST.
42
- module Arel
43
-
44
- # Hack Attributes dispatcher to recognize geometry columns.
45
- # This is deprecated but necessary to support legacy Arel versions.
46
- module Attributes # :nodoc:
47
- class << self
48
- if method_defined?(:for)
49
- alias_method :for_without_geometry, :for
50
- def for(column_)
51
- column_.type == :geometry ? Attribute : for_without_geometry(column_)
52
- end
53
- end
54
- end
55
- end
43
+ module RGeo
56
44
 
57
- # Visitors are modified to handle RGeo::Feature::Instance objects in
58
- # the AST.
59
- module Visitors
60
-
61
- # RGeo adds visit_RGeo_Feature_Instance to the Dot visitor.
62
- class Dot
63
- alias :visit_RGeo_Feature_Instance :visit_String
64
- end
45
+ module ActiveRecord
65
46
 
66
- # RGeo adds visit_RGeo_Feature_Instance to the DepthFirst visitor.
67
- class DepthFirst
68
- alias :visit_RGeo_Feature_Instance :terminal
69
- end
47
+ # Current version of RGeo::ActiveRecord as a frozen string
48
+ VERSION_STRING = ::File.read(::File.dirname(__FILE__)+'/../../../Version').strip.freeze
70
49
 
71
- # RGeo adds visit_RGeo_Feature_Instance to the ToSql visitor.
72
- class ToSql
73
- alias :visit_RGeo_Feature_Instance :visit_String
74
- end
50
+ # Current version of RGeo::ActiveRecord as a Versionomy object, if the
51
+ # Versionomy gem is available; otherwise equal to VERSION_STRING.
52
+ VERSION = defined?(::Versionomy) ? ::Versionomy.parse(VERSION_STRING) : VERSION_STRING
75
53
 
76
54
  end
77
55
 
data/test/tc_basic.rb CHANGED
@@ -45,6 +45,11 @@ module RGeo
45
45
  class TestBasic < ::Test::Unit::TestCase # :nodoc:
46
46
 
47
47
 
48
+ def test_has_version
49
+ assert_not_nil(::RGeo::ActiveRecord::VERSION)
50
+ end
51
+
52
+
48
53
  def test_default_factory_generator
49
54
  ::ActiveRecord::Base.rgeo_factory_generator = nil
50
55
  factory_ = ::ActiveRecord::Base.rgeo_factory_for_column(:hello).call(:has_z_coordinate => true, :srid => 4326)
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
- - 4
9
- version: 0.2.4
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Daniel Azuma
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-01-13 00:00:00 -08:00
17
+ date: 2011-01-26 00:00:00 -08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -73,10 +73,12 @@ extra_rdoc_files:
73
73
  - README.rdoc
74
74
  files:
75
75
  - lib/rgeo/active_record/adapter_test_helper.rb
76
- - lib/rgeo/active_record/ar_modifications.rb
77
- - lib/rgeo/active_record/arel_modifications.rb
78
- - lib/rgeo/active_record/common.rb
76
+ - lib/rgeo/active_record/ar_factory_settings.rb
77
+ - lib/rgeo/active_record/arel_spatial_queries.rb
78
+ - lib/rgeo/active_record/common_adapter_elements.rb
79
+ - lib/rgeo/active_record/spatial_expressions.rb
79
80
  - lib/rgeo/active_record/task_hacker.rb
81
+ - lib/rgeo/active_record/version.rb
80
82
  - lib/rgeo/active_record.rb
81
83
  - History.rdoc
82
84
  - README.rdoc
@@ -1,148 +0,0 @@
1
- # -----------------------------------------------------------------------------
2
- #
3
- # Common tools for spatial adapters for ActiveRecord
4
- #
5
- # -----------------------------------------------------------------------------
6
- # Copyright 2010 Daniel Azuma
7
- #
8
- # All rights reserved.
9
- #
10
- # Redistribution and use in source and binary forms, with or without
11
- # modification, are permitted provided that the following conditions are met:
12
- #
13
- # * Redistributions of source code must retain the above copyright notice,
14
- # this list of conditions and the following disclaimer.
15
- # * Redistributions in binary form must reproduce the above copyright notice,
16
- # this list of conditions and the following disclaimer in the documentation
17
- # and/or other materials provided with the distribution.
18
- # * Neither the name of the copyright holder, nor the names of any other
19
- # contributors to this software, may be used to endorse or promote products
20
- # derived from this software without specific prior written permission.
21
- #
22
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
- # POSSIBILITY OF SUCH DAMAGE.
33
- # -----------------------------------------------------------------------------
34
- ;
35
-
36
-
37
- module RGeo
38
-
39
- module ActiveRecord
40
-
41
-
42
- # Additional column types for geometries.
43
- GEOMETRY_TYPES = [:geometry, :point, :line_string, :polygon, :geometry_collection, :multi_line_string, :multi_point, :multi_polygon].freeze
44
-
45
- # The default factory generator for ActiveRecord::Base.
46
- DEFAULT_FACTORY_GENERATOR = ::Proc.new do |config_|
47
- if config_.delete(:geographic)
48
- ::RGeo::Geographic.spherical_factory(config_)
49
- else
50
- ::RGeo::Cartesian.preferred_factory(config_)
51
- end
52
- end
53
-
54
-
55
- # A set of common Arel visitor hacks for spatial ToSql visitors.
56
-
57
- module SpatialToSql
58
-
59
- # Map a standard OGC SQL function name to the actual name used by
60
- # a particular database.
61
- def st_func(standard_name_)
62
- standard_name_
63
- end
64
-
65
- # Returns true if the given node is of spatial type-- that is, if
66
- # it is a spatial literal or a reference to a spatial attribute.
67
- def node_has_spatial_type?(node_)
68
- case node_
69
- when ::Arel::Attribute
70
- @connection.instance_variable_set(:@_getting_columns, true)
71
- begin
72
- col_ = @engine.columns_hash[node_.name.to_s] unless @engine == ::ActiveRecord::Base
73
- col_ && col_.respond_to?(:spatial?) && col_.spatial?
74
- ensure
75
- @connection.instance_variable_set(:@_getting_columns, false)
76
- end
77
- when ::RGeo::Feature::Instance
78
- true
79
- else
80
- false
81
- end
82
- end
83
-
84
- # Generates SQL for a spatial node.
85
- def visit_spatial(node_)
86
- case node_
87
- when ::String
88
- "#{st_func('ST_WKTToSQL')}(#{visit_String(node_)})"
89
- when ::RGeo::Feature::Instance
90
- visit_RGeo_Feature_Instance(node_)
91
- else
92
- visit(node_)
93
- end
94
- end
95
-
96
- def visit_Arel_Nodes_Equality(node_) # :nodoc:
97
- right_ = node_.right
98
- left_ = node_.left
99
- if !@connection.instance_variable_get(:@_getting_columns) && (node_has_spatial_type?(right_) || node_has_spatial_type?(left_))
100
- "#{st_func('ST_Equals')}(#{visit_spatial(left_)}, #{visit_spatial(right_)})"
101
- else
102
- super
103
- end
104
- end
105
-
106
- def visit_Arel_Nodes_NotEqual(node_) # :nodoc:
107
- right_ = node_.right
108
- left_ = node_.left
109
- if !@connection.instance_variable_get(:@_getting_columns) && (node_has_spatial_type?(right_) || node_has_spatial_type?(left_))
110
- "NOT #{st_func('ST_Equals')}(#{visit_spatial(left_)}, #{visit_spatial(right_)})"
111
- else
112
- super
113
- end
114
- end
115
-
116
- end
117
-
118
-
119
- # Index definition struct with a spatial flag field.
120
-
121
- class SpatialIndexDefinition < ::Struct.new(:table, :name, :unique, :columns, :lengths, :spatial)
122
- end
123
-
124
-
125
- class << self
126
-
127
-
128
- # Returns a feature type module given a string type.
129
-
130
- def geometric_type_from_name(name_)
131
- case name_.downcase
132
- when 'geometry' then ::RGeo::Feature::Geometry
133
- when 'point' then ::RGeo::Feature::Point
134
- when 'linestring' then ::RGeo::Feature::LineString
135
- when 'polygon' then ::RGeo::Feature::Polygon
136
- when 'geometrycollection' then ::RGeo::Feature::GeometryCollection
137
- when 'multipoint' then ::RGeo::Feature::MultiPoint
138
- when 'multilinestring' then ::RGeo::Feature::MultiLineString
139
- when 'multipolygon' then ::RGeo::Feature::MultiPolygon
140
- else nil
141
- end
142
- end
143
-
144
- end
145
-
146
- end
147
-
148
- end