加入了对赞功能模块,同时预留了踩功能,但暂时不使用。
This commit is contained in:
parent
4676eed9a3
commit
0b5ea87f11
1
Gemfile
1
Gemfile
|
@ -7,6 +7,7 @@ gem "coderay", "~> 1.0.6"
|
||||||
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
|
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
|
||||||
gem "builder", "3.0.0"
|
gem "builder", "3.0.0"
|
||||||
gem 'acts-as-taggable-on'
|
gem 'acts-as-taggable-on'
|
||||||
|
gem 'seems_rateable'
|
||||||
# Optional gem for LDAP authentication
|
# Optional gem for LDAP authentication
|
||||||
group :ldap do
|
group :ldap do
|
||||||
gem "net-ldap", "~> 0.3.1"
|
gem "net-ldap", "~> 0.3.1"
|
||||||
|
|
|
@ -98,6 +98,9 @@ GEM
|
||||||
rmagick (2.13.2)
|
rmagick (2.13.2)
|
||||||
ruby-openid (2.1.8)
|
ruby-openid (2.1.8)
|
||||||
rubyzip (0.9.9)
|
rubyzip (0.9.9)
|
||||||
|
seems_rateable (1.0.9)
|
||||||
|
jquery-rails
|
||||||
|
rails
|
||||||
selenium-webdriver (2.33.0)
|
selenium-webdriver (2.33.0)
|
||||||
childprocess (>= 0.2.5)
|
childprocess (>= 0.2.5)
|
||||||
multi_json (~> 1.0)
|
multi_json (~> 1.0)
|
||||||
|
@ -150,6 +153,7 @@ DEPENDENCIES
|
||||||
rdoc (>= 2.4.2)
|
rdoc (>= 2.4.2)
|
||||||
rmagick (>= 2.0.0)
|
rmagick (>= 2.0.0)
|
||||||
ruby-openid (~> 2.1.4)
|
ruby-openid (~> 2.1.4)
|
||||||
|
seems_rateable
|
||||||
shoulda (~> 3.3.2)
|
shoulda (~> 3.3.2)
|
||||||
sqlite3
|
sqlite3
|
||||||
yard
|
yard
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
// All this logic will automatically be available in application.js.
|
|
@ -0,0 +1,225 @@
|
||||||
|
/************************************************************************
|
||||||
|
*************************************************************************
|
||||||
|
@Name : jRating - jQuery Plugin
|
||||||
|
@Revison : 3.0
|
||||||
|
@Date : 28/01/2013
|
||||||
|
@Author: ALPIXEL - (www.myjqueryplugins.com - www.alpixel.fr)
|
||||||
|
@License : Open Source - MIT License : http://www.opensource.org/licenses/mit-license.php
|
||||||
|
|
||||||
|
**************************************************************************
|
||||||
|
*************************************************************************/
|
||||||
|
(function($) {
|
||||||
|
$.fn.jRating = function(op) {
|
||||||
|
var defaults = {
|
||||||
|
/** String vars **/
|
||||||
|
bigStarsPath : '<%= image_path "seems_rateable/stars.png" %>', // path of the icon stars.png
|
||||||
|
smallStarsPath : '<%= image_path "seems_rateable/small.png" %>', // path of the icon small.png
|
||||||
|
path : '<%= SeemsRateable::Engine.routes.url_helpers.ratings_path %>',
|
||||||
|
type : 'big', // can be set to 'small' or 'big'
|
||||||
|
|
||||||
|
/** Boolean vars **/
|
||||||
|
step:false, // if true, mouseover binded star by star,
|
||||||
|
isDisabled:false,
|
||||||
|
showRateInfo: false,
|
||||||
|
canRateAgain : false,
|
||||||
|
|
||||||
|
/** Integer vars **/
|
||||||
|
length:5, // number of star to display
|
||||||
|
decimalLength : 0, // number of decimals.. Max 3, but you can complete the function 'getNote'
|
||||||
|
rateMax : 20, // maximal rate - integer from 0 to 9999 (or more)
|
||||||
|
rateInfosX : -45, // relative position in X axis of the info box when mouseover
|
||||||
|
rateInfosY : 5, // relative position in Y axis of the info box when mouseover
|
||||||
|
nbRates : 1,
|
||||||
|
|
||||||
|
/** Functions **/
|
||||||
|
onSuccess : null,
|
||||||
|
onError : null
|
||||||
|
};
|
||||||
|
|
||||||
|
if(this.length>0)
|
||||||
|
return this.each(function() {
|
||||||
|
/*vars*/
|
||||||
|
var opts = $.extend(defaults, op),
|
||||||
|
newWidth = 0,
|
||||||
|
starWidth = 0,
|
||||||
|
starHeight = 0,
|
||||||
|
bgPath = '',
|
||||||
|
hasRated = false,
|
||||||
|
globalWidth = 0,
|
||||||
|
nbOfRates = opts.nbRates;
|
||||||
|
|
||||||
|
if($(this).hasClass('jDisabled') || opts.isDisabled)
|
||||||
|
var jDisabled = true;
|
||||||
|
else
|
||||||
|
var jDisabled = false;
|
||||||
|
|
||||||
|
getStarWidth();
|
||||||
|
$(this).height(starHeight);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var average = parseFloat($(this).attr('data-average')), // get the average of all rates
|
||||||
|
idBox = parseInt($(this).attr('data-id')), // get the id of the box
|
||||||
|
kls = $(this).attr('data-kls'),
|
||||||
|
dimension = $(this).attr('data-dimension'),
|
||||||
|
widthRatingContainer = starWidth*opts.length, // Width of the Container
|
||||||
|
widthColor = average/opts.rateMax*widthRatingContainer, // Width of the color Container
|
||||||
|
quotient =
|
||||||
|
$('<div>',
|
||||||
|
{
|
||||||
|
'class' : 'jRatingColor',
|
||||||
|
css:{
|
||||||
|
width:widthColor
|
||||||
|
}
|
||||||
|
}).appendTo($(this)),
|
||||||
|
|
||||||
|
average =
|
||||||
|
$('<div>',
|
||||||
|
{
|
||||||
|
'class' : 'jRatingAverage',
|
||||||
|
css:{
|
||||||
|
width:0,
|
||||||
|
top:- starHeight
|
||||||
|
}
|
||||||
|
}).appendTo($(this)),
|
||||||
|
|
||||||
|
jstar =
|
||||||
|
$('<div>',
|
||||||
|
{
|
||||||
|
'class' : 'jStar',
|
||||||
|
css:{
|
||||||
|
width:widthRatingContainer,
|
||||||
|
height:starHeight,
|
||||||
|
top:- (starHeight*2),
|
||||||
|
background: 'url('+bgPath+') repeat-x'
|
||||||
|
}
|
||||||
|
}).appendTo($(this));
|
||||||
|
|
||||||
|
$(this).css({width: widthRatingContainer,overflow:'hidden',zIndex:1,position:'relative'});
|
||||||
|
|
||||||
|
if(!jDisabled)
|
||||||
|
$(this).unbind().bind({
|
||||||
|
mouseenter : function(e){
|
||||||
|
var realOffsetLeft = findRealLeft(this);
|
||||||
|
var relativeX = e.pageX - realOffsetLeft;
|
||||||
|
if (opts.showRateInfo)
|
||||||
|
var tooltip =
|
||||||
|
$('<p>',{
|
||||||
|
'class' : 'jRatingInfos',
|
||||||
|
html : getNote(relativeX)+' <span class="maxRate">/ '+opts.rateMax+'</span>',
|
||||||
|
css : {
|
||||||
|
top: (e.pageY + opts.rateInfosY),
|
||||||
|
left: (e.pageX + opts.rateInfosX)
|
||||||
|
}
|
||||||
|
}).appendTo('body').show();
|
||||||
|
},
|
||||||
|
mouseover : function(e){
|
||||||
|
$(this).css('cursor','pointer');
|
||||||
|
},
|
||||||
|
mouseout : function(){
|
||||||
|
$(this).css('cursor','default');
|
||||||
|
if(hasRated) average.width(globalWidth);
|
||||||
|
else average.width(0);
|
||||||
|
},
|
||||||
|
mousemove : function(e){
|
||||||
|
var realOffsetLeft = findRealLeft(this);
|
||||||
|
var relativeX = e.pageX - realOffsetLeft;
|
||||||
|
if(opts.step) newWidth = Math.floor(relativeX/starWidth)*starWidth + starWidth;
|
||||||
|
else newWidth = relativeX;
|
||||||
|
average.width(newWidth);
|
||||||
|
if (opts.showRateInfo)
|
||||||
|
$("p.jRatingInfos")
|
||||||
|
.css({
|
||||||
|
left: (e.pageX + opts.rateInfosX)
|
||||||
|
})
|
||||||
|
.html(getNote(newWidth) +' <span class="maxRate">/ '+opts.rateMax+'</span>');
|
||||||
|
},
|
||||||
|
mouseleave : function(){
|
||||||
|
$("p.jRatingInfos").remove();
|
||||||
|
},
|
||||||
|
click : function(e){
|
||||||
|
var element = this;
|
||||||
|
|
||||||
|
/*set vars*/
|
||||||
|
hasRated = true;
|
||||||
|
globalWidth = newWidth;
|
||||||
|
nbOfRates--;
|
||||||
|
|
||||||
|
if(!opts.canRateAgain || parseInt(nbOfRates) <= 0) $(this).unbind().css('cursor','default').addClass('jDisabled');
|
||||||
|
|
||||||
|
if (opts.showRateInfo) $("p.jRatingInfos").fadeOut('fast',function(){$(this).remove();});
|
||||||
|
e.preventDefault();
|
||||||
|
var rate = getNote(newWidth);
|
||||||
|
average.width(newWidth);
|
||||||
|
|
||||||
|
|
||||||
|
$.post(defaults.path,
|
||||||
|
{
|
||||||
|
idBox : idBox,
|
||||||
|
rate : rate,
|
||||||
|
kls : kls,
|
||||||
|
dimension : dimension
|
||||||
|
/** action : 'rating' **/
|
||||||
|
},
|
||||||
|
function(data) {
|
||||||
|
if(!data.error)
|
||||||
|
{
|
||||||
|
/** Here you can display an alert box,
|
||||||
|
or use the jNotify Plugin :) http://www.myqjqueryplugins.com/jNotify
|
||||||
|
exemple : */
|
||||||
|
if(opts.onSuccess) opts.onSuccess( element, rate );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
/** Here you can display an alert box,
|
||||||
|
or use the jNotify Plugin :) http://www.myqjqueryplugins.com/jNotify
|
||||||
|
exemple : */
|
||||||
|
if(opts.onError) opts.onError( element, rate );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'json'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function getNote(relativeX) {
|
||||||
|
var noteBrut = parseFloat((relativeX*100/widthRatingContainer)*opts.rateMax/100);
|
||||||
|
switch(opts.decimalLength) {
|
||||||
|
case 1 :
|
||||||
|
var note = Math.round(noteBrut*10)/10;
|
||||||
|
break;
|
||||||
|
case 2 :
|
||||||
|
var note = Math.round(noteBrut*100)/100;
|
||||||
|
break;
|
||||||
|
case 3 :
|
||||||
|
var note = Math.round(noteBrut*1000)/1000;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
var note = Math.round(noteBrut*1)/1;
|
||||||
|
}
|
||||||
|
return note;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getStarWidth(){
|
||||||
|
switch(opts.type) {
|
||||||
|
case 'small' :
|
||||||
|
starWidth = 12; // width of the picture small.png
|
||||||
|
starHeight = 10; // height of the picture small.png
|
||||||
|
bgPath = opts.smallStarsPath;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
starWidth = 23; // width of the picture stars.png
|
||||||
|
starHeight = 20; // height of the picture stars.png
|
||||||
|
bgPath = opts.bigStarsPath;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function findRealLeft(obj) {
|
||||||
|
if( !obj ) return 0;
|
||||||
|
return obj.offsetLeft + findRealLeft( obj.offsetParent );
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
})(jQuery);
|
|
@ -0,0 +1,25 @@
|
||||||
|
$(document).ready(function(){
|
||||||
|
$(".rateable").jRating({
|
||||||
|
//default options displayed below ->
|
||||||
|
|
||||||
|
rateMax: 5, //Maximal rate
|
||||||
|
length : 5, //Number of stars
|
||||||
|
//decimalLength : 0, //Number of decimals in the rate
|
||||||
|
//type : 'big', //Big or small
|
||||||
|
//step : true, //If set to true, filling of the stars is done star by star (step by step).
|
||||||
|
//isDisabled: false, //Set true to display static rating
|
||||||
|
//showRateInfo:false, //Rate info panel, set true to display
|
||||||
|
//rateInfosX : 45, //In pixel - Absolute left position of the information box during mousemove.
|
||||||
|
//rateInfosY : 5, //In pixel - Absolute top position of the information box during mousemove.
|
||||||
|
path : '<%= SeemsRateable::Engine.routes.url_helpers.ratings_path %>',
|
||||||
|
onSuccess : function(element, rate){
|
||||||
|
//something like ->
|
||||||
|
//alert('success');
|
||||||
|
$('<span class="text-success"><small style="display:inline-block;">Thanks for rating!</small></span>').insertAfter(element)
|
||||||
|
},
|
||||||
|
onError : function(element, rate) {
|
||||||
|
$('<span class="text-error"><small style="display:inline-block;">You have already rated!</small></span>').insertAfter(element)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,4 @@
|
||||||
|
/*
|
||||||
|
Place all the styles related to the matching controller here.
|
||||||
|
They will automatically be included in application.css.
|
||||||
|
*/
|
|
@ -95,6 +95,9 @@ class ApplicationController < ActionController::Base
|
||||||
|
|
||||||
# Returns the current user or nil if no user is logged in
|
# Returns the current user or nil if no user is logged in
|
||||||
# and starts a session if needed
|
# and starts a session if needed
|
||||||
|
def current_user
|
||||||
|
find_current_user
|
||||||
|
end
|
||||||
def find_current_user
|
def find_current_user
|
||||||
user = nil
|
user = nil
|
||||||
unless api_request?
|
unless api_request?
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
class PraiseTreadController < ApplicationController
|
||||||
|
|
||||||
|
def praise_plus
|
||||||
|
@obj = nil
|
||||||
|
if request.get?
|
||||||
|
@obj = params[:obj] # 传的是对象,最后变成id了
|
||||||
|
|
||||||
|
#首先创建或更新praise_tread 表
|
||||||
|
@pt = PraiseTread.find_by_user_id_and_praise_tread_object_id(User.current.id,@obj)
|
||||||
|
@pt = @pt.nil? ? PraiseTread.new : @pt
|
||||||
|
|
||||||
|
@pt.user_id = User.current.id
|
||||||
|
@pt.praise_tread_object_id = @obj.to_i
|
||||||
|
@pt.praise_tread_object_type = User.find_by_id(@obj).class.name.underscore
|
||||||
|
@pt.praise_or_tread = 1
|
||||||
|
@pt.save
|
||||||
|
|
||||||
|
#再创建或更新praise_tread_cache表
|
||||||
|
@ptc = PraiseTreadCache.find_by_object_id(@obj)
|
||||||
|
@ptc = @ptc.nil? ? PraiseTreadCache.new : @ptc
|
||||||
|
@ptc.object_id = @obj.to_i
|
||||||
|
@ptc.object_type = User.find_by_id(@obj).class.name.underscore
|
||||||
|
@ptc.plus(1)
|
||||||
|
@ptc.save
|
||||||
|
end
|
||||||
|
@obj = User.find_by_id(@obj)
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.js
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def praise_minus
|
||||||
|
@obj = nil
|
||||||
|
if request.get?
|
||||||
|
@obj = params[:obj] # 传的是对象,最后变成id了
|
||||||
|
|
||||||
|
#首先更新praise_tread 表 删除关注记录
|
||||||
|
@pt = PraiseTread.find_by_user_id_and_praise_tread_object_id_and_praise_tread_object_type(User.current.id,@obj,"user")
|
||||||
|
@pt.delete
|
||||||
|
|
||||||
|
#再更新praise_tread_cache表 使相应的记录减1 当为0时删除
|
||||||
|
@ptc = PraiseTreadCache.find_by_object_id(@obj)
|
||||||
|
@ptc.minus(1)
|
||||||
|
if @ptc.praise_num == 0
|
||||||
|
@ptc.delete
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
@obj = User.find_by_id(@obj)
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.js
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def tread_plus
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def tread_minus
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.js
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -26,11 +26,7 @@ module ApplicationHelper
|
||||||
include GravatarHelper::PublicMethods
|
include GravatarHelper::PublicMethods
|
||||||
include Redmine::Pagination::Helper
|
include Redmine::Pagination::Helper
|
||||||
include AvatarHelper
|
include AvatarHelper
|
||||||
|
include PraiseTreadHelper
|
||||||
### added by william
|
|
||||||
include ActsAsTaggableOn::TagsHelper
|
|
||||||
# include WatchersHelper
|
|
||||||
|
|
||||||
|
|
||||||
extend Forwardable
|
extend Forwardable
|
||||||
def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
|
def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
module PraiseTreadHelper
|
||||||
|
#added by william
|
||||||
|
def is_praise_or_tread(object,user_id)
|
||||||
|
@obj_type = object.class.name.underscore
|
||||||
|
@obj_id = object.id
|
||||||
|
@is_praise = PraiseTread.find_by_sql("select * from praise_treads where user_id=#{user_id} and " +
|
||||||
|
"praise_tread_object_type='#{@obj_type}' and praise_tread_object_id=#{@obj_id} ")
|
||||||
|
return @is_praise
|
||||||
|
end
|
||||||
|
#end
|
||||||
|
|
||||||
|
def get_praise_num(object)
|
||||||
|
@obj_type = object.class.name.underscore
|
||||||
|
@obj_id = object.id
|
||||||
|
@record = PraiseTreadCache.find_by_object_id_and_object_type(@obj_id,@obj_type)
|
||||||
|
if @record
|
||||||
|
return @record.praise_num
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
class PraiseTread < ActiveRecord::Base
|
||||||
|
attr_accessible :user_id,:praise_tread_object_id,:praise_tread_object_type,:praise_or_tread
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,11 @@
|
||||||
|
class PraiseTreadCache < ActiveRecord::Base
|
||||||
|
attr_accessible :object_id,:object_type,:praise_num,:tread_num
|
||||||
|
|
||||||
|
def plus(num)
|
||||||
|
self.update_attribute(:praise_num, self.praise_num.to_i + num)
|
||||||
|
end
|
||||||
|
|
||||||
|
def minus(num)
|
||||||
|
self.update_attribute(:praise_num, self.praise_num.to_i - num)
|
||||||
|
end
|
||||||
|
end
|
|
@ -103,7 +103,7 @@ class User < Principal
|
||||||
acts_as_customizable
|
acts_as_customizable
|
||||||
############################added by william
|
############################added by william
|
||||||
acts_as_taggable
|
acts_as_taggable
|
||||||
|
seems_rateable
|
||||||
############################# added by liuping 关注
|
############################# added by liuping 关注
|
||||||
acts_as_watchable
|
acts_as_watchable
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,11 @@
|
||||||
<%= favicon %>
|
<%= favicon %>
|
||||||
<%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2', 'application', :media => 'all' %>
|
<%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2', 'application', :media => 'all' %>
|
||||||
<%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
|
<%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
|
||||||
|
<!-- added by william -->
|
||||||
|
<%= seems_rateable_stylesheet %>
|
||||||
|
<!-- end -->
|
||||||
<%= javascript_heads %>
|
<%= javascript_heads %>
|
||||||
|
|
||||||
<%= heads_for_theme %>
|
<%= heads_for_theme %>
|
||||||
<%= call_hook :view_layouts_base_html_head %>
|
<%= call_hook :view_layouts_base_html_head %>
|
||||||
<!-- page specific tags -->
|
<!-- page specific tags -->
|
||||||
|
@ -44,6 +48,11 @@
|
||||||
</table></td>
|
</table></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<div id="praise_tread">
|
||||||
|
<%= render :partial => "/praise_tread/praise_tread",:locals => {:obj => @user,:show_flag => false,:user_id => User.current.id}%>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<%= l(:label_user_watcher) %> (<strong class="font_small_watch"><%= User.watched_by(@user.id).count %></strong>)
|
<%= l(:label_user_watcher) %> (<strong class="font_small_watch"><%= User.watched_by(@user.id).count %></strong>)
|
||||||
<%= l(:label_user_fans) %> (<strong class="font_small_watch"><%= @user.watcher_users.count %></strong>)
|
<%= l(:label_user_fans) %> (<strong class="font_small_watch"><%= @user.watcher_users.count %></strong>)
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<div id="praise">
|
||||||
|
<% if is_praise_or_tread(obj,user_id).size > 0 %>
|
||||||
|
<%= image_tag("/images/praise.png") %>
|
||||||
|
<%= link_to "取消贊",:controller=>"praise_tread",:action=>"praise_minus",:remote=>true,:obj => obj %>
|
||||||
|
(<%= get_praise_num(obj)%>)
|
||||||
|
<% else %>
|
||||||
|
<%= image_tag("/images/tread.png") %>
|
||||||
|
<%= link_to "贊",:controller=>"praise_tread",:action=>"praise_plus",:remote=>true,:obj => obj %>
|
||||||
|
(<%= get_praise_num(obj)%>)
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% if show_flag %>
|
||||||
|
<div id="tread">
|
||||||
|
<%= link_to image_tag("/images/tread.png"),:controller=>"praise_tread",
|
||||||
|
:action=>"tread_minus",:remote=>true,:obj => obj %>踩
|
||||||
|
</div>
|
||||||
|
<% end %>
|
|
@ -0,0 +1,3 @@
|
||||||
|
$('#praise_tread').html('<%= j(
|
||||||
|
render :partial => "/praise_tread/praise_tread",:locals => {:obj => @obj,:show_flag => false,:user_id => User.current.id}
|
||||||
|
)%>');
|
|
@ -0,0 +1,3 @@
|
||||||
|
$('#praise_tread').html('<%= j(
|
||||||
|
render :partial => "/praise_tread/praise_tread",:locals => {:obj => @obj,:show_flag => false,:user_id => User.current.id}
|
||||||
|
)%>');
|
|
@ -0,0 +1,4 @@
|
||||||
|
#SeemsRateable engine Initializer
|
||||||
|
|
||||||
|
#Configure owner class of the given ratings
|
||||||
|
SeemsRateable::Engine.config.owner_class = "User"
|
|
@ -18,10 +18,12 @@
|
||||||
RedmineApp::Application.routes.draw do
|
RedmineApp::Application.routes.draw do
|
||||||
resources :shares
|
resources :shares
|
||||||
|
|
||||||
|
|
||||||
get "tags/index"
|
get "tags/index"
|
||||||
|
|
||||||
get "tags/show"
|
get "tags/show"
|
||||||
|
get "praise_tread/praise_plus"
|
||||||
|
get "praise_tread/praise_minus"
|
||||||
|
get "praise_tread/tread_minus"
|
||||||
|
|
||||||
root :to => 'welcome#index', :as => 'home'
|
root :to => 'welcome#index', :as => 'home'
|
||||||
|
|
||||||
|
@ -95,7 +97,7 @@ RedmineApp::Application.routes.draw do
|
||||||
match 'users/:id/memberships/:membership_id', :to => 'users#edit_membership', :via => :put, :as => 'user_membership'
|
match 'users/:id/memberships/:membership_id', :to => 'users#edit_membership', :via => :put, :as => 'user_membership'
|
||||||
match 'users/:id/memberships/:membership_id', :to => 'users#destroy_membership', :via => :delete
|
match 'users/:id/memberships/:membership_id', :to => 'users#destroy_membership', :via => :delete
|
||||||
match 'users/:id/memberships', :to => 'users#edit_membership', :via => :post, :as => 'user_memberships'
|
match 'users/:id/memberships', :to => 'users#edit_membership', :via => :post, :as => 'user_memberships'
|
||||||
################# added by william
|
################# added by william
|
||||||
match 'users/tag_save', :to => 'users#tag_save', :via => :post, :as => 'tag'
|
match 'users/tag_save', :to => 'users#tag_save', :via => :post, :as => 'tag'
|
||||||
|
|
||||||
post 'watchers/watch', :to => 'watchers#watch', :as => 'watch'
|
post 'watchers/watch', :to => 'watchers#watch', :as => 'watch'
|
||||||
|
@ -422,8 +424,12 @@ RedmineApp::Application.routes.draw do
|
||||||
|
|
||||||
match 'bids/:id', :controller => 'bids', :action => 'show', :as => 'respond'
|
match 'bids/:id', :controller => 'bids', :action => 'show', :as => 'respond'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
########### added by liuping
|
########### added by liuping
|
||||||
match 'tags/add_tag',:to => 'tags#add_tag',:as=>"add_tag"
|
match 'tags/add_tag',:to => 'tags#add_tag',:as=>"add_tag"
|
||||||
match 'tags/delete_tag',:to => 'tags#delete_tag',:as=>"add_tag"
|
match 'tags/delete_tag',:to => 'tags#delete_tag',:as=>"add_tag"
|
||||||
|
match 'parise_tread/praise_plus',:to => 'parise_tread#praise_plus',:as=>"praise"
|
||||||
|
match 'parise_tread/tread_minus',:to => 'parise_tread#tread_minus',:as=>"tread"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
class CreateSeemsRateableRates < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :seems_rateable_rates do |t|
|
||||||
|
t.belongs_to :rater
|
||||||
|
t.belongs_to :rateable, :polymorphic => true
|
||||||
|
t.float :stars, :null => false
|
||||||
|
t.integer :rater_id, :limit => 8
|
||||||
|
t.integer :rateable_id
|
||||||
|
t.string :rateable_type
|
||||||
|
t.string :dimension
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :rates
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
class CreateSeemsRateableCachedRatings < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :seems_rateable_cached_ratings do |t|
|
||||||
|
t.belongs_to :cacheable, :polymorphic => true
|
||||||
|
t.float :avg, :null => false
|
||||||
|
t.integer :cnt, :null => false
|
||||||
|
t.string :dimension
|
||||||
|
t.integer :cacheable_id, :limit => 8
|
||||||
|
t.string :cacheable_type
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :cached_ratings
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,15 @@
|
||||||
|
class CreatePraiseTreads < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :praise_treads do |t|
|
||||||
|
t.column :user_id,:integer,:null => false
|
||||||
|
t.column :praise_tread_object_id,:integer
|
||||||
|
t.column :praise_tread_object_type,:string
|
||||||
|
t.column :praise_or_tread,:integer
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :praise_treads
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,16 @@
|
||||||
|
class CreatePraiseTreadCaches < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :praise_tread_caches do |t|
|
||||||
|
t.column :object_id,:integer,:null => false
|
||||||
|
t.column :object_type,:string
|
||||||
|
t.column :praise_num,:integer
|
||||||
|
t.column :tread_num,:integer
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :praise_tread_caches
|
||||||
|
end
|
||||||
|
end
|
41
db/schema.rb
41
db/schema.rb
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended to check this file into your version control system.
|
# It's strongly recommended to check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(:version => 20130805131602) do
|
ActiveRecord::Schema.define(:version => 20130807021309) do
|
||||||
|
|
||||||
create_table "a_user_watchers", :force => true do |t|
|
create_table "a_user_watchers", :force => true do |t|
|
||||||
t.string "name"
|
t.string "name"
|
||||||
|
@ -432,6 +432,24 @@ ActiveRecord::Schema.define(:version => 20130805131602) do
|
||||||
t.string "salt", :null => false
|
t.string "salt", :null => false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "praise_tread_caches", :force => true do |t|
|
||||||
|
t.integer "object_id", :null => false
|
||||||
|
t.string "object_type"
|
||||||
|
t.integer "praise_num"
|
||||||
|
t.integer "tread_num"
|
||||||
|
t.datetime "created_at", :null => false
|
||||||
|
t.datetime "updated_at", :null => false
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "praise_treads", :force => true do |t|
|
||||||
|
t.integer "user_id", :null => false
|
||||||
|
t.integer "praise_tread_object_id"
|
||||||
|
t.string "praise_tread_object_type"
|
||||||
|
t.integer "praise_or_tread"
|
||||||
|
t.datetime "created_at", :null => false
|
||||||
|
t.datetime "updated_at", :null => false
|
||||||
|
end
|
||||||
|
|
||||||
create_table "project_tags", :force => true do |t|
|
create_table "project_tags", :force => true do |t|
|
||||||
t.integer "project_id"
|
t.integer "project_id"
|
||||||
t.integer "tag_id"
|
t.integer "tag_id"
|
||||||
|
@ -507,6 +525,26 @@ ActiveRecord::Schema.define(:version => 20130805131602) do
|
||||||
t.string "issues_visibility", :limit => 30, :default => "default", :null => false
|
t.string "issues_visibility", :limit => 30, :default => "default", :null => false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "seems_rateable_cached_ratings", :force => true do |t|
|
||||||
|
t.integer "cacheable_id", :limit => 8
|
||||||
|
t.string "cacheable_type"
|
||||||
|
t.float "avg", :null => false
|
||||||
|
t.integer "cnt", :null => false
|
||||||
|
t.string "dimension"
|
||||||
|
t.datetime "created_at", :null => false
|
||||||
|
t.datetime "updated_at", :null => false
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "seems_rateable_rates", :force => true do |t|
|
||||||
|
t.integer "rater_id", :limit => 8
|
||||||
|
t.integer "rateable_id"
|
||||||
|
t.string "rateable_type"
|
||||||
|
t.float "stars", :null => false
|
||||||
|
t.string "dimension"
|
||||||
|
t.datetime "created_at", :null => false
|
||||||
|
t.datetime "updated_at", :null => false
|
||||||
|
end
|
||||||
|
|
||||||
create_table "settings", :force => true do |t|
|
create_table "settings", :force => true do |t|
|
||||||
t.string "name", :default => "", :null => false
|
t.string "name", :default => "", :null => false
|
||||||
t.text "value"
|
t.text "value"
|
||||||
|
@ -522,6 +560,7 @@ ActiveRecord::Schema.define(:version => 20130805131602) do
|
||||||
t.string "url"
|
t.string "url"
|
||||||
t.datetime "created_at", :null => false
|
t.datetime "created_at", :null => false
|
||||||
t.datetime "updated_at", :null => false
|
t.datetime "updated_at", :null => false
|
||||||
|
t.integer "project_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "students", :force => true do |t|
|
create_table "students", :force => true do |t|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
*.gem
|
||||||
|
*.rbc
|
||||||
|
.bundle
|
||||||
|
.config
|
||||||
|
.yardoc
|
||||||
|
Gemfile.lock
|
||||||
|
InstalledFiles
|
||||||
|
_yardoc
|
||||||
|
coverage
|
||||||
|
doc/
|
||||||
|
lib/bundler/man
|
||||||
|
pkg
|
||||||
|
rdoc
|
||||||
|
spec/reports
|
||||||
|
test/tmp
|
||||||
|
test/version_tmp
|
||||||
|
tmp
|
||||||
|
.project
|
||||||
|
.rvmrc
|
||||||
|
spec
|
||||||
|
test
|
|
@ -0,0 +1,23 @@
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
# Declare your gem's dependencies in seems_rateable.gemspec.
|
||||||
|
# Bundler will treat runtime dependencies like base dependencies, and
|
||||||
|
# development dependencies will be added by default to the :development group.
|
||||||
|
gemspec
|
||||||
|
|
||||||
|
# Declare any dependencies that are still in development here instead of in
|
||||||
|
# your gemspec. These might include edge Rails or gems from your path or
|
||||||
|
# Git. Remember to move these dependencies to your gemspec before releasing
|
||||||
|
# your gem to rubygems.org.
|
||||||
|
|
||||||
|
# To use debugger
|
||||||
|
# gem 'debugger'
|
||||||
|
|
||||||
|
|
||||||
|
group :development do
|
||||||
|
gem 'sqlite3'
|
||||||
|
gem 'jquery-rails'
|
||||||
|
gem 'twitter-bootstrap-rails'
|
||||||
|
gem 'sorcery'
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
Copyright 2013 YOURNAME
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,111 @@
|
||||||
|
# SeemsRateable
|
||||||
|
|
||||||
|
Star rating gem for Rails application using jQuery plugin <a href="http://www.myjqueryplugins.com/jquery-plugin/jrating">jRating</a>
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
<a href="http://rateable.herokuapp.com/">Demo</a> application, requires to sign up before rating
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Add this line to your application's Gemfile:
|
||||||
|
|
||||||
|
gem 'seems_rateable'
|
||||||
|
|
||||||
|
And then execute:
|
||||||
|
|
||||||
|
$ bundle
|
||||||
|
|
||||||
|
Or install it yourself as:
|
||||||
|
|
||||||
|
$ gem install seems_rateable
|
||||||
|
|
||||||
|
### Generation
|
||||||
|
|
||||||
|
$ rails generate seems_rateable:install
|
||||||
|
|
||||||
|
Generator creates migration files, javascript files and initializer
|
||||||
|
|
||||||
|
### Prepare
|
||||||
|
|
||||||
|
Require javascript files by adding this line to application.js
|
||||||
|
|
||||||
|
#application.js
|
||||||
|
//= require_directory ./rateable
|
||||||
|
|
||||||
|
Add <code>seems_rateable</code> to routes.rb file
|
||||||
|
|
||||||
|
Include stylesheet adding <code><%= seems_rateable_stylesheet %></code> to your layout header
|
||||||
|
|
||||||
|
Also make sure you have an existing <code>current_user</code> helper method
|
||||||
|
|
||||||
|
Don't forget to run
|
||||||
|
|
||||||
|
$ rake db:migrate
|
||||||
|
|
||||||
|
To prepare model add <code> seems_rateable </code> to your rateable model file. You can also pass a hash of options to
|
||||||
|
customize the functionality
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><code>:dimensions</code>Array of dimensions e.g <code>:dimensions => [:quality, :quantity]</code> </li>
|
||||||
|
<li><code>:allow_update</code>Allowing user to re-rate his own ratings, default set to false e.g <code>:allow_update=> true</code></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
class Post < ActiveRecord::Base
|
||||||
|
seems_rateable :allow_update => true, :dimensions => [:quality, :length]
|
||||||
|
end
|
||||||
|
|
||||||
|
To access object's rates use <code>rates</code> method, to get dimension rates pass an argument eg :
|
||||||
|
|
||||||
|
@object.rates
|
||||||
|
@object.rates(:quality)
|
||||||
|
@object.rates(:quantity)
|
||||||
|
|
||||||
|
This also applies to cached average rating e.g
|
||||||
|
|
||||||
|
@object.average
|
||||||
|
@object.average(:quality)
|
||||||
|
@object.average(:quantity)
|
||||||
|
|
||||||
|
And to object's raters e.g
|
||||||
|
|
||||||
|
@object.raters
|
||||||
|
@object.raters(:quality)
|
||||||
|
@object.raters(:quantity)
|
||||||
|
|
||||||
|
To track user's given ratings add <code>seems_rateable_rater</code> to your rater model.
|
||||||
|
If your rater class is not "User"(e.g "Client" or "Customer") change configuration in initializer generated by this engine.
|
||||||
|
Now you can access user's ratings by <code>@user.ratings_given</code>
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
To display star rating use helper method <code>rating_for</code> in your view
|
||||||
|
|
||||||
|
#index.html.erb
|
||||||
|
|
||||||
|
rating_for @post
|
||||||
|
|
||||||
|
rating_for @post, :dimension => :quality, :class => 'post', :id => 'list'
|
||||||
|
|
||||||
|
rating_for @post, :static => true
|
||||||
|
|
||||||
|
You can specify these options :
|
||||||
|
<ul>
|
||||||
|
<li><code>:dimension</code>The dimension of the object</li>
|
||||||
|
<li><code>:static</code>Set to true to display static star rating, default false</li>
|
||||||
|
<li><code>:class</code>Class of the div, default set to 'rateable'</li>
|
||||||
|
<li><code>:id</code>ID of the div e.g <code>:id => "info"</code>, default nil</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
To edit the javascript options locate rateable.js file in /app/assets/javascripts/rateable/.
|
||||||
|
The javascript options are explained directly in the file
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
1. Fork it
|
||||||
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||||
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||||
|
4. Push to the branch (`git push origin my-new-feature`)
|
||||||
|
5. Create new Pull Request
|
|
@ -0,0 +1,32 @@
|
||||||
|
begin
|
||||||
|
require 'bundler/setup'
|
||||||
|
rescue LoadError
|
||||||
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
||||||
|
end
|
||||||
|
|
||||||
|
require 'rdoc/task'
|
||||||
|
|
||||||
|
RDoc::Task.new(:rdoc) do |rdoc|
|
||||||
|
rdoc.rdoc_dir = 'rdoc'
|
||||||
|
rdoc.title = 'SeemsRateable'
|
||||||
|
rdoc.options << '--line-numbers'
|
||||||
|
rdoc.rdoc_files.include('README.rdoc')
|
||||||
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||||
|
end
|
||||||
|
|
||||||
|
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
||||||
|
load 'rails/tasks/engine.rake'
|
||||||
|
Bundler::GemHelper.install_tasks
|
||||||
|
|
||||||
|
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
||||||
|
load 'rails/tasks/engine.rake'
|
||||||
|
|
||||||
|
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
|
||||||
|
require 'rspec/core'
|
||||||
|
require 'rspec/core/rake_task'
|
||||||
|
|
||||||
|
task :default => :spec
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 572 B |
Binary file not shown.
After Width: | Height: | Size: 427 B |
Binary file not shown.
After Width: | Height: | Size: 1018 B |
|
@ -0,0 +1,15 @@
|
||||||
|
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
||||||
|
// listed below.
|
||||||
|
//
|
||||||
|
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
||||||
|
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
||||||
|
//
|
||||||
|
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||||
|
// compiled file.
|
||||||
|
//
|
||||||
|
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
||||||
|
// about supported directives.
|
||||||
|
|
||||||
|
//= require_tree .
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||||
|
* listed below.
|
||||||
|
*
|
||||||
|
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
||||||
|
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
||||||
|
*
|
||||||
|
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
||||||
|
* compiled file, but it's generally better to create a new file per style scope.
|
||||||
|
*
|
||||||
|
*= require_self
|
||||||
|
*= require_tree .
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*********************/
|
||||||
|
/** jRating CSS **/
|
||||||
|
/*********************/
|
||||||
|
|
||||||
|
/**Div containing the color of the stars */
|
||||||
|
|
||||||
|
|
||||||
|
.jRatingAverage {
|
||||||
|
background-color:#f62929;
|
||||||
|
position:relative;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
z-index:2;
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
.jRatingColor {
|
||||||
|
background-color:#FFD400; /* bgcolor of the stars*/
|
||||||
|
position:relative;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
z-index:2;
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Div containing the stars **/
|
||||||
|
.jStar {
|
||||||
|
position:relative;
|
||||||
|
left:0;
|
||||||
|
z-index:3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** P containing the rate informations **/
|
||||||
|
p.jRatingInfos {
|
||||||
|
position: absolute;
|
||||||
|
z-index:9999;
|
||||||
|
background: transparent url('bg_jRatingInfos.png') no-repeat;
|
||||||
|
color: #CACACA;
|
||||||
|
display: none;
|
||||||
|
width: 91px;
|
||||||
|
height: 29px;
|
||||||
|
font-size:16px;
|
||||||
|
text-align:center;
|
||||||
|
padding-top:5px;
|
||||||
|
}
|
||||||
|
p.jRatingInfos span.maxRate {
|
||||||
|
color:#c9c9c9;
|
||||||
|
font-size:14px;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
module SeemsRateable
|
||||||
|
class ApplicationController < ActionController::Base
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
require_dependency "seems_rateable/application_controller"
|
||||||
|
|
||||||
|
module SeemsRateable
|
||||||
|
class RatingsController < ::ApplicationController
|
||||||
|
def create
|
||||||
|
raise NoCurrentUserInstanceError unless current_user
|
||||||
|
|
||||||
|
obj = params[:kls].classify.constantize.find(params[:idBox])
|
||||||
|
begin
|
||||||
|
obj.rate(params[:rate].to_i, current_user.id, params[:dimension])
|
||||||
|
render :json => true
|
||||||
|
rescue Errors::AlreadyRatedError
|
||||||
|
render :json => {:error => true}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
module SeemsRateable
|
||||||
|
module ApplicationHelper
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
module SeemsRateable
|
||||||
|
module RatingsHelper
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,5 @@
|
||||||
|
module SeemsRateable
|
||||||
|
class CachedRating < ActiveRecord::Base
|
||||||
|
belongs_to :cacheable, :polymorphic => true
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,6 @@
|
||||||
|
module SeemsRateable
|
||||||
|
class Rate < ActiveRecord::Base
|
||||||
|
belongs_to :rater, :class_name => SeemsRateable::Engine.config.owner_class
|
||||||
|
belongs_to :rateable, :polymorphic => true
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>SeemsRateable</title>
|
||||||
|
<%= stylesheet_link_tag "seems_rateable/application", media: "all" %>
|
||||||
|
<%= javascript_include_tag "seems_rateable/application" %>
|
||||||
|
<%= csrf_meta_tags %>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<%= yield %>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env ruby1.9.1
|
||||||
|
# This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
|
||||||
|
|
||||||
|
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
||||||
|
ENGINE_PATH = File.expand_path('../../lib/seems_rateable/engine', __FILE__)
|
||||||
|
|
||||||
|
require 'rails/all'
|
||||||
|
require 'rails/engine/commands'
|
|
@ -0,0 +1,3 @@
|
||||||
|
SeemsRateable::Engine.routes.draw do
|
||||||
|
resources :ratings, :only => :create
|
||||||
|
end
|
|
@ -0,0 +1,39 @@
|
||||||
|
require 'rails/generators/migration'
|
||||||
|
require 'fileutils'
|
||||||
|
|
||||||
|
module SeemsRateable
|
||||||
|
module Generators
|
||||||
|
class InstallGenerator < ::Rails::Generators::Base
|
||||||
|
include Rails::Generators::Migration
|
||||||
|
source_root File.expand_path('../templates', __FILE__)
|
||||||
|
|
||||||
|
def self.next_migration_number(path)
|
||||||
|
unless @prev_migration_nr
|
||||||
|
@prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
|
||||||
|
else
|
||||||
|
@prev_migration_nr += 1
|
||||||
|
end
|
||||||
|
@prev_migration_nr.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "generating migration files"
|
||||||
|
def copy_migrations
|
||||||
|
migration_template "rates_migration.rb", "db/migrate/create_seems_rateable_rates.rb"
|
||||||
|
migration_template "cached_ratings_migration.rb", "db/migrate/create_seems_rateable_cached_ratings.rb"
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "generating initializer"
|
||||||
|
def copy_initializer
|
||||||
|
template "initializer.rb", "config/initializers/seems_rateable.rb"
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "generating javascript files"
|
||||||
|
def copy_javascript_asset
|
||||||
|
Dir.mkdir "app/assets/javascripts/rateable" unless File.directory?("app/assets/javascripts/rateable")
|
||||||
|
copy_file "rateable.js.erb", "app/assets/javascripts/rateable/rateable.js.erb" unless File.exists?("app/assets/javascripts/rateable/rateable.js.erb")
|
||||||
|
copy_file "jRating.js.erb", "app/assets/javascripts/rateable/jRating.js.erb" unless File.exists?("app/assets/javascripts/rateable/jRating.js.erb")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
class CreateSeemsRateableCachedRatings < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :seems_rateable_cached_ratings do |t|
|
||||||
|
t.belongs_to :cacheable, :polymorphic => true
|
||||||
|
t.float :avg, :null => false
|
||||||
|
t.integer :cnt, :null => false
|
||||||
|
t.string :dimension
|
||||||
|
t.integer :cacheable_id, :limit => 8
|
||||||
|
t.string :cacheable_type
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :cached_ratings
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
#SeemsRateable engine Initializer
|
||||||
|
|
||||||
|
#Configure owner class of the given ratings
|
||||||
|
SeemsRateable::Engine.config.owner_class = "User"
|
|
@ -0,0 +1,225 @@
|
||||||
|
/************************************************************************
|
||||||
|
*************************************************************************
|
||||||
|
@Name : jRating - jQuery Plugin
|
||||||
|
@Revison : 3.0
|
||||||
|
@Date : 28/01/2013
|
||||||
|
@Author: ALPIXEL - (www.myjqueryplugins.com - www.alpixel.fr)
|
||||||
|
@License : Open Source - MIT License : http://www.opensource.org/licenses/mit-license.php
|
||||||
|
|
||||||
|
**************************************************************************
|
||||||
|
*************************************************************************/
|
||||||
|
(function($) {
|
||||||
|
$.fn.jRating = function(op) {
|
||||||
|
var defaults = {
|
||||||
|
/** String vars **/
|
||||||
|
bigStarsPath : '<%= image_path "seems_rateable/stars.png" %>', // path of the icon stars.png
|
||||||
|
smallStarsPath : '<%= image_path "seems_rateable/small.png" %>', // path of the icon small.png
|
||||||
|
path : '<%= SeemsRateable::Engine.routes.url_helpers.ratings_path %>',
|
||||||
|
type : 'big', // can be set to 'small' or 'big'
|
||||||
|
|
||||||
|
/** Boolean vars **/
|
||||||
|
step:false, // if true, mouseover binded star by star,
|
||||||
|
isDisabled:false,
|
||||||
|
showRateInfo: false,
|
||||||
|
canRateAgain : false,
|
||||||
|
|
||||||
|
/** Integer vars **/
|
||||||
|
length:5, // number of star to display
|
||||||
|
decimalLength : 0, // number of decimals.. Max 3, but you can complete the function 'getNote'
|
||||||
|
rateMax : 20, // maximal rate - integer from 0 to 9999 (or more)
|
||||||
|
rateInfosX : -45, // relative position in X axis of the info box when mouseover
|
||||||
|
rateInfosY : 5, // relative position in Y axis of the info box when mouseover
|
||||||
|
nbRates : 1,
|
||||||
|
|
||||||
|
/** Functions **/
|
||||||
|
onSuccess : null,
|
||||||
|
onError : null
|
||||||
|
};
|
||||||
|
|
||||||
|
if(this.length>0)
|
||||||
|
return this.each(function() {
|
||||||
|
/*vars*/
|
||||||
|
var opts = $.extend(defaults, op),
|
||||||
|
newWidth = 0,
|
||||||
|
starWidth = 0,
|
||||||
|
starHeight = 0,
|
||||||
|
bgPath = '',
|
||||||
|
hasRated = false,
|
||||||
|
globalWidth = 0,
|
||||||
|
nbOfRates = opts.nbRates;
|
||||||
|
|
||||||
|
if($(this).hasClass('jDisabled') || opts.isDisabled)
|
||||||
|
var jDisabled = true;
|
||||||
|
else
|
||||||
|
var jDisabled = false;
|
||||||
|
|
||||||
|
getStarWidth();
|
||||||
|
$(this).height(starHeight);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var average = parseFloat($(this).attr('data-average')), // get the average of all rates
|
||||||
|
idBox = parseInt($(this).attr('data-id')), // get the id of the box
|
||||||
|
kls = $(this).attr('data-kls'),
|
||||||
|
dimension = $(this).attr('data-dimension'),
|
||||||
|
widthRatingContainer = starWidth*opts.length, // Width of the Container
|
||||||
|
widthColor = average/opts.rateMax*widthRatingContainer, // Width of the color Container
|
||||||
|
quotient =
|
||||||
|
$('<div>',
|
||||||
|
{
|
||||||
|
'class' : 'jRatingColor',
|
||||||
|
css:{
|
||||||
|
width:widthColor
|
||||||
|
}
|
||||||
|
}).appendTo($(this)),
|
||||||
|
|
||||||
|
average =
|
||||||
|
$('<div>',
|
||||||
|
{
|
||||||
|
'class' : 'jRatingAverage',
|
||||||
|
css:{
|
||||||
|
width:0,
|
||||||
|
top:- starHeight
|
||||||
|
}
|
||||||
|
}).appendTo($(this)),
|
||||||
|
|
||||||
|
jstar =
|
||||||
|
$('<div>',
|
||||||
|
{
|
||||||
|
'class' : 'jStar',
|
||||||
|
css:{
|
||||||
|
width:widthRatingContainer,
|
||||||
|
height:starHeight,
|
||||||
|
top:- (starHeight*2),
|
||||||
|
background: 'url('+bgPath+') repeat-x'
|
||||||
|
}
|
||||||
|
}).appendTo($(this));
|
||||||
|
|
||||||
|
$(this).css({width: widthRatingContainer,overflow:'hidden',zIndex:1,position:'relative'});
|
||||||
|
|
||||||
|
if(!jDisabled)
|
||||||
|
$(this).unbind().bind({
|
||||||
|
mouseenter : function(e){
|
||||||
|
var realOffsetLeft = findRealLeft(this);
|
||||||
|
var relativeX = e.pageX - realOffsetLeft;
|
||||||
|
if (opts.showRateInfo)
|
||||||
|
var tooltip =
|
||||||
|
$('<p>',{
|
||||||
|
'class' : 'jRatingInfos',
|
||||||
|
html : getNote(relativeX)+' <span class="maxRate">/ '+opts.rateMax+'</span>',
|
||||||
|
css : {
|
||||||
|
top: (e.pageY + opts.rateInfosY),
|
||||||
|
left: (e.pageX + opts.rateInfosX)
|
||||||
|
}
|
||||||
|
}).appendTo('body').show();
|
||||||
|
},
|
||||||
|
mouseover : function(e){
|
||||||
|
$(this).css('cursor','pointer');
|
||||||
|
},
|
||||||
|
mouseout : function(){
|
||||||
|
$(this).css('cursor','default');
|
||||||
|
if(hasRated) average.width(globalWidth);
|
||||||
|
else average.width(0);
|
||||||
|
},
|
||||||
|
mousemove : function(e){
|
||||||
|
var realOffsetLeft = findRealLeft(this);
|
||||||
|
var relativeX = e.pageX - realOffsetLeft;
|
||||||
|
if(opts.step) newWidth = Math.floor(relativeX/starWidth)*starWidth + starWidth;
|
||||||
|
else newWidth = relativeX;
|
||||||
|
average.width(newWidth);
|
||||||
|
if (opts.showRateInfo)
|
||||||
|
$("p.jRatingInfos")
|
||||||
|
.css({
|
||||||
|
left: (e.pageX + opts.rateInfosX)
|
||||||
|
})
|
||||||
|
.html(getNote(newWidth) +' <span class="maxRate">/ '+opts.rateMax+'</span>');
|
||||||
|
},
|
||||||
|
mouseleave : function(){
|
||||||
|
$("p.jRatingInfos").remove();
|
||||||
|
},
|
||||||
|
click : function(e){
|
||||||
|
var element = this;
|
||||||
|
|
||||||
|
/*set vars*/
|
||||||
|
hasRated = true;
|
||||||
|
globalWidth = newWidth;
|
||||||
|
nbOfRates--;
|
||||||
|
|
||||||
|
if(!opts.canRateAgain || parseInt(nbOfRates) <= 0) $(this).unbind().css('cursor','default').addClass('jDisabled');
|
||||||
|
|
||||||
|
if (opts.showRateInfo) $("p.jRatingInfos").fadeOut('fast',function(){$(this).remove();});
|
||||||
|
e.preventDefault();
|
||||||
|
var rate = getNote(newWidth);
|
||||||
|
average.width(newWidth);
|
||||||
|
|
||||||
|
|
||||||
|
$.post(defaults.path,
|
||||||
|
{
|
||||||
|
idBox : idBox,
|
||||||
|
rate : rate,
|
||||||
|
kls : kls,
|
||||||
|
dimension : dimension
|
||||||
|
/** action : 'rating' **/
|
||||||
|
},
|
||||||
|
function(data) {
|
||||||
|
if(!data.error)
|
||||||
|
{
|
||||||
|
/** Here you can display an alert box,
|
||||||
|
or use the jNotify Plugin :) http://www.myqjqueryplugins.com/jNotify
|
||||||
|
exemple : */
|
||||||
|
if(opts.onSuccess) opts.onSuccess( element, rate );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
/** Here you can display an alert box,
|
||||||
|
or use the jNotify Plugin :) http://www.myqjqueryplugins.com/jNotify
|
||||||
|
exemple : */
|
||||||
|
if(opts.onError) opts.onError( element, rate );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'json'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function getNote(relativeX) {
|
||||||
|
var noteBrut = parseFloat((relativeX*100/widthRatingContainer)*opts.rateMax/100);
|
||||||
|
switch(opts.decimalLength) {
|
||||||
|
case 1 :
|
||||||
|
var note = Math.round(noteBrut*10)/10;
|
||||||
|
break;
|
||||||
|
case 2 :
|
||||||
|
var note = Math.round(noteBrut*100)/100;
|
||||||
|
break;
|
||||||
|
case 3 :
|
||||||
|
var note = Math.round(noteBrut*1000)/1000;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
var note = Math.round(noteBrut*1)/1;
|
||||||
|
}
|
||||||
|
return note;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getStarWidth(){
|
||||||
|
switch(opts.type) {
|
||||||
|
case 'small' :
|
||||||
|
starWidth = 12; // width of the picture small.png
|
||||||
|
starHeight = 10; // height of the picture small.png
|
||||||
|
bgPath = opts.smallStarsPath;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
starWidth = 23; // width of the picture stars.png
|
||||||
|
starHeight = 20; // height of the picture stars.png
|
||||||
|
bgPath = opts.bigStarsPath;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function findRealLeft(obj) {
|
||||||
|
if( !obj ) return 0;
|
||||||
|
return obj.offsetLeft + findRealLeft( obj.offsetParent );
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
})(jQuery);
|
|
@ -0,0 +1,25 @@
|
||||||
|
$(document).ready(function(){
|
||||||
|
$(".rateable").jRating({
|
||||||
|
//default options displayed below ->
|
||||||
|
|
||||||
|
rateMax: 5, //Maximal rate
|
||||||
|
length : 5, //Number of stars
|
||||||
|
//decimalLength : 0, //Number of decimals in the rate
|
||||||
|
//type : 'big', //Big or small
|
||||||
|
//step : true, //If set to true, filling of the stars is done star by star (step by step).
|
||||||
|
//isDisabled: false, //Set true to display static rating
|
||||||
|
//showRateInfo:false, //Rate info panel, set true to display
|
||||||
|
//rateInfosX : 45, //In pixel - Absolute left position of the information box during mousemove.
|
||||||
|
//rateInfosY : 5, //In pixel - Absolute top position of the information box during mousemove.
|
||||||
|
path : '<%= SeemsRateable::Engine.routes.url_helpers.ratings_path %>',
|
||||||
|
onSuccess : function(element, rate){
|
||||||
|
//something like ->
|
||||||
|
//alert('success');
|
||||||
|
$('<span class="text-success"><small style="display:inline-block;">Thanks for rating!</small></span>').insertAfter(element)
|
||||||
|
},
|
||||||
|
onError : function(element, rate) {
|
||||||
|
$('<span class="text-error"><small style="display:inline-block;">You have already rated!</small></span>').insertAfter(element)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,18 @@
|
||||||
|
class CreateSeemsRateableRates < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :seems_rateable_rates do |t|
|
||||||
|
t.belongs_to :rater
|
||||||
|
t.belongs_to :rateable, :polymorphic => true
|
||||||
|
t.float :stars, :null => false
|
||||||
|
t.integer :rater_id, :limit => 8
|
||||||
|
t.integer :rateable_id
|
||||||
|
t.string :rateable_type
|
||||||
|
t.string :dimension
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :rates
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,14 @@
|
||||||
|
begin
|
||||||
|
require 'rails'
|
||||||
|
rescue LoadError
|
||||||
|
end
|
||||||
|
|
||||||
|
require "seems_rateable/engine"
|
||||||
|
require "seems_rateable/errors"
|
||||||
|
require "seems_rateable/helpers"
|
||||||
|
require "seems_rateable/model"
|
||||||
|
require "seems_rateable/routes"
|
||||||
|
require "seems_rateable/version"
|
||||||
|
|
||||||
|
module SeemsRateable
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
module SeemsRateable
|
||||||
|
class Engine < ::Rails::Engine
|
||||||
|
isolate_namespace SeemsRateable
|
||||||
|
|
||||||
|
config.generators do |g|
|
||||||
|
g.test_framework :rspec, :fixture => false
|
||||||
|
g.fixture_replacement :factory_girl, :dir => 'spec/factories'
|
||||||
|
end
|
||||||
|
|
||||||
|
initializer :seems_rateable do
|
||||||
|
ActiveRecord::Base.send :include, SeemsRateable::Model
|
||||||
|
ActionView::Base.send :include, SeemsRateable::Helpers
|
||||||
|
ActionDispatch::Routing::Mapper.send :include, SeemsRateable::Routes
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,21 @@
|
||||||
|
module SeemsRateable
|
||||||
|
module Errors
|
||||||
|
class InvalidRateableObjectError < StandardError
|
||||||
|
def to_s
|
||||||
|
"Stated object is not rateable. Add 'seems_rateable' to your object's class model."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class NoCurrentUserInstanceError < StandardError
|
||||||
|
def to_s
|
||||||
|
"User instance current_user is not available."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AlreadyRatedError < StandardError
|
||||||
|
def to_s
|
||||||
|
"User has already rated an object."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,27 @@
|
||||||
|
module SeemsRateable
|
||||||
|
module Helpers
|
||||||
|
def rating_for(obj, opts={})
|
||||||
|
raise Errors::InvalidRateableObjectError unless obj.class.respond_to?(:rateable?)
|
||||||
|
|
||||||
|
options = {
|
||||||
|
:dimension => nil,
|
||||||
|
:static => false,
|
||||||
|
:class => 'rateable',
|
||||||
|
:id => nil
|
||||||
|
}.update(opts)
|
||||||
|
|
||||||
|
content_tag :div, "", "data-average" => obj.average(options[:dimension]) ? obj.average(options[:dimension]).avg : 0, :id => options[:id],
|
||||||
|
:class => "#{options[:class]}#{jdisabled?(options[:static])}",
|
||||||
|
"data-id" => obj.id, "data-kls" => obj.class.name, "data-dimension" => options[:dimension]
|
||||||
|
end
|
||||||
|
|
||||||
|
def seems_rateable_stylesheet
|
||||||
|
stylesheet_link_tag "seems_rateable/application", media: "all", "data-turbolinks-track" => true
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def jdisabled?(option)
|
||||||
|
" jDisabled" if option || !current_user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,111 @@
|
||||||
|
require 'active_support/concern'
|
||||||
|
module SeemsRateable
|
||||||
|
module Model
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
def rate(stars, user_id, dimension=nil)
|
||||||
|
if !has_rated?(user_id, dimension)
|
||||||
|
self.rates.create do |r|
|
||||||
|
r.stars = stars
|
||||||
|
r.rater_id = user_id
|
||||||
|
end
|
||||||
|
update_overall_average_rating(stars, dimension)
|
||||||
|
elsif has_rated?(user_id, dimension) && can_update?
|
||||||
|
update_users_rating(stars, user_id, dimension)
|
||||||
|
else
|
||||||
|
raise Errors::AlreadyRatedError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_overall_average_rating(stars, dimension=nil)
|
||||||
|
if average(dimension).nil?
|
||||||
|
CachedRating.create do |r|
|
||||||
|
r.avg = stars
|
||||||
|
r.dimension = dimension
|
||||||
|
r.cacheable_id = self.id
|
||||||
|
r.cacheable_type = self.class.name
|
||||||
|
r.cnt = 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
r = average(dimension)
|
||||||
|
r.avg = (r.avg * r.cnt + stars) / (r.cnt+1)
|
||||||
|
r.cnt += 1
|
||||||
|
r.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_users_rating(stars, user_id, dimension=nil)
|
||||||
|
obj = rates(dimension).where(:rater_id => user_id).first
|
||||||
|
current_record = average(dimension)
|
||||||
|
current_record.avg = (current_record.avg*current_record.cnt - obj.stars + stars) / (current_record.cnt)
|
||||||
|
current_record.save!
|
||||||
|
obj.stars = stars
|
||||||
|
obj.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def average(dimension=nil)
|
||||||
|
if dimension.nil?
|
||||||
|
self.send "rate_average_without_dimension"
|
||||||
|
else
|
||||||
|
self.send "#{dimension}_average"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def rates(dimension=nil)
|
||||||
|
if dimension.nil?
|
||||||
|
self.send "rates_without_dimension"
|
||||||
|
else
|
||||||
|
self.send "#{dimension}_rates"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def raters(dimension=nil)
|
||||||
|
if dimension.nil?
|
||||||
|
self.send "raters_without_dimension"
|
||||||
|
else
|
||||||
|
self.send "#{dimension}_raters"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_rated?(user_id, dimension=nil)
|
||||||
|
record = self.rates(dimension).where(:rater_id => user_id)
|
||||||
|
record.empty? ? false : true
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_update?
|
||||||
|
self.class.can_update?
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def seems_rateable(opts={})
|
||||||
|
#has_many :rates_without_dimension, -> { where(dimension: nil) }, :as => :rateable, :class_name => SeemsRateable::Rate, :dependent => :destroy
|
||||||
|
has_many :rates_without_dimension, :conditions => { dimension: nil }, :as => :rateable, :class_name => SeemsRateable::Rate, :dependent => :destroy
|
||||||
|
has_many :raters_without_dimension, :through => :rates_without_dimension, :source => :rater
|
||||||
|
has_one :rate_average_without_dimension, :conditions => { dimension: nil }, :as => :cacheable, :class_name => SeemsRateable::CachedRating, :dependent => :destroy
|
||||||
|
|
||||||
|
@permission = opts[:allow_update] ? true : false
|
||||||
|
|
||||||
|
def self.can_update?
|
||||||
|
@permission
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.rateable?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts[:dimensions].is_a?(Array)
|
||||||
|
opts[:dimensions].each do |dimension|
|
||||||
|
has_many :"#{dimension}_rates", :conditions => { dimension: dimension.to_s }, :dependent => :destroy, :class_name => SeemsRateable::Rate, :as => :rateable
|
||||||
|
has_many :"#{dimension}_raters", :through => :"#{dimension}_rates", :source => :rater
|
||||||
|
has_one :"#{dimension}_average", :conditions => { dimension: dimension.to_s }, :as => :cacheable, :class_name => SeemsRateable::CachedRating, :dependent => :destroy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def seems_rateable_rater
|
||||||
|
has_many :ratings_given, :class_name => SeemsRateable::Rate, :foreign_key => :rater_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
module SeemsRateable
|
||||||
|
module Routes
|
||||||
|
def seems_rateable
|
||||||
|
mount SeemsRateable::Engine => '/rateable', :as => :rateable
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,3 @@
|
||||||
|
module SeemsRateable
|
||||||
|
VERSION = "1.0.9"
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
# desc "Explaining what the task does"
|
||||||
|
# task :seems_rateable do
|
||||||
|
# # Task goes here
|
||||||
|
# end
|
|
@ -0,0 +1,25 @@
|
||||||
|
$:.push File.expand_path("../lib", __FILE__)
|
||||||
|
|
||||||
|
# Maintain your gem's version:
|
||||||
|
require "seems_rateable/version"
|
||||||
|
|
||||||
|
# Describe your gem and declare its dependencies:
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = "seems_rateable"
|
||||||
|
s.version = SeemsRateable::VERSION
|
||||||
|
s.authors = ["Peter Toth"]
|
||||||
|
s.email = ["proximin@gmail.com"]
|
||||||
|
s.homepage = "http://rateable.herokuapp.com"
|
||||||
|
s.summary = "Star Rating Engine"
|
||||||
|
s.description = "Star rating engine using jQuery plugin jRating for Rails applications"
|
||||||
|
|
||||||
|
s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
|
||||||
|
|
||||||
|
s.add_dependency "rails"
|
||||||
|
s.add_dependency "jquery-rails"
|
||||||
|
|
||||||
|
s.add_development_dependency "sqlite3"
|
||||||
|
s.add_development_dependency 'rspec-rails'
|
||||||
|
s.add_development_dependency 'capybara'
|
||||||
|
s.add_development_dependency 'factory_girl_rails'
|
||||||
|
end
|
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
Loading…
Reference in New Issue