Reputation: 3
I'm quite new to rails so I'm not really familiar with most of its methods etc.
I have this table in my database called "logs" with columns "date" (date) and "hours" (time). I wanted to get all the logs with the same date and then get their total hours converted to seconds.
Let's say I have two existing logs:
Since they have the same date, I'm expecting to get the sum of "01:00" and "04:00" both converted to seconds.
My log model so far contains this:
scope :on_this_day, ->(date) { where(date: date) }
and I used it for this which is in my service:
@log = Log.new(@log_params)
total = Log.on_this_day(@log.date).sum(Time.parse(:hours).utc.seconds_since_midnight) ==> # supposedly the total hours (in seconds) on a particular day
Right now the error is telling me there is no implicit conversion of Symbol into String. I'm guessing it's because of using ":hours" inside Time.parse().
I don't know what's a good approach for this. I'd appreciate some help. Thank you!
Upvotes: 0
Views: 461
Reputation: 1309
OK, so there's quite a lot going on here. First of all, you don't need to add Date and Hours fields to your model, because all models automatically get created_at
and updated_at
fields added (at least, if you follow the recommended way to write your migrations), and it's best to use these.
Whichever field you use to get the creation time, your scope looks fine. The issue, as you saw, is in how you use it. Scopes return an ActiveRecord::Relation
object, which is a collection of records. So, your Log.on_this_day(@log.date)
call will return a collection, and you can't just sum
a collection. You need to work with an individual field from each record in the collection.
Then, you're not using Time.parse
correctly. This expects a text string that encodes a date and returns a Time
object. You're passing a symbol (:hours
), which is therefore raising an error. So, you would need to do something like Time.parse('2021-08-15')
. One way to debug this sort of command is to use it at the console (rails c
) so you can get a feel for how it works.
Ultimately, you're going to need to break this down a bit, and do something like this:
total = Log.on_this_day(@log.date).reduce do |sum, log_record|
sum + Time.parse(log_record.hours.to_s).utc.seconds_since_midnight
end
(If you're not familiar with the #reduce
method yet, it iterates over an enumerable like an array or collection, and reduces it to a single value (here, a sum) based on the calculation provided in the block, which is carried out on each record. You can do something similar with #each
but it's more complicated.)
But, even then, you're over-complicating things because, if you use a field that has a Time type like created_at
, you don't need to parse it at all:
total = Log.on_this_day(@log.date).reduce do |sum, log_record|
sum + log_record.created_at.utc.seconds_since_midnight
end
This takes the collection returned by your scope from the Log table, and then iterates over each log_record
within that collection. It takes the created_at
value and adds to the sum (initially zero) the number of seconds since midnight, and then moves to the next record and adds its number of seconds, and so on.
Hopefully, that gets you where you need to be! Basically, try out the individual steps at the console to make sure you understand what they're doing before combining them, and you'll probably find it easier to get things working.
Upvotes: 0