Releases: foca/granola
v1.0: Stable and ready
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. ❤️ 💜 💙 💚 💛
Deprecations
- Removed all methods deprecated in v0.13.0
Happy serializing! 🎉
v0.13.0
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.
Improvements
-
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. (#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 anenv
method. Background: Rails 5.1 will deprecateActionController::Metal#env
in favor of using theRequest
object.
Bug Fixes
-
Serialize Hashes as primitive types in the Rack helper. This should work:
errors = { error: "Everything is terrible" } granola(errors, status: 400)
Deprecations
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. UseGranola.renderable_formats
andGranola.renderer
instead.Granola::Serializer#mime_type(format)
has been deprecated in favor ofGranola.renderer(format).content_type
. Serializers shouldn't know about content types.Granola::Serializer#render(format)
has been deprecated in favor ofGranola.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! 🎉
New renderer registration and performance improvements
We're getting closer and closer to v1.0.0! In the meantime, here's another intermediate release with some improvements.
Breaking changes
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.
Performance Improvements
- By dropping automatic switching to MultiJson we probably gained a ~10x performance boost for most users. Read the linked issue for more background 🎉
Minor improvements
- The benchmarks have been improved, and include a comparison against ActiveModelSerializers now.
Enjoy!
AKA Almost v1.0… again
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. 😜
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 💖
AKA Almost 1.0
This marks the last general release before we release 1.0
🎉 🎉 🎉
The API should be considered stable and public. Basically, it shouldn't change after this release. The notable changes are:
These changes are backwards-incompatible 💥
Serializer#attributes
was renamed toSerializer#serialized
. See commit.- The rack helper is now called
#granola
. So in Cuba you'd do:halt granola(current_user, with: SessionSerializer)
, for example. Thanks @djanowski for the idea 💙 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 response304
to clients. See commit.
These are backwards-compatible 👌
- Allow passing default headers to the rack helper. Thank @lucasefe for this one 💜. See commit.
- Don't hate so much on MultiJson ❤️ As @luislavena put it, the performance problems alluded to in v0.0.4 have been fixed. We still default to our own way of configuring the JSON backend, but if MultiJson is available then we use that. See commit.
- Open the road for multiple formats (such as serializing to YAML or MsgPack right from Granola). The rack helper now includes an
as:
keyword that specifies the format to render (defaults to:json
). As long as you defineSerializer::MIME_TYPES[format]
and a#to_#{format}
method in your serializers, then you can justgranola(object, as: <format>)
. See commit
Thanks to everyone who offered suggestions and feedback for making this possible. I 💗 you.
v0.0.4: Now with 100% less MultiJson
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 #1 for context.
❤️ ❤️ ❤️ Thanks @djanowski and @guilleiguaran for bringing this up! ❤️ ❤️ ❤️
v0.0.3: Minor Bugfixes
A couple bugs squashed. Getting there!
v0.0.2: Version 0.0.2
Cleaning up the API a bit so it's more flexible and simpler to use. Still… use it at your own risk.
The First One
This code isn't production ready, but feel free to play around with it.