Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/arturictus/easy_serializer

Semantic way to serialize your objects
https://github.com/arturictus/easy_serializer

api json json-api serializer

Last synced: 3 months ago
JSON representation

Semantic way to serialize your objects

Awesome Lists containing this project

README

        

# EasySerializer

[![Build Status](https://travis-ci.org/arturictus/easy_serializer.svg?branch=master)](https://travis-ci.org/arturictus/easy_serializer)
[![Gem Version](https://badge.fury.io/rb/easy_serializer.svg)](https://badge.fury.io/rb/easy_serializer)
[![Test Coverage](https://codeclimate.com/github/arturictus/easy_serializer/badges/coverage.svg)](https://codeclimate.com/github/arturictus/easy_serializer/coverage)
[![Code Climate](https://codeclimate.com/github/arturictus/easy_serializer/badges/gpa.svg)](https://codeclimate.com/github/arturictus/easy_serializer)
[![Issue Count](https://codeclimate.com/github/arturictus/easy_serializer/badges/issue_count.svg)](https://codeclimate.com/github/arturictus/easy_serializer)

Semantic serializer for making easy serializing objects.
EasySerializer is inspired in [ActiveModel Serializer > 0.10] (https://github.com/rails-api/active_model_serializers/tree/v0.10.0.rc3) it's a
simple solution for a day to day work with APIs.
It tries to give you a serializer with flexibility, full of features and important capabilities for caching.

Features:
- Nice and simple serialization DSL.
- Cache helpers to use with your favorite adapter like rails cache.

Advantages:
- Separated responsibility from Model class and serialization allowing multiple serializers for the same Model class, very useful for API versioning.
- In contraposition with active model serializers with EasySerializer you can serialize any object responding to the methods you want to serialize.
- EasySerializer is an small library with few dependencies.

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'easy_serializer'
```

And then execute:

$ bundle

Or install it yourself as:

$ gem install easy_serializer

Add the configuration file:

**Only if you need caching.**

_If your are in a Rails environment place this file at config/initializers_

```ruby
EasySerializer.setup do |config|
# = perform_caching
#
# Enable o disable caching.
# default: false
#
# config.perform_caching = true

# = cache
#
# Set your caching tool for the serializer
# must respond to fetch(obj, opts, &block) like Rails Cache.
# default: nil
#
# config.cache = Rails.cache
end
```

## Usage

### Simple example:

```ruby
user = OpenStruct.new(name: 'John', surname: 'Doe')

class UserSerializer < EasySerializer::Base
attributes :name, :surname
end

UserSerializer.call(user)
# =>
{
name: 'John',
surname: 'Doe'
}
```
**Using blocks:**

Object being serialized is pass in the block as a first argument.

```ruby
class UserSerializer < EasySerializer::Base
attribute(:name) { |user| user.name.capitalize }
attribute(:surname) { |user| user.surname.capitalize }
end
```

**Using helpers in blocks:**

Blocks are executed in the serializer instance, this way you can build your helpers and use them inside the blocks.

```ruby
class BlockExample < EasySerializer::Base
attribute :name do |object|
upcase object.name
end

def upcase(str)
str.upcase
end
end
```
**Passing options as a second argument:**

```ruby
class OptionsSerializer < EasySerializer::Base
attribute :name
attribute :from_opts do
options[:hello]
end
end

OptionsSerializer.call(OpenStruct.new(name: 'Dave'), hello: "hello with from options")
# => {:name=>"Dave", :from_opts=>"hello with from options"}
```

**Changing keys:**

```ruby
class UserSerializer < EasySerializer::Base
attribute :name, key: :first_name
attribute(:surname, key: :last_name) { |user| user.surname.capitalize }
end
```

**Using defaults:**

Default will only be triggered when value is `nil`

```ruby
obj = OpenStruct.new(name: 'Jack', boolean: nil, missing: nil)

class DefaultLiteral < EasySerializer::Base
attribute :name
attribute :boolean, default: true
attribute(:missing, default: 'anything') { |obj| obj.missing }
end

output = DefaultLiteral.call(obj)
output.fetch(:name) #=> 'Jack'
output.fetch(:boolean) #=> true
output.fetch(:missing) #=> 'anything'
```

Using blocks:

```ruby
obj = OpenStruct.new(name: 'Jack', boolean: nil, missing: nil)

class DefaultBlock < EasySerializer::Base
attribute :name
attribute :boolean, default: proc { |obj| obj.name == 'Jack' }
attribute :missing, default: proc { |obj| "#{obj.name}-missing" } do |obj|
obj.missing
end
end

output = DefaultBlock.call(obj)
output.fetch(:name) #=> 'Jack'
output.fetch(:boolean) #=> true
output.fetch(:missing) #=> 'Jack-missing'
```

### Serializing nested objects

```ruby
user = OpenStruct.new(
name: 'John',
surname: 'Doe',
address: OpenStruct.new(
street: 'Happy street',
country: 'Wonderland'
)
)

class AddressSerializer < EasySerializer::Base
attributes :street, :country
end

class UserSerializer < EasySerializer::Base
attributes :name, :surname
attribute :address, serializer: AddressSerializer
end

UserSerializer.call(user)
# =>
{
name: 'John',
surname: 'Doe',
address: {
street: 'Happy street',
country: 'Wonderland'
}
}
```

**Removing keys from nested hashes:**

```ruby
class UserSerializer < EasySerializer::Base
attribute :name, :lastname
attribute :address,
key: false,
serializer: AddressSerializer
end
UserSerializer.call(user)
# =>
{
name: 'John',
surname: 'Doe',
street: 'Happy street',
country: 'Wonderland'
}
```

**Serializer option accepts a Block:**

The block will be executed in the Serializer instance.

```ruby
class DynamicSerializer < EasySerializer::Base
attribute :thing, serializer: proc { serializer_for_object }
attribute :d_name

def serializer_for_object
"#{object.class.name}Serializer".classify
end
end
```
Inside the block is yielded the value of the method

```ruby
thing = OpenStruct.new(name: 'rigoverto', serializer: 'ThingSerializer')
obj = OpenStruct.new(d_name: 'a name', thing: thing)

class DynamicWithContentSerializer < EasySerializer::Base
attribute :thing,
serializer: proc { |value| to_const value.serializer }
# => block will output ThingSerializer
attribute :d_name

def to_const(str)
Class.const_get str.classify
end
end

DynamicWithContentSerializer.call(obj)
```

### Collection Example:

```ruby
user = OpenStruct.new(
name: 'John',
surname: 'Doe',
emails: [
OpenStruct.new(address: '[email protected]', type: 'work')
]
)

class EmailSerializer < EasySerializer::Base
attributes :address, :type
end

class UserSerializer < EasySerializer::Base
attributes :name, :surname
collection :emails, serializer: EmailSerializer
end

UserSerializer.call(user)
# =>
{
name: 'John',
surname: 'Doe',
emails: [ { address: '[email protected]', type: 'work' } ]
}
```

### Cache

**Important** cache will only work if is set in the configuration file.

**Caching the serialized object:**

Serialization will happen only once and the resulting hash will be stored in the cache.

```ruby
class UserSerializer < EasySerializer::Base
cache true
attributes :name, :surname
end
```

**Caching attributes:**

Attributes can be cached independently.

```ruby
class UserSerializer < EasySerializer::Base
attributes :name, :surname
attribute :costly_query, cache: true
end
```

Of course it works with blocks:

```ruby
class UserSerializer < EasySerializer::Base
attributes :name, :surname
attribute(:costly_query, cache: true) do |user|
user.best_friends
end
end
```

Passing cache key:

```ruby
class UserSerializer < EasySerializer::Base
attribute(:costly_query, cache: true, cache_key: 'hello') do |user|
user.best_friends
end
end
```

Passing cache key block:

```ruby
class UserSerializer < EasySerializer::Base
attribute(
:costly_query,
cache: true,
cache_key: proc { |object| [object, 'costly_query'] }
) do |user|
user.best_friends
end
end
```

Passing options to the cache:

Any option passed in the cache method not specified for EasySerializer will be
forwarded as options to the set Cache as options for the fetch method.

example:

```ruby
class OptionForRootCache < EasySerializer::Base
cache true, expires_in: 10.minutes, another_option: true
attribute :name
end
```

Cache fetch will receive:

```ruby
EasySerializer.cache.fetch(
key,# object or defined key
expires_in: 10.minutes,
another_option: true
)
```

Use **cache_options** in attributes

```ruby
class OptionForAttributeCache < EasySerializer::Base
attribute :name, cache: true, cache_options: { expires_in: 10.minutes }
end
```

**Caching Collections:**

Cache will try to fetch the cached object in the collection **one by one, the whole collection is not cached**.

```ruby
class UserSerializer < EasySerializer::Base
attributes :name, :surname
collection :address, serializer: AddressSerializer, cache: true
end
```

### Complex example using all features

```ruby
class PolymorphicSerializer < EasySerializer::Base
cache true
attribute :segment_type do |object|
object.subject.class.name.demodulize
end
attribute :segment_id do |object|
object.id
end
attributes :initial_date,
:end_date

attribute :subject,
key: false,
serializer: proc { |serializer| serializer.serializer_for_subject },
cache: true
collection :elements, serializer: ElementsSerializer, cache: true

def serializer_for_subject
object_name = object.subject_type.demodulize
"#{object_name}Serializer".constantize
end
end
```

```ruby
PolymorphicSerializer.call(Polymorphic.last)
# => Hash with the object serialized
```

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/arturictus/easy_serializer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.

## License

The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).