Override method/variable in a gem rails

463 views Asked by At

I am trying to find a way to override a variable in a rails gem actionpack/http/parameters.rb. I have to process an ndjson stream and the rails middleware cannot process ndjson. It uses ActiveSupport::JSON.decode

This is the source of the code below

  DEFAULT_PARSERS = {
    Mime[:json].symbol => -> (raw_post) {
      data = ActiveSupport::JSON.decode(raw_post)
      data.is_a?(Hash) ? data : { _json: data }
    }
  }

I get this error when it tries to parse ndjson

ActionDispatch::Http::Parameters::ParseError in MyController#activity

My objective is to override the parser to allow it to decode the ndjson... Potentially using split(\n) as opposed to the current ActiveSupport::JSON.decode.

So far i have tried creating a file in lib/ folder and using the following code but it doesn't seem to do the override. How do i do this without Monkey patching

require 'lib/action_dispatch/http/parameters'

module MyParser
  module Overrides
    extend ActiveSupport::Concern

    DEFAULT_PARSERS = {
        Mime[:json].symbol => -> (raw_post) {
          data = raw_post.split("\n")
          data = ActiveSupport::JSON.decode(data)
          data.is_a?(Hash) ? data : { _json: data }
        }
    }

  end
end

ActionDispatch::Http::Parameters.include(MyParser::Overrides)

UPDATE:

The second approach i tried:

ActionDispatch::Http::Parameters.const_set(:DEFAULT_PARSERS, {
  Mime[:json].symbol => -> (raw_post) {
    data = raw_post.split("\n")
    data = ActiveSupport::JSON.decode(data)
    data.is_a?(Hash) ? data : { _json: data }
  },
})

Unfortunately it keeps warning me that the constant is already defined.

1

There are 1 answers

0
Ndeto On

I took another approach. Instead of reinitializing the constant, i created a file in config/initializers/custom_params.rb to override the method parse_formatted_parameters (here) that was using the DEFAULT_PARSERS variable. From within i was able to change the Proc value for the json data type.

This conveniently overrides the method and allows the ActionDispatch::Http::Parameters module to pass the ndjson to my controller without any parsing errors.

module ActionDispatch
  module Http
    module Parameters
      extend ActiveSupport::Concern

      private

      def parse_formatted_parameters(parsers)
        parsers[Mime[:json].symbol] = Proc.new { |raw_post|
            data = ActiveSupport::JSON.decode(raw_post) rescue nil
            if !data
              data = raw_post
            end
            data.is_a?(Hash) ? data : { _json: data }
          }

        return yield if content_length.zero? || content_mime_type.nil?
        # rubocop:enable all

        strategy = parsers.fetch(content_mime_type.symbol) { return yield }

        begin
          strategy.call(raw_post)
        rescue # JSON or Ruby code block errors.
          my_logger = logger || ActiveSupport::Logger.new($stderr)
          my_logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{raw_post}"

          raise ParseError
        end
      end
    end
  end
end