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

Allow Key Format to Be Overridden and Have Sensible Defaults #1029

Closed
wants to merge 14 commits into from
Closed

Allow Key Format to Be Overridden and Have Sensible Defaults #1029

wants to merge 14 commits into from

Conversation

jfelchner
Copy link
Contributor

Overriding the Key Format

You can specify that serializers use a different style for the format of the
keys that are sent over. This can be done at the config, class or instance
level.

# Global
ActiveModel::Serializer.config.key_format = :lower_camel

# Per Serializer
class BlogLowerCamelSerializer < ActiveModel::Serializer
  key_format :lower_camel
end

# Per Instance
BlogSerializer.new(object, key_format: :lower_camel)

Currently Supported Key Formats

  • :unaltered
  • :lower_camel
  • :underscore
  • :dasherize

The default key format will be set depending on the adapter that you specify.
For example, the JSON adapter defaults to lower_camel whereas the JSON API
adapter defaults to dasherize. Any other adapter will default to unaltered.

References: #534, emberjs/data#3455
Closes: #974

Merry Christmas in July everyone. 😀 🎄 🎅

@jfelchner
Copy link
Contributor Author

I looked at the build and everything passed. No idea why it marked it as a failure.

@@ -170,6 +170,35 @@ class PostSerializer < ActiveModel::Serializer
end
```

### Overriding the Key Format
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍
It would be awesome if along with this PR you could add some Docs to our new documentation we have being working on before release.
Maybe event add some article about integrating it with ember.js using our How to section.

@joaomdmoura
Copy link
Member

👍 to the PR itself, I made some comments after reviewing it and we also need to check the tests.
Would be great if we could also squash some commits to keep the ones that makes more sense.
Awesome work @jfelchner, get it up! looking forward to have it on 0.10

@jfelchner
Copy link
Contributor Author

@joaomdmoura I'll get on it and comment back here on Monday or Tuesday. Thanks!

@mcmire
Copy link

mcmire commented Aug 10, 2015

Just wanted to drop by and 👍 this, this looks like a nice addition.

@jfelchner
Copy link
Contributor Author

@joaomdmoura ok, I hit a bit of a snag. I found a bug. :-/

The problem is that we don't want to convert keys for hashes that are inside the values for attributes. Currently it just goes recursively as deep as it needs to to make sure that everything is converted.

For example if we have this:

            @second_post = Post.new(
                            id:                   2,
                            title:                'New Post',
                            body:                 'Body',
                            multi_word_attribute: {
                              multiple_words: 'here are some words'
                            })

we don't want multiple_words to become multiple-words. The implementation I have feels really nice, but it obviously won't work.

Maybe an abstraction is missing or a refactoring is required to make this the "easy" change? I don't have enough familiarity with the codebase so I thought you or someone else might be able to provide some insight.

I think that the end goal is that the following should be converted, but nothing else:

  • attribute names
  • association names
  • types

Those should be converted wherever they are in the hash. Anything else:

  • top level schema keys like data, etc
  • values of attributes

should be left alone.

And obviously this is going to vary from adapter to adapter (eg json-api has a relationships hash which contains types, whereas the json adapter does not).

If you can point me in the right direction for implementation, I'll take a crack at it, but I fear I'm going to have to start from square one for the most part to get this right. Which is fine. I'd rather do it right.

@joaomdmoura
Copy link
Member

Get it @jfelchner, indeed the behavior you described would be most accurate.
We could accomplish it by calling format_key on multiple places: root, attributes (method), associations (method).
I'm not sure how it would like but it seems okay for me in order to keep it simple.
If you want to, we can try to pair on it.
Let me know your thoughts about it 😄

delegate :each, to: :@objects

attr_reader :root, :meta, :meta_key
attr_reader :root, :meta, :meta_key, :options
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't appear the you're using the options reader in any public contexts. Perhaps we can make it private? (It's just so the KeyFormat mixin can do options[:key_format], right?)

@joaomdmoura
Copy link
Member

Hey @jfelchner, would be awesome if you could read through #1052.
It's deeply related to what you are working on.

I merged it for now but I would like to handle it on this PR following the same approach as well.

Let me know if there is anything I can do in order to make it easier for you 😄

@jfelchner
Copy link
Contributor Author

@joaomdmoura perfect! I'll take a look. Just been busy the last few days. 😊

@jfelchner
Copy link
Contributor Author

BTW @joaomdmoura I'm absolutely open to pairing on this. 👍🏻

@joaomdmoura
Copy link
Member

Awesome I did just a few comments, because I know the implementation might change after some changes you will do.

I'm absolutely open to pairing on this.

Cool! Find on Twitter as @joaomdmoura, I will send you my email and we can try to schedule something using screenhero or whatever. But don't wait for me 😁

end

def format_key(formattable_key)
case key_format

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assume we have namespaced object Admin::User then key should be user instead of admin/user. What do you think about adding something like formattable_key.to_s.demodulize?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@timurvafin Do you have a ref for Admin::User -> user? I'm not sure what I would do in such a case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jfelchner
Copy link
Contributor Author

@timurvafin underscore does that by default.

@jfelchner
Copy link
Contributor Author

@timurvafin oh I see what you're saying, you want the opposite. No I don't think that would be a good idea. They are two different classes and therefore two different types. If someone wants to do that they can override manually by overriding json_type on the serializer. Also, this PR doesn't affect values, just the keys.

@timurvafin
Copy link

@jfelchner probably AMS by default should generate something more json-like for Admin::Post then admin/post. Maybe something like admin-post or admin_post?

@jfelchner
Copy link
Contributor Author

@timurvafin we're using AMS with an app with multiple Rails engines and are using / between our modules and - between words. Unfortunately JSON API isn't clear on what to use and IMO there definitely needs to be a way to differentiate Admin::User from AdminUser so / seemed as good a choice as any.

@bf4
Copy link
Member

bf4 commented Aug 18, 2015

@jfelchner @timurvafin

Unfortunately JSON API isn't clear on what to use and IMO there definitely needs to be a way to differentiate Admin::User from AdminUser so / seemed as good a choice as any.

might be worth creating an issue in https://github.com/json-api/json-api/ and linking here

@jfelchner
Copy link
Contributor Author

@bf4 👍

The JSON API adapter follows the JSON API spec and therefore should follow JSON
API recommendations for naming

* http://jsonapi.org/format/#document-member-names
* http://jsonapi.org/recommendations/#naming
Just for completeness

For example, the JSON adapter defaults to `lower_camel` whereas the JSON API
adapter defaults to `dasherize`. Any other adapter will default to `unaltered`.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the JSON API adapter should default to lower_camel as well to avoid huge confusion on upgrade.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@beauby I am very much in the other camp on that. The JSON API standard recommends dasherized keys and the couple JSON API JS libraries I've seen default to expecting incoming requests to be formatted with dasherized keys.

In the vein of making this Just Work with JSON API, I think it should absolutely be dasherized. Otherwise almost everyone who uses it will need to add the global config option to their projects.

I'll bow to the majority opinion, but I just wanted to voice my concern.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, if JSON API recommends dashes, we should do dashes by default.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will allow users to override the key format from the controller.

Example:

```ruby
class MyController < ActionController::Base
  def index
    render json:       resource,
           key_format: :underscore
  end
