Rails 3. Get most recent update - ruby-on-rails-3

I am developing an API using Rails 3. A user can have several contact items, like phones, emails, websites and address. Each item got its own model.
I need to do caching in my iPhone app that is using this API therefore I need to get the date when the latest update to any of the items occured and match it against the timestamp of the cache file.
How can I get the most updated items (when comparing all the item tables)?
I am getting the most recent item for each item table like this. Is this really good?
#phones = #user.phones.order("updated_at desc").limit(1)
Thankful for all help!

You can make use of ActiveRecord's touch method. This is especially useful if you have one parent record with many child records. Every time the child records are saved or destroyed the parent record will have it's updated_at field set to the current time.
class User < ApplicationRecord
has_many :phones
has_many :addresses
end
class Phone < ApplicationRecord
belongs_to :user, touch: true
end
class Address < ApplicationRecord
belongs_to :user, touch: true
end
Now any time an address or a phone is updated, the User's updated_at will be set to the current time.
To check when the last updated for the current user took place, over all their tables, you now do:
#last_updated = #user.updated_at
For a small overhead in writes you gain a lot with simpler queries on checking your cache expiry. The documentation for this can be found under belongs_to options.

Related

A tricky query for finding new forum posts

So I have these models.
class Forum < ActiveRecord::Base
has_ancestry
has_many :forum_topics
end
class ForumTopic < ActiveRecord::Base
belongs_to :forum
has_many :forum_topic_reads
# has a :last_post_at date column
end
class ForumTopicRead < ActiveRecord::Base
belongs_to :forum_topic
belongs_to :user
# has a :updated_at date column
end
Very basic setup.
Now what I want to get is an arry of ids of forums that have unread posts sowhere in their subtree. The presence of new posts is decided by the comparescent of forum_topics.last_post_at with forum_topic_reads.updated_at where forum_topics.id = forum_topic_reads.forum_topic_id for a particular user_id or when a ForumTopicRead record is absent for that topic and user.
The problem is - the only way I managed to get it working is by manualy going through every forum and geting its subtree and then getting all the topics for the subtree etc. That results in a ton of similar queries to the database and thus a very slow process.
I believe there should be a way to make it go faster. I just need the ids of the forums that have at least 1 unread topic in their subtrees, don't need the count, don't need the topic ids themselves.
UPDATE
Got a hint from #MrYoshiji
This query:
ForumTopic.joins(:forum_topic_reads).where('forum_topics.last_post_at > forum_topic_reads.updated_at AND forum_topic_reads.user_id = ?', user.id).pluck(:forum_id).uniq
does not work quite well, 'cause it ignores the topics withought appropriate topic_reads (and creating a read for every topic for every user is a bit of an overhead)
UPDATE 2
So I finally came up with a promissing path. If I drop all the reads on a topic when a new post gets added to it (thus updating the :last_post_at field), I'll be able to collect the forum_ids with this query:
"SELECT distinct forum_id FROM `forum_topics` LEFT JOIN forum_topic_reads ON forum_topic_reads.forum_topic_id = forum_topics.id AND forum_topic_reads.user_id = #{user.id} GROUP BY forum_topics.id having count(forum_topic_reads.id) < 1"
Now the only big problem I have is translating this from SQL to ActiveRecord.
ForumTopic.unscoped.joins(:forum_topic_reads).where('user_id = ?', user[:id]).group(:id).having('forum_topic_reads.count < 1').pluck(:forum_id)

Rails: How do I transactionally add a has_many association to an existing model?

