parent
4676eed9a3
commit
0b5ea87f11
@ -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,4 @@
|
||||
#SeemsRateable engine Initializer
|
||||
|
||||
#Configure owner class of the given ratings
|
||||
SeemsRateable::Engine.config.owner_class = "User"
|
@ -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
|
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.9 KiB |
Loading…
Reference in new issue