I'm following the Treebook tutorial on teamtreehouse.com (making a facebook clone) using rails. I have created a userfriendships controller and am getting this error when trying to reference a friend's full name in my index.html page of the friendships view: undefined method `full_name' for nil:NilClass. The problem occurs on line 4 here, (this is /views/user_friendships/index.html)
<div class="page-header">
<h1>Friends</h1>
</div>
<hr />
<% if #user_friendships.empty? %>
<em>No <%= params[:list] %> friends yet!</em>
<% end %>
<% #user_friendships.each do |friendship| %>
<% friend = friendship.friend %>
<div id="<%= dom_id(friendship) %>" class="friend panel">
<div class="panel-heading">
<span class="pull-right label>"><%= "#{friendship.state}".upcase %></span>
<h3 class="panel-title"><%= friend.full_name %></h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-1">Put gravatar url here</div>
<div class="col-md-11">
<em>Friends since time_ago_in_words ago</em>
</div>
</div>
</div>
<div class="panel-footer clearfix">
<%= link_to "Update friendship", edit_user_friendship_path( friend.profile_name), class: 'btn btn-primary pull-right' %>
</div>
</div>
<% end %>
Here's the User Friendships Controller:
class UserFriendshipsController < ApplicationController
before_filter :authenticate_user!
def index
#user_friendships = current_user.user_friendships.all
end
def new
if params[:friend_id]
#friend = User.where(profile_name: params[:friend_id]).first
raise ActiveRecord::RecordNotFound if #friend.nil?
#user_friendship = current_user.user_friendships.new(friend: #friend)
else
flash[:error] = "Friend required"
end
rescue ActiveRecord::RecordNotFound
render file: 'public/404', status: :not_found
end
def create
if params[:user_friendship] && params[:user_friendship].has_key?(:friend_id)
#friend = User.where(profile_name: params[:user_friendship][:friend_id]).first
#user_friendship = UserFriendship.request(current_user, #friend)
respond_to do |format|
if #user_friendship.new_record?
format.html do
flash[:error] = "There was a problem creating that friend request."
redirect_to profile_path(#friend)
end
format.json { render json: #user_friendship.to_json, status: :precondition_failed }
else
format.html do
flash[:success] = "Friend request sent to #{#friend.full_name}."
redirect_to profile_path(#friend.profile_name)
end
format.json { render json: #user_friendship.to_json }
end
end
else
flash[:error] = "Friend required"
redirect_to root_path
end
end
def edit
#friend = User.where(profile_name: params[:id]).first
#user_friendship = current_user.user_friendships.find(params[:id])
end
def destroy
#user_friendship = current_user.user_friendships.find(params[:id])
if #user_friendship.destroy
flash[:success] = "Friendship destroyed."
end
redirect_to user_friendships_path
end
def accept
#user_friendship = current_user.user_friendships.find(params[:id])
if #user_friendship.accept!
flash[:success] = "You are now friend with #{#user_friendship.friend.full_name}"
else
flash[:error] = "That friendship could not be accepted."
end
redirect_to user_friendships_path
end
def block
#user_friendship = current_user.user_friendships.find(params[:id])
if #user_friendship.block!
flash[:success] = "You have blocked #{#user_friendship.friend.full_name}."
else
flash[:error] = "That friendship could not be blocked."
end
redirect_to user_friendships_path
end
private
def friendship_association
case params[:list]
when nil
current_user.user_friendships
when 'blocked'
current_user.blocked_user_friendships
when 'pending'
current_user.pending_user_friendships
when 'requested'
current_user.requested_user_friendships
when 'accepted'
current_user.accepted_user_friendships
end
end
private
def user_friendship_attributes
params.require(:user_friendship).permit(:user, :friend, :user_id, :friend_id, :state)
end
end
Lastly, I will include the user friendships model:
class UserFriendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, class_name: 'User', foreign_key: 'friend_id'
state_machine :state, initial: :pending do
after_transition on: :accept, do: :send_acceptance_email
state :requested
event :accept do
transition any => :accepted
end
end
def self.request(user1, user2)
transaction do
friendship1 = create(user: user1, friend: user2, state: 'pending')
friendship2 = create(user: user2, friend: user1, state: 'requested')
friendship1.send_request_email
friendship1
end
end
def send_request_email
UserNotifier.friend_requested(id).deliver
end
def send_acceptance_email
UserNotifier.friend_request_accepted(id).deliver
end
def mutual_friendship
self.class.where({user_id: friend_id, friend_id: user_id}).first
end
# Manually updating the state to avoid callbacks and infinite loops
def accept_mutual_friendship
mutual_friendship.update_attribute(:state, 'accepted')
end
end
I'm aware that I must have to change this somehow, but am at a loss as to how to do that. I would like this page to display all of the friends that a user has, either in an accepted or pending state. Can anyone see what I need to do?
Thanks!
EDIT: If I change the views/user_friendships/index.html to friend.full_name instead of #friend.full_name I still get the same error. I can verify that the users currently in the db all have a valid first and last name (so the full name method should work).
Another EDIT: Here is a rails console entry showing that I do have userfriendships in the database:
2.1.2 :003 > UserFriendship.all
UserFriendship Load (0.3ms) SELECT "user_friendships".* FROM "user_friendships"
=> #<ActiveRecord::Relation [#<UserFriendship id: 1, user_id: 6, friend_id: nil, created_at: "2014-10-04 14:20:42", updated_at: "2014-10-04 14:20:42", state: nil>, #<UserFriendship id: 2, user_id: 6, friend_id: nil, created_at: "2014-10-04 14:21:32", updated_at: "2014-10-04 14:21:32", state: nil>, #<UserFriendship id: 3, user_id: 6, friend_id: 3, created_at: "2014-10-04 14:21:50", updated_at: "2014-10-04 14:21:50", state: nil>, #<UserFriendship id: 4, user_id: 6, friend_id: 6, created_at: "2014-10-04 14:23:58", updated_at: "2014-10-04 14:23:58", state: nil>, #<UserFriendship id: 5, user_id: 6, friend_id: 6, created_at: "2014-10-04 14:25:05", updated_at: "2014-10-04 14:25:05", state: nil>, #<UserFriendship id: 6, user_id: 6, friend_id: 4, created_at: "2014-10-04 14:25:15", updated_at: "2014-10-04 14:25:15", state: nil>, #<UserFriendship id: 7, user_id: 6, friend_id: 6, created_at: "2014-10-04 20:42:40", updated_at: "2014-10-04 20:42:40", state: nil>, #<UserFriendship id: 8, user_id: 6, friend_id: 6, created_at: "2014-10-04 20:45:44", updated_at: "2014-10-04 20:45:44", state: nil>, #<UserFriendship id: 9, user_id: 6, friend_id: 6, created_at: "2014-10-04 21:38:17", updated_at: "2014-10-04 21:38:17", state: nil>, #<UserFriendship id: 10, user_id: 6, friend_id: 6, created_at: "2014-10-09 16:53:44", updated_at: "2014-10-09 16:53:44", state: "pending">, ...]>
2.1.2 :004 >
Another Edit: Here is the user model models/user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :first_name, presence: true
validates :last_name, presence: true
validates :profile_name, presence: true,
uniqueness: true,
format: {
with: /\A.+\z/,
message: "must be formatted correctly"
}
has_many :statuses
has_many :user_friendships
has_many :friends, -> { where user_friendships: { state: 'accepted'} }, through: :user_friendships
has_many :pending_user_friendships, -> { where state: 'pending' },
class_name: 'UserFriendship',
foreign_key: :user_id
has_many :pending_friends, through: :pending_user_friendships, source: :friend
def full_name
first_name + " " + last_name
end
def gravatar_url
stripped_email = email.strip
downcased_email = stripped_email.downcase
hash = Digest::MD5.hexdigest(downcased_email)
"http://gravatar.com/avatar/#{hash}"
end
end
instead of #friend.full_name use friend.full_name
You have define it here <% friend = friendship.friend %>
So I am pretty sure what happened was that some of my database tables were dropped during a rake db:migrate that I ran. I did it because I was looking at a similar rails app that was building a social network site and switching my database back and forth between the two. Not sure how or why migrate deleted something ( i believe it deleted first and last name which is why full_name would be an nil error). I am going to revert to a previous commit where everything was working and try to rebuild from there.
Thanks for the posts!
tl;dr: dont run db:migrate without knowing what you're doing
Related
I'm building a simple ecommerce webpage and the functionality I want to create is:
User clicks an "ADD TO CART" button in one of the products --> the ORDER is created with the user_id --> the ORDER_ITEM is created with order_id and product_id.
I want to build all the logic in OrderItem model:
class OrderItem < ApplicationRecord
belongs_to :order
belongs_to :product
before_validation :generate_order
private
def self.generate_order(user)
if Order.find_by(status: 1)
order = Order.find_by(status: 1)
else
order = Order.new(status: 1, total: 0, subtotal: 0, date: Date.today())
order.user = user
order.save!
end
return order
end
end
Basically, if there is an Order open (status = 1) then return that order and if not create one.
And in the OrderItem controller:
class OrderItemsController < ApplicationController
def create
#product = Product.find(params[:product_id])
#order = OrderItem.generate_order(current_user)
#order_item = OrderItem.new
#order_item.product = #product
#order_item.order = #order
if #order_item.save!
redirect_to cart_path
else
redirect_to root_path
end
end
def delete
end
end
Everything goes well until it arrives to the .save! point where it throws this error:
undefined method `generate_order' for #<OrderItem:0x00007fe8f77552c8>
I checked the logs and everything seems to have been created well:
>> #order_item
=> #<OrderItem id: nil, order_id: 1, product_id: 65, created_at: nil, updated_at: nil>
>> #order
=> #<Order id: 1, subtotal: 0, total: 0, date: "2021-09-05 00:00:00", user_id: 5, created_at: "2021-09-05 00:00:12", updated_at: "2021-09-05 00:00:12", status: 1>
>> #product
=> #<Product id: 65, name: "Shou Mei", description: "Sequi dolores facilis rerum quo odit veritatis ips...", price: 5893, rating: 5, user_id: 13, created_at: "2021-09-03 23:54:46", updated_at: "2021-09-03 23:54:47", availability: 2>
Why is throwing that error and how could I make it right? Thanks!
this line in your model is the problem:
before_validation :generate_order
You only have a class method self.generate_order, but this would be looking for a instance method. Judging from the code inside self.generate_order it doesn't seem you want that to be checked before each validation, so you can delete the line (or write an instance method that serves whatever purpose you had in mind).
I'm writing a simple Rails api that connects to a legacy SQL Server database. I am testing my REST actions for my contacts controller. When using FactoryGirl to create test objects, I ran into the error message mentioned in the title. My index and show actions work fine, but the create action is throwing this error. The relevant parts of my contacts_controller look like this:
def create
contact = Contact.new(contact_params)
if contact.save
render json: contact, status: 201, location: [:api, contact]
else
render json: { errors: contact.errors }, status: 422
end
end
...
private
def contact_params
params.require(:contact).permit(:name, :address_1, :city, :zip_code_5, :country)
end
And here is the relevant test code:
describe "POST #create" do
context "when is successfully created" do
before(:each) do
#user = FactoryGirl.create :user
#contact = FactoryGirl.create :contact
post :create, { contact: #contact }
end
it "renders the json representation for the contact record just created" do
contact_response = json_response
expect(contact_response[:name]).to eq #contact_attributes[:name]
end
it { should respond_with 201 }
end
end
The model:
class Contact < ActiveRecord::Base
belongs_to :user
validates :name, :address_1, :city, :zip_code_5, :country, :createddate, presence: true
end
The serializer (using the active_model_serializer gem):
class ContactSerializer < ActiveModel::Serializer
belongs_to :user
attributes :id, :name, :address_1, :city, :zip_code_5, :country
end
Things I've tried include:
Changing the 'belongs_to' to 'has_one' in the serializer (no change)
Removing the 'zip_code_5' from the permite...require line (strangely, I still got the error message about this property, perhaps because of the serializer?)
Removing the serializer (no change)
Any thoughts? I'm happy to provide any more necessary information.
EDIT
The value of #contact when it's passed to the create action:
#<Contact id: 89815, user_id: "d67b0d57-8f7f-4854-95b5-f07105741fa8", title: nil, firstname: nil, lastname: nil, name: "Alene Stark", company: nil, address_1: "72885 Bauch Island", address_2: nil, address_3: nil, city: "Joestad", state: nil, zip_code_5: "98117", zip_code_4: nil, country: "MF", status_id: 1, createddate: "2015-10-23 07:00:00", lastmodifieddate: "2012-11-29 08:00:00", errorreasonid: nil, computergenerated: true, sandbox: true, emailsubject: nil, jobtitle: nil, mergevar1: nil, mergevar2: nil, mergevar3: nil, mergevar4: nil, mergevar5: nil, mergevar6: nil, mergevar7: nil, mergevar8: nil, mergevar9: nil, mergevar10: nil, clientid: 1, isshared: true>
The value of params[:contact] at runtime:
{"city"=>"Seattle", "state"=>"WA", "zip_code_5"=>"98117", "country"=>"US"}
I also have my wrap parameters set to :json format, if that's relevant.
I used the console to recreate what my test was doing. I discovered that Contact was being passed as a string, instead of a hash. After a little Googling, I passed the #contact object as #contact.attributes, which passes a hash of the object. This solved the 'permit' problem, thanks for pointing me in the right direction.
Im learning ruby on rails and have a trouble with aasm callbacks and actionmailer.
I have a hotels model. Heres a code:
class Hotel < ActiveRecord::Base
include AASM
scope :approved_hotels, -> { where(aasm_state: "approved") }
has_many :comments
belongs_to :user, :counter_cache => true
has_many :ratings
belongs_to :address
aasm do
state :pending, initial: true
state :approved
state :rejected
event :approve, :after => :send_email do
transitions from: :pending, to: :approved
end
event :reject, :after => :send_email do
transitions from: :pending, to: :rejected
end
end
def send_email
end
end
As you see user has to get email when state of the hotel he added was changed. Heres what i wrote but its not THE solution cos user gets emails every time admin updates hotel with "pending" state.
class HotelsController < ApplicationController
before_filter :authenticate_user!, except: [:index, :show, :top5hotels]
def update
#hotel = Hotel.find(params[:id])
if #hotel.aasm_state == "pending"
#hotel.aasm_state = params[:state]
UserMailer.changed_state_email(current_user, #hotel.name,
#hotel.aasm_state).deliver
end
if #hotel.update_attributes!(params[:hotel])
redirect_to admin_hotel_path(#hotel), notice: "Hotel was successfully updated."
else
render "edit"
end
end
end
So i think i need to use callback but i dont know how to call
UserMailer.changed_state_email(current_user, #hotel.name,
#hotel.aasm_state).deliver
from the model.
I tried
UserMailer.changed_state_email(User.find(:id), Hotel.find(:name),
Hotel.find(aasm_state)).deliver
but that doesnt work.
Im really out of options and looking for any help.
Thanks!
UPDATE 1:
Thank to Amit Sharma! I`ve made these changes and now getting
NoMethodError: undefined method `email' for nil:NilClass
Looks like user object Im passing to changed_state_email() method is nill but I have no idea why.
Here is my mailer file aswell:
class UserMailer < ActionMailer::Base
default from: "localhost"
# Send email to user when hotels state change
def changed_state_email(user, hotel_name, current_state)
mail(to: user.email, subject: 'State of your hotel '+hotel_name+'has been
changed to '+current_state)
end
end
Here is a result of puts "====#{self.inspect}":
====#<Hotel id: nil, name: "CoolName", breakfast: nil, room_description: nil, price_for_room: 34, star_rating: 3, user_id: nil, address_id: nil, created_at: nil, updated_at: nil, average_rating: nil, photo_file_name: nil, photo_content_type: nil, photo_file_size: nil, photo_updated_at: nil, aasm_state: "approved">
F.====#
F.====#
UPDATE 2:
It returns user object. Output from the console:
1.9.3-p551 :006 > h = Hotel.find(1)
Hotel Load (0.4ms) SELECT "hotels".* FROM "hotels" WHERE "hotels"."id" = ? LIMIT 1 [["id", 1]]
=> #<Hotel id: 1, name: "QWERTYUI", breakfast: nil, room_description: nil, price_for_room: 44, star_rating: 4, user_id: 2, address_id: nil, created_at: "2015-05-30 22:55:01", updated_at: "2015-05-30 22:55:01", average_rating: nil, photo_file_name: nil, photo_content_type: nil, photo_file_size: nil, photo_updated_at: nil, aasm_state: "pending">
1.9.3-p551 :007 > h
=> #<Hotel id: 1, name: "QWERTYUI", breakfast: nil, room_description: nil, price_for_room: 44, star_rating: 4, user_id: 2, address_id: nil, created_at: "2015-05-30 22:55:01", updated_at: "2015-05-30 22:55:01", average_rating: nil, photo_file_name: nil, photo_content_type: nil, photo_file_size: nil, photo_updated_at: nil, aasm_state: "pending">
1.9.3-p551 :008 > h.user
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 2]]
=> #<User id: 2, name: "qwerty", email: "qweqweqweqwe#qwe.com", encrypted_password: "$2a$10$FG5xXb/9wYLcdsCrfJtuDOTsslyY8p.m0qkbP4a5OEvJ...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, admin: false, created_at: "2015-05-30 22:54:14", updated_at: "2015-05-30 22:54:14", comments_count: 0, hotels_count: 1>
You can Try this. I hope this will help you.
In Hotels Controller.
class HotelsController < ApplicationController
before_filter :authenticate_user!, except: [:index, :show, :top5hotels]
def update
#hotel = Hotel.find(params[:id])
if #hotel.pending?
if params[:state] == "approved"
#hotel.approved!
elsif params[:state] == "rejected"
#hotel.rejected!
end
end
if #hotel.update_attributes!(params[:hotel])
redirect_to admin_hotel_path(#hotel), notice: "Hotel was successfully updated."
else
render "edit"
end
end
end
In Hotel model.
def send_email
user = self.user
puts "====#{self.inspect}===#{user.inspect}"
UserMailer.changed_state_email(user, self.name,
self.aasm_state).deliver
end
Please revert back to me if you face any issue.
So in my wiki model I have an attribute for private. If private is true then the wiki should not be viewable to users who are not assign to the wiki_ids via a HABTM relationship.
wiki.rb:
class Wiki
include Mongoid::Document
include Mongoid::Timestamps
has_and_belongs_to_many :users
field :title, type: String
field :body, type: String
field :private, type: Boolean, default: false
scope :visible_to, ->(user) {
user.present? || user.blank? ?
where(:private => false) : where(:private => false).or(:id => user.wiki_ids)
}
def public?
!self.private?
end
end
WikisController:
def index
##wikis = policy_scope(Wiki)
##wikis = Wiki.all
#wikis = Wiki.visible_to(current_user)
authorize #wikis
end
def show
#wiki = Wiki.find(params[:id])
end
def new
#wiki = Wiki.new
authorize #wiki
end
def create
#wiki = current_user.wikis.build(params.require(:wiki).permit(:title, :body, :private, :user))
authorize #wiki
if #wiki.save
flash[:notice] = "Wiki was saved."
redirect_to #wiki
# report success
else
flash[:error] = "There was an error saving your wiki. Please try again."
render :new
end
I'm pretty confident its the scope that needs to be modified in the model, because if i comment out the scope in the model and replace the index in the controler to Wiki.all. I see all the wikis.
As of right now as somebody who created the wiki plus flagged it private and I am logged in I do not see that wiki nor does anybody that I add as a user to the wiki.
I tried adding other conditions to the end such as user.present? ? where(:id => user.wiki_ids) and user.present? && where(:id => user.wiki_ids) but just get errors thrown back at me.
DB entry for User:
User_id: 547eb8867261691268000000, wiki_ids: [BSON::ObjectId('54807226726 1690be0260000'),
BSON::ObjectId('5480735c7261690bae000000'), BSON::ObjectId('548
136e57261690aef000000'), BSON::ObjectId('5489af337261690d95000000'),
BSON::Objec tId('5489b57e7261690d95010000'),
BSON::ObjectId('548f9f607261690bb5060000'), BSO
N::ObjectId('54908f127261690be8000000'),
BSON::ObjectId('54908f207261690be801000 0')], name: "Carey VonRueden",
email: "admin#email.com", encrypted_password: "$2a
$10$NrlQ2XH64UucOPcI1aje9.57eoSO74676264YrIjfGvncyGcpGWy",
reset_password_token : nil, reset_password_sent_at: nil,
remember_created_at: nil, sign_in_count: 7, current_sign_in_at:
2014-12-17 18:51:15 UTC, last_sign_in_at: 2014-12-16 02:38:5 8 UTC,
current_sign_in_ip: "10.0.2.2", last_sign_in_ip: "10.0.2.2",
confirmation
_token: nil, confirmed_at: 2014-12-03 07:15:18 UTC, confirmation_sent_at: nil, u nconfirmed_email: nil, role: "admin">
DB entry for Wiki:
Wiki _id: 54908f207261690be8010000, created_at: 2014-12-16 19:59:28 UTC, updated_at: 2014-12-16 19:59:28 UTC, user_ids:
[BSON::ObjectId('547eb886726169126 8000000')], title: "Private", body:
"Private", private: true>
your scope condition is wrong
user.present? || user.blank? -> this will be true always. if user is present or user is blank, it will always return only the public wikis
Change your scope to something like below.(assuming you want all public wiki's if user is not signed in. If user is signed in, you want public + the wikis created by user)
scope :visible_to, ->(user) {
user.nil? ? where(:private => false) : where(:private => false).or(:id => user.wiki_ids)
}
If you are still not getting what you are expecting, check if user.wiki_ids is returning the right values
My Controller (User Controller)
def reset_password
#user = User.find_by_reset_code(params[:reset_code]) unless params[:reset_code].nil?
if request.post?
if #user && #user.update_attributes(:password => params[:user][:password], :password_confirmation => params[:user][:password_confirmation])
self.current_user = #user
#user.delete_reset_code
flash[:success] = t('helpers.password_reset_successful')
render :template => "sessions/new"
else
flash[:error] = t('helpers.password_reset_error')
redirect_to root_path
end
end
end
I would like test it and I do...
it "POST 'reset password with reset code page'" do
#user.reset_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
User.should_receive(:find_by_reset_code).with(#user.reset_code)
post :reset_password, :user => {"id" => #user.id}
end
But I've exception in Rspec -
Failure/Error: User.should_receive(:find_by_reset_code).with(#user.reset_code)
(<User(id: integer, name: string, email: string, encrypted_password: string, salt: string, created_at: datetime, updated_at: datetime, admin: boolean, reset_code: string) (class)>).find_by_reset_code("acd8a322d9554fbde375f5c39446276188a41678")
expected: 1 time
received: 0 times
What's wrong?
There is no reset_code param in your request.
Replace with:
post :reset_password, :reset_code => #user.reset_code
And you'd rather do:
User.should_receive(:find_by_reset_code).with(#user.reset_code).and_return #user