Commit b1ac13770d65f52d2cd22b0e973c1e8be1299975
1 parent
b67c05a5
Exists in
master
and in
29 other branches
ActionItem243: reimplemented search to allow be filtered
git-svn-id: https://svn.colivre.coop.br/svn/noosfero/trunk@1590 3f533792-8f58-4932-b0fe-aaf55b0a4547
Showing
11 changed files
with
214 additions
and
22 deletions
Show diff stats
app/controllers/application.rb
... | ... | @@ -102,4 +102,13 @@ class ApplicationController < ActionController::Base |
102 | 102 | end |
103 | 103 | end |
104 | 104 | |
105 | + def load_category | |
106 | + path = params[:category_path].join('/') | |
107 | + @category = environment.categories.find_by_path(path) | |
108 | + if @category.nil? | |
109 | + render_not_found(path) | |
110 | + end | |
111 | + end | |
112 | + | |
113 | + | |
105 | 114 | end | ... | ... |
app/controllers/public/category_controller.rb
... | ... | @@ -11,12 +11,5 @@ class CategoryController < PublicController |
11 | 11 | attr_reader :category |
12 | 12 | |
13 | 13 | before_filter :load_category, :only => [ :view ] |
14 | - def load_category | |
15 | - path = params[:path].join('/') | |
16 | - @category = environment.categories.find_by_path(path) | |
17 | - if @category.nil? | |
18 | - render_not_found(path) | |
19 | - end | |
20 | - end | |
21 | 14 | |
22 | 15 | end | ... | ... |
app/controllers/public/search_controller.rb
... | ... | @@ -4,8 +4,8 @@ class SearchController < ApplicationController |
4 | 4 | |
5 | 5 | protected |
6 | 6 | |
7 | - def search(klass, query) | |
8 | - klass.find_by_contents(query).sort_by do |hit| | |
7 | + def search(finder, query) | |
8 | + finder.find_by_contents(query).sort_by do |hit| | |
9 | 9 | -(relevance_for(hit)) |
10 | 10 | end |
11 | 11 | end |
... | ... | @@ -14,13 +14,42 @@ class SearchController < ApplicationController |
14 | 14 | |
15 | 15 | include SearchHelper |
16 | 16 | |
17 | + ###################################################### | |
18 | + | |
19 | + class Finder | |
20 | + attr_reader :environment | |
21 | + def initialize(env) | |
22 | + @environment = env | |
23 | + end | |
24 | + | |
25 | + def articles | |
26 | + environment.articles | |
27 | + end | |
28 | + | |
29 | + def comments | |
30 | + environment.comments | |
31 | + end | |
32 | + end | |
33 | + | |
17 | 34 | def index |
18 | 35 | @query = params[:query] || '' |
19 | 36 | @filtered_query = remove_stop_words(@query) |
20 | - @articles, @people, @enterprises, @communities, @products = | |
21 | - [Article, Person, Enterprise, Community, Product].map{ |klass| search(klass, @query) } | |
37 | + | |
38 | + @finder ||= SearchController::Finder.new(@environment) | |
39 | + | |
40 | + @results = { :articles => search(@finder.articles, @query), | |
41 | + :comments => search(@finder.comments, @query) } | |
22 | 42 | end |
23 | 43 | |
44 | + before_filter :load_category, :only => :filter | |
45 | + def filter | |
46 | + @finder = @category | |
47 | + index | |
48 | + render :action => 'index' | |
49 | + end | |
50 | + | |
51 | + ####################################################### | |
52 | + | |
24 | 53 | def tags |
25 | 54 | @tags = Tag.find(:all).inject({}) do |memo,tag| |
26 | 55 | memo[tag.name] = tag.taggings.count |
... | ... | @@ -33,6 +62,8 @@ class SearchController < ApplicationController |
33 | 62 | @tagged = @tag.taggings.map(&:taggable) |
34 | 63 | end |
35 | 64 | |
65 | + ####################################################### | |
66 | + | |
36 | 67 | def popup |
37 | 68 | render :action => 'popup', :layout => false |
38 | 69 | end | ... | ... |
app/models/comment.rb
app/models/environment.rb
... | ... | @@ -211,4 +211,7 @@ class Environment < ActiveRecord::Base |
211 | 211 | self.articles.recent(limit) |
212 | 212 | end |
213 | 213 | |
214 | + # FIXME is this the better/faster way to do this? | |
215 | + 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}' | |
216 | + | |
214 | 217 | end | ... | ... |
config/environment.rb
... | ... | @@ -104,3 +104,54 @@ lambda do |localconfig| |
104 | 104 | require localconfig |
105 | 105 | end |
106 | 106 | end.call(File.join(RAILS_ROOT, 'config', 'local.rb')) |
107 | +# These defaults are used in GeoKit::Mappable.distance_to and in acts_as_mappable | |
108 | +GeoKit::default_units = :miles | |
109 | +GeoKit::default_formula = :sphere | |
110 | + | |
111 | +# This is the timeout value in seconds to be used for calls to the geocoder web | |
112 | +# services. For no timeout at all, comment out the setting. The timeout unit | |
113 | +# is in seconds. | |
114 | +GeoKit::Geocoders::timeout = 3 | |
115 | + | |
116 | +# These settings are used if web service calls must be routed through a proxy. | |
117 | +# These setting can be nil if not needed, otherwise, addr and port must be | |
118 | +# filled in at a minimum. If the proxy requires authentication, the username | |
119 | +# and password can be provided as well. | |
120 | +GeoKit::Geocoders::proxy_addr = nil | |
121 | +GeoKit::Geocoders::proxy_port = nil | |
122 | +GeoKit::Geocoders::proxy_user = nil | |
123 | +GeoKit::Geocoders::proxy_pass = nil | |
124 | + | |
125 | +# This is your yahoo application key for the Yahoo Geocoder. | |
126 | +# See http://developer.yahoo.com/faq/index.html#appid | |
127 | +# and http://developer.yahoo.com/maps/rest/V1/geocode.html | |
128 | +GeoKit::Geocoders::yahoo = 'REPLACE_WITH_YOUR_YAHOO_KEY' | |
129 | + | |
130 | +# This is your Google Maps geocoder key. | |
131 | +# See http://www.google.com/apis/maps/signup.html | |
132 | +# and http://www.google.com/apis/maps/documentation/#Geocoding_Examples | |
133 | +GeoKit::Geocoders::google = 'REPLACE_WITH_YOUR_GOOGLE_KEY' | |
134 | + | |
135 | +# This is your username and password for geocoder.us. | |
136 | +# To use the free service, the value can be set to nil or false. For | |
137 | +# usage tied to an account, the value should be set to username:password. | |
138 | +# See http://geocoder.us | |
139 | +# and http://geocoder.us/user/signup | |
140 | +GeoKit::Geocoders::geocoder_us = false | |
141 | + | |
142 | +# This is your authorization key for geocoder.ca. | |
143 | +# To use the free service, the value can be set to nil or false. For | |
144 | +# usage tied to an account, set the value to the key obtained from | |
145 | +# Geocoder.ca. | |
146 | +# See http://geocoder.ca | |
147 | +# and http://geocoder.ca/?register=1 | |
148 | +GeoKit::Geocoders::geocoder_ca = false | |
149 | + | |
150 | +# This is the order in which the geocoders are called in a failover scenario | |
151 | +# If you only want to use a single geocoder, put a single symbol in the array. | |
152 | +# Valid symbols are :google, :yahoo, :us, and :ca. | |
153 | +# Be aware that there are Terms of Use restrictions on how you can use the | |
154 | +# various geocoders. Make sure you read up on relevant Terms of Use for each | |
155 | +# geocoder you are going to use. | |
156 | +GeoKit::Geocoders::provider_order = [:google,:us] | |
157 | + | ... | ... |
config/routes.rb
... | ... | @@ -31,11 +31,12 @@ ActionController::Routing::Routes.draw do |map| |
31 | 31 | map.tag 'tag/:tag', :controller => 'search', :action => 'tag' |
32 | 32 | |
33 | 33 | # search |
34 | + map.connect 'search/filter/*category_path', :controller => 'search', :action => 'filter' | |
34 | 35 | map.connect 'search/:action', :controller => 'search' |
35 | 36 | |
36 | 37 | # categories controller |
37 | 38 | map.connect 'cat', :controller => 'category', :action => 'index' |
38 | - map.category 'cat/*path', :controller => 'category', :action => 'view' | |
39 | + map.category 'cat/*category_path', :controller => 'category', :action => 'view' | |
39 | 40 | |
40 | 41 | # controllers for blocks |
41 | 42 | map.controllers 'block/:profile/:controller/:action/:id', :controller => Noosfero.pattern_for_controllers_from_design_blocks | ... | ... |
test/functional/search_controller_test.rb
... | ... | @@ -11,15 +11,6 @@ class SearchControllerTest < Test::Unit::TestCase |
11 | 11 | @response = ActionController::TestResponse.new |
12 | 12 | end |
13 | 13 | |
14 | - should 'find enterprise' do | |
15 | - ent = Enterprise.create!(:name => 'teste', :identifier => 'teste') | |
16 | - get 'index', :query => 'teste' | |
17 | - assert_response :success | |
18 | - assert_template 'index' | |
19 | - assert assigns('enterprises') | |
20 | - assert assigns('enterprises').include?(ent) | |
21 | - end | |
22 | - | |
23 | 14 | should 'filter stop words' do |
24 | 15 | @controller.expects(:locale).returns('pt_BR').at_least_once |
25 | 16 | get 'index', :query => 'a carne da vaca' |
... | ... | @@ -28,4 +19,84 @@ class SearchControllerTest < Test::Unit::TestCase |
28 | 19 | assert_equal 'carne vaca', assigns('filtered_query') |
29 | 20 | end |
30 | 21 | |
22 | + should 'search only in specified types of content' do | |
23 | + get :index, :query => 'something not important', :find_in => [ 'articles' ] | |
24 | + assert_equal [:articles], assigns(:results).keys | |
25 | + end | |
26 | + | |
27 | + should 'search in more than one specified types of content' do | |
28 | + get :index, :query => 'something not important', :find_in => [ 'articles', 'comments' ] | |
29 | + assert_equivalent [:articles, :comments ], assigns(:results).keys | |
30 | + end | |
31 | + | |
32 | + should 'render success in search' do | |
33 | + get :index, :query => 'something not important' | |
34 | + assert_response :success | |
35 | + end | |
36 | + | |
37 | + should 'search for articles' do | |
38 | + person = create_user('teste').person | |
39 | + art = person.articles.build(:name => 'an article to be found'); art.save! | |
40 | + | |
41 | + get 'index', :query => 'article found', :find_in => [ 'articles' ] | |
42 | + | |
43 | + assert_includes assigns(:results)[:articles], art | |
44 | + end | |
45 | + | |
46 | + should 'search for articles in a specific category' do | |
47 | + person = create_user('teste').person | |
48 | + category = Category.create!(:name => 'my category', :environment => Environment.default) | |
49 | + | |
50 | + # in category | |
51 | + art1 = person.articles.build(:name => 'an article to be found') | |
52 | + art1.categories << category | |
53 | + art1.save! | |
54 | + | |
55 | + # not in category | |
56 | + art2 = person.articles.build(:name => 'another article to be found') | |
57 | + art2.save! | |
58 | + | |
59 | + get :filter, :category_path => [ 'my-category' ], :query => 'article found', :find_in => [ 'articles' ] | |
60 | + | |
61 | + assert_includes assigns(:results)[:articles], art1 | |
62 | + assert_not_includes assigns(:results)[:articles], art2 | |
63 | + end | |
64 | + | |
65 | + should 'search in comments' do | |
66 | + person = create_user('teste').person | |
67 | + art = person.articles.build(:name => 'an article to be found'); art.save! | |
68 | + comment = art.comments.build(:title => 'comment to be found', :body => 'hfyfyh', :author => person); comment.save! | |
69 | + get 'index', :query => 'found', :find_in => [ 'comments' ] | |
70 | + | |
71 | + assert_includes assigns(:results)[:comments], comment | |
72 | + end | |
73 | + | |
74 | + should 'search in comments in a specific category' | |
75 | + | |
76 | + | |
77 | + should 'find in environment' do | |
78 | + env = mock | |
79 | + finder = SearchController::Finder.new(env) | |
80 | + assert_same env, finder.environment | |
81 | + end | |
82 | + | |
83 | + should 'delegate to environment in default finder' do | |
84 | + env = mock | |
85 | + articles = mock | |
86 | + finder = SearchController::Finder.new(env) | |
87 | + env.expects(:articles).returns(articles) | |
88 | + assert_same articles, finder.articles | |
89 | + end | |
90 | + | |
91 | + should 'find people' | |
92 | + should 'find communities' | |
93 | + | |
94 | + should 'find enterprises' do | |
95 | + ent = Enterprise.create!(:name => 'teste', :identifier => 'teste') | |
96 | + get 'index', :query => 'teste' | |
97 | + assert_includes assigns(:results)[:enterprises], ent | |
98 | + end | |
99 | + | |
100 | + should 'find products' | |
101 | + | |
31 | 102 | end | ... | ... |
test/integration/routing_test.rb
... | ... | @@ -69,7 +69,7 @@ class RoutingTest < ActionController::IntegrationTest |
69 | 69 | end |
70 | 70 | |
71 | 71 | def test_category_browser |
72 | - assert_routing('/cat/products/eletronics', :controller => 'category', :action => 'view', :path => [ 'products', 'eletronics']) | |
72 | + assert_routing('/cat/products/eletronics', :controller => 'category', :action => 'view', :category_path => [ 'products', 'eletronics']) | |
73 | 73 | assert_routing('/cat', :controller => 'category', :action => 'index') |
74 | 74 | end |
75 | 75 | |
... | ... | @@ -91,4 +91,12 @@ class RoutingTest < ActionController::IntegrationTest |
91 | 91 | assert_routing('/profile/ze/friends', :controller => 'profile', :profile => 'ze', :action => 'friends') |
92 | 92 | end |
93 | 93 | |
94 | + def test_search_routing | |
95 | + assert_routing('/search', :controller => 'search', :action => 'index') | |
96 | + end | |
97 | + | |
98 | + def test_search_filter_routing | |
99 | + assert_routing('/search/filter/a/b', :controller => 'search', :action => 'filter', :category_path => ['a','b']) | |
100 | + end | |
101 | + | |
94 | 102 | end | ... | ... |
test/unit/comment_test.rb
... | ... | @@ -93,4 +93,12 @@ class CommentTest < Test::Unit::TestCase |
93 | 93 | assert_equal 'comment-4321', comment.anchor |
94 | 94 | end |
95 | 95 | |
96 | + should 'be searched by contents' do | |
97 | + owner = create_user('testuser').person | |
98 | + art = owner.articles.build(:name => 'ytest'); art.save! | |
99 | + c1 = art.comments.build(:title => 'test comment', :body => 'anything', :author => owner); c1.save! | |
100 | + | |
101 | + assert_includes Comment.find_by_contents('test'), c1 | |
102 | + end | |
103 | + | |
96 | 104 | end | ... | ... |
test/unit/environment_test.rb
... | ... | @@ -263,4 +263,18 @@ class EnvironmentTest < Test::Unit::TestCase |
263 | 263 | assert_kind_of Role, Environment::Roles.admin |
264 | 264 | end |
265 | 265 | |
266 | + should 'have comments on the articles' do | |
267 | + environment = Environment.default | |
268 | + | |
269 | + p1 = create_user('testuser').person | |
270 | + | |
271 | + doc1 = p1.articles.build(:name => 'text 1'); doc1.save! | |
272 | + doc2 = p1.articles.build(:name => 'text 2'); doc2.save! | |
273 | + | |
274 | + c1 = doc1.comments.build(:title => 'a comment', :body => 'bli', :author => p1); c1.save! | |
275 | + c2 = doc2.comments.build(:title => 'a comment', :body => 'bli', :author => p1); c2.save! | |
276 | + | |
277 | + assert_equivalent [c1,c2], environment.comments | |
278 | + end | |
279 | + | |
266 | 280 | end | ... | ... |