okuramasafumi / alba

Alba is a JSON serializer for Ruby, JRuby and TruffleRuby.
https://okuramasafumi.github.io/alba/
MIT License
934 stars 43 forks source link

Dynamic key transformer based on the request/params? #371

Closed kg-currenxie closed 4 months ago

kg-currenxie commented 4 months ago

in jbuilder, I can do do the key transforms based on something dynamic from the controller:

if @use_camel_case
  json.key_format! camelize: :lower
  json.deep_format_keys!
end

But i don't see how that's possible with alba's:

transform_keys :lower_camel

Am I missing something? Is it possible already?

okuramasafumi commented 4 months ago

@kg-currenxie I'm afraid it's not possible currently, but I'd like to add this feature since it sounds useful enough. I came up with the interface like below:

transform_keys do |obj|
  if obj.camel_case?
    :lower_camel
  elsif params[:snake]
    :snake
  else
    :camel
  end
end

Here we can use a block that returns transformation type symbols. Inside that block, we can access underlying object and params. So from Rails controller, you can pass @use_camel_case in params. What do you think?

kg-currenxie commented 4 months ago

What do you think?

Looks great 😁

okuramasafumi commented 4 months ago

@kg-currenxie It turned out that it's a bit difficult to implement my suggestion. Based on the guess that mostly it doesn't depend on serialization target but only depend on params, it could be something like this:

class MyController < ApplicationController
  def index
    key_type = params[:key_type]
    SomeSerializer.new(records, key_transformation: key_type).serialize # key_transformation option is new here
  end
end

This is more straightforward than using params. The only drawback for me is adding new option like this could make the interface complicated, so using options hash might make sense here.

kg-currenxie commented 4 months ago

@kg-currenxie It turned out that it's a bit difficult to implement my suggestion. Based on the guess that mostly it doesn't depend on serialization target but only depend on params, it could be something like this:


class MyController < ApplicationController

  def index

    key_type = params[:key_type]

    SomeSerializer.new(records, key_transformation: key_type).serialize # key_transformation option is new here

  end

end

This is more straightforward than using params. The only drawback for me is adding new option like this could make the interface complicated, so using options hash might make sense here.

an options hash makes sense ye 👌

okuramasafumi commented 4 months ago

@kg-currenxie I noticed that using options doesn't work for nested attributes and associations. I need to come up with a solution for this and it'll take some time.

For a temporary solution, you can define a parent class and subclasses with different key transformation types, and pick the right class in a controller.

class ParentSerializer
  # Serializer content here
end

class CamelSerializer < ParentSerializer
  transform_keys :camel
end

class LowerCamelSerializer < ParentSerializer
  transform_keys :lower_camel
end

class MyController < ApplicationController
  def index
    klass = params[:key_type] == 'camel' ? CamelSerializer : LowerCamelSerializer
    klass.new(records).serialize
  end
end

I know this is not ideal though...

Frexuz commented 4 months ago

Ok :) thx for your efforts!

okuramasafumi commented 4 months ago

OK, now I think I found a way: https://github.com/okuramasafumi/alba/pull/372 @kg-currenxie Could you take a look at it?

Frexuz commented 4 months ago

i'm OK with :)

key_transformation_type = params[:key_transformation_type]

resource_class = key_transformation_type ? FooResource.transform_keys!(key_transformation_type) : FooResource
render json: resource_class.new(foos).serialize # The keys are lower_camel