Commit b660ef0e0481bf320eabac67a0c403e61b5246a2

Authored by MoisesMachado
1 parent e4cf222c

ActionItem129: put the sidebar to the search of products that have been searched


git-svn-id: https://svn.colivre.coop.br/svn/noosfero/trunk@1914 3f533792-8f58-4932-b0fe-aaf55b0a4547
app/controllers/public/search_controller.rb
... ... @@ -108,6 +108,7 @@ class SearchController < ApplicationController
108 108 def index
109 109 @query = params[:query] || ''
110 110 @filtered_query = remove_stop_words(@query)
  111 + @product_category = ProductCategory.find(params[:product_category]) if params[:product_category]
111 112  
112 113 # FIXME name is not unique
113 114 @region = Region.find_by_name(params[:region][:name]) if params[:region]
... ... @@ -116,9 +117,9 @@ class SearchController < ApplicationController
116 117 @names = {}
117 118 SEARCH_IN.each do |key, description|
118 119 if [:enterprises, :people].include?(key) && @region
119   - @results[key] = @finder.find(key, @filtered_query, :within => params[:radius], :region => @region.id) if @searching[key]
  120 + @results[key] = @finder.find(key, @filtered_query, :within => params[:radius], :region => @region.id, :product_category => @product_category) if @searching[key]
120 121 else
121   - @results[key] = @finder.find(key, @filtered_query) if @searching[key]
  122 + @results[key] = @finder.find(key, @filtered_query, :product_category => @product_category) if @searching[key]
122 123 end
123 124 @names[key] = gettext(description)
124 125 end
... ... @@ -136,6 +137,22 @@ class SearchController < ApplicationController
136 137 render :action => 'index'
137 138 end
138 139  
  140 + def products
  141 + @categories = @results[:products].map(&:product_category).compact
  142 + @counts = @categories.uniq.inject({}) do |h, cat|
  143 + h[cat.id] = [cat, 0]
  144 + h
  145 + end
  146 +
  147 + @categories.each do |cat|
  148 + cat.hierarchy.each do |each_cat|
  149 + @counts[each_cat.id][1] += 1 if @counts[each_cat.id]
  150 + end
  151 + end
  152 +
  153 + @cats = @counts.values.sort_by{|v|v[0].full_name}
  154 + end
  155 +
139 156 alias :assets :index
140 157  
141 158 #######################################################
... ... @@ -148,7 +165,6 @@ class SearchController < ApplicationController
148 165 [ :people, _('Recently registered people'), @finder.recent('people') ],
149 166 [ :communities, _('Recently created communities'), @finder.recent('communities') ],
150 167 [ :articles, _('Recent articles'), @finder.recent('articles') ],
151   - [ :comments, _('Recent comments'), @finder.recent('comments') ],
152 168 [ :most_commented_articles, _('Most commented articles'), @finder.most_commented_articles ],
153 169 [ :enterprises, _('Recently created enterprises'), @finder.recent('enterprises') ],
154 170 [ :events, _('Recently added events'), @finder.current_events(params[:year], params[:month]) ]
... ...
app/models/article.rb
... ... @@ -26,7 +26,7 @@ class Article < ActiveRecord::Base
26 26 def comment_data
27 27 comments.map {|item| [item.title, item.body].join(' ') }.join(' ')
28 28 end
29   -
  29 +
30 30 before_update do |article|
31 31 article.advertise = true
32 32 end
... ...
app/models/category_finder.rb
... ... @@ -7,11 +7,20 @@ class CategoryFinder
7 7  
8 8 attr_reader :category_ids
9 9  
  10 +
  11 +
10 12 def find(asset, query = nil, options={}, limit = nil)
11   - if query.blank?
12   - asset_class(asset).find(:all, options_for_find(asset_class(asset), {:limit => limit, :order => "created_at desc, #{asset_table(asset)}.id desc"}))
  13 + @region = Region.find_by_id(options.delete(:region)) if options.has_key?(:region)
  14 + if @region && options[:within]
  15 + options[:origin] = [@region.lat, @region.lng]
13 16 else
14   - find_in_categorized(asset.to_s.singularize.camelize.constantize, query, options)
  17 + options.delete(:within)
  18 + end
  19 +
  20 + if query.blank?
  21 + asset_class(asset).find(:all, options_for_find(asset_class(asset), {:limit => limit, :order => "created_at desc, #{asset_table(asset)}.id desc"}.merge(options)))
  22 + else
  23 + asset_class(asset).find_by_contents(query, {}, options_for_find(asset_class(asset), options)).uniq
