From 0b5ea87f1147b12e586a60aa772425952bc07761 Mon Sep 17 00:00:00 2001 From: william Date: Thu, 8 Aug 2013 17:01:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E4=BA=86=E5=AF=B9=E8=B5=9E?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=A8=A1=E5=9D=97=EF=BC=8C=E5=90=8C=E6=97=B6?= =?UTF-8?q?=E9=A2=84=E7=95=99=E4=BA=86=E8=B8=A9=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E4=BD=86=E6=9A=82=E6=97=B6=E4=B8=8D=E4=BD=BF=E7=94=A8=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile | 1 + Gemfile.lock | 4 + app/assets/javascripts/praise_tread.js | 2 + .../javascripts/rateable/jRating.js.erb | 225 ++++++++++++++++++ .../javascripts/rateable/rateable.js.erb | 25 ++ app/assets/stylesheets/praise_tread.css | 4 + app/controllers/application_controller.rb | 3 + app/controllers/praise_tread_controller.rb | 68 ++++++ app/helpers/application_helper.rb | 8 +- app/helpers/praise_tread_helper.rb | 23 ++ app/models/praise_tread.rb | 4 + app/models/praise_tread_cache.rb | 11 + app/models/user.rb | 2 +- app/views/layouts/base_users.html.erb | 9 + app/views/praise_tread/_praise.html.erb | 0 app/views/praise_tread/_praise_tread.html.erb | 17 ++ app/views/praise_tread/_tread.html.erb | 0 app/views/praise_tread/praise_minus.js.erb | 3 + app/views/praise_tread/praise_plus.js.erb | 3 + app/views/praise_tread/tread_minus.js.erb | 0 config/initializers/seems_rateable.rb | 4 + config/routes.rb | 12 +- ...30806083151_create_seems_rateable_rates.rb | 18 ++ ...52_create_seems_rateable_cached_ratings.rb | 17 ++ .../20130807021235_create_praise_treads.rb | 15 ++ ...130807021309_create_praise_tread_caches.rb | 16 ++ db/schema.rb | 41 +++- lib/plugins/seems_rateable-master/.gitignore | 21 ++ lib/plugins/seems_rateable-master/Gemfile | 23 ++ lib/plugins/seems_rateable-master/MIT-LICENSE | 20 ++ lib/plugins/seems_rateable-master/README.md | 111 +++++++++ lib/plugins/seems_rateable-master/Rakefile | 32 +++ .../images/seems_rateable/bg_jRatingInfos.png | Bin 0 -> 572 bytes .../assets/images/seems_rateable/small.png | Bin 0 -> 427 bytes .../assets/images/seems_rateable/stars.png | Bin 0 -> 1018 bytes .../javascripts/seems_rateable/application.js | 15 ++ .../seems_rateable/application.css | 62 +++++ .../seems_rateable/application_controller.rb | 4 + .../seems_rateable/ratings_controller.rb | 17 ++ .../seems_rateable/application_helper.rb | 4 + .../helpers/seems_rateable/ratings_helper.rb | 4 + .../models/seems_rateable/cached_rating.rb | 5 + .../app/models/seems_rateable/rate.rb | 6 + .../seems_rateable/application.html.erb | 14 ++ lib/plugins/seems_rateable-master/bin/rails | 8 + .../seems_rateable-master/config/routes.rb | 3 + .../install/install_generator.rb | 39 +++ .../templates/cached_ratings_migration.rb | 17 ++ .../install/templates/initializer.rb | 4 + .../install/templates/jRating.js.erb | 225 ++++++++++++++++++ .../install/templates/rateable.js.erb | 25 ++ .../install/templates/rates_migration.rb | 18 ++ .../lib/seems_rateable.rb | 14 ++ .../lib/seems_rateable/engine.rb | 17 ++ .../lib/seems_rateable/errors.rb | 21 ++ .../lib/seems_rateable/helpers.rb | 27 +++ .../lib/seems_rateable/model.rb | 111 +++++++++ .../lib/seems_rateable/routes.rb | 7 + .../lib/seems_rateable/version.rb | 3 + .../lib/tasks/seems_rateable_tasks.rake | 4 + .../seems_rateable.gemspec | 25 ++ public/images/praise.png | Bin 0 -> 3103 bytes public/images/tread.png | Bin 0 -> 3001 bytes 63 files changed, 1430 insertions(+), 11 deletions(-) create mode 100644 app/assets/javascripts/praise_tread.js create mode 100644 app/assets/javascripts/rateable/jRating.js.erb create mode 100644 app/assets/javascripts/rateable/rateable.js.erb create mode 100644 app/assets/stylesheets/praise_tread.css create mode 100644 app/controllers/praise_tread_controller.rb create mode 100644 app/helpers/praise_tread_helper.rb create mode 100644 app/models/praise_tread.rb create mode 100644 app/models/praise_tread_cache.rb create mode 100644 app/views/praise_tread/_praise.html.erb create mode 100644 app/views/praise_tread/_praise_tread.html.erb create mode 100644 app/views/praise_tread/_tread.html.erb create mode 100644 app/views/praise_tread/praise_minus.js.erb create mode 100644 app/views/praise_tread/praise_plus.js.erb create mode 100644 app/views/praise_tread/tread_minus.js.erb create mode 100644 config/initializers/seems_rateable.rb create mode 100644 db/migrate/20130806083151_create_seems_rateable_rates.rb create mode 100644 db/migrate/20130806083152_create_seems_rateable_cached_ratings.rb create mode 100644 db/migrate/20130807021235_create_praise_treads.rb create mode 100644 db/migrate/20130807021309_create_praise_tread_caches.rb create mode 100644 lib/plugins/seems_rateable-master/.gitignore create mode 100644 lib/plugins/seems_rateable-master/Gemfile create mode 100644 lib/plugins/seems_rateable-master/MIT-LICENSE create mode 100644 lib/plugins/seems_rateable-master/README.md create mode 100644 lib/plugins/seems_rateable-master/Rakefile create mode 100644 lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/bg_jRatingInfos.png create mode 100644 lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/small.png create mode 100644 lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/stars.png create mode 100644 lib/plugins/seems_rateable-master/app/assets/javascripts/seems_rateable/application.js create mode 100644 lib/plugins/seems_rateable-master/app/assets/stylesheets/seems_rateable/application.css create mode 100644 lib/plugins/seems_rateable-master/app/controllers/seems_rateable/application_controller.rb create mode 100644 lib/plugins/seems_rateable-master/app/controllers/seems_rateable/ratings_controller.rb create mode 100644 lib/plugins/seems_rateable-master/app/helpers/seems_rateable/application_helper.rb create mode 100644 lib/plugins/seems_rateable-master/app/helpers/seems_rateable/ratings_helper.rb create mode 100644 lib/plugins/seems_rateable-master/app/models/seems_rateable/cached_rating.rb create mode 100644 lib/plugins/seems_rateable-master/app/models/seems_rateable/rate.rb create mode 100644 lib/plugins/seems_rateable-master/app/views/layouts/seems_rateable/application.html.erb create mode 100644 lib/plugins/seems_rateable-master/bin/rails create mode 100644 lib/plugins/seems_rateable-master/config/routes.rb create mode 100644 lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/install_generator.rb create mode 100644 lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/cached_ratings_migration.rb create mode 100644 lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/initializer.rb create mode 100644 lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/jRating.js.erb create mode 100644 lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rateable.js.erb create mode 100644 lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rates_migration.rb create mode 100644 lib/plugins/seems_rateable-master/lib/seems_rateable.rb create mode 100644 lib/plugins/seems_rateable-master/lib/seems_rateable/engine.rb create mode 100644 lib/plugins/seems_rateable-master/lib/seems_rateable/errors.rb create mode 100644 lib/plugins/seems_rateable-master/lib/seems_rateable/helpers.rb create mode 100644 lib/plugins/seems_rateable-master/lib/seems_rateable/model.rb create mode 100644 lib/plugins/seems_rateable-master/lib/seems_rateable/routes.rb create mode 100644 lib/plugins/seems_rateable-master/lib/seems_rateable/version.rb create mode 100644 lib/plugins/seems_rateable-master/lib/tasks/seems_rateable_tasks.rake create mode 100644 lib/plugins/seems_rateable-master/seems_rateable.gemspec create mode 100644 public/images/praise.png create mode 100644 public/images/tread.png diff --git a/Gemfile b/Gemfile index 261b71b7..f4decb75 100644 --- a/Gemfile +++ b/Gemfile @@ -7,6 +7,7 @@ gem "coderay", "~> 1.0.6" gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby] gem "builder", "3.0.0" gem 'acts-as-taggable-on' +gem 'seems_rateable' # Optional gem for LDAP authentication group :ldap do gem "net-ldap", "~> 0.3.1" diff --git a/Gemfile.lock b/Gemfile.lock index 99f29e91..aa29558c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -98,6 +98,9 @@ GEM rmagick (2.13.2) ruby-openid (2.1.8) rubyzip (0.9.9) + seems_rateable (1.0.9) + jquery-rails + rails selenium-webdriver (2.33.0) childprocess (>= 0.2.5) multi_json (~> 1.0) @@ -150,6 +153,7 @@ DEPENDENCIES rdoc (>= 2.4.2) rmagick (>= 2.0.0) ruby-openid (~> 2.1.4) + seems_rateable shoulda (~> 3.3.2) sqlite3 yard diff --git a/app/assets/javascripts/praise_tread.js b/app/assets/javascripts/praise_tread.js new file mode 100644 index 00000000..dee720fa --- /dev/null +++ b/app/assets/javascripts/praise_tread.js @@ -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. diff --git a/app/assets/javascripts/rateable/jRating.js.erb b/app/assets/javascripts/rateable/jRating.js.erb new file mode 100644 index 00000000..4f43a4f9 --- /dev/null +++ b/app/assets/javascripts/rateable/jRating.js.erb @@ -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 = + $('
', + { + 'class' : 'jRatingColor', + css:{ + width:widthColor + } + }).appendTo($(this)), + + average = + $('
', + { + 'class' : 'jRatingAverage', + css:{ + width:0, + top:- starHeight + } + }).appendTo($(this)), + + jstar = + $('
', + { + '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 = + $('

',{ + 'class' : 'jRatingInfos', + html : getNote(relativeX)+' / '+opts.rateMax+'', + 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) +' / '+opts.rateMax+''); + }, + 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); diff --git a/app/assets/javascripts/rateable/rateable.js.erb b/app/assets/javascripts/rateable/rateable.js.erb new file mode 100644 index 00000000..da6cc309 --- /dev/null +++ b/app/assets/javascripts/rateable/rateable.js.erb @@ -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'); + $('Thanks for rating!').insertAfter(element) + }, + onError : function(element, rate) { + $('You have already rated!').insertAfter(element) + } + }); + +}); diff --git a/app/assets/stylesheets/praise_tread.css b/app/assets/stylesheets/praise_tread.css new file mode 100644 index 00000000..afad32db --- /dev/null +++ b/app/assets/stylesheets/praise_tread.css @@ -0,0 +1,4 @@ +/* + Place all the styles related to the matching controller here. + They will automatically be included in application.css. +*/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2a4b5fb5..f57983e3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -95,6 +95,9 @@ class ApplicationController < ActionController::Base # Returns the current user or nil if no user is logged in # and starts a session if needed + def current_user + find_current_user + end def find_current_user user = nil unless api_request? diff --git a/app/controllers/praise_tread_controller.rb b/app/controllers/praise_tread_controller.rb new file mode 100644 index 00000000..52801110 --- /dev/null +++ b/app/controllers/praise_tread_controller.rb @@ -0,0 +1,68 @@ +class PraiseTreadController < ApplicationController + + def praise_plus + @obj = nil + if request.get? + @obj = params[:obj] # 传的是对象,最后变成id了 + + #首先创建或更新praise_tread 表 + @pt = PraiseTread.find_by_user_id_and_praise_tread_object_id(User.current.id,@obj) + @pt = @pt.nil? ? PraiseTread.new : @pt + + @pt.user_id = User.current.id + @pt.praise_tread_object_id = @obj.to_i + @pt.praise_tread_object_type = User.find_by_id(@obj).class.name.underscore + @pt.praise_or_tread = 1 + @pt.save + + #再创建或更新praise_tread_cache表 + @ptc = PraiseTreadCache.find_by_object_id(@obj) + @ptc = @ptc.nil? ? PraiseTreadCache.new : @ptc + @ptc.object_id = @obj.to_i + @ptc.object_type = User.find_by_id(@obj).class.name.underscore + @ptc.plus(1) + @ptc.save + end + @obj = User.find_by_id(@obj) + respond_to do |format| + format.html + format.js + end + end + + def praise_minus + @obj = nil + if request.get? + @obj = params[:obj] # 传的是对象,最后变成id了 + + #首先更新praise_tread 表 删除关注记录 + @pt = PraiseTread.find_by_user_id_and_praise_tread_object_id_and_praise_tread_object_type(User.current.id,@obj,"user") + @pt.delete + + #再更新praise_tread_cache表 使相应的记录减1 当为0时删除 + @ptc = PraiseTreadCache.find_by_object_id(@obj) + @ptc.minus(1) + if @ptc.praise_num == 0 + @ptc.delete + end + + end + @obj = User.find_by_id(@obj) + respond_to do |format| + format.html + format.js + end + end + + def tread_plus + + end + + def tread_minus + respond_to do |format| + format.html + format.js + end + end + +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8d14e63f..9935b577 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -26,12 +26,8 @@ module ApplicationHelper include GravatarHelper::PublicMethods include Redmine::Pagination::Helper include AvatarHelper - - ### added by william - include ActsAsTaggableOn::TagsHelper - # include WatchersHelper - - + include PraiseTreadHelper + extend Forwardable def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter diff --git a/app/helpers/praise_tread_helper.rb b/app/helpers/praise_tread_helper.rb new file mode 100644 index 00000000..9b2eba96 --- /dev/null +++ b/app/helpers/praise_tread_helper.rb @@ -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 diff --git a/app/models/praise_tread.rb b/app/models/praise_tread.rb new file mode 100644 index 00000000..901a3166 --- /dev/null +++ b/app/models/praise_tread.rb @@ -0,0 +1,4 @@ +class PraiseTread < ActiveRecord::Base + attr_accessible :user_id,:praise_tread_object_id,:praise_tread_object_type,:praise_or_tread + +end diff --git a/app/models/praise_tread_cache.rb b/app/models/praise_tread_cache.rb new file mode 100644 index 00000000..330e197c --- /dev/null +++ b/app/models/praise_tread_cache.rb @@ -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 diff --git a/app/models/user.rb b/app/models/user.rb index 6e720c06..8050082a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -103,7 +103,7 @@ class User < Principal acts_as_customizable ############################added by william acts_as_taggable - + seems_rateable ############################# added by liuping 关注 acts_as_watchable diff --git a/app/views/layouts/base_users.html.erb b/app/views/layouts/base_users.html.erb index 985fdc90..f6abf5e6 100644 --- a/app/views/layouts/base_users.html.erb +++ b/app/views/layouts/base_users.html.erb @@ -9,7 +9,11 @@ <%= favicon %> <%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2', 'application', :media => 'all' %> <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %> + + <%= seems_rateable_stylesheet %> + <%= javascript_heads %> + <%= heads_for_theme %> <%= call_hook :view_layouts_base_html_head %> @@ -44,6 +48,11 @@ + +

