procore-oss / blueprinter

Simple, Fast, and Declarative Serialization Library for Ruby
MIT License
1.14k stars 109 forks source link

[Question/Support] Transforming collection arrays #489

Open IanMitchell opened 1 day ago

IanMitchell commented 1 day ago

Is there an existing issue for this?

Describe your question or ask for support

Hello! I'm trying to write a transformer that takes any serialized array and mutates it into a hash keyed by an array item's id value. For instance, I'd like to transform [{id: 1, name: "Ian"}, { id: 2, name: "Leah" }] into {1: {id: 1, name: "Ian"}, 2: { id: 2, name: "Leah" }}. I have this working for the most part on items within a blueprint, but I'm not quite sure how to apply these changes to a top-level collection with a root set.

It looks like the transformer is called for each item within the collection, but isn't running for the collection itself. I'm coming back to Ruby after many years away, so it's very possible I'm missing something blatant, but I was hoping for a pointer or two on how to achieve this!

For reference, the rendering setup I have looks like this:

render json: UserBlueprint.render(@users, root: :users)

My current transformer:

class KeyedListTransformer < Blueprinter::Transformer
  def transform(hash, object, options)
    recursive_replace(hash)
  end

  private

  def recursive_replace(hash)
    hash.transform_values! do |value|
      case value
      when Array
        value
          .index_by { |item| item[:id] }
          .transform_values! { |value| recursive_replace(value) }
      when Hash
        value.transform_values! { |value| recursive_replace(value) }
      else
        value
      end
    end
  end
end

And the current output:

{
  "users": [
    {
      "id": "user_1",
      "name": "Ian",
      "posts": {
        "post_1": {
          "id": "post_1",
          "userId": "user_1",
          "name": "Hello World",
          "createdAt": "2024-11-21 06:00:26 UTC",
          "updatedAt": "2024-11-21 06:00:26 UTC"
        },
        "post_2": {
          "id": "post_2",
          "userId": "user_1",
          "name": "New Post",
          "createdAt": "2024-11-21 06:00:26 UTC",
          "updatedAt": "2024-11-21 06:00:26 UTC"
        }
      },
      "createdAt": "2024-11-21 06:00:26 UTC",
      "updatedAt": "2024-11-21 16:28:07 UTC"
    }
  ]
}

The posts association is transformed, but the top level users collection isn't. I've tried reading through the source code and doing some debugging where possible, but I haven't managed to crack this one yet 😞. Any help would be massively appreciated!