Let's imagine I run an imaginary art store with a couple models (and by models I'm referring to the Rails term not the arts term as in nude models) that looks something like this:
class Artwork < ActiveRecord::Base
belongs_to :purchase
belongs_to :artist
end
class Purchase < ActiveRecord::Base
has_many :artworks
belongs_to :customer
end
The Artwork is created and sometime later it is included in a Purchase. In my create or update controller method for Purchase I would like to associate the new Purchase with the existing Artwork.
If the Artwork did not exist I could do #purchase.artworks.build or #purchase.artworks.create, but these both assume that I'm creating a new Artwork which I am not. I could add the existing artwork with something like this:
params[:artwork_ids].each do |artwork|
#purchase.artworks << Artwork.find(artwork)
end
However, this isn't transactional. The database is updated immediately. (Unless of course I'm in the create controller in which case I think it may be done "transactionally" since the #purchase doesn't exist until I call save, but that doesn't help me for update.) There is also the #purchase.artwork_ids= method, but that is immediate as well.
I think something like this will work for the update action, but it is very inelegant.
#purchase = Purchase.find(params[:id])
result = #purchase.transaction do
#purchase.update_attributes(params[:purchase])
params[:artwork_ids].each do |artwork|
artwork.purchase = #purchase
artwork.save!
end
end
This would be followed by the conventional:
if result
redirect_to purchase_url(#purchase), notice: 'Purchase was successfully updated.' }
else
render action: "edit"
end
What I'm looking for is something like the way it would work from the other direction where I could just put accepts_nested_attributes_for in my model and then call result = #artwork.save and everything works like magic.
I have figured out a way to do what I want which fairly elegant. I needed to make updates to each part of my Product MVC.
Model:
attr_accessible: artwork_ids
I had to add artwork_ids to attr_accessible since it wasn't included before.
View:
= check_box_tag "purchase[artwork_ids][]", artwork.id, artwork.purchase == #purchase
In my view I have an array for each artwork with a check_box_tag. I couldn't use check_box because of the gotcha where not checking the box would cause a hidden value of "true" to be sent instead of an artwork id. However, this leaves me with the problem of deleting all the artwork from a purchase. When doing update, if I uncheck each check box, then the params[:purchase] hash won't have an :artwork_ids entry.
Controller:
params[:purchase][:artwork_ids] ||= []
Adding this guarantees that the value is set, and will have the desired effect of removing all existing associations. However, this causes a pesky rspec failure
Purchase.any_instance.should_receive(:update_attributes).with({'these' => 'params'}) fails because :update_attributes actually received {"these"=>"params", "artwork_ids"=>[]}). I tried setting a hidden_value_tag in the view instead, but couldn't get it to work. I think this nit is worthy of a new question.
It is probably best to use make the purchase model a join table and have many to many associations.
Here is an example for your use case.
Customer model
has_many :purchases
has_many :artwork, :through => :purchase
Artwork model
has_many :purchases
has_many :customers, :through => :purchase
Purchase model
belongs_to :customer
belongs_to :artwork
The purchase model should contain customer_id and artwork_id.
you would also need to create a purchase controller that allows you create a new purchase object.
When a customer presses the purchase button it would create a new purchase object which includes the customer_id and the artwork_id. This allows you to create an association between the customer and the artwork they purchase. You can also have a price_paid column to save the price the customer paid at the time of purchase.
if you need more help you can research join many to many associations using :through.
hope it helps

Rails 3: Chaining `has_many` relations with conditions on the last table, without excessive queries

