前提
以下のコードにおいて 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 になることがあるため、データベースへの保存ができません。