edgarjs-ajaxful_rating 2.1.0
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.
- data/CHANGELOG +2 -0
- data/Manifest +15 -0
- data/README.textile +223 -0
- data/Rakefile +14 -0
- data/ajaxful_rating.gemspec +30 -0
- data/generators/ajaxful_rating/USAGE +5 -0
- data/generators/ajaxful_rating/ajaxful_rating_generator.rb +30 -0
- data/generators/ajaxful_rating/templates/images/star.png +0 -0
- data/generators/ajaxful_rating/templates/images/star_small.png +0 -0
- data/generators/ajaxful_rating/templates/migration.rb +19 -0
- data/generators/ajaxful_rating/templates/model.rb +6 -0
- data/generators/ajaxful_rating/templates/style.css +84 -0
- data/init.rb +1 -0
- data/lib/ajaxful_rating.rb +2 -0
- data/lib/ajaxful_rating_helper.rb +174 -0
- data/lib/ajaxful_rating_model.rb +223 -0
- metadata +77 -0
data/CHANGELOG
ADDED
data/Manifest
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
generators/ajaxful_rating/ajaxful_rating_generator.rb
|
3
|
+
generators/ajaxful_rating/templates/images/star.png
|
4
|
+
generators/ajaxful_rating/templates/images/star_small.png
|
5
|
+
generators/ajaxful_rating/templates/migration.rb
|
6
|
+
generators/ajaxful_rating/templates/model.rb
|
7
|
+
generators/ajaxful_rating/templates/style.css
|
8
|
+
generators/ajaxful_rating/USAGE
|
9
|
+
init.rb
|
10
|
+
lib/ajaxful_rating.rb
|
11
|
+
lib/ajaxful_rating_helper.rb
|
12
|
+
lib/ajaxful_rating_model.rb
|
13
|
+
Manifest
|
14
|
+
Rakefile
|
15
|
+
README.textile
|
data/README.textile
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
h1. Ajaxful Rating
|
2
|
+
|
3
|
+
Provides a simple way to add rating functionality to your application.
|
4
|
+
|
5
|
+
h2. Repository
|
6
|
+
|
7
|
+
Find it at "github.com/edgarjs/ajaxful-rating":http://github.com/edgarjs/ajaxful-rating
|
8
|
+
|
9
|
+
h2. Demo
|
10
|
+
|
11
|
+
You can find a demo working app for this plugin at "http://github.com/edgarjs/ajaxful-rating_demo_app":http://github.com/edgarjs/ajaxful-rating_demo_app
|
12
|
+
Just migrate and run...
|
13
|
+
|
14
|
+
*******************************************************************
|
15
|
+
|
16
|
+
h2. Instructions
|
17
|
+
|
18
|
+
h3. Install
|
19
|
+
|
20
|
+
To install the plugin:
|
21
|
+
|
22
|
+
@script/plugin install git://github.com/edgarjs/ajaxful-rating.git@
|
23
|
+
|
24
|
+
Or the gem
|
25
|
+
|
26
|
+
@sudo gem install edgarjs-ajaxful_rating --source=http://gems.github.com@
|
27
|
+
|
28
|
+
You can configure it in your environment.rb file also:
|
29
|
+
|
30
|
+
@config.gem "edgarjs-ajaxful_rating", :lib => "ajaxful_rating", :source => "http://gems.github.com"@
|
31
|
+
|
32
|
+
h3. Generate
|
33
|
+
|
34
|
+
@script/generate ajaxful_rating UserModelName@
|
35
|
+
|
36
|
+
The generator takes one argument: UserModelName, which is the name of your *current*
|
37
|
+
user model. This is necessary to link both the rate and user models.
|
38
|
+
|
39
|
+
Also this generator copies the necesary images, styles, etc.
|
40
|
+
|
41
|
+
Example:
|
42
|
+
_I suppose you have generated already an authenticated model..._
|
43
|
+
|
44
|
+
<pre><code>
|
45
|
+
script/generate authenticated user sessions
|
46
|
+
script/generate ajaxful_rating user
|
47
|
+
</code></pre>
|
48
|
+
|
49
|
+
So this call will create a Rate model and will link it to your User model.
|
50
|
+
|
51
|
+
h3. Prepare
|
52
|
+
|
53
|
+
To let a model be rateable just add @ajaxful_rateable@. You can pass a hash of options to
|
54
|
+
customise this call:
|
55
|
+
* @:stars@ Max number of stars that can be submitted.
|
56
|
+
* @:allow_update@ Set to true if you want users to be able to update their votes.
|
57
|
+
* @:cache_column@ Name of the column for storing the cached rating average.
|
58
|
+
* @:dimensions@ Array of dimensions. Allows to rate the model on various specs,
|
59
|
+
like for example: a car could be rated for its speed, beauty or price.
|
60
|
+
|
61
|
+
<pre><code>
|
62
|
+
class Car < ActiveRecord::Base
|
63
|
+
ajaxful_rateable :stars => 10, :dimensions => [:speed, :beauty, :price]
|
64
|
+
end
|
65
|
+
</code></pre>
|
66
|
+
|
67
|
+
Then you need to add a call @ajaxful_rater@ in the user model. This includes a simple line so
|
68
|
+
you can add it _by your own_ as well (@has_many :rates@).
|
69
|
+
|
70
|
+
<pre><code>
|
71
|
+
class User < ActiveRecord::Base
|
72
|
+
ajaxful_rater
|
73
|
+
end
|
74
|
+
</code></pre>
|
75
|
+
|
76
|
+
Finally, as a mere recomendation to make it even easier, modify your routes to
|
77
|
+
map a rate action:
|
78
|
+
|
79
|
+
@map.resources :cars, :member => {:rate => :post}@
|
80
|
+
|
81
|
+
h3. Use it
|
82
|
+
|
83
|
+
To add the star links you need to call the helper method @ratings_for@.
|
84
|
+
It tries to call @current_user@ method as the rater instance. You can pass @:static@
|
85
|
+
as the second param to display only the static stars (not clickables).
|
86
|
+
And also you can pass the dimension you want to show the ratings for.
|
87
|
+
|
88
|
+
<pre><code>
|
89
|
+
#show.html.erb
|
90
|
+
<%= ratings_for @article %>
|
91
|
+
|
92
|
+
#To display static stars:
|
93
|
+
<%= ratings_for @article, :static %>
|
94
|
+
|
95
|
+
#To display the ratings for a dimension:
|
96
|
+
<%= ratings_for @article, :dimension => :speed %>
|
97
|
+
</code></pre>
|
98
|
+
|
99
|
+
Or you can specify a custom user instance by passing it as parameter.
|
100
|
+
|
101
|
+
<pre><code>
|
102
|
+
<%= ratings_for @article, @user %>
|
103
|
+
</code></pre>
|
104
|
+
|
105
|
+
There's a condition here, if you didn't add the route @rate@ to your resource
|
106
|
+
(as shown above) or you named it different, you'll need to pass the url to the
|
107
|
+
correct action in your controller:
|
108
|
+
|
109
|
+
<pre><code>
|
110
|
+
<%= ratings_for @article, :remote_options => {:url => your_rate_path(@article)} %>
|
111
|
+
</code></pre>
|
112
|
+
|
113
|
+
*To display the stars properly you need to add a call in the head of your layout, which will generate the
|
114
|
+
required CSS style for the list. Also don't forget to include the javascripts.*
|
115
|
+
|
116
|
+
<pre><code>
|
117
|
+
#within the head tags of your layout...
|
118
|
+
<%= javascript_include_tag :defaults %>
|
119
|
+
<%= ajaxful_rating_style %>
|
120
|
+
</code></pre>
|
121
|
+
|
122
|
+
When a user submits a rating it will call the action in your controller, for
|
123
|
+
example (if you added the @rate@ route):
|
124
|
+
|
125
|
+
<pre><code>
|
126
|
+
def rate
|
127
|
+
@article = Article.find(params[:id])
|
128
|
+
@article.rate(params[:stars], current_user, params[:dimension])
|
129
|
+
id = "ajaxful-rating-#{!params[:dimension].blank? ? "#{params[:dimension]}-" : ''}article-#{@article.id}"
|
130
|
+
render :update do |page|
|
131
|
+
page.replace_html id, ratings_for(@article, :wrap => false, :dimension => params[:dimension])
|
132
|
+
page.visual_effect :highlight, id
|
133
|
+
end
|
134
|
+
end
|
135
|
+
</code></pre>
|
136
|
+
|
137
|
+
There are some more options for this helper, please see the rdoc for details.
|
138
|
+
|
139
|
+
h3. Dimensions
|
140
|
+
|
141
|
+
From now on you can pass an optional parameter to the @rates@ method for your rateable object to retrieve the
|
142
|
+
corresponding rates for the dimension you want.
|
143
|
+
|
144
|
+
For example, you defined these dimensions:
|
145
|
+
|
146
|
+
<pre><code>
|
147
|
+
class Car < ActiveRecord::Base
|
148
|
+
ajaxful_rateable :dimensions => [:speed, :beauty, :price]
|
149
|
+
end
|
150
|
+
</code></pre>
|
151
|
+
|
152
|
+
And hence you can call @car.rates(:price)@ for the price rates or @car.rates(:speed)@ for the speed ratings and so on.
|
153
|
+
|
154
|
+
h3. Namespaces
|
155
|
+
|
156
|
+
If you use the plugin inside a namespace you’ll need to specify the rating url which should points to
|
157
|
+
a controller inside a namespace. Your files should be like these:
|
158
|
+
|
159
|
+
<pre><code>
|
160
|
+
routes.rb:
|
161
|
+
map.namespace :admin do |admin|
|
162
|
+
admin.resources :articles, :member => {:rate => :post}
|
163
|
+
end
|
164
|
+
|
165
|
+
views/admin/articles/show.html.erb
|
166
|
+
<%= ratings_for @article, :remote_options => {:url => rate_admin_article_path(@article)} %>
|
167
|
+
</code></pre>
|
168
|
+
|
169
|
+
h3. Cache
|
170
|
+
|
171
|
+
To cache the model's rating average add a column named @rating_average@ to your model table:
|
172
|
+
|
173
|
+
<pre><code>
|
174
|
+
class AddRatingAverageToArticles < ActiveRecord::Migration
|
175
|
+
def self.up
|
176
|
+
add_column :articles, :rating_average, :decimal, :default => 0
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.down
|
180
|
+
remove_column :articles, :rating_average
|
181
|
+
end
|
182
|
+
end
|
183
|
+
</code></pre>
|
184
|
+
|
185
|
+
If you want to customise the name of the cache column just pass it in the options hash:
|
186
|
+
|
187
|
+
<pre><code>
|
188
|
+
class Article < ActiveRecord::Base
|
189
|
+
ajaxful_rateable :cache_column => :my_cached_rating
|
190
|
+
end
|
191
|
+
</code></pre>
|
192
|
+
|
193
|
+
To use caching with dimensions, make sure you have a cache column defined for each dimension you want cached.
|
194
|
+
So if you want to cache the @spelling@ dimension, you’ll need to have a column called @rating_average_spelling@ on the articles table.
|
195
|
+
If you use a custom cache column name, follow the pattern @cache_column_name_dimension_name@ to add cache columns for dimensions.
|
196
|
+
|
197
|
+
h2. About backwards compatibility
|
198
|
+
|
199
|
+
*Version 2.0 of the plugin works only from Rails 2.2 and on. It uses the module @I18n@ which is new in rails 2.2. Please note that you can use it in past versions as long as you _customise_ the source code.*
|
200
|
+
|
201
|
+
I decided to jump directly to version 2.0 because there are many important changes. You can always
|
202
|
+
checkout the version 1.0 from the repository though.
|
203
|
+
|
204
|
+
h2. Feedback
|
205
|
+
|
206
|
+
If you find bugs please open a ticket at "http://github.com/edgarjs/ajaxful-rating/issues":http://github.com/edgarjs/ajaxful-rating/issues
|
207
|
+
|
208
|
+
I'll really appreciate your feedback, please contact me at e[at]dgar[dot]org
|
209
|
+
|
210
|
+
h2. Credits
|
211
|
+
|
212
|
+
The helper's style is from "komodomedia":http://www.komodomedia.com/blog/2007/01/css-star-rating-redux/ with author's permission.
|
213
|
+
|
214
|
+
If you need the psd files of the stars you can grab them "here":http://aws3-edgarjs-zips.s3.amazonaws.com/ajaxful_rating_stars.zip
|
215
|
+
|
216
|
+
Thanks to "bborn":http://github.com/bborn for the dimensions base implementation.
|
217
|
+
|
218
|
+
h2. License
|
219
|
+
|
220
|
+
This code is released under Creative Commons Attribution-Share Alike 3.0 license.
|
221
|
+
|
222
|
+
!http://i.creativecommons.org/l/by-sa/3.0/88x31.png!:http://creativecommons.org/licenses/by-sa/3.0/
|
223
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('ajaxful_rating', '2.1.0') do |p|
|
6
|
+
p.description = "Provides a simple way to add rating functionality to your application."
|
7
|
+
p.url = "http://github.com/edgarjs/ajaxful-rating"
|
8
|
+
p.author = "Edgar J. Suarez"
|
9
|
+
p.email = "e@dgar.org"
|
10
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
11
|
+
p.development_dependencies = []
|
12
|
+
end
|
13
|
+
|
14
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{ajaxful_rating}
|
5
|
+
s.version = "2.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Edgar J. Suarez"]
|
9
|
+
s.date = %q{2009-07-25}
|
10
|
+
s.description = %q{Provides a simple way to add rating functionality to your application.}
|
11
|
+
s.email = %q{e@dgar.org}
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "lib/ajaxful_rating.rb", "lib/ajaxful_rating_helper.rb", "lib/ajaxful_rating_model.rb", "README.textile"]
|
13
|
+
s.files = ["CHANGELOG", "generators/ajaxful_rating/ajaxful_rating_generator.rb", "generators/ajaxful_rating/templates/images/star.png", "generators/ajaxful_rating/templates/images/star_small.png", "generators/ajaxful_rating/templates/migration.rb", "generators/ajaxful_rating/templates/model.rb", "generators/ajaxful_rating/templates/style.css", "generators/ajaxful_rating/USAGE", "init.rb", "lib/ajaxful_rating.rb", "lib/ajaxful_rating_helper.rb", "lib/ajaxful_rating_model.rb", "Manifest", "Rakefile", "README.textile", "ajaxful_rating.gemspec"]
|
14
|
+
s.homepage = %q{http://github.com/edgarjs/ajaxful-rating}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Ajaxful_rating", "--main", "README.textile"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{ajaxful_rating}
|
18
|
+
s.rubygems_version = %q{1.3.5}
|
19
|
+
s.summary = %q{Provides a simple way to add rating functionality to your application.}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
26
|
+
else
|
27
|
+
end
|
28
|
+
else
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class AjaxfulRatingGenerator < Rails::Generator::NamedBase
|
2
|
+
def initialize(runtime_args, runtime_options = {})
|
3
|
+
super
|
4
|
+
|
5
|
+
# if there's no user model
|
6
|
+
model_file = File.join('app/models', "#{file_path}.rb")
|
7
|
+
raise "User model (#{model_file}) must exits." unless File.exists?(model_file)
|
8
|
+
end
|
9
|
+
|
10
|
+
def manifest
|
11
|
+
record do |m|
|
12
|
+
m.class_collisions 'Rate'
|
13
|
+
m.template 'model.rb', File.join('app/models', 'rate.rb')
|
14
|
+
m.migration_template 'migration.rb', 'db/migrate',
|
15
|
+
:migration_file_name => 'create_rates'
|
16
|
+
|
17
|
+
# style
|
18
|
+
m.directory 'public/images/ajaxful_rating'
|
19
|
+
m.file 'images/star.png', 'public/images/ajaxful_rating/star.png'
|
20
|
+
m.file 'images/star_small.png', 'public/images/ajaxful_rating/star_small.png'
|
21
|
+
m.file 'style.css', 'public/stylesheets/ajaxful_rating.css'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def banner
|
28
|
+
"Usage: #{$0} ajaxful_rating UserModelName"
|
29
|
+
end
|
30
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateRates < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :rates do |t|
|
4
|
+
t.references :<%= file_name %>
|
5
|
+
t.references :rateable, :polymorphic => true
|
6
|
+
t.integer :stars
|
7
|
+
t.string :dimension
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :rates, :<%= file_name %>_id
|
13
|
+
add_index :rates, :rateable_id
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.down
|
17
|
+
drop_table :rates
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
/*
|
2
|
+
* Style by Rogie http://www.komodomedia.com/blog/2007/01/css-star-rating-redux/
|
3
|
+
*/
|
4
|
+
|
5
|
+
.ajaxful-rating,
|
6
|
+
.ajaxful-rating a:hover,
|
7
|
+
.ajaxful-rating a:active,
|
8
|
+
.ajaxful-rating a:focus,
|
9
|
+
.ajaxful-rating .current-rating{
|
10
|
+
background: url(/images/ajaxful_rating/star.png) left -1000px repeat-x;
|
11
|
+
}
|
12
|
+
.ajaxful-rating{
|
13
|
+
position: relative;
|
14
|
+
/*width: 125px; this is setted dynamically */
|
15
|
+
height: 25px;
|
16
|
+
overflow: hidden;
|
17
|
+
list-style: none;
|
18
|
+
margin: 0;
|
19
|
+
padding: 0;
|
20
|
+
background-position: left top;
|
21
|
+
}
|
22
|
+
.ajaxful-rating li{ display: inline; }
|
23
|
+
.ajaxful-rating a,
|
24
|
+
.ajaxful-rating span,
|
25
|
+
.ajaxful-rating .current-rating{
|
26
|
+
position: absolute;
|
27
|
+
top: 0;
|
28
|
+
left: 0;
|
29
|
+
text-indent: -1000em;
|
30
|
+
height: 25px;
|
31
|
+
line-height: 25px;
|
32
|
+
outline: none;
|
33
|
+
overflow: hidden;
|
34
|
+
border: none;
|
35
|
+
}
|
36
|
+
.ajaxful-rating a:hover,
|
37
|
+
.ajaxful-rating a:active,
|
38
|
+
.ajaxful-rating a:focus{
|
39
|
+
background-position: left bottom;
|
40
|
+
}
|
41
|
+
|
42
|
+
/* This section is generated dynamically.
|
43
|
+
Just add a call to the helper method 'ajaxful_rating_style' within
|
44
|
+
the head tags in your main layout
|
45
|
+
.ajaxful-rating .stars-1{
|
46
|
+
width: 20%;
|
47
|
+
z-index: 6;
|
48
|
+
}
|
49
|
+
.ajaxful-rating .stars-2{
|
50
|
+
width: 40%;
|
51
|
+
z-index: 5;
|
52
|
+
}
|
53
|
+
.ajaxful-rating .stars-3{
|
54
|
+
width: 60%;
|
55
|
+
z-index: 4;
|
56
|
+
}
|
57
|
+
.ajaxful-rating .stars-4{
|
58
|
+
width: 80%;
|
59
|
+
z-index: 3;
|
60
|
+
}
|
61
|
+
.ajaxful-rating .stars-5{
|
62
|
+
width: 100%;
|
63
|
+
z-index: 2;
|
64
|
+
}
|
65
|
+
*/
|
66
|
+
.ajaxful-rating .current-rating{
|
67
|
+
z-index: 1;
|
68
|
+
background-position: left center;
|
69
|
+
}
|
70
|
+
|
71
|
+
/* smaller star */
|
72
|
+
.small-star{
|
73
|
+
/*width: 50px; this is setted dynamically */
|
74
|
+
height: 10px;
|
75
|
+
}
|
76
|
+
.small-star,
|
77
|
+
.small-star a:hover,
|
78
|
+
.small-star a:active,
|
79
|
+
.small-star a:focus,
|
80
|
+
.small-star .current-rating{
|
81
|
+
background-image: url(/images/ajaxful_rating/star_small.png);
|
82
|
+
line-height: 10px;
|
83
|
+
height: 10px;
|
84
|
+
}
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'ajaxful_rating'
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module AjaxfulRating # :nodoc:
|
2
|
+
module Helper
|
3
|
+
class MissingRateRoute < StandardError
|
4
|
+
def to_s
|
5
|
+
"Add :member => {:rate => :post} to your routes, or specify a custom url in the options."
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# Generates the stars list to submit a rate.
|
10
|
+
#
|
11
|
+
# It accepts the next options:
|
12
|
+
# * <tt>:class</tt> CSS class for the ul. Default is 'ajaxful-rating'.
|
13
|
+
# * <tt>:link_class_prefix</tt> Prefix for the li a CSS class. Default is 'stars'.
|
14
|
+
# * <tt>:small_stars</tt> Set this param to true to display smaller images. Default is false.
|
15
|
+
# * <tt>:small_star_class</tt> CSS class for the list when using small images. Default is 'small-stars'.
|
16
|
+
# * <tt>:html</tt> Hash of options to customise the ul tag.
|
17
|
+
# * <tt>:remote_options</tt> Hash of options for the link_to_remote function.
|
18
|
+
# Default is {:method => :post, :url => rate_rateablemodel_path(rateable)}.
|
19
|
+
# * <tt>:wrap</tt> Whether the star list is wrapped within a div tag or not. This is useful when page updating. Default is true.
|
20
|
+
#
|
21
|
+
# Example:
|
22
|
+
# <%= ratings_for @article %>
|
23
|
+
# # => Will produce something like:
|
24
|
+
# <ul class="ajaxful-rating">
|
25
|
+
# <li class="current-rating" style="width: 60%;">Currently 3/5 stars</li>
|
26
|
+
# <li><%= link_to_remote 1, :url => rate_article_path(@article, :stars => 1), :method => :post, :html => {:class => 'stars-1', :title => '1 star out of 5'} %></li>
|
27
|
+
# <li><%= link_to_remote 2, :url => rate_article_path(@article, :stars => 2), :method => :post, :html => {:class => 'stars-2', :title => '2 stars out of 5'} %></li>
|
28
|
+
# <li><%= link_to_remote 3, :url => rate_article_path(@article, :stars => 3), :method => :post, :html => {:class => 'stars-3', :title => '3 stars out of 5'} %></li>
|
29
|
+
# <li><%= link_to_remote 4, :url => rate_article_path(@article, :stars => 4), :method => :post, :html => {:class => 'stars-4', :title => '4 stars out of 5'} %></li>
|
30
|
+
# <li><%= link_to_remote 5, :url => rate_article_path(@article, :stars => 5), :method => :post, :html => {:class => 'stars-5', :title => '5 stars out of 5'} %></li>
|
31
|
+
# </ul>
|
32
|
+
#
|
33
|
+
# It will try to use the method <tt>current_user</tt> as the user instance. You can specify a custom instance in the second parameter
|
34
|
+
# or pass <tt>:static</tt> to leave the list of stars static.
|
35
|
+
#
|
36
|
+
# Example:
|
37
|
+
# <%= ratings_for @article, @user, :small_stars => true %>
|
38
|
+
# # => Will use @user instead <tt>current_user</tt>
|
39
|
+
#
|
40
|
+
# <%= ratings_for @article, :static, :small_stars => true %>
|
41
|
+
# # => Will produce a static list of stars showing the current rating average for @article.
|
42
|
+
#
|
43
|
+
# The user passed here will *not* be the one who submits the rate. It will be used only for the display behavior of the stars.
|
44
|
+
# Like for example, if there is a user logged in or if the current logged in user is able to submit a rate depending on the
|
45
|
+
# configuration (accepts update of rates, etc).
|
46
|
+
#
|
47
|
+
# So to actually set the user who will rate the model you need to do it in your controller:
|
48
|
+
#
|
49
|
+
# # controller
|
50
|
+
# def rate
|
51
|
+
# @article = Article.find(params[:id])
|
52
|
+
# @article.rate(params[:stars], current_user) # or any user instance
|
53
|
+
# # update page, etc.
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# I18n:
|
57
|
+
#
|
58
|
+
# You can translate the title of the images (the tool tip that shows when the mouse is over) and the 'Currently x/x stars'
|
59
|
+
# string by setting these keys on your translation hash:
|
60
|
+
#
|
61
|
+
# ajaxful_rating:
|
62
|
+
# stars:
|
63
|
+
# current_average: "Current rating: {{average}}/{{max}}"
|
64
|
+
# title:
|
65
|
+
# one: 1 star out of {{total}}
|
66
|
+
# other: "{{count}} stars out of {{total}}"
|
67
|
+
def ratings_for(rateable, *args)
|
68
|
+
user = extract_options(rateable, *args)
|
69
|
+
ajaxful_styles << %Q(
|
70
|
+
.#{options[:class]} { width: #{rateable.class.max_rate_value * 25}px; }
|
71
|
+
.#{options[:small_star_class]} { width: #{rateable.class.max_rate_value * 10}px; }
|
72
|
+
)
|
73
|
+
width = (rateable.rate_average(true, options[:dimension]) / rateable.class.max_rate_value.to_f) * 100
|
74
|
+
ul = content_tag(:ul, options[:html]) do
|
75
|
+
Range.new(1, rateable.class.max_rate_value).collect do |i|
|
76
|
+
build_star rateable, user, i
|
77
|
+
end.insert(0, content_tag(:li, current_average(rateable),
|
78
|
+
:class => 'current-rating', :style => "width:#{width}%"))
|
79
|
+
end
|
80
|
+
if options[:wrap]
|
81
|
+
content_tag(:div, ul, :id => "ajaxful-rating-#{!options[:dimension].blank? ?
|
82
|
+
"#{options[:dimension]}-" : ''}#{rateable.class.name.downcase}-#{rateable.id}")
|
83
|
+
else
|
84
|
+
ul
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Call this method <strong>within head tags</strong> of the main layout to yield the dynamic styles.
|
89
|
+
# It will include the necessary stlyesheet and output the dynamic CSS.
|
90
|
+
#
|
91
|
+
# Example:
|
92
|
+
# <head>
|
93
|
+
# <%= ajaxful_rating_style %>
|
94
|
+
# </head>
|
95
|
+
def ajaxful_rating_style
|
96
|
+
stylesheet_link_tag('ajaxful_rating') + content_tag(:style, ajaxful_styles,
|
97
|
+
:type => 'text/css') unless ajaxful_styles.blank?
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# Builds a star
|
103
|
+
def build_star(rateable, user, i)
|
104
|
+
a_class = "#{options[:link_class_prefix]}-#{i}"
|
105
|
+
ajaxful_styles << %Q(
|
106
|
+
.#{options[:class]} .#{a_class}{
|
107
|
+
width: #{(i / rateable.class.max_rate_value.to_f) * 100}%;
|
108
|
+
z-index: #{rateable.class.max_rate_value + 2 - i};
|
109
|
+
}
|
110
|
+
)
|
111
|
+
rated = rateable.rated_by?(user, options[:dimension]) if user
|
112
|
+
star = if user && ((rated && rateable.class.options[:allow_update]) || !rated)
|
113
|
+
link_to_remote(i, build_remote_options({:class => a_class, :title => pluralize_title(i, rateable.class.max_rate_value)}, i))
|
114
|
+
else
|
115
|
+
content_tag(:span, i, :class => a_class, :title => current_average(rateable))
|
116
|
+
end
|
117
|
+
content_tag(:li, star)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Default options for the helper.
|
121
|
+
def options
|
122
|
+
@options ||= {
|
123
|
+
:wrap => true,
|
124
|
+
:class => 'ajaxful-rating',
|
125
|
+
:link_class_prefix => :stars,
|
126
|
+
:small_stars => false,
|
127
|
+
:small_star_class => 'small-star',
|
128
|
+
:html => {},
|
129
|
+
:remote_options => {:method => :post}
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
# Builds the proper title for the star.
|
134
|
+
def pluralize_title(current, max)
|
135
|
+
(current == 1) ? I18n.t('ajaxful_rating.stars.title.one', :max => max, :default => "1 star out of {{max}}") :
|
136
|
+
I18n.t('ajaxful_rating.stars.title.other', :count => current, :max => max, :default => "{{count}} stars out of {{max}}")
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns the current average string.
|
140
|
+
def current_average(rateable)
|
141
|
+
I18n.t('ajaxful_rating.stars.current_average', :average => rateable.rate_average(true, options[:dimension]),
|
142
|
+
:max => rateable.class.max_rate_value, :default => "Current rating: {{average}}/{{max}}")
|
143
|
+
end
|
144
|
+
|
145
|
+
# Temporary instance to hold dynamic styles.
|
146
|
+
def ajaxful_styles
|
147
|
+
@ajaxful_styles ||= ''
|
148
|
+
end
|
149
|
+
|
150
|
+
# Builds the default options for the link_to_remote function.
|
151
|
+
def build_remote_options(html, i)
|
152
|
+
options[:remote_options].reverse_merge(:html => html).merge(
|
153
|
+
:url => "#{options[:remote_options][:url]}?#{{:stars => i, :dimension => options[:dimension]}.to_query}")
|
154
|
+
end
|
155
|
+
|
156
|
+
# Extracts the hash options and returns the user instance.
|
157
|
+
def extract_options(rateable, *args)
|
158
|
+
user = if args.first.class.name == rateable.class.user_class_name.classify
|
159
|
+
args.shift
|
160
|
+
elsif args.first != :static
|
161
|
+
current_user if respond_to?(:current_user)
|
162
|
+
end
|
163
|
+
options.merge!(args.last) if !args.empty? && args.last.is_a?(Hash)
|
164
|
+
options[:remote_options][:url] ||= respond_to?(url = "rate_#{rateable.class.name.downcase}_path") ?
|
165
|
+
send(url, rateable) : raise(MissingRateRoute)
|
166
|
+
options[:html].reverse_merge!(:class => "#{options[:class]} #{options[:small_star_class] if options[:small_stars]}")
|
167
|
+
user
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
class ActionView::Base
|
173
|
+
include AjaxfulRating::Helper
|
174
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
module AjaxfulRating # :nodoc:
|
2
|
+
class AlreadyRatedError < StandardError
|
3
|
+
def to_s
|
4
|
+
"Model has already been rated by this user. To allow update of ratings pass :allow_update => true to the ajaxful_rateable call."
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
attr_reader :options
|
14
|
+
|
15
|
+
# Extends the model to be easy ajaxly rateable.
|
16
|
+
#
|
17
|
+
# Options:
|
18
|
+
# * <tt>:stars</tt> Max number of stars that can be submitted.
|
19
|
+
# * <tt>:allow_update</tt> Set to true if you want users to be able to update their votes.
|
20
|
+
# * <tt>:cache_column</tt> Name of the column for storing the cached rating average.
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
# class Article < ActiveRecord::Base
|
24
|
+
# ajaxful_rateable :stars => 10, :cache_column => :custom_column
|
25
|
+
# end
|
26
|
+
def ajaxful_rateable(options = {})
|
27
|
+
has_many :rates_without_dimension, :as => :rateable, :class_name => 'Rate',
|
28
|
+
:dependent => :destroy, :conditions => {:dimension => nil}
|
29
|
+
|
30
|
+
|
31
|
+
options[:dimensions].each do |dimension|
|
32
|
+
has_many "#{dimension}_rates", :dependent => :destroy,
|
33
|
+
:conditions => {:dimension => dimension.to_s}, :class_name => 'Rate', :as => :rateable
|
34
|
+
end if options[:dimensions].is_a?(Array)
|
35
|
+
|
36
|
+
@options = options.reverse_merge(
|
37
|
+
:stars => 5,
|
38
|
+
:allow_update => true,
|
39
|
+
:cache_column => :rating_average
|
40
|
+
)
|
41
|
+
include AjaxfulRating::InstanceMethods
|
42
|
+
extend AjaxfulRating::SingletonMethods
|
43
|
+
end
|
44
|
+
|
45
|
+
# Makes the association between user and Rate model.
|
46
|
+
def ajaxful_rater(options = {})
|
47
|
+
has_many :rates, options
|
48
|
+
end
|
49
|
+
|
50
|
+
# Maximum value accepted when rating the model. Default is 5.
|
51
|
+
#
|
52
|
+
# Change it by passing the :stars option to +ajaxful_rateable+
|
53
|
+
#
|
54
|
+
# ajaxful_rateable :stars => 10
|
55
|
+
def max_rate_value
|
56
|
+
options[:stars]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Instance methods for the rateable object.
|
61
|
+
module InstanceMethods
|
62
|
+
|
63
|
+
# Submits a new rate. Accepts a hash of tipical Ajax request.
|
64
|
+
#
|
65
|
+
# Example:
|
66
|
+
# # Articles Controller
|
67
|
+
# def rate
|
68
|
+
# @article = Article.find(params[:id])
|
69
|
+
# @article.rate(params[:stars], current_user, params[:dimension])
|
70
|
+
# # some page update here ...
|
71
|
+
# end
|
72
|
+
def rate(stars, user, dimension = nil)
|
73
|
+
return false if (stars.to_i > self.class.max_rate_value)
|
74
|
+
raise AlreadyRatedError if (!self.class.options[:allow_update] && rated_by?(user, dimension))
|
75
|
+
|
76
|
+
rate = (self.class.options[:allow_update] && rated_by?(user, dimension)) ?
|
77
|
+
rate_by(user, dimension) : rates(dimension).build
|
78
|
+
rate.stars = stars
|
79
|
+
if user.respond_to?(:rates)
|
80
|
+
user.rates << rate
|
81
|
+
else
|
82
|
+
rate.send "#{self.class.user_class_name}_id=", user.id
|
83
|
+
end if rate.new_record?
|
84
|
+
rate.save!
|
85
|
+
self.update_cached_average(dimension)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns an array with all users that have rated this object.
|
89
|
+
def raters
|
90
|
+
eval(self.class.user_class_name.classify).find_by_sql(
|
91
|
+
["SELECT DISTINCT u.* FROM #{self.class.user_class_name.pluralize} u INNER JOIN rates r ON " +
|
92
|
+
"u.[id] = r.[#{self.class.user_class_name}_id] WHERE r.[rateable_id] = ? AND r.[rateable_type] = ?",
|
93
|
+
id, self.class.name]
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Finds the rate made by the user if he/she has already voted.
|
98
|
+
def rate_by(user, dimension = nil)
|
99
|
+
filter = "find_by_#{self.class.user_class_name}_id"
|
100
|
+
rates(dimension).send filter, user
|
101
|
+
end
|
102
|
+
|
103
|
+
# Return true if the user has rated the object, otherwise false
|
104
|
+
def rated_by?(user, dimension = nil)
|
105
|
+
!rate_by(user, dimension).nil?
|
106
|
+
end
|
107
|
+
|
108
|
+
# Instance's total rates.
|
109
|
+
def total_rates(dimension = nil)
|
110
|
+
rates(dimension).size
|
111
|
+
end
|
112
|
+
|
113
|
+
# Total sum of the rates.
|
114
|
+
def rates_sum(dimension = nil)
|
115
|
+
rates(dimension).sum(:stars)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Rating average for the object.
|
119
|
+
#
|
120
|
+
# Pass false as param to force the calculation if you are caching it.
|
121
|
+
def rate_average(cached = true, dimension = nil)
|
122
|
+
avg = if cached && self.class.caching_average?(dimension)
|
123
|
+
send(caching_column_name(dimension)).to_f
|
124
|
+
else
|
125
|
+
self.rates_sum(dimension).to_f / self.total_rates(dimension).to_f
|
126
|
+
end
|
127
|
+
avg.nan? ? 0.0 : avg
|
128
|
+
end
|
129
|
+
|
130
|
+
# Overrides the default +rates+ method and returns the propper array
|
131
|
+
# for the dimension passed.
|
132
|
+
#
|
133
|
+
# It may works as an alias for +dimension_rates+ methods.
|
134
|
+
def rates(dimension = nil)
|
135
|
+
unless dimension.blank?
|
136
|
+
send("#{dimension}_rates")
|
137
|
+
else
|
138
|
+
rates_without_dimension
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns the name of the cache column for the passed dimension.
|
143
|
+
def caching_column_name(dimension = nil)
|
144
|
+
self.class.caching_column_name(dimension)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Updates the cached average column in the rateable model.
|
148
|
+
def update_cached_average(dimension = nil)
|
149
|
+
if self.class.caching_average?(dimension)
|
150
|
+
rates(:refresh).size if self.respond_to?(:rates_count)
|
151
|
+
send("#{caching_column_name(dimension)}=", self.rate_average(false, dimension))
|
152
|
+
save!
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
module SingletonMethods
|
158
|
+
|
159
|
+
# Name of the class for the user model.
|
160
|
+
def user_class_name
|
161
|
+
@@user_class_name ||= Rate.column_names.find do |c|
|
162
|
+
u = c.scan(/(\w+)_id$/).flatten.first
|
163
|
+
break u if u && u != 'rateable'
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Finds all rateable objects rated by the +user+.
|
168
|
+
def find_rated_by(user)
|
169
|
+
find_statement(:user_id, user.id)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Finds all rateable objects rated with +stars+.
|
173
|
+
def find_rated_with(stars)
|
174
|
+
find_statement(:stars, stars)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Finds the rateable object with the highest rate average.
|
178
|
+
def find_most_popular(dimension = nil)
|
179
|
+
all.sort_by { |o| o.rate_average(true, dimension) }.last
|
180
|
+
end
|
181
|
+
|
182
|
+
# Finds the rateable object with the lowest rate average.
|
183
|
+
def find_less_popular(dimension = nil)
|
184
|
+
all.sort_by { |o| o.rate_average(true, dimension) }.first
|
185
|
+
end
|
186
|
+
|
187
|
+
# Finds rateable objects by Rate's attribute.
|
188
|
+
def find_statement(attr_name, attr_value)
|
189
|
+
rateable = self.base_class.name
|
190
|
+
sql = sanitize_sql(["SELECT DISTINCT r2.* FROM rates r1 INNER JOIN " +
|
191
|
+
"#{rateable.constantize.table_name} r2 ON r1.rateable_id = r2.id " +
|
192
|
+
"WHERE (r1.[rateable_type] = ? AND r1.[#{attr_name}] = ?)",
|
193
|
+
rateable, attr_value])
|
194
|
+
find_by_sql(sql)
|
195
|
+
end
|
196
|
+
|
197
|
+
# Indicates if the rateable model is able to cache the rate average.
|
198
|
+
#
|
199
|
+
# Include a column named +rating_average+ in your rateable model with
|
200
|
+
# default null, as decimal:
|
201
|
+
#
|
202
|
+
# t.decimal :rating_average, :precision => 3, :scale => 1, :default => 0
|
203
|
+
#
|
204
|
+
# To customize the name of the column specify the option <tt>:cache_column</tt> to ajaxful_rateable
|
205
|
+
#
|
206
|
+
# ajaxful_rateable :cache_column => :my_custom_column
|
207
|
+
#
|
208
|
+
def caching_average?(dimension = nil)
|
209
|
+
column_names.include?(caching_column_name(dimension))
|
210
|
+
end
|
211
|
+
|
212
|
+
# Returns the name of the cache column for the passed dimension.
|
213
|
+
def caching_column_name(dimension = nil)
|
214
|
+
name = options[:cache_column].to_s
|
215
|
+
name += "_#{dimension.to_s.underscore}" unless dimension.blank?
|
216
|
+
name
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
class ActiveRecord::Base
|
222
|
+
include AjaxfulRating
|
223
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: edgarjs-ajaxful_rating
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Edgar J. Suarez
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-25 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Provides a simple way to add rating functionality to your application.
|
17
|
+
email: e@dgar.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- CHANGELOG
|
24
|
+
- lib/ajaxful_rating.rb
|
25
|
+
- lib/ajaxful_rating_helper.rb
|
26
|
+
- lib/ajaxful_rating_model.rb
|
27
|
+
- README.textile
|
28
|
+
files:
|
29
|
+
- CHANGELOG
|
30
|
+
- generators/ajaxful_rating/ajaxful_rating_generator.rb
|
31
|
+
- generators/ajaxful_rating/templates/images/star.png
|
32
|
+
- generators/ajaxful_rating/templates/images/star_small.png
|
33
|
+
- generators/ajaxful_rating/templates/migration.rb
|
34
|
+
- generators/ajaxful_rating/templates/model.rb
|
35
|
+
- generators/ajaxful_rating/templates/style.css
|
36
|
+
- generators/ajaxful_rating/USAGE
|
37
|
+
- init.rb
|
38
|
+
- lib/ajaxful_rating.rb
|
39
|
+
- lib/ajaxful_rating_helper.rb
|
40
|
+
- lib/ajaxful_rating_model.rb
|
41
|
+
- Manifest
|
42
|
+
- Rakefile
|
43
|
+
- README.textile
|
44
|
+
- ajaxful_rating.gemspec
|
45
|
+
has_rdoc: false
|
46
|
+
homepage: http://github.com/edgarjs/ajaxful-rating
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options:
|
49
|
+
- --line-numbers
|
50
|
+
- --inline-source
|
51
|
+
- --title
|
52
|
+
- Ajaxful_rating
|
53
|
+
- --main
|
54
|
+
- README.textile
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "1.2"
|
68
|
+
version:
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
rubyforge_project: ajaxful_rating
|
72
|
+
rubygems_version: 1.2.0
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: Provides a simple way to add rating functionality to your application.
|
76
|
+
test_files: []
|
77
|
+
|