+ <%= render :partial => "/praise_tread/praise_tread",:locals => {:obj => @user,:show_flag => false,:user_id => User.current.id}%> +
+
<%= l(:label_user_watcher) %> (<%= User.watched_by(@user.id).count %>)   <%= l(:label_user_fans) %> (<%= @user.watcher_users.count %>) diff --git a/app/views/praise_tread/_praise.html.erb b/app/views/praise_tread/_praise.html.erb new file mode 100644 index 00000000..e69de29b diff --git a/app/views/praise_tread/_praise_tread.html.erb b/app/views/praise_tread/_praise_tread.html.erb new file mode 100644 index 00000000..c579df7f --- /dev/null +++ b/app/views/praise_tread/_praise_tread.html.erb @@ -0,0 +1,17 @@ +
+ <% 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 %> +
+<% if show_flag %> +
+ <%= link_to image_tag("/images/tread.png"),:controller=>"praise_tread", + :action=>"tread_minus",:remote=>true,:obj => obj %>踩 +
+<% end %> diff --git a/app/views/praise_tread/_tread.html.erb b/app/views/praise_tread/_tread.html.erb new file mode 100644 index 00000000..e69de29b diff --git a/app/views/praise_tread/praise_minus.js.erb b/app/views/praise_tread/praise_minus.js.erb new file mode 100644 index 00000000..74f7d6bf --- /dev/null +++ b/app/views/praise_tread/praise_minus.js.erb @@ -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} +)%>'); diff --git a/app/views/praise_tread/praise_plus.js.erb b/app/views/praise_tread/praise_plus.js.erb new file mode 100644 index 00000000..74f7d6bf --- /dev/null +++ b/app/views/praise_tread/praise_plus.js.erb @@ -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} +)%>'); diff --git a/app/views/praise_tread/tread_minus.js.erb b/app/views/praise_tread/tread_minus.js.erb new file mode 100644 index 00000000..e69de29b diff --git a/config/initializers/seems_rateable.rb b/config/initializers/seems_rateable.rb new file mode 100644 index 00000000..2391948f --- /dev/null +++ b/config/initializers/seems_rateable.rb @@ -0,0 +1,4 @@ +#SeemsRateable engine Initializer + +#Configure owner class of the given ratings +SeemsRateable::Engine.config.owner_class = "User" diff --git a/config/routes.rb b/config/routes.rb index 59189de8..d43a594f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -17,11 +17,13 @@ RedmineApp::Application.routes.draw do resources :shares - - + get "tags/index" get "tags/show" + get "praise_tread/praise_plus" + get "praise_tread/praise_minus" + get "praise_tread/tread_minus" root :to => 'welcome#index', :as => 'home' @@ -95,7 +97,7 @@ RedmineApp::Application.routes.draw do match 'users/:id/memberships/:membership_id', :to => 'users#edit_membership', :via => :put, :as => 'user_membership' match 'users/:id/memberships/:membership_id', :to => 'users#destroy_membership', :via => :delete match 'users/:id/memberships', :to => 'users#edit_membership', :via => :post, :as => 'user_memberships' - ################# added by william + ################# added by william match 'users/tag_save', :to => 'users#tag_save', :via => :post, :as => 'tag' post 'watchers/watch', :to => 'watchers#watch', :as => 'watch' @@ -422,8 +424,12 @@ RedmineApp::Application.routes.draw do match 'bids/:id', :controller => 'bids', :action => 'show', :as => 'respond' + + ########### added by liuping match 'tags/add_tag',:to => 'tags#add_tag',:as=>"add_tag" match 'tags/delete_tag',:to => 'tags#delete_tag',:as=>"add_tag" + match 'parise_tread/praise_plus',:to => 'parise_tread#praise_plus',:as=>"praise" + match 'parise_tread/tread_minus',:to => 'parise_tread#tread_minus',:as=>"tread" end diff --git a/db/migrate/20130806083151_create_seems_rateable_rates.rb b/db/migrate/20130806083151_create_seems_rateable_rates.rb new file mode 100644 index 00000000..40303f11 --- /dev/null +++ b/db/migrate/20130806083151_create_seems_rateable_rates.rb @@ -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 diff --git a/db/migrate/20130806083152_create_seems_rateable_cached_ratings.rb b/db/migrate/20130806083152_create_seems_rateable_cached_ratings.rb new file mode 100644 index 00000000..1b91bdad --- /dev/null +++ b/db/migrate/20130806083152_create_seems_rateable_cached_ratings.rb @@ -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 diff --git a/db/migrate/20130807021235_create_praise_treads.rb b/db/migrate/20130807021235_create_praise_treads.rb new file mode 100644 index 00000000..a30c1152 --- /dev/null +++ b/db/migrate/20130807021235_create_praise_treads.rb @@ -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 diff --git a/db/migrate/20130807021309_create_praise_tread_caches.rb b/db/migrate/20130807021309_create_praise_tread_caches.rb new file mode 100644 index 00000000..423286f4 --- /dev/null +++ b/db/migrate/20130807021309_create_praise_tread_caches.rb @@ -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 diff --git a/db/schema.rb b/db/schema.rb index 34ef17bd..b0cf9265 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130805131602) do +ActiveRecord::Schema.define(:version => 20130807021309) do create_table "a_user_watchers", :force => true do |t| t.string "name" @@ -432,6 +432,24 @@ ActiveRecord::Schema.define(:version => 20130805131602) do t.string "salt", :null => false end + create_table "praise_tread_caches", :force => true do |t| + t.integer "object_id", :null => false + t.string "object_type" + t.integer "praise_num" + t.integer "tread_num" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "praise_treads", :force => true do |t| + t.integer "user_id", :null => false + t.integer "praise_tread_object_id" + t.string "praise_tread_object_type" + t.integer "praise_or_tread" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "project_tags", :force => true do |t| t.integer "project_id" t.integer "tag_id" @@ -507,6 +525,26 @@ ActiveRecord::Schema.define(:version => 20130805131602) do t.string "issues_visibility", :limit => 30, :default => "default", :null => false end + create_table "seems_rateable_cached_ratings", :force => true do |t| + t.integer "cacheable_id", :limit => 8 + t.string "cacheable_type" + t.float "avg", :null => false + t.integer "cnt", :null => false + t.string "dimension" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + create_table "seems_rateable_rates", :force => true do |t| + t.integer "rater_id", :limit => 8 + t.integer "rateable_id" + t.string "rateable_type" + t.float "stars", :null => false + t.string "dimension" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "settings", :force => true do |t| t.string "name", :default => "", :null => false t.text "value" @@ -522,6 +560,7 @@ ActiveRecord::Schema.define(:version => 20130805131602) do t.string "url" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false + t.integer "project_id" end create_table "students", :force => true do |t| diff --git a/lib/plugins/seems_rateable-master/.gitignore b/lib/plugins/seems_rateable-master/.gitignore new file mode 100644 index 00000000..f372f8d4 --- /dev/null +++ b/lib/plugins/seems_rateable-master/.gitignore @@ -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 diff --git a/lib/plugins/seems_rateable-master/Gemfile b/lib/plugins/seems_rateable-master/Gemfile new file mode 100644 index 00000000..a1025271 --- /dev/null +++ b/lib/plugins/seems_rateable-master/Gemfile @@ -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 + diff --git a/lib/plugins/seems_rateable-master/MIT-LICENSE b/lib/plugins/seems_rateable-master/MIT-LICENSE new file mode 100644 index 00000000..5146945d --- /dev/null +++ b/lib/plugins/seems_rateable-master/MIT-LICENSE @@ -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. diff --git a/lib/plugins/seems_rateable-master/README.md b/lib/plugins/seems_rateable-master/README.md new file mode 100644 index 00000000..3e78d025 --- /dev/null +++ b/lib/plugins/seems_rateable-master/README.md @@ -0,0 +1,111 @@ +# SeemsRateable + +Star rating gem for Rails application using jQuery plugin jRating + +## Demo + +Demo 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 seems_rateable to routes.rb file + +Include stylesheet adding <%= seems_rateable_stylesheet %> to your layout header + +Also make sure you have an existing current_user helper method + +Don't forget to run + + $ rake db:migrate + +To prepare model add seems_rateable to your rateable model file. You can also pass a hash of options to +customize the functionality + +
    +
  • :dimensionsArray of dimensions e.g :dimensions => [:quality, :quantity]
  • +
  • :allow_updateAllowing user to re-rate his own ratings, default set to false e.g :allow_update=> true
  • +
