implementation issue of a rspec test in ruby - ruby

i have this test in ruby I'm trying to implement
require "silly_blocks"
describe "some silly block functions" do
describe "reverser" do
it "reverses the string returned by the default block" do
result = reverser do
"hello"
end
result.should == "olleh"
end
it "reverses each word in the string returned by the default block" do
result = reverser do
"hello dolly"
end
result.should == "olleh yllod"
end
end
here's the method
def do_reverse(str)
str = str.split
first_str = str[0].reverse
second_str= str[1]
if (second_str == nil)
str = first_str.to_s
else
second_str = str[1].reverse
str = (first_str +" "+ second_str)
end
end
what is the best way that i could implement it . when i try to rake the test it failed , but the method by itself return the reserve. i'm just a little confused.

Try this code:
def reverser
yield.split.map { |word| word.reverse}.join(" ")
end

Here's an easy way of doing what you're looking for, with specs.
# lib/reverse_words.rb
def reverse_words(phrase)
return '' if phrase.nil?
words = phrase.split
phrase.split.map(&:reverse!).join(' ')
end
def reverser
reverse_words(yield)
end
# spec/reverse_words_spec.rb
describe "#reverse_words" do
context "when single word" do
subject { reverse_words("hello") }
it { should == "olleh" }
end
context "when multiple words" do
subject { reverse_words("hello dolly") }
it { should == "olleh yllod" }
end
context "when nil" do
subject { reverse_words(nil) }
it { should == '' }
end
context "when empty" do
subject { reverse_words('') }
it { should == '' }
end
end
Note that the reverser spec simply makes use of the behavior that reverse_words has already been specced to pass.
describe "#reverser" do
subject do
reverser do
"this is a test"
end
end
it { should == reverse_words("this is a test") }
end
Here's a less wordy reverse_words spec:
describe "#reverse_words (less wordy)" do
# counterintuitive keys as answers to support the nil case
cases = { "olleh" => "hello",
"olleh yllod" => "hello dolly",
'' => nil,
'' => ''
}
cases.each_pair do |expected, input|
context "#{input} should equal #{expected}" do
subject { reverse_words(input) }
it { should == expected }
end
end
end

This works. The data you want is stored in "yield".
def reverser
yield.gsub(/\w+/) { |w| w.each_char.to_a.reverse.join }
end

My reverser method:
def reverser
# yield is the string given in the block
words = yield.split(' ')
final = []
words.each do |word|
final.push(word.reverse)
end
final.join(' ')
end

So. I came here looking for information on how to do this also. As the language wasn't clear. I went and looked offsite, and found enough information to pass the tests.
So, blocks are those things between curly braces that sometimes follow functions in ruby, such as
list.each {|i| i.reverse}
So what the spec is doing is trying to figure out what happens when it does:
rerverser {"hello"}
Putting yield in a function just returns whatever is in the block, so
def print_block
puts yield
end
print_block {"Hello world."}
#=> "Hello world"
Then you can just manipulate yield like you would manipulate any argument. There's a lot more to blocks. Here's a good place to start, but that's all you need to know to solve the exercise if you've solved all of Test First's learn_ruby exercises up until now.

Related

Converting Ruby Hash into string with escapes

I have a Hash which needs to be converted in a String with escaped characters.
{name: "fakename"}
and should end up like this:
'name:\'fakename\'
I don't know how this type of string is called. Maybe there is an already existing method, which I simply don't know...
At the end I would do something like this:
name = {name: "fakename"}
metadata = {}
metadata['foo'] = 'bar'
"#{name} AND #{metadata}"
which ends up in that:
'name:\'fakename\' AND metadata[\'foo\']:\'bar\''
Context: This query a requirement to search Stripe API: https://stripe.com/docs/api/customers/search
If possible I would use Stripe's gem.
In case you can't use it, this piece of code extracted from the gem should help you encode the query parameters.
require 'cgi'
# Copied from here: https://github.com/stripe/stripe-ruby/blob/a06b1477e7c28f299222de454fa387e53bfd2c66/lib/stripe/util.rb
class Util
def self.flatten_params(params, parent_key = nil)
result = []
# do not sort the final output because arrays (and arrays of hashes
# especially) can be order sensitive, but do sort incoming parameters
params.each do |key, value|
calculated_key = parent_key ? "#{parent_key}[#{key}]" : key.to_s
if value.is_a?(Hash)
result += flatten_params(value, calculated_key)
elsif value.is_a?(Array)
result += flatten_params_array(value, calculated_key)
else
result << [calculated_key, value]
end
end
result
end
def self.flatten_params_array(value, calculated_key)
result = []
value.each_with_index do |elem, i|
if elem.is_a?(Hash)
result += flatten_params(elem, "#{calculated_key}[#{i}]")
elsif elem.is_a?(Array)
result += flatten_params_array(elem, calculated_key)
else
result << ["#{calculated_key}[#{i}]", elem]
end
end
result
end
def self.url_encode(key)
CGI.escape(key.to_s).
# Don't use strict form encoding by changing the square bracket control
# characters back to their literals. This is fine by the server, and
# makes these parameter strings easier to read.
gsub("%5B", "[").gsub("%5D", "]")
end
end
params = { name: 'fakename', metadata: { foo: 'bar' } }
Util.flatten_params(params).map { |k, v| "#{Util.url_encode(k)}=#{Util.url_encode(v)}" }.join("&")
I use it now with that string, which works... Quite straigt forward:
"email:\'#{email}\'"
email = "test#test.com"
key = "foo"
value = "bar"
["email:\'#{email}\'", "metadata[\'#{key}\']:\'#{value}\'"].join(" AND ")
=> "email:'test#test.com' AND metadata['foo']:'bar'"
which is accepted by Stripe API