15 24 end
16 25 end
17 26  
... ... @@ -39,27 +48,21 @@ class CategoryFinder
39 48  
40 49 protected
41 50  
42   - def find_in_categorized(klass, query, options={})
43   - @region = Region.find_by_id(options.delete(:region)) if options.has_key?(:region)
44   - if @region && options[:within]
45   - options[:origin] = [@region.lat, @region.lng]
46   - else
47   - options.delete(:within)
48   - end
49   -
50   - if query.nil?
51   - klass.find(:all, options_for_find(klass, options))
52   - else
53   - klass.find_by_contents(query, {}, options_for_find(klass, options)).uniq
54   - end
55   - end
56   -
57 51 def options_for_find(klass, options={})
  52 + if defined? options[:product_category]
  53 + prod_cat = options.delete(:product_category)
  54 + prod_cat_ids = prod_cat.map_traversal(&:id) if prod_cat
  55 + end
  56 +
58 57 case klass.name
59 58 when 'Comment'
60 59 {:select => 'distinct comments.*', :joins => 'inner join articles_categories on articles_categories.article_id = comments.article_id', :conditions => ['articles_categories.category_id in (?)', category_ids]}.merge!(options)
61 60 when 'Product'
62   - {:select => 'distinct products.*', :joins => 'inner join categories_profiles on products.enterprise_id = categories_profiles.profile_id', :conditions => ['categories_profiles.category_id in (?)', category_ids]}.merge!(options)
  61 + if prod_cat_ids
  62 + {:select => 'distinct products.*', :joins => 'inner join categories_profiles on products.enterprise_id = categories_profiles.profile_id', :conditions => ['categories_profiles.category_id in (?) and products.product_category_id in (?)', category_ids, prod_cat_ids]}.merge!(options)
  63 + else
  64 + {:select => 'distinct products.*', :joins => 'inner join categories_profiles on products.enterprise_id = categories_profiles.profile_id', :conditions => ['categories_profiles.category_id in (?)', category_ids]}.merge!(options)
  65 + end
63 66 when 'Article', 'Person', 'Community', 'Enterprise', 'Event'
64 67 {:include => 'categories', :conditions => ['categories.id IN (?)', category_ids]}.merge!(options)
65 68 else
... ...
app/models/enterprise.rb
... ... @@ -9,7 +9,7 @@ class Enterprise < Organization
9 9 extra_data_for_index :product_categories
10 10  
11 11 def product_categories
12   - products.map{|p| p.product_category.full_name(' ') }.join(' ')
  12 + products.map{|p| p.category_full_name}
13 13 end
14 14  
15 15 end
... ...
app/models/environment_finder.rb
... ... @@ -12,12 +12,22 @@ class EnvironmentFinder
12 12 options.delete(:within)
13 13 end
14 14  
  15 + product_category = options.delete(:product_category)
  16 + product_category_ids = product_category.map_traversal(&:id) if product_category
  17 +
15 18 if query.blank?
16   - with_options :limit => limit, :order => 'created_at desc, id desc' do |finder|
17   - @environment.send(asset).recent(limit)
18   - end
  19 + if product_category && asset == :products
  20 + @environment.send(asset).find(:all, options.merge({:limit => limit, :order => 'created_at desc, id desc', :conditions => ['product_category_id in (?)', product_category_ids]}))
  21 + else
  22 + @environment.send(asset).find( :all, options.merge( {:limit => limit, :order => 'created_at desc, id desc'} ) )
  23 + end
19 24 else
20   - @environment.send(asset).find_by_contents(query, {}, options)
  25 + if product_category && asset == :products
  26 + # SECURITY no risk of SQL injection, since product_category_ids comes from trusted source
  27 + @environment.send(asset).find_by_contents(query, {}, options.merge({:conditions => 'product_category_id in (%s)' % product_category_ids.join(',') }))
  28 + else
  29 + @environment.send(asset).find_by_contents(query, {}, options)
  30 + end
21 31 end
22 32 end
23 33  
... ...
app/models/product.rb
... ... @@ -21,7 +21,7 @@ class Product < ActiveRecord::Base
21 21 xss_terminate :only => [ :name, :description ]
22 22  
23 23 def category_full_name
24   - product_category.full_name(" ")
  24 + product_category.full_name.split('/')
