0

In an API controller, I'd like to limit what fields of a model can be seen depending on who is logged in. ActiveModel Serializers would seem to allow this, but I've had no luck with the following:

class MyModelSerializer < ActiveModel::Serializer
  attributes :name, :custom_field, :secret_field

  has_many :linked_records

  def custom_field
    object.do_something
  end

  def filter(keys)
    unless scope.is_admin?
      keys.delete :secret_field
      keys.delete :linked_records
    end
    keys
  end
end

But, the filtering is never performed and so my output always contains :secret_field and :linked_records even if there's no user logged in.

Perhaps this is because I am using Rails 6, and it would seem that ActiveModel Serializers might no longer be the best tool (e.g. https://stevenyue.com/blogs/migrating-active-model-serializers-to-jserializer).

Please do offer your suggestions for a means to perform this, if you can think of a better means.

EDIT:

Further to all the comments below, here's some different code:

  attributes :name, :id, :admin_only_field, :is_admin

  $admin_only = %i[:id, :admin_only_field]

  def attributes(*args)
    hash = super
    $admin_only.each do |key|
      unless scope.is_admin?
        hash.delete(key)
      end
    end
    hash
  end

  def is_admin
    if scope.is_admin?
      'admin!'
    else
      'not an admin!'
    end
  end

If I then visit the model's index page without being an admin I see that the admin_only_field and id are both present, and is_admin says that I'm not. Bizarre.

knirirr
  • 1,860
  • 4
  • 23
  • 37
  • Does this help? https://stackoverflow.com/questions/29705802/conditional-attributes-in-active-model-serializers – Thang Jan 23 '20 at 13:34
  • Nothing so far, though I have still to try the last method mentioned (Passalini). – knirirr Jan 23 '20 at 14:23
  • Hmm... The `scope.is_admin?` is really suspicious. Are you able to debug and confirm that the code inside `unless` is reached? – Thang Jan 23 '20 at 14:35
  • I've tried various debugging steps. The is_admin code test works as expected but the hash.delete does not actually delete anything, it would seem. – knirirr Jan 23 '20 at 14:37
  • What is your Ruby version? – Thang Jan 23 '20 at 14:38
  • I just tested with Ruby-2.7.0 and Rails-6.0.2.1. Both of the ***inline condition*** and the ***attributes overriding*** are working for me. :( – Thang Jan 23 '20 at 15:58
  • I'm using Ruby-2.6.5 and Rails-6.0.2.1. I'd be surprised if it's the ruby version which is responsible, but I suppose I'll have to try that. – knirirr Jan 23 '20 at 16:16
  • This is utterly perplexing so I have added some further information in the original post. – knirirr Jan 23 '20 at 17:21
  • ***If I then visit the model's index page without being an admin I see that the admin_only_field and id are both present, and is_admin says that I'm not.*** Definitely the `scope.is_admin?` is `false` here – Thang Jan 24 '20 at 07:50
  • The admin detection is definitely working correctly, so the failure to delete from the hash is perplexing. Anyway, this morning I have tried again with code similar to that above and seem to have had some success, so what has caused the issue remains unclear. – knirirr Jan 24 '20 at 11:49
  • Glad to hear this issue is resolved, but it is better to understand why though. – Thang Jan 24 '20 at 13:04
  • 1
    Indeed - I would very much like to know. I suppose I will have to keep working on it to see if any clues appear. – knirirr Jan 24 '20 at 13:05

1 Answers1

0
class MyModelSerializer < ActiveModel::Serializer
  attributes :name, :custom_field, :secret_field

  has_many :linked_records

  def custom_field
    object.do_something
  end

 private

  def attributes
    hash = super
    unless scope.is_admin?
      hash.delete :secret_field
      hash.delete :linked_records
    end
    hash
  end

end
  • Thanks for the response. That code gives me: `private method 'attributes' called for # Did you mean? attributes_hash`; if I don't have the method as private I get an argument error:: `wrong number of arguments (given 1, expected 0) `. – knirirr Jan 23 '20 at 08:55