Destroy method: Passing params for API gathered data - ruby-on-rails

I'm working on an app where users can search for games (data pulled from an API), and add them to a library. I managed to get the adding part working, but I'm having some issues with deleting a game from the user's library.
Here are my create and destroy functions.
def create
#library_game = Game.new
#library_game.game_id = params[:game_id]
#library_game.fetch_data
#library_game.save!
current_user.build_library
current_user.library.games << #library_game
redirect_to library_path
end
def destroy
current_user.games.destroy(game_id: params[:id])
redirect_to library_path
end
With the current code, when I try to delete something I get the following error:
ActiveRecord::AssociationTypeMismatch in GamesController#destroy
Game(#70255379902700) expected, got {:game=>"52921"} which is an instance of Hash(#70255376633020)
So it seems to be getting the id but it is expecting the entire game? I have tried editing my params to include just :game or :game_id but I am still getting errors.
Here is the link_to to delete the game.
<%= link_to 'Remove from Library', user_game_path(game.id), method: :delete %>
Here are my models showing associations.
Game
class Game < ApplicationRecord
has_many :library_games
has_many :libraries, through: :library_games
has_many :users, through: :libraries
serialize :data
attr_accessor :game_id
def fetch_data
game = GiantBomb::Game.detail(game_id)
self.data = Hash[game.instance_variables.map { |var| [var.to_s[1..-1], game.instance_variable_get(var)] } ]
end
def to_giant_bomb_game
GiantBomb::Game.new(data)
end
end
Library
class Library < ApplicationRecord
belongs_to :user
has_many :library_games
has_many :games, through: :library_games
end
User
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_one :library
has_many :games, through: :library
def has_game?(game)
games.where(id: game.id).exist?
end
def build_library
return if library.present?
self.library = Library.new
end
end
Library_Game
class LibraryGame < ApplicationRecord
belongs_to :library
belongs_to :game
has_one :user, through: :library
end
What am I getting wrong with my destroy method?

Here's my understanding of your domain model,
Users (1 to 1)-> Library (1 to n)-> Library Games (1 to 1)-> Games
If this understanding is correct, then I see a problem with your create and destroy methods. You should be creating and destroying instances of LibraryGames, and not Games. Below is a rough idea of how you can implement things,
def create
# current_user.build_library # Wouldn't all your users already have libraries?
#library_game = LibraryGame.new
#library_game.game_id = params[:game_id]
#library_game.library = current_user.library
# #library_game.fetch_data # What does this do? This might not be needed here
#library_game.save!
redirect_to library_path
end
def destroy
current_user.library_games.destroy(params[:id])
redirect_to library_path
end

Related

Couldn't find Suscription without an ID

I have an application with users using devise for authentication, in the user model I have added in the database a column called admin with false value by default. that way I have managed to have access as administrator to certain parts of the application.
I have a subscription model and each user when authenticated gets a free value by default. what I want to achieve is that the admin user in your user list can be able to switch from free to premium. this is the code i have and i can't get it to work.
Users Model:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
#Validaciones
validates :nombre, :apellido, presence: true
devise :database_authenticatable, :validatable, password_length: 8..128
#Relaciones
has_many :patients, dependent: :destroy
has_many :articles, dependent: :destroy
has_one :profile, dependent: :destroy
has_one :suscription, dependent: :destroy
#Creación de perfil
after_create :set_profile
def set_profile
self.profile = Profile.create()
end
#Creación de suscripcion
after_create :set_suscription
def set_suscription
self.suscription = Suscription.create()
end
end
Suscription Model:
class Suscription < ApplicationRecord
belongs_to :user
enum status: {
free: 0,
premium: 1
}
end
Users controllers:
class UsersController < ApplicationController
def index
#pagy, #users = pagy(User.order(created_at: :asc), items:12)
end
def show
#user = User.find(params[:id])
end
end
Suscriptios controller:
class SuscriptionsController < ApplicationController
before_action :set_suscription
def show
end
def edit
end
def update
#suscription = Suscription.find(params[:id]).update_params
redirect_to profile_path
flash[:notice] = "La suscripción ha sido actualizada"
end
private
def set_suscription
#suscription = (current_user.suscription ||= Suscription.create)
end
def suscription_params
params.require(:suscription).permit(:status)
end
end
Route:
#UPDATE PREMIUM
patch "suscriptions", to:"suscriptions#update", as: "user_premium"
View (Link):
<%= link_to 'Update', user_premium_path ,method: :patch %>
This should fix it:
subscriptions_controller.rb
def update
#suscription = Suscription.find(params[:id]).update(subscription_params)
redirect_to profile_path
flash[:notice] = "La suscripción ha sido actualizada"
end
view
<%= link_to 'Update', user_premium_path(id: #subscription.id, status: "premium"), method: :patch %>
One other thing that is not needed, but normally I would see something like this in a controller:
private
def set_suscription
#suscription = Suscription.find(params[:id])
end
which then makes your update method look like this:
def update
#subscription.update(subscription_params)
redirect_to profile_path
flash[:notice] = "La suscripción ha sido actualizada"
end
This is all assuming you are simply trying to update the subscription from free to premium with your link_to. I wouldn't recommend doing anything like this, because what if someone accidentally marks this? They can no longer go back to a free subscription. Maybe have a modal open that is routed to subscription edit with a drop down to select the status would be better?

Rails error : undefined method `product' for #<User:0x00007faaa8c42700> Did you mean? products products=

I am trying to develop a Shopping application which has 3 models namely User(Devise), Product and Batch. I've made an has_many association between User and Product and created a User(signed up in Devise). And then I changed the association into has_and_belongs_to_many and created a migration to create the join table. I've followed this answer https://stackoverflow.com/a/57017241/9110386 to add the Product to current_user. Then I deleted my User account and tried to sign up but it shows an error like this.
NoMethodError in Devise::RegistrationsController#create
undefined method `product' for # Did you mean? products products=
User model:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
has_and_belongs_to_many :products, :dependent => :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
validates_length_of :product, maximum: 10
end
Product model:
class Product < ApplicationRecord
belongs_to :batch
has_and_belongs_to_many :user
validates :name, presence: true
validates_associated :user
end
Product Controller
class ProductsController < ApplicationController
before_action :authenticate_user!
def index
#products = Product.all
end
def show
#product = Product.find(params[:id])
end
def new
#product = Product.new
end
def edit
end
def create
end
def update
end
def destroy
end
def add_cart
product = Product.find(params[:product_id])
#current_user.products << product
#current_user.products << product unless current_user.products.include?(product)
if current_user.products.include?(product)
redirect_to products_path, notice: "Already in your cart"
else
current_user.products << product
redirect_to products_path, notice: "Added to cart"
end
end
end
What am I doing wrong here. And I also want to remove the Product from the cart by destroying it from the current_user. How to do that?
Thanks in advance.
You have left behind an old validation in your user model.
Delete this line in the app/models/user.rb file
validates_length_of :product, maximum: 10
Your error is flagging the Devise RegistrationsController’s create method. You’ve likely left a reference to user.product in there whereas users have products plural.

How to give an instance variable access to an 'id' in rails?

I have a "userinfos_controller" where user stores their information. They also can upload a video to their profile. I am showing this video under the show method in the userinfos_controller as shown below. I want the '#myvideo" to get the video associated with the user. When a user deletes their video and re-upload, the video_id changes. So I can't call that video using the video_id. Is there a way to call Video.userinfo.last? I don't want to call Video.last, because it'll give me the last video uploaded across all users, not just the last video uploaded by that particular user. You can also see in my rails console, the video has a user_id, userinfo_id and a video_id. So right now, under my show method, when I do:
def show
#myvideo = Video.find(params[:userinfo_id])
end
It looks for Video_id that is equal to userinfo_id, while I want it to look for the last video uploaded by that particular user/userinfo.
My id relationships(please right click and open in new tab to see more clearly):
My userinfos controller:
class UserinfosController < ApplicationController
before_action :find_userinfo, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#userinfors = Userinfo.all
#myvideo = Video.all
end
def show
#myvideo = Video.find(params[:userinfo_id])
end
def new
#userinformation = current_user.userinfos.build
end
def create
#userinformation = current_user.userinfos.build(userinfo_params)
if #userinformation.save
redirect_to root_path
else
render 'new'
end
end
def edit
end
def update
end
def destroy
#userinformation.destroy
redirect_to userinfo_path
end
private
def userinfo_params
params.require(:userinfo).permit(:name, :email, :college, :gpa, :major)
end
def find_userinfo
#userinformation = Userinfo.find(params[:id])
end
end
Show video view:
<div>
<%= video_tag #myvideo.introvideo_url.to_s, :size => "480x320", :controls =>true %>
</div>
Video model:
class Video < ActiveRecord::Base
belongs_to :userinfo
belongs_to :user
mount_uploader :introvideo, VideoUploader
end
Userinfo model:
class Userinfo < ActiveRecord::Base
belongs_to :user
has_many :videos
end
User model:
class User < ActiveRecord::Base
has_many :vidoes
has_many :userinfos
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
My migrations that show all the id's added:
My routes:
Either create a direct association between User and Video - or make it indirect through a join model. Don't do both.
Direct 1-n:
class User < ApplicationRecord
has_many :videos
has_many :user_infos
# ...
end
class Video < ApplicationRecord
belongs_to :user
# ...
end
class UserInfo < ApplicationRecord
belongs_to :user
has_many :videos, through: :user
end
Indirect 1-n:
class User < ApplicationRecord
has_many :user_infos
has_many :videos, through: :user_infos
# ...
end
class UserInfo < ApplicationRecord
belongs_to :user
has_many :videos
end
class Video < ApplicationRecord
belongs_to :user_info
has_one :user, through: :user_info
# ...
end
Both will let you do:
#user.videos.last
#user_info.videos.last
def show
#my_video = #user_info.videos.order(created_at: :desc).last
end
Try with ActiveRecord#last:
def show
#myvideo = Video.where('userinfo_id = ?', params[:userinfo_id]).last
end
That will give you the videos uploaded to userinfo with id equal to params[:userinfo_id] taking the last record.
Not sure if I understood you correctly, but I think you want to access the Video that your user previously uploaded, after it uploaded the new one.
Assuming you have a has_many :videos relationship set up on the User model, you can do this:
#user.videos.order(created_at: :desc).second
Or if you don't have the user instance and just have user_id.
Video.where(userinfo_id: params[:userinfo_id]).order(created_at: :desc).second
Hope this helps.
EDIT:
Or maybe you just want to access the latest users video. Again, I don't know how you set up your relations. I am assuming user can have many videos.
Video.where(userinfo_id: params[:userinfo_id]).order(created_at: :desc).first
Or shorter
Video.where(userinfo_id: params[:userinfo_id]).last

How to add existing songs to a music library Rails 4

What I'm trying to do is add songs that artists have already uploaded to a user library (I have already set up my app so that artists can upload songs). Also, I have set up my code so that an empty user library is created after a user signs up (using the after_create Active Record Callback).
To be more clear, I would like for the user to be able to add songs they see within the site to their library.
However, this is escaping me. I am familiar with CRUD, and have an idea how I would create a library and add existing songs to it, but I am not quite sure how I could add a song to a user library by clicking a button/link saying "Add Song To Library" which would be next to a song, and having it add to the user's existing empty library.
My existing code is below.
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
belongs_to :meta, polymorphic: true
before_create :create_empty_profile
after_create :create_empty_library #may not be the best way to do it ¯\_(ツ)_/¯
acts_as_messageable
has_many :playlists
has_many :user_friendships, dependent: :destroy
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
has_many :chat_rooms, dependent: :destroy
has_many :chat_messages, dependent: :destroy
has_many :votes, dependent: :destroy
mount_uploader :profile_pic, ProfilePicUploader
def mailboxer_name
self.name
end
def mailboxer_email(object)
self.email
end
def admin?
role == 'admin'
end
def moderator?
role == 'moderator'
end
def create_empty_profile
if is_artist?
profile = ArtistProfile.new
else
profile = UserProfile.new
end
profile.save(validate: false)
self.meta_id = profile.id
self.meta_type = profile.class.name
end
def create_empty_library
library = Library.new
library.user_id = self.id
library.save(validate: false)
end
end
Library.rb:
class Library < ActiveRecord::Base
belongs_to :user
has_many :library_songs
has_many :songs, through: :library_songs
has_many :library_albums
has_many :albums, through: :library_albums
end
library_song.rb
class LibrarySong < ActiveRecord::Base
belongs_to :library
belongs_to :song
end
library_album.rb
class LibraryAlbum < ActiveRecord::Base
belongs_to :library
belongs_to :album
end
libraries_controller.rb
class LibrariesController < ApplicationController
def index
#libraries = Library.all
end
def show
#library = Library.find(params[:id])
end
end
I was able to create playlists and add songs to them using the form/controller below.
playlists/new.html.erb:
<h1>New Playlist</h1>
<%= form_for(#playlist) do |f| %>
<%= f.text_field :name %>
<% Song.all.each do |song| -%>
<div>
<%= check_box_tag :song_ids, song.id, false, :name => 'playlist[song_ids][]', id: "song-#{song.id}" %>
<%= song.name %>
</div>
<% end %>
<%= f.submit %>
<% end %>
playlists_controller.rb:
class PlaylistsController < ApplicationController
def index
#playlists = Playlist.all
end
def show
#playlist = Playlist.find(params[:id])
end
def new
#playlist = Playlist.new
end
def create
#playlist = Playlist.create(playlist_params)
redirect_to #playlist
end
private
def playlist_params
params.require(:playlist).permit(:name, song_ids: [])
end
end
However, the main issue is that in the form above, the playlist is being created along with the existing songs. In this case, I would need to add existing songs to an existing library that is empty.
Any ideas, guys? This would be very helpful. I would be happy to upload any code needed.
It looks to me like you don't actually have has_many :libraries set in your user model. Judging by your Library model, I think this was what you had intended. You should really just create the 'new' models before you save the User model. You could use something similar to this and do it all in one action.
class User < ActiveRecord::Base
def self.build_full_user(params, songs)
# Assign all normal attributes here
new_user = User.new
new_user.name = params[:name]
# If you want to assign new songs, just make a new Library model and associate them.
new_library = Library.new
# Build the song models if you haven't found/created or passed them in already.
new_songs = Songs.build_songs_from_list(songs)
new_library.songs << new_songs
new_user.libraries << new_library
# You can do the save check here or up one level if you'd like.
return new_user
end
end

Building a Q&A app in rails, I am a bit unsure about how to send questions to a specific user

As the title suggests, I am building a Q&A application (Like ask.fm) in Ruby on Rails, and I am having some trouble with sending the question to a specific user.
I have 3 models, a User model (from Devise), A Question model with this attribute: content:text and a Answer model with this attribute: content:text
Here are their models
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
end
class Question < ActiveRecord::Base
has_one :answer
belongs_to :user
end
class User < ActiveRecord::Base
has_many :questions
has_many :answers
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
And here are the migrations that I created
This one adds a user_id to the question (So I can check which user sent the question, btw this works fine)
class AddUserIdToQuestion < ActiveRecord::Migration
def change
add_column :questions, :user_id, :integer
end
end
Here I tried to add a receiver (the user that would get the question) but I wont work, when I create a Question It will be equal to 'nil', when I check it out in the rails console (Check the controller to see what I did)
class AddReceiverToQuestion < ActiveRecord::Migration
def change
add_column :questions, :receiver, :integer
end
end
Here is the question controller
class QuestionsController < ApplicationController
def new
#question = Question.new
end
def create
#question = Question.new(question_params)
#question.user_id = current_user.id
if #question.save
redirect_to root_path
else
render 'new'
end
end
private
def question_params
params.require(:question).permit(:content, :user_id)
end
end
I also have a user profile page where I have a form show up, here is the controller for that one
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#user_questions = #user.questions
#question = Question.new
end
def create
#user = User.find(params[:id])
#question = Question.new(question_params)
#question.receiver = #user.id #This does not work
if #question.save
redirect_to root_path
else
render 'new'
end
end
private
def question_params
params.require(:question).permit(:content, :user_id, :receiver)
end
end
I really hope some of you know what I could do here, thank you :)

Resources