TL;DR: Rails 5.1, Ruby 2.4.0 is serialising a hash including Time objects with quotes around the string representation of the time. These quotes weren't there in Rails 2.3, Ruby 1.8.7 and break my app; how do I get rid of them?
Context and details
I'm upgrading an app from Rails 2.3, Ruby 1.8.7 to Rails 5.1, Ruby 2.4.0.
I have a ReportService
class, which has a report_params
constructor argument which takes a hash. Upon creation of these objects, this hash gets serialised in YAML format.
class ReportService < ApplicationRecord
# irrelevant AR associations omitted
serialize :report_params
serialize :output_urls
end
A user submits a form containing details of a report they want to be run, including a string that gets parsed using Time.parse(), which gets passed as a constructor argument; so the code (in procedural form to strip out irrelevant details, with lots of extraneous stuff ommitted) looks like
offset = customer.timezone.nil? ? '+0000' : customer.timezone.formatted_offset(:time => start_date)
params[:date_from] = Time.parse("#{start_date} #{params[:hour_from]}:{params[:min_from]} #{offset}").utc.strftime('%Y-%m-%d %H:%M:%S')
report_args = {...
report_params: { ...
date: params[:date_from]
}
}
ReportService.create(report_args)
When I look in my MYSQL database, I find that my report_params field looks like ... date_from: '2017-12-27 00:00:00' ...
. The corresponding code in the old version produces a result that looks like ... date_from: 2017-12-27 00:00:00 ...
. This is a bad thing, because the YAML in that field is getting parsed by a (legacy) Java app that polls the database to check for new entries, and the quotes seem to break that deserialisation (throwing java.lang.Exception: BaseProperties.getdate()
); if I manually edit the field to remove the quotes, the app works as expected. How can I prevent these quotation marks from being added?
Rails5.1/Ruby2.4 do it correct, since 2017-12-27 00:00:00
is not a valid yaml value.
The good thing is serialize
accepts two parameters, the second one being a serializer class.
So, all you need to do is:
class ReportService < ApplicationRecord
# irrelevant AR associations omitted
serialize :report_params, MyYaml
serialize :output_urls, MyYaml
end
and implement MyYaml
, delegating everything, save for date
/time
to YAML
and producing whatever you need for them.
The above is valid for any format of serialized data, it’s completely format-agnostic. Examples.