@ -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.
|
||||||
|
*/
|
@ -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
|
@ -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,18 @@
|
|||||||
|
<style type="text/css">
|
||||||
|
#tag {
|
||||||
|
background-color: #dbe4ee;
|
||||||
|
border-radius: 5px 5px 5px 5px;
|
||||||
|
color: #3a587d !important;
|
||||||
|
padding: 0px 4px;
|
||||||
|
margin: 3px;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 11px;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<% for tag in @tags %>
|
||||||
|
<span id="tag">
|
||||||
|
<%= tag.name %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
@ -0,0 +1,4 @@
|
|||||||
|
#SeemsRateable engine Initializer
|
||||||
|
|
||||||
|
#Configure owner class of the given ratings
|
||||||
|
SeemsRateable::Engine.config.owner_class = "User"
|
@ -0,0 +1,5 @@
|
|||||||
|
class AddProjectIdToShares < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :shares, :project_id, :integer
|
||||||
|
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
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 572 B |
After Width: | Height: | Size: 427 B |
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
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 194 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.9 KiB |