Undefined method `keys' for nil:NilClass after include Paperclip - ruby-on-rails

Im new in ruby on rails and after I included papperclip i get this error if i try to change photo.
If I change create method like in a README.md on paperclip`s github:
def create
#hotel = Hotel.create( hotel_params )
end
private
# Use strong_parameters for attribute whitelisting
# Be sure to update your create() and update() controller methods.
def hotel_params
params.require(:hotel).permit(:name, :photo, :room_description, :price_for_room, :breakfast, :country, :state, :city, :street)
end
I get the same error in Create method. Could you help me? What am I missing here?
Ruby 1.9.3
Rails 4
Paperclip 4.2.0
Error screenshot
hotels_controller.rb
class HotelsController < ApplicationController
before_action :signed_in_user, except: [:index, :show]
def index
#hotels = Hotel.paginate(:page => params[:page], :per_page => 5)
end
def show
#hotel = Hotel.find(params[:id])
#comments = #hotel.comments
end
def new
#hotel = Hotel.new
end
def edit
#hotel = Hotel.find(params[:id])
end
def create
#hotel = current_user.hotels.new(params[:hotel])
if #hotel.save
redirect_to #hotel, notice: "Hotel was successfully created."
else
render "new"
end
end
def update
#hotel = Hotel.find(params[:id])
if #hotel.update_attributes(params[:hotel])
redirect_to #hotel, notice: "Hotel was successfully updated."
else
render "edit"
end
end
def destroy
#hotel = Hotel.find(params[:id])
#hotel.destroy
redirect_to hotels_url
end
end
hotel.rb
class Hotel < ActiveRecord::Base
has_many :comments
belongs_to :user
has_many :ratings
has_many :raters, :through => :ratings, :source => :users
validates :name, presence: true, length: { minimum: 5 }
validates :room_description, presence: true
validates :price_for_room, presence: true, numericality: true
validates_associated :comments
has_attached_file :photo, :styles => { :medium => "500x500>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :photo, :content_type => /\Aimage\/.*\Z/
def update_average_rating
#value = 0
self.ratings.each do |rating|
#value = #value + rating.value
end
#total = self.ratings.size
update_attributes(average_rating: #value.to_f / #total.to_f)
end
end

As you are using Rails4, your update action should be like this
def update
#hotel = Hotel.find(params[:id])
if #hotel.update_attributes(hotel_params) #Here
redirect_to #hotel, notice: "Hotel was successfully updated."
else
render "edit"
end
end

Related

Unable to make after_create to work

I'm creating a ticket booking app for my sample project using Ruby on Rails 4.1. Three are three models - Events, Tickets and Bookings. Events have many tickets and bookings. Tickets have many bookings and they belong to events. Bookings belongs to events and tickets.
Here's the ticket model:
class Ticket < ActiveRecord::Base
belongs_to :event
has_many :bookings
belongs_to :user
validates :ticket_name, :terms_conditions, presence: true
validates_date :booking_start_date, on: :create, on_or_after: :today
validates_date :booking_end_date, after: :booking_start_date
validates :ticket_price, presence: true, numericality: true
validates :ticket_quantity, :minimum_quantity, :maximum_quantity, presence: true, numericality: { only_integer: true }
before_create :check_start_date
before_update :check_start_date
def check_start_date
if (self.booking_start_date >= DateTime.now) && (self.booking_end_date != DateTime.now)
self.status = 'Open'
else
self.status = 'Closed'
end
end
def maximum_tickets_allowed
(1..maximum_quantity.to_i).to_a
end
end
The bookings model:
class Booking < ActiveRecord::Base
belongs_to :event
belongs_to :ticket
has_many :charges
validates :buyer_name, presence: true
validates :order_quantity, presence: true, numericality: { only_integer: true }
validates :email, presence: true, format: { with: /\A[^#\s]+#([^#.\s]+\.)+[^#.\s]+\z/ }
def total_amount
ticket.ticket_price.to_i * order_quantity.to_i
end
def check_ticket_count
count = ticket.ticket_quantity.to_i - order_quantity.to_i
ticket.update_attribute(:ticket_quantity, count)
end
end
The bookings controller:
class BookingsController < ApplicationController
before_action :authenticate_user!, only: [:index, :destroy]
def index
#event = Event.find(params[:event_id])
##bookings = #event.bookings.all
#bookings = #event.bookings.paginate(page: params[:page], per_page: 10)
end
def new
#event = Event.find(params[:event_id])
#ticket = #event.tickets.find(params[:ticket_id])
#booking = Booking.new
end
def create
#event = Event.find(params[:event_id])
#ticket = #event.tickets.find(params[:ticket_id])
#booking = #event.bookings.create(booking_params)
#booking.ticket = #ticket
Stripe.api_key = Rails.configuration.stripe[:secret_key]
#token = params[:stripeToken]
#amount = #booking.total_amount
begin
customer = Stripe::Customer.create(
:email => #booking.email,
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:currency => "usd",
#:card => token
)
flash[:notice] = "Thanks for the order"
rescue Stripe::CardError => e
flash[:danger] = e.message
end
if #booking.save
BookingMailer.booking_confirmation_user(#booking).deliver
flash[:notice] = "You've successfully booked the tickets!"
redirect_to [#event, #booking]
else
render 'new'
end
end
def show
#event = Event.find(params[:event_id])
#booking = #event.bookings.find(params[:id])
end
def destroy
#event = Event.find(params[:event_id])
#booking = #event.bookings.find(params[:id])
#booking.destroy
redirect_to event_bookings_path
end
private
def booking_params
params.require(:booking).permit(:buyer_name, :email, :mobile, :address, :order_quantity, :ticket_id)
end
end
The check_ticket_count method in Booking.rb works fine as long as I don't an add after_create :check_ticket_count method. The moment I add that after_create method, the app throws the "undefined method `ticket_quantity' for nil:NilClass" error. How to get past this?
Looks like you should first associate ticket with booking, and only then create booking.
#booking = #event.bookings.new(booking_params)
#booking.ticket = #ticket
#booking.save
Hopefully you will ask questions with less code next time.

Multi-select validation always fail

I have few multi-selects in my app that validation always fail (getting form errors: genres can't be blank, languages can't be blank) during every form submit (even if the multi-select options were selected). Code:
models/dvd.rb
class Dvd < ActiveRecord::Base
has_and_belongs_to_many :genres
has_and_belongs_to_many :languages
has_many :rentals, dependent: :destroy
has_many :users, through: :rentals
validates :title, presence: true
validates :year, inclusion: {in: 1900..Time.now.year.to_i}, :presence => {:message => 'Year must be from 1900 till current year.'}
validates :length, inclusion: {in: 1..999}, :presence => {:message => 'DVD length must be in minutes in range 1..999.'}
validates :genres, presence: true
validates :languages, presence: true
end
models/language.rb
class Language < ActiveRecord::Base
has_and_belongs_to_many :dvds
validates :title, presence: true, uniqueness: { case_sensitive: false }
end
models/genre.rb
class Genre < ActiveRecord::Base
has_and_belongs_to_many :dvds
validates :title, presence: true, uniqueness: { case_sensitive: false }
end
dvds_controller.rb
class DvdsController < ApplicationController
before_action :set_dvd, only: [:show, :edit, :update, :destroy]
before_action :set_genres, :set_languages, only: [:new, :edit]
before_action :delete_genres, :delete_languages, only: [:update]
after_action :add_genres, :add_languages, only: [:create, :update]
# GET /dvds
# GET /dvds.json
def index
#dvds = Dvd.all
end
# GET /dvds/1
# GET /dvds/1.json
def show
end
# GET /dvds/new
def new
#dvd = Dvd.new
end
# GET /dvds/1/edit
def edit
end
# POST /dvds
# POST /dvds.json
def create
#dvd = Dvd.new(dvd_params)
respond_to do |format|
if #dvd.save
format.html { redirect_to #dvd, notice: 'Dvd was successfully created.' }
format.json { render :show, status: :created, location: #dvd }
else
format.html { render :new }
format.json { render json: #dvd.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /dvds/1
# PATCH/PUT /dvds/1.json
def update
respond_to do |format|
if #dvd.update(dvd_params)
format.html { redirect_to #dvd, notice: 'Dvd was successfully updated.' }
format.json { render :show, status: :ok, location: #dvd }
else
format.html { render :edit }
format.json { render json: #dvd.errors, status: :unprocessable_entity }
end
end
end
# DELETE /dvds/1
# DELETE /dvds/1.json
def destroy
#dvd.destroy
respond_to do |format|
format.html { redirect_to dvds_url, notice: 'Dvd was successfully deleted.' }
format.json { head :no_content }
end
end
private
def set_dvd
if params[:id]
#dvd = Dvd.find(params[:id])
else
#dvd = Dvd.find(params[:dvd][:id])
end
end
def dvd_params
params.require(:dvd).permit(:title, :description, :year, :genres, :languages, :length)
end
def add_languages
params[:dvd][:languages].each do |l|
if !l.empty?
#dvd.languages << Language.find(l)
end
end
end
def add_genres
params[:dvd][:genres].each do |g|
if !g.empty?
#dvd.genres << Genre.find(g)
end
end
end
def set_genres
#genres = Genre.all
end
def set_languages
#languages = Language.all
end
def delete_genres
# Delete all dvd genre relations
#dvd.genres.delete_all
end
def delete_languages
# Delete all dvd language relations
#dvd.languages.delete_all
end
end
routes.rb
Rails.application.routes.draw do
resources :dvds do
resources :rentals
end
resources :rentals
resources :languages
resources :genres
resources :dvds
resources :users, :path => 'clients'
root to: "index#index"
end
form
<div class="field">
<%= f.label :genres %><br>
<%= f.collection_select(:genres, Genre.all, :id, :title, {:selected => #dvd.genres.map {|dl| dl.id}, :include_blank => false}, {:multiple => true}) %>
</div>
<div class="field">
<%= f.label :languages %><br>
<%= f.select :languages, options_for_select(Language.all.map {|l| [l.title,l.id]}, #dvd.languages.map {|dl| dl.id}), {:include_blank=> false}, {:multiple => true} %>
</div>
Any ideas what is wrong with that?
The field names for the form should be genre_ids and language_ids, not genres and languages.

User returning nil. undefined method `username' for nil:NilClass

Here is the error I receive:
Here is a Gist of the files (some of you may find this easier to read):
https://gist.github.com/drichards2013/7902811
Here is index.html.erb:
<%= render 'pages/home' if !user_signed_in? %>
<div id="things" class="transitions-enabled">
<% #things.each do |thing| %>
<div class='panel panel default'>
<div class="box">
<%= link_to image_tag(thing.image.url(:medium)), thing %>
<div class='panel-body'>
<% if thing.link.blank? %>
<strong><%= thing.title %></strong>
<% else %>
<strong><%= link_to thing.title, "http://#{thing.link}"%></strong>
<% end %>
<p><%= thing.description %></p>
By <%= link_to thing.user.username, user_path(thing.user) %>
<% if thing.user == current_user %>
<%= link_to edit_thing_path(thing) do %>
<span class='glyphicon glyphicon-edit'></span>
<% end %>
<%= link_to thing_path(thing), method: :delete, data: { confirm: 'Are you sure?' } do %>
<span class='glyphicon glyphicon-trash'></span>
<% end %>
</div>
<% end %>
</div>
</div>
<% end %>
</div>
<%= will_paginate #posts, renderer: BootstrapPagination::Rails, class: 'pull-left' %>
Here is thing.rb:
class Thing < ActiveRecord::Base
belongs_to :user
default_scope -> { order('created_at DESC') }
has_attached_file :image, :styles => { :large => '500x500>', :medium => '300x300>', :thumb => '100x100>' }
validates :image, presence: true
validates :title, presence: true, length: { minimum: 5, maximum: 50 }
# Returns microposts from the users being followed by the given user.
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
end
end
class ThingsController < ApplicationController
before_action :set_thing, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /things
# GET /things.json
def index
#things = Thing.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 50)
end
# GET /things/1
# GET /things/1.json
def show
end
# GET /things/new
def new
#thing = current_user.things.build
end
# GET /things/1/edit
def edit
end
# POST /things
# POST /things.json
def create
#thing = current_user.things.build(thing_params)
respond_to do |format|
if #thing.save
format.html { redirect_to #thing, notice: 'Thing was successfully created.' }
format.json { render action: 'show', status: :created, location: #thing }
else
format.html { render action: 'new' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /things/1
# PATCH/PUT /things/1.json
def update
respond_to do |format|
if #thing.update(thing_params)
format.html { redirect_to #thing, notice: 'Thing was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# DELETE /things/1
# DELETE /things/1.json
def destroy
#thing.destroy
respond_to do |format|
format.html { redirect_to things_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_thing
#thing = Thing.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link)
end
end
Here is things_controller.rb:
class ThingsController < ApplicationController
before_action :set_thing, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /things
# GET /things.json
def index
#things = Thing.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 50)
end
# GET /things/1
# GET /things/1.json
def show
end
# GET /things/new
def new
#thing = current_user.things.build
end
# GET /things/1/edit
def edit
end
# POST /things
# POST /things.json
def create
#thing = current_user.things.build(thing_params)
respond_to do |format|
if #thing.save
format.html { redirect_to #thing, notice: 'Thing was successfully created.' }
format.json { render action: 'show', status: :created, location: #thing }
else
format.html { render action: 'new' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /things/1
# PATCH/PUT /things/1.json
def update
respond_to do |format|
if #thing.update(thing_params)
format.html { redirect_to #thing, notice: 'Thing was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# DELETE /things/1
# DELETE /things/1.json
def destroy
#thing.destroy
respond_to do |format|
format.html { redirect_to things_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_thing
#thing = Thing.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link)
end
end
Here is 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
has_many :things
validates :name, presence: true, length: { minimum: 2, maximum: 20}
validates :username, presence: true, length: { minimum: 2, maximum: 20}
validates :username, uniqueness: true
validates :email, presence: true
validates :email, uniqueness: true
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" , :nav => "25x25"}
extend FriendlyId
friendly_id :username
def show
end
#follow features
has_many :followed_users, through: :relationships, source: :followed
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
def following?(other_user)
relationships.find_by(followed_id: other_user.id)
end
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end
def unfollow!(other_user)
relationships.find_by(followed_id: other_user.id).destroy!
end
def feed
Thing.from_users_followed_by(self)
end
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
end
Here is users_controller.rb:
class UsersController < ApplicationController
def show
#user = User.find_by_username(params[:id])
end
def user_params
params.require(:user).permit(:avatar)
end
def following
#title = "Following"
#user = User.find_by_username(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find_by_username(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
end
What I have tried
I went to the console, did thing = Thing.last, then tried to call thing.user, and that returned nil. So it appears the user_id isn't saving. How can I make that happen?
This is where the "try" method is your friend.
You're trying to get the "username" value from your user, which is missing.
If you were to write your line like:
thing.user.try(:username)
Then it would not crash, and it would fetch the username if user existed.
Your user is not saving because your one-to-many relationship is not set up. You might want to add a user_id to your things table.
Fixed by adding :user_id to the thing_params method in my things controller.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link, :user_id)
end

creating an object with has_many association results in item can not be blank

I have following associations and the related controller, in my form I am adding every field as it should be. But I still get an error Ratings item can't be blank when I try to create an Item. I am using Rails 4.0 . I did searched extensively for this but could not still find what I am doing wrong. Thankyou!
class Item < ActiveRecord::Base
has_many :ratings, dependent: :destroy
accepts_nested_attributes_for :ratings, :allow_destroy => true
validates :name , :length => { minimum: 3 }
validates :category , :length => { minimum: 3 }
end
class Ratings < ActiveRecord::Base
belongs_to :user
belongs_to :item
default_scope -> { order('created_at DESC') }
validates :user_id, :presence => true
validates :item_id, :presence => true
validates_numericality_of :rating, :greater_than_or_equal_to => 0
validates_numericality_of :rating, :less_than_or_equal_to => 5
end
class ItemsController < ApplicationController
before_action :set_item, only: [:show]
before_action :user_signed_in?, only: :create
def create
#item = Item.new
#rating = #item.ratings.build
#rating.comment = params[:item][:ratings_attributes][:comment]
#rating.rating = params[:item][:ratings_attributes][:rating]
#rating.user_id = current_user.id
#item.name = params[:item][:name]
#item.url = params[:item][:url]
#item.full_address = params[:item][:full_address]
#item.city = params[:item][:city]
#item.country = params[:item][:country]
#item.category = params[:item][:category]
respond_to do |format|
if #item.save
#TODO create rating here (First rating of an Item)
flash[:success] = "Welcome to inmyopnion"
format.html { redirect_to #item, notice: 'Item was successfully created.' }
format.json { render action: 'show', status: :created, location: #item }
else
format.html { render action: 'new' }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
def new
#item = Item.new
end
def show
end
def destroy
end
private
def set_item
#item = Item.find(params[:id])
end
def item_params
params.require(:item).permit(:name, :url, :full_address, :city, :country, :category, :ratings_attributes => [:rating, :comment])
end
def user_signed_in?
#TODO: should display should sign in to rate an item
redirect_to(root_url) unless signed_in?
end
end
Simplify your controller! Since you are allowing nested_attributes this should be sufficient:
#item = Item.create(params[:item])
The problem might be caused by #rating object not being saved.
I got it working by commenting the below given line in
class Ratings < ActiveRecord::Base
validates :item_id, :presence => true
but my association rspec test fails and saves a Ratings without an item_id.
Rest of the code is similar to what I posted as
#item = Item.create(params[:item])
gives ActiveModel::ForbiddenAttributesError
Alright much playing with the code and docs of nested_attributes finally a working program that validates association too. These are the changes (marked in between ** .... **) listed below
class Item < ActiveRecord::Base
has_many :ratings, dependent: :destroy, **inverse_of: :item**
accepts_nested_attributes_for :ratings, :allow_destroy => true
validates :name , :length => { minimum: 3 }
validates :category , :length => { minimum: 3 }
end
class Ratings < ActiveRecord::Base
belongs_to :user
belongs_to :item, **inverse_of: :ratings**
default_scope -> { order('created_at DESC') }
validates :user_id, :presence => true
validates_presence_of :item
validates_numericality_of :rating, :greater_than_or_equal_to => 0
validates_numericality_of :rating, :less_than_or_equal_to => 5
end
Still not able to create one from #item = Item.create(params[:item]) which still gives an gives
ActiveModel::ForbiddenAttributesError as suggested by #BroiSatse and also the docs of nested_attributes that should not be the case
the problem might be in
class ItemsController < ApplicationController
def item_params
params.require(:item).permit(:name, :url, :full_address, :city, :country, :category, :ratings_attributes => [:rating, :comment])
end
will work on to resolve that too and post an answer if I find a solution.

Cannot favorite users using polymorphic association

Using the system below users are able to favorite styles in the format of:
=> #<Favorite id: 59, user_id: 2, favoritable_id: 15, favoritable_type: "Style", created_at: "2013-04-06 08:47:08", updated_at: "2013-04-06 08:47:08">
I have built the system to enable users to favorite users as well, but for some reason the line Favorite.create!(:user => user) is not creating the expected favorite model object when called on User, as above when called on Style. Instead I am getting nils as below:
=> #<Favorite id: 65, user_id: 2, favoritable_id: nil, favoritable_type: nil, created_at: "2013-04-06 09:35:07", updated_at: "2013-04-06 09:35:07">
Problem is above. Details are below
Favorite Class:
class Favorite < ActiveRecord::Base
belongs_to :favoritable, :polymorphic => true
belongs_to :user
attr_accessible :user
validates_presence_of :user
validates_uniqueness_of :user_id, :scope => [:favoritable_type, :favoritable_id]
end
Model definitions including favoritable module (see below):
class User < ActiveRecord::Base
include Favoritable
class Style < ActiveRecord::Base
include Favoritable
Favoritable Module:
module Favoritable
extend ActiveSupport::Concern
included do
attr_accessible :favorites
has_many :favorites, :as => :favoritable, :autosave => true
accepts_nested_attributes_for :favorites
def add_favorite(user)
self.favorites << Favorite.create!(:user => user)
save
end
def delete_favorite(user)
self.favorites.where(:user_id => user.id).each do |favorite|
favorite.destroy
end
end
end
end
Favorites Controller:
class FavoritesController < ApplicationController
def fav_user
#user = User.find(params[:id])
#user.add_favorite(current_user)
respond_to do |format|
format.js { render :action => "update_favorite_disp", :layout => false }
end
end
def delete_fav_user
#user = User.find(params[:id])
respond_to do |format|
if #user.delete_favorite(current_user)
format.js { render :action => "update_favorite_disp", :layout => false }
end
end
end
def fav_style
#style = Style.find(params[:id])
#style.add_favorite(current_user)
respond_to do |format|
format.js { render :action => "update_favorite", :layout => false }
end
end
def delete_fav_style
#style = Style.find(params[:id])
respond_to do |format|
if #style.delete_favorite(current_user)
format.js { render :action => "update_favorite", :layout => false }
end
end
end
end
You need to pass in the :favoritable model in add_favorite:
def add_favorite(user)
Favorite.create!(:user =>user, :favoritable => self)
save
delete_favorite also needs to delete based on the :favoritable_id, instead of :user_id:
def delete_favorite(user)
user.favorites.where(:favoritable_id => self.id).each do |favorite|
favorite.destroy
end
end

Resources