3

JSON を出力するときに、ActiveRecord/ActiveModel クラスから特定のフィールドを除外したいと考えています。

これを行う最も簡単な方法はas_json、おそらく次のようにオーバーライドすることです。

def as_json (options = nil)
  options ||= {}
  super(options.deep_merge({:except => filter_attributes}))
end

def filter_attributes
  [:password_digest, :some_attribute]
end

これは機能しますが、少し冗長であり、DRY になるのが非常に高速ではありません。フィルタリングされたプロパティを魔法のクラス メソッドで宣言するだけでいいと思いました。例えば:

class User < ActiveRecord::Base
  include FilterJson

  has_secure_password
  filter_json :password_digest
  #...
end

module FilterJson
  extend ActiveSupport::Concern

  module ClassMethods
    def filter_json (*attributes)
      (@filter_attributes ||= Set.new).merge(attributes.map(&:to_s))
    end

    def filter_attributes
      @filter_attributes
    end
  end

  def as_json (options = nil)
    options ||= {}
    super(options.deep_merge({:except => self.class.filter_attributes.to_a}))
  end
end

これに関する問題は、継承を適切に処理することです。User をサブクラス化するとしましょう:

class SecretiveUser < User
  filter_json :some_attribute, :another_attribute
  #...
end

:some_attribute論理的には、 、:another_attribute、および も除外することは理にかなっています:password_digest

ただし、これはクラスで宣言された属性のみをフィルタリングします。super念のため、内で呼び出しを試みましfilter_attributesたが、失敗しました。私はこれを思いつきました、そしてそれはハックです。

def filter_attributes
  if superclass.respond_to?(:filter_attributes)
    superclass.filter_attributes + @filter_attributes
  else
    @filter_attributes
  end
end

これは明らかに脆く、慣用的なものではありませんが、私が達成しようとしている「何」があります。より正確に(そしてできればよりエレガントに)それを行う方法を考えられる人はいますか? ありがとう!

4

1 に答える 1

4

属性をブラックリストに登録するよりも、ホワイトリストに登録する方が安全な解決策だと思います。これにより、属性を.UserSomeUserfilter_json

特定の継承の問題に対する解決策を探しているようです。シリアライゼーションを管理するより健全な方法だと思うので、引き続き active_model_serializersを指摘します。

class UserSerializer < ActiveModel::Serializer
  attributes :id, :first_name, :last_name
end

class SecretUserSerializer < UserSerializer
  attributes :secret_attribute, :another_attribute
end

SecretUser sあなたができることを考えると

SecretUserSerializer.new(s).as_json

:id:first_name:last_name:secret_attribute、およびが得られます:another_attribute。継承は期待どおりに機能します。

于 2013-09-04T01:37:42.950 に答える