I have a conversation partial with route being resources:conversations, but when i try to render this partial in my navigation bar (to list all their active conversations), i get an undefined method error.
undefined method `author' for nil:NilClass
Right now i'm doing <%= render 'conversations/conversation' %>
Is there a special way to render your partials ANYWHERE on your website?
EDIT: Providing codes
conversations/_conversation.html.erb
<div>
From <strong><%= conversation.author.email %></strong> to
<strong><%= conversation.receiver.email %> (<%#= conversation.receiver.online? ? 'online' : 'offline' %>)</strong>
<br>
<%= link_to 'View conversation', conversation_path(conversation) %>
<hr>
</div>
layouts/_navigation.html.erb
<%= render 'conversations/conversation' %>
routes
#Chat
resources :conversations
resources :personal_messages
resources :users
mount ActionCable.server => '/cable'
EDIT2:
conversations_controller.rb
class ConversationsController < ApplicationController
before_action :set_conversation
before_action :check_participating!, except: [:index]
def index
#conversations = Conversation.participating(current_user).order('updated_at DESC')
respond_to do |format|
format.html
format.js
end
end
def show
#personal_message = PersonalMessage.new
#conversations = Conversation.participating(current_user)
end
private
def set_conversation
#conversation = Conversation.find_by(id: params[:id])
end
def check_participating!
redirect_to root_path unless #conversation && #conversation.participates?(current_user)
end
end
applicationcontroller
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :authenticate_user!
end
The normal way to do this is:
render(partial: 'conversations/conversation', object: #conversation)
If you don't provide a reference object there's no data binding to the local conversation variable and you get the nil reference error.
Related
I've been searching for hours but I can't find the answer anywhere. I'm new to ruby on rails and I can't figure out how to fix this. What caused the problem is that I moved an instance variable from one file to another and now no links are working and the error always displays: undefined method `each' for nil:NilClass
here is my code:
Application.html.erb:
<% number = 1 %>
<% #projects.each do |project| %>
<%= link_to project, id: "a-#{number}" do %>
<div class="flex">
<div class="each--project" id="project-<%= number %>">
<h3><%= project.title %></h3>
<p class="date"><%= project.created_at.strftime("%A, %b %d")%></p>
<p class="content"><%= project.description %></p>
</div>
</div>
<% number = number + 1 %>
<% end %>
<% end %>
application_controller.rb
class ApplicationController < ActionController::Base
def index
#projects = Project.all
end
protect_from_forgery with: :exception
end
projects_controller
class ProjectsController < ApplicationController
before_action :find_project, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#projects = Project.all.order("created_at desc")
end
def new
#project = Project.new
end
def create
#project = Project.new project_params
if #project.save
redirect_to #project, notice: "Yay Mia! That project was saved!"
else
render 'new'
end
end
def show
end
def edit
end
def update
if #project.update project_params
redirect_to #project, notice: "Yay Mia! That project was updated!"
else
render 'edit'
end
end
def destroy
#project.destroy
redirect_to projects_path
end
private
def find_project
#project = Project.friendly.find(params[:id])
end
def project_params
params.require(:project).permit(:title, :description, :link, :slug)
end
end
routes rb
Rails.application.routes.draw do
devise_for :users
resources :posts
resources :projects
resources :contacts, only: [:new, :create]
get 'welcome/index'
root 'welcome#index'
get '*path' => redirect('/')
end
You don't have an index action on the ApplicationController
You can however achieve the same thing with a before_action if you want it loaded for all actions in all controllers. This is not something I would recommend though.
class ApplicationController < ActionController::Base
before_action :load_projects
def load_projects
#projects = Project.all
end
end
Hint:
<% number = 1 %>
<% #projects.each do |project| %>
can be much better written as
<% #projects.each_with_index do |project, number| %>
If you're referencing something in your main application layout then it must be loaded for every controller. Putting it in one specific controller only engages it when that controller is in use, which it will only be on very specific routes.
If #projects needs to be populated on every request, move that to a load_projects method and add a before_action filter in your main ApplicationController. Putting it in a default index method doesn't work, that will never get run.
You can always stub in Rails.logger.debug('...') type calls to see if your code is actually being exercised. Watch log/development.log constantly to see what's happening. Most problems can be quickly resolved by examining what's been logged.
So we have two models, Articles and Likes. An article has_many :likes, a like belongs_to :user and belongs_to :article. The resources are nested as below:
resources :articles do
resources :likes
end
We have logic in our view that renders a "like" or "unlike" button as follows:
<% unless signed_in? and current_user.likes? #article %>
<%= link_to "Like this article!", article_likes_path(#article), method: :post, remote: true %>
<% else %>
<%= link_to "Unlike this article!", article_like_path(#article, current_user.article_like(#article)), method: :delete, remote: true %>
<% end %>
Here is our LikesController:
class LikesController < ApplicationController
before_action :set_article
before_action :set_like, only: [:destroy]
before_action :authenticate_user!
after_action :redirect_to_article, only: [:create, :destroy]
respond_to :html
def create
like = Like.new
current_user.likes << like
#article.likes << like
redirect_to #article
end
def destroy
#like.destroy
redirect_to #article
end
private
def set_article
#article = Article.find(params[:article_id])
end
def set_like
#like = Like.find(params[:id])
end
def like_params
params[:like]
end
def redirect_to_article
redirect_to #article
end
end
In the view, the likes count is rendered with:
<%= #article.likes.size %>
The problem is, after we hit "like" or "unlike", the like (or unlike) goes through the backend, but we have to manually refresh the page to see the like count refresh. In other words, the two calls to redirect_to #article in the LikesController do not actually refresh the page.
Any ideas? Thanks.
Your buttons are using remote: true which is telling Rails to use AJAX, but your controller is set to only respond to HTML. From you question, it sounds like you're cool with the page being refreshed, so just delete remote: true from the buttons and it should work.
I am trying to get my followers to display on the pages/friends however, I keep getting multiple errors like an undefined method `each' for nil:NilClass in my Pages#friends error. I am following Michael Harlts tutorials on follow/unfollow however, I needed to tweak a couple of things because I did not follow the entire thing.
When a user goes to their Friends link (pages/friends) I want it to display everyone who is following them. Like how my users/index displays everyone. Please see my code below. Any help of suggestions would be great.
Pages/friends
<h1>Friends</h1>
<% #user ||= current_user %>
<% #users.each do |user| %>
<center><%= link_to image_tag(user.avatar.url(:thumb)), user %></center>
<strong><center><br><%= link_to user.name, user %></br></center></strong>
<% if current_user.admin %>
<% end %>
<% end %>
Pages/controller
def home
end
def about
end
def friends
end
end
**Users/index*
<% provide(:title, 'All users') %>
<h1>All users</h1>
<div class="col-md-offset-4 col-med-8">
<div class="panel panel-default">
<div class="panel-heading center">
<% #users.each do |user| %>
<center><%= link_to image_tag(user.avatar.url(:thumb)), user %></center>
<strong><center><br><%= link_to user.name, user %></br></center></strong>
<% if current_user.admin %>
<% #user ||= current_user %>
<div class="stats">
<a href="<%= friends_path(#user) %>">
<strong id="following" class="stat">
<%= #user.followed_users.count %>
</strong>
following
</a>
<a href="<%= friends_path(#user) %>">
<strong id="followers" class="stat">
<%= #user.followers.count %>
</strong>
followers
</a>
</div>
<center><%= link_to "Delete", user, method: :delete, data: { confirm: "Are you sure?" } %></center>
<% end %>
<% end %>
<div class="center">
<%= will_paginate #users, renderer: BootstrapPagination::Rails %>
</div>
</div>
Routes
devise_for :admins
devise_for :users
resources :posts
resources :users do
member do
get :following, :followers
end
end
resources :relationships, only: [:create, :destroy]
resources :user_friendships do
member do
put :accept
end
end
get "users/show"
root "pages#home"
get 'feed', to: 'posts#index', as: :feed
get "about" => "pages#about"
get "friends" => 'pages#friends'
match 'users/:id' => 'users#show', via: :get
match 'users/:id' => 'users#index', via: :destroy
match 'users/:id' => 'users#destroy', via: :get
match 'users/:id' => 'users#destroy', via: :delete
get '/:id', to: 'users#show'
Users/controller
class UsersController < ApplicationController
before_action :correct_user, only: [:edit, :update, :destroy, :following, :followers]
before_action :authenticate_user!, except: [:index, :show]
before_action :admin_user, only: :destroy
def index
#users = User.paginate(page: params[:page], :per_page => 5)
end
def show
#user = User.find(params[:id])
if #user
#posts = #user.posts.all
render actions: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "Your account has been deleted."
redirect_to root_path
end
def correct_user
#user = User.find(params[:id])
redirect_to root_path
end
def admin_user
redirect_to root_path unless current_user.admin?
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
end
end
Relationships Controller
class RelationshipsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
def create
#user = User.find(params[:relationship][:followed_id])
current_user.follow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
The error undefined method 'each' for nil:NilClass means that you're calling .each on an object that is nil.
For example, here, #users could be nil:
def index
#users = User.paginate(page: params[:page], :per_page => 5)
end
If you then call <% #users.each ... %>, you'll get the undefined method error. You've posted quite a lot of code above, so it's difficult to know exactly where that error is being thrown. Try and work out exactly where the error is being thrown (posting a stack trace would help), and work out which object is nil. Then work out why it's nil - is there no data? Or is your ActiveRecord query returning no results?
Hopefully that gives you some pointers as to where to start looking next.
Edit: As mmichael points out, the error is being caused due to the fact that #users was undeclared in pages#friends. Thanks!
As you're new, I'll give you another opinion. What I'm going to write is similar to Sam's answer; I intend to help you appreciate how Rails works, to further your experience
Objects
You must remember that Rails is built on top of Ruby - an object-orientated language. This means every time you declare / use a variable inside Ruby, you're actually accessing an object, which Ruby treats as having a series of methods:
Ruby is pure object-oriented language and everything appears to Ruby
as an object. Every value in Ruby is an object, even the most
primitive things: strings, numbers and even true and false. Even a
class itself is an object that is an instance of the Class class. This
chapter will take you through all the major functionalities related to
Object Oriented Ruby.
I write this because Ruby's object-orientated structure is much different than handling variables in the "traditional" sense -- in the simplest description, it means that Ruby presumes that objects are ALWAYS present (it just builds them from the NilClass), allowing you call "empty" data sets without running into "undeclared" issues
The problems occur when you try and run methods like .each on empty / nil objects. If you do this, errors like the one you alluded to occur; preventing you from being able to perform the functionality which you intended
--
Fix
Your error basically means you haven't declared your variable.
After seeing your code, the most obvious problem will be here:
#app/controllers/pages_controller.rb
Class PagesController < ApplicationController
def friends
# normally, you'd define your #instance_variables here
end
end
I see that your friends action does not declare your #users variable, which is required to perform the #users.each method. As recommended by Sam, the first step is to ensure you're able to declare this value, allowing you to loop through it as required:
#app/controllers/pages_controller.rb
Class PagesController < ApplicationController
def friends
#users = User.all
end
end
I have this form where a user input a review. A user must be signed with Facebook to save a review.
I use a before_filter to check if the user is signed in or not.
But I get this error: undefined local variable or method signed_in_user'
.
The other thing is, how do I logged the user in with facebook and the save its review? Without losing and making the user input the same review again.
Review form:
<%= form_for [#school, Review.new] do |f| %>
<%= f.text_area :content %>
<% if current_user %>
<%= f.submit 'Save my review', :class => "btn" %>
<% else %>
<%= f.submit 'Save my review and sign me into facebook', :class => "btn" %>
<% end %>
<%end %>
ReviewsController
class ReviewsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
def create
#school = School.find(params[:school_id])
#review = #school.reviews.new(params[:review])
#review.user_id = current_user.id
if #review.save
redirect_to #review.school, notice: "Review has been created."
else
render :new
end
end
def new
#school = School.find_by_id(params[:school_id])
#review = Review.new
end
end
ReviewsHelper
module ReviewsHelper
def signed_in?
!current_user.nil?
end
def signed_in_user
unless signed_in?
redirect_to "/auth/facebook"
end
end
end
I am using omniauth to authenticate users from facebook.
include your ReviewsHelper in controller:
class ReviewsController < ApplicationController
include ReviewsHelper #or helper :reviews
before_filter :signed_in_user, only: [:create, :destroy]
def create
#school = School.find(params[:school_id])
#review = #school.reviews.new(params[:review])
#review.user_id = current_user.id
if #review.save
redirect_to #review.school, notice: "Review has been created."
else
render :new
end
end
def new
#school = School.find_by_id(params[:school_id])
#review = Review.new
end
end
Your helper is not included in the controller by default.
You can include it as codeit suggested.
Most people put their before filters in ApplicationController as a private method.
EDIT:
To persist the log in, save it to the session data. Look up sessions in the Rails Guides.
I have meet the same problem before. The helper is to help edit the view layer. The before_filter method cannot be written in the helper by default, unless you write 'include BlbalblaHelper' in the controller.
You Can just write the before_filter method in the application_controller.rb as a private method, or in lib/ folder. I think both of them are better approach for DRY.
I think I am missing something while using the Authlogic gem w/ Rails. To set the stage I have multiple users and each user can create posts and comments. Upon the display of a post or comment I would like to give the user who created them the option to edit or destroy.
I am successfully using the following code to hide and show elements based on if a user is logged in or not but can't seem to find out how to only show these links to the actual user who created them...not any user that is logged in.
<% if current_user %>
<%= link_to 'Edit', edit_question_path(question) %> |
<%= link_to 'Destroy', question, :confirm => 'Are you sure?', :method => :delete %>
<% else %>
<p>nothing to see here</p>
<% end %>
Here is the def of current_user located in the application controller in case I need to change something here.
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
protect_from_forgery # See ActionController::RequestForgeryProtection for details#
helper_method :current_user
private
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
end
Authentication solutions like authlogic were not built to support what you're trying to do. There are authorization solutions you can use on top on authlogic that let you do fine-grained checks like whether or not a user may access a given action:
<% if current_user.may_update_question? #question %>
<%= link_to 'Edit', edit_question_path(#question) %>
<% end %>
The example above is uses Aegis.
Try this:
class ApplicationController < ActionController::Base
# add your methods (eg: current_user etc)
helper_method :current_user, :logged_in?, :current_user_is_owner?
def init_data
klass = controller_name.camelize.singularize.constantize #User
param_key = controller_name.camelize.downcase.singularize.to_sym # :user
obj = case (action_name.to_sym)
when :new, :create
klass.new(params[param_key])
when :edit, :show, :destroy
klass.find(params[:id])
when :update
klass.find(params[:id]).tap{|o| o.attributes = params[param_key]}
end
instance_variable_set("##{param_key}", obj) # set the obj to #line_item
end
def require_user
return true if logged_in?
render_error_message("You must be logged in to access this page",
new_user_session_url)
return false
end
def require_owner
obj = instance_variable_get("##{controller_name.singularize.camelize.underscore}") # LineItem becomes #line_item
return true if current_user_is_owner?(obj)
render_error_message("You must be the #{controller_name.singularize.camelize} owner to access this page", root_url)
return false
end
def logged_in?
return current_user != nil
end
def current_user_is_owner?(obj)
logged_in? and obj.respond_to?(:user_id) and
(obj.send(:user_id) == current_user.id)
end
def render_error_message message, url
respond_to do |format|
format.html do
flash[:notice] = message
if request.xhr?
head :bad_request, :error => message
else
redirect_to url
end
end
format.json { render :json => message, :status => :unprocessable_entity }
format.xml { render :xml => message, :status => :unprocessable_entity }
end
end
end
Now in your controller
class PostsController < ApplicationController
before_filter :require_user # all actions require user to be logged in
before_filter :init_data # create a member variable called #post, initialized based on the action
before_filter :require_owner, :only => [:edit, :update, :destroy] #edit, update, and destroy actions require ownership
def update
if #post.save
else
end
end
end
In the view code:
<% if current_user_is_owner?(question) %>
.. display something
<% end %>