In a Sinatra app, I have many routes that use a date. They are all formatted:
get '/foo/:bar/:year/:month' do
# code
end
I want to create a before hook setting a requested date according to the route params. This shouldn't run if the route doesn't have month
and year
params.
I tried this:
before do
if params[:year].any? && params[:month].any?
@requested_date = Date.new(params[:year].to_i, params[:month].to_i, 01)
end
end
and this:
before do
if defined?(params[:year]) && defined?(params[:month])
@requested_date = Date.new(params[:year].to_i, params[:month].to_i, 01)
end
end
But I keep running into the same error: Date::Error - invalid date:
CodePudding user response:
To reliably check if params
hash have year/month keys you can use Hash#key?, smth. like:
before do
if params.key?(:year) && params.key?(:month)
<set date>
end
end
But there is still a problem. The route '/foo/:bar/:year/:month'
will match things like /foo/bar/baz/qux
with Sinatra router resolving params to { bar: 'bar', year: 'baz', month: 'qux'}
.
So you still cannot just feed the params to Date
constructor and expect it to give a valid date for you. In simplest case you can just write a helper method like
def build_date(year, month)
Date.new(year.to_i, month.to_i, 01)
rescue Date::Error
# So what now?
end
and use it in your before block, but another question arise - what to do in case of an error? The easiest solution is to just respond with 404, but you might need something more sophisticated (for example, to communicate the invalid date format to the user).
Another thing to mention is Sinatra's capability to match routes using regexps: for example, you could force Sinatra router to recognize only routes that contain 4 digits for year and integers in range 1-12 for month. I'd probably avoid it (makes routes harder to reason about, also accessing the matched params becomes a bit cluttered), but still a good thing to remember about...