ruby program in sublime text 2 throws "undefined method" error in saved .rb files - ruby

This is the Dungeon example from Beginning Ruby:
class Dungeon
def add_room(reference, name, description, connections)
#rooms << Room.new(reference, name, description, connections,)
end
class Player
attr_accessor :name, :location
def initialize(player_name)
#name = player_name
end
end
def initialize(player_name)
#player = Player.new(player_name)
#rooms = []
end
end
my_dungeon = Dungeon.add_room(:largecave, "Large Cave", "a large cavernous cave", {:west => :smallcave})
my_dungeon = Dungeon.add_room(:smallcave, "Small Cave", "a small claustrophobic cave", {:east => :largecave})
This code builds without error in Sublim Text 2 until I save it to a file on my HD. Once it's saved to my PC I get this build error from Sublime Text 2
C:/Ruby193/bin/stephon.rb:18:in `<main>': undefined method `add_room' for Dungeon:Class (NoMethodError)
[Finished in 0.0s with exit code 1]
I used the %PATH% command in a comm window to add both the directory that this code is in and the Ruby directory. No change.
Any help would be appreciated.

The add_room method on your Dungeon class is an instance method. That means, you must create a new instance of the class before you can call the method on the instance.
Something like this should work (as long as you define a Room class somewhere):
my_dungeon = Dungeon.new("Player 1")
my_dungeon.add_room(:largecave, "Large Cave", "a large cavernous cave", {:west => :smallcave})
my_dungeon.add_room(:smallcave, "Small Cave", "a small claustrophobic cave", {:east => :largecave})

Related

Get ruby dungeon to repeat back player name

I'm trying to expand upon Peter Cooper's dungeon game in Beginning Ruby and I want the game to address the player by name at each room and then ask them where they will go to make the game more interactive. I keep getting this error though:
dungeon.rb:82:in <main>': undefined methodname' for nil:NilClass (NoMethodError)
So the main parts I've added to try to make this work are Dungeon initialize (lines 4-13)
def initialize
player_name = ""
until !player_name.empty?
puts "Enter your name!"
player_name = gets.chomp
end
#player = Player.new(player_name)
#player.name = player_name
#rooms = []
end
The line in question that creates the error is this:
my_dungeon.add_room(:largecave, "Large Cave", "#{#player.name}, you find yourself in a large cavernous cave. To the west is a small aperture", {:west => :smallcave})
Full code is here:
Ruby Dungeon Code
What's going on here?
You probably wanted to use
my_dungeon.player.name
instead of
#player.name
in rooms addition method calls(since player has been defined for Dungeon instance).
So just use these lines instead of ones you currently have:
my_dungeon.add_room(:largecave, "Large Cave", "#{my_dungeon.player.name}, you find yourself in a large cavernous cave. To the west is a small aperture", {:west => :smallcave})
my_dungeon.add_room(:smallcave, "Small Cave", "#{my_dungeon.player.name}, you find yourself in a small, claustrophopic cave. To the east is a small aperture", {:east => :largecave}
By the way, you set name in initialize method, so there is no need for #player.name = player_name. And it is simpler to read while player_name.empty? instead of until !player_name.empty?, consider following refactoring:
def initialize
player_name = ""
while player_name.empty?
puts "Enter your name!"
player_name = gets.chomp
end
#player = Player.new(player_name)
#rooms = []
end

Assert_equal undefined local variable LRTHW ex52

Hi I made it to the lase exercise os Learn Ruby The Hard Way, and I come at the wall...
Here is the test code:
def test_gothon_map()
assert_equal(START.go('shoot!'), generic_death)
assert_equal(START.go('dodge!'), generic_death)
room = START.go("tell a joke")
assert_equal(room, laser_weapon_armory)
end
And here is the code of the file it should test:
class Room
attr_accessor :name, :description, :paths
def initialize(name, description)
#name = name
#description = description
#paths = {}
end
def ==(other)
self.name==other.name&&self.description==other.description&&self.paths==other.paths
end
def go(direction)
#paths[direction]
end
def add_paths(paths)
#paths.update(paths)
end
end
generic_death = Room.new("death", "You died.")
And when I try to launch the test file I get an error:
generic_death = Room.new("death", "You died.")
I tried to set the "generic_death = Room.new("death", "You died.")" in test_gothon_map method and it worked but the problem is that description of the next object is extremely long, so my questions are:
why assertion doesn't not respond to defined object?
can it be done different way then by putting whole object to testing method, since description of the next object is extremely long...
The nature of local variable is that they are, well, local. This means that they are not available outside the scope they were defined.
That's why ruby does not know what generic_death means in your test.
You can solve this in a couple of ways:
define rooms as constants in the Room class:
class Room
# ...
GENERIC_DEATH = Room.new("death", "You died.")
LASER_WEAPON_ARMORY = Room.new(...)
end
def test_gothon_map()
assert_equal(Room::START.go('shoot!'), Room::GENERIC_DEATH)
assert_equal(Room::START.go('dodge!'), Room::GENERIC_DEATH)
room = Room::START.go("tell a joke")
assert_equal(room, Room::LASER_WEAPON_ARMORY)
end
assert the room by its name, or some other identifier:
def test_gothon_map()
assert_equal(START.go('shoot!').name, "death")
assert_equal(START.go('dodge!').name, "death")
room = START.go("tell a joke")
assert_equal(room.name, "laser weapon armory")
end

wrong number of arguments ruby rspec

I'm trying to write unit tests for my code using rspec. I keep getting a "wrong number of arguments" error:
class MyClass
attr_accessor :env, :company,:size, :role, :number_of_hosts,:visability
def initialize(env, company, size, role, number_of_hosts, visability)
#env, #company, #size, #role, #number_of_hosts, #visability = env, company, size, role, number_of_hosts, visability
end
end
And here are my tests:
require_relative "../lib/MyClass.rb"
describe MyClass do
it "has an environment" do
MyClass.new("environment").env.should respond_to :env
end
it "has a company" do
MyClass.new("company").company.should respond_to :company
end
...
When I run rspec I get:
1) MyClass has an environment
Failure/Error: MyClass.new("environment").env.should respond_to :env
ArgumentError:
wrong number of arguments (1 for 6)
# ./lib/MyClass.rb:4:in `initialize'
# ./spec/MyClass_spec.rb:5:in `new'
# ./spec/MyClass_spec.rb:5:in `block (2 levels) in <top (required)>'
...
What am I missing?
EDIT
Sergio helped thanks...however
Sergio's answer worked...although I still have a further question:
Given the Class:
class Team
attr_accessor :name, :players
def initialize(name, players = [])
raise Exception unless players.is_a? Array
#name = name
raise Exception if #name && has_bad_name
#players = players
end
def has_bad_name
list_of_words = %w{crappy bad lousy}
list_of_words - #name.downcase.split(" ") != list_of_words
end
def favored?
#players.include? "George Clooney"
end
end
and spec...
require_relative "../lib/team.rb"
describe Team do
it "has a name" do
Team.new("random name").should respond_to :name
end
it "has a list of players" do
Team.new("random name").players.should be_kind_of Array
end
...
The tests pass without the same error...(This works fine: Team.new("random name"))
Any explanation?
Here is the error MyClass.new("environment"). As you have written def initialize(env, company, size, role, number_of_hosts, visability). So you should pass 6 parameters when you are calling MyClass#new method. But in practice you pass only one which is "environment". Thus you got the legitimate error - wrong number of arguments (1 for 6).

