What programming patterns or strategy should I use to deal with small inconsistencies in data processing?

50 views Asked by At

In the ruby gem I am writing I have to take in as input certain known query parameters and massage them into a query string and then use that constructed (url) string as a rest endpoint to retrieve that data.

Now there are some weird inconsistencies in inputs coming in and I am forking my code to normalize inputs into a consistent output.

   def build_query(params, endpoint)
      limit      = Hash[limit:      params[:limit]   ||  0]
      skip       = Hash[skip:       params[:skip]    ||  0]
      asc        = Hash[asc:        params[:asc]     || ""]
      desc       = Hash[desc:       params[:desc]    || ""]

      query = [limit, skip, asc, desc].select { |hash| hash.values.none? { |val| val == '' || val == 0 } }
      encoded = query.map{ |q| q.to_query }.join("&")

      references = build_references(params[:include]) || ""

      query_string = references.empty? ? "#{endpoint}#{encoded}" : "#{endpoint}#{references}&#{encoded}"
    end

You will see above that the references piece of the params are not handled the same way as the rest of the parameters. There are more slightly inconsistent edge cases coming soon. And the only way I know how to deal with these is to keep forking my code inside this function. It's going to get messy soon!

So how should I now refactor this code? Where should I go from here to manage this complexity? Should I use collaborating objects (ParamsBuilder or QueryManager) and some kind of polymorphism strategy?

I would like to keep my code simple and functional as much as possible.

1

There are 1 answers

1
Aleksei Matiushkin On BEST ANSWER
plain = %i|limit skip asc desc| # plain values
built = { include: ->(input) { build_references(input) } } # procs

query = (plain | built).map do |p|
  case p
  when Symbol then params[p]
  when Array  then p.last.(params[p.first])
  end
end.reject(&:blank?).map(&:to_query).join("&")

[endpoint, query].join

Basically, you have two types of parameters: those you are to pass through as is (like :limit,) and those, you are to transform (like :include.)

Former are just passed through, latter are transformed using the list of lambdas specified in the very beginning of this snippet.

Since you were using to_query in the original question, I suggest you use rails, hence you have blank? method on hand and there is no need to explicitly check for empty strings and/or zeroes.

In the last step, we reject blanks and join everything with an ampersand.