Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# UNRELEASED

* attributes initialisation has changed, to provide type for
a computed attribute you now need to use a hash -- e.g.

attributes :attr1 => :key1, :attr2 => {:type => :string},
:attr3 => {:key => :key3, :type => :integer}
attribute :attr4, :key => :key4
attribute :attr5, :key => :key5, :type => :string

* ActiveModel::Serializable was created it has the shared code between
AM::Serializer and AM::ArraySerializer. Basically enable objects to be
serializable by implementing an options method to handle the options
Expand Down
37 changes: 19 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![Build Status](https://api.travis-ci.org/rails-api/active_model_serializers.png)](https://travis-ci.org/rails-api/active_model_serializers) [![Code Climate](https://codeclimate.com/github/rails-api/active_model_serializers.png)](https://codeclimate.com/github/rails-api/active_model_serializers) [![Coverage Status](https://coveralls.io/repos/rails-api/active_model_serializers/badge.png?branch=master)](https://coveralls.io/r/rails-api/active_model_serializers)
[![Build Status](https://api.travis-ci.org/rails-api/active_model_serializers.png)](https://travis-ci.org/rails-api/active_model_serializers) [![Code Climate](https://codeclimate.com/github/rails-api/active_model_serializers.png)](https://codeclimate.com/github/rails-api/active_model_serializers) [![Coverage Status](https://coveralls.io/repos/rails-api/active_model_serializers/badge.png?branch=master)](https://coveralls.io/r/rails-api/active_model_serializers)

# Purpose

Expand All @@ -13,7 +13,7 @@ content.
In short, **serializers replace hash-driven development with object-oriented
development.**

# Installing
# Installing

The easiest way to install `ActiveModel::Serializers` is to add it to your
`Gemfile`:
Expand All @@ -28,7 +28,7 @@ Then, install it on the command line:
$ bundle install
```

#### Ruby 1.8 is no longer supported!
#### Ruby 1.8 is no longer supported!

If you must use a ruby 1.8 version (MRI 1.8.7, REE, Rubinius 1.8, or JRuby 1.8), you need to use version 0.8.x.
Versions after 0.9.0 do not support ruby 1.8. To specify version 0.8, include this in your Gemfile:
Expand Down Expand Up @@ -177,7 +177,7 @@ In an initializer:
ActiveSupport.on_load(:active_model_serializers) do
# Disable for all serializers (except ArraySerializer)
ActiveModel::Serializer.root = false

# Disable for ArraySerializer
ActiveModel::ArraySerializer.root = false
end
Expand Down Expand Up @@ -287,33 +287,34 @@ class PostSerializer < ActiveModel::Serializer
end
```

If you would like the key in the outputted JSON to be different from its name
in ActiveRecord, you can use the `:key` option to customize it:

```ruby
class PostSerializer < ActiveModel::Serializer
# look up :author_name and :msg_size on the model, but use :author and :size in the JSON
attributes :id, :body, {author_name: :author, msg_size: {key: :size}}

# look up :subject on the model, but use +title+ in the JSON
attribute :subject, key: :title
has_many :comments
end
```

The type of a computed attribute (like :full_name above) is not easily
calculated without some sophisticated static code analysis. To specify the
type of a computed attribute:

```ruby
class PersonSerializer < ActiveModel::Serializer
attributes :first_name, :last_name, {full_name: :string}
attributes :first_name, :last_name, {full_name: {type: :string}}

def full_name
"#{object.first_name} #{object.last_name}"
end
end
```

If you would like the key in the outputted JSON to be different from its name
in ActiveRecord, you can use the `:key` option to customize it:

```ruby
class PostSerializer < ActiveModel::Serializer
attributes :id, :body

# look up :subject on the model, but use +title+ in the JSON
attribute :subject, key: :title
has_many :comments
end
```

If you would like to add meta information to the outputted JSON, use the `:meta`
option:

Expand Down
15 changes: 9 additions & 6 deletions lib/active_model/serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ def to_s
class_attribute :_attributes
self._attributes = {}

class_attribute :_attribute_types
self._attribute_types = {}

class_attribute :_associations
self._associations = {}

Expand All @@ -86,7 +89,9 @@ def attributes(*attrs)

attrs.each do |attr|
if Hash === attr
attr.each {|attr_real, key| attribute(attr_real, key: key) }
attr.each do |attr_real, key_or_hash|
attribute(attr_real, (key_or_hash.is_a?(Hash) ? key_or_hash : {key: key_or_hash}))
end
else
attribute attr
end
Expand All @@ -98,6 +103,8 @@ def attribute(attr, options={})

attr = attr.keys[0] if attr.is_a? Hash

self._attribute_types.update(attr => options[:type]) if options[:type]

unless method_defined?(attr)
define_method attr do
object.read_attribute_for_serialization(attr.to_sym)
Expand Down Expand Up @@ -208,11 +215,7 @@ def schema
else
# Computed attribute (method on serializer or model). We cannot
# infer the type, so we put nil, unless specified in the attribute declaration
if name != key
attrs[name] = key
else
attrs[key] = nil
end
attrs[key] = _attribute_types[name]
end
end

Expand Down
19 changes: 18 additions & 1 deletion test/serializer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,23 @@ def test_attribute_method_with_name_as_serializer_prefix
}, hash)
end

def test_attributes_method_with_computed_attribute_with_type
user = User.new
user_serializer = UserAttributesWithComputedSerializer.new(user, scope: {})

hash = user_serializer.as_json

assert_equal UserAttributesWithComputedSerializer.schema, {
attributes: { first_name: :string, last_name: :string, full_name: :string },
associations: {}
}

assert_equal({
user_attributes_with_computed: { first_name: "Jose", last_name: "Valim",
full_name: "Jose Valim" }
}, hash)
end

def test_serializer_receives_scope
user = User.new
user_serializer = UserSerializer.new(user, scope: { scope: true })
Expand Down Expand Up @@ -647,7 +664,7 @@ def can_edit; end
def can_view; end
def drafts; end

attributes :name, :age, { can_edit: :boolean }, :can_view
attributes :name, :age, { can_edit: {type: :boolean} }, :can_view
has_many :posts, serializer: Class.new
has_many :drafts, serializer: Class.new
has_one :parent, serializer: Class.new
Expand Down
16 changes: 16 additions & 0 deletions test/test_fakes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,22 @@ def serializable_hash
end
end

class UserWithColumns < User
def self.columns_hash
{ "first_name" => Struct.new(:type).new(:string), "last_name" => Struct.new(:type).new(:string) }
end
end

class UserAttributesWithComputedSerializer < ActiveModel::Serializer
attributes :first_name, :last_name, full_name: {type: :string}

def self.model_class; UserWithColumns; end

def serializable_hash
attributes.merge(:full_name => "#{attributes[:first_name]} #{attributes[:last_name]}")
end
end

class CommentSerializer
def initialize(comment, options={})
@object = comment
Expand Down