前提
以下のコードにおいて tweet
は Twitter::Tweet
クラスのオブジェクトであるとします。
結論
tweet
に対して to_json
メソッドを用います。結果として得られる値は JSON の文字列なので、データベースにそのまま保存できます*1。
serialized_tweet_object = tweet.to_json
上記の serialized_tweet_object
をオブジェクト(Twitter::Tweet
クラスのオブジェクト)に戻すためには 、Twitter::Tweet
クラスのオブジェクトを作成する際に、コンストラクタの引数として元々のハッシュを渡せばよいです。
元々のハッシュとは、serialized_tweet_object
を JSON からハッシュに変換したものです。一点注意することは、ハッシュのキーはシンボルでなければいけないということです。したがって、JSONJ.parse
の引数に symbolize_names: true
を付与することを忘れないようにします。
たとえば、以下のようになります。
original_tweet_object = Twitter::Tweet.new(JSON.parse(serialized_tweet_object, symbolize_names: true))
これで、original_tweet_object
は Twitter::Tweet
クラスの各種 API を用いることができるように復元できました。
背景
なぜツイートのオブジェクトをシリアライズして保存したいのでしょうか。
それはとにもかくにも「Twitter の API には利用に一定の制限があるから」です*2。
例えば、実行回数制限があります。なので、ひたすらにツイートを取得するとあっという間に上限に達してしまいます。このことから、「一度取得したデータは無用に再取得したくないので、大切にしておきたい」という動機が生まれてきます。また、過去に遡れるツイートの個数や期限にも制限があります。この制限もまた、同様の動機を生みます。
さらに「シリアライズ」する理由は、「デシリアライズしたオブジェクトを Twitter gem の API を用いて利用したいから」です。例えば、API の戻り値の JSON をデータベースに保存すると Twitter gem の API は(そのままでは)使えません。オブジェクトをデータベースに格納し、オブジェクトとして復元すれば、Twitter gem の API を使うことができ、DRY で冪等な記述が期待できます。
補足
ツイートのオブジェクトだけではなく、ユーザのオブジェクトも同様にして保存することができます。
また、このシリアライズされたデータだけを愚直に生ログのように溜め込むことで、ツイートやユーザの履歴*3を保存することができます。シリアライズされたデータさえ蓄えていれば、あとからどうにでもなります。
Marshal ではだめなのか
Marshal
を用いればオブジェクトをそのまま String にできますが、null文字が含まれる String になることがあるため、データベースへの保存ができません。