I am new to both DBs and Rails (using 3.2), and I suspect this is a basic question that I'm just having trouble finding an answer for.
I'm making an app that tracks articles people submit to journals. So, each user has a number of articles. For each article, they can submit it to a number of journals. The submission object itself keeps track not only of the article being submitted and the journal it's submitted to, but also the date it was submitted, the way it was submitted (email, online system, etc.), the date a response was received (if any), the response (if any) (e.g. accepted, resubmit, decline).
I'm trying to allow a user to view all of their outstanding submissions in one place. So, for each user, I'm trying to query the db to get all submissions belonging to articles that belong to that user, but only returning the submissions for which response is NULL (meaning there is no response yet, and thus the submission is still outstanding).
So my models are related like this:
class User < ActiveRecord::Base
has_many :articles
end
class Article < ActiveRecord::Base
belongs_to :user
has_many :submissions
end
class Journal < ActiveRecord::Base
has_many :submissions
end
class Submission < ActiveRecord::Base
belongs_to :article
has_one :journal
end
Now, for a given #user, I think I could do something like
#articles_with_subs = #user.articles.joins(:submissions)
and then
#out_subs = Array.new
#articles_with_subs.each do |article|
outs = article.submissions.where("response NOT NULL")
#out_subs.push outs
end
#out_subs.flatten!
but that seems pretty inefficient. What probably big, obvious thing am I missing?
Thanks very much.
#out_subs = Submission.where("response NOT NULL")
.joins(:article).where(:article => {:user_id => #user.id})

Has_many :through association

I made a relationship with the three models using has_many :through:
class Curriculum class < ActiveRecord::Base
has_many :interests
has_many :vacancies,: through => :interests
end
class Vacancy class < ActiveRecord::Base
has_many :interests
has_many :resumes,: through => :interests
end
class Interest < ActiveRecord:: Base
belongs_to :vacancy
belongs_to :curriculum
end
And to create curriculum and vacancy, I create them by administrative, i need to know how can i create the interest to the id of the vacancy, and how it will be logged on the system I have to get the id of it and make the relationship in creating a new bank interest. I wonder how I can program it to do so, and I wonder how the controller will get the create action, and what better way to do this.
First, try to read the whole "Guide to Rails on Associations", especially the part about has_many :through. Then check your schema if your db is migrated and contains for the table interests the necessary foreign keys to curriculums and vacancies called curriculum_id and vacancy_id.
If that is all in place, the following code will create the relationship between two objects:
#curr = Curriculum.find(1)
#vac = Vacancy.find(1)
#curr.interests << #vac
#curr.save
The last two lines creates an interest between #curr and #vac and store that on the database. So you should not use IDs and handle them directly, but work with objects instead.
The second part now is to provide a UI to allow the definition (and removal) of interests between curricula and vacancies. The base flow here is:
You have one curriculum in focus.
You have a link to add / remove curricula.
The view that opens shows a list of possible vacancies, where every vacancy has a checkbox.
By selecting (or deselecting) the check boxes, the IDs of the vacancies will be held in the params of the request sent to the controller.
See the (older) podcast Railscast #52 how to do that in a similar context. Or see the example for has_many :through with checkboxes.
An alternative way would be to use JQuery autocomplete, and add so interests one-by-one. See the nice podcast Railscast #258 which uses JQuery Tokeninput for that.
I think this is what your looking for:
HABTM Checkboxes
That's the best way to use an Has and Belongs to many association.

Wishlist relationships in Rails?

Im building an app where Users have some sort of wishlist
a User can have only one wishlist, and can add existing Items to that wishlist
however the Items belong to other Users on the site
I need to be able to access the wishlist items through current_user.wishlist.items (im using Devise so current_user is available)
i though of adding a wishlist_id column to the items table, but that wouldnt work since items can belong to multiple wishlists.
this seems simple but im having a hard time visualizing the relationship or the migration im supposed to generate
class User < ActiveRecord::Base
has_one :wishlist # or belongs_to :wishlist, it depends which you prefer
end
class Wishlist < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :items
end
And of course:
./script/rails generate migration create_item_wishlists wishlist_id:integer item_id:integer
to create join table between items and wishlists.
UPDATE: To answer "frank blizzard" question in comment:
Let's say you have the same structure as in my answer (just change Item to Product or other model name), with HABTM relationship you just need to add new "item" to collection of "items", and then save wishlist:
#user.wishlist.items << item
#user.wishlist.save
You can make it method in user:
class User
def add_to_wishlist(item)
wishlist.items << item
end
end
If you want to remove or modify collection of "items", just use any Ruby method from Array and then save wishlist, which will check differences for you and save only changes.