HashWithIndifferentAccess is incompatible with ruby 2.0's keyword arguments #14643

Closed
vrinek opened this Issue Apr 8, 2014 · 8 comments

Projects

None yet

4 participants

@vrinek

In Ruby it is possible to use a POR Hash in place of keyword arguments, making the transition between using an options = {} to keyword arguments easy.

The same is not true for HashWithIndifferentAccess and this makes it difficult to pass the params hash directly to methods with keyword arguments.

Bug test

require 'active_support/hash_with_indifferent_access'
require 'minitest/autorun'

def sum(a: 0, b: 0)
  a + b
end

class BugTest < MiniTest::Unit::TestCase
  def setup
    @normal_hash = {a: 18, b: 24} # just a hash
    @indiff_hash = ActiveSupport::HashWithIndifferentAccess.new(@normal_hash)
  end

  def test_ruby_hash
    assert_equal 42, sum(@normal_hash)
  end

  def test_hash_with_indifferent_access
    assert_equal 42, sum(@indiff_hash)
  end
end

Workaround

sum(@indiff_args.symbolize_keys)

VERSIONS

  • ruby 2.0.0-p353 & activesupport 4.0.1
  • ruby 2.1.0-p0 & activesupport 4.0.3
  • ruby 2.1.1-p76 & activesupport 4.0.4, 3.2.17
@rafaelfranca
Ruby on Rails member

This is not a bug. HashWithIndifferentAccess store the keys as string, so it is the same as:

>> def sum(a: 0, b: 0)
>>   a + b
>>   end
=> :sum
>> hash = { 'a' => 1, 'b' => 2 }
=> {"a"=>1, "b"=>2}
>> sum(hash)
ArgumentError: wrong number of arguments (1 for 0)
    from (irb):1:in `sum'
    from (irb):5
    from /opt/boxen/rbenv/versions/2.1.1/bin/irb:11:in `<main>'
@rafaelfranca
Ruby on Rails member

Also if params were not a HashWithIndifferentAccess it would be the same thing. A hash with string keys.

@vrinek

As far as I know, a HashWithIndifferentAccess is supposed to behave like a hash with string keys when treated like one and like a hash with symbol keys when treated like one. Having it incompatible with Ruby's keyword arguments is confusing.

Wouldn't it be easier for the developer to not have to put .symbolize_keys whenever they need to pass a HashWithIndifferentAccess as keyword arguments to a method?

@rafaelfranca
Ruby on Rails member

As far as I know, a HashWithIndifferentAccess is supposed to behave like a hash with string keys when treated like one and like a hash with symbol keys when treated like one.

This is not true, HashWithIndifferentAccess is a hash which the access is indifferent, but not the storage is indifferent

Wouldn't it be easier for the developer to not have to put .symbolize_keys whenever they need to pass a HashWithIndifferentAccess as keyword arguments to a method?

Yes, I agree, but this is not a HashWithIndifferentAccess issue, so it is not also a Rails issue. As I demonstrated even with a normal hash it is not possible now, so I recommend to report this to Ruby itself.

@vrinek

I recommend to report this to Ruby itself.

Thanks for the advice. I will try reporting it to Ruby and see how it goes.

@cdesch cdesch referenced this issue in mohitjain/social-login-in-rails Apr 22, 2014
Closed

Rake Error #3

@altV

@vrinek, can you please post a link to opened Ruby issue?

I'm afraid it might not be easily closed in Ruby, since right now it uses Object#to_hash convention during method with named arguments calls to strictly cast anything passed to a Hash, and not something like .[] or .fetch (otherwise, it would work).

(Since ActiveRecord::Core#slice returns HashWithIndifferentAccess, you cannot use ActiveRecord objects as easily in methods that uses new keyword arguments instead of old options hashes, like in example above.

That's why either HashWithIndifferentAccess or rather the way AR objects returns it on every Hash-like operation - as it is not compatible with the new Ruby named variables.

I understand, though, that underlying sources where we get those hashes from, like request parameters or JSONs or db values most often have no notion of symbols, that's why there are wrappers like HashWithIndifferentAccess)

@vrinek

The ruby issue is right here: https://bugs.ruby-lang.org/issues/9731. The consensus was "wait for ruby 2.2".

@TiagoCardoso1983

"wait for ruby 2.2" didn't work out for me. should this be changed to "wait for rails 5?"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment