I am pulling game data from an API (https://github.com/games-directory/api-giantbomb) where users can search through games and add it to their library. However I can't get the Create method working that is supposed to create an instance of the game called from the API and post it to the user's library page.
I have my associations set up across four models: Game, User, Library, and Library_Game:
Game.rb
class Game < ApplicationRecord
has_many :library_games
has_many :libraries, through: :library_games
has_many :users, through: :libraries
serialize :data
def fetch_data
game = GiantBomb::Game.detail(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
User.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :libraries
has_many :games, through: :libraries
def has_game?(game)
games.where(id: game.id).exist?
end
end
Library.rb
class Library < ApplicationRecord
belongs_to :user
has_many :library_games
has_many :games, through: :library_games
end
Library_Game.rb
class LibraryGame < ApplicationRecord
belongs_to :library
belongs_to :game
has_one :user, through: :library
end
I am routing the create method through the Games Controller. Here is my create method along with the strong params.
def create
#library_game = Game.new(game_params)
#library_game.fetch_data
#library_game.save
redirect_to library_path
end
private
def game_params
params.require(:game).permit(:data)
end
end
I get an error on my fetch_data method saying "undefined method `each' for nil:NilClass". However if I take out the #library_games.fetch_data part of my create method, it redirects to my library but there is no instance of the game.
My link_to in my view that is supposed to attach the game to a user's library:
<%= link_to 'Add to library', create_path(current_user, game: game), method: :post %>
And the library controller's index that is supposed to display the games.
class LibraryController < ApplicationController
def index
#library_games = User.find(params[:id]).games
end
end
The view for the library index:
<% if #library_games.exists? %>
<% #library_games.map(&:to_giant_bomb_game).each do |game| %>
#etc.
What should I be doing in my create method to create a new instance of the API data that can save to a user's library?
Related
Walls belong to users through a WallAssignments association.
class Wall < ApplicationRecord
belongs_to :user
has_many :wall_assignments
has_many :users, :through => :wall_assignments
end
class User < ApplicationRecord
has_many :wall_assignments
has_many :walls, :through => :wall_assignments
end
class WallAssignment < ApplicationRecord
belongs_to :user
belongs_to :wall
end
In the create action, I'm associating the current user with the new wall record.
def create
#wall = Wall.new
#wall.wall_assignments.build(user_id: current_user.id)
if #wall.save
redirect_to #wall
else
redirect_to current_user
end
end
However, aside from allowing many users to belong to the wall, I'd like to have one user (the user who created it) own the wall.
I'm attempting something like this:
class Wall < ApplicationRecord
after_create { owner }
belongs_to :user
has_many :wall_assignments
has_many :users, :through => :wall_assignments
private
def owner
self.owner = Wall.users.first
end
end
Eventually, I'd like to be able to call #wall.owner.name and #wall.owner.id in my views.
I guess you want to have has_many(as users) and has_one(as owner) with same table User.
In this scenario, your Wall model will be:
class Wall < ApplicationRecord
belongs_to :owner, class_name: 'User', foreign_key: :owner_id
has_many :wall_assignments
has_many :users, :through => :wall_assignments
end
You need to add owner_id column in walls table.
So when you create Wall record, it will
class Wall < ApplicationRecord
after_create { add_owner }
private
def add_owner
self.update_column(:owner_id, self.users.first.id) if self.users.present?
end
end
You can also modify controller's create code(I assumed, create method will get called only once.)
def create
#wall = Wall.new(wall_params)
#wall.owner_id = current_user.id
#wall.wall_assignments.build(user_id: current_user.id)
if #wall.save
redirect_to #wall
else
redirect_to current_user
end
end
with this, you don't need to add after_create callback in Wall model.
And then you can call #wall.owner.name and #wall.owner_id
My app has posts which have comments. This is the functionality I would like: A user sees the comment activity on the post he has created AND a commenter sees comment activity on the posts he has commented on.
My models:
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :course
has_many :comments, dependent: :destroy
end
class Comment < ActiveRecord::Base
include PublicActivity::Model
tracked except: :update, owner: ->(controller, model) { controller && controller.current_user }
belongs_to :post
belongs_to :user
end
And the Activities controller:
class ActivitiesController < ApplicationController
def index
#activities = PublicActivity::Activity.order("created_at desc")
end
end
And the Activities index view:
<% #activities.each do |activity| %>
<div class="activity">
<%= link_to activity.owner.username, activity.owner if activity.owner %>
added comment to <%= link_to activity.trackable.post.title, activity.trackable.post %>
</div>
<% end %>
Thanks!
I'm afraid the PublicActivity gem is not designed to do this. It is meant to create one activity when one activity happens. In your case you need to create possible many notification records (1/user) if an activity happens. We had the same problem at my work and we decided to create a Notification model that is similar to the PublicActivity implementation.
class User < ActiveRecord::Base
has_many :notifications, foreign_key: :notified_user_id, dependent: :destroy
end
class Comment < ActiveRecord::Base
# maybe it would be better to name it as inverse_notifications
has_many :notifications, as: :trackable, dependent: :destroy
end
class Notification < ActiveRecord::Base
belongs_to :trackable, polymorphic: true
belongs_to :acting_user, class_name: "User"
belongs_to :notified_user, class_name: "User"
validates_presence_of :trackable, :notified_user, :key
scope :unread, -> { where("read_at IS NULL") }
end
This allows you to create 2 types of notifications when a comment is created:
one for the owner with key: "post.commented"
many for the commenters with key: "comment.created"
And it is possible to set the read_at attribute if the user has seen it, so you can add different style to them on the frontend.
I'm trying to build a method that pulls an array of arrays, much like nested loops in the view.
I am trying to build a method on my User model that does this:
#past_parties = User.parties
<%= #past_parties.each do |past_party| %>
<%= past_party.guests("email").uniq.each do |guest| %>
<%= guest.name %> <%= guest.email %>
<% end %>
<% end %>
These are my models:
class User < ActiveRecord::Base
has_many :hosts, dependent: :destroy
has_many :parties, through: :hosts
def past_guests
self.parties.guests
end
end
class Host < ActiveRecord::Base
belongs_to :user
has_many :parties, dependent: :destroy
has_many :guests, through: :parties
end
class Party < ActiveRecord::Base
belongs_to :host
has_many :guests, dependent: :destroy
end
class Guest < ActiveRecord::Base
belongs_to :party
end
I can get the loop to work in the view, but when I try to build it as a method (so I can call current_user.past_guests, if current_user is an instance of User.), I get this error
undefined method `guests' for #<ActiveRecord::Associations::CollectionProxy []>
How do I get all of the guests that a user has ever invited without building the loop in the view?
The problem is that you're trying to access an array from another array:
self.parties.guests
self.parties returns an #<ActiveRecord::Associations::CollectionProxy []>, so if you want to get the guests of the parties you have to loop over the elements.
But since you want only the guests, you can simply change your user class to:
class User < ActiveRecord::Base
has_many :hosts, dependent: :destroy
has_many :parties, through: :hosts
has_many :guests, through: :parties
# Call your guests
def past_guests
self.guests
end
end
Each party is going to have collection of guests. You need to do this in your method definition:
def past_guests
self.parties.collect(&:guests)
end
So, this is pretty simple (I think) but I still have a hard time wrapping my head around this. I have an app, where as a User that is signed in can create Topics and Bookmarks which are nested in the topics. A User can also Like a bookmark another user created.
On my users/show.html.erb I'm trying to make a list of all the bookmarks and likes a user has created. Both have user_id's which I would assume that's how I could call them, I'm just retarded and need some help.
What would I put in my user_controller and my user/show.html.erb files?
Thanks for the help!
Here is my User.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_many :topics
has_many :bookmarks, dependent: :destroy
has_many :likes, dependent: :destroy
#liked method takes a bookmark object and returns a like object if one exists
def liked(bookmark)
likes.where(bookmark_id: bookmark.id).first
end
end
Like.rb
class Like < ActiveRecord::Base
#considered a simple join table, that represents a relation between two objects (user/bookmark)
belongs_to :user
belongs_to :bookmark
end
Bookmark.rb
class Bookmark < ActiveRecord::Base
belongs_to :topic
belongs_to :user
has_many :likes, dependent: :destroy
end
In your user_controller:
def show
#user = User.find(user_params[:id])
end
def user_params
params.require(:user).permit(:id)
end
In your show.html.erb:
<% #user.bookmarks.each do |bookmark| %>
<%= bookmark.name %> #guessing here as I don't know your DB schema
<% end %>
<% #user.likes.each do |like| %>
<%= like.name %> #still guessing
<% end %>
You said Bookmarks are nested in Topics, but you don't show that in your code. Instead of User model having:
has_many :bookmarks, dependent: :destroy
it should be
has_many :bookmarks, through: :topics, dependent: :destroy
You can still access bookmarks the same way:
#user.bookmarks
How to create a new record in after_save using other model?
I tried this line which resulted "undefined method `journals' for nil:NilClass"
e.g.
resources :users do
resource :profile
resources :journals
end
class User < ActiveRecord::Base
has_one :profile
has_many :journals
end
class Profile < ActiveRecord::Base
belongs_to :user
after_save :create_new_journal_if_none
private
def create_new_journal_if_none
if user.journals.empty? ????
user.journals.build() ????
end
end
end
class Journals < ActiveRecord::Base
belong_to :user
end
Nested models are going to be saved as well once parent saves, so it's easy to use before_create block and build a nested resource here.
class Profile < ActiveRecord::Base
belongs_to :user
before_create do
user.journals.build unless user.journals.any?
end
end
This line of code will create a profile and a journal assigned with the User
User.find(1).create_profile(name :test)