README.md in the_comments-0.9.0 vs README.md in the_comments-0.9.9

- old
+ new

@@ -1,167 +1,211 @@ -# TheComments 0.9.0 +# TheComments 1.0.0 -TheComments - probably, best solution for comments for Ruby on Rails. +TheComments - just comment system for my Ruby on Rails 4 projects -### What's wrong with other gems? +P.S: and for me it's best prototype of comment system for Rails 4 -Just look at [Ruby-Toolbox](https://www.ruby-toolbox.com/categories/rails_comments). What we can see? +## Keywords -* [Acts as commentable with threading](https://github.com/elight/acts_as_commentable_with_threading) - so, guys, where is the render helper for the tree? There is no helper! Should I make render helper for tree by myself? Nooooo!!! I'm so sorry, but I can't use this gem. -* [acts_as_commentable](https://github.com/jackdempsey/acts_as_commentable) - so, I can see code for models. But I can't see code for controllers and views. Unfortunately, there is no threading. It's not enough for me. -* [opinio](https://github.com/Draiken/opinio) - looks better, but there is no threading. I want to have more! -* [has_threaded_comments](https://github.com/aarongough/has_threaded_comments) - Nice work! Nice gem! Models, controllers, views, view helper for tree rendering! **But**, last activity 2 years ago, I need few features, I think - I can make it better. +Comments for Rails 4, Comments with threading, Nested Comments, Polymorphic comments, Acts as commentable, Comment functionality, Comments, Threading, Rails 4, Comments with moderation, I hate captcha for comments! -### In sum +## Screenshots -![TheComments](https://raw.github.com/open-cook/the_comments/master/the_comments.jpg) +**click to zoom** -### My hopes about comments system +<table> + <tr> + <td width="20%">Guest view</td> + <td width="20%">Admin view</td> + <td width="20%">Edit</td> + <td width="20%">Cache counters & User Cabinet</td> + <td width="20%">Recent comments & Denormalization</td> + </tr> + <tr> + <td width="20%"><img width="100%" height="100%" src="https://raw.github.com/open-cook/the_comments/master/docs/the_comments_view_2.gif" alt="the_comments"></td> + <td width="20%"><img width="100%" height="100%" src="https://raw.github.com/open-cook/the_comments/master/docs/the_comments_view_1.gif" alt="the_comments"></td> + <td width="20%"><img width="100%" height="100%" src="https://raw.github.com/open-cook/the_comments/master/docs/the_comments_view_4.gif" alt="the_comments"></td> + <td width="20%"><img width="100%" height="100%" src="https://raw.github.com/open-cook/the_comments/master/docs/the_comments_view_3.gif" alt="the_comments"></td> + <td width="20%"><img width="100%" height="100%" src="https://raw.github.com/open-cook/the_comments/master/docs/the_comments_view_5.gif" alt="the_comments"></td> + </tr> +</table> -* Open comments for everybody (by default). *I hate user registration* -* Moderation for comments and simple Admin UI -* Spam traps instead Captcha. *I hate Captcha* -* Blacklists for IP and UserAgent -* Comment counters for commentable objects and User -* Denormalization for fast and Request-free comment list building -* Ready for external content filters ( **sanitize**, **RedCloth**, **Markdown**) -* Ready for Rails4 (and Rails::Engine) -* Delete without destroy +### Main features -### Requires +* Threaded comments +* Tree rendering via [TheSortableTree](https://github.com/the-teacher/the_sortable_tree) +* [Denormalization](#denormalization) for Recent comments +* Usefull cache counters +* Basic AntiSpam system +* Online Support via skype: **ilya.killich** -```ruby -gem 'awesome_nested_set' -gem 'the_sortable_tree' -gem 'state_machine' -``` +### Intro (understanding) -### Anti Spam system +* [My hopes about comments system](#my-hopes-about-comments-system) +* [What's wrong with other gems?](#whats-wrong-with-other-gems) +* [Comments, Posted comments & ComComs](#comments-posted-comments-comcoms) +* [Denormalization and Recent comments](#denormalization) +* [Recent comments building](#recent-comments-building) +* [AntiSpam system](#antispam-system) +* [Customization](#customization) +* [User methods](#user-methods) +* [Commentable methods](#commentable-methods) +* [Online Support](#online-support) +* [About author](#about-author) -User agent must have: +## Installation -* Cookies support -* JavaScript and Ajax support +This gem has many steps to install. Be careful when installing and keep calm. -_Usually spambot not support Cookies and JavaScript_ +Just follow an installation instruction step by step and everything will be fine! -Comment form has: +**Installation process consist of 4 main steps:** -* fake (hidden) fields +* [Gem Installation](#gem-installation) +* [Code Installation](#code-installation) +* [Tuning](#tuning) +* [Using](#using) -_Usually spam bot puts data in fake inputs_ +## Gem Installation -Trap via time: +**1)** change your Gemfile -* User should be few seconds on page, before comment sending (by default 5 sec) +```ruby +# I use *awesome_nested_set* gem to provide threading for comments +# But you can use other nested set gem, for example: +# gem 'nested_set' (github.com/skyeagle/nested_set) +gem 'awesome_nested_set' -_Usually spam bots works faster, than human. We can try to use this feature of behavior_ +# I use haml for default views +# You can remove this dependancy, +# but you will should rewrite default views with your template engine +gem 'haml' -## Installation +# finally, this gem +gem 'the_comments' +``` +**2)** bundle + +**3)** Copy migration file into application, <b>OPEN FILE AND FOLLOW AN INSTRUCTION</b>. + ```ruby -gem 'the_comments' +bundle exec rake the_comments_engine:install:migrations ``` -**bundle** +**4)** run migration ```ruby -bundle exec rails g the_comment install +bundle exec rake db:migrate ``` -### Assets +## Code Installation -**app/assets/javascripts/application.js** +**1)** Assets +*app/assets/javascripts/application.js* + ```js //= require the_comments +//= require the_comments_manage ``` -**app/assets/stylesheets/application.css** +*app/assets/stylesheets/application.css* ```css -/* - *= require the_comments -*/ +*= require the_comments ``` -### User Model +**2)** Change your ApplicationController ```ruby -class User < ActiveRecord::Base - # Your implementation of role policy - def admin? - self == User.first - end +class ApplicationController < ActionController::Base + include TheCommentsController::ViewToken +end +``` - # include TheComments methods - include TheCommentsUser - - # denormalization for commentable objects - def commentable_title - login - end +**3)** Run generator, <b>OPEN EACH OF CREATED FILES AND FOLLOW INSTRUCTIONS</b>. - def commentable_url - [class.to_s.tableize, login].join('/') - end +```ruby +bundle exec rails g the_comments install +``` - # Comments moderator checking (simple example) - # Usually comment's holder should be moderator - def comment_moderator? comment - admin? || id == comment.holder_id - end +*List of created files:* -end +```ruby +config/initializers/the_comments.rb + +app/controllers/comments_controller.rb +app/controllers/ip_black_lists_controller.rb +app/controllers/user_agent_black_lists_controller.rb ``` -**User#coments** - comments. Set of all created comments +**4)** Copy view files into your application ```ruby -User.first.comments -# => Array of comments, where User is creator (owner) +bundle exec rails g the_comments:views views ``` -**User#comcoms** - commentable comments. Set of all comments of all owned commentable objects of this user. +*List of created directories with view files:* ```ruby -User.first.comcoms -# => Array of all comments of all owned commentable objects, where User is holder -# Usually comment's holder should be moderator of this comments -# because this user should maintain cleaness of his commentable objects +app/views/the_comments/*.haml +app/views/ip_black_lists/*.haml +app/views/user_agent_black_lists/*.haml ``` -**Attention!** You should be sure that you understand who is owner, and who is holder of comments! +## Tuning -### Commentable Model (Page, Blog, Article, User ...) +### User Model -**Attention!** User model can be commentable object also. +```ruby +class User < ActiveRecord::Base + include TheCommentsUser + # Your implementation of role policy + def admin? + self == User.first + end + + # Comments moderator checking (simple example) + # Usually comment's holder should be moderator + def comment_moderator? comment + admin? || id == comment.holder_id + end +end +``` + +### Any Commentable Model (Page, Blog, Article, User(!) ...) + ```ruby class Blog < ActiveRecord::Base - # include TheComments methods include TheCommentsCommentable - # (!) Every commentable Model must have next 2 methods - # denormalization for commentable objects def commentable_title - # "My first blog post" - title + # by default: try(:title) || 'Undefined title' + # for example: "My first blog post" + blog_post_name end def commentable_url - # blogs/1-my-first-blog-post + # by default: ['', self.class.to_s.tableize, self.to_param].join('/') + # for example: "blogs/1-my-first-blog-post" [self.class.to_s.tableize, slug_id].join('/') end + + def commentable_state + # by default: try(:state) + # for example: "draft" + self.ban_flag == true ? :banned : :published + end end ``` ### Comment Model ```ruby class Comment < ActiveRecord::Base - # include TheComments methods include TheCommentsBase # Define comment's avatar url # Usually we use Comment#user (owner of comment) to define avatar # @blog.comments.includes(:user) <= use includes(:user) to decrease queries count @@ -184,31 +228,16 @@ self.content = text end end ``` -### IP, User Agent black lists +## Using -Models must looks like this: - -```ruby -class IpBlackList < ActiveRecord::Base - include TheCommentsBlackUserAgent -end - -class UserAgentBlackList < ActiveRecord::Base - include TheCommentsBlackIp -end -``` - ### Commentable controller ```ruby class BlogsController < ApplicationController - include TheCommentsController::Cookies - include TheCommentsController::ViewToken - def show @blog = Blog.where(id: params[:id]).with_states(:published).first @comments = @blog.comments.with_state([:draft, :published]).nested_set end end @@ -218,31 +247,279 @@ ```ruby %h1= @blog.title %p= @blog.content -= render partial: 'comments/tree', locals: { comments_tree: @comments, commentable: @blog } += render partial: 'the_comments/tree', locals: { commentable: @blog, comments_tree: @comments } ``` -## Configuration +## Understanding -**config/initializers/the_comments.rb** +### My hopes about comments system +* Open comments for everybody (by default). *I hate user registration* +* Polymorphic comments for any AR Model +* Threading for comments (can be plain comments list) +* Cache counters for commentable objects and User +* Moderation for comments and simple Admin UI +* Spam traps instead Captcha. *I hate Captcha* +* Blacklists for IP and UserAgent +* Denormalization for fast and Request-free Recent comments building +* Ready for external content filters (<b>sanitize</b>, <b>RedCloth</b>, <b>Markdown</b>) +* Highlighting and Jumping to comment via anchor +* Ready for Rails4 (and Rails::Engine) +* Ready for JQuery 1.9+ +* Delete without destroy + +### What's wrong with other gems? + +Just look at [Ruby-Toolbox](https://www.ruby-toolbox.com/categories/rails_comments). What we can see? + +* [Acts as commentable with threading](https://github.com/elight/acts_as_commentable_with_threading) - so, guys, where is the render helper for the tree? There is no helper! Should I make render helper for tree by myself? Nooooo!!! I'm so sorry, but I can't use this gem. +* [acts_as_commentable](https://github.com/jackdempsey/acts_as_commentable) - so, I can see code for models. But I can't see code for controllers and views. Unfortunately, there is no threading. It's not enough for me. +* [opinio](https://github.com/Draiken/opinio) - looks better, but there is no threading. I want to have more! +* [has_threaded_comments](https://github.com/aarongough/has_threaded_comments) - Nice work! Nice gem! Models, controllers, views, view helper for tree rendering! **But**, last activity 2 years ago, I need few features, I think - I can make it better. + +![TheComments](https://raw.github.com/open-cook/the_comments/master/docs/the_comments.jpg) + +## Comments, Posted comments, ComComs + +### Posted comments + +**@user.posted_comments** (has_many) + +Set of comments, where current user is owner (creator). + ```ruby -TheComments.configure do |config| - config.max_reply_depth = 3 # 3 by default - config.tolerance_time = 15 # 5 (sec) by default - config.empty_inputs = [:email, :message, :commentBody] # [:message] by default -end +@my_comments = @user.posted_comments # => [comment, comment, ...] + +@comment = @my_comments.first +@user.id == @comment.user_id # => true ``` -* **max_reply_depth** - comments tree nesting by default -* **tolerance_time** - how many seconds user should spend on page, before comment send -* **empty_inputs** - names of hidden (via css) fields for spam detecting +### Comments +**@commentable.comments** (has_many) + +Set of comments for this commentable object + +```ruby +@comments = @blog.comments # => [comment, comment, ...] + +@comment = @comments.first +@comment.commentable_id == @blog.id # => true +@comment.commentable_type == 'Blog' # => true +``` + +<b>(!) Attention:</b> User Model can be commentable object too! + +```ruby +@comments = @user.comments # => [comment, comment, ...] + +@comment = @comments.first +@comment.commentable_id == @user.id # => true +@comment.commentable_type == 'User' # => true +``` + +### ComComs (COMments of COMmentable objects) + +**@user.comcoms** (has_many) + +Set of All <b>COM</b>ments of All <b>COM</b>mentable objects of this User + +```ruby +@comcoms = @user.comcoms # => [comment, comment, ...] + +@comcom = @comcoms.first +@user.id == @comcom.holder_id # => true +``` + +**Comment#holder_id** should not be empty. Because we should to know, who is moderator of this comment. + +```ruby +@user.id == @comment.holder_id # => true +# => This user should be MODERATOR for this comment +``` + +## Denormalization + +For building of Recent comments list (for polymorphic relationship) we need to have many additional requests to database. It's classic problem of polymorphic comments. + +I use denormalization of commentable objects for solve of this problem. + +My practice shows - We need 3 denormalized fields into comment for (request-free) building of recent comments list: + +<img src="https://raw.github.com/open-cook/the_comments/master/docs/the_comments_view_5.gif" alt="the_comments"> + +* **Comment#commentable_title** - for example: "My first post about Ruby On Rails" +* **Comment#commentable_url** - for example: "/posts/1-my-first-post-about-ruby-on-rails" +* **Comment#commentable_state** - for example: "draft" + +That is why any **Commentable Model should have few methods** to provide denormalization for Comments. + +## Recent comments building + +Denormalization makes, building of Recent comments (for polymorphic relationship) is very easy! + +Controller: + +```ruby +@comments = Comment.with_state(:published) + .where(commentable_state: [:published]) + .order('created_at DESC') + .page(params[:page]) +``` + +View: + +```ruby +- @comments.each do |comment| + %div + %p= comment.commentable_title + %p= link_to comment.commentable_title, comment.commentable_url + %p= comment.content +``` + +### AntiSpam system + +**1) Moderation** + +**2) User agent must have:** + +* Cookies support +* JavaScript and Ajax support + +_Usually spambots not support Cookies and JavaScript_ + +**3) Comment form mast have:** + +* fake (hidden via css) fields + +_Usually spambots puts data in fake inputs_ + +**4) Trap via time:** + +* User should be few seconds on page, before comment sending (by default 5 sec) + +_Usually spambots works faster, than human. We can try to use this feature of behavior_ + +**5) IP and User Agent blacklists** + + +### Strong dependencies of gem + +```ruby +gem 'the_sortable_tree' +gem 'state_machine' +``` + +* **the_sortable_tree** - render helper for nested set +* **state_machine** - states for moderation and callbacks for recalculating of counters when state of comment was changed + +## Customization + +You can use **generators** for copy files into your Application. After that you can customize almost everything + +Generators list: + +```ruby +bundle exec rails g the_comments --help +``` + +Copy View files for customization: + +```ruby +bundle exec rails g the_comments:views assets +bundle exec rails g the_comments:views views +``` + +Copy Helper file for tree customization: + +For more info read [TheSortableTree doc](https://github.com/the-teacher/the_sortable_tree) + +```ruby +bundle exec rails g the_comments:views helper +``` +### User methods + +* @user.<b>posted_comments</b> (all states) +* @user.<b>my_comments</b> (draft, published) +* @user.<b>my_comments_count</b> + +Cache counters methods + +* @user.<b>recalculate_my_comments_counter!</b> +* @user.<b>recalculate_comcoms_counters!</b> + +Comments methods + +* @user.<b>comments</b> +* @user.<b>comments_sum</b> +* @user.<b>draft_comments_count</b> +* @user.<b>published_comments_count</b> +* @user.<b>deleted_comments_count</b> + +Comcoms methods + +* @user.<b>comcoms</b> +* @user.<b>comcoms_sum</b> +* @user.<b>draft_comcoms_count</b> +* @user.<b>published_comcoms_count</b> +* @user.<b>deleted_comcoms_count</b> + +### Commentable methods + +* @post.<b>comments</b> +* @post.<b>draft_comments_count</b> +* @post.<b>published_comments_count</b> +* @post.<b>deleted_comments_count</b> +* @post.<b>comments_sum</b> (draft + published) +* @post.<b>recalculate_comments_counters!</b> + +Denornalization methods + +* @post.<b>commentable_title</b> +* @post.<b>commentable_url</b> +* @post.<b>commentable_state</b> + +## Online Support + +I need your opinion, ideas, user experience - that is why you can ask me about this gem via skype: **ilya.killich** + +## About author + +Yes, It's true - I was a school teacher in the past. +That's why my login is the-teacher. +Now I'm ruby & frontend developer. +I learn, I teach, I make a code. And sorry for my English. + ## Contributing 1. Fork it -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Add some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create new Pull Request +2. Clone it into local folder +3. Add to Gemfile via **gem 'the_comments', path: '/path/to/gem/the_comments'** +4. Change code +5. git push origin master +6. Create pull request via github + +### MIT License + +Copyright (c) 2013 Ilya N. Zykin + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file