Undefined method `courses' for nil:NilClass - ruby-on-rails

When I visit the courses/new page on my heroku app, it raises an error saying that the method courses is not defined. Also, when I create a course on the console, it has no id or created_at.
Courses Controller:
class CoursesController < ApplicationController
def home
#course = current_user.courses.find(params[:id])
end
def new
#course = current_user.courses.build
end
def create
#course = current_user.courses.build(course_params)
if #course.save
flash[:success] = "Course Created"
redirect_to coursehome_path
else
render 'new'
end
end
private
def course_params
params.require(:course).permit(:name)
end
end
College Model:
class College < ApplicationRecord
has_many :courses, dependent: :destroy
has_secure_password
def College.digest(string)
cost = ActiveModel::SecurePassword.min_cost ?
BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
end
Course model:
class Course < ApplicationRecord
belongs_to :college
end
Migration:
class CreateCourses < ActiveRecord::Migration[5.1]
def change
create_table :courses do |t|
t.string :name
t.references :college, foreign_key: true
t.timestamps
end
add_index :courses, [:college_id, :created_at]
end
end
Edit:
Actually, I created a controller called collegesessions and added the current_user method in its helper:
module CollegesessionsHelper
def log_in(college)
session[:user_id] = college.id
end
def current_user
if session[:user_id]
#current_user ||= College.find_by(id: session[:user_id])
end
end
def logged_in?
!current_user.nil?
end
def log_out
session.delete(:user_id)
#current_user = nil
end
end

When you are trying to access
current_user.courses, it looks for courses association in usermodel.
Your course model has no user association.
Course Model
class Course < ApplicationRecord
belongs_to :college
belongs_to :user
end
User model
class User < ApplicationRecord
//fields
has_many: courses
end

Related

association between two model in rails , controller

I can not find the issue with my association, but continuously getting error related to the association. I added has_many to Schools and belongs_to to members.
class CreateMembers < ActiveRecord::Migration[5.0]
def change
create_table :members do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
class CreateSchools < ActiveRecord::Migration[5.0]
def change
create_table :schools do |t|
t.string :name
t.timestamps
end
end
end
class AddSchoolRefToMembers < ActiveRecord::Migration[5.0]
def change
add_reference :members, :school, foreign_key: true
end
end
Controller:
class MembersController < ActionController::Base
before_action :set_school
def index
#members = Member.all
end
def new
#member = Member.new
end
def create
#member = Member.new(member_params)
#member.school = #school
#member.save
redirect_to members_path
end
private
def set_school
#school = School.find(params[:school])
end
def member_params
params.require(:member).permit(:name, :email,:school)
end
end
Instead of assigning the #school itself you should assign the id of that school:
def create
#member = Member.new(member_params)
#member.school = #school.id # here it is #school.id
#member.save
redirect_to members_path
end
The associations work with IDs not Arrays.
#school return the school record completely you just need the id to create the association.

private messaging system in rails

I am working on a messaging system in my rails app. I already have it working properly for sending messages between 2 users(sender and recipient). This setup is fine but how can I make a new conversation for each room so the uniqueness checking will be only between an user and a room or viceversa?? Each user is only allowed to send message to a room from the room show page. So room_id can be fetched there. A single user can have many listings which makes it complicated for me.So am confused on what change to make in the below code to accomplish that??Or do I have to make a different design approach for the models?
I have a user, listing, conversation and message model
conversation.rb
class Conversation < ActiveRecord::Base
belongs_to :sender, foreign_key: :sender_id, class_name: 'User'
belongs_to :recipient, foreign_key: :recipient_id, class_name: 'User'
has_many :messages, dependent: :destroy
validates_uniqueness_of :sender_id, scope: :recipient_id
scope :involving, -> (user) do
where("conversations.sender_id = ? OR conversations.recipient_id = ?", user.id, user.id)
end
scope :between, -> (sender_id, recipient_id) do
where("(conversations.sender_id = ? AND conversations.recipient_id = ?) OR (conversations.sender_id = ? AND conversations.recipient_id = ?)",
sender_id, recipient_id, recipient_id, sender_id)
end
end
Message.rb
class Message < ActiveRecord::Base
belongs_to :conversation
belongs_to :user
validates_presence_of :content, :conversation_id, :user_id
def message_time
created_at.strftime("%v")
end
end
conversations_controller.rb
class ConversationsController < ApplicationController
before_action :authenticate_user!
def index
#conversations = Conversation.involving(current_user)
end
def create
if Conversation.between(params[:sender_id], params[:recipient_id]).present?
#conversation = Conversation.between(params[:sender_id], params[:recipient_id]).first
else
#conversation = Conversation.create(conversation_params)
end
redirect_to conversation_messages_path(#conversation)
end
private
def conversation_params
params.permit(:sender_id, :recipient_id)
end
end
messages_controller.rb
class MessagesController < ApplicationController
before_action :authenticate_user!
before_action :set_conversation
def index
if current_user == #conversation.sender || current_user == #conversation.recipient
#other = current_user == #conversation.sender ? #conversation.recipient : #conversation.sender
#messages = #conversation.messages.order("created_at DESC")
else
redirect_to conversations_path, alert: "You don't have permission to view this."
end
end
def create
#message = #conversation.messages.new(message_params)
#messages = #conversation.messages.order("created_at DESC")
if #message.save
redirect_to conversation_messages_path(#conversation)
end
end
private
def set_conversation
#conversation = Conversation.find(params[:conversation_id])
end
def message_params
params.require(:message).permit(:content, :user_id)
end
end
Your relations are off. A conversation where the sender and recipient are fixed is no good - in fact thats just a monolog!
Instead we need a real many to many relation. That means we need a third table to store the link between users and converstations
So lets start by generating a model:
rails g model UserConversation user:belongs_to conversation:belongs_to
This will generate a model and a migration for a join table which will link users and conversations. We should now also take care of the uniqueness requirement. Open up the migration:
class CreateUserConversations < ActiveRecord::Migration
def change
create_table :user_conversations do |t|
t.belongs_to :user, index: true, foreign_key: true
t.belongs_to :conversation, index: true, foreign_key: true
t.timestamps null: false
end
# Add this constraint
add_index :user_conversations, [:user_id, :conversation_id], unique: true
end
end
That constraint that ensures the uniqueness on the database level and protects against race conditions. We also want a validation on the software level.
class UserConversation < ActiveRecord::Base
belongs_to :user
belongs_to :conversation
validates_uniqueness_of :user_id, scope: :conversation_id
end
Now we setup the relations in User and Conversation so that they go through the join model:
class User < ActiveRecord::Base
has_many :user_conversations
has_many :conversations, through: user_conversations
def has_joined?(conversation)
conversations.where(id: conversation).exist?
end
end
class Conversation < ActiveRecord::Base
has_many :user_conversations
has_many :messages
has_many :users, through: user_conversations
def includes_user?(user)
users.where(id: user).exist?
end
end
This lets us do #user.conversations or #conversation.users. We don't need the hacky scopes.
This is an example of how you could possibly add a user to a conversation on the fly:
class MessagesController < ApplicationController
# ...
def create
unless current_user.has_joined?(conversation)
# #todo handle case where this fails
#conversation.users << current_user
end
#message = #conversation.messages.new(message_params) do |m|
# get the current user from the session or a token
# using params is an open invitation for hacking
m.user = current_user
end
if #message.save
redirect_to conversation_messages_path(#conversation)
else
render :new
end
end
# ...
end
But note that you still have quite a way to go and will likely need several different controllers to properly represent messages in different contexts:
/messages/:id => MessagesController
/users/:user_id/messages => Users::MessagesController
/conversations/:id/messages => Conversations::MessagesController

How to retrieve nested model data from another model? : undefined method error

So the basis of my code so far is:
a customer has_one calendar
a calendar belongs_to a customer
a calendar has_many events
an event belongs_to a calendar
I am trying to, when creating a new event, specify the customer and calendar it belongs to but it throws error "undefined method `Calendar'":
class EventsController < ApplicationController
def new
#event = Event.new
#currentcalendar = current_customer.calendar # this is where it is failing
end
def create
if #event = #currentcalendar.build.event(event_params)
redirect_to '/main'
else
redirect_to '/compose'
end
end
private
def event_params
params.require(:event).permit(:calendar_id, :name, :starts_at, :ends_at)
end
end
this is my current_customer method within application_controller:
def current_customer
if (customer_id = session[:customer_id])
#current_customer ||= Customer.find_by(id: customer_id)
elsif (customer_id = cookies.signed[:customer_id])
customer = Customer.find_by(id: customer_id)
if customer && customer.authenticated?(cookies[:remember_token])
session[:customer_id] = customer.id #log in
#current_customer = customer
end
end
end
Here are the related controller files. Customer:
class CustomersController < ApplicationController
def new
#customer = Customer.new
#businesses = Business.all
#calendar = Calendar.new
end
def create
#customer = Customer.create(customer_params)
#calendar = #customer.build_calendar
#customer.save!
session[:customer_id] = #customer.id
redirect_to '/'
rescue ActiveRecord::RecordInvalid => ex
render action: 'new', alert: ex.message
end
private
def customer_params
params.require(:customer).permit(:first_name, :last_name, :business_no, :email, :password, :business_id)
end
Calendar:
class CalendarsController < ApplicationController
def new
#calendar = Calendar.new(calendar_params)
end
def create
#calendar = Calendar.new(calendar_params)
end
private
def calendar_params
params.require(:customer_id)
end
end
I'm very new to Ruby/ Rails and so can't figure this out by myself. Is this problem occurring because I have wrongly created my calendar? I wanted it to be created when its user is created, which works, but I just don't know how to get to the calendar and user within the events controller.
Thanks for your help!
EDIT: these are the model classes.
customer:
class Customer < ActiveRecord::Base
belongs_to :business
has_one :calendar
has_secure_password
attr_accessor :remember_token
#remembers a user in the database for use in persistent sessions
def remember
self.remember_token = Customer.new_token
update_attribute(:remember_digest, Customer.digest(remember_token))
end
def Customer.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
def forget
update_attribute(:remember_digest, nil)
end
def Customer.new_token
SecureRandom.urlsafe_base64
end
#returns true if the given token matches the digest
def authenticated?(remember_token)
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
end
calendar:
class Calendar < ActiveRecord::Base
belongs_to :customer
has_many :events
end
event:
class Event < ActiveRecord::Base
belongs_to :calendar
end
Your current_customer can be nil at times. To avoid this you can add a before_filter callback that checks if there is a customer that is logged in or not.
In your application_controller create a method called customer_found?
def customer_found?
current_customer.present?
end
Change your events controller to
class EventsController < ApplicationController
before_filter :customer_found?
before_filter :prepare_calendar, only: [:new, :create]
def new
#event = Event.new
end
def create
if #event = #current_calendar.build.event(event_params)
redirect_to '/main'
else
redirect_to '/compose'
end
end
private
def prepare_calendar
#current_calendar = current_customer.calendar
end
def event_params
params.require(:event).permit(:calendar_id, :name, :starts_at, :ends_at)
end
end
Since you did not assign your #current_calendar in your create method then you are gonna get undefined method build for nil class. You need to initialize the variable since it can not get it from the new method. Each action has its own independent variables so make sure to prepare all necessary variables before using them.

