Limiting a search to records from last_request_at - ruby-on-rails

I am trying to figure out how to display a count for records that have been created in a table since the last_request_at of a user.
In my view I am counting the notes of a question with the following code:
<% unless #questions.empty? %>
<% #questions.each do |question| %>
<%= h(question.notes.count) %>
end
end
This is happening in the /views/users/show.html.erb file. Instead of counting all the notes for the question, I would only like to count the notes that have been created since the users last_request_at datetime. I don't neccessarily want to scope notes to display this 'new notes' count application wide, just simply in this one instance.
To accomplish I am assuming I need to create a variable in the User#show action and call it in the view but not really sure how to do that.
Other information you may need:
class Note < ActiveRecord::Base
belongs_to :user
belongs_to :question
end
class Question < ActiveRecord::Base
has_many :notes, :dependent => :destroy
belongs_to :user
end

Just create a named scope and then use it only when it applies:
class Note < ActiveRecord::Base
named_scope :added_since, lambda { |time| {
:conditions => time && [ 'created_at>=?', time ]
}}
end
This should only enforce conditions if a time is provided. If you submit a nil time, the default behavior is to scope all notes.
This way you can do something along the lines of:
#new_notes = #user.notes.added_since(#user.last_login_at)
Adding a named scope does not alter the default scope.

Related

return the count for element in a has_many relationships

i have 2 models one is listing and user
user has_many listings
listing belongs_to user
i have a view setup , i want to display for each user their own listings count ,i try this code :
<% User.all.each do |user| %>
<%= user.listings.count %>
<% end %>
i want to grab the listing count for each user . i found a bunch of solution here , all return the loop .other solutions i tried is to create a class method .
def count_listings
Listing.where(:user_id => user.id).count
end
try to call this way <%= User.count_listings%> it doesn't work .
for some reason there something i'm missing ,can't quite figure it out .
The :counter_cache option can be used to make finding the number of belonging objects more efficient. Consider these models:
class Order < ActiveRecord::Base
belongs_to :customer
end
class Customer < ActiveRecord::Base
has_many :orders
end
With these declarations, asking for the value of #customer.orders.size requires making a call to the database to perform a COUNT(*) query. To avoid this call, you can add a counter cache to the belonging model:
class Order < ActiveRecord::Base
belongs_to :customer, counter_cache: true
end
class Customer < ActiveRecord::Base
has_many :orders
end
With this declaration, Rails will keep the cache value up to date, and then return that value in response to the size method.
Although the :counter_cache option is specified on the model that includes the belongs_to declaration, the actual column must be added to the associated model. In the case above, you would need to add a column named orders_count to the Customer model. You can override the default column name if you need to:
class Order < ActiveRecord::Base
belongs_to :customer, counter_cache: :count_of_orders
end
class Customer < ActiveRecord::Base
has_many :orders
end
Counter cache columns are added to the containing model's list of read-only attributes through attr_readonly.
source: Rails guide on associations
..scroll down to options of belongs_to
If all you need is what you show in the example you can do it better as follows
<% Listing.group(:user_id).count.each do |user, count| %>
<%= "user: #{user} has #{count} listings" %>
<% end %>
This does a single query to the database and fetches only what you need.
SELECT COUNT(*) AS count_all, user_id AS user_id FROM `listings` GROUP BY user_id
and returns a hash like:
{
1: 123,
2: 231
}
#{ user_id: count }

How do you query upstream in a polymorphic association without raw SQL?

In my exercise app, users can "check" an exercise to complete it. If I know that checkable_type is 'Exercise' and have the checkable_id, how do I query the exercise name?
I'm not sure what to put in my controller to have a list of "completed exercises" and their names based on checkable_type and id.
class Exercise < ActiveRecord::Base
attr_accessible :name
has_many :checks, :as => :checkable
end
class Check < ActiveRecord::Base
belongs_to :checkable, :polymorphic => true
attr_accessible :checkable, :checkable_id, :checkable_type
end
Would just joining the other model give you what you wanted?
completed_exercises = Exercise.joins(:checks)
That should produce an inner join, and AR already knows how to include the polymorphic association as part of the query.
You can define a scope checkable_type
scope :checkable_type, lambda { |class_name| where("checkable_type = ?", class_name) }
I guess you want the polymorphic records with type 'Exercise' and that would be created only when exercises are completed or you can maintain it in a completed_at timestamp column for that matter.
Then you can pulls a list of all completed exercises
scope :completed, where('completed_at IS NOT NULL')
completed_exercises = Check.checkable_type('Exercise').completed
You may ignore the completed scope if that's not the case
Figured it out!
class Exercise < ActiveRecord::Base
attr_accessible :exercise_name
has_many :checks, as: :checkable
end
class Check < ActiveRecord::Base
belongs_to :checkable, polymorphic: true
attr_accessible :checkable_id, :checkable_type, :exercise_name
end
I realized I needed to call .checkable before calling the desired attributes in the exercise model.
<% #checks.each do |check| %>
<%= check.checkable.exercise_name %>
<%= check.checkable.created_at %>
<% end %>
This pulls the right exercise name and "completion time" (via created_at) from the exercise table.

Utilizing rails helper and controller methods

