How do I pick the last and the second to last entries in a blog model? - ruby-on-rails-3

I have a model, blog_posts which has a field "published_at". I'd like to select the latest two blogs from that model to display on my homepage. Not sure how to structure that though.
At the moment I have a work around that takes a slice of the data but it keeps failing when I have nothing in the table, rather than fix this I feel there is probably a better way to retrieve the data.
I need to select the blogs in separate calls, for example
#blog_post.latestpost, #blog_post.secondlatestpost

Is this what you're looking for? :
class BlogPost < Activerecord::Base
def self.latestpost
order("published_at DESC").limit(1).first
end
def self.secondlatestpost
order("published_at DESC").offset(1).limit(1).first
end
end
Use it like this :
BlogPost.secondlatestpost
or
BlogPost.latestpost
Hope this helps.

You could also do:
BlogPost.order(:published_at).last # for the last post
BlogPost.order(:published_at).offset(1).last # for the second to last post

As of Rails 5 you can use BlogPost.second_to_last.
http://api.rubyonrails.org/v5.0/classes/ActiveRecord/FinderMethods.html#method-i-second_to_last

At least since Rails 4.x you can specify how many last records you want. E.g. to fetch last 2 records:
BlogPost.last(2) # BlogPost.last(2).first will return the second last post
The same goes for first(). There are also methods second(), third(), fourth(), fifth(), forty_two(). And starting with Rails 5.x, second_to_last(), third_to_last().

You could also use ActiveRecord
BlogPost.order("published_at DESC").second
Although I think going with offset and limit is the somewhat cleaner and more portable version. Internally second (and third, fourth) use the find_nths method. Documented here:
http://apidock.com/rails/ActiveRecord/FinderMethods/second

Related

How can you use distinct in rails while still using ActiveRecord's

I am struggling with the following problem:
I want to have two different tabs, one that displays all recent chugs (Done), and one that displays the chugs that are the fastest per person.
However, this needs to remain an ActiveRecord, since I need to use it with link_to and gravatar, thus restraining me from group_by, as far as I understand it.
AKA: If there are three users who each have three chugs, I want to show 1 chug per user, which contains the fastest time of that particular user.
The current code looks like this, where chugs_unique should be edited:
def show
#pagy, #chugs_all_newest = pagy(#chugtype.chugs.order('created_at DESC'), items: 10, page: params[:page])
#chugs_unique = #chugtype.chugs.order('secs ASC, milis ASC, created_at DESC').uniq
breadcrumb #chugtype.name, chugtypes_path(#chugtype)
end
In this case, a chug belongs to both a chugtype and user, and the chugtype has multiple chugs.
Thanks in advance!

Rails Order by frequency of a column in another table

I have a table KmRelationship which associates Keywords and Movies
In keyword index I would like to list all keywords that appear most frequently in the KmRelationships table and only take(20)
.order doesn't seem to work no matter how I use it and where I put it and same for sort_by
It sounds relatively straight forward but i just can't seem to get it to work
Any ideas?
Assuming your KmRelationship table has keyword_id:
top_keywords = KmRelationship.select('keyword_id, count(keyword_id) as frequency').
order('frequency desc').
group('keyword_id').
take(20)
This may not look right in your console output, but that's because rails doesn't build out an object attribute for the calculated frequency column.
You can see the results like this:
top_keywords.each {|k| puts "#{k.keyword_id} : #{k.freqency}" }
To put this to good use, you can then map out your actual Keyword objects:
class Keyword < ActiveRecord::Base
# other stuff
def self.most_popular
KmRelationship.
select('keyword_id, count(keyword_id) as frequency').
order('frequency desc').
group('keyword_id').
take(20).
map(&:keyword)
end
end
And call with:
Keyword.most_popular
#posts = Post.select([:id, :title]).order("created_at desc").limit(6)
I have this listed in my controller index method which allows the the order to show the last post with a limit of 6. It might be something similar to what you are trying to do. This code actually reflects a most recent post on my home page.

Update more record in one query with Active Record in Rails