end
```
@@ -11,6 +13,7 @@ class Adapter
autoload :Null
autoload :FlattenJson
autoload :CachedSerializer
include Configuration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, in https://github.com/rails-api/active_model_serializers/pull/1050/files#diff-ae1e38e96a8bf28ae37918a65c448c73R11 I just use e.g. ActiveModel::Serializer.config.jsonapi_toplevel_meta = {}

the require could also be a problem since it's incompatible with rails autoload

We want to be able to allow the user to override the adapter defaults on
a project-wide basis.
Rubocop fixes are self-explanatory.

The changes to the tests mainly involve reformatting the hash to use
hashrockets.  These are required on Ruby 1.8 when there are keys that contain
characters which cannot be symbolized.

Example:

```ruby

{
  foo:       'bar',
  'baz-qux': 'foobar',
}

{
  :foo       => 'bar',
  :'baz-qux' => 'foobar',
}
```
Isolated test methods are going to be long and that's ok. Pushing for tests to
be DRY makes individual tests less readable.
@bf4
Copy link
Member

bf4 commented Oct 2, 2015

Related: #1213

@krzkrzkrz
Copy link

Any updates on this? Am looking forward to dasherized format as well

@tylerwillingham
Copy link

I'm also interested in seeing this make it through. Is there anything I can do to contribute?

@andrewhavens
Copy link

I came across this PR while looking for a way to specify model_name in my serializer. I would like to be able to pass a hash instead of a full model instance to a serializer (to get better performance). Is there already a way to do this in master? Does this PR address this issue?

@bf4
Copy link
Member

bf4 commented Jan 6, 2016

No, this would be a feature request to pass a naked attributes hash directly into the adapter, i think

B mobile phone

On Jan 6, 2016, at 1:03 PM, Andrew Havens [email protected] wrote:

I came across this PR while looking for a way to specify model_name in my serializer. I would like to be able to pass a hash instead of a full model instance to a serializer (to get better performance). Is there already a way to do this in master? Does this PR address this issue?


Reply to this email directly or view it on GitHub.

@alessiodallapiazza
Copy link

Any updates on this?
It's very useful

@bf4 bf4 mentioned this pull request Mar 10, 2016
@remear
Copy link
Member

remear commented Mar 15, 2016

Resolved by #1574

@remear remear closed this Mar 15, 2016
@ijunaid8989
Copy link

What is the final results now? This thing is being added to gem?

@NullVoxPopuli
Copy link
Contributor

@rails-api rails-api locked and limited conversation to collaborators Jul 29, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.