Como sobrescrever o to_json no Rails?


Atualizar:

Esse problema não foi devidamente explorado. A questão real está no render :json .

O primeiro código colar na pergunta original produzirá o resultado esperado. No entanto, ainda há uma ressalva. Veja este exemplo:

render :json => current_user

NÃO é o mesmo que

render :json => current_user.to_json

Ou seja, render :json não chamará automaticamente o método to_json associado ao object User. De fato , se to_json estiver sendo substituído no modelo User , render :json => @user gerará o ArgumentError descrito abaixo.

resumo

 # works if User#to_json is not overridden render :json => current_user # If User#to_json is overridden, User requires explicit call render :json => current_user.to_json 

Isso tudo parece bobo para mim. Isso parece estar me dizendo que render não está realmente chamando Model#to_json quando type :json é especificado. Alguém pode explicar o que realmente está acontecendo aqui?

Qualquer gênio que possa me ajudar com isso provavelmente responderá a minha outra pergunta: Como criar uma resposta JSON combinando @ foo.to_json (opções) e @ bars.to_json (opções) no Rails


Pergunta original:

Eu vi alguns outros exemplos no SO, mas nenhum deles faz o que estou procurando.

Estou tentando:

 class User  :username, :methods => [:foo, :bar]) end end 

Estou recebendo ArgumentError: wrong number of arguments (1 for 0) em

 /usr/lib/ruby/gems/1.9.1/gems/activesupport-2.3.5/lib/active_support/json/encoders/object.rb:4:in `to_json 

Alguma ideia?

    Você está recebendo ArgumentError: wrong number of arguments (1 for 0) porque to_json precisa ser substituído por um parâmetro, o hash de options .

     def to_json(options) ... end 

    Explicação mais longa de to_json , as_json e renderização:

    No ActiveSupport 2.3.3, as_json foi adicionado para resolver problemas como o que você encontrou. A criação do json deve ser separada da renderização do json.

    Agora, a qualquer momento to_json é chamado em um object, as_json é invocado para criar a estrutura de dados e, em seguida, esse hash é codificado como uma string JSON usando ActiveSupport::json.encode . Isso acontece para todos os tipos: object, numérico, data, string, etc (veja o código do ActiveSupport).

    Os objects ActiveRecord se comportam da mesma maneira. Existe uma implementação as_json padrão que cria um hash que inclui todos os atributos do modelo. Você deve replace as_json em seu Modelo para criar a estrutura JSON desejada . as_json , assim como o antigo to_json , usa uma opção hash onde você pode especificar atributos e methods para include declarativamente.

     def as_json(options) # this example ignores the user's options super(:only => [:email, :handle]) end 

    No seu controller, render :json => o pode aceitar uma string ou um object. Se for uma string, ela é passada como o corpo da resposta, se for um object, to_json é chamado, o que aciona as_json como explicado acima.

    Portanto, contanto que seus modelos sejam representados corretamente com as_json substituições as_json (ou não), o código do controlador para exibir um modelo deve se parecer com isto:

     format.json { render :json => @user } 

    A moral da história é: evite chamar diretamente to_json , permita que o render faça isso para você. Se você precisar ajustar a saída JSON, chame as_json .

     format.json { render :json => @user.as_json(:only => [:username], :methods => [:avatar]) } 

    Se você está tendo problemas com isso no Rails 3, substitua serializable_hash vez de as_json . Isto irá obter a sua formatação XML de graça também 🙂

    Isso me levou para sempre para descobrir. Espero que ajude alguém.

    Para pessoas que não querem ignorar as opções dos usuários, mas também adicionam as suas opções:

     def as_json(options) # this example DOES NOT ignore the user's options super({:only => [:email, :handle]}.merge(options)) end 

    Espero que isso ajude alguém 🙂

    Substituir não to_json, mas as_json. E a partir do as_json, chame o que quiser:

    Tente isto:

     def as_json { :username => username, :foo => foo, :bar => bar } end