Cerializable
Plain old Ruby serialization.
Rather than using something like jbuilder, you could use cerializable to generate hashes for your model instances and then render the hashes as JSON. This can aid in performance.
It also gives you the option to customize the process via custom serialization options.
Installation
For use with ActiveRecord, simply add cerializable to your Gemfile:
gem 'cerializable', '~> 0.2.0'For other ORMs, you'll also need to include Cerializable::Model in your ORM's base class.
Usage
Call cerializable in the models you wish to use Cerializable with:
class Comment < ApplicationRecord
cerializable
endDefine corresponding serializer modules:
module CommentSerializer
def run(comment, options)
{ id: comment.id,
userId: comment.user_id,
body: comment.body,
ancestorCount: comment.ancestor_count,
childComments: options[:children] || comment.child_comments.map { |c| c.cerializable_hash }
end
endIf you wish, you can specify a serializer module to use:
class Comment < ApplicationRecord
cerializable serialize_with: SomeSerializerModule
endIn these serializer modules, you can define methods and include other modules without polluting the corresponding models.
The only requirement is that the serializer modules have a run method which accepts two arguments and returns a hash detailing how instances of your model should be serialized.
Your model's cerializable_hash methods will use the run method of their serializers to produce their results.
Like serializable_hash, cerializable_hash accepts :only, :except, and :methods options which can be passed as a
symbol or as an array of symbols.
Using the :only option will return a hash that only has the specified keys:
> comment.cerializable_hash(only: :id)
=> { id: 1 }Using the :except option will return a hash that has all default keys except those specified:
> comment.cerializable_hash(except: [:user_id, :ancestorCount, :childComments])
=> { id: 1, body: '...sushi? ' }Using the :methods option add will add a key and value for each method specified.
The key is the method name and the value is the return value given when calling the method on the model instance.
The :methods option is processed after the :only and :except options:
> comment.cerializable_hash(only: id, methods: :hash])
=> { id: 1, hash: -2535926706119161824 }Custom Serialization Options
You can pass in custom options that your serializer modules make use of.
Common use cases for custom options might be for handling roles or versions.
In the example serializer above, we're using a custom :children option.
This allows us to build a comment thread's hierarchy without querying for each comment's children:
class CommentTreeService
def self.call(comment_thread, options = { order: { ancestor_count: :asc, id: :desc } })
self.new.for_thread(comment_thread, options)
end
def initialize
@comments_hash = {}
@result = []
@children_resolver = proc do |parent_comment|
children_array = []
@comments_hash.each_pair do |id, comment|
if comment.parent_type == 'Comment' && comment.parent_id == parent_comment.id
@comments_hash.delete(id)
children_array << comment.cerializable_hash(children: @children_resolver.call(comment))
end
end
children_array
end
end
def for_thread(comment_thread, options)
comment_thread.comments
.order(options[:order])
.each { |comment| @comments_hash[comment.id] = comment }
@comments_hash.each_pair do |id, comment|
if comment.parent_type == 'CommentThread'
@comments_hash.delete(id)
@result << comment.cerializable_hash(children: @children_resolver.call(comment))
end
end
@result
end
end