Here's my code:
class Dictionary
def entries
#entries ||= {}
end
def add(hash)
if hash.class == Hash
hash.each_pair do |k, v|
entries[k] = v
end
else
makehash = {hash => nil}
self.add(makehash)
end
#entries = entries
end
def keywords
#entries.keys
end
def include?(k)
if #entries == nil
false
elsif self.keywords.include?(k)
true
else
false
end
end
end
And here's the test I'm running it against:
require 'dictionary'
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
Now, that test will fail. However, if I change it to
it 'can check whether a given keyword exists' do
#d.include?('fish').should == false
end
then it passes.
How can I change my code so that should be_false passes instead of should == false? Thanks.
be_false matches falsey values (nil and false) and
be_true matches truthy values (other than nil or false)
From Rspec > 3.0,
be_false is renamed to be_falsey and
be_true is renamed to be_truthy
If you want to exactly match false, you should use
obj.should eq false
See the Documentation for more info about 'be' matchers
Related
I was implementing my own enumerable method using Ruby when I reach my_any? method. It doesn't pass some cases
my_any? works for some of the cases but others don't work
module Enumerable
def my_each
i = 0
while i < size
yield self[i]
i += 1
end
end
def my_any?
if block_given?
my_each {|item| return true if yield item}
end
false
end
end
The below test cases are the require output
Test case 1
[4,8,true,18].my_any? # => true
test case 2
[2,nil,5,true].my_any? # => true
test case 3
[nil,nil,nil,false].my_any? # => false
test case 4
[nil,nil,nil].my_any? # => false # But my_any? fails this condition
Any Idea please
def my_any?
if block_given?
my_each {|item| return true if yield item}
end
false
end
This method is immediately checking if it was called with a block and, if not, returns false. For example, [].my_any? will return false for block_given? while [].my_any? { true } will return true. None of your examples are passing a block, so they will always skip the if and return false.
The correct behavior of any? should be this: if a block is given, yield the value to it to determine if it satisfies the condition; if a block is not given, just check if the value is truthy.
def my_any?
my_each do |item|
if block_given?
return true if yield item
else
return true if item
end
end
false
end
or more succinctly
def my_any?
my_each {|item| return true if block_given? ? yield(item) : item }
false
end
This question already has answers here:
expected true to respond to true?
(2 answers)
Closed 8 years ago.
Here's the spec:
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
Here's my method:
def include?(k)
#entries.each_key do |n|
if (n==k)
return true
end
end
return false
end
If I do, say:
puts myd.include?('fish')
console prints false (correctly)
But the spec fails with:
Failure/Error: #d.include?('fish').should be_false
expected false to respond to `false?`
And I do not know why. Been working on this for a day now and completely stumped...
Here's the whole thing:
class Dictionary
attr_accessor :entries, :keywords
def initialize
#entries={}
end
def add(entry)
if entry.class==Hash
#entries.merge!(entry)
elsif entry.class==String
h={entry => nil}
#entries.merge!(h)
end
end
def keywords
#entries.keys.sort
end
def include?(k)
#entries.each_key do |n|
if (n==k)
return true
end
end
return false
end
def find(term)
results={}
#entries.each_key do |n|
if (n.include? term)
results.merge!(n => #entries[n])
end
end
if (results.length)
return results
end
return false
end
def printable
str=""
sortedentries = #entries.sort_by { |k, v| k}
str=sortedentries.map{ |k, v| "[#{k}] \"#{v}\"\n" }.join
return str
end
end
Rspec's magic method is biting you here. The hint is in the error:
expected false to respond to `false?
If the thing you are testing does not have a method false? you cannot run be_false. e.g. nil.nil? can be ran, so then nil.should be_nil is a valid test. But nil.admin? is not runnable, hence nil.should be_admin is not a valid test.
Instead of should be_false consider using should equal false.
I am running this portion of a test:
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
With this code:
class Dictionary
def initialize
#hash = {}
end
def add(new_entry)
new_entry.class == String ? #hash[new_entry] = nil : new_entry.each { |noun, definition| #hash[noun] = definition}
end
def entries
#hash
end
def keywords
#hash.keys
end
def include?(word)
if #hash.has_key?(word)
true
else
false
end
end
end
I don't know what I'm doing wrong, but my tests keep failing and saying this:
> 1) Dictionary can check whether a given keyword exists
> Failure/Error: #d.include?('fish').should be_false
> expected false to respond to `false?`
I am confused at the error since it seems to be giving the correct answer. I would really appreciate if someone could take a few minutes to tell me what's wrong with my code.
Thank you tons.
If you browse the RSpec Expectations 2.99 and RSpec Expectations 2.14 and search the section - Truthiness and existentialism, you will find
expect(actual).to be_true # passes if actual is truthy (not nil or false)
expect(actual).to be_false # passes if actual is falsy (nil or false)
# ...............
# ...
But of you browse RSpec Expectations 3.0 , the above method names got changed to -
expect(actual).to be_truthy # passes if actual is truthy (not nil or false)
expect(actual).to be true # passes if actual == true
expect(actual).to be_falsey # passes if actual is falsy (nil or false)
# ...........
#......
It seems you are in 3.0, and using the method which were exist prior to this version. Thus you were getting the error.
I put the code in my test.rb file as below :-
class Dictionary
def initialize
#hash = {}
end
def add(new_entry)
new_entry.class == String ? #hash[new_entry] = nil : new_entry.each { |noun, definition| #hash[noun] = definition}
end
def entries
#hash
end
def keywords
#hash.keys
end
def include?(word)
if #hash.has_key?(word)
true
else
false
end
end
end
And my spec/test_spec.rb file is -
require_relative "../test.rb"
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
end
Now I am running the code from my console, and it works :
arup#linux-wzza:~/Ruby> rspec -v
2.14.8
arup#linux-wzza:~/Ruby> rspec spec
.
Finished in 0.00169 seconds
1 example, 0 failures
Now I am changing the code in my spec/test_spec.rb file :-
require_relative "../test.rb"
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_falsey
end
end
and again run the test :-
arup#linux-wzza:~/Ruby> rspec -v
2.14.8
arup#linux-wzza:~/Ruby> rspec spec
F
Failures:
1) Dictionary can check whether a given keyword exists
Failure/Error: #d.include?('fish').should be_falsey
NoMethodError:
undefined method `falsey?' for false:FalseClass
# ./spec/test_spec.rb:9:in `block (2 levels) in <top (required)>'
Finished in 0.00179 seconds
1 example, 1 failure
Failed examples:
rspec ./spec/test_spec.rb:8 # Dictionary can check whether a given keyword exists
arup#linux-wzza:~/Ruby>
Now, they also mentioned in the 3.0.0.beta1 / 2013-11-07 changelog
Rename be_true and be_false to be_truthy and be_falsey. (Sam Phippen)
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.
Orginal Question
This is a really horrible method, which checks for equality on base of the code but case agnostic
def ==(another_country)
(code.nil? ? nil : code.downcase) == (another_country.code.nil? ? nil : another_country.code.downcase) unless another_country.nil?
end
Can you point my in the right direction how to write this more elegant w/o reliying on ugly if else structures?
This is the solution I ended up using (+RSpecs)
# Country model
class Country < ActiveRecord::Base
attr_accessible :code
def ==(another_country)
code.to_s.downcase == another_country.code.to_s.downcase rescue false
end
end
Extensive Tests:
# RSpec
describe Country do
describe 'equality based solely on Country.code' do
before do
#country_code_de = FactoryGirl.build(:country, :code => 'de')
end
it 'should be equal if Country.code is equal' do
other_country_code_de = FactoryGirl.build(:country, :code => 'de')
#country_code_de.should == other_country_code_de
end
it 'should be not equal if Country.code is not equal' do
country_code_usa = FactoryGirl.build(:country, :code => 'usa')
#country_code_de.should_not == country_code_usa
end
it 'should be case insensitive' do
country_code_de_uppercase = FactoryGirl.build(:country, :code => 'DE')
#country_code_de.should == country_code_de_uppercase
end
it 'should not rely on id for equality' do
#country_code_de.id = 0
country_code_usa = FactoryGirl.build(:country, :code => 'usa', :id => 0)
#country_code_de.should_not == country_code_usa
end
it 'should be not equal if Country.code of one Country is nil' do
country_code_nil = FactoryGirl.build(:country, :code => nil)
#country_code_de.should_not == country_code_nil
end
it 'should be equal if Country.code for both countries is nil' do
country_code_nil = FactoryGirl.build(:country, :code => nil)
other_country_code_nil = FactoryGirl.build(:country, :code => nil)
country_code_nil.should == other_country_code_nil
end
it 'should be not equal if other Country is nil' do
#country_code_de.should_not == nil
end
it 'should be not equal if other object is not a Country' do
#country_code_de.should_not == 'test'
end
it 'should be equal for descendants of Country with same Country.code' do
class CountryChild < Country
end
country_child = CountryChild.new(:code => 'de')
#country_code_de.should == country_child
end
end
end
How about this,
def ==(another_country)
return false if code.blank? # Remove this line if you want to return true if code and antoher_country.code are nil
code.to_s.downcase == another_country.to_s.code.downcase rescue false
end
Here if any of code, another_country or another_country.code is nil, it will through up an exception and rescue false statement will return false value.
If everything goes well, the comparison will happen and true or false will be returned based on the input.
Perhaps you could break the logic into two methods, one returning the object's identity, another for checking equality:
class MyClass
def identity
return nil if code.nil?
code.downcase
end
def ==(other)
return false unless other.is_a?(MyClass)
self.identity == other.identity
end
end
If you are using Rails:
def ==(another_country)
return nil unless another_country
code.try(:downcase) == another_country.code.try(:downcase)
end
nil has a to_s method:
def ==(another_country)
#return nil if another_country.nil?
self.code.to_s.downcase == another_country.code.to_s.downcase
end
Since any value that is not nil or false acting like true in conditions, there is some tricks what you can do with the code.
The expression like
(code.nil? ? nil : code.downcase)
can be painlessly replaced by
(code.downcase if code) # or by this one (code && code.downcase)
The second one
(do_something) unless another_country.nil?
as same as
(do_something) if another_country
# or
another_contry && (do_something)
So eventually you can turn your method into this
def ==(another_country)
code && another_country.code &&
code.downcase == another_country.code.downcase
end
Some tests
class Country
attr_accessor :code
def initialize(code)
#code = code
end
def ==(another_country)
code && another_country.code &&
code.downcase == another_country.code.downcase
end
end
p Country.new("FOObar") == Country.new("fooBAR") # => true
p Country.new(nil) == Country.new(nil) # => nil
p Country.new("XXX") == Country.new(nil) # => nil
p Country.new(nil) == Country.new("XXX") # => nil
def == (another_country)
if code.nil? || another_country.nil? || another_country.code.nil?
return nil
end
code.downcase == another_country.code.downcase
end
This way, it is visible at a glance what you are doing - nil check and a comparision.
def == (another_country)
return unless another_country.is_a?(Country)
return if code.nil? || another_country.code.nil?
code.casecmp(another_country.code).zero?
end
The class check is a good practice in case you end up with an array of mixed types.
If you are not worried about the '' vs nil case you can compress it a bit to the following. I don't think it's worth it though.
def == (another_country)
code.try(:casecmp, another_country.code.to_s).try(:zero?) if another_country.is_a?(Country)
end
Note, if you are overriding == you should also override eql? and hash otherwise you can get unexpected results, with hashes and enumerable methods.
Ruby Monk - Equality of Objects