Gem to interact with a third party API I am writing a gem for users to interact with a third party api. I want users to have easy and natural access to objects and properties delivered by the JSON payload.

The collection of entities delivered are called Entries so after making an http request I pass the json response to an Entries object like so:

  class Entries
    include Enumerable

    attr_reader :body, :entries

    def initialize(body)
      @body = body
      @entries = body.map { |entry| Entry.new(entry) }
    end

    def each(&amp;block)
      entries.each(&amp;block)
    end
  end


And for my single Entry object I have written:

  class Entry

    attr_reader :properties, :attributes

    def initialize(attributes)
      @attributes = attributes
      @properties = attributes.keys

      properties.each do |prop|
        define_singleton_method prop do
          @attributes[prop]
        end
      end
    end

    def to_s
      "#{attributes[:title]} created on #{attributes[:created_at]}"
    end

    def is_entry?
      true
    end
  end


I haven't tested this much but things seem to be working so far. Users of my gem can now do this:

entries = client.entries(content_type: 'shirts')
entries.each { |entry| puts entry.title }


But I am worried this is an inefficient way of doing things and that perhaps I am using too much memory in these loops.

Also I want to keep magical meta-programming to a minimal but still achieve this. Is this a reasonable way to do this in Ruby? Would love some feedback and advice.
The first thing I'd do is to create your Entry objects on demand, rather than in Entries#initialize:

class Entries
  include Enumerable
  ...
  def initialize(body)
    @body = body
  end
  ...
  def each(&amp;block)
    @body.each do |entry| 
      block.call(Entry.new(entry))
    end
  end
end


As for dynamically creating methods for each attribute, I'd be wary of doing that. Without knowing your JSON API schema, there could be two possibilities going on:


Each Entry always has the same attributes, so you are spending processing time and memory dynamically generating the same methods every time.
Each Entry may have wildly differing attributes – in which case your clients won't know at runtime which methods each entry object actually responds to, and will have to use some checks of their own, e.g., #respond_to? in order for their code to work reliably.


In either case, you really want your clients to know that each Entry object they receive will have the same attributes at all times. I'd be inclined to explicitly define each attribute method, e.g.:

class Entry
  attr_reader :attributes
  ...
  def title
    attributes[:title]
  end
end


This gives you the option of how to deal with missing attributes in the API. For example, if an array field, such as authors, is missing, you could choose to return an empty array instead:

def authors
  attributes.fetch(:authors, [])
end


I know the non-metaprogramming way can seem like a more boring way of doing things, but even though your entry.rb source code will be much longer, everything will be much clearer and easier to maintain and debug. 

And at the end of the day, your gem's users will end up with an interface which is much more predictable – and the methods you include will be more easily documentable through YARD or any other automated documentation tool.