What distinguishes .new from .new(true)? "Indent" XML document in Test-First-Ruby

I'm working through these problems and am a little stuck on how to finish this out. This is the RSPEC and what's specifically troubling me is the last "it indents" test:
# # Topics
#
# * method_missing
# * blocks
# * strings
# * hashes
require "13_xml_document"
describe XmlDocument do
before do
#xml = XmlDocument.new
end
it "renders an empty tag" do
#xml.hello.should == "<hello/>"
end
it "renders a tag with attributes" do
#xml.hello(:name => 'dolly').should == "<hello name='dolly'/>"
end
it "renders a randomly named tag" do
tag_name = (1..8).map{|i| ('a'..'z').to_a[rand(26)]}.join
#xml.send(tag_name).should == "<#{tag_name}/>"
end
it "renders block with text inside" do
#xml.hello do
"dolly"
end.should == "<hello>dolly</hello>"
end
it "nests one level" do
#xml.hello do
#xml.goodbye
end.should == "<hello><goodbye/></hello>"
end
it "nests several levels" do
xml = XmlDocument.new
xml.hello do
xml.goodbye do
xml.come_back do
xml.ok_fine(:be => "that_way")
end
end
end.should == "<hello><goodbye><come_back><ok_fine be='that_way'/></come_back></goodbye></hello>"
end
it "indents" do
#xml = XmlDocument.new(true)
#xml.hello do
#xml.goodbye do
#xml.come_back do
#xml.ok_fine(:be => "that_way")
end
end
end.should ==
"<hello>\n" +
" <goodbye>\n" +
" <come_back>\n" +
" <ok_fine be='that_way'/>\n" +
" </come_back>\n" +
" </goodbye>\n" +
"</hello>\n"
end
end
I feel like I understand the problem and the solution would be something like
"<#{method}>\n" + " #{yield}" + "</#{method}>\n"
given my code:
class XmlDocument
#use method missing so that arbitrary methods
#can be called and converted to XML
def method_missing(method, hash=nil, &block)
if (hash == nil && block == nil)
"<#{method}/>"
elsif hash.is_a?(Hash)
#renders tag with attributes (from hash)
my_key = nil
my_val = nil
hash.each do |key, value|
my_key = key
my_val = value
end
"<#{method} #{my_key}='#{my_val}'/>"
else
#passes whatever to between tags including nested methods.
"<#{method}>#{yield}</#{method}>"
end
end
end
My problem is I don't know how to distinguish the "it nests several levels" test from the "it indents" test so I can fit it into my "if" statement. The only thing that seems to distinguish them is the the "it indents" test has
#xml = XmlDocument.new(true)
What does it mean to have "true" as an argument of #new? Is it relevant to my problem?
You can pass an argument to the object when it is initialized. In this case, you want the value to default to false. This way, the code for indents only runs when XmlDocument.new(true) is called.
class XmlDocument
def initialize(indent = false)
#indent = indent
end
def method_missing(method, args=nil, &block)
if #indent == true
#run with indents
else
#run without indents
end
end
end

Capitalize Second Word in a String (ruby)

