acts_as_starrable 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +46 -0
- data/app/assets/javascripts/application.js +15 -0
- data/app/assets/javascripts/jquery.rateit.min.js +2 -0
- data/app/assets/javascripts/starrable.js +35 -0
- data/app/assets/stylesheets/application.css +13 -0
- data/app/assets/stylesheets/rateit.css +98 -0
- data/app/controllers/application_controller.rb +5 -0
- data/app/controllers/ratings_controller.rb +15 -0
- data/app/helpers/application_helper.rb +5 -0
- data/app/views/layouts/application.html.erb +14 -0
- data/app/views/starrable/_stars.html.erb +6 -0
- data/config/routes.rb +4 -0
- data/lib/acts_as_starrable/engine.rb +13 -0
- data/lib/acts_as_starrable/rating.rb +10 -0
- data/lib/acts_as_starrable/starrable.rb +39 -0
- data/lib/acts_as_starrable/starrable_helper.rb +10 -0
- data/lib/acts_as_starrable/version.rb +4 -0
- data/lib/acts_as_starrable.rb +10 -0
- data/lib/generators/acts_as_starrable/migration/migration_generator.rb +40 -0
- data/lib/generators/acts_as_starrable/migration/templates/active_record/migration.rb +17 -0
- data/lib/tasks/acts_as_starrable_tasks.rake +4 -0
- data/spec/controllers/ratings_controller_spec.rb +35 -0
- data/spec/dummy/Gemfile +38 -0
- data/spec/dummy/Gemfile.lock +114 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/images/rails.png +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/flick.rb +4 -0
- data/spec/dummy/app/models/user.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +62 -0
- data/spec/dummy/config/boot.rb +6 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20140409035450_create_flicks.rb +9 -0
- data/spec/dummy/db/migrate/20140409041520_create_users.rb +9 -0
- data/spec/dummy/db/migrate/20140412132501_acts_as_starrable_migration.rb +16 -0
- data/spec/dummy/db/schema.rb +39 -0
- data/spec/dummy/db/seeds.rb +8 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/doc/README_FOR_APP +2 -0
- data/spec/dummy/log/development.log +158 -0
- data/spec/dummy/log/test.log +627 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/index.html +241 -0
- data/spec/dummy/public/robots.txt +5 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/tmp/cache/assets/CF0/DA0/sprockets%2Fd7d5b37686831d37c4dd75e645f5e016 +0 -0
- data/spec/dummy/tmp/cache/assets/E25/4C0/sprockets%2Fde2fd9fd11c04a582cdbbe3d84a35ae6 +0 -0
- data/spec/factories.rb +11 -0
- data/spec/models/flick_spec.rb +10 -0
- data/spec/models/user_spec.rb +10 -0
- data/spec/spec_helper.rb +30 -0
- metadata +301 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 Alexander Clark
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'rdoc/task'
|
10
|
+
rescue LoadError
|
11
|
+
require 'rdoc/rdoc'
|
12
|
+
require 'rake/rdoctask'
|
13
|
+
RDoc::Task = Rake::RDocTask
|
14
|
+
end
|
15
|
+
|
16
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'ActsAsStarrable'
|
19
|
+
rdoc.options << '--line-numbers'
|
20
|
+
rdoc.rdoc_files.include('README.rdoc')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
23
|
+
|
24
|
+
task :console do
|
25
|
+
require 'irb'
|
26
|
+
require 'irb/completion'
|
27
|
+
require 'acts_as_starrable'
|
28
|
+
ARGV.clear
|
29
|
+
IRB.start
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
APP_RAKEFILE = File.expand_path('../spec/dummy/Rakefile', __FILE__)
|
34
|
+
load 'rails/tasks/engine.rake'
|
35
|
+
|
36
|
+
Bundler::GemHelper.install_tasks
|
37
|
+
|
38
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
|
39
|
+
|
40
|
+
require 'rspec/core'
|
41
|
+
require 'rspec/core/rake_task'
|
42
|
+
|
43
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
44
|
+
RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare')
|
45
|
+
|
46
|
+
task default: :spec
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// the compiled file.
|
9
|
+
//
|
10
|
+
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
11
|
+
// GO AFTER THE REQUIRES BELOW.
|
12
|
+
//
|
13
|
+
//= require jquery
|
14
|
+
//= require jquery_ujs
|
15
|
+
//= require_tree .
|
@@ -0,0 +1,2 @@
|
|
1
|
+
(function(n){function t(n){var u=n.originalEvent.changedTouches,t=u[0],i="",r;switch(n.type){case"touchmove":i="mousemove";break;case"touchend":i="mouseup";break;default:return}r=document.createEvent("MouseEvent"),r.initMouseEvent(i,!0,!0,window,1,t.screenX,t.screenY,t.clientX,t.clientY,!1,!1,!1,!1,0,null),t.target.dispatchEvent(r),n.preventDefault()}n.rateit={aria:{resetLabel:"reset rating",ratingLabel:"rating"}},n.fn.rateit=function(i,r){var e=1,u={},o="init",s=function(n){return n.charAt(0).toUpperCase()+n.substr(1)},f;if(this.length==0)return this;if(f=n.type(i),f=="object"||i===undefined||i==null)u=n.extend({},n.fn.rateit.defaults,i);else{if(f=="string"&&r===undefined)return this.data("rateit"+s(i));f=="string"&&(o="setvalue")}return this.each(function(){var c=n(this),f=function(n,t){if(t!=null){var i="aria-value"+(n=="value"?"now":n),r=c.find(".rateit-range");r.attr(i)!=undefined&&r.attr(i,t)}return arguments[0]="rateit"+s(n),c.data.apply(c,arguments)},v,h,b,k,l,y,p,a;if(c.hasClass("rateit")||c.addClass("rateit"),v=c.css("direction")!="rtl",o=="setvalue"){if(!f("init"))throw"Can't set value before init";i!="readonly"||r!=!0||f("readonly")||(c.find(".rateit-range").unbind(),f("wired",!1)),i=="value"&&(r=r==null?f("min"):Math.max(f("min"),Math.min(f("max"),r))),f("backingfld")&&(h=n(f("backingfld")),i=="value"&&h.val(r),i=="min"&&h[0].min&&(h[0].min=r),i=="max"&&h[0].max&&(h[0].max=r),i=="step"&&h[0].step&&(h[0].step=r)),f(i,r)}f("init")||(f("min",f("min")||u.min),f("max",f("max")||u.max),f("step",f("step")||u.step),f("readonly",f("readonly")!==undefined?f("readonly"):u.readonly),f("resetable",f("resetable")!==undefined?f("resetable"):u.resetable),f("backingfld",f("backingfld")||u.backingfld),f("starwidth",f("starwidth")||u.starwidth),f("starheight",f("starheight")||u.starheight),f("value",Math.max(f("min"),Math.min(f("max"),f("value")||u.value||u.min))),f("ispreset",f("ispreset")!==undefined?f("ispreset"):u.ispreset),f("backingfld")&&(h=n(f("backingfld")),f("value",h.hide().val()),(h.attr("disabled")||h.attr("readonly"))&&f("readonly",!0),h[0].nodeName=="INPUT"&&(h[0].type=="range"||h[0].type=="text")&&(f("min",parseInt(h.attr("min"))||f("min")),f("max",parseInt(h.attr("max"))||f("max")),f("step",parseInt(h.attr("step"))||f("step"))),h[0].nodeName=="SELECT"&&h[0].options.length>1&&(f("min",Number(h[0].options[0].value)),f("max",Number(h[0].options[h[0].length-1].value)),f("step",Number(h[0].options[1].value)-Number(h[0].options[0].value)))),b=c[0].nodeName=="DIV"?"div":"span",e++,k='<button id="rateit-reset-{{index}}" class="rateit-reset" aria-label="'+n.rateit.aria.resetLabel+'" aria-controls="rateit-range-{{index}}"><\/button><{{element}} id="rateit-range-{{index}}" class="rateit-range" tabindex="0" role="slider" aria-label="'+n.rateit.aria.ratingLabel+'" aria-owns="rateit-reset-{{index}}" aria-valuemin="'+f("min")+'" aria-valuemax="'+f("max")+'" aria-valuenow="'+f("value")+'"><{{element}} class="rateit-selected" style="height:'+f("starheight")+'px"><\/{{element}}><{{element}} class="rateit-hover" style="height:'+f("starheight")+'px"><\/{{element}}><\/{{element}}>',c.append(k.replace(/{{index}}/gi,e).replace(/{{element}}/gi,b)),v||(c.find(".rateit-reset").css("float","right"),c.find(".rateit-selected").addClass("rateit-selected-rtl"),c.find(".rateit-hover").addClass("rateit-hover-rtl")),f("init",!0)),l=c.find(".rateit-range"),l.width(f("starwidth")*(f("max")-f("min"))).height(f("starheight")),y="rateit-preset"+(v?"":"-rtl"),f("ispreset")?c.find(".rateit-selected").addClass(y):c.find(".rateit-selected").removeClass(y),f("value")!=null&&(p=(f("value")-f("min"))*f("starwidth"),c.find(".rateit-selected").width(p)),a=c.find(".rateit-reset"),a.data("wired")!==!0&&a.bind("click",function(t){t.preventDefault(),a.blur(),f("value",f("min")),l.find(".rateit-hover").hide().width(0),l.find(".rateit-selected").width(0).show(),f("backingfld")&&n(f("backingfld")).val(f("min")),c.trigger("reset")}).data("wired",!0);var d=function(t,i){var u=i.changedTouches?i.changedTouches[0].pageX:i.pageX,r=u-n(t).offset().left;return v||(r=l.width()-r),r>l.width()&&(r=l.width()),r<0&&(r=0),p=Math.ceil(r/f("starwidth")*(1/f("step")))},g=function(n){var t=n*f("starwidth")*f("step"),r=l.find(".rateit-hover"),i;r.data("width")!=t&&(l.find(".rateit-selected").hide(),r.width(t).show().data("width",t),i=[n*f("step")+f("min")],c.trigger("hover",i).trigger("over",i))},w=function(t){f("value",t),f("backingfld")&&n(f("backingfld")).val(t),f("ispreset")&&(l.find(".rateit-selected").removeClass(y),f("ispreset",!1)),l.find(".rateit-hover").hide(),l.find(".rateit-selected").width(t*f("starwidth")-f("min")*f("starwidth")).show(),c.trigger("hover",[null]).trigger("over",[null]).trigger("rated",[t])};f("readonly")?a.hide():(f("resetable")||a.hide(),f("wired")||(l.bind("touchmove touchend",t),l.mousemove(function(n){var t=d(this,n);g(t)}),l.mouseleave(function(){l.find(".rateit-hover").hide().width(0).data("width",""),c.trigger("hover",[null]).trigger("over",[null]),l.find(".rateit-selected").show()}),l.mouseup(function(n){var t=d(this,n),i=t*f("step")+f("min");w(i)}),l.keyup(function(n){(n.which==38||n.which==(v?39:37))&&w(Math.min(f("value")+f("step"),f("max"))),(n.which==40||n.which==(v?37:39))&&w(Math.max(f("value")-f("step"),f("min")))}),f("wired",!0)),f("resetable")&&a.show()),l.attr("aria-readonly",f("readonly"))})},n.fn.rateit.defaults={min:0,max:5,step:.5,starwidth:16,starheight:16,readonly:!1,resetable:!0,ispreset:!1},n(function(){n("div.rateit, span.rateit").rateit()})})(jQuery);
|
2
|
+
//@ sourceMappingURL=jquery.rateit.min.js.map
|
@@ -0,0 +1,35 @@
|
|
1
|
+
$(document).ready(function() {
|
2
|
+
// $.getJSON('products/124.json', function(data) {
|
3
|
+
// $("div[data-productid=423]").attr('data-rateit-value', data.average_rating / 2);
|
4
|
+
// $("div[data-productid=423]").attr('data-rateit-ispreset', true);
|
5
|
+
// /*var items = [];
|
6
|
+
// $.each(data, function(key, val) {
|
7
|
+
// items.push('<li id=""' + key + '">' + val + '</li>');
|
8
|
+
// });*/
|
9
|
+
// });
|
10
|
+
|
11
|
+
$('.rateit').bind('rated reset', function (e) {
|
12
|
+
console.log("hello");
|
13
|
+
var ri = $(this);
|
14
|
+
|
15
|
+
//if the use pressed reset, it will get value: 0 (to be compatible with the HTML range control), we could check if e.type == 'reset', and then set the value to null .
|
16
|
+
var value = ri.rateit('value');
|
17
|
+
var rateableID = ri.data('rateableid'); // if the product id was in some hidden field: ri.closest('li').find('input[name="productid"]').val()
|
18
|
+
var stype = 'Gallery';
|
19
|
+
|
20
|
+
//maybe we want to disable voting?
|
21
|
+
//ri.rateit('readonly', true);
|
22
|
+
|
23
|
+
$.ajax({
|
24
|
+
url: '/starrable/ratings.json', //your server side script
|
25
|
+
data: { id: rateableID, value: value, stype: stype }, //our data
|
26
|
+
type: 'POST',
|
27
|
+
success: function (data) {
|
28
|
+
//$('#response').append('<li>' + data + '</li>');
|
29
|
+
},
|
30
|
+
error: function (jxhr, msg, err) {
|
31
|
+
//$('#response').append('<li style="color:red">' + msg + '</li>');
|
32
|
+
}
|
33
|
+
});
|
34
|
+
});
|
35
|
+
});
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree .
|
13
|
+
*/
|
@@ -0,0 +1,98 @@
|
|
1
|
+
.rateit {
|
2
|
+
display: -moz-inline-box;
|
3
|
+
display: inline-block;
|
4
|
+
position: relative;
|
5
|
+
-webkit-user-select: none;
|
6
|
+
-khtml-user-select: none;
|
7
|
+
-moz-user-select: none;
|
8
|
+
-o-user-select: none;
|
9
|
+
-ms-user-select: none;
|
10
|
+
user-select: none;
|
11
|
+
-webkit-touch-callout: none;
|
12
|
+
}
|
13
|
+
|
14
|
+
.rateit .rateit-range
|
15
|
+
{
|
16
|
+
position: relative;
|
17
|
+
display: -moz-inline-box;
|
18
|
+
display: inline-block;
|
19
|
+
background: url(star.gif);
|
20
|
+
height: 16px;
|
21
|
+
outline: none;
|
22
|
+
}
|
23
|
+
|
24
|
+
.rateit .rateit-range * {
|
25
|
+
display:block;
|
26
|
+
}
|
27
|
+
|
28
|
+
/* for IE 6 */
|
29
|
+
* html .rateit, * html .rateit .rateit-range
|
30
|
+
{
|
31
|
+
display: inline;
|
32
|
+
}
|
33
|
+
|
34
|
+
/* for IE 7 */
|
35
|
+
* + html .rateit, * + html .rateit .rateit-range
|
36
|
+
{
|
37
|
+
display: inline;
|
38
|
+
}
|
39
|
+
|
40
|
+
.rateit .rateit-hover, .rateit .rateit-selected
|
41
|
+
{
|
42
|
+
position: absolute;
|
43
|
+
left: 0px;
|
44
|
+
}
|
45
|
+
|
46
|
+
.rateit .rateit-hover-rtl, .rateit .rateit-selected-rtl
|
47
|
+
{
|
48
|
+
left: auto;
|
49
|
+
right: 0px;
|
50
|
+
}
|
51
|
+
|
52
|
+
.rateit .rateit-hover
|
53
|
+
{
|
54
|
+
background: url(star.gif) left -32px;
|
55
|
+
}
|
56
|
+
|
57
|
+
.rateit .rateit-hover-rtl
|
58
|
+
{
|
59
|
+
background-position: right -32px;
|
60
|
+
}
|
61
|
+
|
62
|
+
.rateit .rateit-selected
|
63
|
+
{
|
64
|
+
background: url(star.gif) left -16px;
|
65
|
+
}
|
66
|
+
|
67
|
+
.rateit .rateit-selected-rtl
|
68
|
+
{
|
69
|
+
background-position: right -16px;
|
70
|
+
}
|
71
|
+
|
72
|
+
.rateit .rateit-preset
|
73
|
+
{
|
74
|
+
background: url(star.gif) left -48px;
|
75
|
+
}
|
76
|
+
|
77
|
+
.rateit .rateit-preset-rtl
|
78
|
+
{
|
79
|
+
background: url(star.gif) left -48px;
|
80
|
+
}
|
81
|
+
|
82
|
+
.rateit button.rateit-reset
|
83
|
+
{
|
84
|
+
background: url(delete.gif) 0 0;
|
85
|
+
width: 16px;
|
86
|
+
height: 16px;
|
87
|
+
display: -moz-inline-box;
|
88
|
+
display: inline-block;
|
89
|
+
float: left;
|
90
|
+
outline: none;
|
91
|
+
border:none;
|
92
|
+
padding: 0;
|
93
|
+
}
|
94
|
+
|
95
|
+
.rateit button.rateit-reset:hover, .rateit button.rateit-reset:focus
|
96
|
+
{
|
97
|
+
background-position: 0 -16px;
|
98
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
class RatingsController < ApplicationController
|
3
|
+
respond_to :json
|
4
|
+
|
5
|
+
def create
|
6
|
+
@rating = ::ActsAsStarrable::Rating.new(:starrable_type => params[:stype],
|
7
|
+
:starrable_id => params[:id],
|
8
|
+
:rating => params[:value])
|
9
|
+
if @rating.save
|
10
|
+
render :json => @rating, :status => :created
|
11
|
+
else
|
12
|
+
render :json => @rating.errors, :status => :unprocessable_entity
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>ActsAsStarrable</title>
|
5
|
+
<%= stylesheet_link_tag "acts_as_starrable/application", :media => "all" %>
|
6
|
+
<%= javascript_include_tag "acts_as_starrable/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<div class="rateit" data-rateableid="<%= @gallery.id %>" data-rateit-value="<%= @gallery.average_rating %>" data-rateit-ispreset="true"></div>
|
2
|
+
<span itemprop="aggregateRating" itemscope itemtype="http://schema.org/AggregateRating">
|
3
|
+
<span itemprop="ratingValue"><%= starrable.average_rating %></span> stars out of
|
4
|
+
<span itemprop="bestRating"><%= starrable.max_rating %></span>
|
5
|
+
(<span itemprop="ratingCount"><%= pluralize(starrable.ratings.count, 'rating') %></span> total).
|
6
|
+
</span>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
module ActsAsStarrable
|
3
|
+
class Engine < ::Rails::Engine
|
4
|
+
# isolate_namespace ActsAsStarrable
|
5
|
+
|
6
|
+
config.generators do |g|
|
7
|
+
g.test_framework :rspec, :fixture => false
|
8
|
+
g.fixture_replacement :factory_girl, :dir => 'spec/factories'
|
9
|
+
g.assets false
|
10
|
+
g.helper false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
module ActsAsStarrable
|
3
|
+
class Rating < ::ActiveRecord::Base
|
4
|
+
attr_accessible :starrable_id, :starrable_type, :rater_id, :rater_type,
|
5
|
+
:rating
|
6
|
+
|
7
|
+
belongs_to :starrable, :polymorphic => true
|
8
|
+
belongs_to :rater, :polymorphic => true
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
module ActsAsStarrable
|
3
|
+
module Starrable
|
4
|
+
def starrable?
|
5
|
+
false
|
6
|
+
end
|
7
|
+
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def acts_as_starrable
|
15
|
+
has_many :ratings, :as => :starrable, :dependent => :destroy,
|
16
|
+
:class_name => 'ActsAsStarrable::Rating'
|
17
|
+
include ActsAsStarrable::Starrable::LocalInstanceMethods
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module LocalInstanceMethods
|
22
|
+
def starrable?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def average_rating
|
27
|
+
if avg = ratings.average('rating')
|
28
|
+
(avg * 2).round / 2.0 # round to nearest .5 or .0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def max_rating
|
33
|
+
5
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
ActiveRecord::Base.send :include, ActsAsStarrable::Starrable
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
require 'acts_as_starrable/engine'
|
3
|
+
require 'acts_as_starrable/starrable'
|
4
|
+
require 'acts_as_starrable/starrable_helper'
|
5
|
+
require 'acts_as_starrable/rating'
|
6
|
+
|
7
|
+
module ActsAsStarrable
|
8
|
+
end
|
9
|
+
|
10
|
+
ActionView::Base.send :include, ActsAsStarrable::StarrableHelper
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
require 'rails/generators'
|
3
|
+
require 'rails/generators/migration'
|
4
|
+
|
5
|
+
module ActsAsStarrable
|
6
|
+
class MigrationGenerator < Rails::Generators::Base
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
|
9
|
+
desc 'Generates migration for Rating model'
|
10
|
+
|
11
|
+
def self.orm
|
12
|
+
Rails::Generators.options[:rails][:orm]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.source_root
|
16
|
+
File.join(File.dirname(__FILE__), 'templates',
|
17
|
+
(orm.to_s unless orm.class.eql?(String)))
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.orm_has_migration?
|
21
|
+
[:active_record].include? orm
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.next_migration_number(dirname)
|
25
|
+
if ActiveRecord::Base.timestamped_migrations
|
26
|
+
migration_number = Time.now.utc.strftime('%Y%m%d%H%M%S').to_i
|
27
|
+
migration_number += 1
|
28
|
+
migration_number.to_s
|
29
|
+
else
|
30
|
+
'%.3d' % (current_migration_number(dirname) + 1)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_migration_file
|
35
|
+
if self.class.orm_has_migration?
|
36
|
+
migration_template 'migration.rb', 'db/migrate/acts_as_starrable_migration'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
class ActsAsStarrableMigration < ActiveRecord::Migration
|
3
|
+
def self.up
|
4
|
+
create_table :ratings do |t|
|
5
|
+
t.belongs_to :starrable, :polymorphic => true
|
6
|
+
t.belongs_to :rater, :polymorphic => true
|
7
|
+
t.integer :rating
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index :ratings, [:starrable_id, :starrable_type]
|
11
|
+
add_index :ratings, [:rater_id, :rater_type]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.down
|
15
|
+
drop_table :ratings
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RatingsController do
|
4
|
+
|
5
|
+
let(:instance) { mock_model(ActsAsStarrable::Rating) }
|
6
|
+
|
7
|
+
describe '#create' do
|
8
|
+
|
9
|
+
context 'success' do
|
10
|
+
|
11
|
+
before do
|
12
|
+
expect(ActsAsStarrable::Rating).to receive(:new).and_return(instance)
|
13
|
+
expect(instance).to receive(:save).and_return(true)
|
14
|
+
post :create, :format => 'json', :rating => { :id => '1',
|
15
|
+
:stype => 'Flick',
|
16
|
+
:value => '2.5'}
|
17
|
+
end
|
18
|
+
|
19
|
+
it { should respond_with 201 }
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'failure' do
|
23
|
+
|
24
|
+
before do
|
25
|
+
expect(ActsAsStarrable::Rating).to receive(:new).and_return(instance)
|
26
|
+
expect(instance).to receive(:save).and_return(false)
|
27
|
+
post :create, :format => 'json', :rating => { :id => '1',
|
28
|
+
:stype => 'Flick',
|
29
|
+
:value => '2.5'}
|
30
|
+
end
|
31
|
+
|
32
|
+
it { should respond_with 422 }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/spec/dummy/Gemfile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'rails', '3.2.12'
|
4
|
+
|
5
|
+
# Bundle edge Rails instead:
|
6
|
+
# gem 'rails', :git => 'git://github.com/rails/rails.git'
|
7
|
+
|
8
|
+
gem 'sqlite3'
|
9
|
+
gem 'acts_as_starrable'
|
10
|
+
|
11
|
+
# Gems used only for assets and not required
|
12
|
+
# in production environments by default.
|
13
|
+
group :assets do
|
14
|
+
gem 'sass-rails', '~> 3.2.3'
|
15
|
+
gem 'coffee-rails', '~> 3.2.1'
|
16
|
+
|
17
|
+
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
18
|
+
# gem 'therubyracer', :platforms => :ruby
|
19
|
+
|
20
|
+
gem 'uglifier', '>= 1.0.3'
|
21
|
+
end
|
22
|
+
|
23
|
+
gem 'jquery-rails'
|
24
|
+
|
25
|
+
# To use ActiveModel has_secure_password
|
26
|
+
# gem 'bcrypt-ruby', '~> 3.0.0'
|
27
|
+
|
28
|
+
# To use Jbuilder templates for JSON
|
29
|
+
# gem 'jbuilder'
|
30
|
+
|
31
|
+
# Use unicorn as the app server
|
32
|
+
# gem 'unicorn'
|
33
|
+
|
34
|
+
# Deploy with Capistrano
|
35
|
+
# gem 'capistrano'
|
36
|
+
|
37
|
+
# To use debugger
|
38
|
+
# gem 'debugger'
|
@@ -0,0 +1,114 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
actionmailer (3.2.12)
|
5
|
+
actionpack (= 3.2.12)
|
6
|
+
mail (~> 2.4.4)
|
7
|
+
actionpack (3.2.12)
|
8
|
+
activemodel (= 3.2.12)
|
9
|
+
activesupport (= 3.2.12)
|
10
|
+
builder (~> 3.0.0)
|
11
|
+
erubis (~> 2.7.0)
|
12
|
+
journey (~> 1.0.4)
|
13
|
+
rack (~> 1.4.5)
|
14
|
+
rack-cache (~> 1.2)
|
15
|
+
rack-test (~> 0.6.1)
|
16
|
+
sprockets (~> 2.2.1)
|
17
|
+
activemodel (3.2.12)
|
18
|
+
activesupport (= 3.2.12)
|
19
|
+
builder (~> 3.0.0)
|
20
|
+
activerecord (3.2.12)
|
21
|
+
activemodel (= 3.2.12)
|
22
|
+
activesupport (= 3.2.12)
|
23
|
+
arel (~> 3.0.2)
|
24
|
+
tzinfo (~> 0.3.29)
|
25
|
+
activeresource (3.2.12)
|
26
|
+
activemodel (= 3.2.12)
|
27
|
+
activesupport (= 3.2.12)
|
28
|
+
activesupport (3.2.12)
|
29
|
+
i18n (~> 0.6)
|
30
|
+
multi_json (~> 1.0)
|
31
|
+
acts_as_starrable (0.0.1)
|
32
|
+
rails (~> 3.2.12)
|
33
|
+
arel (3.0.3)
|
34
|
+
builder (3.0.4)
|
35
|
+
coffee-rails (3.2.2)
|
36
|
+
coffee-script (>= 2.2.0)
|
37
|
+
railties (~> 3.2.0)
|
38
|
+
coffee-script (2.2.0)
|
39
|
+
coffee-script-source
|
40
|
+
execjs
|
41
|
+
coffee-script-source (1.7.0)
|
42
|
+
erubis (2.7.0)
|
43
|
+
execjs (2.0.2)
|
44
|
+
hike (1.2.3)
|
45
|
+
i18n (0.6.9)
|
46
|
+
journey (1.0.4)
|
47
|
+
jquery-rails (3.1.0)
|
48
|
+
railties (>= 3.0, < 5.0)
|
49
|
+
thor (>= 0.14, < 2.0)
|
50
|
+
json (1.8.1)
|
51
|
+
mail (2.4.4)
|
52
|
+
i18n (>= 0.4.0)
|
53
|
+
mime-types (~> 1.16)
|
54
|
+
treetop (~> 1.4.8)
|
55
|
+
mime-types (1.25.1)
|
56
|
+
multi_json (1.8.4)
|
57
|
+
polyglot (0.3.3)
|
58
|
+
rack (1.4.5)
|
59
|
+
rack-cache (1.2)
|
60
|
+
rack (>= 0.4)
|
61
|
+
rack-ssl (1.3.3)
|
62
|
+
rack
|
63
|
+
rack-test (0.6.2)
|
64
|
+
rack (>= 1.0)
|
65
|
+
rails (3.2.12)
|
66
|
+
actionmailer (= 3.2.12)
|
67
|
+
actionpack (= 3.2.12)
|
68
|
+
activerecord (= 3.2.12)
|
69
|
+
activeresource (= 3.2.12)
|
70
|
+
activesupport (= 3.2.12)
|
71
|
+
bundler (~> 1.0)
|
72
|
+
railties (= 3.2.12)
|
73
|
+
railties (3.2.12)
|
74
|
+
actionpack (= 3.2.12)
|
75
|
+
activesupport (= 3.2.12)
|
76
|
+
rack-ssl (~> 1.3.2)
|
77
|
+
rake (>= 0.8.7)
|
78
|
+
rdoc (~> 3.4)
|
79
|
+
thor (>= 0.14.6, < 2.0)
|
80
|
+
rake (10.1.1)
|
81
|
+
rdoc (3.12.2)
|
82
|
+
json (~> 1.4)
|
83
|
+
sass (3.2.14)
|
84
|
+
sass-rails (3.2.6)
|
85
|
+
railties (~> 3.2.0)
|
86
|
+
sass (>= 3.1.10)
|
87
|
+
tilt (~> 1.3)
|
88
|
+
sprockets (2.2.2)
|
89
|
+
hike (~> 1.2)
|
90
|
+
multi_json (~> 1.0)
|
91
|
+
rack (~> 1.0)
|
92
|
+
tilt (~> 1.1, != 1.3.0)
|
93
|
+
sqlite3 (1.3.8)
|
94
|
+
thor (0.18.1)
|
95
|
+
tilt (1.4.1)
|
96
|
+
treetop (1.4.15)
|
97
|
+
polyglot
|
98
|
+
polyglot (>= 0.3.1)
|
99
|
+
tzinfo (0.3.38)
|
100
|
+
uglifier (2.4.0)
|
101
|
+
execjs (>= 0.3.0)
|
102
|
+
json (>= 1.8.0)
|
103
|
+
|
104
|
+
PLATFORMS
|
105
|
+
ruby
|
106
|
+
|
107
|
+
DEPENDENCIES
|
108
|
+
acts_as_starrable
|
109
|
+
coffee-rails (~> 3.2.1)
|
110
|
+
jquery-rails
|
111
|
+
rails (= 3.2.12)
|
112
|
+
sass-rails (~> 3.2.3)
|
113
|
+
sqlite3
|
114
|
+
uglifier (>= 1.0.3)
|