Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby

How to create a custom 'NoMethodError' message?

This is from an Extra Credit exercise.

Create a protected method and create a custom message if it's called incorrectly.

class BankAccount

    def initialize(first_name, last_name)
        @first_name = first_name
        @last_name = last_name
        @balance = 0
    end

    def public_deposit(amount)
        @balance += amount
    end

    def protected_deposit(amount)
        @balance += amount
    end
    protected :protected_deposit

    def private_deposit(amount)
        @balance += amount
    end
    private :private_deposit

    def call_private_deposit(amount)
        private_deposit(amount)
    end

    def call_protected_deposit(amount)
        protected_deposit(amount)
    end

    #To show that you can't call a private method with a different instance of the same class.
    def private_add_to_different_account(account, amount)
        account.private_deposit(amount)
    end

    #To show that you can call a protected method with a different instance of the same class.
    def protected_add_to_different_account(account, amount)
        account.protected_deposit(amount)
    end
end

There's a lot of chaff in there that you don't need to see, but what I do with this is load it into irb, then: one_instance = BankAccount.new("Something", "Something")

Then do: one_instance.protected_deposit(500)

And you get a NoMethodError, as expected.

I need to make the program return a custom message, like "This is a custom NoMethodError message." instead.

I've found various bits and bobs on the internet, but it's a nightmare. I've looked at using a rescue block, but I can't get this to work, probably because the NoMethodError is raised before the protected_deposit method is even executed, so putting a rescue block inside that doesn't achieve anything.

Help?! I don't want a full answer though, good hints and pointers are better for learning.

5 Answers

My suggestion would be to define method_missing which will get called for any method that that someone attempts to call on the object that isn't defined. You can print out a custom message, raise further exceptions, or whatever else you're looking to do there.

class BankAccount

  def method_missing(method)
    puts "You tried to call #{method}, don't do that!"
  end

end

Hope that helps!

But that method wouldn't get called unless I called it. It doesn't get called when I do

an_instance.protected_deposit(1000)

method_missing is a special method in ruby that is called when the interpreter can't find the method you called. For example with the following code:

class BankAccount

  def method_missing(method, args)
    puts "You tried to call #{method}, don't do that!"
  end

  protected def protected_deposit(num)
    puts "You called protected_deposit with #{num}"
  end

end

Running in IRB you would get this:

>>   account = BankAccount.new
=> #<BankAccount:0x007f8590873620>
>> account.protected_depsoit(1000)
You tried to call protected_depsoit, don't do that!
=> nil

This will allow you to print out custom messages when someone tries to call a method that isn't defined or is protected.

If you want a custom message for your NoMethodError instead you can just pass a string as an argument:

>> raise NoMethodError.new("Don't do that!")
NoMethodError: Don't do that!
    from (irb):117
    from /Users/gparsons/.rvm/rubies/ruby-2.1.3/bin/irb:11:in `<main>'

Is this what you're looking for or am I misunderstanding your original post?

Bit disappointed with the lack of help I'm receiving here. I even posted up on Stackoverflow and no response there either.

Well, time is pressing on, so I've resorted to taking the easy way out, which is saving a bunch of code and just running it i.e. "ruby mycode.rb".

class BankAccount

    def initialize(first_name, last_name)
        @first_name = first_name
        @last_name = last_name
        @balance = 0
    end

    def protected_deposit(amount)
            @balance += amount
    end
    protected :protected_deposit

    def call_protected_deposit(amount)
        protected_deposit(amount)
    end

end

an_instance = BankAccount.new("Andrew", "Stelmach")

puts "Your BankAccount instance: #{an_instance.inspect}"

puts "How much money would you like to deposit?"

first_deposit = gets.to_i

puts "Depositing #{first_deposit} into your account..."
an_instance.call_protected_deposit(first_deposit)

puts "Your BankAccount instance now: #{an_instance.inspect}"

puts "How much money would you like to deposit by calling the protected method?"

second_deposit = gets.to_i

puts "Trying to deposit money using the protected method..."

begin
    an_instance.protected_deposit(second_deposit)
rescue StandardError
    puts "Failed."
end

This works, and yes, I achieved the extra credit objective of "try writing a program with a protected method. Have that method raise a specific custom error if it's called inappropriately."

....kind of...

The wording of this task, to me, sounds like I should create the conditions within my code so that, "WhenEVER a protected method is called inappropriately, a custom error message appears."

I don't think I've achieved that. Please see my original post at the top of this thread.

I DO feel really let down by Treehouse on this occasion. Up til now, my requests for help have always been answered well and promptly. On this occasion, apart from a reply that wasn't particularly helpful, it's been nothing but tumbleweeds. I even posted the problem up on stackoverflow and I've had no reply there, either.

I understand that the easier problems get more attention by fellow students, but I would expect harder problems like this to attract the assistance of treehouse mods and staff.

I spent five hours last night experimenting and scouring the internet trying to solve this problem. Now, fair enough, I guess if I could work this out for myself... eventually... but the process would be greatly eased by the assistance of some treehouse mods or staff.

It would be really really useful for me if someone could help me with the original problem here (at the top of this thread). I do feel quite let down by what has been, up til now, a stellar service.

Jason Seifer
STAFF
Jason Seifer
Treehouse Guest Teacher

Hey Andrew Stelmach I took a look another look at that extra credit and it's worded very poorly to the point of being misleading. I've updated the extra credit so it's easier to understand. You're just meant to rescue a NoMethodError gracefully. Sorry about that! Let me know if that helps clear things up.

I also struggled wildly with this Extra Credit. It says, I quote:

Attempt to instantiate and call that method, then rescue the exception.

The wording still leads you to use rescue without (in my case, never having heard of) method_missing. As Andrew points out, rescue won't work with a protected method.

/cc Jason Seifer