Rails 3 - Display Submission Errors in the Polymorphic Comment Model

It’s rare for Rails 3 and Googling to each fail to solve the following problem, while most tutorials do not deal with processing errors.

I created a Rails 3 project with several types / models of content such as articles, blogs, etc. Each type of content has comments; all of them are stored in one comment table as an embedded resource and with polymorphic associations. There is only one action for comments, the “create” action, because there is no need for a show, etc., since it is a type of parental content and should just redraw this page when submitted.

Now I have most of this work, and comments are sent and published just fine, but the last remaining problem is errors when the user does not fill out the required field. If the fields are not filled, he should return to the parent page and display validation errors, such as Rails, usually with MVC.

The action of my comment controller looks like this, and this is what I tried first ...

def create
   @commentable = find_commentable
   @comment = @commentable.comments.build(params[:comment])

   respond_to do |format|
      if @comment.save
         format.html { redirect_to(@commentable, :notice => 'Comment was successfully created.') }
      else
         format.html { redirect_to @commentable }
         format.xml  { render :xml => @commentable.errors, :status => :unprocessable_entity }
      end
   end
end 

When you do not fill out anything and submit a comment form, the page redirects the corresponding parent element back to it, but not a single flash or anything is displayed. Now I understand why, as I understand it, the flash will not be saved on redirect_to, only on the render. Now here is the problem.

"create", "blogs/show" (. , , , ). "else" ...

else
   format.html { render 'blogs/show' }
   format.xml  { render :xml => @commentable.errors, :status => :unprocessable_entity }
end

, , " [...]/app/views/blogs/show.html.erb, # 1 : undefined `title ' nil: NilClass."

URL-, , , ... , /blogs/the -title-of-my-article ( friendly_id), /blogs/the -title- --/. , "" .

, , ? ?

, , route.rb / ...

resources :blogs, :only => [:show] do
   resources :comments, :only => [:create]
end
+3
3

, , , , , / , . AJAX, , .

, , , , , , .

, , , Rails. , -, , , . , , , - .

comments_controller.rb

class CommentsController < ApplicationController
    # this include will bring all the Text Helper methods into your Controller
    include ActionView::Helpers::TextHelper

    def create
        @commentable = find_commentable
        @comment = @commentable.comments.build(params[:comment])

        respond_to do |format|
            if @comment.save
                format.html { redirect_to(@commentable, :notice => 'Comment was successfully created.') }
            else

                # Transform class of commentable into pluralized content type
                content_type = find_commentable.class.to_s.downcase.pluralize

                # Choose appropriate instance variable based on @commentable, rendered page won't work without it
                if content_type == 'blogs'
                    @blog = @commentable
                elsif content_type == 'articles'
                    @article = @commentable
                end

                format.html { render "#{content_type}/show" }
                format.xml  { render :xml => @commentable.errors, :status => :unprocessable_entity }
            end
        end
    end 

    private

    # Gets the ID/type of parent model, see Comment#create in controller
    def find_commentable
        params.each do |name, value|
            if name =~ /(.+)_id$/
                return $1.classify.constantize.find(value)
            end
        end
    end
end

articles_controller.rb

class ArticlesController < ApplicationController

  def show
    @article = Article.where(:status => 1).find_by_cached_slug(params[:id])
    @comment = Comment.new

    # On another content type like blogs_controller.rb, replace with appropriate instance variable
    @content = @article

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @article }
    end
  end

end

show.html.erb ( - )

<h1><%= @article.title %></h1>

<%= @article.body.html_safe %>

<%= render :partial => 'shared/comments', :locals => { :commentable => @article } %>

shared/_comments.html.erb( , )

<%= form_for([commentable, @comment]) do |f| %>
    <h3>Post a new comment</h3>

    <%= render :partial => 'shared/errors', :locals => { :content => @comment } %>  

    <div class="field">
        <%= f.label :name, :value => params[:name] %>
        <%= f.text_field :name, :class => 'textfield' %>
    </div>

    <div class="field">
        <%= f.label :mail, :value => params[:mail] %>
        <%= f.text_field :mail, :class => 'textfield'  %>
    </div>

    <div class="field">
        <%= f.text_area :body, :rows => 10, :class => 'textarea full', :value => params[:body] %>
    </div>

    <%= f.submit :class => 'button blue' %>
<% end %>

shared/_errors.html.erb( , , .., )

<% if content.errors.any? %>
    <div class="flash error">
        <p><strong><%= pluralize(content.errors.count, "error") %> prohibited this page from being saved:</strong></p>
        <ul>
            <% content.errors.full_messages.each do |msg| %>
                <li><%= msg %></li>
            <% end %>
        </ul>
    </div>
<% end %>
+3

, blogs/show @blog, , .

redirect_to, . -, , , . .

0

I reorganized @Shannon's answer a bit to make it more dynamic. In my find_parent method, I grab the URL and select the name of the controller. In the create method, I create an instance_variable_set that creates a dynamic variable for articles (@article) or Blogs (@blog) or whatever.

Hope you enjoy what I did? Please let me know if you have any doubts or something can be improved?

def create
    @comment = @commentable.comments.new(params[:comment])
    if @comment.save
        redirect_to @commentable, notice: "Comment created."
    else
        content_type = find_parent
        instance_variable_set "@#{content_type.singularize}".to_sym, @commentable
        @comments = @commentable.comments

        render "#{content_type}/show"
    end
end

def find_parent
    resource = request.path.split('/')[1]
    return resource.downcase
end
0
source

Source: https://habr.com/ru/post/1792914/


All Articles