diff --git a/lib/active_model/convertable.rb b/lib/active_model/convertable.rb new file mode 100644 index 000000000..b0d7de234 --- /dev/null +++ b/lib/active_model/convertable.rb @@ -0,0 +1,45 @@ +module ActiveModel + module Convertable + extend ActiveSupport::Concern + + included do + class_attribute :_root_proc, :_key_proc + + self._key_proc = ->(original) { original } + self._root_proc = ->(original) { original } + end + + module ClassMethods + def camelize_keys!(first_letter = :lower) + self._key_proc = ->(original){ camelize_keys original, first_letter } + self._root_proc = ->(original){ original.to_s.camelize(first_letter) } + end + + def convert_keys(&proc) + self._key_proc = proc + end + + def camelize_keys(original, first_letter = :lower) + original.each_with_object({}) do |(key,value), hash| + hash.merge!(key.to_s.camelize(first_letter) => (value.is_a?(Hash) ? camelize_keys(value, first_letter) : value)) + end + end + + def _convert_root(root) + _root_proc.call(root) + end + + def _convert_keys(original = {}) + _key_proc.call(original) + end + end + + def _convert_root(root) + _root_proc.call(root) + end + + def _convert_keys(original = {}) + _key_proc.call(original) + end + end +end diff --git a/lib/active_model/serializable.rb b/lib/active_model/serializable.rb index fa4b7d83f..825866db7 100644 --- a/lib/active_model/serializable.rb +++ b/lib/active_model/serializable.rb @@ -1,8 +1,15 @@ +require 'active_model/convertable' module ActiveModel module Serializable + extend ActiveSupport::Concern + + included do + include Convertable + end + def as_json(options={}) if root = options[:root] || self.root - hash = { root => serializable_object } + hash = { _convert_root(root) => serializable_object } hash.merge!(serializable_data) hash else @@ -12,7 +19,7 @@ def as_json(options={}) def serializable_data if respond_to?(:meta) && meta - { meta_key => meta } + _convert_keys({ meta_key => meta }) else {} end diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index b37ef7cdf..371c91e3b 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -113,7 +113,7 @@ def initialize(object, options={}) def root=(root) @root = root @root = self.class._root if @root.nil? - @root = self.class.root_name if @root == true || @root.nil? + @root = _convert_root(self.class.root_name) if @root == true || @root.nil? end def attributes @@ -178,6 +178,7 @@ def serializable_hash(options={}) return nil if object.nil? hash = attributes hash.merge! associations + _convert_keys(hash) end alias serializable_object serializable_hash end diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index a43954b53..5efa37d99 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -25,6 +25,9 @@ def profile class Profile < Model end +class CamelCase < Model +end + class Post < Model def comments @comments ||= [Comment.new(content: 'C1'), @@ -62,3 +65,8 @@ class PostSerializer < ActiveModel::Serializer class CommentSerializer < ActiveModel::Serializer attributes :content end + +class CamelCaseSerializer < ActiveModel::Serializer + attributes :key_one, :key_two + camelize_keys! +end diff --git a/test/unit/active_model/serializer/convertable_test.rb b/test/unit/active_model/serializer/convertable_test.rb new file mode 100644 index 000000000..6892e28ab --- /dev/null +++ b/test/unit/active_model/serializer/convertable_test.rb @@ -0,0 +1,62 @@ +require 'test_helper' +def MiniTest.filter_backtrace(bt) + bt +end + +module ActiveModel + class Serializer + class CamelCaseLowerTest < ActiveModel::TestCase + def setup + @camel_case = CamelCase.new({ key_one: 'Name 1', key_two: 'Name 2' }) + @camel_case_serializer = CamelCaseSerializer.new(@camel_case) + @camel_case_serializer.class_eval do + camelize_keys! + end + end + + def test_attributes_definition + assert_equal([:key_one, :key_two], + @camel_case_serializer.class._attributes) + end + + def test_convert_keys_using_serializable_hash + assert_equal({ + 'keyOne' => 'Name 1', 'keyTwo' => 'Name 2' + }, @camel_case_serializer.serializable_hash) + end + + def test_convert_keys_using_as_json + assert_equal({ + 'camelCase' => { 'keyOne' => 'Name 1', 'keyTwo' => 'Name 2' } + }, @camel_case_serializer.as_json) + end + end + + class CamelCaseUpperTest < ActiveModel::TestCase + def setup + @camel_case = CamelCase.new({ key_one: 'Name 1', key_two: 'Name 2' }) + @camel_case_serializer = CamelCaseSerializer.new(@camel_case) + @camel_case_serializer.class_eval do + camelize_keys! :upper + end + end + + def test_attributes_definition + assert_equal([:key_one, :key_two], + @camel_case_serializer.class._attributes) + end + + def test_convert_keys_using_serializable_hash + assert_equal({ + 'KeyOne' => 'Name 1', 'KeyTwo' => 'Name 2' + }, @camel_case_serializer.serializable_hash) + end + + def test_convert_keys_using_as_json + assert_equal({ + 'CamelCase' => { 'KeyOne' => 'Name 1', 'KeyTwo' => 'Name 2' } + }, @camel_case_serializer.as_json) + end + end + end +end