Commit 1fb84838d1ea57d5066441ea180fe07fc1b27395
1 parent
a03718fe
Exists in
master
and in
28 other branches
ActionItem280: PostgreSQL porting
git-svn-id: https://svn.colivre.coop.br/svn/noosfero/trunk@1749 3f533792-8f58-4932-b0fe-aaf55b0a4547
Showing
11 changed files
with
71 additions
and
90 deletions
Show diff stats
app/controllers/public/search_controller.rb
@@ -67,7 +67,7 @@ class SearchController < ApplicationController | @@ -67,7 +67,7 @@ class SearchController < ApplicationController | ||
67 | @results = {} | 67 | @results = {} |
68 | @names = {} | 68 | @names = {} |
69 | SEARCH_IN.each do |key, description| | 69 | SEARCH_IN.each do |key, description| |
70 | - @results[key] = @finder.send(key, @filtered_query) if @searching[key] | 70 | + @results[key] = @finder.find(key, @filtered_query) if @searching[key] |
71 | @names[key] = gettext(description) | 71 | @names[key] = gettext(description) |
72 | end | 72 | end |
73 | end | 73 | end |
app/models/article.rb
@@ -113,7 +113,7 @@ class Article < ActiveRecord::Base | @@ -113,7 +113,7 @@ class Article < ActiveRecord::Base | ||
113 | end | 113 | end |
114 | 114 | ||
115 | def self.find_by_initial(initial) | 115 | def self.find_by_initial(initial) |
116 | - self.find(:all, :order => 'articles.name', :conditions => [ 'articles.name like (?)', initial + '%']) | 116 | + self.find(:all, :order => 'articles.name', :conditions => [ 'articles.name like (?) or articles.name like (?)', initial + '%', initial.upcase + '%']) |
117 | end | 117 | end |
118 | 118 | ||
119 | private | 119 | private |
app/models/category_finder.rb
@@ -7,28 +7,8 @@ class CategoryFinder | @@ -7,28 +7,8 @@ class CategoryFinder | ||
7 | 7 | ||
8 | attr_reader :category_ids | 8 | attr_reader :category_ids |
9 | 9 | ||
10 | - def articles(query='*', options={}) | ||
11 | - find_in_categorized(Article, query, options) | ||
12 | - end | ||
13 | - | ||
14 | - def people(query='*', options={}) | ||
15 | - find_in_categorized(Person, query, options) | ||
16 | - end | ||
17 | - | ||
18 | - def communities(query='*', options={}) | ||
19 | - find_in_categorized(Community, query, options) | ||
20 | - end | ||
21 | - | ||
22 | - def enterprises(query='*', options={}) | ||
23 | - find_in_categorized(Enterprise, query, options) | ||
24 | - end | ||
25 | - | ||
26 | - def products(query='*', options={}) | ||
27 | - find_in_categorized(Product, query, options) | ||
28 | - end | ||
29 | - | ||
30 | - def comments(query='*', options={}) | ||
31 | - find_in_categorized(Comment, query, options) | 10 | + def find(asset, query) |
11 | + find_in_categorized(asset.to_s.singularize.camelize.constantize, query) | ||
32 | end | 12 | end |
33 | 13 | ||
34 | def recent(asset, limit = 10) | 14 | def recent(asset, limit = 10) |
@@ -70,11 +50,11 @@ class CategoryFinder | @@ -70,11 +50,11 @@ class CategoryFinder | ||
70 | # FIXME copy/pasted from options_for_find above !!! | 50 | # FIXME copy/pasted from options_for_find above !!! |
71 | case klass.name | 51 | case klass.name |
72 | when 'Comment' | 52 | when 'Comment' |
73 | - {:select => 'distinct comments.*', :joins => 'inner join articles_categories on articles_categories.article_id = comments.article_id', :conditions => ['articles_categories.category_id in (?) and comments.title like (?)', category_ids, initial + '%']} | 53 | + {:select => 'distinct comments.*', :joins => 'inner join articles_categories on articles_categories.article_id = comments.article_id', :conditions => ['articles_categories.category_id in (?) and (comments.title like (?) or comments.title like (?))', category_ids, initial + '%', initial.upcase + '%']} |
74 | when 'Product' | 54 | when 'Product' |
75 | - {:select => 'distinct products.*', :joins => 'inner join categories_profiles on products.enterprise_id = categories_profiles.profile_id', :conditions => ['categories_profiles.category_id in (?) and products.name like (?)', category_ids, initial + '%']} | 55 | + {:select => 'distinct products.*', :joins => 'inner join categories_profiles on products.enterprise_id = categories_profiles.profile_id', :conditions => ['categories_profiles.category_id in (?) and (products.name like (?) or products.name like (?))', category_ids, initial + '%', initial.upcase + '%']} |
76 | when 'Article', 'Person', 'Community', 'Enterprise' | 56 | when 'Article', 'Person', 'Community', 'Enterprise' |
77 | - {:include => 'categories', :conditions => ['categories.id IN (?) and %s.name like (?)' % klass.table_name, category_ids, initial + '%']} | 57 | + {:include => 'categories', :conditions => ['categories.id IN (?) and (%s.name like (?) or %s.name like (?))' % [klass.table_name, klass.table_name], category_ids, initial + '%', initial.upcase + '%']} |
78 | else | 58 | else |
79 | raise "unreconized class #{klass.name}" | 59 | raise "unreconized class #{klass.name}" |
80 | end | 60 | end |
app/models/comment.rb
@@ -42,7 +42,7 @@ class Comment < ActiveRecord::Base | @@ -42,7 +42,7 @@ class Comment < ActiveRecord::Base | ||
42 | end | 42 | end |
43 | 43 | ||
44 | def self.find_by_initial(initial) | 44 | def self.find_by_initial(initial) |
45 | - self.find(:all, :order => 'comments.title', :conditions => ['comments.title like (?)', initial + '%']) | 45 | + self.find(:all, :order => 'comments.title', :conditions => ['comments.title like (?) or comments.title like (?)', initial + '%', initial.upcase + '%']) |
46 | end | 46 | end |
47 | 47 | ||
48 | end | 48 | end |
app/models/environment_finder.rb
@@ -4,33 +4,13 @@ class EnvironmentFinder | @@ -4,33 +4,13 @@ class EnvironmentFinder | ||
4 | @environment = env | 4 | @environment = env |
5 | end | 5 | end |
6 | 6 | ||
7 | - def articles(query='*', options = {}) | ||
8 | - @environment.articles.find_by_contents(query, {}, options) | ||
9 | - end | ||
10 | - | ||
11 | - def people(query='*', options = {}) | ||
12 | - @environment.people.find_by_contents(query, {}, options) | ||
13 | - end | ||
14 | - | ||
15 | - def communities(query='*', options = {}) | ||
16 | - @environment.communities.find_by_contents(query, {}, options) | ||
17 | - end | ||
18 | - | ||
19 | - def products(query='*', options = {}) | ||
20 | - @environment.products.find_by_contents(query, {}, options) | ||
21 | - end | ||
22 | - | ||
23 | - def enterprises(query='*', options = {}) | ||
24 | - @environment.enterprises.find_by_contents(query, {}, options) | ||
25 | - end | ||
26 | - | ||
27 | - def comments(query='*', options = {}) | ||
28 | - @environment.comments.find_by_contents(query, {}, options) | 7 | + def find(asset, query) |
8 | + @environment.send(asset).find_by_contents(query) | ||
29 | end | 9 | end |
30 | 10 | ||
31 | def recent(asset, limit = 10) | 11 | def recent(asset, limit = 10) |
32 | with_options :limit => limit, :order => 'created_at desc, id desc' do |finder| | 12 | with_options :limit => limit, :order => 'created_at desc, id desc' do |finder| |
33 | - finder.send(asset, '*', {}) | 13 | + @environment.send(asset).recent(limit) |
34 | end | 14 | end |
35 | end | 15 | end |
36 | 16 |
app/models/product.rb
@@ -39,7 +39,7 @@ class Product < ActiveRecord::Base | @@ -39,7 +39,7 @@ class Product < ActiveRecord::Base | ||
39 | end | 39 | end |
40 | 40 | ||
41 | def self.find_by_initial(initial) | 41 | def self.find_by_initial(initial) |
42 | - self.find(:all, :order => 'products.name', :conditions => [ 'products.name like (?)', initial + '%']) | 42 | + self.find(:all, :order => 'products.name', :conditions => [ 'products.name like (?) or products.name like (?)', initial + '%', initial.upcase + '%']) |
43 | end | 43 | end |
44 | 44 | ||
45 | end | 45 | end |
app/models/profile.rb
@@ -290,7 +290,7 @@ class Profile < ActiveRecord::Base | @@ -290,7 +290,7 @@ class Profile < ActiveRecord::Base | ||
290 | end | 290 | end |
291 | 291 | ||
292 | def self.find_by_initial(initial) | 292 | def self.find_by_initial(initial) |
293 | - self.find(:all, :order => 'profiles.name', :conditions => [ 'profiles.name like (?)', (initial + '%') ]) | 293 | + self.find(:all, :order => 'profiles.name', :conditions => [ 'profiles.name like (?) or profiles.name like (?)', (initial + '%'), (initial.upcase + '%') ]) |
294 | end | 294 | end |
295 | 295 | ||
296 | end | 296 | end |
test/functional/search_controller_test.rb
@@ -236,7 +236,7 @@ class SearchControllerTest < Test::Unit::TestCase | @@ -236,7 +236,7 @@ class SearchControllerTest < Test::Unit::TestCase | ||
236 | 236 | ||
237 | get :assets, :asset => 'people' | 237 | get :assets, :asset => 'people' |
238 | 238 | ||
239 | - assert_equal [p2,p1], assigns(:results)[:people].instance_variable_get('@results') | 239 | + assert_equivalent [p2,p1], assigns(:results)[:people] |
240 | end | 240 | end |
241 | 241 | ||
242 | # 'assets' menu inside a category | 242 | # 'assets' menu inside a category |
@@ -274,7 +274,7 @@ class SearchControllerTest < Test::Unit::TestCase | @@ -274,7 +274,7 @@ class SearchControllerTest < Test::Unit::TestCase | ||
274 | c2 = Community.create!(:name => 'another beautiful community', :identifier => 'an_bea_comm', :environment => Environment.default) | 274 | c2 = Community.create!(:name => 'another beautiful community', :identifier => 'an_bea_comm', :environment => Environment.default) |
275 | 275 | ||
276 | get :assets, :asset => 'communities' | 276 | get :assets, :asset => 'communities' |
277 | - assert_equal [c2, c1], assigns(:results)[:communities].instance_variable_get('@results') | 277 | + assert_equivalent [c2, c1], assigns(:results)[:communities] |
278 | end | 278 | end |
279 | 279 | ||
280 | # 'assets' menu | 280 | # 'assets' menu |
test/unit/category_finder_test.rb
@@ -19,8 +19,9 @@ class CategoryFinderTest < ActiveSupport::TestCase | @@ -19,8 +19,9 @@ class CategoryFinderTest < ActiveSupport::TestCase | ||
19 | art2 = person.articles.build(:name => 'another article to be found') | 19 | art2 = person.articles.build(:name => 'another article to be found') |
20 | art2.save! | 20 | art2.save! |
21 | 21 | ||
22 | - assert_includes @finder.articles, art1 | ||
23 | - assert_not_includes @finder.articles, art2 | 22 | + list = @finder.find(:articles, 'found') |
23 | + assert_includes list, art1 | ||
24 | + assert_not_includes list, art2 | ||
24 | end | 25 | end |
25 | 26 | ||
26 | should 'search for comments in a specific category' do | 27 | should 'search for comments in a specific category' do |
@@ -37,35 +38,41 @@ class CategoryFinderTest < ActiveSupport::TestCase | @@ -37,35 +38,41 @@ class CategoryFinderTest < ActiveSupport::TestCase | ||
37 | art2.save! | 38 | art2.save! |
38 | comment2 = art2.comments.build(:title => 'comment to be found', :body => 'hfyfyh', :author => person); comment2.save! | 39 | comment2 = art2.comments.build(:title => 'comment to be found', :body => 'hfyfyh', :author => person); comment2.save! |
39 | 40 | ||
40 | - assert_includes @finder.comments, comment1 | ||
41 | - assert_not_includes @finder.comments, comment2 | 41 | + list = @finder.find(:comments, 'found') |
42 | + assert_includes list, comment1 | ||
43 | + assert_not_includes list, comment2 | ||
42 | end | 44 | end |
43 | 45 | ||
44 | should 'search for enterprises in a specific category' do | 46 | should 'search for enterprises in a specific category' do |
45 | 47 | ||
46 | # in category | 48 | # in category |
47 | - ent1 = Enterprise.create!(:name => 'testing enterprise 1', :identifier => 'test1', :categories => [@category]) | 49 | + ent1 = Enterprise.create!(:name => 'beautiful enterprise 1', :identifier => 'test1', :categories => [@category]) |
48 | 50 | ||
49 | # not in category | 51 | # not in category |
50 | - ent2 = Enterprise.create!(:name => 'testing enterprise 2', :identifier => 'test2') | 52 | + ent2 = Enterprise.create!(:name => 'beautiful enterprise 2', :identifier => 'test2') |
51 | 53 | ||
52 | - assert_includes @finder.enterprises, ent1 | ||
53 | - assert_not_includes @finder.enterprises, ent2 | 54 | + list = @finder.find(:enterprises, 'beautiful') |
55 | + assert_includes list, ent1 | ||
56 | + assert_not_includes list, ent2 | ||
54 | end | 57 | end |
55 | 58 | ||
56 | should 'search for people in a specific category' do | 59 | should 'search for people in a specific category' do |
57 | p1 = create_user('people_1').person; p1.name = 'a beautiful person'; p1.categories << @category; p1.save! | 60 | p1 = create_user('people_1').person; p1.name = 'a beautiful person'; p1.categories << @category; p1.save! |
58 | p2 = create_user('people_2').person; p2.name = 'another beautiful person'; p2.save! | 61 | p2 = create_user('people_2').person; p2.name = 'another beautiful person'; p2.save! |
59 | - assert_includes @finder.people, p1 | ||
60 | - assert_not_includes @finder.people, p2 | 62 | + |
63 | + list = @finder.find(:people, 'beautiful') | ||
64 | + assert_includes list, p1 | ||
65 | + assert_not_includes list, p2 | ||
61 | end | 66 | end |
62 | 67 | ||
63 | should 'search for communities in a specific category' do | 68 | should 'search for communities in a specific category' do |
64 | c1 = Community.create!(:name => 'a beautiful community', :identifier => 'bea_comm', :environment => Environment.default) | 69 | c1 = Community.create!(:name => 'a beautiful community', :identifier => 'bea_comm', :environment => Environment.default) |
65 | c2 = Community.create!(:name => 'another beautiful community', :identifier => 'an_bea_comm', :environment => Environment.default) | 70 | c2 = Community.create!(:name => 'another beautiful community', :identifier => 'an_bea_comm', :environment => Environment.default) |
66 | c1.categories << @category; c1.save! | 71 | c1.categories << @category; c1.save! |
67 | - assert_includes @finder.communities, c1 | ||
68 | - assert_not_includes @finder.communities, c2 | 72 | + |
73 | + list = @finder.find(:communities, 'beautiful') | ||
74 | + assert_includes list, c1 | ||
75 | + assert_not_includes list, c2 | ||
69 | end | 76 | end |
70 | 77 | ||
71 | should 'search for products in a specific category' do | 78 | should 'search for products in a specific category' do |
@@ -73,8 +80,10 @@ class CategoryFinderTest < ActiveSupport::TestCase | @@ -73,8 +80,10 @@ class CategoryFinderTest < ActiveSupport::TestCase | ||
73 | ent2 = Enterprise.create!(:name => 'teste2', :identifier => 'teste2') | 80 | ent2 = Enterprise.create!(:name => 'teste2', :identifier => 'teste2') |
74 | prod1 = ent1.products.create!(:name => 'a beautiful product') | 81 | prod1 = ent1.products.create!(:name => 'a beautiful product') |
75 | prod2 = ent2.products.create!(:name => 'another beautiful product') | 82 | prod2 = ent2.products.create!(:name => 'another beautiful product') |
76 | - assert_includes @finder.products, prod1 | ||
77 | - assert_not_includes @finder.products, prod2 | 83 | + |
84 | + list = @finder.find(:products, 'beautiful') | ||
85 | + assert_includes list, prod1 | ||
86 | + assert_not_includes list, prod2 | ||
78 | end | 87 | end |
79 | 88 | ||
80 | should 'load ids for category full hierarchy' do | 89 | should 'load ids for category full hierarchy' do |
@@ -93,7 +102,7 @@ class CategoryFinderTest < ActiveSupport::TestCase | @@ -93,7 +102,7 @@ class CategoryFinderTest < ActiveSupport::TestCase | ||
93 | p1 = create_user('people_1').person; p1.name = 'a beautiful person'; p1.categories << child; p1.save! | 102 | p1 = create_user('people_1').person; p1.name = 'a beautiful person'; p1.categories << child; p1.save! |
94 | 103 | ||
95 | f = CategoryFinder.new(parent) | 104 | f = CategoryFinder.new(parent) |
96 | - assert_includes f.people, p1 | 105 | + assert_includes f.find(:people, 'beautiful'), p1 |
97 | end | 106 | end |
98 | 107 | ||
99 | should 'list recent enterprises' do | 108 | should 'list recent enterprises' do |
@@ -141,7 +150,9 @@ class CategoryFinderTest < ActiveSupport::TestCase | @@ -141,7 +150,9 @@ class CategoryFinderTest < ActiveSupport::TestCase | ||
141 | p1.categories << parent; p1.save! | 150 | p1.categories << parent; p1.save! |
142 | 151 | ||
143 | f = CategoryFinder.new(parent) | 152 | f = CategoryFinder.new(parent) |
144 | - result = f.people | 153 | + result = f.find(:people, 'beautiful') |
154 | + | ||
155 | + assert_equivalent [p1], result | ||
145 | assert_equal 1, result.size | 156 | assert_equal 1, result.size |
146 | end | 157 | end |
147 | 158 |
test/unit/environment_finder_test.rb
@@ -8,39 +8,39 @@ class EnvironmentFinderTest < ActiveSupport::TestCase | @@ -8,39 +8,39 @@ class EnvironmentFinderTest < ActiveSupport::TestCase | ||
8 | person = create_user('teste').person | 8 | person = create_user('teste').person |
9 | art = person.articles.build(:name => 'an article to be found'); art.save! | 9 | art = person.articles.build(:name => 'an article to be found'); art.save! |
10 | finder = EnvironmentFinder.new(Environment.default) | 10 | finder = EnvironmentFinder.new(Environment.default) |
11 | - assert_includes finder.articles, art | 11 | + assert_includes finder.find(:articles, 'found'), art |
12 | end | 12 | end |
13 | 13 | ||
14 | should 'find people' do | 14 | should 'find people' do |
15 | p1 = create_user('people_1').person; p1.name = 'a beautiful person'; p1.save! | 15 | p1 = create_user('people_1').person; p1.name = 'a beautiful person'; p1.save! |
16 | finder = EnvironmentFinder.new(Environment.default) | 16 | finder = EnvironmentFinder.new(Environment.default) |
17 | - assert_includes finder.people, p1 | 17 | + assert_includes finder.find(:people, 'beautiful'), p1 |
18 | end | 18 | end |
19 | 19 | ||
20 | should 'find communities' do | 20 | should 'find communities' do |
21 | c1 = Community.create!(:name => 'a beautiful community', :identifier => 'bea_comm', :environment => Environment.default) | 21 | c1 = Community.create!(:name => 'a beautiful community', :identifier => 'bea_comm', :environment => Environment.default) |
22 | finder = EnvironmentFinder.new(Environment.default) | 22 | finder = EnvironmentFinder.new(Environment.default) |
23 | - assert_includes finder.communities, c1 | 23 | + assert_includes finder.find(:communities, 'beautiful'), c1 |
24 | end | 24 | end |
25 | 25 | ||
26 | should 'find comments' do | 26 | should 'find comments' do |
27 | person = create_user('teste').person | 27 | person = create_user('teste').person |
28 | art = person.articles.build(:name => 'an article to be found'); art.save! | 28 | art = person.articles.build(:name => 'an article to be found'); art.save! |
29 | comment = art.comments.build(:title => 'comment to be found', :body => 'some sample text', :author => person); comment.save! | 29 | comment = art.comments.build(:title => 'comment to be found', :body => 'some sample text', :author => person); comment.save! |
30 | - assert_includes EnvironmentFinder.new(Environment.default).comments('found'), comment | 30 | + assert_includes EnvironmentFinder.new(Environment.default).find(:comments, 'found'), comment |
31 | end | 31 | end |
32 | 32 | ||
33 | should 'find products' do | 33 | should 'find products' do |
34 | finder = EnvironmentFinder.new(Environment.default) | 34 | finder = EnvironmentFinder.new(Environment.default) |
35 | ent = Enterprise.create!(:name => 'teste', :identifier => 'teste') | 35 | ent = Enterprise.create!(:name => 'teste', :identifier => 'teste') |
36 | prod = ent.products.create!(:name => 'a beautiful product') | 36 | prod = ent.products.create!(:name => 'a beautiful product') |
37 | - assert_includes finder.products, prod | 37 | + assert_includes finder.find(:products, 'beautiful'), prod |
38 | end | 38 | end |
39 | 39 | ||
40 | should 'find enterprises' do | 40 | should 'find enterprises' do |
41 | finder = EnvironmentFinder.new(Environment.default) | 41 | finder = EnvironmentFinder.new(Environment.default) |
42 | - ent = Enterprise.create!(:name => 'teste', :identifier => 'teste') | ||
43 | - assert_includes finder.enterprises, ent | 42 | + ent = Enterprise.create!(:name => 'a beautiful enterprise', :identifier => 'teste') |
43 | + assert_includes finder.find(:enterprises, 'beautiful'), ent | ||
44 | end | 44 | end |
45 | 45 | ||
46 | should 'list recent enterprises' do | 46 | should 'list recent enterprises' do |
test/unit/sqlite_extension_test.rb
@@ -4,21 +4,31 @@ require File.dirname(__FILE__) + '/../test_helper' | @@ -4,21 +4,31 @@ require File.dirname(__FILE__) + '/../test_helper' | ||
4 | # will just pass. The idea is to test our local extensions to SQLite. | 4 | # will just pass. The idea is to test our local extensions to SQLite. |
5 | class SQliteExtensionTest < Test::Unit::TestCase | 5 | class SQliteExtensionTest < Test::Unit::TestCase |
6 | 6 | ||
7 | - should 'have power function' do | ||
8 | - assert_in_delta 8.0, ActiveRecord::Base.connection.execute('select pow(2.0, 3.0) as result').first['result'], 0.0001 | ||
9 | - end | 7 | + if ActiveRecord::Base.connection.adapter_name =~ /^sqlite$/i |
10 | 8 | ||
11 | - should 'have radians function' do | ||
12 | - assert_in_delta Math::PI/2, ActiveRecord::Base.connection.execute('select radians(90) as rad').first['rad'], 0.0001 | ||
13 | - end | 9 | + should 'have power function' do |
10 | + assert_in_delta 8.0, ActiveRecord::Base.connection.execute('select pow(2.0, 3.0) as result').first['result'], 0.0001 | ||
11 | + end | ||
14 | 12 | ||
15 | - should 'have square root function' do | ||
16 | - assert_in_delta 1.4142, ActiveRecord::Base.connection.execute('select sqrt(2) as sqrt').first['sqrt'], 0.0001 | ||
17 | - end | 13 | + should 'have radians function' do |
14 | + assert_in_delta Math::PI/2, ActiveRecord::Base.connection.execute('select radians(90) as rad').first['rad'], 0.0001 | ||
15 | + end | ||
16 | + | ||
17 | + should 'have square root function' do | ||
18 | + assert_in_delta 1.4142, ActiveRecord::Base.connection.execute('select sqrt(2) as sqrt').first['sqrt'], 0.0001 | ||
19 | + end | ||
20 | + | ||
21 | + should 'have a distance function' do | ||
22 | + args = [32.918593, -96.958444, 32.951613, -96.958444].map{|l|l * Math::PI/180} | ||
23 | + assert_in_delta 2.28402, ActiveRecord::Base.connection.execute("select spheric_distance(#{args.inspect[1..-2]}, 3963.19) as dist").first['dist'], 0.0001 | ||
24 | + end | ||
25 | + | ||
26 | + else | ||
27 | + | ||
28 | + should 'just pass (not using SQLite)' do | ||
29 | + assert true | ||
30 | + end | ||
18 | 31 | ||
19 | - should 'have a distance function' do | ||
20 | - args = [32.918593, -96.958444, 32.951613, -96.958444].map{|l|l * Math::PI/180} | ||
21 | - assert_in_delta 2.28402, ActiveRecord::Base.connection.execute("select spheric_distance(#{args.inspect[1..-2]}, 3963.19) as dist").first['dist'], 0.0001 | ||
22 | end | 32 | end |
23 | 33 | ||
24 | end | 34 | end |