mongoid-letsrate 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e3164c5447bfe42189414dd97891c3b98f577891
4
+ data.tar.gz: ca25dd5075b2ba8f5bd3a744ac5efbe7a0080c21
5
+ SHA512:
6
+ metadata.gz: c906be4921d5d101813607d7aab642403fd4f90b462ff72accaad7fd716333846465962c50fd2891a24d39cc17fdc83736201f1395ed6fd58774087546ebbef4
7
+ data.tar.gz: 40122580836e47c985ea7150b63fff1fa2e92ef337527f826b9bfbdd60d061a6e56221f17f581f30d84156d679238a5dbb201302b7260f3bec27f9cc84c62b0c
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .DS_Store
2
+ /pkg/*
3
+ .idea
4
+ .bundle
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.2"
4
+ - "1.9.3"
5
+ - "2.0.0"
6
+ - jruby-18mode
7
+ - jruby-19mode
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+ # uncomment this line if your project needs to run something other than `rake`:
11
+ # script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'jquery-rails'
4
+
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,53 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mongoid-letsrate (1.0.9)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ actionpack (4.0.2)
10
+ activesupport (= 4.0.2)
11
+ builder (~> 3.1.0)
12
+ erubis (~> 2.7.0)
13
+ rack (~> 1.5.2)
14
+ rack-test (~> 0.6.2)
15
+ activesupport (4.0.2)
16
+ i18n (~> 0.6, >= 0.6.4)
17
+ minitest (~> 4.2)
18
+ multi_json (~> 1.3)
19
+ thread_safe (~> 0.1)
20
+ tzinfo (~> 0.3.37)
21
+ atomic (1.1.14)
22
+ atomic (1.1.14-java)
23
+ builder (3.1.4)
24
+ erubis (2.7.0)
25
+ i18n (0.6.9)
26
+ jquery-rails (3.0.4)
27
+ railties (>= 3.0, < 5.0)
28
+ thor (>= 0.14, < 2.0)
29
+ minitest (4.7.5)
30
+ multi_json (1.8.2)
31
+ rack (1.5.2)
32
+ rack-test (0.6.2)
33
+ rack (>= 1.0)
34
+ railties (4.0.2)
35
+ actionpack (= 4.0.2)
36
+ activesupport (= 4.0.2)
37
+ rake (>= 0.8.7)
38
+ thor (>= 0.18.1, < 2.0)
39
+ rake (10.1.1)
40
+ thor (0.18.1)
41
+ thread_safe (0.1.3)
42
+ atomic
43
+ thread_safe (0.1.3-java)
44
+ atomic
45
+ tzinfo (0.3.38)
46
+
47
+ PLATFORMS
48
+ java
49
+ ruby
50
+
51
+ DEPENDENCIES
52
+ jquery-rails
53
+ mongoid-letsrate!
data/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # mongoid-letsrate Rating Gem
2
+
3
+ Provides the best way to add rating capabilites to your Rails application with jQuery Raty plugin.
4
+
5
+ ## Repository
6
+
7
+ [![Build Status](https://secure.travis-ci.org/malagant/mongoid-letsrate.png)](http://travis-ci.org/malagant/mongoid-letsrate)
8
+ [![Dependency Status](https://gemnasium.com/malagant/mongoid-letsrate.png)](https://gemnasium.com/malagant/mongoid-letsrate)
9
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/malagant/mongoid-letsrate)
10
+
11
+ Find it at [github.com/malagant/mongoid-letsrate](https://github.com/malagant/mongoid-letsrate)
12
+
13
+ ## Instructions
14
+
15
+ ### Install
16
+
17
+ Add the mongoid-letsrate gem into your Gemfile
18
+
19
+ ```ruby
20
+ gem 'mongoid-letsrate'
21
+ ```
22
+
23
+ ### Generate
24
+
25
+ ```
26
+ rails g mongoid-letsrate User
27
+ ```
28
+
29
+ The generator takes one argument which is the name of your existing devise user model UserModelName. This is necessary to bind the user and rating datas.
30
+ Also the generator copies necessary files (jquery raty plugin files, star icons and javascripts)
31
+
32
+ Example:
33
+
34
+ Suppose you will have a devise user model which name is User. The devise generator and mongoid-letsrate generator should be like below
35
+
36
+ ```
37
+ rails g devise:install
38
+ rails g devise user
39
+
40
+ rails g mongoid-letsrate user # => user is the model generated by devise
41
+ ```
42
+
43
+ This generator will create Rate and RatingCache models and link to your user model.
44
+
45
+ ### Prepare
46
+
47
+ I suppose you have a car model
48
+
49
+ ```
50
+ rails g model car name:string
51
+ ```
52
+
53
+ You should add the mongoid-letsrate_rateable function with its dimensions option. You can add multiple dimensions.
54
+
55
+ ```ruby
56
+ class Car < ActiveRecord::Base
57
+ mongoid-letsrate_rateable "speed", "engine", "price"
58
+ end
59
+ ```
60
+
61
+ Then you need to add a call mongoid-letsrate_rater in the user model.
62
+
63
+ ```ruby
64
+ class User < ActiveRecord::Base
65
+ mongoid-letsrate_rater
66
+ end
67
+ ```
68
+
69
+ ### Using
70
+
71
+ There is a helper method which name is rating_for to add the star links. By default rating_for will display the average rating and accept the
72
+ new rating value from authenticated user.
73
+
74
+ ```erb
75
+ <%# show.html.erb -> /cars/1 %>
76
+
77
+ Speed : <%= rating_for @car, "speed" %>
78
+ Engine : <%= rating_for @car, "engine" %>
79
+ Price : <%= rating_for @car, "price" %>
80
+ ```
81
+
82
+ If you need to change the star number, you should use star option like below.
83
+
84
+ ```erb
85
+ Speed : <%= rating_for @car, "speed", :star => 10 %>
86
+ Speed : <%= rating_for @car, "engine", :star => 7 %>
87
+ Speed : <%= rating_for @car, "price" %>
88
+ ```
89
+
90
+ You can use the rating_for_user helper method to show the star rating for the user.
91
+
92
+ ```erb
93
+ Speed : <%= rating_for_user @car, current_user, "speed", :star => 10 %>
94
+ ```
95
+
96
+
97
+ ## Feedback
98
+ If you find bugs please open a ticket at [https://github.com/malagant/mongoid-letsrate/issues](https://github.com/malagant/mongoid-letsrate/issues)
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ desc 'Bundle the gem'
4
+ task :bundle do
5
+ sh('bundle install')
6
+ sh 'gem build *.gemspec'
7
+ sh 'gem install *.gem'
8
+ sh 'rm *.gem'
9
+ end
10
+
11
+ task(:default).clear
12
+ task :default => :bundle
File without changes
@@ -0,0 +1,26 @@
1
+ class MongoidLetsrateGenerator < Rails::Generators::Base
2
+ source_root File.expand_path('../templates', __FILE__)
3
+
4
+ desc 'copying jquery.raty files to assets directory ...'
5
+ def copying
6
+ copy_file 'jquery.raty.js', 'app/assets/javascripts/jquery.raty.js'
7
+ copy_file 'star-on.png', 'app/assets/images/star-on.png'
8
+ copy_file 'star-off.png', 'app/assets/images/star-off.png'
9
+ copy_file 'star-half.png', 'app/assets/images/star-half.png'
10
+ copy_file 'letsrate.js.erb', 'app/assets/javascripts/letsrate.js.erb'
11
+ copy_file 'rater_controller.rb', 'app/controllers/rater_controller.rb'
12
+ end
13
+
14
+ desc 'model is being created...'
15
+ def create_model
16
+ model_file = File.join('app/models', "#{file_path}.rb")
17
+ raise "User model (#{model_file}) must exits." unless File.exists?(model_file)
18
+ class_collisions 'Rate'
19
+ template 'model.rb', File.join('app/models', 'rate.rb')
20
+ template 'cache_model.rb', File.join('app/models', 'rating_cache.rb')
21
+ end
22
+
23
+ def add_rate_path_to_route
24
+ route "post '/rate' => 'rater#create', :as => 'rate'"
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ class RatingCache
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+
5
+ field :avg, type: Float, null: false
6
+ field :qty, type: Integer, null: false
7
+ field :dimension, type: String
8
+
9
+ belongs_to :cacheable, :polymorphic => true
10
+ end
@@ -0,0 +1,464 @@
1
+ /*!
2
+ * jQuery Raty - A Star Rating Plugin - http://wbotelhos.com/raty
3
+ * -------------------------------------------------------------------
4
+ *
5
+ * jQuery Raty is a plugin that generates a customizable star rating.
6
+ *
7
+ * Licensed under The MIT License
8
+ *
9
+ * @version 2.4.5
10
+ * @since 2010.06.11
11
+ * @author Washington Botelho
12
+ * @documentation wbotelhos.com/raty
13
+ * @twitter twitter.com/wbotelhos
14
+ *
15
+ * Usage:
16
+ * -------------------------------------------------------------------
17
+ * $('#star').raty();
18
+ *
19
+ * <div id="star"></div>
20
+ *
21
+ */
22
+
23
+ ;(function($) {
24
+
25
+ var methods = {
26
+ init: function(settings) {
27
+ return this.each(function() {
28
+ var self = this,
29
+ $this = $(self).empty();
30
+
31
+ self.opt = $.extend(true, {}, $.fn.raty.defaults, settings);
32
+
33
+ $this.data('settings', self.opt);
34
+
35
+ if (typeof self.opt.number == 'function') {
36
+ self.opt.number = self.opt.number.call(self);
37
+ } else {
38
+ self.opt.number = methods.between(self.opt.number, 0, 20)
39
+ }
40
+
41
+ if (self.opt.path.substring(self.opt.path.length - 1, self.opt.path.length) != '/') {
42
+ self.opt.path += '/';
43
+ }
44
+
45
+ if (typeof self.opt.score == 'function') {
46
+ self.opt.score = self.opt.score.call(self);
47
+ }
48
+
49
+ if (self.opt.score) {
50
+ self.opt.score = methods.between(self.opt.score, 0, self.opt.number);
51
+ }
52
+
53
+ for (var i = 1; i <= self.opt.number; i++) {
54
+ $('<img />', {
55
+ src : self.opt.path + ((!self.opt.score || self.opt.score < i) ? self.opt.starOff : self.opt.starOn),
56
+ alt : i,
57
+ title : (i <= self.opt.hints.length && self.opt.hints[i - 1] !== null) ? self.opt.hints[i - 1] : i
58
+ }).appendTo(self);
59
+
60
+ if (self.opt.space) {
61
+ $this.append((i < self.opt.number) ? '&#160;' : '');
62
+ }
63
+ }
64
+
65
+ self.stars = $this.children('img:not(".raty-cancel")');
66
+ self.score = $('<input />', { type: 'hidden', name: self.opt.scoreName }).appendTo(self);
67
+
68
+ if (self.opt.score && self.opt.score > 0) {
69
+ self.score.val(self.opt.score);
70
+ methods.roundStar.call(self, self.opt.score);
71
+ }
72
+
73
+ if (self.opt.iconRange) {
74
+ methods.fill.call(self, self.opt.score);
75
+ }
76
+
77
+ methods.setTarget.call(self, self.opt.score, self.opt.targetKeep);
78
+
79
+ var space = self.opt.space ? 4 : 0,
80
+ width = self.opt.width || (self.opt.number * self.opt.size + self.opt.number * space);
81
+
82
+ if (self.opt.cancel) {
83
+ self.cancel = $('<img />', { src: self.opt.path + self.opt.cancelOff, alt: 'x', title: self.opt.cancelHint, 'class': 'raty-cancel' });
84
+
85
+ if (self.opt.cancelPlace == 'left') {
86
+ $this.prepend('&#160;').prepend(self.cancel);
87
+ } else {
88
+ $this.append('&#160;').append(self.cancel);
89
+ }
90
+
91
+ width += (self.opt.size + space);
92
+ }
93
+
94
+ if (self.opt.readOnly) {
95
+ methods.fixHint.call(self);
96
+
97
+ if (self.cancel) {
98
+ self.cancel.hide();
99
+ }
100
+ } else {
101
+ $this.css('cursor', 'pointer');
102
+
103
+ methods.bindAction.call(self);
104
+ }
105
+
106
+ $this.css('width', width);
107
+ });
108
+ }, between: function(value, min, max) {
109
+ return Math.min(Math.max(parseFloat(value), min), max);
110
+ }, bindAction: function() {
111
+ var self = this,
112
+ $this = $(self);
113
+
114
+ $this.mouseleave(function() {
115
+ var score = self.score.val() || undefined;
116
+
117
+ methods.initialize.call(self, score);
118
+ methods.setTarget.call(self, score, self.opt.targetKeep);
119
+
120
+ if (self.opt.mouseover) {
121
+ self.opt.mouseover.call(self, score);
122
+ }
123
+ });
124
+
125
+ var action = self.opt.half ? 'mousemove' : 'mouseover';
126
+
127
+ if (self.opt.cancel) {
128
+ self.cancel.mouseenter(function() {
129
+ $(this).attr('src', self.opt.path + self.opt.cancelOn);
130
+
131
+ self.stars.attr('src', self.opt.path + self.opt.starOff);
132
+
133
+ methods.setTarget.call(self, null, true);
134
+
135
+ if (self.opt.mouseover) {
136
+ self.opt.mouseover.call(self, null);
137
+ }
138
+ }).mouseleave(function() {
139
+ $(this).attr('src', self.opt.path + self.opt.cancelOff);
140
+
141
+ if (self.opt.mouseover) {
142
+ self.opt.mouseover.call(self, self.score.val() || null);
143
+ }
144
+ }).click(function(evt) {
145
+ self.score.removeAttr('value');
146
+
147
+ if (self.opt.click) {
148
+ self.opt.click.call(self, null, evt);
149
+ }
150
+ });
151
+ }
152
+
153
+ self.stars.bind(action, function(evt) {
154
+ var value = parseInt(this.alt, 10);
155
+
156
+ if (self.opt.half) {
157
+ var position = parseFloat((evt.pageX - $(this).offset().left) / self.opt.size),
158
+ diff = (position > .5) ? 1 : .5;
159
+
160
+ value = parseFloat(this.alt) - 1 + diff;
161
+
162
+ methods.fill.call(self, value);
163
+
164
+ if (self.opt.precision) {
165
+ value = value - diff + position;
166
+ }
167
+
168
+ methods.showHalf.call(self, value);
169
+ } else {
170
+ methods.fill.call(self, value);
171
+ }
172
+
173
+ $this.data('score', value);
174
+
175
+ methods.setTarget.call(self, value, true);
176
+
177
+ if (self.opt.mouseover) {
178
+ self.opt.mouseover.call(self, value, evt);
179
+ }
180
+ }).click(function(evt) {
181
+ self.score.val((self.opt.half || self.opt.precision) ? $this.data('score') : this.alt);
182
+
183
+ if (self.opt.click) {
184
+ self.opt.click.call(self, self.score.val(), evt);
185
+ }
186
+ });
187
+ }, cancel: function(isClick) {
188
+ return $(this).each(function() {
189
+ var self = this,
190
+ $this = $(self);
191
+
192
+ if ($this.data('readonly') === true) {
193
+ return this;
194
+ }
195
+
196
+ if (isClick) {
197
+ methods.click.call(self, null);
198
+ } else {
199
+ methods.score.call(self, null);
200
+ }
201
+
202
+ self.score.removeAttr('value');
203
+ });
204
+ }, click: function(score) {
205
+ return $(this).each(function() {
206
+ if ($(this).data('readonly') === true) {
207
+ return this;
208
+ }
209
+
210
+ methods.initialize.call(this, score);
211
+
212
+ if (this.opt.click) {
213
+ this.opt.click.call(this, score);
214
+ } else {
215
+ methods.error.call(this, 'you must add the "click: function(score, evt) { }" callback.');
216
+ }
217
+
218
+ methods.setTarget.call(this, score, true);
219
+ });
220
+ }, error: function(message) {
221
+ $(this).html(message);
222
+
223
+ $.error(message);
224
+ }, fill: function(score) {
225
+ var self = this,
226
+ number = self.stars.length,
227
+ count = 0,
228
+ $star ,
229
+ star ,
230
+ icon ;
231
+
232
+ for (var i = 1; i <= number; i++) {
233
+ $star = self.stars.eq(i - 1);
234
+
235
+ if (self.opt.iconRange && self.opt.iconRange.length > count) {
236
+ star = self.opt.iconRange[count];
237
+
238
+ if (self.opt.single) {
239
+ icon = (i == score) ? (star.on || self.opt.starOn) : (star.off || self.opt.starOff);
240
+ } else {
241
+ icon = (i <= score) ? (star.on || self.opt.starOn) : (star.off || self.opt.starOff);
242
+ }
243
+
244
+ if (i <= star.range) {
245
+ $star.attr('src', self.opt.path + icon);
246
+ }
247
+
248
+ if (i == star.range) {
249
+ count++;
250
+ }
251
+ } else {
252
+ if (self.opt.single) {
253
+ icon = (i == score) ? self.opt.starOn : self.opt.starOff;
254
+ } else {
255
+ icon = (i <= score) ? self.opt.starOn : self.opt.starOff;
256
+ }
257
+
258
+ $star.attr('src', self.opt.path + icon);
259
+ }
260
+ }
261
+ }, fixHint: function() {
262
+ var $this = $(this),
263
+ score = parseInt(this.score.val(), 10),
264
+ hint = this.opt.noRatedMsg;
265
+
266
+ if (!isNaN(score) && score > 0) {
267
+ hint = (score <= this.opt.hints.length && this.opt.hints[score - 1] !== null) ? this.opt.hints[score - 1] : score;
268
+ }
269
+
270
+ $this.data('readonly', true).css('cursor', 'default').attr('title', hint);
271
+
272
+ this.score.attr('readonly', 'readonly');
273
+ this.stars.attr('title', hint);
274
+ }, getScore: function() {
275
+ var score = [],
276
+ value ;
277
+
278
+ $(this).each(function() {
279
+ value = this.score.val();
280
+
281
+ score.push(value ? parseFloat(value) : undefined);
282
+ });
283
+
284
+ return (score.length > 1) ? score : score[0];
285
+ }, readOnly: function(isReadOnly) {
286
+ return this.each(function() {
287
+ var $this = $(this);
288
+
289
+ if ($this.data('readonly') === isReadOnly) {
290
+ return this;
291
+ }
292
+
293
+ if (this.cancel) {
294
+ if (isReadOnly) {
295
+ this.cancel.hide();
296
+ } else {
297
+ this.cancel.show();
298
+ }
299
+ }
300
+
301
+ if (isReadOnly) {
302
+ $this.unbind();
303
+
304
+ $this.children('img').unbind();
305
+
306
+ methods.fixHint.call(this);
307
+ } else {
308
+ methods.bindAction.call(this);
309
+ methods.unfixHint.call(this);
310
+ }
311
+
312
+ $this.data('readonly', isReadOnly);
313
+ });
314
+ }, reload: function() {
315
+ return methods.set.call(this, {});
316
+ }, roundStar: function(score) {
317
+ var diff = (score - Math.floor(score)).toFixed(2);
318
+
319
+ if (diff > this.opt.round.down) {
320
+ var icon = this.opt.starOn; // Full up: [x.76 .. x.99]
321
+
322
+ if (diff < this.opt.round.up && this.opt.halfShow) { // Half: [x.26 .. x.75]
323
+ icon = this.opt.starHalf;
324
+ } else if (diff < this.opt.round.full) { // Full down: [x.00 .. x.5]
325
+ icon = this.opt.starOff;
326
+ }
327
+
328
+ this.stars.eq(Math.ceil(score) - 1).attr('src', this.opt.path + icon);
329
+ } // Full down: [x.00 .. x.25]
330
+ }, score: function() {
331
+ return arguments.length ? methods.setScore.apply(this, arguments) : methods.getScore.call(this);
332
+ }, set: function(settings) {
333
+ this.each(function() {
334
+ var $this = $(this),
335
+ actual = $this.data('settings'),
336
+ clone = $this.clone().removeAttr('style').insertBefore($this);
337
+
338
+ $this.remove();
339
+
340
+ clone.raty($.extend(actual, settings));
341
+ });
342
+
343
+ return $(this.selector);
344
+ }, setScore: function(score) {
345
+ return $(this).each(function() {
346
+ if ($(this).data('readonly') === true) {
347
+ return this;
348
+ }
349
+
350
+ methods.initialize.call(this, score);
351
+ methods.setTarget.call(this, score, true);
352
+ });
353
+ }, setTarget: function(value, isKeep) {
354
+ if (this.opt.target) {
355
+ var $target = $(this.opt.target);
356
+
357
+ if ($target.length == 0) {
358
+ methods.error.call(this, 'target selector invalid or missing!');
359
+ }
360
+
361
+ var score = value;
362
+
363
+ if (!isKeep || score === undefined) {
364
+ score = this.opt.targetText;
365
+ } else {
366
+ if (this.opt.targetType == 'hint') {
367
+ score = (score === null && this.opt.cancel)
368
+ ? this.opt.cancelHint
369
+ : this.opt.hints[Math.ceil(score - 1)];
370
+ } else {
371
+ score = this.opt.precision
372
+ ? parseFloat(score).toFixed(1)
373
+ : score;
374
+ }
375
+ }
376
+
377
+ if (this.opt.targetFormat.indexOf('{score}') < 0) {
378
+ methods.error.call(this, 'template "{score}" missing!');
379
+ }
380
+
381
+ if (value !== null) {
382
+ score = this.opt.targetFormat.toString().replace('{score}', score);
383
+ }
384
+
385
+ if ($target.is(':input')) {
386
+ $target.val(score);
387
+ } else {
388
+ $target.html(score);
389
+ }
390
+ }
391
+ }, showHalf: function(score) {
392
+ var diff = (score - Math.floor(score)).toFixed(1);
393
+
394
+ if (diff > 0 && diff < .6) {
395
+ this.stars.eq(Math.ceil(score) - 1).attr('src', this.opt.path + this.opt.starHalf);
396
+ }
397
+ }, initialize: function(score) {
398
+ score = !score ? 0 : methods.between(score, 0, this.opt.number);
399
+
400
+ methods.fill.call(this, score);
401
+
402
+ if (score > 0) {
403
+ if (this.opt.halfShow) {
404
+ methods.roundStar.call(this, score);
405
+ }
406
+
407
+ this.score.val(score);
408
+ }
409
+ }, unfixHint: function() {
410
+ for (var i = 0; i < this.opt.number; i++) {
411
+ this.stars.eq(i).attr('title', (i < this.opt.hints.length && this.opt.hints[i] !== null) ? this.opt.hints[i] : i);
412
+ }
413
+
414
+ $(this).data('readonly', false).css('cursor', 'pointer').removeAttr('title');
415
+
416
+ this.score.attr('readonly', 'readonly');
417
+ }
418
+ };
419
+
420
+ $.fn.raty = function(method) {
421
+ if (methods[method]) {
422
+ return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
423
+ } else if (typeof method === 'object' || !method) {
424
+ return methods.init.apply(this, arguments);
425
+ } else {
426
+ $.error('Method ' + method + ' does not exist!');
427
+ }
428
+ };
429
+
430
+ $.fn.raty.defaults = {
431
+ cancel : false,
432
+ cancelHint : 'cancel this rating!',
433
+ cancelOff : 'cancel-off.png',
434
+ cancelOn : 'cancel-on.png',
435
+ cancelPlace : 'left',
436
+ click : undefined,
437
+ half : false,
438
+ halfShow : true,
439
+ hints : ['bad', 'poor', 'regular', 'good', 'gorgeous'],
440
+ iconRange : undefined,
441
+ mouseover : undefined,
442
+ noRatedMsg : 'not rated yet',
443
+ number : 5,
444
+ path : 'img/',
445
+ precision : false,
446
+ round : { down: .25, full: .6, up: .76 },
447
+ readOnly : false,
448
+ score : undefined,
449
+ scoreName : 'score',
450
+ single : false,
451
+ size : 16,
452
+ space : true,
453
+ starHalf : 'star-half.png',
454
+ starOff : 'star-off.png',
455
+ starOn : 'star-on.png',
456
+ target : undefined,
457
+ targetFormat : '{score}',
458
+ targetKeep : false,
459
+ targetText : '',
460
+ targetType : 'hint',
461
+ width : undefined
462
+ };
463
+
464
+ })(jQuery);
@@ -0,0 +1,36 @@
1
+ $.fn.raty.defaults.path = "/assets";
2
+ $.fn.raty.defaults.half_show = true;
3
+
4
+ $(function(){
5
+ $(".star").each(function() {
6
+ var $readonly = ($(this).attr('data-readonly') == 'true');
7
+ $(this).raty({
8
+ score: function(){
9
+ return $(this).attr('data-rating')
10
+ },
11
+ number: function() {
12
+ return $(this).attr('data-star-count')
13
+ },
14
+ readOnly: $readonly,
15
+ click: function(score, evt) {
16
+ var _this = this;
17
+ $.post('<%= Rails.application.class.routes.url_helpers.rate_path %>',
18
+ {
19
+ score: score,
20
+ dimension: $(this).attr('data-dimension'),
21
+ id: $(this).attr('data-id'),
22
+ klass: $(this).attr('data-classname')
23
+ },
24
+ function(data) {
25
+ if(data) {
26
+ // success code goes here ...
27
+
28
+ if ($(_this).attr('data-disable-after-rate') == 'true') {
29
+ $(_this).raty('set', { readOnly: true, score: score });
30
+ }
31
+ }
32
+ });
33
+ }
34
+ });
35
+ });
36
+ });
@@ -0,0 +1,20 @@
1
+ class CreateRates < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ create_table :rates do |t|
5
+ t.belongs_to :rater
6
+ t.belongs_to :rateable, :polymorphic => true
7
+ t.float :stars, :null => false
8
+ t.string :dimension
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :rates, :rater_id
13
+ add_index :rates, [:rateable_id, :rateable_type]
14
+ end
15
+
16
+ def self.down
17
+ drop_table :rates
18
+ end
19
+
20
+ end
@@ -0,0 +1,10 @@
1
+ class Rate
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+
5
+ field :stars, type: Float, null: false
6
+ field :dimension, type: String
7
+
8
+ belongs_to :rater, :class_name => '<%= file_name.classify %>'
9
+ belongs_to :rateable, :polymorphic => true
10
+ end
@@ -0,0 +1,12 @@
1
+ class RaterController < ApplicationController
2
+ def create
3
+ if user_signed_in?
4
+ obj = params[:klass].classify.constantize.find(params[:id])
5
+ obj.rate params[:score].to_i, current_user, params[:dimension]
6
+
7
+ render :json => true
8
+ else
9
+ render :json => false
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ require 'mongoid_letsrate/version'
2
+ require 'mongoid_letsrate/model'
3
+ require 'mongoid_letsrate/helpers'
4
+
5
+ module MongoidLetsrate
6
+
7
+ end
@@ -0,0 +1,45 @@
1
+ module Helpers
2
+ def rating_for(rateable_obj, dimension=nil, options={})
3
+
4
+ cached_average = rateable_obj.average dimension
5
+
6
+ avg = cached_average ? cached_average.avg : 0
7
+
8
+ star = options[:star] || 5
9
+
10
+ disable_after_rate = options[:disable_after_rate] || true
11
+
12
+ readonly = !(current_user && rateable_obj.can_rate?(current_user, dimension))
13
+
14
+ content_tag :div, '', "data-dimension" => dimension, :class => "star", "data-rating" => avg,
15
+ "data-id" => rateable_obj.id, "data-classname" => rateable_obj.class.name,
16
+ "data-disable-after-rate" => disable_after_rate,
17
+ "data-readonly" => readonly,
18
+ "data-star-count" => star
19
+ end
20
+
21
+ def rating_for_user(rateable_obj, rating_user, dimension = nil, options = {})
22
+ @object = rateable_obj
23
+ @user = rating_user
24
+ @rating = Rate.find_by_rater_id_and_rateable_id_and_dimension(@user.id, @object.id, dimension)
25
+ stars = @rating ? @rating.stars : 0
26
+
27
+ disable_after_rate = options[:disable_after_rate] || false
28
+
29
+ readonly=false
30
+ if disable_after_rate
31
+ readonly = current_user.present? ? !rateable_obj.can_rate?(current_user.id, dimension) : true
32
+ end
33
+
34
+ content_tag :div, '', "data-dimension" => dimension, :class => "star", "data-rating" => stars,
35
+ "data-id" => rateable_obj.id, "data-classname" => rateable_obj.class.name,
36
+ "data-disable-after-rate" => disable_after_rate,
37
+ "data-readonly" => readonly,
38
+ "data-star-count" => stars
39
+ end
40
+
41
+ end
42
+
43
+ class ActionView::Base
44
+ include Helpers
45
+ end
@@ -0,0 +1,109 @@
1
+ require 'active_support/concern'
2
+ module MongoidLetsrate
3
+ extend ActiveSupport::Concern
4
+
5
+ def rate(stars, user, dimension=nil, dirichlet_method=false)
6
+ dimension = nil if dimension.blank?
7
+
8
+ if can_rate? user, dimension
9
+ rates(dimension).create! do |r|
10
+ r.stars = stars
11
+ r.rater = user
12
+ end
13
+ if dirichlet_method
14
+ update_rate_average_dirichlet(stars, dimension)
15
+ else
16
+ update_rate_average(stars, dimension)
17
+ end
18
+ else
19
+ raise 'User has already rated.'
20
+ end
21
+ end
22
+
23
+ def update_rate_average_dirichlet(stars, dimension=nil)
24
+ ## assumes 5 possible vote categories
25
+ dp = {1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1}
26
+ stars_group = Hash[rates(dimension).group(:stars).count.map{|k,v| [k.to_i,v] }]
27
+ posterior = dp.merge(stars_group){|key, a, b| a + b}
28
+ sum = posterior.map{ |i, v| v }.inject { |a, b| a + b }
29
+ davg = posterior.map{ |i, v| i * v }.inject { |a, b| a + b }.to_f / sum
30
+
31
+ if average(dimension).nil?
32
+ RatingCache.create! do |avg|
33
+ avg.cacheable_id = self.id
34
+ avg.cacheable_type = self.class.name
35
+ avg.qty = 1
36
+ avg.avg = davg
37
+ avg.dimension = dimension
38
+ end
39
+ else
40
+ a = average(dimension)
41
+ a.qty = rates(dimension).count
42
+ a.avg = davg
43
+ a.save!(validate: false)
44
+ end
45
+ end
46
+
47
+ def update_rate_average(stars, dimension=nil)
48
+ if average(dimension).nil?
49
+ RatingCache.create! do |avg|
50
+ avg.cacheable_id = self.id
51
+ avg.cacheable_type = self.class.name
52
+ avg.avg = stars
53
+ avg.qty = 1
54
+ avg.dimension = dimension
55
+ end
56
+ else
57
+ a = average(dimension)
58
+ a.qty = rates(dimension).count
59
+ a.avg = rates(dimension).average(:stars)
60
+ a.save!(validate: false)
61
+ end
62
+ end
63
+
64
+ def average(dimension=nil)
65
+ dimension ? self.send("#{dimension}_average") : rate_average_without_dimension
66
+ end
67
+
68
+ def can_rate?(user, dimension=nil)
69
+ user.ratings_given.where(dimension: dimension, rateable_id: id, rateable_type: self.class.name).size.zero?
70
+ end
71
+
72
+ def rates(dimension=nil)
73
+ dimension ? self.send("#{dimension}_rates") : rates_without_dimension
74
+ end
75
+
76
+ def raters(dimension=nil)
77
+ dimension ? self.send("#{dimension}_raters") : raters_without_dimension
78
+ end
79
+
80
+ module ClassMethods
81
+
82
+ def letsrate_rater
83
+ has_many :ratings_given, :class_name => "Rate", :foreign_key => :rater_id
84
+ end
85
+
86
+ def letsrate_rateable(*dimensions)
87
+ has_many :rates_without_dimension, -> { where dimension: nil}, :as => :rateable, :class_name => "Rate", :dependent => :destroy
88
+ has_many :raters_without_dimension, :through => :rates_without_dimension, :source => :rater
89
+
90
+ has_one :rate_average_without_dimension, -> { where dimension: nil}, :as => :cacheable,
91
+ :class_name => "RatingCache", :dependent => :destroy
92
+
93
+
94
+ dimensions.each do |dimension|
95
+ has_many "#{dimension}_rates".to_sym, -> {where dimension: dimension.to_s},
96
+ :dependent => :destroy,
97
+ :class_name => "Rate",
98
+ :as => :rateable
99
+
100
+ has_many "#{dimension}_raters".to_sym, :through => "#{dimension}_rates", :source => :rater
101
+
102
+ has_one "#{dimension}_average".to_sym, -> { where dimension: dimension.to_s },
103
+ :as => :cacheable, :class_name => "RatingCache",
104
+ :dependent => :destroy
105
+ end
106
+ end
107
+ end
108
+
109
+ end
@@ -0,0 +1,3 @@
1
+ module MongoidLetsrate
2
+ VERSION = '1.0.9'
3
+ end
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'mongoid_letsrate/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'mongoid-letsrate'
7
+ s.version = MongoidLetsrate::VERSION
8
+ s.authors = ['Michael Johann','Murat GUZEL']
9
+ s.email = ['mjohann@rails-experts.com']
10
+ s.homepage = 'http://github.com/malagant/mongoid-letsrate'
11
+ s.summary = %q{Provides the best solution to add rating functionality to your models using mongoid.}
12
+ s.description = %q{Provides the best solution to add rating functionality to your models using mongoid.}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ['lib']
18
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid-letsrate
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.9
5
+ platform: ruby
6
+ authors:
7
+ - Michael Johann
8
+ - Murat GUZEL
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-02-14 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Provides the best solution to add rating functionality to your models using mongoid.
15
+ email:
16
+ - mjohann@rails-experts.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - .travis.yml
23
+ - Gemfile
24
+ - Gemfile.lock
25
+ - README.md
26
+ - Rakefile
27
+ - lib/generators/letsrate/USAGE
28
+ - lib/generators/letsrate/mongoid_letsrate_generator.rb
29
+ - lib/generators/letsrate/templates/cache_model.rb
30
+ - lib/generators/letsrate/templates/jquery.raty.js
31
+ - lib/generators/letsrate/templates/letsrate.js.erb
32
+ - lib/generators/letsrate/templates/migration.rb
33
+ - lib/generators/letsrate/templates/model.rb
34
+ - lib/generators/letsrate/templates/rater_controller.rb
35
+ - lib/generators/letsrate/templates/star-half.png
36
+ - lib/generators/letsrate/templates/star-off.png
37
+ - lib/generators/letsrate/templates/star-on.png
38
+ - lib/mongoid_letsrate.rb
39
+ - lib/mongoid_letsrate/helpers.rb
40
+ - lib/mongoid_letsrate/model.rb
41
+ - lib/mongoid_letsrate/version.rb
42
+ - mongoid-letsrate.gemspec
43
+ homepage: http://github.com/malagant/mongoid-letsrate
44
+ licenses: []
45
+ metadata: {}
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 2.1.9
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Provides the best solution to add rating functionality to your models using mongoid.
66
+ test_files: []