Cache instance variable after refresh - ruby-on-rails

I've 3 models: Game, Player, Card.
In Game model i've function:
def cards
#cards ||= Card.all.shuffle
end
When i'm dealing cards to players i do:
player.cards << cards.pop
i'm trying to save cards variable, that after refresh i can deal to another player from the remain cards.
Basically i'm trying to avoid remain cards calculation in that way:
def remain_cards
all_cards = Card.all
table_cards = players.map(&:cards).flatten
all_cards - table_cards
end
Is there any way to do that?
It's a BlackJack game, The Game Cycle:
human player enter into the game and click on start game button and ajax request sent to server
the Game model deals two cards to each player, first bots players and then human players.
after the starting dealing, i'm sending back the all data which includes players and their cards.
then i want thatת if player clicks on the 'hit me' button, that will add him random card from the remain cards

Can you give a little more detail on what's going on? What do you mean by refresh?
Update: In that case I believe the simple answer is no. "Each request is treated as an independent event and no state information is carried over apart from what is stored in the user session and any external databases, caches, or file stores. It is best that you design your application with this in mind and not expect things to persist just because you've set them." (source: What data (if any) persists across web-requests in Ruby on Rails?). The only other thing I can think of to allow the "cards" data to persist across requests is by using a cookie. But then you'd have to keep updating the cookie info anyway I believe. So it may be better or not depending on what your situation is.
Update: I've been thinking...One possible way to have a "card deck" persist is to try something like the following:
#config/initializers/card.rb - store an instance to an app-wide Card object using an initializer.
THE_CARD_DECK = Card.new
#Now we change the before filter which sets the #cards variable to use that constant:
class ApplicationController < ActionController::Base
before_filter :init_cards
def init_cards
#cards = THE_CARD_DECK
end
end
Now #cards should be the sole card deck and you can change its value throughout the game.

Related

Send email to only certain Users, Rails 5