I'm trying to create a method to capitalize the second word in a string. The code below works, but I was wondering if there are other ways to do this:
def camelcase(string)
tmp = string.split
tmp[1].capitalize!
tmp.join('')
end
def camelcase(string)
string.gsub(/\s(\w)/) { |match| $1.capitalize }
end
camelcase("foo bar baz") #=> "fooBarBaz"
Or you might wanna take a look at the camelcasemethod that comes with ActiveSupport::Inflector (see: http://apidock.com/rails/String/camelize)
def camelcase(string)
string.sub(/\s.*/) { |s| s.delete(' ').capitalize}
end
puts camelcase("foo bar bas")
=> "fooBarbaz"
You could use tap which "Yields x to the block, and then returns x" according to the docs. In this case capitalize! modifies x in place before being returned to the method chain for further processing by join.
def camelcase(string)
string.split.tap { |words| words[1].capitalize! }.join
end
camelcase('foo bar baz')
=> "fooBarbaz"
Try this:
s = "foo bar"
s.sub(/\s(\w)/) { $1.capitalize } # => "fooBar"

Ruby: No Block Given error

I keep getting a 'no block given' error when trying to pass the string to the is_tut? method. I am new to Ruby and have no idea what I'm doing wrong. Any and all help would be appreciated.
class Tut
##consonants = ["b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"]
def is_tut? string
if string =~ /^(([b-df-hj-np-z]ut)|([aeiou\s])|[[:punct:]])+$/i
yield
else
false
end
end
def self.to_tut string
string.each_char do |c|
c += "ut" if ##consonants.find { |i| i == c.downcase }
yield c
end
end
def self.to_english string
array = string.split //
array.each do |c|
if ##consonants.find { |i| i == c.downcase }
array.shift
array.shift
end
yield c
end
end
end
#Tut.to_tut( "Wow! Look at this get converted to Tut!" ) { |c| print c }
# should output : Wutowut! Lutookut atut tuthutisut gutetut cutonutvuteruttutedut tuto Tututut!
puts
puts
tut = Tut.to_tut( "Wow! Look at this get converted to Tut!" )
puts "from return: #{tut}"
puts
#Tut.to_tut( "Wutowut! Lutookut atut tuthutisut gutetut cutonutvuteruttutedut tuto Tututut!" ) { |c| print c }
# should outout : Wutowut! Lutookut atut tuthutisut gutetut cutonutvuteruttutedut tuto Tututut!
puts
puts
#tut = Tut.to_tut( "Wutowut! Lutookut atut tuthutisut gutetut cutonutvuteruttutedut tuto Tututut!" )
#puts "from return: #{tut}"
puts
#tut_string = ""
#Tut.to_tut( "I'm in tut but I want to be in english." ) { |c| tut_string += c }
#puts tut_string
# should output : I'mut inut tututut bututut I wutanuttut tuto bute inut enutgutlutisuthut.
puts
#Tut.to_english( tut_string ) { |c| print c }
# should output : I'm in tut but I want to be in english.
If you have yield within your method definition, that means you have to obligatorily pass a block when you use it (unless the part including it is not executed according to conditioning etc.). (You might already know, but in case you don't: a block is something that is described as {...} or do ... end) And yield will refer to the block.
If you want to make a block optional, then one way to do it is to put the & symbol before the variable name.
def method(argument, &block_argument)
if block_argument # block is given
block_argument.call(argument_for_block) # use call to execute the block
else # the value of block_argument becomes nil if you didn't give a block
# block was not given
end
end
This will allow optional block. Or, as suggested by Squeegy,
def method(argument)
if block_given? # block is given
yield(argument_for_block) # no need to use call to execute the block
else
# block was not given
end
end
will also work.
Because you're calling yield in your to_tut() method, this line will fail:
tut = Tut.to_tut( "Wow! Look at this get converted to Tut!" )
You either need to give a block (as you did in the first commented-out call to Tut.to_tut()), or you need to modify your to_tut() function to make the code block optional:
def self.to_tut string
string.each_char do |c|
c += "ut" if ##consonants.find { |i| i == c.downcase }
yield c if block_given?
end
end
yield requires a block to be passed to to_tut.
When you do:
Tut.to_tut( "Wow! Look at this get converted to Tut!" ) { |c| print c }
It works because it has the block { |c| print c }.
Without a block it will raise the error.

Return a single value from a nested each block, trying to use 'return'

def get_type
x = [{:type=>'A', :patterns=>['foo.*']}, {:type=>'B', :patterns=>['bar.*']}]
name = 'foo.txt'
result = x.each { |item|
item[:patterns].each { |regex|
puts "Checking #{regex} against #{name}"
if !name.match(regex).nil?
puts "Found match: #{item[:type]}"
return item[:type]
end
}
}
end
result = get_type
puts "result: #{result}"
Expected output:
Checking foo.* against foo.txt
Found match: A
result: A
However, all I see is:
Checking foo.* against foo.txt
Found match: A
My current work around is this:
def get_type
x = [{:type=>'A', :patterns=>['foo.*']}, {:type=>'B', :patterns=>['bar.*']}]
name = 'foo.txt'
result = []
x.each { |item|
item[:patterns].each { |regex|
puts "Checking #{regex} against #{name}"
if !name.match(regex).nil?
puts "Found match: #{item[:type]}"
result << item[:type]
end
}
}
result[0] unless result.empty?
end
Why doesn't the first approach work? or maybe it is 'working', I just don't understand why I'm not getting what I'd expect.
May I suggest a refactor? your code looks kind of clunky because you are using each loops (imperative) when you in fact need a map+first (functional). As Ruby enumerables are not lazy this would be inefficient, so people usually build the abstraction Enumerable#map_detect (or find_yield, or find_first, or map_first):
def get_type_using_map_detect(name)
xs = [{:type => 'A', :patterns => ['foo.*']}, {:type => 'B', :patterns => ['bar.*']}]
xs.map_detect do |item|
item[:patterns].map_detect do |regex|
item[:type] if name.match(regex)
end
end
end
This is a possible implementation of the method:
module Enumerable
# Like Enumerable#map but return only the first non-nil value
def map_detect
self.each do |item|
if result = (yield item)
return result
end
end
nil
end
end
Works fine for me. Are you actually invoking it with
result = get_type puts "result: #{result}"
? Because that shouldn't work at all, though I'm assuming there's a linefeed that got eaten when you posted this.

Resources