3

Given a class instance and a string, how do I convert the string to refer to the instance?

class Room
  def enter
    puts "Welcome!"
  end
end

# Rooms are predefined
lounge = Room.new
kitchen = Room.new
study = Room.new

puts "Which room would you like to go to?"
print "> "
room = gets.strip

# User types "lounge"

room.enter # => undefined method `enter' for "lounge":String (NoMethodError)

I understand why I'm getting NoMethodError, but I haven't been able to work out how to convert the room string to refer to the existing instance of Room named lounge.

5 Answers 5

1
room_name = gets.strip.capitalize
room = Kernel.const_get(room_name)
room.enter

If you require "active_support/all" (part of Rails) you can do this:

room = gets.strip.camelize.constantize
room.new.enter

Edit:

As the Tin Man pointed out, require "active_support/core_ext/string/inflections" will be more efficient.

Sign up to request clarification or add additional context in comments.

4 Comments

All of ActiveSupport is a big chunk of code that won't necessarily be needed. Why not show how to get just the string manipulation chunks?
Didn't realize you could do that.
This doesn't seem to work if the class instance is already defined. It works great when creating new instances: e.g. Object.const_get("Room").new.enter, but not when referring to existing ones: Object.const_get("lounge").enter # => wrong constant name lounge (NameError). I think this is because the Room class is a constant, but lounge is not.
"require "active_support/core_ext/string" will be more efficient". No, require 'active_support/core_ext/string/inflections' will be.
1

perhaps trying to map rooms and get them by key?

class Room

  def enter
    puts "Welcome!"
  end
end

# Rooms are predefined
rooms = %w[lounge kitchen study].inject({}) { |f,c| f.update c => Room.new }

puts "Which room would you like to go to?"
print "> "
if room = rooms[gets.strip]
  room.enter
end

Or even simpler:

class Room

  def initialize room_type
    @room_type = room_type
  end

  def enter
    return puts 'Unsupported room type' unless %w[
      lounge kitchen study
    ].include?(@room_type)
    puts "Welcome to #{@room_type}!"
  end

end

puts "Which room would you like to go to?"
print "> "
room = Room.new(gets.strip)
room.enter

Comments

1
class Room
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def enter
    puts "Welcome to #{@name}!"
  end
end

# Rooms are predefined
rooms = ["lounge", "kitchen", "study"].map(&Room.method(:new))

puts "Which room would you like to go to?"
print "> "
name = gets.strip

# Just an example (not using find to avoid nil.enter)
p rooms.select{ |room| room.name == name }.map(&:enter)

Comments

0

You can use eval method to dynamically execute a piece of ruby script. In this case eval(room) will give you the room instance.

However this is very dangerous method as it allows the end user to execute code. To protect against code execution you'd better verify it's one of the room variable defined:

eval(room).enter if local_variables.include?(room.to_sym)

Comments

0

I ended up storing each Room object in a hash as a class variable on initialization. This lets you refer to each instance via the hash:

class Room
  @@rooms = {}

  def initialize(name)
    @@rooms.store name, self
  end

  def rooms
    @@rooms
  end

  def enter
    puts "Welcome!"
  end
end

# Rooms are predefined
lounge = Room.new('lounge')
kitchen = Room.new('kitchen')
study = Room.new('study')

puts "Which room would you like to go to?"
print "> "
new_room = gets.strip

lounge.rooms[new_room].enter if lounge.rooms.has_key? new_room

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.