Order two #variables together by date - ruby-on-rails

I have a messaging system with two tables: Message and MessageCopy, where each time a message is sent it generates a message_copy for each recipient (with info like read, saved, deleted, etc.).
I would like to show a 'conversation' with sent and received messages for my user, and I would like to splice together sent and received messages, ordered by created_at.
Here are my two currently separated relations.
#sentmessages = Message.where("author_id = ? AND project_id = ?", current_user.id, #project.id).order("created_at DESC")
#receivedmessages = MessageCopy.where("recipient_id = ? AND project_id = ?", current_user.id, #project.id).order("created_at DESC")
I'd like to put these together into one relation using Rails if possible rather than MySQL. Thanks!

I hope I understand correctly - you want a single variable that stores these objects ordered by date.
(#sentmessages + #receivedmessages).sort_by(&:created_at).reverse

Related

Count from associate table

I want to have some stats from my app, User can publish post that I call Idee, and I want to know how many user have publish at least one idee.
I'm assuming that should be something like:
#total_user_active = User.where(idee.size >= 1).count
But this doesn't work.
It's user who is in table idee so maybe I should count how much unique user_id are in this column but how?
You can do the following:
#total_user_active = Idee.distinct.count(:user)

Ruby on Rails - ActiveRecord::Relation count method is wrong?

I'm writing an application that allows users to send one another messages about an 'offer'.
I thought I'd save myself some work and use the Mailboxer gem.
I'm following a test driven development approach with RSpec. I'm writing a test that should ensure that only one Conversation is allowed per offer. An offer belongs_to two different users (the user that made the offer, and the user that received the offer).
Here is my failing test:
describe "after a message is sent to the same user twice" do
before do
2.times { sending_user.message_user_regarding_offer! offer, receiving_user, random_string }
end
specify { sending_user.mailbox.conversations.count.should == 1 }
end
So before the test runs a user sending_user sends a message to the receiving_user twice. The message_user_regarding_offer! looks like this:
def message_user_regarding_offer! offer, receiver, body
conversation = offer.conversation
if conversation.nil?
self.send_message(receiver, body, offer.conversation_subject)
else
self.reply_to_conversation(conversation, body)
# I put a binding.pry here to examine in console
end
offer.create_activity key: PublicActivityKeys.message_received, owner: self, recipient: receiver
end
On the first iteration in the test (when the first message is sent) the conversation variable is nil therefore a message is sent and a conversation is created between the two users.
On the second iteration the conversation created in the first iteration is returned and the user replies to that conversation, but a new conversation isn't created.
This all works, but the test fails and I cannot understand why!
When I place a pry binding in the code in the location specified above I can examine what is going on... now riddle me this:
self.mailbox.conversations[0] returns a Conversation instance
self.mailbox.conversations[1] returns nil
self.mailbox.conversations clearly shows a collection containing ONE object.
self.mailbox.conversations.count returns 2?!
What is going on there? the count method is incorrect and my test is failing...
What am I missing? Or is this a bug?!
EDIT
offer.conversation looks like this:
def conversation
Conversation.where({subject: conversation_subject}).last
end
and offer.conversation_subject:
def conversation_subject
"offer-#{self.id}"
end
EDIT 2 - Showing the first and second iteration in pry
Also...
Conversation.all.count returns 1!
and:
Conversation.all == self.mailbox.conversations returns true
and
Conversation.all.count == self.mailbox.conversations.count returns false
How can that be if the arrays are equal? I don't know what's going on here, blown hours on this now. Think it's a bug?!
EDIT 3
From the source of the Mailboxer gem...
def conversations(options = {})
conv = Conversation.participant(#messageable)
if options[:mailbox_type].present?
case options[:mailbox_type]
when 'inbox'
conv = Conversation.inbox(#messageable)
when 'sentbox'
conv = Conversation.sentbox(#messageable)
when 'trash'
conv = Conversation.trash(#messageable)
when 'not_trash'
conv = Conversation.not_trash(#messageable)
end
end
if (options.has_key?(:read) && options[:read]==false) || (options.has_key?(:unread) && options[:unread]==true)
conv = conv.unread(#messageable)
end
conv
end
The reply_to_convesation code is available here -> http://rubydoc.info/gems/mailboxer/frames.
Just can't see what I'm doing wrong! Might rework my tests to get around this. Or ditch the gem and write my own.
see this Rails 3: Difference between Relation.count and Relation.all.count
In short Rails ignores the select columns (if more than one) when you apply count to the query. This is because
SQL's COUNT allows only one or less columns as parameters.
From Mailbox code
scope :participant, lambda {|participant|
select('DISTINCT conversations.*').
where('notifications.type'=> Message.name).
order("conversations.updated_at DESC").
joins(:receipts).merge(Receipt.recipient(participant))
}
self.mailbox.conversations.count ignores the select('DISTINCT conversations.*') and counts the join table with receipts, essentially counting number of receipts with duplicate conversations in it.
On the other hand, self.mailbox.conversations.all.count first gets the records applying the select, which gets unique conversations and then counts it.
self.mailbox.conversations.all == self.mailbox.conversations since both of them query the db with the select.
To solve your problem you can use sending_user.mailbox.conversations.all.count or sending_user.mailbox.conversations.group('conversations.id').length
I have tended to use the size method in my code. As per the ActiveRecord code, size will use a cached count if available and also returns the correct number when models have been created through relations and have not yet been saved.
# File activerecord/lib/active_record/relation.rb, line 228
def size
loaded? ? #records.length : count
end
There is a blog on this here.
In Ruby, #length and #size are synonyms and both do the same thing: they tell you how many elements are in an array or hash. Technically #length is the method and #size is an alias to it.
In ActiveRecord, there are several ways to find out how many records are in an association, and there are some subtle differences in how they work.
post.comments.count - Determine the number of elements with an SQL COUNT query. You can also specify conditions to count only a subset of the associated elements (e.g. :conditions => {:author_name => "josh"}). If you set up a counter cache on the association, #count will return that cached value instead of executing a new query.
post.comments.length - This always loads the contents of the association into memory, then returns the number of elements loaded. Note that this won't force an update if the association had been previously loaded and then new comments were created through another way (e.g. Comment.create(...) instead of post.comments.create(...)).
post.comments.size - This works as a combination of the two previous options. If the collection has already been loaded, it will return its length just like calling #length. If it hasn't been loaded yet, it's like calling #count.
It is also worth mentioning to be careful if you are not creating models through associations, as the related model will not necessarily have those instances in its association proxy/collection.
# do this
mailbox.conversations.build(attrs)
# or this
mailbox.conversations << Conversation.new(attrs)
# or this
mailbox.conversations.create(attrs)
# or this
mailbox.conversations.create!(attrs)
# NOT this
Conversation.new(mailbox_id: some_id, ....)
I don't know if this explains what's going on, but the ActiveRecord count method queries the database for the number of records stored. The length of the Relation could be different, as discussed in http://archive.railsforum.com/viewtopic.php?id=6255, although in that example, the number of records in the database was less than the number of items in the Rails data structure.
Try
self.mailbox.conversations.reload; self.mailbox.conversations.count
or perhaps
self.mailbox.reload; self.mailbox.conversations.count
or, if neither of those work, just try reloading as many of the objects as possible to see if you can get it to work (self, mailbox, conversations, etc.).
My guess is that something is messed up between memory and the DB. This is definitely a really weird error though, might wanna put in an issue on Rails to see why this would be the case.
The result of mailbox.conversations is cached after the first call. To reload it write mailbox.conversations(true)

How to group and limit on count

I am building a Rails 3.2 app and in this app I got two tables (postgres).
One called Users and one called Messages.
In the messages table I got a column called user_id. I need to be able to
get how many messages each user have made and present them in a list AND I only what to get those that have made more than 100 messages. If this could be done in one call it would be perfect.
This is my code so far and it works to get how many messages that each user has made but I need to only get those with more than 100 messages.
#messages = Message.group("user_id").order("count_all DESC").count
Try:
#messages = Message.count(:all, :group => 'user_id, HAVING COUNT(*) > 100')
Or
#messages = Message.group('user_id').having('COUNT(*) > 100').count

Count current users on the page

I'm trying to count current viewers on the particular page. I need this count to be stored in the DB. The main trouble is to clean up after user leaves the page.
Users are anonymous. Every active user sends AJAX-request every 5 seconds.
What's the best algorithm to do that? Any suggestions?
UPD: I'm trying to reduce amount of queries to the DB, so, I think, I don't really need to store that count in the DB while I can access it other way from the code.
Don't even think about storing this in database, your app will be incredibly slowed down.
So use Cache for this kind of operation.
To count the number of people, I'd say:
assign a random ID to each anonymous user and store it in his session
send the ID within your ajax call
store an Array of Hashes in cache with [{ :user_id, :latest_ping }, {} ] (create a cache var for each page)
delete the elements of the array which appear to be too old
you've your solution: number of users = nb of elements in the array
If you store the users in the database somehow, you could store a last_seen_at field in the users table, and update that with Time.now for every AJAX request that user sends.
To display how many users you currently have, you can just perform a query such as:
#user_count = User.where("last_seen_at < ?", 5.seconds.ago).count
If you want to clean up old users, I suggest that you run some kind of cron job, or use the whenever gem, or something like that, to periodically delete all users that haven't been seen for some time.
I would suggest you create a model that contains a unique key (cookie-id or something) that you save or update with every AJAX heartbeat request.
You then have a session controller that could look like this:
def create
ActiveUser.where(:cookie => params[:id]) || ActiveUser.new
ActiveUser.cookie = prams[:id]
ActiveUser.timestamp = Time.now
ActiveUser.save
end
Your number of active users is then simply a SELECT COUNT(*) FROM ActiveUsers WHERE timestamp > NOW() - 5 or something like that.
Martin Frost is on the right track. There's the #touch method to update last_seen_at: user.touch(:last_seen_at)
But it would be even more efficient to just update the user without having to fetch the model from the database:
> User.update_all({:last_seen_at => Time.now}, {:id => params[:user_id})
SQL (3.1ms) UPDATE "users" SET "last_seen_at" = '2011-11-17 12:37:46.863660' WHERE "users"."id" = 27
=> 1

how to store facebook friends to DB (newbie)

i'm creating a facebook-app for university project and i'm trying to store all my friends in the DB.
By using the API-syntax "me/friends" i get a facebook-respond looking like this:
{"data"=>[{"name"=>"Albert Einstein", "id"=>"11111111"}, {"name"=>"Max Mustermann", "id"=>"222222222"}, {"name"=>"Just Another Name", "id"=>"333333333"}]}
I believe its a json-object, but i'm not sure.
Question: How can i save the data, i need a DB with all the User-IDs of my friends.
Thx!
Edit:
Hey, this is what i have searched for. But i still get an error and don't know why.
My code:
def insert_1
fb_friends = rest_graph.get('me/friends')
fb_friends[:data].each do |f|
#friend = Model.new(:name => f["name"] )
#friend.save
end
end
I get an Heroku error (We're sorry, but something went wrong.)
You have two options -
Option 1-
You can create a friends table which will belong to users table. If a user has 200 friends, it will create 200 entries in friends table all belonging to the user via has_many-belongs_to relationship. For storing data, you just have to iterate over facebook friends hash and then save each of them separately
Pros : You can search for any friend separately.
Cons : There will be so many of friend entries. Saving them will take time, if somebody has many friends(say 500-700). Repeating entries will be created for mutual friends.
Options 2
You can add a friends column in your users table and declare this in your user.rb
serialize :friends
This way, you just have to pass a hash object to friends attribute of user table, and rails will save that in yaml format for you. When you will do #user.friends, rails will again convert that yaml formatted data to hash object and return it.
Pros : There will be only one query to save all users. You can iterate through this hash to show list of all friends.
Cons : You can't update them separately, you will update all together. Not good if you want to store some other information in relation to user's friends.
Update
as per your code example above
fb_friends = #your logic to get data as shown above.
fb_friends[:data].each do |f|
#friend = Friend.new(:name => f["name"],:fb_user_id => f["id"] )#creating Friend model obj.
#friend.save
end

Resources