Rails - Proper associations/data model for content display "cooldown" - sql

I have a user model and a content model. When a user views a piece of content, I need to make sure the user does not see that content again for say, 48 hours.
What's the Rails way to model this out? I'd like to have a table with a user_id, content_id, and a timestamp that the view was recorded, then have a worker clear out entries with timestamps > 2 days. This way when a user requests more content, I can filter out content that has an entry where user_id and content_id match.
Don't think it should matter, but I'm using MySQL with Rails 3.2.

I think you can do the following in your model.
class User < ActiveRecord::Base
...
has_many :contents, -> { where(["EXTRACT(HOUR FROM last_viewed_at) > ? OR last_viewed_at IS ?", 48, nil)}
end
I used or condition to make it nil because when it is initialized or new record created so that user can be able to see it.
I am not sure how you are using your worker.
Please suggest me if I am missing anything. I am not intended to answer accurately, rather trying a way I can realize what is possible.

Related

Ruby on rails - Sort data with SQL/ActiveRecord instead of ruby

I'm working with two tables Video and Picture and I would like to regroup them using SQL instead of ruby. This is how I do it now :
#medias = (Video.all + Picture.all).sort_by { |model| model.created_at }
Is their a way to do the same thing only with SQL/ActiveRecord?
Since you don’t have the same columns in each model you could create a polymorphic relationship with a new model called media. Your Videos and Pictures would be associated with this new model and when you need to work on only your media you don’t need to worry about whether it is a video or a picture. I’m not sure if this fits into your schema and design since there is not much info to go on from your post but this might work if you wanted to take the time to restructure your schema. This would allow you to use the query interface to access media. See the Rails Guide here:
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
You can create a media model with all the fields need to satisfy a Video or Picture object. The media model will also have a type field to keep track of what kind of media it is: Video or Picture.
Yes, using ActiveRecord's #order:
#video = Video.order(:created_at)
#pictures = Picture.order(:created_at)
#medias = #video.all + #pictures.all # Really bad idea!
Also calling all on the models like that will unnecessarily load them to memory. If you don't absolutely need all records at that time, then don't use all.
To run sql queries in Rails you could do this:
sql_statement = "Select * from ..."
#data = ActiveRecord::Base.connection.execute(sql_statement)
Then in your view you could simply reference the #data object

Unexpected behavior with ActiveRecord includes

I'm using the AR includes method to execute a LEFT OUTER JOIN between objects User and Building, where a User may or may not have a Building association:
users = User.includes(:building).references(:buildings)
Since I'm using references, any associated Building objects will be eager loaded.
My expectation was that I would then be able to iterate through the list of users, and check whether a user had a building associated with them without triggering additional queries, but I see that in fact whenever I try to access the building property of a user that doesn't have one, AR makes another SQL call to try and retrieve that building (though on subsequent tries it will just return nil).
These queries are obviously redundant as the association would have been loaded during the initial join, and seems to defeat the whole purpose of eager loading with includes/references, as now I'm looking at N times the number of queries equal to the number of empty associations.
users.each do | user |
# This will trigger a new query when building is not present:
# SELECT "buildings".* FROM "buildings" WHERE "buildings"."address" = $1 LIMIT 1 [["address", "123 my street"]]
if user.building
puts 'User has building'
else
puts 'User has no building'
end
end
User class:
class User < ActiveRecord::Base
belongs_to :building, foreign_key: 'residence_id'
end
Is there a way to check the presence of the users' building association without triggering extra queries?
ON RAILS 4.2.0 / POSTGRES
UPDATE:
Thank you #BoraMa for putting together this test. Looks like we're getting different behavior across recent Rails versions:
OUTPUT (RAILS 4.2.0):
User 1 has building
User 2 has building
User 3 has no building
D, [2016-05-26T11:48:38.147316 #11910] DEBUG -- : Building Load (0.2ms) SELECT "buildings".* FROM "buildings" WHERE "buildings"."id" = $1 LIMIT 1 [["id", 123]]
User 4 has no building
OUTPUT (RAILS 4.2.6)
User 1 has building
User 2 has building
User 3 has no building
User 4 has no building
OUTPUT (RAILS 5.0.0)
User 1 has building
User 2 has building
User 3 has no building
User 4 has no building
Take aways:
This issue was limited to "dangling foreign keys (ie the residence_id
column is not nil but there is no corresponding building object)"
(THANKS #FrederickCheung)
The issue has been resolved as of Rails 4.2.6
Sounds like you got bit by a bug in Active Record, that was fixed in rails 4.2.3.
In the case where the column was nil Active Record already knows that it doesn't even need to try loading the associated object. The remaining cases were the ones impacted by this bug
Seems like a typo, please notice building instead of buildings: User.includes(:building).references(:buildings)
That should trigger the big query that uses the format of AS tX_rY for each association and table.
It seems that since rails 4.1 there are potential clashes with how just how implicit #includes should be, see the following open issue.
This code is all untested for syntax, but there would be two approaches I would try:
1/ Make the eager loading implicit
users = User.eager_load(:building).preload(:buildings)
2/ Separate out the two types of users, ones where the building is attached, meaning you don't even try and preload the building, removing the innefficiency.
users = User.includes(:building).where.not(residence_id: nil).references(:buildings)
users.each do | user|
puts "User has building: #{user} #{user.building}"
end
# No additional references needed to be eager-loaded.
users = User.where(residence_id: nil)
users.each do | user |
puts "#{User} has no building."
end

How to find_or_initialize based on two fields when both fields correspond to possible uninitialized objects

Hi guys I have a situation where on a form I'm taking in orders for a car servicing application. I have the following models:
Car
belongs_to :car_company
Car_company
has_many :cars
Services
attributes_accessible :car_company_id, :car_id
#virtual attributes
attributes_accessible :car_company_name, :car_reg
The thing is that on a single form the user can enter in the name of the car company as well as the registration number of a car. If the company name doesnt exist it creates a new company and associates it with the service and the same goes for the car. I got this part working however the thing is that I want that on submitting this form the car created should be automatically associated with the car_company whether the carcompany exists or doesn't exist.
I'm pretty stuck here on how to get this thing done the right way? Its basically just to avoid having to enter the car details and the company details seperately just to use them on a form. Any ideas guys?
I see you are using an unconventional model name. By convention in rails, your model should be CarCompany. However, I think what you have will work.
Putting something like this in the appropriate controller may be something like what you want. If not, please clarify what you want.
car_company = Car_company.find_or_initialize_by_name(params[:car_company_name])
car = Car.find_or_initialize_by_registration(params[:car_registration])
car_company.cars << car
car_company.save
You actually may be able to combine the middle two lines with car_company.cars.find_or..., but I'm not sure if that works or not.
I hope that helps.

Finding Records using Rails without a Match (Fault without Fault Cleared)

I'm attempting to write a site in Rails where a user in a manufacturing plant can see what devices are failing. The program storing the alarm data stores one entry when a device faults, and then stores another entry when the device gets fixed. The entries are linked only by having the same value in the EventAssociationID column. How might I write a named scope in Rails to check which faults have been fixed and which ones haven't?
I wasn't able to do it in a named scope, however, I was able to define a method for the model that solved the problem:
def inAlarm
return ConditionEvent.count(:all, :conditions => ['EventAssociationID = ?', self.EventAssociationID]) == 1
end

Find all records of a certain type in Polymorphic table using ActiveRecord in Rails 3

I have a table Category that is a polymorphic model for a bunch of other models. For instance
model Address has shipping, billing,
home, work category
model Phone has home, mobile, work,
fax category
model Product has medical, it
equipment, automotive, aerospace, etc
categories.
What I want to be able to do is something like
Product.all_categories and get and array of all categories that are specific to this model.
Of course I can do something like this for each model in question:
Category.select("name").where("categorizable_type = ?","address")
Also pace_car - which is rails 3 ready, allows me to do something like this:
Category.for_category_type(Address)
But I was wondering if there is a more straightforward / elegant solution to this problem using Active Record iteself - without relying on a gem?
Thank you
I'm not aware of anything built-in to ActiveRecord to give you this, but you could set up that for_category_type method in one line of code in your Category controller:
scope :for_category_type, lambda { |class_name| where("categorizable_type = ?", class_name) }