Rails validating search params
I have an API which is fairly restful but am struggling to work out how to implement a search cleanly. I want to be able to search for all the records between two date-times, the date-times are allowed to be a maximum of 6 hours apart. At the moment in my controller method I have the following:
required_params = [:start_time, :end_time] if check_required_params(required_params, params) and check_max_time_bound(params, 6.hours) ... rest of controller code here ... end
check_required_params is an application method that looks like this:
def check_required_params(required_params, params_sent) required_params.each do |param| unless has_param(param, params_sent) unprocessable_entity return false end end true end
check_max_time is fairly similar.
I know it's against best practices to do validation in the controller but I can't see how I can add it to the model cleanly.
Actually what you are doing is (almost) best practice and will (almost) be incorporated in Rails 4 with strong parametsers. (I say almost because your check_max_time looks like it should be a validation in your model.)
You should go ahead and pull in the feature today and make upgrades easier on yourself. Strong Parameters https://github.com/rails/strong_parameters
Documentation is there, but here is how you incorporate it.
class SearchController < ApplicationController include ActiveModel::ForbiddenAttributesProtection def create # Doesn't have to be an ActiveRecord model @results = Search.create(search_params) respond_with @results end private def search_params # This will ensure that you have :start_time and :end_time, but will allow :foo and :bar params.require(:start_time, :end_time).permit(:foo, :bar #, whatever else) end end class Search < ActiveRecord::Base validates :time_less_than_six_hours private def time_less_than_six_hours errors.add(:end_time, "should be less than 6 hours from start") if (end_time - start_time) > 6.hours end end
Never found a clean answer for this. However if you're making an API Grape has inbuilt Parameter Validation and Coercion to take care of it.
Well, what I would do in this scenario is the set the default value between these two datetimes so that i won't have to do validation and raise the exception.
class SearchController < ApplicationController before_filter :assign_default_params def index end private def assign_default_params params[:start_time] ||= Time.now params[:end_time] ||= params[:start_time] + 6.hours params[:end_time] = params[:start_time] + 6.hours if ((params[:end_time] - params[:start_time]) / 3600).round) > 6 end end
With this code above, it always has the params required to the search. The method assign_default_params try to assign default values if they are not sent from the clients. The last thing it does is that it assign params[:end_time] to a maximum value.
It's much neater because we don't have to do validation and the client won't need to handle different response code such as 422. And you should have an API documentation, stating about this fact as well.