random loop with conditions in rails - ruby-on-rails

I have a feature called "Browse" that allows users to browse through random profiles. When a user clicks on "browse" they are immediately taken to a users profile that they are NOT already friends with. What should my controller look like?
Right now I've got:
def browse
#users = User.all.offset(rand(current_user.matches.count))
#users.each do |user|
if !current_user.friends.include?(user)
#user = user
return
end
end
end
However that doesn't seem to be working. Any advice? I am admittedly bad with blocks, it seems!

You could try something like this
def browse
#user = (User.all - current_user.friends).sample
end
A better version would be
def browse
#user = User.where('id not in (?)', current_user.friends.map(&:id))
.offset(rand(current_user.matches.count)).limit(1)
end
Also, if you are too concerned about performance, instead of using the offset technique, better use the randumb gem to fetch the random record. It uses database specific functions for selecting random records, if available.

Add an extra method to your User, something like this:
def random_stranger
self.class.where(%Q{
id not in (
select friend_id
from friends
where user_id = ?
}, self.id).
order('random()').
limit(1).
first
end
Then in your controller:
def browse
#user = current_user.random_stranger
end
If your database doesn't know how to optimize that not in then you could replace it with a LEFT OUTER JOIN combined with WHERE friend_id is null.

Related

To show your comment first if you are logged (and if you left a comment)

I am editing a Book-page improving the Comments-Area.
Now it is a "classic" area where the latest 3 comments are showed.
But I want to edit it showing first the comment left by the "logged user" (current_user), if it is logged and if he already left a comment in this page, and then the others (excluding the comment by current user already showed).
Now they are showed using a script in book_controller.rb
def load_comments!
#comments = #book.comments.roots.includes(:user).latest.limit(3)
end
As I said, I have to check if the user is logged and if he already left a comment.
But I don't know how to merge then the two queries.
So my first idea was to add first if current_user
def load_comments!
#comments = if current_user
#book.comments.roots.includes(:user).where(user: current_user).latest.limit(1).any?
+ #book.comments.roots.includes(:user).where(user: !current_user).latest.limit(2)
else
#book.comments.roots.includes(:user).latest.limit(3)
end
end
What you're doing won't actually work since the where clause would apply to all the comments.
The most straight forward solution is to just perform a separate query and concatenate the arrays:
def last_user_comment
current_user.comments
.order(created_at: :desc)
.where(book: #book)
.last if current_user
end
def load_comments!
#comments = #book.comments.roots.then do |base|
if last_user_comment
base.limit(2).to_a.unshift(last_user_comment)
else
base.limit(3)
end
end
end

Show curent user's members based on user_id (Index View)

I have a user_id column. Instead of calling for all members how can I call up members based on current user's and user_id?
This is my controller, I tried changing .all to user_id or current_user.id plus many variations based on examples. Still can't get it. I also have no models (using authrocket). The create action also works and inserts the user_id, I have a def current_user at the bottom.
class Members::MainsController < ApplicationController
# Member Profile List
def index
#members_mains.user_id = current_user.id
#members_mains = Members::Main.all
end
private
# Common Callbacks
def set_members_main
#members_main = Members::Main.find(params[:id])
end
# White List
def members_main_params
params.require(:members_main).permit(:mfirstname, :mlastname, :mtitle, :memail, :mphone, :mnotes, :smfacebook, :smtwitter, :smlinkedin, :user_id)
end
end
If I got it right, your index action should be something like this:
# Member Profile List
def index
#current_member = Members::Main.find(current_user.id)
end
Do you intend to show a list of profiles for all members?
If not, your index action can simply be removed. If so, you wouldn't normally filter on a user_id at all for that action and you can remove that line.
To load a member profile for a specific user_id, try a show action something like this:
def show
#members_main = Members::Main.find_by(user_id: params[:id])
end
That will load a member profile based on the :id from the URL.
If you want to just show the current user's own profile instead, use current_user.id which will give you the AuthRocket user's ID.
def show
#members_main = Members::Main.find_by(user_id: current_user.id)
end
In either case, you may need to remove :show from the set_members_main callback.
Lastly, you probably want to remove :user_id from members_main_params so that users can't modify the AuthRocket user_id. You only want to control that directly (as you already are in the create action).
Hopefully that's clear enough to get you on your way, but I could be off a bit based on what functionality you actually intend for index and show.

Rails: Record checking if user is already following another user through controller

Is there a quick way of only getting users that have not followed another user yet?
I'm trying to create a little "Suggested users to follow" module and I want to display only users that are nearby and that have not been followed yet.
This is my controller:
#user = User.near(current_user.location,50).where.not(id: current_user.id)
In my view I can get the list of users that are nearby and do some kind of check like this:
- #user.each do |user|
- if !current_user.following?(user)
= user.name
= <follow btn code>
But I want it to already be checked through #user in the controller. I think this would be cleaner, or if someone has any other suggestions?
It's easy to filter using select of the Enumerable module (already included in ActiveRecord collections)
in the controller:
#user = User.near(current_user.location,50).where.not(id: current_user.id)
#user = #user.select{ |u| !current_user.following?(u) }
but be warned that this will change the type of #user from an ActiveRecord collection to a simple array.

Rails 4: How to filter an ActiveRecord::Relation based on the presence of some string within an array field?

I am expanding the sample application of Michael Hartl's Rails Tutorial, which you can find here http://ruby.railstutorial.org/chapters/. The objective of the tutorial is to create a twitter-like website, therefore some of the models are User, Micropost and Relationship, where the last one is intended to establish the relationships of follower and followed among the users (just like in twitter) and to define the logic for retrieveng microposts from the database according the user.
That being said, I want to expand the sample application adding the following functionality:
When writing a micropost, this can be retrieved either by all the user's followers by default or by specific followers whose user names are explicitly included in the content of the micropost, with each name preceded by # symbol.
Example:
User ExampleUser posted two microposts:
micropost_1 with content "hello world" will appear in the micropost feed of every follower.
micropost_2 with content "hello world #john #peter" will appear only in peter's and john's micropost feeds (as long as peter and john are the names of actual users that follow ExampleUser).
In order to achieve this objective, so far I have done the following:
1) I added the field "receivers" of type text to the Micropost model through the console lines:
rails g migration AddReceiverToMicropost receivers:text
rake db:migrate
And then I added the following line to the Micropost model
class Micropost < ActiveRecord::Base
serialize :receivers
This allows me to save an array in the receivers field thanks to the serialize functionality (for this I based in the following site: http://amberonrails.com/storing-arrays-in-a-database-field-w-rails-activerecord/)
Thus, I would be able to store a string array with the receivers' names for a given micropost.
2) Next, I tried to solve the problem of extracting the receivers' names from a micropost's content. So I went to the MicropostsController and created the following method:
private
def get_receivers(content)
receivers = []
i = 0
i0 = 0
while i < content.length do
i = content.index('#',i0)
if i.nil?
break
else
i0 = content.index(' ',i)
if i0.nil?
unless i + 1 == content.length
receivers << content[i+1..-1]
end
break
else
unless i + 1 == i0
receivers << content[i+1..i0-1]
end
end
end
end
receivers
end
and then I applied it withing the MicropostsController's create method:
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
#micropost.update(receivers: get_receivers(#micropost.content)) #this line uses the get_receivers method to set the receivers field
flash[:success] = "Micropost created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
The line followed by the comment is the added one.
So far this works properly.
3) Finnaly I tried to update the method that retrieves all the micropost feed of a user (properly signed-in) in order to implement the new functionality. The method is a class method called from_users_followed_by(user), defined in the Micropost model class, and this is the original code (as it appears in the tutorial):
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
This method is invoked by the feed method of User class, here is the code:
class User < ActiveRecord::Base
.
.
.
def feed
Micropost.from_users_followed_by(self)
end
And finally, feed in turn is invoked within the home method of the StaticPagesController in response to a request of displaying the sites's homepage provided that a user is properly signed-in at the moment:
class StaticPagesController < ApplicationController
def home
if signed_in?
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
So, you can see that the original self.from_users_followed_by(user) method retrieves all the user's followed users' microposts plus his own microposts. But in order to add the receivers filter layer, I updated it to this new version:
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
microposts = where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
microposts_filtered = []
microposts.each do |m|
if m.receivers.nil? or m.receivers.length == 0 or m.receivers.include? user.name
microposts_filtered << m
end
end
microposts_filtered
end
As you can see, my attempt was to manually filter the microposts of the microposts Relation returned by the where method and pass the approved ones into the array microposts_filtered. I thought this was going to work, but a problem arises in the StaticPagesController's home method in the line
#feed_items = current_user.feed.paginate(page: params[:page])
and this is that an array has no method called paginate, which is a method of ActiveRecord::Relation class. That's the reason for my title question: how can I add this receivers filter layer in the where method in order to keep the Relation as output, since an array does not allow pagination?
Sorry for the long exposition of the problem but I did it this way for the sake of understanding. I would like to know how I could solve this problem or if there are other better approaches to achieve the same results in a better fashion. Improvements for any part of the whole problem (such as a better method to get the user names from a micropost's content) are welcomed. Thanks in advance.

Rails getting access to current user

I'm trying to save in Note which Employee was the last editor of a `Note'
In a View, I'm able to access the current employee like this:
<h4><%= current_user.employee.id%></h4>
But, in a Model, I can't use current_user.employee.id.
So, I'm trying this in the User model:
def self.current
Thread.current[:user]
end
def self.current=(user)
Thread.current[:user] = user
end
And this in the Note model:
before_create :record_update
before_update :record_update
protected
def record_update
self.lasteditor_id = User.current.employee.id unless User.current.employee.id.nil?
end
What I'm getting is the last User in the Users table.
Thanks for the help!
current_user gets the logged in user information from the session. You cannot access session variables from model. If you want to update the Note model with the Last employee who viewed it, do it in your controller(most likely show action of your note or any other action you think would be right)
def show
#note = Note.find(params[:id])
#note.update_atribute(:last_viewed_by, current_user.id)
end
You code might look different from above. But this is the idea

Resources