25 25 end
26 26  
27 27 acts_as_having_image
... ...
app/models/profile.rb
... ... @@ -43,7 +43,7 @@ class Profile < ActiveRecord::Base
43 43 self.extra_index_methods = []
44 44  
45 45 def extra_data_for_index
46   - self.class.extra_index_methods.map { |meth| meth.to_proc.call(self) }
  46 + self.class.extra_index_methods.map { |meth| meth.to_proc.call(self) }.flatten
47 47 end
48 48  
49 49 def self.extra_data_for_index(sym = nil, &block)
... ...
script/extract_sies_data.rb
... ... @@ -8,7 +8,7 @@ require 'active_support'
8 8 require File.dirname(__FILE__) + "/../" + 'lib/noosfero/core_ext/string.rb'
9 9  
10 10  
11   -LIMIT = (ENV['DUMP_ALL'] ? nil : 5)
  11 +LIMIT = (ENV['DUMP_ALL'] ? nil : 10)
12 12 DUMP_ALL = LIMIT.nil?
13 13  
14 14 # To connect with the database that contains the data to be extracted cofigure it in the 'database_farejador.yml' with the name 'farejador'
... ... @@ -52,6 +52,8 @@ class Dumper
52 52 def initialize
53 53 @seq = 0
54 54 @seqs = {}
  55 + @r_seq = 0
  56 + @r_seqs = {}