Create review in two scopes at once

I have two types of users, one that can create movies and one that can create reviews:
class User < ActiveRecord::Base
has_many :created_movies, foreign_key: 'creator_id', class_name: 'Movie'
has_many :reviewed_movies, foreign_key: 'reviewer_id', class_name: 'Review'
end
class Review < ActiveRecord::Base
belongs_to :movie
belongs_to :reviewer, class_name: 'User'
end
class Movie < ActiveRecord::Base
has_many :reviews
belongs_to :creator, class_name: 'User'
end
What I want to do is create the particular review and set the reviewer_id to the current_user at once. Here is my code for Review#create:
def create
#movie = Movie.find(params[:movie_id])
#review = #movie.reviews.merge(current_user.reviewed_movies).create(review_params)
if #review.save
.
.
.
end
This does not make sense to me as I do have current_user defined in my SessionsHelper and have 'include SessionsHelper' in my application controller. Code for current_user:
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
I've been stuck on this problem for a couple hours and would appreciate any assistance on how I can get past it, thanks!
The following code will create the new review and assign it to the current_user:
def create
#movie = Movie.find(params[:movie_id])
#review = #movie.reviews.build(review_params)
#review.reviewer = current_user
if #review.save
# ...
end
or alternatively:
def create
#review = Review.new(review_params)
#review.movie = Movie.find(params[:movie_id])
#review.reviewer = current_user
if #review.save
# ...
end

