Sanjay
Sanjay

Reputation: 111

Rails: Design pattern to modify params based on users type before passing on to model

There are mulitple controller actions that receive startDate as a parameter and pass it on to relevant models to fetch the data. Based on a user property, there are validation rules that has to be applied on this parameter. Validation wouldn't just fail, but instead assigns a default value specific to that user property.

The modification required on startDate is common across all the actions and should be applied in all the cases. So, i assume this logic shouldn't be repeated at each model. Instead, controller's before_action filter, something like filtered_params sounds the right place to do it.

before_action :filtered_params

def filtered_params
   params.require(:query).permit(:start_date)
   user = User.instance
   if(user.type == 'student') {} # startDate should be Greater than or equal to 12-03-2018. modify params.startDate with the logic
   elsif(user.type == 'professor') {} // startDate should be Greater than equal to 01-01-2018
   else {} // do nothing
   end
end

The above approach works, but i do not want to get stuck in if else loop. Is there a better way to do it ?

Upvotes: 0

Views: 327

Answers (2)

jvillian
jvillian

Reputation: 20263

Perhaps you could do something like:

before_action :filtered_params

def filtered_params
  @filtered_params ||= get_filtered_params
end

def get_filtered_params
  # not sure about this user assignment, but let's go with it...
  user = User.instance
  begin
    "ParamsFilter::#{user.type.camelize}Service".constantize.call params
  rescue NameError => e
    # I'm guessing at the default behavior here. You would need to 
    # modify to meet your requirements.
    return params.require(:query).permit(:start_date)
  end
end

Then you would need something like:

app
 |- services
 |   |- params_filter
 |   |   |- service_base.rb
 |   |   |- student_service.rb
 |   |   |- professor_service.rb
 |   |- service_base.rb

And a service might look something like

class ParamsFilter::StudentService < ParamsFilter::ServiceBase

  MIN_START_DATE = '12-03-2018'

  #======================================================================
  # Instance Methods
  #======================================================================

    def call
      # In this case, given how ServiceBase is defined, the params will 
      # be received as `args`, so you'll need to work with the `args` 
      # variable, which will contain `params`.

      # You could do stuff unique to `student`:
      student_stuff

      # and then do some common stuff by calling `super`.
      super

    end

  private

    def student_stuff
    end

end

Where ParamsFilter::ServiceBase might look something like:

class ParamsFilter::ServiceBase < ServiceBase

  #======================================================================
  # Instance Methods
  #======================================================================

    def call
      args[:start_date] = self.class::MIN_START_DATE unless start_date_okay?
    end

  private 

    def start_date_okay?
      args[:start_date] >= self.class::MIN_START_DATE
    end

end

Where service_base.rb might look something like:

class ServiceBase

  attr_accessor *%w(
    args
  ).freeze

  class << self

    def call(args=nil)
      new(args).call
    end

  end # Class Methods

  #======================================================================
  # Instance Methods
  #======================================================================

  def initialize(args)
    @args = args
  end

end

Upvotes: 3

Isaiah Fasoldt
Isaiah Fasoldt

Reputation: 190

As Dave notes, this is odd behavior. However, if you must alter the start_date, consider using a before_validation callback. It's much safer/cleaner NOT to alter the incoming params, but do it at the model level.

class User

MINIMUM_STUDENT_START_DATE = '12-03-2018'
MINIMUM_PROFESSOR_START_DATE = '01-01-2018'

before_validation :set_start_date

def set_start_date
  if type == 'student' && start_date < MINIMUM_STUDENT_START_DATE
    self.start_date = MINIMUM_STUDENT_START_DATE
  elsif type == 'professor' && start_date < MINIMUM_PROFESSOR_START_DATE
    self.start_date = MINIMUM_PROFESSOR_START_DATE
  end
end

Upvotes: 0

Related Questions