I've been following along with this tutorial from lynda.com.
I'm in chapter 15: Nesting pages in subjects
So I've followed through and have even resorted to just copying the exercise files they provide you with. However I keep getting this error:
NoMethodError in PagesController#new
undefined method `id' for nil:NilClass
It hi-lights the second line:
def new
#page = Page.new({:subject_id => #subject.id, :name => "Default"})
#subjects = Subject.order('position ASC')
#page_count = Page.count + 1
end
Here's my controller
class PagesController < ApplicationController
layout "admin"
before_action :confirm_logged_in
before_action :find_subject
def index
# #pages = Page.where(:subject_id => #subject.id).sorted
#pages = #subject.pages.sorted
end
def show
#page = Page.find(params[:id])
end
def new
#page = Page.new({:subject_id => #subject.id, :name => "Default"})
#subjects = Subject.order('position ASC')
#page_count = Page.count + 1
end
def create
#page = Page.new(page_params)
if #page.save
flash[:notice] = "Page created successfully"
redirect_to(:action => 'index', :subject_id => #subject_id)
else
#subjects = Subject.order('position ASC')
#page_count = Page.count + 1
render('new')
end
end
def edit
#page = Page.find(params[:id])
#subjects = Subject.order('position ASC')
#page_count = Page.count
end
def update
# Find an existing object using form parameters
#page = Page.find(params[:id])
# Update the object
if #page.update_attributes(page_params)
# If update succeeds, redirect to the index action
flash[:notice] = "Page updated successfully"
redirect_to(:action => 'show', :id => #page.id, :subject_id => #subject_id)
else
# If the update fails, redisplay the form so user can fix problems
#subjects = Subject.order('position ASC')
#page_count = Page.count
render('edit')
end
end
def delete
#page = Page.find(params[:id])
end
def destroy
page = Page.find(params[:id]).destroy
flash[:notice] = "Page '#{page.name}' destroyed successfully"
redirect_to(:action => 'index', :subject_id => #subject_id)
end
private
def page_params
# Same as using "params[:subject]", except that it:
# - raises as error if :subject is not present
# - allows listed attributes to be mass-assigned
params.require(:page).permit(:subject_id, :name, :permalink, :position, :visible)
end
def find_subject
if params[:subject_id]
#subject = Subject.find(params[:subject_id])
end
end
end
Then here's the view:
<% #page_title = "New Page" %>
<%= link_to("<< Back to list", {:action => 'index', :subject_id => #subject_id}, :class => 'back-link') %>
<div class="pages new">
<h2>Create Page</h2>
<%= form_for(:page, :url => {:action => 'create', :subject_id => #subject_id}) do |f| %>
<%= render(:partial => "form", :locals => {:f => f}) %>
<div class="form-buttons">
<%= submit_tag("Create page") %>
</div>
<% end %>
</div>
A question similar to this one has been asked here, but I don't see any solutions there.
Any help is greatly appreciated!
I think the problem is in
Page.new({:subject_id => #subject.id, :name => "Default"})
#subject is not defined and you are asking for its id
You have to define #subject.
Put this as first line in your new action
#subject = Subject.find(params[:id])
Right now #subject is undefined therefore it's nil, calling .id on nil is resulting in the error you're seeing.
Related
When I want to go to "localhost:3000/blog" the web page gives this error...
Showing C:/Sites/ifurniture/app/views/refinery/blog/posts/index.html.erb where line #3 raised:
undefined method `to_sym' for {:title=>"Body", :slug=>"body"}:Hash
Rails.root: C:/Sites/ifurniture
this is the blog controller..
module Refinery
module Blog
class PostsController < BlogController
before_filter :find_all_blog_posts, :except => [:archive]
before_filter :find_blog_post, :only => [:show, :comment, :update_nav]
before_filter :find_tags
respond_to :html, :js, :rss
def index
if request.format.rss?
#posts = if params["max_results"].present?
# limit rss feed for services (like feedburner) who have max size
Post.recent(params["max_results"])
else
Post.newest_first.live.includes(:comments, :categories)
end
end
respond_with (#posts) do |format|
format.html
format.rss { render :layout => false }
end
end
def show
#comment = Comment.new
#canonical = refinery.url_for(:locale => Refinery::I18n.current_frontend_locale) if canonical?
#post.increment!(:access_count, 1)
respond_with (#post) do |format|
format.html { present(#post) }
format.js { render :partial => 'post', :layout => false }
end
end
def comment
#comment = #post.comments.create(comment_params)
if #comment.valid?
if Comment::Moderation.enabled? or #comment.ham?
begin
CommentMailer.notification(#comment, request).deliver_now
rescue
logger.warn "There was an error delivering a blog comment notification.\n#{$!}\n"
end
end
if Comment::Moderation.enabled?
flash[:notice] = t('thank_you_moderated', :scope => 'refinery.blog.posts.comments')
redirect_to refinery.blog_post_url(params[:id])
else
flash[:notice] = t('thank_you', :scope => 'refinery.blog.posts.comments')
redirect_to refinery.blog_post_url(params[:id],
:anchor => "comment-#{#comment.to_param}")
end
else
render :show
end
end
def archive
if params[:month].present?
date = "#{params[:month]}/#{params[:year]}"
archive_date = Time.parse(date)
#date_title = ::I18n.l(archive_date, :format => '%B %Y')
#posts = Post.live.by_month(archive_date).page(params[:page])
else
date = "01/#{params[:year]}"
archive_date = Time.parse(date)
#date_title = ::I18n.l(archive_date, :format => '%Y')
#posts = Post.live.by_year(archive_date).page(params[:page])
end
respond_with (#posts)
end
def tagged
#tag = ActsAsTaggableOn::Tag.find(params[:tag_id])
#tag_name = #tag.name
#posts = Post.live.tagged_with(#tag_name).page(params[:page])
end
private
def comment_params
params.require(:comment).permit(:name, :email, :message)
end
protected
def canonical?
Refinery::I18n.default_frontend_locale != Refinery::I18n.current_frontend_locale
end
end
end
end
the post controller...
module Refinery
module Blog
class PostsController < BlogController
before_filter :find_all_blog_posts, :except => [:archive]
before_filter :find_blog_post, :only => [:show, :comment, :update_nav]
before_filter :find_tags
respond_to :html, :js, :rss
def index
if request.format.rss?
#posts = if params["max_results"].present?
# limit rss feed for services (like feedburner) who have max size
Post.recent(params["max_results"])
else
Post.newest_first.live.includes(:comments, :categories)
end
end
respond_with (#posts) do |format|
format.html
format.rss { render :layout => false }
end
end
def show
#comment = Comment.new
#canonical = refinery.url_for(:locale => Refinery::I18n.current_frontend_locale) if canonical?
#post.increment!(:access_count, 1)
respond_with (#post) do |format|
format.html { present(#post) }
format.js { render :partial => 'post', :layout => false }
end
end
def comment
#comment = #post.comments.create(comment_params)
if #comment.valid?
if Comment::Moderation.enabled? or #comment.ham?
begin
CommentMailer.notification(#comment, request).deliver_now
rescue
logger.warn "There was an error delivering a blog comment notification.\n#{$!}\n"
end
end
if Comment::Moderation.enabled?
flash[:notice] = t('thank_you_moderated', :scope => 'refinery.blog.posts.comments')
redirect_to refinery.blog_post_url(params[:id])
else
flash[:notice] = t('thank_you', :scope => 'refinery.blog.posts.comments')
redirect_to refinery.blog_post_url(params[:id],
:anchor => "comment-#{#comment.to_param}")
end
else
render :show
end
end
def archive
if params[:month].present?
date = "#{params[:month]}/#{params[:year]}"
archive_date = Time.parse(date)
#date_title = ::I18n.l(archive_date, :format => '%B %Y')
#posts = Post.live.by_month(archive_date).page(params[:page])
else
date = "01/#{params[:year]}"
archive_date = Time.parse(date)
#date_title = ::I18n.l(archive_date, :format => '%Y')
#posts = Post.live.by_year(archive_date).page(params[:page])
end
respond_with (#posts)
end
def tagged
#tag = ActsAsTaggableOn::Tag.find(params[:tag_id])
#tag_name = #tag.name
#posts = Post.live.tagged_with(#tag_name).page(params[:page])
end
private
def comment_params
params.require(:comment).permit(:name, :email, :message)
end
protected
def canonical?
Refinery::I18n.default_frontend_locale != Refinery::I18n.current_frontend_locale
end
end
end
end
and this is the index.html.erb of Blog.
<section class="container">
<% content_for :body do %>
<%= raw #page.content_for(Refinery::Pages.default_parts.first.to_sym) if Refinery::Pages.default_parts.any? %>
<% if #posts.any? %>
<section id="blog_posts" class="news">
<%= render :partial => "/refinery/blog/shared/post", :collection => #posts %>
<%= will_paginate #posts %>
</section>
<% else %>
<p><%= t('.no_blog_articles_yet') %></p>
<% end %>
<% end %>
<% content_for :side_body_prepend do -%>
<%= raw #page.content_for(Refinery::Pages.default_parts.second.to_sym) %>
<% end if Refinery::Pages.default_parts.many? -%>
<%= render "/refinery/content_page" %>
<% content_for :stylesheets, stylesheet_link_tag('refinery/blog/frontend') %>
</section>
I'll be watching for your help, thanks.
You can use to_sym method on hash or string so in your code you can do something like this:
index.html
instead this:
<%= raw #page.content_for(Refinery::Pages.default_parts.first.to_sym) if Refinery::Pages.default_parts.any? %>
put this:
<%= raw #page.content_for(Refinery::Pages.default_parts.first[:title].to_sym) if Refinery::Pages.default_parts.any? %>
instead [:title]you can also use [:slug]
I want to delete an image on click.
This is in my view
<%=form_for #area, url: areas_update_path, remote: true, html: {class: "form-horizontal",:multipart => true} do |f|%>
....
<% #area.area_attachments.each do |a| %>
<%unless a.image.blank?%>
<%= link_to delete_area_attachment_path(a), :remote => true, :method => :delete do%>
<%= image_tag a.image_url(:thumb), class:"delete-image" %>
<% end %>
<% end %>
<% end %>
....
<% end %>
After I click on an image, the image gets deleted, but I get
First argument in form cannot contain nil or be empty
In the first line of the code that I posted (#area I guess)
My delete_area_attachment method in my area_attachments_controller
def delete_area_attachment
#areaAttachment = AreaAttachment.find(params[:id])
#areaAttachment.destroy
respond_to do |format|
format.js
end
end
I guess the #area variable has to be initialized, but why?
What I am trying to delete is an area_attachment not an area, and I already initialized it, so what does the #area variable has to do with that?
How do I go about it here?
EDIT:
my relative routes:
#Area Paths
get '/areas/new', to: 'areas#new', :as => 'areas_new'
post '/areas/create', to: 'areas#create', :as => 'areas_create'
get '/areas/:id/destroy', to: 'areas#destroy', :as => 'areas_destroy'
delete 'delete_area/:id', controller: 'areas', action: 'delete_area'
get '/areas/:id/edit', to: 'areas#edit', :as => 'areas_edit'
patch '/areas/:id/update', to: 'areas#update', :as => 'areas_update'
#Area Attachment Paths
delete 'delete_area_attachment/:id', controller: 'area_attachments', action: 'delete_area_attachment', :as => 'delete_area_attachment'
My areas_controller
class AreasController < ApplicationController
before_action :set_areas
before_action :set_area, only: [:edit, :delete, :update, :destroy]
def new
#area = Area.new
#languages = Language.all
#area_attachment = #area.area_attachments.build
end
def create
#area = Area.new(area_params)
if #area.save && manage_strings
params[:area_attachments]['image'].each do |a|
#area_attachment = #area.area_attachments.create!(:image => a, :area_id => #area.id)
end
#status = 'success'
else
#status = 'error'
#errormessages = #area.errors.full_messages
end
respond_to do |format|
format.js
end
end
def edit
end
def update
if #area.update(area_params) && manage_strings
params[:area_attachments]['image'].each do |a|
#area_attachment = #area.area_attachments.create!(:image => a, :area_id => #area.id)
end
#status = 'success'
else
#status = 'error'
#errormessages = #area.errors.full_messages
end
respond_to do |format|
format.js
end
end
def delete_area
#area = Area.find(params[:id])
#area.destroy
respond_to do |format|
format.js
end
end
def find_area_by_id
area = Area.find(params[:id])
render json: area
end
protected
def news_list
respond_to do |format|
format.js
end
end
private
def area_params
params.require(:area).permit(:id, area_attachments_attributes: [:id, :area_id, :image])
end
def set_area
#area = Area.find_by_id(params[:id])
#languages = Language.all
#area_attachments = #area.area_attachments.all
end
def set_areas
#areas = Area.all
end
def manage_strings
if params[:area][:strings].any?
params[:area][:strings].each do |key,value|
string = #area.article_localizations.find_or_initialize_by(:language_id => key.to_i)
string.title = params[:area][:strings][key][:title]
string.text = params[:area][:strings][key][:text]
string.save
end
end
end
end
In delete_area_attachment before #areaAttachment.destroy add:
#area = Area.find_by_id(#areaAttachment.area_id)
I've got several problems with a Rails 4.2.1 and Ruby 2.1 application when testing in IE. If I save something to a database and do a redirect, the values are saved twice in the database.
Does anyone have this as well?
Save Methode in controller:
if request.post?
#question.attributes = params[:question]
if #question.save
flash.now[:notice] = t("flash.saved")
update_answers params
redirect_after_save #question
end
end
def update_answers params
if !params[:add].nil?
params[:add].each do | id, data |
answer = Answer.new
data[:question_id] = #question.id
answer.update_attributes data
end
end
end
def redirect_after_save q
if params[:action_after_save] == 'back'
flash[:notice] = I18n.t("flash.saved")
redirect_to :controller => :lessons, :action => :edit, :id => q.lesson_id
end
if params[:action_after_save] == 'new'
flash[:notice] = I18n.t("flash.saved")
redirect_to :action => :new, :id => q.lesson_id
end
if params[:action_after_save] == 'test'
flash[:notice] = I18n.t("flash.saved")
redirect_to :action => :test, :id => q.id
end
end
I'm not using turbolinks.
View Code:
<%= f.submit t("main.save"), :icon => :save %>
<%= t("question.or_save_and") %>:
<% [[t('question.after_save_back'), 'back'],
[t('question.after_save_new'), 'new'],
[t('question.test_question'), 'test']].each do |it| %>
<%= f.submit(it.first, :disable_with => "saving",
:onclick => "$('#edit_question').append('<input type=\"hidden\" name=\"action_after_save\" value=\"" + it.second + "\" />');",
:icon => :save) %>
<% end %>
this is the Code which is called during saving.
def multiple_choice
#question = Question.find params[:id]
return false unless check_authorization #question
add_edit_breadcrumbs #question
if request.post?
#question.attributes = params[:question]
if #question.save
flash.now[:notice] = t("flash.saved")
SLACKNOTIFIER.ping "Bevor update answers"
update_answers params
SLACKNOTIFIER.ping "After update answers"
redirect_after_save #question
end
end
end
the whole code is called twice in IE ...
Adding the property:
disable_with: 'button-text'
to the submit button solved the problem (from comments above).
<% #idea.each do |#idea| %>
<div id="basic_details" class="idea-show-columns">
<%= render :partial => '/ideas/idea_basic_show', :locals => {:idea => #idea} %>
<%= render :partial => 'ideas/comments' %>
<%= render :partial => 'ideas/mockups' %>
</div>
<div id="copy_details" class="idea-show-columns">
<%= render :partial => 'ideas/ copy_show', :locals => {:idea => #idea} %>
</div>
<% end %>
When I take the # off it says "Parameter 'idea' is not used", I cannot find the proper syntax for this.
IdeasController
class IdeasController < ApplicationController
def index
respond_to do |format|
if request.xhr?
#ideas = Idea.select("sku, id").order(:sku)
else
if params[:search]
#ideas = Idea.where("sku like '%#{params[:search]}%' or working_name like '%#
{params[:search]}%' or product_name like '%#{params[:search]}%'").paginate(:page =>
params[:page]).reload
else
#ideas = Idea.paginate(:page => params[:page]).reload
end
end
format.html
format.json {render json: #ideas.where("sku like ?", "%#{params[:q]}%") }
end
end
def show
#idea = Idea.find_by_permalink(params[:id])
end
def new
#idea = Idea.new
end
def create
#idea = Idea.new(new_idea_params)
if #idea.save
flash[:notice] = 'Idea created!'
redirect_to ideas_url
else
render :new
end
end
def edit
#idea = Idea.find_by_permalink(params[:id])
end
def update
#idea = Idea.find_by_permalink(params[:id])
if request.xhr?
if params[:comment]
#idea.comments.create(:title => params[:comment][:title], :comment => params[:comment][:comment], :user_id => current_user.id)
end
if #idea.update_attributes(update_status_params)
#success = true
else
#success = false
end
else
if #idea.update_attributes(edit_idea_params)
flash[:notice] = 'Idea updated!'
redirect_to idea_url(params[:id])
else
render :edit
end
end
end
def destroy
Idea.find_by_permalink(params[:id]).destroy
flash[:success] = "Idea destroyed."
redirect_to ideas_url
end
def generate_mockups
idea = Idea.find_by_permalink(params[:id])
begin
idea.generate_mockups
rescue Exception => e
flash[:error] = "There was an error generating the mockups for #{idea.working_name}! # {e.message}"
end
flash[:notice] = "Successfully generated mockups for #{idea.working_name}"
redirect_to idea_url(params[:id])
end
def signoff
if current_user.has_overlord_access?
if params[:idea_id]
idea = Idea.find(params[:idea_id])
idea.overlord_id = current_user.id
idea.status = 'Ready To Publish'
idea.save
flash[:notice] = "#{current_user.full_name} just signed off on #{idea.sku}"
redirect_to :action => :signoff
end
#ideas = Idea.awaiting_overlord_signoff.reload
#idea = #ideas.first.reload
else
flash[:notice] = 'Sorry, you must have overlord access to sign off on ideas.'
redirect_to ideas_path
end
end
private
def new_idea_params
params.require(:idea).permit(:sku, :working_name, :working_description, :priority)
end
def update_status_params
params.require(:idea).permit(:art_status, :copy_status)
end
def edit_idea_params
(params[:idea][:color_ids] ||= []) unless !current_user.has_artist_access?
(params[:idea][:imprintable_ids] ||= []) unless !current_user.has_copywriter_access?
(params[:idea][:stores_ids] ||= []) unless !current_user.has_copywriter_access?
(params[:idea][:taxonomies_ids] ||= []) unless !current_user.has_copywriter_access?
params.require(:idea).permit(:sku, :working_name, :working_description, :priority,
:product_name, :product_line_tokens,
:description, :meta_description, :meta_keywords, :artist_id,
:copywriter_id, :overlord_id,
{:store_ids => []}, {:taxonomy_ids => []}, :base_price,
:shipping_category, :default_artwork_id,
:tax_category, {:imprintable_ids => []}, :marketplace,
:product_type,
:base, :colors_offered, :special_instructions, :copy_status,
:art_status, {:color_ids => []},
:artworks_attributes => [:height, :width, :from_top,
:from_center, :idea_id, :dimensions, :file, :_destroy, :id])
end
def update_status_params
params.require(:idea).permit(:copy_status, :art_status)
end
end
I am getting the error
undefined method `each' for nil:NilClass
It should :
<% unless #ideas.nil? %>
<% #ideas.each do |idea| %>
<div id="basic_details" class="idea-show-columns">
<%= render :partial => '/ideas/idea_basic_show', :locals => {:idea => idea} %>
<%= render :partial => 'ideas/comments' %>
<%= render :partial => 'ideas/mockups' %>
</div>
<div id="copy_details" class="idea-show-columns">
<%= render :partial => 'ideas/ copy_show', :locals => {:idea => idea} %>
</div>
<% end %>
<% end %>
Few things:
if you need to use #ideas in show view you need to load them using before_filter
dont do this dea.where("sku like '%#{params[:search]}%' because this is not secure. Do something like where("code = ?", params[:code])
This is a continuation of Confused as to which Prototype helper to use. My code has been updated to reflect other user's suggestions:
(model) message.rb:
class Message < ActiveRecord::Base
after_create :destroy_old_messages
def old_messages
messages = Message.all(:order => 'updated_at DESC')
if messages.size >= 24
return messages[24..-1]
else
return []
end
end
protected # works without protected
def destroy_old_messages
messages = Message.all(:order => 'updated_at DESC')
messages[24..-1].each {|p| p.destroy } if messages.size >= 24
end
end
(view) index.html.erb:
<div id="messages">
<%= render :partial => #messages %>
</div>
<%= render :partial => "message_form" %>
(view) _message.html.erb:
<% div_for message do %>
<%= h message.created_at.strftime("%X") %> - <%= h message.author %><%= h message.message %>
<% end %>
(view) _message_form.html.erb:
<% remote_form_for :message, :url => { :action => "create" }, :html => { :id => 'message_form'} do |f| %>
<%= f.text_area :message, :size => "44x3" %><br />
<%= submit_to_remote 'submit_btn', 'submit', :url => { :action => 'create' } %><br />
<% end %>
(view) create.rjs:
page.insert_html :top, :messages, :partial => #message
page[#message].visual_effect :grow
page[:message_form].reset
flash[:notice]
flash.discard
# #old_messages.each do |m|
# page.remove(m.id)
# end
(controller) messages_controller.rb:
class MessagesController < ApplicationController
def index
#messages = Message.all(:order => "created_at DESC")
respond_to do |format|
format.html
format.js
end
end
def new
#message = Message.new
respond_to do |format|
format.html
end
end
def create
#message = Message.new(params[:message])
# #old_messages = Message.old_messages
respond_to do |format|
if #message.save
flash[:notice] = 'message created.'
format.html { redirect_to(messages_url) }
format.js
else
format.html { render :action => "new" }
end
end
end
def update
#message = Message.find(params[:id])
respond_to do |format|
if #message.update_attributes(params[:message])
flash[:notice] = 'message updated.'
format.html { redirect_to(messages_url) }
format.js
else
format.html { render :action => "edit" }
end
end
end
def destroy
#message = Message.find(params[:id])
#message.destroy
respond_to do |format|
format.html { redirect_to(messages_url) }
format.js
end
end
end
With the exception of the old_messages method in the model, all of the commented code were recommended changes from the previous post to make this work. But as soon as I uncomment the last three lines from create.rjs and #old_messages = Message.old_messages from the controller, I can't even submit messages with the message_form partial. Can anyone see what's wrong here? I'm just trying to create a basic app to help further my understanding of rails and rjs. I would greatly appreciate any suggestions or corrections you have to share, thank you for reading my post.
it's not what you're asking for, but i have a suggestion...
to get the older messages you can use named_scope.
in your case, (if i understood what you want) i think it would be something like:
# model
named_scope :limit, lambda { |num| { :limit => num } }
named_scope :order, lambda { |ord| { :order => ord } }
and
#controller
Message.order("updated_at DESC").limit(24)
the problem is that old_messages is an instance method, and you're calling from a class.
if you do
def self.old_messages
# ...
end
it's now a class method.
this blog has a good explanation about class and instance methods.