ckruse / CFPropertyList

Read, write and manipulate both binary and XML property lists as defined by apple
MIT License
212 stars 47 forks source link

Defining Enumerable::Enumerator breaks AWS S3 Ruby client #52

Closed geoffk01 closed 6 years ago

geoffk01 commented 6 years ago

A short program like this:

#!/usr/bin/ruby
require 'aws-sdk-s3'
require 'cfpropertylist'

bucket = Aws::S3::Resource.new().bucket('any-bucket-name')
bucket_objs = { }
bucket.objects.each { |obj| printf "%s\n",obj.key }

fails with

/Library/Ruby/Gems/2.3.0/gems/aws-sdk-core-3.13.1/lib/aws-sdk-core/resources/collection.rb:56:ineach': undefined method each' for #<Enumerable::Enumerator:0x007f80e8f0d758> (NoMethodError)

with ruby 2.3.3p222, CFPropertyList 2.3.6, and aws-sdk-core 3.13.1 (and other stuff, but those are the relevant pieces).

The problem is that cfpropertylist has defined, in lib/cfpropertylist/rbCFPropertyList.rb,

module Enumerable
  class Enumerator
  end
end

in addition to the usual Ruby 2.3 top-level Enumerator. Then the AWS SDK does

module Aws
  module Resources
    class Collection

      extend Aws::Deprecations
      include Enumerable
...
      def each(&block)
        enum = Enumerator.new do |y|
...

and instead of the usual Enumerator class, it's getting the Enumerable::Enumerator that cfpropertylist has defined, which doesn't have any useful code, and in particular has no 'each' method.

I worked around this by writing

require 'aws-sdk-s3'
require 'cfpropertylist'

module Enumerable
       remove_const(:Enumerator)
end

but really I think CFPropertyList shouldn't be defining things in system modules.

ckruse commented 6 years ago

I'm defining this for backwards compatibility with Ruby 1.8. In Ruby 1.8 the top-level Enumerator didn't exist. Dropping this means dropping Ruby 1.8 compatibility… while personally I would be fine with that I'm not sure if every user of this gem is already on 1.9 or later. Maybe as a new major release…