poctek
poctek

Reputation: 256

define_method doesn't receive block as a parameter ruby

I'm currently doing second week assignment 1 from this metaprogramming tutorial and have some problems with sending block for using it with define_method. The program simply doesn't see the block, returning false when I call block_given? even though I provide a block.

Here's the file that sends the block:

require_relative 'dog'

lassie, fido, stimpy = %w[Lassie Fido Stimpy].collect{|name| Dog.new(name)}
lassie.can :dance, :poo, :laugh
fido.can :poo
stimpy.can :dance

stimpy.can(:cry){"#{name} cried AHHHH"} # the block that I can't receive

puts lassie.name

p lassie.dance
p lassie.poo
p lassie.laugh
puts
p fido.dance
p fido.poo
p fido.laugh
puts
p stimpy.dance
p stimpy.poo
p stimpy.laugh
p stimpy.cry # method call

And the file that receives:

Dog = Class.new do
    MESSAGES = { dance: "is dancing", poo: "is a smelly doggy!", laugh: "finds this hilarious" }

    define_method :initialize do |name|
    instance_variable_set(:@name, name)
    end

    define_method :name do
    instance_variable_get :@name
    end

    define_method :can do |*args, &block|
    puts block_given? # false 
    if block_given?
        define_method args.to_sym do
        yield
        end
    else
        args.each do |ability|
        self.class.instance_eval do
            define_method "#{ability}".to_sym do
            @name + " " + MESSAGES[ability]
            end
        end
        end
    end
    end

    define_method :method_missing do |arg|
    puts "#{@name} doesn't understand #{arg}"
    end
end

Upvotes: 0

Views: 265

Answers (2)

Jörg W Mittag
Jörg W Mittag

Reputation: 369458

I believe (but haven't checked) block_given? refers to a block being passed to the method defined by the closest lexically enclosing method definition, i.e. def, and does not work inside methods defined with define_method.

I know for a fact that yield only yields to a block being passed to the method defined by the closest lexically enclosing method definition, i.e. def, and does not yield from a block (which, after all, define_method is, it's just a method like any other method which takes a block, and just like any other taking a block, yield yields to the block of the method, not some other block).

It's kind of strange to combine yield and block_given? with explicitly named block-Procs anyway. If you have the name, there is no need for anonymity, you can just say

if block
  define_method(args.to_sym) do block.() end
end

Or did you mean to pass the block to define_method to be used as the implementation of the method? Then it would be

if block
  define_method(args.to_sym, &block)
end

Upvotes: 1

radubogdan
radubogdan

Reputation: 2834

Not sure if you can pass arguments and block to something that just gets defined. read this

define_method(symbol, method) → symbol
define_method(symbol) { block } → symbol 

Instead of define_method :can do |*args, &block| try the explicit def can(*args, &block)

It's weird to do it like that anyway..

Upvotes: 0

Related Questions