diff --git a/Gemfile b/Gemfile index 09a70cfe5..da69551ab 100644 --- a/Gemfile +++ b/Gemfile @@ -25,7 +25,7 @@ gem 'sdoc', '~> 0.4.0', group: :doc gem 'devise' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' - +gem 'acts_as_commentable_with_threading' # Use Unicorn as the app server # gem 'unicorn' diff --git a/Gemfile.lock b/Gemfile.lock index 9124a06fd..601246774 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -38,8 +38,14 @@ GEM tzinfo (~> 1.1) acts-as-taggable-on (3.5.0) activerecord (>= 3.2, < 5) + acts_as_commentable_with_threading (2.0.0) + activerecord (>= 4.0) + activesupport (>= 4.0) + awesome_nested_set (>= 3.0) acts_as_votable (0.10.0) arel (6.0.2) + awesome_nested_set (3.0.2) + activerecord (>= 4.0.0, < 5) bcrypt (3.1.10) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) @@ -192,6 +198,7 @@ PLATFORMS DEPENDENCIES acts-as-taggable-on + acts_as_commentable_with_threading acts_as_votable byebug capybara diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb new file mode 100644 index 000000000..91a92e5ba --- /dev/null +++ b/app/controllers/comments_controller.rb @@ -0,0 +1,28 @@ +class CommentsController < ApplicationController + before_action :authenticate_user! + before_action :set_debate, :set_parent + + def create + comment = Comment.build(@debate, current_user, params[:comment][:body]) + comment.save! + comment.move_to_child_of(@parent) if reply? + redirect_to @debate, notice: "Comentario guardado" + end + + private + def comment_params + params.require(:comments).permit(:commentable_type, :commentable_id, :body) + end + + def set_debate + @debate = Debate.find(params[:debate_id]) + end + + def set_parent + @parent = Comment.find_parent(params[:comment]) + end + + def reply? + @parent.class == Comment + end +end \ No newline at end of file diff --git a/app/models/comment.rb b/app/models/comment.rb new file mode 100644 index 000000000..3f8dd83f4 --- /dev/null +++ b/app/models/comment.rb @@ -0,0 +1,19 @@ +class Comment < ActiveRecord::Base + acts_as_nested_set :scope => [:commentable_id, :commentable_type] + + validates :body, :presence => true + validates :user, :presence => true + + belongs_to :commentable, :polymorphic => true + belongs_to :user + + def self.build(commentable, user, body) + new commentable: commentable, + user_id: user.id, + body: body + end + + def self.find_parent(params) + params[:commentable_type].constantize.find(params[:commentable_id]) + end +end diff --git a/app/models/debate.rb b/app/models/debate.rb index 727d805e7..0bebf6b8e 100644 --- a/app/models/debate.rb +++ b/app/models/debate.rb @@ -1,8 +1,9 @@ require 'numeric' class Debate < ActiveRecord::Base acts_as_votable + acts_as_commentable acts_as_taggable - + belongs_to :author, class_name: 'User', foreign_key: 'author_id' validates :title, presence: true diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb new file mode 100644 index 000000000..a27a689c9 --- /dev/null +++ b/app/views/comments/_comment.html.erb @@ -0,0 +1,13 @@ +
+

<%= comment.user.name %>

+

hace <%= time_ago_in_words(comment.created_at) %>

+

<%= comment.body %>

+ +
+ <%= render comment.children %> +
+ +
+ <%= render 'comments/form', parent: comment %> +
+
\ No newline at end of file diff --git a/app/views/comments/_form.html.erb b/app/views/comments/_form.html.erb new file mode 100644 index 000000000..4ef99b4b0 --- /dev/null +++ b/app/views/comments/_form.html.erb @@ -0,0 +1,7 @@ +<%= form_for [@debate, Comment.new] do |f| %> + <%= f.text_area :body %> + <%= f.hidden_field :commentable_type, value: parent.class %> + <%= f.hidden_field :commentable_id, value: parent.id %> + + <%= f.submit 'Publicar comentario' %> +<% end %> \ No newline at end of file diff --git a/app/views/debates/show.html.erb b/app/views/debates/show.html.erb index 5319939bd..5efdd833a 100644 --- a/app/views/debates/show.html.erb +++ b/app/views/debates/show.html.erb @@ -25,7 +25,14 @@ <%= render 'shared/tags', debate: @debate %>

