garyM
garyM

Reputation: 892

Empty hash as default parameter turning into an Array?

A parameter defaulted to an empty hash attrs={} returns an error:

can't convert Array into Hash
(TypeError)

I've tried this on Ruby versions 1.8.6, 1.8.7 and 1.9.1. A hash will be passed to attrs.

class Category < Object   

  attr_accessor :attributes  
  attr_accessor :rel_attrs  
  attr_accessor :cls_str

  def initialize (term='',title='', attrs={}, scheme = '', rel=[], cls_str='')

    @attributes ={}
    @attributes['scheme']  = scheme
    @attributes['term']    = term
    @attributes['title']   = title
    @attributes['related'] = rel
    @cls_str = cls_str

    if not attrs.empty?
       @attributes.update attrs
    end
  end
end

What am I doing wrong?

Upvotes: 2

Views: 4188

Answers (1)

Michael Kohl
Michael Kohl

Reputation: 66837

Some notes:

  • You don't have to inherit from Object.
  • if not can more idiomatically be expressed as unless.
  • Making the attrs hash the last argument has the advantage that you can leave off the {} around the hash elements when calling Category.new. You can not do this if the hash is the middle, so it would make sense to show us your call to Category.new.

I changed your code accordingly:

class Category 

  attr_accessor :attributes
  attr_accessor :rel_attrs  
  attr_accessor :cls_str

  def initialize (term='',title='', scheme = '', rel=[], cls_str='', attrs={})

    @attributes ={}
    @attributes['scheme']  = scheme
    @attributes['term']    = term
    @attributes['title']   = title
    @attributes['related'] = rel
    @cls_str = cls_str

    @attributes.update(attrs) unless attrs.empty?
  end
end

Here's how you call it:

>> c = Category.new("term", "title", "scheme", [1,2,3], 'cls_string', :foo => 'bar', :baz => 'qux') 
#=> #<Category:0x00000100b7bff0 @attributes={"scheme"=>"scheme", "term"=>"term", "title"=>"title", "related"=>[1, 2, 3], :foo=>"bar", :baz=>"qux"}, cls_str"cls_string"
>> c.attributes 
#=> {"scheme"=>"scheme", "term"=>"term", "title"=>"title", "related"=>[1, 2, 3], :foo=>"bar", :baz=>"qux"}

This obviously has the problem that all other optional arguments have to be specified in order to be able to specify attrs. If you don't want this, move attrs back to the middle of the argument list and make sure to include the {} in the call. Or even better, make the whole argument list a hash and merge it with the defaults args. Something like

 class Category(opts = {})
   # stuff skipped
   @options = { :bla => 'foo', :bar => 'baz'}.merge(opts)
   # more stuff skipped
 end

Upvotes: 3

Related Questions