@@ -72,15 +72,20 class SearchController < ApplicationController | |||||
72 | @tokens.slice! 5..-1 if @tokens.size > 5 |
|
72 | @tokens.slice! 5..-1 if @tokens.size > 5 | |
73 | # strings used in sql like statement |
|
73 | # strings used in sql like statement | |
74 | like_tokens = @tokens.collect {|w| "%#{w.downcase}%"} |
|
74 | like_tokens = @tokens.collect {|w| "%#{w.downcase}%"} | |
|
75 | ||||
75 | @results = [] |
|
76 | @results = [] | |
|
77 | @results_by_type = Hash.new {|h,k| h[k] = 0} | |||
|
78 | ||||
76 | limit = 10 |
|
79 | limit = 10 | |
77 | @scope.each do |s| |
|
80 | @scope.each do |s| | |
78 |
|
|
81 | r, c = s.singularize.camelcase.constantize.search(like_tokens, projects_to_search, | |
79 | :all_words => @all_words, |
|
82 | :all_words => @all_words, | |
80 | :titles_only => @titles_only, |
|
83 | :titles_only => @titles_only, | |
81 | :limit => (limit+1), |
|
84 | :limit => (limit+1), | |
82 | :offset => offset, |
|
85 | :offset => offset, | |
83 | :before => params[:previous].nil?) |
|
86 | :before => params[:previous].nil?) | |
|
87 | @results += r | |||
|
88 | @results_by_type[s] += c | |||
84 | end |
|
89 | end | |
85 | @results = @results.sort {|a,b| b.event_datetime <=> a.event_datetime} |
|
90 | @results = @results.sort {|a,b| b.event_datetime <=> a.event_datetime} | |
86 | if params[:previous].nil? |
|
91 | if params[:previous].nil? |
@@ -36,6 +36,10 module SearchHelper | |||||
36 | result |
|
36 | result | |
37 | end |
|
37 | end | |
38 |
|
38 | |||
|
39 | def type_label(t) | |||
|
40 | l("label_#{t.singularize}_plural") | |||
|
41 | end | |||
|
42 | ||||
39 | def project_select_tag |
|
43 | def project_select_tag | |
40 | options = [[l(:label_project_all), 'all']] |
|
44 | options = [[l(:label_project_all), 'all']] | |
41 | options << [l(:label_my_projects), 'my_projects'] unless User.current.memberships.empty? |
|
45 | options << [l(:label_my_projects), 'my_projects'] unless User.current.memberships.empty? | |
@@ -43,4 +47,16 module SearchHelper | |||||
43 | options << [@project.name, ''] unless @project.nil? |
|
47 | options << [@project.name, ''] unless @project.nil? | |
44 | select_tag('scope', options_for_select(options, params[:scope].to_s)) if options.size > 1 |
|
48 | select_tag('scope', options_for_select(options, params[:scope].to_s)) if options.size > 1 | |
45 | end |
|
49 | end | |
|
50 | ||||
|
51 | def render_results_by_type(results_by_type) | |||
|
52 | links = [] | |||
|
53 | # Sorts types by results count | |||
|
54 | results_by_type.keys.sort {|a, b| results_by_type[b] <=> results_by_type[a]}.each do |t| | |||
|
55 | c = results_by_type[t] | |||
|
56 | next if c == 0 | |||
|
57 | text = "#{type_label(t)} (#{c})" | |||
|
58 | links << link_to(text, :q => params[:q], :titles_only => params[:title_only], :all_words => params[:all_words], :scope => params[:scope], t => 1) | |||
|
59 | end | |||
|
60 | ('<ul>' + links.map {|link| content_tag('li', link)}.join(' ') + '</ul>') unless links.empty? | |||
|
61 | end | |||
46 | end |
|
62 | end |
@@ -35,7 +35,10 class Issue < ActiveRecord::Base | |||||
35 |
|
35 | |||
36 | acts_as_customizable |
|
36 | acts_as_customizable | |
37 | acts_as_watchable |
|
37 | acts_as_watchable | |
38 |
acts_as_searchable :columns => ['subject', "#{table_name}.description"], |
|
38 | acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"], | |
|
39 | :include => [:project, :journals], | |||
|
40 | # sort by id so that limited eager loading doesn't break with postgresql | |||
|
41 | :order_column => "#{table_name}.id" | |||
39 | acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id}: #{o.subject}"}, |
|
42 | acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id}: #{o.subject}"}, | |
40 | :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}} |
|
43 | :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}} | |
41 |
|
44 |
@@ -25,12 +25,6 class Journal < ActiveRecord::Base | |||||
25 | has_many :details, :class_name => "JournalDetail", :dependent => :delete_all |
|
25 | has_many :details, :class_name => "JournalDetail", :dependent => :delete_all | |
26 | attr_accessor :indice |
|
26 | attr_accessor :indice | |
27 |
|
27 | |||
28 | acts_as_searchable :columns => 'notes', |
|
|||
29 | :include => {:issue => :project}, |
|
|||
30 | :project_key => "#{Issue.table_name}.project_id", |
|
|||
31 | :date_column => "#{Issue.table_name}.created_on", |
|
|||
32 | :permission => :view_issues |
|
|||
33 |
|
||||
34 | acts_as_event :title => Proc.new {|o| status = ((s = o.new_status) ? " (#{s})" : nil); "#{o.issue.tracker} ##{o.issue.id}#{status}: #{o.issue.subject}" }, |
|
28 | acts_as_event :title => Proc.new {|o| status = ((s = o.new_status) ? " (#{s})" : nil); "#{o.issue.tracker} ##{o.issue.id}#{status}: #{o.issue.subject}" }, | |
35 | :description => :notes, |
|
29 | :description => :notes, | |
36 | :author => :user, |
|
30 | :author => :user, |
@@ -10,7 +10,7 | |||||
10 | </p> |
|
10 | </p> | |
11 | <p> |
|
11 | <p> | |
12 | <% @object_types.each do |t| %> |
|
12 | <% @object_types.each do |t| %> | |
13 |
<label><%= check_box_tag t, 1, @scope.include?(t) %> <%= l( |
|
13 | <label><%= check_box_tag t, 1, @scope.include?(t) %> <%= type_label(t) %></label> | |
14 | <% end %> |
|
14 | <% end %> | |
15 | </p> |
|
15 | </p> | |
16 |
|
16 | |||
@@ -19,12 +19,16 | |||||
19 | </div> |
|
19 | </div> | |
20 |
|
20 | |||
21 | <% if @results %> |
|
21 | <% if @results %> | |
22 | <h3><%= l(:label_result_plural) %></h3> |
|
22 | <div id="search-results-counts"> | |
|
23 | <%= render_results_by_type(@results_by_type) unless @scope.size == 1 %> | |||
|
24 | </div> | |||
|
25 | ||||
|
26 | <h3><%= l(:label_result_plural) %> (<%= @results_by_type.values.sum %>)</h3> | |||
23 | <dl id="search-results"> |
|
27 | <dl id="search-results"> | |
24 | <% @results.each do |e| %> |
|
28 | <% @results.each do |e| %> | |
25 | <dt class="<%= e.event_type %>"><%= content_tag('span', h(e.project), :class => 'project') unless @project == e.project %> <%= link_to highlight_tokens(truncate(e.event_title, 255), @tokens), e.event_url %></dt> |
|
29 | <dt class="<%= e.event_type %>"><%= content_tag('span', h(e.project), :class => 'project') unless @project == e.project %> <%= link_to highlight_tokens(truncate(e.event_title, 255), @tokens), e.event_url %></dt> | |
26 | <dd><span class="description"><%= highlight_tokens(e.event_description, @tokens) %></span> |
|
30 | <dd><span class="description"><%= highlight_tokens(e.event_description, @tokens) %></span> | |
27 | <span class="author"><%= format_time(e.event_datetime) %></span><dd> |
|
31 | <span class="author"><%= format_time(e.event_datetime) %></span></dd> | |
28 | <% end %> |
|
32 | <% end %> | |
29 | </dl> |
|
33 | </dl> | |
30 | <% end %> |
|
34 | <% end %> |
@@ -185,9 +185,13 div#activity dt.me .time { border-bottom: 1px solid #999; } | |||||
185 | div#activity dt .time { color: #777; font-size: 80%; } |
|
185 | div#activity dt .time { color: #777; font-size: 80%; } | |
186 | div#activity dd .description, #search-results dd .description { font-style: italic; } |
|
186 | div#activity dd .description, #search-results dd .description { font-style: italic; } | |
187 | div#activity span.project:after, #search-results span.project:after { content: " -"; } |
|
187 | div#activity span.project:after, #search-results span.project:after { content: " -"; } | |
188 | #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px;} |
|
|||
189 | div#activity dd span.description, #search-results dd span.description { display:block; } |
|
188 | div#activity dd span.description, #search-results dd span.description { display:block; } | |
190 |
|
189 | |||
|
190 | #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; } | |||
|
191 | div#search-results-counts {float:right;} | |||
|
192 | div#search-results-counts ul { margin-top: 0.5em; } | |||
|
193 | div#search-results-counts li { list-style-type:none; float: left; margin-left: 1em; } | |||
|
194 | ||||
191 | dt.issue { background-image: url(../images/ticket.png); } |
|
195 | dt.issue { background-image: url(../images/ticket.png); } | |
192 | dt.issue-edit { background-image: url(../images/ticket_edit.png); } |
|
196 | dt.issue-edit { background-image: url(../images/ticket_edit.png); } | |
193 | dt.issue-closed { background-image: url(../images/ticket_checked.png); } |
|
197 | dt.issue-closed { background-image: url(../images/ticket_checked.png); } |
@@ -32,9 +32,17 class SearchControllerTest < Test::Unit::TestCase | |||||
32 | get :index, :q => 'recipe subproject commit', :submit => 'Search' |
|
32 | get :index, :q => 'recipe subproject commit', :submit => 'Search' | |
33 | assert_response :success |
|
33 | assert_response :success | |
34 | assert_template 'index' |
|
34 | assert_template 'index' | |
|
35 | ||||
35 | assert assigns(:results).include?(Issue.find(2)) |
|
36 | assert assigns(:results).include?(Issue.find(2)) | |
36 | assert assigns(:results).include?(Issue.find(5)) |
|
37 | assert assigns(:results).include?(Issue.find(5)) | |
37 | assert assigns(:results).include?(Changeset.find(101)) |
|
38 | assert assigns(:results).include?(Changeset.find(101)) | |
|
39 | assert_tag :dt, :attributes => { :class => /issue/ }, | |||
|
40 | :child => { :tag => 'a', :content => /Add ingredients categories/ }, | |||
|
41 | :sibling => { :tag => 'dd', :content => /should be classified by categories/ } | |||
|
42 | ||||
|
43 | assert assigns(:results_by_type).is_a?(Hash) | |||
|
44 | assert_equal 4, assigns(:results_by_type)['changesets'] | |||
|
45 | assert_tag :a, :content => 'Changesets (4)' | |||
38 | end |
|
46 | end | |
39 |
|
47 | |||
40 | def test_search_project_and_subprojects |
|
48 | def test_search_project_and_subprojects |
@@ -41,24 +41,24 class SearchTest < Test::Unit::TestCase | |||||
41 | def test_search_by_anonymous |
|
41 | def test_search_by_anonymous | |
42 | User.current = nil |
|
42 | User.current = nil | |
43 |
|
43 | |||
44 | r = Issue.search(@issue_keyword) |
|
44 | r = Issue.search(@issue_keyword).first | |
45 | assert r.include?(@issue) |
|
45 | assert r.include?(@issue) | |
46 | r = Changeset.search(@changeset_keyword) |
|
46 | r = Changeset.search(@changeset_keyword).first | |
47 | assert r.include?(@changeset) |
|
47 | assert r.include?(@changeset) | |
48 |
|
48 | |||
49 | # Removes the :view_changesets permission from Anonymous role |
|
49 | # Removes the :view_changesets permission from Anonymous role | |
50 | remove_permission Role.anonymous, :view_changesets |
|
50 | remove_permission Role.anonymous, :view_changesets | |
51 |
|
51 | |||
52 | r = Issue.search(@issue_keyword) |
|
52 | r = Issue.search(@issue_keyword).first | |
53 | assert r.include?(@issue) |
|
53 | assert r.include?(@issue) | |
54 | r = Changeset.search(@changeset_keyword) |
|
54 | r = Changeset.search(@changeset_keyword).first | |
55 | assert !r.include?(@changeset) |
|
55 | assert !r.include?(@changeset) | |
56 |
|
56 | |||
57 | # Make the project private |
|
57 | # Make the project private | |
58 | @project.update_attribute :is_public, false |
|
58 | @project.update_attribute :is_public, false | |
59 | r = Issue.search(@issue_keyword) |
|
59 | r = Issue.search(@issue_keyword).first | |
60 | assert !r.include?(@issue) |
|
60 | assert !r.include?(@issue) | |
61 | r = Changeset.search(@changeset_keyword) |
|
61 | r = Changeset.search(@changeset_keyword).first | |
62 | assert !r.include?(@changeset) |
|
62 | assert !r.include?(@changeset) | |
63 | end |
|
63 | end | |
64 |
|
64 | |||
@@ -66,24 +66,24 class SearchTest < Test::Unit::TestCase | |||||
66 | User.current = User.find_by_login('rhill') |
|
66 | User.current = User.find_by_login('rhill') | |
67 | assert User.current.memberships.empty? |
|
67 | assert User.current.memberships.empty? | |
68 |
|
68 | |||
69 | r = Issue.search(@issue_keyword) |
|
69 | r = Issue.search(@issue_keyword).first | |
70 | assert r.include?(@issue) |
|
70 | assert r.include?(@issue) | |
71 | r = Changeset.search(@changeset_keyword) |
|
71 | r = Changeset.search(@changeset_keyword).first | |
72 | assert r.include?(@changeset) |
|
72 | assert r.include?(@changeset) | |
73 |
|
73 | |||
74 | # Removes the :view_changesets permission from Non member role |
|
74 | # Removes the :view_changesets permission from Non member role | |
75 | remove_permission Role.non_member, :view_changesets |
|
75 | remove_permission Role.non_member, :view_changesets | |
76 |
|
76 | |||
77 | r = Issue.search(@issue_keyword) |
|
77 | r = Issue.search(@issue_keyword).first | |
78 | assert r.include?(@issue) |
|
78 | assert r.include?(@issue) | |
79 | r = Changeset.search(@changeset_keyword) |
|
79 | r = Changeset.search(@changeset_keyword).first | |
80 | assert !r.include?(@changeset) |
|
80 | assert !r.include?(@changeset) | |
81 |
|
81 | |||
82 | # Make the project private |
|
82 | # Make the project private | |
83 | @project.update_attribute :is_public, false |
|
83 | @project.update_attribute :is_public, false | |
84 | r = Issue.search(@issue_keyword) |
|
84 | r = Issue.search(@issue_keyword).first | |
85 | assert !r.include?(@issue) |
|
85 | assert !r.include?(@issue) | |
86 | r = Changeset.search(@changeset_keyword) |
|
86 | r = Changeset.search(@changeset_keyword).first | |
87 | assert !r.include?(@changeset) |
|
87 | assert !r.include?(@changeset) | |
88 | end |
|
88 | end | |
89 |
|
89 | |||
@@ -91,16 +91,16 class SearchTest < Test::Unit::TestCase | |||||
91 | User.current = User.find_by_login('jsmith') |
|
91 | User.current = User.find_by_login('jsmith') | |
92 | assert User.current.projects.include?(@project) |
|
92 | assert User.current.projects.include?(@project) | |
93 |
|
93 | |||
94 | r = Issue.search(@issue_keyword) |
|
94 | r = Issue.search(@issue_keyword).first | |
95 | assert r.include?(@issue) |
|
95 | assert r.include?(@issue) | |
96 | r = Changeset.search(@changeset_keyword) |
|
96 | r = Changeset.search(@changeset_keyword).first | |
97 | assert r.include?(@changeset) |
|
97 | assert r.include?(@changeset) | |
98 |
|
98 | |||
99 | # Make the project private |
|
99 | # Make the project private | |
100 | @project.update_attribute :is_public, false |
|
100 | @project.update_attribute :is_public, false | |
101 | r = Issue.search(@issue_keyword) |
|
101 | r = Issue.search(@issue_keyword).first | |
102 | assert r.include?(@issue) |
|
102 | assert r.include?(@issue) | |
103 | r = Changeset.search(@changeset_keyword) |
|
103 | r = Changeset.search(@changeset_keyword).first | |
104 | assert r.include?(@changeset) |
|
104 | assert r.include?(@changeset) | |
105 | end |
|
105 | end | |
106 |
|
106 | |||
@@ -112,19 +112,28 class SearchTest < Test::Unit::TestCase | |||||
112 | User.current = User.find_by_login('jsmith') |
|
112 | User.current = User.find_by_login('jsmith') | |
113 | assert User.current.projects.include?(@project) |
|
113 | assert User.current.projects.include?(@project) | |
114 |
|
114 | |||
115 | r = Issue.search(@issue_keyword) |
|
115 | r = Issue.search(@issue_keyword).first | |
116 | assert r.include?(@issue) |
|
116 | assert r.include?(@issue) | |
117 | r = Changeset.search(@changeset_keyword) |
|
117 | r = Changeset.search(@changeset_keyword).first | |
118 | assert !r.include?(@changeset) |
|
118 | assert !r.include?(@changeset) | |
119 |
|
119 | |||
120 | # Make the project private |
|
120 | # Make the project private | |
121 | @project.update_attribute :is_public, false |
|
121 | @project.update_attribute :is_public, false | |
122 | r = Issue.search(@issue_keyword) |
|
122 | r = Issue.search(@issue_keyword).first | |
123 | assert r.include?(@issue) |
|
123 | assert r.include?(@issue) | |
124 | r = Changeset.search(@changeset_keyword) |
|
124 | r = Changeset.search(@changeset_keyword).first | |
125 | assert !r.include?(@changeset) |
|
125 | assert !r.include?(@changeset) | |
126 | end |
|
126 | end | |
127 |
|
127 | |||
|
128 | def test_search_issue_with_multiple_hits_in_journals | |||
|
129 | i = Issue.find(1) | |||
|
130 | assert_equal 2, i.journals.count(:all, :conditions => "notes LIKE '%notes%'") | |||
|
131 | ||||
|
132 | r = Issue.search('%notes%').first | |||
|
133 | assert_equal 1, r.size | |||
|
134 | assert_equal i, r.first | |||
|
135 | end | |||
|
136 | ||||
128 | private |
|
137 | private | |
129 |
|
138 | |||
130 | def remove_permission(role, permission) |
|
139 | def remove_permission(role, permission) |
@@ -23,6 +23,12 module Redmine | |||||
23 | end |
|
23 | end | |
24 |
|
24 | |||
25 | module ClassMethods |
|
25 | module ClassMethods | |
|
26 | # Options: | |||
|
27 | # * :columns - a column or an array of columns to search | |||
|
28 | # * :project_key - project foreign key (default to project_id) | |||
|
29 | # * :date_column - name of the datetime column (default to created_on) | |||
|
30 | # * :sort_order - name of the column used to sort results (default to :date_column or created_on) | |||
|
31 | # * :permission - permission required to search the model (default to :view_"objects") | |||
26 | def acts_as_searchable(options = {}) |
|
32 | def acts_as_searchable(options = {}) | |
27 | return if self.included_modules.include?(Redmine::Acts::Searchable::InstanceMethods) |
|
33 | return if self.included_modules.include?(Redmine::Acts::Searchable::InstanceMethods) | |
28 |
|
34 | |||
@@ -49,6 +55,8 module Redmine | |||||
49 | raise 'No date column defined defined.' |
|
55 | raise 'No date column defined defined.' | |
50 | end |
|
56 | end | |
51 |
|
57 | |||
|
58 | searchable_options[:order_column] ||= searchable_options[:date_column] | |||
|
59 | ||||
52 | # Permission needed to search this model |
|
60 | # Permission needed to search this model | |
53 | searchable_options[:permission] = "view_#{self.name.underscore.pluralize}".to_sym unless searchable_options.has_key?(:permission) |
|
61 | searchable_options[:permission] = "view_#{self.name.underscore.pluralize}".to_sym unless searchable_options.has_key?(:permission) | |
54 |
|
62 | |||
@@ -65,15 +73,22 module Redmine | |||||
65 | end |
|
73 | end | |
66 |
|
74 | |||
67 | module ClassMethods |
|
75 | module ClassMethods | |
68 | # Search the model for the given tokens |
|
76 | # Searches the model for the given tokens | |
69 | # projects argument can be either nil (will search all projects), a project or an array of projects |
|
77 | # projects argument can be either nil (will search all projects), a project or an array of projects | |
|
78 | # Returns the results and the results count | |||
70 | def search(tokens, projects=nil, options={}) |
|
79 | def search(tokens, projects=nil, options={}) | |
71 | tokens = [] << tokens unless tokens.is_a?(Array) |
|
80 | tokens = [] << tokens unless tokens.is_a?(Array) | |
72 | projects = [] << projects unless projects.nil? || projects.is_a?(Array) |
|
81 | projects = [] << projects unless projects.nil? || projects.is_a?(Array) | |
73 |
|
82 | |||
74 | find_options = {:include => searchable_options[:include]} |
|
83 | find_options = {:include => searchable_options[:include]} | |
75 | find_options[:limit] = options[:limit] if options[:limit] |
|
84 | find_options[:order] = "#{searchable_options[:order_column]} " + (options[:before] ? 'DESC' : 'ASC') | |
76 | find_options[:order] = "#{searchable_options[:date_column]} " + (options[:before] ? 'DESC' : 'ASC') |
|
85 | ||
|
86 | limit_options = {} | |||
|
87 | limit_options[:limit] = options[:limit] if options[:limit] | |||
|
88 | if options[:offset] | |||
|
89 | limit_options[:conditions] = "(#{searchable_options[:date_column]} " + (options[:before] ? '<' : '>') + "'#{connection.quoted_date(options[:offset])}')" | |||
|
90 | end | |||
|
91 | ||||
77 | columns = searchable_options[:columns] |
|
92 | columns = searchable_options[:columns] | |
78 | columns.slice!(1..-1) if options[:titles_only] |
|
93 | columns.slice!(1..-1) if options[:titles_only] | |
79 |
|
94 | |||
@@ -94,9 +109,6 module Redmine | |||||
94 |
|
109 | |||
95 | sql = (['(' + token_clauses.join(' OR ') + ')'] * tokens.size).join(options[:all_words] ? ' AND ' : ' OR ') |
|
110 | sql = (['(' + token_clauses.join(' OR ') + ')'] * tokens.size).join(options[:all_words] ? ' AND ' : ' OR ') | |
96 |
|
111 | |||
97 | if options[:offset] |
|
|||
98 | sql = "(#{sql}) AND (#{searchable_options[:date_column]} " + (options[:before] ? '<' : '>') + "'#{connection.quoted_date(options[:offset])}')" |
|
|||
99 | end |
|
|||
100 | find_options[:conditions] = [sql, * (tokens * token_clauses.size).sort] |
|
112 | find_options[:conditions] = [sql, * (tokens * token_clauses.size).sort] | |
101 |
|
113 | |||
102 | project_conditions = [] |
|
114 | project_conditions = [] | |
@@ -104,16 +116,16 module Redmine | |||||
104 | Project.allowed_to_condition(User.current, searchable_options[:permission])) |
|
116 | Project.allowed_to_condition(User.current, searchable_options[:permission])) | |
105 | project_conditions << "#{searchable_options[:project_key]} IN (#{projects.collect(&:id).join(',')})" unless projects.nil? |
|
117 | project_conditions << "#{searchable_options[:project_key]} IN (#{projects.collect(&:id).join(',')})" unless projects.nil? | |
106 |
|
118 | |||
107 | results = with_scope(:find => {:conditions => project_conditions.join(' AND ')}) do |
|
119 | results = [] | |
108 | find(:all, find_options) |
|
120 | results_count = 0 | |
109 |
|
|
121 | ||
110 | if searchable_options[:with] && !options[:titles_only] |
|
122 | with_scope(:find => {:conditions => project_conditions.join(' AND ')}) do | |
111 | searchable_options[:with].each do |model, assoc| |
|
123 | with_scope(:find => find_options) do | |
112 | results += model.to_s.camelcase.constantize.search(tokens, projects, options).collect {|r| r.send assoc} |
|
124 | results_count = count(:all) | |
|
125 | results = find(:all, limit_options) | |||
113 | end |
|
126 | end | |
114 | results.uniq! |
|
|||
115 | end |
|
127 | end | |
116 | results |
|
128 | [results, results_count] | |
117 | end |
|
129 | end | |
118 | end |
|
130 | end | |
119 | end |
|
131 | end |
General Comments 0
You need to be logged in to leave comments.
Login now