简体   繁体   中英

Are the contents of 'methods' option on Active Model's 'as_json' method ignored when undefined?

I have a model model , and am trying to render that with as_json . I am calling the :methods option on it like this:

model.as_json(methods: [:foo, :bar])

The output includes a key-value pair for the method foo , but not for bar . It might be the case that bar is not defined correctly while foo is not, I am not sure.

In such case, would as_json silently ignore the application of the undefined methods? If so, is there a way to be notified by an error? Or, could there be any other reason that particular key-value pairs become silently ignored from the as_json output?

I am using Active Model 4.2.7.

An option is override as_json method in your model. Here you have to check if the methods are available in your model, if the method is not defined, remove it from options and do some notify action. This way the not available methods will be ignored.

An approximation code would be

class Model < ApplicationRecord

  def as_json(options = {})

    if options.key? :methods
      if options[:methods].is_a? Array
        methods_copy = options[:methods].clone
        methods_copy.each do |opt|
          if !self.respond_to? opt
            # do notify, log, etc
            options[:methods].delete opt
          end
        end
      elsif options[:methods].is_a? Symbol
        if !self.respond_to? options[:methods]
          # do notify, log, etc
          options.delete :methods
        end
      else
        # do some actions here
      end
    end

    super(options)
  end
end

Then call as_json as usual

model.as_json(methods: [:foo, :bar])

UPDATE

As I misread the question, here an update based on the answer of @sawa

Older versions of ActiveModel don't notify when some method is not defined when methods options is passed.

Upgrading the above code

def as_json(options = {})
  Array(options[:methods]).each do |m|
    raise "method #{m} doesn't exists" if !self.respond_to?(m)
  end
  super(options)
end

Methods included in as_json must be public methods. If bar is a private method, it will not be included.

This situation is a subset of the responds_to? issue mentioned in @sawa's answer , and is probably resolved by the same commit linked in the answer.

In such case, would as_json silently ignore the application of the undefined methods?

Yes in older versions of active model, and no since a certain version. To be more precise with older versions of active model, it is not the case that undefined methods are ignored; what are ignored are methods that do not return a truthy value to respond_to? . In the code at: https://github.com/rails/rails/blob/master/activemodel/lib/active_model/serialization.rb , there used to be a line:

Array(options[:methods]).each { |m| hash[m.to_s] = send(m) if respond_to?(m) }

where hash is the output of this method. Methods specified by the :methods option were ignored when they did not respond. However, this change altered the corresponding line into:

Array(options[:methods]).each { |m| hash[m.to_s] = send(m) }

which raises a method missing error when the method is not defined.

If so, is there a way to be notified by an error?

The easiest way is to update the active model to at least later than the commit mentioned above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM