When to use Ruby's OpenStruct

OpenStruct is valuable when you want to treat data as an object and you have no control on the data you’re recieving (i.e. Working with someone else’s API).

Lets take, the internet’s favorite, 4Chan and their Official API as an example.

This API has some interesting ‘features’ like both name and id being required for most calls. Maybe because the ids are not globally unique, or their API is just a quick hack. (probably the latter)

More importantly, like most APIs, you don’t actually know what data you’re getting back on any given call.

API Endpoint to get posts within a thread:

     "https://a.4cdn.org/#{board_name}/thread/#{thread_id}.json"

The response back would be an array containing of Hashes representing posts either with an image

    {"no"=>737260859, #this an id?
      "now"=>"06/28/17(Wed)21:21:23", #prob won't parse this.
      "name"=>"Anonymous", #isnt everyone Anon?
      "com"=>"i found a really nice looking tree today",
      "filename"=>"3dbc552f-4888-441a-a2c0-779145ebb3de",
      "ext"=>".jpg",
      "w"=>540,
      "h"=>960,
      "tn_w"=>70, #There is a thumbnail i think?
      "tn_h"=>125, #But I do not know where it is
      "tim"=>1498699283609, #??
      "time"=>1498699283, #Same as "now"?
      "md5"=>"So/ELYW+RuQzatlp6wIH6w==",
      "fsize"=>56508,
      "resto"=>737260138}, #???????

or without an image

    {"no"=>737260859,
      "now"=>"06/28/17(Wed)21:21:23",
      "name"=>"Anonymous",
      "com"=>"i found a really nice looking tree today",
      "time"=>1498699283,
      "md5"=>"So/ELYW+RuQzatlp6wIH6w==",
      "resto"=>737260138},

or maybe something else?

With OpenStruct you are able to write methods on arbitrary data in very clean ways:

#The `if self` statements can be abstracted a bit better but besides the point of the post.

class Post < OpenStruct
  def image_url
    "https://i.4cdn.org/#{self.board_name}/#{self.tim}#{self.ext}" if self.tim
  end

  def thread_url
    "http://boards.4chan.org/#{self.board_name}/thread/#{self.thread_id}/#{self.semantic_url}" if self.semantic_url
  end
end

To invoke it on data, you just simply Post.new arbitrary_post_hash