Simple JSON serializers (Cereal-izers. GET IT?)
After a couple months of production use without any bugs, and with the stable API finalized in v0.13, I'm confident to finally release v1.0. π π π
This has been a long time coming, with over 2 years of work to make sure we can have the smallest and fastest ruby library to give some structure around serializing responses from your API.
Many thanks go to everyone who contributed with feedback, comments, guidance, or code. β€οΈ π π π π
Happy serializing! π
We keep getting closer to v1.0. This release is mostly improvements to allow plugins to integrate better with Granola, and formalizing the APIs we want to have.
Introduce Renderer
objects. These wrap around the rendering callable. Add a couple methods around the renderer registration:
Granola.render :json, via: Oj.method(:dump),
content_type: "application/json"
Granola.render :yaml, via: YAML.method(:dump),
content_type: "text/x-yaml"
# List all registered rendering formats
Granola.renderable_formats #=> [:json, :yaml]
# Get a Renderer object
json_renderer = Granola.renderer(:json)
json_renderer.content_type #=> "application/json"
json_renderer.render(a_serializer) #=> JSON stream
In the Rack helper, automatically choose the best rendering format based on a request's Accept
header. (https://github.com/foca/granola/pull/14)
Allow customizing how we lookup constants when finding a serializer class for a given object.
Granola::Util.constant_lookup = API.method(:const_get)
Granola::Util.serializer_class_for(User.new) #=> API::UserSerializer (or NameError)
Add ruby 2.3's frozen string literal magic comments everywhere. This should give us a small performance boost.
Allow passing an env
Hash manually to the Rack helper, for contexts where we don't have access an env
method. Background: Rails 5.1 will deprecate ActionController::Metal#env
in favor of using the Request
object.
Serialize Hashes as primitive types in the Rack helper. This should work:
errors = { error: "Everything is terrible" }
granola(errors, status: 400)
As we move closer to finalizing the public API, we're getting rid of a few methods that will be gone in 1.0:
Granola::RENDERERS
. This constant is effectively private now, and shouldn't be accessed. Use Granola.renderable_formats
and Granola.renderer
instead.Granola::Serializer#mime_type(format)
has been deprecated in favor of Granola.renderer(format).content_type
. Serializers shouldn't know about content types.Granola::Serializer#render(format)
has been deprecated in favor of Granola.renderer(format).render(serializer)
. Rendering to a particular format is, again, orthogonal to a Serializer's responsibility, which is defining the structure something takes when serialized.Happy serializing! π
We're getting closer and closer to v1.0.0! In the meantime, here's another intermediate release with some improvements.
The old Granola.json = callable
is deprecated (and will be gone in v1.0). Now you can register any rendering mechanism (JSON or other formats) like so:
Granola.render :json, via: JSON.method(:generate), content_type: "application/json"
If you want your serializers to be renderable into other formats, just register them via Granola.render
. They all work the same now π
You can find the full rationale behind this change in the original pull request.
Enjoy!
After v0.9.0 I was pointed out that Serializer#serialized
was a pretty bad name for the thing that has not yet been serialized. :stuck_out_tongue_winking_eye:
Pretty fair. So last backwards-incompatible change before we reach 1.0: The method is now #data
:
class UserSerializer < Granola::Serializer
def data
{
"name" => object.name,
"email" => object.email,
"age" => object.age,
}
end
end
Thanks @pote for the feedback, and @soveran for the name suggestion :sparkling_heart:
This marks the last general release before we release 1.0
:tada: :tada: :tada:
The API should be considered stable and public. Basically, it shouldn't change after this release. The notable changes are:
Serializer#attributes
was renamed to Serializer#serialized
. See commit.#granola
. So in Cuba you'd do: halt granola(current_user, with: SessionSerializer)
, for example. Thanks @djanowski for the idea :blue_heart: See commit.Granola::Rack
no longer cares about checking freshness of a response. Instead, it makes the body defer to the call to each before actually calling #to_json
. This means that you can put Rack::ConditionalGet in the middleware stack and use that to make the response 304
to clients. See commit.as:
keyword that specifies the format to render (defaults to :json
). As long as you define Serializer::MIME_TYPES[format]
and a #to_#{format}
method in your serializers, then you can just granola(object, as: <format>)
. See commit
Thanks to everyone who offered suggestions and feedback for making this possible. I :heartpulse: you.
Don't depend on MultiJson, as it doesn't help performance-wise and it's easy to swap the JSON backend anyway. For example, if you were to change to Yajl:
require "yajl"
Granola.json = ->(obj, **opts) { Yajl::Encoder.encode(obj, opts) }
See https://github.com/foca/granola/issues/1 for context.
:heart: :heart: :heart: Thanks @djanowski and @guilleiguaran for bringing this up! :heart: :heart: :heart:
A couple bugs squashed. Getting there!
Cleaning up the API a bit so it's more flexible and simpler to use. Still⦠use it at your own risk.
This code isn't production ready, but feel free to play around with it.