From b1ac13770d65f52d2cd22b0e973c1e8be1299975 Mon Sep 17 00:00:00 2001 From: MoisesMachado Date: Wed, 19 Mar 2008 16:11:46 +0000 Subject: [PATCH] ActionItem243: reimplemented search to allow be filtered --- app/controllers/application.rb | 9 +++++++++ app/controllers/public/category_controller.rb | 7 ------- app/controllers/public/search_controller.rb | 39 +++++++++++++++++++++++++++++++++++---- app/models/comment.rb | 3 +++ app/models/environment.rb | 3 +++ config/environment.rb | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ config/routes.rb | 3 ++- test/functional/search_controller_test.rb | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- test/integration/routing_test.rb | 10 +++++++++- test/unit/comment_test.rb | 8 ++++++++ test/unit/environment_test.rb | 14 ++++++++++++++ 11 files changed, 214 insertions(+), 22 deletions(-) diff --git a/app/controllers/application.rb b/app/controllers/application.rb index 4d6fda0..3932f17 100644 --- a/app/controllers/application.rb +++ b/app/controllers/application.rb @@ -102,4 +102,13 @@ class ApplicationController < ActionController::Base end end + def load_category + path = params[:category_path].join('/') + @category = environment.categories.find_by_path(path) + if @category.nil? + render_not_found(path) + end + end + + end diff --git a/app/controllers/public/category_controller.rb b/app/controllers/public/category_controller.rb index 6b394d0..87f1158 100644 --- a/app/controllers/public/category_controller.rb +++ b/app/controllers/public/category_controller.rb @@ -11,12 +11,5 @@ class CategoryController < PublicController attr_reader :category before_filter :load_category, :only => [ :view ] - def load_category - path = params[:path].join('/') - @category = environment.categories.find_by_path(path) - if @category.nil? - render_not_found(path) - end - end end diff --git a/app/controllers/public/search_controller.rb b/app/controllers/public/search_controller.rb index 67c217a..1aa0f98 100644 --- a/app/controllers/public/search_controller.rb +++ b/app/controllers/public/search_controller.rb @@ -4,8 +4,8 @@ class SearchController < ApplicationController protected - def search(klass, query) - klass.find_by_contents(query).sort_by do |hit| + def search(finder, query) + finder.find_by_contents(query).sort_by do |hit| -(relevance_for(hit)) end end @@ -14,13 +14,42 @@ class SearchController < ApplicationController include SearchHelper + ###################################################### + + class Finder + attr_reader :environment + def initialize(env) + @environment = env + end + + def articles + environment.articles + end + + def comments + environment.comments + end + end + def index @query = params[:query] || '' @filtered_query = remove_stop_words(@query) - @articles, @people, @enterprises, @communities, @products = - [Article, Person, Enterprise, Community, Product].map{ |klass| search(klass, @query) } + + @finder ||= SearchController::Finder.new(@environment) + + @results = { :articles => search(@finder.articles, @query), + :comments => search(@finder.comments, @query) } end + before_filter :load_category, :only => :filter + def filter + @finder = @category + index + render :action => 'index' + end + + ####################################################### + def tags @tags = Tag.find(:all).inject({}) do |memo,tag| memo[tag.name] = tag.taggings.count @@ -33,6 +62,8 @@ class SearchController < ApplicationController @tagged = @tag.taggings.map(&:taggable) end + ####################################################### + def popup render :action => 'popup', :layout => false end diff --git a/app/models/comment.rb b/app/models/comment.rb index d9f7d32..1fad56e 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,4 +1,7 @@ class Comment < ActiveRecord::Base + + acts_as_searchable :fields => [:title, :body] + validates_presence_of :title, :body belongs_to :article, :counter_cache => true belongs_to :author, :class_name => 'Person', :foreign_key => 'author_id' diff --git a/app/models/environment.rb b/app/models/environment.rb index ec5ae1e..9a2a1ac 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -211,4 +211,7 @@ class Environment < ActiveRecord::Base self.articles.recent(limit) end + # FIXME is this the better/faster way to do this? + has_many :comments, :finder_sql => 'select comments.* from comments left join articles on articles.id = comments.article_id left join profiles on profiles.id = articles.profile_id where profiles.environment_id = #{id}' + end diff --git a/config/environment.rb b/config/environment.rb index 1d3af49..299e9e2 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -104,3 +104,54 @@ lambda do |localconfig| require localconfig end end.call(File.join(RAILS_ROOT, 'config', 'local.rb')) +# These defaults are used in GeoKit::Mappable.distance_to and in acts_as_mappable +GeoKit::default_units = :miles +GeoKit::default_formula = :sphere + +# This is the timeout value in seconds to be used for calls to the geocoder web +# services. For no timeout at all, comment out the setting. The timeout unit +# is in seconds. +GeoKit::Geocoders::timeout = 3 + +# These settings are used if web service calls must be routed through a proxy. +# These setting can be nil if not needed, otherwise, addr and port must be +# filled in at a minimum. If the proxy requires authentication, the username +# and password can be provided as well. +GeoKit::Geocoders::proxy_addr = nil +GeoKit::Geocoders::proxy_port = nil +GeoKit::Geocoders::proxy_user = nil +GeoKit::Geocoders::proxy_pass = nil + +# This is your yahoo application key for the Yahoo Geocoder. +# See http://developer.yahoo.com/faq/index.html#appid +# and http://developer.yahoo.com/maps/rest/V1/geocode.html +GeoKit::Geocoders::yahoo = 'REPLACE_WITH_YOUR_YAHOO_KEY' + +# This is your Google Maps geocoder key. +# See http://www.google.com/apis/maps/signup.html +# and http://www.google.com/apis/maps/documentation/#Geocoding_Examples +GeoKit::Geocoders::google = 'REPLACE_WITH_YOUR_GOOGLE_KEY' + +# This is your username and password for geocoder.us. +# To use the free service, the value can be set to nil or false. For +# usage tied to an account, the value should be set to username:password. +# See http://geocoder.us +# and http://geocoder.us/user/signup +GeoKit::Geocoders::geocoder_us = false + +# This is your authorization key for geocoder.ca. +# To use the free service, the value can be set to nil or false. For +# usage tied to an account, set the value to the key obtained from +# Geocoder.ca. +# See http://geocoder.ca +# and http://geocoder.ca/?register=1 +GeoKit::Geocoders::geocoder_ca = false + +# This is the order in which the geocoders are called in a failover scenario +# If you only want to use a single geocoder, put a single symbol in the array. +# Valid symbols are :google, :yahoo, :us, and :ca. +# Be aware that there are Terms of Use restrictions on how you can use the +# various geocoders. Make sure you read up on relevant Terms of Use for each +# geocoder you are going to use. +GeoKit::Geocoders::provider_order = [:google,:us] + diff --git a/config/routes.rb b/config/routes.rb index adf92c5..cc3e157 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -31,11 +31,12 @@ ActionController::Routing::Routes.draw do |map| map.tag 'tag/:tag', :controller => 'search', :action => 'tag' # search + map.connect 'search/filter/*category_path', :controller => 'search', :action => 'filter' map.connect 'search/:action', :controller => 'search' # categories controller map.connect 'cat', :controller => 'category', :action => 'index' - map.category 'cat/*path', :controller => 'category', :action => 'view' + map.category 'cat/*category_path', :controller => 'category', :action => 'view' # controllers for blocks map.controllers 'block/:profile/:controller/:action/:id', :controller => Noosfero.pattern_for_controllers_from_design_blocks diff --git a/test/functional/search_controller_test.rb b/test/functional/search_controller_test.rb index b7ddf60..cda0bc3 100644 --- a/test/functional/search_controller_test.rb +++ b/test/functional/search_controller_test.rb @@ -11,15 +11,6 @@ class SearchControllerTest < Test::Unit::TestCase @response = ActionController::TestResponse.new end - should 'find enterprise' do - ent = Enterprise.create!(:name => 'teste', :identifier => 'teste') - get 'index', :query => 'teste' - assert_response :success - assert_template 'index' - assert assigns('enterprises') - assert assigns('enterprises').include?(ent) - end - should 'filter stop words' do @controller.expects(:locale).returns('pt_BR').at_least_once get 'index', :query => 'a carne da vaca' @@ -28,4 +19,84 @@ class SearchControllerTest < Test::Unit::TestCase assert_equal 'carne vaca', assigns('filtered_query') end + should 'search only in specified types of content' do + get :index, :query => 'something not important', :find_in => [ 'articles' ] + assert_equal [:articles], assigns(:results).keys + end + + should 'search in more than one specified types of content' do + get :index, :query => 'something not important', :find_in => [ 'articles', 'comments' ] + assert_equivalent [:articles, :comments ], assigns(:results).keys + end + + should 'render success in search' do + get :index, :query => 'something not important' + assert_response :success + end + + should 'search for articles' do + person = create_user('teste').person + art = person.articles.build(:name => 'an article to be found'); art.save! + + get 'index', :query => 'article found', :find_in => [ 'articles' ] + + assert_includes assigns(:results)[:articles], art + end + + should 'search for articles in a specific category' do + person = create_user('teste').person + category = Category.create!(:name => 'my category', :environment => Environment.default) + + # in category + art1 = person.articles.build(:name => 'an article to be found') + art1.categories << category + art1.save! + + # not in category + art2 = person.articles.build(:name => 'another article to be found') + art2.save! + + get :filter, :category_path => [ 'my-category' ], :query => 'article found', :find_in => [ 'articles' ] + + assert_includes assigns(:results)[:articles], art1 + assert_not_includes assigns(:results)[:articles], art2 + end + + should 'search in comments' do + person = create_user('teste').person + art = person.articles.build(:name => 'an article to be found'); art.save! + comment = art.comments.build(:title => 'comment to be found', :body => 'hfyfyh', :author => person); comment.save! + get 'index', :query => 'found', :find_in => [ 'comments' ] + + assert_includes assigns(:results)[:comments], comment + end + + should 'search in comments in a specific category' + + + should 'find in environment' do + env = mock + finder = SearchController::Finder.new(env) + assert_same env, finder.environment + end + + should 'delegate to environment in default finder' do + env = mock + articles = mock + finder = SearchController::Finder.new(env) + env.expects(:articles).returns(articles) + assert_same articles, finder.articles + end + + should 'find people' + should 'find communities' + + should 'find enterprises' do + ent = Enterprise.create!(:name => 'teste', :identifier => 'teste') + get 'index', :query => 'teste' + assert_includes assigns(:results)[:enterprises], ent + end + + should 'find products' + end diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 1485335..cd3627a 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -69,7 +69,7 @@ class RoutingTest < ActionController::IntegrationTest end def test_category_browser - assert_routing('/cat/products/eletronics', :controller => 'category', :action => 'view', :path => [ 'products', 'eletronics']) + assert_routing('/cat/products/eletronics', :controller => 'category', :action => 'view', :category_path => [ 'products', 'eletronics']) assert_routing('/cat', :controller => 'category', :action => 'index') end @@ -91,4 +91,12 @@ class RoutingTest < ActionController::IntegrationTest assert_routing('/profile/ze/friends', :controller => 'profile', :profile => 'ze', :action => 'friends') end + def test_search_routing + assert_routing('/search', :controller => 'search', :action => 'index') + end + + def test_search_filter_routing + assert_routing('/search/filter/a/b', :controller => 'search', :action => 'filter', :category_path => ['a','b']) + end + end diff --git a/test/unit/comment_test.rb b/test/unit/comment_test.rb index 85134c7..ec58bff 100644 --- a/test/unit/comment_test.rb +++ b/test/unit/comment_test.rb @@ -93,4 +93,12 @@ class CommentTest < Test::Unit::TestCase assert_equal 'comment-4321', comment.anchor end + should 'be searched by contents' do + owner = create_user('testuser').person + art = owner.articles.build(:name => 'ytest'); art.save! + c1 = art.comments.build(:title => 'test comment', :body => 'anything', :author => owner); c1.save! + + assert_includes Comment.find_by_contents('test'), c1 + end + end diff --git a/test/unit/environment_test.rb b/test/unit/environment_test.rb index be25f0d..f6c980e 100644 --- a/test/unit/environment_test.rb +++ b/test/unit/environment_test.rb @@ -263,4 +263,18 @@ class EnvironmentTest < Test::Unit::TestCase assert_kind_of Role, Environment::Roles.admin end + should 'have comments on the articles' do + environment = Environment.default + + p1 = create_user('testuser').person + + doc1 = p1.articles.build(:name => 'text 1'); doc1.save! + doc2 = p1.articles.build(:name => 'text 2'); doc2.save! + + c1 = doc1.comments.build(:title => 'a comment', :body => 'bli', :author => p1); c1.save! + c2 = doc2.comments.build(:title => 'a comment', :body => 'bli', :author => p1); c2.save! + + assert_equivalent [c1,c2], environment.comments + end + end -- libgit2 0.21.2