Ruby Name Error - Uninitialized constant

I am doing exercises and am
getting NameError:Unitialized Constant MyUnitTests::Room when running test_ex47.rb.
test_ex47.rb:
require 'test/unit'
require_relative '../lib/ex47'
class MyUnitTests < Test::Unit::TestCase
def test_room()
gold = Room.new("Gold Room", """This room has gold in it you can grab. There's a doo to the north.""")
assert_equal(gold.name, "GoldRoom")
assert_equal(gold.paths, {})
end
def test_room_paths()
center = Room.new("Center", "Test room in the center.")
north = Room.new("North", "Test room in the north.")
south = Room.new("South", "Test room in the south.")
center.add_paths({:north => north, :south => south})
assert_equal(center.go(:north), north)
assert_equal(center.go(:south), south)
end
def test_map()
start = Room.new("Start", "You can go west and down a hole.")
west = Room.new("Trees", "There are trees here, you can go east.")
down = Room.new("Dungeon", "It's dark down here, you can go up.")
start.add_paths({:west => west, :down => down})
west.add_paths({:east => start})
down.add_paths({:up => start})
assert_equal(start.go(:west), west)
assert_equal(start.go(:west).go(:east), start)
assert_equal(start.go(down).go(up), start)
end
end
ex47.rb is located in the lib folder and looks like:
class Room
aatr_accessor :name, :description, :paths
def initialize(name, description)
#name = name
#description = description
#paths = {}
end
def go(direction)
#paths[direction]
end
def add_paths(paths)
#paths.update(paths)
end
end
Error:
Finished tests in 0.000872s, 3440.3670 tests/s, 0.0000 assertions/s.
1) Error:
test_map(MyUnitTests):
NameError: uninitialized constant MyUnitTests::Room
test_ex47.rb:22:in `test_map'
2) Error:
test_room(MyUnitTests):
NameError: uninitialized constant MyUnitTests::Room
test_ex47.rb:6:in `test_room'
3) Error:
test_room_paths(MyUnitTests):
NameError: uninitialized constant MyUnitTests::Room
test_ex47.rb:12:in `test_room_paths'
3 tests, 0 assertions, 0 failures, 3 errors, 0 skips]
The problem here is that you are creating a Room object inside the MyUnitTests class on line 3. Ruby thinks you want to use a class called MyUnitTest::Room, which doesn't exist. You need to use an absolute class reference, like so:
class MyUnitTests < Test::Unit::TestCase
def test_room()
gold = ::Room.new("Gold Room", """This room has gold in it you can grab. There's a doo to the north.""")
assert_equal(gold.name, "GoldRoom")
assert_equal(gold.paths, {})
end
Notice the :: before Room.new on line 3 there? That tells Ruby that you want to create a Room object from the top level name space :)
I hope that answers your question.
EDIT: You'll also need to change your other references to the Room class to ::Room. Sorry, I thought only the top one was a problem because of the indentation. A closer look reveals that the rest need the :: as well.

Ruby structure for extendable handler/plugin architechture

I'm writing something that is a bit like Facebook's shared link preview.
I would like to make it easily extendable for new sites by just dropping in a new file for each new site I want to write a custom parser for. I have the basic idea of the design pattern figured out but don't have enough experience with modules to nail the details. I'm sure there are plenty of examples of something like this in other projects.
The result should be something like this:
> require 'link'
=> true
> Link.new('http://youtube.com/foo').preview
=> {:title => 'Xxx', :description => 'Yyy', :embed => '<zzz/>' }
> Link.new('http://stackoverflow.com/bar').preview
=> {:title => 'Xyz', :description => 'Zyx' }
And the code would be something like this:
#parsers/youtube.rb
module YoutubeParser
url_match /(youtube\.com)|(youtu.be)\//
def preview
get_stuff_using youtube_api
end
end
#parsers/stackoverflow.rb
module SOFParser
url_match /stachoverflow.com\//
def preview
get_stuff
end
end
#link.rb
class Link
def initialize(url)
extend self with the module that has matching regexp
end
end
# url_processor.rb
class UrlProcessor
# registers url handler for given pattern
def self.register_url pattern, &block
#patterns ||= {}
#patterns[pattern] = block
end
def self.process_url url
_, handler = #patterns.find{|p, _| url =~ p}
if handler
handler.call(url)
else
{}
end
end
end
# plugins/so_plugin.rb
class SOPlugin
UrlProcessor.register_url /stackoverflow\.com/ do |url|
{:title => 'foo', :description => 'bar'}
end
end
# plugins/youtube_plugin.rb
class YoutubePlugin
UrlProcessor.register_url /youtube\.com/ do |url|
{:title => 'baz', :description => 'boo'}
end
end
p UrlProcessor.process_url 'http://www.stackoverflow.com/1234'
#=>{:title=>"foo", :description=>"bar"}
p UrlProcessor.process_url 'http://www.youtube.com/1234'
#=>{:title=>"baz", :description=>"boo"}
p UrlProcessor.process_url 'http://www.foobar.com/1234'
#=>{}
You just need to require every .rb from plugins directory.
If you're willing to take this approach you should probably scan the filed for the mathing string and then include the right one.
In the same situation I attempted a different approach. I'm extending the module with new methods, ##registering them so that I won't register two identically named methods. So far it works good, though the project I started is nowhere near leaving the specific domain of one tangled mess of a particular web-site.
This is the main file.
module Onigiri
extend self
##registry ||= {}
class OnigiriHandlerTaken < StandardError
def description
"There was an attempt to override registered handler. This usually indicates a bug in Onigiri."
end
end
def clean(data, *params)
dupe = Onigiri::Document.parse data
params.flatten.each do |method|
dupe = dupe.send(method) if ##registry[method]
end
dupe.to_html
end
class Document < Nokogiri::HTML::DocumentFragment
end
private
def register_handler(name)
unless ##registry[name]
##registry[name] = true
else
raise OnigiriHandlerTaken
end
end
end
And here's the extending file.
# encoding: utf-8
module Onigiri
register_handler :fix_backslash
class Document
def fix_backslash
dupe = dup
attrset = ['src', 'longdesc', 'href', 'action']
dupe.css("[#{attrset.join('], [')}]").each do |target|
attrset.each do |attr|
target[attr] = target[attr].gsub("\\", "/") if target[attr]
end
end
dupe
end
end
end
Another way I see is to use a set of different (but behaviorally indistinguishable) classes with a simple decision making mechanism to call a right one. A simple hash that holds class names and corresponding url_matcher would probably suffice.
Hope this helps.

Resources