ruby-on-railsrubyruby-grapegrape-entity

How to expose Hash in Grape Entity and not use `using`


I have this entity.

class EmpEntity < Grape::Entity
  expose :id
  expose :age
  expose :name do
    expose :firstname
    expose :lastname
    expose :nickname
  end
end

And I take a result like this.

data = {
  id: 1,
  age: 18,
  name: {
    firstname: 'foo',
    lastname: 'bar',
    nickname: 'foobar',
  },
}

When I use entity's method, it return this.

EmpEntity.represent(data)
# => #<EmpEntity:15940 id=1 age=18 name={:firstname=>nil, :lastname=>nil, :nickname=>nil}>

How to take result like this.

# => #<EmpEntity:15940 id=1 age=18 name={:firstname=>'foo', :lastname=>'bar', :nickname=>'foobar'}>

and dot't use entity's using option. Because my app's result is not appropriate new entity.


Solution

  • i think firstname and lastname are nil because your entity don't know how to get them from hash, you could add a lambda to check if the represented instance is a hash or not to determine how you return value

    class EmpEntity < Grape::Entity
      expose :id
      expose :age
      expose :name do
        expose :firstname, proc: lambda {|instance, options| 
          if instance.is_a? Hash
           instance[:name][:firstname]
          else
           instance.firstname
          end
        } 
        expose :lastname, proc: lambda {|instance, options| 
          # same as above
        } 
        expose :nickname, proc: lambda {|instance, options| 
          # same as aboce
        } 
      end
    end
    

    update a general version

    class HashEntity < Grape::Entity
      class << self
        def hash_keys
          @hash_keys ||= []
        end
    
        def expose_hash(key)
          hash_keys << key
          if block_given?
            expose key do
              yield
            end
          else
            keys = hash_keys.dup
            expose key, proc: lambda { |instance, _|
             instance.dig(*keys)
            }
          end
          hash_keys.pop
        end
      end
    end
    

    demo

    class InfoEntity < HashEntity
      expose :id
      expose_hash :name do
       expose_hash :first_name
       expose_hash :last_name
      end
    end