Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Any way to configure/perform something like key_transform in AMS? #158

Closed
davidferril opened this issue Jun 11, 2019 · 10 comments
Closed

Any way to configure/perform something like key_transform in AMS? #158

davidferril opened this issue Jun 11, 2019 · 10 comments

Comments

@davidferril
Copy link

davidferril commented Jun 11, 2019

We're switching off AMS, and we made heavy use of the key_transform feature it had, allowing us to CamelCase or underscore_case keys as specified by a request header we bubble up.

Now that we're moving to Blueprinter, is there a clean way to manipulate keys returned by blueprinter? if not, is there a less than clean way to do so, other than revisiting all the keys in the serialized objects, and transforming them after the fact?

Thanks,

David

@philipqnguyen
Copy link
Contributor

The only supported way is to pass in the name option.

field :firstName, name: :first_name

But it sounds like you need to rename them in a lot of places, so it may not be easy to do it everywhere.

Here is an unsupported solution (monkey patch), however this is at app initialization level:

# config/initializers/blueprinter.rb
Module Blueprinter
  class Field
    def name
      @name.camelize
    end
  end
end

If you're really looking for something dynamic, based on the API request header, you're really going to need a bigger monkey patch. You could override this https://github.com/procore/blueprinter/blame/80eef670cfedcda45528b925fa8c77394ed4a15a/lib/blueprinter/helpers/base_helpers.rb#L46-L51 to something like this:

# config/initializers/blueprinter.rb
module Blueprinter
  class Base
    def self.object_to_hash(object, view_name:, local_options:)
      view_collection.fields_for(view_name).each_with_object({}) do |field, hash|
        next if field.skip?(object, local_options)
        name = if local_options[:camelize]
          field.name.to_s.camelize.to_sym
        else
          field.name.to_s.underscore.to_sym
        end
        hash[name] = field.extract(object, local_options)
      end
    end
  end
end

# in your controller
def show
  render json: SomeBlueprint.render(yourObject, camelize: true)
end

Future versions of blueprinter could break my suggestions above because these are private methods you'd be overriding.

@cabello
Copy link

cabello commented Jan 13, 2020

@philipqnguyen I used your suggested solution with 🐒 Monkey patching as I wanted all fields to be camelCase. Thank you 🙏

# config/initializers/blueprinter.rb
module Blueprinter
  class Field
    def name
      @name.to_s.camelize(:lower)
    end
  end
end

@supremebeing7
Copy link
Contributor

For anyone who stumbled on this like I did, there's another way to do it now that does not require any monkey patching, using the transform option (added in #164).

class UserBlueprint < Blueprinter::Base
  transform LowerCamelTransformer
end

class LowerCamelTransformer < Blueprinter::Transformer
  def transform(hash, object, options)
	# keys are symbols, have to convert to string to camelize then back to symbol
    hash.deep_transform_keys! { |key| key.to_s.camelize(:lower).to_sym }
  end
end

I set this option in a BaseBlueprint and then inherited most all of my other blueprints from it. So, it's more or less app-wide, but also configurable per blueprint as needed.

@stlewis
Copy link

stlewis commented Feb 14, 2020

@supremebeing7 @philipqnguyen

Your solution worked for me in most cases, but I've found that using a view seems to break the functionality.

For example:

class CompanyBlueprint < BaseBlueprint
  identifier :id
  fields :name, :address

  view :customer_view do
    fields :name, :address, :industry_name
  end
end

# Elsewhere:

CompanyBlueprint.render(@company, view: :customer_view)

In the case above, industry_name will remain snake-cased. Does the transform method treat views on Blueprints differently somehow?

@supremebeing7
Copy link
Contributor

Interesting. I'm new to this gem and haven't used views yet. My first thought would be to try defining the transform inside the view block, or as an arg in the render method, but I haven't tried either of those myself or checked the docs on it.

@philipqnguyen
Copy link
Contributor

@stlewis transform in your view should work for this as @supremebeing7 suggested. If it doesn't, comment here and we can reopen this issue and investigate.

@stlewis
Copy link

stlewis commented Feb 14, 2020

@philipqnguyen @supremebeing7

That worked, thank you!

@supremebeing7
Copy link
Contributor

Nice! Love it when something works that intuitively.

@elacalle
Copy link

elacalle commented Jan 3, 2022

@philipqnguyen I used your suggested solution with 🐒 Monkey patching as I wanted all fields to be camelCase. Thank you 🙏

# config/initializers/blueprinter.rb
module Blueprinter
  class Field
    def name
      @name.to_s.camelize(:lower)
    end
  end
end

This could be dangerous and break the functionality of other gems/engines using Blueprinter

@kg-currenxie
Copy link

kg-currenxie commented Jun 14, 2024

If you're really looking for something dynamic, based on the API request header, you're really going to need a bigger monkey patch. You could override this procore/blueprinter@80eef67/lib/blueprinter/helpers/base_helpers.rb#L46-L51 (blame) to something like this:

# config/initializers/blueprinter.rb
module Blueprinter
  class Base
    def self.object_to_hash(object, view_name:, local_options:)
      view_collection.fields_for(view_name).each_with_object({}) do |field, hash|
        next if field.skip?(object, local_options)
        name = if local_options[:camelize]
          field.name.to_s.camelize.to_sym
        else
          field.name.to_s.underscore.to_sym
        end
        hash[name] = field.extract(object, local_options)
      end
    end
  end
end

# in your controller
def show
  render json: SomeBlueprint.render(yourObject, camelize: true)
end

Future versions of blueprinter could break my suggestions above because these are private methods you'd be overriding.

render json: MyBlueprint.render(record, camelize: @use_camel_case)
this only transforms each field, but not if the field returns a hash. maybe that's "obvious" that it wouldn't.
however, this is still a bit disappointing since this was a request 5 years ago

jbuilder does this hilariously easily:

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

json.result {
  ...
}

is it not something the library can consider having out-of-the-box?

my use case is that the developer chooses which format they want as the response, when calling my api

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants