Because we still have applications using Rails 3.2, and it’s really such a pain to upgrade apps with so many complicated logics like us.
ActiveSupport::JSON module provides a super simple API composed by two methods:
ActiveSupport::JSON.encode(object) takes a Ruby object as value and returns a JSON-encoded string. On the opposite, ActiveSupport::JSON.decode(string) takes a JSON-encoded string and returns the corresponding Ruby object.
As I mentioned in my previous article, we should implement two more methods to make our custom object serialization work: to_json and self.json_create. Do we have to do that if we are using Rails a.k.a ActiveSupport::JSON?
The common usage of serializing custom objects in Rails is serializing them to a string field stored in database. And we could achieve that with writing our own custom serializer for custom objects. I won’t cover this in this post, maybe in the near future I will do that.
So, like the previous post, we will pay some attention on the performance of serialization in Rails.
ActiveSupport depends on multi_json gem, which is a simple library that allows you to semalessly provide multiple JSON backends with intelligent defaulting. And ActiveSupport::JSON uses json as its default engine, we could know that by ActiveSupport::JSON.engine # => MultiJson::Adapters::JsonGem.
Then let’s do some benchmarking, eg: setting oj as ActiveSupport’s default engine.
And the result may just blow your mind. We already specify our engine to Oj, right? They are supposed to behave like each other . What’s wrong with active_support?
The root cause is ActiveSupport’s implementation problem. In active_support/core_ext/object/to_json.rbfile :
Did you see the to_json method? It will use ActiveSupport::JSON.encode every time with hard-coded guaranteed.
This monkeypatch was introduced to fix a problem (see comments above the code), but also caused another pain: If you are currently using the JSON/oj/whatever gem adapter with Rails (AS::JSON::Encoding), and you have been calling object#to_json, you are actually using Rail’s pure Ruby JSON encoder. And this explains why the performance suck so much.
Then how to fix this? The solution is quite simple: apply a similar change as the one in active_support/core_ext/object/to_json.rb, monkeypatch to override to_json method again, let it use Oj to do encoding job.
Rails 4.1 removes multi_json dependency, and the previous patch/gem is unnecessary and will no longer work. Instead, if you want to use oj to deal with JSON, use the oj_mimic_jsongem with oj in your Gemfile to have Oj mimic the JSON gem and be used in its place by ActiveSupport JSON handling:
Let’s talk about serializing objects in Ruby today.
Built-In Serialization Mechanisms
Ruby has two object serialization mechanisms built into the lauguage. One is what we are very familiar of, YAML(YAML Ain’t Markup Language), which is also human readable format, and the other one is binary format.
In Ruby, any objects can be serialized into YAML format. And it’s really easy:
# app/use_cases/post_comment.rb# Called from the "create" action in a controllerclassPostCommentdefinitialize(user,entry,attributes)@user=user@entry=entry@attributes=attributesenddefpost@firstname.lastname@example.org@comment.assign_attributes(@attributes)@comment.entry=@email@example.com!LanguageDetector.new(@comment).set_languageSpamChecker.new(@comment).check_spamCommentMailer.new(@comment).firstname.lastname@example.org_on_twitteremail@example.com_on_facebook?@commentendprivatedefpost_to_twitterPostToTwitter.new(@user,@comment).postenddefpost_to_facebookPostToFacebook.new(@user,@comment).action(:comment)endend