Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 401 lines (274 sloc) 14.285 kb
d721aa47 »
2009-09-09 README-updates.
1 h1. IS_REVIEWABLE ~ concept version ~
e4b4173e »
2009-09-06 Initial commit.
2
3 _Rails: Make an ActiveRecord resource ratable/reviewable (rating + comment), without the usual extra code-smell._
4
abf53d1f »
2009-09-06 Updated README a bit, even though this is work in progress...
5 h2. Why another rating-plugin?
6
7 Many reasons; I felt the existing plugins all suck in one way or another (sorry, no hating). This is why IsReviweable is better if you ask me:
8
9 * Don't do assumptions that your rater/reviewer model is @User@. Relying on polymorphic assocation completely, so your reviewer can be...@DonaldDuck@.
10 * ...and optionally even accepts an IP as rater/reviewer. Disabled by default though.
11 * Don't make assumptions about your Review model - you can extend it without meta-programming, good 'ol subclassing folks.
12 * Don't make assumptions about what rating scale you wanna have, how the rating scale should be divided, or average rating rounding precision. The 1-5 scale is 80% of the cases, but there's no real reason or overhead to support any scale. To sum it up: Scale can consist negative and/or positive range or explicit integer/float values...and you won't even notice the difference on the outside. See the examples! =)
13 * Possible to submit additional custom attributes while rating, such as @title@ and @body@ to make up a "review" instead of just a "rating". Feel free.
14 * Very sassy finders implemented using named scopes, i.e. less code smell.
d721aa47 »
2009-09-09 README-updates.
15 * No lame out-of-the-box views/javascripts/images that you probably won't use in the final product anyway. That should be an extension to the plugin.
abf53d1f »
2009-09-06 Updated README a bit, even though this is work in progress...
16 * Transparently supports column-caching expensive calculations for the reviewable model. Will simply be turned on if these fields exists - otherwise fallback with an optimized DB hit instead.
17 * If I can say it myself...this code should be very solid, optimized, and DRY. Shoot the messenger! o<;)
18
19 If you got suggestions how it could be even better, just hit me up. I appreciate critics for the cause of a rock solid plugin for rating/reviewing. Such a common "problem" as rating/reviewing should have had a great solution by now!
20
e4b4173e »
2009-09-06 Initial commit.
21 h2. Installation
22
d721aa47 »
2009-09-09 README-updates.
23 *Gem:*
abf53d1f »
2009-09-06 Updated README a bit, even though this is work in progress...
24
e4b4173e »
2009-09-06 Initial commit.
25 <pre>sudo gem install grimen-is_reviewable</pre>
26
d721aa47 »
2009-09-09 README-updates.
27 and in @config/environment.rb@:
28
29 <pre>config.gem 'grimen-is_reviewable', :lib => 'is_reviewable'</pre>
30
31 *Plugin:*
abf53d1f »
2009-09-06 Updated README a bit, even though this is work in progress...
32
33 <pre>./script/plugin install git://github.com/grimen/is_reviewable.git</pre>
34
e4b4173e »
2009-09-06 Initial commit.
35 h2. Usage
36
2e2767d5 »
2009-09-21 README-formatting.
37 h3. 1. Generate migration:
e4b4173e »
2009-09-06 Initial commit.
38
238e4b6e »
2009-09-06 Some error logging and notes.
39 <pre>$ ./script/generate is_reviewable_migration</pre>
e4b4173e »
2009-09-06 Initial commit.
40
41 Generates @db/migrations/{timestamp}_is_reviewable_migration@ with:
42
43 <pre>
44 class IsReviewableMigration < ActiveRecord::Migration
45 def self.up
46 create_table :reviews do |t|
47 t.references :reviewable, :polymorphic => true
10509147 »
2009-09-06 Version bump to 0.0.0
48
e4b4173e »
2009-09-06 Initial commit.
49 t.references :reviewer, :polymorphic => true
10509147 »
2009-09-06 Version bump to 0.0.0
50 t.string :ip, :limit => 24
e4b4173e »
2009-09-06 Initial commit.
51
10509147 »
2009-09-06 Version bump to 0.0.0
52 t.float :rating
419b71b3 »
2009-09-18 Now saves any additional custom attributes using the review! method. …
53 t.text :body
54
55 #
56 # Custom fields goes here...
57 #
58 # t.string :title
bc2d3a05 »
2009-09-18 Added support for initialization of multiple reviewer classes. Now Ac…
59 # t.string :mood
419b71b3 »
2009-09-18 Now saves any additional custom attributes using the review! method. …
60 # ...
61 #
e4b4173e »
2009-09-06 Initial commit.
62
63 t.timestamps
64 end
65
66 add_index :reviews, :reviewer_id
67 add_index :reviews, :reviewer_type
68 add_index :reviews, [:reviewer_id, :reviewer_type]
69 add_index :reviews, [:reviewable_id, :reviewable_type]
70 end
71
72 def self.down
73 drop_table :reviews
74 end
75 end
76 </pre>
77
2e2767d5 »
2009-09-21 README-formatting.
78 h3. 2. Make your model reviewable:
e4b4173e »
2009-09-06 Initial commit.
79
80 <pre>
81 class Post < ActiveRecord::Base
82 is_reviewable :scale => 0..5
83 end
84 </pre>
85
bc2d3a05 »
2009-09-18 Added support for initialization of multiple reviewer classes. Now Ac…
86 or, with explicit reviewer (or reviewers):
e4b4173e »
2009-09-06 Initial commit.
87
88 <pre>
89 class Book < ActiveRecord::Base
bc2d3a05 »
2009-09-18 Added support for initialization of multiple reviewer classes. Now Ac…
90 # Setup associations for the reviewer class(es) automatically, and specify an explicit scale instead.
91 is_reviewable :by => [:users, :journalists], :scale => 0..5
e4b4173e »
2009-09-06 Initial commit.
92 end
93 </pre>
94
95 *Note:* @:by@ is optional if you choose any of @User@ or @Account@ as reviewer classes.
96
2e2767d5 »
2009-09-21 README-formatting.
97 h3. 3. ...and here we go:
e4b4173e »
2009-09-06 Initial commit.
98
419b71b3 »
2009-09-18 Now saves any additional custom attributes using the review! method. …
99 Examples:
e4b4173e »
2009-09-06 Initial commit.
100
101 <pre>
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
102 Review.destroy_all # just in case...
103 @post = Post.first
104 @user = User.first
105
e59182ae »
2009-09-24 README-updates.
106 @post.review!(:reviewer => @user, :rating => 2) # new reviewer (type: object) => create
107 @post.review!(:reviewer => @user, :rating => 5) # same reviewer (type: object) => update
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
108
e59182ae »
2009-09-24 README-updates.
109 @post.total_reviews # => 1
110 @post.average_rating # => 5.0
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
111
e59182ae »
2009-09-24 README-updates.
112 @post.review!(:reviewer => '128.0.0.0', :rating => 4) # new reviewer (type: IP) => create
113 @post.review!(:reviewer => '128.0.0.0', :rating => 2) # same reviewer (type: IP) => update
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
114
e59182ae »
2009-09-24 README-updates.
115 @post.total_reviews # => 2
116 @post.average_rating # => 3.5
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
117
e59182ae »
2009-09-24 README-updates.
118 @post.review!(:reviewer => @user, :rating => nil, :body => "Lorem ipsum...") # same reviewer (type: IP) => update
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
119
e59182ae »
2009-09-24 README-updates.
120 @post.total_reviews # => 2
121 @post.average_rating # => 2.0, i.e. don't count nil (a.k.a. "no opinion")
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
122
e59182ae »
2009-09-24 README-updates.
123 @post.unreview!(:reviewer => '128.0.0.0') # delete existing review (type: IP) => destroy
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
124
e59182ae »
2009-09-24 README-updates.
125 @post.total_reviews # => 1
126 @post.average_rating # => 2.0
127
128 @post.reviews # => reviews on @post
129 @post.reviewers # => reviewers that reviewed @post
130 @user.reviews # => reviews by @user
131 @user.reviewables # => reviewable objects that got reviewed by @user
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
132
133 # TODO: A few more samples...
134
135 # etc...
e4b4173e »
2009-09-06 Initial commit.
136 </pre>
137
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
138 h2. Mixin Arguments
e4b4173e »
2009-09-06 Initial commit.
139
140 The @is_reviewable@ mixin takes some hash arguments for customization:
141
142 * @:by@ - the reviewer model, e.g. User, Account, etc. (accepts either symbol or class, i.e. @User@ <=> @:user@ <=> @:users@). The reviewer model will be setup for you. Note: Polymorhic, so it accepts any model. Default: @User@, or auto-detection fails.
d721aa47 »
2009-09-09 README-updates.
143 * @:scale@/@:range@/@:values@ - range, or array, of valid rating values. Default: @1..5@. Note: Negative values are allowed too, and a range of values are not required, i.e. [-1, 1] is valid as well as [1,3,5]. =)
144 * @:total_precision@ - maximum number of digits for the average rating value. Default: @1@.
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
145 * @:accept_ip@ - accept anonymous users uniquely identified by IP (well...you handle the bots =D). See examples below how to use this in comparison to reviewer model. Default: @false@.
e4b4173e »
2009-09-06 Initial commit.
146
147 h2. Finders
148
149 IsReviewable has plenty of useful finders implemented using named scopes. Here they are:
150
93338a9e »
2009-09-08 Refactored some stuff, and fixing invalid gemspec generation.
151 h3. @Review@
e4b4173e »
2009-09-06 Initial commit.
152
e59182ae »
2009-09-24 README-updates.
153 h4. Order:
e4b4173e »
2009-09-06 Initial commit.
154
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
155 * @in_order@ - most recent reviews last (order by creation date).
156 * @most_recent@ - most recent reviews first (opposite of @in_order@ above).
e4b4173e »
2009-09-06 Initial commit.
157 * @lowest_rating@ - reviews with lowest ratings first.
158 * @highest_rating@ - reviews with lowest ratings first.
159
e59182ae »
2009-09-24 README-updates.
160 h4. Filter:
e4b4173e »
2009-09-06 Initial commit.
161
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
162 * @limit(<number_of_items>)@ - maximum @<number_of_items>@ reviews.
163 * @since(<created_at_datetime>)@ - reviews created since @<created_at_datetime>@.
164 * @recent(<datetime_or_size>)@ - if DateTime: reviews created since @<datetime_or_size>@, else if Fixnum: pick last @<datetime_or_size>@ number of reviews.
165 * @between_dates(<from_date>, to_date)@ - reviews created between two datetimes.
166 * @with_rating(<rating_value_or_range>)@ - reviews with(in) rating value (or range) @<rating_value_or_range>@.
167 * @with_a_rating@ - reviews with a rating value, i.e. not nil.
168 * @without_a_rating@ - opposite of @with_a_rating@ (above).
169 * @with_a_body@ - reviews with a body/comment, i.e. not nil/blank.
170 * @without_a_body@ - opposite of @with_a_body@ (above).
e59182ae »
2009-09-24 README-updates.
171 * @complete@ - reviews with both rating and comments, i.e. "full reviews" where.
172 * @of_reviewable_type(<reviewable_type>)@ - reviews of @<reviewable_type>@ type of reviewable models.
173 * @by_reviewer_type(<reviewer_type>)@ - reviews of @<reviewer_type>@ type of reviewer models.
e4b4173e »
2009-09-06 Initial commit.
174 * @on(<reviewable_object>)@ - reviews on the reviewable object @<reviewable_object>@ .
175 * @by(<reviewer_object>)@ - reviews by the @<reviewer_object>@ type of reviewer models.
176
419b71b3 »
2009-09-18 Now saves any additional custom attributes using the review! method. …
177 h3. @Reviewable@
178
e59182ae »
2009-09-24 README-updates.
179 h4. Order:
180
181 _TODO: Documentation on named scopes for reviewable._
182
183 h4. Filter:
184
185 _TODO: Documentation on named scopes for reviewable._
186
187 h3. @Reviewer@
188
189 h4. Order:
190
191 _TODO: Documentation on named scopes for reviewable._
192
193 h4. Filter:
5aa409f7 »
2009-09-18 More docmentation and examples in the README - still not complete tho…
194
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
195 _TODO: Documentation on named scopes for reviewable._
419b71b3 »
2009-09-18 Now saves any additional custom attributes using the review! method. …
196
e59182ae »
2009-09-24 README-updates.
197 h3. Examples using finders:
e4b4173e »
2009-09-06 Initial commit.
198
199 <pre>
200 @user = User.first
201 @post = Post.first
202
5aa409f7 »
2009-09-18 More docmentation and examples in the README - still not complete tho…
203 @post.reviews.recent(10) # => [10 most recent reviews]
204 @post.reviews.recent(1.week.ago) # => [reviews created since 1 week ago]
205
206 @post.reviews.with_rating(3.5..4.0) # => [all reviews on @post with rating between 3.5 and 4.0]
207
208 @post.reviews.by_reviewer_type(:user) # => [all reviews on @post by User-objects]
209 # ...or:
210 @post.reviews.by_reviewer_type(:users) # => [all reviews on @post by User-objects]
211 # ...or:
212 @post.reviews.by_reviewer_type(User) # => [all reviews on @post by User-objects]
213
214 @user.reviews.on(@post) # => [all reviews by @user on @post]
215 @post.reviews.by(@user) # => [all reviews by @user on @post] (equivalent with above)
e4b4173e »
2009-09-06 Initial commit.
216
5aa409f7 »
2009-09-18 More docmentation and examples in the README - still not complete tho…
217 Review.on(@post) # => [all reviews on @user] <=> @post.reviews
218 Review.by(@user) # => [all reviews by @user] <=> @user.reviews
e4b4173e »
2009-09-06 Initial commit.
219
5aa409f7 »
2009-09-18 More docmentation and examples in the README - still not complete tho…
220 # etc, etc. It's all named scopes, so it's really no new hokus-pokus you have to learn.
e4b4173e »
2009-09-06 Initial commit.
221 </pre>
222
e59182ae »
2009-09-24 README-updates.
223 h2. Additional Methods
224
225 *Note:* See documentation (RDoc).
226
abf53d1f »
2009-09-06 Updated README a bit, even though this is work in progress...
227 h2. Extend the Review model
228
229 This is optional, but if you wanna be in control of your models (in this case @Review@) you can take control like this:
230
231 <pre>
232 class Review < IsReviewable::Review
233
234 # Do what you do best here... (stating the obvious: core IsReviewable associations, named scopes, etc. will be inherited)
235
236 end
237 </pre>
238
e4b4173e »
2009-09-06 Initial commit.
239 h2. Caching
240
241 If the visitable class table - in the sample above @Post@ - contains a columns @reviews_count@ and @average_rating@, then a cached value will be maintained within it for the number of reviews and the average rating the object have got.
242
243 Additional caching fields (to a reviewable model table):
244
245 <pre>
246 class AddIsReviewableToPostsMigration < ActiveRecord::Migration
247 def self.up
248 # Enable is_reviewable-caching.
249 add_column :posts, :cached_total_reviews, :integer
250 add_column :posts, :cached_average_rating, :integer
251 end
252
253 def self.down
254 remove_column :posts, :cached_total_reviews
255 remove_column :posts, :cached_average_rating
256 end
257 end
258 </pre>
259
419b71b3 »
2009-09-18 Now saves any additional custom attributes using the review! method. …
260 h2. Example
261
262 h3. Controller
263
264 Depending on your implementation: You might - or might not - need a Controller, but for most cases where you only want to allow rating of something, a controller most probably is overkill. In the case of a review, this is how one cold look like:
265
266 Using the excellent "InheritedResources":http://github.com/josevalim/inherited_resources - @app/controllers/reviews_controller.rb@:
267
268 <pre>
269 class ReviewsController < InheritedResources::Base
270
271 actions :create, :update, :destroy
272 respond_to :js
273 layout false
274
275 end
276 </pre>
277
278 ..or in the more basic rating case - @app/controllers/posts_controller.rb@:
279
280 <pre>
281 class PostsController < InheritedResources::Base
282
283 actions :all
284 respond_to :html, :js
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
285 layout false if request.format == :js
419b71b3 »
2009-09-18 Now saves any additional custom attributes using the review! method. …
286
287 def rate
288 begin
289 @post.review! :reviewer => current_user, params.slice(:rating, :body)
290 rescue
291 flash[:error] = 'Sad panda...could not rate for some reason. O_o'
292 end
293 respond_to do |format|
294 format.html { redirect_to @post }
295 format.js # app/views/posts/rate.js.rjs
296 end
297 end
298
299 end
300 </pre>
301
5aa409f7 »
2009-09-18 More docmentation and examples in the README - still not complete tho…
302 h3. Routes
303
304 @config/routes.rs@
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
305
5aa409f7 »
2009-09-18 More docmentation and examples in the README - still not complete tho…
306 <pre>
307 map.resources :posts, :member => {:rate => :put}
308 </pre>
309
419b71b3 »
2009-09-18 Now saves any additional custom attributes using the review! method. …
310 h3. Views
311
5aa409f7 »
2009-09-18 More docmentation and examples in the README - still not complete tho…
312 IsReviewable comes with no view templates (etc.) because of already stated reasons, but basic rating mechanism is trivial to implement (in this example, I'm using HAML because I despise ERB):
313
314 Example: @app/views/posts/show.html.haml@
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
315
5aa409f7 »
2009-09-18 More docmentation and examples in the README - still not complete tho…
316 <pre>
317 %h1
318 = @post.title
319 %p
320 = @post.body
321 %p
322 = "Your rating:"
323 = render '/reviews/rating', :resource => @post
324 </pre>
419b71b3 »
2009-09-18 Now saves any additional custom attributes using the review! method. …
325
5aa409f7 »
2009-09-18 More docmentation and examples in the README - still not complete tho…
326 Example: @app/views/reviews/_rating.html.haml@
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
327
419b71b3 »
2009-09-18 Now saves any additional custom attributes using the review! method. …
328 <pre>
5aa409f7 »
2009-09-18 More docmentation and examples in the README - still not complete tho…
329 .rating
330 - if resource.present? && resource.reviewable?
331 - if current_user.present?
332 - current_rating = resource.review_by(current_user).try(:rating)
333 - resource.reviewable_scale.each do |rating|
334 = link_to_remote "#{rating.to_i}", :url => rate_post_path(resource, :rating => rating.to_i), :method => :put, :html => {:class => "rate rated_#{rating.to_i}#{' current' if current_rating == rating}"}
335 = link_to_remote "no opinion", :url => rate_post_path(resource, :rating => nil), :method => :put, :html => {:class => "rate rated_none#{' current' unless current_rating}"}
336 - else # static rating
337 - current_rating = resource.average_rating.round
338 - resource.reviewable_scale.each do |rating|
339 {:class => "rate rated_#{rating}#{' current' if current_rating == rating}"}
419b71b3 »
2009-09-18 Now saves any additional custom attributes using the review! method. …
340 </pre>
341
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
342 *Note:* Skipping review-body (etc.) in this view sample, but that is straightforward to implement anyways.
343
419b71b3 »
2009-09-18 Now saves any additional custom attributes using the review! method. …
344 h2. Design Implications: Additional Use Cases
345
346 h3. Like/Dislike
e4b4173e »
2009-09-06 Initial commit.
347
348 IsReviewable is designed in such way that you as a developer are not locked to how traditional rating works. As an example, this is how you could implement like/dislike (like VoteFu) pattern using IsReviewable:
349
350 Example:
351
352 <pre>
353 class Post < ActiveRecord::Base
354 is_reviewable :by => :users, :values => [-1, 1]
355 end
356 </pre>
357
358 *Note:* @:values@ is an alias for @:scale@ for semantical reasons in cases like these.
359
360 h2. Dependencies
361
362 Basic usage:
363
364 * "rails":http://github.com/rails/rails (well...)
365
366 For running tests:
367
368 * sqlite3-ruby
369 * "thoughtbot-shoulda":http://github.com/thoughtbot/shoulda
370 * "nakajima-acts_as_fu":http://github.com/nakajima/acts_as_fu
871254b4 »
2009-09-21 Fixed typo in named scopes. =P
371 * "jgre-monkeyspecdoc":http://github.com/jgre/monkeyspecdoc (optional)
e4b4173e »
2009-09-06 Initial commit.
372
373 h2. Notes
374
375 * Tested with Ruby 1.9.1.
376 * Let me know if you find any bugs; not used in production yet so consider this a concept version.
377
378 h2. TODO
379
e59182ae »
2009-09-24 README-updates.
380 h3. Priority:
bc2d3a05 »
2009-09-18 Added support for initialization of multiple reviewer classes. Now Ac…
381
e59182ae »
2009-09-24 README-updates.
382 * bug: Accept multiple reviewers (of different types): @average_rating_by@, etc..
383 * documentation: A few more README-examples.
384 * feature: Reviewable on multiple contexts, e.g. @is_reviewable :on => [:acting, :writing, ...]@. Alias method @is_reviewable_on@.
385 * testing: More thorough tests, especially for named scopes which is a bit tricky.
386 * feature: Useful finders for @Reviewable@.
387 * feature: Useful finders for @Reviewer@.
bc2d3a05 »
2009-09-18 Added support for initialization of multiple reviewer classes. Now Ac…
388
e59182ae »
2009-09-24 README-updates.
389 h3. Maybe:
bc2d3a05 »
2009-09-18 Added support for initialization of multiple reviewer classes. Now Ac…
390
e59182ae »
2009-09-24 README-updates.
391 * feature: Make alias helpers to implement functionality like VoteFu (http://github.com/peteonrails/vote_fu), simply just aliasing methods with existing ones with hardcoded parameters. Not required because this is supported already, it's all about semantics and sassy code.
e4b4173e »
2009-09-06 Initial commit.
392
d92d7a8d »
2009-09-21 Adding "Related Links".
393 h2. Related Links
394
e59182ae »
2009-09-24 README-updates.
395 ...that might be of interest.
396
d92d7a8d »
2009-09-21 Adding "Related Links".
397 * "jQuery Star Rating":http://github.com/rathgar/jquery-star-rating/ - javascript star rating plugin for Rails on jQuery, if you don't want to do the rating widget on your own. It should be quite straightforward to customize the appearance of it for your needs too.
398
e4b4173e »
2009-09-06 Initial commit.
399 h2. License
400
133af2a2 »
2009-09-21 Fixed some named scopes-related, and updated README with more samples.
401 Copyright (c) 2009 Jonas Grimfelt, released under the MIT-license.
Something went wrong with that request. Please try again.