(NoMethodError) Undefined private method in model - Rails - ruby-on-rails

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).

Related

Callbacks in aasm gem and ActionMailer

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.

Ruby error NoMethodError for nil:NilClass

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

Grouping by attribute of associated objects

class Event < ActiveRecord::Base
has_many :meetings
end
class Meeting < ActiveRecord::Base
belongs_to :event
end
How to write mysql query to search all events group_by meeting DATE(start_at)?
Event.inludes(:meetings).group ...
As a result I want to get a Hash:
{"2014-01-24"=>[#<Event id: , title: "First", created_at: "2014-01-24 16:02:52", updated_at: "2014-01-24 16:02:52">, #<Event id: 2, title: "Second", created_at: "2014-01-24 16:02:52", updated_at: "2014-01-24 16:02:52">], "2013-01-29"=>[#<Event id: 3, title: "Third", created_at: "2013-01-29 05:30:40", updated_at: "2014-01-29 05:30:40">], ...]}
P.S: I am using PostgreSQL
Now I get it by this way:
hash = {}
Meeting.where("extract(month from start_at) = ?", Date.today.month).pluck('DATE(start_at)').uniq.each do |date|
hash[date] = Event.includes(:meetings).where("DATE(meetings.start_at) = ?", date).references(:meetings)
end
But it produced so many queries to the database :(
Event.joins(:meetings).group('meetings.start_at') should do. But want you want is a group_by array method http://apidock.com/ruby/Enumerable/group_by so what you should do is
#events.group_by {|e| e.meeting.start_date}
In case of many to many you should be better off with
result = Hash.new
Meeting.include(:events).each {|m| result[m.start_at]||=[]; result[m.start_at] << m.events}
and with one liner you could
Meeting.includes(:events).inject(Hash.new) do |result, m|
result[m.start_at]||=[]
result[m.start_at] << w.events
result
end
This code should execute two database calls i think

Creating an array of values in FactoryGirl, each of which is unique

I have this factory defined:
factory :post, :parent => :post_without_countries, class: Post do |p|
p.country_ids {|country_ids| [country_ids.association(:country), country_ids.association(:country)]}
end
And I'm wanting it to output two unique countries. Instead it just inserts the same country as the association twice:
#<Post id: nil, title: "Atque id dolorum consequatur.", body: "Praesentium saepe ullam magnam. Voluptatum tempora ...", created_at: nil, updated_at: nil, user_id: 1>
[#<Country id: 1, name: "Dominican Republic", isocode: "lyb", created_at: "2012-10-20 13:52:18", updated_at: "2012-10-20 13:52:18">, #<Country id: 1, name: "Dominican Republic", isocode: "lyb", created_at: "2012-10-20 13:52:18", updated_at: "2012-10-20 13:52:18">]
Any ideas?
Better use the build_list or create_list methods:
post.countries = create_list(:country, 2)
Instead of doing:
2.times { post.countries << FactoryGirl.create(:country) }
in RSpec, you can make an after_create hook like this:
after_create do |post|
2.times { post.countries << FactoryGirl.create(:country) }
end
If you need to customize the number of times you want to create a country, you can make a transient attribute:
#in the post factory definition
ignore do
num_countries 0 #default to zero
end
#different after_create
after_create do |post, proxy|
proxy.num_countries.times { post.countries << FactoryGirl.create(:country) }
end
It looks like factory girl might not be iterating properly. The two questions that pop into my mind are.
Are you using FactoryGirl.build when you meant to use FactoryGirl.create?
Have you tried replacing p.country_ids with p.sequence(:country_ids)
I hope that those point you in the right direction. If not, perhaps more information?
OK, I fixed this by taking my creation of the many Countries relationship out the factory and just creating it in RSpec instead:
post = FactoryGirl.build(:post)
2.times { post.countries << FactoryGirl.create(:country) }

Ruby on Rails: related models functional test error

I have been trying to figure out what is wrong with my test.
First, it works in the console.
The console:
1.9.2-p290 :015 > a = Allergy.all
Allergy Load (0.4ms) SELECT "allergies".* FROM "allergies"
=> [#<Allergy id: 1, name: "Milk", desc: "Allergic to milk", created_at: "2012-01-08 16:38:55", updated_at: "2012-01-09 11:48:20", patient_id: 1>, #<Allergy id: 2, name: "Blah", desc: "Test", created_at: "2012-01-09 12:20:48", updated_at: "2012-01-09 12:20:48", patient_id: 2>]
1.9.2-p290 :016 > a[0]
=> #<Allergy id: 1, name: "Milk", desc: "Allergic to milk", created_at: "2012-01-08 16:38:55", updated_at: "2012-01-09 11:48:20", patient_id: 1>
1.9.2-p290 :017 > a[0].patient.full_name
Patient Load (0.5ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = 1 LIMIT 1
=> "Test Full Name"
1.9.2-p290 :018 >
Allergy Controller
class AllergiesController < ApplicationController
# GET /allergies
# GET /allergies.json
def index
#allergies = Allergy.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #allergies }
end
end
# GET /allergies/1
# GET /allergies/1.json
def show
#allergy = Allergy.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #allergy }
end
end
...
Allergy Model
class Allergy < ActiveRecord::Base
validates :name, :presence => true
belongs_to :patient
end
Patient Model
class Patient < ActiveRecord::Base
validates :first_name, :last_name, :dob, :presence => true
has_many :allergies
def age
now = Time.now.utc.to_date
now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
end
def full_name
first_name
end
end
The test:
require 'test_helper'
class AllergiesControllerTest < ActionController::TestCase
setup do
#allergy = allergies(:one)
#allergy.patient = patients(:one)
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:allergies)
end
...
The fixtures:
# For Allergy
one:
name: MyString
desc: MyText
patient_id: 1
two:
name: MyString
desc: MyText
patient_id: 1
#For Patient
one:
first_name: Jose
last_name: Rizal
middle_name: H
dob: 2009-10-29
two:
first_name: MyString
last_name: MyString
middle_name: MyString
dob: 1982-02-11
The error:
AllergiesControllerTest:
PASS should create allergy (0.22s)
PASS should destroy allergy (0.01s)
PASS should get edit (0.13s)
ERROR should get index (0.14s)
ActionView::Template::Error: undefined method `full_name' for nil:NilClass
/Users/wenbert/.rvm/gems/ruby-1.9.2-p290/gems/activesupport-3.1.3/lib/active_support/whiny_nil.rb:48:in `method_missing'
PASS should get new (0.02s)
PASS should show allergy (0.02s)
PASS should update allergy (0.02s)
I have checked the logs and no Patient is saved when I run the test. I just get:
--- !ruby/object:Allergy
attributes:
id: 298486374
name: MyString
desc: MyText
created_at: 2012-01-09 14:28:21.000000000Z
updated_at: 2012-01-09 14:28:21.000000000Z
patient_id: 1
Patient Load (0.2ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = 1 LIMIT 1
--- !!null
...
So, what gives?
Any replies will be greatly appreciated. I have been stuck at this for almost 4 hours now. :(
Could it be my fixtures? My Models?
Best Regards,
W
FYI: I am using Ruby on Rails 3.1
Your fixtures have the same names, name them as following
allergy_one:
name: ...
patient: patient_one
allergy_two:
name: ...
patient_one:
name: ..
You will notice that I do not use patient_id in the fixtures. You need to reference the patient fixture you want as fixtures use random ids and do not start at 1.

Resources