reassigning variable even though it should already be defined - ruby-on-rails

I am cross referencing my users' Facebook friends with user's signed up with my site. Don't ask me why I did it this way, but I have a piece of code that I want to basically run once and then "cache" so that it doesn't have to cross reference every time the page loads.
if defined? #friends
p "did NOT have to reload"
#friends
else
p "had to reload"
#friends = FbGraph::User.me(current_user.oauth_token).friends
#friends.map! do |friend|
friend if User.find_by_uid(friend.identifier) != nil
end
#friends.compact!
end
Say this loads every time the friends_list action is loaded. Why does it continually have to reload #friends?

#friends is just an instance variable that gets created then destroyed with every request.
Your best bet is to cache the response like so:
#friends = Rails.cache.fetch("friends-#{current_user.cache_key}") do
friends = FbGraph::User.me(current_user.oauth_token).friends
friends.map! do |friend|
friend if User.find_by_uid(friend.identifier) != nil
end
friends.compact!
end

Related

Ruby on Rails beginner question : equality

I'm starting to know ROR and I was doing a kind of blog with articles, etc...
I did this code :
def show
id = params[:id]
list = Article.all
is_valid = false
list.all.each do |article|
if article.id == id
#is_valid = true
break
end
end
As you can see, this code just wants to check if the article ID exists or not. So I'm testing equality between id and article.id (which's a model linked to the appropriated table in the database) BUT when I try to use or display #is_valid boolean I saw that article.id == id is FALSE every time, even if article.id = 2 and id = 2. I tried to think about everything that can make this occuring, but I admit I still misunderstand this.
Then I ask you if you know why this is occuring. Of course, an equality like 2 == 2 will change #is_valid to true.
Thank you for your help !
Maybe its because params[:id] it's a string and article.id it's an Integer
(byebug) params
{"controller"=>"admin/my_controller", "action"=>"edit", "id"=>"1"}
And yes it is... "id" is a string "1", so you may try this:
def show
id = params[:id].to_i
list = Article.all
is_valid = false
list.all.each do |article|
if article.id == id
#is_valid = true
break
end
end
end
And maybe could work.
This is the answer to your question,
But if you want to learn a little more about Activerecord you can do this
Article.exists?(params[:id])
and that will do what you are trying to do just with a query against db.
and if you want to get just a simple article
record = Article.find_by(id: params[:id]) #return nil when not exist
if record # if nil will threat like false on ruby
#my code when exist
else
#my code when not exist
end
will work (you also can use find but find will throw an exception ActiveRecord::RecordNotFound when not exists so you have to catch that exception.
Activerecord has many ways to check this you dont need to do it by hand.
def show
#article = Article.find(params[:id])
end
This will create a database query which returns a single row. .find raises a ActiveRecord::NotFound exception if the record is not found. Rails catches this error and shows a 404 page. Article.find_by(id: params[:id]) is the "safe" alternative that does not raise.
Your code is problematic since list = Article.all will load all the records out of the database which is slow and will exhaust the memory on the server if you have enough articles. Its the least effective way possible to solve the task.
If you want to just test for existence use .exists? or .any?. This creates a COUNT query instead of selecting the rows.
Article.where(title: 'Hello World').exists?

ActiveRecord Grouping posts created by the same user in the past hour

I have a Story model. A story is created by a user, and those stories go into a feed. If a user creates more than one story within an hour, I want to only show one story, so my question is how do I write an ActiveRecord query to group by created_at to the hour, and user_id?
So this turned out to be pretty easy.
Story.find_each.group_by do |story|
[story.user_id, story.created_at.beginning_of_hour]
end
then you can map over the results and do whatever. I checked if a group had more than one object in it, and if it did, created a new Story, inserted the info for all the stories I wanted to group, and then added it to an array of stories. I never save the new story, so I don't end up with a bunch of weird stories saved.
If anyone has a better way to do this, I'm open to suggestions.
Here is the entire method:
def self.smart_feed
feed = []
self.find_each(batch_size: 10).group_by do |story|
[story.user_id, story.created_at.beginning_of_hour]
end.map do |stories|
if stories.second.count > 1
temp_story = self.new()
stories.second.map do |story|
if story.ratings.any?
temp_story.ratings.append(story.ratings)
elsif story.flights.any?
temp_story.flights.append(story.flights)
end
temp_story.description = "Added #{temp_story.ratings.size} new ratings and #{temp_story.flights.size} new flights"
temp_story.user_id = story.user_id
end
feed.append(temp_story)
else
feed.append(stories.second.first)
end
end
return feed.reverse
end

Rails application helper return if false

I'm writing a helper method to determine if current has any pending reviews to write. If there is a pending review, simply print a line in the view.
My helper is putting exactly the right stuff to the console, however I'm struggling with how to simply return it. In this scenario, current user has an id: 4.
My Code:
def review_pending
gallery = current_user.galleries.first
if gallery
if gallery.permissions.accepted
gallery.permissions.accepted.each do |p|
return true if p.user.reviews.find_by_reviewer_id(!current_user)
puts "already written review: #{p.user.reviews.find_by_reviewer_id(4)} - prints correctly"
end
end
end
end
My goal: if there is a user from the list that current user has not yet reviewed return true.
Thanks!!!
Thanks for all your pointers!
I had forgotten/learned 2 things to make it work:
First, if nil is returned, ruby returns the last returned value which in my case was true (if gallery.permissions.accepted).
Secondly, I placed the "!" before current_user, and should have placed it before the entire line.
Corrected Code:
def review_pending
gallery = current_user.galleries.first
if gallery
gallery.permissions.accepted.each do |p|
return !p.user.reviews.find_by_reviewer_id(current_user.id)
end
end
return false
end

wrong content loaded due to browser cache

I used Ruby on Rails for my website. In one web page, it will load a poll information based on the poll id, which set in url like "http://mywebsite/polls/1". The poll information includes the poll owner name, the poll title, the item pictures in the poll and the people's name who voted on the poll.
I found sometimes it loaded wrong information. That is, it loaded the wrong poll owner name, poll title and voted people from the other poll while the item pictures are correct.I checked the back end and found there was nothing wrong in rails controller. All the variables got the right values. But the chrome browser told me the view is wrong.
If I cleared all the cache and reload the page then it would work normally.Anyone knows why does it happen and what should I do? Thanks
The relavant action codes:
def show
#poll=Poll.where("is_deleted = false AND id = #{params[:id]}")[0]
#items=#poll.items.where("is_deleted = false")
#voted_user_ids = #poll.audiences.where('has_voted != 0').map(&:user_id).uniq
#voted_users = User.where('id IN (?)',#voted_user_ids)
#voted_user_names = #voted_users.map(&:user_name)
if current_user.nil?
#poll_vote_url = "/voted_choice"
else
#current_user_name = current_user.user_name
#poll_vote_url = "/audiences/"+#poll.id.to_s
#if_current_user_voted = #voted_users.include?(current_user)
#is_poll_owner = (current_user == #poll.user)
check_item_id_in_cookies
end
end
def check_item_id_in_cookies
if !cookies.signed[:item_id].nil?
item = Item.find(cookies.signed[:item_id].to_i)
#create audience if the voter is not the poll owner
if current_user == item.poll.user
flash.now[:alert] = "You can't vote on your own poll."
cookies.signed[:item_id] = nil
else
create_audience cookies.signed[:item_id]
end
end
end
def create_audience item_id
#item_id = item_id.to_i
#item = Item.find(#item_id)
#poll = #item.poll
#voted_user_ids = #poll.audiences.where('has_voted != 0').map(&:user_id).uniq
if #voted_user_ids.include?(current_user.id)
flash.now[:alert]="You already voted."
else
audience = #item.poll.audiences.find{|audience| audience.user_id == current_user.id} || Audience.create(:poll_id => #poll.id,:user_id => current_user.id)
#update audience
audience.is_following = true
audience.has_voted = #item.id
audience.save
cookies.signed[:item_id]=nil
flash[:alert]="Thank you for your vote"
redirect_to "/polls/#{#poll.id}"
end
end
Please monitor the network while loading and reloading the page. Especially to request to request to http://mywebsite/polls/1. Check the response headers as well. Even if you don't do on application side the web server or a proxy server may be modifying the request.
You can find help on who to use the network panel of chrome here.

How to store the result of my algorithm?

I have an algorithm that searches through all of my sites users, finding those which share a common property with the user using the algorithm (by going to a certain page). It can find multiple users, each can have multiple shared properties. The algorithm works fine, in terms of finding the matches, but I'm having trouble working out how to store the data so that later I'll be able to use each unit of information. I need to be able to access both the found users, and each of the respective shared properties, so I can't just build a string. This is an example of the output, being run from the perspective of user 1:
user 4
sharedproperty3
sharedproperty6
user 6
sharedproperty6
sharedproperty10
shareproperty11
What do I need to do to be able to store this data, and have access to any bit of it for further manipulation? I was thinking of a hash of a hash, but I can't really wrap my head around it. I'm pretty new to programming, and Ruby in particular. Thanks for reading!
EDIT - Here's the code. I'm fully expecting this to be the most incorrect way to do this, but it's my first try so be gentle :)
So if I'm understanding you guys correctly, instead of adding the interests to a string, I should be creating an array or a hash, adding each interest as I find it, then storing each of these in an array or hash? Thanks so much for the help.
def getMatchedUsers
matched_user_html = nil
combined_properties = nil
online_user_list = User.logged_in.all
shared_interest = false
online_user_list.each do |n| # for every online user
combined_properties = nil
if n.email != current_user.email # that is not the current user
current_user.properties.each do |o| # go through all of the current users properties
n.properties.each do |p| # go through the online users properties
if p.interestname.eql?(o.interestname) # if the online users property matches the current user
shared_interest = true
if combined_properties == nil
combined_properties = o.interestname
else
combined_properties = combined_properties + ", " + o.interestname
end
end
end
if shared_interest == true
matched_user_html = n.actualname + ": " + combined_properties
end
end
end
end
return matched_user_html
render :nothing => true
end
This returns an array of hashes with all users and their corresponding sharedproperties.
class User
def find_matching_users
returning Array.new do |matching_users|
self.logged_in.each do |other_user|
next if current_user == other_user # jump if current_user
# see http://ruby-doc.org/core/classes/Array.html#M002212 for more details on the & opreator
unless (common_properties = current_user.properties & other_user.properties).empty?
matching_users << { :user => other_user, :common_properties => common_properties }
end
end
end
end
end
In your view you can do something like this:
<%- current_user.find_matching_users.each do |matching_user| -%>
<%-# you can acccess the user with matching_user[:user] -%>
<%-# you can acccess the common properties with matching_user[:common_properties] -%>
<%- end -%>
You can use a hash table with the key being the user object and the value being an array of the shared properties . This is assuming that you first need to do a lookup based on the user .
Something like this :
#user_results = { user1 => [sharedproperty3,sharedproperty7] , user2 => [sharedproperty10,sharedproperty11,sharedproperty12]}
You can then acces the values like :
#user_results[user1]
or you can also iterate over all the keys using #user_results.keys

Resources