Suppose I have a Rails app that deals with Posts and Comment objects. A Post has_many Comments and each Comment belongs_to a Post.
Each Comment has a word_count property. The Post object has an average_comment_word_count property which is an average of each of the Comment's word_count.
First question is if the Post object gets modified asynchronously (comments get added which affects the average word count), at what point should I recalculate the property? When the object is returned? Or each time a new comment is added? Does it go into the comment or post helper methods? Which controller function should call this method?
Also when I include the following Post helper method, I get a NULL value returned as JSON.
def average_word_count
#average_word_count = 0
# current_user returns the current user object
# user has_many posts and each post belongs_to a user
current_user.posts.find(params[:id]).comments.each do |comment|
#average_word_count += comment.word_count / current_user.posts.find(params[:id]).comments.count
end
#average_word_count
end
class Comment < ActiveRecord::Base
belongs_to :post
after_save :update_post_word_count
def update_post_word_count
average_wc = post.comments.average(:word_count)
post.update_attributes average_comment_word_count: average_wc
end
end
Or, derive it only when you need it:
class Post < ActiveRecord::Base
has_many :comments
def average_comment_word_count
comments.average :word_count
end
end
Or, if it's just used once somewhere with low traffic, brazenly flout the Law of Demeter and just calculate it as needed from a post object:
Average Comment Word Count: <%= #post.comments.average :word_count %>
Update: As #coreward notes, the first part of this answer isn't useful for asynchronous updates, but the rest of the answer may still be helpful.
You would be a lot better off just building a custom counter cache based on what's already in ActiveModel that keeps track of the total number of words, then just count comments to do math manually.
# you need a comments_count column and a words_count column in this table
class Post < ActiveRecord::Base
has_many :comments
def avg_words_per_comment
words_count / comments_count
end
end
class Comment < ActiveRecord::Base
belongs_to :post, :counter_cache => true
after_save { update_counters(post.id, :words => word_count }
before_destroy { update_counters(post.id, :words => -word_count }
end
# And in your view:
<p>
The average comment for this post has <%= #post.avg_words_per_comment %> words.
</p>
Then you don't need to worry about asynchonicity and the calculation on view is minimal.
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/counter_cache.rb#L65

Displaying Polymorphic Associations in a View

There's probably a simple answer to this, but I'm lost.
I created my first Polymorphic Association today to create an activity field.
Here's the activity.rb:
class Activity < ActiveRecord::Base
belongs_to :trackable, :polymorphic => true
end
In the database for activities, I have the columns:
id
name
trackable_id
trackable_type
created_at
updated_at
Here's the note.rb:
class Note < ActiveRecord::Base
has_many :activities, :as => :trackable
after_create :create_an_activity
def create_an_activity
self.activities.build(:name => candidate_id)
end
end
In my index.html.erb view I have:
<% #activities.each do |activity| %>
<p>activity.name</p>
<% end >
My question is:
Currently, activity.name in the view is outputting the id because I have :name => candidate_id. A note is created for a candidate. But, what I really want it to output is candidate.full_name (which is in the candidates table). However, this doesn't work because full_name is not in the notes table. It's in the candidates table. Is there any way to access that? Candidates has_many notes and a note belongs_to a candidate.
enjoyed your skill share with Vin a couple months ago!
I believe what you're looking for can be accessed by going through the parent association, by calling self -> parent -> attribute:
def create_an_activity
self.activities.create(:name => self.candidate.full_name)
end
Also correct me if i'm wrong, but unless you are calling a save later on, it seems like self.activities.create is what you are looking for instead of .build

Include with condition, but always show the primary table results

I have a function like :
# get all locations, if the user has discovered them or not
def self.getAll(user)
self.find(:all, :order => 'min_level asc', :include => 'discovered_locations',
:conditions => [ "discovered_locations.user_id = ? OR discovered_locations.id is null", user.id] )
end
self is actually the BossLocation model. I want to get a result set of the bosslocation and the discovered location IF that location was discovered by my user. However, if it was not discovered, i still need the bosslocation and no object as a discovered location. With the above code, if the user has not discovered anything, i don't get the bosslocations at all.
EDIT :
My associations are like :
class BossLocation < ActiveRecord::Base
has_many :discovered_locations
has_many :users, :through => :discovered_locations
class DiscoveredLocation < ActiveRecord::Base
belongs_to :user
belongs_to :boss_location
class User < ActiveRecord::Base
has_many :discovered_locations
has_many :boss_locations, :through => :discovered_locations
I think the problem is that you specify the user_id in the where conditions and not in the join condition. Your query will only give you the BossLocation if the user has discovered it or if no user at all has discovered it.
To make the database query match your need, you could change the include to the following joins:
:joins => "discovered_locations ON discovered_locations.boss_location_id = boss_locations.id
AND discovered_locations.user_id = '#{user.id}'"
BUT, I don't think it would help that much since the eager loading of Rails will not work when using joins like this instead of include.
If I where to do something similar, I would probably split it up. Perhaps by adding associations for user like this:
class User < ActiveRecord::Base
has_many :disovered_locations
has_many :discovered_boss_locations, :through => :discovered_locations
Update:
That way, in your Controller you can get all BossLocations and all discovered BossLocations like this:
#locations = BossLocation.all
#discovered = current_user.discovered_locations.all.group_by(&:boss_location_id)
To use these when you loop through them, do something like this:
<% #locations.each do |location| %>
<h1><%= location.name %></h1>
<% unless #discovered[location.id].nil? %>
<p>Discovered at <%= #discovered[location.id].first.created_at %></p>
<% end %>
<% end %>
What this does is it groups all discovered locations into a hash where the key is the boss_location_id. So when you loop through all boss_locations, you just see if there is an entry in the discovered hash that matches the boss_id.
"a result set of the bosslocation and the discovered location IF that location was discovered by my user."This is not a left outer join.You will get all bosslocations anytime.So,your conditions are wrong!This will get the bosslocation that it's discovered_locations.user_id = user.id OR discovered_locations.id is null".In this condition, this may be difficult for one sql statement. Also you can use union in your find_by_sql,but i suggest you use two find function.

Resources