diff --git a/.travis.yml b/.travis.yml index fd881a3a9..0a0b1a99f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,15 +2,19 @@ language: ruby rvm: - 1.9.3 - 2.0.0 - - jruby-19mode - - rbx-19mode + - ruby-head + - jruby + - rbx gemfile: - Gemfile - Gemfile.rails3 - Gemfile.edge matrix: allow_failures: + - rvm: jruby + - rvm: rbx - gemfile: Gemfile.edge + fast_finish: true notifications: email: false campfire: diff --git a/lib/active_model/serializable.rb b/lib/active_model/serializable.rb index 7a34bc04c..c25b56349 100644 --- a/lib/active_model/serializable.rb +++ b/lib/active_model/serializable.rb @@ -1,7 +1,9 @@ module ActiveModel module Serializable def as_json(options={}) - if root = options.fetch(:root, json_key) + root = options.fetch(:root, json_key) + root = apply_conversion(root) if @convert_type + if root hash = { root => serializable_object } hash.merge!(serializable_data) hash diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index c582128a4..9e5009ff4 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -8,7 +8,7 @@ module ActiveModel class Serializer include Serializable - + class_attribute :_root @mutex = Mutex.new class << self @@ -59,7 +59,7 @@ def serializer_for(resource) end end - attr_accessor :_root, :_attributes, :_associations + attr_accessor :_attributes, :_associations alias root _root= alias root= _root= @@ -101,14 +101,15 @@ def associate(klass, *attrs) end def initialize(object, options={}) - @object = object - @scope = options[:scope] - @root = options.fetch(:root, self.class._root) - @meta_key = options[:meta_key] || :meta - @meta = options[@meta_key] + @object = object + @scope = options[:scope] + @root = options.fetch(:root, self.class._root) + @meta_key = options[:meta_key] || :meta + @meta = options[@meta_key] + @convert_type = options[:convert_type] @options = options.reject{|k,v| [:scope, :root, :meta_key, :meta].include?(k) } end - attr_accessor :object, :scope, :meta_key, :meta, :root, :options + attr_accessor :object, :scope, :meta_key, :meta, :root, :convert_type, :options def json_key if root == true || root.nil? @@ -172,8 +173,26 @@ def serialize_ids(association) def serializable_hash(options={}) return nil if object.nil? hash = attributes - hash.merge! associations + convert_keys hash.merge!(associations) end alias_method :serializable_object, :serializable_hash + + def convert_keys(hash) + hash.inject({}) { |h, (k, v)| h[apply_conversion(k)] = v; h } + end + + def apply_conversion(key) + return key.to_s.camelize(:lower) if convert_type == 'camelcase' + return key.to_s.upcase if convert_type == 'upcase' + key + end + + def camelize_keys! + @convert_type = "camelcase" + end + + def upcase_keys! + @convert_type = "upcase" + end end end diff --git a/lib/active_model/serializer/associations.rb b/lib/active_model/serializer/associations.rb index 76574dd78..3e6431c3e 100644 --- a/lib/active_model/serializer/associations.rb +++ b/lib/active_model/serializer/associations.rb @@ -34,8 +34,9 @@ def embed=(embed) @embed_objects = embed == :object || embed == :objects end - def build_serializer(object, options = {}) - @serializer_class.new(object, options.merge(@options)) + def build_serializer(object, convert_type = nil) + @serializer_class ||= Serializer.serializer_for(object) || DefaultSerializer + @serializer_class.new(object, @options.merge(:convert_type => convert_type)) end private diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index a43954b53..fb550e2c0 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -54,7 +54,7 @@ def description end class PostSerializer < ActiveModel::Serializer - attributes :title, :body + attributes :title, :body, :created_at, :updated_at has_many :comments end diff --git a/test/unit/active_model/serializer/attributes_test.rb b/test/unit/active_model/serializer/attributes_test.rb index 0914747e8..52f7b0299 100644 --- a/test/unit/active_model/serializer/attributes_test.rb +++ b/test/unit/active_model/serializer/attributes_test.rb @@ -25,5 +25,43 @@ def test_attributes_serialization_using_as_json }, @profile_serializer.as_json) end end + + class HashKeyTest < ActiveModel::TestCase + def setup + @post = Post.new({ title: 'test', body: 'lorem ipsum', created_at: Time.now, updated_at: Time.now }) + @post_serializer = PostSerializer.new(@post) + end + + def test_attributes_serialization_using_camelcase_key_conversion + @post_serializer.convert_type = 'camelcase' + assert_match({ + 'title' => 'test', 'body' => 'lorem ipsum', 'createdAt' => Time.now, 'updatedAt' => Time.now, 'comments' => [{ 'content' => 'C1'}, { 'content' => 'C2'}] + }.to_s, @post_serializer.serializable_hash.to_s) + end + + def test_attributes_serialization_using_upcase_key_conversion + @post_serializer.convert_type = 'upcase' + assert_match({ + 'TITLE' => 'test', 'BODY' => 'lorem ipsum', 'CREATED_AT' => Time.now, 'UPDATED_AT' => Time.now, 'COMMENTS' => [{'CONTENT' => 'C1'}, { 'CONTENT' => 'C2'}] + }.to_s, @post_serializer.serializable_hash.to_s) + end + end + + class HelpersTest < ActiveModel::TestCase + def setup + @post = Post.new({ title: 'test', body: 'lorem ipsum', created_at: Time.now, updated_at: Time.now }) + @post_serializer = PostSerializer.new(@post) + end + + def test_attributes_serialization_using_camelize_keys_helper + @post_serializer.camelize_keys! + assert_equal("camelcase", @post_serializer.convert_type) + end + + def test_attributes_serialization_using_upcase_keys_helper + @post_serializer.upcase_keys! + assert_equal("upcase", @post_serializer.convert_type) + end + end end end diff --git a/test/unit/active_model/serializer/filter_test.rb b/test/unit/active_model/serializer/filter_test.rb index bf33b5e74..2df5829fd 100644 --- a/test/unit/active_model/serializer/filter_test.rb +++ b/test/unit/active_model/serializer/filter_test.rb @@ -41,7 +41,7 @@ def teardown def test_filtered_associations_serialization assert_equal({ - 'post' => { title: 'Title 1' } + 'post' => { title: 'Title 1', created_at: nil, updated_at: nil } }, @post_serializer.as_json) end end diff --git a/test/unit/active_model/serializer/has_many_test.rb b/test/unit/active_model/serializer/has_many_test.rb index c89e05a9c..6f6372b6d 100644 --- a/test/unit/active_model/serializer/has_many_test.rb +++ b/test/unit/active_model/serializer/has_many_test.rb @@ -25,7 +25,7 @@ def test_associations_embedding_ids_serialization_using_serializable_hash @association.embed = :ids assert_equal({ - title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } + title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil, 'comment_ids' => @post.comments.map { |c| c.object_id } }, @post_serializer.serializable_hash) end @@ -33,7 +33,7 @@ def test_associations_embedding_ids_serialization_using_as_json @association.embed = :ids assert_equal({ - 'post' => { title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } } + 'post' => { title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil, 'comment_ids' => @post.comments.map { |c| c.object_id } } }, @post_serializer.as_json) end @@ -42,7 +42,7 @@ def test_associations_embedding_ids_serialization_using_serializable_hash_and_ke @association.key = 'key' assert_equal({ - title: 'Title 1', body: 'Body 1', 'key' => @post.comments.map { |c| c.object_id } + title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil, 'key' => @post.comments.map { |c| c.object_id } }, @post_serializer.serializable_hash) end @@ -50,7 +50,7 @@ def test_associations_embedding_objects_serialization_using_serializable_hash @association.embed = :objects assert_equal({ - title: 'Title 1', body: 'Body 1', comments: [{ content: 'C1' }, { content: 'C2' }] + title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil, comments: [{ content: 'C1' }, { content: 'C2' }] }, @post_serializer.serializable_hash) end @@ -58,7 +58,7 @@ def test_associations_embedding_objects_serialization_using_as_json @association.embed = :objects assert_equal({ - 'post' => { title: 'Title 1', body: 'Body 1', comments: [{ content: 'C1' }, { content: 'C2' }] } + 'post' => { title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil, comments: [{ content: 'C1' }, { content: 'C2' }] } }, @post_serializer.as_json) end @@ -71,7 +71,7 @@ def comments end assert_equal({ - 'post' => { title: 'Title 1', body: 'Body 1', comments: [nil] } + 'post' => { title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil, comments: [nil] } }, @post_serializer.as_json) end @@ -80,7 +80,7 @@ def test_associations_embedding_objects_serialization_using_serializable_hash_an @association.embedded_key = 'root' assert_equal({ - title: 'Title 1', body: 'Body 1', 'root' => [{ content: 'C1' }, { content: 'C2' }] + title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil, 'root' => [{ content: 'C1' }, { content: 'C2' }] }, @post_serializer.serializable_hash) end @@ -89,7 +89,7 @@ def test_associations_embedding_ids_including_objects_serialization_using_serial @association.embed_in_root = true assert_equal({ - title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } + title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil, 'comment_ids' => @post.comments.map { |c| c.object_id } }, @post_serializer.serializable_hash) end @@ -98,7 +98,7 @@ def test_associations_embedding_ids_including_objects_serialization_using_as_jso @association.embed_in_root = true assert_equal({ - 'post' => { title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } }, + 'post' => { title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil, 'comment_ids' => @post.comments.map { |c| c.object_id } }, comments: [{ content: 'C1' }, { content: 'C2' }] }, @post_serializer.as_json) end @@ -108,7 +108,7 @@ def test_associations_embedding_nothing_including_objects_serialization_using_as @association.embed_in_root = true assert_equal({ - 'post' => { title: 'Title 1', body: 'Body 1' }, + 'post' => { title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil }, comments: [{ content: 'C1' }, { content: 'C2' }] }, @post_serializer.as_json) end @@ -125,7 +125,7 @@ def content end assert_equal({ - 'post' => { title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } }, + 'post' => { title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil, 'comment_ids' => @post.comments.map { |c| c.object_id } }, comments: [{ content: 'fake' }, { content: 'fake' }] }, @post_serializer.as_json) end @@ -140,7 +140,7 @@ def serializable_object end assert_equal({ - 'post' => { title: 'Title 1', body: 'Body 1', 'comment_ids' => @post.comments.map { |c| c.object_id } }, + 'post' => { title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil, 'comment_ids' => @post.comments.map { |c| c.object_id } }, comments: { my_content: ['fake'] } }, @post_serializer.as_json) end @@ -153,7 +153,7 @@ def serializable_object end assert_equal({ - 'post' => { title: 'Title 1', body: 'Body 1', comments: { my_content: ['fake'] } } + 'post' => { title: 'Title 1', body: 'Body 1', created_at: nil, updated_at: nil, comments: { my_content: ['fake'] } } }, @post_serializer.as_json) end end diff --git a/test/unit/active_model/serializer/root_test.rb b/test/unit/active_model/serializer/root_test.rb index 6cf2c546f..6b45cf6af 100644 --- a/test/unit/active_model/serializer/root_test.rb +++ b/test/unit/active_model/serializer/root_test.rb @@ -6,7 +6,7 @@ class RootAsOptionTest < ActiveModel::TestCase def setup @old_root = ProfileSerializer._root @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) - @serializer = ProfileSerializer.new(@profile, root: :initialize) + @serializer = ProfileSerializer.new(@profile, root: :new_posts) ProfileSerializer._root = true end @@ -22,12 +22,21 @@ def test_root_is_not_displayed_using_serializable_hash def test_root_using_as_json assert_equal({ - initialize: { + new_posts: { name: 'Name 1', description: 'Description 1' } }, @serializer.as_json) end + def test_root_applied_conversion_using_as_json + @serializer.camelize_keys! + assert_equal({ + 'newPosts' => { + 'name' => 'Name 1', 'description' => 'Description 1' + } + }, @serializer.as_json) + end + def test_root_from_serializer_name @serializer = ProfileSerializer.new(@profile)