Rails 4: can't update polymorphic nested resource through form - ruby-on-rails

Just in the final stages of transferring an app stuck in Rails 2 on a shared server to Rails 4 on a VPS, but stuck on one thing.
I have an Image model that uses Carrierwave to upload and display attached images. The Image model has a polymorphic association to a couple of other models. The host and associated models are updated together on a combined multipart form.
The form works perfectly well for creating and editing the primary model and for creating and attaching new images, but the image attributes can't be edited, and no error is thrown to help.
Here are the relevant parts of my Image model:
class Image < ActiveRecord::Base
belongs_to :imagings, polymorphic: true, touch: true
mount_uploader :image, ImageUploader, mount_on: :image_file_name
end
And my Review model:
class Review < ActiveRecord::Base
has_many :images, as: :imagings, dependent: :destroy
accepts_nested_attributes_for :images, reject_if: lambda { |t| t['image'].nil? }, allow_destroy: true
end
And my Reviews Controller:
class ReviewsController < ApplicationController
def edit
#review = Review.find(params[:id])
3.times {#review.images.build}
end
def update
#review = Review.find(params[:id])
if #review.update(reviews_params)
redirect_to #review, notice: "Successfully updated review."
else
render :edit
end
end
private
def reviews_params
params.require(:review).permit(:title, :authors, :venue, :startdate, :enddate, :body, :approved, :company, :user_id, { images_attributes: [:title, :credits, :image, :_destroy, :id] })
end
end
I can add images to existing reviews or to a new review perfectly well with this setup, but if I try to change text fields for an existing image, the parameters show in the console but they are ignored:
Started PATCH "/reviews/492" for 127.0.0.1 at 2014-07-16 11:03:42 +0100
Processing by ReviewsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"HvrsCAXXvO4zIqTBw05qSUwU9I2241DXyilhswEbU9o=", "review"=>{"title"=>"Here's a new review", "authors"=>"", "company"=>"", "venue"=>"", "startdate"=>"2014-07-16", "enddate"=>"2014-07-31", "body"=>"<p>Something</p>", "images_attributes"=>{"0"=>{"title"=>"Doggie", "credits"=>"Icons galore", "_destroy"=>"0", "id"=>"1239"}, "1"=>{"title"=>"", "credits"=>""}, "2"=>{"title"=>"", "credits"=>""}, "3"=>{"title"=>"", "credits"=>""}}, "user_id"=>"4", "approved"=>"1"}, "commit"=>"Update Review", "id"=>"492"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
Review Load (0.3ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" = $1 LIMIT 1 [["id", 492]]
(12.4ms) BEGIN
Image Load (0.4ms) SELECT "images".* FROM "images" WHERE "images"."imagings_id" = $1 AND "images"."imagings_type" = $2 AND "images"."id" IN (1239) [["imagings_id", 492], ["imagings_type", "Review"]]
(0.2ms) COMMIT
Redirected to http://127.0.0.1:3000/reviews/492
Completed 302 Found in 27ms (ActiveRecord: 13.6ms)
You can see here how the images attribute for credit has the change to "Icons galore", but the SQL only fetches the linked image without updating it. Contrast this with the following update to the main model which does a select followed by an update between the BEGIN and COMMIT.
Started PATCH "/reviews/492" for 127.0.0.1 at 2014-07-16 11:26:01 +0100
Processing by ReviewsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"HvrsCAXXvO4zIqTBw05qSUwU9I2241DXyilhswEbU9o=", "review"=>{"title"=>"Here's a new review", "authors"=>"", "company"=>"", "venue"=>"", "startdate"=>"2014-07-16", "enddate"=>"2014-07-31", "body"=>"<p>Something else</p>", "images_attributes"=>{"0"=>{"title"=>"Doggie", "credits"=>"Icons", "_destroy"=>"0", "id"=>"1239"}, "1"=>{"title"=>"", "credits"=>""}, "2"=>{"title"=>"", "credits"=>""}, "3"=>{"title"=>"", "credits"=>""}}, "user_id"=>"4", "approved"=>"1"}, "commit"=>"Update Review", "id"=>"492"}
User Load (6.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
Review Load (0.3ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" = $1 LIMIT 1 [["id", 492]]
(18.1ms) BEGIN
Image Load (0.5ms) SELECT "images".* FROM "images" WHERE "images"."imagings_id" = $1 AND "images"."imagings_type" = $2 AND "images"."id" IN (1239) [["imagings_id", 492], ["imagings_type", "Review"]]
SQL (5.9ms) UPDATE "reviews" SET "body" = $1, "updated_at" = $2 WHERE "reviews"."id" = 492 [["body", "<p>Something else</p>"], ["updated_at", "2014-07-16 10:26:01.455481"]]
(24.8ms) COMMIT
Redirected to http://127.0.0.1:3000/reviews/492
Completed 302 Found in 120ms (ActiveRecord: 56.0ms)
Yet if I try to make the same update in the Rails console, it sometimes works perfectly:
2.1.1 :062 > review = Review.find(492)
Review Load (0.5ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" = $1 LIMIT 1 [["id", 492]]
=> #<Review id: 492, title: "Here's a new review", authors: "", venue: "", startdate: "2014-07-16", enddate: "2014-07-31", body: "<p>Something else</p>", approved: true, created_at: "2014-07-16 09:38:33", updated_at: "2014-07-16 10:26:01", user_id: 4, company: "">
2.1.1 :064 > review.images.first.title = "Doggies galore"
=> "Doggies galore"
2.1.1 :066 > review.save
(0.8ms) BEGIN
SQL (7.5ms) UPDATE "images" SET "title" = $1, "updated_at" = $2 WHERE "images"."id" = 1239 [["title", "Doggies galore"], ["updated_at", "2014-07-16 10:32:39.109784"]]
Review Load (0.3ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" = $1 LIMIT 1 [["id", 492]]
SQL (1.7ms) UPDATE "reviews" SET "updated_at" = '2014-07-16 10:32:39.129314' WHERE "reviews"."id" = 492
(1.6ms) COMMIT
=> true
And sometimes doesn't:
2.1.1 :082 > review = Review.find(492)
Review Load (0.3ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" = $1 LIMIT 1 [["id", 492]]
=> #<Review id: 492, title: "Here's a new review", authors: "", venue: "", startdate: "2014-07-16", enddate: "2014-07-31", body: "<p>Something else</p>", approved: true, created_at: "2014-07-16 09:38:33", updated_at: "2014-07-16 10:38:30", user_id: 4, company: "">
2.1.1 :083 > review.images.first.credits = "Icons galore"
Image Load (0.5ms) SELECT "images".* FROM "images" WHERE "images"."imagings_id" = $1 AND "images"."imagings_type" = $2 ORDER BY "images"."id" ASC LIMIT 1 [["imagings_id", 492], ["imagings_type", "Review"]]
=> "Icons galore"
2.1.1 :084 > review.save!
(0.2ms) BEGIN
(0.3ms) COMMIT
=> true
2.1.1 :085 > review.images.first
Image Load (0.8ms) SELECT "images".* FROM "images" WHERE "images"."imagings_id" = $1 AND "images"."imagings_type" = $2 ORDER BY "images"."id" ASC LIMIT 1 [["imagings_id", 492], ["imagings_type", "Review"]]
=> #<Image id: 1239, title: "Doggies", credits: "Icons", created_at: "2014-07-16 09:38:33", updated_at: "2014-07-16 10:38:30", image_file_name: "ddad7b05-3430-49ed-b129-fd484ac793ad.gif", image_content_type: nil, image_file_size: nil, image_updated_at: nil, imagings_id: 492, imagings_type: "Review", original_filename: "Doggie.gif">
2.1.1 :086 >
I know I'm missing something obvious. Can anyone help please?

I think that you need to make a small change in reviews_params def as follows:
def reviews_params
params.require(:review).permit(:title, :authors, :venue, :startdate, :enddate, :body, :approved, :company, :user_id, images_attributes => [:title, :credits, :image, :_destroy, :id] )
end
Does that work?

i don't think you need the curly braces. Try removing them. Usually nested attributes are listed as a symbol which maps to an array of symbols(attributes). They are separated from attributes of the model in question with just a comma. So something such as,
params.require(:review).permit(:title, :authors, :venue, :startdate, :enddate, :body, :approved, :company, :user_id, :images_attributes => [:title, :credits, :image, :_destroy, :id] ) should work.

Related

Rails ActionController::UnpermittedParameters found unpermitted parameter when param present

I have the following form that is trying to load in checkboxes with labels all regions and allow those to be saved to logos on the Logo form page.
= tb_form_for [:admin, #logo],
remote: true,
data: { errors: :inline, success: admin_logos_path } do |f|
= tb_form_errors(f.object, :base)
= f.tb_text_field :name
= f.tb_check_box :home_page, class: 'checkmark'
= f.tb_file_field :logo_photo
= image_tag #logo.logo_photo, style: 'padding-left: 180px; padding-bottom: 20px;' if #logo.logo_photo.present?
= f.collection_check_boxes :region_id, Region.all, :id, :name, checked: Region.all.map(&:id) do |x|
div
= x.check_box
= x.label
= f.tb_save_buttons('Logo', admin_logos_path)
The page loads properly but the error cames with saving I end up with
ActionController::UnpermittedParameters (found unpermitted parameter: :region_id)
Cool so I check my LogosController and I have the following:
class LogosController < ApplicationController
before_action :load_logo, only: [:show, :edit, :update, :destroy]
def create
#logo = Logo.new(logo_params)
flash[:notice] = 'Created' if #logo.save
respond_with #logo
end
def edit
respond_with #logo
end
def update
flash[:notice] = 'Updated' if #logo.update_attributes(logo_params)
respond_with #logo
end
private
def logo_params
params.require(:logo).permit(:name, :home_page, :user, :region, :logo_photo, :region_id, :user_id)
end
end
I've tried several variations on the first argument being passed:
#logo.region results in
found unpermitted parameter: :Region::ActiveRecord_Associations_CollectionProxy:0x00007f81a79a81e0>
:region results in
found unpermitted parameter: :region
#logo results in
found unpermitted parameter: :Logo:0x00007f81a34da8
#logo.region_id ends up with a Bad Request
expected Array (got Rack::QueryParser::Params) for param `logo'
How do I accurately hit this?
Edit:
From the log:
Parameters: {"id"=>"12"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
SQL (0.5ms) UPDATE "users" SET "perishable_token" = $1, "last_request_at" = $2, "updated_at" = $3 WHERE "spud_users"."id" = $4 [["perishable_token", "aFl2KBnsphPP4I6CwXO2"], ["last_request_at", "2018-08-31 15:48:15.999796"], ["updated_at", "2018-08-31 15:48:16.000920"], ["id", 1]]
(5.4ms) COMMIT
Logo Load (0.5ms) SELECT "logos".* FROM "logos" WHERE "logos"."id" = $1 LIMIT $2 [["id", 12], ["LIMIT", 1]]
Rendering admin/logos/edit.html.slim within layouts/admin/detail
Region Load (0.5ms) SELECT "regions".* FROM "regions"
CACHE Region Load (0.0ms) SELECT "regions".* FROM "regions"
Rendered admin/logos/_form.html.slim (111.2ms)
Log when submitting the form:
Started PATCH "/admin/logos/12" for 127.0.0.1 at 2018-09-04 09:02:43 -0400
Processing by Admin::LogosController#update as JSON
Parameters: {"utf8"=>"✓", "logo"=>{"name"=>"Mr-T", "home_page"=>"1", "region_id"=>["", "1", "3", "4", "2"]}, "commit"=>"Save Logo", "id"=>"12"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Logo Load (0.2ms) SELECT "logos".* FROM "logos" WHERE "logos"."id" = $1 LIMIT $2 [["id", 12], ["LIMIT", 1]]
Completed 500 Internal Server Error in 4ms (ActiveRecord: 0.5ms)
ActionController::UnpermittedParameters (found unpermitted parameter: :region_id):
app/controllers/admin/logos_controller.rb:48:in `logo_params'
app/controllers/admin/logos_controller.rb:32:in `update'
Change ":region" by ":region_id" on "logo_params" like this
params.require(:logo).permit(:name, :home_page, :user, :region_id, :logo_photo,:user_id)

Error while setting up tagging for multiple models

I'm trying to setup multiple tagging in my app. I have used a single tag and tagging model which works fine for tagging a single model.
However, after setting up the association, i got this error when i wanted to create a new question undefined method map for nil:NilClass which actually points to <%= select_tag(:kategory_id, options_for_select(#kategories), :prompt => "Select Category", class: "form-control") %>. I don't know why it's pointing to this since it has nothing to do with the tagging. I might have made a mistake somewhere which i could not figure out.
Here is how i do the setup.
I added taggable_id and taggable_type to taggings table.
class AddAttributesToTaggings < ActiveRecord::Migration[5.1]
def change
add_column :taggings, :taggable_id, :integer
add_column :taggings, :taggable_type, :string
add_index :taggings, [:taggable_type, :taggable_id]
end
end
Tag.rb
class Tag < ApplicationRecord
has_many :taggings
has_many :posts, through: :taggings, source: :taggable, source_type: Post
has_many :questions, through: :taggings, source: :taggable, source_type: Question
has_many :user_tags, dependent: :destroy
has_many :users, through: :user_tags
extend FriendlyId
friendly_id :name, use: :slugged
def should_generate_new_friendly_id?
name_changed?
end
end
tagging.rb
class Tagging < ApplicationRecord
belongs_to :tag
belongs_to :taggable, :polymorphic => true
end
Question.rb
class Question < ApplicationRecord
belongs_to :user
belongs_to :kategory
validates :title, :content, presence: true
is_impressionable counter_cache: true, unique: :all
Question.order('impressions_count DESC')
scope :most_recent, -> { order(created_at: :desc) }
extend FriendlyId
friendly_id :title, use: :slugged
def should_generate_new_friendly_id?
title_changed?
end
include Taggable
def related_questions
Question.joins(:tags).where(tags: { id: self.tags.pluck(:id) }).where.not(id: self.id)
end
end
models/concerns/taggable.rb
module Taggable
extend ActiveSupport::Concern
included do
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
def tag_list
self.tags.collect do |tag|
tag.name
end.join(", ")
end
def tag_list=(tags_string)
tag_names = tags_string.split(",").collect{|s| s.strip.downcase}.uniq
new_or_found_tags = tag_names.collect { |name| Tag.friendly.find_or_create_by(name: name) }
self.tags = new_or_found_tags
end
end
tags and taggings table in schema.rb
create_table "taggings", force: :cascade do |t|
t.bigint "tag_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "taggable_id"
t.string "taggable_type"
t.index ["tag_id"], name: "index_taggings_on_tag_id"
t.index ["taggable_type", "taggable_id"], name: "index_taggings_on_taggable_type_and_taggable_id"
end
create_table "tags", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.index ["slug"], name: "index_tags_on_slug", unique: true
end
questions/_form
<%= form_with(model: question, local: true) do |form| %>
<% if question.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(question.errors.count, "error") %> prohibited this question from being saved:</h2>
<ul>
<% question.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<b><%= form.label "Category" %></b>
<%= select_tag(:kategory_id, options_for_select(#kategories), :prompt => "Select Category", class: "form-control") %>
</div>
<br>
<div class="field">
<b><%= form.label :title %></b>
<%= form.text_field :title, id: :question_title, class: "form-control" %>
</div>
<br>
<div class="field">
<%= form.label :content %>
<%= form.text_area :content, id: :question_content, class: "form-control" %>
</div>
<br>
<div>
<%= form.label :tag_list %>
<%= form.text_field :tag_list %>
</div>
<br>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
questions_controller.rb
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
before_action :find_contacts
before_action :find_kategory
impressionist :actions=>[:show,:index]
# GET /questions
# GET /questions.json
def index
if params[:kategory].blank?
#questions = Question.most_recent
else
#kategory_id = Kategory.find_by(name: params[:kategory]).id
#questions = Question.where(:kategory_id => #kategory_id).most_recent
end
end
# GET /questions/1
# GET /questions/1.json
def show
impressionist(#question)
#most_viewed = Question.order('impressions_count DESC').take(20)
end
# GET /questions/new
def new
#question = current_user.questions.build
#kategories = Kategory.all.map{ |k| [k.name, k.id] }
end
# GET /questions/1/edit
def edit
#kategories = Kategory.all.map{ |k| [k.name, k.id] }
end
# POST /questions
# POST /questions.json
def create
#question = current_user.questions.build(question_params)
#question.kategory_id = params[:kategory_id]
respond_to do |format|
if #question.save
flash[:success] = 'Your question was successfully posted.'
format.html { redirect_to #question }
format.json { render :show, status: :created, location: #question }
else
format.html { render :new }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /questions/1
# PATCH/PUT /questions/1.json
def update
respond_to do |format|
if #question.update(question_params)
flash[:success] = 'Your question was successfully updated.'
format.html { redirect_to #question }
format.json { render :show, status: :ok, location: #question }
else
format.html { render :edit }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# DELETE /questions/1
# DELETE /questions/1.json
def destroy
#question.destroy
respond_to do |format|
flash[:success] = 'Your question has been deleted.'
format.html { redirect_to questions_url }
format.json { head :no_content }
end
end
def most_viewed
#questions = Question.order('impressions_count DESC').take(20)
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.friendly.includes(:tags).find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params.require(:question).permit(:title, :content, :kategory_id, :tag_list)
end
def find_contacts
#contacts = user_signed_in? ? current_user.all_active_contacts : ''
end
def find_kategory
#questions = Kategory.where(:kategory_id => #kategory_id)
end
end
UPDATE:**I'm now able to save a question without adding any tag but with tags added, I get a form error that tagging is invalid. **Tags are properly loaded when I edit a question.
Error Log:
Started POST "/questions" for 127.0.0.1 at 2018-07-13 12:01:01 +0100
Processing by QuestionsController#create as HTML
Parameters: {"utf8"=>"√", "authenticity_token"=>"hgLn8bzd6fVSzCIPtRd3P0Jp/LEso
/h5cKNBY0a80x3i4RF6tpLlRyqmls3xRFryqyP1/s/rLDVIt9C1/uK+qw==", "kategory_id"=>"1"
, "question"=>{"title"=>"Help!!! My Dell Inspiron 6400 is hanging", "content"=>"
Lorem ipsum dolor amet seitan offal ethical, beard viral lo-fi put a bird on it
salvia actually yr. Ethical ennui pitchfork fanny pack, gentrify seitan sartoria
l bespoke. Viral 90's church-key swag, you probably haven't heard of them banh m
i intelligentsia brunch DIY iceland wolf pitchfork. Everyday carry photo booth n
ormcore XOXO tumblr portland.\r\n\r\nBrooklyn heirloom kombucha, edison bulb leg
gings hell of DIY chartreuse austin tacos bitters. Blog hexagon copper mug blue
bottle cray. Post-ironic direct trade kale chips mumblecore. Craft beer squid cr
onut vape, hoodie bitters succulents ramps snackwave vegan small batch brunch ch
ia food truck umami. Chillwave blue bottle viral raclette authentic health goth
vape coloring book cardigan 3 wolf moon taxidermy. Gluten-free tote bag hella, l
omo pinterest direct trade gastropub tattooed. Iceland hammock post-ironic, bush
wick cornhole tumeric dreamcatcher blog palo santo chartreuse 90's food truck sy
nth chicharrones.", "tag_list"=>"hardware"}, "commit"=>"Create Question"}
User Load (2.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDE
R BY "users"."id" ASC LIMIT $2 [["id", 3], ["LIMIT", 1]]
Private::Conversation Load (2.9ms) SELECT "private_conversations".* FROM "pri
vate_conversations" WHERE ("private_conversations"."recipient_id" = $1 OR "priva
te_conversations"."sender_id" = $2) [["recipient_id", 3], ["sender_id", 3]]
Private::Message Load (2.0ms) SELECT "private_messages".* FROM "private_messa
ges" WHERE "private_messages"."conversation_id" IN (2, 3, 4, 6)
Group::Conversation Load (3.9ms) SELECT "group_conversations".* FROM "group_c
onversations" INNER JOIN "group_conversations_users" ON "group_conversations"."i
d" = "group_conversations_users"."conversation_id" WHERE "group_conversations_us
ers"."user_id" = $1 [["user_id", 3]]
Group::Message Load (2.0ms) SELECT "group_messages".* FROM "group_messages" W
HERE "group_messages"."conversation_id" = 1
User Load (2.0ms) SELECT "users".* FROM "users" WHERE "users"."id" IN (2, 3,
5)
(40.0ms) SELECT "group_conversations"."id" FROM "group_conversations" INNER
JOIN "group_conversations_users" ON "group_conversations"."id" = "group_conversa
tions_users"."conversation_id" WHERE "group_conversations_users"."user_id" = $1
[["user_id", 3]]
CACHE (0.0ms) SELECT "group_conversations"."id" FROM "group_conversations" I
NNER JOIN "group_conversations_users" ON "group_conversations"."id" = "group_con
versations_users"."conversation_id" WHERE "group_conversations_users"."user_id"
= $1 [["user_id", 3]]
User Load (2.9ms) SELECT "users".* FROM "users" INNER JOIN "contacts" ON "use
rs"."id" = "contacts"."contact_id" WHERE "contacts"."user_id" = $1 AND "contacts
"."accepted" = $2 [["user_id", 3], ["accepted", "t"]]
User Load (4.9ms) SELECT "users".* FROM "users" INNER JOIN "contacts" ON "use
rs"."id" = "contacts"."user_id" WHERE "contacts"."contact_id" = $1 AND "contacts
"."accepted" = $2 ORDER BY "users"."created_at" ASC [["contact_id", 3], ["accep
ted", "t"]]
Tag Load (2.0ms) SELECT "tags".* FROM "tags" WHERE "tags"."name" = $1 LIMIT
$2 [["name", "hardware"], ["LIMIT", 1]]
Kategory Load (10.7ms) SELECT "kategories".* FROM "kategories"
(1.0ms) BEGIN
Question Exists (4.9ms) SELECT 1 AS one FROM "questions" WHERE ("questions".
"id" IS NOT NULL) AND "questions"."slug" = $1 LIMIT $2 [["slug", "help-my-dell-
inspiron-6400-is-hanging"], ["LIMIT", 1]]
Kategory Load (5.9ms) SELECT "kategories".* FROM "kategories" WHERE "kategor
ies"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.0ms) ROLLBACK
Rendering questions/new.html.erb within layouts/application
Rendered questions/_form.html.erb (17.6ms)
Rendered questions/new.html.erb within layouts/application (141.6ms)
Rendered layouts/navigation/header/_toggle_button.html.erb (1.0ms)
Rendered layouts/navigation/header/_home_button.html.erb (30.3ms)
User Load (28.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIM
IT $2 [["id", 5], ["LIMIT", 1]]
Rendered layouts/navigation/header/dropdowns/conversations/_private.html.erb (
73.2ms)
User Load (2.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMI
T $2 [["id", 3], ["LIMIT", 1]]
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $
1 LIMIT $2 [["id", 5], ["LIMIT", 1]]
Rendered layouts/navigation/header/dropdowns/conversations/_private.html.erb (
109.4ms)
Rendered layouts/navigation/header/dropdowns/conversations/_group.html.erb (3.
9ms)
User Load (30.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIM
IT $2 [["id", 1], ["LIMIT", 1]]
Rendered layouts/navigation/header/dropdowns/conversations/_private.html.erb (
40.0ms)
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $
1 LIMIT $2 [["id", 3], ["LIMIT", 1]]
User Load (2.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMI
T $2 [["id", 2], ["LIMIT", 1]]
Rendered layouts/navigation/header/dropdowns/conversations/_private.html.erb (
100.6ms)
Rendered layouts/navigation/header/dropdowns/_conversations.html.erb (983.4ms)
User Load (2.0ms) SELECT "users".* FROM "users" INNER JOIN "contacts" ON "use
rs"."id" = "contacts"."user_id" WHERE "contacts"."contact_id" = $1 AND "contacts
"."accepted" = $2 [["contact_id", 3], ["accepted", "f"]]
Rendered layouts/navigation/header/dropdowns/contact_requests/_no_requests.htm
l.erb (1.0ms)
Rendered layouts/navigation/header/dropdowns/_contact_requests.html.erb (374.0
ms)
Rendered layouts/navigation/header/_dropdowns.html.erb (1713.9ms)
Rendered layouts/navigation/_header.html.erb (2485.4ms)
Kategory Load (2.9ms) SELECT "kategories".* FROM "kategories"
Rendered layouts/navigation/collapsible_elements/_constant_links.html.erb (2.0
ms)
(2.0ms) SELECT COUNT(*) FROM "questions" INNER JOIN "save_questions" ON "que
stions"."id" = "save_questions"."question_id" WHERE "save_questions"."user_id" =
$1 [["user_id", 3]]
Rendered layouts/navigation/collapsible_elements/_signed_in_links.html.erb (21
.5ms)
Rendered layouts/navigation/_collapsible_elements.html.erb (543.0ms)
Rendered layouts/_navigation.html.erb (3323.2ms)
Rendered layouts/application/_private_conversations_windows.html.erb (1.0ms)
Rendered layouts/application/_group_conversations_windows.html.erb (2.0ms)
Completed 200 OK in 22203ms (Views: 21796.6ms | ActiveRecord: 158.2ms)
You need to define #kategories = Kategory.all.map{ |k| [k.name, k.id] } in create and update actions. This actions rerender new/edit actions if they can't save the question, and they need #kategories for rerendering
If you provide the full server log for create action I can try to help you to figure out - why #question can not be saved
Looks to me like your #kategories, or something within there (perhaps a name), is nil, hence the error :) (Hopefully this is a good thing, as all may be well with your tagging.)
Ensure Kategory.all.map { |k| [k.name, k.id] } in your new action is returning records and this error should go away.
N.B. you can also use options_from_collection_for_select here.
# in the controller
def new
#question = current_user.questions.build
#kategories = Kategory.all
end
# and the view
<%= select_tag(:kategory_id, options_from_collection_for_select(#kategories, :name, :id), prompt: "Select Category", class: "form-control") %>
This way, if you don't have any categories, you won't get an error - instead, you just won't have any options available.

How to sort :order nil?

Basically everytime a page loads this error pops up in the terminal. It doesn't seem to be causing any tangible issues:
NoMethodError (undefined method `each' for nil:NilClass): app/controllers/habits_controller.rb:8:in `sort'
I want to fix it though and I have a general idea how to.
When a new habit is created instead of its :order defaulting to nil it should default to the next highest integer.
HabitsController
def sort
params[:order].each do |id, order|
Habit.where(id: id).update_all(order: order)
end
render nothing: true
end
def habit_params
params.require(:habit).permit(
:user_id,
:trigger,
:tag_list,
:current_level,
:conceal,
:missed_days,
:target,
:reward,
:comment,
:commentable,
:like,
:likeable,
:action,
:order,
:date_started,
:missed_one,
:completed,
:completed_at,
:notes_text,
:notes_date,
:notable,
:note,
:committed => [],
levels_attributes: [
:missed_days,
:days_lost], notes_attributes: [:notable, :note, :notes_text, :notes_date, :_destroy])
end
schema
create_table "habits", force: true do |t|
t.integer "order"
end
rails c
[2] pry(main)> Habit.last
Habit Load (2.9ms) SELECT "habits".* FROM "habits" ORDER BY "habits"."id" DESC LIMIT 1
=> #<Habit:0x007f94e0da39b0
id: 12,
missed_days: 0,
conceal: false,
likes: nil,
date_started: Tue, 15 Sep 2015 00:00:00 EDT -04:00,
trigger: "wake up",
action: "run",
target: "2 miles",
reward: "eat dessert",
user_id: 2,
created_at: Tue, 15 Sep 2015 09:53:31 EDT -04:00,
updated_at: Tue, 15 Sep 2015 09:53:31 EDT -04:00,
order: nil,
completed_at: nil,
committed: ["sun", "mon", "tue", "wed", "thu", "fri", "sat", ""],
strike_date: nil,
missed_days_date: nil>
rails s
Started POST "/habits/sort" for 127.0.0.1 at 2015-09-15 10:02:36 -0400
Processing by HabitsController#sort as */*
User Load (2.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 2]]
Habit Load (0.4ms) SELECT "habits".* FROM "habits" WHERE "habits"."user_id" = $1 [["user_id", 2]]
ActsAsTaggableOn::Tag Load (0.5ms) SELECT "tags".* FROM "tags" WHERE (LOWER(name) = LOWER('ingrain'))
CACHE (0.0ms) SELECT "habits".* FROM "habits" WHERE "habits"."user_id" = $1 [["user_id", 2]]
Level Load (0.3ms) SELECT "levels".* FROM "levels" WHERE "levels"."habit_id" = $1 ORDER BY "levels"."id" ASC [["habit_id", 11]]
Level Load (0.3ms) SELECT "levels".* FROM "levels" WHERE "levels"."habit_id" = $1 ORDER BY "levels"."id" ASC [["habit_id", 12]]
Level Load (0.3ms) SELECT "levels".* FROM "levels" WHERE "levels"."habit_id" = $1 ORDER BY "levels"."id" ASC [["habit_id", 10]]
ActsAsTaggableOn::Tag Load (0.3ms) SELECT DISTINCT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."tagger_id" = $1 AND "taggings"."tagger_type" = $2 ORDER BY taggings_count desc LIMIT 20 [["tagger_id", 2], ["tagger_type", "User"]]
(0.3ms) SELECT COUNT(*) FROM "habits" WHERE "habits"."user_id" = $1 [["user_id", 2]]
Completed 500 Internal Server Error in 51ms
NoMethodError (undefined method `each' for nil:NilClass): app/controllers/habits_controller.rb:8:in `sort'
habit-sort.js
var update_orders, update_remote_orders;
update_remote_orders = function(orders) {
return $.ajax({
url: "/habits/sort",
type: "POST",
data: {
order: orders
},
success: function(data) {}
});
};
update_orders = function() {
var orders;
orders = {};
$("#sortable tr input.order:hidden").each(function(i, o) {
orders[$(o).attr("data-id")] = i;
return $(o).val(i);
});
return update_remote_orders(orders);
};
$(document).ready(function() {
update_orders();
$("#sortable").sortable({
axis: 'y',
handle: ".btn",
stop: function(ui, event) {
return update_orders();
}
});
});

Why does validates_presence_of fail on save, even when the condition is not violated

In my Post.rb model, I am doing this:
validates_presence_of :body
In my controller I have this action that I am executing:
def mark_as_published
if #post.unpublished?
#post.published!
redirect_to post_path(#post), notice: "Successfully published."
else
redirect_to post_path(#post), notice: "Post already published"
end
end
This is the error I am getting:
app/controllers/posts_controller.rb:104:in `mark_as_published'
Started PUT "/posts/ebola-death-climbs-past-6-000/mark_as_published" for 67.230.41.168 at 2014-12-10 20:15:29 +0000
app[web.1]: ActiveRecord::RecordInvalid (Validation failed: Body can't be blank):
This is the record in the console:
> Post.last
=> #<Post id: 29, title: "Ebola death climbs past 6,000", photo: nil, body: "Fresh figures from WHO has revealed that the death...", created_at: "2014-12-10 20:08:58", updated_at: "2014-12-10 20:08:58", user_id: 10, ancestry: nil, file: nil, status: 2, slug: "ebola-death-climbs-past-6-000", publication_status: 0, has_eyewitness: false, youtube_embed_code: "", soundcloud_embed_code: "">
Why does this validation fail, only on the save/update, even though the post.body is not blank?
Edit 1
The publication_status is just an enum:
enum publication_status: [ :unpublished, :published ]
Which comes with a set of handy methods, including published?, unpublished?, published!, and unpublished!. The latter two basically toggle the value to be the flag, i.e. unpublished! changes the publication_status to be unpublished and vice versa.
Here are other validations on the Post.rb model:
validates_length_of :body, maximum: 150, too_long: 'The report must be less than 150 words.',
tokenizer: ->(str) { str.scan(/\w+/) }
validates_length_of :title, maximum: 7, too_long: 'The title must be less than 7 words.',
tokenizer: ->(str) { str.scan(/\w+/) }
Edit 2
This is what happens when I mark the record as published in the console:
0> p = Post.last
=> #<Post id: 29, title: "Ebola death climbs past 6,000", photo: nil, body: "Fresh figures from WHO has revealed that the death...", created_at: "2014-12-10 20:08:58", updated_at: "2014-12-10 20:08:58", user_id: 10, ancestry: nil, file: nil, status: 2, slug: "ebola-death-climbs-past-6-000", publication_status: 0, has_eyewitness: false, youtube_embed_code: "", soundcloud_embed_code: "">
irb(main):002:0> p.published!
=> true
irb(main):003:0> p.save
=> true
No SQL is generated. Not sure if this is because I am doing this in production on Heroku.
Edit 3
I tried to publish a record in development, and this is the SQL & server log:
Started PUT "/posts/longword-verylongword-longword-longword-longword-vellylongword-prettylongword/mark_as_published" for 127.0.0.1 at 2014-12-10 19:22:56 -0500
Processing by PostsController#mark_as_published as HTML
Parameters: {"authenticity_token"=>"8kYDxjYS54sGozjSS4ZZwQFJUTtIBgLpEmpAlTRZc4k=", "id"=>"longword-verylongword-longword-longword-longword-vellylongword-prettylongword"}
User Load (1.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
(2.0ms) SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["user_id", 1]]
(1.2ms) SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (((roles.name = 'editor') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["user_id", 1]]
(0.2ms) BEGIN
(0.2ms) ROLLBACK
Completed 422 Unprocessable Entity in 62ms
ActiveRecord::RecordInvalid - Validation failed: Body can't be blank:
When I do it in the console, this is the log:
> p = Post.last
Post Load (0.6ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" DESC LIMIT 1
=> #<Post id: 37, title: "LongWord VeryLongWord LongWord LongWord LongWord V...", photo: nil, body: "10PP gives you a lot of one on one attention that ...", created_at: "2014-11-27 09:21:06", updated_at: "2014-11-27 09:21:06", user_id: nil, ancestry: nil, file: nil, status: 1, slug: "longword-verylongword-longword-longword-longword-v...", publication_status: 0, has_eyewitness: false, youtube_embed_code: "", soundcloud_embed_code: "">
[21] pry(main)> p.published!
(0.2ms) BEGIN
SQL (2.0ms) UPDATE "posts" SET "publication_status" = $1, "updated_at" = $2 WHERE "posts"."id" = 37 [["publication_status", 1], ["updated_at", "2014-12-11 00:24:18.419390"]]
FriendlyId::Slug Load (1.9ms) SELECT "friendly_id_slugs".* FROM "friendly_id_slugs" WHERE "friendly_id_slugs"."sluggable_id" = $1 AND "friendly_id_slugs"."sluggable_type" = $2 ORDER BY "friendly_id_slugs".id DESC LIMIT 1 [["sluggable_id", 37], ["sluggable_type", "Post"]]
(0.9ms) COMMIT
=> true
From the log I can see clearly you do not retrieve the Post from the database. So you are saving a "new/empty" post. Please verify how your #post variable is set. I am guessing you are using the incorrect before_action now, or your post retrieval is too liberal and defaults to a new post if not found?
One option, if and only if it works within your application's logic, is to limit the validation to creates:
validates_presence_of :body, on: :create
If your app's logic dictates that updates need to run the validation, it would be helpful to see more code in order to better know how #post is populated, what the published/unpublished? methods look like, etc.

Rails creating new active record returns false

I have two models: Video and Image.
When I create a new video, I'd like to save an image of the video thumbnail image. I am attempting to this with the following lines in my video controller "create" action:
newImage = Image.new(:step_id=>#video.step_id, :imagePath=>#video.thumbnail_url, :project_id=>#video.project_id, :saved=> true, :position=>position).save
logger.debug "newImage #{newImage}"
Right now, the image doesn't seem to be saving, and I'm not sure what I'm doing wrong. Here is the error message I'm receiving:
ActiveRecord::RecordInvalid (Validation failed: Imagepath can't be blank):
app/controllers/videos_controller.rb:35:in `create'
Rendered /Users/ttseng/.rvm/gems/ruby-1.9.3-p448/gems/actionpack-3.2.8/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.0ms)
Rendered /Users/ttseng/.rvm/gems/ruby-1.9.3-p448/gems/actionpack-3.2.8/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.9ms)
Rendered /Users/ttseng/.rvm/gems/ruby-1.9.3-p448/gems/actionpack-3.2.8/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (7.3ms)
However, my logs clearly show that I'm not passing a blank imagePath (see below).
This is my controller action for the video:
# POST /videos
def create
# need to validate
#video = Video.create(params[:video])
# get the thumbnail image
thumbnail = #video.thumb_url
logger.debug "thumbnail video: #{thumbnail}"
#video.update_attributes(:thumbnail_url => thumbnail)
# create a new image record for the thumbnail
logger.debug "creating new image"
logger.debug "#video.step_id: #{#video.step_id}"
position = Step.find(#video.step_id).images.count # set the position of the added thumbnail to the last
logger.debug "position: #{position}"
logger.debug "imagePath: #{#video.thumbnail_url}"
logger.debug "project_id: #{#video.project_id}"
newImage = Image.new(:step_id=>#video.step_id, :imagePath=>#video.thumbnail_url, :project_id=>#video.project_id, :saved=> true, :position=>position).save
logger.debug "newImage #{newImage}"
respond_to do |format|
if #video.save
format.js
else
format.json { render :json => #video.errors, :status => :unprocessable_entity }
end
end
end
This returns the following logs:
Started POST "/videos" for 127.0.0.1 at 2013-07-05 14:09:45 -0400
Processing by VideosController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"e5YFBA3CSbeUiVgsimzrw2DlMrQbWNZmMfpYJoGLNCY=", "video"=>{"project_id"=>"108", "step_id"=>"523", "saved"=>"true", "url"=>"http://youtu.be/O9k-MsfIkMY"}, "button"=>""}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = 5 LIMIT 1
(0.2ms) BEGIN
video id: O9k-MsfIkMY
SQL (0.9ms) INSERT INTO "videos" ("created_at", "position", "project_id", "saved", "step_id", "thumbnail_url", "updated_at", "url") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id" [["created_at", Fri, 05 Jul 2013 14:09:45 EDT -04:00], ["position", nil], ["project_id", 108], ["saved", true], ["step_id", 523], ["thumbnail_url", nil], ["updated_at", Fri, 05 Jul 2013 14:09:45 EDT -04:00], ["url", "http://youtu.be/O9k-MsfIkMY"]]
(0.4ms) COMMIT
video id: O9k-MsfIkMY
thumbnail_url: http://img.youtube.com/vi/O9k-MsfIkMY/default.jpg
thumbnail video: http://img.youtube.com/vi/O9k-MsfIkMY/default.jpg
(0.1ms) BEGIN
video id: O9k-MsfIkMY
(0.3ms) UPDATE "videos" SET "thumbnail_url" = 'http://img.youtube.com/vi/O9k-MsfIkMY/default.jpg', "updated_at" = '2013-07-05 18:09:45.952229' WHERE "videos"."id" = 41
(0.4ms) COMMIT
creating new image
#video.step_id: 523
Step Load (0.3ms) SELECT "steps".* FROM "steps" WHERE "steps"."id" = $1 LIMIT 1 [["id", 523]]
(0.4ms) SELECT COUNT(*) FROM "images" WHERE "images"."step_id" = 523
position: 0
imagePath: http://img.youtube.com/vi/O9k-MsfIkMY/default.jpg
project_id: 108
(0.1ms) BEGIN
(0.1ms) ROLLBACK
newImage false
(0.1ms) BEGIN
video id: O9k-MsfIkMY
(0.1ms) COMMIT
video id: O9k-MsfIkMY
Rendered videos/create.js.erb (0.3ms)
Completed 200 OK in 26ms (Views: 5.7ms | ActiveRecord: 4.0ms)
Video.rb:
class Video < ActiveRecord::Base
attr_accessible :position, :project_id, :saved, :step_id, :url, :thumbnail_url
belongs_to :step
belongs_to :project
validates :url, url: true
validates :url, :presence=> true
validates :embed_code, :presence => true
validate :url_is_from_approved_site
...
end
Image.rb:
class Image < ActiveRecord::Base
attr_accessible :project_id, :step_id, :imagePath, :caption, :position, :saved
belongs_to :step
belongs_to :project
mount_uploader :imagePath, ImagePathUploader
before_create :default_name
validates :imagePath, :presence => true
def default_name
self.imagePath ||= File.basename(imagePath.filename, '.*').titleize if imagePath
end
def image=(val)
if !val.is_a?(String) && valid?
image_will_change!
super
end
end
end
You've set :imagePath, but it should be :imagepath.
Also, you can use create! method instead of new followed by save!
Image.create!(:step_id=>#video.step_id, :imagepath=>#video.thumbnail_url, :project_id=>#video.project_id, :saved=> true, :position=>position)
The error ended up being that you can't manually override Carrierwave attributes. Instead, I need to use :remote_image_path_url to set the :image_path attribute:
Manually Setting Image Path for Carrierwave Uploader
This uploads the image from the url via carrierwave directly (rather than pointing to the URL for the image).

Resources