Rails: How to display users having specific criteria in search? - ruby-on-rails

When User ABC clicks on the "Search" link, I want ABC to see only those members who live in the same city as ABC. Right now I have the following code in the index action of the Users controller, which displays all the members registered on the website. I am also using will_paginate gem for pagination.
def index
#users = User.paginate :page => params[:page], :per_page => 10
end
In the view, I am iterating through the #users array to display all users.
However, I want ABC to only see members from his/her city. Once ABC can only see members from his/her city, I am going to implement filters to further narrow the results. But that is a later step. Also, ABC should not be able to see profiles of users from other city by simply typing their username in the address bar. How do I do this?

You could use ActiveRecord scopes (Ruby on Rails Guides / ActiveRecord Query Interface)
Then in your User model you will have something like this:
scope :living_in_the_same_city_with, lambda { |user| /* your where condition where(:city_id => user.city_id) */ }
And inside controller:
#users = User.living_in_the_same_city_with(current_user).paginate :page => params[:page], :per_page => 10

Related

Rails query where associated (joined) 'rich_text' is not nil/empty

I have the following right now, which works successfully to produce a list of users, with their rich_text_tlk_with_me 'rich_text' attachments.
User.joins(:rich_text_tlk_with_me).paginate(page: params[:page], per_page: 5).order(created_at: :desc)
However, right now this query is also displaying users who have empty rich_text attachments.
I would like to run a query which only returns the users that have a non empty rich_text attachment.
I have tried:
User.joins(:rich_text_tlk_with_me).where.not(rich_text_tlk_with_me: [nil, ""]).paginate(page: params[:page], per_page: 5).order(created_at: :desc)
But this did not return anything.
I have also searched for documentation, but not found anything relevant.
Assuming user has_many :rich_text_tlk_with_mes and you are using Rails version > 5.
You can get all users who have a associated rich_text_tlk_with_me by the following query.
User.left_outer_joins(:rich_text_tlk_with_mes).where.not(rich_text_tlk_with_mes: {id: nil}).paginate(page: params[:page], per_page: 5).order(created_at: :desc)

Rails scope that orders Users by a Reputation Score

I have a users table and I keep a reputation score on each user. The reputation score is the combined score from 2 factors:
total followers on the Users projects (projects he/she created)
total votes on the Users tasks (tasks that he/she created)
My goal is to allow users to filter users by most recent (the standard index action) or by users with the most reputation. What Rails scope would give back the list of Users ordered by this 'total reputation score' in order of highest score to lowest? thx,
My code is as follows:
User.RB Model
def total_reputation_score
project_follower_count + task_vote_count
end
scope :by_reputation, -> { #HOW DO I CREATE THIS SCOPE? }
Users Controller
def index
#users = User.text_search(params[:query]).paginate(page: params[:page], :per_page => 12)
end
def most_reputation
#users = policy_scope(User)
#users = #users.
text_search(params[:query]).
by_reputation.
paginate(page: params[:page], :per_page => 12)
render :index
end
While you can do this in SQL on the fly, you really want to:
Add a counter cache for each of the two count items shown here; this adds these as calculated columns that Rails will maintain for you, then do this for your scope:
User.order(User.arel_table[:project_follower_count] + User.arel_table[:task_vote_count]).reverse_order
(or something similar)
Alternately you can double down on your RDBMS and build out indices to handle on the fly.

Random record selection and pagination in rails 4

I need to select random 5 records in sqlite3 and paginate them.
def test
add_breadcrumb "Age Test", ages_test_url
#posts = Age.limit(5).order('RANDOM()')
#posts = #posts.page(params[:page]).per_page(2)
end
The above code displays all the record but I need only 5.
Try alternative syntax:
Age.paginate(:page => params[:page], :per_page => 2).order('RANDOM()').limit(5)
However, what is the point of paginating a random set of elements. Every time a user visits the first "page" s/he will see elements other than s/he saw the first. The idea of paginating a randomized elements doesn't seem logical.

Rails 4 - will_paginate: go to last page by default

I have a conversation and messages that belong to a conversation. It would make sense to send users directly to the last page of conversation with the latest messages.
Using .order('created_at DESC') on messages would not be intuitive to users, because it would put latest messages on top of the first page, which is not how most conversations/forums threads work.
Now I have something like this:
#messages = Message.where(conversation_id: params[:id]).paginate(:page => params[:page], :per_page => 15)
will_paginate supports a page option, so you could always give it the latest page as an argument. Ex:
messages.paginate(page: latest_page)
So you just need to implement the latest_page method, counting the number of records and dividing by the number of records per page.
Edit:
Something like this:
def latest_page
messages = Message.where(conversation_id: params[:id])
messages.count / 15
end

How do I Order on common attribute of two models in the DB?

If i have two tables Books, CDs with corresponding models.
I want to display to the user a list of books and CDs. I also want to be able to sort this list on common attributes (release date, genre, price, etc.). I also have basic filtering on the common attributes.
The list will be large so I will be using pagination in manage the load.
items = []
items << CD.all(:limit => 20, :page => params[:page], :order => "genre ASC")
items << Book.all(:limit => 20, :page => params[:page], :order => "genre ASC")
re_sort(items,"genre ASC")
Right now I am doing two queries concatenating them and then sorting them. This is very inefficient. Also this breaks down when I use paging and filtering. If I am on page 2 of how do I know what page of each table individual table I am really on? There is no way to determine this information without getting all items from each table.
I have though that if I create a new Class called items that has a one to one relationship with either a Book or CD and do something like
Item.all(:limit => 20, :page => params[:page], :include => [:books, :cds], :order => "genre ASC")
However this gives back an ambiguous error. So can only be refined as
Item.all(:limit => 20, :page => params[:page], :include => [:books, :cds], :order => "books.genre ASC")
And does not interleave the books and CDs in a way that I want.
Any suggestions.
The Item model idea will work, but you are going to have to pull out all the common attributes and store them in Item. Then update all you forms to store those specific values in the new table. This way, adding a different media type later would be easier.
Update after comment:
What about a union? Do find_by_sql and hand-craft the SQL. It won't be simple, but your DB scheme isn't simple. So do something like this:
class Item < ActiveModel::Base
attr_reader :title, :genre, :media_type, ...
def self.search(options = {})
# parse options for search criteria, sorting, page, etc.
# will leave that for you :)
sql = <<-SQL
(SELECT id, title, genre, 'cd' AS media_type
FROM cds
WHERE ???
ORDER BY ???
LIMIT ???
) UNION
(SELECT id, title, genre, 'book' AS media_type
FROM books
WHERE ???
ORDER BY ???
LIMIT ???
)
SQL
items = find_by_sql(sql)
end
end
untested
Or something to that effect. Basically build the item rows on the fly (you will need a blank items table). The media_type column is so you know how to create the links when displaying the results.
Or...
Depending on how often the data changes you could, gasp, duplicate the needed data into the items table with a rake task on a schedule.
You say you can't change how books and CDs are stored in the database, but could you add an items view? Do you have any control over the database at all?
CREATE VIEW items
(id, type, title, genre, created_at, updated_at)
AS
SELECT b.id, 'Book', b.title, b.genre, b.created_at, b.updated_at
FROM books b
UNION
SELECT c.id, 'CD', c.title, c.genre, c.created_at, c.updated_at
FROM cds c;
You can paginate on a results array, so leave pagination out of the invidual model queries, and add it to your results array:
re_sort(items,"genre ASC").paginate(:page => params[:page], :per_page => items_per_page)

Resources