-
- <%= link_to 'Edit', edit_debate_path(@debate) %> | - <%= link_to 'Back', debates_path %> -
\ No newline at end of file +Deja tu comentario +<%= render 'comments/form', parent: @debate %> + +
+

Comentarios

+ <%= render @debate.root_comments %> +
+ +

+<%= link_to 'Edit', edit_debate_path(@debate) %> | +<%= link_to 'Back', debates_path %> \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 948beedb8..a527a7ad5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,6 +8,7 @@ Rails.application.routes.draw do root 'welcome#index' resources :debates do resources :votes, only: :create + resources :comments, only: :create end # Example of regular route: diff --git a/db/migrate/20150718091702_acts_as_commentable_with_threading_migration.rb b/db/migrate/20150718091702_acts_as_commentable_with_threading_migration.rb new file mode 100644 index 000000000..728b26b9e --- /dev/null +++ b/db/migrate/20150718091702_acts_as_commentable_with_threading_migration.rb @@ -0,0 +1,21 @@ +class ActsAsCommentableWithThreadingMigration < ActiveRecord::Migration + def self.up + create_table :comments, :force => true do |t| + t.integer :commentable_id + t.string :commentable_type + t.string :title + t.text :body + t.string :subject + t.integer :user_id, :null => false + t.integer :parent_id, :lft, :rgt + t.timestamps + end + + add_index :comments, :user_id + add_index :comments, [:commentable_id, :commentable_type] + end + + def self.down + drop_table :comments + end +end diff --git a/db/schema.rb b/db/schema.rb index a9958aba6..1a32c61a7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,24 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150718075337) do +ActiveRecord::Schema.define(version: 20150718091702) do + + create_table "comments", force: :cascade do |t| + t.integer "commentable_id" + t.string "commentable_type" + t.string "title" + t.text "body" + t.string "subject" + t.integer "user_id", null: false + t.integer "parent_id" + t.integer "lft" + t.integer "rgt" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "comments", ["commentable_id", "commentable_type"], name: "index_comments_on_commentable_id_and_commentable_type" + add_index "comments", ["user_id"], name: "index_comments_on_user_id" create_table "debates", force: :cascade do |t| t.string "title" diff --git a/spec/factories.rb b/spec/factories.rb index b34315ef0..9a7ce6181 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -18,6 +18,15 @@ FactoryGirl.define do association :votable, factory: :debate association :voter, factory: :user vote_flag true + + factory :comment do + commentable + user + body 'Comment body' + end + + factory :commentable do + debate end end \ No newline at end of file diff --git a/spec/features/comments_spec.rb b/spec/features/comments_spec.rb new file mode 100644 index 000000000..c4b0e163b --- /dev/null +++ b/spec/features/comments_spec.rb @@ -0,0 +1,73 @@ +require 'rails_helper' +include ActionView::Helpers::DateHelper + +feature 'Comments' do + + scenario 'Index' do + debate = create(:debate) + 3.times { create(:comment, commentable: debate) } + + visit debate_path(debate) + + expect(page).to have_css('.comment', count: 3) + + comment = Comment.first + within first('.comment') do + expect(page).to have_content comment.user.name + expect(page).to have_content time_ago_in_words(comment.created_at) + expect(page).to have_content comment.body + end + end + + scenario 'Create' do + user = create(:user) + debate = create(:debate) + + login_as(user) + visit debate_path(debate) + + fill_in 'comment_body', with: '¿Has pensado en esto...?' + click_button 'Publicar comentario' + + expect(page).to have_content 'Comentario guardado' + + within "#comments" do + expect(page).to have_content '¿Has pensado en esto...?' + end + end + + scenario 'Reply' do + citizen = create(:user, first_name: 'Ana') + manuela = create(:user, first_name: 'Manuela') + debate = create(:debate) + comment = create(:comment, commentable: debate, user: citizen) + + visit debate_path(debate) + login_as(manuela) + + within "#comment-#{comment.id}" do + fill_in 'comment_body', with: 'La semana que viene está hecho.' + click_button 'Publicar comentario' + end + + expect(page).to have_content 'Comentario guardado' + within "#comment-#{comment.id}" do + expect(page).to have_content 'La semana que viene está hecho.' + end + end + + scenario "N replies" do + debate = create(:debate) + parent = create(:comment, commentable: debate) + + 7.times do + create(:comment, commentable: debate). + move_to_child_of(parent) + parent = parent.children.first + end + + visit debate_path(debate) + expect(page).to have_css(".comment.comment.comment.comment.comment.comment.comment.comment") + end + +end \ No newline at end of file