1

I am using 'grape-entity', '~> 0.7.1'

I have a hash in the format:

temp_data = [{sheet_index: 0, other_names: []},{'sheet_index' => 1, 'other_names': ['a']}]

And I have the following entities

 class Sheet < Grape::Entity
   expose :sheet_index, documentation: {type: Integer, desc: "Sheet index"}
   expose :other_names, documentation: {type: Array, desc: "Other names"}
 end

 class Sheets < Grape::Entity
  present_collection true

  expose :items, as: 'sheet_history', using Entities::Sheet
 end


# response from the entities
present temp_data, with: Entities::Sheets

Now I need to make sure that no matter the type of keys in my Hash it should still give me the correct output for the above case

expected_response = {"sheet_history" => [{"sheet_index"=>0, "other_names"=>[]}, {"sheet_index"=>1, "other_names"=>["a"]}]}

but the response I am getting is in the format below

actual_response = {"sheet_history" => [{"sheet_index"=>0, "other_names"=>[]}, {"sheet_index"=>nil, "other_names"=>nil}]}

so in the actual response sheet_index and other_names of the second element are nil because their keys were Strings, not Symbols. (Refer to temp_data.)

I have referred to https://github.com/ruby-grape/grape-entity/pull/85 to get the above implementation but still am not able to make it work without using HashWithIndifferentAccess or OpenStructs

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
VXCSL
  • 37
  • 8
  • 1
    Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. See: "[How to create a Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example)". A link to the GitHub code isn't sufficient; We need to see what you tried. – the Tin Man Mar 02 '20 at 19:19
  • @theTinMan I have added the required changes(the expected and the actual responses that I am able to reproduce and the reason why there is a discrepancy in the two responses). Please check now. – VXCSL Mar 03 '20 at 05:22
  • See "[What is the XY problem?](https://meta.stackexchange.com/a/66378/153968)". Too often we ask how to fix Y when we should ask how to do X. How are you gathering your data that results in a hash with both String and Symbol keys? Back up and look at that and see if you can coerce the keys into consistent types, either Strings or Symbols. – the Tin Man Mar 05 '20 at 03:10

1 Answers1

0

You are missing a colon after using, but I wouldn't set up multiple entities like that as it's likely to result in wonky behavior. Try this:

# Dummy definition of your class
class Item
  include ActiveModel::Serialization

  attr_accessor :sheet_index
  attr_accessor :other_names

  def initialize(index, names)
    @sheet_index = index
    @other_names = names
  end
end
items = []
items << Item.new(0, [])
items << Item.new(1, ['a'])
=> [
  #<Item:0x00007f860f740e40 @other_names=[], @sheet_index=0>, 
  #<Item:0x00007f860f513618 @other_names=["a"], @sheet_index=1>
]
# Entity Definition
class Sheet < Grape::Entity
  # The first arg here is the key to use for a collection, 
  # the second is the key to use for a single object
  root 'sheet_history', 'sheet_history'

  expose :sheet_index, documentation: {
    type: Integer, 
    desc: "Sheet index" # Plz use locales
  }

  expose :other_names, documentation: {
    type: Array, 
    desc: "Other names" # Plz use locales
  }
end
# Test it
representation = Sheet.represent(items)
=> {
  "sheet_history"=>[
    #<Sheet:70106854276160 sheet_index=0 other_names=[]>, 
    #<Sheet:70106854275680 sheet_index=1 other_names=["a"]>
  ]
}
# This is just more a more readable, but as you can see it's 
# both mapping all the attributes correctly and 
# setting the root key that you wanted:
representation['sheet_history'].map do |r| r.serializable_hash end
=> [
  {
    :sheet_index=>0, 
    :other_names=>[]
  }, 
  {
    :sheet_index=>1, 
    :other_names=>["a"]
  }
]

# Endpoint
get do
  items = current_user.items # or whatever
  present items, with: Entities::Sheet
end

You can send your array of hashes to the represent method, but it doesn't like the stringified key. Ideally you should be passing DB objects to your entity instead of hashes but, if you for some reason cannot, I would pass temp_data.map(&:symbolize_keys) as your argument to the entity to ensure the top-level keys in the hash it's parsing are symbols.

Allison
  • 1,925
  • 1
  • 18
  • 25