Skip to content

Instantly share code, notes, and snippets.

@schmidt
Created February 23, 2019 14:46
Show Gist options
  • Save schmidt/c05b90a881856e45a3cba46fc25de858 to your computer and use it in GitHub Desktop.
Save schmidt/c05b90a881856e45a3cba46fc25de858 to your computer and use it in GitHub Desktop.
Meta programming challenge
class Class
def reader(*names)
names.each do |name|
module_eval <<~CODE
def #{name}
@#{name}
end
CODE
end
end
end
class A
def initialize
@a = :a
@b = :b
end
private
attr_reader :a
reader :b
end
p A.new.a # => test.rb:25:in `<main>': private method `a' called for #<A:0x00007fb5e90b42e8 @a=:a, @b=:b> (NoMethodError)
p A.new.b # => :b
__END__
How can I achieve, that my newly defined reader methods are also private.
I could not figure out how to detect the private block or which API to use,
so that the newly defined methods are private by default.
@schmidt
Copy link
Author

schmidt commented Feb 25, 2019

This is the only way I could find to make it work:

class Class
  def reader(*names, binding)
    names.each do |name|
      eval <<~CODE, binding
        def #{name}
          @#{name}
        end
      CODE
    end
  end
end

class A
  def initialize
    @a = :a 
    @b = :b
  end

  private

  attr_reader :a
  reader :b, binding
end

or, using the bindings Gem this can be simplified to

require 'bindings'

class Class
  def reader(*names, binding = Binding.of_caller(1))
    names.each do |name|
      eval <<~CODE, binding
        def #{name}
          @#{name}
        end
      CODE
    end
  end
end

class A
  def initialize
    @a = :a 
    @b = :b
  end

  private

  attr_reader :a
  reader :b
end

@schm
Copy link

schm commented Feb 25, 2019

class Class
  def reader(&block)
    names = Array(block.call)
    names.each do |name|
      eval <<~CODE, block.binding
        def #{name}
          @#{name}
        end
      CODE
    end
  end
end

class A
  def initialize
    @a = :a 
    @b = :b
  end

  private

  attr_reader :a
  reader { :b }
end

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