I'm having trouble sending an email blast to only certain users who have a boolean set to true and to not send the email to those users who have it set to false.
In my app I have Fans following Artists through Artists Relationships. Inside my ArtistRelationship model I have a boolean that fans can set to true or false based on if they want email blasts from Artists or not when the Artist makes a post.
So far, I have this:
artist.rb
class Artist < ApplicationRecord
def self.fan_post_email
Artist.inlcudes(:fans).find_each do |fan|
fan.includes(:artist_relationships).where(:post_email => true).find_each do |fan|
FanMailer.post_email(fan).deliver_now
end
end
end
end
posts_controller.rb
class Artists::PostsController < ApplicationController
def create
#artist = current_artist
#post = #artist.artist_posts.build(post_params)
if #post.save
redirect_to artist_path(#artist)
#artist.fan_post_email
else
render 'new'
flash.now[:alert] = "You've failed!"
end
end
end
I'm having trouble getting the fan_post_email method to work. I'm not entirely sure how to find the Fans that have the boolean set to true in the ArtistRelationship model.
You want to send mails to fans of a particular artist. Therefore you call
#artist.fan_post_email
That is you call a method on an instance of the Artist class. Instance methods are not defined with a self.[METHOD_NAME]. Doing so defines class methods (if you where to call e.g. Artist.foo).
First part then is to remove the self. part, second is adapting the scope. The complete method should look like this:
def fan_post_email
artists_relationships
.includes(:fan)
.where(post_email: true)
.find_each do |relationship|
FanMailer.post_email(relationship.fan).deliver_now
end
end
end
Let's walk through this method.
We need to get all fans in order to send mails to them. This can be done by using the artist_relationships association. But as we only want to have those fans having checked the e-mail flag, we limit those by the where statement.
The resulting SQL condition will give us all such relationships. But we do it in batches (find_each) in order to not have to load all of the records into memory upfront.
The block provided to find_each is yielded with an artists_relationships instance. But we need the fan instances and not the artists_relationships instances to send the mail in our block and thus call post_email with the fan instance associated with the relationship. In order to avoid N+1 queries (a query for the fan record of every artists_relationships record one by one) there, we eager load the fan association on the artists_relationships.
Unrelated to the question
The usage of that method within the normal request/response cycle of a user's request will probably slow down the application quite a lot. If an artists has many fans, the application will send an e-mail to every one of them before rendering the response for the user. If it is a popular artist, I can easily imagine this taking minutes.
There is a counterpart to deliver_now which is deliver_later (documentation. Jobs, like sending an e-mail, can be queued and resolved independent from the request/response cycle. It will require setting up a worker like Sidekiq or delayed_job but the increase in performance is definitely worth it.
If the queueing mechanism is set up, it probably makes sense to move the call to fan_post_email there as well as the method itself might also take some time.
Additionally, it might make sense to send e-mail as BCC which would allow you to send one e-mail to multiple fans at the same time.

Design - How to structure filters taking advantage of OOP in Rails API app

My app shows items in a feed. This items are ratings made by different users. Ratings can have a value, a review, etc.
Usual case is logged in user asking for feed (/users/feed). The controller takes the action:
def feed
if authenticate_with_token
imos_and_cursor = Feed.new(RandomItemFeeder.new(params[:cursor], #current_user)).feed
render json: {cursor: imos_and_cursor[imos_and_cursor.length-1], imos: imos_and_cursor[0..imos_and_cursor.length-2]}
end
end
Feed is the boss here. It controles what is served (it serves to respond with items but it also will know how to respond for feeding the people call (index of users basically).
Here are some of the feeders I have:
FriendsFeeder
RandomItemsFeeder
MostRecentItemsFeeder
Following is RandomItemFeeder, responsible of feed with random items:
class RandomItemFeeder < Feeder
def feed
influencers_ids = User.influencers.distinct(:id)
friends = User.friends(#watching_user).distinct(:id) if #watching_user
source_users = influencers_ids.concat(friends)
Rating.random_from_influencers(#watching_user, source_users).scroll(#current_cursor) do |rating, cursor|
#next_cursor = cursor
#feed << ImoPresenter.new(Imo.new(rating), #watching_user)
end
#feed << #next_cursor.to_s
end
end
Presenters
I've structured the rendering of jsons with presenters, so I have different presenters for different cases (feed, user profile, etc.).
Now, I want to incorporate filters. For example, I want that RandomItemFeeder feeds with just items of last 5 five years (obviously, Item model has corresponding fields).
The question is how can I incorporate this filtering utilizing best OOP practices. At the end, is just a scope and i can implement it in different ways, but I just want to do it right now so that I don't have to come back and refactor everything.
Thanks in advance.

saving and updating on serialized column

Apologies in advance for the ugly code (in addition to it not working).
I have a Rails app that has many users who play different quiz games, which were created by various users. There is no rails-style association between user and quizes except that each quiz has been made by a particular user. There's actually not even a 'quiz' model in the application (quizes are made of question and answer models).
For obvious reasons, users are only allowed to try each quiz once; therefore, I have to record which users' quizers a particular user has tried. Therefore, I created a 'quiz' attribute on the user model, and serialized it, so that I would hopefully have a database column like this ['2','4','10'] if I had played the quizes that were made by users with ids 2,4 and 10.
In the user model, after creating :quiz as a 'string', I did this to turn the string into an array in the db
attr_accessible :quiz
serialize :quiz
However, I'm having a lot of trouble writing code that does all of the following
a) checks if that column's not empty (maybe the player hasn't played a quiz yet): I did player.quiz? to test that.
b) checking whether the array includes a particular user.id (i.e. if the player has already played that quiz) I do player.quiz.include?(user.id.to_s) to test that
c) creating the first entry if a user hasn't played any quizzes yet. I did quiz = user.id.to_s
d) adding to the array of game's once a user's played a second user's game (i.e. player's quiz column is ['2'] and now they play quiz by user.id 4.
With various combinations of the code below, I keep erasing/resetting the quiz column when I try to update, or I'm creating strings like '68' when I want ['6','8'] instead.
I'd be grateful if you could give me some tips to fix this up.
code not working
if player && player.quiz? && player.quiz.include?(user.id.to_s) #checks if signed in (if player), if the player.quiz column isn't empty, and if it includes a user id
alreadyplayedthisquiz = true
else
if player.quiz? #it might be that no quizzes are registered on db
quiz = player.quiz #this is a string
quiz.push(user.id.to_s) #can't push onto a string
quiz += user.id.to_s #this is wrong, because if I've played quiz 6 and 8, it'll be '68' in the db
else
quiz = user.id.to_s #if no quiz on player's db, create first one
end
alreadyplayedthisquiz = false
end
player.update_attributes({quiz: quiz})
First, I would store the values in the database as "2","4","6" rather than as ["2","4","6"]. The [ and ] are not necessary.
I would use the String#split to convert the string in the database to an array that can be used in the code. I would use Array#join to convert the array used in the code back into a string for storage in the database.
quiz = player.quiz? ? player.quiz.split(",") : []
alreadyplayedthisquiz = quiz.include? user.id.to_s
if (!alreadyplayedthisquiz)
quiz.push user.id.to_s
player.update_attributes({:quiz => quiz.join(",")})
end

Is this a race condition issue in Rails 3?

Basically I have this User model which has certain attributes say 'health' and another Battle model which records all the fight between Users. Users can fight with one another and some probability will determine who wins. Both will lose health after a fight.
So in the Battle controller, 'CREATE' action I did,
#battle = Battle.attempt current_user.id, opponent.id
In the Battle model,
def self.attempt current_user.id, opponent_id
battle = Battle.new({:user_id => current_user.id, :opponent_id => opponent_id})
# all the math calculation here
...
# Update Health
...
battle.User.health = new_health
battle.User.save
battle.save
return battle
end
Back to the Battle controller, I did ...
new_user_health = current_user.health
to get the new health value after the Battle. However the value I got is the old health value (the health value before the Battle).
Has anyone face this kind of problem before ???
UPDATE
I just add
current_user.reload
before the line
new_user_health = current_user.health
and that works. Problem solved. Thanks!
It appears that you are getting current_user, then updating battle.user and then expecting current_user to automatically have the updated values. This type of thing is possible using Rails' Identity Map but there are some caveats that you'll want to read up on first.
The problem is that even though the two objects are backed by the same data in the database, you have two objects in memory. To refresh the information, you can call current_user.reload.
As a side note, this wouldn't be classified a race condition because you aren't using more than one process to modify/read the data. In this example, you are reading the data, then updating the data on a different object in memory. A race condition could happen if you were using two threads to access the same information at the same time.
Also, you should use battle.user, not battle.User like Wayne mentioned in the comments.

Allow an action once per session

I have a site that tracks video views... every time the controller is called. One could flood their own video views with some heavy F5ing.
How to make it so a view counts or a method runs only once per session?
def show
new_views = #video.views + 1
#video.update_attributes(:views => new_views)
end
You can create a session variable, probably upon login, like :
session[:is_logged] = 1
Then, every time you are about to increment the counter, just check this variable.
Single session variable doesn't work because you have many videos and you would like to separate counts for different videos. I think that a better way is to store view events in db.
pros: you can even allow action once
per user avoiding login/logout
cons: huge size of users_videos table
Well the simplest way would be to track it in the session itself:
def show
if session[:has_counted_view] == nil
new_views = #video.views + 1
#video.update_attributes(:views => new_views)
session[:has_counted_view] = true
end
end

Resources