55 57 end
56 58  
57 59 def pretty(str, alt = nil)
... ... @@ -72,7 +74,7 @@ categories[#{cat.id}] = cat#{@seq}.id
72 74 @seq += 1
73 75  
74 76 Category.find(:all, :conditions => { :id_mae => cat.id }).each do |child|
75   - dump_category(child, cat) if (DUMP_ALL || (@seq <= LIMIT))
  77 + dump_category(child, cat) #if (DUMP_ALL || (@seq <= LIMIT))
76 78 end
77 79  
78 80 end
... ... @@ -99,13 +101,19 @@ categories[#{cat.id}] = cat#{@seq}.id
99 101 :lng => #{ent.long.inspect},
100 102 :geocode_precision => #{ent.geomodificou.inspect},
101 103 :data => { :id_sies => #{ent.id_sies.inspect} },
102   - :organization_info => OrganizationInfo.new(:contact_email => #{email.inspect}) },
  104 + :contact_email => #{email.inspect},
  105 + :categories => [cities[#{ent.id_cidade}]]},
103 106 [#{ent.products.map{|p| "{ :name => #{p.category.nome.inspect} , :product_category_id => categories[#{p.category.id}] }"}.join(', ')}],
104 107 [#{ent.input_products.map{|p| "{ :product_category_id => categories[#{p.category.id}]}" }.join(', ')}])"
105 108 end
106 109  
107 110 def dump_city(city)
108   - puts "Region.create!(:name => #{city.cidade.inspect}, :parent => STATES[#{city.id.to_s[0..1]}], :lat => #{city.latitude}, :lng => #{city.longitude}, :environment => Environment.default)"
  111 + @r_seqs[city] = @r_seq
  112 + puts <<-EOF
  113 +city#{@r_seq} = new_region(#{city.cidade.inspect}, STATES[#{city.id.to_s[0..1]}], #{city.latitude}, #{city.longitude})
  114 +cities[#{city.id}] = city#{@r_seq}.id
  115 + EOF
  116 + @r_seq += 1
109 117 end
110 118  
111 119 end
... ... @@ -126,10 +134,11 @@ Category.find(:all, :conditions =&gt; &#39;id_mae is null or id_mae = -1&#39;, :limit =&gt; LI
126 134 dumper.dump_category(cat, nil)
127 135 end
128 136  
129   -Enterprise.find(:all, :limit => LIMIT).each do |ent|
130   - dumper.dump_enterprise(ent)
131   -end
132   -
  137 +puts "regions = {}"
133 138 City.find(:all, :limit => LIMIT).each do |city|
134 139 dumper.dump_city(city)
135 140 end
  141 +
  142 +Enterprise.find(:all, :limit => LIMIT).each do |ent|
  143 + dumper.dump_enterprise(ent)
  144 +end
... ...
script/fbes_populate_helper.rb
... ... @@ -35,6 +35,11 @@ require File.dirname(__FILE__) + &#39;/../config/environment&#39;
35 35 ProductCategory.find_by_path(path) || ProductCategory.create!(:name => name, :parent => parent, :environment => Environment.default)
36 36 end
37 37  
  38 + def new_region(name, parent, lat, lng)
  39 + path = (parent ? parent.path + '/' : '') + name.to_slug
  40 + Region.find_by_path(path) || Region.create!(:name => name, :parent => parent, :lat => lat, :lng => lng, :environment => Environment.default)
  41 + end
  42 +
38 43 def new_ent(data, products, consumptions)
39 44 posfix = ''
40 45 count = 1
... ...
test/functional/search_controller_test.rb
... ... @@ -391,17 +391,6 @@ class SearchControllerTest &lt; Test::Unit::TestCase
391 391 assert_same recent, assigns(:results)[:articles]
392 392 end
393 393  
394   - should 'list recent comments in the category' do
395   - recent = []
396   - finger = CategoryFinder.new(@category)
397   - finger.expects(:recent).with(anything).at_least_once
398   - finger.expects(:recent).with('comments').returns(recent)
399   - CategoryFinder.expects(:new).with(@category).returns(finger)
400   -
401   - get :category_index, :category_path => [ 'my-category' ]
402   - assert_same recent, assigns(:results)[:comments]
403   - end
404   -
405 394 should 'list most commented articles in the category' do
406 395 most_commented = []
407 396 finger = CategoryFinder.new(@category)
... ... @@ -562,17 +551,6 @@ class SearchControllerTest &lt; Test::Unit::TestCase
562 551 assert_not_includes assigns(:results)[:enterprises], ent2
563 552 end
564 553  
565   - should 'display products with a given initial' do
566   - ent = Enterprise.create!(:name => 'teste', :identifier => 'teste')
567   - prod1 = ent.products.create!(:name => 'a beautiful product')
568   - prod2 = ent.products.create!(:name => 'beautiful product (another)')
569   -
570   - get :directory, :asset => 'products', :initial => 'a'
571   -
572   - assert_includes assigns(:results)[:products], prod1
573   - assert_not_includes assigns(:results)[:products], prod2
574   - end
575   -
576 554 should 'display articles with a given initial' do
577 555 person = create_user('teste').person
578 556 art1 = person.articles.build(:name => 'an article to be found'); art1.save!
... ... @@ -632,24 +610,6 @@ class SearchControllerTest &lt; Test::Unit::TestCase
632 610 assert_not_includes assigns(:results)[:enterprises], ent4
633 611 end
634 612  
635   - should 'display products with a given initial, under a specific category' do
636   - ent = Enterprise.create!(:name => 'teste', :identifier => 'teste')
637   - ent.categories << @category
638   - prod1 = ent.products.create!(:name => 'a beautiful product')
639   - prod2 = ent.products.create!(:name => 'beautiful product (another)')
640   -
641   - ent2 = Enterprise.create!(:name => 'test2', :identifier => 'test2')
642   - prod3 = ent2.products.create!(:name => 'another product')
643   - prod4 = ent2.products.create!(:name => 'damn product (another)')
644   -
645   - get :directory, :asset => 'products', :initial => 'a', :category_path => [ 'my-category' ]
646   -
647   - assert_includes assigns(:results)[:products], prod1
648   - assert_not_includes assigns(:results)[:products], prod2
649   - assert_not_includes assigns(:results)[:products], prod3
650   - assert_not_includes assigns(:results)[:products], prod4
651   - end
652   -
653 613 should 'display articles with a given initial, under a specific category' do
654 614 person = create_user('teste').person
655 615 art1 = person.articles.build(:name => 'an article to be found'); art1.save!
... ... @@ -809,6 +769,48 @@ class SearchControllerTest &lt; Test::Unit::TestCase
809 769 end
810 770 end
811 771  
  772 + should 'list only categories with products' do
  773 + cat1 = ProductCategory.create!(:name => 'pc test 1', :environment => Environment.default)
  774 + cat2 = ProductCategory.create!(:name => 'pc test 2', :environment => Environment.default)
  775 + ent = Enterprise.create!(:name => 'test ent', :identifier => 'test_ent')
  776 +
  777 + cat1.products.create!(:name => 'prod test 1', :enterprise => ent)
  778 +
  779 + get :index, :find_in => 'products'
  780 +
  781 + assert_equal 1, assigns(:counts)[cat1.id][1]
  782 + assert_equal nil, assigns(:counts)[cat2.id]
  783 + end
  784 +
  785 + should 'not list ancestor if no product in it' do
  786 + cat1 = ProductCategory.create!(:name => 'pc test 1', :environment => Environment.default)
  787 + cat2 = ProductCategory.create!(:name => 'pc test 2', :environment => Environment.default, :parent => cat1)
  788 + ent = Enterprise.create!(:name => 'test ent', :identifier => 'test_ent')
  789 +
  790 + cat1.products.create!(:name => 'prod test 1', :enterprise => ent)
  791 +
  792 + get :index, :find_in => 'products'
  793 +
  794 + assert_equal 1, assigns(:counts)[cat1.id][1]
  795 + assert_equal nil, assigns(:counts)[cat2.id]
  796 + end
  797 +
  798 + should 'add hits of children in ancestor when it has products on results' do
  799 + cat1 = ProductCategory.create!(:name => 'pc test 1', :environment => Environment.default)
  800 + cat2 = ProductCategory.create!(:name => 'pc test 2', :environment => Environment.default, :parent => cat1)
  801 + ent = Enterprise.create!(:name => 'test ent', :identifier => 'test_ent')
  802 +
  803 + cat1.products.create!(:name => 'prod test 1', :enterprise => ent)
  804 + cat2.products.create!(:name => 'prod test 2', :enterprise => ent)
  805 +
  806 + get :index, :find_in => 'products'
  807 +
  808 + assert_equal 2, assigns(:counts)[cat1.id][1]
  809 + assert_equal 1, assigns(:counts)[cat2.id][1]
  810 + end
  811 +
  812 + should 'test somehow the display of events as calendar'
  813 +
812 814 should 'provide calendar for events' do
813 815 get :index, :find_in => [ 'events' ]
814 816 assert_equal 0, assigns(:calendar).size % 7
... ...
test/unit/category_finder_test.rb
... ... @@ -254,4 +254,40 @@ class CategoryFinderTest &lt; ActiveSupport::TestCase
254 254 assert_not_includes events, e2
255 255 end
256 256  
  257 + should 'find person and enterprise in category by radius and region even without query' do
  258 + cat = Category.create!(:name => 'test category', :environment => Environment.default)
  259 + finder = CategoryFinder.new(cat)
  260 +
  261 + region = Region.create!(:name => 'r-test', :environment => Environment.default, :lat => 45.0, :lng => 45.0)
  262 + ent1 = Enterprise.create!(:name => 'test 1', :identifier => 'test1', :lat => 45.0, :lng => 45.0, :categories => [cat])
  263 + p1 = create_user('test2').person
  264 + p1.name = 'test 2'; p1.lat = 45.0; p1.lng = 45.0; p1.categories = [cat]; p1.save!
  265 + ent2 = Enterprise.create!(:name => 'test 3', :identifier => 'test3', :lat => 30.0, :lng => 30.0, :categories => [cat])
  266 + p2 = create_user('test4').person
  267 + p2.name = 'test 4'; p2.lat = 30.0; p2.lng = 30.0; p2.categories = [cat]; p2.save!
  268 +
  269 + ents = finder.find(:enterprises, nil, :within => 10, :region => region.id)
  270 + people = finder.find(:people, nil, :within => 10, :region => region.id)
  271 +
  272 + assert_includes ents, ent1
  273 + assert_not_includes ents, ent2
  274 + assert_includes people, p1
  275 + assert_not_includes people, p2
  276 + end
  277 +
  278 + should 'find products in category wihin product category' do
  279 + cat = Category.create!(:name => 'test category', :environment => Environment.default)
  280 + finder = CategoryFinder.new(cat)
  281 +
  282 + prod_cat = ProductCategory.create!(:name => 'test product category', :environment => Environment.default)
  283 + ent = Enterprise.create!(:name => 'test enterprise', :identifier => 'test_ent', :categories => [cat])
  284 + prod1 = ent.products.create!(:name => 'test product 1', :product_category => prod_cat)
  285 + prod2 = ent.products.create!(:name => 'test product 2')
  286 +
  287 + prods = finder.find(:products, nil, :product_category => prod_cat)
  288 +
  289 + assert_includes prods, prod1
  290 + assert_not_includes prods, prod2
  291 + end
  292 +
257 293 end
... ...
test/unit/environment_finder_test.rb
... ... @@ -131,4 +131,64 @@ class EnvironmentFinderTest &lt; ActiveSupport::TestCase
131 131 assert_not_includes people, p2
132 132 end
133 133  
  134 + should 'find person and enterprise by radius and region even without query' do
  135 + finder = EnvironmentFinder.new(Environment.default)
  136 +
  137 + region = Region.create!(:name => 'r-test', :environment => Environment.default, :lat => 45.0, :lng => 45.0)
  138 + ent1 = Enterprise.create!(:name => 'test 1', :identifier => 'test1', :lat => 45.0, :lng => 45.0)
  139 + p1 = create_user('test2').person
  140 + p1.name = 'test 2'; p1.lat = 45.0; p1.lng = 45.0; p1.save!
  141 + ent2 = Enterprise.create!(:name => 'test 3', :identifier => 'test3', :lat => 30.0, :lng => 30.0)
  142 + p2 = create_user('test4').person
  143 + p2.name = 'test 4'; p2.lat = 30.0; p2.lng = 30.0; p2.save!
  144 +
  145 + ents = finder.find(:enterprises, nil, :within => 10, :region => region.id)
  146 + people = finder.find(:people, nil, :within => 10, :region => region.id)
  147 +
  148 + assert_includes ents, ent1
  149 + assert_not_includes ents, ent2
  150 + assert_includes people, p1
  151 + assert_not_includes people, p2
  152 + end
  153 +
  154 + should 'find products wihin product category' do
  155 + finder = EnvironmentFinder.new(Environment.default)
  156 + cat = ProductCategory.create!(:name => 'test category', :environment => Environment.default)
  157 + ent = Enterprise.create!(:name => 'test enterprise', :identifier => 'test_ent')
  158 + prod1 = ent.products.create!(:name => 'test product 1', :product_category => cat)
  159 + prod2 = ent.products.create!(:name => 'test product 2')
  160 +
  161 + prods = finder.find(:products, nil, :product_category => cat)
  162 +
  163 + assert_includes prods, prod1
  164 + assert_not_includes prods, prod2
  165 + end
  166 +
  167 + should 'find products wihin product category with query' do
  168 + finder = EnvironmentFinder.new(Environment.default)
  169 + cat = ProductCategory.create!(:name => 'test category', :environment => Environment.default)
  170 + ent = Enterprise.create!(:name => 'test enterprise', :identifier => 'test_ent')
  171 + prod1 = ent.products.create!(:name => 'test product a_word 1', :product_category => cat)
  172 + prod2 = ent.products.create!(:name => 'test product b_word 1', :product_category => cat)
  173 + prod3 = ent.products.create!(:name => 'test product a_word 2')
  174 + prod4 = ent.products.create!(:name => 'test product b_word 2')
  175 +
  176 + prods = finder.find(:products, 'a_word', :product_category => cat)
  177 +
  178 + assert_includes prods, prod1
  179 + assert_not_includes prods, prod2
  180 + assert_not_includes prods, prod3
  181 + assert_not_includes prods, prod4
  182 + end
  183 +
  184 + should 'find in order of creation' do
  185 + finder = EnvironmentFinder.new(Environment.default)
  186 + ent1 = Enterprise.create!(:name => 'test enterprise 1', :identifier => 'test_ent1')
  187 + ent2 = Enterprise.create!(:name => 'test enterprise 2', :identifier => 'test_ent2')
  188 +
  189 + ents = finder.find(:enterprises, nil)
  190 +
  191 + assert ents.index(ent2) < ents.index(ent1), "expected #{ents.index(ent2)} be smaller than #{ents.index(ent1)}"
  192 + end
  193 +
134 194 end
... ...
test/unit/product_test.rb
... ... @@ -69,11 +69,11 @@ class ProductTest &lt; Test::Unit::TestCase
69 69  
70 70 should 'calculate catagory full name' do
71 71 cat = mock
72   - cat.expects(:full_name).returns('A B C')
  72 + cat.expects(:full_name).returns('A/B/C')
73 73  
74 74 p = Product.new
75 75 p.expects(:product_category).returns(cat)
76   - assert_equal 'A B C', p.category_full_name
  76 + assert_equal ['A','B','C'], p.category_full_name
77 77 end
78 78  
79 79 should 'be indexed by category full name' do
... ...