From 74eb16396c2a3767f5cae289ef111c0d7b179ac3 Mon Sep 17 00:00:00 2001 From: wujibear Date: Fri, 16 Dec 2022 22:52:31 -0800 Subject: [PATCH] Add CacheCrispies::Base#transform_keys method --- README.md | 22 +++++++++++++++++++++ lib/cache_crispies/base.rb | 25 ++++++++++++++++++++++-- spec/cache_crispies/base_spec.rb | 33 +++++++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e88405e..802dff7 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,28 @@ end Put serializer files in `app/serializers/`. For instance this file should be at `app/serializers/cereal_serializer.rb`. +### Snakecase to lower camel case +You can pass a method to `transform_keys` in a serializer which will transform the keys for each attribute unless a `from` attribute was specified for that attribute. +The configuration for `transform_keys` can be inherited, and overridden in the serializers that inherit from another. +```ruby +# app/serializers/base_serializer.rb +class BaseSerializer < CacheCrispies::Base + transform_keys lambda { |key| key.to_s.camelize(:lower) } +end + +# app/serializers/crispy_serializer.rb +class CrispySerializer < BaseSerializer + serialize :id, :created_at +end +``` +This will format the keys for each attribute using the passed in lambda like so: +```json +{ + "id": "123", + "createdAt": "somedate" +} +``` + ### In your Rails controller ```ruby class CerealsController diff --git a/lib/cache_crispies/base.rb b/lib/cache_crispies/base.rb index 88705e5..2543897 100644 --- a/lib/cache_crispies/base.rb +++ b/lib/cache_crispies/base.rb @@ -178,6 +178,16 @@ def self.file_hashes ).uniq.sort end + # Sets a method or Proc to transform keys by + # + # @param proc [Proc] a Proc to execute on serializable keys + # @return [Proc] a Proc to execute on the keys + def self.transform_keys(proc = nil) + return @transform_keys ||= proc if proc.present? || @transform_keys.present? + + self.superclass.transform_keys if self.superclass.respond_to?(:transform_keys) + end + private def self.file_hash @@ -226,13 +236,14 @@ def self.serialize( ) attribute_names.flat_map do |attrib| attrib = attrib&.to_sym + key = attrib current_nesting = Array(@nesting).dup current_conditions = Array(@conditions).dup @attributes << Attribute.new( - attrib, - from: from, + transform_key(attrib), + from: from || attrib, with: with, through: through, to: to, @@ -250,5 +261,15 @@ def self.merge(attribute = nil, with: nil) serialize(nil, from: attribute, with: with) end private_class_method :merge + + # Transforms an attribute key using a specified Proc + # + # @param key [String] the key for an attribute + # @return [String] a transformed key + def self.transform_key(key) + return key if transform_keys.blank? + + transform_keys.call(key) + end end end diff --git a/spec/cache_crispies/base_spec.rb b/spec/cache_crispies/base_spec.rb index 14b1dbc..0c4e6c3 100644 --- a/spec/cache_crispies/base_spec.rb +++ b/spec/cache_crispies/base_spec.rb @@ -39,6 +39,20 @@ def visible? end end + class BaseSerializer < CacheCrispies::Base + transform_keys lambda { |key| key.to_s.camelize(:lower) } + end + + class InheritingSerializer < BaseSerializer + serialize :id, :snake_case + end + + class OverriddenSerializer < BaseSerializer + transform_keys lambda { |key| key.to_s.upcase } + + serialize :id, :snake_case + end + let(:model) do OpenStruct.new( id: 42, @@ -47,7 +61,8 @@ def visible? deeply_nested: true, nutrition_info: OpenStruct.new(calories: 1_000), organic: 'true', - legal: OpenStruct.new(parent_company: 'Disney probably') + legal: OpenStruct.new(parent_company: 'Disney probably'), + snake_case: 'i was snakecased' ) end @@ -95,6 +110,22 @@ def visible? end end + describe '.transform_keys' do + let(:serializer) { InheritingSerializer } + + it 'transforms attribute keys' do + expect(subject.as_json.keys).to eq(%w[id snakeCase]) + end + + describe 'overridden serializers' do + let(:serializer) { OverriddenSerializer } + + it 'transforms attribute keys' do + expect(subject.as_json.keys).to eq(%w[ID SNAKE_CASE]) + end + end + end + describe '.key' do it 'underscores the demodulized class name by default' do expect(serializer.key).to eq :cache_crispies_test