Getting Private Method error in Ruby on Rails - ruby-on-rails

I have a controller:
class StatsController < ApplicationController
require 'time'
def index
#started = "Thu Feb 04 16:12:09 UTC 2010"
#finished = "Thu Feb 04 16:13:44 UTC 2010"
#duration_time = stats_duration(#started, #finished)
end
private
def stats_duration(started, finished)
time_taken = distance_of_time_in_words(Time.parse(started), Time.parse(finished))
time_taken
end
end
It takes in a start and end time and calculates the duration between the times.
When I run this I get the following error:
private method `gsub!' called for Thu
Feb 04 16:12:09 UTC 2010:Time
Why is this happening?

private method gsub! called when using Time.parse usually means that you have called parse with a Time object rather than a String so it sounds like your code is actually trying to parse the time twice.
e.g.
>> t = Time.now
=> Fri Feb 05 13:12:17 +0000 2010
>> Time.parse(t)
NoMethodError: private method `gsub!' called for Fri Feb 05 13:12:17 +0000 2010:Time
from c:/ruby/lib/ruby/1.8/date/format.rb:965:in `_parse'
from c:/ruby/lib/ruby/1.8/time.rb:240:in `parse'
from (irb):6

Related

Converting time zone of Rails ActiveRecord timestamps

I extended the Time class in my Rails projects so I can easily get the time in NYC:
/lib/extensions.rb .
class Time
# Get NYC time:
def nyc
self.in_time_zone('Eastern Time (US & Canada)')
end
end
Testing it out, looks good:
time_a = Time.now.utc.nyc
=> Sun, 21 Apr 2019 18:42:12 EDT -04:00
The problem is when I pull timestamps from the DB:
time_b = object.created_at.in_time_zone('Eastern Time (US & Canada)')
=> Sun, 21 Apr 2019 17:22:04 EDT -04:00
time_c = object.created_at.nyc
=> Sun, 21 Apr 2019 17:22:04 UTC +00:00
Super confused. Converting the timestamp to EDT works when I use in_time_zone in the console, but not when I use the extension? Even though my extension method works on Time objects I create in console? What's happening here?
(Note: Time instances in Rails are in fact instances of ActiveSupport::TimeWithZone. "TimeWithZone instances implement the same API as Ruby Time instances, so that Time and TimeWithZone instances are interchangeable." - ActiveSupportTimeWithZone)
you would need to patch ActiveSupport::TimeWithZone instead of Time, e.g.
class ActiveSupport::TimeWithZone
def nyc
in_time_zone('Eastern Time (US & Canada)')
end
end
Time.zone.now.nyc # => Mon, 22 Apr 2019 06:44:41 EDT -04:00
User.last.created_at.nyc # => Sun, 21 Apr 2019 13:34:45 EDT -04:00
https://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html
(edit: I previously said "DateTime" instead of "ActiveSupport::TimeWithZone")

Take date from Form Data but Time from Actual Time

In one Form the user also has to specify a date for example the 21.04.2014:
So when a post request is sent to the rails-application, to save a post there is also provided a created_at data in the request:
post[text]:"First Message"
post[created_at]:21.04.2014
So at the end a message is saved. Now when i try to get the created_at for the message:
Message.find(1).created_at
>> 2014-04-21 00:00:00
But i would like that the time corresponds to the actual time when the message was created, so the ouput must look something like this:
>> 2014-04-21 19:52:07
How do i have to save the message so that it meets my concept? Thanks
Actual i save a message like this:
#user.message.new(message_params)
def diagnosis_params
params[:message].permit(:created_at, :text)
end
You can do something like this:
dt = Date.parse('21.04.2014')
# => Mon, 21 Apr 2014
tm = Time.zone.now
# => Tue, 22 Apr 2014 00:34:07 MSK +04:00
created_at = DateTime.new(dt.year, dt.month, dt.day, tm.hour, tm.min, tm.sec)
# => Mon, 21 Apr 2014 00:34:07 +0000
Not very elegant but you can create some utility method...

ActiveSupport::JSON::Encoding::CircularReferenceError in content_tag

