I'm working through the Rails Tutorial Chapter 12, and get the following error on the Home/Main page when user is signed out (signed in is OK):
I'm a newbie to Rails so please be explicit in your response! Many thanks..
NoMethodError in PagesController#home
undefined method `feed' for nil:NilClass
Rails.root: /Users/fkhalid2008/Documents/First-app
Application Trace | Framework Trace | Full Trace
app/controllers/pages_controller.rb:6:in `home'
Pages Controller
class PagesController < ApplicationController
def home
#title = "Home"
#post = Post.new if signed_in?
#feed_items = current_user.feed.paginate(:page => params[:page])
end
def contact
#title = "Contact"
end
def about
#title = "About Us"
end
end
Home Page View (/app/views/pages/home.html.erb)
<% if signed_in? %>
<table class="front" summary="For signed-in users">
<tr>
<td class="main">
<h1 class="post">What's up?</h1>
<%= render 'shared/post_form' %>
<%= render 'shared/feed' %>
</td>
<td class="sidebar round">
<%= render 'shared/user_info' %>
</td>
</tr>
</table>
<% else %>
<h1>Palazzo Valencia</h1>
<p>
This is the home page for the
Palazzo Valencia
sample application.
</p>
<%= link_to "Sign up now!", signup_path, :class => "signup_button round" %>
<% end %>
Feed Partial
<% unless #feed_items.empty? %>
<table class="posts" summary="User posts">
<%= render :partial => 'shared/feed_item', :collection => #feed_items %>
</table>
<%= will_paginate #feed_items %>
<% end %>
Feed_item Partial
<tr>
<td class="gravatar">
<%= link_to gravatar_for(feed_item.user), feed_item.user %>
</td>
<td class="post">
<span class="user">
<%= link_to feed_item.user.name, feed_item.user %>
</span>
<span class="content"><%= feed_item.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(feed_item.created_at) %> ago.
</span>
</td>
<% if current_user?(feed_item.user) %>
<td>
<%= link_to "delete", feed_item, :method => :delete,
:confirm => "You sure?",
:title => feed_item.content %>
</td>
<% end %>
</tr>
The user is not signed in. Therefore, the current_user method is returning nil, and ruby can't find the feed method.
You could change the code to this:
#title = "Home"
if signed_in?
#post = Post.new
#feed_items = current_user.feed.paginate(:page => params[:page])
end
Now, the new post and the feed items will only be retrieved when the user is signed in.
app/controllers/static_pages_controller.rb
def home
if logged_in?
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
Related
I have an index page with a partial form to submit new records to Package model. I keep this form in the index page, so the user doesn't need to leave the page when repeating this action, several times.
In the same page I have a form_tag fir multiple updates for the same controller, namely packages_controller.
Everything works fine, except the following: when hit the update button, going to the form BUT instead of submitting I go back (with the browser) and try to select other records to be updated then I have a routing error:
Routing Error
No route matches [PUT] "/projects/47/orderlines/18/packages"
My index page looks like this:
<% if current_user %>
<%= render "packages/form" %>
<% end %>
<% if #packages.count >= 1 %>
<table class="table table-striped">
<thead>
<tr>
<th> <input type="checkbox" id="selectAll" value="selectAll"></th>
<th>Packed </th>
<th>#No.</th>
<th>Type</th>
<th>Gross weight</th>
<th>Length</th>
<th>Width</th>
<th>Height</th>
<th></th>
<th>Container</th>
</tr>
</thead>
<%= form_tag edit_multiple_project_orderline_packages_path, method: :get do %>
<tbody>
<% for package in #packages %>
<% if package.packed== true %>
<% #label_type="success" %>
<% else %>
<% #label_type="default" %>
<% end %>
<tr>
<td><%= check_box_tag "package_ids[]", package.id %></td>
<td><span class="label label-<%= #label_type %>"><% if package.packed==true %>Packed<% else %>Unpacked<% end %></span></td>
<td><%= package.package_no %></td>
<td><%= package.package_type %></td>
<td><%= package.gross_weight %></td>
<td><%= package.length %></td>
<td><%= package.width %></td>
<td><%= package.height %></td>
<% if #orderline.packages.count >= 1 %>
<td><%= link_to 'Delete', [package.orderline.project, package.orderline, package],
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
<td><%= #containers.find(package.container_id).container_id if package.packed %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
<%= submit_tag "Add to container", class: "btn btn-primary" %>
<% end %>
<br />
<%= will_paginate %>
<br>
And the multiple_edit form
<div class="col-sm-4">
<%= form_tag update_multiple_project_orderline_packages_path, method: :put do %>
<ul>
<% #packages.each do |package| %>
<li>
<%= hidden_field_tag "package_ids[]", package.id %>
<%= package.package_no %>
<%= package.container_id %>
<% package.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</li>
<% end %>
</ul>
<%= fields_for :package do |f| %>
<div class="field">
<%= f.label :package_no %><br />
<%= f.text_field :package_no, :class => "form-control" %>
</div>
<br />
<div class="field">
<%= f.label :container_id %><br />
<%= select_tag 'package[container_id]', options_from_collection_for_select(#project.containers, 'id', 'container_id', default_blank: true), prompt: "- Select container -", :class => "form-control" %>
</div>
<br />
<div class="field">
<%= f.label :packed %><br />
<%= f.select :packed, [["Packed", true], ["Unpacked", false]],{ prompt: "- Packing -"},{ :class => "form-control" } %>
</div>
<% end %>
<div class="actions">
<br />
<%= submit_tag "Update", :class => "btn btn-primary" %>
</div>
<% end %>
</div>
And the packages controller edit_multiple actions:
def edit_multiple
#project = Project.find(params[:project_id])
#packages = Package.find(params[:package_ids])
end
def update_multiple
#packages = Package.find(params[:package_ids])
#packages.reject! do |package|
package.update_attributes(package_params.reject { |k,v| v.blank? })
end
if #packages.empty?
redirect_to project_orderline_packages_url
else
#package = Package.new(package_params)
render "edit_multiple"
end
end
packages_controller create action:
def create
project = Project.find(params[:project_id])
orderline = project.orderlines.find(params[:orderline_id])
#package = orderline.packages.new(package_params)
#package.save
if #package.save
flash[:success] = "Package(s) was successfully added."
redirect_to :back
else
render 'new'
end
And my routes:
resources :projects do
resources :containers
resources :orderlines do
resources :packages do
collection do
put :packed
get :edit_multiple
put :update_multiple
end
end
end
end
I just added my routes here:
edit_multiple_project_orderline_packages_path GET /projects/:project_id/orderlines/:orderline_id/packages/edit_multiple(.:format)
packages#edit_multiple
update_multiple_project_orderline_packages_path PUT /projects/:project_id/orderlines/:orderline_id/packages/update_multiple(.:format)
packages#update_multiple
project_orderline_packages_path GET /projects/:project_id/orderlines/:orderline_id/packages(.:format)
packages#index
POST /projects/:project_id/orderlines/:orderline_id/packages(.:format)
packages#create
new_project_orderline_package_path GET /projects/:project_id/orderlines/:orderline_id/packages/new(.:format)
packages#new
edit_project_orderline_package_path GET /projects/:project_id/orderlines/:orderline_id/packages/:id/edit(.:format)
packages#edit
project_orderline_package_path GET /projects/:project_id/orderlines/:orderline_id/packages/:id(.:format)
packages#show
PATCH /projects/:project_id/orderlines/:orderline_id/packages/:id(.:format)
packages#update
PUT /projects/:project_id/orderlines/:orderline_id/packages/:id(.:format)
packages#update
DELETE /projects/:project_id/orderlines/:orderline_id/packages/:id(.:format)
your form_tag code is update_multiple_project_orderline_packages_path
I think it should be update_multiple_project_orderline_package_path(project_id, orderline_id, package_id)
I am not 100% sure with my statement above, because you gave scrambled Rails Routes, hard to read
and your form action seems goes to packages#edit_multiple controller
so paste your edit_multiple method, not create method
are you implementing your scenario above with javascript, or just plain HTML?
The error output is:
undefined method `total_pages' for #<Review:0x007fe460871f08>
Movie#Show:
def show
#review = #movie.reviews.paginate(page: params[:page], per_page: 6).order('created_at DESC').build
end
I set #movie via before_filter.
My view:
<% if #movie.now_playing %>
<% if #movie.reviews.any? %>
<% #movie.reviews.each do |review| %>
<div id="each_review_container">
<span><%= link_to #movie.creator.name, user_path(#movie.creator) %> | </span>
<span id="time"><%= review.created_at.try(:strftime,'%b %d, %Y') %></span>
<p>Rating: <%= review.rating %>/10</p>
<p><%= review.content %></p>
</div>
<% end %>
<div class="digg_pagination"><%= will_paginate #review %></div>
<% else %>
<span id="review_message">No Reviews yet!</span>
<span id="add_new_review_link"><%= link_to 'Add New Review', new_movie_review_path(#movie) %></span>
<% end %>
<% else %>
<p id="review_message">You will be able to submit a review when the movie releases</p>
<% end %>
Restarted my server and I get the same error.
Been stuck on this for a while and would appreciate any assistance, thanks!
It looks like a bug, I think you want to get reviews not one review:
def show
#reviews = #movie.reviews.order('created_at DESC').paginate(page: params[:page], per_page: 6)
end
View:
<% if #movie.now_playing %>
<% if #reviews.any? %>
<% #reviews.each do |review| %>
<div id="each_review_container">
<span><%= link_to #movie.creator.name, user_path(#movie.creator) %> | </span>
<span id="time"><%= review.created_at.try(:strftime,'%b %d, %Y') %></span>
<p>Rating: <%= review.rating %>/10</p>
<p><%= review.content %></p>
</div>
<% end %>
<div class="digg_pagination"><%= will_paginate #reviews %></div>
<% else %>
<span id="review_message">No Reviews yet!</span>
<span id="add_new_review_link"><%= link_to 'Add New Review', new_movie_review_path(#movie) %></span>
<% end %>
<% else %>
<p id="review_message">You will be able to submit a review when the movie releases</p>
<% end %>
In Your view ,you are calling #movie.reviews
Why are you writing #movie.reviews in loop ?You should be using #review of action instead.It is firing query every time you call it.
#movie.reviews in your view is causing error here's guess,since it doesn't include pagination parameter and you are trying to pagination through it.
I am trying to add the option to upload and download files in my Rails application, but I keep getting this error when I try to upload my file:
name can't be blank
Here's my code:
newsletters_controller.rb:
class NewslettersController < ApplicationController
def index
#newsletters = Newsletter.all
end
def new
#newsletter = Newsletter.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #newsletter }
end
end
def create
#newsletter = Newsletter.new(newsletter_params)
if #newsletter.save
redirect_to newsletters_path, notice: "The newsletter #{#newsletter.name} has been uploaded."
else
render "new"
end
end
def destroy
#newsletter = Newsletter.find(params[:id])
#newsletter.destroy
redirect_to newsletters_path, notice: "The newsletter #{#newsletter.name} has been deleted."
end
private
def newsletter_params
params.require(:newsletter).permit(:newsletter, :attachment)
end
end
index.html.erb:
<% if !flash[:notice].blank? %>
<div class="alert alert-info">
<%= flash[:notice] %>
</div>
<% end %>
<br />
<%= link_to "New Newsletter", new_newsletter_path, class: "btn btn-primary" %>
<br />
<br />
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Download Link</th>
<th> </th>
</tr>
</thead>
<tbody>
<% #newsletters.each do |newsletter| %>
<tr>
<td><%= newsletter.name %></td>
<td><%= link_to "Download Newsletter", newsletter.attachment_url %></td>
<td><%= button_to "Delete", newsletter, method: :delete, class: "btn btn-danger", confirm: "Are you sure that you wish to delete #{newsletter.name}?" %></td>
</tr>
<% end %>
</tbody>
</table>
new.html.erb:
<% if !#newsletter.errors.empty? %>
<div class="alert alert-error">
<ul>
<% #newsletter.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="well">
<%= form_for #newsletter, html: { multipart: true } do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :attachment %>
<%= f.file_field :attachment %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
</div>
routes.rb:
resources :newsletters, only: [:index, :new, :create, :destroy]
root "newsletters#index"
get "newsletters/index"
get "newsletters/new"
get "newsletters/create"
get "newsletters/destroy"
change:
params.require(:newsletter).permit(:newsletter, :attachment)
to:
params.require(:newsletter).permit(:newsletter, :attachment, :name)
This way, the model will receive the name from the form and validation will succeed.
I'm having problems destroying the session, the link doesn't seem to work, the URL changes in the address bar from /dashboard to /log_out but the page does not redirect to the log in page. I'm rather baffled at this.
Dashboard View:
<% if logged_in? %>
<% if request.env['mobvious.device_type'] == :mobile %>
<div class="container" style="width: 90%" >
<table>
<tr>
<td class="dash_cont">
<%= link_to "Logout", log_out_path %>
</td>
</tr>
<tr>
<td class="dash_cont">
<h1 class="form-signin-heading" >Welcome to your personal Twitter Manager!</h1>
<p>Use this site to keep track of your followers, see how many posts they've made and many more features!</p>
<p>It's a really cool site, check it out! It's free!</p><br/><br/>
</td>
</tr>
</table>
</div>
<% elsif request.env['mobvious.device_type'] == :desktop %>
<div class="container" style="width: 70%;" >
<table>
<tr>
<td class="dash_cont">
<%= link_to "Logout", log_out_path %>
</td>
</tr>
<tr>
<td class="dash_cont">
<h1 class="form-signin-heading" >Welcome to your personal Twitter Manager!</h1>
<p>Use this site to keep track of your followers, see how many posts they've made and many more features!</p>
<p>It's a really cool site, check it out! It's free!</p><br/><br/>
</td>
</tr>
</table>
</div>
<% end %>
<% else %>
<script type="text/javascript">
window.location.href="/" // put your correct path in a string here
</script>
<% end %>
Log In View:
<% if logged_in? %>
<script type="text/javascript">
window.location.href="/dashboard" // put your correct path in a string here
</script>
<% else %>
<% if request.env['mobvious.device_type'] == :mobile %>
<div class="container" style="width: 90%" >
<table>
<tr class="login-cont">
<td class="login-tcell">
<h1 class="form-signin-heading" >Log in</h1>
<%= form_tag sessions_path, class: "form-signin" do %>
<%= text_field_tag :email, params[:email], class: "form-control", autofocus: "", required: "", placeholder: "Email address" %>
<%= password_field_tag :password, nil, class: "form-control", required: "", placeholder: "Password" %></br>
<p class="button"><%= submit_tag "Log in", class: "btn btn-lg btn-primary btn-block" %></p>
<% end %>
<%= link_to "Register", "/sign_up" %>
<% if defined?(#error) %>
<div class="error">
<%= "*" + #error %>
</div>
<% end %>
<% if defined?(#notice) %>
<div class="error">
<%= "*" + #notice %>
</div>
<% end %>
</td>
</tr>
</table>
</div>
<% elsif request.env['mobvious.device_type'] == :desktop %>
<div class="container" style="width: 70%" >
<table>
<tr class="login-cont">
<td class="login-tcell">
<h1 class="form-signin-heading" >Log in</h1>
<%= form_tag sessions_path, class: "form-signin" do %>
<%= text_field_tag :email, params[:email], class: "form-control", autofocus: "", required: "", placeholder: "Email address" %>
<%= password_field_tag :password, nil, class: "form-control", required: "", placeholder: "Password" %></br>
<p class="button"><%= submit_tag "Log in", class: "btn btn-lg btn-primary btn-block" %></p>
<% end %>
<%= link_to "Register", "/sign_up" %>
<% if defined?(#error) %>
<div class="error">
<%= "*" + #error %>
</div>
<% end %>
<% if defined?(#notice) %>
<div class="error">
<%= "*" + #notice %>
</div>
<% end %>
</td>
<td class="welcome-msg white">
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
<h1 class="form-signin-heading" >Welcome to your personal Twitter Manager!</h1>
<p>Use this site to keep track of your followers, see how many posts they've made and many more features!</p>
<p>It's a really cool site, check it out! It's free!</p><br/><br/>
</td>
</tr>
</table>
</div>
<% end %>
<% end %>
Dashboard Controller:
class DashboardController < ApplicationController
helper_method :logged_in?
def new
end
def logged_in?
if defined? session[:user_id]
true
else
false
end
end
end
Sessions Controller:
class SessionsController < ApplicationController
helper_method :logged_in?
# Use this to detect device type:
# request.env['mobvious.device_type']
def new
end
def create
user = User.authenticate(params[:email], params[:password])
if user
session[:user_id] = user.id
#notice = "Logged in!"
redirect_to "/dashboard"
else
#error = "Invalid email or password"
render "new"
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "Logged out!"
end
def logged_in?
if defined? session[:user_id]
redirect_to "/dashboard"
else
redirect_to "/"
end
end
end
Routes:
TwitterApp::Application.routes.draw do
get "dashboard/new"
get "users/new"
get "sessions/new"
get "log_out" => "sessions#destroy", :as => "log_out"
get "" => "sessions#new", :as => "log_in"
get "sign_up" => "users#new", :as => "sign_up"
get "dashboard" => "dashboard#new", :as => "dashboard"
root :to => "sessions#new"
resources :users
resources :sessions
end
Don't use defined? to check for the presence of hash values. It will always return a non false value:
some_hash = { :some_key => nil }
defined? some_hash[:some_key] # => "method"
defined? some_hash[:i_dont_exist] # => "method"
See the docs on this for more info.
In rails you can use session[:user_id].present?. This will check that the value at that key is not blank or nil. Also a better way to delete from the session is to call delete on it: session.delete(:user_id) or session.clear if you want to wipe everything.
Hi I worked through Michael Hartl's RAILSTUTORIAL book and I have a question about how he builds the user's show page.
The page is supposed to list all the posts a user has made.
UsersController
def show
#user = User.find(params[:id])
#posts = #user.posts.paginate(:per_page => "10",:page => params[:page])
#title = #user.name
end
users/show.html.erb
<table class="profile" summary="Profile information">
<tr>
<td class="main">
<h1><%= #user.name %></h1>
<%= render 'follow_form' if user_signed_in? %>
<% unless #user.posts.empty? %>
<table class="posts" summary="User posts">
<%= render #posts %> # this goes to posts/_post and sends the object as post
# that makes the _post view use a local variable correct?
</table> # is there a way to do with with an #post variable?
<%= will_paginate #posts %>
<% end %>
</td>
<td class="sidebar round">
<%= link_to avatar_for(#user), #user.avatar.url %><br />
<strong>Name</strong> <%= #user.name %><br />
<strong>URL</strong> <%= link_to user_path(#user), user_path(#user) %>
<strong>Posts</strong> <%= #user.posts.count %>
<%= render 'shared/stats' %>
</td>
</tr>
</table>
posts/_post.html.erb
<tr>
<td class="post">
<span class="title"><strong><%= link_to post.title, post %></strong></span><br />
<span class="timestamp">
Posted <%= time_ago_in_words(post.created_at) %> ago. </span>
Likers<span id="likers"><br />
</span>
</td>
<% if current_user?(post.user)%>
<td>
<%= link_to "delete", post, :method => :delete,
:confirm => "You sure?",
:title => post.content %>
</td>
<%end%>
</tr>
I need to render a partial in the users view that uses the post object, but it asks for it as #post and since no #post has been defined in the show action of the user's controller I get a nil error.
It seem odd to me to go from the user's controller to the posts view and use a local variable, which if I understand local variables correctly can't be used outsider that view. Is there a way to assign the value of post in that view to #post in the users view?
Thanks for the help
You need to use a local variable in the partial, and assign it in the locals hash. This line is a shortcut for iterating through the array and rendering the partial. I'm not sure if this works anymore in Rails 3:
<%= render #posts %>
The way I would do it:
<% #posts.each do |post| %>
<%= render 'posts/post', :post => post %>
<% end %>
The slightly older way of rendering partials:
<% #posts.each do |post| %>
<%= render :partial => 'posts/post', :locals => {:post => post} %>
<% end %>