+ + class Post < ActiveRecord::Base + seems_rateable :allow_update => true, :dimensions => [:quality, :length] + end + +To access object's rates use rates 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 seems_rateable_rater 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 @user.ratings_given + +### Usage + +To display star rating use helper method rating_for 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 : +
    +
  • :dimensionThe dimension of the object
  • +
  • :staticSet to true to display static star rating, default false
  • +
  • :classClass of the div, default set to 'rateable'
  • +
  • :idID of the div e.g :id => "info", default nil
  • +
+ +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 diff --git a/lib/plugins/seems_rateable-master/Rakefile b/lib/plugins/seems_rateable-master/Rakefile new file mode 100644 index 00000000..c2ef0373 --- /dev/null +++ b/lib/plugins/seems_rateable-master/Rakefile @@ -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 + + + + diff --git a/lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/bg_jRatingInfos.png b/lib/plugins/seems_rateable-master/app/assets/images/seems_rateable/bg_jRatingInfos.png new file mode 100644 index 0000000000000000000000000000000000000000..af5bf556e15c884bc24c0ab00537123d373bbcc9 GIT binary patch literal 572 zcmV-C0>k}@P)V!IyWhyxINQN4+b0cZ8;>G@@1HMkvg zce~v)5k0ukBUEc;t(9r%=82o%)PBEzW#-ZNee4$xhXYkr1yWbm>2#vS&w{4YX-{kY zXpG6T{!M=~yc-r}S<>ulM(g#O+#+aQSy`4*r_-UjuKlR$YO5%tV)MJ9`S;v|Zf-Ar7!Jz)q)Zz>W|5 z?AaV(GpIy^O`fmfplXJ)qtX_Gc%MO5w~LuWhY{7 zFKL94MxvG#zCLGj$k_~K#}l@7J{{5sA^N3BBe0P~Hgf5WV1taSZ0i$=l1So}jp!Fo z+4juWM?XR%D!Q_L^h1j^(FRHK@_+z41#ChZNuEYHbp7DjNjw`5H7Ooy@WMBpXE*0Y zIA<46n83R| zHOo0aU28g3NBL_ap8eurclA-hC)PLWUpbaRcbdp3*ocB+SQLkB+eWNX45a&|L zu&Sy#RGvCc7gK8W5i+F^S+rcLv1N5mEXz8!ZTo=Lb(JMact4pO?rDw2V8Ve6ytM1` zF2*=0isT`V<2y(=!p%U?uP;RU$ocL$A;bZ$%4@Bal;yh+VmKO&Z$US!i0iuhj9J$Z z7mP6iL=7pWswnc_U@*LCP1xTsm=H?Afe;N`fMmlQ3|cb4c1!;WbuB|HM>?Bi4 zQQNc?)dFe-Ef-bQX@N~}3lhHoY1iCvv!Ag`?hpcQxR?;)el&EOMn_dx$D(xoP}4Mt z+kDtfZO8U8kK3jj+e9O6M>>i9`kd#y@AEw8B>@IAz-TlY-wA?{&@}BcX28J?vdDGa zt#bLvubZ3YUnr%`Hwhl>?CktFKK?eNC{m|ZtNrxGz>klu)p~vJZ$;5E+3aw-RQjig zN-D1n4msR%x%9)t#JK2rj7TYwa=B5uwYBwa`1!TKbzQ&BF#et_D@n*s7>2OvbVh5n z4%Q9BSh@(D^xfilUcJ}rRc+he?{>S}&1Q4kvbvS2sdp5P6Xd?9Se7u3V;QAV+19kw zXfm11$+Db_Mx#S;(=gBTnRD>{{r&GD*b2|Hrteb|3oKPlu*swxjlx+5`U}$K_<2bgf>$Td-%3sNunVG3`T0p~}%gc9e&CY%x zh@z~7{Icwc9AY>DF5sU#6VVT?kJ$?ar!-(Vj-iIpY(IMRL@X3OzA1|0_bUMEm zi+8?RSeTatK}us&5}+~;;V|(?*i!?Y4pivj*8~-YX}0wT5B>9$VHC07)VQ#sI%;fa&9zVg{!%jz>&2j22y9zUu?{tLK4VvZAW0AI;BS z`C?x8NhA_;16?9qZ8Roo^E@X!vmQCGp#K~W*jBXd<1TDy4g~HvPDc6TbJmG}Z{X9~224yb7FuF<@Dij@uUc9GNnH5O^KKXdgU%x^1Bvf<7Zhqp=JQ zR6-lZ<1&vuHE{m!h2Td=<{?@uDstP^YQ4U@Tj_y-(R@B%K!(o4hiktH0@*+VlW+?! z$+Cn!M|wJ)zBuznqrPWZjpo|gV|Qd^;&MJea}^su1kYUP^=vk~h=z8*R4Ntk-Mhb$ zN=aSU<*Q?3WB(71=4Ey|CiOfaKQ}iwuW9NCL{m_96;_O%2tcW$C`xX6dOCf0IJ~*K ox_XyoyAI+m1Q(vi`&WPg02xTa4XYF-ng9R*07*qoM6N<$g5Sm5VgLXD literal 0 HcmV?d00001 diff --git a/lib/plugins/seems_rateable-master/app/assets/javascripts/seems_rateable/application.js b/lib/plugins/seems_rateable-master/app/assets/javascripts/seems_rateable/application.js new file mode 100644 index 00000000..806c8cc0 --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/assets/javascripts/seems_rateable/application.js @@ -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 . + + diff --git a/lib/plugins/seems_rateable-master/app/assets/stylesheets/seems_rateable/application.css b/lib/plugins/seems_rateable-master/app/assets/stylesheets/seems_rateable/application.css new file mode 100644 index 00000000..64099e54 --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/assets/stylesheets/seems_rateable/application.css @@ -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; +} diff --git a/lib/plugins/seems_rateable-master/app/controllers/seems_rateable/application_controller.rb b/lib/plugins/seems_rateable-master/app/controllers/seems_rateable/application_controller.rb new file mode 100644 index 00000000..cd94242d --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/controllers/seems_rateable/application_controller.rb @@ -0,0 +1,4 @@ +module SeemsRateable + class ApplicationController < ActionController::Base + end +end diff --git a/lib/plugins/seems_rateable-master/app/controllers/seems_rateable/ratings_controller.rb b/lib/plugins/seems_rateable-master/app/controllers/seems_rateable/ratings_controller.rb new file mode 100644 index 00000000..afc47d5e --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/controllers/seems_rateable/ratings_controller.rb @@ -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 diff --git a/lib/plugins/seems_rateable-master/app/helpers/seems_rateable/application_helper.rb b/lib/plugins/seems_rateable-master/app/helpers/seems_rateable/application_helper.rb new file mode 100644 index 00000000..596eeb4c --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/helpers/seems_rateable/application_helper.rb @@ -0,0 +1,4 @@ +module SeemsRateable + module ApplicationHelper + end +end diff --git a/lib/plugins/seems_rateable-master/app/helpers/seems_rateable/ratings_helper.rb b/lib/plugins/seems_rateable-master/app/helpers/seems_rateable/ratings_helper.rb new file mode 100644 index 00000000..91c7411c --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/helpers/seems_rateable/ratings_helper.rb @@ -0,0 +1,4 @@ +module SeemsRateable + module RatingsHelper + end +end diff --git a/lib/plugins/seems_rateable-master/app/models/seems_rateable/cached_rating.rb b/lib/plugins/seems_rateable-master/app/models/seems_rateable/cached_rating.rb new file mode 100644 index 00000000..fa56e7d4 --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/models/seems_rateable/cached_rating.rb @@ -0,0 +1,5 @@ +module SeemsRateable + class CachedRating < ActiveRecord::Base + belongs_to :cacheable, :polymorphic => true + end +end diff --git a/lib/plugins/seems_rateable-master/app/models/seems_rateable/rate.rb b/lib/plugins/seems_rateable-master/app/models/seems_rateable/rate.rb new file mode 100644 index 00000000..f9db806a --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/models/seems_rateable/rate.rb @@ -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 diff --git a/lib/plugins/seems_rateable-master/app/views/layouts/seems_rateable/application.html.erb b/lib/plugins/seems_rateable-master/app/views/layouts/seems_rateable/application.html.erb new file mode 100644 index 00000000..3c63ba43 --- /dev/null +++ b/lib/plugins/seems_rateable-master/app/views/layouts/seems_rateable/application.html.erb @@ -0,0 +1,14 @@ + + + + SeemsRateable + <%= stylesheet_link_tag "seems_rateable/application", media: "all" %> + <%= javascript_include_tag "seems_rateable/application" %> + <%= csrf_meta_tags %> + + + +<%= yield %> + + + diff --git a/lib/plugins/seems_rateable-master/bin/rails b/lib/plugins/seems_rateable-master/bin/rails new file mode 100644 index 00000000..773450b9 --- /dev/null +++ b/lib/plugins/seems_rateable-master/bin/rails @@ -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' diff --git a/lib/plugins/seems_rateable-master/config/routes.rb b/lib/plugins/seems_rateable-master/config/routes.rb new file mode 100644 index 00000000..4191f546 --- /dev/null +++ b/lib/plugins/seems_rateable-master/config/routes.rb @@ -0,0 +1,3 @@ +SeemsRateable::Engine.routes.draw do + resources :ratings, :only => :create +end diff --git a/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/install_generator.rb b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/install_generator.rb new file mode 100644 index 00000000..9a3aac0a --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/install_generator.rb @@ -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 diff --git a/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/cached_ratings_migration.rb b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/cached_ratings_migration.rb new file mode 100644 index 00000000..1b91bdad --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/cached_ratings_migration.rb @@ -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 diff --git a/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/initializer.rb b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/initializer.rb new file mode 100644 index 00000000..2391948f --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/initializer.rb @@ -0,0 +1,4 @@ +#SeemsRateable engine Initializer + +#Configure owner class of the given ratings +SeemsRateable::Engine.config.owner_class = "User" diff --git a/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/jRating.js.erb b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/jRating.js.erb new file mode 100644 index 00000000..4f43a4f9 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/jRating.js.erb @@ -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 = + $('
', + { + 'class' : 'jRatingColor', + css:{ + width:widthColor + } + }).appendTo($(this)), + + average = + $('
', + { + 'class' : 'jRatingAverage', + css:{ + width:0, + top:- starHeight + } + }).appendTo($(this)), + + jstar = + $('
', + { + '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 = + $('

',{ + 'class' : 'jRatingInfos', + html : getNote(relativeX)+' / '+opts.rateMax+'', + 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) +' / '+opts.rateMax+''); + }, + 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); diff --git a/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rateable.js.erb b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rateable.js.erb new file mode 100644 index 00000000..da6cc309 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rateable.js.erb @@ -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'); + $('Thanks for rating!').insertAfter(element) + }, + onError : function(element, rate) { + $('You have already rated!').insertAfter(element) + } + }); + +}); diff --git a/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rates_migration.rb b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rates_migration.rb new file mode 100644 index 00000000..40303f11 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/generators/seems_rateable/install/templates/rates_migration.rb @@ -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 diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable.rb new file mode 100644 index 00000000..5f84a8b8 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable.rb @@ -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 diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable/engine.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable/engine.rb new file mode 100644 index 00000000..185d6f9d --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable/engine.rb @@ -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 diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable/errors.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable/errors.rb new file mode 100644 index 00000000..8a83059b --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable/errors.rb @@ -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 diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable/helpers.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable/helpers.rb new file mode 100644 index 00000000..399f06fd --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable/helpers.rb @@ -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 diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable/model.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable/model.rb new file mode 100644 index 00000000..fa1b7af1 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable/model.rb @@ -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 diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable/routes.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable/routes.rb new file mode 100644 index 00000000..81a24333 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable/routes.rb @@ -0,0 +1,7 @@ +module SeemsRateable + module Routes + def seems_rateable + mount SeemsRateable::Engine => '/rateable', :as => :rateable + end + end +end diff --git a/lib/plugins/seems_rateable-master/lib/seems_rateable/version.rb b/lib/plugins/seems_rateable-master/lib/seems_rateable/version.rb new file mode 100644 index 00000000..180e4782 --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/seems_rateable/version.rb @@ -0,0 +1,3 @@ +module SeemsRateable + VERSION = "1.0.9" +end diff --git a/lib/plugins/seems_rateable-master/lib/tasks/seems_rateable_tasks.rake b/lib/plugins/seems_rateable-master/lib/tasks/seems_rateable_tasks.rake new file mode 100644 index 00000000..98c5403a --- /dev/null +++ b/lib/plugins/seems_rateable-master/lib/tasks/seems_rateable_tasks.rake @@ -0,0 +1,4 @@ +# desc "Explaining what the task does" +# task :seems_rateable do +# # Task goes here +# end diff --git a/lib/plugins/seems_rateable-master/seems_rateable.gemspec b/lib/plugins/seems_rateable-master/seems_rateable.gemspec new file mode 100644 index 00000000..b4f32a9f --- /dev/null +++ b/lib/plugins/seems_rateable-master/seems_rateable.gemspec @@ -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 diff --git a/public/images/praise.png b/public/images/praise.png new file mode 100644 index 0000000000000000000000000000000000000000..808d18134a663dc97c959bd531f0d0eb75e91a6d GIT binary patch literal 3103 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 literal 0 HcmV?d00001