David Geismar
David Geismar

Reputation: 3422

Dynamically building accessors in ruby on object initialization

I created a ruby wrapper around a json API that converts json formatted responses into ruby object. A typical resource looks like that :

module Learning360
  class User
    attr_accessor(
      :_id,
      :mail,
      :firstName,
      :lastName,
      :assignedPrograms,
      :paths,
      :certifications,
      :championAchievements,
      :comments,
      :completedPrograms,
      :groups,
      :imageUrl,
      :labels,
      :lastLoginAt,
      :championStatus,
      :learnerAchievements,
      :managers,
      :messages,
      :publications,
      :reactions,
      :skills,
      :subordinates,
      :toDeactivateAt,
      :totalTimeSpentInMinutes,
      :custom
    )

    def initialize(options = {})
      options.map { |(k, v)| send("#{k}=", v) }
    end
  end
end

When I receive the json payload, I pass it to the initializer as a hash of options and I then assign each one of the key its value as an instance variable. This works well as long as I maintain an updated list of attr_accessor. However if the API decides to change the naming of its keys or add a new key this will throw a

undefined method `unexpected_key_from_api=' for #<Learning360::User>

How can I avoid that problem and make my wrapper more robust. I would like my wrapper object to just take any key from the response and automatically build the corresponding accessor if it doesnt exist.

Upvotes: 1

Views: 385

Answers (1)

sbagdat
sbagdat

Reputation: 850

You can create attributes with attr_accessor inside the initialize method. You only need to reach to it like below:

module Learning360
  class User
    def initialize(options = {})
      options.each do |(k, v)|
        self.class.attr_accessor(k)
        send("#{k}=", v)
      end
    end
  end
end

user = Learning360::User.new({ name: "Matz" })
puts user.name  # Matz

It is also possible to use class name diectly just like User.attr_accessor(k).

Upvotes: 2

Related Questions