search engine!
This commit is contained in:
parent
b01f9e9027
commit
abb8392c16
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
# Ignore the default SQLite database.
|
# Ignore the default SQLite database.
|
||||||
/db/*.sqlite3
|
/db/*.sqlite3
|
||||||
|
/db/sphinx
|
||||||
|
|
||||||
# Ignore all logfiles and tempfiles.
|
# Ignore all logfiles and tempfiles.
|
||||||
/log/*.log
|
/log/*.log
|
||||||
|
|
4
Gemfile
4
Gemfile
|
@ -1,6 +1,6 @@
|
||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
|
|
||||||
gem "rails", "3.2.2"
|
gem "rails", "3.2.6"
|
||||||
|
|
||||||
# Bundle edge Rails instead:
|
# Bundle edge Rails instead:
|
||||||
# gem "rails", :git => "git://github.com/rails/rails.git"
|
# gem "rails", :git => "git://github.com/rails/rails.git"
|
||||||
|
@ -27,6 +27,8 @@ gem "htmlentities"
|
||||||
|
|
||||||
gem "rdiscount"
|
gem "rdiscount"
|
||||||
|
|
||||||
|
gem "thinking-sphinx", "2.0.12"
|
||||||
|
|
||||||
group :test, :development do
|
group :test, :development do
|
||||||
gem "rspec-rails", "~> 2.6"
|
gem "rspec-rails", "~> 2.6"
|
||||||
gem "machinist"
|
gem "machinist"
|
||||||
|
|
108
Gemfile.lock
108
Gemfile.lock
|
@ -1,31 +1,31 @@
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actionmailer (3.2.2)
|
actionmailer (3.2.6)
|
||||||
actionpack (= 3.2.2)
|
actionpack (= 3.2.6)
|
||||||
mail (~> 2.4.0)
|
mail (~> 2.4.4)
|
||||||
actionpack (3.2.2)
|
actionpack (3.2.6)
|
||||||
activemodel (= 3.2.2)
|
activemodel (= 3.2.6)
|
||||||
activesupport (= 3.2.2)
|
activesupport (= 3.2.6)
|
||||||
builder (~> 3.0.0)
|
builder (~> 3.0.0)
|
||||||
erubis (~> 2.7.0)
|
erubis (~> 2.7.0)
|
||||||
journey (~> 1.0.1)
|
journey (~> 1.0.1)
|
||||||
rack (~> 1.4.0)
|
rack (~> 1.4.0)
|
||||||
rack-cache (~> 1.1)
|
rack-cache (~> 1.2)
|
||||||
rack-test (~> 0.6.1)
|
rack-test (~> 0.6.1)
|
||||||
sprockets (~> 2.1.2)
|
sprockets (~> 2.1.3)
|
||||||
activemodel (3.2.2)
|
activemodel (3.2.6)
|
||||||
activesupport (= 3.2.2)
|
activesupport (= 3.2.6)
|
||||||
builder (~> 3.0.0)
|
builder (~> 3.0.0)
|
||||||
activerecord (3.2.2)
|
activerecord (3.2.6)
|
||||||
activemodel (= 3.2.2)
|
activemodel (= 3.2.6)
|
||||||
activesupport (= 3.2.2)
|
activesupport (= 3.2.6)
|
||||||
arel (~> 3.0.2)
|
arel (~> 3.0.2)
|
||||||
tzinfo (~> 0.3.29)
|
tzinfo (~> 0.3.29)
|
||||||
activeresource (3.2.2)
|
activeresource (3.2.6)
|
||||||
activemodel (= 3.2.2)
|
activemodel (= 3.2.6)
|
||||||
activesupport (= 3.2.2)
|
activesupport (= 3.2.6)
|
||||||
activesupport (3.2.2)
|
activesupport (3.2.6)
|
||||||
i18n (~> 0.6)
|
i18n (~> 0.6)
|
||||||
multi_json (~> 1.0)
|
multi_json (~> 1.0)
|
||||||
arel (3.0.2)
|
arel (3.0.2)
|
||||||
|
@ -36,26 +36,26 @@ GEM
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
exception_notification (2.6.1)
|
exception_notification (2.6.1)
|
||||||
actionmailer (>= 3.0.4)
|
actionmailer (>= 3.0.4)
|
||||||
execjs (1.3.2)
|
execjs (1.4.0)
|
||||||
multi_json (~> 1.0)
|
multi_json (~> 1.0)
|
||||||
hike (1.2.1)
|
hike (1.2.1)
|
||||||
htmlentities (4.3.1)
|
htmlentities (4.3.1)
|
||||||
i18n (0.6.0)
|
i18n (0.6.0)
|
||||||
journey (1.0.3)
|
journey (1.0.4)
|
||||||
jquery-rails (2.0.1)
|
jquery-rails (2.0.2)
|
||||||
railties (>= 3.2.0, < 5.0)
|
railties (>= 3.2.0, < 5.0)
|
||||||
thor (~> 0.14)
|
thor (~> 0.14)
|
||||||
json (1.6.5)
|
json (1.7.3)
|
||||||
kgio (2.7.4)
|
kgio (2.7.4)
|
||||||
machinist (2.0)
|
machinist (2.0)
|
||||||
mail (2.4.4)
|
mail (2.4.4)
|
||||||
i18n (>= 0.4.0)
|
i18n (>= 0.4.0)
|
||||||
mime-types (~> 1.16)
|
mime-types (~> 1.16)
|
||||||
treetop (~> 1.4.8)
|
treetop (~> 1.4.8)
|
||||||
mime-types (1.18)
|
mime-types (1.19)
|
||||||
multi_json (1.1.0)
|
multi_json (1.3.6)
|
||||||
mysql2 (0.3.11)
|
mysql2 (0.3.11)
|
||||||
nokogiri (1.5.4)
|
nokogiri (1.5.5)
|
||||||
polyglot (0.3.3)
|
polyglot (0.3.3)
|
||||||
rack (1.4.1)
|
rack (1.4.1)
|
||||||
rack-cache (1.2)
|
rack-cache (1.2)
|
||||||
|
@ -64,53 +64,58 @@ GEM
|
||||||
rack
|
rack
|
||||||
rack-test (0.6.1)
|
rack-test (0.6.1)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rails (3.2.2)
|
rails (3.2.6)
|
||||||
actionmailer (= 3.2.2)
|
actionmailer (= 3.2.6)
|
||||||
actionpack (= 3.2.2)
|
actionpack (= 3.2.6)
|
||||||
activerecord (= 3.2.2)
|
activerecord (= 3.2.6)
|
||||||
activeresource (= 3.2.2)
|
activeresource (= 3.2.6)
|
||||||
activesupport (= 3.2.2)
|
activesupport (= 3.2.6)
|
||||||
bundler (~> 1.0)
|
bundler (~> 1.0)
|
||||||
railties (= 3.2.2)
|
railties (= 3.2.6)
|
||||||
railties (3.2.2)
|
railties (3.2.6)
|
||||||
actionpack (= 3.2.2)
|
actionpack (= 3.2.6)
|
||||||
activesupport (= 3.2.2)
|
activesupport (= 3.2.6)
|
||||||
rack-ssl (~> 1.3.2)
|
rack-ssl (~> 1.3.2)
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
rdoc (~> 3.4)
|
rdoc (~> 3.4)
|
||||||
thor (~> 0.14.6)
|
thor (>= 0.14.6, < 2.0)
|
||||||
raindrops (0.9.0)
|
raindrops (0.10.0)
|
||||||
rake (0.9.2.2)
|
rake (0.9.2.2)
|
||||||
rdiscount (1.6.8)
|
rdiscount (1.6.8)
|
||||||
rdoc (3.12)
|
rdoc (3.12)
|
||||||
json (~> 1.4)
|
json (~> 1.4)
|
||||||
rspec (2.9.0)
|
riddle (1.5.2)
|
||||||
rspec-core (~> 2.9.0)
|
rspec (2.11.0)
|
||||||
rspec-expectations (~> 2.9.0)
|
rspec-core (~> 2.11.0)
|
||||||
rspec-mocks (~> 2.9.0)
|
rspec-expectations (~> 2.11.0)
|
||||||
rspec-core (2.9.0)
|
rspec-mocks (~> 2.11.0)
|
||||||
rspec-expectations (2.9.0)
|
rspec-core (2.11.0)
|
||||||
|
rspec-expectations (2.11.1)
|
||||||
diff-lcs (~> 1.1.3)
|
diff-lcs (~> 1.1.3)
|
||||||
rspec-mocks (2.9.0)
|
rspec-mocks (2.11.1)
|
||||||
rspec-rails (2.9.0)
|
rspec-rails (2.11.0)
|
||||||
actionpack (>= 3.0)
|
actionpack (>= 3.0)
|
||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
railties (>= 3.0)
|
railties (>= 3.0)
|
||||||
rspec (~> 2.9.0)
|
rspec (~> 2.11.0)
|
||||||
sprockets (2.1.2)
|
sprockets (2.1.3)
|
||||||
hike (~> 1.2)
|
hike (~> 1.2)
|
||||||
rack (~> 1.0)
|
rack (~> 1.0)
|
||||||
tilt (~> 1.1, != 1.3.0)
|
tilt (~> 1.1, != 1.3.0)
|
||||||
sqlite3 (1.3.6)
|
sqlite3 (1.3.6)
|
||||||
thor (0.14.6)
|
thinking-sphinx (2.0.12)
|
||||||
|
activerecord (>= 3.0.3)
|
||||||
|
builder (>= 2.1.2)
|
||||||
|
riddle (>= 1.5.2)
|
||||||
|
thor (0.15.4)
|
||||||
tilt (1.3.3)
|
tilt (1.3.3)
|
||||||
treetop (1.4.10)
|
treetop (1.4.10)
|
||||||
polyglot
|
polyglot
|
||||||
polyglot (>= 0.3.1)
|
polyglot (>= 0.3.1)
|
||||||
tzinfo (0.3.32)
|
tzinfo (0.3.33)
|
||||||
uglifier (1.2.4)
|
uglifier (1.2.6)
|
||||||
execjs (>= 0.3.0)
|
execjs (>= 0.3.0)
|
||||||
multi_json (>= 1.0.2)
|
multi_json (~> 1.3)
|
||||||
unicorn (4.3.1)
|
unicorn (4.3.1)
|
||||||
kgio (~> 2.6)
|
kgio (~> 2.6)
|
||||||
rack
|
rack
|
||||||
|
@ -128,9 +133,10 @@ DEPENDENCIES
|
||||||
machinist
|
machinist
|
||||||
mysql2
|
mysql2
|
||||||
nokogiri
|
nokogiri
|
||||||
rails (= 3.2.2)
|
rails (= 3.2.6)
|
||||||
rdiscount
|
rdiscount
|
||||||
rspec-rails (~> 2.6)
|
rspec-rails (~> 2.6)
|
||||||
sqlite3
|
sqlite3
|
||||||
|
thinking-sphinx (= 2.0.12)
|
||||||
uglifier
|
uglifier
|
||||||
unicorn
|
unicorn
|
||||||
|
|
|
@ -297,7 +297,8 @@ div#footer a {
|
||||||
/* stories */
|
/* stories */
|
||||||
|
|
||||||
ol.stories,
|
ol.stories,
|
||||||
ol.comments {
|
ol.comments,
|
||||||
|
ol.search_results {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -321,6 +322,16 @@ ol.comments.preview {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ol.search_results {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-bottom: 0em;
|
||||||
|
padding-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol.search_results li.story {
|
||||||
|
padding-bottom: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
div.voters {
|
div.voters {
|
||||||
float: left;
|
float: left;
|
||||||
margin-top: -4px;
|
margin-top: -4px;
|
||||||
|
@ -371,13 +382,13 @@ li.downvoted div.voters a.downvoter {
|
||||||
border-top-color: gray;
|
border-top-color: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
ol.stories li.story,
|
li.story,
|
||||||
ol.comments li.comment {
|
li.comment {
|
||||||
clear: both;
|
clear: both;
|
||||||
padding-top: 0.4em;
|
padding-top: 0.4em;
|
||||||
padding-bottom: 0.4em;
|
padding-bottom: 0.4em;
|
||||||
}
|
}
|
||||||
ol.comments li.comment {
|
li.comment {
|
||||||
padding-top: 0.5em;
|
padding-top: 0.5em;
|
||||||
padding-bottom: 0.5em;
|
padding-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
@ -389,21 +400,21 @@ li div.details {
|
||||||
padding-top: 0.1em;
|
padding-top: 0.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
ol.comments li.negative {
|
li.negative {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
color: gray !important;
|
color: gray !important;
|
||||||
}
|
}
|
||||||
ol.comments li.negative_3 {
|
li.negative_3 {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
ol.comments li.negative_5 {
|
li.negative_5 {
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
ol.comments li.negative_7 {
|
li.negative_7 {
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
ol.comments li.highlighted {
|
li.comment.highlighted {
|
||||||
background-color: #ffffbf;
|
background-color: #ffffbf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,7 +428,7 @@ li .link a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
ol.stories a.tag {
|
li.story a.tag {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,7 +447,7 @@ li .byline {
|
||||||
color: #888;
|
color: #888;
|
||||||
font-size: 8.5pt;
|
font-size: 8.5pt;
|
||||||
}
|
}
|
||||||
ol.stories li .byline {
|
li.story .byline {
|
||||||
margin-top: 1px;
|
margin-top: 1px;
|
||||||
}
|
}
|
||||||
li .byline a {
|
li .byline a {
|
||||||
|
@ -461,7 +472,8 @@ div.story_content {
|
||||||
margin-bottom: 3em;
|
margin-bottom: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.morelink {
|
div.morelink,
|
||||||
|
div.page_link_buttons {
|
||||||
margin-top: 1.5em;
|
margin-top: 1.5em;
|
||||||
margin-left: 40px;
|
margin-left: 40px;
|
||||||
}
|
}
|
||||||
|
@ -470,6 +482,24 @@ div.morelink a {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
div.page_link_buttons {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
||||||
|
div.page_link_buttons a {
|
||||||
|
color: #666;
|
||||||
|
border: 1px solid #d0d0d0;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
div.page_link_buttons a.cur {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
div.story_text {
|
div.story_text {
|
||||||
margin-bottom: 1.5em;
|
margin-bottom: 1.5em;
|
||||||
|
|
24
app/controllers/search_controller.rb
Normal file
24
app/controllers/search_controller.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
class SearchController < ApplicationController
|
||||||
|
def index
|
||||||
|
@title = "Search"
|
||||||
|
@cur_url = "/search"
|
||||||
|
|
||||||
|
@search = Search.new
|
||||||
|
|
||||||
|
if params[:q].present?
|
||||||
|
@search.q = params[:q]
|
||||||
|
@search.what = params[:what]
|
||||||
|
@search.order = params[:order]
|
||||||
|
|
||||||
|
if params[:page]
|
||||||
|
@search.page = params[:page].to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
if @search.valid?
|
||||||
|
@search.search_for_user!(@user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
render :action => "index"
|
||||||
|
end
|
||||||
|
end
|
|
@ -17,6 +17,16 @@ class Comment < ActiveRecord::Base
|
||||||
|
|
||||||
MAX_EDIT_MINS = 45
|
MAX_EDIT_MINS = 45
|
||||||
|
|
||||||
|
define_index do
|
||||||
|
indexes comment
|
||||||
|
indexes user.username, :as => :author
|
||||||
|
|
||||||
|
has "(upvotes - downvotes)", :as => :score, :type => :integer,
|
||||||
|
:sortable => true
|
||||||
|
|
||||||
|
has created_at
|
||||||
|
end
|
||||||
|
|
||||||
validate do
|
validate do
|
||||||
self.comment.to_s.strip == "" &&
|
self.comment.to_s.strip == "" &&
|
||||||
errors.add(:comment, "cannot be blank.")
|
errors.add(:comment, "cannot be blank.")
|
||||||
|
|
88
app/models/search.rb
Normal file
88
app/models/search.rb
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
class Search
|
||||||
|
include ActiveModel::Validations
|
||||||
|
include ActiveModel::Conversion
|
||||||
|
include ActiveModel::AttributeMethods
|
||||||
|
extend ActiveModel::Naming
|
||||||
|
|
||||||
|
attr_accessor :q, :what, :order
|
||||||
|
attr_accessor :results, :page, :total_results, :per_page
|
||||||
|
|
||||||
|
validates_length_of :q, :minimum => 2
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@q = ""
|
||||||
|
@what = "all"
|
||||||
|
@order = "relevance"
|
||||||
|
|
||||||
|
@page = 1
|
||||||
|
@per_page = 20
|
||||||
|
|
||||||
|
@results = []
|
||||||
|
@total_results = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def persisted?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_url_params
|
||||||
|
[ :q, :what, :order ].map{|p| "#{p}=#{CGI.escape(self.send(p))}"
|
||||||
|
}.join("&")
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_for_user!(user)
|
||||||
|
opts = {
|
||||||
|
:match_mode => :extended,
|
||||||
|
:rank_mode => :bm25,
|
||||||
|
:page => @page,
|
||||||
|
:per_page => @per_page,
|
||||||
|
}
|
||||||
|
|
||||||
|
if order == "newest"
|
||||||
|
opts[:order] = :created_at
|
||||||
|
opts[:sort_mode] = :desc
|
||||||
|
elsif order == "points"
|
||||||
|
opts[:order] = :score
|
||||||
|
opts[:sort_mode] = :desc
|
||||||
|
end
|
||||||
|
|
||||||
|
opts[:classes] = []
|
||||||
|
if what == "all"
|
||||||
|
opts[:classes] = [ Story, Comment ]
|
||||||
|
elsif what == "comments"
|
||||||
|
opts[:classes] = [ Comment ]
|
||||||
|
elsif what == "stories"
|
||||||
|
opts[:classes] = [ Story ]
|
||||||
|
end
|
||||||
|
|
||||||
|
opts[:include] = [ :story, :user ]
|
||||||
|
|
||||||
|
# go go gadget search
|
||||||
|
@results = ThinkingSphinx.search @q, opts
|
||||||
|
@total_results = @results.total_entries
|
||||||
|
|
||||||
|
# bind votes for both types
|
||||||
|
|
||||||
|
if opts[:classes].include?(Comment) && user
|
||||||
|
votes = Vote.comment_votes_by_user_for_comment_ids_hash(user.id,
|
||||||
|
@results.select{|r| r.class == Comment }.map{|c| c.id })
|
||||||
|
|
||||||
|
@results.each do |r|
|
||||||
|
if r.class == Comment && votes[r.id]
|
||||||
|
r.current_vote = votes[r.id]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts[:classes].include?(Story) && user
|
||||||
|
votes = Vote.story_votes_by_user_for_story_ids_hash(user.id,
|
||||||
|
@results.select{|r| r.class == Story }.map{|s| s.id })
|
||||||
|
|
||||||
|
@results.each do |r|
|
||||||
|
if r.class == Story && votes[r.id]
|
||||||
|
r.vote = votes[r.id][:vote]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -14,14 +14,32 @@ class Story < ActiveRecord::Base
|
||||||
MAX_EDIT_MINS = 30
|
MAX_EDIT_MINS = 30
|
||||||
|
|
||||||
attr_accessor :_comment_count
|
attr_accessor :_comment_count
|
||||||
attr_accessor :vote, :story_type, :already_posted_story, :fetched_content
|
attr_accessor :vote, :already_posted_story, :fetched_content
|
||||||
attr_accessor :new_tags, :tags_to_add, :tags_to_delete
|
attr_accessor :new_tags, :tags_to_add, :tags_to_delete
|
||||||
|
|
||||||
attr_accessible :title, :description, :story_type, :tags_a
|
attr_accessible :title, :description, :tags_a
|
||||||
|
|
||||||
before_create :assign_short_id
|
before_create :assign_short_id
|
||||||
after_create :mark_submitter
|
after_create :mark_submitter
|
||||||
after_save :deal_with_tags
|
after_save :deal_with_tags
|
||||||
|
|
||||||
|
define_index do
|
||||||
|
indexes url
|
||||||
|
indexes title
|
||||||
|
indexes description
|
||||||
|
indexes user.username, :as => :author
|
||||||
|
indexes tags(:tag), :as => :tags
|
||||||
|
|
||||||
|
has created_at, :sortable => true
|
||||||
|
has hotness, is_moderated, is_expired
|
||||||
|
has "(upvotes - downvotes)", :as => :score, :type => :integer,
|
||||||
|
:sortable => true
|
||||||
|
|
||||||
|
set_property :field_weights => {
|
||||||
|
:title => 10,
|
||||||
|
:tags => 5,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
validate do
|
validate do
|
||||||
if self.url.present?
|
if self.url.present?
|
||||||
|
|
|
@ -23,7 +23,8 @@ class Vote < ActiveRecord::Base
|
||||||
attr_accessible nil
|
attr_accessible nil
|
||||||
|
|
||||||
def self.votes_by_user_for_stories_hash(user, stories)
|
def self.votes_by_user_for_stories_hash(user, stories)
|
||||||
votes = []
|
votes = {}
|
||||||
|
|
||||||
Vote.where(:user_id => user, :story_id => stories,
|
Vote.where(:user_id => user, :story_id => stories,
|
||||||
:comment_id => nil).each do |v|
|
:comment_id => nil).each do |v|
|
||||||
votes[v.story_id] = v.vote
|
votes[v.story_id] = v.vote
|
||||||
|
@ -42,6 +43,48 @@ class Vote < ActiveRecord::Base
|
||||||
|
|
||||||
votes
|
votes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.story_votes_by_user_for_story_ids_hash(user_id, story_ids)
|
||||||
|
if !story_ids.any?
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
votes = {}
|
||||||
|
|
||||||
|
cond = [ "user_id = ? AND comment_id IS NULL AND story_id IN (", user_id ]
|
||||||
|
story_ids.each_with_index do |s,x|
|
||||||
|
cond.push s
|
||||||
|
cond[0] += (x == 0 ? "" : ",") + "?"
|
||||||
|
end
|
||||||
|
cond[0] += ")"
|
||||||
|
|
||||||
|
Vote.find(:all, :conditions => cond).each do |v|
|
||||||
|
votes[v.story_id] = { :vote => v.vote, :reason => v.reason }
|
||||||
|
end
|
||||||
|
|
||||||
|
votes
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.comment_votes_by_user_for_comment_ids_hash(user_id, comment_ids)
|
||||||
|
if !comment_ids.any?
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
votes = {}
|
||||||
|
|
||||||
|
cond = [ "user_id = ? AND comment_id IN (", user_id ]
|
||||||
|
comment_ids.each_with_index do |c,x|
|
||||||
|
cond.push c
|
||||||
|
cond[0] += (x == 0 ? "" : ",") + "?"
|
||||||
|
end
|
||||||
|
cond[0] += ")"
|
||||||
|
|
||||||
|
Vote.find(:all, :conditions => cond).each do |v|
|
||||||
|
votes[v.comment_id] = { :vote => v.vote, :reason => v.reason }
|
||||||
|
end
|
||||||
|
|
||||||
|
votes
|
||||||
|
end
|
||||||
|
|
||||||
def self.vote_thusly_on_story_or_comment_for_user_because(vote, story_id,
|
def self.vote_thusly_on_story_or_comment_for_user_because(vote, story_id,
|
||||||
comment_id, user_id, reason, update_counters = true)
|
comment_id, user_id, reason, update_counters = true)
|
||||||
|
|
|
@ -54,7 +54,7 @@ class="comment <%= comment.current_vote ? (comment.current_vote[:vote] == 1 ?
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if defined?(show_story) && show_story %>
|
<% if defined?(show_story) && show_story %>
|
||||||
| on
|
| on:
|
||||||
<a href="<%= story.comments_url %>"><%= story.title %></a>
|
<a href="<%= story.comments_url %>"><%= story.title %></a>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -38,8 +38,8 @@
|
||||||
<a href="/settings"><%= @user.username %> (<%= @user.karma %>)</a>
|
<a href="/settings"><%= @user.username %> (<%= @user.karma %>)</a>
|
||||||
|
|
||||||
<%= link_to "Logout", { :controller => "login", :action => "logout" },
|
<%= link_to "Logout", { :controller => "login", :action => "logout" },
|
||||||
{ :confirm => "Are you sure you want to logout?",
|
:data => { :confirm => "Are you sure you want to logout?" },
|
||||||
"method" => "post" } %>
|
:method => "post" %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<a href="/login">Login</a>
|
<a href="/login">Login</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
83
app/views/search/index.html.erb
Normal file
83
app/views/search/index.html.erb
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<div class="box">
|
||||||
|
<div class="legend">
|
||||||
|
Search
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= form_tag "/search", :method => :get do %>
|
||||||
|
<div class="boxline">
|
||||||
|
<%= text_field_tag "q", @search.q, :size => 40 %>
|
||||||
|
<input type="submit" value="Search">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="boxline">
|
||||||
|
<label class="required">Include:</label>
|
||||||
|
|
||||||
|
<%= radio_button_tag "what", "all", @search.what == "all" %>
|
||||||
|
<label for="search_what_all" class="normal">All</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<%= radio_button_tag "what", "stories", @search.what == "stories" %>
|
||||||
|
<label for="search_what_stories" class="normal">Stories</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<%= radio_button_tag "what", "comments", @search.what == "comments" %>
|
||||||
|
<label for="search_what_comments" class="normal">Comments</label>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<label class="required">Order By:</label>
|
||||||
|
|
||||||
|
<%= radio_button_tag "order", "relevance", @search.order == "relevance" %>
|
||||||
|
<label for="search_order_relevance" class="normal">Relevance</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<%= radio_button_tag "order", "newest", @search.order == "newest" %>
|
||||||
|
<label for="search_order_newest" class="normal">Newest</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<%= radio_button_tag "order", "points", @search.order == "points" %>
|
||||||
|
<label for="search_order_points" class="normal">Points</label>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% if @search.results.any? %>
|
||||||
|
<div class="box">
|
||||||
|
<p>
|
||||||
|
<div class="legend">
|
||||||
|
<%= @search.total_results %> result<%= @search.total_results == 1 ? "" :
|
||||||
|
"s" %> for "<%= @search.q %>"
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ol class="search_results">
|
||||||
|
<% @search.results.each do |res| %>
|
||||||
|
<% if res.class == Story %>
|
||||||
|
<%= render :partial => "stories/listdetail",
|
||||||
|
:locals => { :story => res } %>
|
||||||
|
<% elsif res.class == Comment %>
|
||||||
|
<%= render :partial => "comments/comment",
|
||||||
|
:locals => { :comment => res, :story => res.story,
|
||||||
|
:show_story => true, :hide_voters => true } %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<% if @search.total_results > @search.per_page %>
|
||||||
|
<div class="page_link_buttons">
|
||||||
|
Page:
|
||||||
|
|
||||||
|
<% (@search.total_results.to_f / @search.per_page.to_f).ceil.
|
||||||
|
times do |p| %>
|
||||||
|
<a href="/search?<%= raw(@search.to_url_params) %>&page=<%= p + 1
|
||||||
|
%>" class="<%= @search.page == p + 1 ? "cur" : "" %>"><%= p + 1
|
||||||
|
%></a>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
|
@ -26,6 +26,8 @@ Lobsters::Application.routes.draw do
|
||||||
match "/t/:tag" => "home#tagged", :as => "tag"
|
match "/t/:tag" => "home#tagged", :as => "tag"
|
||||||
match "/t/:tag/page/:page" => "home#tagged"
|
match "/t/:tag/page/:page" => "home#tagged"
|
||||||
|
|
||||||
|
get "/search" => "search#index"
|
||||||
|
|
||||||
resources :stories do
|
resources :stories do
|
||||||
post "upvote"
|
post "upvote"
|
||||||
post "downvote"
|
post "downvote"
|
||||||
|
|
3
config/sphinx.yml
Normal file
3
config/sphinx.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
production:
|
||||||
|
address: 127.0.0.1
|
||||||
|
port: 9313
|
Loading…
Reference in a new issue