undefined method `team_id' - Ruby on Rails

The error is undefined method 'team_id' for Player:0x007fb5f41f3838.
I am trying to edit players and I am not able to do that because of an undefined method.
My guess is it has something to do with my relations. I am learning relations between models so they may not be correct.
This is my Player Model
class Player < ActiveRecord::Base
validates_length_of :description, :maximum=>4000
has_many :descriptions, through: :fouls
has_many :fouls, as: :foul_by_id
has_many :fouls, as: :foul_on_id
belongs_to :team
end
This is my Player Controller
class PlayersController < ApplicationController
before_action :authenticate_user!
before_action :most_recent_fouls
def index
#players = Player.all
end
def show
#player = Player.find(params[:id])
end
def new
#player = Player.new
end
def create
#player = Player.new(players_params)
if #player.save
redirect_to(:action => "index")
else
render("new")
end
end
def edit
#player = Player.find(params[:id])
end
def update
#player = Player.find(params[:id])
if #player.update_attributes(players_params)
redirect_to(:action => "show", :id => #player.id)
else
render("index")
end
end
def destroy
player = Player.find(params[:id]).destroy
redirect_to(:action => "index")
end
private
def players_params
params.require(:player).permit(:name, :number, :position, :bios, :descriptions, :team_id)
end
end
Because of my gut saying that it has to do with relations, here is my Team Model
class Team < ActiveRecord::Base
has_many :players
validates :name, presence: true
end
My migration table for Player
class CreatePlayers < ActiveRecord::Migration
def change
create_table :players do |p|
p.string :name
p.string :number
p.string :position
p.string :bio
p.string :description
p.integer :team_id
p.timestamps
end
end
end
Any help is appreciated. Please explain your answer. Tell me if you need any more code to be displayed.

Resources