fuck_comments 2.3.4
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 +7 -0
- data/.gitignore +30 -0
- data/.ruby-gemset.example +1 -0
- data/.ruby-version.example +1 -0
- data/.rvmrc.example +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +338 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/the_comments.js.coffee +108 -0
- data/app/assets/javascripts/the_comments_manage.js.coffee +27 -0
- data/app/assets/stylesheets/the_comments.css.scss +248 -0
- data/app/controllers/_templates_/comments_controller.rb +44 -0
- data/app/controllers/concerns/the_comments/controller.rb +197 -0
- data/app/controllers/concerns/the_comments/view_token.rb +20 -0
- data/app/helpers/render_comments_tree_helper.rb +111 -0
- data/app/models/_templates_/comment.rb +38 -0
- data/app/models/concerns/the_comments/comment.rb +116 -0
- data/app/models/concerns/the_comments/comment_states.rb +80 -0
- data/app/models/concerns/the_comments/commentable.rb +69 -0
- data/app/models/concerns/the_comments/user.rb +56 -0
- data/app/views/the_comments/_tree.html.erb +3 -0
- data/app/views/the_comments/haml/_additional_info.html.haml +13 -0
- data/app/views/the_comments/haml/_comment.html.haml +1 -0
- data/app/views/the_comments/haml/_comment_body.html.haml +25 -0
- data/app/views/the_comments/haml/_comment_edit.html.haml +26 -0
- data/app/views/the_comments/haml/_form.html.haml +8 -0
- data/app/views/the_comments/haml/_guest_form.html.haml +22 -0
- data/app/views/the_comments/haml/_logined_form.html.haml +18 -0
- data/app/views/the_comments/haml/_manage_controls.html.haml +30 -0
- data/app/views/the_comments/haml/_sidebar.html.haml +9 -0
- data/app/views/the_comments/haml/_sidebar_admin.html.haml +12 -0
- data/app/views/the_comments/haml/_sidebar_backlink.html.haml +3 -0
- data/app/views/the_comments/haml/_sidebar_user.html.haml +29 -0
- data/app/views/the_comments/haml/_tree.html.haml +16 -0
- data/app/views/the_comments/haml/manage.html.haml +26 -0
- data/app/views/the_comments/slim/_additional_info.html.slim +13 -0
- data/app/views/the_comments/slim/_comment.html.slim +1 -0
- data/app/views/the_comments/slim/_comment_body.html.slim +24 -0
- data/app/views/the_comments/slim/_comment_edit.html.slim +26 -0
- data/app/views/the_comments/slim/_form.html.slim +8 -0
- data/app/views/the_comments/slim/_guest_form.html.slim +22 -0
- data/app/views/the_comments/slim/_logined_form.html.slim +18 -0
- data/app/views/the_comments/slim/_manage_controls.html.slim +30 -0
- data/app/views/the_comments/slim/_sidebar.html.slim +9 -0
- data/app/views/the_comments/slim/_sidebar_admin.html.slim +12 -0
- data/app/views/the_comments/slim/_sidebar_backlink.html.slim +3 -0
- data/app/views/the_comments/slim/_sidebar_user.html.slim +29 -0
- data/app/views/the_comments/slim/_tree.html.slim +16 -0
- data/app/views/the_comments/slim/index.html.slim +18 -0
- data/app/views/the_comments/slim/manage.html.slim +26 -0
- data/app/views/the_comments/slim/my_comments.html.slim +28 -0
- data/config/initializers/the_comments.rb +15 -0
- data/config/locales/en.yml +68 -0
- data/config/locales/ru.yml +71 -0
- data/config/routes.rb +38 -0
- data/db/migrate/20130101010101_the_comments_change_user.rb +18 -0
- data/db/migrate/20130101010102_the_comments_create_comments.rb +50 -0
- data/db/migrate/20130101010103_the_comments_change_commentable.rb +13 -0
- data/docs/admin_ui_installation.md +145 -0
- data/docs/advanced_installation.md +185 -0
- data/docs/comment_api.md +58 -0
- data/docs/commentable_api.md +59 -0
- data/docs/config_file.md +27 -0
- data/docs/content_preprocessors.md +73 -0
- data/docs/customazation_of_views.md +30 -0
- data/docs/denormalization_and_recent_comments.md +40 -0
- data/docs/documentation.md +29 -0
- data/docs/generators.md +74 -0
- data/docs/pagination.md +123 -0
- data/docs/routes.md +77 -0
- data/docs/screencast.jpg +0 -0
- data/docs/the_comments.jpg +0 -0
- data/docs/the_comments_view_1.gif +0 -0
- data/docs/the_comments_view_2.gif +0 -0
- data/docs/the_comments_view_3.gif +0 -0
- data/docs/the_comments_view_4.gif +0 -0
- data/docs/the_comments_view_5.gif +0 -0
- data/docs/user_api.md +75 -0
- data/docs/what_is_comcoms.md +63 -0
- data/docs/whats_wrong_with_other_gems.md +28 -0
- data/docs/where_is_example_application.md +37 -0
- data/gem_version.rb +3 -0
- data/lib/generators/the_comments/USAGE +44 -0
- data/lib/generators/the_comments/the_comments_generator.rb +56 -0
- data/lib/generators/the_comments/views_generator.rb +79 -0
- data/lib/the_comments/config.rb +39 -0
- data/lib/the_comments/version.rb +1 -0
- data/lib/the_comments.rb +27 -0
- data/spec/dummy_app/.gitignore +18 -0
- data/spec/dummy_app/.rspec +1 -0
- data/spec/dummy_app/Gemfile +43 -0
- data/spec/dummy_app/README.md +33 -0
- data/spec/dummy_app/Rakefile +6 -0
- data/spec/dummy_app/app/assets/images/.keep +0 -0
- data/spec/dummy_app/app/assets/javascripts/admin_panel.js +5 -0
- data/spec/dummy_app/app/assets/javascripts/application.js +16 -0
- data/spec/dummy_app/app/assets/stylesheets/admin_panel.css +3 -0
- data/spec/dummy_app/app/assets/stylesheets/app.css.scss +4 -0
- data/spec/dummy_app/app/assets/stylesheets/application.css +16 -0
- data/spec/dummy_app/app/controllers/application_controller.rb +7 -0
- data/spec/dummy_app/app/controllers/comments_controller.rb +28 -0
- data/spec/dummy_app/app/controllers/concerns/.keep +0 -0
- data/spec/dummy_app/app/controllers/posts_controller.rb +13 -0
- data/spec/dummy_app/app/controllers/users_controller.rb +7 -0
- data/spec/dummy_app/app/helpers/application_helper.rb +2 -0
- data/spec/dummy_app/app/mailers/.keep +0 -0
- data/spec/dummy_app/app/models/.keep +0 -0
- data/spec/dummy_app/app/models/comment.rb +32 -0
- data/spec/dummy_app/app/models/concerns/.keep +0 -0
- data/spec/dummy_app/app/models/post.rb +17 -0
- data/spec/dummy_app/app/models/user.rb +21 -0
- data/spec/dummy_app/app/views/layouts/admin.html.haml +25 -0
- data/spec/dummy_app/app/views/layouts/application.html.haml +20 -0
- data/spec/dummy_app/app/views/posts/index.html.haml +22 -0
- data/spec/dummy_app/app/views/posts/show.html.haml +7 -0
- data/spec/dummy_app/bin/bundle +3 -0
- data/spec/dummy_app/bin/rails +4 -0
- data/spec/dummy_app/bin/rake +4 -0
- data/spec/dummy_app/config/application.rb +23 -0
- data/spec/dummy_app/config/boot.rb +4 -0
- data/spec/dummy_app/config/database.yml +11 -0
- data/spec/dummy_app/config/environment.rb +5 -0
- data/spec/dummy_app/config/environments/development.rb +29 -0
- data/spec/dummy_app/config/environments/production.rb +80 -0
- data/spec/dummy_app/config/environments/test.rb +36 -0
- data/spec/dummy_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy_app/config/initializers/inflections.rb +16 -0
- data/spec/dummy_app/config/initializers/mime_types.rb +5 -0
- data/spec/dummy_app/config/initializers/secret_token.rb +12 -0
- data/spec/dummy_app/config/initializers/session_store.rb +3 -0
- data/spec/dummy_app/config/initializers/sorcery.rb +437 -0
- data/spec/dummy_app/config/initializers/the_comments.rb +15 -0
- data/spec/dummy_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy_app/config/locales/en.yml +23 -0
- data/spec/dummy_app/config/routes.rb +19 -0
- data/spec/dummy_app/config.ru +4 -0
- data/spec/dummy_app/db/migrate/20130712061503_sorcery_core.rb +16 -0
- data/spec/dummy_app/db/migrate/20130712065951_create_posts.rb +11 -0
- data/spec/dummy_app/db/migrate/20131027185332_change_user.the_comments_engine.rb +19 -0
- data/spec/dummy_app/db/migrate/20131027185333_create_comments.the_comments_engine.rb +51 -0
- data/spec/dummy_app/db/migrate/20131027185334_change_commentable.the_comments_engine.rb +14 -0
- data/spec/dummy_app/db/schema.rb +74 -0
- data/spec/dummy_app/db/seeds.rb +42 -0
- data/spec/dummy_app/lib/assets/.keep +0 -0
- data/spec/dummy_app/lib/tasks/.keep +0 -0
- data/spec/dummy_app/lib/tasks/app_bootstrap.rake +15 -0
- data/spec/dummy_app/log/.keep +0 -0
- data/spec/dummy_app/public/404.html +58 -0
- data/spec/dummy_app/public/422.html +58 -0
- data/spec/dummy_app/public/500.html +57 -0
- data/spec/dummy_app/public/favicon.ico +0 -0
- data/spec/dummy_app/public/robots.txt +5 -0
- data/spec/dummy_app/spec/factories/post.rb +6 -0
- data/spec/dummy_app/spec/factories/user.rb +6 -0
- data/spec/dummy_app/spec/models/user_counters_spec.rb +339 -0
- data/spec/dummy_app/spec/spec_helper.rb +29 -0
- data/spec/dummy_app/test/controllers/.keep +0 -0
- data/spec/dummy_app/test/fixtures/.keep +0 -0
- data/spec/dummy_app/test/helpers/.keep +0 -0
- data/spec/dummy_app/test/integration/.keep +0 -0
- data/spec/dummy_app/test/mailers/.keep +0 -0
- data/spec/dummy_app/test/models/.keep +0 -0
- data/spec/dummy_app/test/test_helper.rb +15 -0
- data/spec/dummy_app/vendor/assets/javascripts/.keep +0 -0
- data/spec/dummy_app/vendor/assets/stylesheets/.keep +0 -0
- data/the_comments.gemspec +23 -0
- data/views_converter.rb +16 -0
- metadata +332 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module TheComments
|
|
2
|
+
# Cookies and View token for spam protection
|
|
3
|
+
# include TheComments::ViewToken
|
|
4
|
+
module ViewToken
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included { before_action :set_the_comments_cookies }
|
|
8
|
+
|
|
9
|
+
def comments_view_token
|
|
10
|
+
cookies[:comments_view_token]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def set_the_comments_cookies
|
|
16
|
+
cookies[:the_comment_cookies] = { value: TheComments::COMMENTS_COOKIES_TOKEN, expires: 1.year.from_now, domain: TheComments.config.cookie_domain }
|
|
17
|
+
cookies[:comments_view_token] = { value: SecureRandom.hex, expires: 7.days.from_now, domain: TheComments.config.cookie_domain } unless cookies[:comments_view_token]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# coding: UTF-8
|
|
2
|
+
# DOC:
|
|
3
|
+
# We use Helper Methods for tree building,
|
|
4
|
+
# because it's faster than View Templates and Partials
|
|
5
|
+
|
|
6
|
+
# SECURITY note
|
|
7
|
+
# Prepare your data on server side for rendering
|
|
8
|
+
# or use h.html_escape(node.content)
|
|
9
|
+
# for escape potentially dangerous content
|
|
10
|
+
module RenderCommentsTreeHelper
|
|
11
|
+
module Render
|
|
12
|
+
class << self
|
|
13
|
+
attr_accessor :h, :options
|
|
14
|
+
|
|
15
|
+
# Main Helpers
|
|
16
|
+
def controller
|
|
17
|
+
@options[:controller]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def t str
|
|
21
|
+
controller.t str
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Render Helpers
|
|
25
|
+
def visible_draft?
|
|
26
|
+
controller.try(:comments_view_token) == @comment.view_token
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def moderator?
|
|
30
|
+
controller.try(:current_user).try(:comments_moderator?, @comment)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Render Methods
|
|
34
|
+
def render_node(h, options)
|
|
35
|
+
@h, @options = h, options
|
|
36
|
+
@comment = options[:node]
|
|
37
|
+
|
|
38
|
+
@max_reply_depth = options[:max_reply_depth] || TheComments.config.max_reply_depth
|
|
39
|
+
|
|
40
|
+
if @comment.draft?
|
|
41
|
+
draft_comment
|
|
42
|
+
else @comment.published?
|
|
43
|
+
published_comment
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def draft_comment
|
|
48
|
+
if visible_draft? || moderator?
|
|
49
|
+
published_comment
|
|
50
|
+
else
|
|
51
|
+
"<li class='draft'>
|
|
52
|
+
<div class='comment draft' id='comment_#{@comment.anchor}'>
|
|
53
|
+
#{ t('the_comments.waiting_for_moderation') }
|
|
54
|
+
#{ h.link_to '#', '#comment_' + @comment.anchor }
|
|
55
|
+
</div>
|
|
56
|
+
#{ children }
|
|
57
|
+
</li>"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def published_comment
|
|
62
|
+
"<li>
|
|
63
|
+
<div id='comment_#{@comment.anchor}' class='comment #{@comment.state}' data-comment-id='#{@comment.to_param}'>
|
|
64
|
+
<div>
|
|
65
|
+
#{ avatar }
|
|
66
|
+
#{ userbar }
|
|
67
|
+
<div class='cbody'>#{ @comment.content }</div>
|
|
68
|
+
#{ reply }
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div class='form_holder'></div>
|
|
73
|
+
#{ children }
|
|
74
|
+
</li>"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def avatar
|
|
78
|
+
"<div class='userpic'>
|
|
79
|
+
<img src='#{ @comment.avatar_url }' alt='userpic' />
|
|
80
|
+
#{ controls }
|
|
81
|
+
</div>"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def userbar
|
|
85
|
+
anchor = h.link_to('#', '#comment_' + @comment.anchor)
|
|
86
|
+
title = @comment.title.blank? ? t('the_comments.guest_name') : @comment.title
|
|
87
|
+
"<div class='userbar'>#{ title } #{ anchor }</div>"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def moderator_controls
|
|
91
|
+
if moderator?
|
|
92
|
+
h.link_to(t('the_comments.edit'), h.edit_comment_url(@comment), class: :edit)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def reply
|
|
97
|
+
if @comment.depth < (@max_reply_depth - 1)
|
|
98
|
+
"<p class='reply'><a href='#' class='reply_link'>#{ t('the_comments.reply') }</a>"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def controls
|
|
103
|
+
"<div class='controls'>#{ moderator_controls }</div>"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def children
|
|
107
|
+
"<ol class='nested_set'>#{ options[:children] }</ol>"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class Comment < ActiveRecord::Base
|
|
2
|
+
include TheComments::Comment
|
|
3
|
+
# ---------------------------------------------------
|
|
4
|
+
# Define comment's avatar url
|
|
5
|
+
# Usually we use Comment#user (owner of comment) to define avatar
|
|
6
|
+
# @blog.comments.includes(:user) <= use includes(:user) to decrease queries count
|
|
7
|
+
# comment#user.avatar_url
|
|
8
|
+
# ---------------------------------------------------
|
|
9
|
+
|
|
10
|
+
# public
|
|
11
|
+
# ---------------------------------------------------
|
|
12
|
+
# Simple way to define avatar url
|
|
13
|
+
#
|
|
14
|
+
# def avatar_url
|
|
15
|
+
# src = id.to_s
|
|
16
|
+
# src = title unless title.blank?
|
|
17
|
+
# src = contacts if !contacts.blank? && /@/ =~ contacts
|
|
18
|
+
# hash = Digest::MD5.hexdigest(src)
|
|
19
|
+
# "https://2.gravatar.com/avatar/#{hash}?s=42&d=https://identicons.github.com/#{hash}.png"
|
|
20
|
+
# end
|
|
21
|
+
# ---------------------------------------------------
|
|
22
|
+
|
|
23
|
+
# private
|
|
24
|
+
# ---------------------------------------------------
|
|
25
|
+
# Define your content filters
|
|
26
|
+
# gem 'RedCloth'
|
|
27
|
+
# gem 'sanitize'
|
|
28
|
+
# gem 'MySmilesProcessor'
|
|
29
|
+
#
|
|
30
|
+
# def prepare_content
|
|
31
|
+
# text = self.raw_content
|
|
32
|
+
# text = RedCloth.new(text).to_html
|
|
33
|
+
# text = MySmilesProcessor.new(text)
|
|
34
|
+
# text = Sanitize.clean(text, Sanitize::Config::RELAXED)
|
|
35
|
+
# self.content = text
|
|
36
|
+
# end
|
|
37
|
+
# ---------------------------------------------------
|
|
38
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
module TheComments
|
|
2
|
+
module Comment
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
scope :active, -> { with_state [:draft, :published] }
|
|
7
|
+
scope :with_users, -> { includes(:user) }
|
|
8
|
+
|
|
9
|
+
# Nested Set
|
|
10
|
+
acts_as_nested_set scope: [:commentable_type, :commentable_id]
|
|
11
|
+
|
|
12
|
+
# simple sort scopes
|
|
13
|
+
include ::TheSimpleSort::Base
|
|
14
|
+
|
|
15
|
+
# TheSortableTree
|
|
16
|
+
include ::TheSortableTree::Scopes
|
|
17
|
+
|
|
18
|
+
# Comments State Machine
|
|
19
|
+
include TheComments::CommentStates
|
|
20
|
+
|
|
21
|
+
validates :raw_content, presence: true
|
|
22
|
+
|
|
23
|
+
# relations
|
|
24
|
+
belongs_to :user
|
|
25
|
+
belongs_to :holder, class_name: :User
|
|
26
|
+
belongs_to :commentable, polymorphic: true
|
|
27
|
+
|
|
28
|
+
# callbacks
|
|
29
|
+
before_create :define_holder, :define_default_state, :define_anchor, :denormalize_commentable
|
|
30
|
+
after_create :update_cache_counters
|
|
31
|
+
before_save :prepare_content
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def header_title
|
|
35
|
+
title.present? ? title : I18n.t('the_comments.guest_name')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def user_name
|
|
39
|
+
user.try(:username) || user.try(:login) || header_title
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def avatar_url
|
|
43
|
+
src = id.to_s
|
|
44
|
+
src = title unless title.blank?
|
|
45
|
+
src = contacts if !contacts.blank? && /@/ =~ contacts
|
|
46
|
+
hash = Digest::MD5.hexdigest(src)
|
|
47
|
+
"https://2.gravatar.com/avatar/#{hash}?s=42&d=https://identicons.github.com/#{hash}.png"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def mark_as_spam
|
|
51
|
+
count = self_and_descendants.update_all({spam: true})
|
|
52
|
+
update_spam_counter
|
|
53
|
+
count
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def mark_as_not_spam
|
|
57
|
+
count = self_and_descendants.update_all({spam: false})
|
|
58
|
+
update_spam_counter
|
|
59
|
+
count
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def to_spam
|
|
63
|
+
mark_as_spam
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def update_spam_counter
|
|
69
|
+
holder.try :update_comcoms_spam_counter
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def define_anchor
|
|
73
|
+
self.anchor = SecureRandom.hex[0..5]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def define_holder
|
|
77
|
+
c = self.commentable
|
|
78
|
+
self.holder = c.is_a?(User) ? c : c.try(:user)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def define_default_state
|
|
82
|
+
self.state = TheComments.config.default_owner_state if user && user == holder
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def denormalize_commentable
|
|
86
|
+
self.commentable_title = commentable.try :commentable_title
|
|
87
|
+
self.commentable_state = commentable.try :commentable_state
|
|
88
|
+
self.commentable_url = commentable.try :commentable_url
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def prepare_content
|
|
92
|
+
self.content = self.raw_content
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Warn: increment! doesn't call validation =>
|
|
96
|
+
# before_validation filters doesn't work =>
|
|
97
|
+
# We have few unuseful requests
|
|
98
|
+
# I impressed that I found it and reduce DB requests
|
|
99
|
+
# Awesome logic pazzl! I'm really pedant :D
|
|
100
|
+
def update_cache_counters
|
|
101
|
+
user.try :recalculate_my_comments_counter!
|
|
102
|
+
|
|
103
|
+
if holder
|
|
104
|
+
holder.send :try, :define_denormalize_flags
|
|
105
|
+
holder.increment! "#{ state }_comcoms_count"
|
|
106
|
+
# holder.class.increment_counter("#{ state }_comcoms_count", holder.id)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
if commentable
|
|
110
|
+
commentable.send :define_denormalize_flags
|
|
111
|
+
commentable.increment! "#{ state }_comments_count"
|
|
112
|
+
# holder.class.increment_counter("#{ state }_comments_count", holder.id)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module TheComments
|
|
2
|
+
module CommentStates
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
# :draft | :published | :deleted
|
|
7
|
+
state_machine :state, :initial => TheComments.config.default_state do
|
|
8
|
+
|
|
9
|
+
# events
|
|
10
|
+
event :to_draft do
|
|
11
|
+
transition all - :draft => :draft
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
event :to_published do
|
|
15
|
+
transition all - :published => :published
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
event :to_deleted do
|
|
19
|
+
transition any - :deleted => :deleted
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# transition callbacks
|
|
23
|
+
after_transition any => any do |comment|
|
|
24
|
+
@comment = comment
|
|
25
|
+
@owner = comment.user
|
|
26
|
+
@holder = comment.holder
|
|
27
|
+
@commentable = comment.commentable
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# between draft and published
|
|
31
|
+
after_transition [:draft, :published] => [:draft, :published] do |comment, transition|
|
|
32
|
+
from = transition.from_name
|
|
33
|
+
to = transition.to_name
|
|
34
|
+
|
|
35
|
+
if @holder
|
|
36
|
+
@holder.send :try, :define_denormalize_flags
|
|
37
|
+
@holder.increment! "#{to}_comcoms_count"
|
|
38
|
+
@holder.decrement! "#{from}_comcoms_count"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if @commentable
|
|
42
|
+
@commentable.send :define_denormalize_flags
|
|
43
|
+
@commentable.increment! "#{to}_comments_count"
|
|
44
|
+
@commentable.decrement! "#{from}_comments_count"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# to deleted (cascade like query)
|
|
49
|
+
after_transition [:draft, :published] => :deleted do |comment|
|
|
50
|
+
ids = comment.self_and_descendants.map(&:id)
|
|
51
|
+
::Comment.where(id: ids).update_all(state: :deleted)
|
|
52
|
+
|
|
53
|
+
@owner.try :recalculate_my_comments_counter!
|
|
54
|
+
@holder.try :recalculate_comcoms_counters!
|
|
55
|
+
@commentable.try :recalculate_comments_counters!
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# from deleted
|
|
59
|
+
after_transition :deleted => [:draft, :published] do |comment, transition|
|
|
60
|
+
to = transition.to_name
|
|
61
|
+
comment.mark_as_not_spam
|
|
62
|
+
|
|
63
|
+
@owner.try :recalculate_my_comments_counter!
|
|
64
|
+
|
|
65
|
+
if @holder
|
|
66
|
+
@holder.send :try, :define_denormalize_flags
|
|
67
|
+
@holder.decrement! :deleted_comcoms_count
|
|
68
|
+
@holder.increment! "#{to}_comcoms_count"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if @commentable
|
|
72
|
+
@commentable.send :define_denormalize_flags
|
|
73
|
+
@commentable.decrement! :deleted_comments_count
|
|
74
|
+
@commentable.increment! "#{to}_comments_count"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module TheComments
|
|
2
|
+
module Commentable
|
|
3
|
+
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
has_many :comments, as: :commentable
|
|
8
|
+
|
|
9
|
+
# *define_denormalize_flags* - should be placed before title or url builder filters
|
|
10
|
+
before_validation :define_denormalize_flags
|
|
11
|
+
after_save :denormalize_for_comments, if: -> { !id_changed? }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Default Denormalization methods
|
|
15
|
+
# Overwrite it with your Application
|
|
16
|
+
def commentable_title
|
|
17
|
+
# My first blog post
|
|
18
|
+
try(:title) || TheComments.config.default_title
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def commentable_url
|
|
22
|
+
# /posts/1
|
|
23
|
+
['', self.class.to_s.tableize, self.to_param].join('/')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def commentable_state
|
|
27
|
+
# 'draft'
|
|
28
|
+
try(:state)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Helper methods
|
|
32
|
+
def comments_sum
|
|
33
|
+
published_comments_count + draft_comments_count
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def recalculate_comments_counters!
|
|
37
|
+
update_attributes!({
|
|
38
|
+
draft_comments_count: comments.with_state(:draft).count,
|
|
39
|
+
published_comments_count: comments.with_state(:published).count,
|
|
40
|
+
deleted_comments_count: comments.with_state(:deleted).count
|
|
41
|
+
})
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def define_denormalize_flags
|
|
47
|
+
@trackable_commentable_title = commentable_title
|
|
48
|
+
@trackable_commentable_state = commentable_state
|
|
49
|
+
@trackable_commentable_url = commentable_url
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def denormalization_fields_changed?
|
|
53
|
+
a = @trackable_commentable_title != commentable_title
|
|
54
|
+
b = @trackable_commentable_state != commentable_state
|
|
55
|
+
c = @trackable_commentable_url != commentable_url
|
|
56
|
+
a || b || c
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def denormalize_for_comments
|
|
60
|
+
if denormalization_fields_changed?
|
|
61
|
+
comments.update_all({
|
|
62
|
+
commentable_title: commentable_title,
|
|
63
|
+
commentable_state: commentable_state,
|
|
64
|
+
commentable_url: commentable_url
|
|
65
|
+
})
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module TheComments
|
|
2
|
+
module User
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
has_many :comcoms, class_name: :Comment, foreign_key: :holder_id
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def my_comments; ::Comment.where(user: self); end
|
|
10
|
+
|
|
11
|
+
%w[draft published deleted].each do |state|
|
|
12
|
+
define_method "my_#{state}_comments" do
|
|
13
|
+
my_comments.with_state state
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
define_method "#{state}_comcoms" do
|
|
17
|
+
comcoms.with_state state
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def my_spam_comments
|
|
22
|
+
my_comments.where(spam: true)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# I think we shouldn't to have my_deleted_comments cache counter
|
|
26
|
+
def recalculate_my_comments_counter!
|
|
27
|
+
dcount = my_draft_comments.count
|
|
28
|
+
pcount = my_published_comments.count
|
|
29
|
+
update_attributes!({
|
|
30
|
+
my_draft_comments_count: dcount,
|
|
31
|
+
my_published_comments_count: pcount,
|
|
32
|
+
my_comments_count: dcount + pcount
|
|
33
|
+
})
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def recalculate_comcoms_counters!
|
|
37
|
+
update_attributes!({
|
|
38
|
+
draft_comcoms_count: draft_comcoms.count,
|
|
39
|
+
published_comcoms_count: published_comcoms.count,
|
|
40
|
+
deleted_comcoms_count: deleted_comcoms.count
|
|
41
|
+
})
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def update_comcoms_spam_counter
|
|
45
|
+
update!(spam_comcoms_count: comcoms.where(spam: true).count)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def comments_sum
|
|
49
|
+
published_comments_count + draft_comments_count
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def comcoms_sum
|
|
53
|
+
published_comcoms_count + draft_comcoms_count
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|