I have my User model with the following functions:
def self.chart_data(start = 1.weeks.ago)
total_users = users_by_day(start)
(start.to_date..Date.today).map do |date|
{
created_at: date,
users: total_users[date] || 0
}
end
end
def self.users_by_day(start)
users = where(created_at: start.beginning_of_day..Time.zone.now)
users = users.group("date(created_at), id")
users = users.select("created_at, count(id) as total_users")
users.each_with_object({}) do |user, total_users|
total_users[user.created_at.to_date] = total_users
end
end
And my View has the following line:
<%= content_tag :div, "", id: "users_chart", data: {users: User.chart_data} %>
I get the following error:
ActiveSupport::JSON::Encoding::CircularReferenceError in Static#statistics
Showing statistics.html.erb where line #5 raised:
object references itself
When i i just print the data it looks as the following:
[{:created_at=>Wed, 27 Nov 2013, :users=>0}, {:created_at=>Thu, 28 Nov 2013, :users=>0}, {:created_at=>Fri, 29 Nov 2013, :users=>0}, {:created_at=>Sat, 30 Nov 2013, :users=>0}, {:created_at=>Sun, 01 Dec 2013, :users=>0}, {:created_at=>Mon, 02 Dec 2013, :users=>0}, {:created_at=>Tue, 03 Dec 2013, :users=>{Tue, 03 Dec 2013=>{...}}}, {:created_at=>Wed, 04 Dec 2013, :users=>0}]
The only user is on 03 Dec, if there are no users there is also no error.
As you'll note in the printed data, there is a recursive reference on the 3rd:
{:created_at=>Tue, 03 Dec 2013, :users=>{Tue, 03 Dec 2013=>{...}}}
The origins come from how users_by_day is constructed. The has building references itself, by assigning an object on a given day to itself:
users.each_with_object({}) do |user, total_users|
total_users[user.created_at.to_date] = total_users
end
I think this is just an oversight in variable naming. The aggregate object you are creating is called total_users by the block, and it's the same name you give it inside the query. I believe the correct block would be this:
users.each_with_object({}) do |user, total_users|
total_users[user.created_at.to_date] = user.total_users
end
Since you need to get the total off the user variable.

My testing database leaves two days into the past and doesn't care for rescue

There are two issues here:
I have this code to publish on users' facebook walls:
def publish_on_facebook
if user.last_published < 1.day.ago
#graph = Koala::Facebook::API.new(user.service.token)
begin
#graph.put_wall_post("some post")
user.last_published = Time.now
user.save
rescue
user.last_published = 1.week.from_now
user.save
end
end
end
It works perfectly:
if the user has authorized me, it publishes and updates the last_published (type datetime) field to now.
If the user has not authorized me to publish stuff in his wall, then it updates the last_published field to 1 week from now.
Now when I run in through my Cucumber, testing, it doesn't work:
When the user has authorized me, the last_published field updates to 1 minute from now, but 2 days ago
expected: > Sun, 03 Mar 2013 16:12:44 UTC +00:00
got: Fri, 01 Mar 2013 16:13:43 UTC +00:00
When the user hasn't authorized me, no change on the last_published field (i set the default value of the field to march 1st)
expected: > Sat, 09 Mar 2013 16:13:47 UTC +00:00
got: Fri, 01 Mar 2013 15:01:11 UTC +00:00
Any ideas?
When updating attributes, the variable #user must be reloaded:
#user.update_attributes(attribute: value)
#user = User.find(#user)
Cucumber was evaluating the values stored in #user, which were not up to date after the method ran.

Is it possible to create a list of months between two dates in Rails

I am trying to create a page to display a list of links for each month, grouped into years. The months need to be between two dates, Today, and The date of the first entry.
I am at a brick wall, I have no idea how to create this.
Any help would be massively appriciated
Regards
Adam
Just put what you want inside a range loop and use the Date::MONTHNAMES array like so
(date.year..laterdate.year).each do |y|
mo_start = (date.year == y) ? date.month : 1
mo_end = (laterdate.year == y) ? laterdate.month : 12
(mo_start..mo_end).each do |m|
puts Date::MONTHNAMES[m]
end
end
The following code will add a months_between instance method to the Date class
#!/usr/bin/ruby
require 'date'
class Date
def self.months_between(d1, d2)
months = []
start_date = Date.civil(d1.year, d1.month, 1)
end_date = Date.civil(d2.year, d2.month, 1)
raise ArgumentError unless d1 <= d2
while (start_date < end_date)
months << start_date
start_date = start_date >>1
end
months << end_date
end
end
This is VERY lightly tested, however it returns an Array of dates each date being the 1st day in each affected month.
I don't know if I've completely understood your problem, but some of the following might be useful. I've taken advantage of the extensions to Date provided in ActiveSupport:
d1 = Date.parse("20070617") # => Sun, 17 Jun 2007
d2 = Date.parse("20090529") #=> Fri, 29 May 2009
eom = d1.end_of_month #=> Sat, 30 Jun 2007
mth_ends = [eom] #=> [Sat, 30 Jun 2007]
while eom < d2
eom = eom.advance(:days => 1).end_of_month
mth_ends << eom
end
yrs = mth_ends.group_by{|me| me.year}
The final line uses another handy extension: Array#group_by, which does pretty much exactly what it promises.
d1.year.upto(d2.year) do |yr|
puts "#{yrs[yr].min}, #{yrs[yr].max}"
end
2007-06-30, 2007-12-31
2008-01-31, 2008-12-31
2009-01-31, 2009-05-31
I don't know if the start/end points are as desired, but you should be able to figure out what else you might need.
HTH
Use the date_helper gem which adds the months_between method to the Date class similar to Steve's answer.
xmas = Date.parse("2013-12-25")
hksar_establishment_day = Date.parse("2014-07-01")
Date.months_between(xmas,hksar_establishment_day)
=> [Sun, 01 Dec 2013, Wed, 01 Jan 2014, Sat, 01 Feb 2014, Sat, 01 Mar 2014, Tue, 01 Apr 2014, Thu, 01 May 2014, Sun, 01 Jun 2014, Tue, 01 Jul 2014]

Resources