rspec test not passing - ruby

I'm working through the learn ruby tutorials and I'm trying pass the last example where it tests the printable method. I tested the method by calling the method directly within my ruby program and it spits out exactly whats needed. What is preventing my code from properly passing? Any help is greatly appreciated.
Here's the rspec file:
require 'dictionary'
describe Dictionary do
before do
#d = Dictionary.new
end
it 'is empty when created' do
#d.entries.should == {}
end
it 'can add whole entries with keyword and definition' do
#d.add('fish' => 'aquatic animal')
#d.entries.should == {'fish' => 'aquatic animal'}
#d.keywords.should == ['fish']
end
it 'add keywords (without definition)' do
#d.add('fish')
#d.entries.should == {'fish' => nil}
#d.keywords.should == ['fish']
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
it "doesn't cheat when checking whether a given keyword exists" do
#d.include?('fish').should be_false # if the method is empty, this test passes with nil returned
#d.add('fish')
#d.include?('fish').should be_true # confirms that it actually checks
#d.include?('bird').should be_false # confirms not always returning true after add
end
it "doesn't include a prefix that wasn't added as a word in and of itself" do
#d.add('fish')
#d.include?('fi').should be_false
end
it "doesn't find a word in empty dictionary" do
#d.find('fi').should be_empty # {}
end
it 'finds nothing if the prefix matches nothing' do
#d.add('fiend')
#d.add('great')
#d.find('nothing').should be_empty
end
it "finds an entry" do
#d.add('fish' => 'aquatic animal')
#d.find('fish').should == {'fish' => 'aquatic animal'}
end
it 'finds multiple matches from a prefix and returns the entire entry (keyword + definition)' do
#d.add('fish' => 'aquatic animal')
#d.add('fiend' => 'wicked person')
#d.add('great' => 'remarkable')
#d.find('fi').should == {'fish' => 'aquatic animal', 'fiend' => 'wicked person'}
end
it 'lists keywords alphabetically' do
#d.add('zebra' => 'African land animal with stripes')
#d.add('fish' => 'aquatic animal')
#d.add('apple' => 'fruit')
#d.keywords.should == %w(apple fish zebra)
end
it 'can produce printable output like so: [keyword] "definition"' do
#d.add('zebra' => 'African land animal with stripes')
#d.add('fish' => 'aquatic animal')
#d.add('apple' => 'fruit')
#d.printable.should == %Q{[apple] "fruit"\n[fish] "aquatic animal"\n[zebra] "African land animal with stripes"}
end
end
and here's what I've created so far for the printable function:
class Dictionary
def initialize(opts = {})
#opts = opts
end
def entries
#opts
end
def add(opts)
opts.is_a?(String) ? #opts.merge!(opts => nil) : #opts.merge!(opts)
end
def keywords
#opts.keys.sort
end
def include?(key)
#opts.has_key?(key)
end
def find(key)
#opts.select { |word,defin| word.scan(key).join == key }
end
def printable
opts_sorted = #opts.sort_by { |word,defin| word}
opts_sorted.each do |word,defin|
print "[#{word}] \"#{defin}\"\n"
end
end
end
and here's the error:
1) Dictionary can produce printable output like so: [keyword] "definition"
Failure/Error: #d.printable.should == %Q{[apple] "fruit"\n[fish] "aquatic animal
"\n[zebra] "African land animal with stripes"}
expected: "[apple] \"fruit\"\n[fish] \"aquatic animal\"\n[zebra] \"African lan
d animal with stripes\""
got: [["apple", "fruit"], ["fish", "aquatic animal"], ["zebra", "African
land animal with stripes"]] (using ==)
Diff:
## -1,4 +1,4 ##
-[apple] "fruit"
-[fish] "aquatic animal"
-[zebra] "African land animal with stripes"
+["apple", "fruit"]
+["fish", "aquatic animal"]
+["zebra", "African land animal with stripes"]
# ./11_dictionary/dictionary_spec.rb:81:in `block (2 levels) in <top (required)>
'

Lucky for you I did this whole thing just now! Below is the answer code with comments for the explanation! I hope this still helps even after 4 years!
class Dictionary # Create the class
attr_accessor :entries # Attribute Accessor; this is our setter and getter
def initialize(entries = {}) # Create constructor; if there is no value passed the default value is {}
#entries = {} # Declare instance variable with empty hash
if entries.is_a? Hash # is_a? is a method where it sees if its a class; Hash is the class we compare it to
entries.each {|key, value| #entries[key] = value} # if there is a value that's passed we copy the hash to our instance variable
end # End conditional
end # End constructor
def keywords # Main purpose of this method is to return what's inside our keys
keywords = [] # Create empty keyword variable
#entries.each_key {|key| keywords.push(key.to_s)} # each_key method only takes the keys in our hash and pushes them into the keywords array
keywords.sort # We sort the keywords variable
end # End method
def add(entry) # add method adds in an entry either a hash or a string
if entry.is_a? Hash # If the argument belongs to the class Hash; or if its a hash
entry.each {|key, value| #entries[key] = value} # Then we copy the key and values to our instance variable
elsif entry.is_a? String # If the arguemnt belongs to the class String; or if its a String
#entries[entry] = nil # We create a key for that string and set the value to nil
end # End conditional
end # End method
def include?(entry) # include? method this checks if the argument is in our entries and returns a boolean value
#entries.has_key?(entry) # has_key? checks the instance variable if it has the key
end # End method
def find(entry) # find method finds if certain letters are in our keys
#entries.select {|key| /#{entry}/.match(key)} # select gets the keys that match a certain keyword in our entries
# if #entries.has_key?(entry) # First attepmt to solve the test case
# #entries.select {|key,value| key == entry}
# else
# puts {}
# end
end # End method
def printable # printable method just prints out our entries like a dictionary
printable = [] # Create an empty array
#entries.sort.each do |key,value| # Sort and iterate to each key-value pair
printable.push("[#{key}] \"#{value}\"") # push the key-value pair formatted accordingly to the test case
end # End hash iteration
printable.join("\n") # join with newlines to get desired result
end # End method
end # End class

I think the problem is that your printable method is not returning what you want it to return.
The return value of printable is the value of the method's last statement (opts_sorted.each ...). You are using print to output the expected string but this does not change the return value of the method (which is what you are testing).
If you want to return the string you are printing instead try this:
def printable
opts_sorted = #opts.sort_by { |word, defin| word}
opts_sorted.map{ |word, defin| "[#{word}] \"#{defin}\"\n" }.join
end
map will transform the key-value pairs in the hash into an array of strings formatted the way you want them and then join will append them into a single string.

Related

Dictionary Class in Ruby from TestFirst.org

I'm trying to solve the Dictionary problem from TestFirst.org. The goal is to get my codes to work with the test spec. Apparently I am getting the NoMethodError: undefined method '[]=' for nil: NilClass
I post the my codes and test spec below.
It looks like the test can not read my add and keywords methods for some reasons. Can someone help me determine why it is behaving that way?
My codes:
class Dictionary
#initialization of entries
def entries
#hash = {}
end
def add(keyvalue)
keyvalue.each do |key, value|
#hash[key] = value #create key-value pair in #hash
end
end
def keywords
key_array = []
#hash.each {|key,value| key_array.push(key)} #push key inside key_array
end
end
Test Spec:
require '11_dictionary'
describe Dictionary do
before do
#d = Dictionary.new
end
it 'is empty when created' do
#d.entries.should == {}
end
it 'can add whole entries with keyword and definition' do
#d.add('fish' => 'aquatic animal')
#d.entries.should == {'fish' => 'aquatic animal'}
#d.keywords.should == ['fish']
end
it 'add keywords (without definition)' do
#d.add('fish')
#d.entries.should == {'fish' => nil}
#d.keywords.should == ['fish']
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be false
end
it "doesn't cheat when checking whether a given keyword exists" do
#d.include?('fish').should be false # if the method is empty, this test passes with nil returned
#d.add('fish')
#d.include?('fish').should be true # confirms that it actually checks
#d.include?('bird').should be false # confirms not always returning true after add
end
it "doesn't include a prefix that wasn't added as a word in and of itself" do
#d.add('fish')
#d.include?('fi').should be false
end
it "doesn't find a word in empty dictionary" do
#d.find('fi').should be_empty # {}
end
it 'finds nothing if the prefix matches nothing' do
#d.add('fiend')
#d.add('great')
#d.find('nothing').should be_empty
end
it "finds an entry" do
#d.add('fish' => 'aquatic animal')
#d.find('fish').should == {'fish' => 'aquatic animal'}
end
it 'finds multiple matches from a prefix and returns the entire entry (keyword + definition)' do
#d.add('fish' => 'aquatic animal')
#d.add('fiend' => 'wicked person')
#d.add('great' => 'remarkable')
#d.find('fi').should == {'fish' => 'aquatic animal', 'fiend' => 'wicked person'}
end
it 'lists keywords alphabetically' do
#d.add('zebra' => 'African land animal with stripes')
#d.add('fish' => 'aquatic animal')
#d.add('apple' => 'fruit')
#d.keywords.should == %w(apple fish zebra)
end
it 'can produce printable output like so: [keyword] "definition"' do
#d.add('zebra' => 'African land animal with stripes')
#d.add('fish' => 'aquatic animal')
#d.add('apple' => 'fruit')
#d.printable.should == %Q{[apple] "fruit"\n[fish] "aquatic animal"\n[zebra] "African land animal with stripes"}
end
end
It's because every test block executes separately, so, for example, in can add whole entries with keyword and definition you execute:
#d.add('fish' => 'aquatic animal')
but #hash instance variable in #d object is uninitialized. To fix it, you should initialize #hash upon object initialization:
class Dictionary
def initialize
#hash = {}
end
def entries
#hash
end
# etc.
end
or, instead of operating directly on #hash instance variable, you can create private accessor:
class Dictionary
def entries
hash
end
def keywords
key_array = []
hash.each {|key,value| key_array.push(key)} #push key inside key_array
end
# etc.
private
def hash
#hash ||= {}
end
end

Method works outside of Class, but not inside

The method "find" seems to work when I pull it out of the Class and test it, but for some reason it is returning empty when it is inside the Class. I can't figure out why...
class Dictionary
def entries
#entries ||= {}
end
def add(entry)
if entry.is_a?(String) == true
#entries = {entry => nil}
else
#entries= entry
end
end
def keywords
#entries.keys.sort
end
def include?(word)
entries.keys.include?(word)
end
def find(word)
result = {}
entries.each_pair do |key, value|
if key =~ /#{word}/
result[key] = value
end
end
result
end
end
It gets stuck at this part of the spec...
it 'finds multiple matches from a prefix and returns the entire entry (keyword + definition)' do
#d.add('fish' => 'aquatic animal')
#d.add('fiend' => 'wicked person')
#d.add('great' => 'remarkable')
#d.find('fi').should == {'fish' => 'aquatic animal', 'fiend' => 'wicked person'}
end
The error says...
expected: {"fish" => "aquatic animal", "fiend" => "wicked person"}
got: {} (using ==)
Diff: ## -1, 3, +1 ##
-"fiend" => "wicked person"
-"fish" => "aquatic animal"
# ./11_dictionary/dictionary_spec.rb:67:in 'block (2 levels) in >'
Your add method replaces the entire entries hash, rather than actually adding an entry. Fix that, and the find method should work.
For completeness, here's how I would implement add:
def add(entry)
if entry.is_a?(Hash)
#entries.merge!(entry)
else
#entries[entry] = nil
end
end

can I use hash.each even if i'm passing only a string key? Dictionary without definition

This is the error I'm getting. I need to pass a string key to a method that usually accepts string key and string symbol combo
Dictionary
is empty when created`
can add whole entries with keyword and definition
add keywords (without definition) (FAILED - 1)
Failures:
1) Dictionary add keywords (without definition)
**Failure/Error: #d.add('fish')
NoMethodError:
undefined method `each' for "fish":String
# ./11_dictionary/dictionary.rb:9:in `add'**
# ./11_dictionary/dictionary_spec.rb:27:in `block (2 levels) in <top (required)>'
This is the following spec. the #d.add('fish') passes a string without definition.
require 'dictionary'
describe Dictionary do
before do
#d = Dictionary.new
end
it 'is empty when created' do
#d.entries.should == {}
end
it 'can add whole entries with keyword and definition' do
#d.add('fish' => 'aquatic animal')
#d.entries.should == {'fish' => 'aquatic animal'}
#d.keywords.should == ['fish']
end
it 'add keywords (without definition)' do
#d.add('fish')
#d.entries.should == {'fish' => nil}
#d.keywords.should == ['fish']
end
my code:
I think my definition.each is where it goes wrong. From what I understand each goes through the array or hash in this example but only has one value so it doesn't know how to loop through it? So I thought about setting a default nil value for meaning and word but it doesn't accept it and comes with different errors.
class Dictionary
attr_accessor :keywords, :entries
def initialize
#entries = {}
end
def add(definition)
definition.each do |word, meaning|
#entries[word] = meaning
end
end
def keywords
#entries.keys.sort
end
def include?(key)
#entries.key?(key)
end
def find(query)
#entries.select { |word, definition| word.scan(query).join == query}
end
def entries
#entries
end
end
Thanks for you help in advance!
You're trying to call each on a String in #d.add('fish'), which isn't allowed. You could just add the following line above definition.each do |word, meaning|:
definition = { definition => nil } unless definition.is_a? Hash
although this could have unexpected consequences if you are passing different kinds of objects to add.
My code is rough.. This works too.
def add(hash)
if (hash.is_a? Hash)
#d=hash
else
#d = {hash => nil}
end

Dictionary Object in Ruby

So I'm trying to create a dictionary object in Ruby and get it to pass a bunch of RSPEC tests as part of a project. So far it's been good, but I'm stuck on one particular test. Here's the RSPEC up until that test:
require 'dictionary'
describe Dictionary do
before do
#d = Dictionary.new
end
it 'is empty when created' do
#d.entries.should == {}
end
it 'can add whole entries with keyword and definition' do
#d.add('fish' => 'aquatic animal')
#d.entries.should == {'fish' => 'aquatic animal'}
#d.keywords.should == ['fish']
end
it 'add keywords (without definition)' do
#d.add('fish')
#d.entries.should == {'fish' => nil}
#d.keywords.should == ['fish']
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
it "doesn't cheat when checking whether a given keyword exists" do
#d.include?('fish').should be_false # if the method is empty, this test passes with nil returned
#d.add('fish')
#d.include?('fish').should be_true # confirms that it actually checks
#d.include?('bird').should be_false # confirms not always returning true after add
end
end
Everything passes so far except for the last test "doesn't cheat when checking whether a given keyword exists". I'm trying to wrap my head around how I can get that to pass, but so far no success. Any help would be greatly appreciated. Here's what I have so far:
class Dictionary
attr_accessor :keywords, :entries
def initialize
#entries = {}
end
def add(defs)
defs.each do |word, definition|
#entries[word] = definition
end
end
def keywords
input = []
#entries.each do |key, value|
input << key
end
input.sort
end
def include?(key)
self.keywords.include?(keywords.to_s)
end
end
Thanks in advance!
There's a bug in:
self.keywords.include?(keywords.to_s)
keywords returns an array. You can't use keywords.to_s as a parameter for keywords.include? and expect it to find a match:
irb(main):002:0> keywords = %w[a b c]
=> ["a", "b", "c"]
irb(main):003:0> keywords.to_s
=> "[\"a\", \"b\", \"c\"]"
irb(main):004:0> keywords.include?(keywords.to_s)
=> false
irb(main):005:0> keywords.include?('a')
=> true
because you need to use an individual element in the keywords array if you want to find it. Notice that keywords.to_s is a String-ized version of the array, which could also be: '["a", "b", "c"]'. Hopefully that will help you recognize the problem the next time you encounter it.
From the documentation for include?:
a = [ "a", "b", "c" ]
a.include?("b") #=> true
a.include?("z") #=> false
So, change:
def include?(key)
self.keywords.include?(keywords.to_s)
end
to:
def include?(key)
self.keywords.include?(key)
end
What do you mean by "doesn't cheat"? How can code cheat? It only does what you told it. All the previous tests look like they'd rule out the conditions being tested in the "doesn't cheat" block which makes only:
#d.include?('bird').should be_false # confirms not always returning true after add
worth including in it. You could use:
#d.add('fish')
#d.include?('bird').should be_false # confirms not always returning true after add
if you really aren't sure how your code works.
Instead of building keywords using an array, which will get slower the larger your #entries list is, and results in include? running slower any time you call it,
take advantage of the fact that #entries is already a hash and use its methods:
def keywords
#entries.keys.sort
end
def include?(key)
!!#entries[key]
end
Or use this for include?:
def include?(key)
#entries.key?(key)
end
As totallymike mentions in the comment, most of the functions you want already exist in Hash. For the slightly different interfaces that you want, you should inherit Hash.
class Dictionary < Hash
def add(defs)
defs = {defs => nil} unless defs.kind_of?(Hash)
merge!(defs)
end
alias entries dup
def keywords; keys.sort end
end
Does this give you an idea how to get to pass "doesn't cheat when checking whether a given keyword exists"?
#h = Hash.new{|h,k,v| h[k] = nil}
#h["fish"]
p #h #=> {"fish"=>nil}
The {|h,k,v| h[k] = nil}part is run when a key is not present in the hash. It adds the key and gives it a nil value.

How to push keys and values into an empty hash w/ Ruby?

I have a dictionary class and want to be able to push keys (as keywords) and values (as definitions) into an empty hash with an 'add' method. I don't understand how to syntactically write that. I have included an RSPEC file too.
ruby:
class Dictionary
attr_accessor :keyword, :definition
def entries
#hash = {}
end
def add(options)
options.map do |keyword, definition|
#hash[keyword.to_sym] = definition
end
end
end
Rspec:
require 'dictionary'
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can add whole entries with keyword and definition' do
#d.add('fish' => 'aquatic animal')
#d.entries.should == {'fish' => 'aquatic animal'}
#d.keywords.should == ['fish']
end
Any help is appreciated. Thank you!
UPDATE:
Thank you Dave Newton for replying. I used your code and got this error:
ERROR:
*Failure/Error: #d.keywords.should == ['fish']
NoMethodError:
undefined method `keywords' for #<Dictionary:0x007fb0c31bd458
#hash={"fish"=>"aquatic animal"}>*
I get a different error when I convert 'word' into a symbol using #hash[word.to_sym] = defintion
*Failure/Error: #d.entries.should == {'fish' => 'aquatic animal'}
expected: {"fish"=>"aquatic animal"}
got: {:fish=>"aquatic animal"} (using ==)
Diff:
## -1,2 +1,2 ##
-"fish" => "aquatic animal"
+:fish => "aquatic animal"*
Instantiate your hash in Dictionary's initialize:
class Dictionary
def initialize
#hash = {}
end
def add(defs)
defs.each do |word, definition|
#hash[word] = definition
end
end
end
Right now you have no hash until you call entries, which you don't.
entries should return the existing hash, not create a new one.
keywords should return the hash's keys.
You do not need accessors for keyword and definition. Such singular items are meaningless in a dictionary class. You might want something like lookup(word) that returned a definition.
Also, you convert words to symbols, but I don't know why–particularly since you use string keys in your spec. Pick one, although I'm not convinced this is a case where symbols add value.
Please consider variable naming carefully to provide as much context as possible.
Looking at your Rspec, It looks like you need this setup.
class Dictionary
def initialize
#hash = {}
end
def add(key_value)
key_value.each do |key, value|
#hash[key] = value
end
end
def entries
#hash
end
def keywords
#hash.keys
end
end
Do not use key.to_sym in add method, just key
To give flexibility to add method, I can return the self object to continuesly add.
def add(key_value)
key_value.each do |key, value|
#hash[key] = value
end
self
end
So, I can do this now.
#d.add("a" => "apple").add("b" => "bat")

Resources