aetherknight / recursive-open-struct

OpenStruct subclass that returns nested hash attributes as RecursiveOpenStructs
Other
276 stars 54 forks source link

RSpec failure when testing RecursiveOpenStruct.new(nil) #35

Closed ckoch-ncsa closed 9 years ago

ckoch-ncsa commented 9 years ago

When testing RecursiveOpenStruct (ROS) using RSpec in a Rails project, my test against initializing ROS with nil is failing and reports a 'no method error for nil class' when sending a key to the ROS instance.

I am wrapping calls to RecursiveOpenStruct in a method that makes the call: RecursiveOpenStruct.new(hash, recurse_over_arrays: true)

I tested this with 2 different versions of RecursiveOpenStruct and received 2 different outcomes: first using the current 0.6.4 version gem build, I instantiated the RecursiveOpenStruct class passing nil and recurse_over_arrays: true. RecursiveOpenStruct.new(nil, recurse_over_arrays: true) This test fails

Using the older version 0.5.0 of the gem and the same initialization, I have no issues with the tests failing.

When I pry the session in the failing test, I see that RecursiveOpenStruct.new(nil, recurse_over_arrays: true) returns a RecursiveOpenStruct instance, however when I send a key to the ROS instance that does not exist, then the "NoMethodError: undefined method []' for nil:NilClass" appears.

Pry session debug output: [1] pry(#RSpec::ExampleGroups::ResponseDeserializer)> described_class.recursive_open_struct(nil).client_id NoMethodError: undefined method []' for nil:NilClass from .../.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/ostruct.rb:185:in method_missing'

It appears that somewhere in the method_missing calls, the ROS object gets lost, perhaps because the instance does not correctly maintain the @table object?

Files and additional test output are below.

Test Error: `From: ../spec/lib/serializers/response_deserializer_spec.rb @ line 79 :

74:   it "handles nil" do
77:     nil_ostruct = described_class.recursive_open_struct(nil)
78:     binding.pry
79:     expect(nil_ostruct.client_id).to be_nil
80:     # expect{nil_ostruct.client_id}.to_not raise_error

`

[1] pry(#RSpec::ExampleGroups::ResponseDeserializer)> nil_ostruct => #RecursiveOpenStruct:0x3fc71154d338

[2] pry(#RSpec::ExampleGroups::ResponseDeserializer)> nil_ostruct.client NoMethodError: undefined method []' for nil:NilClass from .../.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/ostruct.rb:185:in method_missing'

More console output, notice that it is not possible to assign a new key-value pair to the ROS instance that was initialized with nil.

[10] pry(main)> b=RecursiveOpenStruct.new(nil) => #RecursiveOpenStruct:0x3ff34cd4f2b8

[11] pry(main)> b.test NoMethodError: undefined method []' for nil:NilClass from .../.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/ostruct.rb:185:in method_missing'

[12] pry(main)> b.test=0 NoMethodError: undefined method has_key?' for nil:NilClass from .../.rvm/gems/ruby-2.2.2/gems/recursive-open-struct-0.6.4/lib/recursive_open_struct.rb:93:in _get_key_fromtable'

[13] pry(main)> b => #RecursiveOpenStruct:0x3ff34cd4f2b8

[6] pry(main)> a=OpenStruct.new(nil) => #

[7] pry(main)> a.test => nil

[8] pry(main)> a.test = 0 => 0

[9] pry(main)> a => #

` class ResponseDeserializer class << self

def recursive_open_struct(json)
  hash = json.kind_of?(String) ? from_json_string(json) : json
  hash.kind_of?(Array) ? recurse_array(hash) : recurse(hash)
end

private

def recurse_array(array)
  new_array = []
  array.each do |el|
    new_array << recurse(el)
  end
  new_array
end

def recurse(hash)
  RecursiveOpenStruct.new(hash, recurse_over_arrays: true)
end

def from_json_string(json)
  JSON.parse(json) unless json.nil?
end

end end `

aetherknight commented 9 years ago
OpenStruct.new(nil).foo # => nil

RecursiveOpenStruct.new(nil).foo # => NoMethodError: undefined method `[]' for nil:NilClass

0.6 reworked the internals of the ROS class a bit in order to add recursion support to subscript notation and to make it possible to choose whether the input hash is mutated or duplicated, so I'm not too surprised that un-spec'd OpenStruct functionality broke. This should be an easy fix though.

aetherknight commented 9 years ago

@ckoch-ncsa could you look over my commit and see if that solves your problem? If so, I will cut a patch release in the next day or so.

ckoch-ncsa commented 9 years ago

Looks great and works as expected now. Thanks for fixing this so quickly.

aetherknight commented 9 years ago

Just released v0.6.5 to rubygems.