Kali_89
Kali_89

Reputation: 617

Instance of Rails model only creatable through console

I've been following the tutorial on creating a Rails blog (http://www.roberthuberdeau.com/articles/4-How-to-create-a-blog-in-Ruby-on-Rails-3) and have basically got all the way to the end.

However, after all the migrations I'm now struggling. Whenever I complete the form I previously used to create articles, I can't see them on the index page. I've dug around and believe the root of the error is that I'm not saving any articles when I press 'create article'.

To test this, I created an article using the console and this is appearing as it should so I think the problem lies somewhere between the form that creates articles and the controller (though am happy to be corrected on this).

Whenever I try the following appears in the logs:

Started POST "/articles" for 127.0.0.1 at 2013-04-01 21:12:58 +0100
Processing by ArticlesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"XLeHm+4Tgd6n9vt4RxAQ5YVTbWTi+UnqkmBso9Iuo+4=", "article"=>{"title"=>"I rule", "body"=>"Change teams.", "tag_names"=>"kill", "published"=>"1"}, "commit"=>"Create Article"}
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
  Role Load (0.1ms)  SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id" = "roles_users"."role_id" WHERE "roles_users"."user_id" = 1 AND "roles"."name" = 'Admin' LIMIT 1
  Role Load (0.1ms)  SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id" = "roles_users"."role_id" WHERE "roles_users"."user_id" = 1 AND "roles"."name" = 'Moderator' LIMIT 1
  Role Load (0.1ms)  SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id" = "roles_users"."role_id" WHERE "roles_users"."user_id" = 1 AND "roles"."name" = 'Member' LIMIT 1
Redirected to http://localhost:3000/
Completed 302 Found in 5ms (ActiveRecord: 0.5ms)

With the database schema:

ActiveRecord::Schema.define(:version => 20130401171646) do

  create_table "articles", :force => true do |t|
    t.string   "title"
    t.text     "body"
    t.datetime "created_at",                    :null => false
    t.datetime "updated_at",                    :null => false
    t.integer  "user_id",                       :null => false
    t.boolean  "published",  :default => false
  end

  create_table "comments", :force => true do |t|
    t.integer  "article_id"
    t.string   "name"
    t.string   "email"
    t.text     "body"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  create_table "roles", :force => true do |t|
    t.string   "name"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  create_table "roles_users", :id => false, :force => true do |t|
    t.integer "role_id"
    t.integer "user_id"
  end

  create_table "taggings", :force => true do |t|
    t.integer  "article_id"
    t.integer  "tag_id"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  create_table "tags", :force => true do |t|
    t.string   "name"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  create_table "users", :force => true do |t|
    t.string   "email",                  :default => "", :null => false
    t.string   "encrypted_password",     :default => "", :null => false
    t.string   "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer  "sign_in_count",          :default => 0
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string   "current_sign_in_ip"
    t.string   "last_sign_in_ip"
    t.datetime "created_at",                             :null => false
    t.datetime "updated_at",                             :null => false
  end

  add_index "users", ["email"], :name => "index_users_on_email", :unique => true
  add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true

end

Articles controller:

class ArticlesController < ApplicationController
  before_filter :authenticate_user!, :except => [:index, :show]
  # GET /articles
  # GET /articles.xml
  def index
    @articles = Article.published.page(params[:page]).per(5).ordered

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

  # GET /articles/1
  # GET /articles/1.xml
  def show
    @article = Article.find(params[:id])
    @comment = Comment.new(:article=>@article)

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

  # GET /articles/new
  # GET /articles/new.xml
  def new
    @article = Article.new

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

  # GET /articles/1/edit
  def edit
    @article = Article.find(params[:id])
    authorize! :edit, @article
  end

  # POST /articles
  # POST /articles.xml
  def create
    authorize! :create, @article
    @article = Article.new(params[:article])
    @article.user_id = current_user.id

    respond_to do |format|
      if @article.save
        format.html { redirect_to(@article, :notice => 'Article was successfully created.') }
        format.xml  { render :xml => @article, :status => :created, :location => @article }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @article.errors, :status => :unprocessable_entity }
      end
    end
  end

  # PUT /articles/1
  # PUT /articles/1.xml
  def update
    @article = Article.find(params[:id])
    authorize! :update, @article
    respond_to do |format|
      if @article.update_attributes(params[:article])
        format.html { redirect_to(@article, :notice => 'Article was successfully updated.') }
        format.xml  { head :ok }
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @article.errors, :status => :unprocessable_entity }
      end
    end
  end

  # DELETE /articles/1
  # DELETE /articles/1.xml
  def destroy
    @article = Article.find(params[:id])
    authorize! :destroy, @article
    @article.destroy

    respond_to do |format|
      format.html { redirect_to(articles_url) }
      format.xml  { head :ok }
    end
  end


end

Articles model:

class Article < ActiveRecord::Base
  attr_accessible :body, :title, :tag_names
  has_many :comments, :dependent => :destroy
  has_many :taggings, :dependent => :destroy
  has_many :tags, :through => :taggings
  validates_presence_of :title, :body
  validates_uniqueness_of :title
  attr_writer :tag_names
  after_save :assign_tags
  validates_numericality_of :user_id  
  belongs_to :user
  scope :published, lambda {{:conditions => ['published = ?', true]}}
  scope :ordered, lambda {{:order => "Created_at DESC" }}
  def tag_names
    @tag_names || tags.map(&:name).join(' ')
  end

  private

  def assign_tags
    if @tag_names
      self.tags = @tag_names.split(/\,/).map do |name|
        Tag.find_or_create_by_name(name)
      end
    end
  end
end

And the form partial used to create the article:

<%= form_for(@article) do |f| %>
  <% if @article.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved:</h2>

      <ul>
      <% @article.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :title %><br />
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="field">
    <%= f.label :tag_names, "Tags" %>  
    <%= f.text_field :tag_names %>  
  </div>
  <div class="field">
    <%= check_box("article", "published" ) %>
    <%= "Publish article" %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Any help you can give me on this would be greatly appreciated.

By request:

Ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # guest user

    if user.role? :Admin
      can :manage, :all
      can :publish, Article
    elsif user.role? :Moderator
      can :read, [Article, Comment]
      can [:edit, :update], Comment
    elsif user.role? :Member
       can :read, :all
       can :create, [Article, Comment]
       can [:edit, :update], Comment
    end
  end
end

p.s. the only other error I can see (and I don't know if it's relevant or a different issue entirely) is that when trying to view an article (show.html.erb) I receive the following error:

Processing by ArticlesController#show as HTML
  Parameters: {"id"=>"1"}
  Article Load (0.2ms)  SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT 1  [["id", "1"]]
Completed 500 Internal Server Error in 44ms

ActiveModel::MassAssignmentSecurity::Error (Can't mass-assign protected attributes: article):
  app/controllers/articles_controller.rb:18:in `new'
  app/controllers/articles_controller.rb:18:in `show'

Upvotes: 1

Views: 261

Answers (2)

Jason Kim
Jason Kim

Reputation: 19051

The article is not being created because of this.

authorize! :create, @article

You will have to show us your ability model, ability.rb.

You might also want to try the obvious. bundle install and restart the server.

Upvotes: 0

Dan Wich
Dan Wich

Reputation: 4943

I'm guessing there's a problem with the authorize! :create, @article line in the ArticleController.create. At the point that that executes, @article isn't created yet.

Judging by the CanCan source, I think the following might do what you want:

def create
   authorize! :create, Article
   @article = Article.new(params[:article])
   @article.user_id = current_user.id
   ...

Upvotes: 1

Related Questions