Is there a better way to update more record in one query with different values in Ruby on Rails? I solved using CASE in SQL, but is there any Active Record solution for that?
Basically I save a new sort order when a new list arrive back from a jquery ajax post.
#List of product ids in sorted order. Get from jqueryui sortable plugin.
#product_ids = [3,1,2,4,7,6,5]
# Simple solution which generate a loads of queries. Working but slow.
#product_ids.each_with_index do |id, index|
# Product.where(id: id).update_all(sort_order: index+1)
#end
##CASE syntax example:
##Product.where(id: product_ids).update_all("sort_order = CASE id WHEN 539 THEN 1 WHEN 540 THEN 2 WHEN 542 THEN 3 END")
case_string = "sort_order = CASE id "
product_ids.each_with_index do |id, index|
case_string += "WHEN #{id} THEN #{index+1} "
end
case_string += "END"
Product.where(id: product_ids).update_all(case_string)
This solution works fast and only one query, but I create a query string like in php. :) What would be your suggestion?
You should check out the acts_as_list gem. It does everything you need and it uses 1-3 queries behind the scenes. Its a perfect match to use with jquery sortable plugin. It relies on incrementing/decrementing the position (sort_order) field directly in SQL.
This won't be a good solution for you, if your UI/UX relies on saving the order manually by the user (user sorts out the things and then clicks update/save). However I strongly discourage this kind of interface, unless there is a specific reason (for example you cannot have intermediate state in database between old and new order, because something else depends on that order).
If thats not the case, then by all means just do an asynchronous update after user moves one element (and acts_as_list will be great to help you accomplish that).
Check out:
https://github.com/swanandp/acts_as_list/blob/master/lib/acts_as_list/active_record/acts/list.rb#L324
# This has the effect of moving all the higher items down one.
def increment_positions_on_higher_items
return unless in_list?
acts_as_list_class.unscoped.where(
"#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
).update_all(
"#{position_column} = (#{position_column} + 1)"
)
end

Math with datetime in SQL query in rails

So I have an table of phone_numbers in Rails 3.2, and I would like to be able to make a query such as the following:
PhoneNumber.where("last_called_at - #{Time.now} > call_spacing * 60")
where last_called_at is a datetime and call_spacing is an integer, representing the number of minutes between calls.
I have tried the above, I have also tried using
PhoneNumber.where("datediff(mi, last_called_at, #{Time.now}) > call_spacing")
If anyone could help me make this work, or even recommend a current, up-to-date rails SQL gem, I would be very grateful.
Edit: So the best way to make this work for me was to instead add a ready_at column to my database, and update that whenever either call_spacing or last_called_at was updated, using the before_save method. This is probably the most semantic way to approach this problem in rails.
You have to use quotes around #{Time.now}. To make your first query work you may use TIME_TO_SEC() function:
PhoneNumber.where("(TIME_TO_SEC('#{Time.now}') - TIME_TO_SEC(last_called_at)) > (call_spacing * 60)")
Here is my way to do this:
PhoneNumber.where("last_called_at > '#{Time.zone.now.utc - (call_spacing * 60)}'")
also take a look at this article to be aware of how to play with timezones:
http://danilenko.org/2012/7/6/rails_timezones/

Arel: Left outer join using symbols

I have this use case where I get the symbolized deep associations from a certain model, and I have to perform certain queries that involve using outer joins. How can one do it WITHOUT resorting to write the full SQL by hand?
Answers I don't want:
- using includes (doesn't solve deep associations very well ( .includes(:cars => [:windows, :engine => [:ignition]..... works unexpectedly ) and I don't want its side-effects)
- writing the SQL myself (sorry, it's 2013, cross-db support, etc etc..., and the objects I fetch are read_only, more side-effects)
I'd like to have an Arel solution. I know that using the arel_table's from the models I can construct SQL expressions, there's also a DSL for the joins, but somehow i cannot use it in the joins method from the model:
car = Car.arel_table
engine = Engine.arel_table
eng_exp = car.join(engine).on(car[:engine_id].eq(engine[:id]))
eng_exp.to_sql #=> GOOD! very nice!
Car.joins(eng_exp) #=> Breaks!!
Why this doesn't work is beyond me. I don't know exactly what is missing. But it's the closest thing to a solution I have now. If somebody could help me completing my example or provide me with a nice work-around or tell me when will Rails include such an obviously necessary feature will have my everlasting gratitude.
This is an old question, but for the benefit of anyone finding it through search engines:
If you want something you can pass into .joins, you can either use .create_join and .create_on:
join_on = car.create_on(car[:engine_id].eq(engine[:id]))
eng_join = car.create_join(engine, join_on, Arel::Nodes::OuterJoin)
Car.joins(eng_join)
OR
use the .join_sources from your constructed join object:
eng_exp = car.join(engine, Arel::Nodes::OuterJoin).on(car[:engine_id].eq(engine[:id]))
Car.joins(eng_exp.join_sources)
I found a blog post that purports to address this problem: http://blog.donwilson.net/2011/11/constructing-a-less-than-simple-query-with-rails-and-arel/
Based on this (and my own testing), the following should work for your situation:
car = Car.arel_table
engine = Engine.arel_table
sql = car.project(car[Arel.star])
.join(engine, Arel::Nodes::OuterJoin).on(car[:engine_id].eq(engine[:id]))
Car.find_by_sql(sql)
If you don't mind adding a dependency and skipping AREL altogether, you could use Ernie Miller's excellent Squeel gem. It would be something like
Car.joins{engine.outer}.where(...)
This would require that the Car model be associated with Engine like so:
belongs_to :engine