Skip to content

Fullscript/ftl-serializer

Repository files navigation

FTL (Faster Than Light) Serializer 🚀

A ruby serializer that can make the kessel run in less than 12 parsecs.

Why FTL?

This library is an extraction from Fullscript. We originally wrote this at a time when there weren't many options out there. Serializers were mostly slow, with over-complicated DSLs, or they weren't flexible enough for our needs (for example only supporting a specific spec like JSON:API).

Our main design decisions centered around 3 principles.

  • Speed. Slow stuff happens at boot time rather than at runtime.
  • Simple DSL. We've opted for a very simple DSL. Mostly to avoid any meta-programming slowness (no has_many, belongs_to, etc. that you see in most serializers) but we also wanted an early-career developer to pick up FTL without much effort.
  • Flexibility. You should be able to serialize data to an existing spec (like JSON:API) or come up with your own.

Installation

Add this line to your application's Gemfile:

gem 'ftl-serializer'

And then execute:

$ bundle

Or install it yourself as:

$ gem install ftl-serializer

Then in an initializer you just need to point to the path(s) where your serializers live.

# app/initializers/ftl.rb

FTL::Configuration.serializer_paths = ["#{Rails.root.join}/app/serializers"]

Usage

Example Rails Model

For our examples here's a simple Rails Model. (Note that FTL can accept any data structure and isn't limited to models.)

class Ship
  belongs_to :classification
  
  attr_accessor :id, :name, :special_modifications
end
ship = Ship.new(id: 10, name: "Millenium Falcon", special_modifications: true, classification_id: 20)

Serializer Definition

We define our serializer by inheriting from FTL::Serializer::Base

class FastestHunkOfJunkInTheGalaxy < FTL::Serializer::Base
  attributes :name, :special_modifications, :type

  def type
    obj.classification.name
  end
end

.to_h

hash = FastestHunkOfJunkInTheGalaxy.new(ship).to_h

returns:

{
  id: "10",
  name: "Millenium Falcon",
  special_modifications: true,
  type: "YT-1300 Corellian light freighter"
}

.to_json

json_string = FastestHunkOfJunkInTheGalaxy.new(ship).to_json

returns:

{
  "id": "10",
  "name": "Millenium Falcon",
  "special_modifications": true,
  "type": "YT-1300 Corellian light freighter"
}

Options

format

By default FTL underscores the key names but it also supports camel case.

class FastestHunkOfJunkInTheGalaxy < FTL::Serializer::Base
  # Available options :camel, :underscore (default)
  format :camel
end

Examples:

keys :camel # "some_key" => "someKey"
keys :underscore # "some_key" => "some_key"

root

FTL can also support a root key.

class FastestHunkOfJunkInTheGalaxy < FTL::Serializer::Base
  root "starship"
end

Returns:

{
  "starship": {
    "id": "10",
    "name": "Millenium Falcon",
    "special_modifications": true,
    "type": "YT-1300 Corellian light freighter"
  }
}

Roots can also be disabled when you are initializing your serializer. (Occasionally helpful when calling other serializers from within another serializer.)

FastestHunkOfJunkInTheGalaxy.new(obj).root(:disabled)

Attributes

Attributes are defined using the attributes keyword.

class FastestHunkOfJunkInTheGalaxy < FTL::Serializer::Base
  attributes :name
end

Custom attributes can be overridden by defining a method.

The object (that is passed into the serializer) is referrenced to as obj.

class FastestHunkOfJunkInTheGalaxy < FTL::Serializer::Base
  attributes :name

  def name
    obj.first_name
  end
end

Locals

In some cases, you might want to use some ancillary data that's not necessarily available on your objects. For example, current_user or current_account are examples of a dependency that you may want to inject into your serializer.

To do this you can just pass a locals hash into the serializer.

class FastestHunkOfJunkInTheGalaxy < FTL::Serializer::Base
  attributes :name, :current_pilot

  def current_pilot
    locals.current_pilot.full_name
  end
end

# ...
lando = User.find_by(first_name: "Lando", last_name: "Calrissian")
serializer = FastestHunkOfJunkInTheGalaxy.new(ship, { locals: { current_pilot: lando } })
serializer.to_h

Locals can be in a hash format or it can be chained as a method.

# This is the same:
FastestHunkOfJunkInTheGalaxy.new(ship, { locals: { current_pilot: lando } })

# as this:
FastestHunkOfJunkInTheGalaxy.new(ship).locals(current_pilot: lando)

Loading

It's also worth mentioning how serializers are loaded. They're hooked into a Rails::Railtie that loads up the serializers and sets all the attributes during the Rails boot time. We did this for speed so that everything is ready to go and we don't need to do any expensive meta-programming when you call your serializer. All the attributes are set and you just need to pass it some data to serialize.

If you ever need to manually load up a serializer it's just:

FTL::Serializer.bootstrap!

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec 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.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/fullscript/ftl-serializer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the Ftl project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.

About

FTL (Faster Than Light) is a ruby serializer that is optimized for simplicity and speed.

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published