Daniel Grimes
Daniel Grimes

Reputation: 333

NoMethodError: undefined method `meow?' for # - ruby mixin failing?

I'm playing with some of the very basics of ruby mixins, and for some reason can't access behavior from my module.

Running this on Ruby Fiddle:

module Cats
  MEOW = "meow meow meow"

  def Cats.meow?
    return Cats::MEOW
  end
end

class Example
  include Cats

  def sample
    return "it's a sample"
  end
end

e = Example.new
puts e.sample
puts e.meow?

This keeps returning NoMethodError: undefined method 'meow?' for #

My understanding of how mixins should work from tutorialspoint makes me feel like I should be able to validly call e.meow?, and get back the same result I would get from calling Cats.meow?.

Here's the code in RubyFiddle.

Incredibly basic, but any ideas where I'm falling down here?

Upvotes: 0

Views: 156

Answers (2)

mgiagante
mgiagante

Reputation: 41

As a general rule to using include and extend in Ruby:

If you want to use your module as a namespace

module Outer
  module Inner
    def self.my_method
      "namespaced method!"
    end
  end
end

You use it like this Outer::Inner::my_method or Outer::Inner.my_method.

And if you want to use the module as a mixin:

# In some cases it makes sense to use names ending in -able, since it expreses
# what kind of messages you can send to an instance or class that mixes
# this module in.
# Like Devise's Recoverable module: https://github.com/plataformatec/devise/blob/f39c6fd92774cb66f96f546d8d5e8281542b4e78/lib/devise/models/recoverable.rb#L24

module Fooable
  def foo
    "#{self} has been foo'ed!"
  end
end

Then you can include it (instances of Something obtain #foo):

class Something
  include Fooable # Now Something.new can receive the #foo message.
end

Something.new.foo
=> "#<Something:0x0055c2dc104650> has been foo'ed!"

Or you can extend it (Something itself obtains #foo as a class message):

class Something
  extend Fooable # Now Something can receive the #foo message.
end

Something.foo
=> "Something has been foo'ed!"

Upvotes: 1

Daniel Grimes
Daniel Grimes

Reputation: 333

As it turns out, I was being overly specific when defining Cats.meow?. If you want to use a module as a mixin you'll want to define your methods more generally, not with respect to their specific module namespace.

So instead of

def Cats.meow?
  ...
end

it should have been

def meow?
  ...
end 

This lets you call e.meow?, since the method definition no longer limits it just to the Cats namespace.

Whoops.

Upvotes: 2

Related Questions