Erik J
Erik J

Reputation: 838

Ruby method with argument hash with default values: how to DRY?

I have an initialize method that accepts a hash to set some instance variables. This is basically what I'm trying to do:

class Ad
  DEFAULT_PAGE = 'index'.freeze

  DEFAULT_CHANNEL = 'general'.freeze

  DEFAULT_AREA = 'general'.freeze

  attr_accessor :page, :area, :channel

  def initialize args={}
    @page = args[:page] || DEFAULT_PAGE
    @area = args[:area] || DEFAULT_AREA
    @channel = args[:channel] || DEFAULT_CHANNEL
  end

  # ...
end

I saw a tip to allow dynamic setting of instance variables, but I'm not sure how to also include the default values...

  def initialize args={}
    args.each do |attr,val|
      instance_variable_set("@#{attr}", val) unless val.nil?
    end
  end

Can I refer to a constant dynamically? Or any better ways of doing this sort of thing are welcome!

... I also realize that attr_accessor variables can be set individually. But I just want to do it like this. :)

Upvotes: 3

Views: 6488

Answers (2)

Harish Shetty
Harish Shetty

Reputation: 64363

Try this:

  def initialize args={}
    defaults = {
      :page    => DEFAULT_AREA, 
      :area    => DEFAULT_AREA, 
      :channel => DEFAULT_CHANNEL
    }
    args.reverse_merge(defaults).each do |attr,val|
      instance_variable_set("@#{attr}", val) unless val.nil?
    end
  end

Upvotes: 1

Jorge Israel Peña
Jorge Israel Peña

Reputation: 38606

This one also only creates the instance variables if they are in your defaults hash, so that you don't accidentally create/overwrite other instance variables.

I'm assuming you meant to say unless val.nil?:

def initialize(args={})
  defaults = {
    :page    => DEFAULT_PAGE,
    :area    => DEFAULT_AREA,
    :channel => DEFAULT_CHANNEL
  }.merge(args).each do |attr, val|
    instance_variable_set("@#{attr}", val) if defaults.has_key?(attr) && (not val.nil?)
  end if args
end

Upvotes: 10

Related Questions